tcpdump.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. /* $Id$ */
  2. /*
  3. * Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
  4. * Copyright (c) 2013-2018 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. * This code allows us to use tcpdump to print packet decodes.
  21. * Basically, we create a local AF_UNIX socketpair, fork a copy
  22. * of ourselves, link 1/2 of the pair to STDIN of the child and
  23. * replace the child with tcpdump. We then send a "pcap" file
  24. * over the socket so that tcpdump can print it's decode to STDOUT.
  25. *
  26. * Idea and a lot of code stolen from Christain Kreibich's
  27. * <christian@whoop.org> libnetdude 0.4 code. Any bugs are mine. :)
  28. *
  29. * This product includes software developed by the University of California,
  30. * Lawrence Berkeley Laboratory and its contributors
  31. */
  32. #include "config.h"
  33. #include "defines.h"
  34. #include "common.h"
  35. #include <stdlib.h>
  36. #include <sys/types.h>
  37. #include <unistd.h>
  38. #include <sys/socket.h>
  39. #include <sys/wait.h>
  40. #include <errno.h>
  41. #include <string.h>
  42. #ifdef HAVE_SIGNAL_H
  43. #include <signal.h>
  44. #endif
  45. #include "tcpdump.h"
  46. #ifndef HAVE_STRLCPY
  47. #include "lib/strlcpy.h"
  48. #endif
  49. #ifdef DEBUG
  50. extern int debug;
  51. #endif
  52. char *options_vec[OPTIONS_VEC_SIZE];
  53. static int tcpdump_fill_in_options(char *opt);
  54. static int can_exec(const char *filename);
  55. /**
  56. * given a packet, print a decode of via tcpdump
  57. */
  58. int
  59. tcpdump_print(tcpdump_t *tcpdump, struct pcap_pkthdr *pkthdr, const u_char *data)
  60. {
  61. struct pollfd poller;
  62. int res, total;
  63. char decode[TCPDUMP_DECODE_LEN];
  64. struct compact_pkthdr {
  65. struct {
  66. uint32_t ts_sec;
  67. uint32_t ts_usec;
  68. } ts;
  69. uint32_t caplen; /* length of portion present */
  70. uint32_t len; /* length this packet (off wire) */
  71. } actual_pkthdr;
  72. assert(tcpdump);
  73. assert(pkthdr);
  74. assert(data);
  75. /* convert header to file-format packet header */
  76. actual_pkthdr.ts.ts_sec = (uint32_t)pkthdr->ts.tv_sec;
  77. actual_pkthdr.ts.ts_usec = (uint32_t)pkthdr->ts.tv_sec;
  78. actual_pkthdr.caplen = pkthdr->caplen;
  79. actual_pkthdr.len = pkthdr->len;
  80. total = 0;
  81. header_again:
  82. poller.fd = PARENT_WRITE_FD;
  83. poller.events = POLLOUT;
  84. poller.revents = 0;
  85. /* wait until we can write the header to the tcpdump pipe */
  86. res = poll(&poller, 1, TCPDUMP_POLL_TIMEOUT);
  87. if (res < 0)
  88. errx(-1, "Error writing header to fd %d during poll() to write to tcpdump\n%s",
  89. PARENT_WRITE_FD, strerror(errno));
  90. if (res == 0)
  91. err(-1, "poll() timeout... tcpdump seems to be having a problem keeping up\n"
  92. "Try increasing TCPDUMP_POLL_TIMEOUT");
  93. #ifdef DEBUG
  94. if (debug >= 5) {
  95. if (write(tcpdump->debugfd, (char *)&actual_pkthdr, sizeof(actual_pkthdr))
  96. != sizeof(actual_pkthdr))
  97. errx(-1, "Error writing pcap file header to tcpdump debug\n%s", strerror(errno));
  98. }
  99. #endif
  100. /* res > 0 if we get here */
  101. while (total != sizeof(actual_pkthdr) &&
  102. (res = write(PARENT_WRITE_FD, &actual_pkthdr + total,
  103. sizeof(actual_pkthdr) - total))) {
  104. if (res < 0) {
  105. if (errno == EAGAIN)
  106. goto header_again;
  107. errx(-1, "Error writing pcap file header to tcpdump\n%s",
  108. strerror(errno));
  109. }
  110. total += res;
  111. }
  112. total = 0;
  113. data_again:
  114. /* wait until we can write data to the tcpdump pipe */
  115. poller.fd = PARENT_WRITE_FD;
  116. poller.events = POLLOUT;
  117. poller.revents = 0;
  118. res = poll(&poller, 1, TCPDUMP_POLL_TIMEOUT);
  119. if (res < 0)
  120. errx(-1, "Error writing to fd %d during poll() to write to tcpdump\n%s",
  121. PARENT_WRITE_FD, strerror(errno));
  122. if (res == 0)
  123. err(-1, "poll() timeout... tcpdump seems to be having a problem keeping up\n"
  124. "Try increasing TCPDUMP_POLL_TIMEOUT");
  125. #ifdef DEBUG
  126. if (debug >= 5) {
  127. if (write(tcpdump->debugfd, data, pkthdr->caplen) != (ssize_t)pkthdr->caplen)
  128. errx(-1, "Error writing packet data to tcpdump debug\n%s", strerror(errno));
  129. }
  130. #endif
  131. while (total != (ssize_t)pkthdr->caplen &&
  132. (res = write(PARENT_WRITE_FD, data + total, pkthdr->caplen - total))) {
  133. if (res < 0) {
  134. if (errno == EAGAIN)
  135. goto data_again;
  136. errx(-1, "Error writing packet data to tcpdump\n%s", strerror(errno));
  137. }
  138. total += res;
  139. }
  140. /* Wait for output from tcpdump */
  141. poller.fd = PARENT_READ_FD;
  142. poller.events = POLLIN;
  143. poller.revents = 0;
  144. res = poll(&poller, 1, TCPDUMP_POLL_TIMEOUT);
  145. if (res < 0)
  146. errx(-1, "Error out to fd %d during poll() to read from tcpdump\n%s",
  147. PARENT_READ_FD, strerror(errno));
  148. if (res == 0)
  149. err(-1, "poll() timeout... tcpdump seems to be having a problem keeping up\n"
  150. "Try increasing TCPDUMP_POLL_TIMEOUT");
  151. while ((res = read(PARENT_READ_FD, decode, TCPDUMP_DECODE_LEN))) {
  152. if (res < 0) {
  153. if (errno == EAGAIN)
  154. break;
  155. errx(-1, "Error reading tcpdump decode: %s", strerror(errno));
  156. }
  157. decode[min(res, TCPDUMP_DECODE_LEN-1)] = 0;
  158. dbgx(4, "read %d byte from tcpdump", res);
  159. printf("%s", decode);
  160. }
  161. return TRUE;
  162. }
  163. /**
  164. * init our tcpdump handle using the given pcap handle
  165. * Basically, this starts up tcpdump as a child and communicates
  166. * to it via a pair of sockets (stdout/stdin)
  167. */
  168. int
  169. tcpdump_open(tcpdump_t *tcpdump, pcap_t *pcap)
  170. {
  171. assert(tcpdump);
  172. assert(pcap);
  173. if (tcpdump->pid != 0) {
  174. warn("tcpdump process already running");
  175. return FALSE;
  176. }
  177. /* is tcpdump executable? */
  178. if (! can_exec(TCPDUMP_BINARY)) {
  179. errx(-1, "Unable to execute tcpdump binary: %s", TCPDUMP_BINARY);
  180. }
  181. #ifdef DEBUG
  182. strlcpy(tcpdump->debugfile, TCPDUMP_DEBUG, sizeof(tcpdump->debugfile));
  183. if (debug >= 5) {
  184. dbgx(5, "Opening tcpdump debug file: %s", tcpdump->debugfile);
  185. if ((tcpdump->debugfd = open(tcpdump->debugfile, O_WRONLY|O_CREAT|O_TRUNC,
  186. S_IREAD|S_IWRITE|S_IRGRP|S_IROTH)) == -1) {
  187. errx(-1, "Error opening tcpdump debug file: %s\n%s", tcpdump->debugfile, strerror(errno));
  188. }
  189. }
  190. #endif
  191. /* copy over the args */
  192. dbg(2, "Prepping tcpdump options...");
  193. tcpdump_fill_in_options(tcpdump->args);
  194. dbg(2, "Starting tcpdump...");
  195. /* create our pipe to send packet data to tcpdump via */
  196. if (pipe(tcpdump->pipes[PARENT_READ_PIPE]) < 0 ||
  197. pipe(tcpdump->pipes[PARENT_WRITE_PIPE]) < 0)
  198. errx(-1, "Unable to create pipe: %s", strerror(errno));
  199. if ((tcpdump->pid = fork() ) < 0)
  200. errx(-1, "Fork failed: %s", strerror(errno));
  201. dbgx(2, "tcpdump pid: %d", tcpdump->pid);
  202. if (tcpdump->pid > 0) {
  203. /* parent - we're still in tcpreplay */
  204. /* close fds not required by parent */
  205. dbgx(2, "[parent] closing child read/write fd %d/%d", CHILD_READ_FD,
  206. CHILD_WRITE_FD);
  207. close(CHILD_READ_FD);
  208. close(CHILD_WRITE_FD);
  209. CHILD_READ_FD = 0;
  210. CHILD_WRITE_FD = 0;
  211. /* send the pcap file header to tcpdump */
  212. FILE *writer = fdopen(PARENT_WRITE_FD, "w");
  213. if ((pcap_dump_fopen(pcap, writer)) == NULL) {
  214. warnx("[parent] pcap_dump_fopen(): %s", pcap_geterr(pcap));
  215. return FALSE;
  216. }
  217. pcap_dump_flush((pcap_dumper_t*)writer);
  218. if (fcntl(PARENT_WRITE_FD, F_SETFL, O_NONBLOCK) < 0)
  219. warnx("[parent] Unable to fcntl write pipe:\n%s", strerror(errno));
  220. if (fcntl(PARENT_READ_FD, F_SETFL, O_NONBLOCK) < 0)
  221. warnx("[parent] Unable to fnctl read pip:\n%s", strerror(errno));
  222. } else {
  223. dbg(2, "[child] started the kid");
  224. /* we're in the child process - run "tcpdump <options> -r -" */
  225. if (dup2(CHILD_READ_FD, STDIN_FILENO) != STDIN_FILENO) {
  226. errx(-1, "[child] Unable to duplicate socket to stdin: %s",
  227. strerror(errno));
  228. }
  229. if (dup2(CHILD_WRITE_FD, STDOUT_FILENO) != STDOUT_FILENO) {
  230. errx(-1, "[child] Unable to duplicate socket to stdout: %s",
  231. strerror(errno));
  232. }
  233. /*
  234. * Close sockets not required by child. The exec'ed program must
  235. * not know that they ever existed.
  236. */
  237. dbgx(2, "[child] closing in fds %d/%d/%d/%d", CHILD_READ_FD,
  238. CHILD_WRITE_FD, PARENT_READ_FD, PARENT_WRITE_FD);
  239. close(CHILD_READ_FD);
  240. close(CHILD_WRITE_FD);
  241. close(PARENT_READ_FD);
  242. close(PARENT_WRITE_FD);
  243. /* exec tcpdump */
  244. dbg(2, "[child] Exec'ing tcpdump...");
  245. if (execv(TCPDUMP_BINARY, options_vec) < 0)
  246. errx(-1, "Unable to exec tcpdump: %s", strerror(errno));
  247. dbg(2, "[child] tcpdump done!");
  248. }
  249. return TRUE;
  250. }
  251. /**
  252. * shutdown tcpdump
  253. */
  254. void
  255. tcpdump_close(tcpdump_t *tcpdump)
  256. {
  257. if (! tcpdump)
  258. return;
  259. if (tcpdump->pid <= 0)
  260. return;
  261. dbgx(2, "[parent] killing tcpdump pid: %d", tcpdump->pid);
  262. kill(tcpdump->pid, SIGKILL);
  263. close(PARENT_READ_FD);
  264. close(PARENT_WRITE_FD);
  265. if (waitpid(tcpdump->pid, NULL, 0) != tcpdump->pid)
  266. errx(-1, "[parent] Error in waitpid: %s", strerror(errno));
  267. tcpdump->pid = 0;
  268. PARENT_READ_FD = 0;
  269. PARENT_WRITE_FD = 0;
  270. }
  271. /**
  272. * forcefully kill tcpdump
  273. */
  274. void
  275. tcpdump_kill(tcpdump_t *tcpdump)
  276. {
  277. if (tcpdump->pid) {
  278. if (kill(tcpdump->pid, SIGTERM) != 0)
  279. kill(tcpdump->pid, SIGKILL);
  280. }
  281. tcpdump->pid = 0;
  282. }
  283. /**
  284. * copy the string of args (*opt) to the vector (**opt_vec)
  285. * for a max of opt_len. Returns the number of options
  286. * in the vector
  287. */
  288. static int
  289. tcpdump_fill_in_options(char *opt)
  290. {
  291. char options[256];
  292. char *arg, *newarg;
  293. int i = 1, arglen;
  294. char *token = NULL;
  295. /* zero out our options_vec for execv() */
  296. memset(options_vec, '\0', sizeof(options_vec));
  297. /* first arg should be the binary (by convention) */
  298. options_vec[0] = TCPDUMP_BINARY;
  299. /* prep args */
  300. memset(options, '\0', 256);
  301. if (opt != NULL) {
  302. strlcat(options, opt, sizeof(options));
  303. }
  304. strlcat(options, TCPDUMP_ARGS, sizeof(options));
  305. dbgx(2, "[child] Will execute: tcpdump %s", options);
  306. /* process args */
  307. /* process the first argument */
  308. arg = strtok_r(options, OPT_DELIM, &token);
  309. arglen = strlen(arg) + 2; /* -{arg}\0 */
  310. newarg = (char *)safe_malloc(arglen);
  311. strlcat(newarg, "-", arglen);
  312. strlcat(newarg, arg, arglen);
  313. options_vec[i++] = newarg;
  314. /* process the remaining args
  315. * note that i < OPTIONS_VEC_SIZE - 1
  316. * because: a) we need to add '-' as an option to the end
  317. * b) because the array has to be null terminated
  318. */
  319. while (((arg = strtok_r(NULL, OPT_DELIM, &token)) != NULL) &&
  320. (i < OPTIONS_VEC_SIZE - 1)) {
  321. arglen = strlen(arg) + 2;
  322. newarg = (char *)safe_malloc(arglen);
  323. strlcat(newarg, "-", arglen);
  324. strlcat(newarg, arg, arglen);
  325. options_vec[i++] = newarg;
  326. }
  327. /* tell -r to read from stdin */
  328. options_vec[i] = "-";
  329. return i;
  330. }
  331. /**
  332. * can we exec the given file?
  333. */
  334. static int
  335. can_exec(const char *filename)
  336. {
  337. struct stat st;
  338. if (!filename || filename[0] == '\0')
  339. return FALSE;
  340. /* Stat the file to see if it's executable and
  341. if the user may run it.
  342. */
  343. if (lstat(filename, &st) < 0)
  344. return FALSE;
  345. if ((st.st_mode & S_IXUSR) ||
  346. (st.st_mode & S_IXGRP) ||
  347. (st.st_mode & S_IXOTH))
  348. return TRUE;
  349. return FALSE;
  350. }