#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pcap-int.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "privsep.h"
int
setfilter(int bpfd, int sock, char *filter)
{
struct bpf_program fcode;
int oflag, snap, link;
u_int32_t netmask;
pcap_t hpcap;
must_read(sock, &oflag, sizeof(oflag));
must_read(sock, &netmask, sizeof(netmask));
must_read(sock, &snap, sizeof(snap));
must_read(sock, &link, sizeof(link));
if (snap < 0) {
snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE, "invalid snaplen");
goto err;
}
hpcap.snapshot = snap;
hpcap.linktype = link;
if (pcap_compile(&hpcap, &fcode, filter, oflag, netmask))
goto err;
if (bpfd >= 0 && ioctl(bpfd, BIOCSETF, &fcode) == -1) {
snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE,
"ioctl: BIOCSETF: %s", strerror(errno));
pcap_freecode(&fcode);
goto err;
}
if (fcode.bf_len > 0) {
must_write(sock, &fcode.bf_len, sizeof(fcode.bf_len));
must_write(sock, fcode.bf_insns,
fcode.bf_len * sizeof(struct bpf_insn));
} else {
snprintf(hpcap.errbuf, PCAP_ERRBUF_SIZE, "Invalid filter size");
pcap_freecode(&fcode);
goto err;
}
pcap_freecode(&fcode);
return (0);
err:
fcode.bf_len = 0;
must_write(sock, &fcode.bf_len, sizeof(fcode.bf_len));
write_string(sock, hpcap.errbuf);
return (1);
}
struct bpf_program *
priv_pcap_setfilter(pcap_t *hpcap, int oflag, u_int32_t netmask)
{
struct bpf_program *fcode = NULL;
int snap, link;
char *ebuf;
if (priv_fd < 0)
errx(1, "%s: called from privileged portion", __func__);
ebuf = pcap_geterr(hpcap);
snap = pcap_snapshot(hpcap);
link = pcap_datalink(hpcap);
fcode = calloc(1, sizeof(*fcode));
if (fcode == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "out of memory");
return (NULL);
}
write_command(priv_fd, PRIV_SETFILTER);
must_write(priv_fd, &oflag, sizeof(oflag));
must_write(priv_fd, &netmask, sizeof(netmask));
must_write(priv_fd, &snap, sizeof(snap));
must_write(priv_fd, &link, sizeof(link));
must_read(priv_fd, &fcode->bf_len, sizeof(fcode->bf_len));
if (fcode->bf_len <= 0) {
int len;
len = read_string(priv_fd, ebuf, PCAP_ERRBUF_SIZE, __func__);
if (len == 0)
snprintf(ebuf, PCAP_ERRBUF_SIZE, "pcap compile error");
goto err;
}
fcode->bf_insns = calloc(fcode->bf_len, sizeof(struct bpf_insn));
if (fcode->bf_insns == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "out of memory");
goto err;
}
must_read(priv_fd, fcode->bf_insns,
fcode->bf_len * sizeof(struct bpf_insn));
pcap_setfilter(hpcap, fcode);
return (fcode);
err:
free(fcode);
return (NULL);
}
int
pcap_live(const char *device, int snaplen, int promisc, u_int dlt,
u_int dirfilt, u_int fildrop)
{
int fd;
struct ifreq ifr;
unsigned v;
if (device == NULL || snaplen <= 0)
return (-1);
if ((fd = open("/dev/bpf", O_RDONLY)) == -1)
return (-1);
v = 32768;
ioctl(fd, BIOCSBLEN, &v);
strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
if (ioctl(fd, BIOCSETIF, &ifr) == -1)
goto error;
if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt) == -1)
goto error;
if (promisc)
ioctl(fd, BIOCPROMISC, NULL);
if (ioctl(fd, BIOCSDIRFILT, &dirfilt) == -1)
goto error;
if (ioctl(fd, BIOCSFILDROP, &fildrop) == -1)
goto error;
return (fd);
error:
close(fd);
return (-1);
}
pcap_t *
priv_pcap_live(const char *dev, int slen, int prom, int to_ms,
char *ebuf, u_int dlt, u_int dirfilt, u_int fildrop)
{
int fd, err;
struct bpf_version bv;
u_int v;
pcap_t *p;
if (priv_fd < 0)
errx(1, "%s: called from privileged portion", __func__);
if (dev == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "No interface specified");
return (NULL);
}
p = malloc(sizeof(*p));
if (p == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
return (NULL);
}
bzero(p, sizeof(*p));
write_command(priv_fd, PRIV_OPEN_BPF);
must_write(priv_fd, &slen, sizeof(int));
must_write(priv_fd, &prom, sizeof(int));
must_write(priv_fd, &dlt, sizeof(u_int));
must_write(priv_fd, &dirfilt, sizeof(u_int));
must_write(priv_fd, &fildrop, sizeof(fildrop));
write_string(priv_fd, dev);
fd = receive_fd(priv_fd);
must_read(priv_fd, &err, sizeof(int));
if (fd < 0) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"Failed to open bpf device for %s: %s",
dev, strerror(err));
goto bad;
}
if (ioctl(fd, BIOCVERSION, &bv) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
pcap_strerror(errno));
goto bad;
}
if (bv.bv_major != BPF_MAJOR_VERSION ||
bv.bv_minor < BPF_MINOR_VERSION) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"kernel bpf filter out of date");
goto bad;
}
p->fd = fd;
p->snapshot = slen;
if (ioctl(fd, BIOCGDLT, &v) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
pcap_strerror(errno));
goto bad;
}
p->linktype = v;
if (p->linktype == DLT_PFLOG && p->snapshot < 160)
p->snapshot = 160;
if (to_ms != 0) {
struct timeval to;
to.tv_sec = to_ms / 1000;
to.tv_usec = (to_ms * 1000) % 1000000;
if (ioctl(p->fd, BIOCSRTIMEOUT, &to) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
pcap_strerror(errno));
goto bad;
}
}
if (ioctl(fd, BIOCGBLEN, &v) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
pcap_strerror(errno));
goto bad;
}
p->bufsize = v;
p->buffer = malloc(p->bufsize);
if (p->buffer == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
goto bad;
}
return (p);
bad:
if (fd >= 0)
close(fd);
free(p);
return (NULL);
}
static void
swap_hdr(struct pcap_file_header *hp)
{
hp->version_major = swap16(hp->version_major);
hp->version_minor = swap16(hp->version_minor);
hp->thiszone = swap32(hp->thiszone);
hp->sigfigs = swap32(hp->sigfigs);
hp->snaplen = swap32(hp->snaplen);
hp->linktype = swap32(hp->linktype);
}
pcap_t *
priv_pcap_offline(const char *fname, char *errbuf)
{
pcap_t *p;
FILE *fp = NULL;
struct pcap_file_header hdr;
int linklen, err;
if (priv_fd < 0)
errx(1, "%s: called from privileged portion", __func__);
p = malloc(sizeof(*p));
if (p == NULL) {
strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
return (NULL);
}
memset((char *)p, 0, sizeof(*p));
if (fname[0] == '-' && fname[1] == '\0') {
p->fd = -1;
fp = stdin;
} else {
write_command(priv_fd, PRIV_OPEN_DUMP);
p->fd = receive_fd(priv_fd);
must_read(priv_fd, &err, sizeof(int));
if (p->fd < 0) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"Failed to open input file %s: %s",
fname, strerror(err));
goto bad;
}
fp = fdopen(p->fd, "r");
if (fp == NULL) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname,
pcap_strerror(errno));
close(p->fd);
p->fd = -1;
goto bad;
}
}
if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s",
pcap_strerror(errno));
goto bad;
}
if (hdr.magic != TCPDUMP_MAGIC) {
if (swap32(hdr.magic) != TCPDUMP_MAGIC) {
snprintf(errbuf, PCAP_ERRBUF_SIZE,
"bad dump file format");
goto bad;
}
p->sf.swapped = 1;
swap_hdr(&hdr);
}
if (hdr.version_major < PCAP_VERSION_MAJOR) {
snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format");
goto bad;
}
p->tzoff = hdr.thiszone;
p->snapshot = hdr.snaplen;
p->linktype = hdr.linktype;
p->sf.rfile = fp;
p->bufsize = hdr.snaplen;
switch (p->linktype) {
case DLT_EN10MB:
linklen = 14;
break;
case DLT_FDDI:
linklen = 13 + 8;
break;
case DLT_NULL:
default:
linklen = 0;
break;
}
if (p->bufsize < 0)
p->bufsize = BPF_MAXBUFSIZE;
p->sf.base = malloc(p->bufsize + BPF_ALIGNMENT);
if (p->sf.base == NULL) {
strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE);
goto bad;
}
p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT);
p->sf.version_major = hdr.version_major;
p->sf.version_minor = hdr.version_minor;
#ifdef PCAP_FDDIPAD
pcap_fddipad = 0;
#endif
return (p);
bad:
if (fp != NULL && p->fd != -1)
fclose(fp);
free(p);
return (NULL);
}
static int
sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen)
{
struct pcap_file_header hdr;
bzero(&hdr, sizeof hdr);
hdr.magic = TCPDUMP_MAGIC;
hdr.version_major = PCAP_VERSION_MAJOR;
hdr.version_minor = PCAP_VERSION_MINOR;
hdr.thiszone = thiszone;
hdr.snaplen = snaplen;
hdr.sigfigs = 0;
hdr.linktype = linktype;
if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1)
return (-1);
return (0);
}
pcap_dumper_t *
priv_pcap_dump_open(pcap_t *p, char *fname)
{
int fd, err;
FILE *f;
if (priv_fd < 0)
errx(1, "%s: called from privileged portion", __func__);
if (fname[0] == '-' && fname[1] == '\0')
f = stdout;
else {
write_command(priv_fd, PRIV_OPEN_OUTPUT);
fd = receive_fd(priv_fd);
must_read(priv_fd, &err, sizeof(err));
if (fd < 0) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"Failed to open output file %s: %s",
fname, strerror(err));
return (NULL);
}
f = fdopen(fd, "w");
if (f == NULL) {
snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
fname, pcap_strerror(errno));
close(fd);
return (NULL);
}
}
priv_init_done();
(void)sf_write_header(f, p->linktype, p->tzoff, p->snapshot);
return ((pcap_dumper_t *)f);
}