FreeBSD/Linux Kernel Cross Reference
sys/net/pf_ruleset.c
1 /* $OpenBSD: pf_ruleset.c,v 1.19 2022/07/20 09:33:11 mbuhl Exp $ */
2
3 /*
4 * Copyright (c) 2001 Daniel Hartmeier
5 * Copyright (c) 2002,2003 Henning Brauer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * Effort sponsored in part by the Defense Advanced Research Projects
33 * Agency (DARPA) and Air Force Research Laboratory, Air Force
34 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
35 *
36 */
37
38 #include <sys/param.h>
39 #include <sys/socket.h>
40 #ifdef _KERNEL
41 #include <sys/systm.h>
42 #include <sys/mbuf.h>
43 #include <sys/pool.h>
44 #endif /* _KERNEL */
45 #include <sys/syslog.h>
46
47 #include <netinet/in.h>
48 #include <netinet/ip.h>
49 #include <netinet/tcp.h>
50
51 #include <net/if.h>
52 #include <net/pfvar.h>
53
54 #ifdef INET6
55 #include <netinet/ip6.h>
56 #endif /* INET6 */
57
58
59 #ifdef _KERNEL
60 #define rs_malloc(x) malloc(x, M_TEMP, M_WAITOK|M_CANFAIL|M_ZERO)
61 #define rs_free(x, siz) free(x, M_TEMP, siz)
62 #define rs_pool_get_anchor() pool_get(&pf_anchor_pl, \
63 PR_WAITOK|PR_LIMITFAIL|PR_ZERO)
64 #define rs_pool_put_anchor(x) pool_put(&pf_anchor_pl, x)
65
66 struct pool pf_anchor_pl;
67
68 #else /* !_KERNEL */
69 /* Userland equivalents so we can lend code to pfctl et al. */
70
71 #include <arpa/inet.h>
72 #include <errno.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #define rs_malloc(x) calloc(1, x)
77 #define rs_free(x, siz) freezero(x, siz)
78 #define rs_pool_get_anchor() calloc(1, sizeof(struct pf_anchor))
79 #define rs_pool_put_anchor(x) freezero(x, sizeof(struct pf_anchor))
80
81 #ifdef PFDEBUG
82 #include <sys/stdarg.h> /* for DPFPRINTF() */
83 #endif /* PFDEBUG */
84 #endif /* _KERNEL */
85
86
87 struct pf_anchor_global pf_anchors;
88 struct pf_anchor pf_main_anchor;
89
90 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
91
92 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
93 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
94
95 static __inline int
96 pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
97 {
98 int c = strcmp(a->path, b->path);
99
100 return (c ? (c < 0 ? -1 : 1) : 0);
101 }
102
103 void
104 pf_init_ruleset(struct pf_ruleset *ruleset)
105 {
106 memset(ruleset, 0, sizeof(struct pf_ruleset));
107 TAILQ_INIT(&ruleset->rules.queues[0]);
108 TAILQ_INIT(&ruleset->rules.queues[1]);
109 ruleset->rules.active.ptr = &ruleset->rules.queues[0];
110 ruleset->rules.inactive.ptr = &ruleset->rules.queues[1];
111 }
112
113 struct pf_anchor *
114 pf_find_anchor(const char *path)
115 {
116 struct pf_anchor *key, *found;
117
118 key = rs_malloc(sizeof(*key));
119 if (key == NULL)
120 return (NULL);
121 strlcpy(key->path, path, sizeof(key->path));
122 found = RB_FIND(pf_anchor_global, &pf_anchors, key);
123 rs_free(key, sizeof(*key));
124 return (found);
125 }
126
127 struct pf_ruleset *
128 pf_find_ruleset(const char *path)
129 {
130 struct pf_anchor *anchor;
131
132 while (*path == '/')
133 path++;
134 if (!*path)
135 return (&pf_main_ruleset);
136 anchor = pf_find_anchor(path);
137 if (anchor == NULL)
138 return (NULL);
139 else
140 return (&anchor->ruleset);
141 }
142
143 struct pf_ruleset *
144 pf_get_leaf_ruleset(char *path, char **path_remainder)
145 {
146 struct pf_ruleset *ruleset;
147 char *leaf, *p;
148 int i = 0;
149
150 p = path;
151 while (*p == '/')
152 p++;
153
154 ruleset = pf_find_ruleset(p);
155 leaf = p;
156 while (ruleset == NULL) {
157 leaf = strrchr(p, '/');
158 if (leaf != NULL) {
159 *leaf = '\0';
160 i++;
161 ruleset = pf_find_ruleset(p);
162 } else {
163 leaf = path;
164 /*
165 * if no path component exists, then main ruleset is
166 * our parent.
167 */
168 ruleset = &pf_main_ruleset;
169 }
170 }
171
172 if (path_remainder != NULL)
173 *path_remainder = leaf;
174
175 /* restore slashes in path. */
176 while (i != 0) {
177 while (*leaf != '\0')
178 leaf++;
179 *leaf = '/';
180 i--;
181 }
182
183 return (ruleset);
184 }
185
186 struct pf_anchor *
187 pf_create_anchor(struct pf_anchor *parent, const char *aname)
188 {
189 struct pf_anchor *anchor, *dup;
190
191 if (!*aname || (strlen(aname) >= PF_ANCHOR_NAME_SIZE) ||
192 ((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH)))
193 return (NULL);
194
195 anchor = rs_pool_get_anchor();
196 if (anchor == NULL)
197 return (NULL);
198
199 RB_INIT(&anchor->children);
200 strlcpy(anchor->name, aname, sizeof(anchor->name));
201 if (parent != NULL) {
202 /*
203 * Make sure path for levels 2, 3, ... is terminated by '/':
204 * 1/2/3/...
205 */
206 strlcpy(anchor->path, parent->path, sizeof(anchor->path));
207 strlcat(anchor->path, "/", sizeof(anchor->path));
208 }
209 strlcat(anchor->path, anchor->name, sizeof(anchor->path));
210
211 if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != NULL) {
212 DPFPRINTF(LOG_NOTICE,
213 "%s: RB_INSERT to global '%s' '%s' collides with '%s' '%s'",
214 __func__, anchor->path, anchor->name, dup->path, dup->name);
215 rs_pool_put_anchor(anchor);
216 return (NULL);
217 }
218
219 if (parent != NULL) {
220 anchor->parent = parent;
221 dup = RB_INSERT(pf_anchor_node, &parent->children, anchor);
222 if (dup != NULL) {
223 DPFPRINTF(LOG_NOTICE,
224 "%s: RB_INSERT to parent '%s' '%s' collides with "
225 "'%s' '%s'", __func__, anchor->path, anchor->name,
226 dup->path, dup->name);
227 RB_REMOVE(pf_anchor_global, &pf_anchors,
228 anchor);
229 rs_pool_put_anchor(anchor);
230 return (NULL);
231 }
232 }
233
234 pf_init_ruleset(&anchor->ruleset);
235 anchor->ruleset.anchor = anchor;
236
237 return (anchor);
238 }
239
240 struct pf_ruleset *
241 pf_find_or_create_ruleset(const char *path)
242 {
243 char *p, *aname, *r;
244 struct pf_ruleset *ruleset;
245 struct pf_anchor *anchor;
246
247 if (path[0] == 0)
248 return (&pf_main_ruleset);
249
250 while (*path == '/')
251 path++;
252
253 ruleset = pf_find_ruleset(path);
254 if (ruleset != NULL)
255 return (ruleset);
256
257 p = rs_malloc(MAXPATHLEN);
258 if (p == NULL)
259 return (NULL);
260 strlcpy(p, path, MAXPATHLEN);
261
262 ruleset = pf_get_leaf_ruleset(p, &aname);
263 anchor = ruleset->anchor;
264
265 while (*aname == '/')
266 aname++;
267 /*
268 * aname is a path remainder, which contains nodes we must create. We
269 * process the aname path from left to right, effectively descending
270 * from parents to children.
271 */
272 while ((r = strchr(aname, '/')) != NULL || *aname) {
273 if (r != NULL)
274 *r = 0;
275
276 anchor = pf_create_anchor(anchor, aname);
277 if (anchor == NULL) {
278 rs_free(p, MAXPATHLEN);
279 return (NULL);
280 }
281
282 if (r == NULL)
283 break;
284 else
285 aname = r + 1;
286 }
287
288 rs_free(p, MAXPATHLEN);
289 return (&anchor->ruleset);
290 }
291
292 void
293 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
294 {
295 struct pf_anchor *parent;
296
297 while (ruleset != NULL) {
298 if (ruleset == &pf_main_ruleset ||
299 !RB_EMPTY(&ruleset->anchor->children) ||
300 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
301 ruleset->topen)
302 return;
303 if (!TAILQ_EMPTY(ruleset->rules.active.ptr) ||
304 !TAILQ_EMPTY(ruleset->rules.inactive.ptr) ||
305 ruleset->rules.inactive.open)
306 return;
307 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
308 if ((parent = ruleset->anchor->parent) != NULL)
309 RB_REMOVE(pf_anchor_node, &parent->children,
310 ruleset->anchor);
311 rs_pool_put_anchor(ruleset->anchor);
312 if (parent == NULL)
313 return;
314 ruleset = &parent->ruleset;
315 }
316 }
317
318 int
319 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
320 const char *name)
321 {
322 char *p, *path;
323 struct pf_ruleset *ruleset;
324
325 r->anchor = NULL;
326 r->anchor_relative = 0;
327 r->anchor_wildcard = 0;
328 if (!name[0])
329 return (0);
330 path = rs_malloc(MAXPATHLEN);
331 if (path == NULL)
332 return (1);
333 if (name[0] == '/')
334 strlcpy(path, name + 1, MAXPATHLEN);
335 else {
336 /* relative path */
337 r->anchor_relative = 1;
338 if (s->anchor == NULL || !s->anchor->path[0])
339 path[0] = 0;
340 else
341 strlcpy(path, s->anchor->path, MAXPATHLEN);
342 while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
343 if (!path[0]) {
344 DPFPRINTF(LOG_NOTICE,
345 "pf_anchor_setup: .. beyond root");
346 rs_free(path, MAXPATHLEN);
347 return (1);
348 }
349 if ((p = strrchr(path, '/')) != NULL)
350 *p = 0;
351 else
352 path[0] = 0;
353 r->anchor_relative++;
354 name += 3;
355 }
356 if (path[0])
357 strlcat(path, "/", MAXPATHLEN);
358 strlcat(path, name, MAXPATHLEN);
359 }
360 if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
361 r->anchor_wildcard = 1;
362 *p = 0;
363 }
364 ruleset = pf_find_or_create_ruleset(path);
365 rs_free(path, MAXPATHLEN);
366 if (ruleset == NULL || ruleset == &pf_main_ruleset) {
367 DPFPRINTF(LOG_NOTICE,
368 "pf_anchor_setup: ruleset");
369 return (1);
370 }
371 r->anchor = ruleset->anchor;
372 r->anchor->refcnt++;
373 return (0);
374 }
375
376 int
377 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
378 struct pfioc_rule *pr)
379 {
380 pr->anchor_call[0] = 0;
381 if (r->anchor == NULL)
382 return (0);
383 if (!r->anchor_relative) {
384 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
385 strlcat(pr->anchor_call, r->anchor->path,
386 sizeof(pr->anchor_call));
387 } else {
388 char *a, *p;
389 int i;
390
391 a = rs_malloc(MAXPATHLEN);
392 if (a == NULL)
393 return (1);
394 if (rs == &pf_main_ruleset)
395 a[0] = 0;
396 else
397 strlcpy(a, rs->anchor->path, MAXPATHLEN);
398 for (i = 1; i < r->anchor_relative; ++i) {
399 if ((p = strrchr(a, '/')) == NULL)
400 p = a;
401 *p = 0;
402 strlcat(pr->anchor_call, "../",
403 sizeof(pr->anchor_call));
404 }
405 if (strncmp(a, r->anchor->path, strlen(a))) {
406 DPFPRINTF(LOG_NOTICE,
407 "pf_anchor_copyout: '%s' '%s'", a,
408 r->anchor->path);
409 rs_free(a, MAXPATHLEN);
410 return (1);
411 }
412 if (strlen(r->anchor->path) > strlen(a))
413 strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
414 strlen(a) + 1 : 0), sizeof(pr->anchor_call));
415 rs_free(a, MAXPATHLEN);
416 }
417 if (r->anchor_wildcard)
418 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
419 sizeof(pr->anchor_call));
420 return (0);
421 }
422
423 void
424 pf_remove_anchor(struct pf_rule *r)
425 {
426 if (r->anchor == NULL)
427 return;
428 if (r->anchor->refcnt <= 0)
429 DPFPRINTF(LOG_NOTICE, "pf_remove_anchor: broken refcount");
430 else if (!--r->anchor->refcnt)
431 pf_remove_if_empty_ruleset(&r->anchor->ruleset);
432 r->anchor = NULL;
433 }
Cache object: f4844168d87ee5f14f66a6f34d02b176
|