xref: /openbsd-src/usr.sbin/bgpd/pftable.c (revision 660f4b3c4ebfb5ad23635ac212012bb9d9260b3e)
1*660f4b3cSclaudio /*	$OpenBSD: pftable.c,v 1.18 2024/02/13 16:35:43 claudio Exp $ */
2972b7d94Sdjm 
3972b7d94Sdjm /*
4972b7d94Sdjm  * Copyright (c) 2004 Damien Miller <djm@openbsd.org>
5972b7d94Sdjm  *
6972b7d94Sdjm  * Permission to use, copy, modify, and distribute this software for any
7972b7d94Sdjm  * purpose with or without fee is hereby granted, provided that the above
8972b7d94Sdjm  * copyright notice and this permission notice appear in all copies.
9972b7d94Sdjm  *
10972b7d94Sdjm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11972b7d94Sdjm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12972b7d94Sdjm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13972b7d94Sdjm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14972b7d94Sdjm  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15972b7d94Sdjm  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16972b7d94Sdjm  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17972b7d94Sdjm  */
18972b7d94Sdjm 
19972b7d94Sdjm #include <sys/types.h>
20972b7d94Sdjm #include <sys/ioctl.h>
21972b7d94Sdjm #include <sys/socket.h>
22972b7d94Sdjm 
2368928c43Sderaadt #include <netinet/in.h>
24972b7d94Sdjm #include <net/if.h>
25972b7d94Sdjm #include <net/pfvar.h>
26972b7d94Sdjm 
27972b7d94Sdjm #include <stdlib.h>
28972b7d94Sdjm #include <string.h>
29972b7d94Sdjm #include <errno.h>
30972b7d94Sdjm #include <fcntl.h>
31972b7d94Sdjm 
325e3f6f95Sbenno #include "log.h"
335e3f6f95Sbenno 
34*660f4b3cSclaudio /* Namespace collision: these are defined in pfvar.h and bgpd.h */
35972b7d94Sdjm #undef v4
36972b7d94Sdjm #undef v6
37972b7d94Sdjm 
38972b7d94Sdjm #include "bgpd.h"
39972b7d94Sdjm 
40972b7d94Sdjm static int devpf = -1;
41972b7d94Sdjm 
42972b7d94Sdjm struct pf_table {
43972b7d94Sdjm 	LIST_ENTRY(pf_table)	entry;
44972b7d94Sdjm 	char			name[PFTABLE_LEN];
45972b7d94Sdjm 	unsigned long		what;
46972b7d94Sdjm 	struct pfr_addr		*worklist;
47972b7d94Sdjm 	int			naddrs;
48972b7d94Sdjm 	int			nalloc;
49972b7d94Sdjm };
50972b7d94Sdjm 
51972b7d94Sdjm /* List of tables under management */
52972b7d94Sdjm LIST_HEAD(, pf_table) tables = LIST_HEAD_INITIALIZER(tables);
53972b7d94Sdjm 
54972b7d94Sdjm static int
pftable_change(struct pf_table * pft)55972b7d94Sdjm pftable_change(struct pf_table *pft)
56972b7d94Sdjm {
57972b7d94Sdjm 	struct pfioc_table tio;
58972b7d94Sdjm 	int ret;
59972b7d94Sdjm 
60972b7d94Sdjm 	if (pft->naddrs == 0 || pft->what == 0)
61972b7d94Sdjm 		return (0);
62972b7d94Sdjm 
633ac77e71Sclaudio 	if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR|O_CLOEXEC)) == -1))
64972b7d94Sdjm 		fatal("open(/dev/pf)");
65972b7d94Sdjm 
66eafe309eSclaudio 	memset(&tio, 0, sizeof(tio));
67972b7d94Sdjm 	strlcpy(tio.pfrio_table.pfrt_name, pft->name,
68972b7d94Sdjm 	    sizeof(tio.pfrio_table.pfrt_name));
69972b7d94Sdjm 	tio.pfrio_buffer = pft->worklist;
70972b7d94Sdjm 	tio.pfrio_esize = sizeof(*pft->worklist);
71972b7d94Sdjm 	tio.pfrio_size = pft->naddrs;
72972b7d94Sdjm 
73972b7d94Sdjm 	ret = ioctl(devpf, pft->what, &tio);
74972b7d94Sdjm 
75972b7d94Sdjm 	/* bad prefixes shouldn't cause us to die */
76972b7d94Sdjm 	if (ret == -1) {
77972b7d94Sdjm 		if (errno == EINVAL)
78972b7d94Sdjm 			return (0);
79e2691ddeSclaudio 		log_warn("pftable_change ioctl");
80972b7d94Sdjm 	}
81972b7d94Sdjm 
82972b7d94Sdjm 	return (ret);
83972b7d94Sdjm }
84972b7d94Sdjm 
85972b7d94Sdjm static int
pftable_clear(const char * name)86972b7d94Sdjm pftable_clear(const char *name)
87972b7d94Sdjm {
88972b7d94Sdjm 	struct pfioc_table tio;
89972b7d94Sdjm 
903ac77e71Sclaudio 	if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR|O_CLOEXEC)) == -1))
91972b7d94Sdjm 		fatal("open(/dev/pf)");
92972b7d94Sdjm 
93eafe309eSclaudio 	memset(&tio, 0, sizeof(tio));
94972b7d94Sdjm 	strlcpy(tio.pfrio_table.pfrt_name, name,
95972b7d94Sdjm 	    sizeof(tio.pfrio_table.pfrt_name));
96972b7d94Sdjm 
97df69c215Sderaadt 	if (ioctl(devpf, DIOCRCLRADDRS, &tio) == -1) {
98e2691ddeSclaudio 		log_warn("pftable_clear ioctl");
99972b7d94Sdjm 		return (-1);
100972b7d94Sdjm 	}
101972b7d94Sdjm 
102972b7d94Sdjm 	return (0);
103972b7d94Sdjm }
104972b7d94Sdjm 
105972b7d94Sdjm int
pftable_exists(const char * name)106972b7d94Sdjm pftable_exists(const char *name)
107972b7d94Sdjm {
108972b7d94Sdjm 	struct pfioc_table tio;
109972b7d94Sdjm 	struct pfr_astats dummy;
110972b7d94Sdjm 
1113ac77e71Sclaudio 	if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR|O_CLOEXEC)) == -1))
112972b7d94Sdjm 		fatal("open(/dev/pf)");
113972b7d94Sdjm 
114eafe309eSclaudio 	memset(&tio, 0, sizeof(tio));
115972b7d94Sdjm 	strlcpy(tio.pfrio_table.pfrt_name, name,
116972b7d94Sdjm 	    sizeof(tio.pfrio_table.pfrt_name));
117972b7d94Sdjm 	tio.pfrio_buffer = &dummy;
118972b7d94Sdjm 	tio.pfrio_esize = sizeof(dummy);
119972b7d94Sdjm 	tio.pfrio_size = 1;
120972b7d94Sdjm 
121df69c215Sderaadt 	if (ioctl(devpf, DIOCRGETASTATS, &tio) == -1)
122972b7d94Sdjm 		return (-1);
123972b7d94Sdjm 
124972b7d94Sdjm 	return (0);
125972b7d94Sdjm }
126972b7d94Sdjm 
127972b7d94Sdjm int
pftable_add(const char * name)128972b7d94Sdjm pftable_add(const char *name)
129972b7d94Sdjm {
130972b7d94Sdjm 	struct pf_table *pft;
131972b7d94Sdjm 
132972b7d94Sdjm 	/* Ignore duplicates */
133972b7d94Sdjm 	LIST_FOREACH(pft, &tables, entry)
134972b7d94Sdjm 		if (strcmp(pft->name, name) == 0)
135972b7d94Sdjm 			return (0);
136972b7d94Sdjm 
137ce09f343Sderaadt 	if ((pft = calloc(1, sizeof(*pft))) == NULL) {
138972b7d94Sdjm 		log_warn("pftable malloc");
139972b7d94Sdjm 		return (-1);
140972b7d94Sdjm 	}
141972b7d94Sdjm 
142972b7d94Sdjm 	if (strlcpy(pft->name, name, sizeof(pft->name)) >= sizeof(pft->name)) {
143972b7d94Sdjm 		log_warn("pf_table name too long");
14455ba340aShenning 		free(pft);
145972b7d94Sdjm 		return (-1);
146972b7d94Sdjm 	}
147972b7d94Sdjm 
148972b7d94Sdjm 	LIST_INSERT_HEAD(&tables, pft, entry);
149972b7d94Sdjm 
150972b7d94Sdjm 	return (0);
151972b7d94Sdjm }
152972b7d94Sdjm 
153972b7d94Sdjm int
pftable_clear_all(void)154972b7d94Sdjm pftable_clear_all(void)
155972b7d94Sdjm {
156972b7d94Sdjm 	struct pf_table *pft;
157972b7d94Sdjm 
158972b7d94Sdjm 	LIST_FOREACH(pft, &tables, entry) {
159972b7d94Sdjm 		if (pftable_clear(pft->name) != 0)
160972b7d94Sdjm 			return (-1);
161972b7d94Sdjm 		free(pft->worklist);
162972b7d94Sdjm 		pft->worklist = NULL;
163972b7d94Sdjm 		pft->nalloc = pft->naddrs = 0;
164972b7d94Sdjm 		pft->what = 0;
165972b7d94Sdjm 	}
166972b7d94Sdjm 
167972b7d94Sdjm 	return (0);
168972b7d94Sdjm }
169972b7d94Sdjm 
170972b7d94Sdjm static int
pftable_add_work(const char * table,struct bgpd_addr * addr,uint8_t len,int del)171972b7d94Sdjm pftable_add_work(const char *table, struct bgpd_addr *addr,
17239386878Sclaudio     uint8_t len, int del)
173972b7d94Sdjm {
174972b7d94Sdjm 	struct pf_table *pft;
175972b7d94Sdjm 	struct pfr_addr *pfa, *tmp;
176972b7d94Sdjm 	unsigned long what;
177972b7d94Sdjm 
178972b7d94Sdjm 	if (*table == '\0' || len > 128)
179972b7d94Sdjm 		fatal("pftable_add_work: insane");
180972b7d94Sdjm 
181972b7d94Sdjm 	/* Find table */
182972b7d94Sdjm 	LIST_FOREACH(pft, &tables, entry)
183972b7d94Sdjm 		if (strcmp(pft->name, table) == 0)
184972b7d94Sdjm 			break;
185972b7d94Sdjm 
186972b7d94Sdjm 	if (pft == NULL) {
187972b7d94Sdjm 		log_warn("pf table %s not found", table);
188972b7d94Sdjm 		return (-1);
189972b7d94Sdjm 	}
190972b7d94Sdjm 
1911bba8e9bSclaudio 	/*
1921bba8e9bSclaudio 	 * Only one type of work on the list at a time,
1931bba8e9bSclaudio 	 * commit pending work first before adding new work
1941bba8e9bSclaudio 	 */
195972b7d94Sdjm 	what = del ? DIOCRDELADDRS : DIOCRADDADDRS;
196972b7d94Sdjm 	if (pft->naddrs != 0 && pft->what != what)
1971bba8e9bSclaudio 		pftable_commit();
198972b7d94Sdjm 
199972b7d94Sdjm 	if (pft->nalloc <= pft->naddrs)
200972b7d94Sdjm 		pft->nalloc = pft->nalloc == 0 ? 1 : pft->nalloc * 2;
201b795643eSderaadt 	tmp = reallocarray(pft->worklist, pft->nalloc, sizeof(*tmp));
202972b7d94Sdjm 	if (tmp == NULL) {
203972b7d94Sdjm 		if (pft->worklist != NULL) {
204b6a37229Shenning 			log_warn("pftable_add_work: malloc");
205972b7d94Sdjm 			free(pft->worklist);
206972b7d94Sdjm 			pft->worklist = NULL;
207972b7d94Sdjm 		}
208972b7d94Sdjm 		pft->nalloc = pft->naddrs = 0;
209972b7d94Sdjm 		pft->what = 0;
210972b7d94Sdjm 		return (-1);
211972b7d94Sdjm 	}
212972b7d94Sdjm 	pft->worklist = tmp;
213972b7d94Sdjm 	pfa = &pft->worklist[pft->naddrs];
214972b7d94Sdjm 
215eafe309eSclaudio 	memset(pfa, 0, sizeof(*pfa));
216*660f4b3cSclaudio 	memcpy(&pfa->pfra_u, &addr->v6, (len + 7U) / 8);
217d6c2e4e8Sclaudio 	pfa->pfra_af = aid2af(addr->aid);
218972b7d94Sdjm 	pfa->pfra_net = len;
219972b7d94Sdjm 
220972b7d94Sdjm 	pft->naddrs++;
221972b7d94Sdjm 	pft->what = what;
222972b7d94Sdjm 
223972b7d94Sdjm 	/* Don't let the list grow too large */
224972b7d94Sdjm 	if (pft->naddrs >= 1024)
225972b7d94Sdjm 		pftable_commit();
226972b7d94Sdjm 
227972b7d94Sdjm 	return (0);
228972b7d94Sdjm }
229972b7d94Sdjm 
230972b7d94Sdjm /* imsg handlers */
231972b7d94Sdjm int
pftable_addr_add(struct pftable_msg * m)232972b7d94Sdjm pftable_addr_add(struct pftable_msg *m)
233972b7d94Sdjm {
234972b7d94Sdjm 	return (pftable_add_work(m->pftable, &m->addr, m->len, 0));
235972b7d94Sdjm }
236972b7d94Sdjm 
237972b7d94Sdjm int
pftable_addr_remove(struct pftable_msg * m)238972b7d94Sdjm pftable_addr_remove(struct pftable_msg *m)
239972b7d94Sdjm {
240972b7d94Sdjm 	return (pftable_add_work(m->pftable, &m->addr, m->len, 1));
241972b7d94Sdjm }
242972b7d94Sdjm 
243972b7d94Sdjm int
pftable_commit(void)244972b7d94Sdjm pftable_commit(void)
245972b7d94Sdjm {
246972b7d94Sdjm 	struct pf_table *pft;
247972b7d94Sdjm 	int ret = 0;
248972b7d94Sdjm 
249972b7d94Sdjm 	LIST_FOREACH(pft, &tables, entry) {
250972b7d94Sdjm 		if (pft->what != 0 && pftable_change(pft) != 0)
251972b7d94Sdjm 			ret = -1;
252972b7d94Sdjm 		free(pft->worklist);
253972b7d94Sdjm 		pft->worklist = NULL;
254972b7d94Sdjm 		pft->nalloc = pft->naddrs = 0;
255972b7d94Sdjm 		pft->what = 0;
256972b7d94Sdjm 	}
257972b7d94Sdjm 
258972b7d94Sdjm 	return (ret);
259972b7d94Sdjm }
260