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