tcpprep.c 20 KB

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