github.com/vishvananda/netlink@v1.3.0/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 // XDPSocketGet returns the XDP socket identified by its inode number and/or 56 // socket cookie. Specify the cookie as SOCK_ANY_COOKIE if 57 func SocketXDPGetInfo(ino uint32, cookie uint64) (*XDPDiagInfoResp, error) { 58 // We have a problem here: dumping AF_XDP sockets currently does not support 59 // filtering. We thus need to dump all XSKs and then only filter afterwards 60 // :( 61 xsks, err := SocketDiagXDP() 62 if err != nil { 63 return nil, err 64 } 65 checkCookie := cookie != SOCK_ANY_COOKIE && cookie != 0 66 crumblingCookie := [2]uint32{uint32(cookie), uint32(cookie >> 32)} 67 checkIno := ino != 0 68 var xskinfo *XDPDiagInfoResp 69 for _, xsk := range xsks { 70 if checkIno && xsk.XDPDiagMsg.Ino != ino { 71 continue 72 } 73 if checkCookie && xsk.XDPDiagMsg.Cookie != crumblingCookie { 74 continue 75 } 76 if xskinfo != nil { 77 return nil, errors.New("multiple matching XDP sockets") 78 } 79 xskinfo = xsk 80 } 81 if xskinfo == nil { 82 return nil, errors.New("no matching XDP socket") 83 } 84 return xskinfo, nil 85 } 86 87 // SocketDiagXDP requests XDP_DIAG_INFO for XDP family sockets. 88 func SocketDiagXDP() ([]*XDPDiagInfoResp, error) { 89 var result []*XDPDiagInfoResp 90 err := socketDiagXDPExecutor(func(m syscall.NetlinkMessage) error { 91 sockInfo := &XDPSocket{} 92 if err := sockInfo.deserialize(m.Data); err != nil { 93 return err 94 } 95 attrs, err := nl.ParseRouteAttr(m.Data[sizeofXDPSocket:]) 96 if err != nil { 97 return err 98 } 99 100 res, err := attrsToXDPDiagInfoResp(attrs, sockInfo) 101 if err != nil { 102 return err 103 } 104 105 result = append(result, res) 106 return nil 107 }) 108 if err != nil { 109 return nil, err 110 } 111 return result, nil 112 } 113 114 // socketDiagXDPExecutor requests XDP_DIAG_INFO for XDP family sockets. 115 func socketDiagXDPExecutor(receiver func(syscall.NetlinkMessage) error) error { 116 s, err := nl.Subscribe(unix.NETLINK_INET_DIAG) 117 if err != nil { 118 return err 119 } 120 defer s.Close() 121 122 req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP) 123 req.AddData(&xdpSocketRequest{ 124 Family: unix.AF_XDP, 125 Show: XDP_SHOW_INFO | XDP_SHOW_RING_CFG | XDP_SHOW_UMEM | XDP_SHOW_STATS, 126 }) 127 if err := s.Send(req); err != nil { 128 return err 129 } 130 131 loop: 132 for { 133 msgs, from, err := s.Receive() 134 if err != nil { 135 return err 136 } 137 if from.Pid != nl.PidKernel { 138 return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel) 139 } 140 if len(msgs) == 0 { 141 return errors.New("no message nor error from netlink") 142 } 143 144 for _, m := range msgs { 145 switch m.Header.Type { 146 case unix.NLMSG_DONE: 147 break loop 148 case unix.NLMSG_ERROR: 149 error := int32(native.Uint32(m.Data[0:4])) 150 return syscall.Errno(-error) 151 } 152 if err := receiver(m); err != nil { 153 return err 154 } 155 } 156 } 157 return nil 158 } 159 160 func attrsToXDPDiagInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *XDPSocket) (*XDPDiagInfoResp, error) { 161 resp := &XDPDiagInfoResp{ 162 XDPDiagMsg: sockInfo, 163 XDPInfo: &XDPInfo{}, 164 } 165 for _, a := range attrs { 166 switch a.Attr.Type { 167 case XDP_DIAG_INFO: 168 resp.XDPInfo.Ifindex = native.Uint32(a.Value[0:4]) 169 resp.XDPInfo.QueueID = native.Uint32(a.Value[4:8]) 170 case XDP_DIAG_UID: 171 resp.XDPInfo.UID = native.Uint32(a.Value[0:4]) 172 case XDP_DIAG_RX_RING: 173 resp.XDPInfo.RxRingEntries = native.Uint32(a.Value[0:4]) 174 case XDP_DIAG_TX_RING: 175 resp.XDPInfo.TxRingEntries = native.Uint32(a.Value[0:4]) 176 case XDP_DIAG_UMEM_FILL_RING: 177 resp.XDPInfo.UmemFillRingEntries = native.Uint32(a.Value[0:4]) 178 case XDP_DIAG_UMEM_COMPLETION_RING: 179 resp.XDPInfo.UmemCompletionRingEntries = native.Uint32(a.Value[0:4]) 180 case XDP_DIAG_UMEM: 181 umem := &XDPDiagUmem{} 182 if err := umem.deserialize(a.Value); err != nil { 183 return nil, err 184 } 185 resp.XDPInfo.Umem = umem 186 case XDP_DIAG_STATS: 187 stats := &XDPDiagStats{} 188 if err := stats.deserialize(a.Value); err != nil { 189 return nil, err 190 } 191 resp.XDPInfo.Stats = stats 192 } 193 } 194 return resp, nil 195 }