xref: /openbsd/sbin/pflogd/pflogd.c (revision 76d0caae)
1 /*	$OpenBSD: pflogd.c,v 1.62 2019/07/25 17:32:33 brynet 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/stat.h>
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <pcap-int.h>
43 #include <pcap.h>
44 #include <syslog.h>
45 #include <signal.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <stdarg.h>
49 #include <fcntl.h>
50 #include <util.h>
51 #include "pflogd.h"
52 
53 pcap_t *hpcap;
54 static FILE *dpcap;
55 
56 int Debug = 0;
57 static int snaplen = DEF_SNAPLEN;
58 static int cur_snaplen = DEF_SNAPLEN;
59 
60 volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup;
61 
62 char *filename = PFLOGD_LOG_FILE;
63 char *interface = PFLOGD_DEFAULT_IF;
64 char *filter = NULL;
65 
66 char errbuf[PCAP_ERRBUF_SIZE];
67 
68 int log_debug = 0;
69 unsigned int delay = FLUSH_DELAY;
70 
71 char *copy_argv(char * const *);
72 void  dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
73 void  dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *);
74 int   flush_buffer(FILE *);
75 int   if_exists(char *);
76 pcap_t *pflog_read_live(const char *, int, int, int, char *);
77 void  logmsg(int, const char *, ...);
78 void  purge_buffer(void);
79 int   reset_dump(void);
80 int   scan_dump(FILE *, off_t);
81 int   set_snaplen(int);
82 void  set_suspended(int);
83 void  sig_alrm(int);
84 void  sig_close(int);
85 void  sig_hup(int);
86 void  usage(void);
87 
88 /* buffer must always be greater than snaplen */
89 static int    bufpkt = 0;	/* number of packets in buffer */
90 static int    buflen = 0;	/* allocated size of buffer */
91 static char  *buffer = NULL;	/* packet buffer */
92 static char  *bufpos = NULL;	/* position in buffer */
93 static int    bufleft = 0;	/* bytes left in buffer */
94 
95 /* if error, stop logging but count dropped packets */
96 static int suspended = -1;
97 static long packets_dropped = 0;
98 
99 void
100 set_suspended(int s)
101 {
102 	if (suspended == s)
103 		return;
104 
105 	suspended = s;
106 	setproctitle("[%s] -s %d -i %s -f %s",
107 	    suspended ? "suspended" : "running",
108 	    cur_snaplen, interface, filename);
109 }
110 
111 char *
112 copy_argv(char * const *argv)
113 {
114 	size_t len = 0, n;
115 	char *buf;
116 
117 	if (argv == NULL)
118 		return (NULL);
119 
120 	for (n = 0; argv[n]; n++)
121 		len += strlen(argv[n])+1;
122 	if (len == 0)
123 		return (NULL);
124 
125 	buf = malloc(len);
126 	if (buf == NULL)
127 		return (NULL);
128 
129 	strlcpy(buf, argv[0], len);
130 	for (n = 1; argv[n]; n++) {
131 		strlcat(buf, " ", len);
132 		strlcat(buf, argv[n], len);
133 	}
134 	return (buf);
135 }
136 
137 void
138 logmsg(int pri, const char *message, ...)
139 {
140 	va_list ap;
141 	va_start(ap, message);
142 
143 	if (log_debug) {
144 		vfprintf(stderr, message, ap);
145 		fprintf(stderr, "\n");
146 	} else
147 		vsyslog(pri, message, ap);
148 	va_end(ap);
149 }
150 
151 __dead void
152 usage(void)
153 {
154 	fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename]");
155 	fprintf(stderr, " [-i interface] [-s snaplen]\n");
156 	fprintf(stderr, "              [expression]\n");
157 	exit(1);
158 }
159 
160 void
161 sig_close(int sig)
162 {
163 	gotsig_close = 1;
164 }
165 
166 void
167 sig_hup(int sig)
168 {
169 	gotsig_hup = 1;
170 }
171 
172 void
173 sig_alrm(int sig)
174 {
175 	gotsig_alrm = 1;
176 }
177 
178 void
179 set_pcap_filter(void)
180 {
181 	struct bpf_program bprog;
182 
183 	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0)
184 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
185 	else {
186 		if (pcap_setfilter(hpcap, &bprog) < 0)
187 			logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
188 		pcap_freecode(&bprog);
189 	}
190 }
191 
192 int
193 if_exists(char *ifname)
194 {
195 	return (if_nametoindex(ifname) != 0);
196 }
197 
198 pcap_t *
199 pflog_read_live(const char *source, int slen, int promisc, int to_ms,
200     char *ebuf)
201 {
202 	int		fd;
203 	struct bpf_version bv;
204 	struct ifreq	ifr;
205 	u_int		v, dlt = DLT_PFLOG;
206 	pcap_t		*p;
207 
208 	if (source == NULL || slen <= 0)
209 		return (NULL);
210 
211 	p = pcap_create(source, ebuf);
212 	if (p == NULL)
213 		return (NULL);
214 
215 	/* Open bpf(4) read only */
216 	if ((fd = open("/dev/bpf", O_RDONLY)) == -1)
217 		return (NULL);
218 
219 	if (ioctl(fd, BIOCVERSION, &bv) == -1) {
220 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
221 		    pcap_strerror(errno));
222 		goto bad;
223 	}
224 
225 	if (bv.bv_major != BPF_MAJOR_VERSION ||
226 	    bv.bv_minor < BPF_MINOR_VERSION) {
227 		snprintf(ebuf, PCAP_ERRBUF_SIZE,
228 		    "kernel bpf filter out of date");
229 		goto bad;
230 	}
231 
232 	strlcpy(ifr.ifr_name, source, sizeof(ifr.ifr_name));
233 	if (ioctl(fd, BIOCSETIF, &ifr) == -1) {
234 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSETIF: %s",
235 		    pcap_strerror(errno));
236 		goto bad;
237 	}
238 
239 	if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt)) {
240 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSDLT: %s",
241 		    pcap_strerror(errno));
242 		goto bad;
243 	}
244 
245 	p->fd = fd;
246 	p->snapshot = slen;
247 	p->linktype = dlt;
248 
249 	/* set timeout */
250 	if (to_ms != 0) {
251 		struct timeval to;
252 		to.tv_sec = to_ms / 1000;
253 		to.tv_usec = (to_ms * 1000) % 1000000;
254 		if (ioctl(p->fd, BIOCSRTIMEOUT, &to) == -1) {
255 			snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
256 			    pcap_strerror(errno));
257 			goto bad;
258 		}
259 	}
260 	if (promisc)
261 		/* this is allowed to fail */
262 		ioctl(fd, BIOCPROMISC, NULL);
263 
264 	if (ioctl(fd, BIOCGBLEN, &v) == -1) {
265 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
266 		    pcap_strerror(errno));
267 		goto bad;
268 	}
269 	p->bufsize = v;
270 	p->buffer = malloc(p->bufsize);
271 	if (p->buffer == NULL) {
272 		snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
273 		    pcap_strerror(errno));
274 		goto bad;
275 	}
276 	p->activated = 1;
277 	return (p);
278 
279 bad:
280 	pcap_close(p);
281 	return (NULL);
282 }
283 
284 int
285 init_pcap(void)
286 {
287 	hpcap = pflog_read_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);
288 	if (hpcap == NULL) {
289 		logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
290 		return (-1);
291 	}
292 
293 	if (pcap_datalink(hpcap) != DLT_PFLOG) {
294 		logmsg(LOG_ERR, "Invalid datalink type");
295 		pcap_close(hpcap);
296 		hpcap = NULL;
297 		return (-1);
298 	}
299 
300 	set_pcap_filter();
301 
302 	/* lock */
303 	if (ioctl(pcap_fileno(hpcap), BIOCLOCK) == -1) {
304 		logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno));
305 		return (-1);
306 	}
307 
308 	return (0);
309 }
310 
311 int
312 set_snaplen(int snap)
313 {
314 	if (priv_set_snaplen(snap))
315 		return (1);
316 
317 	if (cur_snaplen > snap)
318 		purge_buffer();
319 
320 	cur_snaplen = snap;
321 
322 	return (0);
323 }
324 
325 int
326 reset_dump(void)
327 {
328 	struct pcap_file_header hdr;
329 	struct stat st;
330 	int fd;
331 	FILE *fp;
332 
333 	if (hpcap == NULL)
334 		return (-1);
335 
336 	if (dpcap) {
337 		flush_buffer(dpcap);
338 		fclose(dpcap);
339 		dpcap = NULL;
340 	}
341 
342 	/*
343 	 * Basically reimplement pcap_dump_open() because it truncates
344 	 * files and duplicates headers and such.
345 	 */
346 	fd = priv_open_log();
347 	if (fd < 0)
348 		return (-1);
349 
350 	fp = fdopen(fd, "a+");
351 
352 	if (fp == NULL) {
353 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
354 		close(fd);
355 		return (-1);
356 	}
357 	if (fstat(fileno(fp), &st) == -1) {
358 		logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno));
359 		fclose(fp);
360 		return (-1);
361 	}
362 
363 	/* set FILE unbuffered, we do our own buffering */
364 	if (setvbuf(fp, NULL, _IONBF, 0)) {
365 		logmsg(LOG_ERR, "Failed to set output buffers");
366 		fclose(fp);
367 		return (-1);
368 	}
369 
370 #define TCPDUMP_MAGIC 0xa1b2c3d4
371 
372 	if (st.st_size == 0) {
373 		if (snaplen != cur_snaplen) {
374 			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
375 			if (set_snaplen(snaplen))
376 				logmsg(LOG_WARNING,
377 				    "Failed, using old settings");
378 		}
379 		hdr.magic = TCPDUMP_MAGIC;
380 		hdr.version_major = PCAP_VERSION_MAJOR;
381 		hdr.version_minor = PCAP_VERSION_MINOR;
382 		hdr.thiszone = hpcap->tzoff;
383 		hdr.snaplen = hpcap->snapshot;
384 		hdr.sigfigs = 0;
385 		hdr.linktype = hpcap->linktype;
386 
387 		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
388 			fclose(fp);
389 			return (-1);
390 		}
391 	} else if (scan_dump(fp, st.st_size)) {
392 		fclose(fp);
393 		logmsg(LOG_ERR,
394 		    "Invalid/incompatible log file, move it away");
395 		return (-1);
396 	}
397 
398 	dpcap = fp;
399 
400 	set_suspended(0);
401 	flush_buffer(fp);
402 
403 	return (0);
404 }
405 
406 int
407 scan_dump(FILE *fp, off_t size)
408 {
409 	struct pcap_file_header hdr;
410 	struct pcap_pkthdr ph;
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 
480 	if (suspended) {
481 		packets_dropped++;
482 		return;
483 	}
484 
485 	if (fwrite((char *)h, sizeof(*h), 1, f) != 1) {
486 		off_t pos = ftello(f);
487 
488 		/* try to undo header to prevent corruption */
489 		if (pos < sizeof(*h) ||
490 		    ftruncate(fileno(f), pos - sizeof(*h))) {
491 			logmsg(LOG_ERR, "Write failed, corrupted logfile!");
492 			set_suspended(1);
493 			gotsig_close = 1;
494 			return;
495 		}
496 		goto error;
497 	}
498 
499 	if (fwrite((char *)sp, h->caplen, 1, f) != 1)
500 		goto error;
501 
502 	return;
503 
504 error:
505 	set_suspended(1);
506 	packets_dropped ++;
507 	logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno));
508 }
509 
510 int
511 flush_buffer(FILE *f)
512 {
513 	off_t offset;
514 	int len = bufpos - buffer;
515 
516 	if (len <= 0)
517 		return (0);
518 
519 	offset = ftello(f);
520 	if (offset == (off_t)-1) {
521 		set_suspended(1);
522 		logmsg(LOG_ERR, "Logging suspended: ftello: %s",
523 		    strerror(errno));
524 		return (1);
525 	}
526 
527 	if (fwrite(buffer, len, 1, f) != 1) {
528 		set_suspended(1);
529 		logmsg(LOG_ERR, "Logging suspended: fwrite: %s",
530 		    strerror(errno));
531 		ftruncate(fileno(f), offset);
532 		return (1);
533 	}
534 
535 	set_suspended(0);
536 	bufpos = buffer;
537 	bufleft = buflen;
538 	bufpkt = 0;
539 
540 	return (0);
541 }
542 
543 void
544 purge_buffer(void)
545 {
546 	packets_dropped += bufpkt;
547 
548 	set_suspended(0);
549 	bufpos = buffer;
550 	bufleft = buflen;
551 	bufpkt = 0;
552 }
553 
554 /* append packet to the buffer, flushing if necessary */
555 void
556 dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
557 {
558 	FILE *f = (FILE *)user;
559 	size_t len = sizeof(*h) + h->caplen;
560 
561 	if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) {
562 		logmsg(LOG_NOTICE, "invalid size %zu (%d/%d), packet dropped",
563 		       len, cur_snaplen, snaplen);
564 		packets_dropped++;
565 		return;
566 	}
567 
568 	if (len <= bufleft)
569 		goto append;
570 
571 	if (suspended) {
572 		packets_dropped++;
573 		return;
574 	}
575 
576 	if (flush_buffer(f)) {
577 		packets_dropped++;
578 		return;
579 	}
580 
581 	if (len > bufleft) {
582 		dump_packet_nobuf(user, h, sp);
583 		return;
584 	}
585 
586  append:
587 	memcpy(bufpos, h, sizeof(*h));
588 	memcpy(bufpos + sizeof(*h), sp, h->caplen);
589 
590 	bufpos += len;
591 	bufleft -= len;
592 	bufpkt++;
593 
594 	return;
595 }
596 
597 int
598 main(int argc, char **argv)
599 {
600 	int ch, np, ret, Pflag = 0, Xflag = 0;
601 	pcap_handler phandler = dump_packet;
602 	const char *errstr = NULL;
603 
604 	ret = 0;
605 
606 	while ((ch = getopt(argc, argv, "Dxd:f:i:Ps:")) != -1) {
607 		switch (ch) {
608 		case 'D':
609 			Debug = 1;
610 			break;
611 		case 'd':
612 			delay = strtonum(optarg, 5, 60*60, &errstr);
613 			if (errstr)
614 				usage();
615 			break;
616 		case 'f':
617 			filename = optarg;
618 			break;
619 		case 'i':
620 			interface = optarg;
621 			break;
622 		case 'P': /* used internally, exec the child */
623 			if (strcmp("-P", argv[1]) == 0)
624 				Pflag = 1;
625 			break;
626 		case 's':
627 			snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN,
628 			    &errstr);
629 			if (snaplen <= 0)
630 				snaplen = DEF_SNAPLEN;
631 			if (errstr)
632 				snaplen = PFLOGD_MAXSNAPLEN;
633 			cur_snaplen = snaplen;
634 			break;
635 		case 'x':
636 			Xflag = 1;
637 			break;
638 		default:
639 			usage();
640 		}
641 
642 	}
643 
644 	log_debug = Debug;
645 	argc -= optind;
646 	argv += optind;
647 
648 	/* does interface exist */
649 	if (!if_exists(interface)) {
650 		warn("Failed to initialize: %s", interface);
651 		logmsg(LOG_ERR, "Failed to initialize: %s", interface);
652 		logmsg(LOG_ERR, "Exiting, init failure");
653 		exit(1);
654 	}
655 
656 	if (!Debug) {
657 		openlog("pflogd", LOG_PID, LOG_DAEMON);
658 		if (!Pflag) {
659 			if (daemon(0, 0)) {
660 				logmsg(LOG_WARNING,
661 				    "Failed to become daemon: %s",
662 				    strerror(errno));
663 			}
664 		}
665 	}
666 
667 	tzset();
668 	(void)umask(S_IRWXG | S_IRWXO);
669 
670 	/* filter will be used by the privileged process */
671 	if (argc) {
672 		filter = copy_argv(argv);
673 		if (filter == NULL)
674 			logmsg(LOG_NOTICE, "Failed to form filter expression");
675 	}
676 	argc += optind;
677 	argv -= optind;
678 
679 	/* Privilege separation begins here */
680 	priv_init(Pflag, argc, argv);
681 
682 	if (pledge("stdio recvfd", NULL) == -1)
683 		err(1, "pledge");
684 
685 	setproctitle("[initializing]");
686 	/* Process is now unprivileged and inside a chroot */
687 	signal(SIGTERM, sig_close);
688 	signal(SIGINT, sig_close);
689 	signal(SIGQUIT, sig_close);
690 	signal(SIGALRM, sig_alrm);
691 	signal(SIGHUP, sig_hup);
692 	alarm(delay);
693 
694 	if (priv_init_pcap(snaplen))
695 		errx(1, "priv_init_pcap failed");
696 
697 	buffer = malloc(PFLOGD_BUFSIZE);
698 
699 	if (buffer == NULL) {
700 		logmsg(LOG_WARNING, "Failed to allocate output buffer");
701 		phandler = dump_packet_nobuf;
702 	} else {
703 		bufleft = buflen = PFLOGD_BUFSIZE;
704 		bufpos = buffer;
705 		bufpkt = 0;
706 	}
707 
708 	if (reset_dump() < 0) {
709 		if (Xflag)
710 			return (1);
711 
712 		logmsg(LOG_ERR, "Logging suspended: open error");
713 		set_suspended(1);
714 	} else if (Xflag)
715 		return (0);
716 
717 	while (1) {
718 		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS,
719 		    phandler, (u_char *)dpcap);
720 		if (np < 0) {
721 			if (!if_exists(interface)) {
722 				logmsg(LOG_NOTICE, "interface %s went away",
723 				    interface);
724 				ret = -1;
725 				break;
726 			}
727 			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
728 		}
729 
730 		if (gotsig_close)
731 			break;
732 		if (gotsig_hup) {
733 			int was_suspended = suspended;
734 			if (reset_dump()) {
735 				logmsg(LOG_ERR,
736 				    "Logging suspended: open error");
737 				set_suspended(1);
738 			} else {
739 				if (was_suspended)
740 					logmsg(LOG_NOTICE, "Logging resumed");
741 			}
742 			gotsig_hup = 0;
743 		}
744 
745 		if (gotsig_alrm) {
746 			if (dpcap)
747 				flush_buffer(dpcap);
748 			else
749 				gotsig_hup = 1;
750 			gotsig_alrm = 0;
751 			alarm(delay);
752 		}
753 	}
754 
755 	logmsg(LOG_NOTICE, "Exiting");
756 	if (dpcap) {
757 		flush_buffer(dpcap);
758 		fclose(dpcap);
759 	}
760 	purge_buffer();
761 
762 	pcap_close(hpcap);
763 	if (!Debug)
764 		closelog();
765 	return (ret);
766 }
767