udp_linux.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  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. // Default the ECN header to the socket's own family. writeEntryCmsg
  151. // finalizes Level/Type per entry from the destination address (a v4-mapped
  152. // dst on a dual-stack v6 socket needs IP_TOS, not IPV6_TCLASS), so this is
  153. // only the value used before the first per-entry rewrite.
  154. ecnLevel := int32(unix.IPPROTO_IP)
  155. ecnType := int32(unix.IP_TOS)
  156. if !u.isV4 {
  157. ecnLevel = unix.IPPROTO_IPV6
  158. ecnType = unix.IPV6_TCLASS
  159. }
  160. for k := 0; k < n; k++ {
  161. base := k * u.writeCmsgSpace
  162. seg := (*unix.Cmsghdr)(unsafe.Pointer(&u.writeCmsg[base]))
  163. seg.Level = unix.SOL_UDP
  164. seg.Type = unix.UDP_SEGMENT
  165. setCmsgLen(seg, unix.CmsgLen(2))
  166. ecn := (*unix.Cmsghdr)(unsafe.Pointer(&u.writeCmsg[base+u.writeCmsgSegSpace]))
  167. ecn.Level = ecnLevel
  168. ecn.Type = ecnType
  169. setCmsgLen(ecn, unix.CmsgLen(4))
  170. }
  171. for i := range u.writeMsgs {
  172. u.writeNames[i] = make([]byte, unix.SizeofSockaddrInet6)
  173. u.writeMsgs[i].Hdr.Name = &u.writeNames[i][0]
  174. }
  175. }
  176. // maxGSOBytes bounds the total payload per sendmsg() when UDP_SEGMENT is
  177. // set. The kernel stitches all iovecs into a single skb whose length the
  178. // UDP length field can represent, and also enforces sk_gso_max_size (which
  179. // on most devices is 65536). We use 65000 to leave headroom under the
  180. // 65535 UDP-length cap, avoiding EMSGSIZE on large TSO superpackets.
  181. const maxGSOBytes = 65000
  182. // prepareGSO probes UDP_SEGMENT support and sets u.gsoSupported on success.
  183. // Best-effort; failure leaves it false.
  184. func (u *StdConn) prepareGSO() {
  185. u.maxGSOSegments = 63 //gotta be one less than the max so we can still attach a header
  186. var probeErr error
  187. if err := u.rawConn.Control(func(fd uintptr) {
  188. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT, 0)
  189. }); err != nil {
  190. u.l.Info("udp: GSO disabled", "reason", "rawconn control failed", "error", err)
  191. recordCapability("udp.gso.enabled", false)
  192. return
  193. }
  194. if probeErr != nil {
  195. u.l.Info("udp: GSO disabled", "reason", "kernel rejected probe", "error", probeErr)
  196. recordCapability("udp.gso.enabled", false)
  197. return
  198. }
  199. var un unix.Utsname
  200. if err := unix.Uname(&un); err != nil {
  201. u.l.Info("udp: GSO disabled", "reason", "kernel uname probe failed", "error", err)
  202. recordCapability("udp.gso.enabled", false)
  203. return
  204. }
  205. u.maxGSOSegments = gsoMaxSegments(string(un.Release[:]))
  206. u.gsoSupported = true
  207. u.l.Info("udp: GSO enabled", "maxGSOSegments", u.maxGSOSegments)
  208. recordCapability("udp.gso.enabled", true)
  209. }
  210. // gsoMaxSegments returns the largest number of UDP_SEGMENT segments a single
  211. // sendmsg may carry on the running kernel, reserving one segment for the
  212. // header. UDP_MAX_SEGMENTS was 64 until Linux v6.9 (commit 1382e3b6a350,
  213. // "udp: change maximum number of UDP segments to 128") raised it to 128;
  214. // nothing about this changed in 5.5. On kernels older than 6.9 packing more
  215. // than 64 segments gets the sendmsg rejected with EINVAL, so cap at 63 there
  216. // and only use 127 from 6.9 on. (Maintainer stance: update your kernel if you
  217. // want to go fast — this is a plain version gate, not a runtime probe.)
  218. func gsoMaxSegments(release string) int {
  219. major, minor := parseRelease(release)
  220. if major > 6 || (major == 6 && minor >= 9) {
  221. return 127
  222. }
  223. return 63
  224. }
  225. // udpGROBufferSize sizes the per-entry recvmmsg buffer when UDP_GRO is on.
  226. // The kernel stitches a run of same-flow datagrams into a single skb whose
  227. // length is bounded by sk_gso_max_size (typically 65535); anything larger
  228. // would be MSG_TRUNCed. We use the maximum representable UDP length so a
  229. // full superpacket always lands intact.
  230. const udpGROBufferSize = 65535
  231. // udpGROCmsgPayload is the size of the UDP_GRO cmsg data delivered by the
  232. // kernel: a single int (gso_size in bytes). See udp_cmsg_recv() in
  233. // net/ipv4/udp.c.
  234. const udpGROCmsgPayload = 4
  235. // prepareGRO turns on UDP_GRO so the kernel coalesces consecutive same-flow
  236. // datagrams into one recvmmsg entry, with a cmsg carrying the gso_size used
  237. // to split them back apart on the application side.
  238. func (u *StdConn) prepareGRO() {
  239. var probeErr error
  240. if err := u.rawConn.Control(func(fd uintptr) {
  241. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_GRO, 1)
  242. }); err != nil {
  243. u.l.Info("udp: GRO disabled", "reason", "rawconn control failed", "error", err)
  244. recordCapability("udp.gro.enabled", false)
  245. return
  246. }
  247. if probeErr != nil {
  248. u.l.Info("udp: GRO disabled", "reason", "kernel rejected probe", "error", probeErr)
  249. recordCapability("udp.gro.enabled", false)
  250. return
  251. }
  252. u.groSupported = true
  253. u.l.Info("udp: GRO enabled")
  254. recordCapability("udp.gro.enabled", true)
  255. }
  256. // prepareECNRecv turns on IP_RECVTOS / IPV6_RECVTCLASS so the outer IP-ECN
  257. // field of each arriving datagram is delivered as ancillary data alongside
  258. // the payload. listenOutBatch reads it via parseRecvCmsg and passes the
  259. // codepoint through the EncReader for RFC 6040 combine on the decap side.
  260. // Best-effort: we keep going on failure.
  261. func (u *StdConn) prepareECNRecv() {
  262. var v4err, v6err error
  263. if err := u.rawConn.Control(func(fd uintptr) {
  264. v4err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1)
  265. if !u.isV4 {
  266. v6err = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1)
  267. }
  268. }); err != nil {
  269. u.l.Info("udp: outer-ECN RX disabled", "reason", "rawconn control failed", "error", err)
  270. recordCapability("udp.ecn_rx.enabled", false)
  271. return
  272. }
  273. if u.isV4 { //only check the V4 attempt
  274. if v4err != nil {
  275. u.l.Info("udp: outer-ECN RX disabled", "reason", "kernel rejected probe", "error", v4err)
  276. recordCapability("udp.ecn_rx.enabled", false)
  277. } else {
  278. u.ecnRecvSupported = true
  279. u.l.Info("udp: outer-ECN RX enabled")
  280. recordCapability("udp.ecn_rx.enabled", true)
  281. }
  282. return
  283. } else {
  284. if v6err != nil { //no V6 ECN? disable it.
  285. u.l.Info("udp: outer-ECN RX disabled", "reason", "kernel rejected probe", "error", errors.Join(v4err, v6err))
  286. recordCapability("udp.ecn_rx.enabled", false)
  287. return
  288. } else if v4err != nil { //no V4, but yes V6? Low level warning. Could be a V6-specific bind.
  289. u.l.Debug("udp: outer-ECN RX degraded", "reason", "kernel rejected probe on IPv4", "error", v4err)
  290. }
  291. // all good
  292. u.ecnRecvSupported = true
  293. u.l.Info("udp: outer-ECN RX enabled")
  294. recordCapability("udp.ecn_rx.enabled", true)
  295. return
  296. }
  297. }
  298. // recordCapability registers (or updates) a boolean gauge for one of the
  299. // kernel-feature probes. Gauges go to 1 when the feature is enabled, 0 when
  300. // it is not — dashboards can show degraded state on partially-supported
  301. // kernels at a glance. Calling repeatedly with the same name updates the
  302. // existing gauge rather than registering a duplicate.
  303. func recordCapability(name string, enabled bool) {
  304. g := metrics.GetOrRegisterGauge(name, nil)
  305. if enabled {
  306. g.Update(1)
  307. } else {
  308. g.Update(0)
  309. }
  310. }
  311. func (u *StdConn) SupportsMultipleReaders() bool {
  312. return true
  313. }
  314. func (u *StdConn) Rebind() error {
  315. return nil
  316. }
  317. func (u *StdConn) getSockOptInt(opt int) (int, error) {
  318. if u.rawConn == nil {
  319. return 0, fmt.Errorf("no UDP connection")
  320. }
  321. var out int
  322. var opErr error
  323. err := u.rawConn.Control(func(fd uintptr) {
  324. out, opErr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, opt)
  325. })
  326. if err != nil {
  327. return 0, err
  328. }
  329. return out, opErr
  330. }
  331. func (u *StdConn) setSockOptInt(opt int, n int) error {
  332. if u.rawConn == nil {
  333. return fmt.Errorf("no UDP connection")
  334. }
  335. var opErr error
  336. err := u.rawConn.Control(func(fd uintptr) {
  337. opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, opt, n)
  338. })
  339. if err != nil {
  340. return err
  341. }
  342. return opErr
  343. }
  344. func (u *StdConn) SetRecvBuffer(n int) error {
  345. return u.setSockOptInt(unix.SO_RCVBUFFORCE, n)
  346. }
  347. func (u *StdConn) SetSendBuffer(n int) error {
  348. return u.setSockOptInt(unix.SO_SNDBUFFORCE, n)
  349. }
  350. func (u *StdConn) SetSoMark(mark int) error {
  351. return u.setSockOptInt(unix.SO_MARK, mark)
  352. }
  353. func (u *StdConn) GetRecvBuffer() (int, error) {
  354. return u.getSockOptInt(unix.SO_RCVBUF)
  355. }
  356. func (u *StdConn) GetSendBuffer() (int, error) {
  357. return u.getSockOptInt(unix.SO_SNDBUF)
  358. }
  359. func (u *StdConn) GetSoMark() (int, error) {
  360. return u.getSockOptInt(unix.SO_MARK)
  361. }
  362. func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
  363. a := u.udpConn.LocalAddr()
  364. switch v := a.(type) {
  365. case *net.UDPAddr:
  366. addr, ok := netip.AddrFromSlice(v.IP)
  367. if !ok {
  368. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned invalid IP address: %s", v.IP)
  369. }
  370. return netip.AddrPortFrom(addr, uint16(v.Port)), nil
  371. default:
  372. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned: %#v", a)
  373. }
  374. }
  375. func recvmmsg(fd uintptr, msgs []rawMessage) (int, bool, error) {
  376. var errno syscall.Errno
  377. n, _, errno := unix.Syscall6(
  378. unix.SYS_RECVMMSG,
  379. fd,
  380. uintptr(unsafe.Pointer(&msgs[0])),
  381. uintptr(len(msgs)),
  382. unix.MSG_WAITFORONE,
  383. 0,
  384. 0,
  385. )
  386. if errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK {
  387. // No data available, block for I/O and try again.
  388. return int(n), false, nil
  389. }
  390. if errno != 0 {
  391. return int(n), true, &net.OpError{Op: "recvmmsg", Err: errno}
  392. }
  393. return int(n), true, nil
  394. }
  395. func (u *StdConn) listenOutSingle(r EncReader, flush func()) error {
  396. var err error
  397. var n int
  398. var from netip.AddrPort
  399. buffer := make([]byte, MTU)
  400. for {
  401. n, from, err = u.udpConn.ReadFromUDPAddrPort(buffer)
  402. if err != nil {
  403. return err
  404. }
  405. from = netip.AddrPortFrom(from.Addr().Unmap(), from.Port())
  406. // listenOutSingle uses ReadFromUDPAddrPort which discards cmsgs,
  407. // so the outer ECN field is not visible on this path. Zero RxMeta
  408. // (Not-ECT) means RFC 6040 combine is a no-op.
  409. r(from, buffer[:n], RxMeta{})
  410. flush()
  411. }
  412. }
  413. func getFrom(names [][]byte, i int, isV4 bool) netip.AddrPort {
  414. var ip netip.Addr
  415. // Its ok to skip the ok check here, the slicing is the only error that can occur and it will panic
  416. if isV4 {
  417. ip, _ = netip.AddrFromSlice(names[i][4:8])
  418. } else {
  419. ip, _ = netip.AddrFromSlice(names[i][8:24])
  420. }
  421. return netip.AddrPortFrom(ip.Unmap(), binary.BigEndian.Uint16(names[i][2:4]))
  422. }
  423. func (u *StdConn) listenOutBatch(r EncReader, flush func()) error {
  424. var n int
  425. var operr error
  426. bufSize := MTU
  427. cmsgSpace := 0
  428. if u.groSupported {
  429. bufSize = udpGROBufferSize
  430. cmsgSpace = unix.CmsgSpace(udpGROCmsgPayload)
  431. }
  432. if u.ecnRecvSupported {
  433. // IP_TOS arrives as 1 byte; IPV6_TCLASS arrives as a 4-byte int.
  434. // Reserve enough for the wider of the two so the same buffer fits
  435. // either family alongside any UDP_GRO cmsg.
  436. cmsgSpace += unix.CmsgSpace(4)
  437. }
  438. msgs, buffers, names, _ := u.PrepareRawMessages(u.batch, bufSize, cmsgSpace)
  439. //reader needs to capture variables from this function, since it's used as a lambda with rawConn.Read
  440. //defining it outside the loop so it gets re-used
  441. reader := func(fd uintptr) (done bool) {
  442. n, done, operr = recvmmsg(fd, msgs)
  443. return done
  444. }
  445. for {
  446. if cmsgSpace > 0 {
  447. for i := range msgs {
  448. setMsgControllen(&msgs[i].Hdr, cmsgSpace)
  449. }
  450. }
  451. err := u.rawConn.Read(reader)
  452. if err != nil {
  453. return err
  454. }
  455. if operr != nil {
  456. return operr
  457. }
  458. for i := 0; i < n; i++ {
  459. from := getFrom(names, i, u.isV4)
  460. payload := buffers[i][:msgs[i].Len]
  461. segSize := 0
  462. outerECN := byte(0)
  463. if cmsgSpace > 0 {
  464. segSize, outerECN = parseRecvCmsg(&msgs[i].Hdr, u.groSupported, u.ecnRecvSupported)
  465. }
  466. if segSize <= 0 || segSize >= len(payload) {
  467. r(from, payload, RxMeta{OuterECN: outerECN})
  468. } else {
  469. for off := 0; off < len(payload); off += segSize {
  470. end := off + segSize
  471. if end > len(payload) {
  472. end = len(payload)
  473. }
  474. seg := payload[off:end]
  475. r(from, seg, RxMeta{OuterECN: outerECN})
  476. }
  477. }
  478. }
  479. flush()
  480. }
  481. }
  482. // headerCounter returns the big-endian uint64 message counter at bytes
  483. // [8:16] of a nebula packet, or 0 if the buffer is too short.
  484. func headerCounter(buf []byte) uint64 {
  485. if len(buf) < 16 {
  486. return 0
  487. }
  488. return binary.BigEndian.Uint64(buf[8:16])
  489. }
  490. // parseRecvCmsg walks the per-slot ancillary buffer once and extracts up to
  491. // two values of interest in a single pass: the UDP_GRO gso_size (when
  492. // wantGRO is true) and the outer IP-level ECN codepoint stamped on the
  493. // carrier (when wantECN is true). Returns zeros for whichever field is not
  494. // requested or not present.
  495. //
  496. // The outer ECN is accepted from EITHER an IP_TOS (IPPROTO_IP, 1-byte) or an
  497. // IPV6_TCLASS (IPPROTO_IPV6, 4-byte int) cmsg, regardless of the socket's
  498. // family: a dual-stack v6 socket (isV4 == false) delivers IPv4 peers' outer
  499. // ECN as an IP_TOS cmsg — gating on socket family here dropped v4-underlay
  500. // ECN entirely. Whichever cmsg the kernel delivered carries the value.
  501. func parseRecvCmsg(hdr *msghdr, wantGRO, wantECN bool) (gso int, ecn byte) {
  502. controllen := int(hdr.Controllen)
  503. if controllen < unix.SizeofCmsghdr || hdr.Control == nil {
  504. return 0, 0
  505. }
  506. ctrl := unsafe.Slice(hdr.Control, controllen)
  507. off := 0
  508. for off+unix.SizeofCmsghdr <= len(ctrl) {
  509. ch := (*unix.Cmsghdr)(unsafe.Pointer(&ctrl[off]))
  510. clen := int(ch.Len)
  511. if clen < unix.SizeofCmsghdr || off+clen > len(ctrl) {
  512. return gso, ecn
  513. }
  514. dataOff := off + unix.CmsgLen(0)
  515. switch {
  516. case wantGRO && ch.Level == unix.SOL_UDP && ch.Type == unix.UDP_GRO:
  517. if dataOff+udpGROCmsgPayload <= len(ctrl) {
  518. gso = int(int32(binary.NativeEndian.Uint32(ctrl[dataOff : dataOff+udpGROCmsgPayload])))
  519. }
  520. case wantECN && ch.Level == unix.IPPROTO_IP && ch.Type == unix.IP_TOS:
  521. // IP_TOS arrives as a single byte; only the low 2 bits are ECN.
  522. // A dual-stack v6 socket carries v4 peers' outer ECN here.
  523. if dataOff+1 <= len(ctrl) {
  524. ecn = ctrl[dataOff] & 0x03
  525. }
  526. case wantECN && ch.Level == unix.IPPROTO_IPV6 && ch.Type == unix.IPV6_TCLASS:
  527. // IPV6_TCLASS arrives as a 4-byte int; ECN is the low 2 bits.
  528. if dataOff+4 <= len(ctrl) {
  529. ecn = byte(binary.NativeEndian.Uint32(ctrl[dataOff:dataOff+4])) & 0x03
  530. }
  531. }
  532. // Advance by the aligned cmsg space.
  533. off += unix.CmsgSpace(clen - unix.CmsgLen(0))
  534. }
  535. return gso, ecn
  536. }
  537. func (u *StdConn) ListenOut(r EncReader, flush func()) error {
  538. if u.batch == 1 {
  539. return u.listenOutSingle(r, flush)
  540. } else {
  541. return u.listenOutBatch(r, flush)
  542. }
  543. }
  544. func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
  545. _, err := u.udpConn.WriteToUDPAddrPort(b, ip)
  546. return err
  547. }
  548. // WriteBatch sends bufs via sendmmsg(2) using the preallocated scratch on
  549. // StdConn. Consecutive packets to the same destination with matching segment
  550. // sizes (all but possibly the last) are coalesced into a single mmsghdr entry
  551. // carrying a UDP_SEGMENT cmsg, so one syscall can mix runs of GSO superpackets
  552. // with plain one-off datagrams. Without GSO support every packet is its own
  553. // entry, matching the prior behaviour.
  554. //
  555. // Chunks larger than the scratch are processed across multiple syscalls. If
  556. // sendmmsg returns an error AND zero entries went out we fall back to
  557. // per-packet WriteTo for that chunk so the caller still gets best-effort
  558. // delivery; on a partial-success error we just replay the remainder.
  559. func (u *StdConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort, ecns []byte) error {
  560. if len(bufs) != len(addrs) {
  561. return fmt.Errorf("WriteBatch: len(bufs)=%d != len(addrs)=%d", len(bufs), len(addrs))
  562. }
  563. if ecns != nil && len(ecns) != len(bufs) {
  564. return fmt.Errorf("WriteBatch: len(ecns)=%d != len(bufs)=%d", len(ecns), len(bufs))
  565. }
  566. // Callers deliver same-destination packets contiguously and in counter
  567. // order, so we run the GSO planner directly without a pre-sort. A
  568. // sorting pass measurably hurt throughput in microbenchmarks while
  569. // providing no observed reordering benefit.
  570. i := 0
  571. sendChunks:
  572. for i < len(bufs) {
  573. baseI := i
  574. entry := 0
  575. iovIdx := 0
  576. for entry < len(u.writeMsgs) && i < len(bufs) {
  577. iovBudget := len(u.writeIovs) - iovIdx
  578. if iovBudget < 1 {
  579. break
  580. }
  581. runLen, segSize := u.planRun(bufs, addrs, ecns, i, iovBudget)
  582. if runLen == 0 {
  583. break
  584. }
  585. for k := 0; k < runLen; k++ {
  586. b := bufs[i+k]
  587. if len(b) == 0 {
  588. u.writeIovs[iovIdx+k].Base = nil
  589. setIovLen(&u.writeIovs[iovIdx+k], 0)
  590. } else {
  591. u.writeIovs[iovIdx+k].Base = &b[0]
  592. setIovLen(&u.writeIovs[iovIdx+k], len(b))
  593. }
  594. }
  595. nlen, err := writeSockaddr(u.writeNames[entry], addrs[i], u.isV4)
  596. if err != nil {
  597. // One destination in this chunk has an address family the
  598. // socket can't send to (e.g. an IPv6 remote on a v4-bound
  599. // socket → ErrInvalidIPv6RemoteForSocket). Abandoning the whole
  600. // sendmmsg here would drop every packet already packed for this
  601. // chunk plus every packet still ahead of us in bufs. Instead
  602. // fall back to per-packet WriteTo for the packets packed so far
  603. // in this chunk and the offending one: WriteTo delivers each
  604. // good destination and only errors on the bad one, which we
  605. // drop and keep going. One bad destination costs one packet,
  606. // never the batch. (Same fallback the zero-sent sendmmsg path
  607. // below uses, extended to cover the misaddressed packet.)
  608. for k := baseI; k <= i; k++ {
  609. if werr := u.WriteTo(bufs[k], addrs[k]); werr != nil && k != i {
  610. return werr
  611. }
  612. }
  613. i++
  614. continue sendChunks
  615. }
  616. hdr := &u.writeMsgs[entry].Hdr
  617. hdr.Iov = &u.writeIovs[iovIdx]
  618. setMsgIovlen(hdr, runLen)
  619. hdr.Namelen = uint32(nlen)
  620. var ecn byte
  621. if ecns != nil {
  622. ecn = ecns[i]
  623. }
  624. // ECN cmsg family follows the destination, not the socket: a
  625. // v4-mapped dst on a dual-stack v6 socket must be stamped via
  626. // IP_TOS. addrs[i] is this run's destination (i advances below).
  627. dstIsV4 := addrs[i].Addr().Unmap().Is4()
  628. u.writeEntryCmsg(entry, runLen, segSize, ecn, dstIsV4)
  629. i += runLen
  630. iovIdx += runLen
  631. u.writeEntryEnd[entry] = i
  632. entry++
  633. }
  634. if entry == 0 {
  635. return fmt.Errorf("sendmmsg: no progress")
  636. }
  637. sent, serr := u.sendmmsg(entry)
  638. if serr != nil && sent <= 0 {
  639. // Nothing went out for this chunk; fall back to WriteTo for each
  640. // packet that was queued this iteration. We only enter this path
  641. // when sendmmsg returned an error AND zero entries succeeded —
  642. // otherwise the partial-success advance below replays only the
  643. // remainder, avoiding duplicates of already-sent packets.
  644. //
  645. // sent=-1 from sendmmsg means message 0 itself failed (partial
  646. // success returns the count instead), so log entry 0's parameters
  647. // — that's the entry the kernel rejected.
  648. hdr0 := &u.writeMsgs[0].Hdr
  649. runLen0 := u.writeEntryEnd[0] - baseI
  650. seg0 := len(bufs[baseI])
  651. ecn0 := byte(0)
  652. if ecns != nil {
  653. ecn0 = ecns[baseI]
  654. }
  655. u.l.Warn("sendmmsg had problem",
  656. "sent", sent, "err", serr,
  657. "entries", entry,
  658. "entry0_runLen", runLen0,
  659. "entry0_segSize", seg0,
  660. "entry0_iovlen", hdr0.Iovlen,
  661. "entry0_controllen", hdr0.Controllen,
  662. "entry0_namelen", hdr0.Namelen,
  663. "entry0_ecn", ecn0,
  664. "entry0_dst", addrs[baseI],
  665. "isV4", u.isV4,
  666. "gso", u.gsoSupported,
  667. "gro", u.groSupported,
  668. )
  669. for k := baseI; k < i; k++ {
  670. if werr := u.WriteTo(bufs[k], addrs[k]); werr != nil {
  671. return werr
  672. }
  673. }
  674. continue
  675. }
  676. if sent == 0 {
  677. return fmt.Errorf("sendmmsg made no progress")
  678. }
  679. // Rewind i to the end of the last successfully sent entry. For a
  680. // full-success send this leaves i unchanged; for a partial send it
  681. // replays the remainder on the next outer-loop iteration.
  682. i = u.writeEntryEnd[sent-1]
  683. }
  684. return nil
  685. }
  686. // planRun groups consecutive packets starting at `start` that can be sent as
  687. // a single UDP GSO superpacket (one sendmmsg entry with UDP_SEGMENT cmsg).
  688. // A run of length 1 means the entry carries no UDP_SEGMENT cmsg and the
  689. // kernel treats it as a plain datagram. Returns the run length and the
  690. // per-segment size (which equals len(bufs[start])). Without GSO support
  691. // every call returns runLen=1. Outer ECN (when ecns != nil) is also a run
  692. // boundary — the kernel stamps one outer codepoint per sendmsg entry, so
  693. // mixing values inside a run would lose information.
  694. func (u *StdConn) planRun(bufs [][]byte, addrs []netip.AddrPort, ecns []byte, start, iovBudget int) (int, int) {
  695. if start >= len(bufs) || iovBudget < 1 {
  696. return 0, 0
  697. }
  698. segSize := len(bufs[start])
  699. if !u.gsoSupported || segSize == 0 || segSize > maxGSOBytes {
  700. return 1, segSize
  701. }
  702. dst := addrs[start]
  703. var ecn byte
  704. if ecns != nil {
  705. ecn = ecns[start]
  706. }
  707. maxLen := u.maxGSOSegments
  708. if iovBudget < maxLen {
  709. maxLen = iovBudget
  710. }
  711. runLen := 1
  712. total := segSize
  713. for runLen < maxLen && start+runLen < len(bufs) {
  714. nextLen := len(bufs[start+runLen])
  715. if nextLen == 0 || nextLen > segSize {
  716. break
  717. }
  718. if addrs[start+runLen] != dst {
  719. break
  720. }
  721. if ecns != nil && ecns[start+runLen] != ecn {
  722. break
  723. }
  724. if total+nextLen > maxGSOBytes {
  725. break
  726. }
  727. total += nextLen
  728. runLen++
  729. if nextLen < segSize {
  730. // A short packet must be the last in the run.
  731. break
  732. }
  733. }
  734. return runLen, segSize
  735. }
  736. // writeEntryCmsg sets up the per-mmsghdr Hdr.Control / Hdr.Controllen for one
  737. // entry. It writes the UDP_SEGMENT payload when runLen >= 2 and the
  738. // IP_TOS/IPV6_TCLASS payload when ecn != 0, then points hdr.Control at the
  739. // smallest contiguous span that covers whichever cmsg(s) actually apply.
  740. //
  741. // The outer-ECN cmsg family must match the *destination*, not the socket: on
  742. // the default dual-stack v6 bind, a v4-mapped destination is routed through
  743. // the kernel's IPv4 path, which parses IP_TOS (IPPROTO_IP) and ignores an
  744. // IPV6_TCLASS cmsg. prepareWriteMessages pre-fills a default header; here we
  745. // rewrite its Level/Type (and Len) per entry from dstIsV4 so v4 peers get
  746. // IP_TOS and v6 peers get IPV6_TCLASS. The data payload is a 4-byte int for
  747. // both families, so the pre-computed cmsg space is unchanged.
  748. func (u *StdConn) writeEntryCmsg(entry, runLen, segSize int, ecn byte, dstIsV4 bool) {
  749. hdr := &u.writeMsgs[entry].Hdr
  750. useSeg := runLen >= 2
  751. useEcn := ecn != 0
  752. base := entry * u.writeCmsgSpace
  753. if useSeg {
  754. dataOff := base + unix.CmsgLen(0)
  755. binary.NativeEndian.PutUint16(u.writeCmsg[dataOff:dataOff+2], uint16(segSize))
  756. }
  757. if useEcn {
  758. ecnHdr := (*unix.Cmsghdr)(unsafe.Pointer(&u.writeCmsg[base+u.writeCmsgSegSpace]))
  759. if dstIsV4 {
  760. ecnHdr.Level = int32(unix.IPPROTO_IP)
  761. ecnHdr.Type = int32(unix.IP_TOS)
  762. } else {
  763. ecnHdr.Level = int32(unix.IPPROTO_IPV6)
  764. ecnHdr.Type = int32(unix.IPV6_TCLASS)
  765. }
  766. setCmsgLen(ecnHdr, unix.CmsgLen(4))
  767. dataOff := base + u.writeCmsgSegSpace + unix.CmsgLen(0)
  768. binary.NativeEndian.PutUint32(u.writeCmsg[dataOff:dataOff+4], uint32(ecn))
  769. }
  770. switch {
  771. case useSeg && useEcn:
  772. hdr.Control = &u.writeCmsg[base]
  773. setMsgControllen(hdr, u.writeCmsgSpace)
  774. case useSeg:
  775. hdr.Control = &u.writeCmsg[base]
  776. setMsgControllen(hdr, u.writeCmsgSegSpace)
  777. case useEcn:
  778. hdr.Control = &u.writeCmsg[base+u.writeCmsgSegSpace]
  779. setMsgControllen(hdr, u.writeCmsgEcnSpace)
  780. default:
  781. hdr.Control = nil
  782. setMsgControllen(hdr, 0)
  783. }
  784. }
  785. // sendmmsg issues sendmmsg(2) over u.rawConn against the first n entries
  786. // of u.writeMsgs. Routes through u.rawSend so the per-call kernel callback
  787. // stays alloc-free.
  788. func (u *StdConn) sendmmsg(n int) (int, error) {
  789. return u.rawSend.send(u.rawConn, n)
  790. }
  791. // writeSockaddr encodes addr into buf (which must be at least
  792. // SizeofSockaddrInet6 bytes). Returns the number of bytes used. If isV4 is
  793. // true and addr is not a v4 (or v4-in-v6) address, returns an error.
  794. func writeSockaddr(buf []byte, addr netip.AddrPort, isV4 bool) (int, error) {
  795. ap := addr.Addr().Unmap()
  796. if isV4 {
  797. if !ap.Is4() {
  798. return 0, ErrInvalidIPv6RemoteForSocket
  799. }
  800. // struct sockaddr_in: { sa_family_t(2), in_port_t(2, BE), in_addr(4), zero(8) }
  801. // sa_family is host endian.
  802. binary.NativeEndian.PutUint16(buf[0:2], unix.AF_INET)
  803. binary.BigEndian.PutUint16(buf[2:4], addr.Port())
  804. ip4 := ap.As4()
  805. copy(buf[4:8], ip4[:])
  806. for j := 8; j < 16; j++ {
  807. buf[j] = 0
  808. }
  809. return unix.SizeofSockaddrInet4, nil
  810. }
  811. // struct sockaddr_in6: { sa_family_t(2), in_port_t(2, BE), flowinfo(4), in6_addr(16), scope_id(4) }
  812. binary.NativeEndian.PutUint16(buf[0:2], unix.AF_INET6)
  813. binary.BigEndian.PutUint16(buf[2:4], addr.Port())
  814. binary.NativeEndian.PutUint32(buf[4:8], 0)
  815. ip6 := addr.Addr().As16()
  816. copy(buf[8:24], ip6[:])
  817. binary.NativeEndian.PutUint32(buf[24:28], 0)
  818. return unix.SizeofSockaddrInet6, nil
  819. }
  820. func (u *StdConn) ReloadConfig(c *config.C) {
  821. b := c.GetInt("listen.read_buffer", 0)
  822. if b > 0 {
  823. err := u.SetRecvBuffer(b)
  824. if err == nil {
  825. s, err := u.GetRecvBuffer()
  826. if err == nil {
  827. u.l.Info("listen.read_buffer was set", "size", s)
  828. } else {
  829. u.l.Warn("Failed to get listen.read_buffer", "error", err)
  830. }
  831. } else {
  832. u.l.Error("Failed to set listen.read_buffer", "error", err)
  833. }
  834. }
  835. b = c.GetInt("listen.write_buffer", 0)
  836. if b > 0 {
  837. err := u.SetSendBuffer(b)
  838. if err == nil {
  839. s, err := u.GetSendBuffer()
  840. if err == nil {
  841. u.l.Info("listen.write_buffer was set", "size", s)
  842. } else {
  843. u.l.Warn("Failed to get listen.write_buffer", "error", err)
  844. }
  845. } else {
  846. u.l.Error("Failed to set listen.write_buffer", "error", err)
  847. }
  848. }
  849. b = c.GetInt("listen.so_mark", 0)
  850. s, err := u.GetSoMark()
  851. if b > 0 || (err == nil && s != 0) {
  852. err := u.SetSoMark(b)
  853. if err == nil {
  854. s, err := u.GetSoMark()
  855. if err == nil {
  856. u.l.Info("listen.so_mark was set", "mark", s)
  857. } else {
  858. u.l.Warn("Failed to get listen.so_mark", "error", err)
  859. }
  860. } else {
  861. u.l.Error("Failed to set listen.so_mark", "error", err)
  862. }
  863. }
  864. }
  865. func (u *StdConn) getMemInfo(meminfo *[unix.SK_MEMINFO_VARS]uint32) error {
  866. var vallen uint32 = 4 * unix.SK_MEMINFO_VARS
  867. if u.rawConn == nil {
  868. return fmt.Errorf("no UDP connection")
  869. }
  870. var opErr error
  871. err := u.rawConn.Control(func(fd uintptr) {
  872. _, _, syserr := unix.Syscall6(unix.SYS_GETSOCKOPT, fd, uintptr(unix.SOL_SOCKET), uintptr(unix.SO_MEMINFO), uintptr(unsafe.Pointer(meminfo)), uintptr(unsafe.Pointer(&vallen)), 0)
  873. if syserr != 0 {
  874. opErr = syserr
  875. }
  876. })
  877. if err != nil {
  878. return err
  879. }
  880. return opErr
  881. }
  882. func (u *StdConn) Close() error {
  883. if u.udpConn != nil {
  884. return u.udpConn.Close()
  885. }
  886. return nil
  887. }
  888. func NewUDPStatsEmitter(udpConns []Conn) func() {
  889. // Check if our kernel supports SO_MEMINFO before registering the gauges
  890. var udpGauges [][unix.SK_MEMINFO_VARS]metrics.Gauge
  891. var meminfo [unix.SK_MEMINFO_VARS]uint32
  892. if err := udpConns[0].(*StdConn).getMemInfo(&meminfo); err == nil {
  893. udpGauges = make([][unix.SK_MEMINFO_VARS]metrics.Gauge, len(udpConns))
  894. for i := range udpConns {
  895. udpGauges[i] = [unix.SK_MEMINFO_VARS]metrics.Gauge{
  896. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rmem_alloc", i), nil),
  897. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rcvbuf", i), nil),
  898. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_alloc", i), nil),
  899. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.sndbuf", i), nil),
  900. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.fwd_alloc", i), nil),
  901. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_queued", i), nil),
  902. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.optmem", i), nil),
  903. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.backlog", i), nil),
  904. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.drops", i), nil),
  905. }
  906. }
  907. }
  908. return func() {
  909. for i, gauges := range udpGauges {
  910. if err := udpConns[i].(*StdConn).getMemInfo(&meminfo); err == nil {
  911. for j := 0; j < unix.SK_MEMINFO_VARS; j++ {
  912. gauges[j].Update(int64(meminfo[j]))
  913. }
  914. }
  915. }
  916. }
  917. }
  918. func parseRelease(r string) (major, minor int) {
  919. // strip anything after the second dot or any non-digit
  920. parts := strings.SplitN(r, ".", 3)
  921. if len(parts) < 2 {
  922. return 0, 0
  923. }
  924. major, _ = strconv.Atoi(parts[0])
  925. // minor may have trailing junk like "15-generic"
  926. mp := parts[1]
  927. for i, c := range mp {
  928. if c < '0' || c > '9' {
  929. mp = mp[:i]
  930. break
  931. }
  932. }
  933. minor, _ = strconv.Atoi(mp)
  934. return
  935. }