| 1 | |
| 2 | |
| 3 | * Patrick Schaaf <[email protected]>
|
| 4 | * Martin Josefsson <[email protected]>
|
| 5 | * Copyright (C) 2003-2013 Jozsef Kadlecsik <[email protected]>
|
| 6 |
|
| 7 |
|
| 8 | |
| 9 | * for netfilter/iptables.
|
| 10 |
|
| 11 |
|
| 12 | #include <linux/module.h>
|
| 13 | #include <linux/skbuff.h>
|
| 14 |
|
| 15 | #include <linux/netfilter/x_tables.h>
|
| 16 | #include <linux/netfilter/ipset/ip_set.h>
|
| 17 | #include <uapi/linux/netfilter/xt_set.h>
|
| 18 |
|
| 19 | MODULE_LICENSE("GPL");
|
| 20 | MODULE_AUTHOR("Jozsef Kadlecsik <[email protected]>");
|
| 21 | MODULE_DESCRIPTION("Xtables: IP set match and target module");
|
| 22 | MODULE_ALIAS("xt_SET");
|
| 23 | MODULE_ALIAS("ipt_set");
|
| 24 | MODULE_ALIAS("ip6t_set");
|
| 25 | MODULE_ALIAS("ipt_SET");
|
| 26 | MODULE_ALIAS("ip6t_SET");
|
| 27 |
|
| 28 | static inline int
|
| 29 | match_set(ip_set_id_t index, const struct sk_buff *skb,
|
| 30 | const struct xt_action_param *par,
|
| 31 | struct ip_set_adt_opt *opt, int inv)
|
| 32 | {
|
| 33 | if (ip_set_test(index, skb, par, opt))
|
| 34 | inv = !inv;
|
| 35 | return inv;
|
| 36 | }
|
| 37 |
|
| 38 | #define ADT_OPT(n, f, d, fs, cfs, t, p, b, po, bo) \
|
| 39 | struct ip_set_adt_opt n = { \
|
| 40 | .family = f, \
|
| 41 | .dim = d, \
|
| 42 | .flags = fs, \
|
| 43 | .cmdflags = cfs, \
|
| 44 | .ext.timeout = t, \
|
| 45 | .ext.packets = p, \
|
| 46 | .ext.bytes = b, \
|
| 47 | .ext.packets_op = po, \
|
| 48 | .ext.bytes_op = bo, \
|
| 49 | }
|
| 50 |
|
| 51 |
|
| 52 |
|
| 53 | static bool
|
| 54 | set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
|
| 55 | {
|
| 56 | const struct xt_set_info_match_v0 *info = par->matchinfo;
|
| 57 |
|
| 58 | ADT_OPT(opt, xt_family(par), info->match_set.u.compat.dim,
|
| 59 | info->match_set.u.compat.flags, 0, UINT_MAX,
|
| 60 | 0, 0, 0, 0);
|
| 61 |
|
| 62 | return match_set(info->match_set.index, skb, par, &opt,
|
| 63 | info->match_set.u.compat.flags & IPSET_INV_MATCH);
|
| 64 | }
|
| 65 |
|
| 66 | static void
|
| 67 | compat_flags(struct xt_set_info_v0 *info)
|
| 68 | {
|
| 69 | u_int8_t i;
|
| 70 |
|
| 71 |
|
| 72 | info->u.compat.dim = IPSET_DIM_ZERO;
|
| 73 | if (info->u.flags[0] & IPSET_MATCH_INV)
|
| 74 | info->u.compat.flags |= IPSET_INV_MATCH;
|
| 75 | for (i = 0; i < IPSET_DIM_MAX - 1 && info->u.flags[i]; i++) {
|
| 76 | info->u.compat.dim++;
|
| 77 | if (info->u.flags[i] & IPSET_SRC)
|
| 78 | info->u.compat.flags |= (1 << info->u.compat.dim);
|
| 79 | }
|
| 80 | }
|
| 81 |
|
| 82 | static int
|
| 83 | set_match_v0_checkentry(const struct xt_mtchk_param *par)
|
| 84 | {
|
| 85 | struct xt_set_info_match_v0 *info = par->matchinfo;
|
| 86 | ip_set_id_t index;
|
| 87 |
|
| 88 | index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
|
| 89 |
|
| 90 | if (index == IPSET_INVALID_ID) {
|
| 91 | pr_info_ratelimited("Cannot find set identified by id %u to match\n",
|
| 92 | info->match_set.index);
|
| 93 | return -ENOENT;
|
| 94 | }
|
| 95 | if (info->match_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
|
| 96 | pr_info_ratelimited("set match dimension is over the limit!\n");
|
| 97 | ip_set_nfnl_put(par->net, info->match_set.index);
|
| 98 | return -ERANGE;
|
| 99 | }
|
| 100 |
|
| 101 |
|
| 102 | compat_flags(&info->match_set);
|
| 103 |
|
| 104 | return 0;
|
| 105 | }
|
| 106 |
|
| 107 | static void
|
| 108 | set_match_v0_destroy(const struct xt_mtdtor_param *par)
|
| 109 | {
|
| 110 | struct xt_set_info_match_v0 *info = par->matchinfo;
|
| 111 |
|
| 112 | ip_set_nfnl_put(par->net, info->match_set.index);
|
| 113 | }
|
| 114 |
|
| 115 |
|
| 116 |
|
| 117 | static bool
|
| 118 | set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
|
| 119 | {
|
| 120 | const struct xt_set_info_match_v1 *info = par->matchinfo;
|
| 121 |
|
| 122 | ADT_OPT(opt, xt_family(par), info->match_set.dim,
|
| 123 | info->match_set.flags, 0, UINT_MAX,
|
| 124 | 0, 0, 0, 0);
|
| 125 |
|
| 126 | if (opt.flags & IPSET_RETURN_NOMATCH)
|
| 127 | opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
|
| 128 |
|
| 129 | return match_set(info->match_set.index, skb, par, &opt,
|
| 130 | info->match_set.flags & IPSET_INV_MATCH);
|
| 131 | }
|
| 132 |
|
| 133 | static int
|
| 134 | set_match_v1_checkentry(const struct xt_mtchk_param *par)
|
| 135 | {
|
| 136 | struct xt_set_info_match_v1 *info = par->matchinfo;
|
| 137 | ip_set_id_t index;
|
| 138 |
|
| 139 | index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
|
| 140 |
|
| 141 | if (index == IPSET_INVALID_ID) {
|
| 142 | pr_info_ratelimited("Cannot find set identified by id %u to match\n",
|
| 143 | info->match_set.index);
|
| 144 | return -ENOENT;
|
| 145 | }
|
| 146 | if (info->match_set.dim > IPSET_DIM_MAX) {
|
| 147 | pr_info_ratelimited("set match dimension is over the limit!\n");
|
| 148 | ip_set_nfnl_put(par->net, info->match_set.index);
|
| 149 | return -ERANGE;
|
| 150 | }
|
| 151 |
|
| 152 | return 0;
|
| 153 | }
|
| 154 |
|
| 155 | static void
|
| 156 | set_match_v1_destroy(const struct xt_mtdtor_param *par)
|
| 157 | {
|
| 158 | struct xt_set_info_match_v1 *info = par->matchinfo;
|
| 159 |
|
| 160 | ip_set_nfnl_put(par->net, info->match_set.index);
|
| 161 | }
|
| 162 |
|
| 163 |
|
| 164 |
|
| 165 | static bool
|
| 166 | set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
|
| 167 | {
|
| 168 | const struct xt_set_info_match_v3 *info = par->matchinfo;
|
| 169 |
|
| 170 | ADT_OPT(opt, xt_family(par), info->match_set.dim,
|
| 171 | info->match_set.flags, info->flags, UINT_MAX,
|
| 172 | info->packets.value, info->bytes.value,
|
| 173 | info->packets.op, info->bytes.op);
|
| 174 |
|
| 175 | if (info->packets.op != IPSET_COUNTER_NONE ||
|
| 176 | info->bytes.op != IPSET_COUNTER_NONE)
|
| 177 | opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
|
| 178 |
|
| 179 | return match_set(info->match_set.index, skb, par, &opt,
|
| 180 | info->match_set.flags & IPSET_INV_MATCH);
|
| 181 | }
|
| 182 |
|
| 183 | #define set_match_v3_checkentry set_match_v1_checkentry
|
| 184 | #define set_match_v3_destroy set_match_v1_destroy
|
| 185 |
|
| 186 |
|
| 187 |
|
| 188 | static bool
|
| 189 | set_match_v4(const struct sk_buff *skb, struct xt_action_param *par)
|
| 190 | {
|
| 191 | const struct xt_set_info_match_v4 *info = par->matchinfo;
|
| 192 |
|
| 193 | ADT_OPT(opt, xt_family(par), info->match_set.dim,
|
| 194 | info->match_set.flags, info->flags, UINT_MAX,
|
| 195 | info->packets.value, info->bytes.value,
|
| 196 | info->packets.op, info->bytes.op);
|
| 197 |
|
| 198 | if (info->packets.op != IPSET_COUNTER_NONE ||
|
| 199 | info->bytes.op != IPSET_COUNTER_NONE)
|
| 200 | opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
|
| 201 |
|
| 202 | return match_set(info->match_set.index, skb, par, &opt,
|
| 203 | info->match_set.flags & IPSET_INV_MATCH);
|
| 204 | }
|
| 205 |
|
| 206 | #define set_match_v4_checkentry set_match_v1_checkentry
|
| 207 | #define set_match_v4_destroy set_match_v1_destroy
|
| 208 |
|
| 209 |
|
| 210 |
|
| 211 | static unsigned int
|
| 212 | set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
|
| 213 | {
|
| 214 | const struct xt_set_info_target_v0 *info = par->targinfo;
|
| 215 |
|
| 216 | ADT_OPT(add_opt, xt_family(par), info->add_set.u.compat.dim,
|
| 217 | info->add_set.u.compat.flags, 0, UINT_MAX,
|
| 218 | 0, 0, 0, 0);
|
| 219 | ADT_OPT(del_opt, xt_family(par), info->del_set.u.compat.dim,
|
| 220 | info->del_set.u.compat.flags, 0, UINT_MAX,
|
| 221 | 0, 0, 0, 0);
|
| 222 |
|
| 223 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 224 | ip_set_add(info->add_set.index, skb, par, &add_opt);
|
| 225 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 226 | ip_set_del(info->del_set.index, skb, par, &del_opt);
|
| 227 |
|
| 228 | return XT_CONTINUE;
|
| 229 | }
|
| 230 |
|
| 231 | static int
|
| 232 | set_target_v0_checkentry(const struct xt_tgchk_param *par)
|
| 233 | {
|
| 234 | struct xt_set_info_target_v0 *info = par->targinfo;
|
| 235 | ip_set_id_t index;
|
| 236 |
|
| 237 | if (info->add_set.index != IPSET_INVALID_ID) {
|
| 238 | index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
|
| 239 | if (index == IPSET_INVALID_ID) {
|
| 240 | pr_info_ratelimited("Cannot find add_set index %u as target\n",
|
| 241 | info->add_set.index);
|
| 242 | return -ENOENT;
|
| 243 | }
|
| 244 | }
|
| 245 |
|
| 246 | if (info->del_set.index != IPSET_INVALID_ID) {
|
| 247 | index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
|
| 248 | if (index == IPSET_INVALID_ID) {
|
| 249 | pr_info_ratelimited("Cannot find del_set index %u as target\n",
|
| 250 | info->del_set.index);
|
| 251 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 252 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 253 | return -ENOENT;
|
| 254 | }
|
| 255 | }
|
| 256 | if (info->add_set.u.flags[IPSET_DIM_MAX - 1] != 0 ||
|
| 257 | info->del_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
|
| 258 | pr_info_ratelimited("SET target dimension over the limit!\n");
|
| 259 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 260 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 261 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 262 | ip_set_nfnl_put(par->net, info->del_set.index);
|
| 263 | return -ERANGE;
|
| 264 | }
|
| 265 |
|
| 266 |
|
| 267 | compat_flags(&info->add_set);
|
| 268 | compat_flags(&info->del_set);
|
| 269 |
|
| 270 | return 0;
|
| 271 | }
|
| 272 |
|
| 273 | static void
|
| 274 | set_target_v0_destroy(const struct xt_tgdtor_param *par)
|
| 275 | {
|
| 276 | const struct xt_set_info_target_v0 *info = par->targinfo;
|
| 277 |
|
| 278 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 279 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 280 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 281 | ip_set_nfnl_put(par->net, info->del_set.index);
|
| 282 | }
|
| 283 |
|
| 284 |
|
| 285 |
|
| 286 | static unsigned int
|
| 287 | set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
|
| 288 | {
|
| 289 | const struct xt_set_info_target_v1 *info = par->targinfo;
|
| 290 |
|
| 291 | ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
|
| 292 | info->add_set.flags, 0, UINT_MAX,
|
| 293 | 0, 0, 0, 0);
|
| 294 | ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
|
| 295 | info->del_set.flags, 0, UINT_MAX,
|
| 296 | 0, 0, 0, 0);
|
| 297 |
|
| 298 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 299 | ip_set_add(info->add_set.index, skb, par, &add_opt);
|
| 300 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 301 | ip_set_del(info->del_set.index, skb, par, &del_opt);
|
| 302 |
|
| 303 | return XT_CONTINUE;
|
| 304 | }
|
| 305 |
|
| 306 | static int
|
| 307 | set_target_v1_checkentry(const struct xt_tgchk_param *par)
|
| 308 | {
|
| 309 | const struct xt_set_info_target_v1 *info = par->targinfo;
|
| 310 | ip_set_id_t index;
|
| 311 |
|
| 312 | if (info->add_set.index != IPSET_INVALID_ID) {
|
| 313 | index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
|
| 314 | if (index == IPSET_INVALID_ID) {
|
| 315 | pr_info_ratelimited("Cannot find add_set index %u as target\n",
|
| 316 | info->add_set.index);
|
| 317 | return -ENOENT;
|
| 318 | }
|
| 319 | }
|
| 320 |
|
| 321 | if (info->del_set.index != IPSET_INVALID_ID) {
|
| 322 | index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
|
| 323 | if (index == IPSET_INVALID_ID) {
|
| 324 | pr_info_ratelimited("Cannot find del_set index %u as target\n",
|
| 325 | info->del_set.index);
|
| 326 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 327 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 328 | return -ENOENT;
|
| 329 | }
|
| 330 | }
|
| 331 | if (info->add_set.dim > IPSET_DIM_MAX ||
|
| 332 | info->del_set.dim > IPSET_DIM_MAX) {
|
| 333 | pr_info_ratelimited("SET target dimension over the limit!\n");
|
| 334 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 335 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 336 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 337 | ip_set_nfnl_put(par->net, info->del_set.index);
|
| 338 | return -ERANGE;
|
| 339 | }
|
| 340 |
|
| 341 | return 0;
|
| 342 | }
|
| 343 |
|
| 344 | static void
|
| 345 | set_target_v1_destroy(const struct xt_tgdtor_param *par)
|
| 346 | {
|
| 347 | const struct xt_set_info_target_v1 *info = par->targinfo;
|
| 348 |
|
| 349 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 350 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 351 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 352 | ip_set_nfnl_put(par->net, info->del_set.index);
|
| 353 | }
|
| 354 |
|
| 355 |
|
| 356 |
|
| 357 | static unsigned int
|
| 358 | set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
|
| 359 | {
|
| 360 | const struct xt_set_info_target_v2 *info = par->targinfo;
|
| 361 |
|
| 362 | ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
|
| 363 | info->add_set.flags, info->flags, info->timeout,
|
| 364 | 0, 0, 0, 0);
|
| 365 | ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
|
| 366 | info->del_set.flags, 0, UINT_MAX,
|
| 367 | 0, 0, 0, 0);
|
| 368 |
|
| 369 |
|
| 370 | if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
|
| 371 | add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
|
| 372 | add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
|
| 373 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 374 | ip_set_add(info->add_set.index, skb, par, &add_opt);
|
| 375 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 376 | ip_set_del(info->del_set.index, skb, par, &del_opt);
|
| 377 |
|
| 378 | return XT_CONTINUE;
|
| 379 | }
|
| 380 |
|
| 381 | #define set_target_v2_checkentry set_target_v1_checkentry
|
| 382 | #define set_target_v2_destroy set_target_v1_destroy
|
| 383 |
|
| 384 |
|
| 385 |
|
| 386 | #define MOPT(opt, member) ((opt).ext.skbinfo.member)
|
| 387 |
|
| 388 | static unsigned int
|
| 389 | set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
|
| 390 | {
|
| 391 | const struct xt_set_info_target_v3 *info = par->targinfo;
|
| 392 | int ret;
|
| 393 |
|
| 394 | ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
|
| 395 | info->add_set.flags, info->flags, info->timeout,
|
| 396 | 0, 0, 0, 0);
|
| 397 | ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
|
| 398 | info->del_set.flags, 0, UINT_MAX,
|
| 399 | 0, 0, 0, 0);
|
| 400 | ADT_OPT(map_opt, xt_family(par), info->map_set.dim,
|
| 401 | info->map_set.flags, 0, UINT_MAX,
|
| 402 | 0, 0, 0, 0);
|
| 403 |
|
| 404 |
|
| 405 | if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
|
| 406 | add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
|
| 407 | add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
|
| 408 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 409 | ip_set_add(info->add_set.index, skb, par, &add_opt);
|
| 410 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 411 | ip_set_del(info->del_set.index, skb, par, &del_opt);
|
| 412 | if (info->map_set.index != IPSET_INVALID_ID) {
|
| 413 | map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |
|
| 414 | IPSET_FLAG_MAP_SKBPRIO |
|
| 415 | IPSET_FLAG_MAP_SKBQUEUE);
|
| 416 | ret = match_set(info->map_set.index, skb, par, &map_opt,
|
| 417 | info->map_set.flags & IPSET_INV_MATCH);
|
| 418 | if (!ret)
|
| 419 | return XT_CONTINUE;
|
| 420 | if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)
|
| 421 | skb->mark = (skb->mark & ~MOPT(map_opt,skbmarkmask))
|
| 422 | ^ MOPT(map_opt, skbmark);
|
| 423 | if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)
|
| 424 | skb->priority = MOPT(map_opt, skbprio);
|
| 425 | if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&
|
| 426 | skb->dev &&
|
| 427 | skb->dev->real_num_tx_queues > MOPT(map_opt, skbqueue))
|
| 428 | skb_set_queue_mapping(skb, MOPT(map_opt, skbqueue));
|
| 429 | }
|
| 430 | return XT_CONTINUE;
|
| 431 | }
|
| 432 |
|
| 433 | static int
|
| 434 | set_target_v3_check_hooks(const struct xt_tgchk_param *par)
|
| 435 | {
|
| 436 | const struct xt_set_info_target_v3 *info = par->targinfo;
|
| 437 |
|
| 438 | if (info->map_set.index != IPSET_INVALID_ID) {
|
| 439 | if (strncmp(par->table, "mangle", 7)) {
|
| 440 | pr_info_ratelimited("--map-set only usable from mangle table\n");
|
| 441 | return -EINVAL;
|
| 442 | }
|
| 443 | if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
|
| 444 | (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
|
| 445 | (par->hook_mask & ~(1 << NF_INET_FORWARD |
|
| 446 | 1 << NF_INET_LOCAL_OUT |
|
| 447 | 1 << NF_INET_POST_ROUTING))) {
|
| 448 | pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
|
| 449 | return -EINVAL;
|
| 450 | }
|
| 451 | }
|
| 452 |
|
| 453 | return 0;
|
| 454 | }
|
| 455 |
|
| 456 | static int
|
| 457 | set_target_v3_checkentry(const struct xt_tgchk_param *par)
|
| 458 | {
|
| 459 | const struct xt_set_info_target_v3 *info = par->targinfo;
|
| 460 | ip_set_id_t index;
|
| 461 | int ret = 0;
|
| 462 |
|
| 463 | if (info->add_set.index != IPSET_INVALID_ID) {
|
| 464 | index = ip_set_nfnl_get_byindex(par->net,
|
| 465 | info->add_set.index);
|
| 466 | if (index == IPSET_INVALID_ID) {
|
| 467 | pr_info_ratelimited("Cannot find add_set index %u as target\n",
|
| 468 | info->add_set.index);
|
| 469 | return -ENOENT;
|
| 470 | }
|
| 471 | }
|
| 472 |
|
| 473 | if (info->del_set.index != IPSET_INVALID_ID) {
|
| 474 | index = ip_set_nfnl_get_byindex(par->net,
|
| 475 | info->del_set.index);
|
| 476 | if (index == IPSET_INVALID_ID) {
|
| 477 | pr_info_ratelimited("Cannot find del_set index %u as target\n",
|
| 478 | info->del_set.index);
|
| 479 | ret = -ENOENT;
|
| 480 | goto cleanup_add;
|
| 481 | }
|
| 482 | }
|
| 483 |
|
| 484 | if (info->map_set.index != IPSET_INVALID_ID) {
|
| 485 | index = ip_set_nfnl_get_byindex(par->net,
|
| 486 | info->map_set.index);
|
| 487 | if (index == IPSET_INVALID_ID) {
|
| 488 | pr_info_ratelimited("Cannot find map_set index %u as target\n",
|
| 489 | info->map_set.index);
|
| 490 | ret = -ENOENT;
|
| 491 | goto cleanup_del;
|
| 492 | }
|
| 493 | }
|
| 494 |
|
| 495 | if (info->add_set.dim > IPSET_DIM_MAX ||
|
| 496 | info->del_set.dim > IPSET_DIM_MAX ||
|
| 497 | info->map_set.dim > IPSET_DIM_MAX) {
|
| 498 | pr_info_ratelimited("SET target dimension over the limit!\n");
|
| 499 | ret = -ERANGE;
|
| 500 | goto cleanup_mark;
|
| 501 | }
|
| 502 |
|
| 503 | return 0;
|
| 504 | cleanup_mark:
|
| 505 | if (info->map_set.index != IPSET_INVALID_ID)
|
| 506 | ip_set_nfnl_put(par->net, info->map_set.index);
|
| 507 | cleanup_del:
|
| 508 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 509 | ip_set_nfnl_put(par->net, info->del_set.index);
|
| 510 | cleanup_add:
|
| 511 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 512 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 513 | return ret;
|
| 514 | }
|
| 515 |
|
| 516 | static void
|
| 517 | set_target_v3_destroy(const struct xt_tgdtor_param *par)
|
| 518 | {
|
| 519 | const struct xt_set_info_target_v3 *info = par->targinfo;
|
| 520 |
|
| 521 | if (info->add_set.index != IPSET_INVALID_ID)
|
| 522 | ip_set_nfnl_put(par->net, info->add_set.index);
|
| 523 | if (info->del_set.index != IPSET_INVALID_ID)
|
| 524 | ip_set_nfnl_put(par->net, info->del_set.index);
|
| 525 | if (info->map_set.index != IPSET_INVALID_ID)
|
| 526 | ip_set_nfnl_put(par->net, info->map_set.index);
|
| 527 | }
|
| 528 |
|
| 529 | static struct xt_match set_matches[] __read_mostly = {
|
| 530 | {
|
| 531 | .name = "set",
|
| 532 | .family = NFPROTO_IPV4,
|
| 533 | .revision = 0,
|
| 534 | .match = set_match_v0,
|
| 535 | .matchsize = sizeof(struct xt_set_info_match_v0),
|
| 536 | .checkentry = set_match_v0_checkentry,
|
| 537 | .destroy = set_match_v0_destroy,
|
| 538 | .me = THIS_MODULE
|
| 539 | },
|
| 540 | {
|
| 541 | .name = "set",
|
| 542 | .family = NFPROTO_IPV4,
|
| 543 | .revision = 1,
|
| 544 | .match = set_match_v1,
|
| 545 | .matchsize = sizeof(struct xt_set_info_match_v1),
|
| 546 | .checkentry = set_match_v1_checkentry,
|
| 547 | .destroy = set_match_v1_destroy,
|
| 548 | .me = THIS_MODULE
|
| 549 | },
|
| 550 | {
|
| 551 | .name = "set",
|
| 552 | .family = NFPROTO_IPV6,
|
| 553 | .revision = 1,
|
| 554 | .match = set_match_v1,
|
| 555 | .matchsize = sizeof(struct xt_set_info_match_v1),
|
| 556 | .checkentry = set_match_v1_checkentry,
|
| 557 | .destroy = set_match_v1_destroy,
|
| 558 | .me = THIS_MODULE
|
| 559 | },
|
| 560 |
|
| 561 | {
|
| 562 | .name = "set",
|
| 563 | .family = NFPROTO_IPV4,
|
| 564 | .revision = 2,
|
| 565 | .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,
|
| 569 | .me = THIS_MODULE
|
| 570 | },
|
| 571 | {
|
| 572 | .name = "set",
|
| 573 | .family = NFPROTO_IPV6,
|
| 574 | .revision = 2,
|
| 575 | .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,
|
| 579 | .me = THIS_MODULE
|
| 580 | },
|
| 581 |
|
| 582 | {
|
| 583 | .name = "set",
|
| 584 | .family = NFPROTO_IPV4,
|
| 585 | .revision = 3,
|
| 586 | .match = set_match_v3,
|
| 587 | .matchsize = sizeof(struct xt_set_info_match_v3),
|
| 588 | .checkentry = set_match_v3_checkentry,
|
| 589 | .destroy = set_match_v3_destroy,
|
| 590 | .me = THIS_MODULE
|
| 591 | },
|
| 592 | {
|
| 593 | .name = "set",
|
| 594 | .family = NFPROTO_IPV6,
|
| 595 | .revision = 3,
|
| 596 | .match = set_match_v3,
|
| 597 | .matchsize = sizeof(struct xt_set_info_match_v3),
|
| 598 | .checkentry = set_match_v3_checkentry,
|
| 599 | .destroy = set_match_v3_destroy,
|
| 600 | .me = THIS_MODULE
|
| 601 | },
|
| 602 |
|
| 603 | {
|
| 604 | .name = "set",
|
| 605 | .family = NFPROTO_IPV4,
|
| 606 | .revision = 4,
|
| 607 | .match = set_match_v4,
|
| 608 | .matchsize = sizeof(struct xt_set_info_match_v4),
|
| 609 | .checkentry = set_match_v4_checkentry,
|
| 610 | .destroy = set_match_v4_destroy,
|
| 611 | .me = THIS_MODULE
|
| 612 | },
|
| 613 | {
|
| 614 | .name = "set",
|
| 615 | .family = NFPROTO_IPV6,
|
| 616 | .revision = 4,
|
| 617 | .match = set_match_v4,
|
| 618 | .matchsize = sizeof(struct xt_set_info_match_v4),
|
| 619 | .checkentry = set_match_v4_checkentry,
|
| 620 | .destroy = set_match_v4_destroy,
|
| 621 | .me = THIS_MODULE
|
| 622 | },
|
| 623 | };
|
| 624 |
|
| 625 | static struct xt_target set_targets[] __read_mostly = {
|
| 626 | {
|
| 627 | .name = "SET",
|
| 628 | .revision = 0,
|
| 629 | .family = NFPROTO_IPV4,
|
| 630 | .target = set_target_v0,
|
| 631 | .targetsize = sizeof(struct xt_set_info_target_v0),
|
| 632 | .checkentry = set_target_v0_checkentry,
|
| 633 | .destroy = set_target_v0_destroy,
|
| 634 | .me = THIS_MODULE
|
| 635 | },
|
| 636 | {
|
| 637 | .name = "SET",
|
| 638 | .revision = 1,
|
| 639 | .family = NFPROTO_IPV4,
|
| 640 | .target = set_target_v1,
|
| 641 | .targetsize = sizeof(struct xt_set_info_target_v1),
|
| 642 | .checkentry = set_target_v1_checkentry,
|
| 643 | .destroy = set_target_v1_destroy,
|
| 644 | .me = THIS_MODULE
|
| 645 | },
|
| 646 | {
|
| 647 | .name = "SET",
|
| 648 | .revision = 1,
|
| 649 | .family = NFPROTO_IPV6,
|
| 650 | .target = set_target_v1,
|
| 651 | .targetsize = sizeof(struct xt_set_info_target_v1),
|
| 652 | .checkentry = set_target_v1_checkentry,
|
| 653 | .destroy = set_target_v1_destroy,
|
| 654 | .me = THIS_MODULE
|
| 655 | },
|
| 656 |
|
| 657 | {
|
| 658 | .name = "SET",
|
| 659 | .revision = 2,
|
| 660 | .family = NFPROTO_IPV4,
|
| 661 | .target = set_target_v2,
|
| 662 | .targetsize = sizeof(struct xt_set_info_target_v2),
|
| 663 | .checkentry = set_target_v2_checkentry,
|
| 664 | .destroy = set_target_v2_destroy,
|
| 665 | .me = THIS_MODULE
|
| 666 | },
|
| 667 | {
|
| 668 | .name = "SET",
|
| 669 | .revision = 2,
|
| 670 | .family = NFPROTO_IPV6,
|
| 671 | .target = set_target_v2,
|
| 672 | .targetsize = sizeof(struct xt_set_info_target_v2),
|
| 673 | .checkentry = set_target_v2_checkentry,
|
| 674 | .destroy = set_target_v2_destroy,
|
| 675 | .me = THIS_MODULE
|
| 676 | },
|
| 677 |
|
| 678 | {
|
| 679 | .name = "SET",
|
| 680 | .revision = 3,
|
| 681 | .family = NFPROTO_IPV4,
|
| 682 | .target = set_target_v3,
|
| 683 | .targetsize = sizeof(struct xt_set_info_target_v3),
|
| 684 | .check_hooks = set_target_v3_check_hooks,
|
| 685 | .checkentry = set_target_v3_checkentry,
|
| 686 | .destroy = set_target_v3_destroy,
|
| 687 | .me = THIS_MODULE
|
| 688 | },
|
| 689 | {
|
| 690 | .name = "SET",
|
| 691 | .revision = 3,
|
| 692 | .family = NFPROTO_IPV6,
|
| 693 | .target = set_target_v3,
|
| 694 | .targetsize = sizeof(struct xt_set_info_target_v3),
|
| 695 | .check_hooks = set_target_v3_check_hooks,
|
| 696 | .checkentry = set_target_v3_checkentry,
|
| 697 | .destroy = set_target_v3_destroy,
|
| 698 | .me = THIS_MODULE
|
| 699 | },
|
| 700 | };
|
| 701 |
|
| 702 | static int __init xt_set_init(void)
|
| 703 | {
|
| 704 | int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
|
| 705 |
|
| 706 | if (!ret) {
|
| 707 | ret = xt_register_targets(set_targets,
|
| 708 | ARRAY_SIZE(set_targets));
|
| 709 | if (ret)
|
| 710 | xt_unregister_matches(set_matches,
|
| 711 | ARRAY_SIZE(set_matches));
|
| 712 | }
|
| 713 | return ret;
|
| 714 | }
|
| 715 |
|
| 716 | static void __exit xt_set_fini(void)
|
| 717 | {
|
| 718 | xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
|
| 719 | xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
|
| 720 | }
|
| 721 |
|
| 722 | module_init(xt_set_init);
|
| 723 | module_exit(xt_set_fini);
|