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
57extern int dev;
58
59static int	 pfr_next_token(char buf[], FILE *);
60
61static void
62pfr_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
79int
80pfr_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
95int
96pfr_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
118int
119pfr_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
141int
142pfr_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
166int
167pfr_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
191int
192pfr_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
210int
211pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
212    int *nadd, int flags)
213{
214	struct pfioc_table io;
215
216	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
217		errno = EINVAL;
218		return (-1);
219	}
220	bzero(&io, sizeof io);
221	io.pfrio_flags = flags;
222	io.pfrio_table = *tbl;
223	io.pfrio_buffer = addr;
224	io.pfrio_esize = sizeof(*addr);
225	io.pfrio_size = size;
226	if (ioctl(dev, DIOCRADDADDRS, &io)) {
227		pfr_report_error(tbl, &io, "add addresses in");
228		return (-1);
229	}
230	if (nadd != NULL)
231		*nadd = io.pfrio_nadd;
232	return (0);
233}
234
235int
236pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
237    int *ndel, int flags)
238{
239	struct pfioc_table io;
240
241	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
242		errno = EINVAL;
243		return (-1);
244	}
245	bzero(&io, sizeof io);
246	io.pfrio_flags = flags;
247	io.pfrio_table = *tbl;
248	io.pfrio_buffer = addr;
249	io.pfrio_esize = sizeof(*addr);
250	io.pfrio_size = size;
251	if (ioctl(dev, DIOCRDELADDRS, &io)) {
252		pfr_report_error(tbl, &io, "delete addresses in");
253		return (-1);
254	}
255	if (ndel != NULL)
256		*ndel = io.pfrio_ndel;
257	return (0);
258}
259
260int
261pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
262    int *size2, int *nadd, int *ndel, int *nchange, int flags)
263{
264	struct pfioc_table io;
265
266	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
267		errno = EINVAL;
268		return (-1);
269	}
270	bzero(&io, sizeof io);
271	io.pfrio_flags = flags;
272	io.pfrio_table = *tbl;
273	io.pfrio_buffer = addr;
274	io.pfrio_esize = sizeof(*addr);
275	io.pfrio_size = size;
276	io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
277	if (ioctl(dev, DIOCRSETADDRS, &io)) {
278		pfr_report_error(tbl, &io, "set addresses in");
279		return (-1);
280	}
281	if (nadd != NULL)
282		*nadd = io.pfrio_nadd;
283	if (ndel != NULL)
284		*ndel = io.pfrio_ndel;
285	if (nchange != NULL)
286		*nchange = io.pfrio_nchange;
287	if (size2 != NULL)
288		*size2 = io.pfrio_size2;
289	return (0);
290}
291
292int
293pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
294    int flags)
295{
296	struct pfioc_table io;
297
298	if (tbl == NULL || size == NULL || *size < 0 ||
299	    (*size && addr == NULL)) {
300		errno = EINVAL;
301		return (-1);
302	}
303	bzero(&io, sizeof io);
304	io.pfrio_flags = flags;
305	io.pfrio_table = *tbl;
306	io.pfrio_buffer = addr;
307	io.pfrio_esize = sizeof(*addr);
308	io.pfrio_size = *size;
309	if (ioctl(dev, DIOCRGETADDRS, &io)) {
310		pfr_report_error(tbl, &io, "get addresses from");
311		return (-1);
312	}
313	*size = io.pfrio_size;
314	return (0);
315}
316
317int
318pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
319    int flags)
320{
321	struct pfioc_table io;
322
323	if (tbl == NULL || size == NULL || *size < 0 ||
324	    (*size && addr == NULL)) {
325		errno = EINVAL;
326		return (-1);
327	}
328	bzero(&io, sizeof io);
329	io.pfrio_flags = flags;
330	io.pfrio_table = *tbl;
331	io.pfrio_buffer = addr;
332	io.pfrio_esize = sizeof(*addr);
333	io.pfrio_size = *size;
334	if (ioctl(dev, DIOCRGETASTATS, &io)) {
335		pfr_report_error(tbl, &io, "get astats from");
336		return (-1);
337	}
338	*size = io.pfrio_size;
339	return (0);
340}
341
342int
343pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
344{
345	struct pfioc_table io;
346
347	if (size < 0 || (size && !tbl)) {
348		errno = EINVAL;
349		return (-1);
350	}
351	bzero(&io, sizeof io);
352	io.pfrio_flags = flags;
353	io.pfrio_buffer = tbl;
354	io.pfrio_esize = sizeof(*tbl);
355	io.pfrio_size = size;
356	if (ioctl(dev, DIOCRCLRTSTATS, &io)) {
357		pfr_report_error(tbl, &io, "clear tstats from");
358		return (-1);
359	}
360	if (nzero)
361		*nzero = io.pfrio_nzero;
362	return (0);
363}
364
365int
366pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
367    int *nmatch, int flags)
368{
369	struct pfioc_table io;
370
371	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
372		errno = EINVAL;
373		return (-1);
374	}
375	bzero(&io, sizeof io);
376	io.pfrio_flags = flags;
377	io.pfrio_table = *tbl;
378	io.pfrio_buffer = addr;
379	io.pfrio_esize = sizeof(*addr);
380	io.pfrio_size = size;
381	if (ioctl(dev, DIOCRTSTADDRS, &io)) {
382		pfr_report_error(tbl, &io, "test addresses in");
383		return (-1);
384	}
385	if (nmatch)
386		*nmatch = io.pfrio_nmatch;
387	return (0);
388}
389
390int
391pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
392    int *nadd, int *naddr, int ticket, int flags)
393{
394	struct pfioc_table io;
395
396	if (tbl == NULL || size < 0 || (size && addr == NULL)) {
397		errno = EINVAL;
398		return (-1);
399	}
400	bzero(&io, sizeof io);
401	io.pfrio_flags = flags;
402	io.pfrio_table = *tbl;
403	io.pfrio_buffer = addr;
404	io.pfrio_esize = sizeof(*addr);
405	io.pfrio_size = size;
406	io.pfrio_ticket = ticket;
407	if (ioctl(dev, DIOCRINADEFINE, &io)) {
408		pfr_report_error(tbl, &io, "define inactive set table");
409		return (-1);
410	}
411	if (nadd != NULL)
412		*nadd = io.pfrio_nadd;
413	if (naddr != NULL)
414		*naddr = io.pfrio_naddr;
415	return (0);
416}
417
418/* interface management code */
419
420int
421pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
422{
423	struct pfioc_iface io;
424
425	if (size == NULL || *size < 0 || (*size && buf == NULL)) {
426		errno = EINVAL;
427		return (-1);
428	}
429	bzero(&io, sizeof io);
430	if (filter != NULL)
431		if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
432		    sizeof(io.pfiio_name)) {
433			errno = EINVAL;
434			return (-1);
435		}
436	io.pfiio_buffer = buf;
437	io.pfiio_esize = sizeof(*buf);
438	io.pfiio_size = *size;
439	if (ioctl(dev, DIOCIGETIFACES, &io))
440		return (-1);
441	*size = io.pfiio_size;
442	return (0);
443}
444
445/* buffer management code */
446
447const size_t buf_esize[PFRB_MAX] = { 0,
448	sizeof(struct pfr_table), sizeof(struct pfr_tstats),
449	sizeof(struct pfr_addr), sizeof(struct pfr_astats),
450	sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
451};
452
453/*
454 * add one element to the buffer
455 */
456int
457pfr_buf_add(struct pfr_buffer *b, const void *e)
458{
459	size_t bs;
460
461	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
462	    e == NULL) {
463		errno = EINVAL;
464		return (-1);
465	}
466	bs = buf_esize[b->pfrb_type];
467	if (b->pfrb_size == b->pfrb_msize)
468		if (pfr_buf_grow(b, 0))
469			return (-1);
470	memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
471	b->pfrb_size++;
472	return (0);
473}
474
475/*
476 * return next element of the buffer (or first one if prev is NULL)
477 * see PFRB_FOREACH macro
478 */
479void *
480pfr_buf_next(struct pfr_buffer *b, const void *prev)
481{
482	size_t bs;
483
484	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
485		return (NULL);
486	if (b->pfrb_size == 0)
487		return (NULL);
488	if (prev == NULL)
489		return (b->pfrb_caddr);
490	bs = buf_esize[b->pfrb_type];
491	if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
492		return (NULL);
493	return (((caddr_t)prev) + bs);
494}
495
496/*
497 * minsize:
498 *    0: make the buffer somewhat bigger
499 *    n: make room for "n" entries in the buffer
500 */
501int
502pfr_buf_grow(struct pfr_buffer *b, int minsize)
503{
504	caddr_t p;
505	size_t bs;
506
507	if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
508		errno = EINVAL;
509		return (-1);
510	}
511	if (minsize != 0 && minsize <= b->pfrb_msize)
512		return (0);
513	bs = buf_esize[b->pfrb_type];
514	if (!b->pfrb_msize) {
515		if (minsize < 64)
516			minsize = 64;
517		b->pfrb_caddr = calloc(bs, minsize);
518		if (b->pfrb_caddr == NULL)
519			return (-1);
520		b->pfrb_msize = minsize;
521	} else {
522		if (minsize == 0)
523			minsize = b->pfrb_msize * 2;
524		if (minsize < 0 || minsize >= SIZE_T_MAX / bs) {
525			/* msize overflow */
526			errno = ENOMEM;
527			return (-1);
528		}
529		p = realloc(b->pfrb_caddr, minsize * bs);
530		if (p == NULL)
531			return (-1);
532		bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
533		b->pfrb_caddr = p;
534		b->pfrb_msize = minsize;
535	}
536	return (0);
537}
538
539/*
540 * reset buffer and free memory.
541 */
542void
543pfr_buf_clear(struct pfr_buffer *b)
544{
545	if (b == NULL)
546		return;
547	if (b->pfrb_caddr != NULL)
548		free(b->pfrb_caddr);
549	b->pfrb_caddr = NULL;
550	b->pfrb_size = b->pfrb_msize = 0;
551}
552
553int
554pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork,
555    int (*append_addr)(struct pfr_buffer *, char *, int))
556{
557	FILE	*fp;
558	char	 buf[BUF_SIZE];
559	int	 rv;
560
561	if (file == NULL)
562		return (0);
563	if (!strcmp(file, "-"))
564		fp = stdin;
565	else {
566		fp = pfctl_fopen(file, "r");
567		if (fp == NULL)
568			return (-1);
569	}
570	while ((rv = pfr_next_token(buf, fp)) == 1)
571		if (append_addr(b, buf, nonetwork)) {
572			rv = -1;
573			break;
574		}
575	if (fp != stdin)
576		fclose(fp);
577	return (rv);
578}
579
580int
581pfr_next_token(char buf[BUF_SIZE], FILE *fp)
582{
583	static char	next_ch = ' ';
584	int		i = 0;
585
586	for (;;) {
587		/* skip spaces */
588		while (isspace(next_ch) && !feof(fp))
589			next_ch = fgetc(fp);
590		/* remove from '#' until end of line */
591		if (next_ch == '#')
592			while (!feof(fp)) {
593				next_ch = fgetc(fp);
594				if (next_ch == '\n')
595					break;
596			}
597		else
598			break;
599	}
600	if (feof(fp)) {
601		next_ch = ' ';
602		return (0);
603	}
604	do {
605		if (i < BUF_SIZE)
606			buf[i++] = next_ch;
607		next_ch = fgetc(fp);
608	} while (!feof(fp) && !isspace(next_ch));
609	if (i >= BUF_SIZE) {
610		errno = EINVAL;
611		return (-1);
612	}
613	buf[i] = '\0';
614	return (1);
615}
616
617char *
618pfr_strerror(int errnum)
619{
620	switch (errnum) {
621	case ESRCH:
622		return "Table does not exist";
623	case ENOENT:
624		return "Anchor or Ruleset does not exist";
625	default:
626		return strerror(errnum);
627	}
628}
629