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