Skip to content

Commit

Permalink
Retry Sendto() for IPv6 NA for tentative IPv6 addresses and EADDRNOTA…
Browse files Browse the repository at this point in the history
…VAIL

When kernel initially adds an IPv6 address, it is still tentative and
pending DuplicateAddressDetection. Trying to use the address results in
EADDRNOTAVAIL error.

Detect that error, and retry for up to 3 seconds to send the message.

Unfortunately, DAD can easily take more than a second, so this change
delays the completion of the CNI binary quite a bit.

Also, as we announce IPv4 and IPv6 addresses, with this approach waiting
for IPv6 will also delay IPv4 addresses. This problem might be solved by
announcing the addresses in parallel. But given the long wait duration,
maybe the better approach would be to enable opportunistic DAD on the
interface.
  • Loading branch information
thom311 committed Jul 30, 2024
1 parent 16795cd commit d6ec1df
Showing 1 changed file with 29 additions and 5 deletions.
34 changes: 29 additions & 5 deletions pkg/utils/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,36 @@ func SendUnsolicitedNeighborAdvertisement(srcIP net.IP, linkObj netlink.Link) er
// Set the destination IPv6 address to the IPv6 link-local all nodes multicast address (ff02::1).
var r [16]byte
copy(r[:], net.IPv6linklocalallnodes.To16())
sockAddr := syscall.SockaddrInet6{Addr: r}
if err := syscall.Sendto(soc, icmpv6Bytes, 0, &sockAddr); err != nil {
return fmt.Errorf("failed to send Unsolicited Neighbor Advertisement for IPv6 %s on Interface %s: %v", srcIP.String(), linkObj.Attrs().Name, err)
}
destSockAddr := syscall.SockaddrInet6{Addr: r}

return nil
var nextSleepDuration = 10 * time.Millisecond
var retryTime = 3000 * time.Millisecond
var start = time.Now()

for {
err2 := syscall.Sendto(soc, icmpv6Bytes, 0, &destSockAddr)

if err2 == nil {
return nil
}

errno, ok := err2.(syscall.Errno)
if ok && errno == syscall.EADDRNOTAVAIL {
// Assume the error happened because the IPv6 source address is
// still tentative. Retry?
if time.Since(start) < retryTime {
time.Sleep(nextSleepDuration)
/* Grow wait time exponentially (factor 1.5). */
nextSleepDuration += nextSleepDuration / 2
if nextSleepDuration > 100*time.Millisecond {
nextSleepDuration = 100 * time.Millisecond
}
continue
}
}

return fmt.Errorf("failed to send Unsolicited Neighbor Advertisement for IPv6 %s on Interface %s: %v", srcIP.String(), linkObj.Attrs().Name, err2)
}
}

// AnnounceIPs sends either a GARP or Unsolicited NA depending on the IP address type (IPv4 vs. IPv6 respectively) configured on the interface.
Expand Down

0 comments on commit d6ec1df

Please sign in to comment.