Skip to content

v0.3.4

Choose a tag to compare

@github-actions github-actions released this 03 Oct 07:34

Fixed

  • Interface.primary_ip() preferred a link-local address over a routable
    one.
    It returned the first entry that was not loopback, and an interface
    commonly lists its link-local address first -- fe80:: is configured before
    SLAAC or DHCPv6 completes on Linux and macOS NICs -- so a NIC with both
    answered with an address that is useless as a bind target and unreachable
    off-link. The IPv4 twin was real too and is fixed by the same change: an
    interface holding an APIPA 169.254/16 address and a DHCP lease answered
    with the APIPA one.

    Now ranked routable, then loopback, then link-local, keeping OS order
    within a rank. Loopback outranks link-local deliberately: the only interface
    carrying both is the loopback adapter, where ::1 is what every caller means,
    and a real NIC has no loopback entry so the rank takes nothing from it. A NIC
    holding only a link-local address still yields it, and loopback_ok=False
    skips the loopback rank rather than returning None.

    Measured on a macOS loopback adapter (127.0.0.1/8, ::1/128, fe80::1/64):
    primary_ip(ipv6=True) returned fe80::1, and bind(interface=..., family=AF_INET6) then failed with "Can't assign requested address".

  • bind(interface=...) dropped the scope of a link-local address, and a
    link-local bind then failed on every POSIX platform -- not only macOS, as
    first reported. The same fe80:: address can exist on several adapters, so
    the kernel cannot tell which is meant. Measured on Linux against a real NIC:

    spelling result
    bind(("fe80::1", 0)) EINVAL
    bind(("fe80::1%2", 0)) EINVAL
    bind(("fe80::1%eth0", 0)) EINVAL
    bind(("fe80::1", 0, 0, 2)) ok

    So the zone has to become the sockaddr's numeric scope id; it cannot stay in
    the address string.
    bind() now converts a %zone suffix -- name or index
    -- into the 4-tuple form, which also means a caller writing
    bind("fe80::1%eth0", ...) by hand gets a working bind for the first time.

    Windows accepts all four spellings, which is why a Windows-only measurement
    says nothing about this and why the first attempt at the fix -- putting
    %index in the string, copying what UdpEndpoint.reply_socket does -- passed
    locally and broke all six Linux CI jobs and both macOS jobs. reply_socket's
    own link-local path went through the same string form and is fixed by the same
    change, since both now bind through one helper.

Full Changelog: v0.3.3...v0.3.4