aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am1
-rwxr-xr-xtools/generate-sysdig-event.py345
2 files changed, 346 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 658cb2a1e3..2b582c8cbc 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -137,6 +137,7 @@ EXTRA_DIST = \
fuzz-test.sh \
gen-bugnote \
generate-bacnet-vendors.py \
+ generate-sysdig-event.py \
Get-HardenFlags.ps1 \
git-compare-abis.sh \
git-export-release.sh \
diff --git a/tools/generate-sysdig-event.py b/tools/generate-sysdig-event.py
new file mode 100755
index 0000000000..659bcb38f1
--- /dev/null
+++ b/tools/generate-sysdig-event.py
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Wireshark - Network traffic analyzer
+# By Gerald Combs <gerald@wireshark.org>
+# Copyright 1998 Gerald Combs
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+'''\
+Generate Sysdig event dissector sections from the sysdig sources.
+
+Reads driver/event_table.c and driver/ppm_events_public.h and generates
+corresponding dissection code in packet-sysdig-event.c. Updates are
+performed in-place in the dissector code.
+
+Requires an Internet connection. Assets are loaded from GitHub over HTTPS.
+'''
+
+import os
+import os.path
+import re
+import urllib2
+import sys
+
+sysdig_repo_pfx = 'https://raw.githubusercontent.com/draios/sysdig/0.5.0/'
+
+ppm_ev_pub = urllib2.urlopen(sysdig_repo_pfx + 'driver/ppm_events_public.h')
+ppm_ev_pub_lines = ppm_ev_pub.readlines()
+ppm_ev_pub.close()
+
+ppme_re = re.compile('^\s+PPME_([A-Z0-9_]+_[EX])\s*=\s*([0-9]+)\s*,')
+
+event_info_d = {}
+
+def get_event_defines():
+ event_d = {}
+ for line in ppm_ev_pub_lines:
+ m = ppme_re.match(line)
+ if m:
+ event_d[int(m.group(2))] = m.group(1)
+ return event_d
+
+ppm_ev_table = urllib2.urlopen(sysdig_repo_pfx + 'driver/event_table.c')
+ppm_ev_table_lines = ppm_ev_table.readlines()
+ppm_ev_table.close()
+
+hf_d = {}
+
+event_info_re = re.compile('^\s+/\*\s*PPME_.*\*\/\s*{\s*"([A-Za-z0-9_]+)"\s*,[^,]+,[^,]+,\s*([0-9]+)\s*[,{}]')
+event_param_re = re.compile('{\s*"([A-Za-z0-9_]+)"\s*,\s*PT_([A-Z0-9_]+)\s*,\s*PF_([A-Z0-9_]+)\s*[,}]')
+
+def get_event_names():
+ '''Return a contiguous list of event names. Names are lower case.'''
+ event_name_l = []
+ for line in ppm_ev_table_lines:
+ ei = event_info_re.match(line)
+ if ei:
+ event_name_l.append(ei.group(1))
+ return event_name_l
+
+# PT_xxx to FT_xxx
+pt_to_ft = {
+ 'BYTEBUF': 'BYTES',
+ 'CHARBUF': 'STRING',
+ 'FD': 'INT64',
+ 'FSPATH': 'STRING',
+}
+
+def get_event_params():
+ '''Return a list of dictionaries containing event names and parameter info.'''
+ event_param_l = []
+ event_num = 0
+ force_string_l = ['args', 'env']
+ for line in ppm_ev_table_lines:
+ ei = event_info_re.match(line)
+ ep = event_param_re.findall(line)
+ if ei and ep:
+ src_param_count = int(ei.group(2))
+ if len(ep) != src_param_count:
+ err_msg = '{}: found {} parameters. Expected {}. Params: {}'.format(
+ ei.group(1), len(ep), src_param_count, repr(ep))
+ raise NameError(err_msg)
+ for p in ep:
+ if p[0] in force_string_l:
+ param_type = 'STRING'
+ elif p[1] in pt_to_ft:
+ param_type = pt_to_ft[p[1]]
+ elif p[0] == 'flags' and p[1].startswith('INT') and 'HEX' in p[2]:
+ param_type = 'U' + p[1]
+ elif 'INT' in p[1]:
+ # Ints
+ param_type = p[1]
+ else:
+ # Fall back to bytes
+ param_type = 'BYTES'
+
+ if p[2] == 'NA':
+ if 'INT' in param_type:
+ param_format = 'DEC'
+ else:
+ param_format = 'NONE'
+ elif param_type == 'BYTES':
+ param_format = 'NONE'
+ else:
+ param_format = p[2]
+ param_d = {
+ 'event_name': ei.group(1),
+ 'event_num': event_num,
+ 'param_name': p[0],
+ 'param_type': param_type,
+ 'param_format': param_format,
+ }
+ event_param_l.append(param_d)
+ if ei:
+ event_num += 1
+ return event_param_l
+
+def param_to_hf_name(param):
+ return 'hf_param_{}_{}'.format(param['param_name'], param['param_type'].lower())
+
+def param_to_value_string_name(param):
+ return '{}_{}_vals'.format(param['param_name'], param['param_type'].lower())
+
+def get_param_desc(param):
+ # Try to coerce event names and parameters into human-friendly
+ # strings.
+ # XXX This could use some work.
+
+ # Specific descriptions. Event name + parameter name.
+ param_descs = {
+ 'accept.queuepct': 'Accept queue per connection',
+ 'execve.args': 'Program arguments',
+ 'execve.comm': 'Command',
+ 'execve.cwd': 'Current working directory',
+ }
+ # General descriptions. Event name only.
+ event_descs = {
+ 'ioctl': 'I/O control',
+ }
+
+ event_name = param['event_name']
+ param_id = '{}.{}'.format(event_name, param['param_name'])
+ if param_id in param_descs:
+ param_desc = param_descs[param_id]
+ elif event_name in event_descs:
+ param_desc = '{}: {}'.format(event_descs[event_name], param['param_name'])
+ else:
+ param_desc = param['param_name']
+ return param_desc
+
+def main():
+ # Event list
+ event_d = get_event_defines()
+ event_nums = event_d.keys()
+ event_nums.sort()
+
+ event_name_l = get_event_names()
+ event_param_l = get_event_params()
+
+ hf_d = {}
+ for param in event_param_l:
+ hf_name = param_to_hf_name(param)
+ hf_d[hf_name] = param
+
+ idx_id_to_name = { '': 'no' }
+ parameter_index_l = []
+
+ for en in range (0, len(event_nums)):
+ param_id = ''
+ param_l = []
+ event_var = event_d[en].lower()
+ for param in event_param_l:
+ if param['event_num'] == en:
+ hf_name = param_to_hf_name(param)
+ param_l.append(hf_name)
+ param_id += ':' + param['param_name'] + '_' + param['param_type']
+
+ ei_str = ''
+ if param_id not in idx_id_to_name:
+ idx_id_to_name[param_id] = event_var
+ ei_str = 'static const int *{}_indexes[] = {{ &{}, NULL }};'.format(
+ event_var,
+ ', &'.join(param_l)
+ )
+ else:
+ ei_str = '#define {}_indexes {}_indexes'.format(event_var, idx_id_to_name[param_id])
+
+ parameter_index_l.append(ei_str)
+
+ dissector_path = os.path.join(os.path.dirname(__file__),
+ '..', 'epan', 'dissectors', 'packet-sysdig-event.c')
+ dissector_f = open(dissector_path, 'r')
+ dissector_lines = list(dissector_f)
+ dissector_f = open(dissector_path, 'w+')
+
+ # Strip out old content
+ strip_re_l = []
+ strip_re_l.append(re.compile('^static\s+int\s+hf_param_.*;'))
+ strip_re_l.append(re.compile('^#define\s+EVT_STR_[A-Z0-9_]+\s+"[A-Za-z0-9_]+"'))
+ strip_re_l.append(re.compile('^#define\s+EVT_[A-Z0-9_]+\s+[0-9]+'))
+ strip_re_l.append(re.compile('^\s*{\s*EVT_[A-Z0-9_]+\s*,\s*EVT_STR_[A-Z0-9_]+\s*}'))
+ strip_re_l.append(re.compile('^static\s+const\s+int\s+\*\s*[a-z0-9_]+_[ex]_indexes\[\]\s*=\s*\{\s*&hf_param_.*NULL\s*\}\s*;'))
+ strip_re_l.append(re.compile('^\s*#define\s+[a-z0-9_]+_[ex]_indexes\s+[a-z0-9_]+_indexes'))
+ strip_re_l.append(re.compile('^\s*\{\s*EVT_[A-Z0-9_]+_[EX]\s*,\s*[a-z0-9_]+_[ex]_indexes\s*}\s*,'))
+ strip_re_l.append(re.compile('^\s*{\s*&hf_param_.*},')) # Must all be on one line
+
+ for strip_re in strip_re_l:
+ dissector_lines = [l for l in dissector_lines if not strip_re.search(l)]
+
+ # Find our value strings
+ value_string_re = re.compile('static\s+const\s+value_string\s+([A-Za-z0-9_]+_vals)')
+ value_string_l = []
+ for line in dissector_lines:
+ vs = value_string_re.match(line)
+ if vs:
+ value_string_l.append(vs.group(1))
+
+ # Add in new content after comments.
+
+ header_fields_c = 'Header fields'
+ header_fields_re = re.compile('/\*\s+' + header_fields_c, flags = re.IGNORECASE)
+ header_fields_l = []
+ for hf_name in sorted(hf_d.keys()):
+ header_fields_l.append('static int {} = -1;'.format(hf_name))
+
+ event_names_c = 'Event names'
+ event_names_re = re.compile('/\*\s+' + event_names_c, flags = re.IGNORECASE)
+ event_names_l = []
+ event_str_l = list(set(event_name_l))
+ event_str_l.sort()
+ for evt_str in event_str_l:
+ event_names_l.append('#define EVT_STR_{0:24s} "{1:s}"'.format(evt_str.upper(), evt_str))
+
+ event_definitions_c = 'Event definitions'
+ event_definitions_re = re.compile('/\*\s+' + event_definitions_c, flags = re.IGNORECASE)
+ event_definitions_l = []
+ for evt in event_nums:
+ event_definitions_l.append('#define EVT_{0:24s} {1:3d}'.format(event_d[evt], evt))
+
+ value_strings_c = 'Value strings'
+ value_strings_re = re.compile('/\*\s+' + value_strings_c, flags = re.IGNORECASE)
+ value_strings_l = []
+ for evt in event_nums:
+ evt_num = 'EVT_{},'.format(event_d[evt])
+ evt_str = 'EVT_STR_' + event_name_l[evt].upper()
+ value_strings_l.append(' {{ {0:<32s} {1:s} }},'.format(evt_num, evt_str))
+
+ parameter_index_c = 'Parameter indexes'
+ parameter_index_re = re.compile('/\*\s+' + parameter_index_c, flags = re.IGNORECASE)
+ # parameter_index_l defined above.
+
+ event_tree_c = 'Event tree'
+ event_tree_re = re.compile('/\*\s+' + event_tree_c, flags = re.IGNORECASE)
+ event_tree_l = []
+ for evt in event_nums:
+ evt_num = 'EVT_{}'.format(event_d[evt])
+ evt_idx = '{}_indexes'.format(event_d[evt].lower())
+ event_tree_l.append(' {{ {}, {} }},'.format(evt_num, evt_idx))
+
+ header_field_reg_c = 'Header field registration'
+ header_field_reg_re = re.compile('/\*\s+' + header_field_reg_c, flags = re.IGNORECASE)
+ header_field_reg_l = []
+ for hf_name in sorted(hf_d.keys()):
+ param = hf_d[hf_name]
+ event_name = param['event_name']
+ param_desc = get_param_desc(param)
+ param_name = param['param_name']
+ param_type = param['param_type']
+ param_format = param['param_format']
+ fieldconvert = 'NULL'
+ vs_name = param_to_value_string_name(param)
+ if vs_name in value_string_l and 'INT' in param_type:
+ fieldconvert = 'VALS({})'.format(vs_name)
+ header_field_reg_l.append(' {{ &{}, {{ "{}", "sysdig.param.{}.{}", FT_{}, BASE_{}, {}, 0, NULL, HFILL }} }},'.format(
+ hf_name,
+ param_desc,
+ event_name,
+ param_name,
+ param_type,
+ param_format,
+ fieldconvert
+ ))
+
+ for line in dissector_lines:
+ fill_comment = None
+ fill_l = []
+
+ if header_fields_re.match(line):
+ fill_comment = header_fields_c
+ fill_l = header_fields_l
+ elif event_names_re.match(line):
+ fill_comment = event_names_c
+ fill_l = event_names_l
+ elif event_definitions_re.match(line):
+ fill_comment = event_definitions_c
+ fill_l = event_definitions_l
+ elif value_strings_re.match(line):
+ fill_comment = value_strings_c
+ fill_l = value_strings_l
+ elif parameter_index_re.match(line):
+ fill_comment = parameter_index_c
+ fill_l = parameter_index_l
+ elif event_tree_re.match(line):
+ fill_comment = event_tree_c
+ fill_l = event_tree_l
+ elif header_field_reg_re.match(line):
+ fill_comment = header_field_reg_c
+ fill_l = header_field_reg_l
+
+ if fill_comment is not None:
+ # Write our comment followed by the content
+ print('Generating {}, {:d} lines'.format(fill_comment, len(fill_l)))
+ dissector_f.write('/* {}. Automatically generated by tools/{} */\n'.format(
+ fill_comment,
+ os.path.basename(__file__)
+ ))
+ for line in fill_l:
+ dissector_f.write('{}\n'.format(line))
+ # Fill each section only once
+ del fill_l[:]
+ else:
+ # Existing content
+ dissector_f.write(line)
+
+ dissector_f.close()
+
+#
+# On with the show
+#
+
+if __name__ == "__main__":
+ sys.exit(main())