tcpprep.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /* $Id$ */
  2. /*
  3. * Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
  4. * Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
  5. *
  6. * The Tcpreplay Suite of tools is free software: you can redistribute it
  7. * and/or modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation, either version 3 of the
  9. * License, or with the authors permission any later version.
  10. *
  11. * The Tcpreplay Suite is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with the Tcpreplay Suite. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /*
  20. * Purpose:
  21. * 1) Remove the performance bottleneck in tcpreplay for choosing an NIC
  22. * 2) Separate code to make it more manageable
  23. * 3) Add additional features which require multiple passes of a pcap
  24. *
  25. * Support:
  26. * Right now we support matching source IP based upon on of the following:
  27. * - Regular expression
  28. * - IP address is contained in one of a list of CIDR blocks
  29. * - Auto learning of CIDR block for servers (clients all other)
  30. */
  31. #include "config.h"
  32. #include "defines.h"
  33. #include "common.h"
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <regex.h>
  38. #include <string.h>
  39. #include <unistd.h>
  40. #include <errno.h>
  41. #include "tcpprep.h"
  42. #include "tcpprep_api.h"
  43. #include "tcpprep_opts.h"
  44. #include "lib/tree.h"
  45. #include "tree.h"
  46. #include "lib/sll.h"
  47. /*
  48. * global variables
  49. */
  50. #ifdef DEBUG
  51. int debug = 0;
  52. #endif
  53. tcpprep_t *tcpprep;
  54. int info = 0;
  55. char *ourregex = NULL;
  56. char *cidr = NULL;
  57. tcpr_data_tree_t treeroot;
  58. void print_comment(const char *);
  59. void print_info(const char *);
  60. void print_stats(const char *);
  61. static int check_ipv4_regex(const unsigned long ip);
  62. static int check_ipv6_regex(const struct tcpr_in6_addr *addr);
  63. static COUNTER process_raw_packets(pcap_t * pcap);
  64. static int check_dst_port(ipv4_hdr_t *ip_hdr, ipv6_hdr_t *ip6_hdr, int len);
  65. /*
  66. * main()
  67. */
  68. int
  69. main(int argc, char *argv[])
  70. {
  71. int out_file;
  72. COUNTER totpackets = 0;
  73. char errbuf[PCAP_ERRBUF_SIZE];
  74. tcpprep_opt_t *options;
  75. tcpprep = tcpprep_init();
  76. options = tcpprep->options;
  77. optionProcess(&tcpprepOptions, argc, argv);
  78. tcpprep_post_args(tcpprep, argc, argv);
  79. /* open the cache file */
  80. if ((out_file = open(OPT_ARG(CACHEFILE), O_WRONLY | O_CREAT | O_TRUNC,
  81. S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH)) == -1)
  82. errx(-1, "Unable to open cache file %s for writing: %s",
  83. OPT_ARG(CACHEFILE), strerror(errno));
  84. readpcap:
  85. /* open the pcap file */
  86. if ((options->pcap = pcap_open_offline(OPT_ARG(PCAP), errbuf)) == NULL)
  87. errx(-1, "Error opening file: %s", errbuf);
  88. #ifdef HAVE_PCAP_SNAPSHOT
  89. if (pcap_snapshot(options->pcap) < 65535)
  90. warnx("%s was captured using a snaplen of %d bytes. This may mean you have truncated packets.",
  91. OPT_ARG(PCAP), pcap_snapshot(options->pcap));
  92. #endif
  93. /* make sure we support the DLT type */
  94. switch(pcap_datalink(options->pcap)) {
  95. case DLT_EN10MB:
  96. case DLT_LINUX_SLL:
  97. case DLT_RAW:
  98. case DLT_C_HDLC:
  99. case DLT_JUNIPER_ETHER:
  100. case DLT_PPP_SERIAL:
  101. break; /* do nothing because all is good */
  102. default:
  103. errx(-1, "Unsupported pcap DLT type: 0x%x", pcap_datalink(options->pcap));
  104. }
  105. /* Can only split based on MAC address for ethernet */
  106. if ((pcap_datalink(options->pcap) != DLT_EN10MB) &&
  107. (options->mode == MAC_MODE)) {
  108. err(-1, "MAC mode splitting is only supported by DLT_EN10MB packet captures.");
  109. }
  110. #ifdef ENABLE_VERBOSE
  111. if (HAVE_OPT(VERBOSE)) {
  112. tcpdump_open(&tcpprep->tcpdump, options->pcap);
  113. }
  114. #endif
  115. /* do we apply a bpf filter? */
  116. if (options->bpf.filter != NULL) {
  117. if (pcap_compile(options->pcap, &options->bpf.program, options->bpf.filter,
  118. options->bpf.optimize, 0) != 0) {
  119. errx(-1, "Error compiling BPF filter: %s", pcap_geterr(options->pcap));
  120. }
  121. pcap_setfilter(options->pcap, &options->bpf.program);
  122. pcap_freecode(&options->bpf.program);
  123. }
  124. if ((totpackets = process_raw_packets(options->pcap)) == 0) {
  125. pcap_close(options->pcap);
  126. err(-1, "No packets were processed. Filter too limiting?");
  127. }
  128. pcap_close(options->pcap);
  129. #ifdef ENABLE_VERBOSE
  130. tcpdump_close(&tcpprep->tcpdump);
  131. #endif
  132. /* we need to process the pcap file twice in HASH/AUTO mode */
  133. if (options->mode == AUTO_MODE) {
  134. options->mode = options->automode;
  135. if (options->mode == ROUTER_MODE) { /* do we need to convert TREE->CIDR? */
  136. if (info)
  137. notice("Building network list from pre-cache...\n");
  138. if (!process_tree()) {
  139. err(-1, "Error: unable to build a valid list of servers. Aborting.");
  140. }
  141. }
  142. else {
  143. /*
  144. * in bridge mode we need to calculate client/sever
  145. * manually since this is done automatically in
  146. * process_tree()
  147. */
  148. tree_calculate(&treeroot);
  149. }
  150. if (info)
  151. notice("Building cache file...\n");
  152. /*
  153. * re-process files, but this time generate
  154. * cache
  155. */
  156. goto readpcap;
  157. }
  158. #ifdef DEBUG
  159. if (debug && (options->cidrdata != NULL))
  160. print_cidr(options->cidrdata);
  161. #endif
  162. /* write cache data */
  163. totpackets = write_cache(options->cachedata, out_file, totpackets,
  164. options->comment);
  165. if (info)
  166. notice("Done.\nCached " COUNTER_SPEC " packets.\n", totpackets);
  167. /* close cache file */
  168. close(out_file);
  169. restore_stdin();
  170. return 0;
  171. }
  172. /**
  173. * checks the dst port to see if this is destined for a server port.
  174. * returns 1 for true, 0 for false
  175. */
  176. static int
  177. check_dst_port(ipv4_hdr_t *ip_hdr, ipv6_hdr_t *ip6_hdr, int len)
  178. {
  179. tcp_hdr_t *tcp_hdr = NULL;
  180. udp_hdr_t *udp_hdr = NULL;
  181. tcpprep_opt_t *options = tcpprep->options;
  182. uint8_t proto;
  183. u_char *l4;
  184. if (ip_hdr) {
  185. if (len < ((ip_hdr->ip_hl * 4) + 4))
  186. return 0; /* not enough data in the packet to know */
  187. proto = ip_hdr->ip_p;
  188. l4 = get_layer4_v4(ip_hdr, (u_char*)ip_hdr + len);
  189. } else if (ip6_hdr) {
  190. if (len < (TCPR_IPV6_H + 4))
  191. return 0; /* not enough data in the packet to know */
  192. proto = get_ipv6_l4proto(ip6_hdr, (u_char*)ip6_hdr + len);
  193. dbgx(3, "Our layer4 proto is 0x%hhu", proto);
  194. if ((l4 = get_layer4_v6(ip6_hdr, (u_char*)ip6_hdr + len)) == NULL)
  195. return 0;
  196. dbgx(3, "Found proto %u at offset %p. base %p (%p)", proto, (void *)l4, (void *)ip6_hdr, (void*)(l4 - (u_char *)ip6_hdr));
  197. } else {
  198. assert(0);
  199. }
  200. dbg(3, "Checking the destination port...");
  201. switch(proto) {
  202. case IPPROTO_TCP:
  203. tcp_hdr = (tcp_hdr_t *)l4;
  204. /* is a service? */
  205. if (options->services.tcp[ntohs(tcp_hdr->th_dport)]) {
  206. dbgx(1, "TCP packet is destined for a server port: %d", ntohs(tcp_hdr->th_dport));
  207. return 1;
  208. }
  209. /* nope */
  210. dbgx(1, "TCP packet is NOT destined for a server port: %d", ntohs(tcp_hdr->th_dport));
  211. return 0;
  212. break;
  213. case IPPROTO_UDP:
  214. udp_hdr = (udp_hdr_t *)l4;
  215. /* is a service? */
  216. if (options->services.udp[ntohs(udp_hdr->uh_dport)]) {
  217. dbgx(1, "UDP packet is destined for a server port: %d", ntohs(udp_hdr->uh_dport));
  218. return 1;
  219. }
  220. /* nope */
  221. dbgx(1, "UDP packet is NOT destined for a server port: %d", ntohs(udp_hdr->uh_dport));
  222. return 0;
  223. break;
  224. default:
  225. /* not a TCP or UDP packet... return as non_ip */
  226. dbg(1, "Packet isn't a UDP or TCP packet... no port to process.");
  227. return options->nonip;
  228. }
  229. }
  230. /**
  231. * checks to see if an ip address matches a regex. Returns 1 for true
  232. * 0 for false
  233. */
  234. static int
  235. check_ipv4_regex(const unsigned long ip)
  236. {
  237. int eflags = 0;
  238. u_char src_ip[16];
  239. size_t nmatch = 0;
  240. tcpprep_opt_t *options = tcpprep->options;
  241. memset(src_ip, '\0', sizeof(src_ip));
  242. strlcpy((char *)src_ip, (char *)get_addr2name4(ip, RESOLVE),
  243. sizeof(src_ip));
  244. if (regexec(&options->preg, (char *)src_ip, nmatch, NULL, eflags) == 0) {
  245. return 1;
  246. } else {
  247. return 0;
  248. }
  249. }
  250. static int
  251. check_ipv6_regex(const struct tcpr_in6_addr *addr)
  252. {
  253. int eflags = 0;
  254. u_char src_ip[INET6_ADDRSTRLEN];
  255. size_t nmatch = 0;
  256. tcpprep_opt_t *options = tcpprep->options;
  257. memset(src_ip, '\0', sizeof(src_ip));
  258. strlcpy((char *)src_ip, (char *)get_addr2name6(addr, RESOLVE),
  259. sizeof(src_ip));
  260. if (regexec(&options->preg, (char *)src_ip, nmatch, NULL, eflags) == 0) {
  261. return 1;
  262. } else {
  263. return 0;
  264. }
  265. }
  266. /**
  267. * uses libpcap library to parse the packets and build
  268. * the cache file.
  269. */
  270. static COUNTER
  271. process_raw_packets(pcap_t * pcap)
  272. {
  273. struct pcap_pkthdr pkthdr;
  274. const u_char *pktdata = NULL;
  275. COUNTER packetnum = 0;
  276. int l2len;
  277. u_char *ipbuff, *buffptr;
  278. tcpr_dir_t direction = TCPR_DIR_ERROR;
  279. tcpprep_opt_t *options = tcpprep->options;
  280. assert(pcap);
  281. ipbuff = safe_malloc(MAXPACKET);
  282. while ((pktdata = safe_pcap_next(pcap, &pkthdr)) != NULL) {
  283. ipv4_hdr_t *ip_hdr = NULL;
  284. ipv6_hdr_t *ip6_hdr = NULL;
  285. eth_hdr_t *eth_hdr = NULL;
  286. packetnum++;
  287. dbgx(1, "Packet " COUNTER_SPEC, packetnum);
  288. /* look for include or exclude LIST match */
  289. if (options->xX.list != NULL) {
  290. if (options->xX.mode < xXExclude) {
  291. /* include list */
  292. if (!check_list(options->xX.list, packetnum)) {
  293. add_cache(&(options->cachedata), DONT_SEND, 0);
  294. continue;
  295. }
  296. }
  297. /* exclude list */
  298. else if (check_list(options->xX.list, packetnum)) {
  299. add_cache(&(options->cachedata), DONT_SEND, 0);
  300. continue;
  301. }
  302. }
  303. /*
  304. * If the packet doesn't include an IPv4 header we should just treat
  305. * it as a non-IP packet, UNLESS we're in MAC mode, in which case
  306. * we should let the MAC matcher below handle it
  307. */
  308. if (options->mode != MAC_MODE) {
  309. dbg(3, "Looking for IPv4/v6 header in non-MAC mode");
  310. /* get the IP header (if any) */
  311. buffptr = ipbuff;
  312. /* first look for IPv4 */
  313. if ((ip_hdr = (ipv4_hdr_t *)get_ipv4(pktdata, pkthdr.caplen,
  314. pcap_datalink(pcap), &buffptr)) != NULL) {
  315. dbg(2, "Packet is IPv4");
  316. } else if ((ip6_hdr = (ipv6_hdr_t *)get_ipv6(pktdata, pkthdr.caplen,
  317. pcap_datalink(pcap), &buffptr)) != NULL) {
  318. /* IPv6 */
  319. dbg(2, "Packet is IPv6");
  320. } else {
  321. /* we're something else... */
  322. dbg(2, "Packet isn't IPv4/v6");
  323. /* we don't want to cache these packets twice */
  324. if (options->mode != AUTO_MODE) {
  325. dbg(3, "Adding to cache using options for Non-IP packets");
  326. add_cache(&options->cachedata, SEND, options->nonip);
  327. }
  328. /* go to next packet */
  329. continue;
  330. }
  331. l2len = get_l2len(pktdata, pkthdr.caplen, pcap_datalink(pcap));
  332. if (l2len < 0) {
  333. /* go to next packet */
  334. continue;
  335. }
  336. /* look for include or exclude CIDR match */
  337. if (options->xX.cidr != NULL) {
  338. if (ip_hdr) {
  339. if (!process_xX_by_cidr_ipv4(options->xX.mode, options->xX.cidr, ip_hdr)) {
  340. add_cache(&options->cachedata, DONT_SEND, 0);
  341. continue;
  342. }
  343. } else if (ip6_hdr) {
  344. if (!process_xX_by_cidr_ipv6(options->xX.mode, options->xX.cidr, ip6_hdr)) {
  345. add_cache(&options->cachedata, DONT_SEND, 0);
  346. continue;
  347. }
  348. }
  349. }
  350. }
  351. switch (options->mode) {
  352. case REGEX_MODE:
  353. dbg(2, "processing regex mode...");
  354. if (ip_hdr) {
  355. direction = check_ipv4_regex(ip_hdr->ip_src.s_addr);
  356. } else if (ip6_hdr) {
  357. direction = check_ipv6_regex(&ip6_hdr->ip_src);
  358. }
  359. /* reverse direction? */
  360. if (HAVE_OPT(REVERSE) && (direction == TCPR_DIR_C2S || direction == TCPR_DIR_S2C))
  361. direction = direction == TCPR_DIR_C2S ? TCPR_DIR_S2C : TCPR_DIR_C2S;
  362. add_cache(&options->cachedata, SEND, direction);
  363. break;
  364. case CIDR_MODE:
  365. dbg(2, "processing cidr mode...");
  366. if (ip_hdr) {
  367. direction = check_ip_cidr(options->cidrdata, ip_hdr->ip_src.s_addr) ? TCPR_DIR_C2S : TCPR_DIR_S2C;
  368. } else if (ip6_hdr) {
  369. direction = check_ip6_cidr(options->cidrdata, &ip6_hdr->ip_src) ? TCPR_DIR_C2S : TCPR_DIR_S2C;
  370. }
  371. /* reverse direction? */
  372. if (HAVE_OPT(REVERSE) && (direction == TCPR_DIR_C2S || direction == TCPR_DIR_S2C))
  373. direction = direction == TCPR_DIR_C2S ? TCPR_DIR_S2C : TCPR_DIR_C2S;
  374. add_cache(&options->cachedata, SEND, direction);
  375. break;
  376. case MAC_MODE:
  377. dbg(2, "processing mac mode...");
  378. if (pkthdr.caplen < sizeof(*eth_hdr)) {
  379. dbg(2, "capture length too short for mac mode processing");
  380. break;
  381. }
  382. eth_hdr = (eth_hdr_t *)pktdata;
  383. direction = macinstring(options->maclist, (u_char *)eth_hdr->ether_shost);
  384. /* reverse direction? */
  385. if (HAVE_OPT(REVERSE) && (direction == TCPR_DIR_C2S || direction == TCPR_DIR_S2C))
  386. direction = direction == TCPR_DIR_C2S ? TCPR_DIR_S2C : TCPR_DIR_C2S;
  387. add_cache(&options->cachedata, SEND, direction);
  388. break;
  389. case AUTO_MODE:
  390. dbg(2, "processing first pass of auto mode...");
  391. /* first run through in auto mode: create tree */
  392. if (options->automode != FIRST_MODE) {
  393. if (ip_hdr) {
  394. add_tree_ipv4(ip_hdr->ip_src.s_addr,
  395. pktdata,
  396. pkthdr.caplen,
  397. pcap_datalink(pcap));
  398. } else if (ip6_hdr) {
  399. add_tree_ipv6(&ip6_hdr->ip_src,
  400. pktdata,
  401. pkthdr.caplen,
  402. pcap_datalink(pcap));
  403. }
  404. } else {
  405. if (ip_hdr) {
  406. add_tree_first_ipv4(pktdata,
  407. pkthdr.caplen,
  408. pcap_datalink(pcap));
  409. } else if (ip6_hdr) {
  410. add_tree_first_ipv6(pktdata,
  411. pkthdr.caplen,
  412. pcap_datalink(pcap));
  413. }
  414. }
  415. break;
  416. case ROUTER_MODE:
  417. /*
  418. * second run through in auto mode: create route
  419. * based cache
  420. */
  421. dbg(2, "processing second pass of auto: router mode...");
  422. if (ip_hdr) {
  423. add_cache(&options->cachedata, SEND,
  424. check_ip_tree(options->nonip, ip_hdr->ip_src.s_addr));
  425. } else {
  426. add_cache(&options->cachedata, SEND,
  427. check_ip6_tree(options->nonip, &ip6_hdr->ip_src));
  428. }
  429. break;
  430. case BRIDGE_MODE:
  431. /*
  432. * second run through in auto mode: create bridge
  433. * based cache
  434. */
  435. dbg(2, "processing second pass of auto: bridge mode...");
  436. if (ip_hdr) {
  437. add_cache(&options->cachedata, SEND,
  438. check_ip_tree(DIR_UNKNOWN, ip_hdr->ip_src.s_addr));
  439. } else {
  440. add_cache(&options->cachedata, SEND,
  441. check_ip6_tree(DIR_UNKNOWN, &ip6_hdr->ip_src));
  442. }
  443. break;
  444. case SERVER_MODE:
  445. /*
  446. * second run through in auto mode: create bridge
  447. * where unknowns are servers
  448. */
  449. dbg(2, "processing second pass of auto: server mode...");
  450. if (ip_hdr) {
  451. add_cache(&options->cachedata, SEND,
  452. check_ip_tree(DIR_SERVER, ip_hdr->ip_src.s_addr));
  453. } else {
  454. add_cache(&options->cachedata, SEND,
  455. check_ip6_tree(DIR_SERVER, &ip6_hdr->ip_src));
  456. }
  457. break;
  458. case CLIENT_MODE:
  459. /*
  460. * second run through in auto mode: create bridge
  461. * where unknowns are clients
  462. */
  463. dbg(2, "processing second pass of auto: client mode...");
  464. if (ip_hdr) {
  465. add_cache(&options->cachedata, SEND,
  466. check_ip_tree(DIR_CLIENT, ip_hdr->ip_src.s_addr));
  467. } else {
  468. add_cache(&options->cachedata, SEND,
  469. check_ip6_tree(DIR_CLIENT, &ip6_hdr->ip_src));
  470. }
  471. break;
  472. case PORT_MODE:
  473. /*
  474. * process ports based on their destination port
  475. */
  476. dbg(2, "processing port mode...");
  477. add_cache(&options->cachedata, SEND,
  478. check_dst_port(ip_hdr, ip6_hdr, (pkthdr.caplen - l2len)));
  479. break;
  480. case FIRST_MODE:
  481. /*
  482. * First packet mode, looks at each host and picks clients
  483. * by the ones which send the first packet in a session
  484. */
  485. dbg(2, "processing second pass of auto: first packet mode...");
  486. if (ip_hdr) {
  487. add_cache(&options->cachedata, SEND,
  488. check_ip_tree(DIR_UNKNOWN, ip_hdr->ip_src.s_addr));
  489. } else {
  490. add_cache(&options->cachedata, SEND,
  491. check_ip6_tree(DIR_UNKNOWN, &ip6_hdr->ip_src));
  492. }
  493. break;
  494. default:
  495. errx(-1, "Whoops! What mode are we in anyways? %d", options->mode);
  496. }
  497. #ifdef ENABLE_VERBOSE
  498. if (options->verbose)
  499. tcpdump_print(&tcpprep->tcpdump, &pkthdr, pktdata);
  500. #endif
  501. }
  502. safe_free(ipbuff);
  503. return packetnum;
  504. }
  505. /**
  506. * print the tcpprep cache file comment
  507. */
  508. void
  509. print_comment(const char *file)
  510. {
  511. char *cachedata = NULL;
  512. char *comment = NULL;
  513. COUNTER count = 0;
  514. count = read_cache(&cachedata, file, &comment);
  515. printf("tcpprep args: %s\n", comment);
  516. printf("Cache contains data for " COUNTER_SPEC " packets\n", count);
  517. exit(0);
  518. }
  519. /**
  520. * prints out the cache file details
  521. */
  522. void
  523. print_info(const char *file)
  524. {
  525. char *cachedata = NULL;
  526. char *comment = NULL;
  527. COUNTER count = 0, i;
  528. count = read_cache(&cachedata, file, &comment);
  529. if (count > 65535)
  530. exit(-1);
  531. for (i = 1; i <= count; i ++) {
  532. switch (check_cache(cachedata, i)) {
  533. case TCPR_DIR_C2S:
  534. printf("Packet " COUNTER_SPEC " -> Primary\n", i);
  535. break;
  536. case TCPR_DIR_S2C:
  537. printf("Packet " COUNTER_SPEC " -> Secondary\n", i);
  538. break;
  539. case TCPR_DIR_NOSEND:
  540. printf("Packet " COUNTER_SPEC " -> Don't Send\n", i);
  541. break;
  542. default:
  543. err(-1, "Invalid cachedata value!");
  544. break;
  545. }
  546. }
  547. exit(0);
  548. }
  549. /**
  550. * Print the per-packet statistics
  551. */
  552. void
  553. print_stats(const char *file)
  554. {
  555. char *cachedata = NULL;
  556. char *comment = NULL;
  557. COUNTER i, count = 0;
  558. COUNTER pri = 0, sec = 0, nosend = 0;
  559. count = read_cache(&cachedata, file, &comment);
  560. for (i = 1; i <= count; i ++) {
  561. int cacheval = check_cache(cachedata, i);
  562. switch (cacheval) {
  563. case TCPR_DIR_C2S:
  564. pri ++;
  565. break;
  566. case TCPR_DIR_S2C:
  567. sec ++;
  568. break;
  569. case TCPR_DIR_NOSEND:
  570. nosend ++;
  571. break;
  572. default:
  573. errx(-1, "Unknown cache value: %d", cacheval);
  574. }
  575. }
  576. printf("Primary packets:\t" COUNTER_SPEC "\n", pri);
  577. printf("Secondary packets:\t" COUNTER_SPEC "\n", sec);
  578. printf("Skipped packets:\t" COUNTER_SPEC "\n", nosend);
  579. printf("------------------------------\n");
  580. printf("Total packets:\t\t" COUNTER_SPEC "\n", count);
  581. exit(0);
  582. }