xt_set.c revision b66554cf03fe866b3fb7b9f40f430b8ba09f41c8
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#include <linux/version.h> 17 18#include <linux/netfilter/x_tables.h> 19#include <linux/netfilter/xt_set.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 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 return match_set(info->match_set.index, skb, par, &opt, 193 info->match_set.flags & IPSET_INV_MATCH); 194} 195 196static int 197set_match_v1_checkentry(const struct xt_mtchk_param *par) 198{ 199 struct xt_set_info_match_v1 *info = par->matchinfo; 200 ip_set_id_t index; 201 202 index = ip_set_nfnl_get_byindex(info->match_set.index); 203 204 if (index == IPSET_INVALID_ID) { 205 pr_warning("Cannot find set indentified by id %u to match\n", 206 info->match_set.index); 207 return -ENOENT; 208 } 209 if (info->match_set.dim > IPSET_DIM_MAX) { 210 pr_warning("Protocol error: set match dimension " 211 "is over the limit!\n"); 212 ip_set_nfnl_put(info->match_set.index); 213 return -ERANGE; 214 } 215 216 return 0; 217} 218 219static void 220set_match_v1_destroy(const struct xt_mtdtor_param *par) 221{ 222 struct xt_set_info_match_v1 *info = par->matchinfo; 223 224 ip_set_nfnl_put(info->match_set.index); 225} 226 227static unsigned int 228set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) 229{ 230 const struct xt_set_info_target_v1 *info = par->targinfo; 231 ADT_OPT(add_opt, par->family, info->add_set.dim, 232 info->add_set.flags, 0, UINT_MAX); 233 ADT_OPT(del_opt, par->family, info->del_set.dim, 234 info->del_set.flags, 0, UINT_MAX); 235 236 if (info->add_set.index != IPSET_INVALID_ID) 237 ip_set_add(info->add_set.index, skb, par, &add_opt); 238 if (info->del_set.index != IPSET_INVALID_ID) 239 ip_set_del(info->del_set.index, skb, par, &del_opt); 240 241 return XT_CONTINUE; 242} 243 244static int 245set_target_v1_checkentry(const struct xt_tgchk_param *par) 246{ 247 const struct xt_set_info_target_v1 *info = par->targinfo; 248 ip_set_id_t index; 249 250 if (info->add_set.index != IPSET_INVALID_ID) { 251 index = ip_set_nfnl_get_byindex(info->add_set.index); 252 if (index == IPSET_INVALID_ID) { 253 pr_warning("Cannot find add_set index %u as target\n", 254 info->add_set.index); 255 return -ENOENT; 256 } 257 } 258 259 if (info->del_set.index != IPSET_INVALID_ID) { 260 index = ip_set_nfnl_get_byindex(info->del_set.index); 261 if (index == IPSET_INVALID_ID) { 262 pr_warning("Cannot find del_set index %u as target\n", 263 info->del_set.index); 264 if (info->add_set.index != IPSET_INVALID_ID) 265 ip_set_nfnl_put(info->add_set.index); 266 return -ENOENT; 267 } 268 } 269 if (info->add_set.dim > IPSET_DIM_MAX || 270 info->del_set.dim > IPSET_DIM_MAX) { 271 pr_warning("Protocol error: SET target dimension " 272 "is over the limit!\n"); 273 if (info->add_set.index != IPSET_INVALID_ID) 274 ip_set_nfnl_put(info->add_set.index); 275 if (info->del_set.index != IPSET_INVALID_ID) 276 ip_set_nfnl_put(info->del_set.index); 277 return -ERANGE; 278 } 279 280 return 0; 281} 282 283static void 284set_target_v1_destroy(const struct xt_tgdtor_param *par) 285{ 286 const struct xt_set_info_target_v1 *info = par->targinfo; 287 288 if (info->add_set.index != IPSET_INVALID_ID) 289 ip_set_nfnl_put(info->add_set.index); 290 if (info->del_set.index != IPSET_INVALID_ID) 291 ip_set_nfnl_put(info->del_set.index); 292} 293 294/* Revision 2 target */ 295 296static unsigned int 297set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) 298{ 299 const struct xt_set_info_target_v2 *info = par->targinfo; 300 ADT_OPT(add_opt, par->family, info->add_set.dim, 301 info->add_set.flags, info->flags, info->timeout); 302 ADT_OPT(del_opt, par->family, info->del_set.dim, 303 info->del_set.flags, 0, UINT_MAX); 304 305 if (info->add_set.index != IPSET_INVALID_ID) 306 ip_set_add(info->add_set.index, skb, par, &add_opt); 307 if (info->del_set.index != IPSET_INVALID_ID) 308 ip_set_del(info->del_set.index, skb, par, &del_opt); 309 310 return XT_CONTINUE; 311} 312 313#define set_target_v2_checkentry set_target_v1_checkentry 314#define set_target_v2_destroy set_target_v1_destroy 315 316static struct xt_match set_matches[] __read_mostly = { 317 { 318 .name = "set", 319 .family = NFPROTO_IPV4, 320 .revision = 0, 321 .match = set_match_v0, 322 .matchsize = sizeof(struct xt_set_info_match_v0), 323 .checkentry = set_match_v0_checkentry, 324 .destroy = set_match_v0_destroy, 325 .me = THIS_MODULE 326 }, 327 { 328 .name = "set", 329 .family = NFPROTO_IPV4, 330 .revision = 1, 331 .match = set_match_v1, 332 .matchsize = sizeof(struct xt_set_info_match_v1), 333 .checkentry = set_match_v1_checkentry, 334 .destroy = set_match_v1_destroy, 335 .me = THIS_MODULE 336 }, 337 { 338 .name = "set", 339 .family = NFPROTO_IPV6, 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 349static struct xt_target set_targets[] __read_mostly = { 350 { 351 .name = "SET", 352 .revision = 0, 353 .family = NFPROTO_IPV4, 354 .target = set_target_v0, 355 .targetsize = sizeof(struct xt_set_info_target_v0), 356 .checkentry = set_target_v0_checkentry, 357 .destroy = set_target_v0_destroy, 358 .me = THIS_MODULE 359 }, 360 { 361 .name = "SET", 362 .revision = 1, 363 .family = NFPROTO_IPV4, 364 .target = set_target_v1, 365 .targetsize = sizeof(struct xt_set_info_target_v1), 366 .checkentry = set_target_v1_checkentry, 367 .destroy = set_target_v1_destroy, 368 .me = THIS_MODULE 369 }, 370 { 371 .name = "SET", 372 .revision = 1, 373 .family = NFPROTO_IPV6, 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 = 2, 383 .family = NFPROTO_IPV4, 384 .target = set_target_v2, 385 .targetsize = sizeof(struct xt_set_info_target_v2), 386 .checkentry = set_target_v2_checkentry, 387 .destroy = set_target_v2_destroy, 388 .me = THIS_MODULE 389 }, 390 { 391 .name = "SET", 392 .revision = 2, 393 .family = NFPROTO_IPV6, 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 402static int __init xt_set_init(void) 403{ 404 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); 405 406 if (!ret) { 407 ret = xt_register_targets(set_targets, 408 ARRAY_SIZE(set_targets)); 409 if (ret) 410 xt_unregister_matches(set_matches, 411 ARRAY_SIZE(set_matches)); 412 } 413 return ret; 414} 415 416static void __exit xt_set_fini(void) 417{ 418 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); 419 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); 420} 421 422module_init(xt_set_init); 423module_exit(xt_set_fini); 424