122ac3eadSMax Laier /* $OpenBSD: pflogd.c,v 1.27 2004/02/13 19:01:57 otto Exp $ */ 213b9f610SMax Laier 313b9f610SMax Laier /* 413b9f610SMax Laier * Copyright (c) 2001 Theo de Raadt 513b9f610SMax Laier * Copyright (c) 2001 Can Erkin Acar 613b9f610SMax Laier * All rights reserved. 713b9f610SMax Laier * 813b9f610SMax Laier * Redistribution and use in source and binary forms, with or without 913b9f610SMax Laier * modification, are permitted provided that the following conditions 1013b9f610SMax Laier * are met: 1113b9f610SMax Laier * 1213b9f610SMax Laier * - Redistributions of source code must retain the above copyright 1313b9f610SMax Laier * notice, this list of conditions and the following disclaimer. 1413b9f610SMax Laier * - Redistributions in binary form must reproduce the above 1513b9f610SMax Laier * copyright notice, this list of conditions and the following 1613b9f610SMax Laier * disclaimer in the documentation and/or other materials provided 1713b9f610SMax Laier * with the distribution. 1813b9f610SMax Laier * 1913b9f610SMax Laier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2013b9f610SMax Laier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2113b9f610SMax Laier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2213b9f610SMax Laier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2313b9f610SMax Laier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2413b9f610SMax Laier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2513b9f610SMax Laier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2613b9f610SMax Laier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 2713b9f610SMax Laier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2813b9f610SMax Laier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 2913b9f610SMax Laier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3013b9f610SMax Laier * POSSIBILITY OF SUCH DAMAGE. 3113b9f610SMax Laier */ 3213b9f610SMax Laier 33a10f530fSDavid E. O'Brien #include <sys/cdefs.h> 34a10f530fSDavid E. O'Brien __FBSDID("$FreeBSD$"); 35a10f530fSDavid E. O'Brien 3613b9f610SMax Laier #include <sys/types.h> 3722ac3eadSMax Laier #include <sys/ioctl.h> 3813b9f610SMax Laier #include <sys/file.h> 3913b9f610SMax Laier #include <sys/stat.h> 4013b9f610SMax Laier #include <stdio.h> 4113b9f610SMax Laier #include <stdlib.h> 4213b9f610SMax Laier #include <string.h> 4313b9f610SMax Laier #include <unistd.h> 4413b9f610SMax Laier #include <pcap-int.h> 4513b9f610SMax Laier #include <pcap.h> 4613b9f610SMax Laier #include <syslog.h> 4713b9f610SMax Laier #include <signal.h> 4813b9f610SMax Laier #include <errno.h> 4913b9f610SMax Laier #include <stdarg.h> 5013b9f610SMax Laier #include <fcntl.h> 51b83a49e9SMax Laier #ifdef __FreeBSD__ 528c8618f5SMax Laier #include "pidfile.h" 538c8618f5SMax Laier #else 5413b9f610SMax Laier #include <util.h> 558c8618f5SMax Laier #endif 568c8618f5SMax Laier 5722ac3eadSMax Laier #include "pflogd.h" 5813b9f610SMax Laier 5913b9f610SMax Laier pcap_t *hpcap; 6022ac3eadSMax Laier static FILE *dpcap; 6113b9f610SMax Laier 6213b9f610SMax Laier int Debug = 0; 6322ac3eadSMax Laier static int snaplen = DEF_SNAPLEN; 6422ac3eadSMax Laier static int cur_snaplen = DEF_SNAPLEN; 6513b9f610SMax Laier 6613b9f610SMax Laier volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; 6713b9f610SMax Laier 6813b9f610SMax Laier char *filename = PFLOGD_LOG_FILE; 6913b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF; 7013b9f610SMax Laier char *filter = NULL; 7113b9f610SMax Laier 7213b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE]; 7313b9f610SMax Laier 7413b9f610SMax Laier int log_debug = 0; 7513b9f610SMax Laier unsigned int delay = FLUSH_DELAY; 7613b9f610SMax Laier 7722ac3eadSMax Laier char *copy_argv(char * const *); 7822ac3eadSMax Laier void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); 7922ac3eadSMax Laier void dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *); 8022ac3eadSMax Laier int flush_buffer(FILE *); 8113b9f610SMax Laier int init_pcap(void); 8222ac3eadSMax Laier void logmsg(int, const char *, ...); 8322ac3eadSMax Laier void purge_buffer(void); 8413b9f610SMax Laier int reset_dump(void); 8522ac3eadSMax Laier int scan_dump(FILE *, off_t); 8622ac3eadSMax Laier int set_snaplen(int); 8722ac3eadSMax Laier void set_suspended(int); 8813b9f610SMax Laier void sig_alrm(int); 8913b9f610SMax Laier void sig_close(int); 9013b9f610SMax Laier void sig_hup(int); 9113b9f610SMax Laier void usage(void); 9213b9f610SMax Laier 9322ac3eadSMax Laier /* buffer must always be greater than snaplen */ 9422ac3eadSMax Laier static int bufpkt = 0; /* number of packets in buffer */ 9522ac3eadSMax Laier static int buflen = 0; /* allocated size of buffer */ 9622ac3eadSMax Laier static char *buffer = NULL; /* packet buffer */ 9722ac3eadSMax Laier static char *bufpos = NULL; /* position in buffer */ 9822ac3eadSMax Laier static int bufleft = 0; /* bytes left in buffer */ 9922ac3eadSMax Laier 10022ac3eadSMax Laier /* if error, stop logging but count dropped packets */ 10122ac3eadSMax Laier static int suspended = -1; 10222ac3eadSMax Laier static long packets_dropped = 0; 10322ac3eadSMax Laier 10422ac3eadSMax Laier void 10522ac3eadSMax Laier set_suspended(int s) 10622ac3eadSMax Laier { 10722ac3eadSMax Laier if (suspended == s) 10822ac3eadSMax Laier return; 10922ac3eadSMax Laier 11022ac3eadSMax Laier suspended = s; 11122ac3eadSMax Laier setproctitle("[%s] -s %d -f %s", 11222ac3eadSMax Laier suspended ? "suspended" : "running", cur_snaplen, filename); 11322ac3eadSMax Laier } 11413b9f610SMax Laier 11513b9f610SMax Laier char * 11613b9f610SMax Laier copy_argv(char * const *argv) 11713b9f610SMax Laier { 11813b9f610SMax Laier size_t len = 0, n; 11913b9f610SMax Laier char *buf; 12013b9f610SMax Laier 12113b9f610SMax Laier if (argv == NULL) 12213b9f610SMax Laier return (NULL); 12313b9f610SMax Laier 12413b9f610SMax Laier for (n = 0; argv[n]; n++) 12513b9f610SMax Laier len += strlen(argv[n])+1; 12613b9f610SMax Laier if (len == 0) 12713b9f610SMax Laier return (NULL); 12813b9f610SMax Laier 12913b9f610SMax Laier buf = malloc(len); 13013b9f610SMax Laier if (buf == NULL) 13113b9f610SMax Laier return (NULL); 13213b9f610SMax Laier 13313b9f610SMax Laier strlcpy(buf, argv[0], len); 13413b9f610SMax Laier for (n = 1; argv[n]; n++) { 13513b9f610SMax Laier strlcat(buf, " ", len); 13613b9f610SMax Laier strlcat(buf, argv[n], len); 13713b9f610SMax Laier } 13813b9f610SMax Laier return (buf); 13913b9f610SMax Laier } 14013b9f610SMax Laier 14113b9f610SMax Laier void 14213b9f610SMax Laier logmsg(int pri, const char *message, ...) 14313b9f610SMax Laier { 14413b9f610SMax Laier va_list ap; 14513b9f610SMax Laier va_start(ap, message); 14613b9f610SMax Laier 14713b9f610SMax Laier if (log_debug) { 14813b9f610SMax Laier vfprintf(stderr, message, ap); 14913b9f610SMax Laier fprintf(stderr, "\n"); 15013b9f610SMax Laier } else 15113b9f610SMax Laier vsyslog(pri, message, ap); 15213b9f610SMax Laier va_end(ap); 15313b9f610SMax Laier } 15413b9f610SMax Laier 155b83a49e9SMax Laier #ifdef __FreeBSD__ 156b83a49e9SMax Laier __dead2 void 157b83a49e9SMax Laier #else 15813b9f610SMax Laier __dead void 159b83a49e9SMax Laier #endif 16013b9f610SMax Laier usage(void) 16113b9f610SMax Laier { 16222ac3eadSMax Laier fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename] "); 16313b9f610SMax Laier fprintf(stderr, "[-s snaplen] [expression]\n"); 16413b9f610SMax Laier exit(1); 16513b9f610SMax Laier } 16613b9f610SMax Laier 16713b9f610SMax Laier void 16813b9f610SMax Laier sig_close(int sig) 16913b9f610SMax Laier { 17013b9f610SMax Laier gotsig_close = 1; 17113b9f610SMax Laier } 17213b9f610SMax Laier 17313b9f610SMax Laier void 17413b9f610SMax Laier sig_hup(int sig) 17513b9f610SMax Laier { 17613b9f610SMax Laier gotsig_hup = 1; 17713b9f610SMax Laier } 17813b9f610SMax Laier 17913b9f610SMax Laier void 18013b9f610SMax Laier sig_alrm(int sig) 18113b9f610SMax Laier { 18213b9f610SMax Laier gotsig_alrm = 1; 18313b9f610SMax Laier } 18413b9f610SMax Laier 18522ac3eadSMax Laier void 18622ac3eadSMax Laier set_pcap_filter(void) 18713b9f610SMax Laier { 18813b9f610SMax Laier struct bpf_program bprog; 18913b9f610SMax Laier 19013b9f610SMax Laier if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 19113b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 19222ac3eadSMax Laier else { 19322ac3eadSMax Laier if (pcap_setfilter(hpcap, &bprog) < 0) 19413b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 19522ac3eadSMax Laier pcap_freecode(&bprog); 19622ac3eadSMax Laier } 19722ac3eadSMax Laier } 19822ac3eadSMax Laier 19922ac3eadSMax Laier int 20022ac3eadSMax Laier init_pcap(void) 20122ac3eadSMax Laier { 20222ac3eadSMax Laier hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 20322ac3eadSMax Laier if (hpcap == NULL) { 20422ac3eadSMax Laier logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 20522ac3eadSMax Laier return (-1); 20622ac3eadSMax Laier } 20713b9f610SMax Laier 20813b9f610SMax Laier if (pcap_datalink(hpcap) != DLT_PFLOG) { 20913b9f610SMax Laier logmsg(LOG_ERR, "Invalid datalink type"); 21013b9f610SMax Laier pcap_close(hpcap); 21122ac3eadSMax Laier hpcap = NULL; 21213b9f610SMax Laier return (-1); 21313b9f610SMax Laier } 21413b9f610SMax Laier 21522ac3eadSMax Laier set_pcap_filter(); 21613b9f610SMax Laier 21722ac3eadSMax Laier cur_snaplen = snaplen = pcap_snapshot(hpcap); 21822ac3eadSMax Laier 21922ac3eadSMax Laier #ifdef __FreeBSD__ 22022ac3eadSMax Laier /* We can not lock bpf devices ... yet */ 22122ac3eadSMax Laier #else 22222ac3eadSMax Laier /* lock */ 22322ac3eadSMax Laier if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) { 22422ac3eadSMax Laier logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); 22522ac3eadSMax Laier return (-1); 22622ac3eadSMax Laier } 22722ac3eadSMax Laier #endif 22822ac3eadSMax Laier 22922ac3eadSMax Laier return (0); 23022ac3eadSMax Laier } 23122ac3eadSMax Laier 23222ac3eadSMax Laier int 23322ac3eadSMax Laier set_snaplen(int snap) 23422ac3eadSMax Laier { 23522ac3eadSMax Laier if (priv_set_snaplen(snap)) 23622ac3eadSMax Laier return (1); 23722ac3eadSMax Laier 23822ac3eadSMax Laier if (cur_snaplen > snap) 23922ac3eadSMax Laier purge_buffer(); 24022ac3eadSMax Laier 24122ac3eadSMax Laier cur_snaplen = snap; 24222ac3eadSMax Laier 24313b9f610SMax Laier return (0); 24413b9f610SMax Laier } 24513b9f610SMax Laier 24613b9f610SMax Laier int 24713b9f610SMax Laier reset_dump(void) 24813b9f610SMax Laier { 24913b9f610SMax Laier struct pcap_file_header hdr; 25013b9f610SMax Laier struct stat st; 25122ac3eadSMax Laier int fd; 25213b9f610SMax Laier FILE *fp; 25313b9f610SMax Laier 25413b9f610SMax Laier if (hpcap == NULL) 25522ac3eadSMax Laier return (-1); 25622ac3eadSMax Laier 25713b9f610SMax Laier if (dpcap) { 25822ac3eadSMax Laier flush_buffer(dpcap); 25922ac3eadSMax Laier fclose(dpcap); 26022ac3eadSMax Laier dpcap = NULL; 26113b9f610SMax Laier } 26213b9f610SMax Laier 26313b9f610SMax Laier /* 26413b9f610SMax Laier * Basically reimplement pcap_dump_open() because it truncates 26513b9f610SMax Laier * files and duplicates headers and such. 26613b9f610SMax Laier */ 26722ac3eadSMax Laier fd = priv_open_log(); 26822ac3eadSMax Laier if (fd < 0) 26922ac3eadSMax Laier return (1); 27022ac3eadSMax Laier 27122ac3eadSMax Laier fp = fdopen(fd, "a+"); 27222ac3eadSMax Laier 27313b9f610SMax Laier if (fp == NULL) { 27422ac3eadSMax Laier logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 27513b9f610SMax Laier return (1); 27613b9f610SMax Laier } 27713b9f610SMax Laier if (fstat(fileno(fp), &st) == -1) { 27822ac3eadSMax Laier logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 27913b9f610SMax Laier return (1); 28013b9f610SMax Laier } 28113b9f610SMax Laier 28222ac3eadSMax Laier /* set FILE unbuffered, we do our own buffering */ 28322ac3eadSMax Laier if (setvbuf(fp, NULL, _IONBF, 0)) { 28422ac3eadSMax Laier logmsg(LOG_ERR, "Failed to set output buffers"); 28522ac3eadSMax Laier return (1); 28622ac3eadSMax Laier } 28713b9f610SMax Laier 28813b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4 28913b9f610SMax Laier 29013b9f610SMax Laier if (st.st_size == 0) { 29122ac3eadSMax Laier if (snaplen != cur_snaplen) { 29213b9f610SMax Laier logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 29322ac3eadSMax Laier if (set_snaplen(snaplen)) { 29422ac3eadSMax Laier logmsg(LOG_WARNING, 29522ac3eadSMax Laier "Failed, using old settings"); 29613b9f610SMax Laier } 29713b9f610SMax Laier } 29813b9f610SMax Laier hdr.magic = TCPDUMP_MAGIC; 29913b9f610SMax Laier hdr.version_major = PCAP_VERSION_MAJOR; 30013b9f610SMax Laier hdr.version_minor = PCAP_VERSION_MINOR; 30113b9f610SMax Laier hdr.thiszone = hpcap->tzoff; 30213b9f610SMax Laier hdr.snaplen = hpcap->snapshot; 30313b9f610SMax Laier hdr.sigfigs = 0; 30413b9f610SMax Laier hdr.linktype = hpcap->linktype; 30513b9f610SMax Laier 30613b9f610SMax Laier if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 30713b9f610SMax Laier fclose(fp); 30813b9f610SMax Laier return (1); 30913b9f610SMax Laier } 31022ac3eadSMax Laier } else if (scan_dump(fp, st.st_size)) { 31122ac3eadSMax Laier /* XXX move file and continue? */ 31222ac3eadSMax Laier fclose(fp); 31322ac3eadSMax Laier return (1); 31413b9f610SMax Laier } 31522ac3eadSMax Laier 31622ac3eadSMax Laier dpcap = fp; 31722ac3eadSMax Laier 31822ac3eadSMax Laier set_suspended(0); 31922ac3eadSMax Laier flush_buffer(fp); 32022ac3eadSMax Laier 32122ac3eadSMax Laier return (0); 32222ac3eadSMax Laier } 32322ac3eadSMax Laier 32422ac3eadSMax Laier int 32522ac3eadSMax Laier scan_dump(FILE *fp, off_t size) 32622ac3eadSMax Laier { 32722ac3eadSMax Laier struct pcap_file_header hdr; 32822ac3eadSMax Laier struct pcap_pkthdr ph; 32922ac3eadSMax Laier off_t pos; 33022ac3eadSMax Laier 33122ac3eadSMax Laier /* 33222ac3eadSMax Laier * Must read the file, compare the header against our new 33322ac3eadSMax Laier * options (in particular, snaplen) and adjust our options so 33422ac3eadSMax Laier * that we generate a correct file. Furthermore, check the file 33522ac3eadSMax Laier * for consistency so that we can append safely. 33622ac3eadSMax Laier * 33722ac3eadSMax Laier * XXX this may take a long time for large logs. 33822ac3eadSMax Laier */ 33922ac3eadSMax Laier (void) fseek(fp, 0L, SEEK_SET); 34022ac3eadSMax Laier 34122ac3eadSMax Laier if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 34222ac3eadSMax Laier logmsg(LOG_ERR, "Short file header"); 34322ac3eadSMax Laier return (1); 34422ac3eadSMax Laier } 34522ac3eadSMax Laier 34622ac3eadSMax Laier if (hdr.magic != TCPDUMP_MAGIC || 34722ac3eadSMax Laier hdr.version_major != PCAP_VERSION_MAJOR || 34822ac3eadSMax Laier hdr.version_minor != PCAP_VERSION_MINOR || 34922ac3eadSMax Laier hdr.linktype != hpcap->linktype || 35022ac3eadSMax Laier hdr.snaplen > PFLOGD_MAXSNAPLEN) { 35122ac3eadSMax Laier logmsg(LOG_ERR, "Invalid/incompatible log file, move it away"); 35222ac3eadSMax Laier return (1); 35322ac3eadSMax Laier } 35422ac3eadSMax Laier 35522ac3eadSMax Laier pos = sizeof(hdr); 35622ac3eadSMax Laier 35722ac3eadSMax Laier while (!feof(fp)) { 35822ac3eadSMax Laier off_t len = fread((char *)&ph, 1, sizeof(ph), fp); 35922ac3eadSMax Laier if (len == 0) 36022ac3eadSMax Laier break; 36122ac3eadSMax Laier 36222ac3eadSMax Laier if (len != sizeof(ph)) 36322ac3eadSMax Laier goto error; 36422ac3eadSMax Laier if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN) 36522ac3eadSMax Laier goto error; 36622ac3eadSMax Laier pos += sizeof(ph) + ph.caplen; 36722ac3eadSMax Laier if (pos > size) 36822ac3eadSMax Laier goto error; 36922ac3eadSMax Laier fseek(fp, ph.caplen, SEEK_CUR); 37022ac3eadSMax Laier } 37122ac3eadSMax Laier 37222ac3eadSMax Laier if (pos != size) 37322ac3eadSMax Laier goto error; 37422ac3eadSMax Laier 37522ac3eadSMax Laier if (hdr.snaplen != cur_snaplen) { 37622ac3eadSMax Laier logmsg(LOG_WARNING, 37722ac3eadSMax Laier "Existing file has different snaplen %u, using it", 37822ac3eadSMax Laier hdr.snaplen); 37922ac3eadSMax Laier if (set_snaplen(hdr.snaplen)) { 38022ac3eadSMax Laier logmsg(LOG_WARNING, 38122ac3eadSMax Laier "Failed, using old settings, offset %llu", 38222ac3eadSMax Laier (unsigned long long) size); 38313b9f610SMax Laier } 38413b9f610SMax Laier } 38513b9f610SMax Laier 38613b9f610SMax Laier return (0); 38722ac3eadSMax Laier 38822ac3eadSMax Laier error: 38922ac3eadSMax Laier logmsg(LOG_ERR, "Corrupted log file."); 39022ac3eadSMax Laier return (1); 39122ac3eadSMax Laier } 39222ac3eadSMax Laier 39322ac3eadSMax Laier /* dump a packet directly to the stream, which is unbuffered */ 39422ac3eadSMax Laier void 39522ac3eadSMax Laier dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 39622ac3eadSMax Laier { 39722ac3eadSMax Laier FILE *f = (FILE *)user; 39822ac3eadSMax Laier 39922ac3eadSMax Laier if (suspended) { 40022ac3eadSMax Laier packets_dropped++; 40122ac3eadSMax Laier return; 40222ac3eadSMax Laier } 40322ac3eadSMax Laier 40422ac3eadSMax Laier if (fwrite((char *)h, sizeof(*h), 1, f) != 1) { 40522ac3eadSMax Laier /* try to undo header to prevent corruption */ 40622ac3eadSMax Laier off_t pos = ftello(f); 40722ac3eadSMax Laier if (pos < sizeof(*h) || 40822ac3eadSMax Laier ftruncate(fileno(f), pos - sizeof(*h))) { 40922ac3eadSMax Laier logmsg(LOG_ERR, "Write failed, corrupted logfile!"); 41022ac3eadSMax Laier set_suspended(1); 41122ac3eadSMax Laier gotsig_close = 1; 41222ac3eadSMax Laier return; 41322ac3eadSMax Laier } 41422ac3eadSMax Laier goto error; 41522ac3eadSMax Laier } 41622ac3eadSMax Laier 41722ac3eadSMax Laier if (fwrite((char *)sp, h->caplen, 1, f) != 1) 41822ac3eadSMax Laier goto error; 41922ac3eadSMax Laier 42022ac3eadSMax Laier return; 42122ac3eadSMax Laier 42222ac3eadSMax Laier error: 42322ac3eadSMax Laier set_suspended(1); 42422ac3eadSMax Laier packets_dropped ++; 42522ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno)); 42622ac3eadSMax Laier } 42722ac3eadSMax Laier 42822ac3eadSMax Laier int 42922ac3eadSMax Laier flush_buffer(FILE *f) 43022ac3eadSMax Laier { 43122ac3eadSMax Laier off_t offset; 43222ac3eadSMax Laier int len = bufpos - buffer; 43322ac3eadSMax Laier 43422ac3eadSMax Laier if (len <= 0) 43522ac3eadSMax Laier return (0); 43622ac3eadSMax Laier 43722ac3eadSMax Laier offset = ftello(f); 43822ac3eadSMax Laier if (offset == (off_t)-1) { 43922ac3eadSMax Laier set_suspended(1); 44022ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: ftello: %s", 44122ac3eadSMax Laier strerror(errno)); 44222ac3eadSMax Laier return (1); 44322ac3eadSMax Laier } 44422ac3eadSMax Laier 44522ac3eadSMax Laier if (fwrite(buffer, len, 1, f) != 1) { 44622ac3eadSMax Laier set_suspended(1); 44722ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", 44822ac3eadSMax Laier strerror(errno)); 44922ac3eadSMax Laier ftruncate(fileno(f), offset); 45022ac3eadSMax Laier return (1); 45122ac3eadSMax Laier } 45222ac3eadSMax Laier 45322ac3eadSMax Laier set_suspended(0); 45422ac3eadSMax Laier bufpos = buffer; 45522ac3eadSMax Laier bufleft = buflen; 45622ac3eadSMax Laier bufpkt = 0; 45722ac3eadSMax Laier 45822ac3eadSMax Laier return (0); 45922ac3eadSMax Laier } 46022ac3eadSMax Laier 46122ac3eadSMax Laier void 46222ac3eadSMax Laier purge_buffer(void) 46322ac3eadSMax Laier { 46422ac3eadSMax Laier packets_dropped += bufpkt; 46522ac3eadSMax Laier 46622ac3eadSMax Laier set_suspended(0); 46722ac3eadSMax Laier bufpos = buffer; 46822ac3eadSMax Laier bufleft = buflen; 46922ac3eadSMax Laier bufpkt = 0; 47022ac3eadSMax Laier } 47122ac3eadSMax Laier 47222ac3eadSMax Laier /* append packet to the buffer, flushing if necessary */ 47322ac3eadSMax Laier void 47422ac3eadSMax Laier dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 47522ac3eadSMax Laier { 47622ac3eadSMax Laier FILE *f = (FILE *)user; 47722ac3eadSMax Laier size_t len = sizeof(*h) + h->caplen; 47822ac3eadSMax Laier 47922ac3eadSMax Laier if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) { 48022ac3eadSMax Laier logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped", 48122ac3eadSMax Laier len, cur_snaplen, snaplen); 48222ac3eadSMax Laier packets_dropped++; 48322ac3eadSMax Laier return; 48422ac3eadSMax Laier } 48522ac3eadSMax Laier 48622ac3eadSMax Laier if (len <= bufleft) 48722ac3eadSMax Laier goto append; 48822ac3eadSMax Laier 48922ac3eadSMax Laier if (suspended) { 49022ac3eadSMax Laier packets_dropped++; 49122ac3eadSMax Laier return; 49222ac3eadSMax Laier } 49322ac3eadSMax Laier 49422ac3eadSMax Laier if (flush_buffer(f)) { 49522ac3eadSMax Laier packets_dropped++; 49622ac3eadSMax Laier return; 49722ac3eadSMax Laier } 49822ac3eadSMax Laier 49922ac3eadSMax Laier if (len > bufleft) { 50022ac3eadSMax Laier dump_packet_nobuf(user, h, sp); 50122ac3eadSMax Laier return; 50222ac3eadSMax Laier } 50322ac3eadSMax Laier 50422ac3eadSMax Laier append: 50522ac3eadSMax Laier memcpy(bufpos, h, sizeof(*h)); 50622ac3eadSMax Laier memcpy(bufpos + sizeof(*h), sp, h->caplen); 50722ac3eadSMax Laier 50822ac3eadSMax Laier bufpos += len; 50922ac3eadSMax Laier bufleft -= len; 51022ac3eadSMax Laier bufpkt++; 51122ac3eadSMax Laier 51222ac3eadSMax Laier return; 51313b9f610SMax Laier } 51413b9f610SMax Laier 51513b9f610SMax Laier int 51613b9f610SMax Laier main(int argc, char **argv) 51713b9f610SMax Laier { 51813b9f610SMax Laier struct pcap_stat pstat; 51922ac3eadSMax Laier int ch, np, Xflag = 0; 52022ac3eadSMax Laier pcap_handler phandler = dump_packet; 52113b9f610SMax Laier 52222ac3eadSMax Laier #ifdef __FreeBSD__ 52322ac3eadSMax Laier /* another ?paranoid? safety measure we do not have */ 52422ac3eadSMax Laier #else 52522ac3eadSMax Laier closefrom(STDERR_FILENO + 1); 52622ac3eadSMax Laier #endif 52722ac3eadSMax Laier 52822ac3eadSMax Laier while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) { 52913b9f610SMax Laier switch (ch) { 53013b9f610SMax Laier case 'D': 53113b9f610SMax Laier Debug = 1; 53213b9f610SMax Laier break; 53313b9f610SMax Laier case 'd': 53413b9f610SMax Laier delay = atoi(optarg); 53513b9f610SMax Laier if (delay < 5 || delay > 60*60) 53613b9f610SMax Laier usage(); 53713b9f610SMax Laier break; 53813b9f610SMax Laier case 'f': 53913b9f610SMax Laier filename = optarg; 54013b9f610SMax Laier break; 54113b9f610SMax Laier case 's': 54213b9f610SMax Laier snaplen = atoi(optarg); 54313b9f610SMax Laier if (snaplen <= 0) 54413b9f610SMax Laier snaplen = DEF_SNAPLEN; 54522ac3eadSMax Laier if (snaplen > PFLOGD_MAXSNAPLEN) 54622ac3eadSMax Laier snaplen = PFLOGD_MAXSNAPLEN; 54722ac3eadSMax Laier break; 54822ac3eadSMax Laier case 'x': 54922ac3eadSMax Laier Xflag++; 55013b9f610SMax Laier break; 55113b9f610SMax Laier default: 55213b9f610SMax Laier usage(); 55313b9f610SMax Laier } 55413b9f610SMax Laier 55513b9f610SMax Laier } 55613b9f610SMax Laier 55713b9f610SMax Laier log_debug = Debug; 55813b9f610SMax Laier argc -= optind; 55913b9f610SMax Laier argv += optind; 56013b9f610SMax Laier 56113b9f610SMax Laier if (!Debug) { 56213b9f610SMax Laier openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 56313b9f610SMax Laier if (daemon(0, 0)) { 56413b9f610SMax Laier logmsg(LOG_WARNING, "Failed to become daemon: %s", 56513b9f610SMax Laier strerror(errno)); 56613b9f610SMax Laier } 56713b9f610SMax Laier pidfile(NULL); 56813b9f610SMax Laier } 56913b9f610SMax Laier 57013b9f610SMax Laier (void)umask(S_IRWXG | S_IRWXO); 57113b9f610SMax Laier 57222ac3eadSMax Laier /* filter will be used by the privileged process */ 57322ac3eadSMax Laier if (argc) { 57422ac3eadSMax Laier filter = copy_argv(argv); 57522ac3eadSMax Laier if (filter == NULL) 57622ac3eadSMax Laier logmsg(LOG_NOTICE, "Failed to form filter expression"); 57722ac3eadSMax Laier } 57822ac3eadSMax Laier 57922ac3eadSMax Laier /* initialize pcap before dropping privileges */ 58022ac3eadSMax Laier if (init_pcap()) { 58122ac3eadSMax Laier logmsg(LOG_ERR, "Exiting, init failure"); 58222ac3eadSMax Laier exit(1); 58322ac3eadSMax Laier } 58422ac3eadSMax Laier 58522ac3eadSMax Laier /* Privilege separation begins here */ 58622ac3eadSMax Laier if (priv_init()) { 58722ac3eadSMax Laier logmsg(LOG_ERR, "unable to privsep"); 58822ac3eadSMax Laier exit(1); 58922ac3eadSMax Laier } 59022ac3eadSMax Laier 59122ac3eadSMax Laier setproctitle("[initializing]"); 59222ac3eadSMax Laier /* Process is now unprivileged and inside a chroot */ 59313b9f610SMax Laier signal(SIGTERM, sig_close); 59413b9f610SMax Laier signal(SIGINT, sig_close); 59513b9f610SMax Laier signal(SIGQUIT, sig_close); 59613b9f610SMax Laier signal(SIGALRM, sig_alrm); 59713b9f610SMax Laier signal(SIGHUP, sig_hup); 59813b9f610SMax Laier alarm(delay); 59913b9f610SMax Laier 60022ac3eadSMax Laier buffer = malloc(PFLOGD_BUFSIZE); 60113b9f610SMax Laier 60222ac3eadSMax Laier if (buffer == NULL) { 60322ac3eadSMax Laier logmsg(LOG_WARNING, "Failed to allocate output buffer"); 60422ac3eadSMax Laier phandler = dump_packet_nobuf; 60522ac3eadSMax Laier } else { 60622ac3eadSMax Laier bufleft = buflen = PFLOGD_BUFSIZE; 60722ac3eadSMax Laier bufpos = buffer; 60822ac3eadSMax Laier bufpkt = 0; 60913b9f610SMax Laier } 61013b9f610SMax Laier 61113b9f610SMax Laier if (reset_dump()) { 61222ac3eadSMax Laier if (Xflag) 61322ac3eadSMax Laier return (1); 61422ac3eadSMax Laier 61522ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: open error"); 61622ac3eadSMax Laier set_suspended(1); 61722ac3eadSMax Laier } else if (Xflag) 61822ac3eadSMax Laier return (0); 61913b9f610SMax Laier 62013b9f610SMax Laier while (1) { 62122ac3eadSMax Laier np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, 62222ac3eadSMax Laier dump_packet, (u_char *)dpcap); 62313b9f610SMax Laier if (np < 0) 62413b9f610SMax Laier logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 62513b9f610SMax Laier 62613b9f610SMax Laier if (gotsig_close) 62713b9f610SMax Laier break; 62813b9f610SMax Laier if (gotsig_hup) { 62913b9f610SMax Laier if (reset_dump()) { 63022ac3eadSMax Laier logmsg(LOG_ERR, 63122ac3eadSMax Laier "Logging suspended: open error"); 63222ac3eadSMax Laier set_suspended(1); 63313b9f610SMax Laier } 63413b9f610SMax Laier gotsig_hup = 0; 63513b9f610SMax Laier } 63613b9f610SMax Laier 63713b9f610SMax Laier if (gotsig_alrm) { 63822ac3eadSMax Laier if (dpcap) 63922ac3eadSMax Laier flush_buffer(dpcap); 64013b9f610SMax Laier gotsig_alrm = 0; 64113b9f610SMax Laier alarm(delay); 64213b9f610SMax Laier } 64313b9f610SMax Laier } 64413b9f610SMax Laier 64522ac3eadSMax Laier logmsg(LOG_NOTICE, "Exiting"); 64622ac3eadSMax Laier if (dpcap) { 64722ac3eadSMax Laier flush_buffer(dpcap); 64822ac3eadSMax Laier fclose(dpcap); 64922ac3eadSMax Laier } 65022ac3eadSMax Laier purge_buffer(); 65113b9f610SMax Laier 65213b9f610SMax Laier if (pcap_stats(hpcap, &pstat) < 0) 65313b9f610SMax Laier logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 65413b9f610SMax Laier else 65522ac3eadSMax Laier logmsg(LOG_NOTICE, 65622ac3eadSMax Laier "%u packets received, %u/%u dropped (kernel/pflogd)", 65722ac3eadSMax Laier pstat.ps_recv, pstat.ps_drop, packets_dropped); 65813b9f610SMax Laier 65913b9f610SMax Laier pcap_close(hpcap); 66013b9f610SMax Laier if (!Debug) 66113b9f610SMax Laier closelog(); 66213b9f610SMax Laier return (0); 66313b9f610SMax Laier } 664