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