diff --git a/runsc/config/config.go b/runsc/config/config.go index 856a8e5cc..998fd94eb 100644 --- a/runsc/config/config.go +++ b/runsc/config/config.go @@ -282,17 +282,8 @@ 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"` - - // AFXDPRedirectHost is the name of a network interface. runsc will - // scrape the address, routes, and neighbors of that interface, and - // send packets via an AF_XDP socket on that interface. - // - // Requires use of `xdp_loader redirect` to setup the XDP program and - // eBPF map that runsc hooks into. - AFXDPRedirectHost string `flag:"EXPERIMENTAL-xdp-redirect-host"` + // XDP controls Whether and how to use XDP. + XDP XDP `flag:"EXPERIMENTAL-xdp"` // AFXDPUseNeedWakeup determines whether XDP_USE_NEED_WAKEUP is set // when using AF_XDP sockets. @@ -924,3 +915,75 @@ func (o *Overlay2) SubMountOverlayMedium() OverlayMedium { } return o.medium } + +// XDP holds configuration for whether and how to use XDP. +type XDP struct { + Mode XDPMode + IfaceName string +} + +// XDPMode specifies a particular use of XDP. +type XDPMode int + +const ( + // XDPModeOff doesn't use XDP. + XDPModeOff XDPMode = iota + + // XDPModeNS uses an AF_XDP socket to read from the VETH device inside + // the container's network namespace. + XDPModeNS + + // XDPModeRedirect uses an AF_XDP socket on the host NIC to bypass the + // Linux network stack. + XDPModeRedirect +) + +const ( + xdpModeStrOff = "off" + xdpModeStrNS = "ns" + xdpModeStrRedirect = "redirect" +) + +var xdpConfig XDP + +// Get implements flag.Getter. +func (xd *XDP) Get() any { + return *xd +} + +// String implements flag.Getter. +func (xd *XDP) String() string { + switch xd.Mode { + case XDPModeOff: + return xdpModeStrOff + case XDPModeNS: + return xdpModeStrNS + case XDPModeRedirect: + return fmt.Sprintf("%s:%s", xdpModeStrRedirect, xd.IfaceName) + default: + panic(fmt.Sprintf("unknown mode %d", xd.Mode)) + } +} + +// Set implements flag.Getter. +func (xd *XDP) Set(input string) error { + parts := strings.Split(input, ":") + if len(parts) > 2 { + return fmt.Errorf("invalid --xdp value: %q", input) + } + + switch { + case input == xdpModeStrOff: + xd.Mode = XDPModeOff + xd.IfaceName = "" + case input == xdpModeStrNS: + xd.Mode = XDPModeNS + xd.IfaceName = "" + case len(parts) == 2 && parts[0] == xdpModeStrRedirect && parts[1] != "": + xd.Mode = XDPModeRedirect + xd.IfaceName = parts[1] + default: + return fmt.Errorf("invalid --xdp value: %q", input) + } + return nil +} diff --git a/runsc/config/flags.go b/runsc/config/flags.go index f87888d53..76a9ce540 100644 --- a/runsc/config/flags.go +++ b/runsc/config/flags.go @@ -120,9 +120,8 @@ 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", true, "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.") - flagSet.String("EXPERIMENTAL-xdp-redirect-host", "", "EXPERIMENTAL. Use an AF_XDP socket attached to . Use the IP of that interface.") - flagSet.Bool("EXPERIMENTAL-xdp-need-wakeup", true, "EXPERIMENTAL. Use XDP_USE_NEED_WAKEUP with XDP sockets.") + flagSet.Var(&xdpConfig, "EXPERIMENTAL-xdp", `whether and how to use XDP. Can be one of: "off" (default), "ns", or "redirect:"`) + flagSet.Bool("EXPERIMENTAL-xdp-need-wakeup", true, "EXPERIMENTAL. Use XDP_USE_NEED_WAKEUP with XDP sockets.") // TODO(b/240191988): Figure out whether this helps and remove it as a flag. flagSet.Bool("reproduce-nat", false, "Scrape the host netns NAT table and reproduce it in the sandbox.") flagSet.Bool("reproduce-nftables", false, "Attempt to scrape and reproduce nftable rules inside the sandbox. Overrides reproduce-nat when true.") diff --git a/runsc/sandbox/network.go b/runsc/sandbox/network.go index d46797b26..27b121756 100644 --- a/runsc/sandbox/network.go +++ b/runsc/sandbox/network.go @@ -120,11 +120,16 @@ func isRootNS() (bool, error) { // net namespace with the given path, creates them in the sandbox, and removes // them from the host. func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *config.Config) error { - if conf.AFXDPRedirectHost != "" { + switch conf.XDP.Mode { + case config.XDPModeOff: + case config.XDPModeNS: + case config.XDPModeRedirect: if err := createRedirectInterfacesAndRoutes(conn, conf); err != nil { return fmt.Errorf("failed to create XDP redirect interface: %w", err) } return nil + default: + return fmt.Errorf("unknown XDP mode: %v", conf.XDP.Mode) } // Join the network namespace that we will be copying. @@ -250,7 +255,7 @@ func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string, conf *con } } - if conf.AFXDP { + if conf.XDP.Mode == config.XDPModeNS { xdpSockFDs, err := createSocketXDP(iface) if err != nil { return fmt.Errorf("failed to create XDP socket: %v", err) diff --git a/runsc/sandbox/xdp.go b/runsc/sandbox/xdp.go index 9a8607770..7cfe07304 100644 --- a/runsc/sandbox/xdp.go +++ b/runsc/sandbox/xdp.go @@ -157,7 +157,7 @@ func prepareRedirectInterfaceArgs(conf *config.Config) (boot.CreateLinksAndRoute continue } - if iface.Name != conf.AFXDPRedirectHost { + if iface.Name != conf.XDP.IfaceName { log.Infof("Skipping interface %q", iface.Name) continue }