1e0bfbfceSBjoern A. Zeeb /* $OpenBSD: pflogd.c,v 1.46 2008/10/22 08:16:49 henning 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> 40e0bfbfceSBjoern A. Zeeb #include <sys/socket.h> 41e0bfbfceSBjoern A. Zeeb #include <net/if.h> 4213b9f610SMax Laier #include <stdio.h> 4313b9f610SMax Laier #include <stdlib.h> 4413b9f610SMax Laier #include <string.h> 4513b9f610SMax Laier #include <unistd.h> 4613b9f610SMax Laier #include <pcap-int.h> 4713b9f610SMax Laier #include <pcap.h> 4813b9f610SMax Laier #include <syslog.h> 4913b9f610SMax Laier #include <signal.h> 50e0bfbfceSBjoern A. Zeeb #include <err.h> 5113b9f610SMax Laier #include <errno.h> 5213b9f610SMax Laier #include <stdarg.h> 5313b9f610SMax Laier #include <fcntl.h> 54b83a49e9SMax Laier #ifdef __FreeBSD__ 55e0bfbfceSBjoern A. Zeeb #include <ifaddrs.h> 568c8618f5SMax Laier #include "pidfile.h" 578c8618f5SMax Laier #else 5813b9f610SMax Laier #include <util.h> 598c8618f5SMax Laier #endif 6022ac3eadSMax Laier #include "pflogd.h" 6113b9f610SMax Laier 6213b9f610SMax Laier pcap_t *hpcap; 6322ac3eadSMax Laier static FILE *dpcap; 6413b9f610SMax Laier 6513b9f610SMax Laier int Debug = 0; 6622ac3eadSMax Laier static int snaplen = DEF_SNAPLEN; 6722ac3eadSMax Laier static int cur_snaplen = DEF_SNAPLEN; 6813b9f610SMax Laier 69e0bfbfceSBjoern A. Zeeb volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup, gotsig_usr1; 7013b9f610SMax Laier 7113b9f610SMax Laier char *filename = PFLOGD_LOG_FILE; 7213b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF; 7313b9f610SMax Laier char *filter = NULL; 7413b9f610SMax Laier 7513b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE]; 7613b9f610SMax Laier 7713b9f610SMax Laier int log_debug = 0; 7813b9f610SMax Laier unsigned int delay = FLUSH_DELAY; 7913b9f610SMax Laier 8022ac3eadSMax Laier char *copy_argv(char * const *); 8122ac3eadSMax Laier void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); 8222ac3eadSMax Laier void dump_packet_nobuf(u_char *, const struct pcap_pkthdr *, const u_char *); 83e0bfbfceSBjoern A. Zeeb void log_pcap_stats(void); 8422ac3eadSMax Laier int flush_buffer(FILE *); 85e0bfbfceSBjoern A. Zeeb int if_exists(char *); 8613b9f610SMax Laier int init_pcap(void); 8722ac3eadSMax Laier void logmsg(int, const char *, ...); 8822ac3eadSMax Laier void purge_buffer(void); 895ee7cd21SMax Laier int reset_dump(int); 9022ac3eadSMax Laier int scan_dump(FILE *, off_t); 9122ac3eadSMax Laier int set_snaplen(int); 9222ac3eadSMax Laier void set_suspended(int); 9313b9f610SMax Laier void sig_alrm(int); 94e0bfbfceSBjoern A. Zeeb void sig_usr1(int); 9513b9f610SMax Laier void sig_close(int); 9613b9f610SMax Laier void sig_hup(int); 9713b9f610SMax Laier void usage(void); 9813b9f610SMax Laier 995ee7cd21SMax Laier static int try_reset_dump(int); 1005ee7cd21SMax Laier 10122ac3eadSMax Laier /* buffer must always be greater than snaplen */ 10222ac3eadSMax Laier static int bufpkt = 0; /* number of packets in buffer */ 10322ac3eadSMax Laier static int buflen = 0; /* allocated size of buffer */ 10422ac3eadSMax Laier static char *buffer = NULL; /* packet buffer */ 10522ac3eadSMax Laier static char *bufpos = NULL; /* position in buffer */ 10622ac3eadSMax Laier static int bufleft = 0; /* bytes left in buffer */ 10722ac3eadSMax Laier 10822ac3eadSMax Laier /* if error, stop logging but count dropped packets */ 10922ac3eadSMax Laier static int suspended = -1; 11022ac3eadSMax Laier static long packets_dropped = 0; 11122ac3eadSMax Laier 11222ac3eadSMax Laier void 11322ac3eadSMax Laier set_suspended(int s) 11422ac3eadSMax Laier { 11522ac3eadSMax Laier if (suspended == s) 11622ac3eadSMax Laier return; 11722ac3eadSMax Laier 11822ac3eadSMax Laier suspended = s; 1195ee7cd21SMax Laier setproctitle("[%s] -s %d -i %s -f %s", 1205ee7cd21SMax Laier suspended ? "suspended" : "running", 1215ee7cd21SMax Laier cur_snaplen, interface, filename); 12222ac3eadSMax Laier } 12313b9f610SMax Laier 12413b9f610SMax Laier char * 12513b9f610SMax Laier copy_argv(char * const *argv) 12613b9f610SMax Laier { 12713b9f610SMax Laier size_t len = 0, n; 12813b9f610SMax Laier char *buf; 12913b9f610SMax Laier 13013b9f610SMax Laier if (argv == NULL) 13113b9f610SMax Laier return (NULL); 13213b9f610SMax Laier 13313b9f610SMax Laier for (n = 0; argv[n]; n++) 13413b9f610SMax Laier len += strlen(argv[n])+1; 13513b9f610SMax Laier if (len == 0) 13613b9f610SMax Laier return (NULL); 13713b9f610SMax Laier 13813b9f610SMax Laier buf = malloc(len); 13913b9f610SMax Laier if (buf == NULL) 14013b9f610SMax Laier return (NULL); 14113b9f610SMax Laier 14213b9f610SMax Laier strlcpy(buf, argv[0], len); 14313b9f610SMax Laier for (n = 1; argv[n]; n++) { 14413b9f610SMax Laier strlcat(buf, " ", len); 14513b9f610SMax Laier strlcat(buf, argv[n], len); 14613b9f610SMax Laier } 14713b9f610SMax Laier return (buf); 14813b9f610SMax Laier } 14913b9f610SMax Laier 15013b9f610SMax Laier void 15113b9f610SMax Laier logmsg(int pri, const char *message, ...) 15213b9f610SMax Laier { 15313b9f610SMax Laier va_list ap; 15413b9f610SMax Laier va_start(ap, message); 15513b9f610SMax Laier 15613b9f610SMax Laier if (log_debug) { 15713b9f610SMax Laier vfprintf(stderr, message, ap); 15813b9f610SMax Laier fprintf(stderr, "\n"); 15913b9f610SMax Laier } else 16013b9f610SMax Laier vsyslog(pri, message, ap); 16113b9f610SMax Laier va_end(ap); 16213b9f610SMax Laier } 16313b9f610SMax Laier 164b83a49e9SMax Laier #ifdef __FreeBSD__ 165b83a49e9SMax Laier __dead2 void 166b83a49e9SMax Laier #else 16713b9f610SMax Laier __dead void 168b83a49e9SMax Laier #endif 16913b9f610SMax Laier usage(void) 17013b9f610SMax Laier { 17122ac3eadSMax Laier fprintf(stderr, "usage: pflogd [-Dx] [-d delay] [-f filename]"); 172e0bfbfceSBjoern A. Zeeb fprintf(stderr, " [-i interface] [-p pidfile]\n"); 173e0bfbfceSBjoern A. Zeeb fprintf(stderr, " [-s snaplen] [expression]\n"); 17413b9f610SMax Laier exit(1); 17513b9f610SMax Laier } 17613b9f610SMax Laier 17713b9f610SMax Laier void 17813b9f610SMax Laier sig_close(int sig) 17913b9f610SMax Laier { 18013b9f610SMax Laier gotsig_close = 1; 18113b9f610SMax Laier } 18213b9f610SMax Laier 18313b9f610SMax Laier void 18413b9f610SMax Laier sig_hup(int sig) 18513b9f610SMax Laier { 18613b9f610SMax Laier gotsig_hup = 1; 18713b9f610SMax Laier } 18813b9f610SMax Laier 18913b9f610SMax Laier void 19013b9f610SMax Laier sig_alrm(int sig) 19113b9f610SMax Laier { 19213b9f610SMax Laier gotsig_alrm = 1; 19313b9f610SMax Laier } 19413b9f610SMax Laier 19522ac3eadSMax Laier void 196e0bfbfceSBjoern A. Zeeb sig_usr1(int sig) 197e0bfbfceSBjoern A. Zeeb { 198e0bfbfceSBjoern A. Zeeb gotsig_usr1 = 1; 199e0bfbfceSBjoern A. Zeeb } 200e0bfbfceSBjoern A. Zeeb 201e0bfbfceSBjoern A. Zeeb void 20222ac3eadSMax Laier set_pcap_filter(void) 20313b9f610SMax Laier { 20413b9f610SMax Laier struct bpf_program bprog; 20513b9f610SMax Laier 20613b9f610SMax Laier if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 20713b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 20822ac3eadSMax Laier else { 20922ac3eadSMax Laier if (pcap_setfilter(hpcap, &bprog) < 0) 21013b9f610SMax Laier logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 21122ac3eadSMax Laier pcap_freecode(&bprog); 21222ac3eadSMax Laier } 21322ac3eadSMax Laier } 21422ac3eadSMax Laier 21522ac3eadSMax Laier int 216e0bfbfceSBjoern A. Zeeb if_exists(char *ifname) 217e0bfbfceSBjoern A. Zeeb { 218e0bfbfceSBjoern A. Zeeb #ifdef __FreeBSD__ 219e0bfbfceSBjoern A. Zeeb struct ifaddrs *ifdata, *mb; 220e0bfbfceSBjoern A. Zeeb int exists = 0; 221e0bfbfceSBjoern A. Zeeb 222e0bfbfceSBjoern A. Zeeb getifaddrs(&ifdata); 223e0bfbfceSBjoern A. Zeeb if (ifdata == NULL) 224e0bfbfceSBjoern A. Zeeb return (0); 225e0bfbfceSBjoern A. Zeeb 226e0bfbfceSBjoern A. Zeeb for (mb = ifdata; mb != NULL; mb = mb->ifa_next) { 227e0bfbfceSBjoern A. Zeeb if (mb == NULL) 228e0bfbfceSBjoern A. Zeeb continue; 229e0bfbfceSBjoern A. Zeeb if (strlen(ifname) != strlen(mb->ifa_name)) 230e0bfbfceSBjoern A. Zeeb continue; 231e0bfbfceSBjoern A. Zeeb if (strncmp(ifname, mb->ifa_name, strlen(ifname)) != 0) 232e0bfbfceSBjoern A. Zeeb continue; 233e0bfbfceSBjoern A. Zeeb exists = 1; 234e0bfbfceSBjoern A. Zeeb break; 235e0bfbfceSBjoern A. Zeeb } 236e0bfbfceSBjoern A. Zeeb freeifaddrs(ifdata); 237e0bfbfceSBjoern A. Zeeb 238e0bfbfceSBjoern A. Zeeb return (exists); 239e0bfbfceSBjoern A. Zeeb #else 240e0bfbfceSBjoern A. Zeeb int s; 241e0bfbfceSBjoern A. Zeeb struct ifreq ifr; 242e0bfbfceSBjoern A. Zeeb struct if_data ifrdat; 243e0bfbfceSBjoern A. Zeeb 244e0bfbfceSBjoern A. Zeeb if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 245e0bfbfceSBjoern A. Zeeb err(1, "socket"); 246e0bfbfceSBjoern A. Zeeb bzero(&ifr, sizeof(ifr)); 247e0bfbfceSBjoern A. Zeeb if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 248e0bfbfceSBjoern A. Zeeb sizeof(ifr.ifr_name)) 249e0bfbfceSBjoern A. Zeeb errx(1, "main ifr_name: strlcpy"); 250e0bfbfceSBjoern A. Zeeb ifr.ifr_data = (caddr_t)&ifrdat; 251e0bfbfceSBjoern A. Zeeb if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) 252e0bfbfceSBjoern A. Zeeb return (0); 253e0bfbfceSBjoern A. Zeeb if (close(s)) 254e0bfbfceSBjoern A. Zeeb err(1, "close"); 255e0bfbfceSBjoern A. Zeeb 256e0bfbfceSBjoern A. Zeeb return (1); 257e0bfbfceSBjoern A. Zeeb #endif 258e0bfbfceSBjoern A. Zeeb } 259e0bfbfceSBjoern A. Zeeb 260e0bfbfceSBjoern A. Zeeb int 26122ac3eadSMax Laier init_pcap(void) 26222ac3eadSMax Laier { 26322ac3eadSMax Laier hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 26422ac3eadSMax Laier if (hpcap == NULL) { 26522ac3eadSMax Laier logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 26622ac3eadSMax Laier return (-1); 26722ac3eadSMax Laier } 26813b9f610SMax Laier 26913b9f610SMax Laier if (pcap_datalink(hpcap) != DLT_PFLOG) { 27013b9f610SMax Laier logmsg(LOG_ERR, "Invalid datalink type"); 27113b9f610SMax Laier pcap_close(hpcap); 27222ac3eadSMax Laier hpcap = NULL; 27313b9f610SMax Laier return (-1); 27413b9f610SMax Laier } 27513b9f610SMax Laier 27622ac3eadSMax Laier set_pcap_filter(); 27713b9f610SMax Laier 27822ac3eadSMax Laier cur_snaplen = snaplen = pcap_snapshot(hpcap); 27922ac3eadSMax Laier 28022ac3eadSMax Laier /* lock */ 28122ac3eadSMax Laier if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) { 28222ac3eadSMax Laier logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno)); 28322ac3eadSMax Laier return (-1); 28422ac3eadSMax Laier } 28522ac3eadSMax Laier 28622ac3eadSMax Laier return (0); 28722ac3eadSMax Laier } 28822ac3eadSMax Laier 28922ac3eadSMax Laier int 29022ac3eadSMax Laier set_snaplen(int snap) 29122ac3eadSMax Laier { 29222ac3eadSMax Laier if (priv_set_snaplen(snap)) 29322ac3eadSMax Laier return (1); 29422ac3eadSMax Laier 29522ac3eadSMax Laier if (cur_snaplen > snap) 29622ac3eadSMax Laier purge_buffer(); 29722ac3eadSMax Laier 29822ac3eadSMax Laier cur_snaplen = snap; 29922ac3eadSMax Laier 30013b9f610SMax Laier return (0); 30113b9f610SMax Laier } 30213b9f610SMax Laier 30313b9f610SMax Laier int 3045ee7cd21SMax Laier reset_dump(int nomove) 3055ee7cd21SMax Laier { 3065ee7cd21SMax Laier int ret; 3075ee7cd21SMax Laier 3085ee7cd21SMax Laier for (;;) { 3095ee7cd21SMax Laier ret = try_reset_dump(nomove); 3105ee7cd21SMax Laier if (ret <= 0) 3115ee7cd21SMax Laier break; 3125ee7cd21SMax Laier } 3135ee7cd21SMax Laier 3145ee7cd21SMax Laier return (ret); 3155ee7cd21SMax Laier } 3165ee7cd21SMax Laier 3175ee7cd21SMax Laier /* 3185ee7cd21SMax Laier * tries to (re)open log file, nomove flag is used with -x switch 3195ee7cd21SMax Laier * returns 0: success, 1: retry (log moved), -1: error 3205ee7cd21SMax Laier */ 3215ee7cd21SMax Laier int 3225ee7cd21SMax Laier try_reset_dump(int nomove) 32313b9f610SMax Laier { 32413b9f610SMax Laier struct pcap_file_header hdr; 32513b9f610SMax Laier struct stat st; 32622ac3eadSMax Laier int fd; 32713b9f610SMax Laier FILE *fp; 32813b9f610SMax Laier 32913b9f610SMax Laier if (hpcap == NULL) 33022ac3eadSMax Laier return (-1); 33122ac3eadSMax Laier 33213b9f610SMax Laier if (dpcap) { 33322ac3eadSMax Laier flush_buffer(dpcap); 33422ac3eadSMax Laier fclose(dpcap); 33522ac3eadSMax Laier dpcap = NULL; 33613b9f610SMax Laier } 33713b9f610SMax Laier 33813b9f610SMax Laier /* 33913b9f610SMax Laier * Basically reimplement pcap_dump_open() because it truncates 34013b9f610SMax Laier * files and duplicates headers and such. 34113b9f610SMax Laier */ 34222ac3eadSMax Laier fd = priv_open_log(); 34322ac3eadSMax Laier if (fd < 0) 3445ee7cd21SMax Laier return (-1); 34522ac3eadSMax Laier 34622ac3eadSMax Laier fp = fdopen(fd, "a+"); 34722ac3eadSMax Laier 34813b9f610SMax Laier if (fp == NULL) { 34922ac3eadSMax Laier logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 3505ee7cd21SMax Laier close(fd); 3515ee7cd21SMax Laier return (-1); 35213b9f610SMax Laier } 35313b9f610SMax Laier if (fstat(fileno(fp), &st) == -1) { 35422ac3eadSMax Laier logmsg(LOG_ERR, "Error: %s: %s", filename, strerror(errno)); 3555ee7cd21SMax Laier fclose(fp); 3565ee7cd21SMax Laier return (-1); 35713b9f610SMax Laier } 35813b9f610SMax Laier 35922ac3eadSMax Laier /* set FILE unbuffered, we do our own buffering */ 36022ac3eadSMax Laier if (setvbuf(fp, NULL, _IONBF, 0)) { 36122ac3eadSMax Laier logmsg(LOG_ERR, "Failed to set output buffers"); 3625ee7cd21SMax Laier fclose(fp); 3635ee7cd21SMax Laier return (-1); 36422ac3eadSMax Laier } 36513b9f610SMax Laier 36613b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4 36713b9f610SMax Laier 36813b9f610SMax Laier if (st.st_size == 0) { 36922ac3eadSMax Laier if (snaplen != cur_snaplen) { 37013b9f610SMax Laier logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 3715ee7cd21SMax Laier if (set_snaplen(snaplen)) 37222ac3eadSMax Laier logmsg(LOG_WARNING, 37322ac3eadSMax Laier "Failed, using old settings"); 37413b9f610SMax Laier } 37513b9f610SMax Laier hdr.magic = TCPDUMP_MAGIC; 37613b9f610SMax Laier hdr.version_major = PCAP_VERSION_MAJOR; 37713b9f610SMax Laier hdr.version_minor = PCAP_VERSION_MINOR; 37813b9f610SMax Laier hdr.thiszone = hpcap->tzoff; 37913b9f610SMax Laier hdr.snaplen = hpcap->snapshot; 38013b9f610SMax Laier hdr.sigfigs = 0; 38113b9f610SMax Laier hdr.linktype = hpcap->linktype; 38213b9f610SMax Laier 38313b9f610SMax Laier if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 38413b9f610SMax Laier fclose(fp); 3855ee7cd21SMax Laier return (-1); 38613b9f610SMax Laier } 38722ac3eadSMax Laier } else if (scan_dump(fp, st.st_size)) { 38822ac3eadSMax Laier fclose(fp); 3895ee7cd21SMax Laier if (nomove || priv_move_log()) { 3905ee7cd21SMax Laier logmsg(LOG_ERR, 3915ee7cd21SMax Laier "Invalid/incompatible log file, move it away"); 3925ee7cd21SMax Laier return (-1); 3935ee7cd21SMax Laier } 39422ac3eadSMax Laier return (1); 39513b9f610SMax Laier } 39622ac3eadSMax Laier 39722ac3eadSMax Laier dpcap = fp; 39822ac3eadSMax Laier 39922ac3eadSMax Laier set_suspended(0); 40022ac3eadSMax Laier flush_buffer(fp); 40122ac3eadSMax Laier 40222ac3eadSMax Laier return (0); 40322ac3eadSMax Laier } 40422ac3eadSMax Laier 40522ac3eadSMax Laier int 40622ac3eadSMax Laier scan_dump(FILE *fp, off_t size) 40722ac3eadSMax Laier { 40822ac3eadSMax Laier struct pcap_file_header hdr; 4096964e37dSMax Laier #ifdef __FreeBSD__ 4106964e37dSMax Laier struct pcap_sf_pkthdr ph; 4116964e37dSMax Laier #else 41222ac3eadSMax Laier struct pcap_pkthdr ph; 4136964e37dSMax Laier #endif 41422ac3eadSMax Laier off_t pos; 41522ac3eadSMax Laier 41622ac3eadSMax Laier /* 41722ac3eadSMax Laier * Must read the file, compare the header against our new 41822ac3eadSMax Laier * options (in particular, snaplen) and adjust our options so 41922ac3eadSMax Laier * that we generate a correct file. Furthermore, check the file 42022ac3eadSMax Laier * for consistency so that we can append safely. 42122ac3eadSMax Laier * 42222ac3eadSMax Laier * XXX this may take a long time for large logs. 42322ac3eadSMax Laier */ 42422ac3eadSMax Laier (void) fseek(fp, 0L, SEEK_SET); 42522ac3eadSMax Laier 42622ac3eadSMax Laier if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 42722ac3eadSMax Laier logmsg(LOG_ERR, "Short file header"); 42822ac3eadSMax Laier return (1); 42922ac3eadSMax Laier } 43022ac3eadSMax Laier 43122ac3eadSMax Laier if (hdr.magic != TCPDUMP_MAGIC || 43222ac3eadSMax Laier hdr.version_major != PCAP_VERSION_MAJOR || 43322ac3eadSMax Laier hdr.version_minor != PCAP_VERSION_MINOR || 43422ac3eadSMax Laier hdr.linktype != hpcap->linktype || 43522ac3eadSMax Laier hdr.snaplen > PFLOGD_MAXSNAPLEN) { 43622ac3eadSMax Laier return (1); 43722ac3eadSMax Laier } 43822ac3eadSMax Laier 43922ac3eadSMax Laier pos = sizeof(hdr); 44022ac3eadSMax Laier 44122ac3eadSMax Laier while (!feof(fp)) { 44222ac3eadSMax Laier off_t len = fread((char *)&ph, 1, sizeof(ph), fp); 44322ac3eadSMax Laier if (len == 0) 44422ac3eadSMax Laier break; 44522ac3eadSMax Laier 44622ac3eadSMax Laier if (len != sizeof(ph)) 44722ac3eadSMax Laier goto error; 44822ac3eadSMax Laier if (ph.caplen > hdr.snaplen || ph.caplen > PFLOGD_MAXSNAPLEN) 44922ac3eadSMax Laier goto error; 45022ac3eadSMax Laier pos += sizeof(ph) + ph.caplen; 45122ac3eadSMax Laier if (pos > size) 45222ac3eadSMax Laier goto error; 45322ac3eadSMax Laier fseek(fp, ph.caplen, SEEK_CUR); 45422ac3eadSMax Laier } 45522ac3eadSMax Laier 45622ac3eadSMax Laier if (pos != size) 45722ac3eadSMax Laier goto error; 45822ac3eadSMax Laier 45922ac3eadSMax Laier if (hdr.snaplen != cur_snaplen) { 46022ac3eadSMax Laier logmsg(LOG_WARNING, 46122ac3eadSMax Laier "Existing file has different snaplen %u, using it", 46222ac3eadSMax Laier hdr.snaplen); 46322ac3eadSMax Laier if (set_snaplen(hdr.snaplen)) { 46422ac3eadSMax Laier logmsg(LOG_WARNING, 46522ac3eadSMax Laier "Failed, using old settings, offset %llu", 46622ac3eadSMax Laier (unsigned long long) size); 46713b9f610SMax Laier } 46813b9f610SMax Laier } 46913b9f610SMax Laier 47013b9f610SMax Laier return (0); 47122ac3eadSMax Laier 47222ac3eadSMax Laier error: 47322ac3eadSMax Laier logmsg(LOG_ERR, "Corrupted log file."); 47422ac3eadSMax Laier return (1); 47522ac3eadSMax Laier } 47622ac3eadSMax Laier 47722ac3eadSMax Laier /* dump a packet directly to the stream, which is unbuffered */ 47822ac3eadSMax Laier void 47922ac3eadSMax Laier dump_packet_nobuf(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 48022ac3eadSMax Laier { 48122ac3eadSMax Laier FILE *f = (FILE *)user; 4826964e37dSMax Laier #ifdef __FreeBSD__ 4836964e37dSMax Laier struct pcap_sf_pkthdr sh; 4846964e37dSMax Laier #endif 48522ac3eadSMax Laier 48622ac3eadSMax Laier if (suspended) { 48722ac3eadSMax Laier packets_dropped++; 48822ac3eadSMax Laier return; 48922ac3eadSMax Laier } 49022ac3eadSMax Laier 4916964e37dSMax Laier #ifdef __FreeBSD__ 4926964e37dSMax Laier sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec; 4936964e37dSMax Laier sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec; 4946964e37dSMax Laier sh.caplen = h->caplen; 4956964e37dSMax Laier sh.len = h->len; 4966964e37dSMax Laier 4976964e37dSMax Laier if (fwrite((char *)&sh, sizeof(sh), 1, f) != 1) { 4986964e37dSMax Laier #else 49922ac3eadSMax Laier if (fwrite((char *)h, sizeof(*h), 1, f) != 1) { 5006964e37dSMax Laier #endif 50122ac3eadSMax Laier off_t pos = ftello(f); 5020baf7c86SMax Laier 5030baf7c86SMax Laier /* try to undo header to prevent corruption */ 5046964e37dSMax Laier #ifdef __FreeBSD__ 5056964e37dSMax Laier if (pos < sizeof(sh) || 5066964e37dSMax Laier ftruncate(fileno(f), pos - sizeof(sh))) { 5076964e37dSMax Laier #else 50822ac3eadSMax Laier if (pos < sizeof(*h) || 50922ac3eadSMax Laier ftruncate(fileno(f), pos - sizeof(*h))) { 5106964e37dSMax Laier #endif 51122ac3eadSMax Laier logmsg(LOG_ERR, "Write failed, corrupted logfile!"); 51222ac3eadSMax Laier set_suspended(1); 51322ac3eadSMax Laier gotsig_close = 1; 51422ac3eadSMax Laier return; 51522ac3eadSMax Laier } 51622ac3eadSMax Laier goto error; 51722ac3eadSMax Laier } 51822ac3eadSMax Laier 51922ac3eadSMax Laier if (fwrite((char *)sp, h->caplen, 1, f) != 1) 52022ac3eadSMax Laier goto error; 52122ac3eadSMax Laier 52222ac3eadSMax Laier return; 52322ac3eadSMax Laier 52422ac3eadSMax Laier error: 52522ac3eadSMax Laier set_suspended(1); 52622ac3eadSMax Laier packets_dropped ++; 52722ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", strerror(errno)); 52822ac3eadSMax Laier } 52922ac3eadSMax Laier 53022ac3eadSMax Laier int 53122ac3eadSMax Laier flush_buffer(FILE *f) 53222ac3eadSMax Laier { 53322ac3eadSMax Laier off_t offset; 53422ac3eadSMax Laier int len = bufpos - buffer; 53522ac3eadSMax Laier 53622ac3eadSMax Laier if (len <= 0) 53722ac3eadSMax Laier return (0); 53822ac3eadSMax Laier 53922ac3eadSMax Laier offset = ftello(f); 54022ac3eadSMax Laier if (offset == (off_t)-1) { 54122ac3eadSMax Laier set_suspended(1); 54222ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: ftello: %s", 54322ac3eadSMax Laier strerror(errno)); 54422ac3eadSMax Laier return (1); 54522ac3eadSMax Laier } 54622ac3eadSMax Laier 54722ac3eadSMax Laier if (fwrite(buffer, len, 1, f) != 1) { 54822ac3eadSMax Laier set_suspended(1); 54922ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: fwrite: %s", 55022ac3eadSMax Laier strerror(errno)); 55122ac3eadSMax Laier ftruncate(fileno(f), offset); 55222ac3eadSMax Laier return (1); 55322ac3eadSMax Laier } 55422ac3eadSMax Laier 55522ac3eadSMax Laier set_suspended(0); 55622ac3eadSMax Laier bufpos = buffer; 55722ac3eadSMax Laier bufleft = buflen; 55822ac3eadSMax Laier bufpkt = 0; 55922ac3eadSMax Laier 56022ac3eadSMax Laier return (0); 56122ac3eadSMax Laier } 56222ac3eadSMax Laier 56322ac3eadSMax Laier void 56422ac3eadSMax Laier purge_buffer(void) 56522ac3eadSMax Laier { 56622ac3eadSMax Laier packets_dropped += bufpkt; 56722ac3eadSMax Laier 56822ac3eadSMax Laier set_suspended(0); 56922ac3eadSMax Laier bufpos = buffer; 57022ac3eadSMax Laier bufleft = buflen; 57122ac3eadSMax Laier bufpkt = 0; 57222ac3eadSMax Laier } 57322ac3eadSMax Laier 57422ac3eadSMax Laier /* append packet to the buffer, flushing if necessary */ 57522ac3eadSMax Laier void 57622ac3eadSMax Laier dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 57722ac3eadSMax Laier { 57822ac3eadSMax Laier FILE *f = (FILE *)user; 5796964e37dSMax Laier #ifdef __FreeBSD__ 5806964e37dSMax Laier struct pcap_sf_pkthdr sh; 5816964e37dSMax Laier size_t len = sizeof(sh) + h->caplen; 5826964e37dSMax Laier #else 58322ac3eadSMax Laier size_t len = sizeof(*h) + h->caplen; 5846964e37dSMax Laier #endif 58522ac3eadSMax Laier 58622ac3eadSMax Laier if (len < sizeof(*h) || h->caplen > (size_t)cur_snaplen) { 58722ac3eadSMax Laier logmsg(LOG_NOTICE, "invalid size %u (%u/%u), packet dropped", 58822ac3eadSMax Laier len, cur_snaplen, snaplen); 58922ac3eadSMax Laier packets_dropped++; 59022ac3eadSMax Laier return; 59122ac3eadSMax Laier } 59222ac3eadSMax Laier 59322ac3eadSMax Laier if (len <= bufleft) 59422ac3eadSMax Laier goto append; 59522ac3eadSMax Laier 59622ac3eadSMax Laier if (suspended) { 59722ac3eadSMax Laier packets_dropped++; 59822ac3eadSMax Laier return; 59922ac3eadSMax Laier } 60022ac3eadSMax Laier 60122ac3eadSMax Laier if (flush_buffer(f)) { 60222ac3eadSMax Laier packets_dropped++; 60322ac3eadSMax Laier return; 60422ac3eadSMax Laier } 60522ac3eadSMax Laier 60622ac3eadSMax Laier if (len > bufleft) { 60722ac3eadSMax Laier dump_packet_nobuf(user, h, sp); 60822ac3eadSMax Laier return; 60922ac3eadSMax Laier } 61022ac3eadSMax Laier 61122ac3eadSMax Laier append: 6126964e37dSMax Laier #ifdef __FreeBSD__ 6136964e37dSMax Laier sh.ts.tv_sec = (bpf_int32)h->ts.tv_sec; 6146964e37dSMax Laier sh.ts.tv_usec = (bpf_int32)h->ts.tv_usec; 6156964e37dSMax Laier sh.caplen = h->caplen; 6166964e37dSMax Laier sh.len = h->len; 6176964e37dSMax Laier 6186964e37dSMax Laier memcpy(bufpos, &sh, sizeof(sh)); 6196964e37dSMax Laier memcpy(bufpos + sizeof(sh), sp, h->caplen); 6206964e37dSMax Laier #else 62122ac3eadSMax Laier memcpy(bufpos, h, sizeof(*h)); 62222ac3eadSMax Laier memcpy(bufpos + sizeof(*h), sp, h->caplen); 6236964e37dSMax Laier #endif 62422ac3eadSMax Laier 62522ac3eadSMax Laier bufpos += len; 62622ac3eadSMax Laier bufleft -= len; 62722ac3eadSMax Laier bufpkt++; 62822ac3eadSMax Laier 62922ac3eadSMax Laier return; 63013b9f610SMax Laier } 63113b9f610SMax Laier 632e0bfbfceSBjoern A. Zeeb void 633e0bfbfceSBjoern A. Zeeb log_pcap_stats(void) 634e0bfbfceSBjoern A. Zeeb { 635e0bfbfceSBjoern A. Zeeb struct pcap_stat pstat; 636e0bfbfceSBjoern A. Zeeb if (pcap_stats(hpcap, &pstat) < 0) 637e0bfbfceSBjoern A. Zeeb logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 638e0bfbfceSBjoern A. Zeeb else 639e0bfbfceSBjoern A. Zeeb logmsg(LOG_NOTICE, 640e0bfbfceSBjoern A. Zeeb "%u packets received, %u/%u dropped (kernel/pflogd)", 641e0bfbfceSBjoern A. Zeeb pstat.ps_recv, pstat.ps_drop, packets_dropped); 642e0bfbfceSBjoern A. Zeeb } 643e0bfbfceSBjoern A. Zeeb 64413b9f610SMax Laier int 64513b9f610SMax Laier main(int argc, char **argv) 64613b9f610SMax Laier { 647e0bfbfceSBjoern A. Zeeb int ch, np, ret, Xflag = 0; 64822ac3eadSMax Laier pcap_handler phandler = dump_packet; 6491a58af5eSMax Laier const char *errstr = NULL; 650e0bfbfceSBjoern A. Zeeb char *pidf = NULL; 651e0bfbfceSBjoern A. Zeeb 652e0bfbfceSBjoern A. Zeeb ret = 0; 65313b9f610SMax Laier 65422ac3eadSMax Laier closefrom(STDERR_FILENO + 1); 65522ac3eadSMax Laier 656e0bfbfceSBjoern A. Zeeb while ((ch = getopt(argc, argv, "Dxd:f:i:p:s:")) != -1) { 65713b9f610SMax Laier switch (ch) { 65813b9f610SMax Laier case 'D': 65913b9f610SMax Laier Debug = 1; 66013b9f610SMax Laier break; 66113b9f610SMax Laier case 'd': 6620baf7c86SMax Laier delay = strtonum(optarg, 5, 60*60, &errstr); 6630baf7c86SMax Laier if (errstr) 66413b9f610SMax Laier usage(); 66513b9f610SMax Laier break; 66613b9f610SMax Laier case 'f': 66713b9f610SMax Laier filename = optarg; 66813b9f610SMax Laier break; 6695ee7cd21SMax Laier case 'i': 6705ee7cd21SMax Laier interface = optarg; 6715ee7cd21SMax Laier break; 672e0bfbfceSBjoern A. Zeeb case 'p': 673e0bfbfceSBjoern A. Zeeb pidf = optarg; 674e0bfbfceSBjoern A. Zeeb break; 67513b9f610SMax Laier case 's': 6760baf7c86SMax Laier snaplen = strtonum(optarg, 0, PFLOGD_MAXSNAPLEN, 6770baf7c86SMax Laier &errstr); 67813b9f610SMax Laier if (snaplen <= 0) 67913b9f610SMax Laier snaplen = DEF_SNAPLEN; 6800baf7c86SMax Laier if (errstr) 68122ac3eadSMax Laier snaplen = PFLOGD_MAXSNAPLEN; 68222ac3eadSMax Laier break; 68322ac3eadSMax Laier case 'x': 68422ac3eadSMax Laier Xflag++; 68513b9f610SMax Laier break; 68613b9f610SMax Laier default: 68713b9f610SMax Laier usage(); 68813b9f610SMax Laier } 68913b9f610SMax Laier 69013b9f610SMax Laier } 69113b9f610SMax Laier 69213b9f610SMax Laier log_debug = Debug; 69313b9f610SMax Laier argc -= optind; 69413b9f610SMax Laier argv += optind; 69513b9f610SMax Laier 696e0bfbfceSBjoern A. Zeeb /* does interface exist */ 697e0bfbfceSBjoern A. Zeeb if (!if_exists(interface)) { 698e0bfbfceSBjoern A. Zeeb warn("Failed to initialize: %s", interface); 699e0bfbfceSBjoern A. Zeeb logmsg(LOG_ERR, "Failed to initialize: %s", interface); 700e0bfbfceSBjoern A. Zeeb logmsg(LOG_ERR, "Exiting, init failure"); 701e0bfbfceSBjoern A. Zeeb exit(1); 702e0bfbfceSBjoern A. Zeeb } 703e0bfbfceSBjoern A. Zeeb 70413b9f610SMax Laier if (!Debug) { 70513b9f610SMax Laier openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 70613b9f610SMax Laier if (daemon(0, 0)) { 70713b9f610SMax Laier logmsg(LOG_WARNING, "Failed to become daemon: %s", 70813b9f610SMax Laier strerror(errno)); 70913b9f610SMax Laier } 710e0bfbfceSBjoern A. Zeeb pidfile(pidf); 71113b9f610SMax Laier } 71213b9f610SMax Laier 7130baf7c86SMax Laier tzset(); 71413b9f610SMax Laier (void)umask(S_IRWXG | S_IRWXO); 71513b9f610SMax Laier 71622ac3eadSMax Laier /* filter will be used by the privileged process */ 71722ac3eadSMax Laier if (argc) { 71822ac3eadSMax Laier filter = copy_argv(argv); 71922ac3eadSMax Laier if (filter == NULL) 72022ac3eadSMax Laier logmsg(LOG_NOTICE, "Failed to form filter expression"); 72122ac3eadSMax Laier } 72222ac3eadSMax Laier 72322ac3eadSMax Laier /* initialize pcap before dropping privileges */ 72422ac3eadSMax Laier if (init_pcap()) { 72522ac3eadSMax Laier logmsg(LOG_ERR, "Exiting, init failure"); 72622ac3eadSMax Laier exit(1); 72722ac3eadSMax Laier } 72822ac3eadSMax Laier 72922ac3eadSMax Laier /* Privilege separation begins here */ 73022ac3eadSMax Laier if (priv_init()) { 73122ac3eadSMax Laier logmsg(LOG_ERR, "unable to privsep"); 73222ac3eadSMax Laier exit(1); 73322ac3eadSMax Laier } 73422ac3eadSMax Laier 73522ac3eadSMax Laier setproctitle("[initializing]"); 73622ac3eadSMax Laier /* Process is now unprivileged and inside a chroot */ 73713b9f610SMax Laier signal(SIGTERM, sig_close); 73813b9f610SMax Laier signal(SIGINT, sig_close); 73913b9f610SMax Laier signal(SIGQUIT, sig_close); 74013b9f610SMax Laier signal(SIGALRM, sig_alrm); 741e0bfbfceSBjoern A. Zeeb signal(SIGUSR1, sig_usr1); 74213b9f610SMax Laier signal(SIGHUP, sig_hup); 74313b9f610SMax Laier alarm(delay); 74413b9f610SMax Laier 74522ac3eadSMax Laier buffer = malloc(PFLOGD_BUFSIZE); 74613b9f610SMax Laier 74722ac3eadSMax Laier if (buffer == NULL) { 74822ac3eadSMax Laier logmsg(LOG_WARNING, "Failed to allocate output buffer"); 74922ac3eadSMax Laier phandler = dump_packet_nobuf; 75022ac3eadSMax Laier } else { 75122ac3eadSMax Laier bufleft = buflen = PFLOGD_BUFSIZE; 75222ac3eadSMax Laier bufpos = buffer; 75322ac3eadSMax Laier bufpkt = 0; 75413b9f610SMax Laier } 75513b9f610SMax Laier 7565ee7cd21SMax Laier if (reset_dump(Xflag) < 0) { 75722ac3eadSMax Laier if (Xflag) 75822ac3eadSMax Laier return (1); 75922ac3eadSMax Laier 76022ac3eadSMax Laier logmsg(LOG_ERR, "Logging suspended: open error"); 76122ac3eadSMax Laier set_suspended(1); 76222ac3eadSMax Laier } else if (Xflag) 76322ac3eadSMax Laier return (0); 76413b9f610SMax Laier 76513b9f610SMax Laier while (1) { 76622ac3eadSMax Laier np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, 7670baf7c86SMax Laier phandler, (u_char *)dpcap); 76822d6889bSMax Laier if (np < 0) { 769f7862a87SDimitry Andric if (!if_exists(interface)) { 770e0bfbfceSBjoern A. Zeeb logmsg(LOG_NOTICE, "interface %s went away", 771e0bfbfceSBjoern A. Zeeb interface); 772e0bfbfceSBjoern A. Zeeb ret = -1; 77322d6889bSMax Laier break; 77422d6889bSMax Laier } 77513b9f610SMax Laier logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 77622d6889bSMax Laier } 77713b9f610SMax Laier 77813b9f610SMax Laier if (gotsig_close) 77913b9f610SMax Laier break; 78013b9f610SMax Laier if (gotsig_hup) { 7815ee7cd21SMax Laier if (reset_dump(0)) { 78222ac3eadSMax Laier logmsg(LOG_ERR, 78322ac3eadSMax Laier "Logging suspended: open error"); 78422ac3eadSMax Laier set_suspended(1); 78513b9f610SMax Laier } 78613b9f610SMax Laier gotsig_hup = 0; 78713b9f610SMax Laier } 78813b9f610SMax Laier 78913b9f610SMax Laier if (gotsig_alrm) { 79022ac3eadSMax Laier if (dpcap) 79122ac3eadSMax Laier flush_buffer(dpcap); 7925ee7cd21SMax Laier else 7935ee7cd21SMax Laier gotsig_hup = 1; 79413b9f610SMax Laier gotsig_alrm = 0; 79513b9f610SMax Laier alarm(delay); 79613b9f610SMax Laier } 797e0bfbfceSBjoern A. Zeeb 798e0bfbfceSBjoern A. Zeeb if (gotsig_usr1) { 799e0bfbfceSBjoern A. Zeeb log_pcap_stats(); 800e0bfbfceSBjoern A. Zeeb gotsig_usr1 = 0; 801e0bfbfceSBjoern A. Zeeb } 80213b9f610SMax Laier } 80313b9f610SMax Laier 80422ac3eadSMax Laier logmsg(LOG_NOTICE, "Exiting"); 80522ac3eadSMax Laier if (dpcap) { 80622ac3eadSMax Laier flush_buffer(dpcap); 80722ac3eadSMax Laier fclose(dpcap); 80822ac3eadSMax Laier } 80922ac3eadSMax Laier purge_buffer(); 81013b9f610SMax Laier 811e0bfbfceSBjoern A. Zeeb log_pcap_stats(); 81213b9f610SMax Laier pcap_close(hpcap); 81313b9f610SMax Laier if (!Debug) 81413b9f610SMax Laier closelog(); 815e0bfbfceSBjoern A. Zeeb return (ret); 81613b9f610SMax Laier } 817