udp_linux.go 29 KB

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