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; 3286964e37dSMax Laier #ifdef __FreeBSD__ 3296964e37dSMax Laier struct pcap_sf_pkthdr ph; 3306964e37dSMax Laier #else 33122ac3eadSMax Laier struct pcap_pkthdr ph; 3326964e37dSMax Laier #endif 33322ac3eadSMax Laier off_t pos; 33422ac3eadSMax Laier 33522ac3eadSMax Laier /* 33622ac3eadSMax Laier * Must read the file, compare the header against our new 33722ac3eadSMax Laier * options (in particular, snaplen) and adjust our options so 33822ac3eadSMax Laier * that we generate a correct file. Furthermore, check the file 33922ac3eadSMax Laier * for consistency so that we can append safely. 34022ac3eadSMax Laier * 34122ac3eadSMax Laier * XXX this may take a long time for large logs. 34222ac3eadSMax Laier */ 34322ac3eadSMax Laier (void) fseek(fp, 0L, SEEK_SET); 34422ac3eadSMax Laier 34522ac3eadSMax Laier if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 34622ac3eadSMax Laier logmsg(LOG_ERR, "Short file header"); 34722ac3eadSMax Laier return (1); 34822ac3eadSMax Laier } 34922ac3eadSMax Laier 35022ac3eadSMax Laier if (hdr.magic != TCPDUMP_MAGIC || 35122ac3eadSMax Laier hdr.version_major != PCAP_VERSION_MAJOR || 35222ac3eadSMax Laier hdr.version_minor != PCAP_VERSION_MINOR || 35322ac3eadSMax Laier hdr.linktype != hpcap->linktype || 35422ac3eadSMax Laier hdr.snaplen > PFLOGD_MAXSNAPLEN) { 35522ac3eadSMax Laier logmsg(LOG_ERR, "Invalid/incompatible log file, move it away"); 35622ac3eadSMax Laier return (1); 35722ac3eadSMax Laier } 35822ac3eadSMax Laier 35922ac3eadSMax Laier pos = sizeof(hdr); 36022ac3eadSMax Laier 36122ac3eadSMax Laier while (!feof(fp)) { 36222ac3eadSMax Laier off_t len = fread((char *)&ph, 1, sizeof(ph), fp); 36322ac3eadSMax Laier if (len == 0) 36422ac3eadSMax Laier break; 36522ac3eadSMax Laier 36622ac3eadSMax Laier if (len != sizeof(ph)) 36722ac3eadSMax Laier goto error; 36822ac3eadSMax Laier if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN) 36922ac3eadSMax Laier goto error; 37022ac3eadSMax Laier pos += sizeof(ph) + ph.caplen; 37122ac3eadSMax Laier if (pos > size) 37222ac3eadSMax Laier goto error; 37322ac3eadSMax Laier fseek(fp, ph.caplen, SEEK_CUR); 37422ac3eadSMax Laier } 37522ac3eadSMax Laier 37622ac3eadSMax Laier if (pos != size) 37722ac3eadSMax Laier goto error; 37822ac3eadSMax Laier 37922ac3eadSMax Laier if (hdr.snaplen != cur_snaplen) { 38022ac3eadSMax Laier logmsg(LOG_WARNING, 38122ac3eadSMax Laier "Existing file has different snaplen %u, using it", 38222ac3eadSMax Laier hdr.snaplen); 38322ac3eadSMax Laier if (set_snaplen(hdr.snaplen)) { 38422ac3eadSMax Laier logmsg(LOG_WARNING, 38522ac3eadSMax Laier "Failed, using old settings, offset %llu", 38622ac3eadSMax Laier (unsigned long long) size); 38713b9f610SMax Laier } 38813b9f610SMax Laier } 38913b9f610SMax Laier 39013b9f610SMax Laier return (0); 39122ac3eadSMax Laier 39222ac3eadSMax Laier error: 39322ac3eadSMax Laier logmsg(LOG_ERR, "Corrupted log file."); 39422ac3eadSMax Laier return (1); 39522ac3eadSMax Laier } 39622ac3eadSMax Laier 39722ac3eadSMax Laier /* dump a packet directly to the stream, which is unbuffered */ 39822ac3eadSMax Laier void 39922ac3eadSMax Laier dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 40022ac3eadSMax Laier { 40122ac3eadSMax Laier FILE *f = (FILE *)user; 4026964e37dSMax Laier #ifdef __FreeBSD__ 4036964e37dSMax Laier struct pcap_sf_pkthdr sh; 4046964e37dSMax Laier #endif 40522ac3eadSMax Laier 40622ac3eadSMax Laier if (suspended) { 40722ac3eadSMax Laier packets_dropped++; 40822ac3eadSMax Laier return; 40922ac3eadSMax Laier } 41022ac3eadSMax Laier 4116964e37dSMax Laier #ifdef __FreeBSD__ 4126964e37dSMax Laier sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec; 4136964e37dSMax Laier sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec; 4146964e37dSMax Laier sh.caplen = h->caplen; 4156964e37dSMax Laier sh.len = h->len; 4166964e37dSMax Laier 4176964e37dSMax Laier if (fwrite((char *)&sh, sizeof(sh), 1, f) != 1) { 4186964e37dSMax Laier #else 41922ac3eadSMax Laier if (fwrite((char *)h, sizeof(*h), 1, f) != 1) { 4206964e37dSMax Laier #endif 42122ac3eadSMax Laier /* try to undo header to prevent corruption */ 42222ac3eadSMax Laier off_t pos = ftello(f); 4236964e37dSMax Laier #ifdef __FreeBSD__ 4246964e37dSMax Laier if (pos < sizeof(sh) || 4256964e37dSMax Laier ftruncate(fileno(f), pos - sizeof(sh))) { 4266964e37dSMax Laier #else 42722ac3eadSMax Laier if (pos < sizeof(*h) || 42822ac3eadSMax Laier ftruncate(fileno(f), pos - sizeof(*h))) { 4296964e37dSMax Laier #endif 43022ac3eadSMax Laier logmsg(LOG_ERR, "Write failed, corrupted logfile!"); 43122ac3eadSMax Laier set_suspended(1); 43222ac3eadSMax Laier gotsig_close = 1; 43322ac3eadSMax Laier return; 43422ac3eadSMax Laier } 43522ac3eadSMax Laier goto error; 43622ac3eadSMax Laier } 43722ac3eadSMax Laier 43822ac3eadSMax Laier if (fwrite((char *)sp, h->caplen, 1, f) != 1) 43922ac3eadSMax Laier goto error; 44022ac3eadSMax Laier 44122ac3eadSMax Laier return; 44222ac3eadSMax Laier 44322ac3eadSMax Laier error: 44422ac3eadSMax Laier set_suspended(1); 44522ac3eadSMax Laier packets_dropped ++; 44622ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno)); 44722ac3eadSMax Laier } 44822ac3eadSMax Laier 44922ac3eadSMax Laier int 45022ac3eadSMax Laier flush_buffer(FILE *f) 45122ac3eadSMax Laier { 45222ac3eadSMax Laier off_t offset; 45322ac3eadSMax Laier int len = bufpos - buffer; 45422ac3eadSMax Laier 45522ac3eadSMax Laier if (len <= 0) 45622ac3eadSMax Laier return (0); 45722ac3eadSMax Laier 45822ac3eadSMax Laier offset = ftello(f); 45922ac3eadSMax Laier if (offset == (off_t)-1) { 46022ac3eadSMax Laier set_suspended(1); 46122ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: ftello: %s", 46222ac3eadSMax Laier strerror(errno)); 46322ac3eadSMax Laier return (1); 46422ac3eadSMax Laier } 46522ac3eadSMax Laier 46622ac3eadSMax Laier if (fwrite(buffer, len, 1, f) != 1) { 46722ac3eadSMax Laier set_suspended(1); 46822ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", 46922ac3eadSMax Laier strerror(errno)); 47022ac3eadSMax Laier ftruncate(fileno(f), offset); 47122ac3eadSMax Laier return (1); 47222ac3eadSMax Laier } 47322ac3eadSMax Laier 47422ac3eadSMax Laier set_suspended(0); 47522ac3eadSMax Laier bufpos = buffer; 47622ac3eadSMax Laier bufleft = buflen; 47722ac3eadSMax Laier bufpkt = 0; 47822ac3eadSMax Laier 47922ac3eadSMax Laier return (0); 48022ac3eadSMax Laier } 48122ac3eadSMax Laier 48222ac3eadSMax Laier void 48322ac3eadSMax Laier purge_buffer(void) 48422ac3eadSMax Laier { 48522ac3eadSMax Laier packets_dropped += bufpkt; 48622ac3eadSMax Laier 48722ac3eadSMax Laier set_suspended(0); 48822ac3eadSMax Laier bufpos = buffer; 48922ac3eadSMax Laier bufleft = buflen; 49022ac3eadSMax Laier bufpkt = 0; 49122ac3eadSMax Laier } 49222ac3eadSMax Laier 49322ac3eadSMax Laier /* append packet to the buffer, flushing if necessary */ 49422ac3eadSMax Laier void 49522ac3eadSMax Laier dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 49622ac3eadSMax Laier { 49722ac3eadSMax Laier FILE *f = (FILE *)user; 4986964e37dSMax Laier #ifdef __FreeBSD__ 4996964e37dSMax Laier struct pcap_sf_pkthdr sh; 5006964e37dSMax Laier size_t len = sizeof(sh) + h->caplen; 5016964e37dSMax Laier #else 50222ac3eadSMax Laier size_t len = sizeof(*h) + h->caplen; 5036964e37dSMax Laier #endif 50422ac3eadSMax Laier 50522ac3eadSMax Laier if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) { 50622ac3eadSMax Laier logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped", 50722ac3eadSMax Laier len, cur_snaplen, snaplen); 50822ac3eadSMax Laier packets_dropped++; 50922ac3eadSMax Laier return; 51022ac3eadSMax Laier } 51122ac3eadSMax Laier 51222ac3eadSMax Laier if (len <= bufleft) 51322ac3eadSMax Laier goto append; 51422ac3eadSMax Laier 51522ac3eadSMax Laier if (suspended) { 51622ac3eadSMax Laier packets_dropped++; 51722ac3eadSMax Laier return; 51822ac3eadSMax Laier } 51922ac3eadSMax Laier 52022ac3eadSMax Laier if (flush_buffer(f)) { 52122ac3eadSMax Laier packets_dropped++; 52222ac3eadSMax Laier return; 52322ac3eadSMax Laier } 52422ac3eadSMax Laier 52522ac3eadSMax Laier if (len > bufleft) { 52622ac3eadSMax Laier dump_packet_nobuf(user, h, sp); 52722ac3eadSMax Laier return; 52822ac3eadSMax Laier } 52922ac3eadSMax Laier 53022ac3eadSMax Laier append: 5316964e37dSMax Laier #ifdef __FreeBSD__ 5326964e37dSMax Laier sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec; 5336964e37dSMax Laier sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec; 5346964e37dSMax Laier sh.caplen = h->caplen; 5356964e37dSMax Laier sh.len = h->len; 5366964e37dSMax Laier 5376964e37dSMax Laier memcpy(bufpos, &sh, sizeof(sh)); 5386964e37dSMax Laier memcpy(bufpos + sizeof(sh), sp, h->caplen); 5396964e37dSMax Laier #else 54022ac3eadSMax Laier memcpy(bufpos, h, sizeof(*h)); 54122ac3eadSMax Laier memcpy(bufpos + sizeof(*h), sp, h->caplen); 5426964e37dSMax Laier #endif 54322ac3eadSMax Laier 54422ac3eadSMax Laier bufpos += len; 54522ac3eadSMax Laier bufleft -= len; 54622ac3eadSMax Laier bufpkt++; 54722ac3eadSMax Laier 54822ac3eadSMax Laier return; 54913b9f610SMax Laier } 55013b9f610SMax Laier 55113b9f610SMax Laier int 55213b9f610SMax Laier main(int argc, char **argv) 55313b9f610SMax Laier { 55413b9f610SMax Laier struct pcap_stat pstat; 55522ac3eadSMax Laier int ch, np, Xflag = 0; 55622ac3eadSMax Laier pcap_handler phandler = dump_packet; 55713b9f610SMax Laier 55822ac3eadSMax Laier #ifdef __FreeBSD__ 55922ac3eadSMax Laier /* another ?paranoid? safety measure we do not have */ 56022ac3eadSMax Laier #else 56122ac3eadSMax Laier closefrom(STDERR_FILENO + 1); 56222ac3eadSMax Laier #endif 56322ac3eadSMax Laier 56422ac3eadSMax Laier while ((ch = getopt(argc, argv, "Dxd:s:f:")) != -1) { 56513b9f610SMax Laier switch (ch) { 56613b9f610SMax Laier case 'D': 56713b9f610SMax Laier Debug = 1; 56813b9f610SMax Laier break; 56913b9f610SMax Laier case 'd': 57013b9f610SMax Laier delay = atoi(optarg); 57113b9f610SMax Laier if (delay < 5 || delay > 60*60) 57213b9f610SMax Laier usage(); 57313b9f610SMax Laier break; 57413b9f610SMax Laier case 'f': 57513b9f610SMax Laier filename = optarg; 57613b9f610SMax Laier break; 57713b9f610SMax Laier case 's': 57813b9f610SMax Laier snaplen = atoi(optarg); 57913b9f610SMax Laier if (snaplen <= 0) 58013b9f610SMax Laier snaplen = DEF_SNAPLEN; 58122ac3eadSMax Laier if (snaplen > PFLOGD_MAXSNAPLEN) 58222ac3eadSMax Laier snaplen = PFLOGD_MAXSNAPLEN; 58322ac3eadSMax Laier break; 58422ac3eadSMax Laier case 'x': 58522ac3eadSMax Laier Xflag++; 58613b9f610SMax Laier break; 58713b9f610SMax Laier default: 58813b9f610SMax Laier usage(); 58913b9f610SMax Laier } 59013b9f610SMax Laier 59113b9f610SMax Laier } 59213b9f610SMax Laier 59313b9f610SMax Laier log_debug = Debug; 59413b9f610SMax Laier argc -= optind; 59513b9f610SMax Laier argv += optind; 59613b9f610SMax Laier 59713b9f610SMax Laier if (!Debug) { 59813b9f610SMax Laier openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 59913b9f610SMax Laier if (daemon(0, 0)) { 60013b9f610SMax Laier logmsg(LOG_WARNING, "Failed to become daemon: %s", 60113b9f610SMax Laier strerror(errno)); 60213b9f610SMax Laier } 60313b9f610SMax Laier pidfile(NULL); 60413b9f610SMax Laier } 60513b9f610SMax Laier 60613b9f610SMax Laier (void)umask(S_IRWXG | S_IRWXO); 60713b9f610SMax Laier 60822ac3eadSMax Laier /* filter will be used by the privileged process */ 60922ac3eadSMax Laier if (argc) { 61022ac3eadSMax Laier filter = copy_argv(argv); 61122ac3eadSMax Laier if (filter == NULL) 61222ac3eadSMax Laier logmsg(LOG_NOTICE, "Failed to form filter expression"); 61322ac3eadSMax Laier } 61422ac3eadSMax Laier 61522ac3eadSMax Laier /* initialize pcap before dropping privileges */ 61622ac3eadSMax Laier if (init_pcap()) { 61722ac3eadSMax Laier logmsg(LOG_ERR, "Exiting, init failure"); 61822ac3eadSMax Laier exit(1); 61922ac3eadSMax Laier } 62022ac3eadSMax Laier 62122ac3eadSMax Laier /* Privilege separation begins here */ 62222ac3eadSMax Laier if (priv_init()) { 62322ac3eadSMax Laier logmsg(LOG_ERR, "unable to privsep"); 62422ac3eadSMax Laier exit(1); 62522ac3eadSMax Laier } 62622ac3eadSMax Laier 62722ac3eadSMax Laier setproctitle("[initializing]"); 62822ac3eadSMax Laier /* Process is now unprivileged and inside a chroot */ 62913b9f610SMax Laier signal(SIGTERM, sig_close); 63013b9f610SMax Laier signal(SIGINT, sig_close); 63113b9f610SMax Laier signal(SIGQUIT, sig_close); 63213b9f610SMax Laier signal(SIGALRM, sig_alrm); 63313b9f610SMax Laier signal(SIGHUP, sig_hup); 63413b9f610SMax Laier alarm(delay); 63513b9f610SMax Laier 63622ac3eadSMax Laier buffer = malloc(PFLOGD_BUFSIZE); 63713b9f610SMax Laier 63822ac3eadSMax Laier if (buffer == NULL) { 63922ac3eadSMax Laier logmsg(LOG_WARNING, "Failed to allocate output buffer"); 64022ac3eadSMax Laier phandler = dump_packet_nobuf; 64122ac3eadSMax Laier } else { 64222ac3eadSMax Laier bufleft = buflen = PFLOGD_BUFSIZE; 64322ac3eadSMax Laier bufpos = buffer; 64422ac3eadSMax Laier bufpkt = 0; 64513b9f610SMax Laier } 64613b9f610SMax Laier 64713b9f610SMax Laier if (reset_dump()) { 64822ac3eadSMax Laier if (Xflag) 64922ac3eadSMax Laier return (1); 65022ac3eadSMax Laier 65122ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: open error"); 65222ac3eadSMax Laier set_suspended(1); 65322ac3eadSMax Laier } else if (Xflag) 65422ac3eadSMax Laier return (0); 65513b9f610SMax Laier 65613b9f610SMax Laier while (1) { 65722ac3eadSMax Laier np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, 65822ac3eadSMax Laier dump_packet, (u_char *)dpcap); 65922d6889bSMax Laier if (np < 0) { 66022d6889bSMax Laier #ifdef __FreeBSD__ 66122d6889bSMax Laier if (errno == ENXIO) { 66222d6889bSMax Laier logmsg(LOG_ERR, 66322d6889bSMax Laier "Device not/no longer configured"); 66422d6889bSMax Laier break; 66522d6889bSMax Laier } 66622d6889bSMax Laier #endif 66713b9f610SMax Laier logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 66822d6889bSMax Laier } 66913b9f610SMax Laier 67013b9f610SMax Laier if (gotsig_close) 67113b9f610SMax Laier break; 67213b9f610SMax Laier if (gotsig_hup) { 67313b9f610SMax Laier if (reset_dump()) { 67422ac3eadSMax Laier logmsg(LOG_ERR, 67522ac3eadSMax Laier "Logging suspended: open error"); 67622ac3eadSMax Laier set_suspended(1); 67713b9f610SMax Laier } 67813b9f610SMax Laier gotsig_hup = 0; 67913b9f610SMax Laier } 68013b9f610SMax Laier 68113b9f610SMax Laier if (gotsig_alrm) { 68222ac3eadSMax Laier if (dpcap) 68322ac3eadSMax Laier flush_buffer(dpcap); 68413b9f610SMax Laier gotsig_alrm = 0; 68513b9f610SMax Laier alarm(delay); 68613b9f610SMax Laier } 68713b9f610SMax Laier } 68813b9f610SMax Laier 68922ac3eadSMax Laier logmsg(LOG_NOTICE, "Exiting"); 69022ac3eadSMax Laier if (dpcap) { 69122ac3eadSMax Laier flush_buffer(dpcap); 69222ac3eadSMax Laier fclose(dpcap); 69322ac3eadSMax Laier } 69422ac3eadSMax Laier purge_buffer(); 69513b9f610SMax Laier 69613b9f610SMax Laier if (pcap_stats(hpcap, &pstat) < 0) 69713b9f610SMax Laier logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 69813b9f610SMax Laier else 69922ac3eadSMax Laier logmsg(LOG_NOTICE, 70022ac3eadSMax Laier "%u packets received, %u/%u dropped (kernel/pflogd)", 70122ac3eadSMax Laier pstat.ps_recv, pstat.ps_drop, packets_dropped); 70213b9f610SMax Laier 70313b9f610SMax Laier pcap_close(hpcap); 70413b9f610SMax Laier if (!Debug) 70513b9f610SMax Laier closelog(); 70613b9f610SMax Laier return (0); 70713b9f610SMax Laier } 708