root/trunk/src/common/tcpdump.c

Revision 2074, 11.3 KB (checked in by aturner, 2 months ago)

err and errx take -1 as first argument. fixes #331

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id HeadURL Author Rev Date
Line 
1/* $Id$ */
2
3/*
4 * Copyright (c) 2001-2004 Aaron Turner.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright owners nor the names of its
17 *    contributors may be used to endorse or promote products derived from
18 *    this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
28 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * This code allows us to use tcpdump to print packet decodes.
35 * Basically, we create a local AF_UNIX socketpair, fork a copy
36 * of ourselves, link 1/2 of the pair to STDIN of the child and
37 * replace the child with tcpdump.  We then send a "pcap" file
38 * over the socket so that tcpdump can print it's decode to STDOUT.
39 *
40 * Idea and a lot of code stolen from Christain Kreibich's
41 *  <christian@whoop.org> libnetdude 0.4 code.  Any bugs are mine. :)
42 *
43 * This product includes software developed by the University of California,
44 * Lawrence Berkeley Laboratory and its contributors
45 */
46
47#include "config.h"
48#include "defines.h"
49#include "common.h"
50
51#include <sys/types.h>
52#include <unistd.h>
53#include <sys/socket.h>
54#include <sys/wait.h>
55#include <errno.h>
56#include <string.h>
57#ifdef HAVE_SIGNAL_H
58#include <signal.h>
59#endif
60
61#include "tcpdump.h"
62#include "lib/strlcpy.h"
63
64#ifdef DEBUG
65extern int debug;
66#endif
67
68char *options_vec[OPTIONS_VEC_SIZE];
69static int tcpdump_fill_in_options(char *opt);
70static int can_exec(const char *filename);
71
72/**
73 * given a packet, print a decode of via tcpdump
74 */
75int
76tcpdump_print(tcpdump_t *tcpdump, struct pcap_pkthdr *pkthdr, const u_char *data)
77{
78    struct pollfd poller[1];
79    int result;
80    char decode[TCPDUMP_DECODE_LEN];
81
82    assert(tcpdump);
83    assert(pkthdr);
84    assert(data);
85
86    poller[0].fd = tcpdump->infd;
87    poller[0].events = POLLOUT;
88    poller[0].revents = 0;
89   
90    /* wait until we can write to the tcpdump socket */
91    result = poll(poller, 1, TCPDUMP_POLL_TIMEOUT);
92    if (result < 0)
93        errx(-1, "Error during poll() to write to tcpdump\n%s", strerror(errno));
94
95    if (result == 0)
96        err(-1, "poll() timeout... tcpdump seems to be having a problem keeping up\n"
97            "Try increasing TCPDUMP_POLL_TIMEOUT");
98
99
100    /* result > 0 if we get here */
101
102    if (write(tcpdump->infd, (char *)pkthdr, sizeof(struct pcap_pkthdr))
103        != sizeof(struct pcap_pkthdr))
104        errx(-1, "Error writing pcap file header to tcpdump\n%s", strerror(errno));
105
106#ifdef DEBUG
107    if (debug >= 5) {
108        if (write(tcpdump->debugfd, (char *)pkthdr, sizeof(struct pcap_pkthdr))
109            != sizeof(struct pcap_pkthdr))
110            errx(-1, "Error writing pcap file header to tcpdump debug\n%s", strerror(errno));
111    }
112#endif
113
114    if (write(tcpdump->infd, data, pkthdr->caplen) != (ssize_t)pkthdr->caplen)
115        errx(-1, "Error writing packet data to tcpdump\n%s", strerror(errno));
116
117#ifdef DEBUG
118    if (debug >= 5) {
119        if (write(tcpdump->debugfd, data, pkthdr->caplen) != (ssize_t)pkthdr->caplen)
120            errx(-1, "Error writing packet data to tcpdump debug\n%s", strerror(errno));
121    }
122#endif
123
124    /* Wait for output from tcpdump */
125    poller[0].fd = tcpdump->outfd;
126    poller[0].events = POLLIN;
127    poller[0].revents = 0;
128
129    result = poll(poller, 1, TCPDUMP_POLL_TIMEOUT);
130    if (result < 0)
131        errx(-1, "Error during poll() to write to tcpdump\n%s", strerror(errno));
132
133    if (result == 0)
134        err(-1, "poll() timeout... tcpdump seems to be having a problem keeping up\n"
135            "Try increasing TCPDUMP_POLL_TIMEOUT");
136
137    /* result > 0 if we get here */
138    if (read(tcpdump->outfd, &decode, TCPDUMP_DECODE_LEN) < 0)
139        errx(-1, "Error reading tcpdump decode: %s", strerror(errno));
140           
141    printf("%s", decode);
142
143    return TRUE;
144}
145
146/**
147 * init our tcpdump handle using the given pcap handle
148 * Basically, this starts up tcpdump as a child and communicates
149 * to it via a pair of sockets (stdout/stdin)
150 */
151int
152tcpdump_open(tcpdump_t *tcpdump, pcap_t *pcap)
153{
154    int infd[2], outfd[2];
155    FILE *writer;
156   
157    assert(tcpdump);
158    assert(pcap);
159
160    if (tcpdump->pid != 0) {
161        warn("tcpdump process already running");
162        return FALSE;
163    }
164
165    /* is tcpdump executable? */
166    if (! can_exec(TCPDUMP_BINARY)) {
167        errx(-1, "Unable to execute tcpdump binary: %s", TCPDUMP_BINARY);
168    }
169   
170#ifdef DEBUG
171     strlcpy(tcpdump->debugfile, TCPDUMP_DEBUG, sizeof(tcpdump->debugfile));
172     if (debug >= 5) {
173         dbgx(5, "Opening tcpdump debug file: %s", tcpdump->debugfile);
174
175         if ((tcpdump->debugfd = open(tcpdump->debugfile, O_WRONLY|O_CREAT|O_TRUNC, 
176                S_IREAD|S_IWRITE|S_IRGRP|S_IROTH)) == -1) {
177            errx(-1, "Error opening tcpdump debug file: %s\n%s", tcpdump->debugfile, strerror(errno));
178        }
179    }
180#endif
181
182    /* copy over the args */
183    dbg(2, "Prepping tcpdump options...");
184    tcpdump_fill_in_options(tcpdump->args);
185
186    dbg(2, "Starting tcpdump...");
187
188    /* create our socket pair to send packet data to tcpdump via */
189    if (socketpair(AF_UNIX, SOCK_STREAM, 0, infd) < 0)
190        errx(-1, "Unable to create stdin socket pair: %s", strerror(errno));
191
192    /* create our socket pair to read packet decode from tcpdump */
193    if (socketpair(AF_UNIX, SOCK_STREAM, 0, outfd) < 0)
194        errx(-1, "Unable to create stdout socket pair: %s", strerror(errno));
195 
196         
197    if ((tcpdump->pid = fork() ) < 0)
198        errx(-1, "Fork failed: %s", strerror(errno));
199
200    dbgx(2, "tcpdump pid: %d", tcpdump->pid);
201   
202    if (tcpdump->pid > 0) {
203        /* we're still in tcpreplay */
204        dbgx(2, "[parent] closing input fd %d", infd[1]);
205        close(infd[1]);  /* close the tcpdump side */
206        dbgx(2, "[parent] closing output fd %d", outfd[1]);
207        close(outfd[1]);
208        tcpdump->infd = infd[0];
209        tcpdump->outfd = outfd[0];
210
211        /* send the pcap file header to tcpdump */
212        writer = fdopen(tcpdump->infd, "w");
213        if ((tcpdump->dumper = pcap_dump_fopen(pcap, writer)) == NULL) {
214            warnx("[parent] pcap_dump_fopen(): %s", pcap_geterr(pcap));
215            return FALSE;
216        }
217        pcap_dump_flush(tcpdump->dumper);
218
219        if (fcntl(tcpdump->infd, F_SETFL, O_NONBLOCK) < 0)
220            warnx("[parent] Unable to fcntl tcpreplay socket:\n%s", strerror(errno));
221
222        if (fcntl(tcpdump->outfd, F_SETFL, O_NONBLOCK) < 0)
223            warnx("[parent] Unable to fnctl stdout socket:\n%s", strerror(errno));
224           
225    }
226    else {
227        dbg(2, "[child] started the kid");
228
229        /* we're in the child process */
230        dbgx(2, "[child] closing in fd %d", infd[0]);
231        dbgx(2, "[child] closing out fd %d", outfd[0]);
232        close(infd[0]); /* close the tcpreplay side */
233        close(outfd[0]);
234   
235        /* copy our side of the socketpair to our stdin */
236        if (infd[1] != STDIN_FILENO) {
237            if (dup2(infd[1], STDIN_FILENO) != STDIN_FILENO)
238                errx(-1, "[child] Unable to copy socket to stdin: %s", 
239                    strerror(errno));
240        }
241   
242        /* copy our side of the socketpair to our stdout */
243        if (outfd[1] != STDOUT_FILENO) {
244            if (dup2(outfd[1], STDOUT_FILENO) != STDOUT_FILENO)
245                errx(-1, "[child] Unable to copy socket to stdout: %s", 
246                    strerror(errno));
247        }
248
249        /* exec tcpdump */
250        dbg(2, "[child] Exec'ing tcpdump...");
251        if (execv(TCPDUMP_BINARY, options_vec) < 0)
252            errx(-1, "Unable to exec tcpdump: %s", strerror(errno));
253
254    }
255   
256    return TRUE;
257}
258
259/**
260 * shutdown tcpdump
261 */
262void
263tcpdump_close(tcpdump_t *tcpdump)
264{
265    if (! tcpdump)
266        return;
267
268    if (tcpdump->pid <= 0)
269        return;
270
271    dbgx(2, "[parent] killing tcpdump pid: %d", tcpdump->pid);
272
273    kill(tcpdump->pid, SIGKILL);
274    close(tcpdump->infd);
275    close(tcpdump->outfd);
276
277    if (waitpid(tcpdump->pid, NULL, 0) != tcpdump->pid)
278        errx(-1, "[parent] Error in waitpid: %s", strerror(errno));
279
280    tcpdump->pid = 0;
281    tcpdump->infd = 0;
282    tcpdump->outfd = 0;
283}
284
285/**
286 * forcefully kill tcpdump
287 */
288void
289tcpdump_kill(tcpdump_t *tcpdump)
290{
291    if (tcpdump->pid) {
292        if (kill(tcpdump->pid, SIGTERM) != 0) {
293            kill(tcpdump->pid, SIGKILL);
294        }
295    }
296    tcpdump->infd = 0;
297    tcpdump->outfd = 0;
298    tcpdump->pid = 0;
299}
300
301
302/**
303 * copy the string of args (*opt) to the vector (**opt_vec)
304 * for a max of opt_len.  Returns the number of options
305 * in the vector
306 */
307static int
308tcpdump_fill_in_options(char *opt)
309{
310    char options[256];
311    char *arg, *newarg;
312    int i = 1, arglen;
313    char *token = NULL;
314
315    /* zero out our options_vec for execv() */
316    memset(options_vec, '\0', OPTIONS_VEC_SIZE);
317   
318    /* first arg should be the binary (by convention) */
319    options_vec[0] = TCPDUMP_BINARY;
320       
321
322    /* prep args */
323    memset(options, '\0', 256);
324    if (opt != NULL) {
325        strlcat(options, opt, sizeof(options));
326    }
327    strlcat(options, TCPDUMP_ARGS, sizeof(options));
328    dbgx(2, "[child] Will execute: tcpdump %s", options);
329
330
331    /* process args */
332   
333    /* process the first argument */
334    arg = strtok_r(options, OPT_DELIM, &token);
335    arglen = strlen(arg) + 2; /* -{arg}\0 */
336    newarg = (char *)safe_malloc(arglen);
337    strlcat(newarg, "-", arglen); 
338    strlcat(newarg, arg, arglen);
339    options_vec[i++] = newarg;
340
341    /* process the remaining args
342       note that i < OPTIONS_VEC_SIZE - 1
343       because: a) we need to add '-' as an option to the end
344       b) because the array has to be null terminated
345    */
346    while (((arg = strtok_r(NULL, OPT_DELIM, &token)) != NULL) &&
347           (i < OPTIONS_VEC_SIZE - 1)) {
348
349        arglen = strlen(arg) + 2;
350        newarg = (char *)safe_malloc(arglen);
351        strlcat(newarg, "-", arglen);
352        strlcat(newarg, arg, arglen);
353        options_vec[i++] = newarg;
354
355    }
356
357    /* tell -r to read from stdin */
358    options_vec[i] = "-";
359
360    return(i);
361}
362
363
364/**
365 * can we exec the given file?
366 */
367static int
368can_exec(const char *filename)
369{
370    struct stat st;
371
372    if (!filename || filename[0] == '\0')
373        return FALSE;
374
375    /* Stat the file to see if it's executable and
376       if the user may run it.
377    */
378    if (lstat(filename, &st) < 0)
379        return FALSE;
380
381    if ((st.st_mode & S_IXUSR) ||
382        (st.st_mode & S_IXGRP) ||
383        (st.st_mode & S_IXOTH))
384        return TRUE;
385
386    return FALSE;
387}
Note: See TracBrowser for help on using the browser.