← 输入框|github.com|刷新
Skip to content

IPv6 address still in duplicate address detection is skipped and never retried #1915

Open

Description

@bdraco

Problem

A link-local IPv6 address that is still in duplicate address detection (DAD, "tentative") cannot be bound. new_socket treats EADDRNOTAVAIL as benign, logs a warning and returns None, so the interface gets no responder socket. add_interface then drops the multicast group membership it had just joined for that interface.

Nothing retries the bind. Once DAD finishes (about a second later) the address is usable, but the address set has not changed, so a caller that reconciles on address changes has no reason to call async_update_interfaces again. The instance stays without IPv6 on that interface until some unrelated address change.

Where

  • new_socket returns None on EADDRNOTAVAIL.
  • add_interface drops the group membership when the responder socket could not be created.

This affects both construction (create_sockets) and async_update_interfaces.

Reproduce

Start a container on an IPv6-enabled Docker network and construct AsyncZeroconf(ip_version=IPVersion.All) as soon as the process starts:

docker network create --ipv6 --subnet fd00:dead:beef::/64 zc6test
docker run --rm --network zc6test <image> python -c "..."

Log output:

WARNING zeroconf: Address not available when binding to ('fe80::943f:67ff:fe01:cbb8', 5353, 0, 11), it is expected to happen on some systems

The instance has an IPv4 sender only. Calling async_update_interfaces() a few seconds later binds the link-local sender.

Seen with 0.151.2 on Linux (arm64, Python 3.13). The same window exists whenever an interface comes up shortly before construction or a reconcile, for example at container or host boot.

Suggested fix

Treat EADDRNOTAVAIL on an IPv6 address as retryable: remember the interfaces skipped for that reason and schedule one short internal retry (a few seconds), for both construction and async_update_interfaces.

Filtering tentative addresses up front is not an option, since ifaddr does not expose IFA_F_TENTATIVE.

Workaround

esphome/device-builder#2864 runs one extra async_update_interfaces ten seconds after startup and after each binding change.

Activity

  1. Sharawey74 commented on Oct 25, 2026

    @Sharawey74

    Hi, I’d like to take this one if you’re not already on it.

    Plan: when new_socket hits EADDRNOTAVAIL for a link-local IPv6 address that’s still tentative (DAD), schedule a short retry of the bind instead of dropping the interface. I’d also keep the multicast membership until the retry gives up, and add a test for it.

    Would you prefer the retry inside the engine, or should the caller be notified so it can call async_update_interfaces again?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions