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