1223637Sbz/* $OpenBSD: pflogd.c,v 1.46 2008/10/22 08:16:49 henning Exp $ */ 2126353Smlaier 3126353Smlaier/* 4126353Smlaier * Copyright (c) 2001 Theo de Raadt 5126353Smlaier * Copyright (c) 2001 Can Erkin Acar 6126353Smlaier * All rights reserved. 7126353Smlaier * 8126353Smlaier * Redistribution and use in source and binary forms, with or without 9126353Smlaier * modification, are permitted provided that the following conditions 10126353Smlaier * are met: 11126353Smlaier * 12126353Smlaier * - Redistributions of source code must retain the above copyright 13126353Smlaier * notice, this list of conditions and the following disclaimer. 14126353Smlaier * - Redistributions in binary form must reproduce the above 15126353Smlaier * copyright notice, this list of conditions and the following 16126353Smlaier * disclaimer in the documentation and/or other materials provided 17126353Smlaier * with the distribution. 18126353Smlaier * 19126353Smlaier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20126353Smlaier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21126353Smlaier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22126353Smlaier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23126353Smlaier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24126353Smlaier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25126353Smlaier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26126353Smlaier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27126353Smlaier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28126353Smlaier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29126353Smlaier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30126353Smlaier * POSSIBILITY OF SUCH DAMAGE. 31126353Smlaier */ 32126353Smlaier 33127082Sobrien#include <sys/cdefs.h> 34127082Sobrien__FBSDID("$FreeBSD$"); 35127082Sobrien 36126353Smlaier#include <sys/types.h> 37130617Smlaier#include <sys/ioctl.h> 38126353Smlaier#include <sys/file.h> 39126353Smlaier#include <sys/stat.h> 40223637Sbz#include <sys/socket.h> 41223637Sbz#include <net/if.h> 42126353Smlaier#include <stdio.h> 43126353Smlaier#include <stdlib.h> 44126353Smlaier#include <string.h> 45126353Smlaier#include <unistd.h> 46126353Smlaier#include <pcap-int.h> 47126353Smlaier#include <pcap.h> 48126353Smlaier#include <syslog.h> 49126353Smlaier#include <signal.h> 50223637Sbz#include <err.h> 51126353Smlaier#include <errno.h> 52126353Smlaier#include <stdarg.h> 53126353Smlaier#include <fcntl.h> 54127024Smlaier#ifdef __FreeBSD__ 55223637Sbz#include <ifaddrs.h> 56126355Smlaier#include "pidfile.h" 57126355Smlaier#else 58126353Smlaier#include <util.h> 59126355Smlaier#endif 60130617Smlaier#include "pflogd.h" 61126353Smlaier 62126353Smlaierpcap_t *hpcap; 63130617Smlaierstatic FILE *dpcap; 64126353Smlaier 65126353Smlaierint Debug = 0; 66130617Smlaierstatic int snaplen = DEF_SNAPLEN; 67130617Smlaierstatic int cur_snaplen = DEF_SNAPLEN; 68126353Smlaier 69223637Sbzvolatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup, gotsig_usr1; 70126353Smlaier 71126353Smlaierchar *filename = PFLOGD_LOG_FILE; 72126353Smlaierchar *interface = PFLOGD_DEFAULT_IF; 73126353Smlaierchar *filter = NULL; 74126353Smlaier 75126353Smlaierchar errbuf[PCAP_ERRBUF_SIZE]; 76126353Smlaier 77126353Smlaierint log_debug = 0; 78126353Smlaierunsigned int delay = FLUSH_DELAY; 79126353Smlaier 80130617Smlaierchar *copy_argv(char * const *); 81130617Smlaiervoid dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); 82130617Smlaiervoid dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *); 83223637Sbzvoid log_pcap_stats(void); 84130617Smlaierint flush_buffer(FILE *); 85223637Sbzint if_exists(char *); 86126353Smlaierint init_pcap(void); 87130617Smlaiervoid logmsg(int, const char *, ...); 88130617Smlaiervoid purge_buffer(void); 89171172Smlaierint reset_dump(int); 90130617Smlaierint scan_dump(FILE *, off_t); 91130617Smlaierint set_snaplen(int); 92130617Smlaiervoid set_suspended(int); 93126353Smlaiervoid sig_alrm(int); 94223637Sbzvoid sig_usr1(int); 95126353Smlaiervoid sig_close(int); 96126353Smlaiervoid sig_hup(int); 97126353Smlaiervoid usage(void); 98126353Smlaier 99171172Smlaierstatic int try_reset_dump(int); 100171172Smlaier 101130617Smlaier/* buffer must always be greater than snaplen */ 102130617Smlaierstatic int bufpkt = 0; /* number of packets in buffer */ 103130617Smlaierstatic int buflen = 0; /* allocated size of buffer */ 104130617Smlaierstatic char *buffer = NULL; /* packet buffer */ 105130617Smlaierstatic char *bufpos = NULL; /* position in buffer */ 106130617Smlaierstatic int bufleft = 0; /* bytes left in buffer */ 107126353Smlaier 108130617Smlaier/* if error, stop logging but count dropped packets */ 109130617Smlaierstatic int suspended = -1; 110130617Smlaierstatic long packets_dropped = 0; 111130617Smlaier 112130617Smlaiervoid 113130617Smlaierset_suspended(int s) 114130617Smlaier{ 115130617Smlaier if (suspended == s) 116130617Smlaier return; 117130617Smlaier 118130617Smlaier suspended = s; 119171172Smlaier setproctitle("[%s] -s %d -i %s -f %s", 120171172Smlaier suspended ? "suspended" : "running", 121171172Smlaier cur_snaplen, interface, filename); 122130617Smlaier} 123130617Smlaier 124126353Smlaierchar * 125126353Smlaiercopy_argv(char * const *argv) 126126353Smlaier{ 127126353Smlaier size_t len = 0, n; 128126353Smlaier char *buf; 129126353Smlaier 130126353Smlaier if (argv == NULL) 131126353Smlaier return (NULL); 132126353Smlaier 133126353Smlaier for (n = 0; argv[n]; n++) 134126353Smlaier len += strlen(argv[n])+1; 135126353Smlaier if (len == 0) 136126353Smlaier return (NULL); 137126353Smlaier 138126353Smlaier buf = malloc(len); 139126353Smlaier if (buf == NULL) 140126353Smlaier return (NULL); 141126353Smlaier 142126353Smlaier strlcpy(buf, argv[0], len); 143126353Smlaier for (n = 1; argv[n]; n++) { 144126353Smlaier strlcat(buf, " ", len); 145126353Smlaier strlcat(buf, argv[n], len); 146126353Smlaier } 147126353Smlaier return (buf); 148126353Smlaier} 149126353Smlaier 150126353Smlaiervoid 151126353Smlaierlogmsg(int pri, const char *message, ...) 152126353Smlaier{ 153126353Smlaier va_list ap; 154126353Smlaier va_start(ap, message); 155126353Smlaier 156126353Smlaier if (log_debug) { 157126353Smlaier vfprintf(stderr, message, ap); 158126353Smlaier fprintf(stderr, "\n"); 159126353Smlaier } else 160126353Smlaier vsyslog(pri, message, ap); 161126353Smlaier va_end(ap); 162126353Smlaier} 163126353Smlaier 164127024Smlaier#ifdef __FreeBSD__ 165127024Smlaier__dead2 void 166127024Smlaier#else 167126353Smlaier__dead void 168127024Smlaier#endif 169126353Smlaierusage(void) 170126353Smlaier{ 171171172Smlaier fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename]"); 172223637Sbz fprintf(stderr, " [-i interface] [-p pidfile]\n"); 173223637Sbz fprintf(stderr, " [-s snaplen] [expression]\n"); 174126353Smlaier exit(1); 175126353Smlaier} 176126353Smlaier 177126353Smlaiervoid 178126353Smlaiersig_close(int sig) 179126353Smlaier{ 180126353Smlaier gotsig_close = 1; 181126353Smlaier} 182126353Smlaier 183126353Smlaiervoid 184126353Smlaiersig_hup(int sig) 185126353Smlaier{ 186126353Smlaier gotsig_hup = 1; 187126353Smlaier} 188126353Smlaier 189126353Smlaiervoid 190126353Smlaiersig_alrm(int sig) 191126353Smlaier{ 192126353Smlaier gotsig_alrm = 1; 193126353Smlaier} 194126353Smlaier 195130617Smlaiervoid 196223637Sbzsig_usr1(int sig) 197223637Sbz{ 198223637Sbz gotsig_usr1 = 1; 199223637Sbz} 200223637Sbz 201223637Sbzvoid 202130617Smlaierset_pcap_filter(void) 203130617Smlaier{ 204130617Smlaier struct bpf_program bprog; 205130617Smlaier 206130617Smlaier if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 207130617Smlaier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 208130617Smlaier else { 209130617Smlaier if (pcap_setfilter(hpcap, &bprog) < 0) 210130617Smlaier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 211130617Smlaier pcap_freecode(&bprog); 212130617Smlaier } 213130617Smlaier} 214130617Smlaier 215126353Smlaierint 216223637Sbzif_exists(char *ifname) 217223637Sbz{ 218223637Sbz#ifdef __FreeBSD__ 219223637Sbz struct ifaddrs *ifdata, *mb; 220223637Sbz int exists = 0; 221223637Sbz 222223637Sbz getifaddrs(&ifdata); 223223637Sbz if (ifdata == NULL) 224223637Sbz return (0); 225223637Sbz 226223637Sbz for (mb = ifdata; mb != NULL; mb = mb->ifa_next) { 227223637Sbz if (mb == NULL) 228223637Sbz continue; 229223637Sbz if (strlen(ifname) != strlen(mb->ifa_name)) 230223637Sbz continue; 231223637Sbz if (strncmp(ifname, mb->ifa_name, strlen(ifname)) != 0) 232223637Sbz continue; 233223637Sbz exists = 1; 234223637Sbz break; 235223637Sbz } 236223637Sbz freeifaddrs(ifdata); 237223637Sbz 238223637Sbz return (exists); 239223637Sbz#else 240223637Sbz int s; 241223637Sbz struct ifreq ifr; 242223637Sbz struct if_data ifrdat; 243223637Sbz 244223637Sbz if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 245223637Sbz err(1, "socket"); 246223637Sbz bzero(&ifr, sizeof(ifr)); 247223637Sbz if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 248223637Sbz sizeof(ifr.ifr_name)) 249223637Sbz errx(1, "main ifr_name: strlcpy"); 250223637Sbz ifr.ifr_data = (caddr_t)&ifrdat; 251223637Sbz if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) 252223637Sbz return (0); 253223637Sbz if (close(s)) 254223637Sbz err(1, "close"); 255223637Sbz 256223637Sbz return (1); 257223637Sbz#endif 258223637Sbz} 259223637Sbz 260223637Sbzint 261126353Smlaierinit_pcap(void) 262126353Smlaier{ 263126353Smlaier hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 264126353Smlaier if (hpcap == NULL) { 265126353Smlaier logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 266126353Smlaier return (-1); 267126353Smlaier } 268126353Smlaier 269126353Smlaier if (pcap_datalink(hpcap) != DLT_PFLOG) { 270126353Smlaier logmsg(LOG_ERR, "Invalid datalink type"); 271126353Smlaier pcap_close(hpcap); 272130617Smlaier hpcap = NULL; 273126353Smlaier return (-1); 274126353Smlaier } 275126353Smlaier 276130617Smlaier set_pcap_filter(); 277126353Smlaier 278130617Smlaier cur_snaplen = snaplen = pcap_snapshot(hpcap); 279130617Smlaier 280130617Smlaier /* lock */ 281130617Smlaier if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) { 282130617Smlaier logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); 283130617Smlaier return (-1); 284130617Smlaier } 285130617Smlaier 286126353Smlaier return (0); 287126353Smlaier} 288126353Smlaier 289126353Smlaierint 290130617Smlaierset_snaplen(int snap) 291130617Smlaier{ 292130617Smlaier if (priv_set_snaplen(snap)) 293130617Smlaier return (1); 294130617Smlaier 295130617Smlaier if (cur_snaplen > snap) 296130617Smlaier purge_buffer(); 297130617Smlaier 298130617Smlaier cur_snaplen = snap; 299130617Smlaier 300130617Smlaier return (0); 301130617Smlaier} 302130617Smlaier 303130617Smlaierint 304171172Smlaierreset_dump(int nomove) 305126353Smlaier{ 306171172Smlaier int ret; 307171172Smlaier 308171172Smlaier for (;;) { 309171172Smlaier ret = try_reset_dump(nomove); 310171172Smlaier if (ret <= 0) 311171172Smlaier break; 312171172Smlaier } 313171172Smlaier 314171172Smlaier return (ret); 315171172Smlaier} 316171172Smlaier 317171172Smlaier/* 318171172Smlaier * tries to (re)open log file, nomove flag is used with -x switch 319171172Smlaier * returns 0: success, 1: retry (log moved), -1: error 320171172Smlaier */ 321171172Smlaierint 322171172Smlaiertry_reset_dump(int nomove) 323171172Smlaier{ 324126353Smlaier struct pcap_file_header hdr; 325126353Smlaier struct stat st; 326130617Smlaier int fd; 327126353Smlaier FILE *fp; 328126353Smlaier 329126353Smlaier if (hpcap == NULL) 330130617Smlaier return (-1); 331130617Smlaier 332126353Smlaier if (dpcap) { 333130617Smlaier flush_buffer(dpcap); 334130617Smlaier fclose(dpcap); 335130617Smlaier dpcap = NULL; 336126353Smlaier } 337126353Smlaier 338126353Smlaier /* 339126353Smlaier * Basically reimplement pcap_dump_open() because it truncates 340126353Smlaier * files and duplicates headers and such. 341126353Smlaier */ 342130617Smlaier fd = priv_open_log(); 343130617Smlaier if (fd < 0) 344171172Smlaier return (-1); 345130617Smlaier 346130617Smlaier fp = fdopen(fd, "a+"); 347130617Smlaier 348126353Smlaier if (fp == NULL) { 349171172Smlaier logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 350145840Smlaier close(fd); 351171172Smlaier return (-1); 352126353Smlaier } 353126353Smlaier if (fstat(fileno(fp), &st) == -1) { 354171172Smlaier logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 355145840Smlaier fclose(fp); 356171172Smlaier return (-1); 357126353Smlaier } 358126353Smlaier 359130617Smlaier /* set FILE unbuffered, we do our own buffering */ 360130617Smlaier if (setvbuf(fp, NULL, _IONBF, 0)) { 361171172Smlaier logmsg(LOG_ERR, "Failed to set output buffers"); 362145840Smlaier fclose(fp); 363171172Smlaier return (-1); 364130617Smlaier } 365126353Smlaier 366126353Smlaier#define TCPDUMP_MAGIC 0xa1b2c3d4 367126353Smlaier 368126353Smlaier if (st.st_size == 0) { 369130617Smlaier if (snaplen != cur_snaplen) { 370126353Smlaier logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 371171172Smlaier if (set_snaplen(snaplen)) 372130617Smlaier logmsg(LOG_WARNING, 373130617Smlaier "Failed, using old settings"); 374126353Smlaier } 375126353Smlaier hdr.magic = TCPDUMP_MAGIC; 376126353Smlaier hdr.version_major = PCAP_VERSION_MAJOR; 377126353Smlaier hdr.version_minor = PCAP_VERSION_MINOR; 378126353Smlaier hdr.thiszone = hpcap->tzoff; 379126353Smlaier hdr.snaplen = hpcap->snapshot; 380126353Smlaier hdr.sigfigs = 0; 381126353Smlaier hdr.linktype = hpcap->linktype; 382126353Smlaier 383126353Smlaier if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 384126353Smlaier fclose(fp); 385171172Smlaier return (-1); 386126353Smlaier } 387130617Smlaier } else if (scan_dump(fp, st.st_size)) { 388130617Smlaier fclose(fp); 389171172Smlaier if (nomove || priv_move_log()) { 390171172Smlaier logmsg(LOG_ERR, 391171172Smlaier "Invalid/incompatible log file, move it away"); 392171172Smlaier return (-1); 393171172Smlaier } 394130617Smlaier return (1); 395126353Smlaier } 396126353Smlaier 397130617Smlaier dpcap = fp; 398130617Smlaier 399130617Smlaier set_suspended(0); 400130617Smlaier flush_buffer(fp); 401130617Smlaier 402130617Smlaier return (0); 403130617Smlaier} 404130617Smlaier 405130617Smlaierint 406130617Smlaierscan_dump(FILE *fp, off_t size) 407130617Smlaier{ 408130617Smlaier struct pcap_file_header hdr; 409134578Smlaier#ifdef __FreeBSD__ 410134578Smlaier struct pcap_sf_pkthdr ph; 411134578Smlaier#else 412130617Smlaier struct pcap_pkthdr ph; 413134578Smlaier#endif 414130617Smlaier off_t pos; 415130617Smlaier 416126353Smlaier /* 417130617Smlaier * Must read the file, compare the header against our new 418126353Smlaier * options (in particular, snaplen) and adjust our options so 419130617Smlaier * that we generate a correct file. Furthermore, check the file 420130617Smlaier * for consistency so that we can append safely. 421130617Smlaier * 422130617Smlaier * XXX this may take a long time for large logs. 423126353Smlaier */ 424126353Smlaier (void) fseek(fp, 0L, SEEK_SET); 425130617Smlaier 426130617Smlaier if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 427130617Smlaier logmsg(LOG_ERR, "Short file header"); 428130617Smlaier return (1); 429130617Smlaier } 430130617Smlaier 431130617Smlaier if (hdr.magic != TCPDUMP_MAGIC || 432130617Smlaier hdr.version_major != PCAP_VERSION_MAJOR || 433130617Smlaier hdr.version_minor != PCAP_VERSION_MINOR || 434130617Smlaier hdr.linktype != hpcap->linktype || 435130617Smlaier hdr.snaplen > PFLOGD_MAXSNAPLEN) { 436130617Smlaier return (1); 437130617Smlaier } 438130617Smlaier 439130617Smlaier pos = sizeof(hdr); 440130617Smlaier 441130617Smlaier while (!feof(fp)) { 442130617Smlaier off_t len = fread((char *)&ph, 1, sizeof(ph), fp); 443130617Smlaier if (len == 0) 444130617Smlaier break; 445130617Smlaier 446130617Smlaier if (len != sizeof(ph)) 447130617Smlaier goto error; 448130617Smlaier if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN) 449130617Smlaier goto error; 450130617Smlaier pos += sizeof(ph) + ph.caplen; 451130617Smlaier if (pos > size) 452130617Smlaier goto error; 453130617Smlaier fseek(fp, ph.caplen, SEEK_CUR); 454130617Smlaier } 455130617Smlaier 456130617Smlaier if (pos != size) 457130617Smlaier goto error; 458130617Smlaier 459130617Smlaier if (hdr.snaplen != cur_snaplen) { 460130617Smlaier logmsg(LOG_WARNING, 461130617Smlaier "Existing file has different snaplen %u, using it", 462130617Smlaier hdr.snaplen); 463130617Smlaier if (set_snaplen(hdr.snaplen)) { 464126353Smlaier logmsg(LOG_WARNING, 465130617Smlaier "Failed, using old settings, offset %llu", 466130617Smlaier (unsigned long long) size); 467126353Smlaier } 468126353Smlaier } 469126353Smlaier 470126353Smlaier return (0); 471130617Smlaier 472130617Smlaier error: 473130617Smlaier logmsg(LOG_ERR, "Corrupted log file."); 474130617Smlaier return (1); 475126353Smlaier} 476126353Smlaier 477130617Smlaier/* dump a packet directly to the stream, which is unbuffered */ 478130617Smlaiervoid 479130617Smlaierdump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 480130617Smlaier{ 481130617Smlaier FILE *f = (FILE *)user; 482134578Smlaier#ifdef __FreeBSD__ 483134578Smlaier struct pcap_sf_pkthdr sh; 484134578Smlaier#endif 485130617Smlaier 486130617Smlaier if (suspended) { 487130617Smlaier packets_dropped++; 488130617Smlaier return; 489130617Smlaier } 490130617Smlaier 491134578Smlaier#ifdef __FreeBSD__ 492134578Smlaier sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec; 493134578Smlaier sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec; 494134578Smlaier sh.caplen = h->caplen; 495134578Smlaier sh.len = h->len; 496134578Smlaier 497134578Smlaier if (fwrite((char *)&sh, sizeof(sh), 1, f) != 1) { 498134578Smlaier#else 499130617Smlaier if (fwrite((char *)h, sizeof(*h), 1, f) != 1) { 500134578Smlaier#endif 501145840Smlaier off_t pos = ftello(f); 502145840Smlaier 503130617Smlaier /* try to undo header to prevent corruption */ 504134578Smlaier#ifdef __FreeBSD__ 505134578Smlaier if (pos < sizeof(sh) || 506134578Smlaier ftruncate(fileno(f), pos - sizeof(sh))) { 507134578Smlaier#else 508130617Smlaier if (pos < sizeof(*h) || 509130617Smlaier ftruncate(fileno(f), pos - sizeof(*h))) { 510134578Smlaier#endif 511130617Smlaier logmsg(LOG_ERR, "Write failed, corrupted logfile!"); 512130617Smlaier set_suspended(1); 513130617Smlaier gotsig_close = 1; 514130617Smlaier return; 515130617Smlaier } 516130617Smlaier goto error; 517130617Smlaier } 518130617Smlaier 519130617Smlaier if (fwrite((char *)sp, h->caplen, 1, f) != 1) 520130617Smlaier goto error; 521130617Smlaier 522130617Smlaier return; 523130617Smlaier 524130617Smlaiererror: 525130617Smlaier set_suspended(1); 526130617Smlaier packets_dropped ++; 527130617Smlaier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno)); 528130617Smlaier} 529130617Smlaier 530126353Smlaierint 531130617Smlaierflush_buffer(FILE *f) 532130617Smlaier{ 533130617Smlaier off_t offset; 534130617Smlaier int len = bufpos - buffer; 535130617Smlaier 536130617Smlaier if (len <= 0) 537130617Smlaier return (0); 538130617Smlaier 539130617Smlaier offset = ftello(f); 540130617Smlaier if (offset == (off_t)-1) { 541130617Smlaier set_suspended(1); 542130617Smlaier logmsg(LOG_ERR, "Logging suspended: ftello: %s", 543130617Smlaier strerror(errno)); 544130617Smlaier return (1); 545130617Smlaier } 546130617Smlaier 547130617Smlaier if (fwrite(buffer, len, 1, f) != 1) { 548130617Smlaier set_suspended(1); 549130617Smlaier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", 550130617Smlaier strerror(errno)); 551130617Smlaier ftruncate(fileno(f), offset); 552130617Smlaier return (1); 553130617Smlaier } 554130617Smlaier 555130617Smlaier set_suspended(0); 556130617Smlaier bufpos = buffer; 557130617Smlaier bufleft = buflen; 558130617Smlaier bufpkt = 0; 559130617Smlaier 560130617Smlaier return (0); 561130617Smlaier} 562130617Smlaier 563130617Smlaiervoid 564130617Smlaierpurge_buffer(void) 565130617Smlaier{ 566130617Smlaier packets_dropped += bufpkt; 567130617Smlaier 568130617Smlaier set_suspended(0); 569130617Smlaier bufpos = buffer; 570130617Smlaier bufleft = buflen; 571130617Smlaier bufpkt = 0; 572130617Smlaier} 573130617Smlaier 574130617Smlaier/* append packet to the buffer, flushing if necessary */ 575130617Smlaiervoid 576130617Smlaierdump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 577130617Smlaier{ 578130617Smlaier FILE *f = (FILE *)user; 579134578Smlaier#ifdef __FreeBSD__ 580134578Smlaier struct pcap_sf_pkthdr sh; 581134578Smlaier size_t len = sizeof(sh) + h->caplen; 582134578Smlaier#else 583130617Smlaier size_t len = sizeof(*h) + h->caplen; 584134578Smlaier#endif 585130617Smlaier 586130617Smlaier if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) { 587130617Smlaier logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped", 588130617Smlaier len, cur_snaplen, snaplen); 589130617Smlaier packets_dropped++; 590130617Smlaier return; 591130617Smlaier } 592130617Smlaier 593130617Smlaier if (len <= bufleft) 594130617Smlaier goto append; 595130617Smlaier 596130617Smlaier if (suspended) { 597130617Smlaier packets_dropped++; 598130617Smlaier return; 599130617Smlaier } 600130617Smlaier 601130617Smlaier if (flush_buffer(f)) { 602130617Smlaier packets_dropped++; 603130617Smlaier return; 604130617Smlaier } 605130617Smlaier 606130617Smlaier if (len > bufleft) { 607130617Smlaier dump_packet_nobuf(user, h, sp); 608130617Smlaier return; 609130617Smlaier } 610130617Smlaier 611223637Sbz append: 612134578Smlaier#ifdef __FreeBSD__ 613223637Sbz sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec; 614223637Sbz sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec; 615134578Smlaier sh.caplen = h->caplen; 616134578Smlaier sh.len = h->len; 617134578Smlaier 618134578Smlaier memcpy(bufpos, &sh, sizeof(sh)); 619134578Smlaier memcpy(bufpos + sizeof(sh), sp, h->caplen); 620134578Smlaier#else 621130617Smlaier memcpy(bufpos, h, sizeof(*h)); 622130617Smlaier memcpy(bufpos + sizeof(*h), sp, h->caplen); 623134578Smlaier#endif 624130617Smlaier 625130617Smlaier bufpos += len; 626130617Smlaier bufleft -= len; 627130617Smlaier bufpkt++; 628130617Smlaier 629130617Smlaier return; 630130617Smlaier} 631130617Smlaier 632223637Sbzvoid 633223637Sbzlog_pcap_stats(void) 634223637Sbz{ 635223637Sbz struct pcap_stat pstat; 636223637Sbz if (pcap_stats(hpcap, &pstat) < 0) 637223637Sbz logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 638223637Sbz else 639223637Sbz logmsg(LOG_NOTICE, 640223637Sbz "%u packets received, %u/%u dropped (kernel/pflogd)", 641223637Sbz pstat.ps_recv, pstat.ps_drop, packets_dropped); 642223637Sbz} 643223637Sbz 644130617Smlaierint 645126353Smlaiermain(int argc, char **argv) 646126353Smlaier{ 647223637Sbz int ch, np, ret, Xflag = 0; 648130617Smlaier pcap_handler phandler = dump_packet; 649156744Smlaier const char *errstr = NULL; 650223637Sbz char *pidf = NULL; 651126353Smlaier 652223637Sbz ret = 0; 653223637Sbz 654130617Smlaier closefrom(STDERR_FILENO + 1); 655130617Smlaier 656223637Sbz while ((ch = getopt(argc, argv, "Dxd:f:i:p:s:")) != -1) { 657126353Smlaier switch (ch) { 658126353Smlaier case 'D': 659126353Smlaier Debug = 1; 660126353Smlaier break; 661126353Smlaier case 'd': 662145840Smlaier delay = strtonum(optarg, 5, 60*60, &errstr); 663145840Smlaier if (errstr) 664126353Smlaier usage(); 665126353Smlaier break; 666126353Smlaier case 'f': 667126353Smlaier filename = optarg; 668126353Smlaier break; 669171172Smlaier case 'i': 670171172Smlaier interface = optarg; 671171172Smlaier break; 672223637Sbz case 'p': 673223637Sbz pidf = optarg; 674223637Sbz break; 675126353Smlaier case 's': 676145840Smlaier snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN, 677145840Smlaier &errstr); 678126353Smlaier if (snaplen <= 0) 679126353Smlaier snaplen = DEF_SNAPLEN; 680145840Smlaier if (errstr) 681130617Smlaier snaplen = PFLOGD_MAXSNAPLEN; 682126353Smlaier break; 683130617Smlaier case 'x': 684130617Smlaier Xflag++; 685130617Smlaier break; 686126353Smlaier default: 687126353Smlaier usage(); 688126353Smlaier } 689126353Smlaier 690126353Smlaier } 691126353Smlaier 692126353Smlaier log_debug = Debug; 693126353Smlaier argc -= optind; 694126353Smlaier argv += optind; 695126353Smlaier 696223637Sbz /* does interface exist */ 697223637Sbz if (!if_exists(interface)) { 698223637Sbz warn("Failed to initialize: %s", interface); 699223637Sbz logmsg(LOG_ERR, "Failed to initialize: %s", interface); 700223637Sbz logmsg(LOG_ERR, "Exiting, init failure"); 701223637Sbz exit(1); 702223637Sbz } 703223637Sbz 704126353Smlaier if (!Debug) { 705126353Smlaier openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 706126353Smlaier if (daemon(0, 0)) { 707126353Smlaier logmsg(LOG_WARNING, "Failed to become daemon: %s", 708126353Smlaier strerror(errno)); 709126353Smlaier } 710223637Sbz pidfile(pidf); 711126353Smlaier } 712126353Smlaier 713145840Smlaier tzset(); 714126353Smlaier (void)umask(S_IRWXG | S_IRWXO); 715126353Smlaier 716130617Smlaier /* filter will be used by the privileged process */ 717126353Smlaier if (argc) { 718126353Smlaier filter = copy_argv(argv); 719126353Smlaier if (filter == NULL) 720126353Smlaier logmsg(LOG_NOTICE, "Failed to form filter expression"); 721126353Smlaier } 722126353Smlaier 723130617Smlaier /* initialize pcap before dropping privileges */ 724126353Smlaier if (init_pcap()) { 725126353Smlaier logmsg(LOG_ERR, "Exiting, init failure"); 726126353Smlaier exit(1); 727126353Smlaier } 728126353Smlaier 729130617Smlaier /* Privilege separation begins here */ 730130617Smlaier if (priv_init()) { 731130617Smlaier logmsg(LOG_ERR, "unable to privsep"); 732126353Smlaier exit(1); 733126353Smlaier } 734126353Smlaier 735130617Smlaier setproctitle("[initializing]"); 736130617Smlaier /* Process is now unprivileged and inside a chroot */ 737130617Smlaier signal(SIGTERM, sig_close); 738130617Smlaier signal(SIGINT, sig_close); 739130617Smlaier signal(SIGQUIT, sig_close); 740130617Smlaier signal(SIGALRM, sig_alrm); 741223637Sbz signal(SIGUSR1, sig_usr1); 742130617Smlaier signal(SIGHUP, sig_hup); 743130617Smlaier alarm(delay); 744130617Smlaier 745130617Smlaier buffer = malloc(PFLOGD_BUFSIZE); 746130617Smlaier 747130617Smlaier if (buffer == NULL) { 748130617Smlaier logmsg(LOG_WARNING, "Failed to allocate output buffer"); 749130617Smlaier phandler = dump_packet_nobuf; 750130617Smlaier } else { 751130617Smlaier bufleft = buflen = PFLOGD_BUFSIZE; 752130617Smlaier bufpos = buffer; 753130617Smlaier bufpkt = 0; 754130617Smlaier } 755130617Smlaier 756171172Smlaier if (reset_dump(Xflag) < 0) { 757130617Smlaier if (Xflag) 758130617Smlaier return (1); 759130617Smlaier 760130617Smlaier logmsg(LOG_ERR, "Logging suspended: open error"); 761130617Smlaier set_suspended(1); 762130617Smlaier } else if (Xflag) 763130617Smlaier return (0); 764130617Smlaier 765126353Smlaier while (1) { 766130617Smlaier np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, 767145840Smlaier phandler, (u_char *)dpcap); 768136141Smlaier if (np < 0) { 769293015Sdim if (!if_exists(interface)) { 770223637Sbz logmsg(LOG_NOTICE, "interface %s went away", 771223637Sbz interface); 772223637Sbz ret = -1; 773136141Smlaier break; 774136141Smlaier } 775126353Smlaier logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 776136141Smlaier } 777126353Smlaier 778126353Smlaier if (gotsig_close) 779126353Smlaier break; 780126353Smlaier if (gotsig_hup) { 781171172Smlaier if (reset_dump(0)) { 782130617Smlaier logmsg(LOG_ERR, 783130617Smlaier "Logging suspended: open error"); 784130617Smlaier set_suspended(1); 785126353Smlaier } 786126353Smlaier gotsig_hup = 0; 787126353Smlaier } 788126353Smlaier 789126353Smlaier if (gotsig_alrm) { 790130617Smlaier if (dpcap) 791130617Smlaier flush_buffer(dpcap); 792171172Smlaier else 793171172Smlaier gotsig_hup = 1; 794126353Smlaier gotsig_alrm = 0; 795126353Smlaier alarm(delay); 796126353Smlaier } 797223637Sbz 798223637Sbz if (gotsig_usr1) { 799223637Sbz log_pcap_stats(); 800223637Sbz gotsig_usr1 = 0; 801223637Sbz } 802126353Smlaier } 803126353Smlaier 804130617Smlaier logmsg(LOG_NOTICE, "Exiting"); 805130617Smlaier if (dpcap) { 806130617Smlaier flush_buffer(dpcap); 807130617Smlaier fclose(dpcap); 808130617Smlaier } 809130617Smlaier purge_buffer(); 810126353Smlaier 811223637Sbz log_pcap_stats(); 812126353Smlaier pcap_close(hpcap); 813126353Smlaier if (!Debug) 814126353Smlaier closelog(); 815223637Sbz return (ret); 816126353Smlaier} 817