ipaddress: Shared Address Space (100.64.0.0/10) is neither private nor global #119812
Description
Something I discovered when working on GH-65056 and GH-113171.
IPv4Address
' and IPv4Network
's is_global
and is_private
are false at the same time for addresses in this range:
>>> ipaddress.IPv4Address('100.64.0.0').is_global
False
>>> ipaddress.IPv4Address('100.64.0.0').is_private
False
>>> ipaddress.IPv4Network('100.64.0.0/10').is_global
False
>>> ipaddress.IPv4Network('100.64.0.0/10').is_private
False
I don't believe this is right and I'll explain why.
Historical context
Initial additions
is_private
introduced in dc9b255 ("Issue #14814: addition of the ipaddress module (stage 1 - code and tests)") on 2012-05-20.
It only handled the actual private-use ranges for IPv4 (10.0.0.0/8
, 172.16.0.0/12
, 192.168.0.0/16
).
Documented as such.
100.64.0.0/10
handling and is_private
semantics changes
There have been four ipaddress
patches involving this range:
- 22c3176 ("bpo-26730: Fix SpooledTemporaryFile data corruption #17400; ipaddress should make it easy to identify rfc6598 addresses")
- be9c1b1 ("bpo-26730: Fix SpooledTemporaryFile data corruption #17400: fix documentation, add cache to is_global and correctly handle 100.64.0.0/10")
- e5019d5 ("bpo-26730: Fix SpooledTemporaryFile data corruption #17400: correct handling of 100.64.0.0/10, fixing the docs and updating NEWS")
- 742192a ("Issue bpo-41205: Document Decimal power 0 to the 0 #21386: Implement missing IPv4Address.is_global property")
The first three were part of GH-61602 (https://bugs.python.org/issue17400), the fourth one just adds a missing IPv4Address.is_global
property to match the IPv4Network.is_global
semantics and follows the semantics established in commits 1-3.
Commit 1 changed the semantics of is_private
from "Is this a Private-use IPv4 address or a Unique-Local IPv6 address?" to roughly "Do [1] and [2] say globally reachable = false for this address". The documentation change was not quite precise
A boolean, True if the address is reserved per
iana-ipv4-special-registry or iana-ipv6-special-registry.
but the intent of the implementation is quite clear.
Commit 1 also added is_global
that was effectively not is_private
.
In commit 3 an exception for 100.64.0.0/10
is made and both is_global
and is_private
are false for that range.
is_global
/is_private
semantics clarification
As part of GH-65056 we changed the documentation to make it clear that we follow the "globally reachable" information from IANA (with some caveats, like special handling of IPv4-mapped IPv6 addresses, see 83f0f8d ("bpo-33433 Fix private address checking for IPv4 mapped IPv6. (GH-26172)")).
The problem
The motivation for handling 100.64.0.0/10
like this can be found here:
The rationale for RFC 6598 is precisely that 100.64.0.0/10 is not private in the common sense, so it would deserve a different treatment in the ipaddress module as well.
I have to admit I don't find it convincing enough to make an exception for it.
[1] says the range is not globally reachable so in my opinion is_private
should return true for it as it does for the rest of the not globally reachable address blocks (again, with the exception of IPv4-mapped IPv6 address handling).
I'd find is_private
being false for the range surprising if I wasn't clearly aware of the semantics after reading this code multiple times. I believe it may lead to real-world issues.
Additionally the behavior where is_private
is not the opposite of is_global
is likely to be surprising too.
In short: IMO there should be no exception.
The downside of the proposed solution: this is technically breaking backwards compatibility if some code depends on the current semantics. I'm not sure I'd classify the proposed change strictly as a bug fix.
[1] https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
[2] https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml