xref: /freebsd/contrib/pf/pflogd/pflogd.c (revision 22d6889b)
122ac3eadSMax Laier /*	$OpenBSD: pflogd.c,v 1.27 2004/02/13 19:01:57 otto 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) {
27422ac3eadSMax Laier 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
27513b9f610SMax Laier 		return (1);
27613b9f610SMax Laier 	}
27713b9f610SMax Laier 	if (fstat(fileno(fp), &st) == -1) {
27822ac3eadSMax Laier 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
27913b9f610SMax Laier 		return (1);
28013b9f610SMax Laier 	}
28113b9f610SMax Laier 
28222ac3eadSMax Laier 	/* set FILE unbuffered, we do our own buffering */
28322ac3eadSMax Laier 	if (setvbuf(fp, NULL, _IONBF, 0)) {
28422ac3eadSMax Laier 		logmsg(LOG_ERR, "Failed to set output buffers");
28522ac3eadSMax Laier 		return (1);
28622ac3eadSMax Laier 	}
28713b9f610SMax Laier 
28813b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4
28913b9f610SMax Laier 
29013b9f610SMax Laier 	if (st.st_size == 0) {
29122ac3eadSMax Laier 		if (snaplen != cur_snaplen) {
29213b9f610SMax Laier 			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
29322ac3eadSMax Laier 			if (set_snaplen(snaplen)) {
29422ac3eadSMax Laier 				logmsg(LOG_WARNING,
29522ac3eadSMax Laier 				    "Failed, using old settings");
29613b9f610SMax Laier 			}
29713b9f610SMax Laier 		}
29813b9f610SMax Laier 		hdr.magic = TCPDUMP_MAGIC;
29913b9f610SMax Laier 		hdr.version_major = PCAP_VERSION_MAJOR;
30013b9f610SMax Laier 		hdr.version_minor = PCAP_VERSION_MINOR;
30113b9f610SMax Laier 		hdr.thiszone = hpcap->tzoff;
30213b9f610SMax Laier 		hdr.snaplen = hpcap->snapshot;
30313b9f610SMax Laier 		hdr.sigfigs = 0;
30413b9f610SMax Laier 		hdr.linktype = hpcap->linktype;
30513b9f610SMax Laier 
30613b9f610SMax Laier 		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
30713b9f610SMax Laier 			fclose(fp);
30813b9f610SMax Laier 			return (1);
30913b9f610SMax Laier 		}
31022ac3eadSMax Laier 	} else if (scan_dump(fp, st.st_size)) {
31122ac3eadSMax Laier 		/* XXX move file and continue? */
31222ac3eadSMax Laier 		fclose(fp);
31322ac3eadSMax Laier 		return (1);
31413b9f610SMax Laier 	}
31522ac3eadSMax Laier 
31622ac3eadSMax Laier 	dpcap = fp;
31722ac3eadSMax Laier 
31822ac3eadSMax Laier 	set_suspended(0);
31922ac3eadSMax Laier 	flush_buffer(fp);
32022ac3eadSMax Laier 
32122ac3eadSMax Laier 	return (0);
32222ac3eadSMax Laier }
32322ac3eadSMax Laier 
32422ac3eadSMax Laier int
32522ac3eadSMax Laier scan_dump(FILE *fp, off_t size)
32622ac3eadSMax Laier {
32722ac3eadSMax Laier 	struct pcap_file_header hdr;
3286964e37dSMax Laier #ifdef __FreeBSD__
3296964e37dSMax Laier 	struct pcap_sf_pkthdr ph;
3306964e37dSMax Laier #else
33122ac3eadSMax Laier 	struct pcap_pkthdr ph;
3326964e37dSMax Laier #endif
33322ac3eadSMax Laier 	off_t pos;
33422ac3eadSMax Laier 
33522ac3eadSMax Laier 	/*
33622ac3eadSMax Laier 	 * Must read the file, compare the header against our new
33722ac3eadSMax Laier 	 * options (in particular, snaplen) and adjust our options so
33822ac3eadSMax Laier 	 * that we generate a correct file. Furthermore, check the file
33922ac3eadSMax Laier 	 * for consistency so that we can append safely.
34022ac3eadSMax Laier 	 *
34122ac3eadSMax Laier 	 * XXX this may take a long time for large logs.
34222ac3eadSMax Laier 	 */
34322ac3eadSMax Laier 	(void) fseek(fp, 0L, SEEK_SET);
34422ac3eadSMax Laier 
34522ac3eadSMax Laier 	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
34622ac3eadSMax Laier 		logmsg(LOG_ERR, "Short file header");
34722ac3eadSMax Laier 		return (1);
34822ac3eadSMax Laier 	}
34922ac3eadSMax Laier 
35022ac3eadSMax Laier 	if (hdr.magic != TCPDUMP_MAGIC ||
35122ac3eadSMax Laier 	    hdr.version_major != PCAP_VERSION_MAJOR ||
35222ac3eadSMax Laier 	    hdr.version_minor != PCAP_VERSION_MINOR ||
35322ac3eadSMax Laier 	    hdr.linktype != hpcap->linktype ||
35422ac3eadSMax Laier 	    hdr.snaplen > PFLOGD_MAXSNAPLEN) {
35522ac3eadSMax Laier 		logmsg(LOG_ERR, "Invalid/incompatible log file, move it away");
35622ac3eadSMax Laier 		return (1);
35722ac3eadSMax Laier 	}
35822ac3eadSMax Laier 
35922ac3eadSMax Laier 	pos = sizeof(hdr);
36022ac3eadSMax Laier 
36122ac3eadSMax Laier 	while (!feof(fp)) {
36222ac3eadSMax Laier 		off_t len = fread((char *)&ph, 1, sizeof(ph), fp);
36322ac3eadSMax Laier 		if (len == 0)
36422ac3eadSMax Laier 			break;
36522ac3eadSMax Laier 
36622ac3eadSMax Laier 		if (len != sizeof(ph))
36722ac3eadSMax Laier 			goto error;
36822ac3eadSMax Laier 		if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN)
36922ac3eadSMax Laier 			goto error;
37022ac3eadSMax Laier 		pos += sizeof(ph) + ph.caplen;
37122ac3eadSMax Laier 		if (pos > size)
37222ac3eadSMax Laier 			goto error;
37322ac3eadSMax Laier 		fseek(fp, ph.caplen, SEEK_CUR);
37422ac3eadSMax Laier 	}
37522ac3eadSMax Laier 
37622ac3eadSMax Laier 	if (pos != size)
37722ac3eadSMax Laier 		goto error;
37822ac3eadSMax Laier 
37922ac3eadSMax Laier 	if (hdr.snaplen != cur_snaplen) {
38022ac3eadSMax Laier 		logmsg(LOG_WARNING,
38122ac3eadSMax Laier 		       "Existing file has different snaplen %u, using it",
38222ac3eadSMax Laier 		       hdr.snaplen);
38322ac3eadSMax Laier 		if (set_snaplen(hdr.snaplen)) {
38422ac3eadSMax Laier 			logmsg(LOG_WARNING,
38522ac3eadSMax Laier 			       "Failed, using old settings, offset %llu",
38622ac3eadSMax Laier 			       (unsigned long long) size);
38713b9f610SMax Laier 		}
38813b9f610SMax Laier 	}
38913b9f610SMax Laier 
39013b9f610SMax Laier 	return (0);
39122ac3eadSMax Laier 
39222ac3eadSMax Laier  error:
39322ac3eadSMax Laier 	logmsg(LOG_ERR, "Corrupted log file.");
39422ac3eadSMax Laier 	return (1);
39522ac3eadSMax Laier }
39622ac3eadSMax Laier 
39722ac3eadSMax Laier /* dump a packet directly to the stream, which is unbuffered */
39822ac3eadSMax Laier void
39922ac3eadSMax Laier dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
40022ac3eadSMax Laier {
40122ac3eadSMax Laier 	FILE *f = (FILE *)user;
4026964e37dSMax Laier #ifdef __FreeBSD__
4036964e37dSMax Laier 	struct pcap_sf_pkthdr sh;
4046964e37dSMax Laier #endif
40522ac3eadSMax Laier 
40622ac3eadSMax Laier 	if (suspended) {
40722ac3eadSMax Laier 		packets_dropped++;
40822ac3eadSMax Laier 		return;
40922ac3eadSMax Laier 	}
41022ac3eadSMax Laier 
4116964e37dSMax Laier #ifdef __FreeBSD__
4126964e37dSMax Laier 	sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec;
4136964e37dSMax Laier 	sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec;
4146964e37dSMax Laier 	sh.caplen = h->caplen;
4156964e37dSMax Laier 	sh.len = h->len;
4166964e37dSMax Laier 
4176964e37dSMax Laier 	if (fwrite((char *)&sh, sizeof(sh), 1, f) != 1) {
4186964e37dSMax Laier #else
41922ac3eadSMax Laier 	if (fwrite((char *)h, sizeof(*h), 1, f) != 1) {
4206964e37dSMax Laier #endif
42122ac3eadSMax Laier 		/* try to undo header to prevent corruption */
42222ac3eadSMax Laier 		off_t pos = ftello(f);
4236964e37dSMax Laier #ifdef __FreeBSD__
4246964e37dSMax Laier 		if (pos < sizeof(sh) ||
4256964e37dSMax Laier 		    ftruncate(fileno(f), pos - sizeof(sh))) {
4266964e37dSMax Laier #else
42722ac3eadSMax Laier 		if (pos < sizeof(*h) ||
42822ac3eadSMax Laier 		    ftruncate(fileno(f), pos - sizeof(*h))) {
4296964e37dSMax Laier #endif
43022ac3eadSMax Laier 			logmsg(LOG_ERR, "Write failed, corrupted logfile!");
43122ac3eadSMax Laier 			set_suspended(1);
43222ac3eadSMax Laier 			gotsig_close = 1;
43322ac3eadSMax Laier 			return;
43422ac3eadSMax Laier 		}
43522ac3eadSMax Laier 		goto error;
43622ac3eadSMax Laier 	}
43722ac3eadSMax Laier 
43822ac3eadSMax Laier 	if (fwrite((char *)sp, h->caplen, 1, f) != 1)
43922ac3eadSMax Laier 		goto error;
44022ac3eadSMax Laier 
44122ac3eadSMax Laier 	return;
44222ac3eadSMax Laier 
44322ac3eadSMax Laier error:
44422ac3eadSMax Laier 	set_suspended(1);
44522ac3eadSMax Laier 	packets_dropped ++;
44622ac3eadSMax Laier 	logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno));
44722ac3eadSMax Laier }
44822ac3eadSMax Laier 
44922ac3eadSMax Laier int
45022ac3eadSMax Laier flush_buffer(FILE *f)
45122ac3eadSMax Laier {
45222ac3eadSMax Laier 	off_t offset;
45322ac3eadSMax Laier 	int len = bufpos - buffer;
45422ac3eadSMax Laier 
45522ac3eadSMax Laier 	if (len <= 0)
45622ac3eadSMax Laier 		return (0);
45722ac3eadSMax Laier 
45822ac3eadSMax Laier 	offset = ftello(f);
45922ac3eadSMax Laier 	if (offset == (off_t)-1) {
46022ac3eadSMax Laier 		set_suspended(1);
46122ac3eadSMax Laier 		logmsg(LOG_ERR, "Logging suspended: ftello: %s",
46222ac3eadSMax Laier 		    strerror(errno));
46322ac3eadSMax Laier 		return (1);
46422ac3eadSMax Laier 	}
46522ac3eadSMax Laier 
46622ac3eadSMax Laier 	if (fwrite(buffer, len, 1, f) != 1) {
46722ac3eadSMax Laier 		set_suspended(1);
46822ac3eadSMax Laier 		logmsg(LOG_ERR, "Logging suspended: fwrite: %s",
46922ac3eadSMax Laier 		    strerror(errno));
47022ac3eadSMax Laier 		ftruncate(fileno(f), offset);
47122ac3eadSMax Laier 		return (1);
47222ac3eadSMax Laier 	}
47322ac3eadSMax Laier 
47422ac3eadSMax Laier 	set_suspended(0);
47522ac3eadSMax Laier 	bufpos = buffer;
47622ac3eadSMax Laier 	bufleft = buflen;
47722ac3eadSMax Laier 	bufpkt = 0;
47822ac3eadSMax Laier 
47922ac3eadSMax Laier 	return (0);
48022ac3eadSMax Laier }
48122ac3eadSMax Laier 
48222ac3eadSMax Laier void
48322ac3eadSMax Laier purge_buffer(void)
48422ac3eadSMax Laier {
48522ac3eadSMax Laier 	packets_dropped += bufpkt;
48622ac3eadSMax Laier 
48722ac3eadSMax Laier 	set_suspended(0);
48822ac3eadSMax Laier 	bufpos = buffer;
48922ac3eadSMax Laier 	bufleft = buflen;
49022ac3eadSMax Laier 	bufpkt = 0;
49122ac3eadSMax Laier }
49222ac3eadSMax Laier 
49322ac3eadSMax Laier /* append packet to the buffer, flushing if necessary */
49422ac3eadSMax Laier void
49522ac3eadSMax Laier dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
49622ac3eadSMax Laier {
49722ac3eadSMax Laier 	FILE *f = (FILE *)user;
4986964e37dSMax Laier #ifdef __FreeBSD__
4996964e37dSMax Laier 	struct pcap_sf_pkthdr sh;
5006964e37dSMax Laier 	size_t len = sizeof(sh) + h->caplen;
5016964e37dSMax Laier #else
50222ac3eadSMax Laier 	size_t len = sizeof(*h) + h->caplen;
5036964e37dSMax Laier #endif
50422ac3eadSMax Laier 
50522ac3eadSMax Laier 	if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) {
50622ac3eadSMax Laier 		logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped",
50722ac3eadSMax Laier 		       len, cur_snaplen, snaplen);
50822ac3eadSMax Laier 		packets_dropped++;
50922ac3eadSMax Laier 		return;
51022ac3eadSMax Laier 	}
51122ac3eadSMax Laier 
51222ac3eadSMax Laier 	if (len <= bufleft)
51322ac3eadSMax Laier 		goto append;
51422ac3eadSMax Laier 
51522ac3eadSMax Laier 	if (suspended) {
51622ac3eadSMax Laier 		packets_dropped++;
51722ac3eadSMax Laier 		return;
51822ac3eadSMax Laier 	}
51922ac3eadSMax Laier 
52022ac3eadSMax Laier 	if (flush_buffer(f)) {
52122ac3eadSMax Laier 		packets_dropped++;
52222ac3eadSMax Laier 		return;
52322ac3eadSMax Laier 	}
52422ac3eadSMax Laier 
52522ac3eadSMax Laier 	if (len > bufleft) {
52622ac3eadSMax Laier 		dump_packet_nobuf(user, h, sp);
52722ac3eadSMax Laier 		return;
52822ac3eadSMax Laier 	}
52922ac3eadSMax Laier 
53022ac3eadSMax Laier  append:
5316964e37dSMax Laier #ifdef __FreeBSD__
5326964e37dSMax Laier  	sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec;
5336964e37dSMax Laier  	sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec;
5346964e37dSMax Laier 	sh.caplen = h->caplen;
5356964e37dSMax Laier 	sh.len = h->len;
5366964e37dSMax Laier 
5376964e37dSMax Laier 	memcpy(bufpos, &sh, sizeof(sh));
5386964e37dSMax Laier 	memcpy(bufpos + sizeof(sh), sp, h->caplen);
5396964e37dSMax Laier #else
54022ac3eadSMax Laier 	memcpy(bufpos, h, sizeof(*h));
54122ac3eadSMax Laier 	memcpy(bufpos + sizeof(*h), sp, h->caplen);
5426964e37dSMax Laier #endif
54322ac3eadSMax Laier 
54422ac3eadSMax Laier 	bufpos += len;
54522ac3eadSMax Laier 	bufleft -= len;
54622ac3eadSMax Laier 	bufpkt++;
54722ac3eadSMax Laier 
54822ac3eadSMax Laier 	return;
54913b9f610SMax Laier }
55013b9f610SMax Laier 
55113b9f610SMax Laier int
55213b9f610SMax Laier main(int argc, char **argv)
55313b9f610SMax Laier {
55413b9f610SMax Laier 	struct pcap_stat pstat;
55522ac3eadSMax Laier 	int ch, np, Xflag = 0;
55622ac3eadSMax Laier 	pcap_handler phandler = dump_packet;
55713b9f610SMax Laier 
55822ac3eadSMax Laier #ifdef __FreeBSD__
55922ac3eadSMax Laier 	/* another ?paranoid? safety measure we do not have */
56022ac3eadSMax Laier #else
56122ac3eadSMax Laier 	closefrom(STDERR_FILENO + 1);
56222ac3eadSMax Laier #endif
56322ac3eadSMax Laier 
56422ac3eadSMax Laier 	while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) {
56513b9f610SMax Laier 		switch (ch) {
56613b9f610SMax Laier 		case 'D':
56713b9f610SMax Laier 			Debug = 1;
56813b9f610SMax Laier 			break;
56913b9f610SMax Laier 		case 'd':
57013b9f610SMax Laier 			delay = atoi(optarg);
57113b9f610SMax Laier 			if (delay < 5 || delay > 60*60)
57213b9f610SMax Laier 				usage();
57313b9f610SMax Laier 			break;
57413b9f610SMax Laier 		case 'f':
57513b9f610SMax Laier 			filename = optarg;
57613b9f610SMax Laier 			break;
57713b9f610SMax Laier 		case 's':
57813b9f610SMax Laier 			snaplen = atoi(optarg);
57913b9f610SMax Laier 			if (snaplen <= 0)
58013b9f610SMax Laier 				snaplen = DEF_SNAPLEN;
58122ac3eadSMax Laier 			if (snaplen > PFLOGD_MAXSNAPLEN)
58222ac3eadSMax Laier 				snaplen = PFLOGD_MAXSNAPLEN;
58322ac3eadSMax Laier 			break;
58422ac3eadSMax Laier 		case 'x':
58522ac3eadSMax Laier 			Xflag++;
58613b9f610SMax Laier 			break;
58713b9f610SMax Laier 		default:
58813b9f610SMax Laier 			usage();
58913b9f610SMax Laier 		}
59013b9f610SMax Laier 
59113b9f610SMax Laier 	}
59213b9f610SMax Laier 
59313b9f610SMax Laier 	log_debug = Debug;
59413b9f610SMax Laier 	argc -= optind;
59513b9f610SMax Laier 	argv += optind;
59613b9f610SMax Laier 
59713b9f610SMax Laier 	if (!Debug) {
59813b9f610SMax Laier 		openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON);
59913b9f610SMax Laier 		if (daemon(0, 0)) {
60013b9f610SMax Laier 			logmsg(LOG_WARNING, "Failed to become daemon: %s",
60113b9f610SMax Laier 			    strerror(errno));
60213b9f610SMax Laier 		}
60313b9f610SMax Laier 		pidfile(NULL);
60413b9f610SMax Laier 	}
60513b9f610SMax Laier 
60613b9f610SMax Laier 	(void)umask(S_IRWXG | S_IRWXO);
60713b9f610SMax Laier 
60822ac3eadSMax Laier 	/* filter will be used by the privileged process */
60922ac3eadSMax Laier 	if (argc) {
61022ac3eadSMax Laier 		filter = copy_argv(argv);
61122ac3eadSMax Laier 		if (filter == NULL)
61222ac3eadSMax Laier 			logmsg(LOG_NOTICE, "Failed to form filter expression");
61322ac3eadSMax Laier 	}
61422ac3eadSMax Laier 
61522ac3eadSMax Laier 	/* initialize pcap before dropping privileges */
61622ac3eadSMax Laier 	if (init_pcap()) {
61722ac3eadSMax Laier 		logmsg(LOG_ERR, "Exiting, init failure");
61822ac3eadSMax Laier 		exit(1);
61922ac3eadSMax Laier 	}
62022ac3eadSMax Laier 
62122ac3eadSMax Laier 	/* Privilege separation begins here */
62222ac3eadSMax Laier 	if (priv_init()) {
62322ac3eadSMax Laier 		logmsg(LOG_ERR, "unable to privsep");
62422ac3eadSMax Laier 		exit(1);
62522ac3eadSMax Laier 	}
62622ac3eadSMax Laier 
62722ac3eadSMax Laier 	setproctitle("[initializing]");
62822ac3eadSMax Laier 	/* Process is now unprivileged and inside a chroot */
62913b9f610SMax Laier 	signal(SIGTERM, sig_close);
63013b9f610SMax Laier 	signal(SIGINT, sig_close);
63113b9f610SMax Laier 	signal(SIGQUIT, sig_close);
63213b9f610SMax Laier 	signal(SIGALRM, sig_alrm);
63313b9f610SMax Laier 	signal(SIGHUP, sig_hup);
63413b9f610SMax Laier 	alarm(delay);
63513b9f610SMax Laier 
63622ac3eadSMax Laier 	buffer = malloc(PFLOGD_BUFSIZE);
63713b9f610SMax Laier 
63822ac3eadSMax Laier 	if (buffer == NULL) {
63922ac3eadSMax Laier 		logmsg(LOG_WARNING, "Failed to allocate output buffer");
64022ac3eadSMax Laier 		phandler = dump_packet_nobuf;
64122ac3eadSMax Laier 	} else {
64222ac3eadSMax Laier 		bufleft = buflen = PFLOGD_BUFSIZE;
64322ac3eadSMax Laier 		bufpos = buffer;
64422ac3eadSMax Laier 		bufpkt = 0;
64513b9f610SMax Laier 	}
64613b9f610SMax Laier 
64713b9f610SMax Laier 	if (reset_dump()) {
64822ac3eadSMax Laier 		if (Xflag)
64922ac3eadSMax Laier 			return (1);
65022ac3eadSMax Laier 
65122ac3eadSMax Laier 		logmsg(LOG_ERR, "Logging suspended: open error");
65222ac3eadSMax Laier 		set_suspended(1);
65322ac3eadSMax Laier 	} else if (Xflag)
65422ac3eadSMax Laier 		return (0);
65513b9f610SMax Laier 
65613b9f610SMax Laier 	while (1) {
65722ac3eadSMax Laier 		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS,
65822ac3eadSMax Laier 		    dump_packet, (u_char *)dpcap);
65922d6889bSMax Laier 		if (np < 0) {
66022d6889bSMax Laier #ifdef __FreeBSD__
66122d6889bSMax Laier 			if (errno == ENXIO) {
66222d6889bSMax Laier 				logmsg(LOG_ERR,
66322d6889bSMax Laier 				    "Device not/no longer configured");
66422d6889bSMax Laier 				break;
66522d6889bSMax Laier 			}
66622d6889bSMax Laier #endif
66713b9f610SMax Laier 			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
66822d6889bSMax Laier 		}
66913b9f610SMax Laier 
67013b9f610SMax Laier 		if (gotsig_close)
67113b9f610SMax Laier 			break;
67213b9f610SMax Laier 		if (gotsig_hup) {
67313b9f610SMax Laier 			if (reset_dump()) {
67422ac3eadSMax Laier 				logmsg(LOG_ERR,
67522ac3eadSMax Laier 				    "Logging suspended: open error");
67622ac3eadSMax Laier 				set_suspended(1);
67713b9f610SMax Laier 			}
67813b9f610SMax Laier 			gotsig_hup = 0;
67913b9f610SMax Laier 		}
68013b9f610SMax Laier 
68113b9f610SMax Laier 		if (gotsig_alrm) {
68222ac3eadSMax Laier 			if (dpcap)
68322ac3eadSMax Laier 				flush_buffer(dpcap);
68413b9f610SMax Laier 			gotsig_alrm = 0;
68513b9f610SMax Laier 			alarm(delay);
68613b9f610SMax Laier 		}
68713b9f610SMax Laier 	}
68813b9f610SMax Laier 
68922ac3eadSMax Laier 	logmsg(LOG_NOTICE, "Exiting");
69022ac3eadSMax Laier 	if (dpcap) {
69122ac3eadSMax Laier 		flush_buffer(dpcap);
69222ac3eadSMax Laier 		fclose(dpcap);
69322ac3eadSMax Laier 	}
69422ac3eadSMax Laier 	purge_buffer();
69513b9f610SMax Laier 
69613b9f610SMax Laier 	if (pcap_stats(hpcap, &pstat) < 0)
69713b9f610SMax Laier 		logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap));
69813b9f610SMax Laier 	else
69922ac3eadSMax Laier 		logmsg(LOG_NOTICE,
70022ac3eadSMax Laier 		    "%u packets received, %u/%u dropped (kernel/pflogd)",
70122ac3eadSMax Laier 		    pstat.ps_recv, pstat.ps_drop, packets_dropped);
70213b9f610SMax Laier 
70313b9f610SMax Laier 	pcap_close(hpcap);
70413b9f610SMax Laier 	if (!Debug)
70513b9f610SMax Laier 		closelog();
70613b9f610SMax Laier 	return (0);
70713b9f610SMax Laier }
708