Skip to content

Commit 8a5c94f

Browse files
author
opavliuk
committed
bpo-34788 Add support for scoped IPv6 addresses
Support for scoped IPv6 addresses (including network and interface addresses) is required according to RFC 4007 (https://tools.ietf.org/html/rfc4007).
1 parent eb4a407 commit 8a5c94f

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

Lib/ipaddress.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,30 @@ def _reverse_pointer(self):
17851785
reverse_chars = self.exploded[::-1].replace(':', '')
17861786
return '.'.join(reverse_chars) + '.ip6.arpa'
17871787

1788+
@staticmethod
1789+
def _split_scope_id(ip_str):
1790+
"""Helper function to parse IPv6 string address with scope id.
1791+
1792+
See RFC 4007 for details.
1793+
1794+
Arg:
1795+
ip_str: A string, the IPv6 address.
1796+
1797+
Returns:
1798+
[addr, scope_id] list.
1799+
"""
1800+
if '%' not in ip_str:
1801+
return ip_str, None
1802+
1803+
split_addr = ip_str.split('%')
1804+
if len(split_addr) > 2 or split_addr[-1] == '':
1805+
raise AddressValueError('Invalid IPv6 address: "%r"' % ip_str)
1806+
try:
1807+
addr, scope_id = split_addr
1808+
except ValueError:
1809+
return split_addr, None
1810+
return addr, scope_id
1811+
17881812
@property
17891813
def max_prefixlen(self):
17901814
return self._max_prefixlen
@@ -1798,7 +1822,7 @@ class IPv6Address(_BaseV6, _BaseAddress):
17981822

17991823
"""Represent and manipulate single IPv6 Addresses."""
18001824

1801-
__slots__ = ('_ip', '__weakref__')
1825+
__slots__ = ('_ip', 'scope_id', '__weakref__')
18021826

18031827
def __init__(self, address):
18041828
"""Instantiate a new IPv6 address object.
@@ -1817,6 +1841,8 @@ def __init__(self, address):
18171841
AddressValueError: If address isn't a valid IPv6 address.
18181842
18191843
"""
1844+
self.scope_id = None
1845+
18201846
# Efficient constructor from integer.
18211847
if isinstance(address, int):
18221848
self._check_int_address(address)
@@ -1829,13 +1855,20 @@ def __init__(self, address):
18291855
self._ip = int.from_bytes(address, 'big')
18301856
return
18311857

1858+
18321859
# Assume input argument to be string or any object representation
18331860
# which converts into a formatted IP string.
18341861
addr_str = str(address)
18351862
if '/' in addr_str:
18361863
raise AddressValueError("Unexpected '/' in %r" % address)
1864+
addr_str, self.scope_id = self._split_scope_id(addr_str)
1865+
18371866
self._ip = self._ip_int_from_string(addr_str)
18381867

1868+
def __str__(self):
1869+
ip_str = self._string_from_ip_int(self._ip)
1870+
return ip_str if self.scope_id is None else ip_str + '%' + self.scope_id
1871+
18391872
@property
18401873
def packed(self):
18411874
"""The binary representation of this address."""
@@ -1973,7 +2006,6 @@ def sixtofour(self):
19732006
return None
19742007
return IPv4Address((self._ip >> 80) & 0xFFFFFFFF)
19752008

1976-
19772009
class IPv6Interface(IPv6Address):
19782010

19792011
def __init__(self, address):
@@ -1989,7 +2021,7 @@ def hostmask(self):
19892021
return self.network.hostmask
19902022

19912023
def __str__(self):
1992-
return '%s/%d' % (self._string_from_ip_int(self._ip),
2024+
return '%s/%d' % (super().__str__(),
19932025
self._prefixlen)
19942026

19952027
def __eq__(self, other):

0 commit comments

Comments
 (0)