]> git.saurik.com Git - apple/xnu.git/blob - bsd/net/pf_ruleset.c
xnu-1504.3.12.tar.gz
[apple/xnu.git] / bsd / net / pf_ruleset.c
1 /*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* $apfw: pf_ruleset.c,v 1.2 2007/08/10 03:00:16 jhw Exp $ */
30 /* $OpenBSD: pf_ruleset.c,v 1.1 2006/10/27 13:56:51 mcbride Exp $ */
31
32 /*
33 * Copyright (c) 2001 Daniel Hartmeier
34 * Copyright (c) 2002,2003 Henning Brauer
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 *
41 * - Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * - Redistributions in binary form must reproduce the above
44 * copyright notice, this list of conditions and the following
45 * disclaimer in the documentation and/or other materials provided
46 * with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
51 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
52 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
53 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
54 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
55 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
56 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
58 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59 * POSSIBILITY OF SUCH DAMAGE.
60 *
61 * Effort sponsored in part by the Defense Advanced Research Projects
62 * Agency (DARPA) and Air Force Research Laboratory, Air Force
63 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
64 *
65 */
66
67 #include <sys/param.h>
68 #include <sys/socket.h>
69 #ifdef KERNEL
70 #include <sys/systm.h>
71 #include <sys/malloc.h>
72 #include <libkern/libkern.h>
73 #endif /* KERNEL */
74 #include <sys/mbuf.h>
75
76 #include <netinet/in.h>
77 #include <netinet/in_systm.h>
78 #include <netinet/ip.h>
79 #include <netinet/tcp.h>
80
81 #include <net/if.h>
82 #include <net/pfvar.h>
83
84 #if INET6
85 #include <netinet/ip6.h>
86 #endif /* INET6 */
87
88
89 #ifdef KERNEL
90 #define DPFPRINTF(format, x...) \
91 if (pf_status.debug >= PF_DEBUG_NOISY) \
92 printf(format, ##x)
93 #define rs_malloc(x) _MALLOC(x, M_TEMP, M_WAITOK)
94 #define rs_free(x) _FREE(x, M_TEMP)
95 #define strrchr _strrchr
96
97 static char *
98 _strrchr(const char *c, int ch)
99 {
100 char *p = (char *)(size_t)c, *save;
101
102 for (save = NULL; ; ++p) {
103 if (*p == ch)
104 save = (char *)p;
105 if (*p == '\0')
106 return (save);
107 }
108 /* NOTREACHED */
109 }
110
111 #else
112 /* Userland equivalents so we can lend code to pfctl et al. */
113
114 #include <arpa/inet.h>
115 #include <errno.h>
116 #include <stdio.h>
117 #include <stdlib.h>
118 #include <string.h>
119 #define rs_malloc(x) malloc(x)
120 #define rs_free(x) free(x)
121
122 #ifdef PFDEBUG
123 #include <sys/stdarg.h>
124 #define DPFPRINTF(format, x...) fprintf(stderr, format, ##x)
125 #else
126 #define DPFPRINTF(format, x...) ((void)0)
127 #endif /* PFDEBUG */
128 #endif /* KERNEL */
129
130
131 struct pf_anchor_global pf_anchors;
132 struct pf_anchor pf_main_anchor;
133
134 static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
135
136 RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
137 RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
138
139 static __inline int
140 pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
141 {
142 int c = strcmp(a->path, b->path);
143
144 return (c ? (c < 0 ? -1 : 1) : 0);
145 }
146
147 int
148 pf_get_ruleset_number(u_int8_t action)
149 {
150 switch (action) {
151 case PF_SCRUB:
152 case PF_NOSCRUB:
153 return (PF_RULESET_SCRUB);
154 break;
155 case PF_PASS:
156 case PF_DROP:
157 return (PF_RULESET_FILTER);
158 break;
159 case PF_NAT:
160 case PF_NONAT:
161 return (PF_RULESET_NAT);
162 break;
163 case PF_BINAT:
164 case PF_NOBINAT:
165 return (PF_RULESET_BINAT);
166 break;
167 case PF_RDR:
168 case PF_NORDR:
169 return (PF_RULESET_RDR);
170 break;
171 default:
172 return (PF_RULESET_MAX);
173 break;
174 }
175 }
176
177 void
178 pf_init_ruleset(struct pf_ruleset *ruleset)
179 {
180 int i;
181
182 memset(ruleset, 0, sizeof (struct pf_ruleset));
183 for (i = 0; i < PF_RULESET_MAX; i++) {
184 TAILQ_INIT(&ruleset->rules[i].queues[0]);
185 TAILQ_INIT(&ruleset->rules[i].queues[1]);
186 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
187 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
188 }
189 }
190
191 struct pf_anchor *
192 pf_find_anchor(const char *path)
193 {
194 struct pf_anchor *key, *found;
195
196 key = (struct pf_anchor *)rs_malloc(sizeof (*key));
197 memset(key, 0, sizeof (*key));
198 strlcpy(key->path, path, sizeof (key->path));
199 found = RB_FIND(pf_anchor_global, &pf_anchors, key);
200 rs_free(key);
201 return (found);
202 }
203
204 struct pf_ruleset *
205 pf_find_ruleset(const char *path)
206 {
207 struct pf_anchor *anchor;
208
209 while (*path == '/')
210 path++;
211 if (!*path)
212 return (&pf_main_ruleset);
213 anchor = pf_find_anchor(path);
214 if (anchor == NULL)
215 return (NULL);
216 else
217 return (&anchor->ruleset);
218 }
219
220 struct pf_ruleset *
221 pf_find_or_create_ruleset(const char *path)
222 {
223 char *p, *q, *r;
224 struct pf_ruleset *ruleset;
225 struct pf_anchor *anchor = 0, *dup, *parent = NULL;
226
227 if (path[0] == 0)
228 return (&pf_main_ruleset);
229 while (*path == '/')
230 path++;
231 ruleset = pf_find_ruleset(path);
232 if (ruleset != NULL)
233 return (ruleset);
234 p = (char *)rs_malloc(MAXPATHLEN);
235 bzero(p, MAXPATHLEN);
236 strlcpy(p, path, MAXPATHLEN);
237 while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
238 *q = 0;
239 if ((ruleset = pf_find_ruleset(p)) != NULL) {
240 parent = ruleset->anchor;
241 break;
242 }
243 }
244 if (q == NULL)
245 q = p;
246 else
247 q++;
248 strlcpy(p, path, MAXPATHLEN);
249 if (!*q) {
250 rs_free(p);
251 return (NULL);
252 }
253 while ((r = strchr(q, '/')) != NULL || *q) {
254 if (r != NULL)
255 *r = 0;
256 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
257 (parent != NULL && strlen(parent->path) >=
258 MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
259 rs_free(p);
260 return (NULL);
261 }
262 anchor = (struct pf_anchor *)rs_malloc(sizeof (*anchor));
263 if (anchor == NULL) {
264 rs_free(p);
265 return (NULL);
266 }
267 memset(anchor, 0, sizeof (*anchor));
268 RB_INIT(&anchor->children);
269 strlcpy(anchor->name, q, sizeof (anchor->name));
270 if (parent != NULL) {
271 strlcpy(anchor->path, parent->path,
272 sizeof (anchor->path));
273 strlcat(anchor->path, "/", sizeof (anchor->path));
274 }
275 strlcat(anchor->path, anchor->name, sizeof (anchor->path));
276 if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) !=
277 NULL) {
278 printf("pf_find_or_create_ruleset: RB_INSERT1 "
279 "'%s' '%s' collides with '%s' '%s'\n",
280 anchor->path, anchor->name, dup->path, dup->name);
281 rs_free(anchor);
282 rs_free(p);
283 return (NULL);
284 }
285 if (parent != NULL) {
286 anchor->parent = parent;
287 if ((dup = RB_INSERT(pf_anchor_node, &parent->children,
288 anchor)) != NULL) {
289 printf("pf_find_or_create_ruleset: "
290 "RB_INSERT2 '%s' '%s' collides with "
291 "'%s' '%s'\n", anchor->path, anchor->name,
292 dup->path, dup->name);
293 RB_REMOVE(pf_anchor_global, &pf_anchors,
294 anchor);
295 rs_free(anchor);
296 rs_free(p);
297 return (NULL);
298 }
299 }
300 pf_init_ruleset(&anchor->ruleset);
301 anchor->ruleset.anchor = anchor;
302 parent = anchor;
303 if (r != NULL)
304 q = r + 1;
305 else
306 *q = 0;
307 }
308 rs_free(p);
309 return (anchor ? &anchor->ruleset : 0);
310 }
311
312 void
313 pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
314 {
315 struct pf_anchor *parent;
316 int i;
317
318 while (ruleset != NULL) {
319 if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
320 !RB_EMPTY(&ruleset->anchor->children) ||
321 ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
322 ruleset->topen)
323 return;
324 for (i = 0; i < PF_RULESET_MAX; ++i)
325 if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
326 !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
327 ruleset->rules[i].inactive.open)
328 return;
329 RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
330 if ((parent = ruleset->anchor->parent) != NULL)
331 RB_REMOVE(pf_anchor_node, &parent->children,
332 ruleset->anchor);
333 rs_free(ruleset->anchor);
334 if (parent == NULL)
335 return;
336 ruleset = &parent->ruleset;
337 }
338 }
339
340 int
341 pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
342 const char *name)
343 {
344 char *p, *path;
345 struct pf_ruleset *ruleset;
346
347 r->anchor = NULL;
348 r->anchor_relative = 0;
349 r->anchor_wildcard = 0;
350 if (!name[0])
351 return (0);
352 path = (char *)rs_malloc(MAXPATHLEN);
353 bzero(path, MAXPATHLEN);
354 if (name[0] == '/')
355 strlcpy(path, name + 1, MAXPATHLEN);
356 else {
357 /* relative path */
358 r->anchor_relative = 1;
359 if (s->anchor == NULL || !s->anchor->path[0])
360 path[0] = 0;
361 else
362 strlcpy(path, s->anchor->path, MAXPATHLEN);
363 while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
364 if (!path[0]) {
365 printf("pf_anchor_setup: .. beyond root\n");
366 rs_free(path);
367 return (1);
368 }
369 if ((p = strrchr(path, '/')) != NULL)
370 *p = 0;
371 else
372 path[0] = 0;
373 r->anchor_relative++;
374 name += 3;
375 }
376 if (path[0])
377 strlcat(path, "/", MAXPATHLEN);
378 strlcat(path, name, MAXPATHLEN);
379 }
380 if ((p = strrchr(path, '/')) != NULL && strcmp(p, "/*") == 0) {
381 r->anchor_wildcard = 1;
382 *p = 0;
383 }
384 ruleset = pf_find_or_create_ruleset(path);
385 rs_free(path);
386 if (ruleset == NULL || ruleset->anchor == NULL) {
387 printf("pf_anchor_setup: ruleset\n");
388 return (1);
389 }
390 r->anchor = ruleset->anchor;
391 r->anchor->refcnt++;
392 return (0);
393 }
394
395 int
396 pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
397 struct pfioc_rule *pr)
398 {
399 pr->anchor_call[0] = 0;
400 if (r->anchor == NULL)
401 return (0);
402 if (!r->anchor_relative) {
403 strlcpy(pr->anchor_call, "/", sizeof (pr->anchor_call));
404 strlcat(pr->anchor_call, r->anchor->path,
405 sizeof (pr->anchor_call));
406 } else {
407 char *a, *p;
408 int i;
409
410 a = (char *)rs_malloc(MAXPATHLEN);
411 bzero(a, MAXPATHLEN);
412 if (rs->anchor == NULL)
413 a[0] = 0;
414 else
415 strlcpy(a, rs->anchor->path, MAXPATHLEN);
416 for (i = 1; i < r->anchor_relative; ++i) {
417 if ((p = strrchr(a, '/')) == NULL)
418 p = a;
419 *p = 0;
420 strlcat(pr->anchor_call, "../",
421 sizeof (pr->anchor_call));
422 }
423 if (strncmp(a, r->anchor->path, strlen(a))) {
424 printf("pf_anchor_copyout: '%s' '%s'\n", a,
425 r->anchor->path);
426 rs_free(a);
427 return (1);
428 }
429 if (strlen(r->anchor->path) > strlen(a))
430 strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
431 strlen(a) + 1 : 0), sizeof (pr->anchor_call));
432 rs_free(a);
433 }
434 if (r->anchor_wildcard)
435 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
436 sizeof (pr->anchor_call));
437 return (0);
438 }
439
440 void
441 pf_anchor_remove(struct pf_rule *r)
442 {
443 if (r->anchor == NULL)
444 return;
445 if (r->anchor->refcnt <= 0) {
446 printf("pf_anchor_remove: broken refcount\n");
447 r->anchor = NULL;
448 return;
449 }
450 if (!--r->anchor->refcnt)
451 pf_remove_if_empty_ruleset(&r->anchor->ruleset);
452 r->anchor = NULL;
453 }