1 /* Copyright (C) 2000-2002 Joakim Axelsson <[email protected]> 2 * Patrick Schaaf <[email protected]> 3 * Martin Josefsson <[email protected]> 4 * Copyright (C) 2003-2013 Jozsef Kadlecsik <[email protected]> 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 21 MODULE_LICENSE("GPL"); 22 MODULE_AUTHOR("Jozsef Kadlecsik <[email protected]>"); 23 MODULE_DESCRIPTION("Xtables: IP set match and target module"); 24 MODULE_ALIAS("xt_SET"); 25 MODULE_ALIAS("ipt_set"); 26 MODULE_ALIAS("ip6t_set"); 27 MODULE_ALIAS("ipt_SET"); 28 MODULE_ALIAS("ip6t_SET"); 29 30 static inline int 31 match_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) \ 41 struct 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 51 static bool 52 set_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 62 static void 63 compat_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 78 static int 79 set_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 104 static void 105 set_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 114 static bool 115 set_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 128 static int 129 set_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 151 static void 152 set_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 161 static bool 162 match_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 179 static bool 180 set_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 207 static unsigned int 208 set_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 224 static int 225 set_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 267 static void 268 set_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 280 static unsigned int 281 set_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 297 static int 298 set_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 336 static void 337 set_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 349 static unsigned int 350 set_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 373 static 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 448 static 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 502 static 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 516 static 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 522 module_init(xt_set_init); 523 module_exit(xt_set_fini); 524