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