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  }