/** * \fn int radix_trie_unit_test() * \params none * \return 0 on success * \return 1 on failure */ int radix_trie_unit_test () { struct radix_trie rt, rt2; enum status err; struct in_addr a[10]; unsigned int af[10]; unsigned int i; unsigned int test_failed = 0; unsigned int flag; zfile output; output = zattach(stdout, "w"); if (output == NULL) { fprintf(stderr, "%s: error: could not initialize (possibly compressed) stdout for writing\n", __FUNCTION__); } for (i=0; i<32; i++) { flag = index_to_flag(i); printf("index: %u\tflag: %x\tindex: %u\n", i, flag, flag_to_index(flag)); } err = radix_trie_init(&rt); if (err != ok) { fprintf(stderr, "%s: error: could not initialize radix_trie\n", __FUNCTION__); } a[0].s_addr = htonl(0xcafebabe); af[0] = 1; a[1].s_addr = htonl(0xcafedada); af[1] = 2; a[2].s_addr = htonl(0xbaddecaf); af[2] = 4; a[3].s_addr = htonl(0x01234567); af[3] = 8; a[4].s_addr = htonl(0xffeeddcc); af[4] = 16; a[5].s_addr = htonl(0x0a9b8c7d); af[5] = 32; a[6].s_addr = htonl(0xfedcba98); af[6] = 64; a[7].s_addr = htonl(0x76543210); af[7] = 128; a[8].s_addr = htonl(0xa1b2c3d4); af[8] = 256; printf("testing add\n"); flag = 1; for (i=0; i<3; i++) { if (radix_trie_add_subnet(&rt, a[i], 32, af[i]) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } for (i=6; i<9; i++) { if (radix_trie_add_subnet(&rt, a[i], 16, af[i]) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } printf("testing lookup (expecting success)\n"); for (i=0; i<3; i++) { if (radix_trie_lookup_addr(&rt, a[i]) != af[i]) { zprintf(output, "error: could not lookup subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } for (i=6; i<9; i++) { if (radix_trie_lookup_addr(&rt, a[i]) != af[i]) { zprintf(output, "error: could not lookup subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } printf("testing lookup (expecting failure)\n"); for (i=3; i<6; i++) { if (radix_trie_lookup_addr(&rt, a[i]) != 0) { zprintf(output, "error: false positive lookup subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } printf("testing 14-bit add\n"); for (i=0; i<3; i++) { if (radix_trie_add_subnet(&rt, a[i], 14, 0x100) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } printf("testing 14-bit lookup (expecting success)\n"); for (i=0; i<3; i++) { unsigned int f = radix_trie_lookup_addr(&rt, a[i]); if (f != (af[i] | 0x100)) { zprintf(output, "error: could not lookup address %s (%x), got %x instead\n", inet_ntoa(a[i]), htonl(a[i].s_addr), f); test_failed = 1; } } printf("testing 15-bit add\n"); for (i=0; i<3; i++) { if (radix_trie_add_subnet(&rt, a[i], 15, 0x1000) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(a[i])); test_failed = 1; } } printf("testing 15-bit lookup (expecting success)\n"); for (i=0; i<3; i++) { unsigned int f = radix_trie_lookup_addr(&rt, a[i]); if (f != (af[i] | 0x1000 | 0x100)) { zprintf(output, "error: could not lookup address %s (%x), got %x but expected %x\n", inet_ntoa(a[i]), htonl(a[i].s_addr), f, (af[i] | 0x1000 | 0x100)); test_failed = 1; } } printf("testing lookup (expecting failure)\n"); for (i=3; i<6; i++) { if (radix_trie_lookup_addr(&rt, a[i]) != 0) { zprintf(output, "error: false positive lookup address %s\n", inet_ntoa(a[i])); test_failed = 1; } } if (test_failed) { printf("FAILURE; at least one test failed\n"); } else { printf("all tests passed\n"); } printf("-----------------------------------\n"); radix_trie_print(&rt); printf("testing high level interface\n"); err = radix_trie_init(&rt2); if (err != ok) { fprintf(stderr, "error: could not initialize radix_trie\n"); } attr_flags internal_attr, c2_attr, watchlist_attr, attr; struct in_addr addr; internal_attr = radix_trie_add_attr_label(&rt2, "internal"); printf("attr: %x\n", internal_attr); c2_attr = radix_trie_add_attr_label(&rt2, "c2"); watchlist_attr = radix_trie_add_attr_label(&rt2, "watchlist"); addr = hex2addr(0xcafe0000); if (radix_trie_add_subnet(&rt2, addr, 16, internal_attr) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(addr)); test_failed = 1; } attr = radix_trie_lookup_addr(&rt2, addr); if ((attr & internal_attr) == 0) { zprintf(output, "error: attribute lookup failed (expected %x, got %x)\n", internal_attr, attr); test_failed = 1; } addr = hex2addr(0xdecaf000); if (radix_trie_add_subnet(&rt2, addr, 20, internal_attr) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(addr)); test_failed = 1; } attr = radix_trie_lookup_addr(&rt2, addr); if ((attr & internal_attr) == 0) { zprintf(output, "error: attribute lookup failed (expected %x, got %x)\n", internal_attr, attr); test_failed = 1; } addr = hex2addr(0xdadacafe); if (radix_trie_add_subnet(&rt2, addr, 32, c2_attr) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(addr)); test_failed = 1; } attr = radix_trie_lookup_addr(&rt2, addr); if ((attr & c2_attr) == 0) { zprintf(output, "error: attribute lookup failed (expected %x, got %x)\n", c2_attr, attr); test_failed = 1; } addr = hex2addr(0xdadacafe); if (radix_trie_add_subnet(&rt2, addr, 8, watchlist_attr) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(addr)); test_failed = 1; } attr = radix_trie_lookup_addr(&rt2, addr); if ((attr & watchlist_attr) == 0) { zprintf(output, "error: attribute lookup failed (expected %x, got %x)\n", watchlist_attr, attr); test_failed = 1; } addr = hex2addr(0xffffffff); if (radix_trie_add_subnet(&rt2, addr, 1, watchlist_attr) != ok) { zprintf(output, "error: could not add subnet %s\n", inet_ntoa(addr)); test_failed = 1; } attr = radix_trie_lookup_addr(&rt2, addr); if ((attr & watchlist_attr) == 0) { zprintf(output, "error: attribute lookup failed (expected %x, got %x)\n", c2_attr, attr); test_failed = 1; } if (test_failed) { printf("FAILURE; at least one test failed\n"); } else { printf("all high level interface tests passed\n"); } printf("-----------------------------------\n"); radix_trie_print(&rt2); printf("-----------------------------------\n"); if (radix_trie_high_level_unit_test() != ok) { test_failed = 1; } return test_failed; /* 0 on success, 1 otherwise */ }
int main(int argc, char **argv) { char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 net = PCAP_NETMASK_UNKNOWN; char *filter_exp = "ip"; struct bpf_program fp; int i; int c; int opt_count = 0; int tmp_ret; char *ifile = NULL; unsigned int file_count = 0; char filename[MAX_FILENAME_LEN]; /* output file */ char pcap_filename[MAX_FILENAME_LEN*2]; /* output file */ char *cli_interface = NULL; char *cli_filename = NULL; char *config_file = NULL; struct interface ifl[IFL_MAX]; int num_interfaces; char *capture_if; unsigned int file_base_len = 0; unsigned int num_cmds = 0; unsigned int done_with_options = 0; struct stat sb; DIR *dir; struct dirent *ent; enum operating_mode mode = mode_none; /* sanity check sizeof() expectations */ if (data_sanity_check() != ok) { fprintf(stderr, "error: failed data size sanity check\n"); } /* sanity check arguments */ for (i=1; i<argc; i++) { if (strchr(argv[i], '=')) { if (done_with_options) { fprintf(stderr, "error: option (%s) found after filename (%s)\n", argv[i], argv[i-1]); exit(EXIT_FAILURE); } } else { done_with_options = 1; } } /* * set "info" to stderr; this output stream is used for * debug/info/warnings/errors. setting it here is actually * defensive coding, just in case some function that writes to * "info" gets invoked before info gets set below (if we are in * online mode, it will be set to a log file) */ info = stderr; /* in debug mode, turn off output buffering */ #if P2F_DEBUG setvbuf(stderr, NULL, _IONBF, 0); setbuf(stdout, NULL); #endif /* * set configuration from command line arguments that contain * LHS=RHS commands, then update argv/argc so that those arguments * are not subjected to any further processing */ num_cmds = config_set_from_argv(&config, argv, argc); argv += num_cmds; argc -= num_cmds; /* process command line options */ while (1) { int option_index = 0; struct option long_options[] = { {"help", no_argument, 0, 'h' }, {"xconfig", required_argument, 0, 'x' }, {0, 0, 0, 0 } }; c = getopt_long(argc, argv, "hx:", long_options, &option_index); if (c == -1) break; switch (c) { case 'x': config_file = optarg; opt_count++; break; case 'h': default: return usage(argv[0]); } opt_count++; } if (config_file) { /* * read in configuration from file; note that if we don't read in * a file, then the config structure will use the static defaults * set when it was declared */ config_set_from_file(&config, config_file); } if (config_file || (num_cmds != 0)) { /* * set global variables as needed, if we got some configuration * commands from the config_file or from command line arguments */ bidir = config.bidir; include_zeroes = config.include_zeroes; byte_distribution = config.byte_distribution; report_entropy = config.report_entropy; report_wht = config.report_wht; report_hd = config.report_hd; include_tls = config.include_tls; include_classifier = config.include_classifier; output_level = config.output_level; report_idp = config.idp; report_dns = config.dns; salt_algo = config.type; nfv9_capture_port = config.nfv9_capture_port; if (config.bpf_filter_exp) { filter_exp = config.bpf_filter_exp; } } /* * allow some command line variables to override the config file */ if (cli_filename) { /* * output filename provided on command line supersedes that * provided in the config file */ config.filename = cli_filename; } if (cli_interface) { /* * interface provided on command line supersedes that provided * in the config file */ config.interface = cli_interface; } if (config.filename) { strncpy(filename, config.filename, MAX_FILENAME_LEN); } /* * set the operating mode to online or offline */ if (config.interface != NULL && strcmp(config.interface, NULL_KEYWORD)) { mode = mode_online; } else { mode = mode_offline; } /* * if we are doing a live capture, get interface list, and set "info" * output stream to log file */ if (mode == mode_online) { if (config.logfile && strcmp(config.logfile, NULL_KEYWORD)) { info = fopen(config.logfile, "a"); if (info == NULL) { fprintf(stderr, "error: could not open log file %s\n", config.logfile); return -1; } fprintf(stderr, "writing errors/warnings/info/debug output to %s\n", config.logfile); } /* * cheerful message to indicate the start of a new run of the * daemon */ fprintf(info, "--- %s initialization ---\n", argv[0]); flocap_stats_output(info); num_interfaces = interface_list_get(ifl); if (num_interfaces == 0) { fprintf(info, "warning: could not obtain inferface information\n"); } else { for(i=0; i<num_interfaces; i++) { unsigned char *a = ifl[i].mac_addr; fprintf(info, "interface: %8s\tstatus: %s\t%02x%02x%02x%02x%02x%02x\n", ifl[i].name, (ifl[i].active ? "up" : "down"), a[0], a[1], a[2], a[3], a[4], a[5]); } } } else { info = stderr; } /* * report on running configuration (which may depend on the command * line, the config file, or both) */ config_print(info, &config); /* * configure labeled subnets (which uses a radix trie to identify * addresses that match subnets associated with labels) */ if (config.num_subnets > 0) { attr_flags subnet_flag; enum status err; rt = radix_trie_alloc(); if (rt == NULL) { fprintf(info, "could not allocate memory\n"); } err = radix_trie_init(rt); if (err != ok) { fprintf(stderr, "error: could not initialize subnet labels (radix_trie)\n"); } for (i=0; i<config.num_subnets; i++) { char label[LINEMAX], subnet_file[LINEMAX]; int num; num = sscanf(config.subnet[i], "%[^=:]:%[^=:\n#]", label, subnet_file); if (num != 2) { fprintf(info, "error: could not parse command \"%s\" into form label:subnet\n", config.subnet[i]); exit(1); } subnet_flag = radix_trie_add_attr_label(rt, label); if (subnet_flag == 0) { fprintf(info, "error: count not add subnet label %s to radix_trie\n", label); exit(1); } err = radix_trie_add_subnets_from_file(rt, subnet_file, subnet_flag, info); if (err != ok) { fprintf(info, "error: could not add labeled subnets from file %s\n", subnet_file); exit(1); } } fprintf(info, "configured labeled subnets (radix_trie), using %u bytes of memory\n", get_rt_mem_usage()); } if (config.anon_addrs_file != NULL) { if (anon_init(config.anon_addrs_file, info) == failure) { fprintf(info, "error: could not initialize anonymization subnets from file %s\n", config.anon_addrs_file); return -1; } } if (config.filename != NULL) { char *outputdir; /* * set output directory */ if (config.outputdir) { outputdir = config.outputdir; } else { outputdir = "."; } /* * generate an "auto" output file name, based on the MAC address * and the current time, if we are "auto" configured */ if (strncmp(config.filename, "auto", strlen("auto")) == 0) { if (mode == mode_online) { unsigned char *addr = ifl[0].mac_addr; time_t now = time(0); struct tm *t = localtime(&now); snprintf(filename, MAX_FILENAME_LEN, "%s/flocap-%02x%02x%02x%02x%02x%02x-h%d-m%d-s%d-D%d-M%d-Y%d-%s-", outputdir, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], t->tm_hour, t->tm_min, t->tm_sec, t->tm_mday, t->tm_mon, t->tm_year + 1900, t->tm_zone); } else { fprintf(info, "error: cannot use \"output = auto\" with no interface specified; use -o or -l options\n"); return usage(argv[0]); } fprintf(info, "auto generated output filename: %s\n", filename); } else { /* set output file based on command line or config file */ if (cli_filename) { strncpy(filename, config.filename, MAX_FILENAME_LEN); } else { char tmp_filename[MAX_FILENAME_LEN]; strncpy(tmp_filename, filename, MAX_FILENAME_LEN); snprintf(filename, MAX_FILENAME_LEN, "%s/%s", outputdir, tmp_filename); } } file_base_len = strlen(filename); if (config.max_records != 0) { snprintf(filename + file_base_len, MAX_FILENAME_LEN - file_base_len, "%d", file_count); } output = fopen(filename, "w"); if (output == NULL) { fprintf(info, "error: could not open output file %s (%s)\n", filename, strerror(errno)); return -1; } } else { output = stdout; } if (ifile != NULL) { opt_count--; argv[1+opt_count] = ifile; } if (mode == mode_online) { /* live capture */ int linktype; /* * sanity check: we can't be in both offline mode and online mode * simultaneously */ if ((argc-opt_count > 1) || (ifile != NULL)) { fprintf(info, "error: both interface (%s) and pcap input file (%s) specified\n", config.interface, argv[1+opt_count]); return usage(argv[0]); } anon_print_subnets(info); signal(SIGINT, sig_close); /* Ctl-C causes graceful shutdown */ signal(SIGTERM, sig_close); // signal(SIGHUP, sig_reload); // signal(SIGTSTP, sig_reload); signal(SIGQUIT, sig_reload); /* Ctl-\ causes an info dump */ /* * set capture interface as needed */ if (strncmp(config.interface, "auto", strlen("auto")) == 0) { capture_if = ifl[0].name; fprintf(info, "starting capture on interface %s\n", ifl[0].name); } else { capture_if = config.interface; } errbuf[0] = 0; handle = pcap_open_live(capture_if, 65535, config.promisc, 10000, errbuf); if (handle == NULL) { fprintf(info, "could not open device %s: %s\n", capture_if, errbuf); return -1; } if (errbuf[0] != 0) { fprintf(stderr, "warning: %s\n", errbuf); } /* verify that we can handle the link layer headers */ linktype = pcap_datalink(handle); if (linktype != DLT_EN10MB) { fprintf(info, "device %s has unsupported linktype (%d)\n", capture_if, linktype); return -2; } if (filter_exp) { /* compile the filter expression */ if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) { fprintf(info, "error: could not parse filter %s: %s\n", filter_exp, pcap_geterr(handle)); return -3; } /* apply the compiled filter */ if (pcap_setfilter(handle, &fp) == -1) { fprintf(info, "error: could not install filter %s: %s\n", filter_exp, pcap_geterr(handle)); return -4; } } /* * run as daemon, if so configured, without closing stderr and * stdout, and without changing the working directory */ if (config.daemon) { daemon(1, 1); } /* * flush "info" output stream to ensure log file accuracy */ fflush(info); /* * write out JSON preamble */ fprintf(output, "{\n"); config_print_json(output, &config); fprintf(output, "\"appflows\": [\n"); while(1) { struct timeval time_of_day, inactive_flow_cutoff; /* loop over packets captured from interface */ pcap_loop(handle, NUM_PACKETS_IN_LOOP, process_packet, NULL); if (output_level > none) { fprintf(output, "# pcap processing loop done\n"); } if (config.report_exe) { /* * periodically obtain host/process flow data */ if (get_host_flow_data() != 0) { fprintf(info, "warning: could not obtain host/process flow data\n"); } } /* * periodically report on progress */ if ((flocap_stats_get_num_packets() % NUM_PACKETS_BETWEEN_STATS_OUTPUT) == 0) { flocap_stats_output(info); } /* print out inactive flows */ gettimeofday(&time_of_day, NULL); timer_sub(&time_of_day, &time_window, &inactive_flow_cutoff); flow_record_list_print_json(&inactive_flow_cutoff); if (config.filename) { /* rotate output file if needed */ if (config.max_records && (records_in_file > config.max_records)) { /* * write JSON postamble */ fprintf(output, "\n] }\n"); fclose(output); if (config.upload_servername) { upload_file(filename, config.upload_servername, config.upload_key, config.retain_local); } // printf("records: %d\tmax_records: %d\n", records_in_file, config.max_records); file_count++; if (config.max_records != 0) { snprintf(filename + file_base_len, MAX_FILENAME_LEN - file_base_len, "%d", file_count); } output = fopen(filename, "w"); if (output == NULL) { perror("error: could not open output file"); return -1; } records_in_file = 0; fprintf(output, "{ \"appflows\": [\n"); } /* * flush out buffered debug/info/log messages on the "info" stream */ fflush(info); } // fflush(output); } fprintf(output, "\n] }\n"); if (filter_exp) { pcap_freecode(&fp); } pcap_close(handle); } else { /* mode = mode_offline */ if ((argc-opt_count <= 1) && (ifile == NULL)) { fprintf(stderr, "error: missing pcap file name(s)\n"); return usage(argv[0]); } fprintf(output, "{\n"); config_print_json(output, &config); fprintf(output, "\"appflows\": [\n"); flow_record_list_init(); flocap_stats_timer_init(); for (i=1+opt_count; i<argc; i++) { if (stat(argv[i], &sb) == 0 && S_ISDIR(sb.st_mode)) { if ((dir = opendir(argv[i])) != NULL) { while ((ent = readdir(dir)) != NULL) { if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) { strcpy(pcap_filename, argv[i]); if (pcap_filename[strlen(pcap_filename)-1] != '/') { strcat(pcap_filename, "/"); } strcat(pcap_filename, ent->d_name); tmp_ret = process_pcap_file(pcap_filename, filter_exp, &net, &fp); if (tmp_ret < 0) { return tmp_ret; } } } closedir(dir); } else { /* error opening directory*/ printf("Error opening directory: %s\n", argv[i]); return -1; } } else { tmp_ret = process_pcap_file(argv[i], filter_exp, &net, &fp); if (tmp_ret < 0) { return tmp_ret; } } } fprintf(output, "\n]"); fprintf(output, "\n}\n"); } flocap_stats_output(info); // config_print(info, &config); return 0; }
/* * Function to initiate the radix_trie unit tests * returns 0 on success * returns 1 on failure */ static int radix_trie_high_level_unit_test () { struct radix_trie rt; attr_flags flag_internal, flag_malware, flag; char *configfile = "internal.net"; struct in_addr addr; enum status err; unsigned test_failed = 0; zfile output; output = zattach(stdout, "w"); if (output == NULL) { fprintf(stderr, "%s: error: could not initialize (possibly compressed) stdout for writing\n", __FUNCTION__); } /* initialize */ err = radix_trie_init(&rt); if (err != ok) { fprintf(stderr, "%s: error: could not initialize radix_trie\n", __FUNCTION__); } /* create a label */ flag_internal = radix_trie_add_attr_label(&rt, "internal"); if (flag_internal == 0) { fprintf(stderr, "%s: error: count not add label\n", __FUNCTION__); test_failed = 1; return failure; } flag_malware = radix_trie_add_attr_label(&rt, "malware"); if (flag_internal == 0) { fprintf(stderr, "%s: error: count not add label\n", __FUNCTION__); test_failed = 1; return failure; } /* add subnets from file */ err = radix_trie_add_subnets_from_file(&rt, configfile, flag_internal, stderr); if (err != ok) { fprintf(stderr, "%s: error: could not add subnets to radix_trie from file %s\n", __FUNCTION__, configfile); test_failed = 1; } printf("++++++++++++\n"); err = radix_trie_add_subnets_from_file(&rt, configfile, flag_malware, stderr); if (err != ok) { fprintf(stderr, "%s: error: could not add subnets to radix_trie from file %s\n", __FUNCTION__, configfile); test_failed = 1; } /* verify addresses and labels */ addr.s_addr = htonl(0xc0a80101); /* 192.168.1.1 */ flag = radix_trie_lookup_addr(&rt, addr); if ((flag & flag_internal) == 0) { zprintf(output, "error: attribute lookup failed (expected %x, got %x)\n", flag_internal, flag); test_failed = 1; } attr_flags_json_print_labels(&rt, flag, "addr", output); addr.s_addr = htonl(0x08080808); /* not internal */ flag = radix_trie_lookup_addr(&rt, addr); if ((flag & flag_internal) == 1) { zprintf(output, "error: attribute lookup failed (did not expect %x, but got %x)\n", flag_internal, flag); test_failed = 1; } attr_flags_json_print_labels(&rt, flag, "addr", output); printf("\n==================\n"); radix_trie_print(&rt); if (test_failed) { printf("FAILURE; at least one test failed\n"); } else { printf("all tests passed\n"); } return test_failed; /* 0 on success, 1 otherwise */ }