Skip to content

Commit

Permalink
Make ipv4_to_ipv6 output "human-readable" addrs
Browse files Browse the repository at this point in the history
The requirements keep changing back and forth, but apparently, it
was still important that the resulting IPv6 address was somewhat
human-readably similar to the original IPv4 address.

Also, it isn't always necessary to keep the full IPv4 address inside
the IPv6 address. Mostly, the host address from the IPv4 subnetwork
should be readable as the same on the corresponding IPV6 subnetwork,
so an option to keep only a number of IPv4 octets in the result was
added.
  • Loading branch information
lunkwill42 committed Sep 14, 2022
1 parent 88e6270 commit 14f8734
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 15 deletions.
32 changes: 20 additions & 12 deletions src/cnaas_nms/tools/jinja_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,29 +62,37 @@ def isofy_ipv4(ip_string, prefix=''):


def ipv4_to_ipv6(
v4_address: Union[str, ipaddress.IPv4Interface], v6_network: Union[str, ipaddress.IPv6Network]
v4_address: Union[str, ipaddress.IPv4Interface],
v6_network: Union[str, ipaddress.IPv6Network],
keep_octets: int = 1,
):
"""Transforms an IPv4 address to an IPv6 interface address. This will combine an arbitrary
IPv6 network address with the 32 address bytes of an IPv4 address into a valid IPv6 address
+ prefix length notation - the equivalent of dotted quad compatible notation.
"""Transforms an IPv4 address intoto an IPv6 interface address that should be more-or-less
identifiable as the same device on a different protocol family.
E.g.:
>>> ipv6 = ipv4_to_ipv6("127.0.0.1", "2001:700:dead:babe::/64")
>>> ipv6
IPv6Interface('2001:700:dead:babe::7f00:1/64')
>>> ipv6 == ipaddress.IPv6Interface('2001:700:dead:babe::127.0.0.1/64')
True
A selected number of octets, right-to-left, of the IPv4 address will be converted to
hexadecimal notation that can be read as the decimal IPv4 address. This will then be used as
a host address on the provided IPv6 network.
Examples:
>>> ipv4_to_ipv6("10.1.0.42", "2001:700:dead:babe::/64")
IPv6Interface('2001:700:dead:babe::42/64')
>>> ipv4_to_ipv6("10.1.0.42", "2001:700:dead:babe::/64", keep_octets=4)
IPv6Interface('2001:700:dead:babe:10:1:0:42/64')
Args:
v6_network: IPv6 network in prefix notation
v4_address: IPv4 address
v6_network: IPv6 network in prefix notation
keep_octets: The number of octets to keep from the IPv4 address (from right-to-left)
Returns:
An IPv6Address object on the given network
"""
if isinstance(v6_network, str):
v6_network = ipaddress.IPv6Network(v6_network)
if isinstance(v4_address, str):
v4_address = ipaddress.IPv4Address(v4_address)
assert keep_octets > 0 <= 4

v6_address = v6_network[int(v4_address)]
octets = str(v4_address).split('.')[-keep_octets:]
v6ified = ipaddress.IPv6Address("::" + ":".join(octets))
v6_address = v6_network[int(v6ified)]
return ipaddress.IPv6Interface(f"{v6_address}/{v6_network.prefixlen}")
6 changes: 3 additions & 3 deletions src/cnaas_nms/tools/tests/test_jinja_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,13 @@ class IPv6ToIPv4Tests(unittest.TestCase):
def test_should_convert_short_network_properly(self):
self.assertEqual(
ipv4_to_ipv6('10.0.0.1', '2001:700::/64'),
ipaddress.IPv6Interface('2001:700::10.0.0.1/64'),
ipaddress.IPv6Interface('2001:700::1/64'),
)

def test_should_convert_long_network_properly(self):
self.assertEqual(
ipv4_to_ipv6('10.0.0.1', '2001:700:dead:c0de:babe::/80'),
ipaddress.IPv6Interface('2001:700:dead:c0de:babe::10.0.0.1/80'),
ipv4_to_ipv6('10.0.0.1', '2001:700:dead:c0de::/64', keep_octets=4),
ipaddress.IPv6Interface('2001:700:dead:c0de:10:0:0:1/64'),
)

def test_should_raise_on_invalid_network(self):
Expand Down

0 comments on commit 14f8734

Please sign in to comment.