Repository navigation
v0.3.4
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 APIPA169.254/16address 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::1is 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, andloopback_ok=False
skips the loopback rank rather than returningNone.Measured on a macOS loopback adapter (
127.0.0.1/8,::1/128,fe80::1/64):
primary_ip(ipv6=True)returnedfe80::1, andbind(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 samefe80::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))EINVALbind(("fe80::1%2", 0))EINVALbind(("fe80::1%eth0", 0))EINVALbind(("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%zonesuffix -- 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
%indexin the string, copying whatUdpEndpoint.reply_socketdoes -- 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