diff options
author | dillinger79 <dxnanos@gmail.com> | 2018-03-01 14:13:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-01 14:13:50 +0200 |
commit | b19cd086b41ed17217537664b10b29a7055e3f72 (patch) | |
tree | 9eb61225d2b9f2962f553888cdc1cbe86b93396d /example/ndpiReader.c | |
parent | 9f8fedb3b1f3e1a380baf1600a12096aaf2e2953 (diff) | |
parent | e935ee77bf1802f2bf47afd5d7a27eb1b5116c47 (diff) |
Merge pull request #3 from ntop/dev
update to latest
Diffstat (limited to 'example/ndpiReader.c')
-rw-r--r-- | example/ndpiReader.c | 2182 |
1 files changed, 2017 insertions, 165 deletions
diff --git a/example/ndpiReader.c b/example/ndpiReader.c index e1777d7da..38ce75b14 100644 --- a/example/ndpiReader.c +++ b/example/ndpiReader.c @@ -1,7 +1,7 @@ /* * ndpiReader.c * - * Copyright (C) 2011-16 - ntop.org + * Copyright (C) 2011-17 - ntop.org * * nDPI is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -24,11 +24,11 @@ #endif #include <stdio.h> #include <stdlib.h> +#include <getopt.h> #ifdef WIN32 #include <winsock2.h> /* winsock.h is included automatically */ #include <process.h> #include <io.h> -#include <getopt.h> #define getopt getopt____ #else #include <unistd.h> @@ -42,8 +42,13 @@ #include <pthread.h> #include <sys/socket.h> #include <assert.h> -#include "../config.h" +#include <math.h> #include "ndpi_api.h" +#include "uthash.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <libgen.h> #ifdef HAVE_JSON_C #include <json.h> @@ -52,26 +57,38 @@ #include "ndpi_util.h" /** Client parameters **/ -static char *_pcap_file[MAX_NUM_READER_THREADS]; /**< Ingress pcap file/interafaces */ +static char *_pcap_file[MAX_NUM_READER_THREADS]; /**< Ingress pcap file/interfaces */ static FILE *playlist_fp[MAX_NUM_READER_THREADS] = { NULL }; /**< Ingress playlist */ -static FILE *results_file = NULL; -static char *results_path = NULL; -static char *_bpf_filter = NULL; /**< bpf filter */ -static char *_protoFilePath = NULL; /**< Protocol file path */ +static FILE *results_file = NULL; +static char *results_path = NULL; +static char * bpfFilter = NULL; /**< bpf filter */ +static char *_protoFilePath = NULL; /**< Protocol file path */ #ifdef HAVE_JSON_C -static char *_jsonFilePath = NULL; /**< JSON file path */ +static char *_statsFilePath = NULL; /**< Top stats file path */ +static char *_diagnoseFilePath = NULL; /**< Top stats file path */ +static char *_jsonFilePath = NULL; /**< JSON file path */ +static FILE *stats_fp = NULL; /**< for Top Stats JSON file */ #endif #ifdef HAVE_JSON_C static json_object *jArray_known_flows, *jArray_unknown_flows; +static json_object *jArray_topStats; #endif static u_int8_t live_capture = 0; static u_int8_t undetected_flows_deleted = 0; /** User preferences **/ -static u_int8_t enable_protocol_guess = 1, verbose = 0, nDPI_traceLevel = 0, json_flag = 0; +static u_int8_t enable_protocol_guess = 1, verbose = 0, json_flag = 0; +int nDPI_LogLevel = 0; +char *_debug_protocols = NULL; +static u_int8_t stats_flag = 0, bpf_filter_flag = 0; +#ifdef HAVE_JSON_C +static u_int8_t file_first_time = 1; +#endif +static u_int32_t pcap_analysis_duration = (u_int32_t)-1; static u_int16_t decode_tunnels = 0; static u_int16_t num_loops = 1; static u_int8_t shutdown_app = 0, quiet_mode = 0; static u_int8_t num_threads = 1; +static struct timeval begin, end; #ifdef linux static int core_affinity[MAX_NUM_READER_THREADS]; #endif @@ -80,10 +97,92 @@ static struct timeval pcap_start, pcap_end; static time_t capture_for = 0; static time_t capture_until = 0; static u_int32_t num_flows; +static struct ndpi_detection_module_struct *ndpi_info_mod = NULL; + +struct flow_info { + struct ndpi_flow_info *flow; + u_int16_t thread_id; +}; + +static struct flow_info *all_flows; + + +struct info_pair { + u_int32_t addr; + u_int8_t version; /* IP version */ + char proto[16]; /*app level protocol*/ + int count; +}; + +typedef struct node_a{ + u_int32_t addr; + u_int8_t version; /* IP version */ + char proto[16]; /*app level protocol*/ + int count; + struct node_a *left, *right; +}addr_node; + +struct port_stats { + u_int32_t port; /* we'll use this field as the key */ + u_int32_t num_pkts, num_bytes; + u_int32_t num_flows; + u_int32_t num_addr; /*number of distinct IP addresses */ + u_int32_t cumulative_addr; /*cumulative some of IP addresses */ + addr_node *addr_tree; /* tree of distinct IP addresses */ + struct info_pair top_ip_addrs[MAX_NUM_IP_ADDRESS]; + u_int8_t hasTopHost; /* as boolean flag*/ + u_int32_t top_host; /*host that is contributed to > 95% of traffic*/ + u_int8_t version; /* top host's ip version */ + char proto[16]; /*application level protocol of top host */ + UT_hash_handle hh; /* makes this structure hashable */ +}; + +struct port_stats *srcStats = NULL, *dstStats = NULL; + + +// struct to hold count of flows received by destination ports +struct port_flow_info { + u_int32_t port; /* key */ + u_int32_t num_flows; + UT_hash_handle hh; +}; + +// struct to hold single packet tcp flows sent by source ip address +struct single_flow_info { + u_int32_t saddr; /* key */ + u_int8_t version; /* IP version */ + struct port_flow_info *ports; + u_int32_t tot_flows; + UT_hash_handle hh; +}; + +struct single_flow_info *scannerHosts = NULL; + +// struct to hold top receiver hosts +struct receiver { + u_int32_t addr; /* key */ + u_int8_t version; /* IP version */ + u_int32_t num_pkts; + UT_hash_handle hh; +}; + +struct receiver *receivers = NULL, *topReceivers = NULL; + + +struct ndpi_packet_trailer { + u_int32_t magic; /* 0x19682017 */ + u_int16_t master_protocol /* e.g. HTTP */, app_protocol /* e.g. FaceBook */; + char name[16]; +}; + +static pcap_dumper_t *extcap_dumper = NULL; +static char extcap_buf[16384]; +static char *extcap_capture_fifo = NULL; +static u_int16_t extcap_packet_filter = (u_int16_t)-1; // struct associated to a workflow for a thread struct reader_thread { - struct ndpi_workflow * workflow; + struct ndpi_workflow *workflow; pthread_t pthread; u_int64_t last_idle_scan_time; u_int32_t idle_scan_idx; @@ -104,31 +203,40 @@ typedef struct ndpi_id { u_int32_t current_ndpi_memory = 0, max_ndpi_memory = 0; -/********************** FUNCTIONS ********************* */ +void test_lib(); /* Forward */ + +/* ********************************** */ +#ifdef DEBUG_TRACE +FILE *trace = NULL; +#endif + +/********************** FUNCTIONS ********************* */ /** * @brief Set main components necessary to the detection */ static void setupDetection(u_int16_t thread_id, pcap_t * pcap_handle); - /** * @brief Print help instructions */ static void help(u_int long_help) { printf("Welcome to nDPI %s\n\n", ndpi_revision()); - printf("ndpiReader -i <file|device> [-f <filter>][-s <duration>]\n" + printf("ndpiReader -i <file|device> [-f <filter>][-s <duration>][-m <duration>]\n" " [-p <protos>][-l <loops> [-q][-d][-h][-t][-v <level>]\n" - " [-n <threads>] [-w <file>] [-j <file>]\n\n" + " [-n <threads>] [-w <file>] [-j <file>] [-x <file>] \n\n" "Usage:\n" - " -i <file.pcap|device> | Specify a pcap file/playlist to read packets from or a device for live capture (comma-separated list)\n" + " -i <file.pcap|device> | Specify a pcap file/playlist to read packets from or a\n" + " | device for live capture (comma-separated list)\n" " -f <BPF filter> | Specify a BPF filter for filtering selected traffic\n" " -s <duration> | Maximum capture duration in seconds (live traffic capture only)\n" + " -m <duration> | Split analysis duration in <duration> max seconds\n" " -p <file>.protos | Specify a protocol file (eg. protos.txt)\n" " -l <num loops> | Number of detection loops (test only)\n" - " -n <num threads> | Number of threads. Default: number of interfaces in -i. Ignored with pcap files.\n" + " -n <num threads> | Number of threads. Default: number of interfaces in -i.\n" + " | Ignored with pcap files.\n" " -j <file.json> | Specify a file to write the content of packets in .json format\n" #ifdef linux " -g <id:id...> | Thread affinity mask (one core id per thread)\n" @@ -140,41 +248,243 @@ static void help(u_int long_help) { " -w <path> | Write test output on the specified file. This is useful for\n" " | testing purposes in order to compare results across runs\n" " -h | This help\n" - " -v <1|2> | Verbose 'unknown protocol' packet print. 1=verbose, 2=very verbose\n"); + " -v <1|2|3> | Verbose 'unknown protocol' packet print.\n" + " | 1 = verbose\n" + " | 2 = very verbose\n" + " | 3 = port stats\n" + " -V <1-4> | nDPI logging level\n" + " | 1 - trace, 2 - debug, 3 - full debug\n" + " | >3 - full debug + dbg_proto = all\n" + " -b <file.json> | Specify a file to write port based diagnose statistics\n" + " -x <file.json> | Produce bpf filters for specified diagnose file. Use\n" + " | this option only for .json files generated with -b flag.\n"); + + +#ifndef WIN32 + printf("\nExcap (wireshark) options:\n" + " --extcap-interfaces\n" + " --extcap-version\n" + " --extcap-dlts\n" + " --extcap-interface <name>\n" + " --extcap-config\n" + " --capture\n" + " --extcap-capture-filter\n" + " --fifo <path to file or pipe>\n" + " --debug\n" + " --dbg-proto proto|num[,...]\n" + ); +#endif if(long_help) { printf("\n\nSupported protocols:\n"); num_threads = 1; - setupDetection(0, NULL); - ndpi_dump_protocols(ndpi_thread_info[0].workflow->ndpi_struct); + ndpi_dump_protocols(ndpi_info_mod); } exit(!long_help); } +static struct option longopts[] = { + /* mandatory extcap options */ + { "extcap-interfaces", no_argument, NULL, '0'}, + { "extcap-version", optional_argument, NULL, '1'}, + { "extcap-dlts", no_argument, NULL, '2'}, + { "extcap-interface", required_argument, NULL, '3'}, + { "extcap-config", no_argument, NULL, '4'}, + { "capture", no_argument, NULL, '5'}, + { "extcap-capture-filter", required_argument, NULL, '6'}, + { "fifo", required_argument, NULL, '7'}, + { "debug", no_argument, NULL, '8'}, + { "dbg-proto", required_argument, NULL, 257}, + { "ndpi-proto-filter", required_argument, NULL, '9'}, + + /* ndpiReader options */ + { "enable-protocol-guess", no_argument, NULL, 'd'}, + { "interface", required_argument, NULL, 'i'}, + { "filter", required_argument, NULL, 'f'}, + { "cpu-bind", required_argument, NULL, 'g'}, + { "loops", required_argument, NULL, 'l'}, + { "num-threads", required_argument, NULL, 'n'}, + + { "protos", required_argument, NULL, 'p'}, + { "capture-duration", required_argument, NULL, 's'}, + { "decode-tunnels", no_argument, NULL, 't'}, + { "revision", no_argument, NULL, 'r'}, + { "verbose", no_argument, NULL, 'v'}, + { "version", no_argument, NULL, 'V'}, + { "help", no_argument, NULL, 'h'}, + { "json", required_argument, NULL, 'j'}, + { "result-path", required_argument, NULL, 'w'}, + { "quiet", no_argument, NULL, 'q'}, + + {0, 0, 0, 0} +}; + +/* ********************************** */ + +void extcap_interfaces() { + printf("extcap {version=%s}\n", ndpi_revision()); + printf("interface {value=ndpi}{display=nDPI interface}\n"); + exit(0); +} + +/* ********************************** */ + +void extcap_dlts() { + u_int dlts_number = DLT_EN10MB; + printf("dlt {number=%u}{name=%s}{display=%s}\n", dlts_number, "ndpi", "nDPI Interface"); + exit(0); +} + +/* ********************************** */ + +struct ndpi_proto_sorter { + int id; + char name[16]; +}; + +int cmpProto(const void *_a, const void *_b) { + struct ndpi_proto_sorter *a = (struct ndpi_proto_sorter*)_a; + struct ndpi_proto_sorter *b = (struct ndpi_proto_sorter*)_b; + + return(strcmp(a->name, b->name)); +} + +int cmpFlows(const void *_a, const void *_b) { + struct ndpi_flow_info *fa = ((struct flow_info*)_a)->flow; + struct ndpi_flow_info *fb = ((struct flow_info*)_b)->flow; + uint64_t a_size = fa->src2dst_bytes + fa->dst2src_bytes; + uint64_t b_size = fb->src2dst_bytes + fb->dst2src_bytes; + if(a_size != b_size) + return a_size < b_size ? 1 : -1; + +// copy from ndpi_workflow_node_cmp(); + + if(fa->ip_version < fb->ip_version ) return(-1); else { if(fa->ip_version > fb->ip_version ) return(1); } + if(fa->protocol < fb->protocol ) return(-1); else { if(fa->protocol > fb->protocol ) return(1); } + if(htonl(fa->src_ip) < htonl(fb->src_ip) ) return(-1); else { if(htonl(fa->src_ip) > htonl(fb->src_ip) ) return(1); } + if(htons(fa->src_port) < htons(fb->src_port)) return(-1); else { if(htons(fa->src_port) > htons(fb->src_port)) return(1); } + if(htonl(fa->dst_ip) < htonl(fb->dst_ip) ) return(-1); else { if(htonl(fa->dst_ip) > htonl(fb->dst_ip) ) return(1); } + if(htons(fa->dst_port) < htons(fb->dst_port)) return(-1); else { if(htons(fa->dst_port) > htons(fb->dst_port)) return(1); } + return(0); +} + +void extcap_config() { + int i, argidx = 0; + struct ndpi_proto_sorter *protos; + + /* -i <interface> */ + printf("arg {number=%d}{call=-i}{display=Capture Interface}{type=string}" + "{tooltip=The interface name}\n", argidx++); + printf("arg {number=%d}{call=-i}{display=Pcap File to Analyze}{type=fileselect}" + "{tooltip=The pcap file to analyze (if the interface is unspecified)}\n", argidx++); + + protos = (struct ndpi_proto_sorter*)malloc(sizeof(struct ndpi_proto_sorter) * ndpi_info_mod->ndpi_num_supported_protocols); + if(!protos) exit(0); + + for(i=0; i<(int) ndpi_info_mod->ndpi_num_supported_protocols; i++) { + protos[i].id = i; + snprintf(protos[i].name, sizeof(protos[i].name), "%s", ndpi_info_mod->proto_defaults[i].protoName); + } + + qsort(protos, ndpi_info_mod->ndpi_num_supported_protocols, sizeof(struct ndpi_proto_sorter), cmpProto); + + printf("arg {number=%d}{call=-9}{display=nDPI Protocol Filter}{type=selector}" + "{tooltip=nDPI Protocol to be filtered}\n", argidx); + + printf("value {arg=%d}{value=%d}{display=%s}\n", argidx, -1, "All Protocols (no nDPI filtering)"); + + for(i=0; i<(int)ndpi_info_mod->ndpi_num_supported_protocols; i++) + printf("value {arg=%d}{value=%d}{display=%s (%d)}\n", argidx, protos[i].id, + protos[i].name, protos[i].id); + + free(protos); + + exit(0); +} + +/* ********************************** */ + +void extcap_capture() { +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, " #### %s #### \n", __FUNCTION__); +#endif + + if((extcap_dumper = pcap_dump_open(pcap_open_dead(DLT_EN10MB, 16384 /* MTU */), + extcap_capture_fifo)) == NULL) { + fprintf(stderr, "Unable to open the pcap dumper on %s", extcap_capture_fifo); + +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, "Unable to open the pcap dumper on %s\n", + extcap_capture_fifo); +#endif + return; + } + +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, "Starting packet capture [%p]\n", extcap_dumper); +#endif +} + +/* ********************************** */ + /** * @brief Option parser */ static void parseOptions(int argc, char **argv) { - + int option_idx = 0, do_capture = 0; char *__pcap_file = NULL, *bind_mask = NULL; int thread_id, opt; #ifdef linux u_int num_cores = sysconf(_SC_NPROCESSORS_ONLN); #endif - while ((opt = getopt(argc, argv, "df:g:i:hp:l:s:tv:V:n:j:rp:w:q")) != EOF) { +#ifdef DEBUG_TRACE + trace = fopen("/tmp/ndpiReader.log", "a"); + + if(trace) fprintf(trace, " #### %s #### \n", __FUNCTION__); +#endif + + while ((opt = getopt_long(argc, argv, "df:g:i:hp:l:s:tv:V:n:j:rp:w:q0123:456:7:89:m:b:x:", longopts, &option_idx)) != EOF) { +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, " #### -%c [%s] #### \n", opt, optarg ? optarg : ""); +#endif + switch (opt) { case 'd': enable_protocol_guess = 0; break; case 'i': + case '3': _pcap_file[0] = optarg; break; + case 'b': +#ifndef HAVE_JSON_C + printf("WARNING: this copy of ndpiReader has been compiled without JSON-C: json export disabled\n"); +#else + _statsFilePath = optarg; + stats_flag = 1; +#endif + break; + + case 'm': + pcap_analysis_duration = atol(optarg); + break; + + case 'x': +#ifndef HAVE_JSON_C + printf("WARNING: this copy of ndpiReader has been compiled without JSON-C: json export disabled\n"); +#else + _diagnoseFilePath = optarg; + bpf_filter_flag = 1; +#endif + break; + case 'f': - _bpf_filter = optarg; + case '6': + bpfFilter = optarg; break; case 'g': @@ -211,8 +521,12 @@ static void parseOptions(int argc, char **argv) { break; case 'V': - printf("%d\n",atoi(optarg) ); - nDPI_traceLevel = atoi(optarg); + nDPI_LogLevel = atoi(optarg); + if(nDPI_LogLevel < 0) nDPI_LogLevel = 0; + if(nDPI_LogLevel > 3) { + nDPI_LogLevel = 3; + _debug_protocols = strdup("all"); + } break; case 'h': @@ -238,6 +552,46 @@ static void parseOptions(int argc, char **argv) { case 'q': quiet_mode = 1; + nDPI_LogLevel = 0; + break; + + /* Extcap */ + case '0': + extcap_interfaces(); + break; + + case '1': + printf("extcap {version=%s}\n", ndpi_revision()); + break; + + case '2': + extcap_dlts(); + break; + + case '4': + extcap_config(); + break; + + case '5': + do_capture = 1; + break; + + case '7': + extcap_capture_fifo = strdup(optarg); + break; + + case '8': + nDPI_LogLevel = NDPI_LOG_DEBUG_EXTRA; + _debug_protocols = strdup("all"); + break; + + case '9': + extcap_packet_filter = ndpi_get_proto_by_name(ndpi_info_mod, optarg); + if (extcap_packet_filter == NDPI_PROTOCOL_UNKNOWN) extcap_packet_filter = atoi(optarg); + break; + + case 257: + _debug_protocols = strdup(optarg); break; default: @@ -246,36 +600,47 @@ static void parseOptions(int argc, char **argv) { } } - // check parameters - if(_pcap_file[0] == NULL || strcmp(_pcap_file[0], "") == 0) { - help(0); - } + if(!bpf_filter_flag) { + if(do_capture) { + quiet_mode = 1; + extcap_capture(); + } - if(strchr(_pcap_file[0], ',')) { /* multiple ingress interfaces */ - num_threads = 0; /* setting number of threads = number of interfaces */ - __pcap_file = strtok(_pcap_file[0], ","); - while (__pcap_file != NULL && num_threads < MAX_NUM_READER_THREADS) { - _pcap_file[num_threads++] = __pcap_file; - __pcap_file = strtok(NULL, ","); + // check parameters + if(!bpf_filter_flag && (_pcap_file[0] == NULL || strcmp(_pcap_file[0], "") == 0)) { + help(0); } - } else { - if(num_threads > MAX_NUM_READER_THREADS) num_threads = MAX_NUM_READER_THREADS; - for(thread_id = 1; thread_id < num_threads; thread_id++) - _pcap_file[thread_id] = _pcap_file[0]; - } -#ifdef linux - for(thread_id = 0; thread_id < num_threads; thread_id++) - core_affinity[thread_id] = -1; + if(strchr(_pcap_file[0], ',')) { /* multiple ingress interfaces */ + num_threads = 0; /* setting number of threads = number of interfaces */ + __pcap_file = strtok(_pcap_file[0], ","); + while (__pcap_file != NULL && num_threads < MAX_NUM_READER_THREADS) { + _pcap_file[num_threads++] = __pcap_file; + __pcap_file = strtok(NULL, ","); + } + } else { + if(num_threads > MAX_NUM_READER_THREADS) num_threads = MAX_NUM_READER_THREADS; + for(thread_id = 1; thread_id < num_threads; thread_id++) + _pcap_file[thread_id] = _pcap_file[0]; + } - if(num_cores > 1 && bind_mask != NULL) { - char *core_id = strtok(bind_mask, ":"); - thread_id = 0; - while (core_id != NULL && thread_id < num_threads) { - core_affinity[thread_id++] = atoi(core_id) % num_cores; - core_id = strtok(NULL, ":"); +#ifdef linux + for(thread_id = 0; thread_id < num_threads; thread_id++) + core_affinity[thread_id] = -1; + + if(num_cores > 1 && bind_mask != NULL) { + char *core_id = strtok(bind_mask, ":"); + thread_id = 0; + while (core_id != NULL && thread_id < num_threads) { + core_affinity[thread_id++] = atoi(core_id) % num_cores; + core_id = strtok(NULL, ":"); + } } +#endif } + +#ifdef DEBUG_TRACE + if(trace) fclose(trace); #endif } @@ -349,25 +714,27 @@ char* intoaV4(u_int32_t addr, char* buf, u_int16_t bufLen) { /** * @brief Print the flow */ -static void printFlow(u_int16_t thread_id, struct ndpi_flow_info *flow) { +static void printFlow(u_int16_t id, struct ndpi_flow_info *flow, u_int16_t thread_id) { #ifdef HAVE_JSON_C json_object *jObj; #endif FILE *out = results_file ? results_file : stdout; + if((verbose != 1) && (verbose != 2)) + return; + if(!json_flag) { - fprintf(out, "\t%u", ++num_flows); + fprintf(out, "\t%u", id); + + fprintf(out, "\t%s ", ipProto2Name(flow->protocol)); - fprintf(out, "\t%s %s%s%s:%u <-> %s%s%s:%u ", - ipProto2Name(flow->protocol), + fprintf(out, "%s%s%s:%u %s %s%s%s:%u ", (flow->ip_version == 6) ? "[" : "", - flow->lower_name, - (flow->ip_version == 6) ? "]" : "", - ntohs(flow->lower_port), + flow->src_name, (flow->ip_version == 6) ? "]" : "", ntohs(flow->src_port), + flow->bidirectional ? "<->" : "->", (flow->ip_version == 6) ? "[" : "", - flow->upper_name, - (flow->ip_version == 6) ? "]" : "", - ntohs(flow->upper_port)); + flow->dst_name, (flow->ip_version == 6) ? "]" : "", ntohs(flow->dst_port) + ); if(flow->vlan_id > 0) fprintf(out, "[VLAN: %u]", flow->vlan_id); @@ -375,20 +742,24 @@ static void printFlow(u_int16_t thread_id, struct ndpi_flow_info *flow) { char buf[64]; fprintf(out, "[proto: %u.%u/%s]", - flow->detected_protocol.master_protocol, flow->detected_protocol.protocol, + flow->detected_protocol.master_protocol, flow->detected_protocol.app_protocol, ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol, buf, sizeof(buf))); } else fprintf(out, "[proto: %u/%s]", - flow->detected_protocol.protocol, - ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.protocol)); + flow->detected_protocol.app_protocol, + ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.app_protocol)); - fprintf(out, "[%u pkts/%llu bytes]", - flow->packets, (long long unsigned int) flow->bytes); + fprintf(out, "[%u pkts/%llu bytes ", flow->src2dst_packets, (long long unsigned int) flow->src2dst_bytes); + fprintf(out, "%s %u pkts/%llu bytes]", + (flow->dst2src_packets > 0) ? "<->" : "->", + flow->dst2src_packets, (long long unsigned int) flow->dst2src_bytes); if(flow->host_server_name[0] != '\0') fprintf(out, "[Host: %s]", flow->host_server_name); - if(flow->ssl.client_certificate[0] != '\0') fprintf(out, "[SSL client: %s]", flow->ssl.client_certificate); - if(flow->ssl.server_certificate[0] != '\0') fprintf(out, "[SSL server: %s]", flow->ssl.server_certificate); + if(flow->info[0] != '\0') fprintf(out, "[%s]", flow->info); + + if(flow->ssh_ssl.client_info[0] != '\0') fprintf(out, "[client: %s]", flow->ssh_ssl.client_info); + if(flow->ssh_ssl.server_info[0] != '\0') fprintf(out, "[server: %s]", flow->ssh_ssl.server_info); if(flow->bittorent_hash[0] != '\0') fprintf(out, "[BT Hash: %s]", flow->bittorent_hash); fprintf(out, "\n"); @@ -397,46 +768,46 @@ static void printFlow(u_int16_t thread_id, struct ndpi_flow_info *flow) { jObj = json_object_new_object(); json_object_object_add(jObj,"protocol",json_object_new_string(ipProto2Name(flow->protocol))); - json_object_object_add(jObj,"host_a.name",json_object_new_string(flow->lower_name)); - json_object_object_add(jObj,"host_a.port",json_object_new_int(ntohs(flow->lower_port))); - json_object_object_add(jObj,"host_b.name",json_object_new_string(flow->upper_name)); - json_object_object_add(jObj,"host_b.port",json_object_new_int(ntohs(flow->upper_port))); + json_object_object_add(jObj,"host_a.name",json_object_new_string(flow->src_name)); + json_object_object_add(jObj,"host_a.port",json_object_new_int(ntohs(flow->src_port))); + json_object_object_add(jObj,"host_b.name",json_object_new_string(flow->dst_name)); + json_object_object_add(jObj,"host_b.port",json_object_new_int(ntohs(flow->dst_port))); if(flow->detected_protocol.master_protocol) - json_object_object_add(jObj,"detected.masterprotocol",json_object_new_int(flow->detected_protocol.master_protocol)); + json_object_object_add(jObj,"detected.master_protocol",json_object_new_int(flow->detected_protocol.master_protocol)); - json_object_object_add(jObj,"detected.protocol",json_object_new_int(flow->detected_protocol.protocol)); + json_object_object_add(jObj,"detected.app_protocol",json_object_new_int(flow->detected_protocol.app_protocol)); if(flow->detected_protocol.master_protocol) { char tmp[256]; snprintf(tmp, sizeof(tmp), "%s.%s", ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.master_protocol), - ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.protocol)); + ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->detected_protocol.app_protocol)); json_object_object_add(jObj,"detected.protocol.name", json_object_new_string(tmp)); } else json_object_object_add(jObj,"detected.protocol.name", json_object_new_string(ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, - flow->detected_protocol.protocol))); + flow->detected_protocol.app_protocol))); - json_object_object_add(jObj,"packets",json_object_new_int(flow->packets)); - json_object_object_add(jObj,"bytes",json_object_new_int(flow->bytes)); + json_object_object_add(jObj,"packets",json_object_new_int(flow->src2dst_packets + flow->dst2src_packets)); + json_object_object_add(jObj,"bytes",json_object_new_int(flow->src2dst_bytes + flow->dst2src_bytes)); if(flow->host_server_name[0] != '\0') json_object_object_add(jObj,"host.server.name",json_object_new_string(flow->host_server_name)); - if((flow->ssl.client_certificate[0] != '\0') || (flow->ssl.server_certificate[0] != '\0')) { + if((flow->ssh_ssl.client_info[0] != '\0') || (flow->ssh_ssl.server_info[0] != '\0')) { json_object *sjObj = json_object_new_object(); - if(flow->ssl.client_certificate[0] != '\0') - json_object_object_add(sjObj, "client", json_object_new_string(flow->ssl.client_certificate)); + if(flow->ssh_ssl.client_info[0] != '\0') + json_object_object_add(sjObj, "client", json_object_new_string(flow->ssh_ssl.client_info)); - if(flow->ssl.server_certificate[0] != '\0') - json_object_object_add(sjObj, "server", json_object_new_string(flow->ssl.server_certificate)); + if(flow->ssh_ssl.server_info[0] != '\0') + json_object_object_add(sjObj, "server", json_object_new_string(flow->ssh_ssl.server_info)); - json_object_object_add(jObj, "ssl", sjObj); + json_object_object_add(jObj, "ssh_ssl", sjObj); } if(json_flag == 1) @@ -456,10 +827,13 @@ static void node_print_unknown_proto_walker(const void *node, ndpi_VISIT which, struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)node; u_int16_t thread_id = *((u_int16_t*)user_data); - if(flow->detected_protocol.protocol != NDPI_PROTOCOL_UNKNOWN) return; + if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN) return; - if((which == ndpi_preorder) || (which == ndpi_leaf)) /* Avoid walking the same node multiple times */ - printFlow(thread_id, flow); + if((which == ndpi_preorder) || (which == ndpi_leaf)) { + /* Avoid walking the same node multiple times */ + all_flows[num_flows].thread_id = thread_id, all_flows[num_flows].flow = flow; + num_flows++; + } } /** @@ -470,10 +844,13 @@ static void node_print_known_proto_walker(const void *node, ndpi_VISIT which, in struct ndpi_flow_info *flow = *(struct ndpi_flow_info**)node; u_int16_t thread_id = *((u_int16_t*)user_data); - if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) return; + if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) return; - if((which == ndpi_preorder) || (which == ndpi_leaf)) /* Avoid walking the same node multiple times */ - printFlow(thread_id, flow); + if((which == ndpi_preorder) || (which == ndpi_leaf)) { + /* Avoid walking the same node multiple times */ + all_flows[num_flows].thread_id = thread_id, all_flows[num_flows].flow = flow; + num_flows++; + } } @@ -484,15 +861,15 @@ static u_int16_t node_guess_undetected_protocol(u_int16_t thread_id, struct ndpi flow->detected_protocol = ndpi_guess_undetected_protocol(ndpi_thread_info[thread_id].workflow->ndpi_struct, flow->protocol, - ntohl(flow->lower_ip), - ntohs(flow->lower_port), - ntohl(flow->upper_ip), - ntohs(flow->upper_port)); + ntohl(flow->src_ip), + ntohs(flow->src_port), + ntohl(flow->dst_ip), + ntohs(flow->dst_port)); // printf("Guess state: %u\n", flow->detected_protocol); - if(flow->detected_protocol.protocol != NDPI_PROTOCOL_UNKNOWN) + if(flow->detected_protocol.app_protocol != NDPI_PROTOCOL_UNKNOWN) ndpi_thread_info[thread_id].workflow->stats.guessed_flow_protocols++; - return(flow->detected_protocol.protocol); + return(flow->detected_protocol.app_protocol); } @@ -500,7 +877,6 @@ static u_int16_t node_guess_undetected_protocol(u_int16_t thread_id, struct ndpi * @brief Proto Guess Walker */ static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) { - struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node; u_int16_t thread_id = *((u_int16_t *) user_data); @@ -509,18 +885,452 @@ static void node_proto_guess_walker(const void *node, ndpi_VISIT which, int dept flow->detected_protocol = ndpi_detection_giveup(ndpi_thread_info[0].workflow->ndpi_struct, flow->ndpi_flow); if(enable_protocol_guess) { - if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) { + if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) { node_guess_undetected_protocol(thread_id, flow); - // printFlow(thread_id, flow); } } - ndpi_thread_info[thread_id].workflow->stats.protocol_counter[flow->detected_protocol.protocol] += flow->packets; - ndpi_thread_info[thread_id].workflow->stats.protocol_counter_bytes[flow->detected_protocol.protocol] += flow->bytes; - ndpi_thread_info[thread_id].workflow->stats.protocol_flows[flow->detected_protocol.protocol]++; + process_ndpi_collected_info(ndpi_thread_info[thread_id].workflow, flow); + ndpi_thread_info[thread_id].workflow->stats.protocol_counter[flow->detected_protocol.app_protocol] += flow->src2dst_packets + flow->dst2src_packets; + ndpi_thread_info[thread_id].workflow->stats.protocol_counter_bytes[flow->detected_protocol.app_protocol] += flow->src2dst_bytes + flow->dst2src_bytes; + ndpi_thread_info[thread_id].workflow->stats.protocol_flows[flow->detected_protocol.app_protocol]++; + } +} + +/* *********************************************** */ + +void updateScanners(struct single_flow_info **scanners, u_int32_t saddr, + u_int8_t version, u_int32_t dport) { + struct single_flow_info *f; + struct port_flow_info *p; + + HASH_FIND_INT(*scanners, (int *)&saddr, f); + + if(f == NULL) { + f = (struct single_flow_info*)malloc(sizeof(struct single_flow_info)); + if(!f) return; + f->saddr = saddr; + f->version = version; + f->tot_flows = 1; + f->ports = NULL; + + p = (struct port_flow_info*)malloc(sizeof(struct port_flow_info)); + + if(!p) { + free(f); + return; + } else + p->port = dport, p->num_flows = 1; + + HASH_ADD_INT(f->ports, port, p); + HASH_ADD_INT(*scanners, saddr, f); + } else{ + struct port_flow_info *pp; + f->tot_flows++; + + HASH_FIND_INT(f->ports, (int *)&dport, pp); + + if(pp == NULL) { + pp = (struct port_flow_info*)malloc(sizeof(struct port_flow_info)); + if(!pp) return; + pp->port = dport, pp->num_flows = 1; + + HASH_ADD_INT(f->ports, port, pp); + } else + pp->num_flows++; + } +} + +/* *********************************************** */ + +int updateIpTree(u_int32_t key, u_int8_t version, + addr_node **vrootp, const char *proto) { + addr_node *q; + addr_node **rootp = vrootp; + + if(rootp == (addr_node **)0) + return 0; + + while (*rootp != (addr_node *)0) { + /* Knuth's T1: */ + if((version == (*rootp)->version) && (key == (*rootp)->addr)) { + /* T2: */ + return ++((*rootp)->count); + } + + rootp = (key < (*rootp)->addr) ? + &(*rootp)->left : /* T3: follow left branch */ + &(*rootp)->right; /* T4: follow right branch */ + } + + q = (addr_node *) malloc(sizeof(addr_node)); /* T5: key not found */ + if(q != (addr_node *)0) { /* make new node */ + *rootp = q; /* link new node to old */ + + q->addr = key; + q->version = version; + strncpy(q->proto, proto, sizeof(q->proto)); + q->count = UPDATED_TREE; + q->left = q->right = (addr_node *)0; + + return q->count; } + + return(0); +} +/* *********************************************** */ + +void freeIpTree(addr_node *root) { + if (root == NULL) + return; + + freeIpTree(root->left); + freeIpTree(root->right); + free(root); + root = NULL; } +/* *********************************************** */ + +void updateTopIpAddress(u_int32_t addr, u_int8_t version, const char *proto, + int count, struct info_pair top[], int size) { + struct info_pair pair; + int min = count; + int update = 0; + int min_i = 0; + int i; + + if(count == 0) return; + + pair.addr = addr; + pair.version = version; + pair.count = count; + strncpy(pair.proto, proto, sizeof(pair.proto)); + + for(i=0; i<size; i++) { + /* if the same ip with a bigger + count just update it */ + if(top[i].addr == addr) { + top[i].count = count; + return; + } + /* if array is not full yet + add it to the first empty place */ + if(top[i].count == 0) { + top[i] = pair; + return; + } + } + + /* if bigger than the smallest one, replace it */ + for(i=0; i<size; i++) { + if(top[i].count < count && top[i].count < min) { + min = top[i].count; + min_i = i; + update = 1; + } + } + + if(update) + top[min_i] = pair; +} + +/* *********************************************** */ + +static void updatePortStats(struct port_stats **stats, u_int32_t port, + u_int32_t addr, u_int8_t version, + u_int32_t num_pkts, u_int32_t num_bytes, + const char *proto) { + + struct port_stats *s = NULL; + int count = 0; + + HASH_FIND_INT(*stats, &port, s); + if(s == NULL) { + s = (struct port_stats*)calloc(1, sizeof(struct port_stats)); + if(!s) return; + + s->port = port, s->num_pkts = num_pkts, s->num_bytes = num_bytes; + s->num_addr = 1, s->cumulative_addr = 1; s->num_flows = 1; + + updateTopIpAddress(addr, version, proto, 1, s->top_ip_addrs, MAX_NUM_IP_ADDRESS); + + s->addr_tree = (addr_node *) malloc(sizeof(addr_node)); + if(!s->addr_tree) { + free(s); + return; + } + + s->addr_tree->addr = addr; + s->addr_tree->version = version; + strncpy(s->addr_tree->proto, proto, sizeof(s->addr_tree->proto)); + s->addr_tree->count = 1; + s->addr_tree->left = NULL; + s->addr_tree->right = NULL; + + HASH_ADD_INT(*stats, port, s); + } + else{ + count = updateIpTree(addr, version, &(*s).addr_tree, proto); + + if(count == UPDATED_TREE) s->num_addr++; + + if(count) { + s->cumulative_addr++; + updateTopIpAddress(addr, version, proto, count, s->top_ip_addrs, MAX_NUM_IP_ADDRESS); + } + + s->num_pkts += num_pkts, s->num_bytes += num_bytes, s->num_flows++; + } +} + +/* *********************************************** */ + +/* @brief heuristic choice for receiver stats */ +static int acceptable(u_int32_t num_pkts){ + return num_pkts > 5; +} + +/* *********************************************** */ + +static int receivers_sort(void *_a, void *_b) { + struct receiver *a = (struct receiver *)_a; + struct receiver *b = (struct receiver *)_b; + + return(b->num_pkts - a->num_pkts); +} + +/* *********************************************** */ + +static int receivers_sort_asc(void *_a, void *_b) { + struct receiver *a = (struct receiver *)_a; + struct receiver *b = (struct receiver *)_b; + + return(a->num_pkts - b->num_pkts); +} + +/* ***************************************************** */ +/*@brief removes first (size - max) elements from hash table. + * hash table is ordered in ascending order. +*/ +static struct receiver *cutBackTo(struct receiver **receivers, u_int32_t size, u_int32_t max) { + struct receiver *r, *tmp; + int i=0; + int count; + + if(size < max) //return the original table + return *receivers; + + count = size - max; + + HASH_ITER(hh, *receivers, r, tmp) { + if(i++ == count) + return r; + HASH_DEL(*receivers, r); + free(r); + } + + return(NULL); + +} + +/* *********************************************** */ +/*@brief merge first table to the second table. + * if element already in the second table + * then updates its value + * else adds it to the second table +*/ +static void mergeTables(struct receiver **primary, struct receiver **secondary) { + struct receiver *r, *s, *tmp; + + HASH_ITER(hh, *primary, r, tmp) { + HASH_FIND_INT(*secondary, (int *)&(r->addr), s); + if(s == NULL){ + s = (struct receiver *)malloc(sizeof(struct receiver)); + if(!s) return; + + s->addr = r->addr; + s->version = r->version; + s->num_pkts = r->num_pkts; + + HASH_ADD_INT(*secondary, addr, s); + } + else + s->num_pkts += r->num_pkts; + + HASH_DEL(*primary, r); + free(r); + } +} +/* *********************************************** */ + +static void deleteReceivers(struct receiver *receivers) { + struct receiver *current, *tmp; + + HASH_ITER(hh, receivers, current, tmp) { + HASH_DEL(receivers, current); + free(current); + } +} + +/* *********************************************** */ +/* implementation of: https://jeroen.massar.ch/presentations/files/FloCon2010-TopK.pdf + * + * if (table1.size < max1 || acceptable){ + * create new element and add to the table1 + * if (table1.size > max2) { + * cut table1 back to max1 + * merge table 1 to table2 + * if(table2.size > max1) + * cut table2 back to max1 + * } + * } + * else + * update table1 +*/ +static void updateReceivers(struct receiver **receivers, u_int32_t dst_addr, + u_int8_t version, u_int32_t num_pkts, + struct receiver **topReceivers) { + struct receiver *r; + u_int32_t size; + int a; + + HASH_FIND_INT(*receivers, (int *)&dst_addr, r); + if(r == NULL) { + if(((size = HASH_COUNT(*receivers)) < MAX_TABLE_SIZE_1) + || ((a = acceptable(num_pkts)) != 0)){ + r = (struct receiver *)malloc(sizeof(struct receiver)); + if(!r) return; + + r->addr = dst_addr; + r->version = version; + r->num_pkts = num_pkts; + + HASH_ADD_INT(*receivers, addr, r); + + if((size = HASH_COUNT(*receivers)) > MAX_TABLE_SIZE_2){ + + HASH_SORT(*receivers, receivers_sort_asc); + *receivers = cutBackTo(receivers, size, MAX_TABLE_SIZE_1); + mergeTables(receivers, topReceivers); + + if((size = HASH_COUNT(*topReceivers)) > MAX_TABLE_SIZE_1){ + HASH_SORT(*topReceivers, receivers_sort_asc); + *topReceivers = cutBackTo(topReceivers, size, MAX_TABLE_SIZE_1); + } + + *receivers = NULL; + } + } + } + else + r->num_pkts += num_pkts; +} + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +static void saveReceiverStats(json_object **jObj_group, + struct receiver **receivers, + u_int64_t total_pkt_count) { + + json_object *jArray_stats = json_object_new_array(); + struct receiver *r, *tmp; + int i = 0; + + HASH_ITER(hh, *receivers, r, tmp) { + json_object *jObj_stat = json_object_new_object(); + char addr_name[48]; + + if(r->version == IPVERSION) + inet_ntop(AF_INET, &(r->addr), addr_name, sizeof(addr_name)); + else + inet_ntop(AF_INET6, &(r->addr), addr_name, sizeof(addr_name)); + + + json_object_object_add(jObj_stat,"ip.address",json_object_new_string(addr_name)); + json_object_object_add(jObj_stat,"packets.number", json_object_new_int(r->num_pkts)); + json_object_object_add(jObj_stat,"packets.percent",json_object_new_double(((double)r->num_pkts) / total_pkt_count)); + + json_object_array_add(jArray_stats, jObj_stat); + + i++; + if(i >= 10) break; + } + + json_object_object_add(*jObj_group, "top.receiver.stats", jArray_stats); +} +#endif + + +/* *********************************************** */ + +static void deleteScanners(struct single_flow_info *scanners) { + struct single_flow_info *s, *tmp; + struct port_flow_info *p, *tmp2; + + HASH_ITER(hh, scanners, s, tmp) { + HASH_ITER(hh, s->ports, p, tmp2) { + HASH_DEL(s->ports, p); + free(p); + } + HASH_DEL(scanners, s); + free(s); + } +} + +/* *********************************************** */ + +static void deletePortsStats(struct port_stats *stats) { + struct port_stats *current_port, *tmp; + + HASH_ITER(hh, stats, current_port, tmp) { + HASH_DEL(stats, current_port); + freeIpTree(current_port->addr_tree); + free(current_port); + } +} + +/* *********************************************** */ + +/** + * @brief Ports stats + */ +static void port_stats_walker(const void *node, ndpi_VISIT which, int depth, void *user_data) { + if((which == ndpi_preorder) || (which == ndpi_leaf)) { /* Avoid walking the same node multiple times */ + struct ndpi_flow_info *flow = *(struct ndpi_flow_info **) node; + u_int16_t thread_id = *(int *)user_data; + u_int16_t sport, dport; + char proto[16]; + int r; + + sport = ntohs(flow->src_port), dport = ntohs(flow->dst_port); + + /* get app level protocol */ + if(flow->detected_protocol.master_protocol) + ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, + flow->detected_protocol, proto, sizeof(proto)); + else + strncpy(proto, ndpi_get_proto_name(ndpi_thread_info[thread_id].workflow->ndpi_struct, + flow->detected_protocol.app_protocol),sizeof(proto)); + + if(((r = strcmp(ipProto2Name(flow->protocol), "TCP")) == 0) + && (flow->src2dst_packets == 1) && (flow->dst2src_packets == 0)) { + updateScanners(&scannerHosts, flow->src_ip, flow->ip_version, dport); + } + + updateReceivers(&receivers, flow->dst_ip, flow->ip_version, + flow->src2dst_packets, &topReceivers); + + updatePortStats(&srcStats, sport, flow->src_ip, flow->ip_version, + flow->src2dst_packets, flow->src2dst_bytes, proto); + + updatePortStats(&dstStats, dport, flow->dst_ip, flow->ip_version, + flow->dst2src_packets, flow->dst2src_bytes, proto); + } +} + +/* *********************************************** */ /** * @brief Idle Scan Walker @@ -539,7 +1349,7 @@ static void node_idle_scan_walker(const void *node, ndpi_VISIT which, int depth, /* update stats */ node_proto_guess_walker(node, which, depth, user_data); - if((flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) && !undetected_flows_deleted) + if((flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) && !undetected_flows_deleted) undetected_flows_deleted = 1; ndpi_free_flow_info_half(flow); @@ -561,15 +1371,15 @@ static void on_protocol_discovered(struct ndpi_workflow * workflow, const u_int16_t thread_id = (uintptr_t) udata; - if(verbose > 1){ + if(verbose > 1) { if(enable_protocol_guess) { - if(flow->detected_protocol.protocol == NDPI_PROTOCOL_UNKNOWN) { - flow->detected_protocol.protocol = node_guess_undetected_protocol(thread_id, flow), + if(flow->detected_protocol.app_protocol == NDPI_PROTOCOL_UNKNOWN) { + flow->detected_protocol.app_protocol = node_guess_undetected_protocol(thread_id, flow), flow->detected_protocol.master_protocol = NDPI_PROTOCOL_UNKNOWN; } } - printFlow(thread_id, flow); + // printFlow(thread_id, flow); } } @@ -586,7 +1396,7 @@ static void debug_printf(u_int32_t protocol, void *id_struct, struct tm result; #endif - if(log_level <= nDPI_traceLevel) { + if(log_level <= nDPI_LogLevel) { char buf[8192], out_buf[8192]; char theDate[32]; const char *extra_msg = ""; @@ -656,7 +1466,6 @@ static void setupDetection(u_int16_t thread_id, pcap_t * pcap_handle) { * @brief End of detection and free flow */ static void terminateDetection(u_int16_t thread_id) { - ndpi_workflow_free(ndpi_thread_info[thread_id].workflow); } @@ -722,15 +1531,35 @@ char* formatPackets(float numPkts, char *buf) { static void json_init() { jArray_known_flows = json_object_new_array(); jArray_unknown_flows = json_object_new_array(); + jArray_topStats = json_object_new_array(); +} + +static void json_open_stats_file() { + if((file_first_time && ((stats_fp = fopen(_statsFilePath,"w")) == NULL)) + || + (!file_first_time && (stats_fp = fopen(_statsFilePath,"a")) == NULL)) { + printf("Error creating/opening file %s\n", _statsFilePath); + stats_flag = 0; + } + else file_first_time = 0; +} + +static void json_close_stats_file() { + json_object *jObjFinal = json_object_new_object(); + json_object_object_add(jObjFinal,"duration.in.seconds",json_object_new_int(pcap_analysis_duration)); + json_object_object_add(jObjFinal,"statistics", jArray_topStats); + fprintf(stats_fp,"%s\n",json_object_to_json_string(jObjFinal)); + fclose(stats_fp); + json_object_put(jObjFinal); } #endif +/* *********************************************** */ /** * @brief Bytes stats format */ char* formatBytes(u_int32_t howMuch, char *buf, u_int buf_len) { - char unit = 'B'; if(howMuch < 1024) { @@ -752,12 +1581,252 @@ char* formatBytes(u_int32_t howMuch, char *buf, u_int buf_len) { return(buf); } +/* *********************************************** */ + +static int port_stats_sort(void *_a, void *_b) { + struct port_stats *a = (struct port_stats*)_a; + struct port_stats *b = (struct port_stats*)_b; + + if(b->num_pkts == 0 && a->num_pkts == 0) + return(b->num_flows - a->num_flows); + + return(b->num_pkts - a->num_pkts); +} + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +static int scanners_sort(void *_a, void *_b) { + struct single_flow_info *a = (struct single_flow_info *)_a; + struct single_flow_info *b = (struct single_flow_info *)_b; + + return(b->tot_flows - a->tot_flows); +} + +/* *********************************************** */ + +static int scanners_port_sort(void *_a, void *_b) { + struct port_flow_info *a = (struct port_flow_info *)_a; + struct port_flow_info *b = (struct port_flow_info *)_b; + + return(b->num_flows - a->num_flows); +} + +#endif +/* *********************************************** */ + +static int info_pair_cmp (const void *_a, const void *_b) +{ + struct info_pair *a = (struct info_pair *)_a; + struct info_pair *b = (struct info_pair *)_b; + + return b->count - a->count; +} + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +static int top_stats_sort(void *_a, void *_b) { + struct port_stats *a = (struct port_stats*)_a; + struct port_stats *b = (struct port_stats*)_b; + + return(b->num_addr - a->num_addr); +} + +/* *********************************************** */ + +/** + * @brief Get port based top statistics + */ +static int getTopStats(struct port_stats *stats) { + struct port_stats *sp, *tmp; + struct info_pair inf; + u_int64_t total_ip_addrs = 0; + + HASH_ITER(hh, stats, sp, tmp) { + qsort(sp->top_ip_addrs, MAX_NUM_IP_ADDRESS, sizeof(struct info_pair), info_pair_cmp); + inf = sp->top_ip_addrs[0]; + + if(((inf.count * 100.0)/sp->cumulative_addr) > AGGRESSIVE_PERCENT) { + sp->hasTopHost = 1; + sp->top_host = inf.addr; + sp->version = inf.version; + strncpy(sp->proto, inf.proto, sizeof(sp->proto)); + } else + sp->hasTopHost = 0; + + total_ip_addrs += sp->num_addr; + } + + return total_ip_addrs; +} + +/* *********************************************** */ + +static void saveScannerStats(json_object **jObj_group, struct single_flow_info **scanners) { + struct single_flow_info *s, *tmp; + struct port_flow_info *p, *tmp2; + char addr_name[48]; + int i = 0, j = 0; + + json_object *jArray_stats = json_object_new_array(); + + HASH_SORT(*scanners, scanners_sort); // FIX + + HASH_ITER(hh, *scanners, s, tmp) { + json_object *jObj_stat = json_object_new_object(); + json_object *jArray_ports = json_object_new_array(); + + if(s->version == IPVERSION) + inet_ntop(AF_INET, &(s->saddr), addr_name, sizeof(addr_name)); + else + inet_ntop(AF_INET6, &(s->saddr), addr_name, sizeof(addr_name)); + + json_object_object_add(jObj_stat,"ip.address",json_object_new_string(addr_name)); + json_object_object_add(jObj_stat,"total.flows.number",json_object_new_int(s->tot_flows)); + + HASH_SORT(s->ports, scanners_port_sort); + + HASH_ITER(hh, s->ports, p, tmp2) { + json_object *jObj_port = json_object_new_object(); + + json_object_object_add(jObj_port,"port",json_object_new_int(p->port)); + json_object_object_add(jObj_port,"flows.number",json_object_new_int(p->num_flows)); + + json_object_array_add(jArray_ports, jObj_port); + + j++; + if(j >= 10) break; + } + + json_object_object_add(jObj_stat,"top.dst.ports",jArray_ports); + json_object_array_add(jArray_stats, jObj_stat); + + j = 0; + i++; + if(i >= 10) break; + } + + json_object_object_add(*jObj_group, "top.scanner.stats", jArray_stats); +} + +#endif + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +/* + * @brief Save Top Stats in json format + */ +static void saveTopStats(json_object **jObj_group, + struct port_stats **stats, + u_int8_t direction, + u_int64_t total_flow_count, + u_int64_t total_ip_addr) { + struct port_stats *s, *tmp; + char addr_name[48]; + int i = 0; + + json_object *jArray_stats = json_object_new_array(); + + + HASH_ITER(hh, *stats, s, tmp) { + + if((s->hasTopHost)) { + json_object *jObj_stat = json_object_new_object(); + + json_object_object_add(jObj_stat,"port",json_object_new_int(s->port)); + json_object_object_add(jObj_stat,"packets.number",json_object_new_int(s->num_pkts)); + json_object_object_add(jObj_stat,"flows.number",json_object_new_int(s->num_flows)); + json_object_object_add(jObj_stat,"flows.percent",json_object_new_double((s->num_flows*100.0)/total_flow_count)); + if(s->num_pkts) json_object_object_add(jObj_stat,"flows/packets", + json_object_new_double(((double)s->num_flows)/s->num_pkts)); + else json_object_object_add(jObj_stat,"flows.num_packets",json_object_new_double(0.0)); + + if(s->version == IPVERSION) { + inet_ntop(AF_INET, &(s->top_host), addr_name, sizeof(addr_name)); + } else { + inet_ntop(AF_INET6, &(s->top_host), addr_name, sizeof(addr_name)); + } + + json_object_object_add(jObj_stat,"aggressive.host",json_object_new_string(addr_name)); + json_object_object_add(jObj_stat,"host.app.protocol",json_object_new_string(s->proto)); + + json_object_array_add(jArray_stats, jObj_stat); + i++; + + if(i >= 10) break; + } + } + + json_object_object_add(*jObj_group, (direction == DIR_SRC) ? + "top.src.pkts.stats" : "top.dst.pkts.stats", jArray_stats); + + jArray_stats = json_object_new_array(); + i=0; + + /*sort top stats by ip addr count*/ + HASH_SORT(*stats, top_stats_sort); + + + HASH_ITER(hh, *stats, s, tmp) { + json_object *jObj_stat = json_object_new_object(); + + json_object_object_add(jObj_stat,"port",json_object_new_int(s->port)); + json_object_object_add(jObj_stat,"host.number",json_object_new_int64(s->num_addr)); + json_object_object_add(jObj_stat,"host.percent",json_object_new_double((s->num_addr*100.0)/total_ip_addr)); + json_object_object_add(jObj_stat,"flows.number",json_object_new_int(s->num_flows)); + + json_object_array_add(jArray_stats,jObj_stat); + i++; + + if(i >= 10) break; + } + + json_object_object_add(*jObj_group, (direction == DIR_SRC) ? + "top.src.host.stats" : "top.dst.host.stats", jArray_stats); +} +#endif + +/* *********************************************** */ + +void printPortStats(struct port_stats *stats) { + struct port_stats *s, *tmp; + char addr_name[48]; + int i = 0, j = 0; + + HASH_ITER(hh, stats, s, tmp) { + i++; + printf("\t%2d\tPort %5u\t[%u IP address(es)/%u flows/%u pkts/%u bytes]\n\t\tTop IP Stats:\n", + i, s->port, s->num_addr, s->num_flows, s->num_pkts, s->num_bytes); + + qsort(&s->top_ip_addrs[0], MAX_NUM_IP_ADDRESS, sizeof(struct info_pair), info_pair_cmp); + + for(j=0; j<MAX_NUM_IP_ADDRESS; j++) { + if(s->top_ip_addrs[j].count != 0) { + if(s->top_ip_addrs[j].version == IPVERSION) { + inet_ntop(AF_INET, &(s->top_ip_addrs[j].addr), addr_name, sizeof(addr_name)); + } else { + inet_ntop(AF_INET6, &(s->top_ip_addrs[j].addr), addr_name, sizeof(addr_name)); + } + + printf("\t\t%-36s ~ %.2f%%\n", addr_name, + ((s->top_ip_addrs[j].count) * 100.0) / s->cumulative_addr); + } + } + + printf("\n"); + if(i >= 10) break; + } +} + + +/* *********************************************** */ /** * @brief Print result */ static void printResults(u_int64_t tot_usec) { - u_int32_t i; u_int64_t total_flow_bytes = 0; u_int32_t avg_pkt_size = 0; @@ -777,8 +1846,10 @@ static void printResults(u_int64_t tot_usec) { && (ndpi_thread_info[thread_id].workflow->stats.raw_packet_count == 0)) continue; - for(i=0; i<NUM_ROOTS; i++) + for(i=0; i<NUM_ROOTS; i++) { ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], node_proto_guess_walker, &thread_id); + if(verbose == 3 || stats_flag) ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], port_stats_walker, &thread_id); + } /* Stats aggregation */ cumulative_stats.guessed_flow_protocols += ndpi_thread_info[thread_id].workflow->stats.guessed_flow_protocols; @@ -806,6 +1877,9 @@ static void printResults(u_int64_t tot_usec) { cumulative_stats.max_packet_len += ndpi_thread_info[thread_id].workflow->stats.max_packet_len; } + if(cumulative_stats.total_wire_bytes == 0) + goto free_stats; + if(!quiet_mode) { printf("\nnDPI Memory statistics:\n"); printf("\tnDPI Memory (once): %-13s\n", formatBytes(sizeof(struct ndpi_detection_module_struct), buf, sizeof(buf))); @@ -844,15 +1918,20 @@ static void printResults(u_int64_t tot_usec) { printf("\tPacket Len > 1500: %-13lu\n", (unsigned long)cumulative_stats.packet_len[5]); if(tot_usec > 0) { - char buf[32], buf1[32]; + char buf[32], buf1[32], when[64]; float t = (float)(cumulative_stats.ip_packet_count*1000000)/(float)tot_usec; float b = (float)(cumulative_stats.total_wire_bytes * 8 *1000000)/(float)tot_usec; float traffic_duration; - if (live_capture) traffic_duration = tot_usec; + if(live_capture) traffic_duration = tot_usec; else traffic_duration = (pcap_end.tv_sec*1000000 + pcap_end.tv_usec) - (pcap_start.tv_sec*1000000 + pcap_start.tv_usec); printf("\tnDPI throughput: %s pps / %s/sec\n", formatPackets(t, buf), formatTraffic(b, 1, buf1)); t = (float)(cumulative_stats.ip_packet_count*1000000)/(float)traffic_duration; b = (float)(cumulative_stats.total_wire_bytes * 8 *1000000)/(float)traffic_duration; + + strftime(when, sizeof(when), "%d/%b/%Y %H:%M:%S", localtime(&pcap_start.tv_sec)); + printf("\tAnalysis begin: %s\n", when); + strftime(when, sizeof(when), "%d/%b/%Y %H:%M:%S", localtime(&pcap_end.tv_sec)); + printf("\tAnalysis end: %s\n", when); printf("\tTraffic throughput: %s pps / %s/sec\n", formatPackets(t, buf), formatTraffic(b, 1, buf1)); printf("\tTraffic duration: %.3f sec\n", traffic_duration/1000000); } @@ -865,7 +1944,7 @@ static void printResults(u_int64_t tot_usec) { if(json_flag) { #ifdef HAVE_JSON_C if((json_fp = fopen(_jsonFilePath,"w")) == NULL) { - printf("Error createing .json file %s\n", _jsonFilePath); + printf("Error creating .json file %s\n", _jsonFilePath); json_flag = 0; } else { jObj_main = json_object_new_object(); @@ -954,8 +2033,17 @@ static void printResults(u_int64_t tot_usec) { // printf("\n\nTotal Flow Traffic: %llu (diff: %llu)\n", total_flow_bytes, cumulative_stats.total_ip_bytes-total_flow_bytes); - if(verbose) { + if((verbose == 1) || (verbose == 2)) { FILE *out = results_file ? results_file : stdout; + u_int32_t total_flows = 0; + + for(thread_id = 0; thread_id < num_threads; thread_id++) + total_flows += ndpi_thread_info[thread_id].workflow->num_allocated_flows; + + if((all_flows = (struct flow_info*)malloc(sizeof(struct flow_info)*total_flows)) == NULL) { + printf("Fatal error: not enough memory\n"); + exit(-1); + } if(!json_flag) fprintf(out, "\n"); @@ -965,6 +2053,11 @@ static void printResults(u_int64_t tot_usec) { ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], node_print_known_proto_walker, &thread_id); } + qsort(all_flows, num_flows, sizeof(struct flow_info), cmpFlows); + + for(i=0; i<num_flows; i++) + printFlow(i+1, all_flows[i].flow, all_flows[i].thread_id); + for(thread_id = 0; thread_id < num_threads; thread_id++) { if(ndpi_thread_info[thread_id].workflow->stats.protocol_counter[0 /* 0 = Unknown */] > 0) { if(!json_flag) { @@ -986,6 +2079,13 @@ static void printResults(u_int64_t tot_usec) { ndpi_twalk(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], node_print_unknown_proto_walker, &thread_id); } } + + qsort(all_flows, num_flows, sizeof(struct flow_info), cmpFlows); + + for(i=0; i<num_flows; i++) + printFlow(i+1, all_flows[i].flow, all_flows[i].thread_id); + + free(all_flows); } if(json_flag != 0) { @@ -1000,21 +2100,89 @@ static void printResults(u_int64_t tot_usec) { fclose(json_fp); #endif } -} + if(stats_flag || verbose == 3) { + HASH_SORT(srcStats, port_stats_sort); + HASH_SORT(dstStats, port_stats_sort); + } + + if(verbose == 3) { + printf("\n\nSource Ports Stats:\n"); + printPortStats(srcStats); + + printf("\nDestination Ports Stats:\n"); + printPortStats(dstStats); + } + + if(stats_flag) { +#ifdef HAVE_JSON_C + json_object *jObj_stats = json_object_new_object(); + char timestamp[64]; + int count; + + strftime(timestamp, sizeof(timestamp), "%FT%TZ", localtime(&pcap_start.tv_sec)); + json_object_object_add(jObj_stats, "time", json_object_new_string(timestamp)); + + saveScannerStats(&jObj_stats, &scannerHosts); + + if((count = HASH_COUNT(topReceivers)) == 0){ + HASH_SORT(receivers, receivers_sort); + saveReceiverStats(&jObj_stats, &receivers, cumulative_stats.ip_packet_count); + } + else{ + HASH_SORT(topReceivers, receivers_sort); + saveReceiverStats(&jObj_stats, &topReceivers, cumulative_stats.ip_packet_count); + } + + u_int64_t total_src_addr = getTopStats(srcStats); + u_int64_t total_dst_addr = getTopStats(dstStats); + + saveTopStats(&jObj_stats, &srcStats, DIR_SRC, + cumulative_stats.ndpi_flow_count, total_src_addr); + + saveTopStats(&jObj_stats, &dstStats, DIR_DST, + cumulative_stats.ndpi_flow_count, total_dst_addr); + + json_object_array_add(jArray_topStats, jObj_stats); +#endif + } + + free_stats: + if(scannerHosts) { + deleteScanners(scannerHosts); + scannerHosts = NULL; + } + + if(receivers){ + deleteReceivers(receivers); + receivers = NULL; + } + + if(topReceivers){ + deleteReceivers(topReceivers); + topReceivers = NULL; + } + + if(srcStats) { + deletePortsStats(srcStats); + srcStats = NULL; + } + + if(dstStats) { + deletePortsStats(dstStats); + dstStats = NULL; + } +} /** * @brief Force a pcap_dispatch() or pcap_loop() call to return */ static void breakPcapLoop(u_int16_t thread_id) { - if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL) { pcap_breakloop(ndpi_thread_info[thread_id].workflow->pcap_handle); } } - - /** * @brief Sigproc is executed for each packet in the pcap file */ @@ -1060,16 +2228,16 @@ static int getNextPcapFileFromPlaylist(u_int16_t thread_id, char filename[], u_i */ static void configurePcapHandle(pcap_t * pcap_handle) { - if(_bpf_filter != NULL) { + if(bpfFilter != NULL) { struct bpf_program fcode; - if(pcap_compile(pcap_handle, &fcode, _bpf_filter, 1, 0xFFFFFF00) < 0) { + if(pcap_compile(pcap_handle, &fcode, bpfFilter, 1, 0xFFFFFF00) < 0) { printf("pcap_compile error: '%s'\n", pcap_geterr(pcap_handle)); } else { if(pcap_setfilter(pcap_handle, &fcode) < 0) { printf("pcap_setfilter error: '%s'\n", pcap_geterr(pcap_handle)); } else - printf("Successfully set BPF filter to '%s'\n", _bpf_filter); + printf("Successfully set BPF filter to '%s'\n", bpfFilter); } } } @@ -1086,7 +2254,8 @@ static pcap_t * openPcapFileOrDevice(u_int16_t thread_id, const u_char * pcap_fi pcap_t * pcap_handle = NULL; /* trying to open a live interface */ - if((pcap_handle = pcap_open_live((char*)pcap_file, snaplen, promisc, 500, pcap_error_buffer)) == NULL) { + if((pcap_handle = pcap_open_live((char*)pcap_file, snaplen, promisc, + 500, pcap_error_buffer)) == NULL) { capture_for = capture_until = 0; live_capture = 0; @@ -1094,30 +2263,34 @@ static pcap_t * openPcapFileOrDevice(u_int16_t thread_id, const u_char * pcap_fi /* trying to open a pcap file */ if((pcap_handle = pcap_open_offline((char*)pcap_file, pcap_error_buffer)) == NULL) { - char filename[256]; - - /* trying to open a pcap playlist */ - if(getNextPcapFileFromPlaylist(thread_id, filename, sizeof(filename)) != 0 || - (pcap_handle = pcap_open_offline(filename, pcap_error_buffer)) == NULL) { + char filename[256] = { 0 }; - printf("ERROR: could not open pcap file or playlist: %s\n", pcap_error_buffer); + if(strstr((char*)pcap_file, (char*)".pcap")) + printf("ERROR: could not open pcap file %s: %s\n", pcap_file, pcap_error_buffer); + else if((getNextPcapFileFromPlaylist(thread_id, filename, sizeof(filename)) != 0) + || ((pcap_handle = pcap_open_offline(filename, pcap_error_buffer)) == NULL)) { + printf("ERROR: could not open playlist %s: %s\n", filename, pcap_error_buffer); exit(-1); } else { - if((!json_flag) && (!quiet_mode)) printf("Reading packets from playlist %s...\n", pcap_file); + if((!json_flag) && (!quiet_mode)) + printf("Reading packets from playlist %s...\n", pcap_file); } } else { - if((!json_flag) && (!quiet_mode)) printf("Reading packets from pcap file %s...\n", pcap_file); + if((!json_flag) && (!quiet_mode)) + printf("Reading packets from pcap file %s...\n", pcap_file); } } else { live_capture = 1; - if((!json_flag) && (!quiet_mode)) printf("Capturing live traffic from device %s...\n", pcap_file); + if((!json_flag) && (!quiet_mode)) + printf("Capturing live traffic from device %s...\n", pcap_file); } configurePcapHandle(pcap_handle); if(capture_for > 0) { - if((!json_flag) && (!quiet_mode)) printf("Capturing traffic up to %u seconds\n", (unsigned int)capture_for); + if((!json_flag) && (!quiet_mode)) + printf("Capturing traffic up to %u seconds\n", (unsigned int)capture_for); #ifndef WIN32 alarm(capture_for); @@ -1128,20 +2301,20 @@ static pcap_t * openPcapFileOrDevice(u_int16_t thread_id, const u_char * pcap_fi return pcap_handle; } - /** * @brief Check pcap packet */ -static void pcap_packet_callback_checked(u_char *args, - const struct pcap_pkthdr *header, - const u_char *packet) { - +static void pcap_process_packet(u_char *args, + const struct pcap_pkthdr *header, + const u_char *packet) { + struct ndpi_proto p; u_int16_t thread_id = *((u_int16_t*)args); /* allocate an exact size buffer to check overflows */ uint8_t *packet_checked = malloc(header->caplen); + memcpy(packet_checked, packet, header->caplen); - ndpi_workflow_process_packet(ndpi_thread_info[thread_id].workflow, header, packet_checked); + p = ndpi_workflow_process_packet(ndpi_thread_info[thread_id].workflow, header, packet_checked); if((capture_until != 0) && (header->ts.tv_sec >= capture_until)) { if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL) @@ -1149,11 +2322,8 @@ static void pcap_packet_callback_checked(u_char *args, return; } - /* Check if capture is live or not */ - if (!live_capture) { - if (!pcap_start.tv_sec) pcap_start.tv_sec = header->ts.tv_sec, pcap_start.tv_usec = header->ts.tv_usec; - pcap_end.tv_sec = header->ts.tv_sec, pcap_end.tv_usec = header->ts.tv_usec; - } + if(!pcap_start.tv_sec) pcap_start.tv_sec = header->ts.tv_sec, pcap_start.tv_usec = header->ts.tv_usec; + pcap_end.tv_sec = header->ts.tv_sec, pcap_end.tv_usec = header->ts.tv_usec; /* Idle flows cleanup */ if(live_capture) { @@ -1179,11 +2349,73 @@ static void pcap_packet_callback_checked(u_char *args, } } +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, "Found %u bytes packet %u.%u\n", header->caplen, p.app_protocol, p.master_protocol); +#endif + + if(extcap_dumper + && ((extcap_packet_filter == (u_int16_t)-1) + || (p.app_protocol == extcap_packet_filter) + || (p.master_protocol == extcap_packet_filter) + ) + ) { + struct pcap_pkthdr h; + uint32_t *crc, delta = sizeof(struct ndpi_packet_trailer) + 4 /* ethernet trailer */; + struct ndpi_packet_trailer *trailer; + + memcpy(&h, header, sizeof(h)); + + if(h.caplen > (sizeof(extcap_buf)-sizeof(struct ndpi_packet_trailer) - 4)) { + printf("INTERNAL ERROR: caplen=%u\n", h.caplen); + h.caplen = sizeof(extcap_buf)-sizeof(struct ndpi_packet_trailer) - 4; + } + + trailer = (struct ndpi_packet_trailer*)&extcap_buf[h.caplen]; + memcpy(extcap_buf, packet, h.caplen); + memset(trailer, 0, sizeof(struct ndpi_packet_trailer)); + trailer->magic = htonl(0x19680924); + trailer->master_protocol = htons(p.master_protocol), trailer->app_protocol = htons(p.app_protocol); + ndpi_protocol2name(ndpi_thread_info[thread_id].workflow->ndpi_struct, p, trailer->name, sizeof(trailer->name)); + crc = (uint32_t*)&extcap_buf[h.caplen+sizeof(struct ndpi_packet_trailer)]; + *crc = ethernet_crc32((const void*)extcap_buf, h.caplen+sizeof(struct ndpi_packet_trailer)); + h.caplen += delta; + h.len += delta; + +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, "Dumping %u bytes packet\n", h.caplen); +#endif + + pcap_dump((u_char*)extcap_dumper, &h, (const u_char *)extcap_buf); + pcap_dump_flush(extcap_dumper); + } + /* check for buffer changes */ if(memcmp(packet, packet_checked, header->caplen) != 0) - printf("INTERNAL ERROR: ingress packet was nodified by nDPI: this should not happen [thread_id=%u, packetId=%lu]\n", - thread_id, (unsigned long)ndpi_thread_info[thread_id].workflow->stats.raw_packet_count); + printf("INTERNAL ERROR: ingress packet was modified by nDPI: this should not happen [thread_id=%u, packetId=%lu, caplen=%u]\n", + thread_id, (unsigned long)ndpi_thread_info[thread_id].workflow->stats.raw_packet_count, header->caplen); free(packet_checked); + + if((pcap_end.tv_sec-pcap_start.tv_sec) > pcap_analysis_duration) { + int i; + u_int64_t tot_usec; + + gettimeofday(&end, NULL); + tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin.tv_sec*1000000 + begin.tv_usec); + + printResults(tot_usec); + + for(i=0; i<ndpi_thread_info[thread_id].workflow->prefs.num_roots; i++) { + ndpi_tdestroy(ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i], ndpi_flow_info_freer); + ndpi_thread_info[thread_id].workflow->ndpi_flows_root[i] = NULL; + + memset(&ndpi_thread_info[thread_id].workflow->stats, 0, sizeof(struct ndpi_stats)); + } + + printf("\n-------------------------------------------\n\n"); + + memcpy(&begin, &end, sizeof(begin)); + memcpy(&pcap_start, &pcap_end, sizeof(pcap_start)); + } } @@ -1191,12 +2423,10 @@ static void pcap_packet_callback_checked(u_char *args, * @brief Call pcap_loop() to process packets from a live capture or savefile */ static void runPcapLoop(u_int16_t thread_id) { - if((!shutdown_app) && (ndpi_thread_info[thread_id].workflow->pcap_handle != NULL)) - pcap_loop(ndpi_thread_info[thread_id].workflow->pcap_handle, -1, &pcap_packet_callback_checked, (u_char*)&thread_id); + pcap_loop(ndpi_thread_info[thread_id].workflow->pcap_handle, -1, &pcap_process_packet, (u_char*)&thread_id); } - /** * @brief Process a running thread */ @@ -1241,29 +2471,57 @@ void * processing_thread(void *_thread_id) { * @brief Begin, process, end detection process */ void test_lib() { - - struct timeval begin, end; + struct timeval end; u_int64_t tot_usec; long thread_id; #ifdef HAVE_JSON_C json_init(); + if(stats_flag) json_open_stats_file(); +#endif + +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, "Num threads: %d\n", num_threads); #endif for(thread_id = 0; thread_id < num_threads; thread_id++) { - pcap_t * cap = openPcapFileOrDevice(thread_id, (const u_char*)_pcap_file[thread_id]); + pcap_t *cap; + +#ifdef DEBUG_TRACE + if(trace) fprintf(trace, "Opening %s\n", (const u_char*)_pcap_file[thread_id]); +#endif + + cap = openPcapFileOrDevice(thread_id, (const u_char*)_pcap_file[thread_id]); setupDetection(thread_id, cap); } gettimeofday(&begin, NULL); - /* Running processing threads */ - for(thread_id = 0; thread_id < num_threads; thread_id++) - pthread_create(&ndpi_thread_info[thread_id].pthread, NULL, processing_thread, (void *) thread_id); + int status; + void * thd_res; + /* Running processing threads */ + for(thread_id = 0; thread_id < num_threads; thread_id++) { + status = pthread_create(&ndpi_thread_info[thread_id].pthread, NULL, processing_thread, (void *) thread_id); + /* check pthreade_create return value */ + if(status != 0) { + fprintf(stderr, "error on create %ld thread\n", thread_id); + exit(-1); + } + } /* Waiting for completion */ - for(thread_id = 0; thread_id < num_threads; thread_id++) - pthread_join(ndpi_thread_info[thread_id].pthread, NULL); + for(thread_id = 0; thread_id < num_threads; thread_id++) { + status = pthread_join(ndpi_thread_info[thread_id].pthread, &thd_res); + /* check pthreade_join return value */ + if(status != 0) { + fprintf(stderr, "error on join %ld thread\n", thread_id); + exit(-1); + } + if(thd_res != NULL) { + fprintf(stderr, "error on returned value of %ld joined thread\n", thread_id); + exit(-1); + } + } gettimeofday(&end, NULL); tot_usec = end.tv_sec*1000000 + end.tv_usec - (begin.tv_sec*1000000 + begin.tv_usec); @@ -1271,10 +2529,16 @@ void test_lib() { /* Printing cumulative results */ printResults(tot_usec); + if(stats_flag) { +#ifdef HAVE_JSON_C + json_close_stats_file(); +#endif + } + for(thread_id = 0; thread_id < num_threads; thread_id++) { - if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL) { + if(ndpi_thread_info[thread_id].workflow->pcap_handle != NULL) pcap_close(ndpi_thread_info[thread_id].workflow->pcap_handle); - } + terminateDetection(thread_id); } } @@ -1291,21 +2555,609 @@ void automataUnitTest() { ndpi_free_automa(automa); } +/* *********************************************** */ +/** + * @brief Produce bpf filter to filter ports and hosts + * in order to remove a peak in terms of number of packets + * sent by source hosts. + */ +#ifdef HAVE_JSON_C +void bpf_filter_pkt_peak_filter(json_object **jObj_bpfFilter, + int port_array[], int p_size, + const char *src_host_array[16], + int sh_size, + const char *dst_host_array[16], + int dh_size) { + char filter[2048]; + int produced = 0; + int i = 0; + + if(port_array[0] != INIT_VAL) { + int l; + + strcpy(filter, "not (src port "); + + while(i < p_size && port_array[i] != INIT_VAL) { + l = strlen(filter); + + if(i+1 == p_size || port_array[i+1] == INIT_VAL) + snprintf(&filter[l], sizeof(filter)-l, "%d", port_array[i]); + else + snprintf(&filter[l], sizeof(filter)-l, "%d or ", port_array[i]); + i++; + } + + l = strlen(filter); + snprintf(&filter[l], sizeof(filter)-l, "%s", ")"); + produced = 1; + } + + + if(src_host_array[0] != NULL) { + int l; + + if(port_array[0] != INIT_VAL) + strncat(filter, " and not (src ", sizeof(" and not (src ")); + else + strcpy(filter, "not (src "); + + + i=0; + while(i < sh_size && src_host_array[i] != NULL) { + l = strlen(filter); + + if(i+1 == sh_size || src_host_array[i+1] == NULL) + snprintf(&filter[l], sizeof(filter)-l, "%s", src_host_array[i]); + else + snprintf(&filter[l], sizeof(filter)-l, "%s or ", src_host_array[i]); + + i++; + } + + l = strlen(filter); + snprintf(&filter[l], sizeof(filter)-l, "%s", ")"); + produced = 1; + } + + + if(dst_host_array[0] != NULL) { + int l; + + if(port_array[0] != INIT_VAL || src_host_array[0] != NULL) + strncat(filter, " and not (dst ", sizeof(" and not (dst ")); + else + strcpy(filter, "not (dst "); + + i=0; + + while(i < dh_size && dst_host_array[i] != NULL) { + l = strlen(filter); + + if(i+1 == dh_size || dst_host_array[i+1] == NULL) + snprintf(&filter[l], sizeof(filter)-l, "%s", dst_host_array[i]); + else + snprintf(&filter[l], sizeof(filter)-l, "%s or ", dst_host_array[i]); + + i++; + } + + l = strlen(filter); + snprintf(&filter[l], sizeof(filter)-l, "%s", ")"); + produced = 1; + } + + + + if(produced) + json_object_object_add(*jObj_bpfFilter, "pkt.peak.filter", json_object_new_string(filter)); + else + json_object_object_add(*jObj_bpfFilter, "pkt.peak.filter", json_object_new_string("")); +} +#endif + +/* *********************************************** */ +/** + * @brief Produce bpf filter to filter ports and hosts + * in order to remove a peak in terms of number of source + * addresses. + */ +#ifdef HAVE_JSON_C +void bpf_filter_host_peak_filter(json_object **jObj_bpfFilter, + const char *host_array[16], + int h_size) { + char filter[2048]; + int produced = 0; + int i = 0; + + + if(host_array[0] != NULL) { + int l; + + strcpy(filter, "not (dst "); + + while(i < h_size && host_array[i] != NULL) { + l = strlen(filter); + + if(i+1 == h_size || host_array[i+1] == NULL) + snprintf(&filter[l], sizeof(filter)-l, "%s", host_array[i]); + else + snprintf(&filter[l], sizeof(filter)-l, "%s or ", host_array[i]); + + i++; + } + + l = strlen(filter); + snprintf(&filter[l], sizeof(filter)-l, "%s", ")"); + produced = 1; + } + + if(produced) + json_object_object_add(*jObj_bpfFilter, "host.peak.filter", json_object_new_string(filter)); + else + json_object_object_add(*jObj_bpfFilter, "host.peak.filter", json_object_new_string("")); +} +#endif + +/* *********************************************** */ +/** + * @brief Initialize port array + */ + +void bpf_filter_port_array_init(int array[], int size) { + int i; + for(i=0; i<size; i++) + array[i] = INIT_VAL; +} + +/* *********************************************** */ +/** + * @brief Initialize host array + */ + +void bpf_filter_host_array_init(const char *array[48], int size) { + int i; + for(i=0; i<size; i++) + array[i] = NULL; +} + +/* *********************************************** */ + +/** + * @brief Add host to host filter array + */ + +void bpf_filter_host_array_add(const char *filter_array[48], int size, const char *host) { + int i; + int r; + for(i=0; i<size; i++) { + if((filter_array[i] != NULL) && (r = strcmp(filter_array[i], host)) == 0) + return; + if(filter_array[i] == NULL) { + filter_array[i] = host; + return; + } + } + fprintf(stderr,"bpf_filter_host_array_add: max array size is reached!\n"); + exit(-1); +} + + +/* *********************************************** */ + +/** + * @brief Add port to port filter array + */ + +void bpf_filter_port_array_add(int filter_array[], int size, int port) { + int i; + for(i=0; i<size; i++) { + if(filter_array[i] == port) + return; + if(filter_array[i] == INIT_VAL) { + filter_array[i] = port; + return; + } + } + fprintf(stderr,"bpf_filter_port_array_add: max array size is reached!\n"); + exit(-1); +} + + +/* *********************************************** */ +#ifdef HAVE_JSON_C +/* + * @brief returns average value for a given field + */ +float getAverage(struct json_object *jObj_stat, char *field){ + json_object *field_stat; + json_bool res; + float average; + float sum = 0; + int r; + int j; + + if((r = strcmp(field, "top.scanner.stats")) == 0) { + for(j=0; j<json_object_array_length(jObj_stat); j++) { + field_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_tot_flows_number; + + if((res = json_object_object_get_ex(field_stat, "total.flows.number", &jObj_tot_flows_number)) == 0) { + fprintf(stderr, "ERROR: can't get \"total.flows.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + u_int32_t tot_flows_number = json_object_get_int(jObj_tot_flows_number); + + sum += tot_flows_number; + } + } else if((r = strcmp(field, "top.src.pkts.stats")) == 0) { + for(j=0; j<json_object_array_length(jObj_stat); j++) { + field_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_packets_number; + + if((res = json_object_object_get_ex(field_stat, "packets.number", &jObj_packets_number)) == 0) { + fprintf(stderr, "ERROR: can't get \"packets.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + u_int32_t packets_number = json_object_get_int(jObj_packets_number); + + sum += packets_number; + } + } + + if(j == 0) return 0.0; + + return sum/j; +} +#endif +/* *********************************************** */ +#ifdef HAVE_JSON_C +/* + * @brief returns standard deviation for a given + * field and it's average value. + */ +float getStdDeviation(struct json_object *jObj_stat, float average, char *field){ + json_object *field_stat; + json_bool res; + float sum = 0; + int j; + int r; + + if((r = strcmp(field, "top.scanner.stats")) == 0){ + for(j=0; j<json_object_array_length(jObj_stat); j++) { + field_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_tot_flows_number; + + if((res = json_object_object_get_ex(field_stat, "total.flows.number", &jObj_tot_flows_number)) == 0) { + fprintf(stderr, "ERROR: can't get \"total.flows.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + u_int32_t tot_flows_number = json_object_get_int(jObj_tot_flows_number); + + sum += pow((tot_flows_number - average), 2); + } + } + + return sqrt(sum/(float)j); +} + +#endif + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +void getSourcePorts(struct json_object *jObj_stat, int srcPortArray[], int size, float threshold) { + int j; + + for(j=0; j<json_object_array_length(jObj_stat); j++) { + json_object *src_pkts_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_packets_number; + json_object *jObj_flows_percent; + json_object *jObj_flows_packets; + json_object *jObj_port; + json_bool res; + + if((res = json_object_object_get_ex(src_pkts_stat, "packets.number", &jObj_packets_number)) == 0) { + fprintf(stderr, "ERROR: can't get \"packets.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + u_int32_t packets_number = json_object_get_int(jObj_packets_number); + + if((res = json_object_object_get_ex(src_pkts_stat, "flows.percent", &jObj_flows_percent)) == 0) { + fprintf(stderr, "ERROR: can't get \"flows.percent\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + double flows_percent = json_object_get_double(jObj_flows_percent); + + + if((res = json_object_object_get_ex(src_pkts_stat, "flows/packets", &jObj_flows_packets)) == 0) { + fprintf(stderr, "ERROR: can't get \"flows/packets\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + double flows_packets = json_object_get_double(jObj_flows_packets); + + + if((flows_packets > FLOWS_PACKETS_THRESHOLD) + && (flows_percent >= FLOWS_PERCENT_THRESHOLD) + && packets_number >= threshold) { + if((res = json_object_object_get_ex(src_pkts_stat, "port", &jObj_port)) == 0) { + fprintf(stderr, "ERROR: can't get \"port\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + int port = json_object_get_int(jObj_port); + + bpf_filter_port_array_add(srcPortArray, size, port); + } + } +} +#endif + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +void getReceiverHosts(struct json_object *jObj_stat, const char *dstHostArray[16], int size) { + int j; + + for(j=0; j<json_object_array_length(jObj_stat); j++) { + json_object *scanner_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_host_address; + json_object *jObj_pkts_percent; + json_bool res; + + if((res = json_object_object_get_ex(scanner_stat, "packets.percent", &jObj_pkts_percent)) == 0) { + fprintf(stderr, "ERROR: can't get \"packets.percent\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + double pkts_percent = json_object_get_double(jObj_pkts_percent); + + + if(pkts_percent > PKTS_PERCENT_THRESHOLD) { + if((res = json_object_object_get_ex(scanner_stat, "ip.address", &jObj_host_address)) == 0) { + fprintf(stderr, "ERROR: can't get \"ip.address, use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + const char *host_address = json_object_get_string(jObj_host_address); + + bpf_filter_host_array_add(dstHostArray, size, host_address); + } + } +} +#endif + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +void getScannerHosts(struct json_object *jObj_stat, int duration, + const char *srcHostArray[48], int size, + float threshold) { + int j; + + for(j=0; j<json_object_array_length(jObj_stat); j++) { + json_object *scanner_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_host_address; + json_object *jObj_tot_flows_number; + json_bool res; + + + if((res = json_object_object_get_ex(scanner_stat, "total.flows.number", &jObj_tot_flows_number)) == 0) { + fprintf(stderr, "ERROR: can't get \"total.flows.number\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + u_int32_t tot_flows_number = json_object_get_int(jObj_tot_flows_number); + + + if(((tot_flows_number/(float)duration) > FLOWS_THRESHOLD) && tot_flows_number > threshold) { + if((res = json_object_object_get_ex(scanner_stat, "ip.address", &jObj_host_address)) == 0) { + fprintf(stderr, "ERROR: can't get \"ip.address\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + const char *host_address = json_object_get_string(jObj_host_address); + + bpf_filter_host_array_add(srcHostArray, size, host_address); + + } + } +} +#endif + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +void getDestinationHosts(struct json_object *jObj_stat, int duration, + const char *dstHostArray[16], int size) { + int j; + + for(j=0; j<json_object_array_length(jObj_stat); j++) { + json_object *scanner_stat = json_object_array_get_idx(jObj_stat, j); + json_object *jObj_host_address; + json_object *jObj_flows_percent; + json_bool res; + + + if((res = json_object_object_get_ex(scanner_stat, "flows.percent", &jObj_flows_percent)) == 0) { + fprintf(stderr, "ERROR: can't get \"flows.percent\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + double flows_percent = json_object_get_double(jObj_flows_percent); + + + if(flows_percent > FLOWS_PERCENT_THRESHOLD_2) { + if((res = json_object_object_get_ex(scanner_stat, "aggressive.host", &jObj_host_address)) == 0) { + fprintf(stderr, "ERROR: can't get \"aggressive.host\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + const char *host_address = json_object_get_string(jObj_host_address); + + bpf_filter_host_array_add(dstHostArray, size, host_address); + + } + } +} +#endif + +/* *********************************************** */ + +#ifdef HAVE_JSON_C +static void produceBpfFilter(char *filePath) { + json_object *jObj; /* entire json object from file */ + json_object *jObj_duration; + json_object *jObj_statistics; /* json array */ + json_bool res; + int filterSrcPorts[PORT_ARRAY_SIZE]; + const char *filterSrcHosts[48]; + const char *filterDstHosts[48]; + const char *filterPktDstHosts[48]; + struct stat statbuf; + FILE *fp = NULL; + char *fileName; + char _filterFilePath[1024]; + json_object *jObj_bpfFilter; + void *fmap; + int fsock; + float average; + float deviation; + int duration; + int typeCheck; + int array_len; + int i; + + if((fsock = open(filePath, O_RDONLY)) == -1) { + fprintf(stderr,"error opening file %s\n", filePath); + exit(-1); + } + + if(fstat(fsock, &statbuf) == -1) { + fprintf(stderr,"error getting file stat\n"); + exit(-1); + } + + if((fmap = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fsock, 0)) == MAP_FAILED) { + fprintf(stderr,"error mmap is failed\n"); + exit(-1); + } + + if((jObj = json_tokener_parse(fmap)) == NULL) { + fprintf(stderr,"ERROR: invalid json file. Use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + + + if((res = json_object_object_get_ex(jObj, "duration.in.seconds", &jObj_duration)) == 0) { + fprintf(stderr,"ERROR: can't get \"duration.in.seconds\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + duration = json_object_get_int(jObj_duration); + + + if((res = json_object_object_get_ex(jObj, "statistics", &jObj_statistics)) == 0) { + fprintf(stderr,"ERROR: can't get \"statistics\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + + if((typeCheck = json_object_is_type(jObj_statistics, json_type_array)) == 0) { + fprintf(stderr,"ERROR: invalid json file. Use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + array_len = json_object_array_length(jObj_statistics); + + + bpf_filter_port_array_init(filterSrcPorts, PORT_ARRAY_SIZE); + bpf_filter_host_array_init(filterSrcHosts, HOST_ARRAY_SIZE); + bpf_filter_host_array_init(filterDstHosts, HOST_ARRAY_SIZE); + bpf_filter_host_array_init(filterPktDstHosts, HOST_ARRAY_SIZE/2); + + for(i=0; i<array_len; i++) { + json_object *stats = json_object_array_get_idx(jObj_statistics, i); + json_object *val; + + if((res = json_object_object_get_ex(stats, "top.scanner.stats", &val)) == 0) { + fprintf(stderr,"ERROR: can't get \"top.scanner.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + + if((average = getAverage(val, "top.scanner.stats")) != 0){ + deviation = getStdDeviation(val, average, "top.scanner.stats"); + getScannerHosts(val, duration, filterSrcHosts, HOST_ARRAY_SIZE, average+deviation); + } + + + if((res = json_object_object_get_ex(stats, "top.receiver.stats", &val)) == 0) { + fprintf(stderr,"ERROR: can't get \"top.receiver.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + getReceiverHosts(val, filterPktDstHosts, HOST_ARRAY_SIZE/2); + + + if((res = json_object_object_get_ex(stats, "top.src.pkts.stats", &val)) == 0) { + fprintf(stderr,"ERROR: can't get \"top.src.pkts.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + + if((average = getAverage(val, "top.src.pkts.stats")) != 0) + getSourcePorts(val, filterSrcPorts, PORT_ARRAY_SIZE, average); + + + if((res = json_object_object_get_ex(stats, "top.dst.pkts.stats", &val)) == 0) { + fprintf(stderr,"ERROR: can't get \"top.dst.pkts.stats\", use -x flag only with .json files generated by ndpiReader -b flag.\n"); + exit(-1); + } + getDestinationHosts(val, duration, filterDstHosts, HOST_ARRAY_SIZE); + } + + + fileName = basename(filePath); + snprintf(_filterFilePath, sizeof(_filterFilePath), "%s.bpf", filePath); + + if((fp = fopen(_filterFilePath,"w")) == NULL) { + printf("Error creating .json file %s\n", _filterFilePath); + exit(-1); + } + + jObj_bpfFilter = json_object_new_object(); + + bpf_filter_pkt_peak_filter(&jObj_bpfFilter, filterSrcPorts, PORT_ARRAY_SIZE, + filterSrcHosts, HOST_ARRAY_SIZE, filterPktDstHosts, HOST_ARRAY_SIZE/2); + + bpf_filter_host_peak_filter(&jObj_bpfFilter, filterDstHosts, HOST_ARRAY_SIZE); + + fprintf(fp,"%s\n",json_object_to_json_string(jObj_bpfFilter)); + fclose(fp); + + printf("created: %s\n", _filterFilePath); + + json_object_put(jObj); /* free memory */ +} +#endif + + +/* *********************************************** */ + + /** @brief MAIN FUNCTION **/ +#ifdef APP_HAS_OWN_MAIN +int orginal_main(int argc, char **argv) { +#else int main(int argc, char **argv) { - +#endif int i; automataUnitTest(); + ndpi_info_mod = ndpi_init_detection_module(); + if (ndpi_info_mod == NULL) return -1; + memset(ndpi_thread_info, 0, sizeof(ndpi_thread_info)); - memset(&pcap_start, 0, sizeof(pcap_start)); - memset(&pcap_end, 0, sizeof(pcap_end)); parseOptions(argc, argv); + if(bpf_filter_flag) { +#ifdef HAVE_JSON_C + produceBpfFilter(_diagnoseFilePath); + return 0; +#endif + } + if((!json_flag) && (!quiet_mode)) { printf("\n-----------------------------------------------------------\n" "* NOTE: This is demo app to show *some* nDPI features.\n" @@ -1322,13 +3174,14 @@ int main(int argc, char **argv) { for(i=0; i<num_loops; i++) test_lib(); - if(results_path) free(results_path); - if(results_file) fclose(results_file); + if(results_path) free(results_path); + if(results_file) fclose(results_file); + if(extcap_dumper) pcap_dump_close(extcap_dumper); + if(ndpi_info_mod) ndpi_exit_detection_module(ndpi_info_mod); return 0; } - #ifdef WIN32 #ifndef __GNUC__ #define EPOCHFILETIME (116444736000000000i64) @@ -1336,7 +3189,6 @@ int main(int argc, char **argv) { #define EPOCHFILETIME (116444736000000000LL) #endif - /** @brief Timezone **/ |