Skip to content

Commit 0360df1

Browse files
sethmlarsonmiss-islington
authored andcommitted
pythongh-122792: Make IPv4-mapped IPv6 address properties consistent with IPv4 (pythonGH-122793)
Make IPv4-mapped IPv6 address properties consistent with IPv4. (cherry picked from commit 76a1c5d) Co-authored-by: Seth Michael Larson <[email protected]>
1 parent 0c5fc27 commit 0360df1

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

Lib/ipaddress.py

+15
Original file line numberDiff line numberDiff line change
@@ -1986,6 +1986,9 @@ def is_multicast(self):
19861986
See RFC 2373 2.7 for details.
19871987
19881988
"""
1989+
ipv4_mapped = self.ipv4_mapped
1990+
if ipv4_mapped is not None:
1991+
return ipv4_mapped.is_multicast
19891992
return self in self._constants._multicast_network
19901993

19911994
@property
@@ -1997,6 +2000,9 @@ def is_reserved(self):
19972000
reserved IPv6 Network ranges.
19982001
19992002
"""
2003+
ipv4_mapped = self.ipv4_mapped
2004+
if ipv4_mapped is not None:
2005+
return ipv4_mapped.is_reserved
20002006
return any(self in x for x in self._constants._reserved_networks)
20012007

20022008
@property
@@ -2007,6 +2013,9 @@ def is_link_local(self):
20072013
A boolean, True if the address is reserved per RFC 4291.
20082014
20092015
"""
2016+
ipv4_mapped = self.ipv4_mapped
2017+
if ipv4_mapped is not None:
2018+
return ipv4_mapped.is_link_local
20102019
return self in self._constants._linklocal_network
20112020

20122021
@property
@@ -2063,6 +2072,9 @@ def is_global(self):
20632072
``is_global`` has value opposite to :attr:`is_private`, except for the ``100.64.0.0/10``
20642073
IPv4 range where they are both ``False``.
20652074
"""
2075+
ipv4_mapped = self.ipv4_mapped
2076+
if ipv4_mapped is not None:
2077+
return ipv4_mapped.is_global
20662078
return not self.is_private
20672079

20682080
@property
@@ -2074,6 +2086,9 @@ def is_unspecified(self):
20742086
RFC 2373 2.5.2.
20752087
20762088
"""
2089+
ipv4_mapped = self.ipv4_mapped
2090+
if ipv4_mapped is not None:
2091+
return ipv4_mapped.is_unspecified
20772092
return self._ip == 0
20782093

20792094
@property

Lib/test/test_ipaddress.py

+24
Original file line numberDiff line numberDiff line change
@@ -2415,6 +2415,30 @@ def testIpv4Mapped(self):
24152415
self.assertEqual(ipaddress.ip_address('::ffff:c0a8:101').ipv4_mapped,
24162416
ipaddress.ip_address('192.168.1.1'))
24172417

2418+
def testIpv4MappedProperties(self):
2419+
# Test that an IPv4 mapped IPv6 address has
2420+
# the same properties as an IPv4 address.
2421+
for addr4 in (
2422+
"178.62.3.251", # global
2423+
"169.254.169.254", # link local
2424+
"127.0.0.1", # loopback
2425+
"224.0.0.1", # multicast
2426+
"192.168.0.1", # private
2427+
"0.0.0.0", # unspecified
2428+
"100.64.0.1", # public and not global
2429+
):
2430+
with self.subTest(addr4):
2431+
ipv4 = ipaddress.IPv4Address(addr4)
2432+
ipv6 = ipaddress.IPv6Address(f"::ffff:{addr4}")
2433+
2434+
self.assertEqual(ipv4.is_global, ipv6.is_global)
2435+
self.assertEqual(ipv4.is_private, ipv6.is_private)
2436+
self.assertEqual(ipv4.is_reserved, ipv6.is_reserved)
2437+
self.assertEqual(ipv4.is_multicast, ipv6.is_multicast)
2438+
self.assertEqual(ipv4.is_unspecified, ipv6.is_unspecified)
2439+
self.assertEqual(ipv4.is_link_local, ipv6.is_link_local)
2440+
self.assertEqual(ipv4.is_loopback, ipv6.is_loopback)
2441+
24182442
def testIpv4MappedPrivateCheck(self):
24192443
self.assertEqual(
24202444
True, ipaddress.ip_address('::ffff:192.168.1.1').is_private)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Changed IPv4-mapped ``ipaddress.IPv6Address`` to consistently use the mapped IPv4
2+
address value for deciding properties. Properties which have their behavior fixed
3+
are ``is_multicast``, ``is_reserved``, ``is_link_local``, ``is_global``, and ``is_unspecified``.

0 commit comments

Comments
 (0)