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  }