diff --git a/pkg/agent/netns_linux.go b/pkg/agent/netns_linux.go index b1d97b0..3daddab 100644 --- a/pkg/agent/netns_linux.go +++ b/pkg/agent/netns_linux.go @@ -241,16 +241,18 @@ func configurePodSide(req SetupRequest) error { } } - // Anycast: assign each IP to pod lo. NOT on eth0 (avoids NDP/ARP - // DAD conflicts when multiple replicas share the same IP). + // Anycast: assign each IP to pod eth0 (NOT lo). + // + // The original design doc proposed lo to avoid NDP/ARP DAD + // conflicts "across nodes advertising the same IP". That concern + // doesn't apply to flock: each pod's veth is its own private /64, + // so DAD on eth0 only sees the veth peer (host) — no cross-node + // L2 contention. Putting the IP on eth0 instead means the pod + // kernel answers NDP solicits arriving on eth0 for that IP, which + // is what the host's /128 host route requires. With anycast on + // lo, NDP from the host side fails and the kernel drops the + // packet between routing decision and transmit. if len(req.Anycast) > 0 { - lo, err := netlink.LinkByName("lo") - if err != nil { - return fmt.Errorf("lookup pod lo: %w", err) - } - if err := netlink.LinkSetUp(lo); err != nil { - return fmt.Errorf("set up pod lo: %w", err) - } for _, ip := range req.Anycast { var mask net.IPMask if ip.To4() != nil { @@ -260,8 +262,8 @@ func configurePodSide(req SetupRequest) error { mask = net.CIDRMask(128, 128) } a := &netlink.Addr{IPNet: &net.IPNet{IP: ip, Mask: mask}, Scope: int(netlink.SCOPE_UNIVERSE)} - if err := netlink.AddrAdd(lo, a); err != nil && !errors.Is(err, os.ErrExist) { - return fmt.Errorf("pod lo anycast %s: %w", ip, err) + if err := netlink.AddrAdd(eth0, a); err != nil && !errors.Is(err, os.ErrExist) { + return fmt.Errorf("pod eth0 anycast %s: %w", ip, err) } } }