xref: /openbsd/libexec/snmpd/snmpd_metrics/pf.c (revision c0b7aa14)
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