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