1/* $NetBSD: npfd.c,v 1.9 2017/10/16 11:18:43 christos Exp $ */ 2 3/*- 4 * Copyright (c) 2015 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__RCSID("$NetBSD: npfd.c,v 1.9 2017/10/16 11:18:43 christos Exp $"); 34 35#include <stdio.h> 36#include <string.h> 37#include <fcntl.h> 38#include <signal.h> 39#include <stdlib.h> 40#include <unistd.h> 41#include <stdbool.h> 42#include <poll.h> 43#include <errno.h> 44#include <err.h> 45#include <syslog.h> 46#include <util.h> 47 48#include <net/npf.h> 49 50#include "npfd.h" 51 52static volatile sig_atomic_t hup, stats, done, flush; 53 54static int 55npfd_getctl(void) 56{ 57 int fd, ver; 58 59 fd = open(NPF_DEV_PATH, O_RDONLY); 60 if (fd == -1) { 61 err(EXIT_FAILURE, "cannot open '%s'", NPF_DEV_PATH); 62 } 63 if (ioctl(fd, IOC_NPF_VERSION, &ver) == -1) { 64 err(EXIT_FAILURE, "ioctl(IOC_NPF_VERSION)"); 65 } 66 if (ver != NPF_VERSION) { 67 errx(EXIT_FAILURE, 68 "Incompatible NPF interface version (%d, kernel %d)\n" 69 "Hint: update userland?", NPF_VERSION, ver); 70 } 71 return fd; 72} 73 74static void 75npfd_event_loop(npfd_log_t *log, int delay) 76{ 77 struct pollfd pfd; 78 size_t count = 0; 79 80 pfd.fd = npfd_log_getsock(log); 81 pfd.events = POLLHUP | POLLIN; 82 83 while (!done) { 84 if (hup) { 85 hup = false; 86 npfd_log_file_reopen(log, false); 87 } 88 if (stats) { 89 stats = false; 90 npfd_log_stats(log); 91 } 92 if (flush) { 93 flush = false; 94 npfd_log_flush(log); 95 count = 0; 96 } 97 switch (poll(&pfd, 1, delay)) { 98 case -1: 99 if (errno == EINTR) 100 continue; 101 syslog(LOG_ERR, "poll failed: %m"); 102 exit(EXIT_FAILURE); 103 /*NOTREACHED*/ 104 case 0: 105 npfd_log_flush(log); 106 count = 0; 107 continue; 108 default: 109 if (count++ >= 100) { 110 npfd_log_flush(log); 111 count = 0; 112 } 113 if (npfd_log(log) <= 0) 114 npfd_log_pcap_reopen(log); 115 } 116 117 } 118 npfd_log_destroy(log); 119} 120 121static void 122sighandler(int sig) 123{ 124 switch (sig) { 125 case SIGHUP: 126 hup = true; 127 break; 128 case SIGTERM: 129 case SIGINT: 130 done = true; 131 break; 132 case SIGINFO: 133 stats = true; 134 break; 135 case SIGALRM: 136 flush = true; 137 break; 138 default: 139 syslog(LOG_ERR, "Unhandled signal %d", sig); 140 break; 141 } 142} 143 144static __dead void 145usage(void) 146{ 147 fprintf(stderr, "Usage: %s [-D] [-d <delay>] [-i <interface>]" 148 " [-f <filename>] [-p <pidfile>] [-s <snaplen>] expression\n", 149 getprogname()); 150 exit(EXIT_FAILURE); 151} 152 153static char * 154copyargs(int argc, char **argv) 155{ 156 if (argc == 0) 157 return NULL; 158 159 size_t len = 0, p = 0; 160 char *buf = NULL; 161 162 for (int i = 0; i < argc; i++) { 163 size_t l = strlen(argv[i]); 164 if (p + l + 1 >= len) 165 buf = erealloc(buf, len = p + l + 1); 166 memcpy(buf + p, argv[i], l); 167 p += l; 168 buf[p++] = i == argc - 1 ? '\0' : ' '; 169 } 170 return buf; 171} 172 173int 174main(int argc, char **argv) 175{ 176 bool daemon_off = false; 177 int ch; 178 179 int delay = 5 * 1000; 180 const char *iface = "npflog0"; 181 int snaplen = 116; 182 char *pidname = NULL; 183 char *filename = NULL; 184 185 int fd = npfd_getctl(); 186 (void)close(fd); 187 188 while ((ch = getopt(argc, argv, "Dd:f:i:p:s:")) != -1) { 189 switch (ch) { 190 case 'D': 191 daemon_off = true; 192 break; 193 case 'd': 194 delay = atoi(optarg) * 1000; 195 break; 196 case 'f': 197 filename = optarg; 198 break; 199 case 'i': 200 iface = optarg; 201 break; 202 case 'p': 203 pidname = optarg; 204 break; 205 case 's': 206 snaplen = atoi(optarg); 207 break; 208 default: 209 usage(); 210 } 211 } 212 213 argc -= optind; 214 argv += optind; 215 216 char *filter = copyargs(argc, argv); 217 218 npfd_log_t *log = npfd_log_create(filename, iface, filter, snaplen); 219 220 if (!daemon_off) { 221 if (daemon(0, 0) == -1) 222 err(EXIT_FAILURE, "daemon"); 223 pidfile(pidname); 224 } 225 226 openlog(argv[0], LOG_PID | LOG_NDELAY | LOG_CONS, LOG_DAEMON); 227 signal(SIGHUP, sighandler); 228 signal(SIGINT, sighandler); 229 signal(SIGTERM, sighandler); 230 signal(SIGINFO, sighandler); 231 signal(SIGQUIT, sighandler); 232 233 npfd_event_loop(log, delay); 234 235 closelog(); 236 237 return 0; 238} 239