]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netpfil/pf/pf_ruleset.c
pf: switch pf_status.fcounters to pf_counter_u64
[FreeBSD/FreeBSD.git] / sys / netpfil / pf / pf_ruleset.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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  *      $OpenBSD: pf_ruleset.c,v 1.2 2008/12/18 15:31:37 dhill Exp $
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <sys/systm.h>
45 #include <sys/refcount.h>
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 #ifndef _KERNEL
62 #error "Kernel only file. Please use sbin/pfctl/pf_ruleset.c instead."
63 #endif
64
65 #define DPFPRINTF(format, x...)                         \
66         if (V_pf_status.debug >= PF_DEBUG_NOISY)        \
67                 printf(format , ##x)
68 #define rs_malloc(x)            malloc(x, M_TEMP, M_NOWAIT|M_ZERO)
69 #define rs_free(x)              free(x, M_TEMP)
70
71 VNET_DEFINE(struct pf_kanchor_global,   pf_anchors);
72 VNET_DEFINE(struct pf_kanchor,          pf_main_anchor);
73
74 static __inline int             pf_kanchor_compare(struct pf_kanchor *,
75                                     struct pf_kanchor *);
76 static struct pf_kanchor        *pf_find_kanchor(const char *);
77
78 RB_GENERATE(pf_kanchor_global, pf_kanchor, entry_global, pf_kanchor_compare);
79 RB_GENERATE(pf_kanchor_node, pf_kanchor, entry_node, pf_kanchor_compare);
80
81 static __inline int
82 pf_kanchor_compare(struct pf_kanchor *a, struct pf_kanchor *b)
83 {
84         int c = strcmp(a->path, b->path);
85
86         return (c ? (c < 0 ? -1 : 1) : 0);
87 }
88
89 int
90 pf_get_ruleset_number(u_int8_t action)
91 {
92         switch (action) {
93         case PF_SCRUB:
94         case PF_NOSCRUB:
95                 return (PF_RULESET_SCRUB);
96                 break;
97         case PF_PASS:
98         case PF_MATCH:
99         case PF_DROP:
100                 return (PF_RULESET_FILTER);
101                 break;
102         case PF_NAT:
103         case PF_NONAT:
104                 return (PF_RULESET_NAT);
105                 break;
106         case PF_BINAT:
107         case PF_NOBINAT:
108                 return (PF_RULESET_BINAT);
109                 break;
110         case PF_RDR:
111         case PF_NORDR:
112                 return (PF_RULESET_RDR);
113                 break;
114         default:
115                 return (PF_RULESET_MAX);
116                 break;
117         }
118 }
119
120 static struct pf_kanchor *
121 pf_find_kanchor(const char *path)
122 {
123         struct pf_kanchor       *key, *found;
124
125         key = (struct pf_kanchor *)rs_malloc(sizeof(*key));
126         if (key == NULL)
127                 return (NULL);
128         strlcpy(key->path, path, sizeof(key->path));
129         found = RB_FIND(pf_kanchor_global, &V_pf_anchors, key);
130         rs_free(key);
131         return (found);
132 }
133
134 void
135 pf_init_kruleset(struct pf_kruleset *ruleset)
136 {
137         int     i;
138
139         memset(ruleset, 0, sizeof(struct pf_kruleset));
140         for (i = 0; i < PF_RULESET_MAX; i++) {
141                 TAILQ_INIT(&ruleset->rules[i].queues[0]);
142                 TAILQ_INIT(&ruleset->rules[i].queues[1]);
143                 ruleset->rules[i].active.ptr = &ruleset->rules[i].queues[0];
144                 ruleset->rules[i].inactive.ptr = &ruleset->rules[i].queues[1];
145         }
146 }
147
148 struct pf_kruleset *
149 pf_find_kruleset(const char *path)
150 {
151         struct pf_kanchor       *anchor;
152
153         while (*path == '/')
154                 path++;
155         if (!*path)
156                 return (&pf_main_ruleset);
157         anchor = pf_find_kanchor(path);
158         if (anchor == NULL)
159                 return (NULL);
160         else
161                 return (&anchor->ruleset);
162 }
163
164 struct pf_kruleset *
165 pf_find_or_create_kruleset(const char *path)
166 {
167         char                    *p, *q, *r;
168         struct pf_kruleset      *ruleset;
169         struct pf_kanchor       *anchor = NULL, *dup, *parent = NULL;
170
171         if (path[0] == 0)
172                 return (&pf_main_ruleset);
173         while (*path == '/')
174                 path++;
175         ruleset = pf_find_kruleset(path);
176         if (ruleset != NULL)
177                 return (ruleset);
178         p = (char *)rs_malloc(MAXPATHLEN);
179         if (p == NULL)
180                 return (NULL);
181         strlcpy(p, path, MAXPATHLEN);
182         while (parent == NULL && (q = strrchr(p, '/')) != NULL) {
183                 *q = 0;
184                 if ((ruleset = pf_find_kruleset(p)) != NULL) {
185                         parent = ruleset->anchor;
186                         break;
187                 }
188         }
189         if (q == NULL)
190                 q = p;
191         else
192                 q++;
193         strlcpy(p, path, MAXPATHLEN);
194         if (!*q) {
195                 rs_free(p);
196                 return (NULL);
197         }
198         while ((r = strchr(q, '/')) != NULL || *q) {
199                 if (r != NULL)
200                         *r = 0;
201                 if (!*q || strlen(q) >= PF_ANCHOR_NAME_SIZE ||
202                     (parent != NULL && strlen(parent->path) >=
203                     MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1)) {
204                         rs_free(p);
205                         return (NULL);
206                 }
207                 anchor = (struct pf_kanchor *)rs_malloc(sizeof(*anchor));
208                 if (anchor == NULL) {
209                         rs_free(p);
210                         return (NULL);
211                 }
212                 RB_INIT(&anchor->children);
213                 strlcpy(anchor->name, q, sizeof(anchor->name));
214                 if (parent != NULL) {
215                         strlcpy(anchor->path, parent->path,
216                             sizeof(anchor->path));
217                         strlcat(anchor->path, "/", sizeof(anchor->path));
218                 }
219                 strlcat(anchor->path, anchor->name, sizeof(anchor->path));
220                 if ((dup = RB_INSERT(pf_kanchor_global, &V_pf_anchors, anchor)) !=
221                     NULL) {
222                         printf("pf_find_or_create_ruleset: RB_INSERT1 "
223                             "'%s' '%s' collides with '%s' '%s'\n",
224                             anchor->path, anchor->name, dup->path, dup->name);
225                         rs_free(anchor);
226                         rs_free(p);
227                         return (NULL);
228                 }
229                 if (parent != NULL) {
230                         anchor->parent = parent;
231                         if ((dup = RB_INSERT(pf_kanchor_node, &parent->children,
232                             anchor)) != NULL) {
233                                 printf("pf_find_or_create_ruleset: "
234                                     "RB_INSERT2 '%s' '%s' collides with "
235                                     "'%s' '%s'\n", anchor->path, anchor->name,
236                                     dup->path, dup->name);
237                                 RB_REMOVE(pf_kanchor_global, &V_pf_anchors,
238                                     anchor);
239                                 rs_free(anchor);
240                                 rs_free(p);
241                                 return (NULL);
242                         }
243                 }
244                 pf_init_kruleset(&anchor->ruleset);
245                 anchor->ruleset.anchor = anchor;
246                 parent = anchor;
247                 if (r != NULL)
248                         q = r + 1;
249                 else
250                         *q = 0;
251         }
252         rs_free(p);
253         return (&anchor->ruleset);
254 }
255
256 void
257 pf_remove_if_empty_kruleset(struct pf_kruleset *ruleset)
258 {
259         struct pf_kanchor       *parent;
260         int                      i;
261
262         while (ruleset != NULL) {
263                 if (ruleset == &pf_main_ruleset || ruleset->anchor == NULL ||
264                     !RB_EMPTY(&ruleset->anchor->children) ||
265                     ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
266                     ruleset->topen)
267                         return;
268                 for (i = 0; i < PF_RULESET_MAX; ++i)
269                         if (!TAILQ_EMPTY(ruleset->rules[i].active.ptr) ||
270                             !TAILQ_EMPTY(ruleset->rules[i].inactive.ptr) ||
271                             ruleset->rules[i].inactive.open)
272                                 return;
273                 RB_REMOVE(pf_kanchor_global, &V_pf_anchors, ruleset->anchor);
274                 if ((parent = ruleset->anchor->parent) != NULL)
275                         RB_REMOVE(pf_kanchor_node, &parent->children,
276                             ruleset->anchor);
277                 rs_free(ruleset->anchor);
278                 if (parent == NULL)
279                         return;
280                 ruleset = &parent->ruleset;
281         }
282 }
283
284 int
285 pf_kanchor_setup(struct pf_krule *r, const struct pf_kruleset *s,
286     const char *name)
287 {
288         char                    *p, *path;
289         struct pf_kruleset      *ruleset;
290
291         r->anchor = NULL;
292         r->anchor_relative = 0;
293         r->anchor_wildcard = 0;
294         if (!name[0])
295                 return (0);
296         path = (char *)rs_malloc(MAXPATHLEN);
297         if (path == NULL)
298                 return (1);
299         if (name[0] == '/')
300                 strlcpy(path, name + 1, MAXPATHLEN);
301         else {
302                 /* relative path */
303                 r->anchor_relative = 1;
304                 if (s->anchor == NULL || !s->anchor->path[0])
305                         path[0] = 0;
306                 else
307                         strlcpy(path, s->anchor->path, MAXPATHLEN);
308                 while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
309                         if (!path[0]) {
310                                 DPFPRINTF("pf_anchor_setup: .. beyond root\n");
311                                 rs_free(path);
312                                 return (1);
313                         }
314                         if ((p = strrchr(path, '/')) != NULL)
315                                 *p = 0;
316                         else
317                                 path[0] = 0;
318                         r->anchor_relative++;
319                         name += 3;
320                 }
321                 if (path[0])
322                         strlcat(path, "/", MAXPATHLEN);
323                 strlcat(path, name, MAXPATHLEN);
324         }
325         if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
326                 r->anchor_wildcard = 1;
327                 *p = 0;
328         }
329         ruleset = pf_find_or_create_kruleset(path);
330         rs_free(path);
331         if (ruleset == NULL || ruleset->anchor == NULL) {
332                 DPFPRINTF("pf_anchor_setup: ruleset\n");
333                 return (1);
334         }
335         r->anchor = ruleset->anchor;
336         r->anchor->refcnt++;
337         return (0);
338 }
339
340 int
341 pf_kanchor_nvcopyout(const struct pf_kruleset *rs, const struct pf_krule *r,
342     nvlist_t *nvl)
343 {
344         char anchor_call[MAXPATHLEN] = { 0 };
345
346         if (r->anchor == NULL)
347                 goto done;
348         if (!r->anchor_relative) {
349                 strlcpy(anchor_call, "/", sizeof(anchor_call));
350                 strlcat(anchor_call, r->anchor->path,
351                     sizeof(anchor_call));
352         } else {
353                 char     a[MAXPATHLEN];
354                 char    *p;
355                 int      i;
356                 if (rs->anchor == NULL)
357                         a[0] = 0;
358                 else
359                         strlcpy(a, rs->anchor->path, MAXPATHLEN);
360                 for (i = 1; i < r->anchor_relative; ++i) {
361                         if ((p = strrchr(a, '/')) == NULL)
362                                 p = a;
363                         *p = 0;
364                         strlcat(anchor_call, "../",
365                             sizeof(anchor_call));
366                 }
367                 if (strncmp(a, r->anchor->path, strlen(a))) {
368                         printf("pf_anchor_copyout: '%s' '%s'\n", a,
369                             r->anchor->path);
370                         return (1);
371                 }
372                 if (strlen(r->anchor->path) > strlen(a))
373                         strlcat(anchor_call, r->anchor->path + (a[0] ?
374                             strlen(a) + 1 : 0), sizeof(anchor_call));
375
376         }
377         if (r->anchor_wildcard)
378                 strlcat(anchor_call, anchor_call[0] ? "/*" : "*",
379                     sizeof(anchor_call));
380
381 done:
382         nvlist_add_string(nvl, "anchor_call", anchor_call);
383
384         return (0);
385 }
386
387 int
388 pf_kanchor_copyout(const struct pf_kruleset *rs, const struct pf_krule *r,
389     struct pfioc_rule *pr)
390 {
391         pr->anchor_call[0] = 0;
392         if (r->anchor == NULL)
393                 return (0);
394         if (!r->anchor_relative) {
395                 strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
396                 strlcat(pr->anchor_call, r->anchor->path,
397                     sizeof(pr->anchor_call));
398         } else {
399                 char    *a, *p;
400                 int      i;
401
402                 a = (char *)rs_malloc(MAXPATHLEN);
403                 if (a == NULL)
404                         return (1);
405                 if (rs->anchor == NULL)
406                         a[0] = 0;
407                 else
408                         strlcpy(a, rs->anchor->path, MAXPATHLEN);
409                 for (i = 1; i < r->anchor_relative; ++i) {
410                         if ((p = strrchr(a, '/')) == NULL)
411                                 p = a;
412                         *p = 0;
413                         strlcat(pr->anchor_call, "../",
414                             sizeof(pr->anchor_call));
415                 }
416                 if (strncmp(a, r->anchor->path, strlen(a))) {
417                         printf("pf_anchor_copyout: '%s' '%s'\n", a,
418                             r->anchor->path);
419                         rs_free(a);
420                         return (1);
421                 }
422                 if (strlen(r->anchor->path) > strlen(a))
423                         strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
424                             strlen(a) + 1 : 0), sizeof(pr->anchor_call));
425                 rs_free(a);
426         }
427         if (r->anchor_wildcard)
428                 strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
429                     sizeof(pr->anchor_call));
430         return (0);
431 }
432
433 void
434 pf_kanchor_remove(struct pf_krule *r)
435 {
436         if (r->anchor == NULL)
437                 return;
438         if (r->anchor->refcnt <= 0) {
439                 printf("pf_anchor_remove: broken refcount\n");
440                 r->anchor = NULL;
441                 return;
442         }
443         if (!--r->anchor->refcnt)
444                 pf_remove_if_empty_kruleset(&r->anchor->ruleset);
445         r->anchor = NULL;
446 }