udp_linux.go 29 KB

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