at v3.16-rc4 523 lines 14 kB view raw
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-2013 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 17#include <linux/netfilter/x_tables.h> 18#include <linux/netfilter/xt_set.h> 19#include <linux/netfilter/ipset/ip_set_timeout.h> 20 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, 32 const struct xt_action_param *par, 33 struct ip_set_adt_opt *opt, int inv) 34{ 35 if (ip_set_test(index, skb, par, opt)) 36 inv = !inv; 37 return inv; 38} 39 40#define ADT_OPT(n, f, d, fs, cfs, t) \ 41struct ip_set_adt_opt n = { \ 42 .family = f, \ 43 .dim = d, \ 44 .flags = fs, \ 45 .cmdflags = cfs, \ 46 .ext.timeout = t, \ 47} 48 49/* Revision 0 interface: backward compatible with netfilter/iptables */ 50 51static bool 52set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) 53{ 54 const struct xt_set_info_match_v0 *info = par->matchinfo; 55 ADT_OPT(opt, par->family, info->match_set.u.compat.dim, 56 info->match_set.u.compat.flags, 0, UINT_MAX); 57 58 return match_set(info->match_set.index, skb, par, &opt, 59 info->match_set.u.compat.flags & IPSET_INV_MATCH); 60} 61 62static void 63compat_flags(struct xt_set_info_v0 *info) 64{ 65 u_int8_t i; 66 67 /* Fill out compatibility data according to enum ip_set_kopt */ 68 info->u.compat.dim = IPSET_DIM_ZERO; 69 if (info->u.flags[0] & IPSET_MATCH_INV) 70 info->u.compat.flags |= IPSET_INV_MATCH; 71 for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { 72 info->u.compat.dim++; 73 if (info->u.flags[i] & IPSET_SRC) 74 info->u.compat.flags |= (1<<info->u.compat.dim); 75 } 76} 77 78static int 79set_match_v0_checkentry(const struct xt_mtchk_param *par) 80{ 81 struct xt_set_info_match_v0 *info = par->matchinfo; 82 ip_set_id_t index; 83 84 index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); 85 86 if (index == IPSET_INVALID_ID) { 87 pr_warning("Cannot find set identified by id %u to match\n", 88 info->match_set.index); 89 return -ENOENT; 90 } 91 if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { 92 pr_warning("Protocol error: set match dimension " 93 "is over the limit!\n"); 94 ip_set_nfnl_put(par->net, info->match_set.index); 95 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 109 ip_set_nfnl_put(par->net, info->match_set.index); 110} 111 112/* 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 134 index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); 135 136 if (index == IPSET_INVALID_ID) { 137 pr_warning("Cannot find set identified by id %u to match\n", 138 info->match_set.index); 139 return -ENOENT; 140 } 141 if (info->match_set.dim > IPSET_DIM_MAX) { 142 pr_warning("Protocol error: set match dimension " 143 "is over the limit!\n"); 144 ip_set_nfnl_put(par->net, info->match_set.index); 145 return -ERANGE; 146 } 147 148 return 0; 149} 150 151static void 152set_match_v1_destroy(const struct xt_mtdtor_param *par) 153{ 154 struct xt_set_info_match_v1 *info = par->matchinfo; 155 156 ip_set_nfnl_put(par->net, info->match_set.index); 157} 158 159/* Revision 3 match */ 160 161static bool 162match_counter(u64 counter, const struct ip_set_counter_match *info) 163{ 164 switch (info->op) { 165 case IPSET_COUNTER_NONE: 166 return true; 167 case IPSET_COUNTER_EQ: 168 return counter == info->value; 169 case IPSET_COUNTER_NE: 170 return counter != info->value; 171 case IPSET_COUNTER_LT: 172 return counter < info->value; 173 case IPSET_COUNTER_GT: 174 return counter > info->value; 175 } 176 return false; 177} 178 179static bool 180set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) 181{ 182 const struct xt_set_info_match_v3 *info = par->matchinfo; 183 ADT_OPT(opt, par->family, info->match_set.dim, 184 info->match_set.flags, info->flags, UINT_MAX); 185 int ret; 186 187 if (info->packets.op != IPSET_COUNTER_NONE || 188 info->bytes.op != IPSET_COUNTER_NONE) 189 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; 190 191 ret = match_set(info->match_set.index, skb, par, &opt, 192 info->match_set.flags & IPSET_INV_MATCH); 193 194 if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) 195 return ret; 196 197 if (!match_counter(opt.ext.packets, &info->packets)) 198 return 0; 199 return match_counter(opt.ext.bytes, &info->bytes); 200} 201 202#define set_match_v3_checkentry set_match_v1_checkentry 203#define set_match_v3_destroy set_match_v1_destroy 204 205/* Revision 0 interface: backward compatible with netfilter/iptables */ 206 207static unsigned int 208set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) 209{ 210 const struct xt_set_info_target_v0 *info = par->targinfo; 211 ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim, 212 info->add_set.u.compat.flags, 0, UINT_MAX); 213 ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim, 214 info->del_set.u.compat.flags, 0, UINT_MAX); 215 216 if (info->add_set.index != IPSET_INVALID_ID) 217 ip_set_add(info->add_set.index, skb, par, &add_opt); 218 if (info->del_set.index != IPSET_INVALID_ID) 219 ip_set_del(info->del_set.index, skb, par, &del_opt); 220 221 return XT_CONTINUE; 222} 223 224static int 225set_target_v0_checkentry(const struct xt_tgchk_param *par) 226{ 227 struct xt_set_info_target_v0 *info = par->targinfo; 228 ip_set_id_t index; 229 230 if (info->add_set.index != IPSET_INVALID_ID) { 231 index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); 232 if (index == IPSET_INVALID_ID) { 233 pr_warning("Cannot find add_set index %u as target\n", 234 info->add_set.index); 235 return -ENOENT; 236 } 237 } 238 239 if (info->del_set.index != IPSET_INVALID_ID) { 240 index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); 241 if (index == IPSET_INVALID_ID) { 242 pr_warning("Cannot find del_set index %u as target\n", 243 info->del_set.index); 244 if (info->add_set.index != IPSET_INVALID_ID) 245 ip_set_nfnl_put(par->net, info->add_set.index); 246 return -ENOENT; 247 } 248 } 249 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || 250 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { 251 pr_warning("Protocol error: SET target dimension " 252 "is over the limit!\n"); 253 if (info->add_set.index != IPSET_INVALID_ID) 254 ip_set_nfnl_put(par->net, info->add_set.index); 255 if (info->del_set.index != IPSET_INVALID_ID) 256 ip_set_nfnl_put(par->net, info->del_set.index); 257 return -ERANGE; 258 } 259 260 /* Fill out compatibility data */ 261 compat_flags(&info->add_set); 262 compat_flags(&info->del_set); 263 264 return 0; 265} 266 267static void 268set_target_v0_destroy(const struct xt_tgdtor_param *par) 269{ 270 const struct xt_set_info_target_v0 *info = par->targinfo; 271 272 if (info->add_set.index != IPSET_INVALID_ID) 273 ip_set_nfnl_put(par->net, info->add_set.index); 274 if (info->del_set.index != IPSET_INVALID_ID) 275 ip_set_nfnl_put(par->net, info->del_set.index); 276} 277 278/* Revision 1 target */ 279 280static unsigned int 281set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) 282{ 283 const struct xt_set_info_target_v1 *info = par->targinfo; 284 ADT_OPT(add_opt, par->family, info->add_set.dim, 285 info->add_set.flags, 0, UINT_MAX); 286 ADT_OPT(del_opt, par->family, info->del_set.dim, 287 info->del_set.flags, 0, UINT_MAX); 288 289 if (info->add_set.index != IPSET_INVALID_ID) 290 ip_set_add(info->add_set.index, skb, par, &add_opt); 291 if (info->del_set.index != IPSET_INVALID_ID) 292 ip_set_del(info->del_set.index, skb, par, &del_opt); 293 294 return XT_CONTINUE; 295} 296 297static int 298set_target_v1_checkentry(const struct xt_tgchk_param *par) 299{ 300 const struct xt_set_info_target_v1 *info = par->targinfo; 301 ip_set_id_t index; 302 303 if (info->add_set.index != IPSET_INVALID_ID) { 304 index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); 305 if (index == IPSET_INVALID_ID) { 306 pr_warning("Cannot find add_set index %u as target\n", 307 info->add_set.index); 308 return -ENOENT; 309 } 310 } 311 312 if (info->del_set.index != IPSET_INVALID_ID) { 313 index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); 314 if (index == IPSET_INVALID_ID) { 315 pr_warning("Cannot find del_set index %u as target\n", 316 info->del_set.index); 317 if (info->add_set.index != IPSET_INVALID_ID) 318 ip_set_nfnl_put(par->net, info->add_set.index); 319 return -ENOENT; 320 } 321 } 322 if (info->add_set.dim > IPSET_DIM_MAX || 323 info->del_set.dim > IPSET_DIM_MAX) { 324 pr_warning("Protocol error: SET target dimension " 325 "is over the limit!\n"); 326 if (info->add_set.index != IPSET_INVALID_ID) 327 ip_set_nfnl_put(par->net, info->add_set.index); 328 if (info->del_set.index != IPSET_INVALID_ID) 329 ip_set_nfnl_put(par->net, info->del_set.index); 330 return -ERANGE; 331 } 332 333 return 0; 334} 335 336static void 337set_target_v1_destroy(const struct xt_tgdtor_param *par) 338{ 339 const struct xt_set_info_target_v1 *info = par->targinfo; 340 341 if (info->add_set.index != IPSET_INVALID_ID) 342 ip_set_nfnl_put(par->net, info->add_set.index); 343 if (info->del_set.index != IPSET_INVALID_ID) 344 ip_set_nfnl_put(par->net, info->del_set.index); 345} 346 347/* Revision 2 target */ 348 349static unsigned int 350set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) 351{ 352 const struct xt_set_info_target_v2 *info = par->targinfo; 353 ADT_OPT(add_opt, par->family, info->add_set.dim, 354 info->add_set.flags, info->flags, info->timeout); 355 ADT_OPT(del_opt, par->family, info->del_set.dim, 356 info->del_set.flags, 0, UINT_MAX); 357 358 /* Normalize to fit into jiffies */ 359 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT && 360 add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC) 361 add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC; 362 if (info->add_set.index != IPSET_INVALID_ID) 363 ip_set_add(info->add_set.index, skb, par, &add_opt); 364 if (info->del_set.index != IPSET_INVALID_ID) 365 ip_set_del(info->del_set.index, skb, par, &del_opt); 366 367 return XT_CONTINUE; 368} 369 370#define set_target_v2_checkentry set_target_v1_checkentry 371#define set_target_v2_destroy set_target_v1_destroy 372 373static struct xt_match set_matches[] __read_mostly = { 374 { 375 .name = "set", 376 .family = NFPROTO_IPV4, 377 .revision = 0, 378 .match = set_match_v0, 379 .matchsize = sizeof(struct xt_set_info_match_v0), 380 .checkentry = set_match_v0_checkentry, 381 .destroy = set_match_v0_destroy, 382 .me = THIS_MODULE 383 }, 384 { 385 .name = "set", 386 .family = NFPROTO_IPV4, 387 .revision = 1, 388 .match = set_match_v1, 389 .matchsize = sizeof(struct xt_set_info_match_v1), 390 .checkentry = set_match_v1_checkentry, 391 .destroy = set_match_v1_destroy, 392 .me = THIS_MODULE 393 }, 394 { 395 .name = "set", 396 .family = NFPROTO_IPV6, 397 .revision = 1, 398 .match = set_match_v1, 399 .matchsize = sizeof(struct xt_set_info_match_v1), 400 .checkentry = set_match_v1_checkentry, 401 .destroy = set_match_v1_destroy, 402 .me = THIS_MODULE 403 }, 404 /* --return-nomatch flag support */ 405 { 406 .name = "set", 407 .family = NFPROTO_IPV4, 408 .revision = 2, 409 .match = set_match_v1, 410 .matchsize = sizeof(struct xt_set_info_match_v1), 411 .checkentry = set_match_v1_checkentry, 412 .destroy = set_match_v1_destroy, 413 .me = THIS_MODULE 414 }, 415 { 416 .name = "set", 417 .family = NFPROTO_IPV6, 418 .revision = 2, 419 .match = set_match_v1, 420 .matchsize = sizeof(struct xt_set_info_match_v1), 421 .checkentry = set_match_v1_checkentry, 422 .destroy = set_match_v1_destroy, 423 .me = THIS_MODULE 424 }, 425 /* counters support: update, match */ 426 { 427 .name = "set", 428 .family = NFPROTO_IPV4, 429 .revision = 3, 430 .match = set_match_v3, 431 .matchsize = sizeof(struct xt_set_info_match_v3), 432 .checkentry = set_match_v3_checkentry, 433 .destroy = set_match_v3_destroy, 434 .me = THIS_MODULE 435 }, 436 { 437 .name = "set", 438 .family = NFPROTO_IPV6, 439 .revision = 3, 440 .match = set_match_v3, 441 .matchsize = sizeof(struct xt_set_info_match_v3), 442 .checkentry = set_match_v3_checkentry, 443 .destroy = set_match_v3_destroy, 444 .me = THIS_MODULE 445 }, 446}; 447 448static struct xt_target set_targets[] __read_mostly = { 449 { 450 .name = "SET", 451 .revision = 0, 452 .family = NFPROTO_IPV4, 453 .target = set_target_v0, 454 .targetsize = sizeof(struct xt_set_info_target_v0), 455 .checkentry = set_target_v0_checkentry, 456 .destroy = set_target_v0_destroy, 457 .me = THIS_MODULE 458 }, 459 { 460 .name = "SET", 461 .revision = 1, 462 .family = NFPROTO_IPV4, 463 .target = set_target_v1, 464 .targetsize = sizeof(struct xt_set_info_target_v1), 465 .checkentry = set_target_v1_checkentry, 466 .destroy = set_target_v1_destroy, 467 .me = THIS_MODULE 468 }, 469 { 470 .name = "SET", 471 .revision = 1, 472 .family = NFPROTO_IPV6, 473 .target = set_target_v1, 474 .targetsize = sizeof(struct xt_set_info_target_v1), 475 .checkentry = set_target_v1_checkentry, 476 .destroy = set_target_v1_destroy, 477 .me = THIS_MODULE 478 }, 479 /* --timeout and --exist flags support */ 480 { 481 .name = "SET", 482 .revision = 2, 483 .family = NFPROTO_IPV4, 484 .target = set_target_v2, 485 .targetsize = sizeof(struct xt_set_info_target_v2), 486 .checkentry = set_target_v2_checkentry, 487 .destroy = set_target_v2_destroy, 488 .me = THIS_MODULE 489 }, 490 { 491 .name = "SET", 492 .revision = 2, 493 .family = NFPROTO_IPV6, 494 .target = set_target_v2, 495 .targetsize = sizeof(struct xt_set_info_target_v2), 496 .checkentry = set_target_v2_checkentry, 497 .destroy = set_target_v2_destroy, 498 .me = THIS_MODULE 499 }, 500}; 501 502static int __init xt_set_init(void) 503{ 504 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); 505 506 if (!ret) { 507 ret = xt_register_targets(set_targets, 508 ARRAY_SIZE(set_targets)); 509 if (ret) 510 xt_unregister_matches(set_matches, 511 ARRAY_SIZE(set_matches)); 512 } 513 return ret; 514} 515 516static void __exit xt_set_fini(void) 517{ 518 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); 519 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); 520} 521 522module_init(xt_set_init); 523module_exit(xt_set_fini);