From 7f02b84cedfdde0d9d566edc72f00c780dae395c Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Sun, 27 Aug 2017 08:00:38 -0400 Subject: [PATCH 1/2] fixes #119 - add support for pcap capture filters --- contrib/python-wrapper.py | 34 +++++++++++++++++++++++++++------- src/decpcap.c | 31 ++++++++++++++++++++++++++++++- src/decpcap.h | 2 +- src/libnethogs.cpp | 14 ++++++++------ src/libnethogs.h | 12 ++++++++++-- src/main.cpp | 21 ++++++++++++++++++--- 6 files changed, 94 insertions(+), 20 deletions(-) diff --git a/contrib/python-wrapper.py b/contrib/python-wrapper.py index f4e1895..35897f0 100644 --- a/contrib/python-wrapper.py +++ b/contrib/python-wrapper.py @@ -14,12 +14,29 @@ import threading # until network activity occurs and the callback is executed. By using 2 threads, we can have the # main thread listen for SIGINT while the secondary thread is blocked in the monitor loop. +####################### +# BEGIN CONFIGURATION # +####################### + +# You can use this to monitor only certain devices, like: +# device_names = ['enp4s0', 'docker0'] +device_names = [] # LIBRARY_NAME has to be exact, although it doesn't need to include the full path. # The version tagged as 0.8.5 (download link below) builds a library with this name. # https://github.com/raboof/nethogs/archive/v0.8.5.tar.gz LIBRARY_NAME = 'libnethogs.so.0.8.5' +# Optionally, specify a capture filter in pcap format (same as used by +# tcpdump(1)) or None. See `man pcap-filter` for full information. +# example: +# FILTER = 'port 80 or port 8080 or port 443' +FILTER = None + +##################### +# END CONFIGURATION # +##################### + # Here are some definitions from libnethogs.h # https://github.com/raboof/nethogs/blob/master/src/libnethogs.h # Possible actions are NETHOGS_APP_ACTION_SET & NETHOGS_APP_ACTION_REMOVE @@ -89,18 +106,25 @@ def run_monitor_loop(lib, devnames): # params an int and a pointer to a NethogsMonitorRecord instance. # The params and return type of the callback function are mandated by nethogsmonitor_loop(). # See libnethogs.h. - CALLBACK_FUNC_TYPE = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_int, - ctypes.POINTER(NethogsMonitorRecord)) + CALLBACK_FUNC_TYPE = ctypes.CFUNCTYPE( + ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(NethogsMonitorRecord) + ) + + filter_arg = FILTER + if filter_arg is not None: + filter_arg = ctypes.c_char_p(filter_arg.encode('ascii')) if len(devnames) < 1: # monitor all devices rc = lib.nethogsmonitor_loop( - CALLBACK_FUNC_TYPE(network_activity_callback) + CALLBACK_FUNC_TYPE(network_activity_callback), + filter_arg ) else: devc, devicenames = dev_args(devnames) rc = lib.nethogsmonitor_loop_devices( CALLBACK_FUNC_TYPE(network_activity_callback), + filter_arg, devc, devicenames, ctypes.c_bool(False) @@ -136,10 +160,6 @@ signal.signal(signal.SIGTERM, signal_handler) lib = ctypes.CDLL(LIBRARY_NAME) -device_names = [] -# You can use this to monitor only certain devices, like: -# device_names = ['enp4s0', 'docker0'] - monitor_thread = threading.Thread( target=run_monitor_loop, args=(lib, device_names,) ) diff --git a/src/decpcap.c b/src/decpcap.c index cb115e8..7abc63e 100644 --- a/src/decpcap.c +++ b/src/decpcap.c @@ -77,13 +77,42 @@ struct dp_handle *dp_open_offline(char *fname, char *ebuf) { } struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc, - int to_ms, char *errbuf) { + int to_ms, char *filter, char *errbuf) { + struct bpf_program fp; // compiled filter program + bpf_u_int32 maskp; // subnet mask + bpf_u_int32 netp; // interface IP + pcap_t *temp = pcap_open_live(device, snaplen, promisc, to_ms, errbuf); if (temp == NULL) { return NULL; } + if (filter != NULL) { + pcap_lookupnet(device, &netp, &maskp, errbuf); + + /* Compile the filter */ + if(pcap_compile(temp, &fp, filter, 1, netp) == -1) { + fprintf( + stderr, + "Error calling pcap_compile for filter on device %s: %s\n", + device, pcap_geterr(temp) + ); + return NULL; + } + + /* set the filter */ + if(pcap_setfilter(temp, &fp) == -1) { + fprintf( + stderr, + "Error setting capture filter on device %s: %s\n", + device, pcap_geterr(temp) + ); + return NULL; + } + + } + return dp_fillhandle(temp); } diff --git a/src/decpcap.h b/src/decpcap.h index b23de5c..f5f7a31 100644 --- a/src/decpcap.h +++ b/src/decpcap.h @@ -65,7 +65,7 @@ struct dp_handle { /* functions to set up a handle (which is basically just a pcap handle) */ struct dp_handle *dp_open_live(const char *device, int snaplen, int promisc, - int to_ms, char *errbuf); + int to_ms, char *filter, char *errbuf); struct dp_handle *dp_open_offline(char *fname, char *ebuf); /* functions to add callbacks */ diff --git a/src/libnethogs.cpp b/src/libnethogs.cpp index b45ebca..647aca4 100644 --- a/src/libnethogs.cpp +++ b/src/libnethogs.cpp @@ -72,7 +72,8 @@ static bool wait_for_next_trigger() { return true; } -static int nethogsmonitor_init(int devc, char **devicenames, bool all) { +static int nethogsmonitor_init(int devc, char **devicenames, + bool all, char *filter) { process_init(); device *devices = get_devices(devc, devicenames, all); @@ -100,7 +101,8 @@ static int nethogsmonitor_init(int devc, char **devicenames, bool all) { char errbuf[PCAP_ERRBUF_SIZE]; dp_handle *newhandle = - dp_open_live(current_dev->name, BUFSIZ, promiscuous, 100, errbuf); + dp_open_live(current_dev->name, BUFSIZ, promiscuous, + 100, filter, errbuf); if (newhandle != NULL) { dp_addcb(newhandle, dp_packet_ip, process_ip); dp_addcb(newhandle, dp_packet_ip6, process_ip6); @@ -271,17 +273,17 @@ static void nethogsmonitor_clean_up() { procclean(); } -int nethogsmonitor_loop(NethogsMonitorCallback cb) { - return nethogsmonitor_loop_devices(cb, 0, NULL, false); +int nethogsmonitor_loop(NethogsMonitorCallback cb, char *filter) { + return nethogsmonitor_loop_devices(cb, filter, 0, NULL, false); } -int nethogsmonitor_loop_devices(NethogsMonitorCallback cb, +int nethogsmonitor_loop_devices(NethogsMonitorCallback cb, char *filter, int devc, char **devicenames, bool all) { if (monitor_run_flag) { return NETHOGS_STATUS_FAILURE; } - int return_value = nethogsmonitor_init(devc, devicenames, all); + int return_value = nethogsmonitor_init(devc, devicenames, all, filter); if (return_value != NETHOGS_STATUS_OK) { return return_value; } diff --git a/src/libnethogs.h b/src/libnethogs.h index 4caacb8..af02ad5 100644 --- a/src/libnethogs.h +++ b/src/libnethogs.h @@ -53,9 +53,13 @@ typedef void (*NethogsMonitorCallback)(int action, * occurs. * @param cb A pointer to a callback function following the * NethogsMonitorCallback definition + * @param filter A string (char array) pcap filter to restrict what packets + * are captured, or NULL. The filter string format is the same as that of + * tcpdump(1); for full details, see the man page for pcap-filter(7). */ -NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb); +NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb, + char *filter); /** * @brief Enter the process monitoring loop and reports updates using the @@ -65,6 +69,9 @@ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb); * occurs. * @param cb A pointer to a callback function following the * NethogsMonitorCallback definition + * @param filter A string (char array) pcap filter to restrict what packets + * are captured, or NULL. The filter string format is the same as that of + * tcpdump(1); for full details, see the man page for pcap-filter(7). * @param devc number of values in devicenames array * @param devicenames pointer to array of devicenames (char arrays) * @param all when false, loopback interface and down/not running interfaces @@ -72,7 +79,8 @@ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb); */ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop_devices(NethogsMonitorCallback cb, - int devc, char **devicenames, + char *filter, int devc, + char **devicenames, bool all); /** diff --git a/src/main.cpp b/src/main.cpp index 6892996..0233481 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -27,7 +27,8 @@ static void help(bool iserror) { // output << "usage: nethogs [-V] [-b] [-d seconds] [-t] [-p] [-f (eth|ppp))] // [device [device [device ...]]]\n"; output << "usage: nethogs [-V] [-h] [-b] [-d seconds] [-v mode] [-c count] " - "[-t] [-p] [-s] [-a] [-l] [device [device [device ...]]]\n"; + "[-t] [-p] [-s] [-a] [-l] [-f filter] " + "[device [device [device ...]]]\n"; output << " -V : prints version.\n"; output << " -h : prints this help.\n"; output << " -b : bughunt mode - implies tracemode.\n"; @@ -45,6 +46,7 @@ static void help(bool iserror) { output << " -a : monitor all devices, even loopback/stopped ones.\n"; output << " device : device(s) to monitor. default is all " "interfaces up and running excluding loopback\n"; + output << " -f : specify string pcap filter (like tcpdump).\n"; output << std::endl; output << "When nethogs is running, press:\n"; output << " q: quit\n"; @@ -133,9 +135,10 @@ int main(int argc, char **argv) { int promisc = 0; bool all = false; + char *filter = NULL; int opt; - while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:la")) != -1) { + while ((opt = getopt(argc, argv, "Vhbtpsd:v:c:laf:")) != -1) { switch (opt) { case 'V': versiondisplay(); @@ -171,6 +174,9 @@ int main(int argc, char **argv) { case 'a': all = true; break; + case 'f': + filter = optarg; + break; default: help(true); exit(EXIT_FAILURE); @@ -216,16 +222,20 @@ int main(int argc, char **argv) { char errbuf[PCAP_ERRBUF_SIZE]; + int nb_devices = 0; + int nb_failed_devices = 0; + handle *handles = NULL; device *current_dev = devices; while (current_dev != NULL) { + ++nb_devices; if (!getLocal(current_dev->name, tracemode)) { forceExit(false, "getifaddrs failed while establishing local IP."); } dp_handle *newhandle = - dp_open_live(current_dev->name, BUFSIZ, promisc, 100, errbuf); + dp_open_live(current_dev->name, BUFSIZ, promisc, 100, filter, errbuf); if (newhandle != NULL) { dp_addcb(newhandle, dp_packet_ip, process_ip); dp_addcb(newhandle, dp_packet_ip6, process_ip6); @@ -258,11 +268,16 @@ int main(int argc, char **argv) { } else { fprintf(stderr, "Error opening handler for device %s\n", current_dev->name); + ++nb_failed_devices; } current_dev = current_dev->next; } + if (nb_devices == nb_failed_devices) { + forceExit(false, "Error opening pcap handlers for all devices.\n"); + } + signal(SIGINT, &quit_cb); struct dpargs *userdata = (dpargs *)malloc(sizeof(struct dpargs)); From 76ced1b8d8619af231830f64e0b038db23bfb8e6 Mon Sep 17 00:00:00 2001 From: Jason Antman Date: Sun, 27 Aug 2017 10:37:13 -0400 Subject: [PATCH 2/2] issue #119 - add warnings about capture filter being experimental --- contrib/python-wrapper.py | 6 ++++-- src/libnethogs.h | 16 ++++++++++------ src/main.cpp | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/python-wrapper.py b/contrib/python-wrapper.py index 35897f0..5a6fe0d 100644 --- a/contrib/python-wrapper.py +++ b/contrib/python-wrapper.py @@ -27,8 +27,10 @@ device_names = [] # https://github.com/raboof/nethogs/archive/v0.8.5.tar.gz LIBRARY_NAME = 'libnethogs.so.0.8.5' -# Optionally, specify a capture filter in pcap format (same as used by -# tcpdump(1)) or None. See `man pcap-filter` for full information. +# EXPERIMENTAL: Optionally, specify a capture filter in pcap format (same as +# used by tcpdump(1)) or None. See `man pcap-filter` for full information. +# Note that this feature is EXPERIMENTAL (in libnethogs) and may be removed or +# changed in an incompatible way in a future release. # example: # FILTER = 'port 80 or port 8080 or port 443' FILTER = None diff --git a/src/libnethogs.h b/src/libnethogs.h index af02ad5..6b2f801 100644 --- a/src/libnethogs.h +++ b/src/libnethogs.h @@ -53,9 +53,11 @@ typedef void (*NethogsMonitorCallback)(int action, * occurs. * @param cb A pointer to a callback function following the * NethogsMonitorCallback definition - * @param filter A string (char array) pcap filter to restrict what packets - * are captured, or NULL. The filter string format is the same as that of - * tcpdump(1); for full details, see the man page for pcap-filter(7). + * @param filter EXPERIMENTAL: A string (char array) pcap filter to restrict + * what packets are captured, or NULL. The filter string format is the same as + * that of tcpdump(1); for full details, see the man page for pcap-filter(7). + * Note that this is EXPERIMENTAL, and may be removed or changed in a future + * version. */ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb, @@ -69,9 +71,11 @@ NETHOGS_DSO_VISIBLE int nethogsmonitor_loop(NethogsMonitorCallback cb, * occurs. * @param cb A pointer to a callback function following the * NethogsMonitorCallback definition - * @param filter A string (char array) pcap filter to restrict what packets - * are captured, or NULL. The filter string format is the same as that of - * tcpdump(1); for full details, see the man page for pcap-filter(7). + * @param filter EXPERIMENTAL: A string (char array) pcap filter to restrict + * what packets are captured, or NULL. The filter string format is the same as + * that of tcpdump(1); for full details, see the man page for pcap-filter(7). + * Note that this is EXPERIMENTAL, and may be removed or changed in a future + * version. * @param devc number of values in devicenames array * @param devicenames pointer to array of devicenames (char arrays) * @param all when false, loopback interface and down/not running interfaces diff --git a/src/main.cpp b/src/main.cpp index 0233481..729adc2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,9 +44,10 @@ static void help(bool iserror) { output << " -s : sort output by sent column.\n"; output << " -l : display command line.\n"; output << " -a : monitor all devices, even loopback/stopped ones.\n"; + output << " -f : EXPERIMENTAL: specify string pcap filter (like tcpdump)." + " This may be removed or changed in a future version.\n"; output << " device : device(s) to monitor. default is all " "interfaces up and running excluding loopback\n"; - output << " -f : specify string pcap filter (like tcpdump).\n"; output << std::endl; output << "When nethogs is running, press:\n"; output << " q: quit\n";