]> The Tcpdump Group git mirrors - tcpdump/blobdiff - tcpdump.c
document SIGUSR1 in manual page
[tcpdump] / tcpdump.c
index 11dc8bc8b854f356ccbe805ef0308357afbd637f..8e07d8f2e1572dc307aafe86e7c40c200f4d3e99 100644 (file)
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -43,6 +43,17 @@ The Regents of the University of California.  All rights reserved.\n";
 #include "config.h"
 #endif
 
+/*
+ * Mac OS X may ship pcap.h from libpcap 0.6 with a libpcap based on
+ * 0.8.  That means it has pcap_findalldevs() but the header doesn't
+ * define pcap_if_t, meaning that we can't actually *use* pcap_findalldevs().
+ */
+#ifdef HAVE_PCAP_FINDALLDEVS
+#ifndef HAVE_PCAP_IF_T
+#undef HAVE_PCAP_FINDALLDEVS
+#endif
+#endif
+
 #include <tcpdump-stdinc.h>
 
 #ifdef WIN32
@@ -53,7 +64,7 @@ extern int SIZE_BUF;
 #define uint UINT
 #endif /* WIN32 */
 
-#ifdef HAVE_SMI_H
+#ifdef USE_LIBSMI
 #include <smi.h>
 #endif
 
@@ -66,12 +77,10 @@ extern int SIZE_BUF;
 #else
 #include "getopt_long.h"
 #endif
-#include <pcap.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
+/* Capsicum-specific code requires macros from <net/bpf.h>, which will fail
+ * to compile if <pcap.h> has already been included; including the headers
+ * in the opposite order works fine.
+ */
 #ifdef HAVE_CAPSICUM
 #include <sys/capability.h>
 #include <sys/ioccom.h>
@@ -79,6 +88,12 @@ extern int SIZE_BUF;
 #include <fcntl.h>
 #include <libgen.h>
 #endif /* HAVE_CAPSICUM */
+#include <pcap.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
 #ifndef WIN32
 #include <sys/wait.h>
 #include <sys/resource.h>
@@ -86,10 +101,18 @@ extern int SIZE_BUF;
 #include <grp.h>
 #endif /* WIN32 */
 
-/* capabilities convinience library */
+/* capabilities convenience library */
+/* If a code depends on HAVE_LIBCAP_NG, it depends also on HAVE_CAP_NG_H.
+ * If HAVE_CAP_NG_H is not defined, undefine HAVE_LIBCAP_NG.
+ * Thus, the later tests are done only on HAVE_LIBCAP_NG.
+ */
+#ifdef HAVE_LIBCAP_NG
 #ifdef HAVE_CAP_NG_H
 #include <cap-ng.h>
+#else
+#undef HAVE_LIBCAP_NG
 #endif /* HAVE_CAP_NG_H */
+#endif /* HAVE_LIBCAP_NG */
 
 #include "netdissect.h"
 #include "interface.h"
@@ -614,12 +637,6 @@ show_devices_and_exit (void)
 #define J_FLAG
 #endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
 
-#ifdef HAVE_PCAP_FINDALLDEVS
-#ifndef HAVE_PCAP_IF_T
-#undef HAVE_PCAP_FINDALLDEVS
-#endif
-#endif
-
 #ifdef HAVE_PCAP_FINDALLDEVS
 #define D_FLAG "D"
 #else
@@ -660,6 +677,7 @@ show_devices_and_exit (void)
  */
 #define OPTION_VERSION         128
 #define OPTION_TSTAMP_PRECISION        129
+#define OPTION_IMMEDIATE_MODE  130
 
 static const struct option longopts[] = {
 #if defined(HAVE_PCAP_CREATE) || defined(WIN32)
@@ -691,6 +709,9 @@ static const struct option longopts[] = {
        { "packet-buffered", no_argument, NULL, 'U' },
 #endif
        { "linktype", required_argument, NULL, 'y' },
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+       { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE },
+#endif
 #if defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG)
        { "debug-filter-parser", no_argument, NULL, 'Y' },
 #endif
@@ -721,21 +742,14 @@ droproot(const char *username, const char *chroot_dir)
                                exit(1);
                        }
                }
-#ifdef HAVE_CAP_NG_H
+#ifdef HAVE_LIBCAP_NG
                int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG);
                if (ret < 0) {
                        fprintf(stderr, "error : ret %d\n", ret);
                }
                else {
-                       printf("dropped privs to %s\n", username);
+                       fprintf(stderr, "dropped privs to %s\n", username);
                }
-               /* We don't need CAP_SETUID and CAP_SETGID */
-               capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_SETUID);
-               capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_SETUID);
-               capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_SETUID);
-               capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_SETUID);
-               capng_apply(CAPNG_SELECT_BOTH);
-
 #else
                if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
                    setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
@@ -747,15 +761,26 @@ droproot(const char *username, const char *chroot_dir)
                        exit(1);
                }
                else {
-                       printf("dropped privs to %s\n", username);
+                       fprintf(stderr, "dropped privs to %s\n", username);
                }
-#endif /* HAVE_CAP_NG_H */
+#endif /* HAVE_LIBCAP_NG */
        }
        else {
                fprintf(stderr, "tcpdump: Couldn't find user '%.32s'\n",
                    username);
                exit(1);
        }
+#ifdef HAVE_LIBCAP_NG
+       /* We don't need CAP_SETUID and CAP_SETGID any more. */
+       capng_updatev(
+               CAPNG_DROP,
+               CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+               CAP_SETUID,
+               CAP_SETGID,
+               -1);
+       capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+
 }
 #endif /* WIN32 */
 
@@ -937,6 +962,7 @@ main(int argc, char **argv)
        gndo->ndo_error=ndo_error;
        gndo->ndo_warning=ndo_warning;
        gndo->ndo_snaplen = DEFAULT_SNAPLEN;
+       gndo->ndo_immediate = 0;
 
        cnt = -1;
        device = NULL;
@@ -951,10 +977,17 @@ main(int argc, char **argv)
        else
                program_name = argv[0];
 
+       /*
+        * On platforms where the CPU doesn't support unaligned loads,
+        * force unaligned accesses to abort with SIGBUS, rather than
+        * being fixed up (slowly) by the OS kernel; on those platforms,
+        * misaligned accesses are bugs, and we want tcpdump to crash so
+        * that the bugs are reported.
+        */
        if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
                error("%s", ebuf);
 
-#ifdef LIBSMI
+#ifdef USE_LIBSMI
        smiInit("tcpdump");
 #endif
 
@@ -1135,7 +1168,7 @@ main(int argc, char **argv)
                        break;
 
                case 'm':
-#ifdef LIBSMI
+#ifdef USE_LIBSMI
                        if (smiLoadModule(optarg) == 0) {
                                error("could not load MIB module %s", optarg);
                        }
@@ -1337,6 +1370,12 @@ main(int argc, char **argv)
                        break;
 #endif
 
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+               case OPTION_IMMEDIATE_MODE:
+                       gndo->ndo_immediate = 1;
+                       break;
+#endif
+
                default:
                        print_usage();
                        exit(1);
@@ -1372,6 +1411,17 @@ main(int argc, char **argv)
        if (VFileName != NULL && RFileName != NULL)
                error("-V and -r are mutually exclusive.");
 
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+       /*
+        * If we're printing dissected packets to the standard output
+        * rather than saving raw packets to a file, and the standard
+        * output is a terminal, use immediate mode, as the user's
+        * probably expecting to see packets pop up immediately.
+        */
+       if (WFileName == NULL && isatty(1))
+               gndo->ndo_immediate = 1;
+#endif
+
 #ifdef WITH_CHROOT
        /* if run as root, prepare for chrooting */
        if (getuid() == 0 || geteuid() == 0) {
@@ -1497,6 +1547,15 @@ main(int argc, char **argv)
                                pcap_statustostr(status));
 #endif
 
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+               if (gndo->ndo_immediate) {
+                       status = pcap_set_immediate_mode(pd, 1);
+                       if (status != 0)
+                               error("%s: Can't set immediate mode: %s",
+                               device,
+                               pcap_statustostr(status));
+               }
+#endif
                /*
                 * Is this an interface that supports monitor mode?
                 */
@@ -1533,7 +1592,7 @@ main(int argc, char **argv)
                        status = pcap_set_tstamp_type(pd, jflag);
                        if (status < 0)
                                error("%s: Can't set time stamp type: %s",
-                                   device, pcap_statustostr(status));
+                                     device, pcap_statustostr(status));
                }
 #endif
                status = pcap_activate(pd);
@@ -1679,27 +1738,28 @@ main(int argc, char **argv)
         * savefile doesn't handle the general case.
         */
 
-#ifdef HAVE_CAP_NG_H
-       /* We are running as root and we will be writing to savefile */
-       if ((getuid() == 0 || geteuid() == 0) && WFileName) {
+       if (getuid() == 0 || geteuid() == 0) {
+#ifdef HAVE_LIBCAP_NG
+               /* Initialize capng */
+               capng_clear(CAPNG_SELECT_BOTH);
                if (username) {
-                       /* Drop all capabilities from effective set */
-                       capng_clear(CAPNG_EFFECTIVE);
-                       /* Add capabilities we will need*/
-                       capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_SETUID);
-                       capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_SETGID);
-                       capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_OVERRIDE);
-
-                       capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_SETUID);
-                       capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_SETGID);
-                       capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
-
-                       capng_apply(CAPNG_SELECT_BOTH);
+                       capng_updatev(
+                               CAPNG_ADD,
+                               CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+                               CAP_SETUID,
+                               CAP_SETGID,
+                               -1);
                }
-       }
-#endif /* HAVE_CAP_NG_H */
 
-       if (getuid() == 0 || geteuid() == 0) {
+               if (WFileName) {
+                       capng_update(
+                               CAPNG_ADD,
+                               CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+                               CAP_DAC_OVERRIDE
+                               );
+               }
+               capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
                if (username || chroot_dir)
                        droproot(username, chroot_dir);
 
@@ -1738,10 +1798,19 @@ main(int argc, char **argv)
                  MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
 
                p = pcap_dump_open(pd, dumpinfo.CurrentFileName);
-#ifdef HAVE_CAP_NG_H
-        /* Give up capabilities, clear Effective set */
-        capng_clear(CAPNG_EFFECTIVE);
-#endif
+#ifdef HAVE_LIBCAP_NG
+               /* Give up CAP_DAC_OVERRIDE capability.
+                * Only allow it to be restored if the -C or -G flag have been
+                * set since we may need to create more files later on.
+                */
+               capng_update(
+                       CAPNG_DROP,
+                       (Cflag || Gflag ? 0 : CAPNG_PERMITTED)
+                               | CAPNG_EFFECTIVE,
+                       CAP_DAC_OVERRIDE
+                       );
+               capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
                if (p == NULL)
                        error("%s", pcap_geterr(pd));
 #ifdef HAVE_CAPSICUM
@@ -2155,10 +2224,10 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
                        else
                                MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0);
 
-#ifdef HAVE_CAP_NG_H
+#ifdef HAVE_LIBCAP_NG
                        capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
-                       capng_apply(CAPNG_EFFECTIVE);
-#endif /* HAVE_CAP_NG_H */
+                       capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
 #ifdef HAVE_CAPSICUM
                        fd = openat(dump_info->dirfd,
                            dump_info->CurrentFileName,
@@ -2176,10 +2245,10 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
 #else  /* !HAVE_CAPSICUM */
                        dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
 #endif
-#ifdef HAVE_CAP_NG_H
+#ifdef HAVE_LIBCAP_NG
                        capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
-                       capng_apply(CAPNG_EFFECTIVE);
-#endif /* HAVE_CAP_NG_H */
+                       capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
                        if (dump_info->p == NULL)
                                error("%s", pcap_geterr(pd));
 #ifdef HAVE_CAPSICUM
@@ -2197,59 +2266,74 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
         * larger than Cflag - the last packet written to the
         * file could put it over Cflag.
         */
-       if (Cflag != 0 && pcap_dump_ftell(dump_info->p) > Cflag) {
+       if (Cflag != 0) {
+               long size = pcap_dump_ftell(dump_info->p);
+
+               if (size == -1)
+                       error("ftell fails on output file");
+               if (size > Cflag) {
 #ifdef HAVE_CAPSICUM
-               FILE *fp;
-               int fd;
+                       FILE *fp;
+                       int fd;
 #endif
 
-               /*
-                * Close the current file and open a new one.
-                */
-               pcap_dump_close(dump_info->p);
+                       /*
+                        * Close the current file and open a new one.
+                        */
+                       pcap_dump_close(dump_info->p);
 
-               /*
-                * Compress the file we just closed, if the user asked for it
-                */
-               if (zflag != NULL)
-                       compress_savefile(dump_info->CurrentFileName);
+                       /*
+                        * Compress the file we just closed, if the user
+                        * asked for it.
+                        */
+                       if (zflag != NULL)
+                               compress_savefile(dump_info->CurrentFileName);
 
-               Cflag_count++;
-               if (Wflag > 0) {
-                       if (Cflag_count >= Wflag)
-                               Cflag_count = 0;
-               }
-               if (dump_info->CurrentFileName != NULL)
-                       free(dump_info->CurrentFileName);
-               dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
-               if (dump_info->CurrentFileName == NULL)
-                       error("dump_packet_and_trunc: malloc");
-               MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
+                       Cflag_count++;
+                       if (Wflag > 0) {
+                               if (Cflag_count >= Wflag)
+                                       Cflag_count = 0;
+                       }
+                       if (dump_info->CurrentFileName != NULL)
+                               free(dump_info->CurrentFileName);
+                       dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
+                       if (dump_info->CurrentFileName == NULL)
+                               error("dump_packet_and_trunc: malloc");
+                       MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
+#ifdef HAVE_LIBCAP_NG
+                       capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+                       capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
 #ifdef HAVE_CAPSICUM
-               fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
-                   O_CREAT | O_WRONLY | O_TRUNC, 0644);
-               if (fd < 0) {
-                       error("unable to open file %s",
-                           dump_info->CurrentFileName);
-               }
-               fp = fdopen(fd, "w");
-               if (fp == NULL) {
-                       error("unable to fdopen file %s",
-                           dump_info->CurrentFileName);
-               }
-               dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
+                       fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
+                           O_CREAT | O_WRONLY | O_TRUNC, 0644);
+                       if (fd < 0) {
+                               error("unable to open file %s",
+                                   dump_info->CurrentFileName);
+                       }
+                       fp = fdopen(fd, "w");
+                       if (fp == NULL) {
+                               error("unable to fdopen file %s",
+                                   dump_info->CurrentFileName);
+                       }
+                       dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
 #else  /* !HAVE_CAPSICUM */
-               dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+                       dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
 #endif
-               if (dump_info->p == NULL)
-                       error("%s", pcap_geterr(pd));
+#ifdef HAVE_LIBCAP_NG
+                       capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+                       capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+                       if (dump_info->p == NULL)
+                               error("%s", pcap_geterr(pd));
 #ifdef HAVE_CAPSICUM
-               cap_rights_init(&rights, CAP_SEEK, CAP_WRITE);
-               if (cap_rights_limit(fileno(pcap_dump_file(dump_info->p)),
-                   &rights) < 0 && errno != ENOSYS) {
-                       error("unable to limit dump descriptor");
-               }
+                       cap_rights_init(&rights, CAP_SEEK, CAP_WRITE);
+                       if (cap_rights_limit(fileno(pcap_dump_file(dump_info->p)),
+                           &rights) < 0 && errno != ENOSYS) {
+                               error("unable to limit dump descriptor");
+                       }
 #endif
+               }
        }
 
        pcap_dump((u_char *)dump_info->p, h, sp);
@@ -2303,7 +2387,8 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
        /*
         * Some printers want to check that they're not walking off the
         * end of the packet.
-        * Rather than pass it all the way down, we set this global.
+        * Rather than pass it all the way down, we set this member
+        * of the netdissect_options structure.
         */
        ndo->ndo_snapend = sp + h->caplen;
 
@@ -2313,6 +2398,11 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
                 hdrlen = (*print_info->p.printer)(h, sp);
         }
 
+       /*
+        * Restore the original snapend, as a printer might have
+        * changed it.
+        */
+       ndo->ndo_snapend = sp + h->caplen;
        if (ndo->ndo_Xflag) {
                /*
                 * Print the raw packet data in hex and ASCII.
@@ -2411,7 +2501,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
 static void
 ndo_default_print(netdissect_options *ndo, const u_char *bp, u_int length)
 {
-       hex_and_ascii_print(ndo, "\n\t", bp, length); /* pass on lf and identation string */
+       hex_and_ascii_print(ndo, "\n\t", bp, length); /* pass on lf and indentation string */
 }
 
 void
@@ -2487,7 +2577,7 @@ print_version(void)
        (void)fprintf (stderr, "%s\n", SSLeay_version(SSLEAY_VERSION));
 #endif
 
-#if defined(HAVE_SMI_H)
+#ifdef USE_LIBSMI
        (void)fprintf (stderr, "SMI-library: %s\n", smi_version_string);
 #endif
 }
@@ -2513,6 +2603,9 @@ print_usage(void)
        (void)fprintf(stderr, "[ --time-stamp-precision precision ]\n");
        (void)fprintf(stderr,
 "\t\t");
+#endif
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+       (void)fprintf(stderr, "[ --immediate-mode ] ");
 #endif
        (void)fprintf(stderr, "[ -T type ] [ --version ] [ -V file ]\n");
        (void)fprintf(stderr,