tcpdump.c 12 KB

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