xref: /freebsd/sbin/pfctl/pfctl_radix.c (revision b077aed33b7b6aefca7b17ddb250cf521f938613)
1 /*	$OpenBSD: pfctl_radix.c,v 1.27 2005/05/21 21:03:58 henning Exp $ */
2 
3 /*-
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Copyright (c) 2002 Cedric Berger
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  *    - Redistributions of source code must retain the above copyright
14  *      notice, this list of conditions and the following disclaimer.
15  *    - Redistributions in binary form must reproduce the above
16  *      copyright notice, this list of conditions and the following
17  *      disclaimer in the documentation and/or other materials provided
18  *      with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/types.h>
39 #include <sys/ioctl.h>
40 #include <sys/socket.h>
41 
42 #include <net/if.h>
43 #include <net/pfvar.h>
44 
45 #include <errno.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <limits.h>
51 #include <err.h>
52 
53 #include "pfctl.h"
54 
55 #define BUF_SIZE 256
56 
57 extern int dev;
58 
59 static int	 pfr_next_token(char buf[BUF_SIZE], FILE *);
60 
61 static void
62 pfr_report_error(struct pfr_table *tbl, struct pfioc_table *io,
63     const char *err)
64 {
65 	unsigned long maxcount;
66 	size_t s;
67 
68 	s = sizeof(maxcount);
69 	if (sysctlbyname("net.pf.request_maxcount", &maxcount, &s, NULL,
70 	    0) == -1)
71 		return;
72 
73 	if (io->pfrio_size > maxcount || io->pfrio_size2 > maxcount)
74 		fprintf(stderr, "cannot %s %s: too many elements.\n"
75 		    "Consider increasing net.pf.request_maxcount.",
76 		    err, tbl->pfrt_name);
77 }
78 
79 int
80 pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
81 {
82 	struct pfioc_table io;
83 
84 	bzero(&io, sizeof io);
85 	io.pfrio_flags = flags;
86 	if (filter != NULL)
87 		io.pfrio_table = *filter;
88 	if (ioctl(dev, DIOCRCLRTABLES, &io))
89 		return (-1);
90 	if (ndel != NULL)
91 		*ndel = io.pfrio_ndel;
92 	return (0);
93 }
94 
95 int
96 pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
97 {
98 	struct pfioc_table io;
99 
100 	if (size < 0 || (size && tbl == NULL)) {
101 		errno = EINVAL;
102 		return (-1);
103 	}
104 	bzero(&io, sizeof io);
105 	io.pfrio_flags = flags;
106 	io.pfrio_buffer = tbl;
107 	io.pfrio_esize = sizeof(*tbl);
108 	io.pfrio_size = size;
109 	if (ioctl(dev, DIOCRADDTABLES, &io)) {
110 		pfr_report_error(tbl, &io, "add table");
111 		return (-1);
112 	}
113 	if (nadd != NULL)
114 		*nadd = io.pfrio_nadd;
115 	return (0);
116 }
117 
118 int
119 pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
120 {
121 	struct pfioc_table io;
122 
123 	if (size < 0 || (size && tbl == NULL)) {
124 		errno = EINVAL;
125 		return (-1);
126 	}
127 	bzero(&io, sizeof io);
128 	io.pfrio_flags = flags;
129 	io.pfrio_buffer = tbl;
130 	io.pfrio_esize = sizeof(*tbl);
131 	io.pfrio_size = size;
132 	if (ioctl(dev, DIOCRDELTABLES, &io)) {
133 		pfr_report_error(tbl, &io, "delete table");
134 		return (-1);
135 	}
136 	if (ndel != NULL)
137 		*ndel = io.pfrio_ndel;
138 	return (0);
139 }
140 
141 int
142 pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
143 	int flags)
144 {
145 	struct pfioc_table io;
146 
147 	if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
148 		errno = EINVAL;
149 		return (-1);
150 	}
151 	bzero(&io, sizeof io);
152 	io.pfrio_flags = flags;
153 	if (filter != NULL)
154 		io.pfrio_table = *filter;
155 	io.pfrio_buffer = tbl;
156 	io.pfrio_esize = sizeof(*tbl);
157 	io.pfrio_size = *size;
158 	if (ioctl(dev, DIOCRGETTABLES, &io)) {
159 		pfr_report_error(tbl, &io, "get table");
160 		return (-1);
161 	}
162 	*size = io.pfrio_size;
163 	return (0);
164 }
165 
166 int
167 pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
168 	int flags)
169 {
170 	struct pfioc_table io;
171 
172 	if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
173 		errno = EINVAL;
174 		return (-1);
175 	}
176 	bzero(&io, sizeof io);
177 	io.pfrio_flags = flags;
178 	if (filter != NULL)
179 		io.pfrio_table = *filter;
180 	io.pfrio_buffer = tbl;
181 	io.pfrio_esize = sizeof(*tbl);
182 	io.pfrio_size = *size;
183 	if (ioctl(dev, DIOCRGETTSTATS, &io)) {
184 		pfr_report_error(filter, &io, "get tstats for");
185 		return (-1);
186 	}
187 	*size = io.pfrio_size;
188 	return (0);
189 }
190 
191 int
192 pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
193 {
194 	struct pfioc_table io;
195 
196 	if (tbl == NULL) {
197 		errno = EINVAL;
198 		return (-1);
199 	}
200 	bzero(&io, sizeof io);
201 	io.pfrio_flags = flags;
202 	io.pfrio_table = *tbl;
203 	if (ioctl(dev, DIOCRCLRADDRS, &io))
204 		return (-1);
205 	if (ndel != NULL)
206 		*ndel = io.pfrio_ndel;
207 	return (0);
208 }
209 
210 int
211 pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
212     int *nadd, int flags)
213 {
214 	int ret;
215 
216 	ret = pfctl_table_add_addrs(dev, tbl, addr, size, nadd, flags);
217 	if (ret) {
218 		errno = ret;
219 		return (-1);
220 	}
221 	return (0);
222 }
223 
224 int
225 pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
226     int *ndel, int flags)
227 {
228 	int ret;
229 
230 	ret = pfctl_table_del_addrs(dev, tbl, addr, size, ndel, flags);
231 	if (ret) {
232 		errno = ret;
233 		return (-1);
234 	}
235 	return (0);
236 }
237 
238 int
239 pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
240     int *size2, int *nadd, int *ndel, int *nchange, int flags)
241 {
242 	int ret;
243 
244 	ret = pfctl_table_set_addrs(dev, tbl, addr, size, size2, nadd, ndel,
245 	    nchange, flags);
246 	if (ret) {
247 		errno = ret;
248 		return (-1);
249 	}
250 	return (0);
251 }
252 
253 int
254 pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
255     int flags)
256 {
257 	int ret;
258 
259 	ret = pfctl_table_get_addrs(dev, tbl, addr, size, flags);
260 	if (ret) {
261 		errno = ret;
262 		return (-1);
263 	}
264 	return (0);
265 }
266 
267 int
268 pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
269     int flags)
270 {
271 	struct pfioc_table io;
272 
273 	if (tbl == NULL || size == NULL || *size < 0 ||
274 	    (*size && addr == NULL)) {
275 		errno = EINVAL;
276 		return (-1);
277 	}
278 	bzero(&io, sizeof io);
279 	io.pfrio_flags = flags;
280 	io.pfrio_table = *tbl;
281 	io.pfrio_buffer = addr;
282 	io.pfrio_esize = sizeof(*addr);
283 	io.pfrio_size = *size;
284 	if (ioctl(dev, DIOCRGETASTATS, &io)) {
285 		pfr_report_error(tbl, &io, "get astats from");
286 		return (-1);
287 	}
288 	*size = io.pfrio_size;
289 	return (0);
290 }
291 
292 int
293 pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
294 {
295 	struct pfioc_table io;
296 
297 	if (size < 0 || (size && !tbl)) {
298 		errno = EINVAL;
299 		return (-1);
300 	}
301 	bzero(&io, sizeof io);
302 	io.pfrio_flags = flags;
303 	io.pfrio_buffer = tbl;
304 	io.pfrio_esize = sizeof(*tbl);
305 	io.pfrio_size = size;
306 	if (ioctl(dev, DIOCRCLRTSTATS, &io)) {
307 		pfr_report_error(tbl, &io, "clear tstats from");
308 		return (-1);
309 	}
310 	if (nzero)
311 		*nzero = io.pfrio_nzero;
312 	return (0);
313 }
314 
315 int
316 pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
317     int *nmatch, int flags)
318 {
319 	struct pfioc_table io;
320 
321 	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
322 		errno = EINVAL;
323 		return (-1);
324 	}
325 	bzero(&io, sizeof io);
326 	io.pfrio_flags = flags;
327 	io.pfrio_table = *tbl;
328 	io.pfrio_buffer = addr;
329 	io.pfrio_esize = sizeof(*addr);
330 	io.pfrio_size = size;
331 	if (ioctl(dev, DIOCRTSTADDRS, &io)) {
332 		pfr_report_error(tbl, &io, "test addresses in");
333 		return (-1);
334 	}
335 	if (nmatch)
336 		*nmatch = io.pfrio_nmatch;
337 	return (0);
338 }
339 
340 int
341 pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
342     int *nadd, int *naddr, int ticket, int flags)
343 {
344 	struct pfioc_table io;
345 
346 	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
347 		errno = EINVAL;
348 		return (-1);
349 	}
350 	bzero(&io, sizeof io);
351 	io.pfrio_flags = flags;
352 	io.pfrio_table = *tbl;
353 	io.pfrio_buffer = addr;
354 	io.pfrio_esize = sizeof(*addr);
355 	io.pfrio_size = size;
356 	io.pfrio_ticket = ticket;
357 	if (ioctl(dev, DIOCRINADEFINE, &io)) {
358 		pfr_report_error(tbl, &io, "define inactive set table");
359 		return (-1);
360 	}
361 	if (nadd != NULL)
362 		*nadd = io.pfrio_nadd;
363 	if (naddr != NULL)
364 		*naddr = io.pfrio_naddr;
365 	return (0);
366 }
367 
368 /* interface management code */
369 
370 int
371 pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
372 {
373 	struct pfioc_iface io;
374 
375 	if (size == NULL || *size < 0 || (*size && buf == NULL)) {
376 		errno = EINVAL;
377 		return (-1);
378 	}
379 	bzero(&io, sizeof io);
380 	if (filter != NULL)
381 		if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
382 		    sizeof(io.pfiio_name)) {
383 			errno = EINVAL;
384 			return (-1);
385 		}
386 	io.pfiio_buffer = buf;
387 	io.pfiio_esize = sizeof(*buf);
388 	io.pfiio_size = *size;
389 	if (ioctl(dev, DIOCIGETIFACES, &io))
390 		return (-1);
391 	*size = io.pfiio_size;
392 	return (0);
393 }
394 
395 /* buffer management code */
396 
397 const size_t buf_esize[PFRB_MAX] = { 0,
398 	sizeof(struct pfr_table), sizeof(struct pfr_tstats),
399 	sizeof(struct pfr_addr), sizeof(struct pfr_astats),
400 	sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
401 };
402 
403 /*
404  * add one element to the buffer
405  */
406 int
407 pfr_buf_add(struct pfr_buffer *b, const void *e)
408 {
409 	size_t bs;
410 
411 	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
412 	    e == NULL) {
413 		errno = EINVAL;
414 		return (-1);
415 	}
416 	bs = buf_esize[b->pfrb_type];
417 	if (b->pfrb_size == b->pfrb_msize)
418 		if (pfr_buf_grow(b, 0))
419 			return (-1);
420 	memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
421 	b->pfrb_size++;
422 	return (0);
423 }
424 
425 /*
426  * return next element of the buffer (or first one if prev is NULL)
427  * see PFRB_FOREACH macro
428  */
429 void *
430 pfr_buf_next(struct pfr_buffer *b, const void *prev)
431 {
432 	size_t bs;
433 
434 	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
435 		return (NULL);
436 	if (b->pfrb_size == 0)
437 		return (NULL);
438 	if (prev == NULL)
439 		return (b->pfrb_caddr);
440 	bs = buf_esize[b->pfrb_type];
441 	if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
442 		return (NULL);
443 	return (((caddr_t)prev) + bs);
444 }
445 
446 /*
447  * minsize:
448  *    0: make the buffer somewhat bigger
449  *    n: make room for "n" entries in the buffer
450  */
451 int
452 pfr_buf_grow(struct pfr_buffer *b, int minsize)
453 {
454 	caddr_t p;
455 	size_t bs;
456 
457 	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
458 		errno = EINVAL;
459 		return (-1);
460 	}
461 	if (minsize != 0 && minsize <= b->pfrb_msize)
462 		return (0);
463 	bs = buf_esize[b->pfrb_type];
464 	if (!b->pfrb_msize) {
465 		if (minsize < 64)
466 			minsize = 64;
467 		b->pfrb_caddr = calloc(bs, minsize);
468 		if (b->pfrb_caddr == NULL)
469 			return (-1);
470 		b->pfrb_msize = minsize;
471 	} else {
472 		if (minsize == 0)
473 			minsize = b->pfrb_msize * 2;
474 		if (minsize < 0 || minsize >= SIZE_T_MAX / bs) {
475 			/* msize overflow */
476 			errno = ENOMEM;
477 			return (-1);
478 		}
479 		p = realloc(b->pfrb_caddr, minsize * bs);
480 		if (p == NULL)
481 			return (-1);
482 		bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
483 		b->pfrb_caddr = p;
484 		b->pfrb_msize = minsize;
485 	}
486 	return (0);
487 }
488 
489 /*
490  * reset buffer and free memory.
491  */
492 void
493 pfr_buf_clear(struct pfr_buffer *b)
494 {
495 	if (b == NULL)
496 		return;
497 	if (b->pfrb_caddr != NULL)
498 		free(b->pfrb_caddr);
499 	b->pfrb_caddr = NULL;
500 	b->pfrb_size = b->pfrb_msize = 0;
501 }
502 
503 int
504 pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
505     int (*append_addr)(struct pfr_buffer *, char *, int))
506 {
507 	FILE	*fp;
508 	char	 buf[BUF_SIZE];
509 	int	 rv;
510 
511 	if (file == NULL)
512 		return (0);
513 	if (!strcmp(file, "-"))
514 		fp = stdin;
515 	else {
516 		fp = pfctl_fopen(file, "r");
517 		if (fp == NULL)
518 			return (-1);
519 	}
520 	while ((rv = pfr_next_token(buf, fp)) == 1)
521 		if (append_addr(b, buf, nonetwork)) {
522 			rv = -1;
523 			break;
524 		}
525 	if (fp != stdin)
526 		fclose(fp);
527 	return (rv);
528 }
529 
530 int
531 pfr_next_token(char buf[BUF_SIZE], FILE *fp)
532 {
533 	static char	next_ch = ' ';
534 	int		i = 0;
535 
536 	for (;;) {
537 		/* skip spaces */
538 		while (isspace(next_ch) && !feof(fp))
539 			next_ch = fgetc(fp);
540 		/* remove from '#' until end of line */
541 		if (next_ch == '#')
542 			while (!feof(fp)) {
543 				next_ch = fgetc(fp);
544 				if (next_ch == '\n')
545 					break;
546 			}
547 		else
548 			break;
549 	}
550 	if (feof(fp)) {
551 		next_ch = ' ';
552 		return (0);
553 	}
554 	do {
555 		if (i < BUF_SIZE)
556 			buf[i++] = next_ch;
557 		next_ch = fgetc(fp);
558 	} while (!feof(fp) && !isspace(next_ch));
559 	if (i >= BUF_SIZE) {
560 		errno = EINVAL;
561 		return (-1);
562 	}
563 	buf[i] = '\0';
564 	return (1);
565 }
566 
567 char *
568 pfr_strerror(int errnum)
569 {
570 	switch (errnum) {
571 	case ESRCH:
572 		return "Table does not exist";
573 	case ENOENT:
574 		return "Anchor or Ruleset does not exist";
575 	default:
576 		return strerror(errnum);
577 	}
578 }
579