From ca6eccc389f2c9e1e9ab10615088bf116f3aad9b Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 30 Jul 2024 14:24:31 +0200 Subject: [PATCH] Announce IPv4 addresses (GARP) before IPv6 NA To announce IPv6 addresses, SendUnsolicitedNeighborAdvertisement() possibly needs to wait for tentative IPv6 addresses to become usable (DAD). This can take more than a second. Instead of looping over IPv4 and IPv6 addresses together, split the loop. Iterate first over IPv4 addresses, which potentially can complete fast without waiting. While at it, don't let the first error shortcut the announcement of future addresses. Loop over all addresses, but return the first error we encountered. The caller anyway ignores the error and only uses it for debug logging. --- pkg/utils/packet.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/pkg/utils/packet.go b/pkg/utils/packet.go index f3a2e659..4cec18c9 100644 --- a/pkg/utils/packet.go +++ b/pkg/utils/packet.go @@ -197,28 +197,36 @@ func AnnounceIPs(ifName string, ipConfigs []*current.IPConfig) error { return fmt.Errorf("invalid Ethernet MAC address: %q", linkObj.Attrs().HardwareAddr) } + var errResult error + retryUntil := time.Now().Add(3000 * time.Millisecond) // For all the IP addresses assigned by IPAM, we will send either a GARP (IPv4) or Unsolicited NA (IPv6). + // + // Note that SendUnsolicitedNeighborAdvertisement() can wait for tentative + // IPv6 addresses. We thus first iterate over the IPv4 addresses to not + // delay announcing those. + for _, ipc := range ipConfigs { + if IsIPv4(ipc.Address.IP) { + err := SendGratuitousArp(ipc.Address.IP, linkObj) + if err != nil && errResult == nil { + errResult = fmt.Errorf("failed to send GARP message for ip %s on interface %q: %v", ipc.Address.IP.String(), ifName, err) + } + } + } for _, ipc := range ipConfigs { - var err error if IsIPv6(ipc.Address.IP) { /* As per RFC 4861, sending unsolicited neighbor advertisements should be considered as a performance * optimization. It does not reliably update caches in all nodes. The Neighbor Unreachability Detection * algorithm is more reliable although it may take slightly longer to update. */ - err = SendUnsolicitedNeighborAdvertisement(ipc.Address.IP, linkObj, retryUntil) - } else if IsIPv4(ipc.Address.IP) { - err = SendGratuitousArp(ipc.Address.IP, linkObj) - } else { - return fmt.Errorf("the IP %s on interface %q is neither IPv4 or IPv6", ipc.Address.IP.String(), ifName) - } - - if err != nil { - return fmt.Errorf("failed to send GARP/NA message for ip %s on interface %q: %v", ipc.Address.IP.String(), ifName, err) + err := SendUnsolicitedNeighborAdvertisement(ipc.Address.IP, linkObj, retryUntil) + if err != nil && errResult == nil { + errResult = fmt.Errorf("failed to send NA message for ip %s on interface %q: %v", ipc.Address.IP.String(), ifName, err) + } } } - return nil + return errResult } // Blocking wait for interface ifName to have carrier (!NO_CARRIER flag).