From cd84e47f6d99cbac839e4494db0b0011bf528450 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyevsky Date: Tue, 16 Jun 2026 13:42:23 -0700 Subject: [PATCH] [cli] drive the AF_XDP node path through the vtep.Datapath seam (APO-795) The cli built the forwarder and called forwarder.Start directly, so the per-node VTEP was the one consumer still bypassing the vtep.Datapath seam that the tun and netstack drivers already run behind. Wrap the forwarder with afxdp.Wrap and drive dp.Run -- the last open APO-795 sub-item. afxdp.Datapath is a pure lifecycle adapter: Run delegates to forwarder.Start (which self-closes on return) and Close to the idempotent forwarder.Close, so the in-place shared-UMEM hot loop and minInPlaceHeadroom are untouched and the node data path stays byte-identical (guarded by forwarder_rxheadroom_test + cp_wire_test). The forwarder is still constructed via forwarder.NewForwarder, exactly as afxdp.Wrap documents. --- cli/main.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/cli/main.go b/cli/main.go index 7fe3483..adea7ae 100644 --- a/cli/main.go +++ b/cli/main.go @@ -35,6 +35,7 @@ import ( "github.com/apoxy-dev/icx/permissions" "github.com/apoxy-dev/icx/queues" "github.com/apoxy-dev/icx/veth" + "github.com/apoxy-dev/icx/vtep/afxdp" ) func main() { @@ -441,17 +442,24 @@ func run(c *cli.Context) error { if err != nil { return fmt.Errorf("failed to create forwarder: %w", err) } + // Drive the AF_XDP forwarder through the vtep.Datapath seam rather than calling + // forwarder.Start directly: the per-node VTEP is just one Datapath shape (afxdp), + // and routing its lifecycle through the seam lets the other drivers (tun, netstack) + // reuse the same engine/control wiring. afxdp.Wrap takes ownership of the forwarder + // and its in-place, shared-UMEM hot loop unchanged — Run delegates to Start (which + // self-closes the forwarder on return), so the node data path stays byte-identical. + dp := afxdp.Wrap(fwd) - // Run the rekey loop and the forwarder under one errgroup sharing a cancel: a signal - // (ctx) or a forwarder error stops both. The control plane reconnects indefinitely on + // Run the rekey loop and the datapath under one errgroup sharing a cancel: a signal + // (ctx) or a datapath error stops both. The control plane reconnects indefinitely on // its own (Tunnel.Run does not return on CP failures), so it does not abort the - // forwarder; if it cannot re-establish, the data plane fails closed when the installed + // datapath; if it cannot re-establish, the data plane fails closed when the installed // keys expire (see key lifetime below). g, gctx := errgroup.WithContext(ctx) g.Go(func() error { return tun.Run(gctx) }) g.Go(func() error { - if err := fwd.Start(gctx); err != nil && !errors.Is(err, context.Canceled) { - return fmt.Errorf("forwarder: %w", err) + if err := dp.Run(gctx); err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("datapath: %w", err) } return nil })