]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/netinet/ipfw/ip_fw_table.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / sys / netinet / ipfw / ip_fw_table.c
1 /*-
2  * Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 /*
30  * Lookup table support for ipfw
31  *
32  * Lookup tables are implemented (at the moment) using the radix
33  * tree used for routing tables. Tables store key-value entries, where
34  * keys are network prefixes (addr/masklen), and values are integers.
35  * As a degenerate case we can interpret keys as 32-bit integers
36  * (with a /32 mask).
37  *
38  * The table is protected by the IPFW lock even for manipulation coming
39  * from userland, because operations are typically fast.
40  */
41
42 #include "opt_ipfw.h"
43 #include "opt_inet.h"
44 #ifndef INET
45 #error IPFIREWALL requires INET.
46 #endif /* INET */
47 #include "opt_inet6.h"
48
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/malloc.h>
52 #include <sys/kernel.h>
53 #include <sys/lock.h>
54 #include <sys/rwlock.h>
55 #include <sys/socket.h>
56 #include <net/if.h>     /* ip_fw.h requires IFNAMSIZ */
57 #include <net/radix.h>
58 #include <net/route.h>
59 #include <net/vnet.h>
60
61 #include <netinet/in.h>
62 #include <netinet/ip_var.h>     /* struct ipfw_rule_ref */
63 #include <netinet/ip_fw.h>
64 #include <sys/queue.h> /* LIST_HEAD */
65 #include <netinet/ipfw/ip_fw_private.h>
66
67 #ifdef MAC
68 #include <security/mac/mac_framework.h>
69 #endif
70
71 MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
72
73 struct table_entry {
74         struct radix_node       rn[2];
75         struct sockaddr_in      addr, mask;
76         u_int32_t               value;
77 };
78
79 /*
80  * The radix code expects addr and mask to be array of bytes,
81  * with the first byte being the length of the array. rn_inithead
82  * is called with the offset in bits of the lookup key within the
83  * array. If we use a sockaddr_in as the underlying type,
84  * sin_len is conveniently located at offset 0, sin_addr is at
85  * offset 4 and normally aligned.
86  * But for portability, let's avoid assumption and make the code explicit
87  */
88 #define KEY_LEN(v)      *((uint8_t *)&(v))
89 #define KEY_OFS         (8*offsetof(struct sockaddr_in, sin_addr))
90
91 int
92 ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
93     uint8_t mlen, uint32_t value)
94 {
95         struct radix_node_head *rnh;
96         struct table_entry *ent;
97         struct radix_node *rn;
98
99         if (tbl >= IPFW_TABLES_MAX)
100                 return (EINVAL);
101         rnh = ch->tables[tbl];
102         ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
103         if (ent == NULL)
104                 return (ENOMEM);
105         ent->value = value;
106         KEY_LEN(ent->addr) = KEY_LEN(ent->mask) = 8;
107         ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
108         ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
109         IPFW_WLOCK(ch);
110         rn = rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent);
111         if (rn == NULL) {
112                 IPFW_WUNLOCK(ch);
113                 free(ent, M_IPFW_TBL);
114                 return (EEXIST);
115         }
116         IPFW_WUNLOCK(ch);
117         return (0);
118 }
119
120 int
121 ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
122     uint8_t mlen)
123 {
124         struct radix_node_head *rnh;
125         struct table_entry *ent;
126         struct sockaddr_in sa, mask;
127
128         if (tbl >= IPFW_TABLES_MAX)
129                 return (EINVAL);
130         rnh = ch->tables[tbl];
131         KEY_LEN(sa) = KEY_LEN(mask) = 8;
132         mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
133         sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
134         IPFW_WLOCK(ch);
135         ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
136         if (ent == NULL) {
137                 IPFW_WUNLOCK(ch);
138                 return (ESRCH);
139         }
140         IPFW_WUNLOCK(ch);
141         free(ent, M_IPFW_TBL);
142         return (0);
143 }
144
145 static int
146 flush_table_entry(struct radix_node *rn, void *arg)
147 {
148         struct radix_node_head * const rnh = arg;
149         struct table_entry *ent;
150
151         ent = (struct table_entry *)
152             rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
153         if (ent != NULL)
154                 free(ent, M_IPFW_TBL);
155         return (0);
156 }
157
158 int
159 ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl)
160 {
161         struct radix_node_head *rnh;
162
163         IPFW_WLOCK_ASSERT(ch);
164
165         if (tbl >= IPFW_TABLES_MAX)
166                 return (EINVAL);
167         rnh = ch->tables[tbl];
168         KASSERT(rnh != NULL, ("NULL IPFW table"));
169         rnh->rnh_walktree(rnh, flush_table_entry, rnh);
170         return (0);
171 }
172
173 void
174 ipfw_destroy_tables(struct ip_fw_chain *ch)
175 {
176         uint16_t tbl;
177         struct radix_node_head *rnh;
178
179         IPFW_WLOCK_ASSERT(ch);
180
181         for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++) {
182                 ipfw_flush_table(ch, tbl);
183                 rnh = ch->tables[tbl];
184                 rn_detachhead((void **)&rnh);
185         }
186 }
187
188 int
189 ipfw_init_tables(struct ip_fw_chain *ch)
190
191         int i;
192         uint16_t j;
193
194         for (i = 0; i < IPFW_TABLES_MAX; i++) {
195                 if (!rn_inithead((void **)&ch->tables[i], KEY_OFS)) {
196                         for (j = 0; j < i; j++) {
197                                 (void) ipfw_flush_table(ch, j);
198                         }
199                         return (ENOMEM);
200                 }
201         }
202         return (0);
203 }
204
205 int
206 ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
207     uint32_t *val)
208 {
209         struct radix_node_head *rnh;
210         struct table_entry *ent;
211         struct sockaddr_in sa;
212
213         if (tbl >= IPFW_TABLES_MAX)
214                 return (0);
215         rnh = ch->tables[tbl];
216         KEY_LEN(sa) = 8;
217         sa.sin_addr.s_addr = addr;
218         ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
219         if (ent != NULL) {
220                 *val = ent->value;
221                 return (1);
222         }
223         return (0);
224 }
225
226 static int
227 count_table_entry(struct radix_node *rn, void *arg)
228 {
229         u_int32_t * const cnt = arg;
230
231         (*cnt)++;
232         return (0);
233 }
234
235 int
236 ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt)
237 {
238         struct radix_node_head *rnh;
239
240         if (tbl >= IPFW_TABLES_MAX)
241                 return (EINVAL);
242         rnh = ch->tables[tbl];
243         *cnt = 0;
244         rnh->rnh_walktree(rnh, count_table_entry, cnt);
245         return (0);
246 }
247
248 static int
249 dump_table_entry(struct radix_node *rn, void *arg)
250 {
251         struct table_entry * const n = (struct table_entry *)rn;
252         ipfw_table * const tbl = arg;
253         ipfw_table_entry *ent;
254
255         if (tbl->cnt == tbl->size)
256                 return (1);
257         ent = &tbl->ent[tbl->cnt];
258         ent->tbl = tbl->tbl;
259         if (in_nullhost(n->mask.sin_addr))
260                 ent->masklen = 0;
261         else
262                 ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
263         ent->addr = n->addr.sin_addr.s_addr;
264         ent->value = n->value;
265         tbl->cnt++;
266         return (0);
267 }
268
269 int
270 ipfw_dump_table(struct ip_fw_chain *ch, ipfw_table *tbl)
271 {
272         struct radix_node_head *rnh;
273
274         if (tbl->tbl >= IPFW_TABLES_MAX)
275                 return (EINVAL);
276         rnh = ch->tables[tbl->tbl];
277         tbl->cnt = 0;
278         rnh->rnh_walktree(rnh, dump_table_entry, tbl);
279         return (0);
280 }
281 /* end of file */