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
50 enum {
51 ARG_PROG = 0,
52 ARG_HOST,
53 ARG_IFNAME,
54 ARG_NUM
55 };
56
57 enum {
58 PFD_BPF = 0,
59 PFD_NUM
60 };
61
62 static void sighandler(int);
63 static void log_debug(const char *, ...) __printflike(1, 2);
64 static int bpf_open(void);
65 static void bpf_close(int);
66 static void bpf_read(int);
67
68 static sig_atomic_t quit;
69 static bool daemonize = false;
70 static int verbose = 0;
71 static const char *path_pid = "/var/run/bpfopen.pid";
72 static const char *path_bpf = "/dev/bpf";
73 static const char *ifname = NULL;
74
75 static void
usage(void)76 usage(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
86 int
main(int argc,char * argv[])87 main(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
145 static void
sighandler(int signo)146 sighandler(int signo)
147 {
148
149 quit = 1;
150 }
151
152 static void
log_debug(const char * fmt,...)153 log_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
167 static int
bpf_open(void)168 bpf_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
194 close_bpfd:
195 close(bpfd);
196
197 return -1;
198 }
199
200 static void
bpf_close(int bpfd)201 bpf_close(int bpfd)
202 {
203
204 close(bpfd);
205 }
206
207 static void
bpf_read(int bpfd)208 bpf_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