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