pflogd.c revision 127024
1/* $FreeBSD: head/contrib/pf/pflogd/pflogd.c 127024 2004-03-15 13:41:17Z mlaier $ */ 2/* $OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david Exp $ */ 3 4/* 5 * Copyright (c) 2001 Theo de Raadt 6 * Copyright (c) 2001 Can Erkin Acar 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * - Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * - Redistributions in binary form must reproduce the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer in the documentation and/or other materials provided 18 * with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#include <sys/types.h> 35#include <sys/file.h> 36#include <sys/stat.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <unistd.h> 41#include <pcap-int.h> 42#include <pcap.h> 43#include <syslog.h> 44#include <signal.h> 45#include <errno.h> 46#include <stdarg.h> 47#include <fcntl.h> 48#ifdef __FreeBSD__ 49#include "pidfile.h" 50#else 51#include <util.h> 52#endif 53 54#define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */ 55#define PCAP_TO_MS 500 /* pcap read timeout (ms) */ 56#define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */ 57#define PCAP_OPT_FIL 0 /* filter optimization */ 58#define FLUSH_DELAY 60 /* flush delay */ 59 60#define PFLOGD_LOG_FILE "/var/log/pflog" 61#define PFLOGD_DEFAULT_IF "pflog0" 62 63pcap_t *hpcap; 64pcap_dumper_t *dpcap; 65 66int Debug = 0; 67int snaplen = DEF_SNAPLEN; 68 69volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; 70 71char *filename = PFLOGD_LOG_FILE; 72char *interface = PFLOGD_DEFAULT_IF; 73char *filter = NULL; 74 75char errbuf[PCAP_ERRBUF_SIZE]; 76 77int log_debug = 0; 78unsigned int delay = FLUSH_DELAY; 79 80char *copy_argv(char * const *argv); 81int init_pcap(void); 82void logmsg(int priority, const char *message, ...); 83int reset_dump(void); 84void sig_alrm(int); 85void sig_close(int); 86void sig_hup(int); 87void usage(void); 88 89 90char * 91copy_argv(char * const *argv) 92{ 93 size_t len = 0, n; 94 char *buf; 95 96 if (argv == NULL) 97 return (NULL); 98 99 for (n = 0; argv[n]; n++) 100 len += strlen(argv[n])+1; 101 if (len == 0) 102 return (NULL); 103 104 buf = malloc(len); 105 if (buf == NULL) 106 return (NULL); 107 108 strlcpy(buf, argv[0], len); 109 for (n = 1; argv[n]; n++) { 110 strlcat(buf, " ", len); 111 strlcat(buf, argv[n], len); 112 } 113 return (buf); 114} 115 116void 117logmsg(int pri, const char *message, ...) 118{ 119 va_list ap; 120 va_start(ap, message); 121 122 if (log_debug) { 123 vfprintf(stderr, message, ap); 124 fprintf(stderr, "\n"); 125 } else 126 vsyslog(pri, message, ap); 127 va_end(ap); 128} 129 130#ifdef __FreeBSD__ 131__dead2 void 132#else 133__dead void 134#endif 135usage(void) 136{ 137 fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] "); 138 fprintf(stderr, "[-s snaplen] [expression]\n"); 139 exit(1); 140} 141 142void 143sig_close(int sig) 144{ 145 gotsig_close = 1; 146} 147 148void 149sig_hup(int sig) 150{ 151 gotsig_hup = 1; 152} 153 154void 155sig_alrm(int sig) 156{ 157 gotsig_alrm = 1; 158} 159 160int 161init_pcap(void) 162{ 163 struct bpf_program bprog; 164 pcap_t *oldhpcap = hpcap; 165 166 hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 167 if (hpcap == NULL) { 168 logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 169 hpcap = oldhpcap; 170 return (-1); 171 } 172 173 if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 174 logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 175 else if (pcap_setfilter(hpcap, &bprog) < 0) 176 logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 177 if (filter != NULL) 178 free(filter); 179 180 if (pcap_datalink(hpcap) != DLT_PFLOG) { 181 logmsg(LOG_ERR, "Invalid datalink type"); 182 pcap_close(hpcap); 183 hpcap = oldhpcap; 184 return (-1); 185 } 186 187 if (oldhpcap) 188 pcap_close(oldhpcap); 189 190 snaplen = pcap_snapshot(hpcap); 191 return (0); 192} 193 194int 195reset_dump(void) 196{ 197 struct pcap_file_header hdr; 198 struct stat st; 199 int tmpsnap; 200 FILE *fp; 201 202 if (hpcap == NULL) 203 return (1); 204 if (dpcap) { 205 pcap_dump_close(dpcap); 206 dpcap = 0; 207 } 208 209 /* 210 * Basically reimplement pcap_dump_open() because it truncates 211 * files and duplicates headers and such. 212 */ 213 fp = fopen(filename, "a+"); 214 if (fp == NULL) { 215 snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 216 filename, pcap_strerror(errno)); 217 logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 218 return (1); 219 } 220 if (fstat(fileno(fp), &st) == -1) { 221 snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 222 filename, pcap_strerror(errno)); 223 logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 224 return (1); 225 } 226 227 dpcap = (pcap_dumper_t *)fp; 228 229#define TCPDUMP_MAGIC 0xa1b2c3d4 230 231 if (st.st_size == 0) { 232 if (snaplen != pcap_snapshot(hpcap)) { 233 logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 234 if (init_pcap()) { 235 logmsg(LOG_ERR, "Failed to initialize"); 236 if (hpcap == NULL) return (-1); 237 logmsg(LOG_NOTICE, "Using old settings"); 238 } 239 } 240 hdr.magic = TCPDUMP_MAGIC; 241 hdr.version_major = PCAP_VERSION_MAJOR; 242 hdr.version_minor = PCAP_VERSION_MINOR; 243 hdr.thiszone = hpcap->tzoff; 244 hdr.snaplen = hpcap->snapshot; 245 hdr.sigfigs = 0; 246 hdr.linktype = hpcap->linktype; 247 248 if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 249 dpcap = NULL; 250 fclose(fp); 251 return (-1); 252 } 253 return (0); 254 } 255 256 /* 257 * XXX Must read the file, compare the header against our new 258 * options (in particular, snaplen) and adjust our options so 259 * that we generate a correct file. 260 */ 261 (void) fseek(fp, 0L, SEEK_SET); 262 if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) { 263 if (hdr.magic != TCPDUMP_MAGIC || 264 hdr.version_major != PCAP_VERSION_MAJOR || 265 hdr.version_minor != PCAP_VERSION_MINOR || 266 hdr.linktype != hpcap->linktype) { 267 logmsg(LOG_ERR, 268 "Invalid/incompatible log file, move it away"); 269 fclose(fp); 270 return (1); 271 } 272 if (hdr.snaplen != snaplen) { 273 logmsg(LOG_WARNING, 274 "Existing file specifies a snaplen of %u, using it", 275 hdr.snaplen); 276 tmpsnap = snaplen; 277 snaplen = hdr.snaplen; 278 if (init_pcap()) { 279 logmsg(LOG_ERR, "Failed to re-initialize"); 280 if (hpcap == 0) 281 return (-1); 282 logmsg(LOG_NOTICE, 283 "Using old settings, offset: %llu", 284 (unsigned long long)st.st_size); 285 } 286 snaplen = tmpsnap; 287 } 288 } 289 290 (void) fseek(fp, 0L, SEEK_END); 291 return (0); 292} 293 294int 295main(int argc, char **argv) 296{ 297 struct pcap_stat pstat; 298 int ch, np; 299 300 while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) { 301 switch (ch) { 302 case 'D': 303 Debug = 1; 304 break; 305 case 'd': 306 delay = atoi(optarg); 307 if (delay < 5 || delay > 60*60) 308 usage(); 309 break; 310 case 'f': 311 filename = optarg; 312 break; 313 case 's': 314 snaplen = atoi(optarg); 315 if (snaplen <= 0) 316 snaplen = DEF_SNAPLEN; 317 break; 318 default: 319 usage(); 320 } 321 322 } 323 324 log_debug = Debug; 325 argc -= optind; 326 argv += optind; 327 328 if (!Debug) { 329 openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 330 if (daemon(0, 0)) { 331 logmsg(LOG_WARNING, "Failed to become daemon: %s", 332 strerror(errno)); 333 } 334 pidfile(NULL); 335 } 336 337 (void)umask(S_IRWXG | S_IRWXO); 338 339 signal(SIGTERM, sig_close); 340 signal(SIGINT, sig_close); 341 signal(SIGQUIT, sig_close); 342 signal(SIGALRM, sig_alrm); 343 signal(SIGHUP, sig_hup); 344 alarm(delay); 345 346 if (argc) { 347 filter = copy_argv(argv); 348 if (filter == NULL) 349 logmsg(LOG_NOTICE, "Failed to form filter expression"); 350 } 351 352 if (init_pcap()) { 353 logmsg(LOG_ERR, "Exiting, init failure"); 354 exit(1); 355 } 356 357 if (reset_dump()) { 358 logmsg(LOG_ERR, "Failed to open log file %s", filename); 359 pcap_close(hpcap); 360 exit(1); 361 } 362 363 while (1) { 364 np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap); 365 if (np < 0) 366 logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 367 368 if (gotsig_close) 369 break; 370 if (gotsig_hup) { 371 if (reset_dump()) { 372 logmsg(LOG_ERR, "Failed to open log file!"); 373 break; 374 } 375 logmsg(LOG_NOTICE, "Reopened logfile"); 376 gotsig_hup = 0; 377 } 378 379 if (gotsig_alrm) { 380 /* XXX pcap_dumper is an incomplete type which libpcap 381 * casts to a FILE* currently. For now it is safe to 382 * make the same assumption, however this may change 383 * in the future. 384 */ 385 if (dpcap) { 386 if (fflush((FILE *)dpcap) == EOF) { 387 break; 388 } 389 } 390 gotsig_alrm = 0; 391 alarm(delay); 392 } 393 } 394 395 logmsg(LOG_NOTICE, "Exiting due to signal"); 396 if (dpcap) 397 pcap_dump_close(dpcap); 398 399 if (pcap_stats(hpcap, &pstat) < 0) 400 logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 401 else 402 logmsg(LOG_NOTICE, "%u packets received, %u dropped", 403 pstat.ps_recv, pstat.ps_drop); 404 405 pcap_close(hpcap); 406 if (!Debug) 407 closelog(); 408 return (0); 409} 410