113b9f610SMax Laier /* $OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david 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 3313b9f610SMax Laier #include <sys/types.h> 3413b9f610SMax Laier #include <sys/file.h> 3513b9f610SMax Laier #include <sys/stat.h> 3613b9f610SMax Laier #include <stdio.h> 3713b9f610SMax Laier #include <stdlib.h> 3813b9f610SMax Laier #include <string.h> 3913b9f610SMax Laier #include <unistd.h> 4013b9f610SMax Laier #include <pcap-int.h> 4113b9f610SMax Laier #include <pcap.h> 4213b9f610SMax Laier #include <syslog.h> 4313b9f610SMax Laier #include <signal.h> 4413b9f610SMax Laier #include <errno.h> 4513b9f610SMax Laier #include <stdarg.h> 4613b9f610SMax Laier #include <fcntl.h> 4713b9f610SMax Laier #include <util.h> 4813b9f610SMax Laier 4913b9f610SMax Laier #define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */ 5013b9f610SMax Laier #define PCAP_TO_MS 500 /* pcap read timeout (ms) */ 5113b9f610SMax Laier #define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */ 5213b9f610SMax Laier #define PCAP_OPT_FIL 0 /* filter optimization */ 5313b9f610SMax Laier #define FLUSH_DELAY 60 /* flush delay */ 5413b9f610SMax Laier 5513b9f610SMax Laier #define PFLOGD_LOG_FILE "/var/log/pflog" 5613b9f610SMax Laier #define PFLOGD_DEFAULT_IF "pflog0" 5713b9f610SMax Laier 5813b9f610SMax Laier pcap_t *hpcap; 5913b9f610SMax Laier pcap_dumper_t *dpcap; 6013b9f610SMax Laier 6113b9f610SMax Laier int Debug = 0; 6213b9f610SMax Laier int snaplen = DEF_SNAPLEN; 6313b9f610SMax Laier 6413b9f610SMax Laier volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; 6513b9f610SMax Laier 6613b9f610SMax Laier char *filename = PFLOGD_LOG_FILE; 6713b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF; 6813b9f610SMax Laier char *filter = NULL; 6913b9f610SMax Laier 7013b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE]; 7113b9f610SMax Laier 7213b9f610SMax Laier int log_debug = 0; 7313b9f610SMax Laier unsigned int delay = FLUSH_DELAY; 7413b9f610SMax Laier 7513b9f610SMax Laier char *copy_argv(char * const *argv); 7613b9f610SMax Laier int init_pcap(void); 7713b9f610SMax Laier void logmsg(int priority, const char *message, ...); 7813b9f610SMax Laier int reset_dump(void); 7913b9f610SMax Laier void sig_alrm(int); 8013b9f610SMax Laier void sig_close(int); 8113b9f610SMax Laier void sig_hup(int); 8213b9f610SMax Laier void usage(void); 8313b9f610SMax Laier 8413b9f610SMax Laier 8513b9f610SMax Laier char * 8613b9f610SMax Laier copy_argv(char * const *argv) 8713b9f610SMax Laier { 8813b9f610SMax Laier size_t len = 0, n; 8913b9f610SMax Laier char *buf; 9013b9f610SMax Laier 9113b9f610SMax Laier if (argv == NULL) 9213b9f610SMax Laier return (NULL); 9313b9f610SMax Laier 9413b9f610SMax Laier for (n = 0; argv[n]; n++) 9513b9f610SMax Laier len += strlen(argv[n])+1; 9613b9f610SMax Laier if (len == 0) 9713b9f610SMax Laier return (NULL); 9813b9f610SMax Laier 9913b9f610SMax Laier buf = malloc(len); 10013b9f610SMax Laier if (buf == NULL) 10113b9f610SMax Laier return (NULL); 10213b9f610SMax Laier 10313b9f610SMax Laier strlcpy(buf, argv[0], len); 10413b9f610SMax Laier for (n = 1; argv[n]; n++) { 10513b9f610SMax Laier strlcat(buf, " ", len); 10613b9f610SMax Laier strlcat(buf, argv[n], len); 10713b9f610SMax Laier } 10813b9f610SMax Laier return (buf); 10913b9f610SMax Laier } 11013b9f610SMax Laier 11113b9f610SMax Laier void 11213b9f610SMax Laier logmsg(int pri, const char *message, ...) 11313b9f610SMax Laier { 11413b9f610SMax Laier va_list ap; 11513b9f610SMax Laier va_start(ap, message); 11613b9f610SMax Laier 11713b9f610SMax Laier if (log_debug) { 11813b9f610SMax Laier vfprintf(stderr, message, ap); 11913b9f610SMax Laier fprintf(stderr, "\n"); 12013b9f610SMax Laier } else 12113b9f610SMax Laier vsyslog(pri, message, ap); 12213b9f610SMax Laier va_end(ap); 12313b9f610SMax Laier } 12413b9f610SMax Laier 12513b9f610SMax Laier __dead void 12613b9f610SMax Laier usage(void) 12713b9f610SMax Laier { 12813b9f610SMax Laier fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] "); 12913b9f610SMax Laier fprintf(stderr, "[-s snaplen] [expression]\n"); 13013b9f610SMax Laier exit(1); 13113b9f610SMax Laier } 13213b9f610SMax Laier 13313b9f610SMax Laier void 13413b9f610SMax Laier sig_close(int sig) 13513b9f610SMax Laier { 13613b9f610SMax Laier gotsig_close = 1; 13713b9f610SMax Laier } 13813b9f610SMax Laier 13913b9f610SMax Laier void 14013b9f610SMax Laier sig_hup(int sig) 14113b9f610SMax Laier { 14213b9f610SMax Laier gotsig_hup = 1; 14313b9f610SMax Laier } 14413b9f610SMax Laier 14513b9f610SMax Laier void 14613b9f610SMax Laier sig_alrm(int sig) 14713b9f610SMax Laier { 14813b9f610SMax Laier gotsig_alrm = 1; 14913b9f610SMax Laier } 15013b9f610SMax Laier 15113b9f610SMax Laier int 15213b9f610SMax Laier init_pcap(void) 15313b9f610SMax Laier { 15413b9f610SMax Laier struct bpf_program bprog; 15513b9f610SMax Laier pcap_t *oldhpcap = hpcap; 15613b9f610SMax Laier 15713b9f610SMax Laier hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 15813b9f610SMax Laier if (hpcap == NULL) { 15913b9f610SMax Laier logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 16013b9f610SMax Laier hpcap = oldhpcap; 16113b9f610SMax Laier return (-1); 16213b9f610SMax Laier } 16313b9f610SMax Laier 16413b9f610SMax Laier if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 16513b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 16613b9f610SMax Laier else if (pcap_setfilter(hpcap, &bprog) < 0) 16713b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 16813b9f610SMax Laier if (filter != NULL) 16913b9f610SMax Laier free(filter); 17013b9f610SMax Laier 17113b9f610SMax Laier if (pcap_datalink(hpcap) != DLT_PFLOG) { 17213b9f610SMax Laier logmsg(LOG_ERR, "Invalid datalink type"); 17313b9f610SMax Laier pcap_close(hpcap); 17413b9f610SMax Laier hpcap = oldhpcap; 17513b9f610SMax Laier return (-1); 17613b9f610SMax Laier } 17713b9f610SMax Laier 17813b9f610SMax Laier if (oldhpcap) 17913b9f610SMax Laier pcap_close(oldhpcap); 18013b9f610SMax Laier 18113b9f610SMax Laier snaplen = pcap_snapshot(hpcap); 18213b9f610SMax Laier return (0); 18313b9f610SMax Laier } 18413b9f610SMax Laier 18513b9f610SMax Laier int 18613b9f610SMax Laier reset_dump(void) 18713b9f610SMax Laier { 18813b9f610SMax Laier struct pcap_file_header hdr; 18913b9f610SMax Laier struct stat st; 19013b9f610SMax Laier int tmpsnap; 19113b9f610SMax Laier FILE *fp; 19213b9f610SMax Laier 19313b9f610SMax Laier if (hpcap == NULL) 19413b9f610SMax Laier return (1); 19513b9f610SMax Laier if (dpcap) { 19613b9f610SMax Laier pcap_dump_close(dpcap); 19713b9f610SMax Laier dpcap = 0; 19813b9f610SMax Laier } 19913b9f610SMax Laier 20013b9f610SMax Laier /* 20113b9f610SMax Laier * Basically reimplement pcap_dump_open() because it truncates 20213b9f610SMax Laier * files and duplicates headers and such. 20313b9f610SMax Laier */ 20413b9f610SMax Laier fp = fopen(filename, "a+"); 20513b9f610SMax Laier if (fp == NULL) { 20613b9f610SMax Laier snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 20713b9f610SMax Laier filename, pcap_strerror(errno)); 20813b9f610SMax Laier logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 20913b9f610SMax Laier return (1); 21013b9f610SMax Laier } 21113b9f610SMax Laier if (fstat(fileno(fp), &st) == -1) { 21213b9f610SMax Laier snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 21313b9f610SMax Laier filename, pcap_strerror(errno)); 21413b9f610SMax Laier logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 21513b9f610SMax Laier return (1); 21613b9f610SMax Laier } 21713b9f610SMax Laier 21813b9f610SMax Laier dpcap = (pcap_dumper_t *)fp; 21913b9f610SMax Laier 22013b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4 22113b9f610SMax Laier 22213b9f610SMax Laier if (st.st_size == 0) { 22313b9f610SMax Laier if (snaplen != pcap_snapshot(hpcap)) { 22413b9f610SMax Laier logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 22513b9f610SMax Laier if (init_pcap()) { 22613b9f610SMax Laier logmsg(LOG_ERR, "Failed to initialize"); 22713b9f610SMax Laier if (hpcap == NULL) return (-1); 22813b9f610SMax Laier logmsg(LOG_NOTICE, "Using old settings"); 22913b9f610SMax Laier } 23013b9f610SMax Laier } 23113b9f610SMax Laier hdr.magic = TCPDUMP_MAGIC; 23213b9f610SMax Laier hdr.version_major = PCAP_VERSION_MAJOR; 23313b9f610SMax Laier hdr.version_minor = PCAP_VERSION_MINOR; 23413b9f610SMax Laier hdr.thiszone = hpcap->tzoff; 23513b9f610SMax Laier hdr.snaplen = hpcap->snapshot; 23613b9f610SMax Laier hdr.sigfigs = 0; 23713b9f610SMax Laier hdr.linktype = hpcap->linktype; 23813b9f610SMax Laier 23913b9f610SMax Laier if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 24013b9f610SMax Laier dpcap = NULL; 24113b9f610SMax Laier fclose(fp); 24213b9f610SMax Laier return (-1); 24313b9f610SMax Laier } 24413b9f610SMax Laier return (0); 24513b9f610SMax Laier } 24613b9f610SMax Laier 24713b9f610SMax Laier /* 24813b9f610SMax Laier * XXX Must read the file, compare the header against our new 24913b9f610SMax Laier * options (in particular, snaplen) and adjust our options so 25013b9f610SMax Laier * that we generate a correct file. 25113b9f610SMax Laier */ 25213b9f610SMax Laier (void) fseek(fp, 0L, SEEK_SET); 25313b9f610SMax Laier if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) { 25413b9f610SMax Laier if (hdr.magic != TCPDUMP_MAGIC || 25513b9f610SMax Laier hdr.version_major != PCAP_VERSION_MAJOR || 25613b9f610SMax Laier hdr.version_minor != PCAP_VERSION_MINOR || 25713b9f610SMax Laier hdr.linktype != hpcap->linktype) { 25813b9f610SMax Laier logmsg(LOG_ERR, 25913b9f610SMax Laier "Invalid/incompatible log file, move it away"); 26013b9f610SMax Laier fclose(fp); 26113b9f610SMax Laier return (1); 26213b9f610SMax Laier } 26313b9f610SMax Laier if (hdr.snaplen != snaplen) { 26413b9f610SMax Laier logmsg(LOG_WARNING, 26513b9f610SMax Laier "Existing file specifies a snaplen of %u, using it", 26613b9f610SMax Laier hdr.snaplen); 26713b9f610SMax Laier tmpsnap = snaplen; 26813b9f610SMax Laier snaplen = hdr.snaplen; 26913b9f610SMax Laier if (init_pcap()) { 27013b9f610SMax Laier logmsg(LOG_ERR, "Failed to re-initialize"); 27113b9f610SMax Laier if (hpcap == 0) 27213b9f610SMax Laier return (-1); 27313b9f610SMax Laier logmsg(LOG_NOTICE, 27413b9f610SMax Laier "Using old settings, offset: %llu", 27513b9f610SMax Laier (unsigned long long)st.st_size); 27613b9f610SMax Laier } 27713b9f610SMax Laier snaplen = tmpsnap; 27813b9f610SMax Laier } 27913b9f610SMax Laier } 28013b9f610SMax Laier 28113b9f610SMax Laier (void) fseek(fp, 0L, SEEK_END); 28213b9f610SMax Laier return (0); 28313b9f610SMax Laier } 28413b9f610SMax Laier 28513b9f610SMax Laier int 28613b9f610SMax Laier main(int argc, char **argv) 28713b9f610SMax Laier { 28813b9f610SMax Laier struct pcap_stat pstat; 28913b9f610SMax Laier int ch, np; 29013b9f610SMax Laier 29113b9f610SMax Laier while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) { 29213b9f610SMax Laier switch (ch) { 29313b9f610SMax Laier case 'D': 29413b9f610SMax Laier Debug = 1; 29513b9f610SMax Laier break; 29613b9f610SMax Laier case 'd': 29713b9f610SMax Laier delay = atoi(optarg); 29813b9f610SMax Laier if (delay < 5 || delay > 60*60) 29913b9f610SMax Laier usage(); 30013b9f610SMax Laier break; 30113b9f610SMax Laier case 'f': 30213b9f610SMax Laier filename = optarg; 30313b9f610SMax Laier break; 30413b9f610SMax Laier case 's': 30513b9f610SMax Laier snaplen = atoi(optarg); 30613b9f610SMax Laier if (snaplen <= 0) 30713b9f610SMax Laier snaplen = DEF_SNAPLEN; 30813b9f610SMax Laier break; 30913b9f610SMax Laier default: 31013b9f610SMax Laier usage(); 31113b9f610SMax Laier } 31213b9f610SMax Laier 31313b9f610SMax Laier } 31413b9f610SMax Laier 31513b9f610SMax Laier log_debug = Debug; 31613b9f610SMax Laier argc -= optind; 31713b9f610SMax Laier argv += optind; 31813b9f610SMax Laier 31913b9f610SMax Laier if (!Debug) { 32013b9f610SMax Laier openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 32113b9f610SMax Laier if (daemon(0, 0)) { 32213b9f610SMax Laier logmsg(LOG_WARNING, "Failed to become daemon: %s", 32313b9f610SMax Laier strerror(errno)); 32413b9f610SMax Laier } 32513b9f610SMax Laier pidfile(NULL); 32613b9f610SMax Laier } 32713b9f610SMax Laier 32813b9f610SMax Laier (void)umask(S_IRWXG | S_IRWXO); 32913b9f610SMax Laier 33013b9f610SMax Laier signal(SIGTERM, sig_close); 33113b9f610SMax Laier signal(SIGINT, sig_close); 33213b9f610SMax Laier signal(SIGQUIT, sig_close); 33313b9f610SMax Laier signal(SIGALRM, sig_alrm); 33413b9f610SMax Laier signal(SIGHUP, sig_hup); 33513b9f610SMax Laier alarm(delay); 33613b9f610SMax Laier 33713b9f610SMax Laier if (argc) { 33813b9f610SMax Laier filter = copy_argv(argv); 33913b9f610SMax Laier if (filter == NULL) 34013b9f610SMax Laier logmsg(LOG_NOTICE, "Failed to form filter expression"); 34113b9f610SMax Laier } 34213b9f610SMax Laier 34313b9f610SMax Laier if (init_pcap()) { 34413b9f610SMax Laier logmsg(LOG_ERR, "Exiting, init failure"); 34513b9f610SMax Laier exit(1); 34613b9f610SMax Laier } 34713b9f610SMax Laier 34813b9f610SMax Laier if (reset_dump()) { 34913b9f610SMax Laier logmsg(LOG_ERR, "Failed to open log file %s", filename); 35013b9f610SMax Laier pcap_close(hpcap); 35113b9f610SMax Laier exit(1); 35213b9f610SMax Laier } 35313b9f610SMax Laier 35413b9f610SMax Laier while (1) { 35513b9f610SMax Laier np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap); 35613b9f610SMax Laier if (np < 0) 35713b9f610SMax Laier logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 35813b9f610SMax Laier 35913b9f610SMax Laier if (gotsig_close) 36013b9f610SMax Laier break; 36113b9f610SMax Laier if (gotsig_hup) { 36213b9f610SMax Laier if (reset_dump()) { 36313b9f610SMax Laier logmsg(LOG_ERR, "Failed to open log file!"); 36413b9f610SMax Laier break; 36513b9f610SMax Laier } 36613b9f610SMax Laier logmsg(LOG_NOTICE, "Reopened logfile"); 36713b9f610SMax Laier gotsig_hup = 0; 36813b9f610SMax Laier } 36913b9f610SMax Laier 37013b9f610SMax Laier if (gotsig_alrm) { 37113b9f610SMax Laier /* XXX pcap_dumper is an incomplete type which libpcap 37213b9f610SMax Laier * casts to a FILE* currently. For now it is safe to 37313b9f610SMax Laier * make the same assumption, however this may change 37413b9f610SMax Laier * in the future. 37513b9f610SMax Laier */ 37613b9f610SMax Laier if (dpcap) { 37713b9f610SMax Laier if (fflush((FILE *)dpcap) == EOF) { 37813b9f610SMax Laier break; 37913b9f610SMax Laier } 38013b9f610SMax Laier } 38113b9f610SMax Laier gotsig_alrm = 0; 38213b9f610SMax Laier alarm(delay); 38313b9f610SMax Laier } 38413b9f610SMax Laier } 38513b9f610SMax Laier 38613b9f610SMax Laier logmsg(LOG_NOTICE, "Exiting due to signal"); 38713b9f610SMax Laier if (dpcap) 38813b9f610SMax Laier pcap_dump_close(dpcap); 38913b9f610SMax Laier 39013b9f610SMax Laier if (pcap_stats(hpcap, &pstat) < 0) 39113b9f610SMax Laier logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 39213b9f610SMax Laier else 39313b9f610SMax Laier logmsg(LOG_NOTICE, "%u packets received, %u dropped", 39413b9f610SMax Laier pstat.ps_recv, pstat.ps_drop); 39513b9f610SMax Laier 39613b9f610SMax Laier pcap_close(hpcap); 39713b9f610SMax Laier if (!Debug) 39813b9f610SMax Laier closelog(); 39913b9f610SMax Laier return (0); 40013b9f610SMax Laier } 401