github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/handle_linux.go (about) 1 package netlink 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/sagernet/netlink/nl" 8 "github.com/vishvananda/netns" 9 "golang.org/x/sys/unix" 10 ) 11 12 // Empty handle used by the netlink package methods 13 var pkgHandle = &Handle{} 14 15 // Handle is an handle for the netlink requests on a 16 // specific network namespace. All the requests on the 17 // same netlink family share the same netlink socket, 18 // which gets released when the handle is Close'd. 19 type Handle struct { 20 sockets map[int]*nl.SocketHandle 21 lookupByDump bool 22 } 23 24 // SetSocketTimeout configures timeout for default netlink sockets 25 func SetSocketTimeout(to time.Duration) error { 26 if to < time.Microsecond { 27 return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond) 28 } 29 30 nl.SocketTimeoutTv = unix.NsecToTimeval(to.Nanoseconds()) 31 return nil 32 } 33 34 // GetSocketTimeout returns the timeout value used by default netlink sockets 35 func GetSocketTimeout() time.Duration { 36 nsec := unix.TimevalToNsec(nl.SocketTimeoutTv) 37 return time.Duration(nsec) * time.Nanosecond 38 } 39 40 // SupportsNetlinkFamily reports whether the passed netlink family is supported by this Handle 41 func (h *Handle) SupportsNetlinkFamily(nlFamily int) bool { 42 _, ok := h.sockets[nlFamily] 43 return ok 44 } 45 46 // NewHandle returns a netlink handle on the current network namespace. 47 // Caller may specify the netlink families the handle should support. 48 // If no families are specified, all the families the netlink package 49 // supports will be automatically added. 50 func NewHandle(nlFamilies ...int) (*Handle, error) { 51 return newHandle(netns.None(), netns.None(), nlFamilies...) 52 } 53 54 // SetSocketTimeout sets the send and receive timeout for each socket in the 55 // netlink handle. Although the socket timeout has granularity of one 56 // microsecond, the effective granularity is floored by the kernel timer tick, 57 // which default value is four milliseconds. 58 func (h *Handle) SetSocketTimeout(to time.Duration) error { 59 if to < time.Microsecond { 60 return fmt.Errorf("invalid timeout, minimul value is %s", time.Microsecond) 61 } 62 tv := unix.NsecToTimeval(to.Nanoseconds()) 63 for _, sh := range h.sockets { 64 if err := sh.Socket.SetSendTimeout(&tv); err != nil { 65 return err 66 } 67 if err := sh.Socket.SetReceiveTimeout(&tv); err != nil { 68 return err 69 } 70 } 71 return nil 72 } 73 74 // SetSocketReceiveBufferSize sets the receive buffer size for each 75 // socket in the netlink handle. The maximum value is capped by 76 // /proc/sys/net/core/rmem_max. 77 func (h *Handle) SetSocketReceiveBufferSize(size int, force bool) error { 78 opt := unix.SO_RCVBUF 79 if force { 80 opt = unix.SO_RCVBUFFORCE 81 } 82 for _, sh := range h.sockets { 83 fd := sh.Socket.GetFd() 84 err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, opt, size) 85 if err != nil { 86 return err 87 } 88 } 89 return nil 90 } 91 92 // GetSocketReceiveBufferSize gets the receiver buffer size for each 93 // socket in the netlink handle. The retrieved value should be the 94 // double to the one set for SetSocketReceiveBufferSize. 95 func (h *Handle) GetSocketReceiveBufferSize() ([]int, error) { 96 results := make([]int, len(h.sockets)) 97 i := 0 98 for _, sh := range h.sockets { 99 fd := sh.Socket.GetFd() 100 size, err := unix.GetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_RCVBUF) 101 if err != nil { 102 return nil, err 103 } 104 results[i] = size 105 i++ 106 } 107 return results, nil 108 } 109 110 // SetStrictCheck sets the strict check socket option for each socket in the netlink handle. Returns early if any set operation fails 111 func (h *Handle) SetStrictCheck(state bool) error { 112 for _, sh := range h.sockets { 113 var stateInt int = 0 114 if state { 115 stateInt = 1 116 } 117 err := unix.SetsockoptInt(sh.Socket.GetFd(), unix.SOL_NETLINK, unix.NETLINK_GET_STRICT_CHK, stateInt) 118 if err != nil { 119 return err 120 } 121 } 122 return nil 123 } 124 125 // NewHandleAt returns a netlink handle on the network namespace 126 // specified by ns. If ns=netns.None(), current network namespace 127 // will be assumed 128 func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error) { 129 return newHandle(ns, netns.None(), nlFamilies...) 130 } 131 132 // NewHandleAtFrom works as NewHandle but allows client to specify the 133 // new and the origin netns Handle. 134 func NewHandleAtFrom(newNs, curNs netns.NsHandle) (*Handle, error) { 135 return newHandle(newNs, curNs) 136 } 137 138 func newHandle(newNs, curNs netns.NsHandle, nlFamilies ...int) (*Handle, error) { 139 h := &Handle{sockets: map[int]*nl.SocketHandle{}} 140 fams := nl.SupportedNlFamilies 141 if len(nlFamilies) != 0 { 142 fams = nlFamilies 143 } 144 for _, f := range fams { 145 s, err := nl.GetNetlinkSocketAt(newNs, curNs, f) 146 if err != nil { 147 return nil, err 148 } 149 h.sockets[f] = &nl.SocketHandle{Socket: s} 150 } 151 return h, nil 152 } 153 154 // Close releases the resources allocated to this handle 155 func (h *Handle) Close() { 156 for _, sh := range h.sockets { 157 sh.Close() 158 } 159 h.sockets = nil 160 } 161 162 // Delete releases the resources allocated to this handle 163 // 164 // Deprecated: use Close instead which is in line with typical resource release 165 // patterns for files and other resources. 166 func (h *Handle) Delete() { 167 h.Close() 168 } 169 170 func (h *Handle) newNetlinkRequest(proto, flags int) *nl.NetlinkRequest { 171 // Do this so that package API still use nl package variable nextSeqNr 172 if h.sockets == nil { 173 return nl.NewNetlinkRequest(proto, flags) 174 } 175 return &nl.NetlinkRequest{ 176 NlMsghdr: unix.NlMsghdr{ 177 Len: uint32(unix.SizeofNlMsghdr), 178 Type: uint16(proto), 179 Flags: unix.NLM_F_REQUEST | uint16(flags), 180 }, 181 Sockets: h.sockets, 182 } 183 }