diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2011-02-01 09:56:00 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-02-01 09:56:00 -0500 |
commit | d956798d82d2d331c031301965d69e17a1a48a2b (patch) | |
tree | 3ceb434334d79d9dd8b205fc502cf2d20ab737ab /net | |
parent | f830837f0eed0f9e371b8fd65169365780814bb1 (diff) |
netfilter: xtables: "set" match and "SET" target support
The patch adds the combined module of the "SET" target and "set" match
to netfilter. Both the previous and the current revisions are supported.
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Kconfig | 12 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/xt_set.c | 359 |
3 files changed, 372 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 351abf8ace13..06fa9e4e45c7 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -352,6 +352,18 @@ config NETFILTER_XT_CONNMARK | |||
352 | ctmark), similarly to the packet mark (nfmark). Using this | 352 | ctmark), similarly to the packet mark (nfmark). Using this |
353 | target and match, you can set and match on this mark. | 353 | target and match, you can set and match on this mark. |
354 | 354 | ||
355 | config NETFILTER_XT_SET | ||
356 | tristate 'set target and match support' | ||
357 | depends on IP_SET | ||
358 | depends on NETFILTER_ADVANCED | ||
359 | help | ||
360 | This option adds the "SET" target and "set" match. | ||
361 | |||
362 | Using this target and match, you can add/delete and match | ||
363 | elements in the sets created by ipset(8). | ||
364 | |||
365 | To compile it as a module, choose M here. If unsure, say N. | ||
366 | |||
355 | # alphabetically ordered list of targets | 367 | # alphabetically ordered list of targets |
356 | 368 | ||
357 | comment "Xtables targets" | 369 | comment "Xtables targets" |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 510b586ccb7f..1148643559cb 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o | |||
46 | # combos | 46 | # combos |
47 | obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o | 47 | obj-$(CONFIG_NETFILTER_XT_MARK) += xt_mark.o |
48 | obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o | 48 | obj-$(CONFIG_NETFILTER_XT_CONNMARK) += xt_connmark.o |
49 | obj-$(CONFIG_NETFILTER_XT_SET) += xt_set.o | ||
49 | 50 | ||
50 | # targets | 51 | # targets |
51 | obj-$(CONFIG_NETFILTER_XT_TARGET_AUDIT) += xt_AUDIT.o | 52 | obj-$(CONFIG_NETFILTER_XT_TARGET_AUDIT) += xt_AUDIT.o |
diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c new file mode 100644 index 000000000000..061d48cec137 --- /dev/null +++ b/net/netfilter/xt_set.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | ||
2 | * Patrick Schaaf <bof@bof.de> | ||
3 | * Martin Josefsson <gandalf@wlug.westbo.se> | ||
4 | * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | /* Kernel module which implements the set match and SET target | ||
12 | * for netfilter/iptables. */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/skbuff.h> | ||
16 | #include <linux/version.h> | ||
17 | |||
18 | #include <linux/netfilter/x_tables.h> | ||
19 | #include <linux/netfilter/xt_set.h> | ||
20 | |||
21 | MODULE_LICENSE("GPL"); | ||
22 | MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | ||
23 | MODULE_DESCRIPTION("Xtables: IP set match and target module"); | ||
24 | MODULE_ALIAS("xt_SET"); | ||
25 | MODULE_ALIAS("ipt_set"); | ||
26 | MODULE_ALIAS("ip6t_set"); | ||
27 | MODULE_ALIAS("ipt_SET"); | ||
28 | MODULE_ALIAS("ip6t_SET"); | ||
29 | |||
30 | static inline int | ||
31 | match_set(ip_set_id_t index, const struct sk_buff *skb, | ||
32 | u8 pf, u8 dim, u8 flags, int inv) | ||
33 | { | ||
34 | if (ip_set_test(index, skb, pf, dim, flags)) | ||
35 | inv = !inv; | ||
36 | return inv; | ||
37 | } | ||
38 | |||
39 | /* Revision 0 interface: backward compatible with netfilter/iptables */ | ||
40 | |||
41 | static bool | ||
42 | set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) | ||
43 | { | ||
44 | const struct xt_set_info_match_v0 *info = par->matchinfo; | ||
45 | |||
46 | return match_set(info->match_set.index, skb, par->family, | ||
47 | info->match_set.u.compat.dim, | ||
48 | info->match_set.u.compat.flags, | ||
49 | info->match_set.u.compat.flags & IPSET_INV_MATCH); | ||
50 | } | ||
51 | |||
52 | static void | ||
53 | compat_flags(struct xt_set_info_v0 *info) | ||
54 | { | ||
55 | u_int8_t i; | ||
56 | |||
57 | /* Fill out compatibility data according to enum ip_set_kopt */ | ||
58 | info->u.compat.dim = IPSET_DIM_ZERO; | ||
59 | if (info->u.flags[0] & IPSET_MATCH_INV) | ||
60 | info->u.compat.flags |= IPSET_INV_MATCH; | ||
61 | for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { | ||
62 | info->u.compat.dim++; | ||
63 | if (info->u.flags[i] & IPSET_SRC) | ||
64 | info->u.compat.flags |= (1<<info->u.compat.dim); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static int | ||
69 | set_match_v0_checkentry(const struct xt_mtchk_param *par) | ||
70 | { | ||
71 | struct xt_set_info_match_v0 *info = par->matchinfo; | ||
72 | ip_set_id_t index; | ||
73 | |||
74 | index = ip_set_nfnl_get_byindex(info->match_set.index); | ||
75 | |||
76 | if (index == IPSET_INVALID_ID) { | ||
77 | pr_warning("Cannot find set indentified by id %u to match\n", | ||
78 | info->match_set.index); | ||
79 | return -ENOENT; | ||
80 | } | ||
81 | if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { | ||
82 | pr_warning("Protocol error: set match dimension " | ||
83 | "is over the limit!\n"); | ||
84 | return -ERANGE; | ||
85 | } | ||
86 | |||
87 | /* Fill out compatibility data */ | ||
88 | compat_flags(&info->match_set); | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | static void | ||
94 | set_match_v0_destroy(const struct xt_mtdtor_param *par) | ||
95 | { | ||
96 | struct xt_set_info_match_v0 *info = par->matchinfo; | ||
97 | |||
98 | ip_set_nfnl_put(info->match_set.index); | ||
99 | } | ||
100 | |||
101 | static unsigned int | ||
102 | set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) | ||
103 | { | ||
104 | const struct xt_set_info_target_v0 *info = par->targinfo; | ||
105 | |||
106 | if (info->add_set.index != IPSET_INVALID_ID) | ||
107 | ip_set_add(info->add_set.index, skb, par->family, | ||
108 | info->add_set.u.compat.dim, | ||
109 | info->add_set.u.compat.flags); | ||
110 | if (info->del_set.index != IPSET_INVALID_ID) | ||
111 | ip_set_del(info->del_set.index, skb, par->family, | ||
112 | info->del_set.u.compat.dim, | ||
113 | info->del_set.u.compat.flags); | ||
114 | |||
115 | return XT_CONTINUE; | ||
116 | } | ||
117 | |||
118 | static int | ||
119 | set_target_v0_checkentry(const struct xt_tgchk_param *par) | ||
120 | { | ||
121 | struct xt_set_info_target_v0 *info = par->targinfo; | ||
122 | ip_set_id_t index; | ||
123 | |||
124 | if (info->add_set.index != IPSET_INVALID_ID) { | ||
125 | index = ip_set_nfnl_get_byindex(info->add_set.index); | ||
126 | if (index == IPSET_INVALID_ID) { | ||
127 | pr_warning("Cannot find add_set index %u as target\n", | ||
128 | info->add_set.index); | ||
129 | return -ENOENT; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | if (info->del_set.index != IPSET_INVALID_ID) { | ||
134 | index = ip_set_nfnl_get_byindex(info->del_set.index); | ||
135 | if (index == IPSET_INVALID_ID) { | ||
136 | pr_warning("Cannot find del_set index %u as target\n", | ||
137 | info->del_set.index); | ||
138 | return -ENOENT; | ||
139 | } | ||
140 | } | ||
141 | if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || | ||
142 | info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { | ||
143 | pr_warning("Protocol error: SET target dimension " | ||
144 | "is over the limit!\n"); | ||
145 | return -ERANGE; | ||
146 | } | ||
147 | |||
148 | /* Fill out compatibility data */ | ||
149 | compat_flags(&info->add_set); | ||
150 | compat_flags(&info->del_set); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static void | ||
156 | set_target_v0_destroy(const struct xt_tgdtor_param *par) | ||
157 | { | ||
158 | const struct xt_set_info_target_v0 *info = par->targinfo; | ||
159 | |||
160 | if (info->add_set.index != IPSET_INVALID_ID) | ||
161 | ip_set_nfnl_put(info->add_set.index); | ||
162 | if (info->del_set.index != IPSET_INVALID_ID) | ||
163 | ip_set_nfnl_put(info->del_set.index); | ||
164 | } | ||
165 | |||
166 | /* Revision 1: current interface to netfilter/iptables */ | ||
167 | |||
168 | static bool | ||
169 | set_match(const struct sk_buff *skb, struct xt_action_param *par) | ||
170 | { | ||
171 | const struct xt_set_info_match *info = par->matchinfo; | ||
172 | |||
173 | return match_set(info->match_set.index, skb, par->family, | ||
174 | info->match_set.dim, | ||
175 | info->match_set.flags, | ||
176 | info->match_set.flags & IPSET_INV_MATCH); | ||
177 | } | ||
178 | |||
179 | static int | ||
180 | set_match_checkentry(const struct xt_mtchk_param *par) | ||
181 | { | ||
182 | struct xt_set_info_match *info = par->matchinfo; | ||
183 | ip_set_id_t index; | ||
184 | |||
185 | index = ip_set_nfnl_get_byindex(info->match_set.index); | ||
186 | |||
187 | if (index == IPSET_INVALID_ID) { | ||
188 | pr_warning("Cannot find set indentified by id %u to match\n", | ||
189 | info->match_set.index); | ||
190 | return -ENOENT; | ||
191 | } | ||
192 | if (info->match_set.dim > IPSET_DIM_MAX) { | ||
193 | pr_warning("Protocol error: set match dimension " | ||
194 | "is over the limit!\n"); | ||
195 | return -ERANGE; | ||
196 | } | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static void | ||
202 | set_match_destroy(const struct xt_mtdtor_param *par) | ||
203 | { | ||
204 | struct xt_set_info_match *info = par->matchinfo; | ||
205 | |||
206 | ip_set_nfnl_put(info->match_set.index); | ||
207 | } | ||
208 | |||
209 | static unsigned int | ||
210 | set_target(struct sk_buff *skb, const struct xt_action_param *par) | ||
211 | { | ||
212 | const struct xt_set_info_target *info = par->targinfo; | ||
213 | |||
214 | if (info->add_set.index != IPSET_INVALID_ID) | ||
215 | ip_set_add(info->add_set.index, | ||
216 | skb, par->family, | ||
217 | info->add_set.dim, | ||
218 | info->add_set.flags); | ||
219 | if (info->del_set.index != IPSET_INVALID_ID) | ||
220 | ip_set_del(info->del_set.index, | ||
221 | skb, par->family, | ||
222 | info->add_set.dim, | ||
223 | info->del_set.flags); | ||
224 | |||
225 | return XT_CONTINUE; | ||
226 | } | ||
227 | |||
228 | static int | ||
229 | set_target_checkentry(const struct xt_tgchk_param *par) | ||
230 | { | ||
231 | const struct xt_set_info_target *info = par->targinfo; | ||
232 | ip_set_id_t index; | ||
233 | |||
234 | if (info->add_set.index != IPSET_INVALID_ID) { | ||
235 | index = ip_set_nfnl_get_byindex(info->add_set.index); | ||
236 | if (index == IPSET_INVALID_ID) { | ||
237 | pr_warning("Cannot find add_set index %u as target\n", | ||
238 | info->add_set.index); | ||
239 | return -ENOENT; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if (info->del_set.index != IPSET_INVALID_ID) { | ||
244 | index = ip_set_nfnl_get_byindex(info->del_set.index); | ||
245 | if (index == IPSET_INVALID_ID) { | ||
246 | pr_warning("Cannot find del_set index %u as target\n", | ||
247 | info->del_set.index); | ||
248 | return -ENOENT; | ||
249 | } | ||
250 | } | ||
251 | if (info->add_set.dim > IPSET_DIM_MAX || | ||
252 | info->del_set.flags > IPSET_DIM_MAX) { | ||
253 | pr_warning("Protocol error: SET target dimension " | ||
254 | "is over the limit!\n"); | ||
255 | return -ERANGE; | ||
256 | } | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static void | ||
262 | set_target_destroy(const struct xt_tgdtor_param *par) | ||
263 | { | ||
264 | const struct xt_set_info_target *info = par->targinfo; | ||
265 | |||
266 | if (info->add_set.index != IPSET_INVALID_ID) | ||
267 | ip_set_nfnl_put(info->add_set.index); | ||
268 | if (info->del_set.index != IPSET_INVALID_ID) | ||
269 | ip_set_nfnl_put(info->del_set.index); | ||
270 | } | ||
271 | |||
272 | static struct xt_match set_matches[] __read_mostly = { | ||
273 | { | ||
274 | .name = "set", | ||
275 | .family = NFPROTO_IPV4, | ||
276 | .revision = 0, | ||
277 | .match = set_match_v0, | ||
278 | .matchsize = sizeof(struct xt_set_info_match_v0), | ||
279 | .checkentry = set_match_v0_checkentry, | ||
280 | .destroy = set_match_v0_destroy, | ||
281 | .me = THIS_MODULE | ||
282 | }, | ||
283 | { | ||
284 | .name = "set", | ||
285 | .family = NFPROTO_IPV4, | ||
286 | .revision = 1, | ||
287 | .match = set_match, | ||
288 | .matchsize = sizeof(struct xt_set_info_match), | ||
289 | .checkentry = set_match_checkentry, | ||
290 | .destroy = set_match_destroy, | ||
291 | .me = THIS_MODULE | ||
292 | }, | ||
293 | { | ||
294 | .name = "set", | ||
295 | .family = NFPROTO_IPV6, | ||
296 | .revision = 1, | ||
297 | .match = set_match, | ||
298 | .matchsize = sizeof(struct xt_set_info_match), | ||
299 | .checkentry = set_match_checkentry, | ||
300 | .destroy = set_match_destroy, | ||
301 | .me = THIS_MODULE | ||
302 | }, | ||
303 | }; | ||
304 | |||
305 | static struct xt_target set_targets[] __read_mostly = { | ||
306 | { | ||
307 | .name = "SET", | ||
308 | .revision = 0, | ||
309 | .family = NFPROTO_IPV4, | ||
310 | .target = set_target_v0, | ||
311 | .targetsize = sizeof(struct xt_set_info_target_v0), | ||
312 | .checkentry = set_target_v0_checkentry, | ||
313 | .destroy = set_target_v0_destroy, | ||
314 | .me = THIS_MODULE | ||
315 | }, | ||
316 | { | ||
317 | .name = "SET", | ||
318 | .revision = 1, | ||
319 | .family = NFPROTO_IPV4, | ||
320 | .target = set_target, | ||
321 | .targetsize = sizeof(struct xt_set_info_target), | ||
322 | .checkentry = set_target_checkentry, | ||
323 | .destroy = set_target_destroy, | ||
324 | .me = THIS_MODULE | ||
325 | }, | ||
326 | { | ||
327 | .name = "SET", | ||
328 | .revision = 1, | ||
329 | .family = NFPROTO_IPV6, | ||
330 | .target = set_target, | ||
331 | .targetsize = sizeof(struct xt_set_info_target), | ||
332 | .checkentry = set_target_checkentry, | ||
333 | .destroy = set_target_destroy, | ||
334 | .me = THIS_MODULE | ||
335 | }, | ||
336 | }; | ||
337 | |||
338 | static int __init xt_set_init(void) | ||
339 | { | ||
340 | int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); | ||
341 | |||
342 | if (!ret) { | ||
343 | ret = xt_register_targets(set_targets, | ||
344 | ARRAY_SIZE(set_targets)); | ||
345 | if (ret) | ||
346 | xt_unregister_matches(set_matches, | ||
347 | ARRAY_SIZE(set_matches)); | ||
348 | } | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static void __exit xt_set_fini(void) | ||
353 | { | ||
354 | xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); | ||
355 | xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); | ||
356 | } | ||
357 | |||
358 | module_init(xt_set_init); | ||
359 | module_exit(xt_set_fini); | ||