1/*	$NetBSD: bpfopen.c,v 1.2 2021/08/19 03:27:05 yamaguchi Exp $	*/
2
3/*
4 * Copyright (c) 2021 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__RCSID("$NetBSD: bpfopen.c,v 1.2 2021/08/19 03:27:05 yamaguchi Exp $");
31
32#include <sys/param.h>
33#include <sys/ioctl.h>
34
35#include <net/if.h>
36#include <net/bpf.h>
37
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <poll.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <signal.h>
46#include <unistd.h>
47
48#include <util.h>
49
50enum {
51	ARG_PROG = 0,
52	ARG_HOST,
53	ARG_IFNAME,
54	ARG_NUM
55};
56
57enum {
58	PFD_BPF = 0,
59	PFD_NUM
60};
61
62static void	sighandler(int);
63static void	log_debug(const char *, ...) __printflike(1, 2);
64static int	bpf_open(void);
65static void	bpf_close(int);
66static void	bpf_read(int);
67
68static sig_atomic_t	 quit;
69static bool		 daemonize = false;
70static int		 verbose = 0;
71static const char	*path_pid = "/var/run/bpfopen.pid";
72static const char	*path_bpf = "/dev/bpf";
73static const char	*ifname = NULL;
74
75static void
76usage(void)
77{
78
79	fprintf(stderr, "%s [-vd] [-p pidfile] [-b devbpf ] <ifname>\n"
80	    "\t-v: verbose\n"
81	    "\t-d: daemon mode\n",
82	    getprogname());
83	exit(1);
84}
85
86int
87main(int argc, char *argv[])
88{
89	int bpfd;
90	int ch;
91
92	while ((ch = getopt(argc, argv, "b:dp:v")) != -1) {
93		switch (ch) {
94		case 'b':
95			path_bpf = optarg;
96			break;
97		case 'd':
98			daemonize = true;
99			break;
100		case 'p':
101			path_pid = optarg;
102			break;
103		case 'v':
104			verbose++;
105			break;
106		default:
107			usage();
108		}
109	}
110
111	argc -= optind;
112	argv += optind;
113
114	if (argc != 1)
115		usage();
116
117	ifname = argv[0];
118
119	bpfd = bpf_open();
120	if (bpfd < 0)
121		err(1, "bpf_open");
122	log_debug("bpf opened");
123
124	if (daemonize) {
125		if (daemon(1, 1) != 0) {
126			bpf_close(bpfd);
127			err(1, "daemon");
128		}
129		log_debug("daemonized");
130
131		if (pidfile(path_pid) != 0) {
132			bpf_close(bpfd);
133			err(1, "pidfile");
134		}
135	}
136
137	bpf_read(bpfd);
138	bpf_close(bpfd);
139	if (daemonize)
140		pidfile_clean();
141
142	return 0;
143}
144
145static void
146sighandler(int signo)
147{
148
149	quit = 1;
150}
151
152static void
153log_debug(const char *fmt, ...)
154{
155	va_list ap;
156
157	if (verbose <= 0)
158		return;
159
160	va_start(ap, fmt);
161	vfprintf(stderr, fmt, ap);
162	va_end(ap);
163
164	fprintf(stderr, "\n");
165}
166
167static int
168bpf_open(void)
169{
170	struct ifreq ifr;
171	int bpfd;
172
173	bpfd = open(path_bpf, O_RDONLY);
174	if (bpfd < 0) {
175		log_debug("open: %s", strerror(errno));
176		return -1;
177	}
178
179	memset(&ifr, 0, sizeof(ifr));
180	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
181
182	if (ioctl(bpfd, BIOCSETIF, &ifr) != 0) {
183		log_debug("BIOCSETIF: %s", strerror(errno));
184		goto close_bpfd;
185	}
186
187	if (ioctl(bpfd, BIOCPROMISC, NULL) != 0) {
188		log_debug("BIOCPROMISC: %s", strerror(errno));
189		goto close_bpfd;
190	}
191
192	return bpfd;
193
194close_bpfd:
195	close(bpfd);
196
197	return -1;
198}
199
200static void
201bpf_close(int bpfd)
202{
203
204	close(bpfd);
205}
206
207static void
208bpf_read(int bpfd)
209{
210	struct pollfd pfd[PFD_NUM];
211	int nfds;
212	char *buf;
213	u_int bufsiz;
214	ssize_t n;
215
216	if (ioctl(bpfd, BIOCGBLEN, &bufsiz) != 0) {
217		bufsiz = BPF_DFLTBUFSIZE;
218		log_debug("BIOCGBLEN: %s, use default size %u",
219		    strerror(errno), bufsiz);
220	}
221
222	bufsiz = MIN(bufsiz, BPF_DFLTBUFSIZE * 4);
223
224	buf = malloc(bufsiz);
225	if (buf == NULL) {
226		log_debug("malloc: %s", strerror(errno));
227		return;
228	}
229
230	quit = 0;
231	signal(SIGTERM, sighandler);
232	signal(SIGQUIT, sighandler);
233	signal(SIGINT, sighandler);
234	signal(SIGHUP, sighandler);
235
236	log_debug("start reading %s, bufsiz=%u", ifname, bufsiz);
237
238	while (quit == 0) {
239		pfd[PFD_BPF].fd = bpfd;
240		pfd[PFD_BPF].events = POLLIN;
241
242		nfds = poll(pfd, PFD_NUM, 1 * 1000);
243		if (nfds == -1 && errno != EINTR) {
244			warn("poll");
245			quit = 1;
246		}
247
248		if (nfds > 0 && (pfd[PFD_BPF].revents & POLLIN)) {
249			/* read & drop */
250			memset(buf, 0, bufsiz);
251			n = read(pfd[PFD_BPF].fd, buf, bufsiz);
252			if (n < 0)
253				quit = 1;
254		}
255	}
256
257	log_debug("finish reading %s", ifname);
258	free(buf);
259}
260