@@ -342,11 +342,30 @@ def _popen(command, *args):
342
342
env = env )
343
343
return proc
344
344
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
+
345
363
def _find_mac (command , args , hw_identifiers , get_index ):
364
+ first_local_mac = None
346
365
try :
347
366
proc = _popen (command , * args .split ())
348
367
if not proc :
349
- return
368
+ return None
350
369
with proc :
351
370
for line in proc .stdout :
352
371
words = line .lower ().rstrip ().split ()
@@ -355,8 +374,9 @@ def _find_mac(command, args, hw_identifiers, get_index):
355
374
try :
356
375
word = words [get_index (i )]
357
376
mac = int (word .replace (b':' , b'' ), 16 )
358
- if mac :
377
+ if _is_universal ( mac ) :
359
378
return mac
379
+ first_local_mac = first_local_mac or mac
360
380
except (ValueError , IndexError ):
361
381
# Virtual interfaces, such as those provided by
362
382
# VPNs, do not have a colon-delimited MAC address
@@ -366,6 +386,7 @@ def _find_mac(command, args, hw_identifiers, get_index):
366
386
pass
367
387
except OSError :
368
388
pass
389
+ return first_local_mac or None
369
390
370
391
def _ifconfig_getnode ():
371
392
"""Get the hardware address on Unix by running ifconfig."""
@@ -375,13 +396,15 @@ def _ifconfig_getnode():
375
396
mac = _find_mac ('ifconfig' , args , keywords , lambda i : i + 1 )
376
397
if mac :
377
398
return mac
399
+ return None
378
400
379
401
def _ip_getnode ():
380
402
"""Get the hardware address on Unix by running ip."""
381
403
# This works on Linux with iproute2.
382
404
mac = _find_mac ('ip' , 'link list' , [b'link/ether' ], lambda i : i + 1 )
383
405
if mac :
384
406
return mac
407
+ return None
385
408
386
409
def _arp_getnode ():
387
410
"""Get the hardware address on Unix by running arp."""
@@ -404,8 +427,10 @@ def _arp_getnode():
404
427
# This works on Linux, FreeBSD and NetBSD
405
428
mac = _find_mac ('arp' , '-an' , [os .fsencode ('(%s)' % ip_addr )],
406
429
lambda i : i + 2 )
430
+ # Return None instead of 0.
407
431
if mac :
408
432
return mac
433
+ return None
409
434
410
435
def _lanscan_getnode ():
411
436
"""Get the hardware address on Unix by running lanscan."""
@@ -415,32 +440,36 @@ def _lanscan_getnode():
415
440
def _netstat_getnode ():
416
441
"""Get the hardware address on Unix by running netstat."""
417
442
# This might work on AIX, Tru64 UNIX.
443
+ first_local_mac = None
418
444
try :
419
445
proc = _popen ('netstat' , '-ia' )
420
446
if not proc :
421
- return
447
+ return None
422
448
with proc :
423
449
words = proc .stdout .readline ().rstrip ().split ()
424
450
try :
425
451
i = words .index (b'Address' )
426
452
except ValueError :
427
- return
453
+ return None
428
454
for line in proc .stdout :
429
455
try :
430
456
words = line .rstrip ().split ()
431
457
word = words [i ]
432
458
if len (word ) == 17 and word .count (b':' ) == 5 :
433
459
mac = int (word .replace (b':' , b'' ), 16 )
434
- if mac :
460
+ if _is_universal ( mac ) :
435
461
return mac
462
+ first_local_mac = first_local_mac or mac
436
463
except (ValueError , IndexError ):
437
464
pass
438
465
except OSError :
439
466
pass
467
+ return first_local_mac or None
440
468
441
469
def _ipconfig_getnode ():
442
470
"""Get the hardware address on Windows by running ipconfig.exe."""
443
471
import os , re
472
+ first_local_mac = None
444
473
dirs = ['' , r'c:\windows\system32' , r'c:\winnt\system32' ]
445
474
try :
446
475
import ctypes
@@ -458,18 +487,23 @@ def _ipconfig_getnode():
458
487
for line in pipe :
459
488
value = line .split (':' )[- 1 ].strip ().lower ()
460
489
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
462
495
463
496
def _netbios_getnode ():
464
497
"""Get the hardware address on Windows using NetBIOS calls.
465
498
See http://support.microsoft.com/kb/118623 for details."""
466
499
import win32wnet , netbios
500
+ first_local_mac = None
467
501
ncb = netbios .NCB ()
468
502
ncb .Command = netbios .NCBENUM
469
503
ncb .Buffer = adapters = netbios .LANA_ENUM ()
470
504
adapters ._pack ()
471
505
if win32wnet .Netbios (ncb ) != 0 :
472
- return
506
+ return None
473
507
adapters ._unpack ()
474
508
for i in range (adapters .length ):
475
509
ncb .Reset ()
@@ -488,7 +522,11 @@ def _netbios_getnode():
488
522
bytes = status .adapter_address [:6 ]
489
523
if len (bytes ) != 6 :
490
524
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
492
530
493
531
494
532
_generate_time_safe = _UuidCreate = None
@@ -601,9 +639,19 @@ def _windll_getnode():
601
639
return UUID (bytes = bytes_ (_buffer .raw )).node
602
640
603
641
def _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
605
653
import random
606
- return random .getrandbits (48 ) | 0x010000000000
654
+ return random .getrandbits (48 ) | ( 1 << 40 )
607
655
608
656
609
657
_node = None
@@ -626,13 +674,14 @@ def getnode():
626
674
getters = [_unix_getnode , _ifconfig_getnode , _ip_getnode ,
627
675
_arp_getnode , _lanscan_getnode , _netstat_getnode ]
628
676
629
- for getter in getters + [ _random_getnode ] :
677
+ for getter in getters :
630
678
try :
631
679
_node = getter ()
632
680
except :
633
681
continue
634
682
if _node is not None :
635
683
return _node
684
+ return _random_getnode ()
636
685
637
686
638
687
_last_timestamp = None
0 commit comments