udp_linux.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. //go:build !android && !e2e_testing
  2. // +build !android,!e2e_testing
  3. package udp
  4. import (
  5. "context"
  6. "encoding/binary"
  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. udpConn *net.UDPConn
  19. rawConn syscall.RawConn
  20. isV4 bool
  21. l *logrus.Logger
  22. batch int
  23. // sendmmsg scratch. Each queue has its own StdConn, so no locking is
  24. // needed. Sized to MaxWriteBatch at construction; WriteBatch chunks
  25. // larger inputs.
  26. writeMsgs []rawMessage
  27. writeIovs []iovec
  28. writeNames [][]byte
  29. // Preallocated closure + in/out slots for sendmmsg, so the hot path
  30. // does not heap-allocate a fresh closure per call.
  31. writeChunk int
  32. writeSent int
  33. writeErrno syscall.Errno
  34. writeFunc func(fd uintptr) bool
  35. // UDP GSO (sendmsg with UDP_SEGMENT cmsg) support. gsoSupported is
  36. // probed once at socket creation. When true, WriteSegmented takes a
  37. // single-syscall GSO path; otherwise it falls back to a WriteTo loop.
  38. gsoSupported bool
  39. gsoMsg msghdr
  40. gsoIovs []iovec
  41. gsoName []byte // SizeofSockaddrInet6
  42. gsoCmsg []byte // CmsgSpace(2)
  43. gsoSent int
  44. gsoErrno syscall.Errno
  45. gsoFunc func(fd uintptr) bool
  46. }
  47. func setReusePort(network, address string, c syscall.RawConn) error {
  48. var opErr error
  49. err := c.Control(func(fd uintptr) {
  50. opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
  51. //CloseOnExec already set by the runtime
  52. })
  53. if err != nil {
  54. return err
  55. }
  56. return opErr
  57. }
  58. func NewListener(l *logrus.Logger, ip netip.Addr, port int, multi bool, batch int) (Conn, error) {
  59. listen := netip.AddrPortFrom(ip, uint16(port))
  60. lc := net.ListenConfig{}
  61. if multi {
  62. lc.Control = setReusePort
  63. }
  64. //this context is only used during the bind operation, you can't cancel it to kill the socket
  65. pc, err := lc.ListenPacket(context.Background(), "udp", listen.String())
  66. if err != nil {
  67. return nil, fmt.Errorf("unable to open socket: %s", err)
  68. }
  69. udpConn := pc.(*net.UDPConn)
  70. rawConn, err := udpConn.SyscallConn()
  71. if err != nil {
  72. _ = udpConn.Close()
  73. return nil, err
  74. }
  75. //gotta find out if we got an AF_INET6 socket or not:
  76. out := &StdConn{
  77. udpConn: udpConn,
  78. rawConn: rawConn,
  79. l: l,
  80. batch: batch,
  81. }
  82. af, err := out.getSockOptInt(unix.SO_DOMAIN)
  83. if err != nil {
  84. _ = out.Close()
  85. return nil, err
  86. }
  87. out.isV4 = af == unix.AF_INET
  88. out.prepareWriteMessages(MaxWriteBatch)
  89. out.writeFunc = out.sendmmsgRawWrite
  90. out.prepareGSO()
  91. return out, nil
  92. }
  93. // maxGSOSegments caps the per-sendmsg GSO fan-out. Linux kernels have
  94. // historically capped UDP_MAX_SEGMENTS at 64; newer kernels raise it to 128
  95. // but we stay conservative so the same code works everywhere.
  96. const maxGSOSegments = 64
  97. // maxGSOBytes bounds the total payload per sendmsg() when UDP_SEGMENT is
  98. // set. The kernel stitches all iovecs into a single skb whose length the
  99. // UDP length field can represent, and also enforces sk_gso_max_size (which
  100. // on most devices is 65536). We use 65535 so ciphertext + headers always
  101. // fits, avoiding EMSGSIZE on large TSO superpackets.
  102. const maxGSOBytes = 65535
  103. // prepareGSO probes UDP_SEGMENT support and, on success, sets up the
  104. // reusable sendmsg scratch (iovecs, sockaddr, cmsg) plus the preallocated
  105. // raw-write closure used to avoid heap allocations on the hot path.
  106. func (u *StdConn) prepareGSO() {
  107. var probeErr error
  108. if err := u.rawConn.Control(func(fd uintptr) {
  109. probeErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_UDP, unix.UDP_SEGMENT, 0)
  110. }); err != nil {
  111. return
  112. }
  113. if probeErr != nil {
  114. return
  115. }
  116. u.gsoSupported = true
  117. u.gsoIovs = make([]iovec, maxGSOSegments)
  118. u.gsoName = make([]byte, unix.SizeofSockaddrInet6)
  119. u.gsoCmsg = make([]byte, unix.CmsgSpace(2))
  120. // Wire up the static pieces of gsoMsg. Iovlen / Controllen / Namelen /
  121. // cmsg contents get refreshed per call; Iov, Name, Control pointers are
  122. // fixed because the scratch slices never move.
  123. u.gsoMsg.Iov = &u.gsoIovs[0]
  124. u.gsoMsg.Name = &u.gsoName[0]
  125. u.gsoMsg.Control = &u.gsoCmsg[0]
  126. // Prepopulate the cmsg header. Len/Level/Type are constant for our use;
  127. // only the 2-byte gso_size payload changes per call.
  128. cmsghdr := (*unix.Cmsghdr)(unsafe.Pointer(&u.gsoCmsg[0]))
  129. cmsghdr.Level = unix.SOL_UDP
  130. cmsghdr.Type = unix.UDP_SEGMENT
  131. setCmsgLen(cmsghdr, unix.CmsgLen(2))
  132. u.gsoFunc = u.sendmsgRawWriteGSO
  133. }
  134. func (u *StdConn) SupportsMultipleReaders() bool {
  135. return true
  136. }
  137. func (u *StdConn) Rebind() error {
  138. return nil
  139. }
  140. func (u *StdConn) getSockOptInt(opt int) (int, error) {
  141. if u.rawConn == nil {
  142. return 0, fmt.Errorf("no UDP connection")
  143. }
  144. var out int
  145. var opErr error
  146. err := u.rawConn.Control(func(fd uintptr) {
  147. out, opErr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, opt)
  148. })
  149. if err != nil {
  150. return 0, err
  151. }
  152. return out, opErr
  153. }
  154. func (u *StdConn) setSockOptInt(opt int, n int) error {
  155. if u.rawConn == nil {
  156. return fmt.Errorf("no UDP connection")
  157. }
  158. var opErr error
  159. err := u.rawConn.Control(func(fd uintptr) {
  160. opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, opt, n)
  161. })
  162. if err != nil {
  163. return err
  164. }
  165. return opErr
  166. }
  167. func (u *StdConn) SetRecvBuffer(n int) error {
  168. return u.setSockOptInt(unix.SO_RCVBUFFORCE, n)
  169. }
  170. func (u *StdConn) SetSendBuffer(n int) error {
  171. return u.setSockOptInt(unix.SO_SNDBUFFORCE, n)
  172. }
  173. func (u *StdConn) SetSoMark(mark int) error {
  174. return u.setSockOptInt(unix.SO_MARK, mark)
  175. }
  176. func (u *StdConn) GetRecvBuffer() (int, error) {
  177. return u.getSockOptInt(unix.SO_RCVBUF)
  178. }
  179. func (u *StdConn) GetSendBuffer() (int, error) {
  180. return u.getSockOptInt(unix.SO_SNDBUF)
  181. }
  182. func (u *StdConn) GetSoMark() (int, error) {
  183. return u.getSockOptInt(unix.SO_MARK)
  184. }
  185. func (u *StdConn) LocalAddr() (netip.AddrPort, error) {
  186. a := u.udpConn.LocalAddr()
  187. switch v := a.(type) {
  188. case *net.UDPAddr:
  189. addr, ok := netip.AddrFromSlice(v.IP)
  190. if !ok {
  191. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned invalid IP address: %s", v.IP)
  192. }
  193. return netip.AddrPortFrom(addr, uint16(v.Port)), nil
  194. default:
  195. return netip.AddrPort{}, fmt.Errorf("LocalAddr returned: %#v", a)
  196. }
  197. }
  198. func recvmmsg(fd uintptr, msgs []rawMessage) (int, bool, error) {
  199. var errno syscall.Errno
  200. n, _, errno := unix.Syscall6(
  201. unix.SYS_RECVMMSG,
  202. fd,
  203. uintptr(unsafe.Pointer(&msgs[0])),
  204. uintptr(len(msgs)),
  205. unix.MSG_WAITFORONE,
  206. 0,
  207. 0,
  208. )
  209. if errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK {
  210. // No data available, block for I/O and try again.
  211. return int(n), false, nil
  212. }
  213. if errno != 0 {
  214. return int(n), true, &net.OpError{Op: "recvmmsg", Err: errno}
  215. }
  216. return int(n), true, nil
  217. }
  218. func (u *StdConn) listenOutSingle(r EncReader, flush func()) error {
  219. var err error
  220. var n int
  221. var from netip.AddrPort
  222. buffer := make([]byte, MTU)
  223. for {
  224. n, from, err = u.udpConn.ReadFromUDPAddrPort(buffer)
  225. if err != nil {
  226. return err
  227. }
  228. from = netip.AddrPortFrom(from.Addr().Unmap(), from.Port())
  229. r(from, buffer[:n])
  230. flush()
  231. }
  232. }
  233. func (u *StdConn) listenOutBatch(r EncReader, flush func()) error {
  234. var ip netip.Addr
  235. var n int
  236. var operr error
  237. msgs, buffers, names := u.PrepareRawMessages(u.batch)
  238. //reader needs to capture variables from this function, since it's used as a lambda with rawConn.Read
  239. //defining it outside the loop so it gets re-used
  240. reader := func(fd uintptr) (done bool) {
  241. n, done, operr = recvmmsg(fd, msgs)
  242. return done
  243. }
  244. for {
  245. err := u.rawConn.Read(reader)
  246. if err != nil {
  247. return err
  248. }
  249. if operr != nil {
  250. return operr
  251. }
  252. for i := 0; i < n; i++ {
  253. // Its ok to skip the ok check here, the slicing is the only error that can occur and it will panic
  254. if u.isV4 {
  255. ip, _ = netip.AddrFromSlice(names[i][4:8])
  256. } else {
  257. ip, _ = netip.AddrFromSlice(names[i][8:24])
  258. }
  259. r(netip.AddrPortFrom(ip.Unmap(), binary.BigEndian.Uint16(names[i][2:4])), buffers[i][:msgs[i].Len])
  260. }
  261. // End-of-batch: let callers (e.g. TUN write coalescer) flush any
  262. // state they accumulated across this batch.
  263. flush()
  264. }
  265. }
  266. func (u *StdConn) ListenOut(r EncReader, flush func()) error {
  267. if u.batch == 1 {
  268. return u.listenOutSingle(r, flush)
  269. } else {
  270. return u.listenOutBatch(r, flush)
  271. }
  272. }
  273. func (u *StdConn) WriteTo(b []byte, ip netip.AddrPort) error {
  274. _, err := u.udpConn.WriteToUDPAddrPort(b, ip)
  275. return err
  276. }
  277. // WriteBatch sends bufs via sendmmsg(2) using the preallocated scratch on
  278. // StdConn. Chunks larger than the scratch are processed in multiple syscalls.
  279. // If sendmmsg returns a fatal error mid-chunk we fall back to single WriteTo
  280. // calls for the remainder so the caller still gets best-effort delivery.
  281. func (u *StdConn) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) error {
  282. if len(bufs) != len(addrs) {
  283. return fmt.Errorf("WriteBatch: len(bufs)=%d != len(addrs)=%d", len(bufs), len(addrs))
  284. }
  285. //u.l.WithField("bufs", len(bufs)).Info("WriteBatch")
  286. i := 0
  287. for i < len(bufs) {
  288. chunk := len(bufs) - i
  289. if chunk > len(u.writeMsgs) {
  290. chunk = len(u.writeMsgs)
  291. }
  292. for k := 0; k < chunk; k++ {
  293. b := bufs[i+k]
  294. if len(b) == 0 {
  295. // sendmmsg with an empty iovec is legal but pointless; fall
  296. // through after filling the slot so Base is still valid.
  297. u.writeIovs[k].Base = nil
  298. setIovLen(&u.writeIovs[k], 0)
  299. } else {
  300. u.writeIovs[k].Base = &b[0]
  301. setIovLen(&u.writeIovs[k], len(b))
  302. }
  303. nlen, err := writeSockaddr(u.writeNames[k], addrs[i+k], u.isV4)
  304. if err != nil {
  305. return err
  306. }
  307. u.writeMsgs[k].Hdr.Namelen = uint32(nlen)
  308. }
  309. sent, serr := u.sendmmsg(chunk)
  310. if serr != nil {
  311. if sent <= 0 {
  312. // nothing went out; fall back to WriteTo for this chunk.
  313. for k := 0; k < chunk; k++ {
  314. if err := u.WriteTo(bufs[i+k], addrs[i+k]); err != nil {
  315. return err
  316. }
  317. }
  318. i += chunk
  319. continue
  320. }
  321. // partial: treat as success for the sent packets and retry the
  322. // remainder on the next outer-loop iteration.
  323. }
  324. if sent == 0 {
  325. return fmt.Errorf("sendmmsg made no progress")
  326. }
  327. i += sent
  328. }
  329. return nil
  330. }
  331. // sendmmsgRawWrite is the preallocated callback passed to rawConn.Write. It
  332. // reads its input (u.writeChunk) and writes its outputs (u.writeSent,
  333. // u.writeErrno) through StdConn fields so the closure itself does not
  334. // capture per-call locals and therefore does not heap-allocate.
  335. func (u *StdConn) sendmmsgRawWrite(fd uintptr) bool {
  336. r1, _, errno := unix.Syscall6(
  337. unix.SYS_SENDMMSG,
  338. fd,
  339. uintptr(unsafe.Pointer(&u.writeMsgs[0])),
  340. uintptr(u.writeChunk),
  341. 0,
  342. 0,
  343. 0,
  344. )
  345. if errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK {
  346. return false
  347. }
  348. u.writeSent = int(r1)
  349. u.writeErrno = errno
  350. return true
  351. }
  352. func (u *StdConn) SupportsGSO() bool {
  353. return u.gsoSupported
  354. }
  355. // WriteSegmented sends bufs to addr as a UDP GSO superpacket. The kernel
  356. // emits one datagram per iovec on the wire; all iovecs except the last must
  357. // be exactly segSize bytes. Non-GSO kernels hit the WriteTo fallback.
  358. // Called with len(bufs) >= 1. len(bufs) > maxGSOSegments is chunked.
  359. func (u *StdConn) WriteSegmented(bufs [][]byte, addr netip.AddrPort, segSize int) error {
  360. if len(bufs) == 0 {
  361. return nil
  362. }
  363. if !u.gsoSupported {
  364. for _, b := range bufs {
  365. if err := u.WriteTo(b, addr); err != nil {
  366. return err
  367. }
  368. }
  369. return nil
  370. }
  371. nlen, err := writeSockaddr(u.gsoName, addr, u.isV4)
  372. if err != nil {
  373. return err
  374. }
  375. u.gsoMsg.Namelen = uint32(nlen)
  376. setMsgControllen(&u.gsoMsg, unix.CmsgSpace(2))
  377. // Cap the per-syscall fan-out by both segment count and total bytes.
  378. // Kernel rejects sendmsg with EMSGSIZE when segCount*segSize would
  379. // exceed sk_gso_max_size (typically 65536). For segSize > maxGSOBytes
  380. // we can't use GSO at all and must fall back per-packet.
  381. segsByBytes := maxGSOBytes / segSize
  382. if segsByBytes == 0 {
  383. for _, b := range bufs {
  384. if werr := u.WriteTo(b, addr); werr != nil {
  385. return werr
  386. }
  387. }
  388. return nil
  389. }
  390. maxChunk := maxGSOSegments
  391. if segsByBytes < maxChunk {
  392. maxChunk = segsByBytes
  393. }
  394. i := 0
  395. for i < len(bufs) {
  396. chunk := len(bufs) - i
  397. if chunk > maxChunk {
  398. chunk = maxChunk
  399. }
  400. for k := 0; k < chunk; k++ {
  401. b := bufs[i+k]
  402. if len(b) == 0 {
  403. u.gsoIovs[k].Base = nil
  404. setIovLen(&u.gsoIovs[k], 0)
  405. } else {
  406. u.gsoIovs[k].Base = &b[0]
  407. setIovLen(&u.gsoIovs[k], len(b))
  408. }
  409. }
  410. setMsgIovlen(&u.gsoMsg, chunk)
  411. binary.NativeEndian.PutUint16(u.gsoCmsg[unix.CmsgLen(0):unix.CmsgLen(0)+2], uint16(segSize))
  412. if serr := u.sendmsgGSO(); serr != nil {
  413. // Fall back to a per-packet loop for the remainder of the
  414. // batch. Dropping the GSO call entirely is safer than
  415. // returning mid-superpacket and losing bytes.
  416. for k := 0; k < chunk; k++ {
  417. if werr := u.WriteTo(bufs[i+k], addr); werr != nil {
  418. return werr
  419. }
  420. }
  421. }
  422. i += chunk
  423. }
  424. return nil
  425. }
  426. // sendmsgRawWriteGSO is the preallocated rawConn.Write callback for the GSO
  427. // path. Reads the prebuilt u.gsoMsg and writes u.gsoSent / u.gsoErrno.
  428. func (u *StdConn) sendmsgRawWriteGSO(fd uintptr) bool {
  429. r1, _, errno := unix.Syscall(
  430. unix.SYS_SENDMSG,
  431. fd,
  432. uintptr(unsafe.Pointer(&u.gsoMsg)),
  433. 0,
  434. )
  435. if errno == syscall.EAGAIN || errno == syscall.EWOULDBLOCK {
  436. return false
  437. }
  438. u.gsoSent = int(r1)
  439. u.gsoErrno = errno
  440. return true
  441. }
  442. func (u *StdConn) sendmsgGSO() error {
  443. u.gsoSent = 0
  444. u.gsoErrno = 0
  445. if err := u.rawConn.Write(u.gsoFunc); err != nil {
  446. return err
  447. }
  448. if u.gsoErrno != 0 {
  449. return &net.OpError{Op: "sendmsg", Err: u.gsoErrno}
  450. }
  451. return nil
  452. }
  453. func (u *StdConn) sendmmsg(n int) (int, error) {
  454. u.writeChunk = n
  455. u.writeSent = 0
  456. u.writeErrno = 0
  457. if err := u.rawConn.Write(u.writeFunc); err != nil {
  458. return u.writeSent, err
  459. }
  460. if u.writeErrno != 0 {
  461. return u.writeSent, &net.OpError{Op: "sendmmsg", Err: u.writeErrno}
  462. }
  463. return u.writeSent, nil
  464. }
  465. // writeSockaddr encodes addr into buf (which must be at least
  466. // SizeofSockaddrInet6 bytes). Returns the number of bytes used. If isV4 is
  467. // true and addr is not a v4 (or v4-in-v6) address, returns an error.
  468. func writeSockaddr(buf []byte, addr netip.AddrPort, isV4 bool) (int, error) {
  469. ap := addr.Addr().Unmap()
  470. if isV4 {
  471. if !ap.Is4() {
  472. return 0, ErrInvalidIPv6RemoteForSocket
  473. }
  474. // struct sockaddr_in: { sa_family_t(2), in_port_t(2, BE), in_addr(4), zero(8) }
  475. // sa_family is host endian.
  476. binary.NativeEndian.PutUint16(buf[0:2], unix.AF_INET)
  477. binary.BigEndian.PutUint16(buf[2:4], addr.Port())
  478. ip4 := ap.As4()
  479. copy(buf[4:8], ip4[:])
  480. for j := 8; j < 16; j++ {
  481. buf[j] = 0
  482. }
  483. return unix.SizeofSockaddrInet4, nil
  484. }
  485. // struct sockaddr_in6: { sa_family_t(2), in_port_t(2, BE), flowinfo(4), in6_addr(16), scope_id(4) }
  486. binary.NativeEndian.PutUint16(buf[0:2], unix.AF_INET6)
  487. binary.BigEndian.PutUint16(buf[2:4], addr.Port())
  488. binary.NativeEndian.PutUint32(buf[4:8], 0)
  489. ip6 := addr.Addr().As16()
  490. copy(buf[8:24], ip6[:])
  491. binary.NativeEndian.PutUint32(buf[24:28], 0)
  492. return unix.SizeofSockaddrInet6, nil
  493. }
  494. func (u *StdConn) ReloadConfig(c *config.C) {
  495. b := c.GetInt("listen.read_buffer", 0)
  496. if b > 0 {
  497. err := u.SetRecvBuffer(b)
  498. if err == nil {
  499. s, err := u.GetRecvBuffer()
  500. if err == nil {
  501. u.l.WithField("size", s).Info("listen.read_buffer was set")
  502. } else {
  503. u.l.WithError(err).Warn("Failed to get listen.read_buffer")
  504. }
  505. } else {
  506. u.l.WithError(err).Error("Failed to set listen.read_buffer")
  507. }
  508. }
  509. b = c.GetInt("listen.write_buffer", 0)
  510. if b > 0 {
  511. err := u.SetSendBuffer(b)
  512. if err == nil {
  513. s, err := u.GetSendBuffer()
  514. if err == nil {
  515. u.l.WithField("size", s).Info("listen.write_buffer was set")
  516. } else {
  517. u.l.WithError(err).Warn("Failed to get listen.write_buffer")
  518. }
  519. } else {
  520. u.l.WithError(err).Error("Failed to set listen.write_buffer")
  521. }
  522. }
  523. b = c.GetInt("listen.so_mark", 0)
  524. s, err := u.GetSoMark()
  525. if b > 0 || (err == nil && s != 0) {
  526. err := u.SetSoMark(b)
  527. if err == nil {
  528. s, err := u.GetSoMark()
  529. if err == nil {
  530. u.l.WithField("mark", s).Info("listen.so_mark was set")
  531. } else {
  532. u.l.WithError(err).Warn("Failed to get listen.so_mark")
  533. }
  534. } else {
  535. u.l.WithError(err).Error("Failed to set listen.so_mark")
  536. }
  537. }
  538. }
  539. func (u *StdConn) getMemInfo(meminfo *[unix.SK_MEMINFO_VARS]uint32) error {
  540. var vallen uint32 = 4 * unix.SK_MEMINFO_VARS
  541. if u.rawConn == nil {
  542. return fmt.Errorf("no UDP connection")
  543. }
  544. var opErr error
  545. err := u.rawConn.Control(func(fd uintptr) {
  546. _, _, syserr := unix.Syscall6(unix.SYS_GETSOCKOPT, fd, uintptr(unix.SOL_SOCKET), uintptr(unix.SO_MEMINFO), uintptr(unsafe.Pointer(meminfo)), uintptr(unsafe.Pointer(&vallen)), 0)
  547. if syserr != 0 {
  548. opErr = syserr
  549. }
  550. })
  551. if err != nil {
  552. return err
  553. }
  554. return opErr
  555. }
  556. func (u *StdConn) Close() error {
  557. if u.udpConn != nil {
  558. return u.udpConn.Close()
  559. }
  560. return nil
  561. }
  562. func NewUDPStatsEmitter(udpConns []Conn) func() {
  563. // Check if our kernel supports SO_MEMINFO before registering the gauges
  564. var udpGauges [][unix.SK_MEMINFO_VARS]metrics.Gauge
  565. var meminfo [unix.SK_MEMINFO_VARS]uint32
  566. if err := udpConns[0].(*StdConn).getMemInfo(&meminfo); err == nil {
  567. udpGauges = make([][unix.SK_MEMINFO_VARS]metrics.Gauge, len(udpConns))
  568. for i := range udpConns {
  569. udpGauges[i] = [unix.SK_MEMINFO_VARS]metrics.Gauge{
  570. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rmem_alloc", i), nil),
  571. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.rcvbuf", i), nil),
  572. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_alloc", i), nil),
  573. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.sndbuf", i), nil),
  574. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.fwd_alloc", i), nil),
  575. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.wmem_queued", i), nil),
  576. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.optmem", i), nil),
  577. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.backlog", i), nil),
  578. metrics.GetOrRegisterGauge(fmt.Sprintf("udp.%d.drops", i), nil),
  579. }
  580. }
  581. }
  582. return func() {
  583. for i, gauges := range udpGauges {
  584. if err := udpConns[i].(*StdConn).getMemInfo(&meminfo); err == nil {
  585. for j := 0; j < unix.SK_MEMINFO_VARS; j++ {
  586. gauges[j].Update(int64(meminfo[j]))
  587. }
  588. }
  589. }
  590. }
  591. }