udp_darwin.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //go:build !e2e_testing
  2. package udp
  3. import (
  4. "context"
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "log/slog"
  9. "net"
  10. "net/netip"
  11. "syscall"
  12. "unsafe"
  13. "github.com/slackhq/nebula/config"
  14. "golang.org/x/sys/unix"
  15. )
  16. type StdConn struct {
  17. *net.UDPConn
  18. isV4 bool
  19. sysFd uintptr
  20. l *slog.Logger
  21. }
  22. var _ Conn = &StdConn{}
  23. func NewListener(l *slog.Logger, s Settings) (Conn, error) {
  24. lc := NewListenConfig(s.Multi)
  25. pc, err := lc.ListenPacket(context.TODO(), "udp", s.Listen.String())
  26. if err != nil {
  27. return nil, err
  28. }
  29. if uc, ok := pc.(*net.UDPConn); ok {
  30. c := &StdConn{UDPConn: uc, l: l}
  31. rc, err := uc.SyscallConn()
  32. if err != nil {
  33. return nil, fmt.Errorf("failed to open udp socket: %w", err)
  34. }
  35. err = rc.Control(func(fd uintptr) {
  36. c.sysFd = fd
  37. })
  38. if err != nil {
  39. return nil, fmt.Errorf("failed to get udp fd: %w", err)
  40. }
  41. la, err := c.LocalAddr()
  42. if err != nil {
  43. return nil, err
  44. }
  45. c.isV4 = la.Addr().Is4()
  46. return c, nil
  47. }
  48. return nil, fmt.Errorf("unexpected PacketConn: %T %#v", pc, pc)
  49. }
  50. func NewListenConfig(multi bool) net.ListenConfig {
  51. return net.ListenConfig{
  52. Control: func(network, address string, c syscall.RawConn) error {
  53. if multi {
  54. var controlErr error
  55. err := c.Control(func(fd uintptr) {
  56. if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
  57. controlErr = fmt.Errorf("SO_REUSEPORT failed: %v", err)
  58. return
  59. }
  60. })
  61. if err != nil {
  62. return err
  63. }
  64. if controlErr != nil {
  65. return controlErr
  66. }
  67. }
  68. return nil
  69. },
  70. }
  71. }
  72. //go:linkname sendto golang.org/x/sys/unix.sendto
  73. //go:noescape
  74. func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen int32) (err error)
  75. func (u *StdConn) WriteTo(b []byte, ap netip.AddrPort) error {
  76. var sa unsafe.Pointer
  77. var addrLen int32
  78. if u.isV4 {
  79. if ap.Addr().Is6() {
  80. return ErrInvalidIPv6RemoteForSocket
  81. }
  82. var rsa unix.RawSockaddrInet4
  83. rsa.Family = unix.AF_INET
  84. rsa.Addr = ap.Addr().As4()
  85. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ap.Port())
  86. sa = unsafe.Pointer(&rsa)
  87. addrLen = syscall.SizeofSockaddrInet4
  88. } else {
  89. var rsa unix.RawSockaddrInet6
  90. rsa.Family = unix.AF_INET6
  91. rsa.Addr = ap.Addr().As16()
  92. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ap.Port())
  93. sa = unsafe.Pointer(&rsa)
  94. addrLen = syscall.SizeofSockaddrInet6
  95. }
  96. // Golang stdlib doesn't handle EAGAIN correctly in some situations so we do writes ourselves
  97. // See https://github.com/golang/go/issues/73919
  98. for {
  99. //_, _, err := unix.Syscall6(unix.SYS_SENDTO, u.sysFd, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0, sa, addrLen)
  100. err := sendto(int(u.sysFd), b, 0, sa, addrLen)
  101. if err == nil {
  102. // Written, get out before the error handling
  103. return nil
  104. }
  105. if errors.Is(err, syscall.EINTR) {
  106. // Write was interrupted, retry
  107. continue
  108. }
  109. if errors.Is(err, syscall.EAGAIN) {
  110. return &net.OpError{Op: "sendto", Err: unix.EWOULDBLOCK}
  111. }
  112. if errors.Is(err, syscall.EBADF) {
  113. return net.ErrClosed
  114. }
  115. return &net.OpError{Op: "sendto", Err: err}
  116. }
  117. }
  118. func (u *StdConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) (int, error) {
  119. // An un-sendable destination costs its own packet, never the ones behind it in the batch.
  120. // TODO: WriteTo maps EWOULDBLOCK to an error, so a full send buffer
  121. // silently drops the rest of a burst (linux blocks instead). Poll for
  122. // writability on EAGAIN before giving up on the remainder.
  123. written := 0
  124. for i, b := range bufs {
  125. if err := u.WriteTo(b, addrs[i]); err == nil {
  126. written++
  127. } else {
  128. u.l.Debug("failed to write packet in batch", "udpAddr", addrs[i], "error", err)
  129. }
  130. }
  131. return written, nil
  132. }
  133. func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
  134. a := u.UDPConn.LocalAddr()
  135. switch v := a.(type) {
  136. case *net.UDPAddr:
  137. addr, ok := netip.AddrFromSlice(v.IP)
  138. if !ok {
  139. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned invalid IP address: %s", v.IP)
  140. }
  141. return netip.AddrPortFrom(addr, uint16(v.Port)), nil
  142. default:
  143. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned: %#v", a)
  144. }
  145. }
  146. func (u *StdConn) ReloadConfig(c *config.C) {
  147. // TODO
  148. }
  149. func NewUDPStatsEmitter(udpConns []Conn) func() {
  150. // No UDP stats for non-linux
  151. return func() {}
  152. }
  153. // recvBatch is how many datagrams one recvmsg_x call may return.
  154. const recvBatch = 64
  155. // msghdrX mirrors xnu's struct msghdr_x (bsd/sys/socket_private.h). recvmsg_x reports each
  156. // datagram's length in Datalen rather than in its return value.
  157. type msghdrX struct {
  158. Name *byte
  159. Namelen uint32
  160. Iov *unix.Iovec
  161. Iovlen int32
  162. Control *byte
  163. Controllen uint32
  164. Flags int32
  165. Datalen uint64
  166. }
  167. // ListenOut drains the socket with recvmsg_x, darwin's private batched recvmsg, so one syscall reads
  168. // up to recvBatch datagrams instead of one. Sends stay on sendto: xnu batches sendmsg_x through
  169. // the stack only on connected sockets, and on this unconnected one it loops sendit per message.
  170. func (u *StdConn) ListenOut(r EncReader, flush func()) error {
  171. rc, err := u.UDPConn.SyscallConn()
  172. if err != nil {
  173. return err
  174. }
  175. bufs := make([][]byte, recvBatch)
  176. names := make([]unix.RawSockaddrInet6, recvBatch)
  177. iovs := make([]unix.Iovec, recvBatch)
  178. hdrs := make([]msghdrX, recvBatch)
  179. for i := range hdrs {
  180. bufs[i] = make([]byte, MTU)
  181. iovs[i].Base = &bufs[i][0]
  182. iovs[i].SetLen(MTU)
  183. hdrs[i].Iov = &iovs[i]
  184. hdrs[i].Iovlen = 1
  185. hdrs[i].Name = (*byte)(unsafe.Pointer(&names[i]))
  186. }
  187. for {
  188. var n int
  189. var errno syscall.Errno
  190. err := rc.Read(func(fd uintptr) bool {
  191. for i := range hdrs {
  192. hdrs[i].Namelen = unix.SizeofSockaddrInet6
  193. hdrs[i].Flags = 0
  194. hdrs[i].Datalen = 0
  195. }
  196. r0, _, e := unix.Syscall6(unix.SYS_RECVMSG_X, fd, uintptr(unsafe.Pointer(&hdrs[0])), recvBatch, 0, 0, 0)
  197. if e == unix.EAGAIN {
  198. return false
  199. }
  200. n, errno = int(r0), e
  201. return true
  202. })
  203. if err != nil {
  204. if errors.Is(err, net.ErrClosed) {
  205. return err
  206. }
  207. u.l.Error("unexpected udp socket receive error", "error", err)
  208. continue
  209. }
  210. if errno == unix.ENOSYS || errno == unix.EPERM || errno == unix.EOPNOTSUPP {
  211. // recvmsg_x is private, so a kernel or sandbox (such as an iOS extension's) may refuse it.
  212. u.l.Warn("recvmsg_x unavailable, reading one datagram per syscall", "error", errno)
  213. return u.listenOutSingle(r, flush)
  214. }
  215. if errno != 0 {
  216. if errno != unix.EINTR {
  217. u.l.Error("unexpected udp socket receive error", "error", errno)
  218. }
  219. continue
  220. }
  221. if err := checkMsghdrX(hdrs, names, n); err != nil {
  222. // The batch was read through a layout the kernel no longer writes, so none of it is trustworthy.
  223. u.l.Warn("recvmsg_x returned an unexpected header, reading one datagram per syscall", "error", err)
  224. return u.listenOutSingle(r, flush)
  225. }
  226. for i := 0; i < n; i++ {
  227. addr, ok := sockaddrToAddrPort(&names[i])
  228. if !ok {
  229. continue
  230. }
  231. l := int(hdrs[i].Datalen)
  232. r(addr, bufs[i][:l:l])
  233. }
  234. flush()
  235. }
  236. }
  237. // checkMsghdrX rejects a recvmsg_x result that intact msghdr_x entries can't produce, which is how a change
  238. // to xnu's private struct layout would show up.
  239. func checkMsghdrX(hdrs []msghdrX, names []unix.RawSockaddrInet6, n int) error {
  240. if n > len(hdrs) {
  241. return fmt.Errorf("returned %d datagrams for %d headers", n, len(hdrs))
  242. }
  243. for i := range n {
  244. if hdrs[i].Datalen > MTU {
  245. return fmt.Errorf("datagram %d is %d bytes, past the %d byte buffer", i, hdrs[i].Datalen, MTU)
  246. }
  247. switch {
  248. case names[i].Family == unix.AF_INET && hdrs[i].Namelen == unix.SizeofSockaddrInet4:
  249. case names[i].Family == unix.AF_INET6 && hdrs[i].Namelen == unix.SizeofSockaddrInet6:
  250. default:
  251. return fmt.Errorf("datagram %d has address family %d with length %d", i, names[i].Family, hdrs[i].Namelen)
  252. }
  253. }
  254. return nil
  255. }
  256. // listenOutSingle reads one datagram per recvfrom and flushes after each, for when recvmsg_x is refused or misbehaves.
  257. func (u *StdConn) listenOutSingle(r EncReader, flush func()) error {
  258. buffer := make([]byte, MTU)
  259. for {
  260. n, rua, err := u.ReadFromUDPAddrPort(buffer)
  261. if err != nil {
  262. if errors.Is(err, net.ErrClosed) {
  263. return err
  264. }
  265. u.l.Error("unexpected udp socket receive error", "error", err)
  266. continue
  267. }
  268. r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), buffer[:n:n])
  269. flush()
  270. }
  271. }
  272. func sockaddrToAddrPort(sa *unix.RawSockaddrInet6) (netip.AddrPort, bool) {
  273. switch sa.Family {
  274. case unix.AF_INET:
  275. sa4 := (*unix.RawSockaddrInet4)(unsafe.Pointer(sa))
  276. port := binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&sa4.Port))[:])
  277. return netip.AddrPortFrom(netip.AddrFrom4(sa4.Addr), port), true
  278. case unix.AF_INET6:
  279. port := binary.BigEndian.Uint16((*[2]byte)(unsafe.Pointer(&sa.Port))[:])
  280. return netip.AddrPortFrom(netip.AddrFrom16(sa.Addr).Unmap(), port), true
  281. }
  282. return netip.AddrPort{}, false
  283. }
  284. func (u *StdConn) SupportsMultipleReaders() bool {
  285. return false
  286. }
  287. // Rebind clears the interface the kernel scoped this socket to, so that sends are routed against the current
  288. // routing table instead of the interface we happened to be on when the socket was created. Darwin pins sockets
  289. // this way on its own, which is what strands us after the underlying network changes.
  290. func (u *StdConn) Rebind() error {
  291. var err error
  292. if u.isV4 {
  293. err = syscall.SetsockoptInt(int(u.sysFd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, 0)
  294. } else {
  295. err = syscall.SetsockoptInt(int(u.sysFd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, 0)
  296. }
  297. return err
  298. }