@@ -342,11 +342,30 @@ def _popen(command, *args):
342342 env = env )
343343 return proc
344344
345+ # For MAC (a.k.a. IEEE 802, or EUI-48) addresses, the second least significant
346+ # bit of the first octet signifies whether the MAC address is universally (0)
347+ # or locally (1) administered. Network cards from hardware manufacturers will
348+ # always be universally administered to guarantee global uniqueness of the MAC
349+ # address, but any particular machine may have other interfaces which are
350+ # locally administered. An example of the latter is the bridge interface to
351+ # the Touch Bar on MacBook Pros.
352+ #
353+ # This bit works out to be the 42nd bit counting from 1 being the least
354+ # significant, or 1<<41. We'll prefer universally administered MAC addresses
355+ # over locally administered ones since the former are globally unique, but
356+ # we'll return the first of the latter found if that's all the machine has.
357+ #
358+ # See https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local
359+
360+ def _is_universal (mac ):
361+ return not (mac & (1 << 41 ))
362+
345363def _find_mac (command , args , hw_identifiers , get_index ):
364+ first_local_mac = None
346365 try :
347366 proc = _popen (command , * args .split ())
348367 if not proc :
349- return
368+ return None
350369 with proc :
351370 for line in proc .stdout :
352371 words = line .lower ().rstrip ().split ()
@@ -355,8 +374,9 @@ def _find_mac(command, args, hw_identifiers, get_index):
355374 try :
356375 word = words [get_index (i )]
357376 mac = int (word .replace (b':' , b'' ), 16 )
358- if mac :
377+ if _is_universal ( mac ) :
359378 return mac
379+ first_local_mac = first_local_mac or mac
360380 except (ValueError , IndexError ):
361381 # Virtual interfaces, such as those provided by
362382 # VPNs, do not have a colon-delimited MAC address
@@ -366,6 +386,7 @@ def _find_mac(command, args, hw_identifiers, get_index):
366386 pass
367387 except OSError :
368388 pass
389+ return first_local_mac or None
369390
370391def _ifconfig_getnode ():
371392 """Get the hardware address on Unix by running ifconfig."""
@@ -375,13 +396,15 @@ def _ifconfig_getnode():
375396 mac = _find_mac ('ifconfig' , args , keywords , lambda i : i + 1 )
376397 if mac :
377398 return mac
399+ return None
378400
379401def _ip_getnode ():
380402 """Get the hardware address on Unix by running ip."""
381403 # This works on Linux with iproute2.
382404 mac = _find_mac ('ip' , 'link list' , [b'link/ether' ], lambda i : i + 1 )
383405 if mac :
384406 return mac
407+ return None
385408
386409def _arp_getnode ():
387410 """Get the hardware address on Unix by running arp."""
@@ -404,8 +427,10 @@ def _arp_getnode():
404427 # This works on Linux, FreeBSD and NetBSD
405428 mac = _find_mac ('arp' , '-an' , [os .fsencode ('(%s)' % ip_addr )],
406429 lambda i : i + 2 )
430+ # Return None instead of 0.
407431 if mac :
408432 return mac
433+ return None
409434
410435def _lanscan_getnode ():
411436 """Get the hardware address on Unix by running lanscan."""
@@ -415,32 +440,36 @@ def _lanscan_getnode():
415440def _netstat_getnode ():
416441 """Get the hardware address on Unix by running netstat."""
417442 # This might work on AIX, Tru64 UNIX.
443+ first_local_mac = None
418444 try :
419445 proc = _popen ('netstat' , '-ia' )
420446 if not proc :
421- return
447+ return None
422448 with proc :
423449 words = proc .stdout .readline ().rstrip ().split ()
424450 try :
425451 i = words .index (b'Address' )
426452 except ValueError :
427- return
453+ return None
428454 for line in proc .stdout :
429455 try :
430456 words = line .rstrip ().split ()
431457 word = words [i ]
432458 if len (word ) == 17 and word .count (b':' ) == 5 :
433459 mac = int (word .replace (b':' , b'' ), 16 )
434- if mac :
460+ if _is_universal ( mac ) :
435461 return mac
462+ first_local_mac = first_local_mac or mac
436463 except (ValueError , IndexError ):
437464 pass
438465 except OSError :
439466 pass
467+ return first_local_mac or None
440468
441469def _ipconfig_getnode ():
442470 """Get the hardware address on Windows by running ipconfig.exe."""
443471 import os , re
472+ first_local_mac = None
444473 dirs = ['' , r'c:\windows\system32' , r'c:\winnt\system32' ]
445474 try :
446475 import ctypes
@@ -458,18 +487,23 @@ def _ipconfig_getnode():
458487 for line in pipe :
459488 value = line .split (':' )[- 1 ].strip ().lower ()
460489 if re .match ('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]' , value ):
461- return int (value .replace ('-' , '' ), 16 )
490+ mac = int (value .replace ('-' , '' ), 16 )
491+ if _is_universal (mac ):
492+ return mac
493+ first_local_mac = first_local_mac or mac
494+ return first_local_mac or None
462495
463496def _netbios_getnode ():
464497 """Get the hardware address on Windows using NetBIOS calls.
465498 See http://support.microsoft.com/kb/118623 for details."""
466499 import win32wnet , netbios
500+ first_local_mac = None
467501 ncb = netbios .NCB ()
468502 ncb .Command = netbios .NCBENUM
469503 ncb .Buffer = adapters = netbios .LANA_ENUM ()
470504 adapters ._pack ()
471505 if win32wnet .Netbios (ncb ) != 0 :
472- return
506+ return None
473507 adapters ._unpack ()
474508 for i in range (adapters .length ):
475509 ncb .Reset ()
@@ -488,7 +522,11 @@ def _netbios_getnode():
488522 bytes = status .adapter_address [:6 ]
489523 if len (bytes ) != 6 :
490524 continue
491- return int .from_bytes (bytes , 'big' )
525+ mac = int .from_bytes (bytes , 'big' )
526+ if _is_universal (mac ):
527+ return mac
528+ first_local_mac = first_local_mac or mac
529+ return first_local_mac or None
492530
493531
494532_generate_time_safe = _UuidCreate = None
@@ -601,9 +639,19 @@ def _windll_getnode():
601639 return UUID (bytes = bytes_ (_buffer .raw )).node
602640
603641def _random_getnode ():
604- """Get a random node ID, with eighth bit set as suggested by RFC 4122."""
642+ """Get a random node ID."""
643+ # RFC 4122, $4.1.6 says "For systems with no IEEE address, a randomly or
644+ # pseudo-randomly generated value may be used; see Section 4.5. The
645+ # multicast bit must be set in such addresses, in order that they will
646+ # never conflict with addresses obtained from network cards."
647+ #
648+ # The "multicast bit" of a MAC address is defined to be "the least
649+ # significant bit of the first octet". This works out to be the 41st bit
650+ # counting from 1 being the least significant bit, or 1<<40.
651+ #
652+ # See https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
605653 import random
606- return random .getrandbits (48 ) | 0x010000000000
654+ return random .getrandbits (48 ) | ( 1 << 40 )
607655
608656
609657_node = None
@@ -626,13 +674,14 @@ def getnode():
626674 getters = [_unix_getnode , _ifconfig_getnode , _ip_getnode ,
627675 _arp_getnode , _lanscan_getnode , _netstat_getnode ]
628676
629- for getter in getters + [ _random_getnode ] :
677+ for getter in getters :
630678 try :
631679 _node = getter ()
632680 except :
633681 continue
634682 if _node is not None :
635683 return _node
684+ return _random_getnode ()
636685
637686
638687_last_timestamp = None
0 commit comments