Query a public geo-IP/ASN service *through* one interface, so on a multi-WAN box each WAN's own public IP + ISP is reported. curl --interface binds the socket to that device (SO_BINDTODEVICE), forcing egress out it regardless of the routing table.
(iface)
| 928 | _MAC_VENDOR_SEED = { |
| 929 | 'b8:27:eb': 'Raspberry Pi', 'dc:a6:32': 'Raspberry Pi', |
| 930 | 'e4:5f:01': 'Raspberry Pi', 'd8:3a:dd': 'Raspberry Pi', |
| 931 | '2c:cf:67': 'Raspberry Pi', '28:cd:c1': 'Raspberry Pi', |
| 932 | '3c:22:fb': 'Apple', 'a4:83:e7': 'Apple', 'f0:18:98': 'Apple', |
| 933 | '00:1b:63': 'Apple', 'ac:bc:32': 'Apple', '90:9c:4a': 'Apple', |
| 934 | '00:50:56': 'VMware', '00:0c:29': 'VMware', '00:05:69': 'VMware', |
| 935 | '08:00:27': 'VirtualBox', '00:15:5d': 'Microsoft Hyper-V', |
| 936 | '00:16:6c': 'Samsung', 'e8:50:8b': 'Samsung', '5c:0a:5b': 'Samsung', |
| 937 | '3c:97:0e': 'Intel', '00:1b:21': 'Intel', '34:13:e8': 'Intel', |
| 938 | '00:1a:a1': 'Cisco', '00:1b:0d': 'Cisco', |
| 939 | '50:c7:bf': 'TP-Link', 'a4:2b:b0': 'TP-Link', 'c0:06:c3': 'TP-Link', |
| 940 | '00:14:6c': 'Netgear', '20:e5:2a': 'Netgear', |
| 941 | '24:0a:c4': 'Espressif', '30:ae:a4': 'Espressif', |
| 942 | '7c:9e:bd': 'Espressif', 'a0:20:a6': 'Espressif', |
| 943 | '00:e0:fc': 'Huawei', 'ec:b5:fa': 'Philips', |
| 944 | } |
| 945 | |
| 946 | _OUI_DB_PATHS = ('/usr/share/arp-scan/ieee-oui.txt', |
| 947 | '/usr/share/nmap/nmap-mac-prefixes') |
| 948 | # Cap the loaded OUI table so a pathological file can't blow memory. |
| 949 | _OUI_DB_CAP = 60000 |
| 950 | _oui_db_cache = None |
| 951 | _oui_db_lock = threading.Lock() |
| 952 | |
| 953 | |
| 954 | def _mac_norm(mac): |
| 955 | """Normalise a MAC to lowercase colon form, or None if it isn't a MAC.""" |
| 956 | if not mac or not isinstance(mac, str): |
| 957 | return None |
| 958 | hexs = re.sub(r'[^0-9a-fA-F]', '', mac) |
| 959 | if len(hexs) != 12: |
| 960 | return None |
| 961 | hexs = hexs.lower() |
| 962 | return ':'.join(hexs[i:i + 2] for i in range(0, 12, 2)) |
| 963 | |
| 964 | |
| 965 | def _mac_first_octet(mac): |
| 966 | try: |
| 967 | return int(mac[0:2], 16) |
| 968 | except (ValueError, TypeError): |
| 969 | return None |
| 970 | |
| 971 | |
| 972 | def _is_laa(mac): |
| 973 | """True if the locally-administered (privacy/spoof) bit is set.""" |
| 974 | b0 = _mac_first_octet(mac) |
| 975 | return b0 is not None and bool(b0 & 0x02) |
| 976 | |
| 977 | |
| 978 | def _is_universal_prefix(oui): |
| 979 | """True if a 6-hex OUI could be a legitimately-registered (LAA-clear) OUI. |
| 980 | Filters junk out of the vendor table so an LAA/randomization range in a full |
| 981 | manuf file can't be mistaken for a real vendor by the spoof check.""" |
| 982 | try: |
| 983 | b0 = int(oui[0:2], 16) |
| 984 | except (ValueError, TypeError, IndexError): |
| 985 | return False |
| 986 | return not (b0 & 0x02) and not (b0 & 0x01) |
| 987 |
no test coverage detected