193 lines
5.9 KiB
Python
Executable File
193 lines
5.9 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Parses the nl80211.h interface and generate appropriate enums and fields
|
|
# (value_string) for packet-netlink-nl80211.c
|
|
#
|
|
# Copyright (c) 2017, Peter Wu <peter@lekensteyn.nl>
|
|
#
|
|
# Wireshark - Network traffic analyzer
|
|
# By Gerald Combs <gerald@wireshark.org>
|
|
# Copyright 1998 Gerald Combs
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
#
|
|
#
|
|
# To update the dissector source file, run this from the source directory:
|
|
#
|
|
# python tools/generate-nl80211-fields.py --update
|
|
#
|
|
|
|
import argparse
|
|
import re
|
|
import requests
|
|
import sys
|
|
|
|
# Begin of comment, followed by the actual array definition
|
|
HEADER = "/* Definitions from linux/nl80211.h {{{ */\n"
|
|
FOOTER = "/* }}} */\n"
|
|
# Enums to extract from the header file
|
|
EXPORT_ENUMS = ("nl80211_commands", "nl80211_attrs")
|
|
# File to be patched
|
|
SOURCE_FILE = "epan/dissectors/packet-netlink-nl80211.c"
|
|
# URL where the latest version can be found
|
|
URL = "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/nl80211.h"
|
|
|
|
def make_enum(name, values, indent):
|
|
code = 'enum ws_%s {\n' % name
|
|
for value in values:
|
|
code += '%sWS_%s,\n' % (indent, value)
|
|
code += '};\n'
|
|
return code
|
|
|
|
def make_value_string(name, values, indent):
|
|
code = 'static const value_string ws_%s_vals[] = {\n' % name
|
|
align = 40
|
|
for value in values:
|
|
code += indent + ('{ WS_%s,' % value).ljust(align - 1) + ' '
|
|
code += '"%s" },\n' % value
|
|
code += '%s{ 0, NULL }\n' % indent
|
|
code += '};\n'
|
|
code += 'static value_string_ext ws_%s_vals_ext =' % name
|
|
code += ' VALUE_STRING_EXT_INIT(ws_%s_vals);\n' % name
|
|
return code
|
|
|
|
class EnumStore(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.values = []
|
|
self.active = True
|
|
|
|
def update(self, line):
|
|
if not self.active:
|
|
return
|
|
|
|
# Skip comments and remove trailing comma
|
|
line = re.sub(r'\s*/\*.*?\*/\s*', '', line).rstrip(",")
|
|
if not line:
|
|
return
|
|
|
|
# Try to match a name. Allow aliases only for the previous item.
|
|
m = re.match(r'^(?P<name>\w+)(?: *= *(?P<alias_of>\w+))?$', line)
|
|
assert m, "Failed to find match in %r" % line
|
|
name, alias_of = m.groups()
|
|
if alias_of:
|
|
# Alias must match previous item, skip it otherwise.
|
|
assert alias_of == self.values[-1]
|
|
elif name.startswith("__"):
|
|
# Skip after hitting "__NL80211_CMD_AFTER_LAST"
|
|
self.active = False
|
|
else:
|
|
self.values.append(name)
|
|
|
|
def finish(self):
|
|
assert not self.active
|
|
assert self.values
|
|
return self.name, self.values
|
|
|
|
def parse_header(f):
|
|
enum_store = None
|
|
enums = []
|
|
for line in f:
|
|
line = line.strip()
|
|
if line.startswith("enum "):
|
|
assert not enum_store
|
|
enum_keyword, enum_name, trailer = line.split()
|
|
assert trailer == "{"
|
|
if enum_name in EXPORT_ENUMS:
|
|
enum_store = EnumStore(enum_name)
|
|
elif enum_store:
|
|
if line == "};":
|
|
enums.append(enum_store.finish())
|
|
enum_store = None
|
|
elif line:
|
|
enum_store.update(line)
|
|
return enums
|
|
|
|
def parse_source():
|
|
"""
|
|
Reads the source file and tries to split it in the parts before, inside and
|
|
after the block.
|
|
"""
|
|
begin, block, end = '', '', ''
|
|
# Stages: 1 (before block), 2 (in block, skip), 3 (after block)
|
|
stage = 1
|
|
with open(SOURCE_FILE) as f:
|
|
for line in f:
|
|
if line == FOOTER and stage == 2:
|
|
stage = 3 # End of block
|
|
if stage == 1:
|
|
begin += line
|
|
if line == HEADER:
|
|
stage = 2 # Begin of block
|
|
elif stage == 2:
|
|
block += line
|
|
elif stage == 3:
|
|
end += line
|
|
if stage != 3:
|
|
raise RuntimeError("Could not parse file (in stage %d)" % stage)
|
|
return begin, block, end
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--update", action="store_true",
|
|
help="Update %s as needed instead of writing to stdout" % SOURCE_FILE)
|
|
parser.add_argument("--indent", default=" " * 4,
|
|
help="indentation (use \\t for tabs, default 4 spaces)")
|
|
parser.add_argument("header_file", nargs="?", default=URL,
|
|
help="nl80211.h header file (use - for stdin or a HTTP(S) URL, "
|
|
"default %(default)s)")
|
|
|
|
def main():
|
|
args = parser.parse_args()
|
|
|
|
indent = args.indent.replace("\\t", "\t")
|
|
|
|
if any(args.header_file.startswith(proto) for proto in ('http:', 'https')):
|
|
r = requests.get(args.header_file)
|
|
r.raise_for_status()
|
|
enums = parse_header(r.text.splitlines())
|
|
elif args.header_file == "-":
|
|
enums = parse_header(sys.stdin)
|
|
else:
|
|
with open(args.header_file) as f:
|
|
enums = parse_header(f)
|
|
|
|
assert len(enums) == len(EXPORT_ENUMS), \
|
|
"Could not parse data, found %d/%d results" % \
|
|
(len(enums), len(EXPORT_ENUMS))
|
|
|
|
code_enums, code_vals = '', ''
|
|
for enum_name, enum_values in enums:
|
|
code_enums += make_enum(enum_name, enum_values, indent) + '\n'
|
|
code_vals += make_value_string(enum_name, enum_values, indent) + '\n'
|
|
|
|
code = code_enums + code_vals
|
|
code = code.rstrip("\n") + "\n"
|
|
|
|
if args.update:
|
|
begin, block, end = parse_source()
|
|
if block == code:
|
|
print("File is up-to-date")
|
|
else:
|
|
with open(SOURCE_FILE, "w") as f:
|
|
f.write(begin)
|
|
f.write(code)
|
|
f.write(end)
|
|
print("Updated %s" % SOURCE_FILE)
|
|
else:
|
|
print(code)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
#
|
|
# Editor modelines - https://www.wireshark.org/tools/modelines.html
|
|
#
|
|
# Local variables:
|
|
# c-basic-offset: 4
|
|
# tab-width: 8
|
|
# indent-tabs-mode: nil
|
|
# End:
|
|
#
|
|
# vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
# :indentSize=4:tabSize=8:noTabs=true:
|
|
#
|