xref: /freebsd/contrib/pf/pflogd/pflogd.c (revision 8c8618f5e80ad0493d44ec98fd51e0b4a58b2908)
18c8618f5SMax Laier /*	$FreeBSD$	*/
213b9f610SMax Laier /*	$OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david Exp $	*/
313b9f610SMax Laier 
413b9f610SMax Laier /*
513b9f610SMax Laier  * Copyright (c) 2001 Theo de Raadt
613b9f610SMax Laier  * Copyright (c) 2001 Can Erkin Acar
713b9f610SMax Laier  * All rights reserved.
813b9f610SMax Laier  *
913b9f610SMax Laier  * Redistribution and use in source and binary forms, with or without
1013b9f610SMax Laier  * modification, are permitted provided that the following conditions
1113b9f610SMax Laier  * are met:
1213b9f610SMax Laier  *
1313b9f610SMax Laier  *    - Redistributions of source code must retain the above copyright
1413b9f610SMax Laier  *      notice, this list of conditions and the following disclaimer.
1513b9f610SMax Laier  *    - Redistributions in binary form must reproduce the above
1613b9f610SMax Laier  *      copyright notice, this list of conditions and the following
1713b9f610SMax Laier  *      disclaimer in the documentation and/or other materials provided
1813b9f610SMax Laier  *      with the distribution.
1913b9f610SMax Laier  *
2013b9f610SMax Laier  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2113b9f610SMax Laier  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2213b9f610SMax Laier  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2313b9f610SMax Laier  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2413b9f610SMax Laier  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2513b9f610SMax Laier  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2613b9f610SMax Laier  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2713b9f610SMax Laier  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2813b9f610SMax Laier  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2913b9f610SMax Laier  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3013b9f610SMax Laier  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3113b9f610SMax Laier  * POSSIBILITY OF SUCH DAMAGE.
3213b9f610SMax Laier  */
3313b9f610SMax Laier 
3413b9f610SMax Laier #include <sys/types.h>
3513b9f610SMax Laier #include <sys/file.h>
3613b9f610SMax Laier #include <sys/stat.h>
3713b9f610SMax Laier #include <stdio.h>
3813b9f610SMax Laier #include <stdlib.h>
3913b9f610SMax Laier #include <string.h>
4013b9f610SMax Laier #include <unistd.h>
4113b9f610SMax Laier #include <pcap-int.h>
4213b9f610SMax Laier #include <pcap.h>
4313b9f610SMax Laier #include <syslog.h>
4413b9f610SMax Laier #include <signal.h>
4513b9f610SMax Laier #include <errno.h>
4613b9f610SMax Laier #include <stdarg.h>
4713b9f610SMax Laier #include <fcntl.h>
488c8618f5SMax Laier #if defined(__FreeBSD__)
498c8618f5SMax Laier #include "pidfile.h"
508c8618f5SMax Laier #else
5113b9f610SMax Laier #include <util.h>
528c8618f5SMax Laier #endif
538c8618f5SMax Laier 
548c8618f5SMax Laier #if defined(__FreeBSD__)
558c8618f5SMax Laier #define __dead		__volatile
568c8618f5SMax Laier #endif
5713b9f610SMax Laier 
5813b9f610SMax Laier #define DEF_SNAPLEN 116		/* default plus allow for larger header of pflog */
5913b9f610SMax Laier #define PCAP_TO_MS 500		/* pcap read timeout (ms) */
6013b9f610SMax Laier #define PCAP_NUM_PKTS 1000	/* max number of packets to process at each loop */
6113b9f610SMax Laier #define PCAP_OPT_FIL 0		/* filter optimization */
6213b9f610SMax Laier #define FLUSH_DELAY 60		/* flush delay */
6313b9f610SMax Laier 
6413b9f610SMax Laier #define PFLOGD_LOG_FILE		"/var/log/pflog"
6513b9f610SMax Laier #define PFLOGD_DEFAULT_IF	"pflog0"
6613b9f610SMax Laier 
6713b9f610SMax Laier pcap_t *hpcap;
6813b9f610SMax Laier pcap_dumper_t *dpcap;
6913b9f610SMax Laier 
7013b9f610SMax Laier int Debug = 0;
7113b9f610SMax Laier int snaplen = DEF_SNAPLEN;
7213b9f610SMax Laier 
7313b9f610SMax Laier volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup;
7413b9f610SMax Laier 
7513b9f610SMax Laier char *filename = PFLOGD_LOG_FILE;
7613b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF;
7713b9f610SMax Laier char *filter = NULL;
7813b9f610SMax Laier 
7913b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE];
8013b9f610SMax Laier 
8113b9f610SMax Laier int log_debug = 0;
8213b9f610SMax Laier unsigned int delay = FLUSH_DELAY;
8313b9f610SMax Laier 
8413b9f610SMax Laier char *copy_argv(char * const *argv);
8513b9f610SMax Laier int   init_pcap(void);
8613b9f610SMax Laier void  logmsg(int priority, const char *message, ...);
8713b9f610SMax Laier int   reset_dump(void);
8813b9f610SMax Laier void  sig_alrm(int);
8913b9f610SMax Laier void  sig_close(int);
9013b9f610SMax Laier void  sig_hup(int);
918c8618f5SMax Laier #if defined(__FreeBSD__)
928c8618f5SMax Laier __volatile void  usage(void);
938c8618f5SMax Laier #else
9413b9f610SMax Laier void  usage(void);
958c8618f5SMax Laier #endif
9613b9f610SMax Laier 
9713b9f610SMax Laier 
9813b9f610SMax Laier char *
9913b9f610SMax Laier copy_argv(char * const *argv)
10013b9f610SMax Laier {
10113b9f610SMax Laier 	size_t len = 0, n;
10213b9f610SMax Laier 	char *buf;
10313b9f610SMax Laier 
10413b9f610SMax Laier 	if (argv == NULL)
10513b9f610SMax Laier 		return (NULL);
10613b9f610SMax Laier 
10713b9f610SMax Laier 	for (n = 0; argv[n]; n++)
10813b9f610SMax Laier 		len += strlen(argv[n])+1;
10913b9f610SMax Laier 	if (len == 0)
11013b9f610SMax Laier 		return (NULL);
11113b9f610SMax Laier 
11213b9f610SMax Laier 	buf = malloc(len);
11313b9f610SMax Laier 	if (buf == NULL)
11413b9f610SMax Laier 		return (NULL);
11513b9f610SMax Laier 
11613b9f610SMax Laier 	strlcpy(buf, argv[0], len);
11713b9f610SMax Laier 	for (n = 1; argv[n]; n++) {
11813b9f610SMax Laier 		strlcat(buf, " ", len);
11913b9f610SMax Laier 		strlcat(buf, argv[n], len);
12013b9f610SMax Laier 	}
12113b9f610SMax Laier 	return (buf);
12213b9f610SMax Laier }
12313b9f610SMax Laier 
12413b9f610SMax Laier void
12513b9f610SMax Laier logmsg(int pri, const char *message, ...)
12613b9f610SMax Laier {
12713b9f610SMax Laier 	va_list ap;
12813b9f610SMax Laier 	va_start(ap, message);
12913b9f610SMax Laier 
13013b9f610SMax Laier 	if (log_debug) {
13113b9f610SMax Laier 		vfprintf(stderr, message, ap);
13213b9f610SMax Laier 		fprintf(stderr, "\n");
13313b9f610SMax Laier 	} else
13413b9f610SMax Laier 		vsyslog(pri, message, ap);
13513b9f610SMax Laier 	va_end(ap);
13613b9f610SMax Laier }
13713b9f610SMax Laier 
13813b9f610SMax Laier __dead void
13913b9f610SMax Laier usage(void)
14013b9f610SMax Laier {
14113b9f610SMax Laier 	fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] ");
14213b9f610SMax Laier 	fprintf(stderr, "[-s snaplen] [expression]\n");
14313b9f610SMax Laier 	exit(1);
14413b9f610SMax Laier }
14513b9f610SMax Laier 
14613b9f610SMax Laier void
14713b9f610SMax Laier sig_close(int sig)
14813b9f610SMax Laier {
14913b9f610SMax Laier 	gotsig_close = 1;
15013b9f610SMax Laier }
15113b9f610SMax Laier 
15213b9f610SMax Laier void
15313b9f610SMax Laier sig_hup(int sig)
15413b9f610SMax Laier {
15513b9f610SMax Laier 	gotsig_hup = 1;
15613b9f610SMax Laier }
15713b9f610SMax Laier 
15813b9f610SMax Laier void
15913b9f610SMax Laier sig_alrm(int sig)
16013b9f610SMax Laier {
16113b9f610SMax Laier 	gotsig_alrm = 1;
16213b9f610SMax Laier }
16313b9f610SMax Laier 
16413b9f610SMax Laier int
16513b9f610SMax Laier init_pcap(void)
16613b9f610SMax Laier {
16713b9f610SMax Laier 	struct bpf_program bprog;
16813b9f610SMax Laier 	pcap_t *oldhpcap = hpcap;
16913b9f610SMax Laier 
17013b9f610SMax Laier 	hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);
17113b9f610SMax Laier 	if (hpcap == NULL) {
17213b9f610SMax Laier 		logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
17313b9f610SMax Laier 		hpcap = oldhpcap;
17413b9f610SMax Laier 		return (-1);
17513b9f610SMax Laier 	}
17613b9f610SMax Laier 
17713b9f610SMax Laier 	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0)
17813b9f610SMax Laier 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
17913b9f610SMax Laier 	else if (pcap_setfilter(hpcap, &bprog) < 0)
18013b9f610SMax Laier 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
18113b9f610SMax Laier 	if (filter != NULL)
18213b9f610SMax Laier 		free(filter);
18313b9f610SMax Laier 
18413b9f610SMax Laier 	if (pcap_datalink(hpcap) != DLT_PFLOG) {
18513b9f610SMax Laier 		logmsg(LOG_ERR, "Invalid datalink type");
18613b9f610SMax Laier 		pcap_close(hpcap);
18713b9f610SMax Laier 		hpcap = oldhpcap;
18813b9f610SMax Laier 		return (-1);
18913b9f610SMax Laier 	}
19013b9f610SMax Laier 
19113b9f610SMax Laier 	if (oldhpcap)
19213b9f610SMax Laier 		pcap_close(oldhpcap);
19313b9f610SMax Laier 
19413b9f610SMax Laier 	snaplen = pcap_snapshot(hpcap);
19513b9f610SMax Laier 	return (0);
19613b9f610SMax Laier }
19713b9f610SMax Laier 
19813b9f610SMax Laier int
19913b9f610SMax Laier reset_dump(void)
20013b9f610SMax Laier {
20113b9f610SMax Laier 	struct pcap_file_header hdr;
20213b9f610SMax Laier 	struct stat st;
20313b9f610SMax Laier 	int tmpsnap;
20413b9f610SMax Laier 	FILE *fp;
20513b9f610SMax Laier 
20613b9f610SMax Laier 	if (hpcap == NULL)
20713b9f610SMax Laier 		return (1);
20813b9f610SMax Laier 	if (dpcap) {
20913b9f610SMax Laier 		pcap_dump_close(dpcap);
21013b9f610SMax Laier 		dpcap = 0;
21113b9f610SMax Laier 	}
21213b9f610SMax Laier 
21313b9f610SMax Laier 	/*
21413b9f610SMax Laier 	 * Basically reimplement pcap_dump_open() because it truncates
21513b9f610SMax Laier 	 * files and duplicates headers and such.
21613b9f610SMax Laier 	 */
21713b9f610SMax Laier 	fp = fopen(filename, "a+");
21813b9f610SMax Laier 	if (fp == NULL) {
21913b9f610SMax Laier 		snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
22013b9f610SMax Laier 		    filename, pcap_strerror(errno));
22113b9f610SMax Laier 		logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap));
22213b9f610SMax Laier 		return (1);
22313b9f610SMax Laier 	}
22413b9f610SMax Laier 	if (fstat(fileno(fp), &st) == -1) {
22513b9f610SMax Laier 		snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
22613b9f610SMax Laier 		    filename, pcap_strerror(errno));
22713b9f610SMax Laier 		logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap));
22813b9f610SMax Laier 		return (1);
22913b9f610SMax Laier 	}
23013b9f610SMax Laier 
23113b9f610SMax Laier 	dpcap = (pcap_dumper_t *)fp;
23213b9f610SMax Laier 
23313b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4
23413b9f610SMax Laier 
23513b9f610SMax Laier 	if (st.st_size == 0) {
23613b9f610SMax Laier 		if (snaplen != pcap_snapshot(hpcap)) {
23713b9f610SMax Laier 			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
23813b9f610SMax Laier 			if (init_pcap()) {
23913b9f610SMax Laier 				logmsg(LOG_ERR, "Failed to initialize");
24013b9f610SMax Laier 				if (hpcap == NULL) return (-1);
24113b9f610SMax Laier 				logmsg(LOG_NOTICE, "Using old settings");
24213b9f610SMax Laier 			}
24313b9f610SMax Laier 		}
24413b9f610SMax Laier 		hdr.magic = TCPDUMP_MAGIC;
24513b9f610SMax Laier 		hdr.version_major = PCAP_VERSION_MAJOR;
24613b9f610SMax Laier 		hdr.version_minor = PCAP_VERSION_MINOR;
24713b9f610SMax Laier 		hdr.thiszone = hpcap->tzoff;
24813b9f610SMax Laier 		hdr.snaplen = hpcap->snapshot;
24913b9f610SMax Laier 		hdr.sigfigs = 0;
25013b9f610SMax Laier 		hdr.linktype = hpcap->linktype;
25113b9f610SMax Laier 
25213b9f610SMax Laier 		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
25313b9f610SMax Laier 			dpcap = NULL;
25413b9f610SMax Laier 			fclose(fp);
25513b9f610SMax Laier 			return (-1);
25613b9f610SMax Laier 		}
25713b9f610SMax Laier 		return (0);
25813b9f610SMax Laier 	}
25913b9f610SMax Laier 
26013b9f610SMax Laier 	/*
26113b9f610SMax Laier 	 * XXX Must read the file, compare the header against our new
26213b9f610SMax Laier 	 * options (in particular, snaplen) and adjust our options so
26313b9f610SMax Laier 	 * that we generate a correct file.
26413b9f610SMax Laier 	 */
26513b9f610SMax Laier 	(void) fseek(fp, 0L, SEEK_SET);
26613b9f610SMax Laier 	if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) {
26713b9f610SMax Laier 		if (hdr.magic != TCPDUMP_MAGIC ||
26813b9f610SMax Laier 		    hdr.version_major != PCAP_VERSION_MAJOR ||
26913b9f610SMax Laier 		    hdr.version_minor != PCAP_VERSION_MINOR ||
27013b9f610SMax Laier 		    hdr.linktype != hpcap->linktype) {
27113b9f610SMax Laier 			logmsg(LOG_ERR,
27213b9f610SMax Laier 			    "Invalid/incompatible log file, move it away");
27313b9f610SMax Laier 			fclose(fp);
27413b9f610SMax Laier 			return (1);
27513b9f610SMax Laier 		    }
27613b9f610SMax Laier 		if (hdr.snaplen != snaplen) {
27713b9f610SMax Laier 			logmsg(LOG_WARNING,
27813b9f610SMax Laier 			    "Existing file specifies a snaplen of %u, using it",
27913b9f610SMax Laier 			    hdr.snaplen);
28013b9f610SMax Laier 			tmpsnap = snaplen;
28113b9f610SMax Laier 			snaplen = hdr.snaplen;
28213b9f610SMax Laier 			if (init_pcap()) {
28313b9f610SMax Laier 				logmsg(LOG_ERR, "Failed to re-initialize");
28413b9f610SMax Laier 				if (hpcap == 0)
28513b9f610SMax Laier 					return (-1);
28613b9f610SMax Laier 				logmsg(LOG_NOTICE,
28713b9f610SMax Laier 					"Using old settings, offset: %llu",
28813b9f610SMax Laier 					(unsigned long long)st.st_size);
28913b9f610SMax Laier 			}
29013b9f610SMax Laier 			snaplen = tmpsnap;
29113b9f610SMax Laier 		}
29213b9f610SMax Laier 	}
29313b9f610SMax Laier 
29413b9f610SMax Laier 	(void) fseek(fp, 0L, SEEK_END);
29513b9f610SMax Laier 	return (0);
29613b9f610SMax Laier }
29713b9f610SMax Laier 
29813b9f610SMax Laier int
29913b9f610SMax Laier main(int argc, char **argv)
30013b9f610SMax Laier {
30113b9f610SMax Laier 	struct pcap_stat pstat;
30213b9f610SMax Laier 	int ch, np;
30313b9f610SMax Laier 
30413b9f610SMax Laier 	while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) {
30513b9f610SMax Laier 		switch (ch) {
30613b9f610SMax Laier 		case 'D':
30713b9f610SMax Laier 			Debug = 1;
30813b9f610SMax Laier 			break;
30913b9f610SMax Laier 		case 'd':
31013b9f610SMax Laier 			delay = atoi(optarg);
31113b9f610SMax Laier 			if (delay < 5 || delay > 60*60)
31213b9f610SMax Laier 				usage();
31313b9f610SMax Laier 			break;
31413b9f610SMax Laier 		case 'f':
31513b9f610SMax Laier 			filename = optarg;
31613b9f610SMax Laier 			break;
31713b9f610SMax Laier 		case 's':
31813b9f610SMax Laier 			snaplen = atoi(optarg);
31913b9f610SMax Laier 			if (snaplen <= 0)
32013b9f610SMax Laier 				snaplen = DEF_SNAPLEN;
32113b9f610SMax Laier 			break;
32213b9f610SMax Laier 		default:
32313b9f610SMax Laier 			usage();
32413b9f610SMax Laier 		}
32513b9f610SMax Laier 
32613b9f610SMax Laier 	}
32713b9f610SMax Laier 
32813b9f610SMax Laier 	log_debug = Debug;
32913b9f610SMax Laier 	argc -= optind;
33013b9f610SMax Laier 	argv += optind;
33113b9f610SMax Laier 
33213b9f610SMax Laier 	if (!Debug) {
33313b9f610SMax Laier 		openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON);
33413b9f610SMax Laier 		if (daemon(0, 0)) {
33513b9f610SMax Laier 			logmsg(LOG_WARNING, "Failed to become daemon: %s",
33613b9f610SMax Laier 			    strerror(errno));
33713b9f610SMax Laier 		}
33813b9f610SMax Laier 		pidfile(NULL);
33913b9f610SMax Laier 	}
34013b9f610SMax Laier 
34113b9f610SMax Laier 	(void)umask(S_IRWXG | S_IRWXO);
34213b9f610SMax Laier 
34313b9f610SMax Laier 	signal(SIGTERM, sig_close);
34413b9f610SMax Laier 	signal(SIGINT, sig_close);
34513b9f610SMax Laier 	signal(SIGQUIT, sig_close);
34613b9f610SMax Laier 	signal(SIGALRM, sig_alrm);
34713b9f610SMax Laier 	signal(SIGHUP, sig_hup);
34813b9f610SMax Laier 	alarm(delay);
34913b9f610SMax Laier 
35013b9f610SMax Laier 	if (argc) {
35113b9f610SMax Laier 		filter = copy_argv(argv);
35213b9f610SMax Laier 		if (filter == NULL)
35313b9f610SMax Laier 			logmsg(LOG_NOTICE, "Failed to form filter expression");
35413b9f610SMax Laier 	}
35513b9f610SMax Laier 
35613b9f610SMax Laier 	if (init_pcap()) {
35713b9f610SMax Laier 		logmsg(LOG_ERR, "Exiting, init failure");
35813b9f610SMax Laier 		exit(1);
35913b9f610SMax Laier 	}
36013b9f610SMax Laier 
36113b9f610SMax Laier 	if (reset_dump()) {
36213b9f610SMax Laier 		logmsg(LOG_ERR, "Failed to open log file %s", filename);
36313b9f610SMax Laier 		pcap_close(hpcap);
36413b9f610SMax Laier 		exit(1);
36513b9f610SMax Laier 	}
36613b9f610SMax Laier 
36713b9f610SMax Laier 	while (1) {
36813b9f610SMax Laier 		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap);
36913b9f610SMax Laier 		if (np < 0)
37013b9f610SMax Laier 			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
37113b9f610SMax Laier 
37213b9f610SMax Laier 		if (gotsig_close)
37313b9f610SMax Laier 			break;
37413b9f610SMax Laier 		if (gotsig_hup) {
37513b9f610SMax Laier 			if (reset_dump()) {
37613b9f610SMax Laier 				logmsg(LOG_ERR, "Failed to open log file!");
37713b9f610SMax Laier 				break;
37813b9f610SMax Laier 			}
37913b9f610SMax Laier 			logmsg(LOG_NOTICE, "Reopened logfile");
38013b9f610SMax Laier 			gotsig_hup = 0;
38113b9f610SMax Laier 		}
38213b9f610SMax Laier 
38313b9f610SMax Laier 		if (gotsig_alrm) {
38413b9f610SMax Laier 			/* XXX pcap_dumper is an incomplete type which libpcap
38513b9f610SMax Laier 			 * casts to a FILE* currently.  For now it is safe to
38613b9f610SMax Laier 			 * make the same assumption, however this may change
38713b9f610SMax Laier 			 * in the future.
38813b9f610SMax Laier 			 */
38913b9f610SMax Laier 			if (dpcap) {
39013b9f610SMax Laier 				if (fflush((FILE *)dpcap) == EOF) {
39113b9f610SMax Laier 					break;
39213b9f610SMax Laier 				}
39313b9f610SMax Laier 			}
39413b9f610SMax Laier 			gotsig_alrm = 0;
39513b9f610SMax Laier 			alarm(delay);
39613b9f610SMax Laier 		}
39713b9f610SMax Laier 	}
39813b9f610SMax Laier 
39913b9f610SMax Laier 	logmsg(LOG_NOTICE, "Exiting due to signal");
40013b9f610SMax Laier 	if (dpcap)
40113b9f610SMax Laier 		pcap_dump_close(dpcap);
40213b9f610SMax Laier 
40313b9f610SMax Laier 	if (pcap_stats(hpcap, &pstat) < 0)
40413b9f610SMax Laier 		logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap));
40513b9f610SMax Laier 	else
40613b9f610SMax Laier 		logmsg(LOG_NOTICE, "%u packets received, %u dropped",
40713b9f610SMax Laier 		    pstat.ps_recv, pstat.ps_drop);
40813b9f610SMax Laier 
40913b9f610SMax Laier 	pcap_close(hpcap);
41013b9f610SMax Laier 	if (!Debug)
41113b9f610SMax Laier 		closelog();
41213b9f610SMax Laier 	return (0);
41313b9f610SMax Laier }
414