xref: /freebsd/contrib/pf/pflogd/pflogd.c (revision e0c4386e)
1 /*	$OpenBSD: pflogd.c,v 1.46 2008/10/22 08:16:49 henning Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 Theo de Raadt
5  * Copyright (c) 2001 Can Erkin Acar
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  *    - Redistributions of source code must retain the above copyright
13  *      notice, this list of conditions and the following disclaimer.
14  *    - Redistributions in binary form must reproduce the above
15  *      copyright notice, this list of conditions and the following
16  *      disclaimer in the documentation and/or other materials provided
17  *      with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/file.h>
36 #include <sys/stat.h>
37 #include <sys/socket.h>
38 #include <net/if.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <pcap-int.h>
44 #include <pcap.h>
45 #include <syslog.h>
46 #include <signal.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <stdarg.h>
50 #include <fcntl.h>
51 #ifdef __FreeBSD__
52 #include <ifaddrs.h>
53 #include "pidfile.h"
54 #else
55 #include <util.h>
56 #endif
57 #include "pflogd.h"
58 
59 pcap_t *hpcap;
60 static FILE *dpcap;
61 
62 int Debug = 0;
63 static int snaplen = DEF_SNAPLEN;
64 static int cur_snaplen = DEF_SNAPLEN;
65 
66 volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup, gotsig_usr1;
67 
68 char *filename = PFLOGD_LOG_FILE;
69 char *interface = PFLOGD_DEFAULT_IF;
70 char *filter = NULL;
71 
72 char errbuf[PCAP_ERRBUF_SIZE];
73 
74 int log_debug = 0;
75 unsigned int delay = FLUSH_DELAY;
76 
77 char *copy_argv(char * const *);
78 void  dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
79 void  dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *);
80 void  log_pcap_stats(void);
81 int   flush_buffer(FILE *);
82 int   if_exists(char *);
83 int   init_pcap(void);
84 void  logmsg(int, const char *, ...);
85 void  purge_buffer(void);
86 int   reset_dump(int);
87 int   scan_dump(FILE *, off_t);
88 int   set_snaplen(int);
89 void  set_suspended(int);
90 void  sig_alrm(int);
91 void  sig_usr1(int);
92 void  sig_close(int);
93 void  sig_hup(int);
94 void  usage(void);
95 
96 static int try_reset_dump(int);
97 
98 /* buffer must always be greater than snaplen */
99 static int    bufpkt = 0;	/* number of packets in buffer */
100 static int    buflen = 0;	/* allocated size of buffer */
101 static char  *buffer = NULL;	/* packet buffer */
102 static char  *bufpos = NULL;	/* position in buffer */
103 static int    bufleft = 0;	/* bytes left in buffer */
104 
105 /* if error, stop logging but count dropped packets */
106 static int suspended = -1;
107 static long packets_dropped = 0;
108 
109 void
110 set_suspended(int s)
111 {
112 	if (suspended == s)
113 		return;
114 
115 	suspended = s;
116 	setproctitle("[%s] -s %d -i %s -f %s",
117 	    suspended ? "suspended" : "running",
118 	    cur_snaplen, interface, filename);
119 }
120 
121 char *
122 copy_argv(char * const *argv)
123 {
124 	size_t len = 0, n;
125 	char *buf;
126 
127 	if (argv == NULL)
128 		return (NULL);
129 
130 	for (n = 0; argv[n]; n++)
131 		len += strlen(argv[n])+1;
132 	if (len == 0)
133 		return (NULL);
134 
135 	buf = malloc(len);
136 	if (buf == NULL)
137 		return (NULL);
138 
139 	strlcpy(buf, argv[0], len);
140 	for (n = 1; argv[n]; n++) {
141 		strlcat(buf, " ", len);
142 		strlcat(buf, argv[n], len);
143 	}
144 	return (buf);
145 }
146 
147 void
148 logmsg(int pri, const char *message, ...)
149 {
150 	va_list ap;
151 	va_start(ap, message);
152 
153 	if (log_debug) {
154 		vfprintf(stderr, message, ap);
155 		fprintf(stderr, "\n");
156 	} else
157 		vsyslog(pri, message, ap);
158 	va_end(ap);
159 }
160 
161 #ifdef __FreeBSD__
162 __dead2 void
163 #else
164 __dead void
165 #endif
166 usage(void)
167 {
168 	fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename]");
169 	fprintf(stderr, " [-i interface] [-p pidfile]\n");
170 	fprintf(stderr, "              [-s snaplen] [expression]\n");
171 	exit(1);
172 }
173 
174 void
175 sig_close(int sig)
176 {
177 	gotsig_close = 1;
178 }
179 
180 void
181 sig_hup(int sig)
182 {
183 	gotsig_hup = 1;
184 }
185 
186 void
187 sig_alrm(int sig)
188 {
189 	gotsig_alrm = 1;
190 }
191 
192 void
193 sig_usr1(int sig)
194 {
195 	gotsig_usr1 = 1;
196 }
197 
198 void
199 set_pcap_filter(void)
200 {
201 	struct bpf_program bprog;
202 
203 	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0)
204 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
205 	else {
206 		if (pcap_setfilter(hpcap, &bprog) < 0)
207 			logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
208 		pcap_freecode(&bprog);
209 	}
210 }
211 
212 int
213 if_exists(char *ifname)
214 {
215 #ifdef __FreeBSD__
216 	struct ifaddrs *ifdata, *mb;
217 	int exists = 0;
218 
219 	getifaddrs(&ifdata);
220         if (ifdata == NULL)
221 		return (0);
222 
223 	for (mb = ifdata; mb != NULL; mb = mb->ifa_next) {
224 		if (mb == NULL)
225 			continue;
226 		if (strlen(ifname) != strlen(mb->ifa_name))
227 			continue;
228 		if (strncmp(ifname, mb->ifa_name, strlen(ifname)) != 0)
229 			continue;
230 		exists = 1;
231 		break;
232 	}
233 	freeifaddrs(ifdata);
234 
235 	return (exists);
236 #else
237 	int s;
238 	struct ifreq ifr;
239 	struct if_data ifrdat;
240 
241 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
242 		err(1, "socket");
243 	bzero(&ifr, sizeof(ifr));
244 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
245 		sizeof(ifr.ifr_name))
246 			errx(1, "main ifr_name: strlcpy");
247 	ifr.ifr_data = (caddr_t)&ifrdat;
248 	if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1)
249 		return (0);
250 	if (close(s))
251 		err(1, "close");
252 
253 	return (1);
254 #endif
255 }
256 
257 int
258 init_pcap(void)
259 {
260 	hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);
261 	if (hpcap == NULL) {
262 		logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
263 		return (-1);
264 	}
265 
266 	if (pcap_datalink(hpcap) != DLT_PFLOG) {
267 		logmsg(LOG_ERR, "Invalid datalink type");
268 		pcap_close(hpcap);
269 		hpcap = NULL;
270 		return (-1);
271 	}
272 
273 	set_pcap_filter();
274 
275 	cur_snaplen = snaplen = pcap_snapshot(hpcap);
276 
277 	/* lock */
278 	if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) {
279 		logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno));
280 		return (-1);
281 	}
282 
283 	return (0);
284 }
285 
286 int
287 set_snaplen(int snap)
288 {
289 	if (priv_set_snaplen(snap))
290 		return (1);
291 
292 	if (cur_snaplen > snap)
293 		purge_buffer();
294 
295 	cur_snaplen = snap;
296 
297 	return (0);
298 }
299 
300 int
301 reset_dump(int nomove)
302 {
303 	int ret;
304 
305 	for (;;) {
306 		ret = try_reset_dump(nomove);
307 		if (ret <= 0)
308 			break;
309 	}
310 
311 	return (ret);
312 }
313 
314 /*
315  * tries to (re)open log file, nomove flag is used with -x switch
316  * returns 0: success, 1: retry (log moved), -1: error
317  */
318 int
319 try_reset_dump(int nomove)
320 {
321 	struct pcap_file_header hdr;
322 	struct stat st;
323 	int fd;
324 	FILE *fp;
325 
326 	if (hpcap == NULL)
327 		return (-1);
328 
329 	if (dpcap) {
330 		flush_buffer(dpcap);
331 		fclose(dpcap);
332 		dpcap = NULL;
333 	}
334 
335 	/*
336 	 * Basically reimplement pcap_dump_open() because it truncates
337 	 * files and duplicates headers and such.
338 	 */
339 	fd = priv_open_log();
340 	if (fd < 0)
341 		return (-1);
342 
343 	fp = fdopen(fd, "a+");
344 
345 	if (fp == NULL) {
346 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
347 		close(fd);
348 		return (-1);
349 	}
350 	if (fstat(fileno(fp), &st) == -1) {
351 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
352 		fclose(fp);
353 		return (-1);
354 	}
355 
356 	/* set FILE unbuffered, we do our own buffering */
357 	if (setvbuf(fp, NULL, _IONBF, 0)) {
358 		logmsg(LOG_ERR, "Failed to set output buffers");
359 		fclose(fp);
360 		return (-1);
361 	}
362 
363 #define TCPDUMP_MAGIC 0xa1b2c3d4
364 
365 	if (st.st_size == 0) {
366 		if (snaplen != cur_snaplen) {
367 			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
368 			if (set_snaplen(snaplen))
369 				logmsg(LOG_WARNING,
370 				    "Failed, using old settings");
371 		}
372 		hdr.magic = TCPDUMP_MAGIC;
373 		hdr.version_major = PCAP_VERSION_MAJOR;
374 		hdr.version_minor = PCAP_VERSION_MINOR;
375 		hdr.thiszone = 0;
376 		hdr.snaplen = hpcap->snapshot;
377 		hdr.sigfigs = 0;
378 		hdr.linktype = hpcap->linktype;
379 
380 		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
381 			fclose(fp);
382 			return (-1);
383 		}
384 	} else if (scan_dump(fp, st.st_size)) {
385 		fclose(fp);
386 		if (nomove || priv_move_log()) {
387 			logmsg(LOG_ERR,
388 			    "Invalid/incompatible log file, move it away");
389 			return (-1);
390 		}
391 		return (1);
392 	}
393 
394 	dpcap = fp;
395 
396 	set_suspended(0);
397 	flush_buffer(fp);
398 
399 	return (0);
400 }
401 
402 int
403 scan_dump(FILE *fp, off_t size)
404 {
405 	struct pcap_file_header hdr;
406 #ifdef __FreeBSD__
407 	struct pcap_sf_pkthdr ph;
408 #else
409 	struct pcap_pkthdr ph;
410 #endif
411 	off_t pos;
412 
413 	/*
414 	 * Must read the file, compare the header against our new
415 	 * options (in particular, snaplen) and adjust our options so
416 	 * that we generate a correct file. Furthermore, check the file
417 	 * for consistency so that we can append safely.
418 	 *
419 	 * XXX this may take a long time for large logs.
420 	 */
421 	(void) fseek(fp, 0L, SEEK_SET);
422 
423 	if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
424 		logmsg(LOG_ERR, "Short file header");
425 		return (1);
426 	}
427 
428 	if (hdr.magic != TCPDUMP_MAGIC ||
429 	    hdr.version_major != PCAP_VERSION_MAJOR ||
430 	    hdr.version_minor != PCAP_VERSION_MINOR ||
431 	    hdr.linktype != hpcap->linktype ||
432 	    hdr.snaplen > PFLOGD_MAXSNAPLEN) {
433 		return (1);
434 	}
435 
436 	pos = sizeof(hdr);
437 
438 	while (!feof(fp)) {
439 		off_t len = fread((char *)&ph, 1, sizeof(ph), fp);
440 		if (len == 0)
441 			break;
442 
443 		if (len != sizeof(ph))
444 			goto error;
445 		if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN)
446 			goto error;
447 		pos += sizeof(ph) + ph.caplen;
448 		if (pos > size)
449 			goto error;
450 		fseek(fp, ph.caplen, SEEK_CUR);
451 	}
452 
453 	if (pos != size)
454 		goto error;
455 
456 	if (hdr.snaplen != cur_snaplen) {
457 		logmsg(LOG_WARNING,
458 		       "Existing file has different snaplen %u, using it",
459 		       hdr.snaplen);
460 		if (set_snaplen(hdr.snaplen)) {
461 			logmsg(LOG_WARNING,
462 			       "Failed, using old settings, offset %llu",
463 			       (unsigned long long) size);
464 		}
465 	}
466 
467 	return (0);
468 
469  error:
470 	logmsg(LOG_ERR, "Corrupted log file.");
471 	return (1);
472 }
473 
474 /* dump a packet directly to the stream, which is unbuffered */
475 void
476 dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
477 {
478 	FILE *f = (FILE *)user;
479 #ifdef __FreeBSD__
480 	struct pcap_sf_pkthdr sh;
481 #endif
482 
483 	if (suspended) {
484 		packets_dropped++;
485 		return;
486 	}
487 
488 #ifdef __FreeBSD__
489 	sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec;
490 	sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec;
491 	sh.caplen = h->caplen;
492 	sh.len = h->len;
493 
494 	if (fwrite((char *)&sh, sizeof(sh), 1, f) != 1) {
495 #else
496 	if (fwrite((char *)h, sizeof(*h), 1, f) != 1) {
497 #endif
498 		off_t pos = ftello(f);
499 
500 		/* try to undo header to prevent corruption */
501 #ifdef __FreeBSD__
502 		if (pos < sizeof(sh) ||
503 		    ftruncate(fileno(f), pos - sizeof(sh))) {
504 #else
505 		if (pos < sizeof(*h) ||
506 		    ftruncate(fileno(f), pos - sizeof(*h))) {
507 #endif
508 			logmsg(LOG_ERR, "Write failed, corrupted logfile!");
509 			set_suspended(1);
510 			gotsig_close = 1;
511 			return;
512 		}
513 		goto error;
514 	}
515 
516 	if (fwrite((char *)sp, h->caplen, 1, f) != 1)
517 		goto error;
518 
519 	return;
520 
521 error:
522 	set_suspended(1);
523 	packets_dropped ++;
524 	logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno));
525 }
526 
527 int
528 flush_buffer(FILE *f)
529 {
530 	off_t offset;
531 	int len = bufpos - buffer;
532 
533 	if (len <= 0)
534 		return (0);
535 
536 	offset = ftello(f);
537 	if (offset == (off_t)-1) {
538 		set_suspended(1);
539 		logmsg(LOG_ERR, "Logging suspended: ftello: %s",
540 		    strerror(errno));
541 		return (1);
542 	}
543 
544 	if (fwrite(buffer, len, 1, f) != 1) {
545 		set_suspended(1);
546 		logmsg(LOG_ERR, "Logging suspended: fwrite: %s",
547 		    strerror(errno));
548 		ftruncate(fileno(f), offset);
549 		return (1);
550 	}
551 
552 	set_suspended(0);
553 	bufpos = buffer;
554 	bufleft = buflen;
555 	bufpkt = 0;
556 
557 	return (0);
558 }
559 
560 void
561 purge_buffer(void)
562 {
563 	packets_dropped += bufpkt;
564 
565 	set_suspended(0);
566 	bufpos = buffer;
567 	bufleft = buflen;
568 	bufpkt = 0;
569 }
570 
571 /* append packet to the buffer, flushing if necessary */
572 void
573 dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
574 {
575 	FILE *f = (FILE *)user;
576 #ifdef __FreeBSD__
577 	struct pcap_sf_pkthdr sh;
578 	size_t len = sizeof(sh) + h->caplen;
579 #else
580 	size_t len = sizeof(*h) + h->caplen;
581 #endif
582 
583 	if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) {
584 		logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped",
585 		       len, cur_snaplen, snaplen);
586 		packets_dropped++;
587 		return;
588 	}
589 
590 	if (len <= bufleft)
591 		goto append;
592 
593 	if (suspended) {
594 		packets_dropped++;
595 		return;
596 	}
597 
598 	if (flush_buffer(f)) {
599 		packets_dropped++;
600 		return;
601 	}
602 
603 	if (len > bufleft) {
604 		dump_packet_nobuf(user, h, sp);
605 		return;
606 	}
607 
608  append:
609 #ifdef __FreeBSD__
610 	sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec;
611 	sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec;
612 	sh.caplen = h->caplen;
613 	sh.len = h->len;
614 
615 	memcpy(bufpos, &sh, sizeof(sh));
616 	memcpy(bufpos + sizeof(sh), sp, h->caplen);
617 #else
618 	memcpy(bufpos, h, sizeof(*h));
619 	memcpy(bufpos + sizeof(*h), sp, h->caplen);
620 #endif
621 
622 	bufpos += len;
623 	bufleft -= len;
624 	bufpkt++;
625 
626 	return;
627 }
628 
629 void
630 log_pcap_stats(void)
631 {
632 	struct pcap_stat pstat;
633 	if (pcap_stats(hpcap, &pstat) < 0)
634 		logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap));
635 	else
636 		logmsg(LOG_NOTICE,
637 			"%u packets received, %u/%u dropped (kernel/pflogd)",
638 			pstat.ps_recv, pstat.ps_drop, packets_dropped);
639 }
640 
641 int
642 main(int argc, char **argv)
643 {
644 	int ch, np, ret, Xflag = 0;
645 	pcap_handler phandler = dump_packet;
646 	const char *errstr = NULL;
647 	char *pidf = NULL;
648 
649 	ret = 0;
650 
651 	closefrom(STDERR_FILENO + 1);
652 
653 	while ((ch = getopt(argc, argv, "Dxd:f:i:p:s:")) != -1) {
654 		switch (ch) {
655 		case 'D':
656 			Debug = 1;
657 			break;
658 		case 'd':
659 			delay = strtonum(optarg, 5, 60*60, &errstr);
660 			if (errstr)
661 				usage();
662 			break;
663 		case 'f':
664 			filename = optarg;
665 			break;
666 		case 'i':
667 			interface = optarg;
668 			break;
669 		case 'p':
670 			pidf = optarg;
671 			break;
672 		case 's':
673 			snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN,
674 			    &errstr);
675 			if (snaplen <= 0)
676 				snaplen = DEF_SNAPLEN;
677 			if (errstr)
678 				snaplen = PFLOGD_MAXSNAPLEN;
679 			break;
680 		case 'x':
681 			Xflag++;
682 			break;
683 		default:
684 			usage();
685 		}
686 
687 	}
688 
689 	log_debug = Debug;
690 	argc -= optind;
691 	argv += optind;
692 
693 	/* does interface exist */
694 	if (!if_exists(interface)) {
695 		warn("Failed to initialize: %s", interface);
696 		logmsg(LOG_ERR, "Failed to initialize: %s", interface);
697 		logmsg(LOG_ERR, "Exiting, init failure");
698 		exit(1);
699 	}
700 
701 	if (!Debug) {
702 		openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON);
703 		if (daemon(0, 0)) {
704 			logmsg(LOG_WARNING, "Failed to become daemon: %s",
705 			    strerror(errno));
706 		}
707 		pidfile(pidf);
708 	}
709 
710 	tzset();
711 	(void)umask(S_IRWXG | S_IRWXO);
712 
713 	/* filter will be used by the privileged process */
714 	if (argc) {
715 		filter = copy_argv(argv);
716 		if (filter == NULL)
717 			logmsg(LOG_NOTICE, "Failed to form filter expression");
718 	}
719 
720 	/* initialize pcap before dropping privileges */
721 	if (init_pcap()) {
722 		logmsg(LOG_ERR, "Exiting, init failure");
723 		exit(1);
724 	}
725 
726 	/* Privilege separation begins here */
727 	if (priv_init()) {
728 		logmsg(LOG_ERR, "unable to privsep");
729 		exit(1);
730 	}
731 
732 	setproctitle("[initializing]");
733 	/* Process is now unprivileged and inside a chroot */
734 	signal(SIGTERM, sig_close);
735 	signal(SIGINT, sig_close);
736 	signal(SIGQUIT, sig_close);
737 	signal(SIGALRM, sig_alrm);
738 	signal(SIGUSR1, sig_usr1);
739 	signal(SIGHUP, sig_hup);
740 	alarm(delay);
741 
742 	buffer = malloc(PFLOGD_BUFSIZE);
743 
744 	if (buffer == NULL) {
745 		logmsg(LOG_WARNING, "Failed to allocate output buffer");
746 		phandler = dump_packet_nobuf;
747 	} else {
748 		bufleft = buflen = PFLOGD_BUFSIZE;
749 		bufpos = buffer;
750 		bufpkt = 0;
751 	}
752 
753 	if (reset_dump(Xflag) < 0) {
754 		if (Xflag)
755 			return (1);
756 
757 		logmsg(LOG_ERR, "Logging suspended: open error");
758 		set_suspended(1);
759 	} else if (Xflag)
760 		return (0);
761 
762 	while (1) {
763 		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS,
764 		    phandler, (u_char *)dpcap);
765 		if (np < 0) {
766 			if (!if_exists(interface)) {
767 				logmsg(LOG_NOTICE, "interface %s went away",
768 				    interface);
769 				ret = -1;
770 				break;
771 			}
772 			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
773 		}
774 
775 		if (gotsig_close)
776 			break;
777 		if (gotsig_hup) {
778 			if (reset_dump(0)) {
779 				logmsg(LOG_ERR,
780 				    "Logging suspended: open error");
781 				set_suspended(1);
782 			}
783 			gotsig_hup = 0;
784 		}
785 
786 		if (gotsig_alrm) {
787 			if (dpcap)
788 				flush_buffer(dpcap);
789 			else
790 				gotsig_hup = 1;
791 			gotsig_alrm = 0;
792 			alarm(delay);
793 		}
794 
795 		if (gotsig_usr1) {
796 			log_pcap_stats();
797 			gotsig_usr1 = 0;
798 		}
799 	}
800 
801 	logmsg(LOG_NOTICE, "Exiting");
802 	if (dpcap) {
803 		flush_buffer(dpcap);
804 		fclose(dpcap);
805 	}
806 	purge_buffer();
807 
808 	log_pcap_stats();
809 	pcap_close(hpcap);
810 	if (!Debug)
811 		closelog();
812 	return (ret);
813 }
814