xref: /openbsd/usr.sbin/bgpd/pftable.c (revision 5af055cd)
1 /*	$OpenBSD: pftable.c,v 1.9 2015/12/23 20:42:20 mmcc Exp $ */
2 
3 /*
4  * Copyright (c) 2004 Damien Miller <djm@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 
23 #include <netinet/in.h>
24 #include <net/if.h>
25 #include <net/pfvar.h>
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 
32 /* Namespace collision: these are defined in both bgpd.h and pfvar.h */
33 #undef v4
34 #undef v6
35 #undef addr8
36 #undef addr16
37 #undef addr32
38 
39 #include "bgpd.h"
40 
41 static int devpf = -1;
42 
43 struct pf_table {
44 	LIST_ENTRY(pf_table)	entry;
45 	char			name[PFTABLE_LEN];
46 	unsigned long		what;
47 	struct pfr_addr		*worklist;
48 	int			naddrs;
49 	int			nalloc;
50 };
51 
52 /* List of tables under management */
53 LIST_HEAD(, pf_table) tables = LIST_HEAD_INITIALIZER(tables);
54 
55 static int
56 pftable_change(struct pf_table *pft)
57 {
58 	struct pfioc_table tio;
59 	int ret;
60 
61 	if (pft->naddrs == 0 || pft->what == 0)
62 		return (0);
63 
64 	if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR)) == -1))
65 		fatal("open(/dev/pf)");
66 
67 	bzero(&tio, sizeof(tio));
68 	strlcpy(tio.pfrio_table.pfrt_name, pft->name,
69 	    sizeof(tio.pfrio_table.pfrt_name));
70 	tio.pfrio_buffer = pft->worklist;
71 	tio.pfrio_esize = sizeof(*pft->worklist);
72 	tio.pfrio_size = pft->naddrs;
73 
74 	ret = ioctl(devpf, pft->what, &tio);
75 
76 	/* bad prefixes shouldn't cause us to die */
77 	if (ret == -1) {
78 		if (errno == EINVAL)
79 			return (0);
80 		log_warn("pftable_change ioctl");
81 	}
82 
83 	return (ret);
84 }
85 
86 static int
87 pftable_clear(const char *name)
88 {
89 	struct pfioc_table tio;
90 
91 	if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR)) == -1))
92 		fatal("open(/dev/pf)");
93 
94 	bzero(&tio, sizeof(tio));
95 	strlcpy(tio.pfrio_table.pfrt_name, name,
96 	    sizeof(tio.pfrio_table.pfrt_name));
97 
98 	if (ioctl(devpf, DIOCRCLRADDRS, &tio) != 0) {
99 		log_warn("pftable_clear ioctl");
100 		return (-1);
101 	}
102 
103 	return (0);
104 }
105 
106 int
107 pftable_exists(const char *name)
108 {
109 	struct pfioc_table tio;
110 	struct pfr_astats dummy;
111 
112 	if (devpf == -1 && ((devpf = open("/dev/pf", O_RDWR)) == -1))
113 		fatal("open(/dev/pf)");
114 
115 	bzero(&tio, sizeof(tio));
116 	strlcpy(tio.pfrio_table.pfrt_name, name,
117 	    sizeof(tio.pfrio_table.pfrt_name));
118 	tio.pfrio_buffer = &dummy;
119 	tio.pfrio_esize = sizeof(dummy);
120 	tio.pfrio_size = 1;
121 
122 	if (ioctl(devpf, DIOCRGETASTATS, &tio) != 0)
123 		return (-1);
124 
125 	return (0);
126 }
127 
128 int
129 pftable_add(const char *name)
130 {
131 	struct pf_table *pft;
132 
133 	/* Ignore duplicates */
134 	LIST_FOREACH(pft, &tables, entry)
135 		if (strcmp(pft->name, name) == 0)
136 			return (0);
137 
138 	if ((pft = malloc(sizeof(*pft))) == NULL) {
139 		log_warn("pftable malloc");
140 		return (-1);
141 	}
142 
143 	bzero(pft, sizeof(*pft));
144 	if (strlcpy(pft->name, name, sizeof(pft->name)) >= sizeof(pft->name)) {
145 		log_warn("pf_table name too long");
146 		free(pft);
147 		return (-1);
148 	}
149 
150 	LIST_INSERT_HEAD(&tables, pft, entry);
151 
152 	return (0);
153 }
154 
155 int
156 pftable_clear_all(void)
157 {
158 	struct pf_table *pft;
159 
160 	LIST_FOREACH(pft, &tables, entry) {
161 		if (pftable_clear(pft->name) != 0)
162 			return (-1);
163 		free(pft->worklist);
164 		pft->worklist = NULL;
165 		pft->nalloc = pft->naddrs = 0;
166 		pft->what = 0;
167 	}
168 
169 	return (0);
170 }
171 
172 static int
173 pftable_add_work(const char *table, struct bgpd_addr *addr,
174     u_int8_t len, int del)
175 {
176 	struct pf_table *pft;
177 	struct pfr_addr *pfa, *tmp;
178 	unsigned long what;
179 
180 	if (*table == '\0' || len > 128)
181 		fatal("pftable_add_work: insane");
182 
183 	/* Find table */
184 	LIST_FOREACH(pft, &tables, entry)
185 		if (strcmp(pft->name, table) == 0)
186 			break;
187 
188 	if (pft == NULL) {
189 		log_warn("pf table %s not found", table);
190 		return (-1);
191 	}
192 
193 	/* Only one type of work on the list at a time */
194 	what = del ? DIOCRDELADDRS : DIOCRADDADDRS;
195 	if (pft->naddrs != 0 && pft->what != what)
196 		fatal("attempt to mix pf table additions/deletions");
197 
198 	if (pft->nalloc <= pft->naddrs)
199 		pft->nalloc = pft->nalloc == 0 ? 1 : pft->nalloc * 2;
200 	tmp = reallocarray(pft->worklist, pft->nalloc, sizeof(*tmp));
201 	if (tmp == NULL) {
202 		if (pft->worklist != NULL) {
203 			log_warn("pftable_add_work: malloc");
204 			free(pft->worklist);
205 			pft->worklist = NULL;
206 		}
207 		pft->nalloc = pft->naddrs = 0;
208 		pft->what = 0;
209 		return (-1);
210 	}
211 	pft->worklist = tmp;
212 	pfa = &pft->worklist[pft->naddrs];
213 
214 	bzero(pfa, sizeof(*pfa));
215 	memcpy(&pfa->pfra_u, &addr->ba, (len + 7U) / 8);
216 	pfa->pfra_af = aid2af(addr->aid);
217 	pfa->pfra_net = len;
218 
219 	pft->naddrs++;
220 	pft->what = what;
221 
222 	/* Don't let the list grow too large */
223 	if (pft->naddrs >= 1024)
224 		pftable_commit();
225 
226 	return (0);
227 }
228 
229 /* imsg handlers */
230 int
231 pftable_addr_add(struct pftable_msg *m)
232 {
233 	return (pftable_add_work(m->pftable, &m->addr, m->len, 0));
234 }
235 
236 int
237 pftable_addr_remove(struct pftable_msg *m)
238 {
239 	return (pftable_add_work(m->pftable, &m->addr, m->len, 1));
240 }
241 
242 int
243 pftable_commit(void)
244 {
245 	struct pf_table *pft;
246 	int ret = 0;
247 
248 	LIST_FOREACH(pft, &tables, entry) {
249 		if (pft->what != 0 && pftable_change(pft) != 0)
250 			ret = -1;
251 		free(pft->worklist);
252 		pft->worklist = NULL;
253 		pft->nalloc = pft->naddrs = 0;
254 		pft->what = 0;
255 	}
256 
257 	return (ret);
258 }
259 
260