udp_linux.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. //go:build !android && !e2e_testing
  2. // +build !android,!e2e_testing
  3. package udp
  4. import (
  5. "encoding/binary"
  6. "errors"
  7. "fmt"
  8. "net"
  9. "net/netip"
  10. "syscall"
  11. "unsafe"
  12. "github.com/rcrowley/go-metrics"
  13. "github.com/sirupsen/logrus"
  14. "github.com/slackhq/nebula/config"
  15. "golang.org/x/sys/unix"
  16. )
  17. type StdConn struct {
  18. sysFd int
  19. isV4 bool
  20. l *logrus.Logger
  21. batch int
  22. gsoSupported bool
  23. groSupported bool
  24. }
  25. func maybeIPV4(ip net.IP) (net.IP, bool) {
  26. ip4 := ip.To4()
  27. if ip4 != nil {
  28. return ip4, true
  29. }
  30. return ip, false
  31. }
  32. // supportsUDPOffload checks if the kernel supports UDP GSO (Generic Segmentation Offload)
  33. // by attempting to get the UDP_SEGMENT socket option.
  34. func supportsUDPOffload(fd int) bool {
  35. _, err := unix.GetsockoptInt(fd, unix.IPPROTO_UDP, unix.UDP_SEGMENT)
  36. return err == nil
  37. }
  38. // supportsUDPGRO checks if the kernel supports UDP GRO (Generic Receive Offload)
  39. // and attempts to enable it on the socket.
  40. func supportsUDPGRO(fd int) bool {
  41. // Try to enable UDP_GRO
  42. err := unix.SetsockoptInt(fd, unix.IPPROTO_UDP, unix.UDP_GRO, 1)
  43. return err == nil
  44. }
  45. const (
  46. // Maximum number of datagrams that can be coalesced with GSO/GRO
  47. udpSegmentMaxDatagrams = 64
  48. // Maximum size of a GRO coalesced packet (64KB is the practical limit)
  49. // This is udpSegmentMaxDatagrams * MTU but capped at 65535
  50. groMaxPacketSize = 65535
  51. )
  52. // setGSOSize writes a UDP_SEGMENT control message to the provided buffer
  53. // with the given segment size. Returns the actual control message length.
  54. func setGSOSize(control []byte, gsoSize uint16) int {
  55. // Build the cmsghdr structure
  56. cmsgLen := unix.CmsgLen(2) // 2 bytes for uint16 segment size
  57. cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&control[0]))
  58. cmsg.Level = unix.IPPROTO_UDP
  59. cmsg.Type = unix.UDP_SEGMENT
  60. cmsg.SetLen(cmsgLen)
  61. // Write the segment size after the header (after cmsghdr)
  62. binary.NativeEndian.PutUint16(control[unix.SizeofCmsghdr:], gsoSize)
  63. return unix.CmsgSpace(2) // aligned size
  64. }
  65. // getGROSize parses a control message buffer to extract the UDP_GRO segment size.
  66. // Returns 0 if no GRO control message is present (meaning the packet is not coalesced).
  67. func getGROSize(control []byte, controlLen int) uint16 {
  68. if controlLen < unix.SizeofCmsghdr {
  69. return 0
  70. }
  71. // Parse control messages
  72. for offset := 0; offset < controlLen; {
  73. if offset+unix.SizeofCmsghdr > controlLen {
  74. break
  75. }
  76. cmsg := (*unix.Cmsghdr)(unsafe.Pointer(&control[offset]))
  77. cmsgDataLen := int(cmsg.Len) - unix.SizeofCmsghdr
  78. if cmsgDataLen < 0 {
  79. break
  80. }
  81. if cmsg.Level == unix.IPPROTO_UDP && cmsg.Type == unix.UDP_GRO {
  82. if cmsgDataLen >= 2 {
  83. return binary.NativeEndian.Uint16(control[offset+unix.SizeofCmsghdr:])
  84. }
  85. }
  86. // Move to next control message (aligned)
  87. offset += unix.CmsgSpace(cmsgDataLen)
  88. }
  89. return 0
  90. }
  91. func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
  92. af := unix.AF_INET6
  93. if ip.Is4() {
  94. af = unix.AF_INET
  95. }
  96. syscall.ForkLock.RLock()
  97. fd, err := unix.Socket(af, unix.SOCK_DGRAM, unix.IPPROTO_UDP)
  98. if err == nil {
  99. unix.CloseOnExec(fd)
  100. }
  101. syscall.ForkLock.RUnlock()
  102. if err != nil {
  103. unix.Close(fd)
  104. return nil, fmt.Errorf("unable to open socket: %s", err)
  105. }
  106. if multi {
  107. if err = unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
  108. return nil, fmt.Errorf("unable to set SO_REUSEPORT: %s", err)
  109. }
  110. }
  111. var sa unix.Sockaddr
  112. if ip.Is4() {
  113. sa4 := &unix.SockaddrInet4{Port: port}
  114. sa4.Addr = ip.As4()
  115. sa = sa4
  116. } else {
  117. sa6 := &unix.SockaddrInet6{Port: port}
  118. sa6.Addr = ip.As16()
  119. sa = sa6
  120. }
  121. if err = unix.Bind(fd, sa); err != nil {
  122. return nil, fmt.Errorf("unable to bind to socket: %s", err)
  123. }
  124. gsoSupported := supportsUDPOffload(fd)
  125. if gsoSupported {
  126. l.Info("UDP GSO offload is supported")
  127. }
  128. groSupported := supportsUDPGRO(fd)
  129. if groSupported {
  130. l.Info("UDP GRO offload is supported and enabled")
  131. }
  132. return &StdConn{sysFd: fd, isV4: ip.Is4(), l: l, batch: batch, gsoSupported: gsoSupported, groSupported: groSupported}, err
  133. }
  134. func (u *StdConn) SupportsMultipleReaders() bool {
  135. return true
  136. }
  137. func (u *StdConn) SupportsGSO() bool {
  138. return u.gsoSupported
  139. }
  140. func (u *StdConn) SupportsGRO() bool {
  141. return u.groSupported
  142. }
  143. func (u *StdConn) Rebind() error {
  144. return nil
  145. }
  146. func (u *StdConn) SetRecvBuffer(n int) error {
  147. return unix.SetsockoptInt(u.sysFd, unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, n)
  148. }
  149. func (u *StdConn) SetSendBuffer(n int) error {
  150. return unix.SetsockoptInt(u.sysFd, unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, n)
  151. }
  152. func (u *StdConn) SetSoMark(mark int) error {
  153. return unix.SetsockoptInt(u.sysFd, unix.SOL_SOCKET, unix.SO_MARK, mark)
  154. }
  155. func (u *StdConn) GetRecvBuffer() (int, error) {
  156. return unix.GetsockoptInt(int(u.sysFd), unix.SOL_SOCKET, unix.SO_RCVBUF)
  157. }
  158. func (u *StdConn) GetSendBuffer() (int, error) {
  159. return unix.GetsockoptInt(int(u.sysFd), unix.SOL_SOCKET, unix.SO_SNDBUF)
  160. }
  161. func (u *StdConn) GetSoMark() (int, error) {
  162. return unix.GetsockoptInt(int(u.sysFd), unix.SOL_SOCKET, unix.SO_MARK)
  163. }
  164. func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
  165. sa, err := unix.Getsockname(u.sysFd)
  166. if err != nil {
  167. return netip.AddrPort{}, err
  168. }
  169. switch sa := sa.(type) {
  170. case *unix.SockaddrInet4:
  171. return netip.AddrPortFrom(netip.AddrFrom4(sa.Addr), uint16(sa.Port)), nil
  172. case *unix.SockaddrInet6:
  173. return netip.AddrPortFrom(netip.AddrFrom16(sa.Addr), uint16(sa.Port)), nil
  174. default:
  175. return netip.AddrPort{}, fmt.Errorf("unsupported sock type: %T", sa)
  176. }
  177. }
  178. func (u *StdConn) ListenOut(r EncReader) {
  179. var ip netip.Addr
  180. msgs, buffers, names, controls := u.PrepareRawMessages(u.batch)
  181. read := u.ReadMulti
  182. if u.batch == 1 {
  183. read = u.ReadSingle
  184. }
  185. // Store the original control buffer size for resetting after each read
  186. controlLen := 0
  187. if u.groSupported && len(controls) > 0 && len(controls[0]) > 0 {
  188. controlLen = len(controls[0])
  189. }
  190. for {
  191. // Reset Controllen before each read - the kernel updates this field
  192. // after recvmsg to indicate actual received control data length
  193. if controlLen > 0 {
  194. for i := range msgs {
  195. setMsghdrControllen(&msgs[i].Hdr, controlLen)
  196. }
  197. }
  198. n, err := read(msgs)
  199. if err != nil {
  200. u.l.WithError(err).Debug("udp socket is closed, exiting read loop")
  201. return
  202. }
  203. for i := 0; i < n; i++ {
  204. // Extract source address
  205. if u.isV4 {
  206. ip, _ = netip.AddrFromSlice(names[i][4:8])
  207. } else {
  208. ip, _ = netip.AddrFromSlice(names[i][8:24])
  209. }
  210. srcAddr := netip.AddrPortFrom(ip.Unmap(), binary.BigEndian.Uint16(names[i][2:4]))
  211. // Check for GRO coalesced packet
  212. totalLen := int(msgs[i].Len)
  213. segmentSize := uint16(0)
  214. if controlLen > 0 {
  215. segmentSize = getGROSize(controls[i], getMsghdrControllen(&msgs[i].Hdr))
  216. }
  217. if segmentSize > 0 && totalLen > int(segmentSize) {
  218. // This is a GRO coalesced packet - split it into individual datagrams
  219. for offset := 0; offset < totalLen; {
  220. packetLen := int(segmentSize)
  221. if offset+packetLen > totalLen {
  222. // Last packet may be smaller
  223. packetLen = totalLen - offset
  224. }
  225. r(srcAddr, buffers[i][offset:offset+packetLen])
  226. offset += packetLen
  227. }
  228. } else {
  229. // Single packet, no coalescing
  230. r(srcAddr, buffers[i][:totalLen])
  231. }
  232. }
  233. }
  234. }
  235. func (u *StdConn) ReadSingle(msgs []rawMessage) (int, error) {
  236. for {
  237. n, _, err := unix.Syscall6(
  238. unix.SYS_RECVMSG,
  239. uintptr(u.sysFd),
  240. uintptr(unsafe.Pointer(&(msgs[0].Hdr))),
  241. 0,
  242. 0,
  243. 0,
  244. 0,
  245. )
  246. if err != 0 {
  247. return 0, &net.OpError{Op: "recvmsg", Err: err}
  248. }
  249. msgs[0].Len = uint32(n)
  250. return 1, nil
  251. }
  252. }
  253. func (u *StdConn) ReadMulti(msgs []rawMessage) (int, error) {
  254. for {
  255. n, _, err := unix.Syscall6(
  256. unix.SYS_RECVMMSG,
  257. uintptr(u.sysFd),
  258. uintptr(unsafe.Pointer(&msgs[0])),
  259. uintptr(len(msgs)),
  260. unix.MSG_WAITFORONE,
  261. 0,
  262. 0,
  263. )
  264. if err != 0 {
  265. return 0, &net.OpError{Op: "recvmmsg", Err: err}
  266. }
  267. return int(n), nil
  268. }
  269. }
  270. func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
  271. if u.isV4 {
  272. return u.writeTo4(b, ip)
  273. }
  274. return u.writeTo6(b, ip)
  275. }
  276. func (u *StdConn) writeTo6(b []byte, ip netip.AddrPort) error {
  277. var rsa unix.RawSockaddrInet6
  278. rsa.Family = unix.AF_INET6
  279. rsa.Addr = ip.Addr().As16()
  280. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port())
  281. for {
  282. _, _, err := unix.Syscall6(
  283. unix.SYS_SENDTO,
  284. uintptr(u.sysFd),
  285. uintptr(unsafe.Pointer(&b[0])),
  286. uintptr(len(b)),
  287. uintptr(0),
  288. uintptr(unsafe.Pointer(&rsa)),
  289. uintptr(unix.SizeofSockaddrInet6),
  290. )
  291. if err != 0 {
  292. return &net.OpError{Op: "sendto", Err: err}
  293. }
  294. return nil
  295. }
  296. }
  297. func (u *StdConn) writeTo4(b []byte, ip netip.AddrPort) error {
  298. if !ip.Addr().Is4() {
  299. return ErrInvalidIPv6RemoteForSocket
  300. }
  301. var rsa unix.RawSockaddrInet4
  302. rsa.Family = unix.AF_INET
  303. rsa.Addr = ip.Addr().As4()
  304. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa.Port))[:], ip.Port())
  305. for {
  306. _, _, err := unix.Syscall6(
  307. unix.SYS_SENDTO,
  308. uintptr(u.sysFd),
  309. uintptr(unsafe.Pointer(&b[0])),
  310. uintptr(len(b)),
  311. uintptr(0),
  312. uintptr(unsafe.Pointer(&rsa)),
  313. uintptr(unix.SizeofSockaddrInet4),
  314. )
  315. if err != 0 {
  316. return &net.OpError{Op: "sendto", Err: err}
  317. }
  318. return nil
  319. }
  320. }
  321. func (u *StdConn) ReloadConfig(c *config.C) {
  322. b := c.GetInt("listen.read_buffer", 0)
  323. if b > 0 {
  324. err := u.SetRecvBuffer(b)
  325. if err == nil {
  326. s, err := u.GetRecvBuffer()
  327. if err == nil {
  328. u.l.WithField("size", s).Info("listen.read_buffer was set")
  329. } else {
  330. u.l.WithError(err).Warn("Failed to get listen.read_buffer")
  331. }
  332. } else {
  333. u.l.WithError(err).Error("Failed to set listen.read_buffer")
  334. }
  335. }
  336. b = c.GetInt("listen.write_buffer", 0)
  337. if b > 0 {
  338. err := u.SetSendBuffer(b)
  339. if err == nil {
  340. s, err := u.GetSendBuffer()
  341. if err == nil {
  342. u.l.WithField("size", s).Info("listen.write_buffer was set")
  343. } else {
  344. u.l.WithError(err).Warn("Failed to get listen.write_buffer")
  345. }
  346. } else {
  347. u.l.WithError(err).Error("Failed to set listen.write_buffer")
  348. }
  349. }
  350. b = c.GetInt("listen.so_mark", 0)
  351. s, err := u.GetSoMark()
  352. if b > 0 || (err == nil && s != 0) {
  353. err := u.SetSoMark(b)
  354. if err == nil {
  355. s, err := u.GetSoMark()
  356. if err == nil {
  357. u.l.WithField("mark", s).Info("listen.so_mark was set")
  358. } else {
  359. u.l.WithError(err).Warn("Failed to get listen.so_mark")
  360. }
  361. } else {
  362. u.l.WithError(err).Error("Failed to set listen.so_mark")
  363. }
  364. }
  365. }
  366. func (u *StdConn) getMemInfo(meminfo *[unix.SK_MEMINFO_VARS]uint32) error {
  367. var vallen uint32 = 4 * unix.SK_MEMINFO_VARS
  368. _, _, err := unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(u.sysFd), uintptr(unix.SOL_SOCKET), uintptr(unix.SO_MEMINFO), uintptr(unsafe.Pointer(meminfo)), uintptr(unsafe.Pointer(&vallen)), 0)
  369. if err != 0 {
  370. return err
  371. }
  372. return nil
  373. }
  374. func (u *StdConn) Close() error {
  375. return syscall.Close(u.sysFd)
  376. }
  377. func (u *StdConn) WriteBatch(pkts []BatchPacket) (int, error) {
  378. if len(pkts) == 0 {
  379. return 0, nil
  380. }
  381. // If GSO is supported, try to coalesce packets to the same destination
  382. if u.gsoSupported {
  383. return u.writeBatchGSO(pkts)
  384. }
  385. return u.writeBatchSendmmsg(pkts)
  386. }
  387. // writeBatchSendmmsg sends packets using sendmmsg without GSO coalescing
  388. func (u *StdConn) writeBatchSendmmsg(pkts []BatchPacket) (int, error) {
  389. msgs := make([]rawMessage, len(pkts))
  390. iovecs := make([]iovec, len(pkts))
  391. var names4 []unix.RawSockaddrInet4
  392. var names6 []unix.RawSockaddrInet6
  393. if u.isV4 {
  394. names4 = make([]unix.RawSockaddrInet4, len(pkts))
  395. } else {
  396. names6 = make([]unix.RawSockaddrInet6, len(pkts))
  397. }
  398. for i := range pkts {
  399. setIovecBase(&iovecs[i], &pkts[i].Payload[0])
  400. setIovecLen(&iovecs[i], len(pkts[i].Payload))
  401. msgs[i].Hdr.Iov = &iovecs[i]
  402. setMsghdrIovlen(&msgs[i].Hdr, 1)
  403. if u.isV4 {
  404. names4[i].Family = unix.AF_INET
  405. names4[i].Addr = pkts[i].Addr.Addr().As4()
  406. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&names4[i].Port))[:], pkts[i].Addr.Port())
  407. msgs[i].Hdr.Name = (*byte)(unsafe.Pointer(&names4[i]))
  408. msgs[i].Hdr.Namelen = unix.SizeofSockaddrInet4
  409. } else {
  410. names6[i].Family = unix.AF_INET6
  411. names6[i].Addr = pkts[i].Addr.Addr().As16()
  412. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&names6[i].Port))[:], pkts[i].Addr.Port())
  413. msgs[i].Hdr.Name = (*byte)(unsafe.Pointer(&names6[i]))
  414. msgs[i].Hdr.Namelen = unix.SizeofSockaddrInet6
  415. }
  416. }
  417. var sent int
  418. for sent < len(msgs) {
  419. n, _, errno := unix.Syscall6(
  420. unix.SYS_SENDMMSG,
  421. uintptr(u.sysFd),
  422. uintptr(unsafe.Pointer(&msgs[sent])),
  423. uintptr(len(msgs)-sent),
  424. 0,
  425. 0,
  426. 0,
  427. )
  428. if errno == unix.EINTR {
  429. continue
  430. }
  431. if errno != 0 {
  432. return sent, &net.OpError{Op: "sendmmsg", Err: errno}
  433. }
  434. sent += int(n)
  435. }
  436. return sent, nil
  437. }
  438. // writeBatchGSO sends packets using GSO coalescing when possible.
  439. // Packets to the same destination with the same size are coalesced into a single
  440. // GSO message. Mixed destinations or sizes fall back to individual sendmmsg calls.
  441. func (u *StdConn) writeBatchGSO(pkts []BatchPacket) (int, error) {
  442. // Group packets by destination and try to coalesce
  443. totalSent := 0
  444. i := 0
  445. for i < len(pkts) {
  446. // Find a run of packets to the same destination with compatible sizes
  447. startIdx := i
  448. dst := pkts[i].Addr
  449. segmentSize := len(pkts[i].Payload)
  450. // Count how many packets we can coalesce (same destination, same size except possibly last)
  451. coalescedCount := 1
  452. totalSize := segmentSize
  453. for i+coalescedCount < len(pkts) && coalescedCount < udpSegmentMaxDatagrams {
  454. next := pkts[i+coalescedCount]
  455. if next.Addr != dst {
  456. break
  457. }
  458. nextSize := len(next.Payload)
  459. // For GSO, all packets except the last must have the same size
  460. // The last packet can be smaller (but not larger)
  461. if nextSize != segmentSize {
  462. // Check if this could be the last packet (smaller is ok)
  463. if nextSize < segmentSize && i+coalescedCount == len(pkts)-1 {
  464. coalescedCount++
  465. totalSize += nextSize
  466. }
  467. break
  468. }
  469. coalescedCount++
  470. totalSize += nextSize
  471. }
  472. // If we have multiple packets to coalesce, use GSO
  473. if coalescedCount > 1 {
  474. err := u.sendGSO(pkts[startIdx:startIdx+coalescedCount], dst, segmentSize, totalSize)
  475. if err != nil {
  476. // If GSO fails (e.g., EIO due to NIC not supporting checksum offload),
  477. // disable GSO and fall back to sendmmsg for the rest
  478. if isGSOError(err) {
  479. u.l.WithError(err).Warn("GSO send failed, disabling GSO for this connection")
  480. u.gsoSupported = false
  481. // Send remaining packets with sendmmsg
  482. remaining, rerr := u.writeBatchSendmmsg(pkts[startIdx:])
  483. return totalSent + remaining, rerr
  484. }
  485. return totalSent, err
  486. }
  487. totalSent += coalescedCount
  488. i += coalescedCount
  489. } else {
  490. // Single packet, send without GSO overhead
  491. err := u.WriteTo(pkts[i].Payload, pkts[i].Addr)
  492. if err != nil {
  493. return totalSent, err
  494. }
  495. totalSent++
  496. i++
  497. }
  498. }
  499. return totalSent, nil
  500. }
  501. // sendGSO sends coalesced packets using UDP GSO
  502. func (u *StdConn) sendGSO(pkts []BatchPacket, dst netip.AddrPort, segmentSize, totalSize int) error {
  503. // Allocate a buffer large enough for all packet payloads
  504. coalescedBuf := make([]byte, totalSize)
  505. offset := 0
  506. for _, pkt := range pkts {
  507. copy(coalescedBuf[offset:], pkt.Payload)
  508. offset += len(pkt.Payload)
  509. }
  510. // Prepare control message with GSO segment size
  511. control := make([]byte, unix.CmsgSpace(2))
  512. controlLen := setGSOSize(control, uint16(segmentSize))
  513. // Prepare the iovec
  514. iov := iovec{}
  515. setIovecBase(&iov, &coalescedBuf[0])
  516. setIovecLen(&iov, totalSize)
  517. // Prepare the msghdr
  518. var hdr msghdr
  519. hdr.Iov = &iov
  520. setMsghdrIovlen(&hdr, 1)
  521. hdr.Control = &control[0]
  522. setMsghdrControllen(&hdr, controlLen)
  523. // Declare sockaddr at function scope so it remains valid for the syscall
  524. // (must not go out of scope before the syscall is made)
  525. var rsa4 unix.RawSockaddrInet4
  526. var rsa6 unix.RawSockaddrInet6
  527. // Set destination address
  528. if u.isV4 {
  529. rsa4.Family = unix.AF_INET
  530. rsa4.Addr = dst.Addr().As4()
  531. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa4.Port))[:], dst.Port())
  532. hdr.Name = (*byte)(unsafe.Pointer(&rsa4))
  533. hdr.Namelen = unix.SizeofSockaddrInet4
  534. } else {
  535. rsa6.Family = unix.AF_INET6
  536. rsa6.Addr = dst.Addr().As16()
  537. binary.BigEndian.PutUint16((*[2]byte)(unsafe.Pointer(&rsa6.Port))[:], dst.Port())
  538. hdr.Name = (*byte)(unsafe.Pointer(&rsa6))
  539. hdr.Namelen = unix.SizeofSockaddrInet6
  540. }
  541. for {
  542. _, _, errno := unix.Syscall6(
  543. unix.SYS_SENDMSG,
  544. uintptr(u.sysFd),
  545. uintptr(unsafe.Pointer(&hdr)),
  546. 0,
  547. 0,
  548. 0,
  549. 0,
  550. )
  551. if errno == unix.EINTR {
  552. continue
  553. }
  554. if errno != 0 {
  555. return &net.OpError{Op: "sendmsg", Err: errno}
  556. }
  557. return nil
  558. }
  559. }
  560. // isGSOError returns true if the error indicates GSO is not supported by the NIC
  561. func isGSOError(err error) bool {
  562. var opErr *net.OpError
  563. if !errors.As(err, &opErr) {
  564. return false
  565. }
  566. // EIO typically means the NIC doesn't support checksum offload required for GSO
  567. return errors.Is(opErr.Err, unix.EIO)
  568. }
  569. func NewUDPStatsEmitter(udpConns []Conn) func() {
  570. // Check if our kernel supports SO_MEMINFO before registering the gauges
  571. var udpGauges [][unix.SK_MEMINFO_VARS]metrics.Gauge
  572. var meminfo [unix.SK_MEMINFO_VARS]uint32
  573. if err := udpConns[0].(*StdConn).getMemInfo(&meminfo); err == nil {
  574. udpGauges = make([][unix.SK_MEMINFO_VARS]metrics.Gauge, len(udpConns))
  575. for i := range udpConns {
  576. udpGauges[i] = [unix.SK_MEMINFO_VARS]metrics.Gauge{
  577. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rmem_alloc", i), nil),
  578. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rcvbuf", i), nil),
  579. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_alloc", i), nil),
  580. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.sndbuf", i), nil),
  581. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.fwd_alloc", i), nil),
  582. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_queued", i), nil),
  583. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.optmem", i), nil),
  584. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.backlog", i), nil),
  585. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.drops", i), nil),
  586. }
  587. }
  588. }
  589. return func() {
  590. for i, gauges := range udpGauges {
  591. if err := udpConns[i].(*StdConn).getMemInfo(&meminfo); err == nil {
  592. for j := 0; j < unix.SK_MEMINFO_VARS; j++ {
  593. gauges[j].Update(int64(meminfo[j]))
  594. }
  595. }
  596. }
  597. }
  598. }