1
0

tcpdump.c 12 KB

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