123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715 |
- /* $Id: tcpprep.c 878 2004-11-07 03:31:04Z aturner $ */
- /*
- * Copyright (c) 2001-2004 Aaron Turner.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Purpose:
- * 1) Remove the performance bottleneck in tcpreplay for choosing an NIC
- * 2) Seperate code to make it more manageable
- * 3) Add addtional features which require multiple passes of a pcap
- *
- * Support:
- * Right now we support matching source IP based upon on of the following:
- * - Regular expression
- * - IP address is contained in one of a list of CIDR blocks
- * - Auto learning of CIDR block for servers (clients all other)
- */
- #include <libnet.h>
- #include <pcap.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <regex.h>
- #include <string.h>
- #include <unistd.h>
- #include "config.h"
- #include "tcpreplay.h"
- #include "cache.h"
- #include "cidr.h"
- #include "tcpprep.h"
- #include "tree.h"
- #include "list.h"
- #include "xX.h"
- #include "err.h"
- #include "rbtree.h"
- #include "utils.h"
- #include "services.h"
- #include "sll.h"
- #include "fakepcap.h"
- /*
- * global variables
- */
- #ifdef DEBUG
- int debug = 0;
- #endif
- int info = 0;
- char *ourregex = NULL;
- char *cidr = NULL;
- regex_t *preg = NULL;
- CIDR *cidrdata = NULL;
- CACHE *cachedata = NULL;
- struct data_tree treeroot;
- struct options options;
- struct bpf_program bpf;
- char tcpservices[NUM_PORTS], udpservices[NUM_PORTS];
- int mode = 0;
- int automode = 0;
- double ratio = 0.0;
- int max_mask = DEF_MAX_MASK;
- int min_mask = DEF_MIN_MASK;
- extern char *optarg;
- extern int optind, opterr, optopt;
- int include_exclude_mode;
- CIDR *xX_cidr = NULL;
- LIST *xX_list = NULL;
- /* required to include utils.c */
- int non_ip = 0;
- int maxpacket = 0;
- /* we get this from libpcap */
- extern char pcap_version[];
- static void usage();
- static void version();
- static int check_ip_regex(const unsigned long ip);
- static unsigned long process_raw_packets(pcap_t * pcap);
- static int check_dst_port(ip_hdr_t *ip_hdr, int len);
- static void
- version()
- {
- fprintf(stderr, "tcpprep version: %s", VERSION);
- #ifdef DEBUG
- fprintf(stderr, " (debug)\n");
- #else
- fprintf(stderr, "\n");
- #endif
- fprintf(stderr, "Cache file supported: %s\n", CACHEVERSION);
- fprintf(stderr, "Compiled against libnet: %s\n", LIBNET_VERSION);
- fprintf(stderr, "Compiled against libpcap: %s\n", pcap_version);
- exit(0);
- }
- /*
- * usage
- */
- static void
- usage()
- {
- fprintf(stderr, "Usage: tcpprep [-a -n <mode> -N <type> | -c <cidr> | -p | -r <regex>] \\\n\t\t-o <out> -i <in> <args>\n");
- fprintf(stderr, "-a\t\t\tSplit traffic in Auto Mode\n"
- "-c CIDR1,CIDR2,...\tSplit traffic in CIDR Mode\n"
- "-C <comment>\t\tEmbed comment in tcpprep cache file\n");
- #ifdef DEBUG
- fprintf(stderr, "-d <level>\t\tEnable debug output to STDERR\n");
- #endif
- fprintf(stderr, "-h\t\t\tHelp\n"
- "-i <capfile>\t\tInput capture file to process\n"
- "-m <minmask>\t\tMinimum mask length in Auto/Router mode\n"
- "-M <maxmask>\t\tMaximum mask length in Auto/Router mode\n"
- "-n <auto mode>\t\tUse specified algorithm in Auto Mode\n"
- "-N client|server\tClassify non-IP traffic as client/server\n"
- "-o <outputfile>\t\tOutput cache file name\n"
- "-p\t\t\tSplit traffic based on destination port\n"
- "-P <file>\t\tPrint comment in tcpprep file\n");
- fprintf(stderr, "-r <regex>\t\tSplit traffic in Regex Mode\n"
- "-R <ratio>\t\tSpecify a ratio to use in Auto Mode\n"
- "-s <file>\t\tSpecify service ports in /etc/services format\n"
- "-x <match>\t\tOnly send the packets specified\n"
- "-X <match>\t\tSend all the packets except those specified\n"
- "-v\t\t\tVerbose\n"
- "-V\t\t\tVersion\n");
- exit(0);
- }
- static void
- print_comment(char *file)
- {
- char *cachedata = NULL;
- u_int64_t count = 0;
- count = read_cache(&cachedata, file);
- printf("tcpprep args: %s\n", options.tcpprep_comment);
- printf("Cache contains data for %llu packets\n", count);
- exit(0);
- }
- /*
- * checks the dst port to see if this is destined for a server port.
- * returns 1 for true, 0 for false
- */
- static int
- check_dst_port(ip_hdr_t *ip_hdr, int len)
- {
- tcp_hdr_t *tcp_hdr = NULL;
- udp_hdr_t *udp_hdr = NULL;
- dbg(3, "Checking the destination port...");
- if (ip_hdr->ip_p == IPPROTO_TCP) {
- tcp_hdr = (tcp_hdr_t *)get_layer4(ip_hdr);
- /* is a service? */
- if (tcpservices[ntohs(tcp_hdr->th_dport)]) {
- dbg(1, "TCP packet is destined for a server port: %d", ntohs(tcp_hdr->th_dport));
- return 1;
- }
- /* nope */
- dbg(1, "TCP packet is NOT destined for a server port: %d", ntohs(tcp_hdr->th_dport));
- return 0;
- } else if (ip_hdr->ip_p == IPPROTO_UDP) {
- udp_hdr = (udp_hdr_t *)get_layer4(ip_hdr);
- /* is a service? */
- if (udpservices[ntohs(udp_hdr->uh_dport)]) {
- dbg(1, "UDP packet is destined for a server port: %d", ntohs(udp_hdr->uh_dport));
- return 1;
- }
- /* nope */
- dbg(1, "UDP packet is NOT destined for a server port: %d", ntohs(udp_hdr->uh_dport));
- return 0;
- }
-
- /* not a TCP or UDP packet... return as non_ip */
- dbg(1, "Packet isn't a UDP or TCP packet... no port to process.");
- return non_ip;
- }
- /*
- * checks to see if an ip address matches a regex. Returns 1 for true
- * 0 for false
- */
- static int
- check_ip_regex(const unsigned long ip)
- {
- int eflags = 0;
- u_char src_ip[16];
- size_t nmatch = 0;
- regmatch_t *pmatch = NULL;
- memset(src_ip, '\0', 16);
- strncat((char *)src_ip, (char *)libnet_addr2name4(ip, LIBNET_DONT_RESOLVE),
- 15);
- if (regexec(preg, (char *)src_ip, nmatch, pmatch, eflags) == 0) {
- return (1);
- }
- else {
- return (0);
- }
- }
- /*
- * uses libpcap library to parse the packets and build
- * the cache file.
- */
- static unsigned long
- process_raw_packets(pcap_t * pcap)
- {
- ip_hdr_t *ip_hdr = NULL;
- eth_hdr_t *eth_hdr = NULL;
- struct sll_header *sll_hdr = NULL;
- struct cisco_hdlc_header *hdlc_hdr = NULL;
- int l2len = 0;
- u_int16_t protocol = 0;
- struct pcap_pkthdr pkthdr;
- const u_char *pktdata = NULL;
- unsigned long packetnum = 0;
- int linktype = 0;
- #ifdef FORCE_ALIGN
- u_char ipbuff[MAXPACKET];
- #endif
- while ((pktdata = pcap_next(pcap, &pkthdr)) != NULL) {
- packetnum++;
- eth_hdr = NULL;
- sll_hdr = NULL;
- ip_hdr = NULL;
- hdlc_hdr = NULL;
- linktype = pcap_datalink(pcap);
- dbg(1, "Linktype is %s (0x%x)",
- pcap_datalink_val_to_description(linktype), linktype);
- switch (linktype) {
- case DLT_EN10MB:
- eth_hdr = (eth_hdr_t *) pktdata;
- l2len = LIBNET_ETH_H;
- protocol = eth_hdr->ether_type;
- break;
- case DLT_LINUX_SLL:
- sll_hdr = (struct sll_header *) pktdata;
- l2len = SLL_HDR_LEN;
- protocol = sll_hdr->sll_protocol;
- break;
- case DLT_RAW:
- protocol = ETHERTYPE_IP;
- l2len = 0;
- break;
- case DLT_CHDLC:
- hdlc_hdr = (struct cisco_hdlc_header *)pktdata;
- protocol = hdlc_hdr->protocol;
- l2len = CISCO_HDLC_LEN;
- break;
- default:
- errx(1, "WTF? How'd we get here with an invalid DLT type: %s (0x%x)",
- pcap_datalink_val_to_description(linktype), linktype);
- break;
- }
- dbg(1, "Packet %d", packetnum);
- /* look for include or exclude LIST match */
- if (xX_list != NULL) {
- if (include_exclude_mode < xXExclude) {
- if (!check_list(xX_list, (packetnum))) {
- add_cache(&cachedata, 0, 0);
- continue;
- }
- }
- else if (check_list(xX_list, (packetnum))) {
- add_cache(&cachedata, 0, 0);
- continue;
- }
- }
- if (htons(protocol) != ETHERTYPE_IP) {
- dbg(2, "Packet isn't IP: %#0.4x", protocol);
- if (mode != AUTO_MODE) /* we don't want to cache
- * these packets twice */
- add_cache(&cachedata, 1, non_ip);
- continue;
- }
- #ifdef FORCE_ALIGN
- /*
- * copy layer 3 and up to our temp packet buffer
- * for now on, we have to edit the packetbuff because
- * just before we send the packet, we copy the packetbuff
- * back onto the pkt.data + l2len buffer
- * we do all this work to prevent byte alignment issues
- */
- ip_hdr = (ip_hdr_t *) & ipbuff;
- memcpy(ip_hdr, (pktdata + l2len), (pkthdr.caplen - l2len));
- #else
- /*
- * on non-strict byte align systems, don't need to memcpy(),
- * just point to l2len bytes into the existing buffer
- */
- ip_hdr = (ip_hdr_t *) (pktdata + l2len);
- #endif
- /* look for include or exclude CIDR match */
- if (xX_cidr != NULL) {
- if (!process_xX_by_cidr(include_exclude_mode, xX_cidr, ip_hdr)) {
- add_cache(&cachedata, 0, 0);
- continue;
- }
- }
- switch (mode) {
- case REGEX_MODE:
- add_cache(&cachedata, 1, check_ip_regex(ip_hdr->ip_src.s_addr));
- break;
- case CIDR_MODE:
- add_cache(&cachedata, 1,
- check_ip_CIDR(cidrdata, ip_hdr->ip_src.s_addr));
- break;
- case AUTO_MODE:
- /* first run through in auto mode: create tree */
- add_tree(ip_hdr->ip_src.s_addr, pktdata);
- break;
- case ROUTER_MODE:
- add_cache(&cachedata, 1,
- check_ip_CIDR(cidrdata, ip_hdr->ip_src.s_addr));
- break;
- case BRIDGE_MODE:
- /*
- * second run through in auto mode: create bridge
- * based cache
- */
- add_cache(&cachedata, 1,
- check_ip_tree(UNKNOWN, ip_hdr->ip_src.s_addr));
- break;
- case SERVER_MODE:
- /*
- * second run through in auto mode: create bridge
- * where unknowns are servers
- */
- add_cache(&cachedata, 1,
- check_ip_tree(SERVER, ip_hdr->ip_src.s_addr));
- break;
- case CLIENT_MODE:
- /*
- * second run through in auto mode: create bridge
- * where unknowns are clients
- */
- add_cache(&cachedata, 1,
- check_ip_tree(CLIENT, ip_hdr->ip_src.s_addr));
- break;
- case PORT_MODE:
- /*
- * process ports based on their destination port
- */
- add_cache(&cachedata, 1,
- check_dst_port(ip_hdr, (pkthdr.caplen - l2len)));
- break;
- }
- }
- return packetnum;
- }
- /*
- * main()
- */
- int
- main(int argc, char *argv[])
- {
- int out_file, ch, regex_error = 0, mask_count = 0;
- int regex_flags = REG_EXTENDED|REG_NOSUB;
- int i;
- char *infilename = NULL;
- char *outfilename = NULL;
- char ebuf[EBUF_SIZE];
- u_int64_t totpackets = 0;
- void *xX = NULL;
- pcap_t *pcap = NULL;
- char errbuf[PCAP_ERRBUF_SIZE];
- char myargs[1024];
- memset(&options, '\0', sizeof(options));
- options.bpf_optimize = BPF_OPTIMIZE;
- preg = (regex_t *) malloc(sizeof(regex_t));
- if (preg == NULL)
- err(1, "malloc");
- /* set default server ports (override w/ -s) */
- memset(tcpservices, '\0', NUM_PORTS);
- memset(udpservices, '\0', NUM_PORTS);
- for (i = DEFAULT_LOW_SERVER_PORT; i <= DEFAULT_HIGH_SERVER_PORT; i++) {
- tcpservices[i] = 1;
- udpservices[i] = 1;
- }
- #ifdef DEBUG
- while ((ch = getopt(argc, argv, "ad:c:C:r:R:o:pP:i:hm:M:n:N:s:x:X:vV")) != -1)
- #else
- while ((ch = getopt(argc, argv, "ac:C:r:R:o:pP:i:hm:M:n:N:s:x:X:vV")) != -1)
- #endif
- switch (ch) {
- case 'a':
- mode = AUTO_MODE;
- break;
- case 'c':
- if (!parse_cidr(&cidrdata, optarg, ",")) {
- usage();
- }
- mode = CIDR_MODE;
- break;
- case 'C':
- /* our comment_len is only 16bit - myargs[] */
- if (strlen(optarg) > ((1 << 16) - 1 - sizeof(myargs)))
- errx(1, "Comment length %d is longer then max allowed (%d)",
- strlen(optarg), (1 << 16) - 1 - sizeof(myargs));
- /* copy all of our args to myargs */
- memset(myargs, '\0', sizeof(myargs));
- for (i = 1; i < argc; i ++) {
- /* skip the -C <comment> */
- if (strcmp(argv[i], "-C") == 0)
- i += 2;
- strncat(myargs, argv[i], sizeof(myargs) - 1);
- strncat(myargs, " ", sizeof(myargs) - 1);
- }
- strncat(myargs, "\n", sizeof(myargs) - 1);
- dbg(1, "comment args length: %d", strlen(myargs));
- /* malloc our buffer to be + 1 strlen so we can null terminate */
- if ((options.tcpprep_comment = (char *)malloc(strlen(optarg)
- + strlen(myargs) + 1)) == NULL)
- errx(1, "Unable to malloc() memory for comment");
- memset(options.tcpprep_comment, '\0', strlen(optarg) + 1 + strlen(myargs));
- strcpy(options.tcpprep_comment, myargs);
- strcat(options.tcpprep_comment, optarg);
- dbg(1, "comment length: %d", strlen(optarg));
- break;
- #ifdef DEBUG
- case 'd':
- debug = atoi(optarg);
- break;
- #endif
- case 'h':
- usage();
- break;
- case 'i':
- infilename = optarg;
- break;
- case 'm':
- min_mask = atoi(optarg);
- mask_count++;
- break;
- case 'M':
- max_mask = atoi(optarg);
- mask_count++;
- break;
- case 'n':
- if (strcmp(optarg, "bridge") == 0) {
- automode = BRIDGE_MODE;
- }
- else if (strcmp(optarg, "router") == 0) {
- automode = ROUTER_MODE;
- }
- else if (strcmp(optarg, "client") == 0) {
- automode = CLIENT_MODE;
- }
- else if (strcmp(optarg, "server") == 0) {
- automode = SERVER_MODE;
- }
- else {
- errx(1, "Invalid auto mode type: %s", optarg);
- }
- break;
- case 'N':
- if (strcmp(optarg, "client") == 0) {
- non_ip = 0;
- }
- else if (strcmp(optarg, "server") == 0) {
- non_ip = 1;
- }
- else {
- errx(1, "-N must be client or server");
- }
- break;
- case 'o':
- outfilename = optarg;
- break;
- case 'p':
- mode = PORT_MODE;
- break;
- case 'P':
- print_comment(optarg);
- /* exits */
- break;
- case 'r':
- ourregex = optarg;
- mode = REGEX_MODE;
- if ((regex_error = regcomp(preg, ourregex, regex_flags))) {
- if (regerror(regex_error, preg, ebuf, EBUF_SIZE) != -1) {
- errx(1, "Error compiling regex: %s", ebuf);
- }
- else {
- errx(1, "Error compiling regex.");
- }
- exit(1);
- }
- break;
- case 'R':
- ratio = atof(optarg);
- break;
- case 's':
- printf("Parsing services...\n");
- parse_services(optarg);
- break;
- case 'x':
- if (include_exclude_mode != 0)
- errx(1, "Error: Can only specify -x OR -X");
- include_exclude_mode = 'x';
- if ((include_exclude_mode =
- parse_xX_str(include_exclude_mode, optarg, &xX)) == 0)
- errx(1, "Unable to parse -x: %s", optarg);
- if (include_exclude_mode & xXPacket) {
- xX_list = (LIST *) xX;
- }
- else if (! (include_exclude_mode & xXBPF)) {
- xX_cidr = (CIDR *) xX;
- }
- break;
- case 'X':
- if (include_exclude_mode != 0)
- errx(1, "Error: Can only specify -x OR -X");
- include_exclude_mode = 'X';
- if ((include_exclude_mode =
- parse_xX_str(include_exclude_mode, optarg, &xX)) == 0)
- errx(1, "Unable to parse -X: %s", optarg);
- if (include_exclude_mode & xXPacket) {
- xX_list = (LIST *) xX;
- }
- else {
- xX_cidr = (CIDR *) xX;
- }
- break;
- case 'v':
- info = 1;
- break;
- case 'V':
- version();
- break;
- default:
- usage();
- }
- /* process args */
- if ((mode != CIDR_MODE) && (mode != REGEX_MODE) &&
- (mode != AUTO_MODE) && (mode != PORT_MODE))
- errx(1, "You need to specifiy a vaild CIDR list or regex, or choose auto or port mode");
- if ((mask_count > 0) && (mode != AUTO_MODE))
- errx(1,
- "You can't specify a min/max mask length unless you use auto mode");
- if ((mode == AUTO_MODE) && (automode == 0))
- errx(1,
- "You must specify -n (bridge|router|client|server) with auto mode (-a)");
- if ((ratio != 0.0) && (mode != AUTO_MODE))
- errx(1, "Ratio (-R) only works in auto mode (-a).");
- if (ratio < 0)
- errx(1, "Ratio must be a non-negative number.");
- if (info && mode == AUTO_MODE)
- fprintf(stderr, "Building auto mode pre-cache data structure...\n");
- if (info && mode == CIDR_MODE)
- fprintf(stderr, "Building cache file from CIDR list...\n");
- if (info && mode == REGEX_MODE)
- fprintf(stderr, "Building cache file from regex...\n");
- if (infilename == NULL)
- errx(1, "You must specify a pcap file to read via -i");
- /* set ratio to the default if unspecified */
- if (ratio == 0.0)
- ratio = DEF_RATIO;
- /* open the cache file */
- out_file =
- open(outfilename, O_WRONLY | O_CREAT | O_TRUNC,
- S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH);
- if (out_file == -1)
- err(1, "Unable to open cache file %s for writing.", outfilename);
- readpcap:
- /* open the pcap file */
- if ((pcap = pcap_open_offline(infilename, errbuf)) == NULL) {
- errx(1, "Error opening file: %s", errbuf);
- }
- if ((pcap_datalink(pcap) != DLT_EN10MB) &&
- (pcap_datalink(pcap) != DLT_LINUX_SLL) &&
- (pcap_datalink(pcap) != DLT_RAW) &&
- (pcap_datalink(pcap) != DLT_CHDLC)) {
- errx(1, "Unsupported pcap DLT type: 0x%x", pcap_datalink(pcap));
- }
- /* do we apply a bpf filter? */
- if (options.bpf_filter != NULL) {
- if (pcap_compile(pcap, &bpf, options.bpf_filter,
- options.bpf_optimize, 0) != 0) {
- errx(1, "Error compiling BPF filter: %s", pcap_geterr(pcap));
- }
- pcap_setfilter(pcap, &bpf);
- }
- if ((totpackets = process_raw_packets(pcap)) == 0) {
- pcap_close(pcap);
- errx(1, "Error: no packets were processed. Filter too limiting?");
- }
- pcap_close(pcap);
- /* we need to process the pcap file twice in HASH/AUTO mode */
- if (mode == AUTO_MODE) {
- mode = automode;
- if (mode == ROUTER_MODE) { /* do we need to convert TREE->CIDR? */
- if (info)
- fprintf(stderr, "Building network list from pre-cache...\n");
- if (!process_tree()) {
- errx(1,
- "Error: unable to build a valid list of servers. Aborting.");
- }
- }
- else {
- /*
- * in bridge mode we need to calculate client/sever
- * manually since this is done automatically in
- * process_tree()
- */
- tree_calculate(&treeroot);
- }
- if (info)
- fprintf(stderr, "Buliding cache file...\n");
- /*
- * re-process files, but this time generate
- * cache
- */
- goto readpcap;
- }
- #ifdef DEBUG
- if (debug && (cidrdata != NULL))
- print_cidr(cidrdata);
- #endif
- /* write cache data */
- totpackets = write_cache(cachedata, out_file, totpackets);
- if (info)
- fprintf(stderr, "Done.\nCached %llu packets.\n", totpackets);
- /* close cache file */
- close(out_file);
- return 0;
- }
|