New method in netutils: get_mac_addr_by_ipv6
This method is practically the reverse of get_ipv6_addr_by_EUI64(), so we can extract the MAC address from IPv6 addresses that were generated from interface identifiers. Change-Id: I48720d38649104f9f2f0a8fd208f2aac7548644e Related-Change: https://review.opendev.org/718729
This commit is contained in:
		@@ -193,6 +193,35 @@ def get_ipv6_addr_by_EUI64(prefix, mac):
 | 
			
		||||
                          'EUI-64: %s') % prefix)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_mac_addr_by_ipv6(ipv6, dialect=netaddr.mac_unix_expanded):
 | 
			
		||||
    """Extract MAC address from interface identifier based IPv6 address.
 | 
			
		||||
 | 
			
		||||
    For example from link-local addresses (fe80::/10) generated from MAC.
 | 
			
		||||
 | 
			
		||||
    :param ipv6: An interface identifier (i.e. mostly MAC) based IPv6
 | 
			
		||||
                 address as a netaddr.IPAddress() object.
 | 
			
		||||
    :param dialect: The netaddr dialect of the the object returned.
 | 
			
		||||
                    Defaults to netaddr.mac_unix_expanded.
 | 
			
		||||
    :returns: A MAC address as a netaddr.EUI() object.
 | 
			
		||||
 | 
			
		||||
    See also:
 | 
			
		||||
    * https://tools.ietf.org/html/rfc4291#appendix-A
 | 
			
		||||
    * https://tools.ietf.org/html/rfc4291#section-2.5.6
 | 
			
		||||
 | 
			
		||||
    .. versionadded:: 4.3.0
 | 
			
		||||
    """
 | 
			
		||||
    return netaddr.EUI(int(
 | 
			
		||||
        # out of the lowest 8 bytes (byte positions 8-1)
 | 
			
		||||
        # delete the middle 2 bytes (5-4, 0xff_fe)
 | 
			
		||||
        # by shifting the highest 3 bytes to the right by 2 bytes (8-6 -> 6-4)
 | 
			
		||||
        (((ipv6 & 0xff_ff_ff_00_00_00_00_00) >> 16) +
 | 
			
		||||
         # adding the lowest 3 bytes as they are (3-1)
 | 
			
		||||
         (ipv6 & 0xff_ff_ff)) ^
 | 
			
		||||
        # then invert the universal/local bit
 | 
			
		||||
        0x02_00_00_00_00_00),
 | 
			
		||||
        dialect=dialect)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def is_ipv6_enabled():
 | 
			
		||||
    """Check if IPv6 support is enabled on the platform.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import contextlib
 | 
			
		||||
import socket
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
import netaddr
 | 
			
		||||
import netifaces
 | 
			
		||||
from oslotest import base as test_base
 | 
			
		||||
import six
 | 
			
		||||
@@ -357,6 +358,38 @@ class IPv6byEUI64TestCase(test_base.BaseTestCase):
 | 
			
		||||
                          netutils.get_ipv6_addr_by_EUI64(prefix, mac))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MACbyIPv6TestCase(test_base.BaseTestCase):
 | 
			
		||||
    """Unit tests to extract MAC from IPv6."""
 | 
			
		||||
 | 
			
		||||
    def test_reverse_generate_IPv6_by_EUI64(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            netaddr.EUI('00:16:3e:33:44:55'),
 | 
			
		||||
            netutils.get_mac_addr_by_ipv6(
 | 
			
		||||
                netaddr.IPAddress('2001:db8::216:3eff:fe33:4455')),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_random_qemu_mac(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            netaddr.EUI('52:54:00:42:02:19'),
 | 
			
		||||
            netutils.get_mac_addr_by_ipv6(
 | 
			
		||||
                netaddr.IPAddress('fe80::5054:ff:fe42:219')),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_local(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            netaddr.EUI('02:00:00:00:00:00'),
 | 
			
		||||
            netutils.get_mac_addr_by_ipv6(
 | 
			
		||||
                netaddr.IPAddress('fe80::ff:fe00:0')),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_universal(self):
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            netaddr.EUI('00:00:00:00:00:00'),
 | 
			
		||||
            netutils.get_mac_addr_by_ipv6(
 | 
			
		||||
                netaddr.IPAddress('fe80::200:ff:fe00:0')),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@contextlib.contextmanager
 | 
			
		||||
def mock_file_content(content):
 | 
			
		||||
    # Allows StringIO to act like a context manager-enabled file.
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
features:
 | 
			
		||||
  - |
 | 
			
		||||
    New method ``netutils.get_mac_addr_by_ipv6(ipv6, dialect)`` extracts
 | 
			
		||||
    the MAC address from IPv6 addresses generated from MACs.
 | 
			
		||||
		Reference in New Issue
	
	Block a user