mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
netstack: add --EXPERIMENTAL-afxdp flag
Setting the flag creates an AF_XDP socket that will be consumed by the XDP dispatcher. PiperOrigin-RevId: 470109694
This commit is contained in:
committed by
gVisor bot
parent
385afe9f2c
commit
540c79c8cf
@@ -41,6 +41,7 @@
|
||||
package fdbased
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -88,6 +89,9 @@ const (
|
||||
// primary use-case for this is runsc which uses an AF_PACKET FD to
|
||||
// receive packets from the veth device.
|
||||
PacketMMap
|
||||
// AFXDP utilizes an AF_XDP socket to receive packets. AFXDP requires that
|
||||
// the underlying FD be an AF_XDP socket.
|
||||
AFXDP
|
||||
)
|
||||
|
||||
func (p PacketDispatchMode) String() string {
|
||||
@@ -98,6 +102,8 @@ func (p PacketDispatchMode) String() string {
|
||||
return "RecvMMsg"
|
||||
case PacketMMap:
|
||||
return "PacketMMap"
|
||||
case AFXDP:
|
||||
return "AFXDP"
|
||||
default:
|
||||
return fmt.Sprintf("unknown packet dispatch mode '%d'", p)
|
||||
}
|
||||
@@ -217,6 +223,11 @@ type Options struct {
|
||||
// of struct iovec, msghdr, and mmsghdr that may be passed by each host
|
||||
// system call.
|
||||
MaxSyscallHeaderBytes int
|
||||
|
||||
// AFXDPFD is used with the experimental AF_XDP mode.
|
||||
// TODO(b/240191988): Use multiple sockets.
|
||||
// TODO(b/240191988): How do we handle the MTU issue?
|
||||
AFXDPFD int
|
||||
}
|
||||
|
||||
// fanoutID is used for AF_PACKET based endpoints to enable PACKET_FANOUT
|
||||
@@ -374,6 +385,8 @@ func createInboundDispatcher(e *endpoint, fd int, isSocket bool, fID int32) (lin
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("newRecvMMsgDispatcher(%d, %+v) = %v", fd, e, err)
|
||||
}
|
||||
case AFXDP:
|
||||
return nil, errors.New("AFXDP not yet implemented")
|
||||
}
|
||||
}
|
||||
return inboundDispatcher, nil
|
||||
|
||||
+25
-3
@@ -99,7 +99,7 @@ type FDBasedLink struct {
|
||||
QDisc config.QueueingDiscipline
|
||||
Neighbors []Neighbor
|
||||
|
||||
// NumChannels controls how many underlying FD's are to be used to
|
||||
// NumChannels controls how many underlying FDs are to be used to
|
||||
// create this endpoint.
|
||||
NumChannels int
|
||||
}
|
||||
@@ -123,6 +123,8 @@ type CreateLinksAndRoutesArgs struct {
|
||||
|
||||
Defaultv4Gateway DefaultRoute
|
||||
Defaultv6Gateway DefaultRoute
|
||||
|
||||
AFXDP bool
|
||||
}
|
||||
|
||||
// IPWithPrefix is an address with its subnet prefix length.
|
||||
@@ -162,8 +164,11 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct
|
||||
for _, l := range args.FDBasedLinks {
|
||||
wantFDs += l.NumChannels
|
||||
}
|
||||
if args.AFXDP {
|
||||
wantFDs++
|
||||
}
|
||||
if got := len(args.FilePayload.Files); got != wantFDs {
|
||||
return fmt.Errorf("args.FilePayload.Files has %d FD's but we need %d entries based on FDBasedLinks", got, wantFDs)
|
||||
return fmt.Errorf("args.FilePayload.Files has %d FDs but we need %d entries based on FDBasedLinks. AFXDP is %t", got, wantFDs, args.AFXDP)
|
||||
}
|
||||
|
||||
var nicID tcpip.NICID
|
||||
@@ -206,13 +211,16 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct
|
||||
} else {
|
||||
log.Infof("Host kernel version < 5.6, falling back to RecvMMsg dispatch")
|
||||
}
|
||||
if args.AFXDP {
|
||||
dispatchMode = fdbased.AFXDP
|
||||
}
|
||||
|
||||
fdOffset := 0
|
||||
for _, link := range args.FDBasedLinks {
|
||||
nicID++
|
||||
nicids[link.Name] = nicID
|
||||
|
||||
FDs := []int{}
|
||||
FDs := make([]int, 0, link.NumChannels)
|
||||
for j := 0; j < link.NumChannels; j++ {
|
||||
// Copy the underlying FD.
|
||||
oldFD := args.FilePayload.Files[fdOffset].Fd()
|
||||
@@ -224,11 +232,25 @@ func (n *Network) CreateLinksAndRoutes(args *CreateLinksAndRoutesArgs, _ *struct
|
||||
fdOffset++
|
||||
}
|
||||
|
||||
// If AFXDP is enabled, we perform RX via AF_XDP and TX via
|
||||
// AF_PACKET.
|
||||
AFXDPFD := -1
|
||||
if args.AFXDP {
|
||||
oldFD := args.FilePayload.Files[fdOffset].Fd()
|
||||
newFD, err := unix.Dup(int(oldFD))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to dup AF_XDP fd %v: %v", oldFD, err)
|
||||
}
|
||||
AFXDPFD = newFD
|
||||
fdOffset++
|
||||
}
|
||||
|
||||
mac := tcpip.LinkAddress(link.LinkAddress)
|
||||
log.Infof("gso max size is: %d", link.GSOMaxSize)
|
||||
|
||||
linkEP, err := fdbased.New(&fdbased.Options{
|
||||
FDs: FDs,
|
||||
AFXDPFD: AFXDPFD,
|
||||
MTU: uint32(link.MTU),
|
||||
EthernetHeader: mac != "",
|
||||
Address: mac,
|
||||
|
||||
@@ -225,6 +225,10 @@ type Config struct {
|
||||
// Use pools to manage buffer memory instead of heap.
|
||||
BufferPooling bool `flag:"buffer-pooling"`
|
||||
|
||||
// AFXDP defines whether to use an AF_XDP socket to receive packets
|
||||
// (rather than AF_PACKET). Enabling it disables RX checksum offload.
|
||||
AFXDP bool `flag:"EXPERIMENTAL-afxdp"`
|
||||
|
||||
// FDLimit specifies a limit on the number of host file descriptors that can
|
||||
// be open simultaneously by the sentry and gofer. It applies separately to
|
||||
// each.
|
||||
|
||||
@@ -96,6 +96,7 @@ func RegisterFlags(flagSet *flag.FlagSet) {
|
||||
flagSet.Var(queueingDisciplinePtr(QDiscFIFO), "qdisc", "specifies which queueing discipline to apply by default to the non loopback nics used by the sandbox.")
|
||||
flagSet.Int("num-network-channels", 1, "number of underlying channels(FDs) to use for network link endpoints.")
|
||||
flagSet.Bool("buffer-pooling", false, "enable allocation of buffers from a shared pool instead of the heap.")
|
||||
flagSet.Bool("EXPERIMENTAL-afxdp", false, "EXPERIMENTAL. Use an AF_XDP socket to receive packets.")
|
||||
|
||||
// Test flags, not to be used outside tests, ever.
|
||||
flagSet.Bool("TESTONLY-unsafe-nonroot", false, "TEST ONLY; do not ever use! This skips many security measures that isolate the host from the sandbox.")
|
||||
|
||||
+34
-11
@@ -63,7 +63,7 @@ func setupNetwork(conn *urpc.Client, pid int, conf *config.Config) error {
|
||||
// Build the path to the net namespace of the sandbox process.
|
||||
// This is what we will copy.
|
||||
nsPath := filepath.Join("/proc", strconv.Itoa(pid), "ns/net")
|
||||
if err := createInterfacesAndRoutesFromNS(conn, nsPath, conf.HostGSO, conf.GvisorGSO, conf.TXChecksumOffload, conf.RXChecksumOffload, conf.NumNetworkChannels, conf.QDisc); err != nil {
|
||||
if err := createInterfacesAndRoutesFromNS(conn, nsPath, conf); err != nil {
|
||||
return fmt.Errorf("creating interfaces from net namespace %q: %v", nsPath, err)
|
||||
}
|
||||
case config.NetworkHost:
|
||||
@@ -116,7 +116,7 @@ func isRootNS() (bool, error) {
|
||||
// createInterfacesAndRoutesFromNS scrapes the interface and routes from the
|
||||
// net namespace with the given path, creates them in the sandbox, and removes
|
||||
// them from the host.
|
||||
func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, hostGSO bool, gvisorGSO bool, txChecksumOffload bool, rxChecksumOffload bool, numNetworkChannels int, qDisc config.QueueingDiscipline) error {
|
||||
func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *config.Config) error {
|
||||
// Join the network namespace that we will be copying.
|
||||
restore, err := joinNetNS(nsPath)
|
||||
if err != nil {
|
||||
@@ -213,14 +213,16 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, hostGSO b
|
||||
args.Defaultv6Gateway.Name = iface.Name
|
||||
}
|
||||
|
||||
args.AFXDP = conf.AFXDP
|
||||
|
||||
link := boot.FDBasedLink{
|
||||
Name: iface.Name,
|
||||
MTU: iface.MTU,
|
||||
Routes: routes,
|
||||
TXChecksumOffload: txChecksumOffload,
|
||||
RXChecksumOffload: rxChecksumOffload,
|
||||
NumChannels: numNetworkChannels,
|
||||
QDisc: qDisc,
|
||||
TXChecksumOffload: conf.TXChecksumOffload,
|
||||
RXChecksumOffload: conf.RXChecksumOffload,
|
||||
NumChannels: conf.NumNetworkChannels,
|
||||
QDisc: conf.QDisc,
|
||||
Neighbors: neighbors,
|
||||
}
|
||||
|
||||
@@ -235,7 +237,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, hostGSO b
|
||||
// Create the socket for the device.
|
||||
for i := 0; i < link.NumChannels; i++ {
|
||||
log.Debugf("Creating Channel %d", i)
|
||||
socketEntry, err := createSocket(iface, ifaceLink, hostGSO)
|
||||
socketEntry, err := createSocket(iface, ifaceLink, conf.HostGSO, conf.AFXDP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to createSocket for %s : %w", iface.Name, err)
|
||||
}
|
||||
@@ -250,7 +252,16 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, hostGSO b
|
||||
args.FilePayload.Files = append(args.FilePayload.Files, socketEntry.deviceFile)
|
||||
}
|
||||
|
||||
if link.GSOMaxSize == 0 && gvisorGSO {
|
||||
// If enabled, create an RX socket for AF_XDP.
|
||||
if conf.AFXDP {
|
||||
xdpSock, err := createSocketXDP(iface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create XDP socket: %v", err)
|
||||
}
|
||||
args.FilePayload.Files = append(args.FilePayload.Files, xdpSock)
|
||||
}
|
||||
|
||||
if link.GSOMaxSize == 0 && conf.GvisorGSO {
|
||||
// Host GSO is disabled. Let's enable gVisor GSO.
|
||||
link.GSOMaxSize = stack.GvisorGSOMaxSize
|
||||
link.GvisorGSOEnabled = true
|
||||
@@ -313,9 +324,10 @@ type socketEntry struct {
|
||||
gsoMaxSize uint32
|
||||
}
|
||||
|
||||
// createSocket creates an underlying AF_PACKET socket and configures it for use by
|
||||
// the sentry and returns an *os.File that wraps the underlying socket fd.
|
||||
func createSocket(iface net.Interface, ifaceLink netlink.Link, enableGSO bool) (*socketEntry, error) {
|
||||
// createSocket creates an underlying AF_PACKET socket and configures it for
|
||||
// use by the sentry and returns an *os.File that wraps the underlying socket
|
||||
// fd.
|
||||
func createSocket(iface net.Interface, ifaceLink netlink.Link, enableGSO bool, AFXDP bool) (*socketEntry, error) {
|
||||
// Create the socket.
|
||||
const protocol = 0x0300 // htons(ETH_P_ALL)
|
||||
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, protocol)
|
||||
@@ -375,6 +387,17 @@ func createSocket(iface net.Interface, ifaceLink netlink.Link, enableGSO bool) (
|
||||
return &socketEntry{deviceFile, gsoMaxSize}, nil
|
||||
}
|
||||
|
||||
func createSocketXDP(iface net.Interface) (*os.File, error) {
|
||||
// Create an XDP socket. The sentry will mmap memory for the various
|
||||
// rings and bind to the device.
|
||||
fd, err := unix.Socket(unix.AF_XDP, unix.SOCK_RAW, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create AF_XDP socket: %v", err)
|
||||
}
|
||||
deviceFile := os.NewFile(uintptr(fd), "xdp-fd")
|
||||
return deviceFile, nil
|
||||
}
|
||||
|
||||
// loopbackLink returns the link with addresses and routes for a loopback
|
||||
// interface.
|
||||
func loopbackLink(iface net.Interface, addrs []net.Addr) (boot.LoopbackLink, error) {
|
||||
|
||||
Reference in New Issue
Block a user