tcpprep.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /* $Id: tcpprep.c 1571 2006-08-05 20:02:29Z aturner $ */
  2. /*
  3. * Copyright (c) 2001-2005 Aaron Turner.
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the names of the copyright owners nor the names of its
  16. * contributors may be used to endorse or promote products derived from
  17. * this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  20. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  21. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  22. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  23. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  25. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  27. * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  28. * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  29. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. /*
  32. * Purpose:
  33. * 1) Remove the performance bottleneck in tcpreplay for choosing an NIC
  34. * 2) Seperate code to make it more manageable
  35. * 3) Add addtional features which require multiple passes of a pcap
  36. *
  37. * Support:
  38. * Right now we support matching source IP based upon on of the following:
  39. * - Regular expression
  40. * - IP address is contained in one of a list of CIDR blocks
  41. * - Auto learning of CIDR block for servers (clients all other)
  42. */
  43. #include "config.h"
  44. #include "defines.h"
  45. #include "common.h"
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include <regex.h>
  50. #include <string.h>
  51. #include <unistd.h>
  52. #include <errno.h>
  53. #include "tcpprep.h"
  54. #include "tcpedit/tcpedit.h"
  55. #include "tcpprep_opts.h"
  56. #include "lib/tree.h"
  57. #include "tree.h"
  58. #include "lib/sll.h"
  59. #include "lib/strlcpy.h"
  60. #include "mac.h"
  61. /*
  62. * global variables
  63. */
  64. #ifdef DEBUG
  65. int debug = 0;
  66. #endif
  67. #ifdef HAVE_TCPDUMP
  68. tcpdump_t tcpdump;
  69. #endif
  70. tcpprep_opt_t options;
  71. int info = 0;
  72. char *ourregex = NULL;
  73. char *cidr = NULL;
  74. tcpr_data_tree_t treeroot;
  75. static void init(void);
  76. static void post_args(int, char *[]);
  77. static void print_comment(const char *);
  78. static void print_info(const char *);
  79. static void print_stats(const char *);
  80. static int check_ip_regex(const unsigned long ip);
  81. static COUNTER process_raw_packets(pcap_t * pcap);
  82. static int check_dst_port(ipv4_hdr_t *ip_hdr, int len);
  83. /*
  84. * main()
  85. */
  86. int
  87. main(int argc, char *argv[])
  88. {
  89. int out_file;
  90. COUNTER totpackets = 0;
  91. char errbuf[PCAP_ERRBUF_SIZE];
  92. int optct = 0;
  93. init(); /* init our globals */
  94. optct = optionProcess(&tcpprepOptions, argc, argv);
  95. post_args(argc, argv);
  96. argc -= optct;
  97. argv += optct;
  98. /* open the cache file */
  99. if ((out_file = open(OPT_ARG(CACHEFILE), O_WRONLY | O_CREAT | O_TRUNC,
  100. S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH)) == -1)
  101. errx(1, "Unable to open cache file %s for writing: %s",
  102. OPT_ARG(CACHEFILE), strerror(errno));
  103. readpcap:
  104. /* open the pcap file */
  105. if ((options.pcap = pcap_open_offline(OPT_ARG(PCAP), errbuf)) == NULL)
  106. errx(1, "Error opening file: %s", errbuf);
  107. /* make sure we support the DLT type */
  108. switch(pcap_datalink(options.pcap)) {
  109. case DLT_EN10MB:
  110. case DLT_LINUX_SLL:
  111. case DLT_RAW:
  112. case DLT_C_HDLC:
  113. break; /* do nothing because all is good */
  114. default:
  115. errx(1, "Unsupported pcap DLT type: 0x%x", pcap_datalink(options.pcap));
  116. }
  117. /* Can only split based on MAC address for ethernet */
  118. if ((pcap_datalink(options.pcap) != DLT_EN10MB) &&
  119. (options.mode == MAC_MODE)) {
  120. err(1, "MAC mode splitting is only supported by DLT_EN10MB packet captures.");
  121. }
  122. #ifdef HAVE_TCPDUMP
  123. if (HAVE_OPT(VERBOSE)) {
  124. tcpdump.filename = safe_strdup(OPT_ARG(PCAP));
  125. tcpdump_open(&tcpdump);
  126. }
  127. #endif
  128. /* do we apply a bpf filter? */
  129. if (options.bpf.filter != NULL) {
  130. if (pcap_compile(options.pcap, &options.bpf.program, options.bpf.filter,
  131. options.bpf.optimize, 0) != 0) {
  132. errx(1, "Error compiling BPF filter: %s", pcap_geterr(options.pcap));
  133. }
  134. pcap_setfilter(options.pcap, &options.bpf.program);
  135. }
  136. if ((totpackets = process_raw_packets(options.pcap)) == 0) {
  137. pcap_close(options.pcap);
  138. err(1, "No packets were processed. Filter too limiting?");
  139. }
  140. pcap_close(options.pcap);
  141. #ifdef HAVE_TCPDUMP
  142. tcpdump_close(&tcpdump);
  143. #endif
  144. /* we need to process the pcap file twice in HASH/AUTO mode */
  145. if (options.mode == AUTO_MODE) {
  146. options.mode = options.automode;
  147. if (options.mode == ROUTER_MODE) { /* do we need to convert TREE->CIDR? */
  148. if (info)
  149. fprintf(stderr, "Building network list from pre-cache...\n");
  150. if (!process_tree()) {
  151. err(1, "Error: unable to build a valid list of servers. Aborting.");
  152. }
  153. }
  154. else {
  155. /*
  156. * in bridge mode we need to calculate client/sever
  157. * manually since this is done automatically in
  158. * process_tree()
  159. */
  160. tree_calculate(&treeroot);
  161. }
  162. if (info)
  163. fprintf(stderr, "Buliding cache file...\n");
  164. /*
  165. * re-process files, but this time generate
  166. * cache
  167. */
  168. goto readpcap;
  169. }
  170. #ifdef DEBUG
  171. if (debug && (options.cidrdata != NULL))
  172. print_cidr(options.cidrdata);
  173. #endif
  174. /* write cache data */
  175. totpackets = write_cache(options.cachedata, out_file, totpackets,
  176. options.comment);
  177. if (info)
  178. notice("Done.\nCached " COUNTER_SPEC " packets.\n", totpackets);
  179. /* close cache file */
  180. close(out_file);
  181. return 0;
  182. }
  183. /*
  184. * checks the dst port to see if this is destined for a server port.
  185. * returns 1 for true, 0 for false
  186. */
  187. static int
  188. check_dst_port(ipv4_hdr_t *ip_hdr, int len)
  189. {
  190. tcp_hdr_t *tcp_hdr = NULL;
  191. udp_hdr_t *udp_hdr = NULL;
  192. assert(ip_hdr);
  193. if (len < ((ip_hdr->ip_hl * 4) + 4))
  194. return 0; /* not enough data in the packet to know */
  195. dbg(3, "Checking the destination port...");
  196. if (ip_hdr->ip_p == IPPROTO_TCP) {
  197. tcp_hdr = (tcp_hdr_t *)get_layer4(ip_hdr);
  198. /* is a service? */
  199. if (options.services.tcp[ntohs(tcp_hdr->th_dport)]) {
  200. dbgx(1, "TCP packet is destined for a server port: %d", ntohs(tcp_hdr->th_dport));
  201. return 1;
  202. }
  203. /* nope */
  204. dbgx(1, "TCP packet is NOT destined for a server port: %d", ntohs(tcp_hdr->th_dport));
  205. return 0;
  206. } else if (ip_hdr->ip_p == IPPROTO_UDP) {
  207. udp_hdr = (udp_hdr_t *)get_layer4(ip_hdr);
  208. /* is a service? */
  209. if (options.services.udp[ntohs(udp_hdr->uh_dport)]) {
  210. dbgx(1, "UDP packet is destined for a server port: %d", ntohs(udp_hdr->uh_dport));
  211. return 1;
  212. }
  213. /* nope */
  214. dbgx(1, "UDP packet is NOT destined for a server port: %d", ntohs(udp_hdr->uh_dport));
  215. return 0;
  216. }
  217. /* not a TCP or UDP packet... return as non_ip */
  218. dbg(1, "Packet isn't a UDP or TCP packet... no port to process.");
  219. return options.nonip;
  220. }
  221. /*
  222. * checks to see if an ip address matches a regex. Returns 1 for true
  223. * 0 for false
  224. */
  225. static int
  226. check_ip_regex(const unsigned long ip)
  227. {
  228. int eflags = 0;
  229. u_char src_ip[16];
  230. size_t nmatch = 0;
  231. regmatch_t *pmatch = NULL;
  232. memset(src_ip, '\0', 16);
  233. strlcpy((char *)src_ip, (char *)get_addr2name4(ip, RESOLVE),
  234. sizeof(src_ip));
  235. if (regexec(&options.preg, (char *)src_ip, nmatch, pmatch, eflags) == 0) {
  236. return (1);
  237. }
  238. else {
  239. return (0);
  240. }
  241. }
  242. /*
  243. * uses libpcap library to parse the packets and build
  244. * the cache file.
  245. */
  246. static COUNTER
  247. process_raw_packets(pcap_t * pcap)
  248. {
  249. ipv4_hdr_t *ip_hdr = NULL;
  250. eth_hdr_t *eth_hdr = NULL;
  251. struct pcap_pkthdr pkthdr;
  252. const u_char *pktdata = NULL;
  253. COUNTER packetnum = 0;
  254. int l2len, cache_result = 0;
  255. u_char ipbuff[MAXPACKET], *buffptr;
  256. #ifdef HAVE_TCPDUMP
  257. struct pollfd poller[1];
  258. poller[0].fd = tcpdump.outfd;
  259. poller[0].events = POLLIN;
  260. poller[0].revents = 0;
  261. #endif
  262. while ((pktdata = pcap_next(pcap, &pkthdr)) != NULL) {
  263. packetnum++;
  264. dbgx(1, "Packet " COUNTER_SPEC, packetnum);
  265. /* look for include or exclude LIST match */
  266. if (options.xX.list != NULL) {
  267. if (options.xX.mode < xXExclude) {
  268. if (!check_list(options.xX.list, packetnum)) {
  269. add_cache(&options.cachedata, DONT_SEND, 0);
  270. continue;
  271. }
  272. }
  273. else if (check_list(options.xX.list, packetnum)) {
  274. add_cache(&options.cachedata, DONT_SEND, 0);
  275. continue;
  276. }
  277. }
  278. eth_hdr = (eth_hdr_t *)pktdata;
  279. /* get the IP header (if any) */
  280. buffptr = ipbuff;
  281. ip_hdr = (ipv4_hdr_t *)get_ipv4(pktdata, pkthdr.caplen,
  282. pcap_datalink(pcap), &buffptr);
  283. if (ip_hdr == NULL) {
  284. dbg(2, "Packet isn't IP");
  285. /* we don't want to cache these packets twice */
  286. if (options.mode != AUTO_MODE) {
  287. dbg(3, "Adding to cache using options for Non-IP packets");
  288. add_cache(&options.cachedata, SEND, options.nonip);
  289. }
  290. continue;
  291. }
  292. l2len = get_l2len(pktdata, pkthdr.caplen, pcap_datalink(pcap));
  293. /* look for include or exclude CIDR match */
  294. if (options.xX.cidr != NULL) {
  295. if (!process_xX_by_cidr(options.xX.mode, options.xX.cidr, ip_hdr)) {
  296. add_cache(&options.cachedata, DONT_SEND, 0);
  297. continue;
  298. }
  299. }
  300. switch (options.mode) {
  301. case REGEX_MODE:
  302. dbg(2, "processing regex mode...");
  303. cache_result = add_cache(&options.cachedata, SEND,
  304. check_ip_regex(ip_hdr->ip_src.s_addr));
  305. break;
  306. case CIDR_MODE:
  307. dbg(2, "processing cidr mode...");
  308. cache_result = add_cache(&options.cachedata, SEND,
  309. check_ip_cidr(options.cidrdata, ip_hdr->ip_src.s_addr));
  310. break;
  311. case MAC_MODE:
  312. dbg(2, "processing mac mode...");
  313. cache_result = add_cache(&options.cachedata, SEND,
  314. macinstring(options.maclist, (u_char *)eth_hdr->ether_shost));
  315. break;
  316. case AUTO_MODE:
  317. dbg(2, "processing first pass of auto mode...");
  318. /* first run through in auto mode: create tree */
  319. add_tree(ip_hdr->ip_src.s_addr, pktdata);
  320. break;
  321. case ROUTER_MODE:
  322. /*
  323. * second run through in auto mode: create route
  324. * based cache
  325. */
  326. dbg(2, "processing second pass of auto: router mode...");
  327. cache_result = add_cache(&options.cachedata, SEND,
  328. check_ip_tree(options.nonip, ip_hdr->ip_src.s_addr));
  329. break;
  330. case BRIDGE_MODE:
  331. /*
  332. * second run through in auto mode: create bridge
  333. * based cache
  334. */
  335. dbg(2, "processing second pass of auto: bridge mode...");
  336. cache_result = add_cache(&options.cachedata, SEND,
  337. check_ip_tree(UNKNOWN, ip_hdr->ip_src.s_addr));
  338. break;
  339. case SERVER_MODE:
  340. /*
  341. * second run through in auto mode: create bridge
  342. * where unknowns are servers
  343. */
  344. dbg(2, "processing second pass of auto: server mode...");
  345. cache_result = add_cache(&options.cachedata, SEND,
  346. check_ip_tree(SERVER, ip_hdr->ip_src.s_addr));
  347. break;
  348. case CLIENT_MODE:
  349. /*
  350. * second run through in auto mode: create bridge
  351. * where unknowns are clients
  352. */
  353. dbg(2, "processing second pass of auto: client mode...");
  354. cache_result = add_cache(&options.cachedata, SEND,
  355. check_ip_tree(CLIENT, ip_hdr->ip_src.s_addr));
  356. break;
  357. case PORT_MODE:
  358. /*
  359. * process ports based on their destination port
  360. */
  361. dbg(2, "processing port mode...");
  362. cache_result = add_cache(&options.cachedata, SEND,
  363. check_dst_port(ip_hdr, (pkthdr.caplen - l2len)));
  364. break;
  365. }
  366. #ifdef HAVE_TCPDUMP
  367. if (options.verbose)
  368. tcpdump_print(&tcpdump, &pkthdr, pktdata);
  369. #endif
  370. }
  371. return packetnum;
  372. }
  373. /*
  374. * init our options
  375. */
  376. void
  377. init(void)
  378. {
  379. int i;
  380. memset(&options, '\0', sizeof(options));
  381. options.bpf.optimize = BPF_OPTIMIZE;
  382. for (i = DEFAULT_LOW_SERVER_PORT; i <= DEFAULT_HIGH_SERVER_PORT; i++) {
  383. options.services.tcp[i] = 1;
  384. options.services.udp[i] = 1;
  385. }
  386. }
  387. /*
  388. * post process args
  389. */
  390. static void
  391. post_args(int argc, char *argv[])
  392. {
  393. char myargs[MYARGS_LEN];
  394. int i, bufsize;
  395. char *tempstr;
  396. memset(myargs, 0, MYARGS_LEN);
  397. /* print_comment and print_info don't return */
  398. if (HAVE_OPT(PRINT_COMMENT))
  399. print_comment(OPT_ARG(PRINT_COMMENT));
  400. if (HAVE_OPT(PRINT_INFO))
  401. print_info(OPT_ARG(PRINT_INFO));
  402. if (HAVE_OPT(PRINT_STATS))
  403. print_stats(OPT_ARG(PRINT_STATS));
  404. if (! HAVE_OPT(CACHEFILE) && ! HAVE_OPT(PCAP))
  405. err(1, "Must specify an output cachefile (-o) and input pcap (-i)");
  406. if (! options.mode)
  407. err(1, "Must specify a processing mode: -a, -c, -r, -p");
  408. #ifdef DEBUG
  409. if (HAVE_OPT(DBUG))
  410. debug = OPT_VALUE_DBUG;
  411. #endif
  412. #ifdef HAVE_TCPDUMP
  413. if (HAVE_OPT(VERBOSE)) {
  414. options.verbose = 1;
  415. }
  416. if (HAVE_OPT(DECODE))
  417. tcpdump.args = safe_strdup(OPT_ARG(DECODE));
  418. /*
  419. * put the open after decode options so they are passed to tcpdump
  420. */
  421. #endif
  422. /*
  423. * if we are to include the cli args, then prep it for the
  424. * cache file header
  425. */
  426. if (! options.nocomment) {
  427. /* copy all of our args to myargs */
  428. for (i = 1; i < argc; i ++) {
  429. /* skip the -C <comment> */
  430. if (strcmp(argv[i], "-C") == 0) {
  431. i += 2;
  432. continue;
  433. }
  434. strlcat(myargs, argv[i], MYARGS_LEN);
  435. strlcat(myargs, " ", MYARGS_LEN);
  436. }
  437. /* remove trailing space */
  438. myargs[strlen(myargs) - 1] = 0;
  439. dbgx(1, "Comment args length: %d", strlen(myargs));
  440. }
  441. /* setup or options.comment buffer so that that we get args\ncomment */
  442. if (options.comment != NULL) {
  443. strlcat(myargs, "\n", MYARGS_LEN);
  444. bufsize = strlen(options.comment) + strlen(myargs) + 1;
  445. options.comment = (char *)safe_realloc(options.comment,
  446. bufsize);
  447. tempstr = strdup(options.comment);
  448. strlcpy(options.comment, myargs, bufsize);
  449. strlcat(options.comment, tempstr, bufsize);
  450. } else {
  451. bufsize = strlen(myargs) + 1;
  452. options.comment = (char *)safe_malloc(bufsize);
  453. strlcpy(options.comment, myargs, bufsize);
  454. }
  455. dbgx(1, "Final comment length: %d", strlen(options.comment));
  456. /* copy over our min/max mask */
  457. options.min_mask = OPT_VALUE_MINMASK;
  458. options.max_mask = OPT_VALUE_MAXMASK;
  459. if (! options.min_mask > options.max_mask)
  460. errx(1, "Min network mask len (%d) must be less then max network mask len (%d)",
  461. options.min_mask, options.max_mask);
  462. options.ratio = atof(OPT_ARG(RATIO));
  463. if (options.ratio < 0)
  464. err(1, "Ratio must be a non-negative number.");
  465. }
  466. /*
  467. * print the tcpprep cache file comment
  468. */
  469. static void
  470. print_comment(const char *file)
  471. {
  472. char *cachedata = NULL;
  473. char *comment = NULL;
  474. COUNTER count = 0;
  475. count = read_cache(&cachedata, file, &comment);
  476. printf("tcpprep args: %s\n", comment);
  477. printf("Cache contains data for " COUNTER_SPEC " packets\n", count);
  478. exit(0);
  479. }
  480. /*
  481. * prints out the cache file details
  482. */
  483. static void
  484. print_info(const char *file)
  485. {
  486. char *cachedata = NULL;
  487. char *comment = NULL;
  488. COUNTER count = 0, i;
  489. count = read_cache(&cachedata, file, &comment);
  490. for (i = 1; i <= count; i ++) {
  491. switch (check_cache(cachedata, i)) {
  492. case CACHE_PRIMARY:
  493. printf("Packet " COUNTER_SPEC " -> Primary\n", i);
  494. break;
  495. case CACHE_SECONDARY:
  496. printf("Packet " COUNTER_SPEC " -> Secondary\n", i);
  497. break;
  498. case CACHE_NOSEND:
  499. printf("Packet " COUNTER_SPEC " -> Don't Send\n", i);
  500. break;
  501. default:
  502. err(1, "Invalid cachedata value!");
  503. break;
  504. }
  505. }
  506. exit(0);
  507. }
  508. static void
  509. print_stats(const char *file)
  510. {
  511. char *cachedata = NULL;
  512. char *comment = NULL;
  513. COUNTER count = 0;
  514. COUNTER pri = 0, sec = 0, nosend = 0;
  515. count = read_cache(&cachedata, file, &comment);
  516. for (COUNTER i = 1; i <= count; i ++) {
  517. int cacheval = check_cache(cachedata, i);
  518. switch (cacheval) {
  519. case CACHE_PRIMARY:
  520. pri ++;
  521. break;
  522. case CACHE_SECONDARY:
  523. sec ++;
  524. break;
  525. case CACHE_NOSEND:
  526. nosend ++;
  527. break;
  528. default:
  529. errx(1, "Unknown cache value: %d", cacheval);
  530. }
  531. }
  532. printf("Primary packets:\t" COUNTER_SPEC "\n", pri);
  533. printf("Secondary packets:\t" COUNTER_SPEC "\n", sec);
  534. printf("Skipped packets:\t" COUNTER_SPEC "\n", nosend);
  535. printf("------------------------------\n");
  536. printf("Total packets:\t\t" COUNTER_SPEC "\n", count);
  537. exit(0);
  538. }
  539. /*
  540. Local Variables:
  541. mode:c
  542. indent-tabs-mode:nil
  543. c-basic-offset:4
  544. End:
  545. */