xt_set.c revision a73f89a61f92b364f0b4a3be412b5b70553afc23
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-2011 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 const 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) \ 41const struct ip_set_adt_opt n = { \ 42 .family = f, \ 43 .dim = d, \ 44 .flags = fs, \ 45 .cmdflags = cfs, \ 46 .timeout = t, \ 47} 48#define ADT_MOPT(n, f, d, fs, cfs, t) \ 49struct ip_set_adt_opt n = { \ 50 .family = f, \ 51 .dim = d, \ 52 .flags = fs, \ 53 .cmdflags = cfs, \ 54 .timeout = t, \ 55} 56 57/* Revision 0 interface: backward compatible with netfilter/iptables */ 58 59static bool 60set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) 61{ 62 const struct xt_set_info_match_v0 *info = par->matchinfo; 63 ADT_OPT(opt, par->family, info->match_set.u.compat.dim, 64 info->match_set.u.compat.flags, 0, UINT_MAX); 65 66 return match_set(info->match_set.index, skb, par, &opt, 67 info->match_set.u.compat.flags & IPSET_INV_MATCH); 68} 69 70static void 71compat_flags(struct xt_set_info_v0 *info) 72{ 73 u_int8_t i; 74 75 /* Fill out compatibility data according to enum ip_set_kopt */ 76 info->u.compat.dim = IPSET_DIM_ZERO; 77 if (info->u.flags[0] & IPSET_MATCH_INV) 78 info->u.compat.flags |= IPSET_INV_MATCH; 79 for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { 80 info->u.compat.dim++; 81 if (info->u.flags[i] & IPSET_SRC) 82 info->u.compat.flags |= (1<<info->u.compat.dim); 83 } 84} 85 86static int 87set_match_v0_checkentry(const struct xt_mtchk_param *par) 88{ 89 struct xt_set_info_match_v0 *info = par->matchinfo; 90 ip_set_id_t index; 91 92 index = ip_set_nfnl_get_byindex(info->match_set.index); 93 94 if (index == IPSET_INVALID_ID) { 95 pr_warning("Cannot find set indentified by id %u to match\n", 96 info->match_set.index); 97 return -ENOENT; 98 } 99 if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { 100 pr_warning("Protocol error: set match dimension " 101 "is over the limit!\n"); 102 ip_set_nfnl_put(info->match_set.index); 103 return -ERANGE; 104 } 105 106 /* Fill out compatibility data */ 107 compat_flags(&info->match_set); 108 109 return 0; 110} 111 112static void 113set_match_v0_destroy(const struct xt_mtdtor_param *par) 114{ 115 struct xt_set_info_match_v0 *info = par->matchinfo; 116 117 ip_set_nfnl_put(info->match_set.index); 118} 119 120static unsigned int 121set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) 122{ 123 const struct xt_set_info_target_v0 *info = par->targinfo; 124 ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim, 125 info->add_set.u.compat.flags, 0, UINT_MAX); 126 ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim, 127 info->del_set.u.compat.flags, 0, UINT_MAX); 128 129 if (info->add_set.index != IPSET_INVALID_ID) 130 ip_set_add(info->add_set.index, skb, par, &add_opt); 131 if (info->del_set.index != IPSET_INVALID_ID) 132 ip_set_del(info->del_set.index, skb, par, &del_opt); 133 134 return XT_CONTINUE; 135} 136 137static int 138set_target_v0_checkentry(const struct xt_tgchk_param *par) 139{ 140 struct xt_set_info_target_v0 *info = par->targinfo; 141 ip_set_id_t index; 142 143 if (info->add_set.index != IPSET_INVALID_ID) { 144 index = ip_set_nfnl_get_byindex(info->add_set.index); 145 if (index == IPSET_INVALID_ID) { 146 pr_warning("Cannot find add_set index %u as target\n", 147 info->add_set.index); 148 return -ENOENT; 149 } 150 } 151 152 if (info->del_set.index != IPSET_INVALID_ID) { 153 index = ip_set_nfnl_get_byindex(info->del_set.index); 154 if (index == IPSET_INVALID_ID) { 155 pr_warning("Cannot find del_set index %u as target\n", 156 info->del_set.index); 157 if (info->add_set.index != IPSET_INVALID_ID) 158 ip_set_nfnl_put(info->add_set.index); 159 return -ENOENT; 160 } 161 } 162 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || 163 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { 164 pr_warning("Protocol error: SET target dimension " 165 "is over the limit!\n"); 166 if (info->add_set.index != IPSET_INVALID_ID) 167 ip_set_nfnl_put(info->add_set.index); 168 if (info->del_set.index != IPSET_INVALID_ID) 169 ip_set_nfnl_put(info->del_set.index); 170 return -ERANGE; 171 } 172 173 /* Fill out compatibility data */ 174 compat_flags(&info->add_set); 175 compat_flags(&info->del_set); 176 177 return 0; 178} 179 180static void 181set_target_v0_destroy(const struct xt_tgdtor_param *par) 182{ 183 const struct xt_set_info_target_v0 *info = par->targinfo; 184 185 if (info->add_set.index != IPSET_INVALID_ID) 186 ip_set_nfnl_put(info->add_set.index); 187 if (info->del_set.index != IPSET_INVALID_ID) 188 ip_set_nfnl_put(info->del_set.index); 189} 190 191/* Revision 1 match and target */ 192 193static bool 194set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) 195{ 196 const struct xt_set_info_match_v1 *info = par->matchinfo; 197 ADT_OPT(opt, par->family, info->match_set.dim, 198 info->match_set.flags, 0, UINT_MAX); 199 200 return match_set(info->match_set.index, skb, par, &opt, 201 info->match_set.flags & IPSET_INV_MATCH); 202} 203 204static int 205set_match_v1_checkentry(const struct xt_mtchk_param *par) 206{ 207 struct xt_set_info_match_v1 *info = par->matchinfo; 208 ip_set_id_t index; 209 210 index = ip_set_nfnl_get_byindex(info->match_set.index); 211 212 if (index == IPSET_INVALID_ID) { 213 pr_warning("Cannot find set indentified by id %u to match\n", 214 info->match_set.index); 215 return -ENOENT; 216 } 217 if (info->match_set.dim > IPSET_DIM_MAX) { 218 pr_warning("Protocol error: set match dimension " 219 "is over the limit!\n"); 220 ip_set_nfnl_put(info->match_set.index); 221 return -ERANGE; 222 } 223 224 return 0; 225} 226 227static void 228set_match_v1_destroy(const struct xt_mtdtor_param *par) 229{ 230 struct xt_set_info_match_v1 *info = par->matchinfo; 231 232 ip_set_nfnl_put(info->match_set.index); 233} 234 235static unsigned int 236set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) 237{ 238 const struct xt_set_info_target_v1 *info = par->targinfo; 239 ADT_OPT(add_opt, par->family, info->add_set.dim, 240 info->add_set.flags, 0, UINT_MAX); 241 ADT_OPT(del_opt, par->family, info->del_set.dim, 242 info->del_set.flags, 0, UINT_MAX); 243 244 if (info->add_set.index != IPSET_INVALID_ID) 245 ip_set_add(info->add_set.index, skb, par, &add_opt); 246 if (info->del_set.index != IPSET_INVALID_ID) 247 ip_set_del(info->del_set.index, skb, par, &del_opt); 248 249 return XT_CONTINUE; 250} 251 252static int 253set_target_v1_checkentry(const struct xt_tgchk_param *par) 254{ 255 const struct xt_set_info_target_v1 *info = par->targinfo; 256 ip_set_id_t index; 257 258 if (info->add_set.index != IPSET_INVALID_ID) { 259 index = ip_set_nfnl_get_byindex(info->add_set.index); 260 if (index == IPSET_INVALID_ID) { 261 pr_warning("Cannot find add_set index %u as target\n", 262 info->add_set.index); 263 return -ENOENT; 264 } 265 } 266 267 if (info->del_set.index != IPSET_INVALID_ID) { 268 index = ip_set_nfnl_get_byindex(info->del_set.index); 269 if (index == IPSET_INVALID_ID) { 270 pr_warning("Cannot find del_set index %u as target\n", 271 info->del_set.index); 272 if (info->add_set.index != IPSET_INVALID_ID) 273 ip_set_nfnl_put(info->add_set.index); 274 return -ENOENT; 275 } 276 } 277 if (info->add_set.dim > IPSET_DIM_MAX || 278 info->del_set.dim > IPSET_DIM_MAX) { 279 pr_warning("Protocol error: SET target dimension " 280 "is over the limit!\n"); 281 if (info->add_set.index != IPSET_INVALID_ID) 282 ip_set_nfnl_put(info->add_set.index); 283 if (info->del_set.index != IPSET_INVALID_ID) 284 ip_set_nfnl_put(info->del_set.index); 285 return -ERANGE; 286 } 287 288 return 0; 289} 290 291static void 292set_target_v1_destroy(const struct xt_tgdtor_param *par) 293{ 294 const struct xt_set_info_target_v1 *info = par->targinfo; 295 296 if (info->add_set.index != IPSET_INVALID_ID) 297 ip_set_nfnl_put(info->add_set.index); 298 if (info->del_set.index != IPSET_INVALID_ID) 299 ip_set_nfnl_put(info->del_set.index); 300} 301 302/* Revision 2 target */ 303 304static unsigned int 305set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) 306{ 307 const struct xt_set_info_target_v2 *info = par->targinfo; 308 ADT_MOPT(add_opt, par->family, info->add_set.dim, 309 info->add_set.flags, info->flags, info->timeout); 310 ADT_OPT(del_opt, par->family, info->del_set.dim, 311 info->del_set.flags, 0, UINT_MAX); 312 313 /* Normalize to fit into jiffies */ 314 if (add_opt.timeout != IPSET_NO_TIMEOUT && 315 add_opt.timeout > UINT_MAX/MSEC_PER_SEC) 316 add_opt.timeout = UINT_MAX/MSEC_PER_SEC; 317 if (info->add_set.index != IPSET_INVALID_ID) 318 ip_set_add(info->add_set.index, skb, par, &add_opt); 319 if (info->del_set.index != IPSET_INVALID_ID) 320 ip_set_del(info->del_set.index, skb, par, &del_opt); 321 322 return XT_CONTINUE; 323} 324 325#define set_target_v2_checkentry set_target_v1_checkentry 326#define set_target_v2_destroy set_target_v1_destroy 327 328static struct xt_match set_matches[] __read_mostly = { 329 { 330 .name = "set", 331 .family = NFPROTO_IPV4, 332 .revision = 0, 333 .match = set_match_v0, 334 .matchsize = sizeof(struct xt_set_info_match_v0), 335 .checkentry = set_match_v0_checkentry, 336 .destroy = set_match_v0_destroy, 337 .me = THIS_MODULE 338 }, 339 { 340 .name = "set", 341 .family = NFPROTO_IPV4, 342 .revision = 1, 343 .match = set_match_v1, 344 .matchsize = sizeof(struct xt_set_info_match_v1), 345 .checkentry = set_match_v1_checkentry, 346 .destroy = set_match_v1_destroy, 347 .me = THIS_MODULE 348 }, 349 { 350 .name = "set", 351 .family = NFPROTO_IPV6, 352 .revision = 1, 353 .match = set_match_v1, 354 .matchsize = sizeof(struct xt_set_info_match_v1), 355 .checkentry = set_match_v1_checkentry, 356 .destroy = set_match_v1_destroy, 357 .me = THIS_MODULE 358 }, 359}; 360 361static struct xt_target set_targets[] __read_mostly = { 362 { 363 .name = "SET", 364 .revision = 0, 365 .family = NFPROTO_IPV4, 366 .target = set_target_v0, 367 .targetsize = sizeof(struct xt_set_info_target_v0), 368 .checkentry = set_target_v0_checkentry, 369 .destroy = set_target_v0_destroy, 370 .me = THIS_MODULE 371 }, 372 { 373 .name = "SET", 374 .revision = 1, 375 .family = NFPROTO_IPV4, 376 .target = set_target_v1, 377 .targetsize = sizeof(struct xt_set_info_target_v1), 378 .checkentry = set_target_v1_checkentry, 379 .destroy = set_target_v1_destroy, 380 .me = THIS_MODULE 381 }, 382 { 383 .name = "SET", 384 .revision = 1, 385 .family = NFPROTO_IPV6, 386 .target = set_target_v1, 387 .targetsize = sizeof(struct xt_set_info_target_v1), 388 .checkentry = set_target_v1_checkentry, 389 .destroy = set_target_v1_destroy, 390 .me = THIS_MODULE 391 }, 392 { 393 .name = "SET", 394 .revision = 2, 395 .family = NFPROTO_IPV4, 396 .target = set_target_v2, 397 .targetsize = sizeof(struct xt_set_info_target_v2), 398 .checkentry = set_target_v2_checkentry, 399 .destroy = set_target_v2_destroy, 400 .me = THIS_MODULE 401 }, 402 { 403 .name = "SET", 404 .revision = 2, 405 .family = NFPROTO_IPV6, 406 .target = set_target_v2, 407 .targetsize = sizeof(struct xt_set_info_target_v2), 408 .checkentry = set_target_v2_checkentry, 409 .destroy = set_target_v2_destroy, 410 .me = THIS_MODULE 411 }, 412}; 413 414static int __init xt_set_init(void) 415{ 416 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); 417 418 if (!ret) { 419 ret = xt_register_targets(set_targets, 420 ARRAY_SIZE(set_targets)); 421 if (ret) 422 xt_unregister_matches(set_matches, 423 ARRAY_SIZE(set_matches)); 424 } 425 return ret; 426} 427 428static void __exit xt_set_fini(void) 429{ 430 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); 431 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); 432} 433 434module_init(xt_set_init); 435module_exit(xt_set_fini); 436