diff --git a/cmd/spiderpool/cmd/command_add.go b/cmd/spiderpool/cmd/command_add.go index 7525f9b84..f56010337 100644 --- a/cmd/spiderpool/cmd/command_add.go +++ b/cmd/spiderpool/cmd/command_add.go @@ -25,6 +25,7 @@ import ( "github.com/spidernet-io/spiderpool/api/v1/agent/models" "github.com/spidernet-io/spiderpool/pkg/constant" spiderpoolip "github.com/spidernet-io/spiderpool/pkg/ip" + "github.com/spidernet-io/spiderpool/pkg/networking/networking" "github.com/spidernet-io/spiderpool/pkg/openapi" ) @@ -164,8 +165,7 @@ func CmdAdd(args *skel.CmdArgs) (err error) { // do ip conflict and gateway detection logger.Sugar().Info("postIpam response", zap.Any("DNS", ipamResponse.Payload.DNS), - zap.Any("Routes", ipamResponse.Payload.Routes), - zap.Any("Ips", ipamResponse.Payload.Ips)) + zap.Any("Routes", ipamResponse.Payload.Routes)) if err = DetectIPConflictAndGatewayReachable(logger, args.IfName, hostNs, netns, ipamResponse.Payload.Ips); err != nil { if errors.Is(err, constant.ErrIPConflict) || errors.Is(err, constant.ErrGatewayUnreachable) { @@ -179,6 +179,30 @@ func CmdAdd(args *skel.CmdArgs) (err error) { return err } + // CNI will set the interface to up, and the kernel only sends GARPs/Unsolicited NA when the interface + // goes from down to up or when the link-layer address changes on the interfaces. in order to the + // kernel send GARPs/Unsolicited NA when the interface goes from down to up. + // see https://github.com/spidernet-io/spiderpool/issues/4650 + var ipRes []net.IP + for _, i := range ipamResponse.Payload.Ips { + if i.Address != nil && *i.Address != "" { + ipa, _, err := net.ParseCIDR(*i.Address) + if err != nil { + logger.Error(err.Error()) + continue + } + ipRes = append(ipRes, ipa) + } + } + + err = netns.Do(func(netNS ns.NetNS) error { + return networking.AnnounceIPs(logger, args.IfName, ipRes) + }) + + if err != nil { + logger.Error(err.Error()) + } + // Assemble the result of IPAM request response. result, err := assembleResult(conf.CNIVersion, args.IfName, ipamResponse) if err != nil { diff --git a/pkg/networking/networking/packet.go b/pkg/networking/networking/packet.go index bdc3de2dd..dd746ca7a 100644 --- a/pkg/networking/networking/packet.go +++ b/pkg/networking/networking/packet.go @@ -18,6 +18,7 @@ import ( "syscall" "time" + "go.uber.org/zap" "golang.org/x/net/icmp" "golang.org/x/net/ipv6" "golang.org/x/sys/unix" @@ -313,3 +314,39 @@ func ParseIPv6NeighborAdvertisementMsg(n int, buf []byte) (srcIP net.IP, mac net } return } + +func AnnounceIPs(logger *zap.Logger, iface string, ips []net.IP) error { + l, err := netlink.LinkByName(iface) + if err != nil { + return err + } + + for _, addr := range ips { + logger.Debug("announcing ip", zap.String("ip", addr.String()), zap.String("interface", iface)) + if addr.To4() != nil { + // send an gratuitous arp to announce the new mac address + if err = SendARPReuqest(l, addr, addr); err != nil { + logger.Error("failed to send gratuitous arps", zap.Error(err)) + } else { + logger.Info("Send gratuitous arps successfully", zap.String("interface", iface)) + } + } else { + ifi, err := net.InterfaceByName(iface) + if err != nil { + return fmt.Errorf("failed to InterfaceByName %s: %w", iface, err) + } + + ndpClient, _, err := ndp.Listen(ifi, ndp.LinkLocal) + if err != nil { + return fmt.Errorf("failed to init ndp client: %w", err) + } + defer ndpClient.Close() + if err = SendUnsolicitedNeighborAdvertisement(addr, ifi, ndpClient); err != nil { + logger.Error("failed to send unsolicited neighbor advertisements", zap.Error(err)) + } else { + logger.Info("Send unsolicited neighbor advertisements successfully", zap.String("interface", iface)) + } + } + } + return nil +}