This moves the BIOCGSTATS ioctl operation done by the tcpdump process (at ^C time, but not at -c count expiration) into a service provided by the privsep monitor.
Had a bit of help from canacar, who wrote the original privsep code with otto. Will be needed for "tame". Index: privsep.c =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/privsep.c,v retrieving revision 1.33 diff -u -p -u -r1.33 privsep.c --- privsep.c 15 Mar 2015 00:41:28 -0000 1.33 +++ privsep.c 12 Jul 2015 05:42:29 -0000 @@ -20,6 +20,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> +#include <sys/ioctl.h> #include <netinet/in.h> #include <net/if.h> @@ -59,7 +60,8 @@ enum priv_state { STATE_INIT, /* initial state */ STATE_BPF, /* input file/device opened */ STATE_FILTER, /* filter applied */ - STATE_RUN /* running and accepting network traffic */ + STATE_RUN, /* running and accepting network traffic */ + STATE_EXIT /* in the process of dying */ }; #define ALLOW(action) (1 << (action)) @@ -76,7 +78,8 @@ static const int allowed_max[] = { ALLOW(PRIV_ETHER_NTOHOST) | ALLOW(PRIV_INIT_DONE), /* RUN */ ALLOW(PRIV_GETHOSTBYADDR) | ALLOW(PRIV_ETHER_NTOHOST) | ALLOW(PRIV_GETRPCBYNUMBER) | ALLOW(PRIV_GETLINES) | - ALLOW(PRIV_LOCALTIME) + ALLOW(PRIV_LOCALTIME) | ALLOW(PRIV_PCAP_STATS), + /* EXIT */ 0 }; /* @@ -87,7 +90,9 @@ static int allowed_ext[] = { /* INIT */ ALLOW(PRIV_SETFILTER), /* BPF */ ALLOW(PRIV_SETFILTER), /* FILTER */ ALLOW(PRIV_GETSERVENTRIES), - /* RUN */ ALLOW(PRIV_GETLINES) | ALLOW(PRIV_LOCALTIME) + /* RUN */ ALLOW(PRIV_GETLINES) | ALLOW(PRIV_LOCALTIME) | + ALLOW(PRIV_PCAP_STATS), + /* EXIT */ 0 }; struct ftab { @@ -120,6 +125,7 @@ static void impl_getserventries(int); static void impl_getprotoentries(int); static void impl_localtime(int fd); static void impl_getlines(int); +static void impl_pcap_stats(int, int *); static void test_state(int, int); static void logmsg(int, const char *, ...); @@ -186,6 +192,7 @@ priv_init(int argc, char **argv) } sigprocmask(SIG_SETMASK, &oset, NULL); + signal(SIGINT, SIG_IGN); /* Child - drop suid privileges */ gid = getgid(); @@ -303,6 +310,10 @@ priv_init(int argc, char **argv) test_state(cmd, STATE_RUN); impl_getlines(socks[0]); break; + case PRIV_PCAP_STATS: + test_state(cmd, STATE_RUN); + impl_pcap_stats(socks[0], &bpfd); + break; default: logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); _exit(1); @@ -390,8 +401,6 @@ impl_setfilter(int fd, char *cmdbuf, int if (setfilter(*bpfd, fd, cmdbuf)) logmsg(LOG_DEBUG, "[priv]: setfilter() failed"); - close(*bpfd); /* done with bpf descriptor */ - *bpfd = -1; } static void @@ -401,8 +410,6 @@ impl_init_done(int fd, int *bpfd) logmsg(LOG_DEBUG, "[priv]: msg PRIV_INIT_DONE received"); - close(*bpfd); /* done with bpf descriptor */ - *bpfd = -1; ret = 0; must_write(fd, &ret, sizeof(ret)); } @@ -581,6 +588,19 @@ impl_getlines(int fd) fclose(fp); } +static void +impl_pcap_stats(int fd, int *bpfd) +{ + struct pcap_stat stats; + + logmsg(LOG_DEBUG, "[priv]: msg PRIV_PCAP_STATS received"); + + if (ioctl(*bpfd, BIOCGSTATS, &stats) == -1) + write_zero(fd); + else + must_write(fd, &stats, sizeof(stats)); +} + void priv_init_done(void) { @@ -738,6 +758,17 @@ priv_getlines(size_t sz) write_command(priv_fd, PRIV_GETLINES); must_write(priv_fd, &sz, sizeof(size_t)); +} + +int +priv_pcap_stats(struct pcap_stat *ps) +{ + if (priv_fd < 0) + errx(1, "%s: called from privileged portion", __func__); + + write_command(priv_fd, PRIV_PCAP_STATS); + must_read(priv_fd, ps, sizeof(*ps)); + return (0); } /* retrieve a line from a file, should be called repeatedly after calling Index: privsep.h =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/privsep.h,v retrieving revision 1.7 diff -u -p -u -r1.7 privsep.h --- privsep.h 25 Aug 2009 06:59:17 -0000 1.7 +++ privsep.h 21 May 2015 01:27:53 -0000 @@ -37,7 +37,8 @@ enum cmd_types { PRIV_GETPROTOENTRIES, /* get the ip protocol entries table */ PRIV_LOCALTIME, /* return localtime */ PRIV_GETLINES, /* get lines from a file */ - PRIV_INIT_DONE /* signal that the initialization is done */ + PRIV_INIT_DONE, /* signal that the initialization is done */ + PRIV_PCAP_STATS /* get pcap_stats() results */ }; struct ether_addr; @@ -80,6 +81,9 @@ void priv_getlines(size_t); /* Retrieve a single line from a file, should be called repeatedly after calling priv_getlines() until it returns zero */ size_t priv_getline(char *, size_t); + +/* Return the pcap statistics upon completion */ +int priv_pcap_stats(struct pcap_stat *); pcap_dumper_t *priv_pcap_dump_open(pcap_t *, char *); Index: tcpdump.c =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/tcpdump.c,v retrieving revision 1.70 diff -u -p -u -r1.70 tcpdump.c --- tcpdump.c 18 Apr 2015 18:28:38 -0000 1.70 +++ tcpdump.c 12 Jul 2015 05:41:57 -0000 @@ -515,7 +515,7 @@ cleanup(int signo) /* Can't print the summary if reading from a savefile */ (void)write(STDERR_FILENO, "\n", 1); if (pd != NULL && pcap_file(pd) == NULL) { - if (pcap_stats(pd, &stat) < 0) { + if (priv_pcap_stats(&stat) < 0) { (void)snprintf(buf, sizeof buf, "pcap_stats: %s\n", pcap_geterr(pd)); write(STDERR_FILENO, buf, strlen(buf));