socket_xdp_linux.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package netlink
  2. import (
  3. "errors"
  4. "fmt"
  5. "syscall"
  6. "github.com/vishvananda/netlink/nl"
  7. "golang.org/x/sys/unix"
  8. )
  9. const (
  10. sizeofXDPSocketRequest = 1 + 1 + 2 + 4 + 4 + 2*4
  11. sizeofXDPSocket = 0x10
  12. )
  13. // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/xdp_diag.h#L12
  14. type xdpSocketRequest struct {
  15. Family uint8
  16. Protocol uint8
  17. pad uint16
  18. Ino uint32
  19. Show uint32
  20. Cookie [2]uint32
  21. }
  22. func (r *xdpSocketRequest) Serialize() []byte {
  23. b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)}
  24. b.Write(r.Family)
  25. b.Write(r.Protocol)
  26. native.PutUint16(b.Next(2), r.pad)
  27. native.PutUint32(b.Next(4), r.Ino)
  28. native.PutUint32(b.Next(4), r.Show)
  29. native.PutUint32(b.Next(4), r.Cookie[0])
  30. native.PutUint32(b.Next(4), r.Cookie[1])
  31. return b.Bytes
  32. }
  33. func (r *xdpSocketRequest) Len() int { return sizeofXDPSocketRequest }
  34. func (s *XDPSocket) deserialize(b []byte) error {
  35. if len(b) < sizeofXDPSocket {
  36. return fmt.Errorf("XDP socket data short read (%d); want %d", len(b), sizeofXDPSocket)
  37. }
  38. rb := readBuffer{Bytes: b}
  39. s.Family = rb.Read()
  40. s.Type = rb.Read()
  41. s.pad = native.Uint16(rb.Next(2))
  42. s.Ino = native.Uint32(rb.Next(4))
  43. s.Cookie[0] = native.Uint32(rb.Next(4))
  44. s.Cookie[1] = native.Uint32(rb.Next(4))
  45. return nil
  46. }
  47. // SocketXDPGetInfo returns the XDP socket identified by its inode number and/or
  48. // socket cookie. Specify the cookie as SOCK_ANY_COOKIE if
  49. //
  50. // If the returned error is [ErrDumpInterrupted], the caller should retry.
  51. func SocketXDPGetInfo(ino uint32, cookie uint64) (*XDPDiagInfoResp, error) {
  52. // We have a problem here: dumping AF_XDP sockets currently does not support
  53. // filtering. We thus need to dump all XSKs and then only filter afterwards
  54. // :(
  55. xsks, err := SocketDiagXDP()
  56. if err != nil {
  57. return nil, err
  58. }
  59. checkCookie := cookie != SOCK_ANY_COOKIE && cookie != 0
  60. crumblingCookie := [2]uint32{uint32(cookie), uint32(cookie >> 32)}
  61. checkIno := ino != 0
  62. var xskinfo *XDPDiagInfoResp
  63. for _, xsk := range xsks {
  64. if checkIno && xsk.XDPDiagMsg.Ino != ino {
  65. continue
  66. }
  67. if checkCookie && xsk.XDPDiagMsg.Cookie != crumblingCookie {
  68. continue
  69. }
  70. if xskinfo != nil {
  71. return nil, errors.New("multiple matching XDP sockets")
  72. }
  73. xskinfo = xsk
  74. }
  75. if xskinfo == nil {
  76. return nil, errors.New("no matching XDP socket")
  77. }
  78. return xskinfo, nil
  79. }
  80. // SocketDiagXDP requests XDP_DIAG_INFO for XDP family sockets.
  81. //
  82. // If the returned error is [ErrDumpInterrupted], results may be inconsistent
  83. // or incomplete.
  84. func SocketDiagXDP() ([]*XDPDiagInfoResp, error) {
  85. var result []*XDPDiagInfoResp
  86. err := socketDiagXDPExecutor(func(m syscall.NetlinkMessage) error {
  87. sockInfo := &XDPSocket{}
  88. if err := sockInfo.deserialize(m.Data); err != nil {
  89. return err
  90. }
  91. attrs, err := nl.ParseRouteAttr(m.Data[sizeofXDPSocket:])
  92. if err != nil {
  93. return err
  94. }
  95. res, err := attrsToXDPDiagInfoResp(attrs, sockInfo)
  96. if err != nil {
  97. return err
  98. }
  99. result = append(result, res)
  100. return nil
  101. })
  102. if err != nil && !errors.Is(err, ErrDumpInterrupted) {
  103. return nil, err
  104. }
  105. return result, err
  106. }
  107. // socketDiagXDPExecutor requests XDP_DIAG_INFO for XDP family sockets.
  108. func socketDiagXDPExecutor(receiver func(syscall.NetlinkMessage) error) error {
  109. s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
  110. if err != nil {
  111. return err
  112. }
  113. defer s.Close()
  114. req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
  115. req.AddData(&xdpSocketRequest{
  116. Family: unix.AF_XDP,
  117. Show: XDP_SHOW_INFO | XDP_SHOW_RING_CFG | XDP_SHOW_UMEM | XDP_SHOW_STATS,
  118. })
  119. if err := s.Send(req); err != nil {
  120. return err
  121. }
  122. dumpIntr := false
  123. loop:
  124. for {
  125. msgs, from, err := s.Receive()
  126. if err != nil {
  127. return err
  128. }
  129. if from.Pid != nl.PidKernel {
  130. return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
  131. }
  132. if len(msgs) == 0 {
  133. return errors.New("no message nor error from netlink")
  134. }
  135. for _, m := range msgs {
  136. if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 {
  137. dumpIntr = true
  138. }
  139. switch m.Header.Type {
  140. case unix.NLMSG_DONE:
  141. break loop
  142. case unix.NLMSG_ERROR:
  143. error := int32(native.Uint32(m.Data[0:4]))
  144. return syscall.Errno(-error)
  145. }
  146. if err := receiver(m); err != nil {
  147. return err
  148. }
  149. }
  150. }
  151. if dumpIntr {
  152. return ErrDumpInterrupted
  153. }
  154. return nil
  155. }
  156. func attrsToXDPDiagInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *XDPSocket) (*XDPDiagInfoResp, error) {
  157. resp := &XDPDiagInfoResp{
  158. XDPDiagMsg: sockInfo,
  159. XDPInfo: &XDPInfo{},
  160. }
  161. for _, a := range attrs {
  162. switch a.Attr.Type {
  163. case XDP_DIAG_INFO:
  164. resp.XDPInfo.Ifindex = native.Uint32(a.Value[0:4])
  165. resp.XDPInfo.QueueID = native.Uint32(a.Value[4:8])
  166. case XDP_DIAG_UID:
  167. resp.XDPInfo.UID = native.Uint32(a.Value[0:4])
  168. case XDP_DIAG_RX_RING:
  169. resp.XDPInfo.RxRingEntries = native.Uint32(a.Value[0:4])
  170. case XDP_DIAG_TX_RING:
  171. resp.XDPInfo.TxRingEntries = native.Uint32(a.Value[0:4])
  172. case XDP_DIAG_UMEM_FILL_RING:
  173. resp.XDPInfo.UmemFillRingEntries = native.Uint32(a.Value[0:4])
  174. case XDP_DIAG_UMEM_COMPLETION_RING:
  175. resp.XDPInfo.UmemCompletionRingEntries = native.Uint32(a.Value[0:4])
  176. case XDP_DIAG_UMEM:
  177. umem := &XDPDiagUmem{}
  178. if err := umem.deserialize(a.Value); err != nil {
  179. return nil, err
  180. }
  181. resp.XDPInfo.Umem = umem
  182. case XDP_DIAG_STATS:
  183. stats := &XDPDiagStats{}
  184. if err := stats.deserialize(a.Value); err != nil {
  185. return nil, err
  186. }
  187. resp.XDPInfo.Stats = stats
  188. }
  189. }
  190. return resp, nil
  191. }