xref: /freebsd/contrib/pf/pflogd/pflogd.c (revision 0baf7c8675952c843665097288cc573a11b143b9)
10baf7c86SMax Laier /*	$OpenBSD: pflogd.c,v 1.33 2005/02/09 12:09:30 henning Exp $	*/
213b9f610SMax Laier 
313b9f610SMax Laier /*
413b9f610SMax Laier  * Copyright (c) 2001 Theo de Raadt
513b9f610SMax Laier  * Copyright (c) 2001 Can Erkin Acar
613b9f610SMax Laier  * All rights reserved.
713b9f610SMax Laier  *
813b9f610SMax Laier  * Redistribution and use in source and binary forms, with or without
913b9f610SMax Laier  * modification, are permitted provided that the following conditions
1013b9f610SMax Laier  * are met:
1113b9f610SMax Laier  *
1213b9f610SMax Laier  *    - Redistributions of source code must retain the above copyright
1313b9f610SMax Laier  *      notice, this list of conditions and the following disclaimer.
1413b9f610SMax Laier  *    - Redistributions in binary form must reproduce the above
1513b9f610SMax Laier  *      copyright notice, this list of conditions and the following
1613b9f610SMax Laier  *      disclaimer in the documentation and/or other materials provided
1713b9f610SMax Laier  *      with the distribution.
1813b9f610SMax Laier  *
1913b9f610SMax Laier  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2013b9f610SMax Laier  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2113b9f610SMax Laier  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2213b9f610SMax Laier  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2313b9f610SMax Laier  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2413b9f610SMax Laier  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2513b9f610SMax Laier  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2613b9f610SMax Laier  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2713b9f610SMax Laier  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2813b9f610SMax Laier  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2913b9f610SMax Laier  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3013b9f610SMax Laier  * POSSIBILITY OF SUCH DAMAGE.
3113b9f610SMax Laier  */
3213b9f610SMax Laier 
33a10f530fSDavid E. O'Brien #include <sys/cdefs.h>
34a10f530fSDavid E. O'Brien __FBSDID("$FreeBSD$");
35a10f530fSDavid E. O'Brien 
3613b9f610SMax Laier #include <sys/types.h>
3722ac3eadSMax Laier #include <sys/ioctl.h>
3813b9f610SMax Laier #include <sys/file.h>
3913b9f610SMax Laier #include <sys/stat.h>
4013b9f610SMax Laier #include <stdio.h>
4113b9f610SMax Laier #include <stdlib.h>
4213b9f610SMax Laier #include <string.h>
4313b9f610SMax Laier #include <unistd.h>
4413b9f610SMax Laier #include <pcap-int.h>
4513b9f610SMax Laier #include <pcap.h>
4613b9f610SMax Laier #include <syslog.h>
4713b9f610SMax Laier #include <signal.h>
4813b9f610SMax Laier #include <errno.h>
4913b9f610SMax Laier #include <stdarg.h>
5013b9f610SMax Laier #include <fcntl.h>
51b83a49e9SMax Laier #ifdef __FreeBSD__
528c8618f5SMax Laier #include "pidfile.h"
538c8618f5SMax Laier #else
5413b9f610SMax Laier #include <util.h>
558c8618f5SMax Laier #endif
568c8618f5SMax Laier 
5722ac3eadSMax Laier #include "pflogd.h"
5813b9f610SMax Laier 
5913b9f610SMax Laier pcap_t *hpcap;
6022ac3eadSMax Laier static FILE *dpcap;
6113b9f610SMax Laier 
6213b9f610SMax Laier int Debug = 0;
6322ac3eadSMax Laier static int snaplen = DEF_SNAPLEN;
6422ac3eadSMax Laier static int cur_snaplen = DEF_SNAPLEN;
6513b9f610SMax Laier 
6613b9f610SMax Laier volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup;
6713b9f610SMax Laier 
6813b9f610SMax Laier char *filename = PFLOGD_LOG_FILE;
6913b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF;
7013b9f610SMax Laier char *filter = NULL;
7113b9f610SMax Laier 
7213b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE];
7313b9f610SMax Laier 
7413b9f610SMax Laier int log_debug = 0;
7513b9f610SMax Laier unsigned int delay = FLUSH_DELAY;
7613b9f610SMax Laier 
7722ac3eadSMax Laier char *copy_argv(char * const *);
7822ac3eadSMax Laier void  dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
7922ac3eadSMax Laier void  dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *);
8022ac3eadSMax Laier int   flush_buffer(FILE *);
8113b9f610SMax Laier int   init_pcap(void);
8222ac3eadSMax Laier void  logmsg(int, const char *, ...);
8322ac3eadSMax Laier void  purge_buffer(void);
8413b9f610SMax Laier int   reset_dump(void);
8522ac3eadSMax Laier int   scan_dump(FILE *, off_t);
8622ac3eadSMax Laier int   set_snaplen(int);
8722ac3eadSMax Laier void  set_suspended(int);
8813b9f610SMax Laier void  sig_alrm(int);
8913b9f610SMax Laier void  sig_close(int);
9013b9f610SMax Laier void  sig_hup(int);
9113b9f610SMax Laier void  usage(void);
9213b9f610SMax Laier 
9322ac3eadSMax Laier /* buffer must always be greater than snaplen */
9422ac3eadSMax Laier static int    bufpkt = 0;	/* number of packets in buffer */
9522ac3eadSMax Laier static int    buflen = 0;	/* allocated size of buffer */
9622ac3eadSMax Laier static char  *buffer = NULL;	/* packet buffer */
9722ac3eadSMax Laier static char  *bufpos = NULL;	/* position in buffer */
9822ac3eadSMax Laier static int    bufleft = 0;	/* bytes left in buffer */
9922ac3eadSMax Laier 
10022ac3eadSMax Laier /* if error, stop logging but count dropped packets */
10122ac3eadSMax Laier static int suspended = -1;
10222ac3eadSMax Laier static long packets_dropped = 0;
10322ac3eadSMax Laier 
10422ac3eadSMax Laier void
10522ac3eadSMax Laier set_suspended(int s)
10622ac3eadSMax Laier {
10722ac3eadSMax Laier 	if (suspended == s)
10822ac3eadSMax Laier 		return;
10922ac3eadSMax Laier 
11022ac3eadSMax Laier 	suspended = s;
11122ac3eadSMax Laier 	setproctitle("[%s] -s %d -f %s",
11222ac3eadSMax Laier             suspended ? "suspended" : "running", cur_snaplen, filename);
11322ac3eadSMax Laier }
11413b9f610SMax Laier 
11513b9f610SMax Laier char *
11613b9f610SMax Laier copy_argv(char * const *argv)
11713b9f610SMax Laier {
11813b9f610SMax Laier 	size_t len = 0, n;
11913b9f610SMax Laier 	char *buf;
12013b9f610SMax Laier 
12113b9f610SMax Laier 	if (argv == NULL)
12213b9f610SMax Laier 		return (NULL);
12313b9f610SMax Laier 
12413b9f610SMax Laier 	for (n = 0; argv[n]; n++)
12513b9f610SMax Laier 		len += strlen(argv[n])+1;
12613b9f610SMax Laier 	if (len == 0)
12713b9f610SMax Laier 		return (NULL);
12813b9f610SMax Laier 
12913b9f610SMax Laier 	buf = malloc(len);
13013b9f610SMax Laier 	if (buf == NULL)
13113b9f610SMax Laier 		return (NULL);
13213b9f610SMax Laier 
13313b9f610SMax Laier 	strlcpy(buf, argv[0], len);
13413b9f610SMax Laier 	for (n = 1; argv[n]; n++) {
13513b9f610SMax Laier 		strlcat(buf, " ", len);
13613b9f610SMax Laier 		strlcat(buf, argv[n], len);
13713b9f610SMax Laier 	}
13813b9f610SMax Laier 	return (buf);
13913b9f610SMax Laier }
14013b9f610SMax Laier 
14113b9f610SMax Laier void
14213b9f610SMax Laier logmsg(int pri, const char *message, ...)
14313b9f610SMax Laier {
14413b9f610SMax Laier 	va_list ap;
14513b9f610SMax Laier 	va_start(ap, message);
14613b9f610SMax Laier 
14713b9f610SMax Laier 	if (log_debug) {
14813b9f610SMax Laier 		vfprintf(stderr, message, ap);
14913b9f610SMax Laier 		fprintf(stderr, "\n");
15013b9f610SMax Laier 	} else
15113b9f610SMax Laier 		vsyslog(pri, message, ap);
15213b9f610SMax Laier 	va_end(ap);
15313b9f610SMax Laier }
15413b9f610SMax Laier 
155b83a49e9SMax Laier #ifdef __FreeBSD__
156b83a49e9SMax Laier __dead2 void
157b83a49e9SMax Laier #else
15813b9f610SMax Laier __dead void
159b83a49e9SMax Laier #endif
16013b9f610SMax Laier usage(void)
16113b9f610SMax Laier {
16222ac3eadSMax Laier 	fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename] ");
16313b9f610SMax Laier 	fprintf(stderr, "[-s snaplen] [expression]\n");
16413b9f610SMax Laier 	exit(1);
16513b9f610SMax Laier }
16613b9f610SMax Laier 
16713b9f610SMax Laier void
16813b9f610SMax Laier sig_close(int sig)
16913b9f610SMax Laier {
17013b9f610SMax Laier 	gotsig_close = 1;
17113b9f610SMax Laier }
17213b9f610SMax Laier 
17313b9f610SMax Laier void
17413b9f610SMax Laier sig_hup(int sig)
17513b9f610SMax Laier {
17613b9f610SMax Laier 	gotsig_hup = 1;
17713b9f610SMax Laier }
17813b9f610SMax Laier 
17913b9f610SMax Laier void
18013b9f610SMax Laier sig_alrm(int sig)
18113b9f610SMax Laier {
18213b9f610SMax Laier 	gotsig_alrm = 1;
18313b9f610SMax Laier }
18413b9f610SMax Laier 
18522ac3eadSMax Laier void
18622ac3eadSMax Laier set_pcap_filter(void)
18713b9f610SMax Laier {
18813b9f610SMax Laier 	struct bpf_program bprog;
18913b9f610SMax Laier 
19013b9f610SMax Laier 	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0)
19113b9f610SMax Laier 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
19222ac3eadSMax Laier 	else {
19322ac3eadSMax Laier 		if (pcap_setfilter(hpcap, &bprog) < 0)
19413b9f610SMax Laier 			logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
19522ac3eadSMax Laier 		pcap_freecode(&bprog);
19622ac3eadSMax Laier 	}
19722ac3eadSMax Laier }
19822ac3eadSMax Laier 
19922ac3eadSMax Laier int
20022ac3eadSMax Laier init_pcap(void)
20122ac3eadSMax Laier {
20222ac3eadSMax Laier 	hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);
20322ac3eadSMax Laier 	if (hpcap == NULL) {
20422ac3eadSMax Laier 		logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
20522ac3eadSMax Laier 		return (-1);
20622ac3eadSMax Laier 	}
20713b9f610SMax Laier 
20813b9f610SMax Laier 	if (pcap_datalink(hpcap) != DLT_PFLOG) {
20913b9f610SMax Laier 		logmsg(LOG_ERR, "Invalid datalink type");
21013b9f610SMax Laier 		pcap_close(hpcap);
21122ac3eadSMax Laier 		hpcap = NULL;
21213b9f610SMax Laier 		return (-1);
21313b9f610SMax Laier 	}
21413b9f610SMax Laier 
21522ac3eadSMax Laier 	set_pcap_filter();
21613b9f610SMax Laier 
21722ac3eadSMax Laier 	cur_snaplen = snaplen = pcap_snapshot(hpcap);
21822ac3eadSMax Laier 
21922ac3eadSMax Laier #ifdef __FreeBSD__
22022ac3eadSMax Laier 	/* We can not lock bpf devices ... yet */
22122ac3eadSMax Laier #else
22222ac3eadSMax Laier 	/* lock */
22322ac3eadSMax Laier 	if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) {
22422ac3eadSMax Laier 		logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno));
22522ac3eadSMax Laier 		return (-1);
22622ac3eadSMax Laier 	}
22722ac3eadSMax Laier #endif
22822ac3eadSMax Laier 
22922ac3eadSMax Laier 	return (0);
23022ac3eadSMax Laier }
23122ac3eadSMax Laier 
23222ac3eadSMax Laier int
23322ac3eadSMax Laier set_snaplen(int snap)
23422ac3eadSMax Laier {
23522ac3eadSMax Laier 	if (priv_set_snaplen(snap))
23622ac3eadSMax Laier 		return (1);
23722ac3eadSMax Laier 
23822ac3eadSMax Laier 	if (cur_snaplen > snap)
23922ac3eadSMax Laier 		purge_buffer();
24022ac3eadSMax Laier 
24122ac3eadSMax Laier 	cur_snaplen = snap;
24222ac3eadSMax Laier 
24313b9f610SMax Laier 	return (0);
24413b9f610SMax Laier }
24513b9f610SMax Laier 
24613b9f610SMax Laier int
24713b9f610SMax Laier reset_dump(void)
24813b9f610SMax Laier {
24913b9f610SMax Laier 	struct pcap_file_header hdr;
25013b9f610SMax Laier 	struct stat st;
25122ac3eadSMax Laier 	int fd;
25213b9f610SMax Laier 	FILE *fp;
25313b9f610SMax Laier 
25413b9f610SMax Laier 	if (hpcap == NULL)
25522ac3eadSMax Laier 		return (-1);
25622ac3eadSMax Laier 
25713b9f610SMax Laier 	if (dpcap) {
25822ac3eadSMax Laier 		flush_buffer(dpcap);
25922ac3eadSMax Laier 		fclose(dpcap);
26022ac3eadSMax Laier 		dpcap = NULL;
26113b9f610SMax Laier 	}
26213b9f610SMax Laier 
26313b9f610SMax Laier 	/*
26413b9f610SMax Laier 	 * Basically reimplement pcap_dump_open() because it truncates
26513b9f610SMax Laier 	 * files and duplicates headers and such.
26613b9f610SMax Laier 	 */
26722ac3eadSMax Laier 	fd = priv_open_log();
26822ac3eadSMax Laier 	if (fd < 0)
26922ac3eadSMax Laier 		return (1);
27022ac3eadSMax Laier 
27122ac3eadSMax Laier 	fp = fdopen(fd, "a+");
27222ac3eadSMax Laier 
27313b9f610SMax Laier 	if (fp == NULL) {
2740baf7c86SMax Laier 		close(fd);
27522ac3eadSMax Laier 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
27613b9f610SMax Laier 		return (1);
27713b9f610SMax Laier 	}
27813b9f610SMax Laier 	if (fstat(fileno(fp), &st) == -1) {
2790baf7c86SMax Laier 		fclose(fp);
28022ac3eadSMax Laier 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
28113b9f610SMax Laier 		return (1);
28213b9f610SMax Laier 	}
28313b9f610SMax Laier 
28422ac3eadSMax Laier 	/* set FILE unbuffered, we do our own buffering */
28522ac3eadSMax Laier 	if (setvbuf(fp, NULL, _IONBF, 0)) {
2860baf7c86SMax Laier 		fclose(fp);
28722ac3eadSMax Laier 		logmsg(LOG_ERR, "Failed to set output buffers");
28822ac3eadSMax Laier 		return (1);
28922ac3eadSMax Laier 	}
29013b9f610SMax Laier 
29113b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4
29213b9f610SMax Laier 
29313b9f610SMax Laier 	if (st.st_size == 0) {
29422ac3eadSMax Laier 		if (snaplen != cur_snaplen) {
29513b9f610SMax Laier 			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
29622ac3eadSMax Laier 			if (set_snaplen(snaplen)) {
2970baf7c86SMax Laier 				fclose(fp);
29822ac3eadSMax Laier 				logmsg(LOG_WARNING,
29922ac3eadSMax Laier 				    "Failed, using old settings");
30013b9f610SMax Laier 			}
30113b9f610SMax Laier 		}
30213b9f610SMax Laier 		hdr.magic = TCPDUMP_MAGIC;
30313b9f610SMax Laier 		hdr.version_major = PCAP_VERSION_MAJOR;
30413b9f610SMax Laier 		hdr.version_minor = PCAP_VERSION_MINOR;
30513b9f610SMax Laier 		hdr.thiszone = hpcap->tzoff;
30613b9f610SMax Laier 		hdr.snaplen = hpcap->snapshot;
30713b9f610SMax Laier 		hdr.sigfigs = 0;
30813b9f610SMax Laier 		hdr.linktype = hpcap->linktype;
30913b9f610SMax Laier 
31013b9f610SMax Laier 		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
31113b9f610SMax Laier 			fclose(fp);
31213b9f610SMax Laier 			return (1);
31313b9f610SMax Laier 		}
31422ac3eadSMax Laier 	} else if (scan_dump(fp, st.st_size)) {
31522ac3eadSMax Laier 		/* XXX move file and continue? */
31622ac3eadSMax Laier 		fclose(fp);
31722ac3eadSMax Laier 		return (1);
31813b9f610SMax Laier 	}
31922ac3eadSMax Laier 
32022ac3eadSMax Laier 	dpcap = fp;
32122ac3eadSMax Laier 
32222ac3eadSMax Laier 	set_suspended(0);
32322ac3eadSMax Laier 	flush_buffer(fp);
32422ac3eadSMax Laier 
32522ac3eadSMax Laier 	return (0);
32622ac3eadSMax Laier }
32722ac3eadSMax Laier 
32822ac3eadSMax Laier int
32922ac3eadSMax Laier scan_dump(FILE *fp, off_t size)
33022ac3eadSMax Laier {
33122ac3eadSMax Laier 	struct pcap_file_header hdr;
3326964e37dSMax Laier #ifdef __FreeBSD__
3336964e37dSMax Laier 	struct pcap_sf_pkthdr ph;
3346964e37dSMax Laier #else
33522ac3eadSMax Laier 	struct pcap_pkthdr ph;
3366964e37dSMax Laier #endif
33722ac3eadSMax Laier 	off_t pos;
33822ac3eadSMax Laier 
33922ac3eadSMax Laier 	/*
34022ac3eadSMax Laier 	 * Must read the file, compare the header against our new
34122ac3eadSMax Laier 	 * options (in particular, snaplen) and adjust our options so
34222ac3eadSMax Laier 	 * that we generate a correct file. Furthermore, check the file
34322ac3eadSMax Laier 	 * for consistency so that we can append safely.
34422ac3eadSMax Laier 	 *
34522ac3eadSMax Laier 	 * XXX this may take a long time for large logs.
34622ac3eadSMax Laier 	 */
34722ac3eadSMax Laier 	(void) fseek(fp, 0L, SEEK_SET);
34822ac3eadSMax Laier 
34922ac3eadSMax Laier 	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
35022ac3eadSMax Laier 		logmsg(LOG_ERR, "Short file header");
35122ac3eadSMax Laier 		return (1);
35222ac3eadSMax Laier 	}
35322ac3eadSMax Laier 
35422ac3eadSMax Laier 	if (hdr.magic != TCPDUMP_MAGIC ||
35522ac3eadSMax Laier 	    hdr.version_major != PCAP_VERSION_MAJOR ||
35622ac3eadSMax Laier 	    hdr.version_minor != PCAP_VERSION_MINOR ||
35722ac3eadSMax Laier 	    hdr.linktype != hpcap->linktype ||
35822ac3eadSMax Laier 	    hdr.snaplen > PFLOGD_MAXSNAPLEN) {
35922ac3eadSMax Laier 		logmsg(LOG_ERR, "Invalid/incompatible log file, move it away");
36022ac3eadSMax Laier 		return (1);
36122ac3eadSMax Laier 	}
36222ac3eadSMax Laier 
36322ac3eadSMax Laier 	pos = sizeof(hdr);
36422ac3eadSMax Laier 
36522ac3eadSMax Laier 	while (!feof(fp)) {
36622ac3eadSMax Laier 		off_t len = fread((char *)&ph, 1, sizeof(ph), fp);
36722ac3eadSMax Laier 		if (len == 0)
36822ac3eadSMax Laier 			break;
36922ac3eadSMax Laier 
37022ac3eadSMax Laier 		if (len != sizeof(ph))
37122ac3eadSMax Laier 			goto error;
37222ac3eadSMax Laier 		if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN)
37322ac3eadSMax Laier 			goto error;
37422ac3eadSMax Laier 		pos += sizeof(ph) + ph.caplen;
37522ac3eadSMax Laier 		if (pos > size)
37622ac3eadSMax Laier 			goto error;
37722ac3eadSMax Laier 		fseek(fp, ph.caplen, SEEK_CUR);
37822ac3eadSMax Laier 	}
37922ac3eadSMax Laier 
38022ac3eadSMax Laier 	if (pos != size)
38122ac3eadSMax Laier 		goto error;
38222ac3eadSMax Laier 
38322ac3eadSMax Laier 	if (hdr.snaplen != cur_snaplen) {
38422ac3eadSMax Laier 		logmsg(LOG_WARNING,
38522ac3eadSMax Laier 		       "Existing file has different snaplen %u, using it",
38622ac3eadSMax Laier 		       hdr.snaplen);
38722ac3eadSMax Laier 		if (set_snaplen(hdr.snaplen)) {
38822ac3eadSMax Laier 			logmsg(LOG_WARNING,
38922ac3eadSMax Laier 			       "Failed, using old settings, offset %llu",
39022ac3eadSMax Laier 			       (unsigned long long) size);
39113b9f610SMax Laier 		}
39213b9f610SMax Laier 	}
39313b9f610SMax Laier 
39413b9f610SMax Laier 	return (0);
39522ac3eadSMax Laier 
39622ac3eadSMax Laier  error:
39722ac3eadSMax Laier 	logmsg(LOG_ERR, "Corrupted log file.");
39822ac3eadSMax Laier 	return (1);
39922ac3eadSMax Laier }
40022ac3eadSMax Laier 
40122ac3eadSMax Laier /* dump a packet directly to the stream, which is unbuffered */
40222ac3eadSMax Laier void
40322ac3eadSMax Laier dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
40422ac3eadSMax Laier {
40522ac3eadSMax Laier 	FILE *f = (FILE *)user;
4066964e37dSMax Laier #ifdef __FreeBSD__
4076964e37dSMax Laier 	struct pcap_sf_pkthdr sh;
4086964e37dSMax Laier #endif
40922ac3eadSMax Laier 
41022ac3eadSMax Laier 	if (suspended) {
41122ac3eadSMax Laier 		packets_dropped++;
41222ac3eadSMax Laier 		return;
41322ac3eadSMax Laier 	}
41422ac3eadSMax Laier 
4156964e37dSMax Laier #ifdef __FreeBSD__
4166964e37dSMax Laier 	sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec;
4176964e37dSMax Laier 	sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec;
4186964e37dSMax Laier 	sh.caplen = h->caplen;
4196964e37dSMax Laier 	sh.len = h->len;
4206964e37dSMax Laier 
4216964e37dSMax Laier 	if (fwrite((char *)&sh, sizeof(sh), 1, f) != 1) {
4226964e37dSMax Laier #else
42322ac3eadSMax Laier 	if (fwrite((char *)h, sizeof(*h), 1, f) != 1) {
4246964e37dSMax Laier #endif
42522ac3eadSMax Laier 		off_t pos = ftello(f);
4260baf7c86SMax Laier 
4270baf7c86SMax Laier 		/* try to undo header to prevent corruption */
4286964e37dSMax Laier #ifdef __FreeBSD__
4296964e37dSMax Laier 		if (pos < sizeof(sh) ||
4306964e37dSMax Laier 		    ftruncate(fileno(f), pos - sizeof(sh))) {
4316964e37dSMax Laier #else
43222ac3eadSMax Laier 		if (pos < sizeof(*h) ||
43322ac3eadSMax Laier 		    ftruncate(fileno(f), pos - sizeof(*h))) {
4346964e37dSMax Laier #endif
43522ac3eadSMax Laier 			logmsg(LOG_ERR, "Write failed, corrupted logfile!");
43622ac3eadSMax Laier 			set_suspended(1);
43722ac3eadSMax Laier 			gotsig_close = 1;
43822ac3eadSMax Laier 			return;
43922ac3eadSMax Laier 		}
44022ac3eadSMax Laier 		goto error;
44122ac3eadSMax Laier 	}
44222ac3eadSMax Laier 
44322ac3eadSMax Laier 	if (fwrite((char *)sp, h->caplen, 1, f) != 1)
44422ac3eadSMax Laier 		goto error;
44522ac3eadSMax Laier 
44622ac3eadSMax Laier 	return;
44722ac3eadSMax Laier 
44822ac3eadSMax Laier error:
44922ac3eadSMax Laier 	set_suspended(1);
45022ac3eadSMax Laier 	packets_dropped ++;
45122ac3eadSMax Laier 	logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno));
45222ac3eadSMax Laier }
45322ac3eadSMax Laier 
45422ac3eadSMax Laier int
45522ac3eadSMax Laier flush_buffer(FILE *f)
45622ac3eadSMax Laier {
45722ac3eadSMax Laier 	off_t offset;
45822ac3eadSMax Laier 	int len = bufpos - buffer;
45922ac3eadSMax Laier 
46022ac3eadSMax Laier 	if (len <= 0)
46122ac3eadSMax Laier 		return (0);
46222ac3eadSMax Laier 
46322ac3eadSMax Laier 	offset = ftello(f);
46422ac3eadSMax Laier 	if (offset == (off_t)-1) {
46522ac3eadSMax Laier 		set_suspended(1);
46622ac3eadSMax Laier 		logmsg(LOG_ERR, "Logging suspended: ftello: %s",
46722ac3eadSMax Laier 		    strerror(errno));
46822ac3eadSMax Laier 		return (1);
46922ac3eadSMax Laier 	}
47022ac3eadSMax Laier 
47122ac3eadSMax Laier 	if (fwrite(buffer, len, 1, f) != 1) {
47222ac3eadSMax Laier 		set_suspended(1);
47322ac3eadSMax Laier 		logmsg(LOG_ERR, "Logging suspended: fwrite: %s",
47422ac3eadSMax Laier 		    strerror(errno));
47522ac3eadSMax Laier 		ftruncate(fileno(f), offset);
47622ac3eadSMax Laier 		return (1);
47722ac3eadSMax Laier 	}
47822ac3eadSMax Laier 
47922ac3eadSMax Laier 	set_suspended(0);
48022ac3eadSMax Laier 	bufpos = buffer;
48122ac3eadSMax Laier 	bufleft = buflen;
48222ac3eadSMax Laier 	bufpkt = 0;
48322ac3eadSMax Laier 
48422ac3eadSMax Laier 	return (0);
48522ac3eadSMax Laier }
48622ac3eadSMax Laier 
48722ac3eadSMax Laier void
48822ac3eadSMax Laier purge_buffer(void)
48922ac3eadSMax Laier {
49022ac3eadSMax Laier 	packets_dropped += bufpkt;
49122ac3eadSMax Laier 
49222ac3eadSMax Laier 	set_suspended(0);
49322ac3eadSMax Laier 	bufpos = buffer;
49422ac3eadSMax Laier 	bufleft = buflen;
49522ac3eadSMax Laier 	bufpkt = 0;
49622ac3eadSMax Laier }
49722ac3eadSMax Laier 
49822ac3eadSMax Laier /* append packet to the buffer, flushing if necessary */
49922ac3eadSMax Laier void
50022ac3eadSMax Laier dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
50122ac3eadSMax Laier {
50222ac3eadSMax Laier 	FILE *f = (FILE *)user;
5036964e37dSMax Laier #ifdef __FreeBSD__
5046964e37dSMax Laier 	struct pcap_sf_pkthdr sh;
5056964e37dSMax Laier 	size_t len = sizeof(sh) + h->caplen;
5066964e37dSMax Laier #else
50722ac3eadSMax Laier 	size_t len = sizeof(*h) + h->caplen;
5086964e37dSMax Laier #endif
50922ac3eadSMax Laier 
51022ac3eadSMax Laier 	if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) {
51122ac3eadSMax Laier 		logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped",
51222ac3eadSMax Laier 		       len, cur_snaplen, snaplen);
51322ac3eadSMax Laier 		packets_dropped++;
51422ac3eadSMax Laier 		return;
51522ac3eadSMax Laier 	}
51622ac3eadSMax Laier 
51722ac3eadSMax Laier 	if (len <= bufleft)
51822ac3eadSMax Laier 		goto append;
51922ac3eadSMax Laier 
52022ac3eadSMax Laier 	if (suspended) {
52122ac3eadSMax Laier 		packets_dropped++;
52222ac3eadSMax Laier 		return;
52322ac3eadSMax Laier 	}
52422ac3eadSMax Laier 
52522ac3eadSMax Laier 	if (flush_buffer(f)) {
52622ac3eadSMax Laier 		packets_dropped++;
52722ac3eadSMax Laier 		return;
52822ac3eadSMax Laier 	}
52922ac3eadSMax Laier 
53022ac3eadSMax Laier 	if (len > bufleft) {
53122ac3eadSMax Laier 		dump_packet_nobuf(user, h, sp);
53222ac3eadSMax Laier 		return;
53322ac3eadSMax Laier 	}
53422ac3eadSMax Laier 
53522ac3eadSMax Laier  append:
5366964e37dSMax Laier #ifdef __FreeBSD__
5376964e37dSMax Laier  	sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec;
5386964e37dSMax Laier  	sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec;
5396964e37dSMax Laier 	sh.caplen = h->caplen;
5406964e37dSMax Laier 	sh.len = h->len;
5416964e37dSMax Laier 
5426964e37dSMax Laier 	memcpy(bufpos, &sh, sizeof(sh));
5436964e37dSMax Laier 	memcpy(bufpos + sizeof(sh), sp, h->caplen);
5446964e37dSMax Laier #else
54522ac3eadSMax Laier 	memcpy(bufpos, h, sizeof(*h));
54622ac3eadSMax Laier 	memcpy(bufpos + sizeof(*h), sp, h->caplen);
5476964e37dSMax Laier #endif
54822ac3eadSMax Laier 
54922ac3eadSMax Laier 	bufpos += len;
55022ac3eadSMax Laier 	bufleft -= len;
55122ac3eadSMax Laier 	bufpkt++;
55222ac3eadSMax Laier 
55322ac3eadSMax Laier 	return;
55413b9f610SMax Laier }
55513b9f610SMax Laier 
55613b9f610SMax Laier int
55713b9f610SMax Laier main(int argc, char **argv)
55813b9f610SMax Laier {
55913b9f610SMax Laier 	struct pcap_stat pstat;
56022ac3eadSMax Laier 	int ch, np, Xflag = 0;
56122ac3eadSMax Laier 	pcap_handler phandler = dump_packet;
5620baf7c86SMax Laier 	char *errstr = NULL;
56313b9f610SMax Laier 
56422ac3eadSMax Laier #ifdef __FreeBSD__
56522ac3eadSMax Laier 	/* another ?paranoid? safety measure we do not have */
56622ac3eadSMax Laier #else
56722ac3eadSMax Laier 	closefrom(STDERR_FILENO + 1);
56822ac3eadSMax Laier #endif
56922ac3eadSMax Laier 
57022ac3eadSMax Laier 	while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) {
57113b9f610SMax Laier 		switch (ch) {
57213b9f610SMax Laier 		case 'D':
57313b9f610SMax Laier 			Debug = 1;
57413b9f610SMax Laier 			break;
57513b9f610SMax Laier 		case 'd':
5760baf7c86SMax Laier #ifdef __OpenBSD__
5770baf7c86SMax Laier 			delay = strtonum(optarg, 5, 60*60, &errstr);
5780baf7c86SMax Laier 			if (errstr)
5790baf7c86SMax Laier #else
5800baf7c86SMax Laier 			delay = strtol(optarg, &errstr, 10);
5810baf7c86SMax Laier 			if ((delay < 5) || (delay > 60*60) ||
5820baf7c86SMax Laier 			    ((errstr != NULL) && (*errstr != '\0')))
5830baf7c86SMax Laier #endif
58413b9f610SMax Laier 				usage();
58513b9f610SMax Laier 			break;
58613b9f610SMax Laier 		case 'f':
58713b9f610SMax Laier 			filename = optarg;
58813b9f610SMax Laier 			break;
58913b9f610SMax Laier 		case 's':
5900baf7c86SMax Laier #ifdef __OpenBSD__
5910baf7c86SMax Laier 			snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN,
5920baf7c86SMax Laier 			    &errstr);
59313b9f610SMax Laier 			if (snaplen <= 0)
59413b9f610SMax Laier 				snaplen = DEF_SNAPLEN;
5950baf7c86SMax Laier 			if (errstr)
59622ac3eadSMax Laier 				snaplen = PFLOGD_MAXSNAPLEN;
5970baf7c86SMax Laier #else
5980baf7c86SMax Laier 			snaplen = strtol(optarg, &errstr, 10);
5990baf7c86SMax Laier 			if (snaplen <= 0)
6000baf7c86SMax Laier 				snaplen = DEF_SNAPLEN;
6010baf7c86SMax Laier 			if ((snaplen > PFLOGD_MAXSNAPLEN) ||
6020baf7c86SMax Laier 			    ((errstr != NULL) && (*errstr != '\0')))
6030baf7c86SMax Laier 				snaplen = PFLOGD_MAXSNAPLEN;
6040baf7c86SMax Laier #endif
60522ac3eadSMax Laier 			break;
60622ac3eadSMax Laier 		case 'x':
60722ac3eadSMax Laier 			Xflag++;
60813b9f610SMax Laier 			break;
60913b9f610SMax Laier 		default:
61013b9f610SMax Laier 			usage();
61113b9f610SMax Laier 		}
61213b9f610SMax Laier 
61313b9f610SMax Laier 	}
61413b9f610SMax Laier 
61513b9f610SMax Laier 	log_debug = Debug;
61613b9f610SMax Laier 	argc -= optind;
61713b9f610SMax Laier 	argv += optind;
61813b9f610SMax Laier 
61913b9f610SMax Laier 	if (!Debug) {
62013b9f610SMax Laier 		openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON);
62113b9f610SMax Laier 		if (daemon(0, 0)) {
62213b9f610SMax Laier 			logmsg(LOG_WARNING, "Failed to become daemon: %s",
62313b9f610SMax Laier 			    strerror(errno));
62413b9f610SMax Laier 		}
62513b9f610SMax Laier 		pidfile(NULL);
62613b9f610SMax Laier 	}
62713b9f610SMax Laier 
6280baf7c86SMax Laier 	tzset();
62913b9f610SMax Laier 	(void)umask(S_IRWXG | S_IRWXO);
63013b9f610SMax Laier 
63122ac3eadSMax Laier 	/* filter will be used by the privileged process */
63222ac3eadSMax Laier 	if (argc) {
63322ac3eadSMax Laier 		filter = copy_argv(argv);
63422ac3eadSMax Laier 		if (filter == NULL)
63522ac3eadSMax Laier 			logmsg(LOG_NOTICE, "Failed to form filter expression");
63622ac3eadSMax Laier 	}
63722ac3eadSMax Laier 
63822ac3eadSMax Laier 	/* initialize pcap before dropping privileges */
63922ac3eadSMax Laier 	if (init_pcap()) {
64022ac3eadSMax Laier 		logmsg(LOG_ERR, "Exiting, init failure");
64122ac3eadSMax Laier 		exit(1);
64222ac3eadSMax Laier 	}
64322ac3eadSMax Laier 
64422ac3eadSMax Laier 	/* Privilege separation begins here */
64522ac3eadSMax Laier 	if (priv_init()) {
64622ac3eadSMax Laier 		logmsg(LOG_ERR, "unable to privsep");
64722ac3eadSMax Laier 		exit(1);
64822ac3eadSMax Laier 	}
64922ac3eadSMax Laier 
65022ac3eadSMax Laier 	setproctitle("[initializing]");
65122ac3eadSMax Laier 	/* Process is now unprivileged and inside a chroot */
65213b9f610SMax Laier 	signal(SIGTERM, sig_close);
65313b9f610SMax Laier 	signal(SIGINT, sig_close);
65413b9f610SMax Laier 	signal(SIGQUIT, sig_close);
65513b9f610SMax Laier 	signal(SIGALRM, sig_alrm);
65613b9f610SMax Laier 	signal(SIGHUP, sig_hup);
65713b9f610SMax Laier 	alarm(delay);
65813b9f610SMax Laier 
65922ac3eadSMax Laier 	buffer = malloc(PFLOGD_BUFSIZE);
66013b9f610SMax Laier 
66122ac3eadSMax Laier 	if (buffer == NULL) {
66222ac3eadSMax Laier 		logmsg(LOG_WARNING, "Failed to allocate output buffer");
66322ac3eadSMax Laier 		phandler = dump_packet_nobuf;
66422ac3eadSMax Laier 	} else {
66522ac3eadSMax Laier 		bufleft = buflen = PFLOGD_BUFSIZE;
66622ac3eadSMax Laier 		bufpos = buffer;
66722ac3eadSMax Laier 		bufpkt = 0;
66813b9f610SMax Laier 	}
66913b9f610SMax Laier 
67013b9f610SMax Laier 	if (reset_dump()) {
67122ac3eadSMax Laier 		if (Xflag)
67222ac3eadSMax Laier 			return (1);
67322ac3eadSMax Laier 
67422ac3eadSMax Laier 		logmsg(LOG_ERR, "Logging suspended: open error");
67522ac3eadSMax Laier 		set_suspended(1);
67622ac3eadSMax Laier 	} else if (Xflag)
67722ac3eadSMax Laier 		return (0);
67813b9f610SMax Laier 
67913b9f610SMax Laier 	while (1) {
68022ac3eadSMax Laier 		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS,
6810baf7c86SMax Laier 		    phandler, (u_char *)dpcap);
68222d6889bSMax Laier 		if (np < 0) {
68322d6889bSMax Laier #ifdef __FreeBSD__
68422d6889bSMax Laier 			if (errno == ENXIO) {
68522d6889bSMax Laier 				logmsg(LOG_ERR,
68622d6889bSMax Laier 				    "Device not/no longer configured");
68722d6889bSMax Laier 				break;
68822d6889bSMax Laier 			}
68922d6889bSMax Laier #endif
69013b9f610SMax Laier 			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
69122d6889bSMax Laier 		}
69213b9f610SMax Laier 
69313b9f610SMax Laier 		if (gotsig_close)
69413b9f610SMax Laier 			break;
69513b9f610SMax Laier 		if (gotsig_hup) {
69613b9f610SMax Laier 			if (reset_dump()) {
69722ac3eadSMax Laier 				logmsg(LOG_ERR,
69822ac3eadSMax Laier 				    "Logging suspended: open error");
69922ac3eadSMax Laier 				set_suspended(1);
70013b9f610SMax Laier 			}
70113b9f610SMax Laier 			gotsig_hup = 0;
70213b9f610SMax Laier 		}
70313b9f610SMax Laier 
70413b9f610SMax Laier 		if (gotsig_alrm) {
70522ac3eadSMax Laier 			if (dpcap)
70622ac3eadSMax Laier 				flush_buffer(dpcap);
70713b9f610SMax Laier 			gotsig_alrm = 0;
70813b9f610SMax Laier 			alarm(delay);
70913b9f610SMax Laier 		}
71013b9f610SMax Laier 	}
71113b9f610SMax Laier 
71222ac3eadSMax Laier 	logmsg(LOG_NOTICE, "Exiting");
71322ac3eadSMax Laier 	if (dpcap) {
71422ac3eadSMax Laier 		flush_buffer(dpcap);
71522ac3eadSMax Laier 		fclose(dpcap);
71622ac3eadSMax Laier 	}
71722ac3eadSMax Laier 	purge_buffer();
71813b9f610SMax Laier 
71913b9f610SMax Laier 	if (pcap_stats(hpcap, &pstat) < 0)
72013b9f610SMax Laier 		logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap));
72113b9f610SMax Laier 	else
72222ac3eadSMax Laier 		logmsg(LOG_NOTICE,
72322ac3eadSMax Laier 		    "%u packets received, %u/%u dropped (kernel/pflogd)",
72422ac3eadSMax Laier 		    pstat.ps_recv, pstat.ps_drop, packets_dropped);
72513b9f610SMax Laier 
72613b9f610SMax Laier 	pcap_close(hpcap);
72713b9f610SMax Laier 	if (!Debug)
72813b9f610SMax Laier 		closelog();
72913b9f610SMax Laier 	return (0);
73013b9f610SMax Laier }
731