...
1
2
3 package udevproxy
4
5 import (
6 "fmt"
7 "os"
8 "runtime"
9
10 "github.com/hashicorp/go-multierror"
11 "golang.org/x/sys/unix"
12
13 "edge-infra.dev/pkg/lib/kernel/udev"
14 )
15
16 type UdevProxy interface {
17 Send(path string, uevents []*udev.UEvent) error
18 }
19
20 type udevProxy struct{}
21
22 func NewUdevProxy() UdevProxy {
23 return &udevProxy{}
24 }
25
26 type nsHandle int
27
28 func (ns *nsHandle) Close() error {
29 if err := unix.Close(int(*ns)); err != nil {
30 return err
31 }
32 *ns = -1
33 return nil
34 }
35
36 func (ns *nsHandle) Set() error {
37 return unix.Setns(int(*ns), unix.CLONE_NEWNET)
38 }
39
40
41
42 func (up udevProxy) Send(path string, uevents []*udev.UEvent) (err error) {
43 runtime.LockOSThread()
44 defer runtime.UnlockOSThread()
45
46 currentNs, err := getCurrentNetworkNamespace()
47 if err != nil {
48 return fmt.Errorf("error getting current network namespace: %w", err)
49 }
50
51 containerNs, err := getContainerNetworkNamespace(path)
52 if err != nil {
53 return fmt.Errorf("error fetching container file-descriptor: %w", err)
54 }
55 defer func() {
56 if ctrNsErr := containerNs.Close(); err != nil {
57 err = multierror.Append(err, ctrNsErr)
58 }
59 if currentNsErr := currentNs.Set(); err != nil {
60 err = multierror.Append(err, currentNsErr)
61 }
62 }()
63
64 if nsSetErr := containerNs.Set(); err != nil {
65 return multierror.Append(err, fmt.Errorf("error setting container network namespace: %w", nsSetErr))
66 }
67
68 addr := unix.SockaddrNetlink{
69 Family: unix.AF_NETLINK,
70 Groups: uint32(2),
71 }
72
73 fd, netlinkSocketErr := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_KOBJECT_UEVENT)
74 if netlinkSocketErr != nil {
75 return multierror.Append(err, fmt.Errorf("error creating af_netlink socket: %w", netlinkSocketErr))
76 }
77 defer unix.Close(fd)
78
79 if bindErr := unix.Bind(fd, &addr); err != nil {
80 return multierror.Append(err, fmt.Errorf("error binding af_netlink file-descriptor to netlink socket: %w", bindErr))
81 }
82
83 for _, ue := range uevents {
84 if sendErr := unix.Sendto(fd, *ue.ToBytes(), 0, &addr); err != nil {
85 err = multierror.Append(err, fmt.Errorf("error sending uevent to container network ns: %w", sendErr))
86 }
87 }
88 return err
89 }
90
91
92 func readFileDescriptor(path string) (int, error) {
93 fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC, 0)
94 if err != nil {
95 return -1, err
96 }
97 return fd, nil
98 }
99
100 func getCurrentNetworkNamespace() (nsHandle, error) {
101 pid, tid := os.Getpid(), unix.Gettid()
102 taskPath := fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)
103 fd, err := readFileDescriptor(taskPath)
104 return nsHandle(fd), err
105 }
106
107 func getContainerNetworkNamespace(path string) (nsHandle, error) {
108 fd, err := readFileDescriptor(path)
109 return nsHandle(fd), err
110 }
111
View as plain text