blob: b103e96277169becaa78eeec6ee3c53a2083a307 [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>
Jozsef Kadlecsik075e64c2013-04-27 14:28:55 +02004 * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +01005 *
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>
Jozsef Kadlecsika9756e62015-05-02 19:28:23 +020018#include <linux/netfilter/ipset/ip_set.h>
Jozsef Kadlecsika73f89a2012-06-29 09:42:28 +000019#include <linux/netfilter/ipset/ip_set_timeout.h>
Jozsef Kadlecsika9756e62015-05-02 19:28:23 +020020#include <uapi/linux/netfilter/xt_set.h>
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010021
22MODULE_LICENSE("GPL");
23MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
24MODULE_DESCRIPTION("Xtables: IP set match and target module");
25MODULE_ALIAS("xt_SET");
26MODULE_ALIAS("ipt_set");
27MODULE_ALIAS("ip6t_set");
28MODULE_ALIAS("ipt_SET");
29MODULE_ALIAS("ip6t_SET");
30
31static inline int
32match_set(ip_set_id_t index, const struct sk_buff *skb,
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020033 const struct xt_action_param *par,
Jozsef Kadlecsik075e64c2013-04-27 14:28:55 +020034 struct ip_set_adt_opt *opt, int inv)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010035{
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020036 if (ip_set_test(index, skb, par, opt))
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010037 inv = !inv;
38 return inv;
39}
40
Jozsef Kadlecsik15b4d932011-06-16 19:01:26 +020041#define ADT_OPT(n, f, d, fs, cfs, t) \
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +000042struct ip_set_adt_opt n = { \
43 .family = f, \
44 .dim = d, \
45 .flags = fs, \
46 .cmdflags = cfs, \
Jozsef Kadlecsik075e64c2013-04-27 14:28:55 +020047 .ext.timeout = t, \
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +000048}
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020049
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010050/* Revision 0 interface: backward compatible with netfilter/iptables */
51
52static bool
53set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
54{
55 const struct xt_set_info_match_v0 *info = par->matchinfo;
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020056 ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
57 info->match_set.u.compat.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010058
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020059 return match_set(info->match_set.index, skb, par, &opt,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010060 info->match_set.u.compat.flags & IPSET_INV_MATCH);
61}
62
63static void
64compat_flags(struct xt_set_info_v0 *info)
65{
66 u_int8_t i;
67
68 /* Fill out compatibility data according to enum ip_set_kopt */
69 info->u.compat.dim = IPSET_DIM_ZERO;
70 if (info->u.flags[0] & IPSET_MATCH_INV)
71 info->u.compat.flags |= IPSET_INV_MATCH;
72 for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
73 info->u.compat.dim++;
74 if (info->u.flags[i] & IPSET_SRC)
75 info->u.compat.flags |= (1<<info->u.compat.dim);
76 }
77}
78
79static int
80set_match_v0_checkentry(const struct xt_mtchk_param *par)
81{
82 struct xt_set_info_match_v0 *info = par->matchinfo;
83 ip_set_id_t index;
84
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +020085 index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010086
87 if (index == IPSET_INVALID_ID) {
Joe Perchesb167a372014-09-09 21:17:32 -070088 pr_warn("Cannot find set identified by id %u to match\n",
89 info->match_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010090 return -ENOENT;
91 }
92 if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
Joe Perchesb167a372014-09-09 21:17:32 -070093 pr_warn("Protocol error: set match dimension is over the limit!\n");
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +020094 ip_set_nfnl_put(par->net, info->match_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +010095 return -ERANGE;
96 }
97
98 /* Fill out compatibility data */
99 compat_flags(&info->match_set);
100
101 return 0;
102}
103
104static void
105set_match_v0_destroy(const struct xt_mtdtor_param *par)
106{
107 struct xt_set_info_match_v0 *info = par->matchinfo;
108
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200109 ip_set_nfnl_put(par->net, info->match_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100110}
111
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200112/* Revision 1 match */
113
114static bool
115set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
116{
117 const struct xt_set_info_match_v1 *info = par->matchinfo;
118 ADT_OPT(opt, par->family, info->match_set.dim,
119 info->match_set.flags, 0, UINT_MAX);
120
121 if (opt.flags & IPSET_RETURN_NOMATCH)
122 opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
123
124 return match_set(info->match_set.index, skb, par, &opt,
125 info->match_set.flags & IPSET_INV_MATCH);
126}
127
128static int
129set_match_v1_checkentry(const struct xt_mtchk_param *par)
130{
131 struct xt_set_info_match_v1 *info = par->matchinfo;
132 ip_set_id_t index;
133
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200134 index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200135
136 if (index == IPSET_INVALID_ID) {
Joe Perchesb167a372014-09-09 21:17:32 -0700137 pr_warn("Cannot find set identified by id %u to match\n",
138 info->match_set.index);
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200139 return -ENOENT;
140 }
141 if (info->match_set.dim > IPSET_DIM_MAX) {
Joe Perchesb167a372014-09-09 21:17:32 -0700142 pr_warn("Protocol error: set match dimension is over the limit!\n");
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200143 ip_set_nfnl_put(par->net, info->match_set.index);
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200144 return -ERANGE;
145 }
146
147 return 0;
148}
149
150static void
151set_match_v1_destroy(const struct xt_mtdtor_param *par)
152{
153 struct xt_set_info_match_v1 *info = par->matchinfo;
154
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200155 ip_set_nfnl_put(par->net, info->match_set.index);
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200156}
157
158/* Revision 3 match */
159
160static bool
Jozsef Kadlecsika51b9192014-11-30 19:56:53 +0100161match_counter0(u64 counter, const struct ip_set_counter_match0 *info)
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200162{
163 switch (info->op) {
164 case IPSET_COUNTER_NONE:
165 return true;
166 case IPSET_COUNTER_EQ:
167 return counter == info->value;
168 case IPSET_COUNTER_NE:
169 return counter != info->value;
170 case IPSET_COUNTER_LT:
171 return counter < info->value;
172 case IPSET_COUNTER_GT:
173 return counter > info->value;
174 }
175 return false;
176}
177
178static bool
179set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
180{
181 const struct xt_set_info_match_v3 *info = par->matchinfo;
182 ADT_OPT(opt, par->family, info->match_set.dim,
183 info->match_set.flags, info->flags, UINT_MAX);
184 int ret;
185
186 if (info->packets.op != IPSET_COUNTER_NONE ||
187 info->bytes.op != IPSET_COUNTER_NONE)
188 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
189
190 ret = match_set(info->match_set.index, skb, par, &opt,
191 info->match_set.flags & IPSET_INV_MATCH);
192
193 if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
194 return ret;
195
Jozsef Kadlecsika51b9192014-11-30 19:56:53 +0100196 if (!match_counter0(opt.ext.packets, &info->packets))
Wu Fengguang7f73b9f2015-02-11 20:33:05 +0800197 return false;
Jozsef Kadlecsika51b9192014-11-30 19:56:53 +0100198 return match_counter0(opt.ext.bytes, &info->bytes);
199}
200
201#define set_match_v3_checkentry set_match_v1_checkentry
202#define set_match_v3_destroy set_match_v1_destroy
203
204/* Revision 4 match */
205
206static bool
207match_counter(u64 counter, const struct ip_set_counter_match *info)
208{
209 switch (info->op) {
210 case IPSET_COUNTER_NONE:
211 return true;
212 case IPSET_COUNTER_EQ:
213 return counter == info->value;
214 case IPSET_COUNTER_NE:
215 return counter != info->value;
216 case IPSET_COUNTER_LT:
217 return counter < info->value;
218 case IPSET_COUNTER_GT:
219 return counter > info->value;
220 }
221 return false;
222}
223
224static bool
225set_match_v4(const struct sk_buff *skb, struct xt_action_param *par)
226{
227 const struct xt_set_info_match_v4 *info = par->matchinfo;
228 ADT_OPT(opt, par->family, info->match_set.dim,
229 info->match_set.flags, info->flags, UINT_MAX);
230 int ret;
231
232 if (info->packets.op != IPSET_COUNTER_NONE ||
233 info->bytes.op != IPSET_COUNTER_NONE)
234 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
235
236 ret = match_set(info->match_set.index, skb, par, &opt,
237 info->match_set.flags & IPSET_INV_MATCH);
238
239 if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
240 return ret;
241
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200242 if (!match_counter(opt.ext.packets, &info->packets))
Wu Fengguang7f73b9f2015-02-11 20:33:05 +0800243 return false;
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200244 return match_counter(opt.ext.bytes, &info->bytes);
245}
246
Jozsef Kadlecsika51b9192014-11-30 19:56:53 +0100247#define set_match_v4_checkentry set_match_v1_checkentry
248#define set_match_v4_destroy set_match_v1_destroy
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200249
250/* Revision 0 interface: backward compatible with netfilter/iptables */
251
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100252static unsigned int
253set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
254{
255 const struct xt_set_info_target_v0 *info = par->targinfo;
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200256 ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
257 info->add_set.u.compat.flags, 0, UINT_MAX);
258 ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
259 info->del_set.u.compat.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100260
261 if (info->add_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200262 ip_set_add(info->add_set.index, skb, par, &add_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100263 if (info->del_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200264 ip_set_del(info->del_set.index, skb, par, &del_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100265
266 return XT_CONTINUE;
267}
268
269static int
270set_target_v0_checkentry(const struct xt_tgchk_param *par)
271{
272 struct xt_set_info_target_v0 *info = par->targinfo;
273 ip_set_id_t index;
274
275 if (info->add_set.index != IPSET_INVALID_ID) {
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200276 index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100277 if (index == IPSET_INVALID_ID) {
Joe Perchesb167a372014-09-09 21:17:32 -0700278 pr_warn("Cannot find add_set index %u as target\n",
279 info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100280 return -ENOENT;
281 }
282 }
283
284 if (info->del_set.index != IPSET_INVALID_ID) {
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200285 index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100286 if (index == IPSET_INVALID_ID) {
Joe Perchesb167a372014-09-09 21:17:32 -0700287 pr_warn("Cannot find del_set index %u as target\n",
288 info->del_set.index);
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200289 if (info->add_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200290 ip_set_nfnl_put(par->net, info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100291 return -ENOENT;
292 }
293 }
294 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
295 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
Joe Perchesb167a372014-09-09 21:17:32 -0700296 pr_warn("Protocol error: SET target dimension is over the limit!\n");
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200297 if (info->add_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200298 ip_set_nfnl_put(par->net, info->add_set.index);
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200299 if (info->del_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200300 ip_set_nfnl_put(par->net, info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100301 return -ERANGE;
302 }
303
304 /* Fill out compatibility data */
305 compat_flags(&info->add_set);
306 compat_flags(&info->del_set);
307
308 return 0;
309}
310
311static void
312set_target_v0_destroy(const struct xt_tgdtor_param *par)
313{
314 const struct xt_set_info_target_v0 *info = par->targinfo;
315
316 if (info->add_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200317 ip_set_nfnl_put(par->net, info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100318 if (info->del_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200319 ip_set_nfnl_put(par->net, info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100320}
321
Jozsef Kadlecsikbd3129f2013-09-30 07:49:47 +0200322/* Revision 1 target */
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100323
324static unsigned int
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200325set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100326{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200327 const struct xt_set_info_target_v1 *info = par->targinfo;
328 ADT_OPT(add_opt, par->family, info->add_set.dim,
329 info->add_set.flags, 0, UINT_MAX);
330 ADT_OPT(del_opt, par->family, info->del_set.dim,
331 info->del_set.flags, 0, UINT_MAX);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100332
333 if (info->add_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200334 ip_set_add(info->add_set.index, skb, par, &add_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100335 if (info->del_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200336 ip_set_del(info->del_set.index, skb, par, &del_opt);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100337
338 return XT_CONTINUE;
339}
340
341static int
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200342set_target_v1_checkentry(const struct xt_tgchk_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100343{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200344 const struct xt_set_info_target_v1 *info = par->targinfo;
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100345 ip_set_id_t index;
346
347 if (info->add_set.index != IPSET_INVALID_ID) {
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200348 index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100349 if (index == IPSET_INVALID_ID) {
Joe Perchesb167a372014-09-09 21:17:32 -0700350 pr_warn("Cannot find add_set index %u as target\n",
351 info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100352 return -ENOENT;
353 }
354 }
355
356 if (info->del_set.index != IPSET_INVALID_ID) {
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200357 index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100358 if (index == IPSET_INVALID_ID) {
Joe Perchesb167a372014-09-09 21:17:32 -0700359 pr_warn("Cannot find del_set index %u as target\n",
360 info->del_set.index);
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200361 if (info->add_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200362 ip_set_nfnl_put(par->net, info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100363 return -ENOENT;
364 }
365 }
366 if (info->add_set.dim > IPSET_DIM_MAX ||
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200367 info->del_set.dim > IPSET_DIM_MAX) {
Joe Perchesb167a372014-09-09 21:17:32 -0700368 pr_warn("Protocol error: SET target dimension is over the limit!\n");
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200369 if (info->add_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200370 ip_set_nfnl_put(par->net, info->add_set.index);
Jozsef Kadlecsikeafbd3f2011-04-13 13:45:57 +0200371 if (info->del_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200372 ip_set_nfnl_put(par->net, info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100373 return -ERANGE;
374 }
375
376 return 0;
377}
378
379static void
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200380set_target_v1_destroy(const struct xt_tgdtor_param *par)
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100381{
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200382 const struct xt_set_info_target_v1 *info = par->targinfo;
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100383
384 if (info->add_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200385 ip_set_nfnl_put(par->net, info->add_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100386 if (info->del_set.index != IPSET_INVALID_ID)
Vitaly Lavrov1785e8f2013-09-30 17:07:02 +0200387 ip_set_nfnl_put(par->net, info->del_set.index);
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100388}
389
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200390/* Revision 2 target */
391
392static unsigned int
393set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
394{
395 const struct xt_set_info_target_v2 *info = par->targinfo;
Jozsef Kadlecsik075e64c2013-04-27 14:28:55 +0200396 ADT_OPT(add_opt, par->family, info->add_set.dim,
397 info->add_set.flags, info->flags, info->timeout);
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200398 ADT_OPT(del_opt, par->family, info->del_set.dim,
399 info->del_set.flags, 0, UINT_MAX);
400
Jozsef Kadlecsik127f5592012-05-07 02:35:44 +0000401 /* Normalize to fit into jiffies */
Jozsef Kadlecsik075e64c2013-04-27 14:28:55 +0200402 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
403 add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
404 add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200405 if (info->add_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200406 ip_set_add(info->add_set.index, skb, par, &add_opt);
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200407 if (info->del_set.index != IPSET_INVALID_ID)
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200408 ip_set_del(info->del_set.index, skb, par, &del_opt);
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200409
410 return XT_CONTINUE;
411}
412
413#define set_target_v2_checkentry set_target_v1_checkentry
414#define set_target_v2_destroy set_target_v1_destroy
415
Anton Danilov76cea412014-09-02 14:21:20 +0400416/* Revision 3 target */
417
418static unsigned int
419set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
420{
421 const struct xt_set_info_target_v3 *info = par->targinfo;
422 ADT_OPT(add_opt, par->family, info->add_set.dim,
423 info->add_set.flags, info->flags, info->timeout);
424 ADT_OPT(del_opt, par->family, info->del_set.dim,
425 info->del_set.flags, 0, UINT_MAX);
426 ADT_OPT(map_opt, par->family, info->map_set.dim,
427 info->map_set.flags, 0, UINT_MAX);
428
429 int ret;
430
431 /* Normalize to fit into jiffies */
432 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
433 add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
434 add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
435 if (info->add_set.index != IPSET_INVALID_ID)
436 ip_set_add(info->add_set.index, skb, par, &add_opt);
437 if (info->del_set.index != IPSET_INVALID_ID)
438 ip_set_del(info->del_set.index, skb, par, &del_opt);
439 if (info->map_set.index != IPSET_INVALID_ID) {
440 map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |
441 IPSET_FLAG_MAP_SKBPRIO |
442 IPSET_FLAG_MAP_SKBQUEUE);
443 ret = match_set(info->map_set.index, skb, par, &map_opt,
444 info->map_set.flags & IPSET_INV_MATCH);
445 if (!ret)
446 return XT_CONTINUE;
447 if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)
448 skb->mark = (skb->mark & ~(map_opt.ext.skbmarkmask))
449 ^ (map_opt.ext.skbmark);
450 if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)
451 skb->priority = map_opt.ext.skbprio;
452 if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&
453 skb->dev &&
454 skb->dev->real_num_tx_queues > map_opt.ext.skbqueue)
455 skb_set_queue_mapping(skb, map_opt.ext.skbqueue);
456 }
457 return XT_CONTINUE;
458}
459
460
461static int
462set_target_v3_checkentry(const struct xt_tgchk_param *par)
463{
464 const struct xt_set_info_target_v3 *info = par->targinfo;
465 ip_set_id_t index;
466
467 if (info->add_set.index != IPSET_INVALID_ID) {
468 index = ip_set_nfnl_get_byindex(par->net,
469 info->add_set.index);
470 if (index == IPSET_INVALID_ID) {
471 pr_warn("Cannot find add_set index %u as target\n",
472 info->add_set.index);
473 return -ENOENT;
474 }
475 }
476
477 if (info->del_set.index != IPSET_INVALID_ID) {
478 index = ip_set_nfnl_get_byindex(par->net,
479 info->del_set.index);
480 if (index == IPSET_INVALID_ID) {
481 pr_warn("Cannot find del_set index %u as target\n",
482 info->del_set.index);
483 if (info->add_set.index != IPSET_INVALID_ID)
484 ip_set_nfnl_put(par->net,
485 info->add_set.index);
486 return -ENOENT;
487 }
488 }
489
490 if (info->map_set.index != IPSET_INVALID_ID) {
491 if (strncmp(par->table, "mangle", 7)) {
492 pr_warn("--map-set only usable from mangle table\n");
493 return -EINVAL;
494 }
495 if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
496 (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
497 !(par->hook_mask & (1 << NF_INET_FORWARD |
498 1 << NF_INET_LOCAL_OUT |
499 1 << NF_INET_POST_ROUTING))) {
500 pr_warn("mapping of prio or/and queue is allowed only"
501 "from OUTPUT/FORWARD/POSTROUTING chains\n");
502 return -EINVAL;
503 }
504 index = ip_set_nfnl_get_byindex(par->net,
505 info->map_set.index);
506 if (index == IPSET_INVALID_ID) {
507 pr_warn("Cannot find map_set index %u as target\n",
508 info->map_set.index);
509 if (info->add_set.index != IPSET_INVALID_ID)
510 ip_set_nfnl_put(par->net,
511 info->add_set.index);
512 if (info->del_set.index != IPSET_INVALID_ID)
513 ip_set_nfnl_put(par->net,
514 info->del_set.index);
515 return -ENOENT;
516 }
517 }
518
519 if (info->add_set.dim > IPSET_DIM_MAX ||
520 info->del_set.dim > IPSET_DIM_MAX ||
521 info->map_set.dim > IPSET_DIM_MAX) {
522 pr_warn("Protocol error: SET target dimension "
523 "is over the limit!\n");
524 if (info->add_set.index != IPSET_INVALID_ID)
525 ip_set_nfnl_put(par->net, info->add_set.index);
526 if (info->del_set.index != IPSET_INVALID_ID)
527 ip_set_nfnl_put(par->net, info->del_set.index);
528 if (info->map_set.index != IPSET_INVALID_ID)
529 ip_set_nfnl_put(par->net, info->map_set.index);
530 return -ERANGE;
531 }
532
533 return 0;
534}
535
536static void
537set_target_v3_destroy(const struct xt_tgdtor_param *par)
538{
539 const struct xt_set_info_target_v3 *info = par->targinfo;
540
541 if (info->add_set.index != IPSET_INVALID_ID)
542 ip_set_nfnl_put(par->net, info->add_set.index);
543 if (info->del_set.index != IPSET_INVALID_ID)
544 ip_set_nfnl_put(par->net, info->del_set.index);
545 if (info->map_set.index != IPSET_INVALID_ID)
546 ip_set_nfnl_put(par->net, info->map_set.index);
547}
548
549
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100550static struct xt_match set_matches[] __read_mostly = {
551 {
552 .name = "set",
553 .family = NFPROTO_IPV4,
554 .revision = 0,
555 .match = set_match_v0,
556 .matchsize = sizeof(struct xt_set_info_match_v0),
557 .checkentry = set_match_v0_checkentry,
558 .destroy = set_match_v0_destroy,
559 .me = THIS_MODULE
560 },
561 {
562 .name = "set",
563 .family = NFPROTO_IPV4,
564 .revision = 1,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200565 .match = set_match_v1,
566 .matchsize = sizeof(struct xt_set_info_match_v1),
567 .checkentry = set_match_v1_checkentry,
568 .destroy = set_match_v1_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100569 .me = THIS_MODULE
570 },
571 {
572 .name = "set",
573 .family = NFPROTO_IPV6,
574 .revision = 1,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200575 .match = set_match_v1,
576 .matchsize = sizeof(struct xt_set_info_match_v1),
577 .checkentry = set_match_v1_checkentry,
578 .destroy = set_match_v1_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100579 .me = THIS_MODULE
580 },
Jozsef Kadlecsik3e0304a2012-09-21 22:02:36 +0200581 /* --return-nomatch flag support */
582 {
583 .name = "set",
584 .family = NFPROTO_IPV4,
585 .revision = 2,
586 .match = set_match_v1,
587 .matchsize = sizeof(struct xt_set_info_match_v1),
588 .checkentry = set_match_v1_checkentry,
589 .destroy = set_match_v1_destroy,
590 .me = THIS_MODULE
591 },
592 {
593 .name = "set",
594 .family = NFPROTO_IPV6,
595 .revision = 2,
596 .match = set_match_v1,
597 .matchsize = sizeof(struct xt_set_info_match_v1),
598 .checkentry = set_match_v1_checkentry,
599 .destroy = set_match_v1_destroy,
600 .me = THIS_MODULE
601 },
Jozsef Kadlecsik6e017812013-04-27 14:40:50 +0200602 /* counters support: update, match */
603 {
604 .name = "set",
605 .family = NFPROTO_IPV4,
606 .revision = 3,
607 .match = set_match_v3,
608 .matchsize = sizeof(struct xt_set_info_match_v3),
609 .checkentry = set_match_v3_checkentry,
610 .destroy = set_match_v3_destroy,
611 .me = THIS_MODULE
612 },
613 {
614 .name = "set",
615 .family = NFPROTO_IPV6,
616 .revision = 3,
617 .match = set_match_v3,
618 .matchsize = sizeof(struct xt_set_info_match_v3),
619 .checkentry = set_match_v3_checkentry,
620 .destroy = set_match_v3_destroy,
621 .me = THIS_MODULE
622 },
Jozsef Kadlecsika51b9192014-11-30 19:56:53 +0100623 /* new revision for counters support: update, match */
624 {
625 .name = "set",
626 .family = NFPROTO_IPV4,
627 .revision = 4,
628 .match = set_match_v4,
629 .matchsize = sizeof(struct xt_set_info_match_v4),
630 .checkentry = set_match_v4_checkentry,
631 .destroy = set_match_v4_destroy,
632 .me = THIS_MODULE
633 },
634 {
635 .name = "set",
636 .family = NFPROTO_IPV6,
637 .revision = 4,
638 .match = set_match_v4,
639 .matchsize = sizeof(struct xt_set_info_match_v4),
640 .checkentry = set_match_v4_checkentry,
641 .destroy = set_match_v4_destroy,
642 .me = THIS_MODULE
643 },
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100644};
645
646static struct xt_target set_targets[] __read_mostly = {
647 {
648 .name = "SET",
649 .revision = 0,
650 .family = NFPROTO_IPV4,
651 .target = set_target_v0,
652 .targetsize = sizeof(struct xt_set_info_target_v0),
653 .checkentry = set_target_v0_checkentry,
654 .destroy = set_target_v0_destroy,
655 .me = THIS_MODULE
656 },
657 {
658 .name = "SET",
659 .revision = 1,
660 .family = NFPROTO_IPV4,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200661 .target = set_target_v1,
662 .targetsize = sizeof(struct xt_set_info_target_v1),
663 .checkentry = set_target_v1_checkentry,
664 .destroy = set_target_v1_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100665 .me = THIS_MODULE
666 },
667 {
668 .name = "SET",
669 .revision = 1,
670 .family = NFPROTO_IPV6,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200671 .target = set_target_v1,
672 .targetsize = sizeof(struct xt_set_info_target_v1),
673 .checkentry = set_target_v1_checkentry,
674 .destroy = set_target_v1_destroy,
675 .me = THIS_MODULE
676 },
Jozsef Kadlecsik3e0304a2012-09-21 22:02:36 +0200677 /* --timeout and --exist flags support */
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200678 {
679 .name = "SET",
680 .revision = 2,
681 .family = NFPROTO_IPV4,
682 .target = set_target_v2,
683 .targetsize = sizeof(struct xt_set_info_target_v2),
684 .checkentry = set_target_v2_checkentry,
685 .destroy = set_target_v2_destroy,
686 .me = THIS_MODULE
687 },
688 {
689 .name = "SET",
690 .revision = 2,
691 .family = NFPROTO_IPV6,
692 .target = set_target_v2,
693 .targetsize = sizeof(struct xt_set_info_target_v2),
694 .checkentry = set_target_v2_checkentry,
695 .destroy = set_target_v2_destroy,
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100696 .me = THIS_MODULE
697 },
Anton Danilov76cea412014-09-02 14:21:20 +0400698 /* --map-set support */
699 {
700 .name = "SET",
701 .revision = 3,
702 .family = NFPROTO_IPV4,
703 .target = set_target_v3,
704 .targetsize = sizeof(struct xt_set_info_target_v3),
705 .checkentry = set_target_v3_checkentry,
706 .destroy = set_target_v3_destroy,
707 .me = THIS_MODULE
708 },
709 {
710 .name = "SET",
711 .revision = 3,
712 .family = NFPROTO_IPV6,
713 .target = set_target_v3,
714 .targetsize = sizeof(struct xt_set_info_target_v3),
715 .checkentry = set_target_v3_checkentry,
716 .destroy = set_target_v3_destroy,
717 .me = THIS_MODULE
718 },
Jozsef Kadlecsikd9567982011-02-01 15:56:00 +0100719};
720
721static int __init xt_set_init(void)
722{
723 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
724
725 if (!ret) {
726 ret = xt_register_targets(set_targets,
727 ARRAY_SIZE(set_targets));
728 if (ret)
729 xt_unregister_matches(set_matches,
730 ARRAY_SIZE(set_matches));
731 }
732 return ret;
733}
734
735static void __exit xt_set_fini(void)
736{
737 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
738 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
739}
740
741module_init(xt_set_init);
742module_exit(xt_set_fini);