blob: 865a9e54f3ad85477e8b3d5dc481f1ea444fe978 [file] [log] [blame]
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +01001/* 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>
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010016
17#include <linux/netfilter/x_tables.h>
18#include <linux/netfilter/xt_set.h>
Jozsef Kadlecsika73f89a2012-06-29 09:42:28 +000019#include <linux/netfilter/ipset/ip_set_timeout.h>
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010020
21MODULE_LICENSE("GPL");
22MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
23MODULE_DESCRIPTION("Xtables: IP set match and target module");
24MODULE_ALIAS("xt_SET");
25MODULE_ALIAS("ipt_set");
26MODULE_ALIAS("ip6t_set");
27MODULE_ALIAS("ipt_SET");
28MODULE_ALIAS("ip6t_SET");
29
30static inline int
31match_set(ip_set_id_t index, const struct sk_buff *skb,
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020032 const struct xt_action_param *par,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020033 const struct ip_set_adt_opt *opt, int inv)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010034{
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020035 if (ip_set_test(index, skb, par, opt))
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010036 inv = !inv;
37 return inv;
38}
39
Jozsef Kadlecsik15b4d932011-06-16 19:01:26 +020040#define ADT_OPT(n, f, d, fs, cfs, t) \
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020041const struct ip_set_adt_opt n = { \
42 .family = f, \
43 .dim = d, \
44 .flags = fs, \
45 .cmdflags = cfs, \
46 .timeout = t, \
47}
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +000048#define ADT_MOPT(n, f, d, fs, cfs, t) \
49struct ip_set_adt_opt n = { \
50 .family = f, \
51 .dim = d, \
52 .flags = fs, \
53 .cmdflags = cfs, \
54 .timeout = t, \
55}
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020056
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010057/* Revision 0 interface: backward compatible with netfilter/iptables */
58
59static bool
60set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
61{
62 const struct xt_set_info_match_v0 *info = par->matchinfo;
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020063 ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
64 info->match_set.u.compat.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010065
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020066 return match_set(info->match_set.index, skb, par, &opt,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010067 info->match_set.u.compat.flags & IPSET_INV_MATCH);
68}
69
70static void
71compat_flags(struct xt_set_info_v0 *info)
72{
73 u_int8_t i;
74
75 /* Fill out compatibility data according to enum ip_set_kopt */
76 info->u.compat.dim = IPSET_DIM_ZERO;
77 if (info->u.flags[0] & IPSET_MATCH_INV)
78 info->u.compat.flags |= IPSET_INV_MATCH;
79 for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
80 info->u.compat.dim++;
81 if (info->u.flags[i] & IPSET_SRC)
82 info->u.compat.flags |= (1<<info->u.compat.dim);
83 }
84}
85
86static int
87set_match_v0_checkentry(const struct xt_mtchk_param *par)
88{
89 struct xt_set_info_match_v0 *info = par->matchinfo;
90 ip_set_id_t index;
91
92 index = ip_set_nfnl_get_byindex(info->match_set.index);
93
94 if (index == IPSET_INVALID_ID) {
95 pr_warning("Cannot find set indentified by id %u to match\n",
96 info->match_set.index);
97 return -ENOENT;
98 }
99 if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
100 pr_warning("Protocol error: set match dimension "
101 "is over the limit!\n");
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200102 ip_set_nfnl_put(info->match_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100103 return -ERANGE;
104 }
105
106 /* Fill out compatibility data */
107 compat_flags(&info->match_set);
108
109 return 0;
110}
111
112static void
113set_match_v0_destroy(const struct xt_mtdtor_param *par)
114{
115 struct xt_set_info_match_v0 *info = par->matchinfo;
116
117 ip_set_nfnl_put(info->match_set.index);
118}
119
120static unsigned int
121set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
122{
123 const struct xt_set_info_target_v0 *info = par->targinfo;
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200124 ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
125 info->add_set.u.compat.flags, 0, UINT_MAX);
126 ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
127 info->del_set.u.compat.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100128
129 if (info->add_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200130 ip_set_add(info->add_set.index, skb, par, &add_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100131 if (info->del_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200132 ip_set_del(info->del_set.index, skb, par, &del_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100133
134 return XT_CONTINUE;
135}
136
137static int
138set_target_v0_checkentry(const struct xt_tgchk_param *par)
139{
140 struct xt_set_info_target_v0 *info = par->targinfo;
141 ip_set_id_t index;
142
143 if (info->add_set.index != IPSET_INVALID_ID) {
144 index = ip_set_nfnl_get_byindex(info->add_set.index);
145 if (index == IPSET_INVALID_ID) {
146 pr_warning("Cannot find add_set index %u as target\n",
147 info->add_set.index);
148 return -ENOENT;
149 }
150 }
151
152 if (info->del_set.index != IPSET_INVALID_ID) {
153 index = ip_set_nfnl_get_byindex(info->del_set.index);
154 if (index == IPSET_INVALID_ID) {
155 pr_warning("Cannot find del_set index %u as target\n",
156 info->del_set.index);
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200157 if (info->add_set.index != IPSET_INVALID_ID)
158 ip_set_nfnl_put(info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100159 return -ENOENT;
160 }
161 }
162 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
163 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
164 pr_warning("Protocol error: SET target dimension "
165 "is over the limit!\n");
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200166 if (info->add_set.index != IPSET_INVALID_ID)
167 ip_set_nfnl_put(info->add_set.index);
168 if (info->del_set.index != IPSET_INVALID_ID)
169 ip_set_nfnl_put(info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100170 return -ERANGE;
171 }
172
173 /* Fill out compatibility data */
174 compat_flags(&info->add_set);
175 compat_flags(&info->del_set);
176
177 return 0;
178}
179
180static void
181set_target_v0_destroy(const struct xt_tgdtor_param *par)
182{
183 const struct xt_set_info_target_v0 *info = par->targinfo;
184
185 if (info->add_set.index != IPSET_INVALID_ID)
186 ip_set_nfnl_put(info->add_set.index);
187 if (info->del_set.index != IPSET_INVALID_ID)
188 ip_set_nfnl_put(info->del_set.index);
189}
190
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200191/* Revision 1 match and target */
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100192
193static bool
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200194set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100195{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200196 const struct xt_set_info_match_v1 *info = par->matchinfo;
197 ADT_OPT(opt, par->family, info->match_set.dim,
198 info->match_set.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100199
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200200 return match_set(info->match_set.index, skb, par, &opt,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100201 info->match_set.flags & IPSET_INV_MATCH);
202}
203
204static int
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200205set_match_v1_checkentry(const struct xt_mtchk_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100206{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200207 struct xt_set_info_match_v1 *info = par->matchinfo;
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100208 ip_set_id_t index;
209
210 index = ip_set_nfnl_get_byindex(info->match_set.index);
211
212 if (index == IPSET_INVALID_ID) {
213 pr_warning("Cannot find set indentified by id %u to match\n",
214 info->match_set.index);
215 return -ENOENT;
216 }
217 if (info->match_set.dim > IPSET_DIM_MAX) {
218 pr_warning("Protocol error: set match dimension "
219 "is over the limit!\n");
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200220 ip_set_nfnl_put(info->match_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100221 return -ERANGE;
222 }
223
224 return 0;
225}
226
227static void
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200228set_match_v1_destroy(const struct xt_mtdtor_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100229{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200230 struct xt_set_info_match_v1 *info = par->matchinfo;
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100231
232 ip_set_nfnl_put(info->match_set.index);
233}
234
235static unsigned int
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200236set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100237{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200238 const struct xt_set_info_target_v1 *info = par->targinfo;
239 ADT_OPT(add_opt, par->family, info->add_set.dim,
240 info->add_set.flags, 0, UINT_MAX);
241 ADT_OPT(del_opt, par->family, info->del_set.dim,
242 info->del_set.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100243
244 if (info->add_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200245 ip_set_add(info->add_set.index, skb, par, &add_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100246 if (info->del_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200247 ip_set_del(info->del_set.index, skb, par, &del_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100248
249 return XT_CONTINUE;
250}
251
252static int
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200253set_target_v1_checkentry(const struct xt_tgchk_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100254{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200255 const struct xt_set_info_target_v1 *info = par->targinfo;
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100256 ip_set_id_t index;
257
258 if (info->add_set.index != IPSET_INVALID_ID) {
259 index = ip_set_nfnl_get_byindex(info->add_set.index);
260 if (index == IPSET_INVALID_ID) {
261 pr_warning("Cannot find add_set index %u as target\n",
262 info->add_set.index);
263 return -ENOENT;
264 }
265 }
266
267 if (info->del_set.index != IPSET_INVALID_ID) {
268 index = ip_set_nfnl_get_byindex(info->del_set.index);
269 if (index == IPSET_INVALID_ID) {
270 pr_warning("Cannot find del_set index %u as target\n",
271 info->del_set.index);
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200272 if (info->add_set.index != IPSET_INVALID_ID)
273 ip_set_nfnl_put(info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100274 return -ENOENT;
275 }
276 }
277 if (info->add_set.dim > IPSET_DIM_MAX ||
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200278 info->del_set.dim > IPSET_DIM_MAX) {
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100279 pr_warning("Protocol error: SET target dimension "
280 "is over the limit!\n");
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200281 if (info->add_set.index != IPSET_INVALID_ID)
282 ip_set_nfnl_put(info->add_set.index);
283 if (info->del_set.index != IPSET_INVALID_ID)
284 ip_set_nfnl_put(info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100285 return -ERANGE;
286 }
287
288 return 0;
289}
290
291static void
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200292set_target_v1_destroy(const struct xt_tgdtor_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100293{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200294 const struct xt_set_info_target_v1 *info = par->targinfo;
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100295
296 if (info->add_set.index != IPSET_INVALID_ID)
297 ip_set_nfnl_put(info->add_set.index);
298 if (info->del_set.index != IPSET_INVALID_ID)
299 ip_set_nfnl_put(info->del_set.index);
300}
301
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200302/* Revision 2 target */
303
304static unsigned int
305set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
306{
307 const struct xt_set_info_target_v2 *info = par->targinfo;
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +0000308 ADT_MOPT(add_opt, par->family, info->add_set.dim,
309 info->add_set.flags, info->flags, info->timeout);
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200310 ADT_OPT(del_opt, par->family, info->del_set.dim,
311 info->del_set.flags, 0, UINT_MAX);
312
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +0000313 /* Normalize to fit into jiffies */
Jozsef Kadlecsika73f89a2012-06-29 09:42:28 +0000314 if (add_opt.timeout != IPSET_NO_TIMEOUT &&
315 add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +0000316 add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200317 if (info->add_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200318 ip_set_add(info->add_set.index, skb, par, &add_opt);
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200319 if (info->del_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200320 ip_set_del(info->del_set.index, skb, par, &del_opt);
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200321
322 return XT_CONTINUE;
323}
324
325#define set_target_v2_checkentry set_target_v1_checkentry
326#define set_target_v2_destroy set_target_v1_destroy
327
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100328static struct xt_match set_matches[] __read_mostly = {
329 {
330 .name = "set",
331 .family = NFPROTO_IPV4,
332 .revision = 0,
333 .match = set_match_v0,
334 .matchsize = sizeof(struct xt_set_info_match_v0),
335 .checkentry = set_match_v0_checkentry,
336 .destroy = set_match_v0_destroy,
337 .me = THIS_MODULE
338 },
339 {
340 .name = "set",
341 .family = NFPROTO_IPV4,
342 .revision = 1,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200343 .match = set_match_v1,
344 .matchsize = sizeof(struct xt_set_info_match_v1),
345 .checkentry = set_match_v1_checkentry,
346 .destroy = set_match_v1_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100347 .me = THIS_MODULE
348 },
349 {
350 .name = "set",
351 .family = NFPROTO_IPV6,
352 .revision = 1,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200353 .match = set_match_v1,
354 .matchsize = sizeof(struct xt_set_info_match_v1),
355 .checkentry = set_match_v1_checkentry,
356 .destroy = set_match_v1_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100357 .me = THIS_MODULE
358 },
Jozsef Kadlecsik3e0304a2012-09-21 22:02:36 +0200359 /* --return-nomatch flag support */
360 {
361 .name = "set",
362 .family = NFPROTO_IPV4,
363 .revision = 2,
364 .match = set_match_v1,
365 .matchsize = sizeof(struct xt_set_info_match_v1),
366 .checkentry = set_match_v1_checkentry,
367 .destroy = set_match_v1_destroy,
368 .me = THIS_MODULE
369 },
370 {
371 .name = "set",
372 .family = NFPROTO_IPV6,
373 .revision = 2,
374 .match = set_match_v1,
375 .matchsize = sizeof(struct xt_set_info_match_v1),
376 .checkentry = set_match_v1_checkentry,
377 .destroy = set_match_v1_destroy,
378 .me = THIS_MODULE
379 },
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100380};
381
382static struct xt_target set_targets[] __read_mostly = {
383 {
384 .name = "SET",
385 .revision = 0,
386 .family = NFPROTO_IPV4,
387 .target = set_target_v0,
388 .targetsize = sizeof(struct xt_set_info_target_v0),
389 .checkentry = set_target_v0_checkentry,
390 .destroy = set_target_v0_destroy,
391 .me = THIS_MODULE
392 },
393 {
394 .name = "SET",
395 .revision = 1,
396 .family = NFPROTO_IPV4,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200397 .target = set_target_v1,
398 .targetsize = sizeof(struct xt_set_info_target_v1),
399 .checkentry = set_target_v1_checkentry,
400 .destroy = set_target_v1_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100401 .me = THIS_MODULE
402 },
403 {
404 .name = "SET",
405 .revision = 1,
406 .family = NFPROTO_IPV6,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200407 .target = set_target_v1,
408 .targetsize = sizeof(struct xt_set_info_target_v1),
409 .checkentry = set_target_v1_checkentry,
410 .destroy = set_target_v1_destroy,
411 .me = THIS_MODULE
412 },
Jozsef Kadlecsik3e0304a2012-09-21 22:02:36 +0200413 /* --timeout and --exist flags support */
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200414 {
415 .name = "SET",
416 .revision = 2,
417 .family = NFPROTO_IPV4,
418 .target = set_target_v2,
419 .targetsize = sizeof(struct xt_set_info_target_v2),
420 .checkentry = set_target_v2_checkentry,
421 .destroy = set_target_v2_destroy,
422 .me = THIS_MODULE
423 },
424 {
425 .name = "SET",
426 .revision = 2,
427 .family = NFPROTO_IPV6,
428 .target = set_target_v2,
429 .targetsize = sizeof(struct xt_set_info_target_v2),
430 .checkentry = set_target_v2_checkentry,
431 .destroy = set_target_v2_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100432 .me = THIS_MODULE
433 },
434};
435
436static int __init xt_set_init(void)
437{
438 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
439
440 if (!ret) {
441 ret = xt_register_targets(set_targets,
442 ARRAY_SIZE(set_targets));
443 if (ret)
444 xt_unregister_matches(set_matches,
445 ARRAY_SIZE(set_matches));
446 }
447 return ret;
448}
449
450static void __exit xt_set_fini(void)
451{
452 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
453 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
454}
455
456module_init(xt_set_init);
457module_exit(xt_set_fini);