1 /* $OpenBSD: pf.c,v 1.2 2024/07/10 20:33:31 martijn Exp $ */
2
3 /*
4 * Copyright (c) 2012 Joel Knight <joel@openbsd.org>
5 * Copyright (c) 2002 Cedric Berger
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
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/ioctl.h>
37
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40 #include <net/if.h>
41 #include <net/pfvar.h>
42
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stdint.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <event.h>
52
53 #include "snmpd.h"
54
55 int devpf = 0;
56
57 size_t buf_esize[PFRB_MAX] = { 0,
58 sizeof(struct pfr_table), sizeof(struct pfr_tstats),
59 sizeof(struct pfr_addr), sizeof(struct pfr_astats),
60 sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
61 };
62
63 void
pf_init(void)64 pf_init(void)
65 {
66 if ((devpf = open("/dev/pf", O_RDONLY)) == -1)
67 fatal("pf_init");
68 }
69
70 int
pf_get_stats(struct pf_status * s)71 pf_get_stats(struct pf_status *s)
72 {
73 extern int devpf;
74
75 memset(s, 0, sizeof(*s));
76 if (ioctl(devpf, DIOCGETSTATUS, s) == -1) {
77 log_warn("DIOCGETSTATUS");
78 return (-1);
79 }
80
81 return (0);
82 }
83
84 int
pfr_get_astats(struct pfr_table * tbl,struct pfr_astats * addr,int * size,int flags)85 pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
86 int flags)
87 {
88 struct pfioc_table io;
89 extern int devpf;
90
91 if (tbl == NULL || size == NULL || *size < 0 ||
92 (*size && addr == NULL))
93 return (-1);
94
95 bzero(&io, sizeof io);
96 io.pfrio_flags = flags;
97 io.pfrio_table = *tbl;
98 io.pfrio_buffer = addr;
99 io.pfrio_esize = sizeof(*addr);
100 io.pfrio_size = *size;
101 if (ioctl(devpf, DIOCRGETASTATS, &io) == -1)
102 return (-1);
103 *size = io.pfrio_size;
104 return (0);
105 }
106
107 int
pfr_get_tstats(struct pfr_table * filter,struct pfr_tstats * tbl,int * size,int flags)108 pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
109 int flags)
110 {
111 struct pfioc_table io;
112 extern int devpf;
113
114 if (size == NULL || *size < 0 || (*size && tbl == NULL))
115 return (-1);
116 bzero(&io, sizeof io);
117 io.pfrio_flags = flags;
118 if (filter != NULL)
119 io.pfrio_table = *filter;
120 io.pfrio_buffer = tbl;
121 io.pfrio_esize = sizeof(*tbl);
122 io.pfrio_size = *size;
123 if (ioctl(devpf, DIOCRGETTSTATS, &io) == -1)
124 return (-1);
125 *size = io.pfrio_size;
126 return (0);
127 }
128
129 int
pfr_buf_grow(struct pfr_buffer * b,int minsize)130 pfr_buf_grow(struct pfr_buffer *b, int minsize)
131 {
132 caddr_t p;
133 size_t bs;
134
135 if (minsize != 0 && minsize <= b->pfrb_msize)
136 return (0);
137 bs = buf_esize[b->pfrb_type];
138 if (!b->pfrb_msize) {
139 if (minsize < 64)
140 minsize = 64;
141 b->pfrb_caddr = calloc(bs, minsize);
142 if (b->pfrb_caddr == NULL)
143 return (-1);
144 b->pfrb_msize = minsize;
145 } else {
146 if (minsize == 0)
147 minsize = b->pfrb_msize * 2;
148 if (minsize < 0 || (size_t)minsize >= SIZE_MAX / bs) {
149 /* msize overflow */
150 return (-1);
151 }
152 p = reallocarray(b->pfrb_caddr, minsize, bs);
153 if (p == NULL)
154 return (-1);
155 bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
156 b->pfrb_caddr = p;
157 b->pfrb_msize = minsize;
158 }
159 return (0);
160 }
161
162 const void *
pfr_buf_next(struct pfr_buffer * b,const void * prev)163 pfr_buf_next(struct pfr_buffer *b, const void *prev)
164 {
165 size_t bs;
166
167 if (b == NULL)
168 return (NULL);
169 if (b->pfrb_size == 0)
170 return (NULL);
171 if (prev == NULL)
172 return (b->pfrb_caddr);
173 bs = buf_esize[b->pfrb_type];
174 if ((((const char *)prev)-((char *)b->pfrb_caddr)) / bs >=
175 (size_t)b->pfrb_size-1)
176 return (NULL);
177
178 return (((const char *)prev) + bs);
179 }
180
181 int
pfi_get_ifaces(const char * filter,struct pfi_kif * buf,int * size)182 pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
183 {
184 struct pfioc_iface io;
185 extern int devpf;
186
187 if (size == NULL || *size < 0 || (*size && buf == NULL)) {
188 errno = EINVAL;
189 return (-1);
190 }
191 bzero(&io, sizeof io);
192 if (filter != NULL)
193 if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
194 sizeof(io.pfiio_name)) {
195 errno = EINVAL;
196 return (-1);
197 }
198 io.pfiio_buffer = buf;
199 io.pfiio_esize = sizeof(*buf);
200 io.pfiio_size = *size;
201 if (ioctl(devpf, DIOCIGETIFACES, &io) == -1)
202 return (-1);
203 *size = io.pfiio_size;
204 return (0);
205 }
206
207 int
pfi_get(struct pfr_buffer * b,const char * filter)208 pfi_get(struct pfr_buffer *b, const char *filter)
209 {
210 bzero(b, sizeof(struct pfr_buffer));
211 b->pfrb_type = PFRB_IFACES;
212 for (;;) {
213 pfr_buf_grow(b, 0);
214 b->pfrb_size = b->pfrb_msize;
215 if (pfi_get_ifaces(filter, b->pfrb_caddr, &(b->pfrb_size)))
216 return (1);
217 if (b->pfrb_size < b->pfrb_msize)
218 break;
219 }
220
221 return (0);
222 }
223
224 int
pfi_count(void)225 pfi_count(void)
226 {
227 struct pfr_buffer b;
228 const struct pfi_kif *p;
229 int c = 0;
230
231 if (pfi_get(&b, NULL)) {
232 free(b.pfrb_caddr);
233 return (-1);
234 }
235
236 PFRB_FOREACH(p, &b)
237 c++;
238
239 free(b.pfrb_caddr);
240 return (c);
241 }
242
243 int
pfi_get_if(struct pfi_kif * rp,int idx)244 pfi_get_if(struct pfi_kif *rp, int idx)
245 {
246 struct pfr_buffer b;
247 const struct pfi_kif *p;
248 int i = 1;
249
250 if (pfi_get(&b, NULL)) {
251 free(b.pfrb_caddr);
252 return (-1);
253 }
254
255 PFRB_FOREACH(p, &b) {
256 if (i == idx)
257 break;
258 i++;
259 }
260
261 if (p == NULL) {
262 free(b.pfrb_caddr);
263 return (-1);
264 }
265
266 bcopy(p, rp, sizeof(struct pfi_kif));
267 free(b.pfrb_caddr);
268
269 return (0);
270 }
271
272 int
pft_get(struct pfr_buffer * b,struct pfr_table * filter)273 pft_get(struct pfr_buffer *b, struct pfr_table *filter)
274 {
275 bzero(b, sizeof(struct pfr_buffer));
276 b->pfrb_type = PFRB_TSTATS;
277
278 for (;;) {
279 pfr_buf_grow(b, b->pfrb_size);
280 b->pfrb_size = b->pfrb_msize;
281 if (pfr_get_tstats(filter, b->pfrb_caddr, &(b->pfrb_size), 0))
282 return (1);
283 if (b->pfrb_size <= b->pfrb_msize)
284 break;
285 }
286
287 return (0);
288 }
289
290 int
pft_get_table(struct pfr_tstats * rts,int idx)291 pft_get_table(struct pfr_tstats *rts, int idx)
292 {
293 struct pfr_buffer b;
294 const struct pfr_tstats *ts;
295 int i = 1;
296
297 if (pft_get(&b, NULL)) {
298 free(b.pfrb_caddr);
299 return (-1);
300 }
301
302 PFRB_FOREACH(ts, &b) {
303 if (!(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
304 continue;
305 if (i == idx)
306 break;
307 i++;
308 }
309
310 if (ts == NULL) {
311 free(b.pfrb_caddr);
312 return (-1);
313 }
314
315 bcopy(ts, rts, sizeof(struct pfr_tstats));
316 free(b.pfrb_caddr);
317
318 return (0);
319 }
320
321 int
pft_count(void)322 pft_count(void)
323 {
324 struct pfr_buffer b;
325 const struct pfr_tstats *ts;
326 int c = 0;
327
328 if (pft_get(&b, NULL)) {
329 free(b.pfrb_caddr);
330 return (-1);
331 }
332
333 PFRB_FOREACH(ts, &b) {
334 if (!(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
335 continue;
336 c++;
337 }
338
339 free(b.pfrb_caddr);
340 return (c);
341 }
342
343 int
pfta_get(struct pfr_buffer * b,struct pfr_table * filter)344 pfta_get(struct pfr_buffer *b, struct pfr_table *filter)
345 {
346 bzero(b, sizeof(struct pfr_buffer));
347 b->pfrb_type = PFRB_ASTATS;
348
349 for (;;) {
350 pfr_buf_grow(b, b->pfrb_size);
351 b->pfrb_size = b->pfrb_msize;
352 if (pfr_get_astats(filter, b->pfrb_caddr, &(b->pfrb_size), 0)) {
353 return (1);
354 }
355 if (b->pfrb_size <= b->pfrb_msize)
356 break;
357 }
358
359 return (0);
360 }
361
362 int
pfta_get_addr(struct pfr_astats * ras,int tblidx)363 pfta_get_addr(struct pfr_astats *ras, int tblidx)
364 {
365 struct pfr_buffer ba;
366 struct pfr_tstats ts;
367 struct pfr_table filter;
368 const struct pfr_astats *as;
369
370 if (pft_get_table(&ts, tblidx))
371 return (-1);
372
373 bzero(&filter, sizeof(filter));
374 if (strlcpy(filter.pfrt_name, ts.pfrts_name,
375 sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) {
376 return (-1);
377 }
378
379 if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) {
380 free(ba.pfrb_caddr);
381 return (-1);
382 }
383
384 PFRB_FOREACH(as, &ba) {
385 if (as->pfras_a.pfra_af != AF_INET)
386 continue;
387 if ((memcmp(&as->pfras_a.pfra_ip4addr, &ras->pfras_a.pfra_ip4addr,
388 sizeof(as->pfras_a.pfra_ip4addr)) == 0)
389 && (as->pfras_a.pfra_net == ras->pfras_a.pfra_net))
390 break;
391 }
392
393 if (as == NULL) {
394 free(ba.pfrb_caddr);
395 return (-1);
396 }
397
398 bcopy(as, ras, sizeof(struct pfr_astats));
399 free(ba.pfrb_caddr);
400
401 return (0);
402 }
403
404 int
pfta_get_nextaddr(struct pfr_astats * ras,int * tblidx)405 pfta_get_nextaddr(struct pfr_astats *ras, int *tblidx)
406 {
407 struct pfr_buffer ba;
408 struct pfr_tstats ts;
409 struct pfr_table filter;
410 const struct pfr_astats *as;
411 int i, found = 0, cmp;
412
413 ba.pfrb_caddr = NULL;
414
415 for (i = *tblidx; !pft_get_table(&ts, i); i++) {
416 bzero(&filter, sizeof(filter));
417 if (strlcpy(filter.pfrt_name, ts.pfrts_name,
418 sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name))
419 goto fail;
420
421 if (pfta_get(&ba, &filter) || ba.pfrb_size == 0)
422 goto fail;
423
424 PFRB_FOREACH(as, &ba) {
425 if (as->pfras_a.pfra_af != AF_INET)
426 continue;
427 if (found)
428 goto found;
429 cmp = memcmp(&as->pfras_a.pfra_ip4addr,
430 &ras->pfras_a.pfra_ip4addr,
431 sizeof(as->pfras_a.pfra_ip4addr));
432 if (cmp == 0) {
433 if (as->pfras_a.pfra_net ==
434 ras->pfras_a.pfra_net)
435 found = 1;
436 if (as->pfras_a.pfra_net >
437 ras->pfras_a.pfra_net)
438 goto found;
439 } else if (cmp > 0)
440 goto found;
441 }
442
443 free(ba.pfrb_caddr);
444 ba.pfrb_caddr = NULL;
445 }
446
447
448 fail:
449 free(ba.pfrb_caddr);
450
451 return (-1);
452
453 found:
454 bcopy(as, ras, sizeof(struct pfr_astats));
455 *tblidx = i;
456
457 free(ba.pfrb_caddr);
458
459 return (0);
460 }
461
462 int
pfta_get_first(struct pfr_astats * ras)463 pfta_get_first(struct pfr_astats *ras)
464 {
465 struct pfr_buffer ba;
466 struct pfr_tstats ts;
467 struct pfr_table filter;
468 const struct pfr_astats *as;
469
470 if (pft_get_table(&ts, 1))
471 return (-1);
472
473 bzero(&filter, sizeof(filter));
474 if (strlcpy(filter.pfrt_name, ts.pfrts_name,
475 sizeof(filter.pfrt_name)) >= sizeof(filter.pfrt_name)) {
476 return (-1);
477 }
478
479 if (pfta_get(&ba, &filter) || ba.pfrb_size == 0) {
480 free(ba.pfrb_caddr);
481 return (-1);
482 }
483
484 /* take the first AF_INET addr */
485 PFRB_FOREACH(as, &ba) {
486 if (as->pfras_a.pfra_af != AF_INET)
487 continue;
488 break;
489 }
490
491 if (as == NULL) {
492 free(ba.pfrb_caddr);
493 return (-1);
494 }
495
496 bcopy(as, ras, sizeof(struct pfr_astats));
497 free(ba.pfrb_caddr);
498
499 return (0);
500 }
501
502