diff --git a/pkg/agent/cniserver/interface_configuration_linux.go b/pkg/agent/cniserver/interface_configuration_linux.go index d165876bbb9..4bb8c8ed020 100644 --- a/pkg/agent/cniserver/interface_configuration_linux.go +++ b/pkg/agent/cniserver/interface_configuration_linux.go @@ -34,6 +34,7 @@ import ( "antrea.io/antrea/pkg/agent/util" "antrea.io/antrea/pkg/agent/util/arping" "antrea.io/antrea/pkg/agent/util/ethtool" + "antrea.io/antrea/pkg/agent/util/ndp" cnipb "antrea.io/antrea/pkg/apis/cni/v1beta1" "antrea.io/antrea/pkg/ovs/ovsconfig" ) @@ -301,24 +302,33 @@ func (ic *ifConfigurator) advertiseContainerAddr(containerNetNS string, containe klog.Errorf("Failed to find container interface %s in ns %s: %v", containerIfaceName, containerNetNS, err) return nil } - var targetIP net.IP + var targetIPv4, targetIPv6 net.IP for _, ipc := range result.IPs { if ipc.Version == "4" { - targetIP = ipc.Address.IP + targetIPv4 = ipc.Address.IP + } else if ipc.Version == "6" { + targetIPv6 = ipc.Address.IP } } - if targetIP == nil { - klog.V(2).Infof("No IPv4 address found for container interface %s in ns %s, skip sending Gratuitous ARP", containerIfaceName, containerNetNS) + if targetIPv4 == nil && targetIPv6 == nil { + klog.V(2).Infof("No IPv4 and IPv6 address found for container interface %s in ns %s, skip sending Gratuitous ARP/NDP", containerIfaceName, containerNetNS) return nil } ticker := time.NewTicker(50 * time.Millisecond) defer ticker.Stop() count := 0 for { - // Send gratuitous ARP to network in case of stale mappings for this IP address + // Send gratuitous ARP/NDP to network in case of stale mappings for this IP address // (e.g. if a previous - deleted - Pod was using the same IP). - if err := arping.GratuitousARPOverIface(targetIP, iface); err != nil { - klog.Warningf("Failed to send gratuitous ARP #%d: %v", count, err) + if targetIPv4 != nil { + if err := arping.GratuitousARPOverIface(targetIPv4, iface); err != nil { + klog.Warningf("Failed to send gratuitous ARP #%d: %v", count, err) + } + } + if targetIPv6 != nil { + if err := ndp.GratuitousNDPOverIface(targetIPv6, iface); err != nil { + klog.Warningf("Failed to send gratuitous NDP #%d: %v", count, err) + } } count++ if count == 3 { diff --git a/pkg/agent/util/ndp/ndp.go b/pkg/agent/util/ndp/ndp.go new file mode 100644 index 00000000000..c73270bc23e --- /dev/null +++ b/pkg/agent/util/ndp/ndp.go @@ -0,0 +1,46 @@ +// Copyright 2022 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ndp + +import ( + "fmt" + "net" + + "github.com/mdlayher/ndp" +) + +// GratuitousNDPOverIface sends a gratuitous NDP from 'iface' using 'srcIP' as the source IP. +func GratuitousNDPOverIface(srcIP net.IP, iface *net.Interface) error { + if srcIP.To4() != nil { + return fmt.Errorf("IPv4 is not supported") + } + + conn, _, err := ndp.Listen(iface, ndp.LinkLocal) + if err != nil { + return fmt.Errorf("failed to create NDP responder for %q: %s", iface.Name, err) + } + + na := &ndp.NeighborAdvertisement{ + Override: true, + TargetAddress: srcIP, + Options: []ndp.Option{ + &ndp.LinkLayerAddress{ + Direction: ndp.Target, + Addr: iface.HardwareAddr, + }, + }, + } + return conn.WriteTo(na, nil, net.IPv6linklocalallnodes) +}