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