aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorGerald Combs <gerald@zing.org>2015-06-01 19:48:23 -0700
committerGerald Combs <gerald@wireshark.org>2015-11-10 20:59:53 +0000
commit0d497e812508b3d71716618e39647a107c206b1d (patch)
tree79976b05e07aa180f7b50f4f0ec7d6348b04c3fe /tools
parent6a56224c18ec0ed19ad1a6e2b9172d330eeb14f0 (diff)
Initial Sysdig syscall (event) support.
Add a dissector for reading Sysdig event blocks. It only handles plain events but it's usable for reading trace files on hand here. Use a script to generate various parts of the dissector. As an experiment, update parts in-place instead of using a template. Ultimately there should probably be a top-level "Syscall" or "Event" dissector alongside the "Frame" dissector, which could then call this. You could then directly compare an executable's system calls alongside its network traffic. For now leverage the pcapng_block dissector and keep everything under "Frame". Next steps: - Items listed at the top of packet-sysdig-event.c. Change-Id: I17077e8d7f40d10a946d61189ebc077d81c4da37 Reviewed-on: https://code.wireshark.org/review/11103 Petri-Dish: Gerald Combs <gerald@wireshark.org> Reviewed-by: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Gerald Combs <gerald@wireshark.org>
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())