Commit | Line | Data |
---|---|---|
d956798d JK |
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"); | |
eafbd3fd | 84 | ip_set_nfnl_put(info->match_set.index); |
d956798d JK |
85 | return -ERANGE; |
86 | } | |
87 | ||
88 | /* Fill out compatibility data */ | |
89 | compat_flags(&info->match_set); | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
94 | static void | |
95 | set_match_v0_destroy(const struct xt_mtdtor_param *par) | |
96 | { | |
97 | struct xt_set_info_match_v0 *info = par->matchinfo; | |
98 | ||
99 | ip_set_nfnl_put(info->match_set.index); | |
100 | } | |
101 | ||
102 | static unsigned int | |
103 | set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) | |
104 | { | |
105 | const struct xt_set_info_target_v0 *info = par->targinfo; | |
106 | ||
107 | if (info->add_set.index != IPSET_INVALID_ID) | |
108 | ip_set_add(info->add_set.index, skb, par->family, | |
109 | info->add_set.u.compat.dim, | |
110 | info->add_set.u.compat.flags); | |
111 | if (info->del_set.index != IPSET_INVALID_ID) | |
112 | ip_set_del(info->del_set.index, skb, par->family, | |
113 | info->del_set.u.compat.dim, | |
114 | info->del_set.u.compat.flags); | |
115 | ||
116 | return XT_CONTINUE; | |
117 | } | |
118 | ||
119 | static int | |
120 | set_target_v0_checkentry(const struct xt_tgchk_param *par) | |
121 | { | |
122 | struct xt_set_info_target_v0 *info = par->targinfo; | |
123 | ip_set_id_t index; | |
124 | ||
125 | if (info->add_set.index != IPSET_INVALID_ID) { | |
126 | index = ip_set_nfnl_get_byindex(info->add_set.index); | |
127 | if (index == IPSET_INVALID_ID) { | |
128 | pr_warning("Cannot find add_set index %u as target\n", | |
129 | info->add_set.index); | |
130 | return -ENOENT; | |
131 | } | |
132 | } | |
133 | ||
134 | if (info->del_set.index != IPSET_INVALID_ID) { | |
135 | index = ip_set_nfnl_get_byindex(info->del_set.index); | |
136 | if (index == IPSET_INVALID_ID) { | |
137 | pr_warning("Cannot find del_set index %u as target\n", | |
138 | info->del_set.index); | |
eafbd3fd JK |
139 | if (info->add_set.index != IPSET_INVALID_ID) |
140 | ip_set_nfnl_put(info->add_set.index); | |
d956798d JK |
141 | return -ENOENT; |
142 | } | |
143 | } | |
144 | if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || | |
145 | info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { | |
146 | pr_warning("Protocol error: SET target dimension " | |
147 | "is over the limit!\n"); | |
eafbd3fd JK |
148 | if (info->add_set.index != IPSET_INVALID_ID) |
149 | ip_set_nfnl_put(info->add_set.index); | |
150 | if (info->del_set.index != IPSET_INVALID_ID) | |
151 | ip_set_nfnl_put(info->del_set.index); | |
d956798d JK |
152 | return -ERANGE; |
153 | } | |
154 | ||
155 | /* Fill out compatibility data */ | |
156 | compat_flags(&info->add_set); | |
157 | compat_flags(&info->del_set); | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | static void | |
163 | set_target_v0_destroy(const struct xt_tgdtor_param *par) | |
164 | { | |
165 | const struct xt_set_info_target_v0 *info = par->targinfo; | |
166 | ||
167 | if (info->add_set.index != IPSET_INVALID_ID) | |
168 | ip_set_nfnl_put(info->add_set.index); | |
169 | if (info->del_set.index != IPSET_INVALID_ID) | |
170 | ip_set_nfnl_put(info->del_set.index); | |
171 | } | |
172 | ||
173 | /* Revision 1: current interface to netfilter/iptables */ | |
174 | ||
175 | static bool | |
176 | set_match(const struct sk_buff *skb, struct xt_action_param *par) | |
177 | { | |
178 | const struct xt_set_info_match *info = par->matchinfo; | |
179 | ||
180 | return match_set(info->match_set.index, skb, par->family, | |
181 | info->match_set.dim, | |
182 | info->match_set.flags, | |
183 | info->match_set.flags & IPSET_INV_MATCH); | |
184 | } | |
185 | ||
186 | static int | |
187 | set_match_checkentry(const struct xt_mtchk_param *par) | |
188 | { | |
189 | struct xt_set_info_match *info = par->matchinfo; | |
190 | ip_set_id_t index; | |
191 | ||
192 | index = ip_set_nfnl_get_byindex(info->match_set.index); | |
193 | ||
194 | if (index == IPSET_INVALID_ID) { | |
195 | pr_warning("Cannot find set indentified by id %u to match\n", | |
196 | info->match_set.index); | |
197 | return -ENOENT; | |
198 | } | |
199 | if (info->match_set.dim > IPSET_DIM_MAX) { | |
200 | pr_warning("Protocol error: set match dimension " | |
201 | "is over the limit!\n"); | |
eafbd3fd | 202 | ip_set_nfnl_put(info->match_set.index); |
d956798d JK |
203 | return -ERANGE; |
204 | } | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
209 | static void | |
210 | set_match_destroy(const struct xt_mtdtor_param *par) | |
211 | { | |
212 | struct xt_set_info_match *info = par->matchinfo; | |
213 | ||
214 | ip_set_nfnl_put(info->match_set.index); | |
215 | } | |
216 | ||
217 | static unsigned int | |
218 | set_target(struct sk_buff *skb, const struct xt_action_param *par) | |
219 | { | |
220 | const struct xt_set_info_target *info = par->targinfo; | |
221 | ||
222 | if (info->add_set.index != IPSET_INVALID_ID) | |
223 | ip_set_add(info->add_set.index, | |
224 | skb, par->family, | |
225 | info->add_set.dim, | |
226 | info->add_set.flags); | |
227 | if (info->del_set.index != IPSET_INVALID_ID) | |
228 | ip_set_del(info->del_set.index, | |
229 | skb, par->family, | |
eafbd3fd | 230 | info->del_set.dim, |
d956798d JK |
231 | info->del_set.flags); |
232 | ||
233 | return XT_CONTINUE; | |
234 | } | |
235 | ||
236 | static int | |
237 | set_target_checkentry(const struct xt_tgchk_param *par) | |
238 | { | |
239 | const struct xt_set_info_target *info = par->targinfo; | |
240 | ip_set_id_t index; | |
241 | ||
242 | if (info->add_set.index != IPSET_INVALID_ID) { | |
243 | index = ip_set_nfnl_get_byindex(info->add_set.index); | |
244 | if (index == IPSET_INVALID_ID) { | |
245 | pr_warning("Cannot find add_set index %u as target\n", | |
246 | info->add_set.index); | |
247 | return -ENOENT; | |
248 | } | |
249 | } | |
250 | ||
251 | if (info->del_set.index != IPSET_INVALID_ID) { | |
252 | index = ip_set_nfnl_get_byindex(info->del_set.index); | |
253 | if (index == IPSET_INVALID_ID) { | |
254 | pr_warning("Cannot find del_set index %u as target\n", | |
255 | info->del_set.index); | |
eafbd3fd JK |
256 | if (info->add_set.index != IPSET_INVALID_ID) |
257 | ip_set_nfnl_put(info->add_set.index); | |
d956798d JK |
258 | return -ENOENT; |
259 | } | |
260 | } | |
261 | if (info->add_set.dim > IPSET_DIM_MAX || | |
eafbd3fd | 262 | info->del_set.dim > IPSET_DIM_MAX) { |
d956798d JK |
263 | pr_warning("Protocol error: SET target dimension " |
264 | "is over the limit!\n"); | |
eafbd3fd JK |
265 | if (info->add_set.index != IPSET_INVALID_ID) |
266 | ip_set_nfnl_put(info->add_set.index); | |
267 | if (info->del_set.index != IPSET_INVALID_ID) | |
268 | ip_set_nfnl_put(info->del_set.index); | |
d956798d JK |
269 | return -ERANGE; |
270 | } | |
271 | ||
272 | return 0; | |
273 | } | |
274 | ||
275 | static void | |
276 | set_target_destroy(const struct xt_tgdtor_param *par) | |
277 | { | |
278 | const struct xt_set_info_target *info = par->targinfo; | |
279 | ||
280 | if (info->add_set.index != IPSET_INVALID_ID) | |
281 | ip_set_nfnl_put(info->add_set.index); | |
282 | if (info->del_set.index != IPSET_INVALID_ID) | |
283 | ip_set_nfnl_put(info->del_set.index); | |
284 | } | |
285 | ||
286 | static struct xt_match set_matches[] __read_mostly = { | |
287 | { | |
288 | .name = "set", | |
289 | .family = NFPROTO_IPV4, | |
290 | .revision = 0, | |
291 | .match = set_match_v0, | |
292 | .matchsize = sizeof(struct xt_set_info_match_v0), | |
293 | .checkentry = set_match_v0_checkentry, | |
294 | .destroy = set_match_v0_destroy, | |
295 | .me = THIS_MODULE | |
296 | }, | |
297 | { | |
298 | .name = "set", | |
299 | .family = NFPROTO_IPV4, | |
300 | .revision = 1, | |
301 | .match = set_match, | |
302 | .matchsize = sizeof(struct xt_set_info_match), | |
303 | .checkentry = set_match_checkentry, | |
304 | .destroy = set_match_destroy, | |
305 | .me = THIS_MODULE | |
306 | }, | |
307 | { | |
308 | .name = "set", | |
309 | .family = NFPROTO_IPV6, | |
310 | .revision = 1, | |
311 | .match = set_match, | |
312 | .matchsize = sizeof(struct xt_set_info_match), | |
313 | .checkentry = set_match_checkentry, | |
314 | .destroy = set_match_destroy, | |
315 | .me = THIS_MODULE | |
316 | }, | |
317 | }; | |
318 | ||
319 | static struct xt_target set_targets[] __read_mostly = { | |
320 | { | |
321 | .name = "SET", | |
322 | .revision = 0, | |
323 | .family = NFPROTO_IPV4, | |
324 | .target = set_target_v0, | |
325 | .targetsize = sizeof(struct xt_set_info_target_v0), | |
326 | .checkentry = set_target_v0_checkentry, | |
327 | .destroy = set_target_v0_destroy, | |
328 | .me = THIS_MODULE | |
329 | }, | |
330 | { | |
331 | .name = "SET", | |
332 | .revision = 1, | |
333 | .family = NFPROTO_IPV4, | |
334 | .target = set_target, | |
335 | .targetsize = sizeof(struct xt_set_info_target), | |
336 | .checkentry = set_target_checkentry, | |
337 | .destroy = set_target_destroy, | |
338 | .me = THIS_MODULE | |
339 | }, | |
340 | { | |
341 | .name = "SET", | |
342 | .revision = 1, | |
343 | .family = NFPROTO_IPV6, | |
344 | .target = set_target, | |
345 | .targetsize = sizeof(struct xt_set_info_target), | |
346 | .checkentry = set_target_checkentry, | |
347 | .destroy = set_target_destroy, | |
348 | .me = THIS_MODULE | |
349 | }, | |
350 | }; | |
351 | ||
352 | static int __init xt_set_init(void) | |
353 | { | |
354 | int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); | |
355 | ||
356 | if (!ret) { | |
357 | ret = xt_register_targets(set_targets, | |
358 | ARRAY_SIZE(set_targets)); | |
359 | if (ret) | |
360 | xt_unregister_matches(set_matches, | |
361 | ARRAY_SIZE(set_matches)); | |
362 | } | |
363 | return ret; | |
364 | } | |
365 | ||
366 | static void __exit xt_set_fini(void) | |
367 | { | |
368 | xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); | |
369 | xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); | |
370 | } | |
371 | ||
372 | module_init(xt_set_init); | |
373 | module_exit(xt_set_fini); |