udp_darwin.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. "sync"
  12. "sync/atomic"
  13. "syscall"
  14. "unsafe"
  15. "github.com/slackhq/nebula/config"
  16. "golang.org/x/sys/unix"
  17. )
  18. type StdConn struct {
  19. *net.UDPConn
  20. isV4 bool
  21. sysFd uintptr
  22. l *slog.Logger
  23. // listenHost and port are the configured address and the bound port, which a connected socket shares.
  24. listenHost netip.Addr
  25. port uint16
  26. // rc reaches the listener's descriptor for another listen routine's Rebind.
  27. rc syscall.RawConn
  28. socks *connSocks
  29. // reader is ListenOut's reader, which connected socket readers share; readMu serializes them, since an
  30. // EncReader isn't safe for concurrent use.
  31. reader atomic.Pointer[readerPair]
  32. readMu sync.Mutex
  33. }
  34. type readerPair struct {
  35. r EncReader
  36. flush func()
  37. }
  38. var _ Conn = &StdConn{}
  39. var _ EstablishedPeerConn = &StdConn{}
  40. func NewListener(l *slog.Logger, s Settings) (Conn, error) {
  41. lc := NewListenConfig(s.Multi)
  42. pc, err := lc.ListenPacket(context.TODO(), "udp", s.Listen.String())
  43. if err != nil {
  44. return nil, err
  45. }
  46. if uc, ok := pc.(*net.UDPConn); ok {
  47. c := &StdConn{UDPConn: uc, l: l, listenHost: s.Listen.Addr()}
  48. rc, err := uc.SyscallConn()
  49. if err != nil {
  50. return nil, fmt.Errorf("failed to open udp socket: %w", err)
  51. }
  52. c.rc = rc
  53. err = rc.Control(func(fd uintptr) {
  54. c.sysFd = fd
  55. })
  56. if err != nil {
  57. return nil, fmt.Errorf("failed to get udp fd: %w", err)
  58. }
  59. la, err := c.LocalAddr()
  60. if err != nil {
  61. return nil, err
  62. }
  63. c.isV4 = la.Addr().Is4()
  64. c.port = la.Port()
  65. c.socks = joinConnSocks(c, la, s)
  66. return c, nil
  67. }
  68. return nil, fmt.Errorf("unexpected PacketConn: %T %#v", pc, pc)
  69. }
  70. func NewListenConfig(multi bool) net.ListenConfig {
  71. return net.ListenConfig{
  72. Control: func(network, address string, c syscall.RawConn) error {
  73. if multi {
  74. var controlErr error
  75. err := c.Control(func(fd uintptr) {
  76. if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
  77. controlErr = fmt.Errorf("SO_REUSEPORT failed: %v", err)
  78. return
  79. }
  80. })
  81. if err != nil {
  82. return err
  83. }
  84. if controlErr != nil {
  85. return controlErr
  86. }
  87. }
  88. return nil
  89. },
  90. }
  91. }
  92. //go:linkname sendto golang.org/x/sys/unix.sendto
  93. //go:noescape
  94. func sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen int32) (err error)
  95. func (u *StdConn) WriteTo(b []byte, ap netip.AddrPort) error {
  96. if t := u.socks.table.Load(); t != nil {
  97. if p := (*t)[ap]; p != nil {
  98. bufs := [1][]byte{b}
  99. if sent, err := u.connSockRun(p, ap, bufs[:]); sent == 1 || err != nil {
  100. return err
  101. }
  102. }
  103. }
  104. return u.writeTo(b, ap)
  105. }
  106. // writeTo sends b through the listener.
  107. func (u *StdConn) writeTo(b []byte, ap netip.AddrPort) error {
  108. var sa unsafe.Pointer
  109. var addrLen int32
  110. if u.isV4 {
  111. if ap.Addr().Is6() {
  112. return ErrInvalidIPv6RemoteForSocket
  113. }
  114. var rsa unix.RawSockaddrInet4
  115. rsa.Family = unix.AF_INET
  116. rsa.Addr = ap.Addr().As4()
  117. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ap.Port())
  118. sa = unsafe.Pointer(&rsa)
  119. addrLen = syscall.SizeofSockaddrInet4
  120. } else {
  121. var rsa unix.RawSockaddrInet6
  122. rsa.Family = unix.AF_INET6
  123. rsa.Addr = ap.Addr().As16()
  124. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ap.Port())
  125. sa = unsafe.Pointer(&rsa)
  126. addrLen = syscall.SizeofSockaddrInet6
  127. }
  128. // Golang stdlib doesn't handle EAGAIN correctly in some situations so we do writes ourselves
  129. // See https://github.com/golang/go/issues/73919
  130. for {
  131. //_, _, err := unix.Syscall6(unix.SYS_SENDTO, u.sysFd, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0, sa, addrLen)
  132. err := sendto(int(u.sysFd), b, 0, sa, addrLen)
  133. if err == nil {
  134. // Written, get out before the error handling
  135. return nil
  136. }
  137. if errors.Is(err, syscall.EINTR) {
  138. // Write was interrupted, retry
  139. continue
  140. }
  141. if errors.Is(err, syscall.EAGAIN) {
  142. return &net.OpError{Op: "sendto", Err: unix.EWOULDBLOCK}
  143. }
  144. if errors.Is(err, syscall.EBADF) {
  145. return net.ErrClosed
  146. }
  147. return &net.OpError{Op: "sendto", Err: err}
  148. }
  149. }
  150. func (u *StdConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) (int, error) {
  151. // An un-sendable destination costs its own packet, never the ones behind it in the batch.
  152. // TODO: writeTo maps EWOULDBLOCK to an error, so a full listener send buffer
  153. // silently drops those packets (linux blocks instead). Poll for
  154. // writability on EAGAIN before giving up on them.
  155. written := 0
  156. t := u.socks.table.Load()
  157. if t == nil {
  158. for i, b := range bufs {
  159. if err := u.writeTo(b, addrs[i]); err == nil {
  160. written++
  161. } else {
  162. u.l.Debug("failed to write packet in batch", "udpAddr", addrs[i], "error", err)
  163. }
  164. }
  165. return written, nil
  166. }
  167. for off := 0; off < len(bufs); {
  168. // A run of datagrams to one peer goes out together, on its connected socket if it has or earns one.
  169. dst := addrs[off]
  170. end := off + 1
  171. for end < len(bufs) && addrs[end] == dst {
  172. end++
  173. }
  174. if p := (*t)[dst]; p != nil {
  175. for off < end {
  176. sent, err := u.connSockRun(p, dst, bufs[off:end])
  177. written += sent
  178. off += sent
  179. if err == nil {
  180. break
  181. }
  182. u.l.Debug("failed to write packet in batch", "udpAddr", dst, "error", err)
  183. off++
  184. }
  185. }
  186. // The listener sends what no connected socket took.
  187. for ; off < end; off++ {
  188. if err := u.writeTo(bufs[off], dst); err == nil {
  189. written++
  190. } else {
  191. u.l.Debug("failed to write packet in batch", "udpAddr", dst, "error", err)
  192. }
  193. }
  194. }
  195. return written, nil
  196. }
  197. func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
  198. a := u.UDPConn.LocalAddr()
  199. switch v := a.(type) {
  200. case *net.UDPAddr:
  201. addr, ok := netip.AddrFromSlice(v.IP)
  202. if !ok {
  203. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned invalid IP address: %s", v.IP)
  204. }
  205. return netip.AddrPortFrom(addr, uint16(v.Port)), nil
  206. default:
  207. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned: %#v", a)
  208. }
  209. }
  210. func (u *StdConn) ReloadConfig(c *config.C) {
  211. // TODO
  212. }
  213. func NewUDPStatsEmitter(udpConns []Conn) func() {
  214. // No UDP stats for non-linux
  215. return func() {}
  216. }
  217. func (u *StdConn) ListenOut(r EncReader, flush func()) error {
  218. buffer := make([]byte, MTU)
  219. u.reader.Store(&readerPair{r: r, flush: flush})
  220. u.startConnSocks()
  221. for {
  222. // Just read one packet at a time
  223. n, rua, err := u.ReadFromUDPAddrPort(buffer)
  224. if err != nil {
  225. if errors.Is(err, net.ErrClosed) {
  226. return err
  227. }
  228. u.l.Error("unexpected udp socket receive error", "error", err)
  229. continue
  230. }
  231. u.readMu.Lock()
  232. r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), buffer[:n:n])
  233. flush()
  234. u.readMu.Unlock()
  235. }
  236. }
  237. // Close closes the connected sockets of every listen routine, then the listener.
  238. func (u *StdConn) Close() error {
  239. u.stopConnSocks()
  240. return u.UDPConn.Close()
  241. }
  242. func (u *StdConn) SupportsMultipleReaders() bool {
  243. return false
  244. }
  245. // Rebind clears the interface the kernel scoped this socket to, so that sends are routed against the current
  246. // routing table instead of the interface we happened to be on when the socket was created. Darwin pins sockets
  247. // this way on its own, which is what strands us after the underlying network changes. Control.RebindUDPServer
  248. // rebinds only the first writer, so this clears the other listen routines' listeners too. Connected sockets are
  249. // closed after the listeners are cleared, and later ones copy the cleared scope and pick their source addresses
  250. // afresh.
  251. func (u *StdConn) Rebind() error {
  252. var err error
  253. if u.isV4 {
  254. err = syscall.SetsockoptInt(int(u.sysFd), syscall.IPPROTO_IP, syscall.IP_BOUND_IF, 0)
  255. } else {
  256. err = syscall.SetsockoptInt(int(u.sysFd), syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF, 0)
  257. }
  258. for _, m := range u.socks.siblings(u) {
  259. err = errors.Join(err, m.clearBoundIf())
  260. }
  261. u.closeConnSocks("rebind")
  262. return err
  263. }
  264. // clearBoundIf clears the interface the listener is scoped to through its RawConn, which fails on a listener that
  265. // closed concurrently instead of reaching whatever reused its descriptor number.
  266. func (u *StdConn) clearBoundIf() error {
  267. level, opt := syscall.IPPROTO_IPV6, syscall.IPV6_BOUND_IF
  268. if u.isV4 {
  269. level, opt = syscall.IPPROTO_IP, syscall.IP_BOUND_IF
  270. }
  271. var serr error
  272. if err := u.rc.Control(func(fd uintptr) { serr = syscall.SetsockoptInt(int(fd), level, opt, 0) }); err != nil {
  273. return err
  274. }
  275. return serr
  276. }