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"); | |
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); |