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 dc9b2555a875 ("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:

1. 22c31764262b ("#17400; ipaddress should make it easy to identify rfc6598 addresses")
3. be9c1b133b78 ("#17400: fix documentation, add cache to is_global and correctly handle 100.64.0.0/10")
4. e5019d518304 ("#17400: correct handling of 100.64.0.0/10, fixing the docs and updating NEWS")
5. 742192a4fea8 ("Issue #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 83f0f8d62f27 ("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:

https://github.com/python/cpython/issues/61602#issuecomment-1093610325

> 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

<!-- gh-linked-prs -->
### Linked PRs
* gh-119813
<!-- /gh-linked-prs -->

ipaddress: Shared Address Space (100.64.0.0/10) is neither private nor global
This is a GitHub Issue
repo:cpython
owner:python
Title : ipaddress: Shared Address Space (100.64.0.0/10) is neither private nor global
Issue date:

--- start body ---
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 dc9b2555a875 ("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:

1. 22c31764262b ("#17400; ipaddress should make it easy to identify rfc6598 addresses")
3. be9c1b133b78 ("#17400: fix documentation, add cache to is_global and correctly handle 100.64.0.0/10")
4. e5019d518304 ("#17400: correct handling of 100.64.0.0/10, fixing the docs and updating NEWS")
5. 742192a4fea8 ("Issue #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 83f0f8d62f27 ("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:

https://github.com/python/cpython/issues/61602#issuecomment-1093610325

> 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

<!-- gh-linked-prs -->
### Linked PRs
* gh-119813
<!-- /gh-linked-prs -->

--- end body ---
