pflogd.c revision 126355
1/* $FreeBSD: head/contrib/pf/pflogd/pflogd.c 126355 2004-02-28 17:32:53Z 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#if defined(__FreeBSD__) 49#include "pidfile.h" 50#else 51#include <util.h> 52#endif 53 54#if defined(__FreeBSD__) 55#define __dead __volatile 56#endif 57 58#define DEF_SNAPLEN 116 /* default plus allow for larger header of pflog */ 59#define PCAP_TO_MS 500 /* pcap read timeout (ms) */ 60#define PCAP_NUM_PKTS 1000 /* max number of packets to process at each loop */ 61#define PCAP_OPT_FIL 0 /* filter optimization */ 62#define FLUSH_DELAY 60 /* flush delay */ 63 64#define PFLOGD_LOG_FILE "/var/log/pflog" 65#define PFLOGD_DEFAULT_IF "pflog0" 66 67pcap_t *hpcap; 68pcap_dumper_t *dpcap; 69 70int Debug = 0; 71int snaplen = DEF_SNAPLEN; 72 73volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup; 74 75char *filename = PFLOGD_LOG_FILE; 76char *interface = PFLOGD_DEFAULT_IF; 77char *filter = NULL; 78 79char errbuf[PCAP_ERRBUF_SIZE]; 80 81int log_debug = 0; 82unsigned int delay = FLUSH_DELAY; 83 84char *copy_argv(char * const *argv); 85int init_pcap(void); 86void logmsg(int priority, const char *message, ...); 87int reset_dump(void); 88void sig_alrm(int); 89void sig_close(int); 90void sig_hup(int); 91#if defined(__FreeBSD__) 92__volatile void usage(void); 93#else 94void usage(void); 95#endif 96 97 98char * 99copy_argv(char * const *argv) 100{ 101 size_t len = 0, n; 102 char *buf; 103 104 if (argv == NULL) 105 return (NULL); 106 107 for (n = 0; argv[n]; n++) 108 len += strlen(argv[n])+1; 109 if (len == 0) 110 return (NULL); 111 112 buf = malloc(len); 113 if (buf == NULL) 114 return (NULL); 115 116 strlcpy(buf, argv[0], len); 117 for (n = 1; argv[n]; n++) { 118 strlcat(buf, " ", len); 119 strlcat(buf, argv[n], len); 120 } 121 return (buf); 122} 123 124void 125logmsg(int pri, const char *message, ...) 126{ 127 va_list ap; 128 va_start(ap, message); 129 130 if (log_debug) { 131 vfprintf(stderr, message, ap); 132 fprintf(stderr, "\n"); 133 } else 134 vsyslog(pri, message, ap); 135 va_end(ap); 136} 137 138__dead void 139usage(void) 140{ 141 fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] "); 142 fprintf(stderr, "[-s snaplen] [expression]\n"); 143 exit(1); 144} 145 146void 147sig_close(int sig) 148{ 149 gotsig_close = 1; 150} 151 152void 153sig_hup(int sig) 154{ 155 gotsig_hup = 1; 156} 157 158void 159sig_alrm(int sig) 160{ 161 gotsig_alrm = 1; 162} 163 164int 165init_pcap(void) 166{ 167 struct bpf_program bprog; 168 pcap_t *oldhpcap = hpcap; 169 170 hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf); 171 if (hpcap == NULL) { 172 logmsg(LOG_ERR, "Failed to initialize: %s", errbuf); 173 hpcap = oldhpcap; 174 return (-1); 175 } 176 177 if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0) 178 logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 179 else if (pcap_setfilter(hpcap, &bprog) < 0) 180 logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap)); 181 if (filter != NULL) 182 free(filter); 183 184 if (pcap_datalink(hpcap) != DLT_PFLOG) { 185 logmsg(LOG_ERR, "Invalid datalink type"); 186 pcap_close(hpcap); 187 hpcap = oldhpcap; 188 return (-1); 189 } 190 191 if (oldhpcap) 192 pcap_close(oldhpcap); 193 194 snaplen = pcap_snapshot(hpcap); 195 return (0); 196} 197 198int 199reset_dump(void) 200{ 201 struct pcap_file_header hdr; 202 struct stat st; 203 int tmpsnap; 204 FILE *fp; 205 206 if (hpcap == NULL) 207 return (1); 208 if (dpcap) { 209 pcap_dump_close(dpcap); 210 dpcap = 0; 211 } 212 213 /* 214 * Basically reimplement pcap_dump_open() because it truncates 215 * files and duplicates headers and such. 216 */ 217 fp = fopen(filename, "a+"); 218 if (fp == NULL) { 219 snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 220 filename, pcap_strerror(errno)); 221 logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 222 return (1); 223 } 224 if (fstat(fileno(fp), &st) == -1) { 225 snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 226 filename, pcap_strerror(errno)); 227 logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap)); 228 return (1); 229 } 230 231 dpcap = (pcap_dumper_t *)fp; 232 233#define TCPDUMP_MAGIC 0xa1b2c3d4 234 235 if (st.st_size == 0) { 236 if (snaplen != pcap_snapshot(hpcap)) { 237 logmsg(LOG_NOTICE, "Using snaplen %d", snaplen); 238 if (init_pcap()) { 239 logmsg(LOG_ERR, "Failed to initialize"); 240 if (hpcap == NULL) return (-1); 241 logmsg(LOG_NOTICE, "Using old settings"); 242 } 243 } 244 hdr.magic = TCPDUMP_MAGIC; 245 hdr.version_major = PCAP_VERSION_MAJOR; 246 hdr.version_minor = PCAP_VERSION_MINOR; 247 hdr.thiszone = hpcap->tzoff; 248 hdr.snaplen = hpcap->snapshot; 249 hdr.sigfigs = 0; 250 hdr.linktype = hpcap->linktype; 251 252 if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 253 dpcap = NULL; 254 fclose(fp); 255 return (-1); 256 } 257 return (0); 258 } 259 260 /* 261 * XXX Must read the file, compare the header against our new 262 * options (in particular, snaplen) and adjust our options so 263 * that we generate a correct file. 264 */ 265 (void) fseek(fp, 0L, SEEK_SET); 266 if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) { 267 if (hdr.magic != TCPDUMP_MAGIC || 268 hdr.version_major != PCAP_VERSION_MAJOR || 269 hdr.version_minor != PCAP_VERSION_MINOR || 270 hdr.linktype != hpcap->linktype) { 271 logmsg(LOG_ERR, 272 "Invalid/incompatible log file, move it away"); 273 fclose(fp); 274 return (1); 275 } 276 if (hdr.snaplen != snaplen) { 277 logmsg(LOG_WARNING, 278 "Existing file specifies a snaplen of %u, using it", 279 hdr.snaplen); 280 tmpsnap = snaplen; 281 snaplen = hdr.snaplen; 282 if (init_pcap()) { 283 logmsg(LOG_ERR, "Failed to re-initialize"); 284 if (hpcap == 0) 285 return (-1); 286 logmsg(LOG_NOTICE, 287 "Using old settings, offset: %llu", 288 (unsigned long long)st.st_size); 289 } 290 snaplen = tmpsnap; 291 } 292 } 293 294 (void) fseek(fp, 0L, SEEK_END); 295 return (0); 296} 297 298int 299main(int argc, char **argv) 300{ 301 struct pcap_stat pstat; 302 int ch, np; 303 304 while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) { 305 switch (ch) { 306 case 'D': 307 Debug = 1; 308 break; 309 case 'd': 310 delay = atoi(optarg); 311 if (delay < 5 || delay > 60*60) 312 usage(); 313 break; 314 case 'f': 315 filename = optarg; 316 break; 317 case 's': 318 snaplen = atoi(optarg); 319 if (snaplen <= 0) 320 snaplen = DEF_SNAPLEN; 321 break; 322 default: 323 usage(); 324 } 325 326 } 327 328 log_debug = Debug; 329 argc -= optind; 330 argv += optind; 331 332 if (!Debug) { 333 openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON); 334 if (daemon(0, 0)) { 335 logmsg(LOG_WARNING, "Failed to become daemon: %s", 336 strerror(errno)); 337 } 338 pidfile(NULL); 339 } 340 341 (void)umask(S_IRWXG | S_IRWXO); 342 343 signal(SIGTERM, sig_close); 344 signal(SIGINT, sig_close); 345 signal(SIGQUIT, sig_close); 346 signal(SIGALRM, sig_alrm); 347 signal(SIGHUP, sig_hup); 348 alarm(delay); 349 350 if (argc) { 351 filter = copy_argv(argv); 352 if (filter == NULL) 353 logmsg(LOG_NOTICE, "Failed to form filter expression"); 354 } 355 356 if (init_pcap()) { 357 logmsg(LOG_ERR, "Exiting, init failure"); 358 exit(1); 359 } 360 361 if (reset_dump()) { 362 logmsg(LOG_ERR, "Failed to open log file %s", filename); 363 pcap_close(hpcap); 364 exit(1); 365 } 366 367 while (1) { 368 np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap); 369 if (np < 0) 370 logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap)); 371 372 if (gotsig_close) 373 break; 374 if (gotsig_hup) { 375 if (reset_dump()) { 376 logmsg(LOG_ERR, "Failed to open log file!"); 377 break; 378 } 379 logmsg(LOG_NOTICE, "Reopened logfile"); 380 gotsig_hup = 0; 381 } 382 383 if (gotsig_alrm) { 384 /* XXX pcap_dumper is an incomplete type which libpcap 385 * casts to a FILE* currently. For now it is safe to 386 * make the same assumption, however this may change 387 * in the future. 388 */ 389 if (dpcap) { 390 if (fflush((FILE *)dpcap) == EOF) { 391 break; 392 } 393 } 394 gotsig_alrm = 0; 395 alarm(delay); 396 } 397 } 398 399 logmsg(LOG_NOTICE, "Exiting due to signal"); 400 if (dpcap) 401 pcap_dump_close(dpcap); 402 403 if (pcap_stats(hpcap, &pstat) < 0) 404 logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap)); 405 else 406 logmsg(LOG_NOTICE, "%u packets received, %u dropped", 407 pstat.ps_recv, pstat.ps_drop); 408 409 pcap_close(hpcap); 410 if (!Debug) 411 closelog(); 412 return (0); 413} 414