18c8618f5SMax Laier /* $FreeBSD$ */ 213b9f610SMax Laier /* $OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david Exp $ */ 313b9f610SMax Laier 413b9f610SMax Laier /* 513b9f610SMax Laier * Copyright (c) 2001 Theo de Raadt 613b9f610SMax Laier * Copyright (c) 2001 Can Erkin Acar 713b9f610SMax Laier * All rights reserved. 813b9f610SMax Laier * 913b9f610SMax Laier * Redistribution and use in source and binary forms, with or without 1013b9f610SMax Laier * modification, are permitted provided that the following conditions 1113b9f610SMax Laier * are met: 1213b9f610SMax Laier * 1313b9f610SMax Laier * - Redistributions of source code must retain the above copyright 1413b9f610SMax Laier * notice, this list of conditions and the following disclaimer. 1513b9f610SMax Laier * - Redistributions in binary form must reproduce the above 1613b9f610SMax Laier * copyright notice, this list of conditions and the following 1713b9f610SMax Laier * disclaimer in the documentation and/or other materials provided 1813b9f610SMax Laier * with the distribution. 1913b9f610SMax Laier * 2013b9f610SMax Laier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2113b9f610SMax Laier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2213b9f610SMax Laier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 2313b9f610SMax Laier * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 2413b9f610SMax Laier * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2513b9f610SMax Laier * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2613b9f610SMax Laier * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2713b9f610SMax Laier * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 2813b9f610SMax Laier * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2913b9f610SMax Laier * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 3013b9f610SMax Laier * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3113b9f610SMax Laier * POSSIBILITY OF SUCH DAMAGE. 3213b9f610SMax Laier */ 3313b9f610SMax Laier 3413b9f610SMax Laier #include <sys/types.h> 3513b9f610SMax Laier #include <sys/file.h> 3613b9f610SMax Laier #include <sys/stat.h> 3713b9f610SMax Laier #include <stdio.h> 3813b9f610SMax Laier #include <stdlib.h> 3913b9f610SMax Laier #include <string.h> 4013b9f610SMax Laier #include <unistd.h> 4113b9f610SMax Laier #include <pcap-int.h> 4213b9f610SMax Laier #include <pcap.h> 4313b9f610SMax Laier #include <syslog.h> 4413b9f610SMax Laier #include <signal.h> 4513b9f610SMax Laier #include <errno.h> 4613b9f610SMax Laier #include <stdarg.h> 4713b9f610SMax Laier #include <fcntl.h> 488c8618f5SMax Laier #if defined(__FreeBSD__) 498c8618f5SMax Laier #include "pidfile.h" 508c8618f5SMax Laier #else 5113b9f610SMax Laier #include <util.h> 528c8618f5SMax Laier #endif 538c8618f5SMax Laier 548c8618f5SMax Laier #if defined(__FreeBSD__) 558c8618f5SMax Laier #define __dead __volatile 568c8618f5SMax Laier #endif 5713b9f610SMax Laier 5813b9f610SMax Laier #define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */ 5913b9f610SMax Laier #define PCAP_TO_MS 500 /* pcap read timeout (ms) */ 6013b9f610SMax Laier #define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */ 6113b9f610SMax Laier #define PCAP_OPT_FIL 0 /* filter optimization */ 6213b9f610SMax Laier #define FLUSH_DELAY 60 /* flush delay */ 6313b9f610SMax Laier 6413b9f610SMax Laier #define PFLOGD_LOG_FILE "/var/log/pflog" 6513b9f610SMax Laier #define PFLOGD_DEFAULT_IF "pflog0" 6613b9f610SMax Laier 6713b9f610SMax Laier pcap_t *hpcap; 6813b9f610SMax Laier pcap_dumper_t *dpcap; 6913b9f610SMax Laier 7013b9f610SMax Laier int Debug = 0; 7113b9f610SMax Laier int snaplen = DEF_SNAPLEN; 7213b9f610SMax Laier 7313b9f610SMax Laier volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; 7413b9f610SMax Laier 7513b9f610SMax Laier char *filename = PFLOGD_LOG_FILE; 7613b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF; 7713b9f610SMax Laier char *filter = NULL; 7813b9f610SMax Laier 7913b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE]; 8013b9f610SMax Laier 8113b9f610SMax Laier int log_debug = 0; 8213b9f610SMax Laier unsigned int delay = FLUSH_DELAY; 8313b9f610SMax Laier 8413b9f610SMax Laier char *copy_argv(char * const *argv); 8513b9f610SMax Laier int init_pcap(void); 8613b9f610SMax Laier void logmsg(int priority, const char *message, ...); 8713b9f610SMax Laier int reset_dump(void); 8813b9f610SMax Laier void sig_alrm(int); 8913b9f610SMax Laier void sig_close(int); 9013b9f610SMax Laier void sig_hup(int); 918c8618f5SMax Laier #if defined(__FreeBSD__) 928c8618f5SMax Laier __volatile void usage(void); 938c8618f5SMax Laier #else 9413b9f610SMax Laier void usage(void); 958c8618f5SMax Laier #endif 9613b9f610SMax Laier 9713b9f610SMax Laier 9813b9f610SMax Laier char * 9913b9f610SMax Laier copy_argv(char * const *argv) 10013b9f610SMax Laier { 10113b9f610SMax Laier size_t len = 0, n; 10213b9f610SMax Laier char *buf; 10313b9f610SMax Laier 10413b9f610SMax Laier if (argv == NULL) 10513b9f610SMax Laier return (NULL); 10613b9f610SMax Laier 10713b9f610SMax Laier for (n = 0; argv[n]; n++) 10813b9f610SMax Laier len += strlen(argv[n])+1; 10913b9f610SMax Laier if (len == 0) 11013b9f610SMax Laier return (NULL); 11113b9f610SMax Laier 11213b9f610SMax Laier buf = malloc(len); 11313b9f610SMax Laier if (buf == NULL) 11413b9f610SMax Laier return (NULL); 11513b9f610SMax Laier 11613b9f610SMax Laier strlcpy(buf, argv[0], len); 11713b9f610SMax Laier for (n = 1; argv[n]; n++) { 11813b9f610SMax Laier strlcat(buf, " ", len); 11913b9f610SMax Laier strlcat(buf, argv[n], len); 12013b9f610SMax Laier } 12113b9f610SMax Laier return (buf); 12213b9f610SMax Laier } 12313b9f610SMax Laier 12413b9f610SMax Laier void 12513b9f610SMax Laier logmsg(int pri, const char *message, ...) 12613b9f610SMax Laier { 12713b9f610SMax Laier va_list ap; 12813b9f610SMax Laier va_start(ap, message); 12913b9f610SMax Laier 13013b9f610SMax Laier if (log_debug) { 13113b9f610SMax Laier vfprintf(stderr, message, ap); 13213b9f610SMax Laier fprintf(stderr, "\n"); 13313b9f610SMax Laier } else 13413b9f610SMax Laier vsyslog(pri, message, ap); 13513b9f610SMax Laier va_end(ap); 13613b9f610SMax Laier } 13713b9f610SMax Laier 13813b9f610SMax Laier __dead void 13913b9f610SMax Laier usage(void) 14013b9f610SMax Laier { 14113b9f610SMax Laier fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] "); 14213b9f610SMax Laier fprintf(stderr, "[-s snaplen] [expression]\n"); 14313b9f610SMax Laier exit(1); 14413b9f610SMax Laier } 14513b9f610SMax Laier 14613b9f610SMax Laier void 14713b9f610SMax Laier sig_close(int sig) 14813b9f610SMax Laier { 14913b9f610SMax Laier gotsig_close = 1; 15013b9f610SMax Laier } 15113b9f610SMax Laier 15213b9f610SMax Laier void 15313b9f610SMax Laier sig_hup(int sig) 15413b9f610SMax Laier { 15513b9f610SMax Laier gotsig_hup = 1; 15613b9f610SMax Laier } 15713b9f610SMax Laier 15813b9f610SMax Laier void 15913b9f610SMax Laier sig_alrm(int sig) 16013b9f610SMax Laier { 16113b9f610SMax Laier gotsig_alrm = 1; 16213b9f610SMax Laier } 16313b9f610SMax Laier 16413b9f610SMax Laier int 16513b9f610SMax Laier init_pcap(void) 16613b9f610SMax Laier { 16713b9f610SMax Laier struct bpf_program bprog; 16813b9f610SMax Laier pcap_t *oldhpcap = hpcap; 16913b9f610SMax Laier 17013b9f610SMax Laier hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 17113b9f610SMax Laier if (hpcap == NULL) { 17213b9f610SMax Laier logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 17313b9f610SMax Laier hpcap = oldhpcap; 17413b9f610SMax Laier return (-1); 17513b9f610SMax Laier } 17613b9f610SMax Laier 17713b9f610SMax Laier if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 17813b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 17913b9f610SMax Laier else if (pcap_setfilter(hpcap, &bprog) < 0) 18013b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 18113b9f610SMax Laier if (filter != NULL) 18213b9f610SMax Laier free(filter); 18313b9f610SMax Laier 18413b9f610SMax Laier if (pcap_datalink(hpcap) != DLT_PFLOG) { 18513b9f610SMax Laier logmsg(LOG_ERR, "Invalid datalink type"); 18613b9f610SMax Laier pcap_close(hpcap); 18713b9f610SMax Laier hpcap = oldhpcap; 18813b9f610SMax Laier return (-1); 18913b9f610SMax Laier } 19013b9f610SMax Laier 19113b9f610SMax Laier if (oldhpcap) 19213b9f610SMax Laier pcap_close(oldhpcap); 19313b9f610SMax Laier 19413b9f610SMax Laier snaplen = pcap_snapshot(hpcap); 19513b9f610SMax Laier return (0); 19613b9f610SMax Laier } 19713b9f610SMax Laier 19813b9f610SMax Laier int 19913b9f610SMax Laier reset_dump(void) 20013b9f610SMax Laier { 20113b9f610SMax Laier struct pcap_file_header hdr; 20213b9f610SMax Laier struct stat st; 20313b9f610SMax Laier int tmpsnap; 20413b9f610SMax Laier FILE *fp; 20513b9f610SMax Laier 20613b9f610SMax Laier if (hpcap == NULL) 20713b9f610SMax Laier return (1); 20813b9f610SMax Laier if (dpcap) { 20913b9f610SMax Laier pcap_dump_close(dpcap); 21013b9f610SMax Laier dpcap = 0; 21113b9f610SMax Laier } 21213b9f610SMax Laier 21313b9f610SMax Laier /* 21413b9f610SMax Laier * Basically reimplement pcap_dump_open() because it truncates 21513b9f610SMax Laier * files and duplicates headers and such. 21613b9f610SMax Laier */ 21713b9f610SMax Laier fp = fopen(filename, "a+"); 21813b9f610SMax Laier if (fp == NULL) { 21913b9f610SMax Laier snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 22013b9f610SMax Laier filename, pcap_strerror(errno)); 22113b9f610SMax Laier logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 22213b9f610SMax Laier return (1); 22313b9f610SMax Laier } 22413b9f610SMax Laier if (fstat(fileno(fp), &st) == -1) { 22513b9f610SMax Laier snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 22613b9f610SMax Laier filename, pcap_strerror(errno)); 22713b9f610SMax Laier logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 22813b9f610SMax Laier return (1); 22913b9f610SMax Laier } 23013b9f610SMax Laier 23113b9f610SMax Laier dpcap = (pcap_dumper_t *)fp; 23213b9f610SMax Laier 23313b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4 23413b9f610SMax Laier 23513b9f610SMax Laier if (st.st_size == 0) { 23613b9f610SMax Laier if (snaplen != pcap_snapshot(hpcap)) { 23713b9f610SMax Laier logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 23813b9f610SMax Laier if (init_pcap()) { 23913b9f610SMax Laier logmsg(LOG_ERR, "Failed to initialize"); 24013b9f610SMax Laier if (hpcap == NULL) return (-1); 24113b9f610SMax Laier logmsg(LOG_NOTICE, "Using old settings"); 24213b9f610SMax Laier } 24313b9f610SMax Laier } 24413b9f610SMax Laier hdr.magic = TCPDUMP_MAGIC; 24513b9f610SMax Laier hdr.version_major = PCAP_VERSION_MAJOR; 24613b9f610SMax Laier hdr.version_minor = PCAP_VERSION_MINOR; 24713b9f610SMax Laier hdr.thiszone = hpcap->tzoff; 24813b9f610SMax Laier hdr.snaplen = hpcap->snapshot; 24913b9f610SMax Laier hdr.sigfigs = 0; 25013b9f610SMax Laier hdr.linktype = hpcap->linktype; 25113b9f610SMax Laier 25213b9f610SMax Laier if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 25313b9f610SMax Laier dpcap = NULL; 25413b9f610SMax Laier fclose(fp); 25513b9f610SMax Laier return (-1); 25613b9f610SMax Laier } 25713b9f610SMax Laier return (0); 25813b9f610SMax Laier } 25913b9f610SMax Laier 26013b9f610SMax Laier /* 26113b9f610SMax Laier * XXX Must read the file, compare the header against our new 26213b9f610SMax Laier * options (in particular, snaplen) and adjust our options so 26313b9f610SMax Laier * that we generate a correct file. 26413b9f610SMax Laier */ 26513b9f610SMax Laier (void) fseek(fp, 0L, SEEK_SET); 26613b9f610SMax Laier if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) { 26713b9f610SMax Laier if (hdr.magic != TCPDUMP_MAGIC || 26813b9f610SMax Laier hdr.version_major != PCAP_VERSION_MAJOR || 26913b9f610SMax Laier hdr.version_minor != PCAP_VERSION_MINOR || 27013b9f610SMax Laier hdr.linktype != hpcap->linktype) { 27113b9f610SMax Laier logmsg(LOG_ERR, 27213b9f610SMax Laier "Invalid/incompatible log file, move it away"); 27313b9f610SMax Laier fclose(fp); 27413b9f610SMax Laier return (1); 27513b9f610SMax Laier } 27613b9f610SMax Laier if (hdr.snaplen != snaplen) { 27713b9f610SMax Laier logmsg(LOG_WARNING, 27813b9f610SMax Laier "Existing file specifies a snaplen of %u, using it", 27913b9f610SMax Laier hdr.snaplen); 28013b9f610SMax Laier tmpsnap = snaplen; 28113b9f610SMax Laier snaplen = hdr.snaplen; 28213b9f610SMax Laier if (init_pcap()) { 28313b9f610SMax Laier logmsg(LOG_ERR, "Failed to re-initialize"); 28413b9f610SMax Laier if (hpcap == 0) 28513b9f610SMax Laier return (-1); 28613b9f610SMax Laier logmsg(LOG_NOTICE, 28713b9f610SMax Laier "Using old settings, offset: %llu", 28813b9f610SMax Laier (unsigned long long)st.st_size); 28913b9f610SMax Laier } 29013b9f610SMax Laier snaplen = tmpsnap; 29113b9f610SMax Laier } 29213b9f610SMax Laier } 29313b9f610SMax Laier 29413b9f610SMax Laier (void) fseek(fp, 0L, SEEK_END); 29513b9f610SMax Laier return (0); 29613b9f610SMax Laier } 29713b9f610SMax Laier 29813b9f610SMax Laier int 29913b9f610SMax Laier main(int argc, char **argv) 30013b9f610SMax Laier { 30113b9f610SMax Laier struct pcap_stat pstat; 30213b9f610SMax Laier int ch, np; 30313b9f610SMax Laier 30413b9f610SMax Laier while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) { 30513b9f610SMax Laier switch (ch) { 30613b9f610SMax Laier case 'D': 30713b9f610SMax Laier Debug = 1; 30813b9f610SMax Laier break; 30913b9f610SMax Laier case 'd': 31013b9f610SMax Laier delay = atoi(optarg); 31113b9f610SMax Laier if (delay < 5 || delay > 60*60) 31213b9f610SMax Laier usage(); 31313b9f610SMax Laier break; 31413b9f610SMax Laier case 'f': 31513b9f610SMax Laier filename = optarg; 31613b9f610SMax Laier break; 31713b9f610SMax Laier case 's': 31813b9f610SMax Laier snaplen = atoi(optarg); 31913b9f610SMax Laier if (snaplen <= 0) 32013b9f610SMax Laier snaplen = DEF_SNAPLEN; 32113b9f610SMax Laier break; 32213b9f610SMax Laier default: 32313b9f610SMax Laier usage(); 32413b9f610SMax Laier } 32513b9f610SMax Laier 32613b9f610SMax Laier } 32713b9f610SMax Laier 32813b9f610SMax Laier log_debug = Debug; 32913b9f610SMax Laier argc -= optind; 33013b9f610SMax Laier argv += optind; 33113b9f610SMax Laier 33213b9f610SMax Laier if (!Debug) { 33313b9f610SMax Laier openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 33413b9f610SMax Laier if (daemon(0, 0)) { 33513b9f610SMax Laier logmsg(LOG_WARNING, "Failed to become daemon: %s", 33613b9f610SMax Laier strerror(errno)); 33713b9f610SMax Laier } 33813b9f610SMax Laier pidfile(NULL); 33913b9f610SMax Laier } 34013b9f610SMax Laier 34113b9f610SMax Laier (void)umask(S_IRWXG | S_IRWXO); 34213b9f610SMax Laier 34313b9f610SMax Laier signal(SIGTERM, sig_close); 34413b9f610SMax Laier signal(SIGINT, sig_close); 34513b9f610SMax Laier signal(SIGQUIT, sig_close); 34613b9f610SMax Laier signal(SIGALRM, sig_alrm); 34713b9f610SMax Laier signal(SIGHUP, sig_hup); 34813b9f610SMax Laier alarm(delay); 34913b9f610SMax Laier 35013b9f610SMax Laier if (argc) { 35113b9f610SMax Laier filter = copy_argv(argv); 35213b9f610SMax Laier if (filter == NULL) 35313b9f610SMax Laier logmsg(LOG_NOTICE, "Failed to form filter expression"); 35413b9f610SMax Laier } 35513b9f610SMax Laier 35613b9f610SMax Laier if (init_pcap()) { 35713b9f610SMax Laier logmsg(LOG_ERR, "Exiting, init failure"); 35813b9f610SMax Laier exit(1); 35913b9f610SMax Laier } 36013b9f610SMax Laier 36113b9f610SMax Laier if (reset_dump()) { 36213b9f610SMax Laier logmsg(LOG_ERR, "Failed to open log file %s", filename); 36313b9f610SMax Laier pcap_close(hpcap); 36413b9f610SMax Laier exit(1); 36513b9f610SMax Laier } 36613b9f610SMax Laier 36713b9f610SMax Laier while (1) { 36813b9f610SMax Laier np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap); 36913b9f610SMax Laier if (np < 0) 37013b9f610SMax Laier logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 37113b9f610SMax Laier 37213b9f610SMax Laier if (gotsig_close) 37313b9f610SMax Laier break; 37413b9f610SMax Laier if (gotsig_hup) { 37513b9f610SMax Laier if (reset_dump()) { 37613b9f610SMax Laier logmsg(LOG_ERR, "Failed to open log file!"); 37713b9f610SMax Laier break; 37813b9f610SMax Laier } 37913b9f610SMax Laier logmsg(LOG_NOTICE, "Reopened logfile"); 38013b9f610SMax Laier gotsig_hup = 0; 38113b9f610SMax Laier } 38213b9f610SMax Laier 38313b9f610SMax Laier if (gotsig_alrm) { 38413b9f610SMax Laier /* XXX pcap_dumper is an incomplete type which libpcap 38513b9f610SMax Laier * casts to a FILE* currently. For now it is safe to 38613b9f610SMax Laier * make the same assumption, however this may change 38713b9f610SMax Laier * in the future. 38813b9f610SMax Laier */ 38913b9f610SMax Laier if (dpcap) { 39013b9f610SMax Laier if (fflush((FILE *)dpcap) == EOF) { 39113b9f610SMax Laier break; 39213b9f610SMax Laier } 39313b9f610SMax Laier } 39413b9f610SMax Laier gotsig_alrm = 0; 39513b9f610SMax Laier alarm(delay); 39613b9f610SMax Laier } 39713b9f610SMax Laier } 39813b9f610SMax Laier 39913b9f610SMax Laier logmsg(LOG_NOTICE, "Exiting due to signal"); 40013b9f610SMax Laier if (dpcap) 40113b9f610SMax Laier pcap_dump_close(dpcap); 40213b9f610SMax Laier 40313b9f610SMax Laier if (pcap_stats(hpcap, &pstat) < 0) 40413b9f610SMax Laier logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 40513b9f610SMax Laier else 40613b9f610SMax Laier logmsg(LOG_NOTICE, "%u packets received, %u dropped", 40713b9f610SMax Laier pstat.ps_recv, pstat.ps_drop); 40813b9f610SMax Laier 40913b9f610SMax Laier pcap_close(hpcap); 41013b9f610SMax Laier if (!Debug) 41113b9f610SMax Laier closelog(); 41213b9f610SMax Laier return (0); 41313b9f610SMax Laier } 414