1/*	$OpenBSD: pf.c,v 1.1.1.1 2022/09/01 14:20:33 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
55int	 devpf = 0;
56
57size_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
63void
64pf_init(void)
65{
66	if ((devpf = open("/dev/pf", O_RDONLY)) == -1)
67		fatal("pf_init");
68}
69
70int
71pf_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
84int
85pfr_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
107int
108pfr_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
129int
130pfr_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
162const void *
163pfr_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
181int
182pfi_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
207int
208pfi_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, b->pfrb_size);
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
224int
225pfi_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
243int
244pfi_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
272int
273pft_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
290int
291pft_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
321int
322pft_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
343int
344pfta_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
362int
363pfta_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
404int
405pfta_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
462int
463pfta_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