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