udp_linux.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. //go:build !android && !e2e_testing
  2. // +build !android,!e2e_testing
  3. package udp
  4. import (
  5. "context"
  6. "encoding/binary"
  7. "fmt"
  8. "log/slog"
  9. "net"
  10. "net/netip"
  11. "syscall"
  12. "unsafe"
  13. "github.com/rcrowley/go-metrics"
  14. "github.com/slackhq/nebula/config"
  15. "golang.org/x/sys/unix"
  16. )
  17. type StdConn struct {
  18. udpConn *net.UDPConn
  19. rawConn syscall.RawConn
  20. isV4 bool
  21. l *slog.Logger
  22. batch int
  23. // sendmmsg scratch. Each queue has its own StdConn, so no locking is
  24. // needed. Sized to MaxWriteBatch at construction; WriteBatch chunks
  25. // larger inputs.
  26. writeMsgs []rawMessage
  27. writeIovs []iovec
  28. writeNames [][]byte
  29. // Per-entry cmsg scratch. writeCmsg is one contiguous slab of
  30. // MaxWriteBatch * writeCmsgSpace bytes; each entry holds two cmsg
  31. // headers (UDP_SEGMENT then IP_TOS / IPV6_TCLASS) pre-filled once in
  32. // prepareWriteMessages. WriteBatch only rewrites the per-call data
  33. // payloads and toggles Hdr.Control / Hdr.Controllen to point at
  34. // whichever subset of the two cmsgs applies.
  35. writeCmsg []byte
  36. writeCmsgSpace int
  37. writeCmsgSegSpace int
  38. writeCmsgEcnSpace int
  39. // writeEntryEnd[e] is the bufs index *after* the last packet packed
  40. // into mmsghdr entry e. Used to rewind `i` on partial sendmmsg success.
  41. writeEntryEnd []int
  42. // rawSend wraps the sendmmsg(2) callback in a closure-free helper so
  43. // the hot path doesn't heap-allocate a fresh closure per call.
  44. rawSend rawSendmmsg
  45. // UDP GSO (sendmsg with UDP_SEGMENT cmsg) support. gsoSupported is
  46. // probed once at socket creation. When true, WriteBatch packs same-
  47. // destination consecutive packets into a single sendmmsg entry with a
  48. // UDP_SEGMENT cmsg; otherwise each packet is its own entry.
  49. gsoSupported bool
  50. // UDP GRO (recvmsg with UDP_GRO cmsg) support. groSupported is probed
  51. // once at socket creation. When true, listenOutBatch allocates larger
  52. // RX buffers and a per-entry cmsg slot so the kernel can coalesce
  53. // consecutive same-flow datagrams into a single recvmmsg entry; the
  54. // delivered cmsg carries the gso_size used to split them back apart.
  55. groSupported bool
  56. // ecnRecvSupported is true when IP_RECVTOS / IPV6_RECVTCLASS was
  57. // successfully enabled — the kernel will deliver the outer IP-ECN of
  58. // each arriving datagram as a per-slot cmsg, and listenOutBatch passes
  59. // the parsed value to the EncReader callback for RFC 6040 combine.
  60. ecnRecvSupported bool
  61. // rxOrder is the per-batch scratch listenOutBatch uses to gather every
  62. // segment in a recvmmsg call (after splitting GRO superpackets) and
  63. // stable-sort by (source, message-counter) before delivery. Reordering
  64. // fits within the receiver's replay window so briefly out-of-order
  65. // arrivals do not get rejected as replays.
  66. rxOrder *rxReorderBuffer
  67. }
  68. func setReusePort(network, address string, c syscall.RawConn) error {
  69. var opErr error
  70. err := c.Control(func(fd uintptr) {
  71. opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
  72. //CloseOnExec already set by the runtime
  73. })
  74. if err != nil {
  75. return err
  76. }
  77. return opErr
  78. }
  79. func NewListener(l *slog.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
  80. listen := netip.AddrPortFrom(ip, uint16(port))
  81. lc := net.ListenConfig{}
  82. if multi {
  83. lc.Control = setReusePort
  84. }
  85. //this context is only used during the bind operation, you can't cancel it to kill the socket
  86. pc, err := lc.ListenPacket(context.Background(), "udp", listen.String())
  87. if err != nil {
  88. return nil, fmt.Errorf("unable to open socket: %s", err)
  89. }
  90. udpConn := pc.(*net.UDPConn)
  91. rawConn, err := udpConn.SyscallConn()
  92. if err != nil {
  93. _ = udpConn.Close()
  94. return nil, err
  95. }
  96. //gotta find out if we got an AF_INET6 socket or not:
  97. out := &StdConn{
  98. udpConn: udpConn,
  99. rawConn: rawConn,
  100. l: l,
  101. batch: batch,
  102. }
  103. af, err := out.getSockOptInt(unix.SO_DOMAIN)
  104. if err != nil {
  105. _ = out.Close()
  106. return nil, err
  107. }
  108. out.isV4 = af == unix.AF_INET
  109. out.prepareWriteMessages(MaxWriteBatch)
  110. out.rawSend.msgs = out.writeMsgs
  111. out.rawSend.bind()
  112. out.prepareGSO()
  113. // GRO delivers coalesced superpackets that need a cmsg to split back
  114. // into segments. The single-packet RX path uses ReadFromUDPAddrPort
  115. // and cannot see that cmsg, so only enable GRO for the batch path.
  116. if batch > 1 {
  117. out.prepareGRO()
  118. }
  119. // Best-effort: ask the kernel to deliver outer IP-ECN as ancillary data
  120. // on every recvmmsg slot so the decap side can apply RFC 6040 combine.
  121. // On older kernels these may not exist; failing here just means we get
  122. // 0 (Not-ECT) on every slot, which is the same as ecn_mode=disable.
  123. out.prepareECNRecv()
  124. return out, nil
  125. }
  126. // prepareWriteMessages allocates one mmsghdr/iovec/sockaddr/cmsg scratch
  127. // slot per sendmmsg entry. The iovec slab is sized to n so all entries'
  128. // iovecs share one allocation; per-entry fan-out is further capped at
  129. // maxGSOSegments. Hdr.Iov / Hdr.Iovlen / Hdr.Control / Hdr.Controllen are
  130. // wired per call since each entry can span a variable number of iovecs
  131. // and may or may not carry a cmsg.
  132. //
  133. // Per-mmsghdr cmsg layout. Each entry's slot of length writeCmsgSpace holds
  134. // up to two cmsg headers placed at fixed offsets:
  135. //
  136. // [0 .. writeCmsgSegSpace) UDP_SEGMENT (gso_size, uint16)
  137. // [writeCmsgSegSpace .. writeCmsgSpace) IP_TOS or IPV6_TCLASS (int32)
  138. //
  139. // Both headers are pre-filled once here; per-call we only rewrite the data
  140. // payload and toggle Hdr.Control / Hdr.Controllen to point at whichever
  141. // subset applies (none / segment-only / ecn-only / both).
  142. func (u *StdConn) prepareWriteMessages(n int) {
  143. u.writeMsgs = make([]rawMessage, n)
  144. u.writeIovs = make([]iovec, n)
  145. u.writeNames = make([][]byte, n)
  146. u.writeEntryEnd = make([]int, n)
  147. u.writeCmsgSegSpace = unix.CmsgSpace(2)
  148. u.writeCmsgEcnSpace = unix.CmsgSpace(4)
  149. u.writeCmsgSpace = u.writeCmsgSegSpace + u.writeCmsgEcnSpace
  150. u.writeCmsg = make([]byte, n*u.writeCmsgSpace)
  151. ecnLevel := int32(unix.IPPROTO_IP)
  152. ecnType := int32(unix.IP_TOS)
  153. if !u.isV4 {
  154. ecnLevel = unix.IPPROTO_IPV6
  155. ecnType = unix.IPV6_TCLASS
  156. }
  157. for k := 0; k < n; k++ {
  158. base := k * u.writeCmsgSpace
  159. seg := (*unix.Cmsghdr)(unsafe.Pointer(&u.writeCmsg[base]))
  160. seg.Level = unix.SOL_UDP
  161. seg.Type = unix.UDP_SEGMENT
  162. setCmsgLen(seg, unix.CmsgLen(2))
  163. ecn := (*unix.Cmsghdr)(unsafe.Pointer(&u.writeCmsg[base+u.writeCmsgSegSpace]))
  164. ecn.Level = ecnLevel
  165. ecn.Type = ecnType
  166. setCmsgLen(ecn, unix.CmsgLen(4))
  167. }
  168. for i := range u.writeMsgs {
  169. u.writeNames[i] = make([]byte, unix.SizeofSockaddrInet6)
  170. u.writeMsgs[i].Hdr.Name = &u.writeNames[i][0]
  171. }
  172. }
  173. // maxGSOSegments caps the per-sendmsg GSO fan-out. Linux kernels have
  174. // historically capped UDP_MAX_SEGMENTS at 64; newer kernels raise it to 128.
  175. // We stay one below 64 because the kernel's check is
  176. //
  177. // if (cork->length > cork->gso_size * UDP_MAX_SEGMENTS) return -EINVAL;
  178. //
  179. // and cork->length includes the 8-byte UDP header (udp_sendmsg passes
  180. // ulen = len + sizeof(udphdr) to ip_append_data). Packing exactly 64
  181. // same-size segments puts cork->length at gso_size*64 + 8, which is one
  182. // UDP-header over the bound and the kernel rejects the whole sendmmsg
  183. // with EINVAL. 63 leaves room for the header for any segSize >= 8.
  184. const maxGSOSegments = 63
  185. // maxGSOBytes bounds the total payload per sendmsg() when UDP_SEGMENT is
  186. // set. The kernel stitches all iovecs into a single skb whose length the
  187. // UDP length field can represent, and also enforces sk_gso_max_size (which
  188. // on most devices is 65536). We use 65000 to leave headroom under the
  189. // 65535 UDP-length cap, avoiding EMSGSIZE on large TSO superpackets.
  190. const maxGSOBytes = 65000
  191. // prepareGSO probes UDP_SEGMENT support and sets u.gsoSupported on success.
  192. // Best-effort; failure leaves it false.
  193. func (u *StdConn) prepareGSO() {
  194. var probeErr error
  195. if err := u.rawConn.Control(func(fd uintptr) {
  196. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT, 0)
  197. }); err != nil {
  198. u.l.Info("udp: GSO disabled", "reason", "rawconn control failed", "error", err)
  199. recordCapability("udp.gso.enabled", false)
  200. return
  201. }
  202. if probeErr != nil {
  203. u.l.Info("udp: GSO disabled", "reason", "kernel rejected probe", "error", probeErr)
  204. recordCapability("udp.gso.enabled", false)
  205. return
  206. }
  207. u.gsoSupported = true
  208. u.l.Info("udp: GSO enabled")
  209. recordCapability("udp.gso.enabled", true)
  210. }
  211. // udpGROBufferSize sizes the per-entry recvmmsg buffer when UDP_GRO is on.
  212. // The kernel stitches a run of same-flow datagrams into a single skb whose
  213. // length is bounded by sk_gso_max_size (typically 65535); anything larger
  214. // would be MSG_TRUNCed. We use the maximum representable UDP length so a
  215. // full superpacket always lands intact.
  216. const udpGROBufferSize = 65535
  217. // udpGROCmsgPayload is the size of the UDP_GRO cmsg data delivered by the
  218. // kernel: a single int (gso_size in bytes). See udp_cmsg_recv() in
  219. // net/ipv4/udp.c.
  220. const udpGROCmsgPayload = 4
  221. // prepareGRO turns on UDP_GRO so the kernel coalesces consecutive same-flow
  222. // datagrams into one recvmmsg entry, with a cmsg carrying the gso_size used
  223. // to split them back apart on the application side.
  224. func (u *StdConn) prepareGRO() {
  225. var probeErr error
  226. if err := u.rawConn.Control(func(fd uintptr) {
  227. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
  228. }); err != nil {
  229. u.l.Info("udp: GRO disabled", "reason", "rawconn control failed", "error", err)
  230. recordCapability("udp.gro.enabled", false)
  231. return
  232. }
  233. if probeErr != nil {
  234. u.l.Info("udp: GRO disabled", "reason", "kernel rejected probe", "error", probeErr)
  235. recordCapability("udp.gro.enabled", false)
  236. return
  237. }
  238. u.groSupported = true
  239. u.l.Info("udp: GRO enabled")
  240. recordCapability("udp.gro.enabled", true)
  241. }
  242. // prepareECNRecv turns on IP_RECVTOS / IPV6_RECVTCLASS so the outer IP-ECN
  243. // field of each arriving datagram is delivered as ancillary data alongside
  244. // the payload. listenOutBatch reads it via parseRecvCmsg and passes the
  245. // codepoint through the EncReader for RFC 6040 combine on the decap side.
  246. // Best-effort: we keep going on failure.
  247. func (u *StdConn) prepareECNRecv() {
  248. var probeErr error
  249. if err := u.rawConn.Control(func(fd uintptr) {
  250. if u.isV4 {
  251. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
  252. } else {
  253. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
  254. }
  255. }); err != nil {
  256. u.l.Info("udp: outer-ECN RX disabled", "reason", "rawconn control failed", "error", err)
  257. recordCapability("udp.ecn_rx.enabled", false)
  258. return
  259. }
  260. if probeErr != nil {
  261. u.l.Info("udp: outer-ECN RX disabled", "reason", "kernel rejected probe", "error", probeErr)
  262. recordCapability("udp.ecn_rx.enabled", false)
  263. return
  264. }
  265. u.ecnRecvSupported = true
  266. u.l.Info("udp: outer-ECN RX enabled")
  267. recordCapability("udp.ecn_rx.enabled", true)
  268. }
  269. // recordCapability registers (or updates) a boolean gauge for one of the
  270. // kernel-feature probes. Gauges go to 1 when the feature is enabled, 0 when
  271. // it is not — dashboards can show degraded state on partially-supported
  272. // kernels at a glance. Calling repeatedly with the same name updates the
  273. // existing gauge rather than registering a duplicate.
  274. func recordCapability(name string, enabled bool) {
  275. g := metrics.GetOrRegisterGauge(name, nil)
  276. if enabled {
  277. g.Update(1)
  278. } else {
  279. g.Update(0)
  280. }
  281. }
  282. func (u *StdConn) SupportsMultipleReaders() bool {
  283. return true
  284. }
  285. func (u *StdConn) Rebind() error {
  286. return nil
  287. }
  288. func (u *StdConn) getSockOptInt(opt int) (int, error) {
  289. if u.rawConn == nil {
  290. return 0, fmt.Errorf("no UDP connection")
  291. }
  292. var out int
  293. var opErr error
  294. err := u.rawConn.Control(func(fd uintptr) {
  295. out, opErr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, opt)
  296. })
  297. if err != nil {
  298. return 0, err
  299. }
  300. return out, opErr
  301. }
  302. func (u *StdConn) setSockOptInt(opt int, n int) error {
  303. if u.rawConn == nil {
  304. return fmt.Errorf("no UDP connection")
  305. }
  306. var opErr error
  307. err := u.rawConn.Control(func(fd uintptr) {
  308. opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, opt, n)
  309. })
  310. if err != nil {
  311. return err
  312. }
  313. return opErr
  314. }
  315. func (u *StdConn) SetRecvBuffer(n int) error {
  316. return u.setSockOptInt(unix.SO_RCVBUFFORCE, n)
  317. }
  318. func (u *StdConn) SetSendBuffer(n int) error {
  319. return u.setSockOptInt(unix.SO_SNDBUFFORCE, n)
  320. }
  321. func (u *StdConn) SetSoMark(mark int) error {
  322. return u.setSockOptInt(unix.SO_MARK, mark)
  323. }
  324. func (u *StdConn) GetRecvBuffer() (int, error) {
  325. return u.getSockOptInt(unix.SO_RCVBUF)
  326. }
  327. func (u *StdConn) GetSendBuffer() (int, error) {
  328. return u.getSockOptInt(unix.SO_SNDBUF)
  329. }
  330. func (u *StdConn) GetSoMark() (int, error) {
  331. return u.getSockOptInt(unix.SO_MARK)
  332. }
  333. func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
  334. a := u.udpConn.LocalAddr()
  335. switch v := a.(type) {
  336. case *net.UDPAddr:
  337. addr, ok := netip.AddrFromSlice(v.IP)
  338. if !ok {
  339. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned invalid IP address: %s", v.IP)
  340. }
  341. return netip.AddrPortFrom(addr, uint16(v.Port)), nil
  342. default:
  343. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned: %#v", a)
  344. }
  345. }
  346. func recvmmsg(fd uintptr, msgs []rawMessage) (int, bool, error) {
  347. var errno syscall.Errno
  348. n, _, errno := unix.Syscall6(
  349. unix.SYS_RECVMMSG,
  350. fd,
  351. uintptr(unsafe.Pointer(&msgs[0])),
  352. uintptr(len(msgs)),
  353. unix.MSG_WAITFORONE,
  354. 0,
  355. 0,
  356. )
  357. if errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK {
  358. // No data available, block for I/O and try again.
  359. return int(n), false, nil
  360. }
  361. if errno != 0 {
  362. return int(n), true, &net.OpError{Op: "recvmmsg", Err: errno}
  363. }
  364. return int(n), true, nil
  365. }
  366. func (u *StdConn) listenOutSingle(r EncReader, flush func()) error {
  367. var err error
  368. var n int
  369. var from netip.AddrPort
  370. buffer := make([]byte, MTU)
  371. for {
  372. n, from, err = u.udpConn.ReadFromUDPAddrPort(buffer)
  373. if err != nil {
  374. return err
  375. }
  376. from = netip.AddrPortFrom(from.Addr().Unmap(), from.Port())
  377. // listenOutSingle uses ReadFromUDPAddrPort which discards cmsgs,
  378. // so the outer ECN field is not visible on this path. Zero RxMeta
  379. // (Not-ECT) means RFC 6040 combine is a no-op.
  380. r(from, buffer[:n], RxMeta{})
  381. flush()
  382. }
  383. }
  384. func (u *StdConn) listenOutBatch(r EncReader, flush func()) error {
  385. var ip netip.Addr
  386. var n int
  387. var operr error
  388. bufSize := MTU
  389. cmsgSpace := 0
  390. if u.groSupported {
  391. bufSize = udpGROBufferSize
  392. cmsgSpace = unix.CmsgSpace(udpGROCmsgPayload)
  393. }
  394. if u.ecnRecvSupported {
  395. // IP_TOS arrives as 1 byte; IPV6_TCLASS arrives as a 4-byte int.
  396. // Reserve enough for the wider of the two so the same buffer fits
  397. // either family alongside any UDP_GRO cmsg.
  398. cmsgSpace += unix.CmsgSpace(4)
  399. }
  400. msgs, buffers, names, _ := u.PrepareRawMessages(u.batch, bufSize, cmsgSpace)
  401. if u.rxOrder == nil {
  402. u.rxOrder = newRxReorderBuffer(u.batch * 64)
  403. }
  404. //reader needs to capture variables from this function, since it's used as a lambda with rawConn.Read
  405. //defining it outside the loop so it gets re-used
  406. reader := func(fd uintptr) (done bool) {
  407. n, done, operr = recvmmsg(fd, msgs)
  408. return done
  409. }
  410. for {
  411. if cmsgSpace > 0 {
  412. for i := range msgs {
  413. setMsgControllen(&msgs[i].Hdr, cmsgSpace)
  414. }
  415. }
  416. err := u.rawConn.Read(reader)
  417. if err != nil {
  418. return err
  419. }
  420. if operr != nil {
  421. return operr
  422. }
  423. // Phase 1: gather every segment from this recvmmsg into rxOrder,
  424. // splitting GRO superpackets into their constituent segments.
  425. u.rxOrder.reset()
  426. for i := 0; i < n; i++ {
  427. // Its ok to skip the ok check here, the slicing is the only error that can occur and it will panic
  428. if u.isV4 {
  429. ip, _ = netip.AddrFromSlice(names[i][4:8])
  430. } else {
  431. ip, _ = netip.AddrFromSlice(names[i][8:24])
  432. }
  433. from := netip.AddrPortFrom(ip.Unmap(), binary.BigEndian.Uint16(names[i][2:4]))
  434. payload := buffers[i][:msgs[i].Len]
  435. segSize := 0
  436. outerECN := byte(0)
  437. if cmsgSpace > 0 {
  438. segSize, outerECN = parseRecvCmsg(&msgs[i].Hdr, u.groSupported, u.ecnRecvSupported, u.isV4)
  439. }
  440. u.rxOrder.addEntry(from, payload, segSize, outerECN)
  441. }
  442. // Phase 2 + 3: stable-sort by (src, port, counter), then deliver in
  443. // order. Reorder distance is bounded by len(u.rxOrder.buf), which
  444. // stays well within the receiver's ReplayWindow (currently 8192) so
  445. // older arrivals are not rejected as replays.
  446. u.rxOrder.sortStable()
  447. u.rxOrder.deliver(r)
  448. // End-of-batch: let callers (e.g. TUN write coalescer) flush any
  449. // state they accumulated across this batch.
  450. flush()
  451. }
  452. }
  453. // headerCounter returns the big-endian uint64 message counter at bytes
  454. // [8:16] of a nebula packet, or 0 if the buffer is too short.
  455. func headerCounter(buf []byte) uint64 {
  456. if len(buf) < 16 {
  457. return 0
  458. }
  459. return binary.BigEndian.Uint64(buf[8:16])
  460. }
  461. // parseRecvCmsg walks the per-slot ancillary buffer once and extracts up to
  462. // two values of interest in a single pass: the UDP_GRO gso_size (when
  463. // wantGRO is true) and the outer IP-level ECN codepoint stamped on the
  464. // carrier (when wantECN is true). Returns zeros for whichever field is not
  465. // requested or not present. isV4 selects between IP_TOS (1-byte) and
  466. // IPV6_TCLASS (4-byte int) cmsg payloads.
  467. func parseRecvCmsg(hdr *msghdr, wantGRO, wantECN bool, isV4 bool) (gso int, ecn byte) {
  468. controllen := int(hdr.Controllen)
  469. if controllen < unix.SizeofCmsghdr || hdr.Control == nil {
  470. return 0, 0
  471. }
  472. ctrl := unsafe.Slice(hdr.Control, controllen)
  473. off := 0
  474. for off+unix.SizeofCmsghdr <= len(ctrl) {
  475. ch := (*unix.Cmsghdr)(unsafe.Pointer(&ctrl[off]))
  476. clen := int(ch.Len)
  477. if clen < unix.SizeofCmsghdr || off+clen > len(ctrl) {
  478. return gso, ecn
  479. }
  480. dataOff := off + unix.CmsgLen(0)
  481. switch {
  482. case wantGRO && ch.Level == unix.SOL_UDP && ch.Type == unix.UDP_GRO:
  483. if dataOff+udpGROCmsgPayload <= len(ctrl) {
  484. gso = int(int32(binary.NativeEndian.Uint32(ctrl[dataOff : dataOff+udpGROCmsgPayload])))
  485. }
  486. case wantECN && isV4 && ch.Level == unix.IPPROTO_IP && ch.Type == unix.IP_TOS:
  487. // IP_TOS arrives as a single byte; only the low 2 bits are ECN.
  488. if dataOff+1 <= len(ctrl) {
  489. ecn = ctrl[dataOff] & 0x03
  490. }
  491. case wantECN && !isV4 && ch.Level == unix.IPPROTO_IPV6 && ch.Type == unix.IPV6_TCLASS:
  492. // IPV6_TCLASS arrives as a 4-byte int; ECN is the low 2 bits.
  493. if dataOff+4 <= len(ctrl) {
  494. ecn = byte(binary.NativeEndian.Uint32(ctrl[dataOff:dataOff+4])) & 0x03
  495. }
  496. }
  497. // Advance by the aligned cmsg space.
  498. off += unix.CmsgSpace(clen - unix.CmsgLen(0))
  499. }
  500. return gso, ecn
  501. }
  502. func (u *StdConn) ListenOut(r EncReader, flush func()) error {
  503. if u.batch == 1 {
  504. return u.listenOutSingle(r, flush)
  505. } else {
  506. return u.listenOutBatch(r, flush)
  507. }
  508. }
  509. func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
  510. _, err := u.udpConn.WriteToUDPAddrPort(b, ip)
  511. return err
  512. }
  513. // WriteBatch sends bufs via sendmmsg(2) using the preallocated scratch on
  514. // StdConn. Consecutive packets to the same destination with matching segment
  515. // sizes (all but possibly the last) are coalesced into a single mmsghdr entry
  516. // carrying a UDP_SEGMENT cmsg, so one syscall can mix runs of GSO superpackets
  517. // with plain one-off datagrams. Without GSO support every packet is its own
  518. // entry, matching the prior behaviour.
  519. //
  520. // Chunks larger than the scratch are processed across multiple syscalls. If
  521. // sendmmsg returns an error AND zero entries went out we fall back to
  522. // per-packet WriteTo for that chunk so the caller still gets best-effort
  523. // delivery; on a partial-success error we just replay the remainder.
  524. func (u *StdConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort, ecns []byte) error {
  525. if len(bufs) != len(addrs) {
  526. return fmt.Errorf("WriteBatch: len(bufs)=%d != len(addrs)=%d", len(bufs), len(addrs))
  527. }
  528. if ecns != nil && len(ecns) != len(bufs) {
  529. return fmt.Errorf("WriteBatch: len(ecns)=%d != len(bufs)=%d", len(ecns), len(bufs))
  530. }
  531. // Callers deliver same-destination packets contiguously and in counter
  532. // order, so we run the GSO planner directly without a pre-sort. A
  533. // sorting pass measurably hurt throughput in microbenchmarks while
  534. // providing no observed reordering benefit.
  535. i := 0
  536. for i < len(bufs) {
  537. baseI := i
  538. entry := 0
  539. iovIdx := 0
  540. for entry < len(u.writeMsgs) && i < len(bufs) {
  541. iovBudget := len(u.writeIovs) - iovIdx
  542. if iovBudget < 1 {
  543. break
  544. }
  545. runLen, segSize := u.planRun(bufs, addrs, ecns, i, iovBudget)
  546. if runLen == 0 {
  547. break
  548. }
  549. for k := 0; k < runLen; k++ {
  550. b := bufs[i+k]
  551. if len(b) == 0 {
  552. u.writeIovs[iovIdx+k].Base = nil
  553. setIovLen(&u.writeIovs[iovIdx+k], 0)
  554. } else {
  555. u.writeIovs[iovIdx+k].Base = &b[0]
  556. setIovLen(&u.writeIovs[iovIdx+k], len(b))
  557. }
  558. }
  559. nlen, err := writeSockaddr(u.writeNames[entry], addrs[i], u.isV4)
  560. if err != nil {
  561. return err
  562. }
  563. hdr := &u.writeMsgs[entry].Hdr
  564. hdr.Iov = &u.writeIovs[iovIdx]
  565. setMsgIovlen(hdr, runLen)
  566. hdr.Namelen = uint32(nlen)
  567. var ecn byte
  568. if ecns != nil {
  569. ecn = ecns[i]
  570. }
  571. u.writeEntryCmsg(entry, runLen, segSize, ecn)
  572. i += runLen
  573. iovIdx += runLen
  574. u.writeEntryEnd[entry] = i
  575. entry++
  576. }
  577. if entry == 0 {
  578. return fmt.Errorf("sendmmsg: no progress")
  579. }
  580. sent, serr := u.sendmmsg(entry)
  581. if serr != nil && sent <= 0 {
  582. // Nothing went out for this chunk; fall back to WriteTo for each
  583. // packet that was queued this iteration. We only enter this path
  584. // when sendmmsg returned an error AND zero entries succeeded —
  585. // otherwise the partial-success advance below replays only the
  586. // remainder, avoiding duplicates of already-sent packets.
  587. //
  588. // sent=-1 from sendmmsg means message 0 itself failed (partial
  589. // success returns the count instead), so log entry 0's parameters
  590. // — that's the entry the kernel rejected.
  591. hdr0 := &u.writeMsgs[0].Hdr
  592. runLen0 := u.writeEntryEnd[0] - baseI
  593. seg0 := len(bufs[baseI])
  594. ecn0 := byte(0)
  595. if ecns != nil {
  596. ecn0 = ecns[baseI]
  597. }
  598. u.l.Warn("sendmmsg had problem",
  599. "sent", sent, "err", serr,
  600. "entries", entry,
  601. "entry0_runLen", runLen0,
  602. "entry0_segSize", seg0,
  603. "entry0_iovlen", hdr0.Iovlen,
  604. "entry0_controllen", hdr0.Controllen,
  605. "entry0_namelen", hdr0.Namelen,
  606. "entry0_ecn", ecn0,
  607. "entry0_dst", addrs[baseI],
  608. "isV4", u.isV4,
  609. "gso", u.gsoSupported,
  610. "gro", u.groSupported,
  611. )
  612. for k := baseI; k < i; k++ {
  613. if werr := u.WriteTo(bufs[k], addrs[k]); werr != nil {
  614. return werr
  615. }
  616. }
  617. continue
  618. }
  619. if sent == 0 {
  620. return fmt.Errorf("sendmmsg made no progress")
  621. }
  622. // Rewind i to the end of the last successfully sent entry. For a
  623. // full-success send this leaves i unchanged; for a partial send it
  624. // replays the remainder on the next outer-loop iteration.
  625. i = u.writeEntryEnd[sent-1]
  626. }
  627. return nil
  628. }
  629. // planRun groups consecutive packets starting at `start` that can be sent as
  630. // a single UDP GSO superpacket (one sendmmsg entry with UDP_SEGMENT cmsg).
  631. // A run of length 1 means the entry carries no UDP_SEGMENT cmsg and the
  632. // kernel treats it as a plain datagram. Returns the run length and the
  633. // per-segment size (which equals len(bufs[start])). Without GSO support
  634. // every call returns runLen=1. Outer ECN (when ecns != nil) is also a run
  635. // boundary — the kernel stamps one outer codepoint per sendmsg entry, so
  636. // mixing values inside a run would lose information.
  637. func (u *StdConn) planRun(bufs [][]byte, addrs []netip.AddrPort, ecns []byte, start, iovBudget int) (int, int) {
  638. if start >= len(bufs) || iovBudget < 1 {
  639. return 0, 0
  640. }
  641. segSize := len(bufs[start])
  642. if !u.gsoSupported || segSize == 0 || segSize > maxGSOBytes {
  643. return 1, segSize
  644. }
  645. dst := addrs[start]
  646. var ecn byte
  647. if ecns != nil {
  648. ecn = ecns[start]
  649. }
  650. maxLen := maxGSOSegments
  651. if iovBudget < maxLen {
  652. maxLen = iovBudget
  653. }
  654. runLen := 1
  655. total := segSize
  656. for runLen < maxLen && start+runLen < len(bufs) {
  657. nextLen := len(bufs[start+runLen])
  658. if nextLen == 0 || nextLen > segSize {
  659. break
  660. }
  661. if addrs[start+runLen] != dst {
  662. break
  663. }
  664. if ecns != nil && ecns[start+runLen] != ecn {
  665. break
  666. }
  667. if total+nextLen > maxGSOBytes {
  668. break
  669. }
  670. total += nextLen
  671. runLen++
  672. if nextLen < segSize {
  673. // A short packet must be the last in the run.
  674. break
  675. }
  676. }
  677. return runLen, segSize
  678. }
  679. // writeEntryCmsg sets up the per-mmsghdr Hdr.Control / Hdr.Controllen for one
  680. // entry. It writes the UDP_SEGMENT payload when runLen >= 2 and the
  681. // IP_TOS/IPV6_TCLASS payload when ecn != 0, then points hdr.Control at the
  682. // smallest contiguous span that covers whichever cmsg(s) actually apply.
  683. func (u *StdConn) writeEntryCmsg(entry, runLen, segSize int, ecn byte) {
  684. hdr := &u.writeMsgs[entry].Hdr
  685. useSeg := runLen >= 2
  686. useEcn := ecn != 0
  687. base := entry * u.writeCmsgSpace
  688. if useSeg {
  689. dataOff := base + unix.CmsgLen(0)
  690. binary.NativeEndian.PutUint16(u.writeCmsg[dataOff:dataOff+2], uint16(segSize))
  691. }
  692. if useEcn {
  693. dataOff := base + u.writeCmsgSegSpace + unix.CmsgLen(0)
  694. binary.NativeEndian.PutUint32(u.writeCmsg[dataOff:dataOff+4], uint32(ecn))
  695. }
  696. switch {
  697. case useSeg && useEcn:
  698. hdr.Control = &u.writeCmsg[base]
  699. setMsgControllen(hdr, u.writeCmsgSpace)
  700. case useSeg:
  701. hdr.Control = &u.writeCmsg[base]
  702. setMsgControllen(hdr, u.writeCmsgSegSpace)
  703. case useEcn:
  704. hdr.Control = &u.writeCmsg[base+u.writeCmsgSegSpace]
  705. setMsgControllen(hdr, u.writeCmsgEcnSpace)
  706. default:
  707. hdr.Control = nil
  708. setMsgControllen(hdr, 0)
  709. }
  710. }
  711. // sendmmsg issues sendmmsg(2) over u.rawConn against the first n entries
  712. // of u.writeMsgs. Routes through u.rawSend so the per-call kernel callback
  713. // stays alloc-free.
  714. func (u *StdConn) sendmmsg(n int) (int, error) {
  715. return u.rawSend.send(u.rawConn, n)
  716. }
  717. // writeSockaddr encodes addr into buf (which must be at least
  718. // SizeofSockaddrInet6 bytes). Returns the number of bytes used. If isV4 is
  719. // true and addr is not a v4 (or v4-in-v6) address, returns an error.
  720. func writeSockaddr(buf []byte, addr netip.AddrPort, isV4 bool) (int, error) {
  721. ap := addr.Addr().Unmap()
  722. if isV4 {
  723. if !ap.Is4() {
  724. return 0, ErrInvalidIPv6RemoteForSocket
  725. }
  726. // struct sockaddr_in: { sa_family_t(2), in_port_t(2, BE), in_addr(4), zero(8) }
  727. // sa_family is host endian.
  728. binary.NativeEndian.PutUint16(buf[0:2], unix.AF_INET)
  729. binary.BigEndian.PutUint16(buf[2:4], addr.Port())
  730. ip4 := ap.As4()
  731. copy(buf[4:8], ip4[:])
  732. for j := 8; j < 16; j++ {
  733. buf[j] = 0
  734. }
  735. return unix.SizeofSockaddrInet4, nil
  736. }
  737. // struct sockaddr_in6: { sa_family_t(2), in_port_t(2, BE), flowinfo(4), in6_addr(16), scope_id(4) }
  738. binary.NativeEndian.PutUint16(buf[0:2], unix.AF_INET6)
  739. binary.BigEndian.PutUint16(buf[2:4], addr.Port())
  740. binary.NativeEndian.PutUint32(buf[4:8], 0)
  741. ip6 := addr.Addr().As16()
  742. copy(buf[8:24], ip6[:])
  743. binary.NativeEndian.PutUint32(buf[24:28], 0)
  744. return unix.SizeofSockaddrInet6, nil
  745. }
  746. func (u *StdConn) ReloadConfig(c *config.C) {
  747. b := c.GetInt("listen.read_buffer", 0)
  748. if b > 0 {
  749. err := u.SetRecvBuffer(b)
  750. if err == nil {
  751. s, err := u.GetRecvBuffer()
  752. if err == nil {
  753. u.l.Info("listen.read_buffer was set", "size", s)
  754. } else {
  755. u.l.Warn("Failed to get listen.read_buffer", "error", err)
  756. }
  757. } else {
  758. u.l.Error("Failed to set listen.read_buffer", "error", err)
  759. }
  760. }
  761. b = c.GetInt("listen.write_buffer", 0)
  762. if b > 0 {
  763. err := u.SetSendBuffer(b)
  764. if err == nil {
  765. s, err := u.GetSendBuffer()
  766. if err == nil {
  767. u.l.Info("listen.write_buffer was set", "size", s)
  768. } else {
  769. u.l.Warn("Failed to get listen.write_buffer", "error", err)
  770. }
  771. } else {
  772. u.l.Error("Failed to set listen.write_buffer", "error", err)
  773. }
  774. }
  775. b = c.GetInt("listen.so_mark", 0)
  776. s, err := u.GetSoMark()
  777. if b > 0 || (err == nil && s != 0) {
  778. err := u.SetSoMark(b)
  779. if err == nil {
  780. s, err := u.GetSoMark()
  781. if err == nil {
  782. u.l.Info("listen.so_mark was set", "mark", s)
  783. } else {
  784. u.l.Warn("Failed to get listen.so_mark", "error", err)
  785. }
  786. } else {
  787. u.l.Error("Failed to set listen.so_mark", "error", err)
  788. }
  789. }
  790. }
  791. func (u *StdConn) getMemInfo(meminfo *[unix.SK_MEMINFO_VARS]uint32) error {
  792. var vallen uint32 = 4 * unix.SK_MEMINFO_VARS
  793. if u.rawConn == nil {
  794. return fmt.Errorf("no UDP connection")
  795. }
  796. var opErr error
  797. err := u.rawConn.Control(func(fd uintptr) {
  798. _, _, syserr := unix.Syscall6(unix.SYS_GETSOCKOPT, fd, uintptr(unix.SOL_SOCKET), uintptr(unix.SO_MEMINFO), uintptr(unsafe.Pointer(meminfo)), uintptr(unsafe.Pointer(&vallen)), 0)
  799. if syserr != 0 {
  800. opErr = syserr
  801. }
  802. })
  803. if err != nil {
  804. return err
  805. }
  806. return opErr
  807. }
  808. func (u *StdConn) Close() error {
  809. if u.udpConn != nil {
  810. return u.udpConn.Close()
  811. }
  812. return nil
  813. }
  814. func NewUDPStatsEmitter(udpConns []Conn) func() {
  815. // Check if our kernel supports SO_MEMINFO before registering the gauges
  816. var udpGauges [][unix.SK_MEMINFO_VARS]metrics.Gauge
  817. var meminfo [unix.SK_MEMINFO_VARS]uint32
  818. if err := udpConns[0].(*StdConn).getMemInfo(&meminfo); err == nil {
  819. udpGauges = make([][unix.SK_MEMINFO_VARS]metrics.Gauge, len(udpConns))
  820. for i := range udpConns {
  821. udpGauges[i] = [unix.SK_MEMINFO_VARS]metrics.Gauge{
  822. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rmem_alloc", i), nil),
  823. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rcvbuf", i), nil),
  824. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_alloc", i), nil),
  825. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.sndbuf", i), nil),
  826. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.fwd_alloc", i), nil),
  827. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_queued", i), nil),
  828. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.optmem", i), nil),
  829. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.backlog", i), nil),
  830. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.drops", i), nil),
  831. }
  832. }
  833. }
  834. return func() {
  835. for i, gauges := range udpGauges {
  836. if err := udpConns[i].(*StdConn).getMemInfo(&meminfo); err == nil {
  837. for j := 0; j < unix.SK_MEMINFO_VARS; j++ {
  838. gauges[j].Update(int64(meminfo[j]))
  839. }
  840. }
  841. }
  842. }
  843. }