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