| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- package netlink
- import (
- "errors"
- "fmt"
- "syscall"
- "github.com/vishvananda/netlink/nl"
- "golang.org/x/sys/unix"
- )
- const (
- sizeofXDPSocketRequest = 1 + 1 + 2 + 4 + 4 + 2*4
- sizeofXDPSocket = 0x10
- )
- // https://elixir.bootlin.com/linux/v6.2/source/include/uapi/linux/xdp_diag.h#L12
- type xdpSocketRequest struct {
- Family uint8
- Protocol uint8
- pad uint16
- Ino uint32
- Show uint32
- Cookie [2]uint32
- }
- func (r *xdpSocketRequest) Serialize() []byte {
- b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)}
- b.Write(r.Family)
- b.Write(r.Protocol)
- native.PutUint16(b.Next(2), r.pad)
- native.PutUint32(b.Next(4), r.Ino)
- native.PutUint32(b.Next(4), r.Show)
- native.PutUint32(b.Next(4), r.Cookie[0])
- native.PutUint32(b.Next(4), r.Cookie[1])
- return b.Bytes
- }
- func (r *xdpSocketRequest) Len() int { return sizeofXDPSocketRequest }
- func (s *XDPSocket) deserialize(b []byte) error {
- if len(b) < sizeofXDPSocket {
- return fmt.Errorf("XDP socket data short read (%d); want %d", len(b), sizeofXDPSocket)
- }
- rb := readBuffer{Bytes: b}
- s.Family = rb.Read()
- s.Type = rb.Read()
- s.pad = native.Uint16(rb.Next(2))
- s.Ino = native.Uint32(rb.Next(4))
- s.Cookie[0] = native.Uint32(rb.Next(4))
- s.Cookie[1] = native.Uint32(rb.Next(4))
- return nil
- }
- // SocketXDPGetInfo returns the XDP socket identified by its inode number and/or
- // socket cookie. Specify the cookie as SOCK_ANY_COOKIE if
- //
- // If the returned error is [ErrDumpInterrupted], the caller should retry.
- func SocketXDPGetInfo(ino uint32, cookie uint64) (*XDPDiagInfoResp, error) {
- // We have a problem here: dumping AF_XDP sockets currently does not support
- // filtering. We thus need to dump all XSKs and then only filter afterwards
- // :(
- xsks, err := SocketDiagXDP()
- if err != nil {
- return nil, err
- }
- checkCookie := cookie != SOCK_ANY_COOKIE && cookie != 0
- crumblingCookie := [2]uint32{uint32(cookie), uint32(cookie >> 32)}
- checkIno := ino != 0
- var xskinfo *XDPDiagInfoResp
- for _, xsk := range xsks {
- if checkIno && xsk.XDPDiagMsg.Ino != ino {
- continue
- }
- if checkCookie && xsk.XDPDiagMsg.Cookie != crumblingCookie {
- continue
- }
- if xskinfo != nil {
- return nil, errors.New("multiple matching XDP sockets")
- }
- xskinfo = xsk
- }
- if xskinfo == nil {
- return nil, errors.New("no matching XDP socket")
- }
- return xskinfo, nil
- }
- // SocketDiagXDP requests XDP_DIAG_INFO for XDP family sockets.
- //
- // If the returned error is [ErrDumpInterrupted], results may be inconsistent
- // or incomplete.
- func SocketDiagXDP() ([]*XDPDiagInfoResp, error) {
- var result []*XDPDiagInfoResp
- err := socketDiagXDPExecutor(func(m syscall.NetlinkMessage) error {
- sockInfo := &XDPSocket{}
- if err := sockInfo.deserialize(m.Data); err != nil {
- return err
- }
- attrs, err := nl.ParseRouteAttr(m.Data[sizeofXDPSocket:])
- if err != nil {
- return err
- }
- res, err := attrsToXDPDiagInfoResp(attrs, sockInfo)
- if err != nil {
- return err
- }
- result = append(result, res)
- return nil
- })
- if err != nil && !errors.Is(err, ErrDumpInterrupted) {
- return nil, err
- }
- return result, err
- }
- // socketDiagXDPExecutor requests XDP_DIAG_INFO for XDP family sockets.
- func socketDiagXDPExecutor(receiver func(syscall.NetlinkMessage) error) error {
- s, err := nl.Subscribe(unix.NETLINK_INET_DIAG)
- if err != nil {
- return err
- }
- defer s.Close()
- req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, unix.NLM_F_DUMP)
- req.AddData(&xdpSocketRequest{
- Family: unix.AF_XDP,
- Show: XDP_SHOW_INFO | XDP_SHOW_RING_CFG | XDP_SHOW_UMEM | XDP_SHOW_STATS,
- })
- if err := s.Send(req); err != nil {
- return err
- }
- dumpIntr := false
- loop:
- for {
- msgs, from, err := s.Receive()
- if err != nil {
- return err
- }
- if from.Pid != nl.PidKernel {
- return fmt.Errorf("Wrong sender portid %d, expected %d", from.Pid, nl.PidKernel)
- }
- if len(msgs) == 0 {
- return errors.New("no message nor error from netlink")
- }
- for _, m := range msgs {
- if m.Header.Flags&unix.NLM_F_DUMP_INTR != 0 {
- dumpIntr = true
- }
- switch m.Header.Type {
- case unix.NLMSG_DONE:
- break loop
- case unix.NLMSG_ERROR:
- error := int32(native.Uint32(m.Data[0:4]))
- return syscall.Errno(-error)
- }
- if err := receiver(m); err != nil {
- return err
- }
- }
- }
- if dumpIntr {
- return ErrDumpInterrupted
- }
- return nil
- }
- func attrsToXDPDiagInfoResp(attrs []syscall.NetlinkRouteAttr, sockInfo *XDPSocket) (*XDPDiagInfoResp, error) {
- resp := &XDPDiagInfoResp{
- XDPDiagMsg: sockInfo,
- XDPInfo: &XDPInfo{},
- }
- for _, a := range attrs {
- switch a.Attr.Type {
- case XDP_DIAG_INFO:
- resp.XDPInfo.Ifindex = native.Uint32(a.Value[0:4])
- resp.XDPInfo.QueueID = native.Uint32(a.Value[4:8])
- case XDP_DIAG_UID:
- resp.XDPInfo.UID = native.Uint32(a.Value[0:4])
- case XDP_DIAG_RX_RING:
- resp.XDPInfo.RxRingEntries = native.Uint32(a.Value[0:4])
- case XDP_DIAG_TX_RING:
- resp.XDPInfo.TxRingEntries = native.Uint32(a.Value[0:4])
- case XDP_DIAG_UMEM_FILL_RING:
- resp.XDPInfo.UmemFillRingEntries = native.Uint32(a.Value[0:4])
- case XDP_DIAG_UMEM_COMPLETION_RING:
- resp.XDPInfo.UmemCompletionRingEntries = native.Uint32(a.Value[0:4])
- case XDP_DIAG_UMEM:
- umem := &XDPDiagUmem{}
- if err := umem.deserialize(a.Value); err != nil {
- return nil, err
- }
- resp.XDPInfo.Umem = umem
- case XDP_DIAG_STATS:
- stats := &XDPDiagStats{}
- if err := stats.deserialize(a.Value); err != nil {
- return nil, err
- }
- resp.XDPInfo.Stats = stats
- }
- }
- return resp, nil
- }
|