IPv4Networks have a network address, and a broadcast address, as defined in IETF RFC 1122. The address with all host bits set to 0 is the network address, and the address with all host bits set to 1 is the broadcast address.
IPv6Networks do not. The all 0’s address is defined in RFC 2526/4291 as the “Subnet-Router Anycast” address, and the all 1’s address has no special meaning and unlike in IPv4 could be used for a host.
I therefore proposed.
Replace the network_address and broadcast_address properties on the general _baseNetwork class with more generically named functions, first_address and last_address.
Note: There might be some confusion here between the first address in a network and the first “usable” address in a network, which in both the IPv4 and IPv6 cases does not include the all 0’s address. Similarly for last for IPv4.
Have methods on the IPv4Network class named network_address and broadcast_address, which are just aliases to first_address and last_address.
Have a method on the IPv6Network class named subnet_router_anycast_address which is an alias to first_address.
I propose deprecating the use for network_address and broadcast_address on IPv6Networks with a deprecation warning, but continuing to return the first_address and last_address as appropriate, as there may well be users using broadcast_address on a IPv6Network to get the last address in a subnet.
I propose behaviour like this.
# New Methods
$ python3.14 -c 'import ipaddress; print(ipaddress.ip_network("2001:db8::/48").first_address)'
2001:db8::
$ python3.14 -c 'import ipaddress; print(ipaddress.ip_network("2001:db8::/48").last_address)'
2001:db8:0:ffff:ffff:ffff:ffff:ffff
# Deprecated Methods
$ python3.14 -c 'import ipaddress; print(ipaddress.ip_network("2001:db8::/48").network_address)'
/usr/local/lib/python3.14/functools.py:1124: DeprecationWarning: IPv6 has no network addresses, consider using first_address or subnet_router_anycast_address instead.
2001:db8::
$ python3.14 -c 'import ipaddress; print(ipaddress.ip_network("2001:db8::/48").broadcast_address)'
/usr/local/lib/python3.14/functools.py:1124: DeprecationWarning: IPv6 has no broadcast addresses, consider using last_address instead.
2001:db8:0:ffff:ffff:ffff:ffff:ffff
Agreed that the terminology doesn’t really line up with the realities of IPv6 networks.
I think adding subnet_router_anycast_address and first/last_address makes sense.
I’m not convinced we should add the DeprecationWarning though.
We’re not likely to actually remove the attributes anytime soon as they come from the base class and it could thus prove be disruptive to existing code. I’d start with just noting this in the documentation and recommending people running a recent version of python use the new first and last attributes. We can revisit if it is worth following through with a deprecation period on them in the future.
Confusion around first and last being start and end of the range vs the first and last usable seems like more of a documentation issue. bikeshedding: Alternative names like address_range_start and address_range_end could be used - would that be meaningfully better?
I’m not overly bothered about Deprecation, but my plan was to move network_address and broadcast_address out of the base class and into the IPv4Network and IPv6Network classes.
You use them on an IPv4Network, they work as they do now (by just calling first_address / last_address in the base class.
You use them on an IPv6Network, they work as they do now, also by calling first_address / last_address from the base class but with the deprecation warning letting you know your using “wrong” terminology, but they could continue to return what they do now forever, which your right, doesn’t seem to be a great candidate for deprecation.
As per RFC3021, /31 networks have no directed broadcast address, and they only have two hosts.
So I think a /31 IPv4Network should
Generate some kind of warning when asked for a .broadcast_address, but still return the value (for backwards compatibility). Eventually this might become an exception.
.first_address == .first_host_address, where normally .first_host_address == .first_address + 1
.last_address == .last_host_address, where normally .last_host_address == last_address - 1`
And for /32 IPv4Networks, .first_address == .first_host_address == .last_address == .last_host_address. Asking for the .broadcast_address should be treated the same as for a /31.
Since subnet_router_anycast_address() is new functionality, I propose returning None straight away, as asking for the subnet_router_anycast_address of a IPv6 /127 (or /128) is “meaningless”.