Skip to content

Instantly share code, notes, and snippets.

@gvanem
Last active December 10, 2017 03:33
Show Gist options
  • Save gvanem/1643c946fb2395b6c8a05c3ec8904e13 to your computer and use it in GitHub Desktop.
Save gvanem/1643c946fb2395b6c8a05c3ec8904e13 to your computer and use it in GitHub Desktop.
A script for tcpdump; generates a oui-generated.c from http://standards-oui.ieee.org/oui.txt and http://www.iana.org/assignments/enterprise-numbers
#!/usr/bin/env python
#
# Generates a 'oui-generated.c' for tcpdump.
#
from __future__ import print_function
import sys, os, time, re, getopt, codecs
OUI_URL = "http://standards-oui.ieee.org/oui.txt"
ENT_URL = "http://www.iana.org/assignments/enterprise-numbers"
OUI_TXT = 'oui-generated.txt'
OUI_C = 'oui-generated.c'
ENT_NUM = 'enterprise-numbers'
OUI_C_HEAD = r"""
/*
* %s: "Organizationally Unique Identifier" list
* obtained from %s.
* This file was generated by %s at
* %s.
* DO NOT EDIT!
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <netdissect-stdinc.h>
#include "netdissect.h"
#include "oui.h"
const struct tok oui_values[] = {
"""
SMI_C_HEAD = r"""
const struct tok smi_values[] = {
{ SMI_IETF, "IETF (reserved)"},
"""
OUI_C_BOTTOM = r"""
/* Comparision routine needed by bsearch() routines.
* Don't use unsigned arithmetic.
*/
static int compare (const struct tok *a, const struct tok *b)
{
return ((long)a->v - (long)b->v);
}
typedef int (*CompareFunc) (const void *, const void *);
const char *oui_val_to_name (unsigned oui)
{
size_t num = sizeof(oui_values) / sizeof(oui_values[0]) - 1;
const struct tok *t = bsearch (&oui, oui_values, num, sizeof(oui_values[0]),
(CompareFunc)compare);
return (t ? t->s : "Unknown");
}
const char *smi_val_to_name (unsigned smi)
{
size_t num = sizeof(smi_values) / sizeof(smi_values[0]) - 1;
const struct tok *t = bsearch (&smi, smi_values, num, sizeof(smi_values[0]),
(CompareFunc)compare);
return (t ? t->s : "Unknown");
}
"""
prefixes = dict()
enterprises = dict()
def write_oui_c():
f = codecs.open (OUI_C, 'w+b', 'UTF-8')
f.write (OUI_C_HEAD % (OUI_C, OUI_URL, __file__, time.ctime()))
for p in sorted(prefixes):
f.write (" { 0x%.2s%.2s%.2s, \"%s\" },\n" % (p[0:], p[3:], p[6:], prefixes[p]))
f.write (" { 0xFFFFFF, NULL } /* Since XEROX CORPORATION has value 0, use this */\n };\n")
f.close()
print ("Wrote %d OUI records to %s" % (len(prefixes), OUI_C))
def append_oui_c():
f = codecs.open (OUI_C, 'a+b', 'UTF-8')
f.write (SMI_C_HEAD)
for e in sorted(enterprises):
width = len("SMI_IETF") - len(str(e))
f.write (" { %d,%*s \"%s\" },\n" % (e, width, "", enterprises[e]))
f.write (" { 0, NULL }\n};\n")
f.write (OUI_C_BOTTOM)
f.close()
print ("Appended %d SMI records to %s" % (len(enterprises), OUI_C))
#
# The progress callback for 'urllib.urlretrieve()'.
#
def url_progress (blocks, block_size, total_size):
if blocks:
percent = 100 * (blocks*block_size)/total_size
print ("Got %d kBytes (%u%%)\r" % ((block_size*blocks)/1024, percent), end="")
#
# Parse the lines from the download 'oui-generated.txt' file.
# Extract only the first line from each record.
# Grow the 'prefixes[]' dictionary as we walk the list of 'lines'.
#
def parse_oui_txt (lines):
#
# The format of the oui-generated.txt looks like:
#
# OUI/MA-L Organization
# company_id Organization
# Address
#
# E0-43-DB (hex) Shenzhen ViewAt Technology Co.,Ltd.
# E043DB (base 16) Shenzhen ViewAt Technology Co.,Ltd.
# 9A,Microprofit,6th Gaoxin South Road, High-Tech Industrial Park, Nanshan, Shenzhen, CHINA.
# shenzhen guangdong 518057
# CN
#
# We want to parse only "(hex)" lines.
#
for line in lines:
line = line.rstrip()
if not re.match("^[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}[\t ]*\(hex\)[\t ]*", line):
continue
prefix = line [0:8]
vendor = line [line.rindex('(hex)')+5:].lstrip()
prefixes [prefix] = vendor
#
# Parse the lines from the download 'enterprise-numbers' file.
# Grow the 'enterprises[]' dictionary as we walk the list of 'lines'.
#
def parse_ent_txt (lines):
r = re.compile (r"^(\d+)$")
got_number = 0
for line in lines:
match = r.search(line)
if got_number == 0 and match:
#
# Org 0 ("Reserved") is already in 'smi_values[0]'.
# Expect "Organization" on the next line.
#
got_number = int(match.group(1))
continue
if got_number:
#
# Convert (", \, and some \x codes to C-compatible strings.
#
org = line.strip()
org = org.replace (r'\\', r"\\")
org = org.replace (r'"', r'\"')
org = org.replace (r'\T', r'\\T')
org = org.replace (r'\C', r'\\C')
org = org.replace (r'\D', r'\\D')
enterprises [got_number] = org
got_number = 0
#
# Check if a local 'fname' exist. Otherwise download it from 'url'.
# Return the contents as a list.
#
def get_local_file_or_download (fname, url):
if os.path.exists(fname):
print ("A local %s already exist." % fname)
else:
try:
from urllib import urlretrieve as url_get
except ImportError:
from urllib.request import urlretrieve as url_get
print ("Downloading %s from %s" % (fname, url))
url_get (url, filename=fname, reporthook=url_progress)
print ("")
#
# Now read 'fname' and return it as a list.
#
data = codecs.open (fname, 'rb', 'UTF-8')
lines = data.read().splitlines()
data.close()
return lines
def usage (err=""):
print ("%s%s [-h | --help]:" % (err, os.path.realpath(sys.argv[0])))
print (r"""
This script does 2 things:
Creates a %s file from a local %s file
or from one downloaded from here:
%s""" % (OUI_C, OUI_TXT, OUI_URL))
print (r"""
and:
appends an 'smi_values[]' array at the end of %s.
This is taken from a local %s or from one downloaded from here:
%s""" % (OUI_C, ENT_NUM, ENT_URL))
sys.exit (0)
## __main__
try:
opts, args = getopt.getopt (sys.argv[1:], "?h", ["help"])
except getopt.GetoptError as e:
usage (e.msg + "\n")
for o, a in opts:
if o in ["-h", "-?", "--help"]:
usage()
l = get_local_file_or_download (OUI_TXT, OUI_URL)
parse_oui_txt (l)
write_oui_c()
l = get_local_file_or_download (ENT_NUM, ENT_URL)
parse_ent_txt (l)
append_oui_c()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment