diff options
author | Barry Warsaw <barry@python.org> | 2017-11-27 19:40:10 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-27 19:40:10 (GMT) |
commit | 9522a218f7dff95c490ff359cc60e8c2af35f5c8 (patch) | |
tree | 23bd2c27804e6c0c5e88c3ce91fa02daa834067a /Lib/test/test_uuid.py | |
parent | c9758784eb321fb9771e0bc7205b296e4d658045 (diff) | |
download | cpython-9522a218f7dff95c490ff359cc60e8c2af35f5c8.zip cpython-9522a218f7dff95c490ff359cc60e8c2af35f5c8.tar.gz cpython-9522a218f7dff95c490ff359cc60e8c2af35f5c8.tar.bz2 |
bpo-32107 - Better merge of #4494 (#4576)
Improve UUID1 MAC address calculation and related tests.
There are two bits in the MAC address that are relevant to UUID1. The first is the locally administered vs. universally administered bit (second least significant of the first octet). Physical network interfaces such as ethernet ports and wireless adapters will always be universally administered, but some interfaces --such as the interface that MacBook Pros communicate with their Touch Bars-- are locally administered. The former are guaranteed to be globally unique, while the latter are demonstrably *not* globally unique and are in fact the same on every MBP with a Touch Bar. With this bit is set, the MAC is locally administered; with it unset it is universally administered.
The other bit is the multicast bit (least significant bit of the first octet). When no other MAC address can be found, RFC 4122 mandates that a random 48-bit number be generated. This randomly generated number *must* have the multicast bit set.
The improvements in uuid.py include:
* Preferentially return a universally administered MAC address, falling back to a locally administered address if none of the former can be found.
* Improve several coding style issues, such as adding explicit returns of None, using a more readable bitmask pattern, and assuming that the ultimate fallback, random MAC generation will not fail (and propagating any exception there instead of swallowing them).
Improvements in test_uuid.py include:
* Always testing the calculated MAC for universal administration, unless explicitly disabled (i.e. for the random case), or implicitly disabled due to running in the Travis environment. Travis test machines have *no* universally administered MAC address at the time of this writing.
Diffstat (limited to 'Lib/test/test_uuid.py')
-rw-r--r-- | Lib/test/test_uuid.py | 47 |
1 files changed, 30 insertions, 17 deletions
diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 083c2aa..54d73f7 100644 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -512,60 +512,69 @@ eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab self.assertEqual(mac, 0x1234567890ab) - def check_node(self, node, requires=None, network=False): + def check_node(self, node, requires=None, *, check_bit=True): if requires and node is None: self.skipTest('requires ' + requires) hex = '%012x' % node if support.verbose >= 2: print(hex, end=' ') - if network: - # 47 bit will never be set in IEEE 802 addresses obtained - # from network cards. - self.assertFalse(node & 0x010000000000, hex) + # The MAC address will be universally administered (i.e. the second + # least significant bit of the first octet must be unset) for any + # physical interface, such as an ethernet port or wireless adapter. + # There are some cases where this won't be the case. Randomly + # generated MACs may not be universally administered, but they must + # have their multicast bit set, though this is tested in the + # `test_random_getnode()` method specifically. Another case is the + # Travis-CI case, which apparently only has locally administered MAC + # addresses. + if check_bit and not os.getenv('TRAVIS'): + self.assertFalse(node & (1 << 41), '%012x' % node) self.assertTrue(0 < node < (1 << 48), "%s is not an RFC 4122 node ID" % hex) @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_ifconfig_getnode(self): node = self.uuid._ifconfig_getnode() - self.check_node(node, 'ifconfig', True) + self.check_node(node, 'ifconfig') @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_ip_getnode(self): node = self.uuid._ip_getnode() - self.check_node(node, 'ip', True) + self.check_node(node, 'ip') @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_arp_getnode(self): node = self.uuid._arp_getnode() - self.check_node(node, 'arp', True) + self.check_node(node, 'arp') @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_lanscan_getnode(self): node = self.uuid._lanscan_getnode() - self.check_node(node, 'lanscan', True) + self.check_node(node, 'lanscan') @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_netstat_getnode(self): node = self.uuid._netstat_getnode() - self.check_node(node, 'netstat', True) + self.check_node(node, 'netstat') @unittest.skipUnless(os.name == 'nt', 'requires Windows') def test_ipconfig_getnode(self): node = self.uuid._ipconfig_getnode() - self.check_node(node, 'ipconfig', True) + self.check_node(node, 'ipconfig') @unittest.skipUnless(importable('win32wnet'), 'requires win32wnet') @unittest.skipUnless(importable('netbios'), 'requires netbios') def test_netbios_getnode(self): node = self.uuid._netbios_getnode() - self.check_node(node, network=True) + self.check_node(node) def test_random_getnode(self): node = self.uuid._random_getnode() - # Least significant bit of first octet must be set. - self.assertTrue(node & 0x010000000000, '%012x' % node) - self.check_node(node) + # The multicast bit, i.e. the least significant bit of first octet, + # must be set for randomly generated MAC addresses. See RFC 4122, + # $4.1.6. + self.assertTrue(node & (1 << 40), '%012x' % node) + self.check_node(node, check_bit=False) @unittest.skipUnless(os.name == 'posix', 'requires Posix') def test_unix_getnode(self): @@ -575,13 +584,17 @@ eth0 Link encap:Ethernet HWaddr 12:34:56:78:90:ab node = self.uuid._unix_getnode() except TypeError: self.skipTest('requires uuid_generate_time') - self.check_node(node, 'unix') + # Since we don't know the provenance of the MAC address, don't check + # whether it is locally or universally administered. + self.check_node(node, 'unix', check_bit=False) @unittest.skipUnless(os.name == 'nt', 'requires Windows') @unittest.skipUnless(importable('ctypes'), 'requires ctypes') def test_windll_getnode(self): node = self.uuid._windll_getnode() - self.check_node(node) + # Since we don't know the provenance of the MAC address, don't check + # whether it is locally or universally administered. + self.check_node(node, check_bit=False) class TestInternalsWithoutExtModule(BaseTestInternals, unittest.TestCase): |