1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
#!/usr/bin/env python3
# update/oui.py - script to download and parse data from the IEEE registry
#
# Copyright (C) 2018-2019 Arthur de Jong
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
"""This script downloads data from the IEEE web site
https://regauth.standards.ieee.org/standards-ra-web/pub/view.html
and produces data files that can be use by python-stdnum to look up
manufacturers by MAC address."""
import csv
from collections import defaultdict
from itertools import chain
import requests
# The URLs of the MA-L, MA-M and MA-S registries that are downloaded to
# construct a full list of manufacturer prefixes.
mal_url = 'https://standards-oui.ieee.org/oui/oui.csv'
mam_url = 'https://standards-oui.ieee.org/oui28/mam.csv'
mas_url = 'https://standards-oui.ieee.org/oui36/oui36.csv'
# The user agent that will be passed in requests
user_agent = 'Mozilla/5.0 (compatible; python-stdnum updater; +https://arthurdejong.org/python-stdnum/)'
def download_csv(url):
"""Download the list from the site and provide assignment and
organisation names."""
response = requests.get(url, timeout=500, headers={'User-Agent': user_agent})
response.raise_for_status()
for row in csv.DictReader(line.decode('utf-8') for line in response.iter_lines()):
o = row['Organization Name'].strip().replace('"', '%')
if o not in ('IEEE Registration Authority', 'Private'):
yield (row['Assignment'], o)
def join_items(items):
"""Join the list of items, combining consecutive numbers."""
length = len(items[0])
items = [int(b, 16) for b in items]
first = None
prev = None
res = ''
for item in items:
if first is not None and item == prev + 1:
# this item is consecutive to the previous: make a range
if prev > first:
# replace the previous value
res = res[:-length - 1]
res += '-%%0%dX' % length % item
prev = item
else:
# this is a new item, add a new one to the list
res += ',%%0%dX' % length % item
first = prev = item
return res.strip(',')
if __name__ == '__main__':
# download the MAC Address Block Large (MA-L) list and group by org
toplevel = defaultdict(list)
for a, o in download_csv(mal_url):
toplevel[o].append(a)
# download the MAC Address Block Medium (MA-M) and Small lists
nested = defaultdict(dict)
for a, o in chain(download_csv(mam_url), download_csv(mas_url)):
nested[a[:6]][a[6:]] = o
# Generate output
print('# list of IEEE MAC Address Block registry entries')
print('# %s' % mal_url)
print('# %s' % mam_url)
print('# %s' % mas_url)
# output full-length assignments
for a, o in sorted((tuple(sorted(a)), o) for o, a in toplevel.items()):
print('%s o="%s"' % (join_items(a), o))
# output assignments that are subdivided
for a in sorted(nested.keys()):
print('%s' % a)
for s, o in sorted(nested[a].items()):
print(' %s o="%s"' % (s, o))
|