Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.39-rc1 359 lines 9.3 kB view raw
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 u8 pf, u8 dim, u8 flags, int inv) 33{ 34 if (ip_set_test(index, skb, pf, dim, flags)) 35 inv = !inv; 36 return inv; 37} 38 39/* Revision 0 interface: backward compatible with netfilter/iptables */ 40 41static bool 42set_match_v0(const struct sk_buff *skb, struct xt_action_param *par) 43{ 44 const struct xt_set_info_match_v0 *info = par->matchinfo; 45 46 return match_set(info->match_set.index, skb, par->family, 47 info->match_set.u.compat.dim, 48 info->match_set.u.compat.flags, 49 info->match_set.u.compat.flags & IPSET_INV_MATCH); 50} 51 52static void 53compat_flags(struct xt_set_info_v0 *info) 54{ 55 u_int8_t i; 56 57 /* Fill out compatibility data according to enum ip_set_kopt */ 58 info->u.compat.dim = IPSET_DIM_ZERO; 59 if (info->u.flags[0] & IPSET_MATCH_INV) 60 info->u.compat.flags |= IPSET_INV_MATCH; 61 for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) { 62 info->u.compat.dim++; 63 if (info->u.flags[i] & IPSET_SRC) 64 info->u.compat.flags |= (1<<info->u.compat.dim); 65 } 66} 67 68static int 69set_match_v0_checkentry(const struct xt_mtchk_param *par) 70{ 71 struct xt_set_info_match_v0 *info = par->matchinfo; 72 ip_set_id_t index; 73 74 index = ip_set_nfnl_get_byindex(info->match_set.index); 75 76 if (index == IPSET_INVALID_ID) { 77 pr_warning("Cannot find set indentified by id %u to match\n", 78 info->match_set.index); 79 return -ENOENT; 80 } 81 if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { 82 pr_warning("Protocol error: set match dimension " 83 "is over the limit!\n"); 84 return -ERANGE; 85 } 86 87 /* Fill out compatibility data */ 88 compat_flags(&info->match_set); 89 90 return 0; 91} 92 93static void 94set_match_v0_destroy(const struct xt_mtdtor_param *par) 95{ 96 struct xt_set_info_match_v0 *info = par->matchinfo; 97 98 ip_set_nfnl_put(info->match_set.index); 99} 100 101static unsigned int 102set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) 103{ 104 const struct xt_set_info_target_v0 *info = par->targinfo; 105 106 if (info->add_set.index != IPSET_INVALID_ID) 107 ip_set_add(info->add_set.index, skb, par->family, 108 info->add_set.u.compat.dim, 109 info->add_set.u.compat.flags); 110 if (info->del_set.index != IPSET_INVALID_ID) 111 ip_set_del(info->del_set.index, skb, par->family, 112 info->del_set.u.compat.dim, 113 info->del_set.u.compat.flags); 114 115 return XT_CONTINUE; 116} 117 118static int 119set_target_v0_checkentry(const struct xt_tgchk_param *par) 120{ 121 struct xt_set_info_target_v0 *info = par->targinfo; 122 ip_set_id_t index; 123 124 if (info->add_set.index != IPSET_INVALID_ID) { 125 index = ip_set_nfnl_get_byindex(info->add_set.index); 126 if (index == IPSET_INVALID_ID) { 127 pr_warning("Cannot find add_set index %u as target\n", 128 info->add_set.index); 129 return -ENOENT; 130 } 131 } 132 133 if (info->del_set.index != IPSET_INVALID_ID) { 134 index = ip_set_nfnl_get_byindex(info->del_set.index); 135 if (index == IPSET_INVALID_ID) { 136 pr_warning("Cannot find del_set index %u as target\n", 137 info->del_set.index); 138 return -ENOENT; 139 } 140 } 141 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || 142 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { 143 pr_warning("Protocol error: SET target dimension " 144 "is over the limit!\n"); 145 return -ERANGE; 146 } 147 148 /* Fill out compatibility data */ 149 compat_flags(&info->add_set); 150 compat_flags(&info->del_set); 151 152 return 0; 153} 154 155static void 156set_target_v0_destroy(const struct xt_tgdtor_param *par) 157{ 158 const struct xt_set_info_target_v0 *info = par->targinfo; 159 160 if (info->add_set.index != IPSET_INVALID_ID) 161 ip_set_nfnl_put(info->add_set.index); 162 if (info->del_set.index != IPSET_INVALID_ID) 163 ip_set_nfnl_put(info->del_set.index); 164} 165 166/* Revision 1: current interface to netfilter/iptables */ 167 168static bool 169set_match(const struct sk_buff *skb, struct xt_action_param *par) 170{ 171 const struct xt_set_info_match *info = par->matchinfo; 172 173 return match_set(info->match_set.index, skb, par->family, 174 info->match_set.dim, 175 info->match_set.flags, 176 info->match_set.flags & IPSET_INV_MATCH); 177} 178 179static int 180set_match_checkentry(const struct xt_mtchk_param *par) 181{ 182 struct xt_set_info_match *info = par->matchinfo; 183 ip_set_id_t index; 184 185 index = ip_set_nfnl_get_byindex(info->match_set.index); 186 187 if (index == IPSET_INVALID_ID) { 188 pr_warning("Cannot find set indentified by id %u to match\n", 189 info->match_set.index); 190 return -ENOENT; 191 } 192 if (info->match_set.dim > IPSET_DIM_MAX) { 193 pr_warning("Protocol error: set match dimension " 194 "is over the limit!\n"); 195 return -ERANGE; 196 } 197 198 return 0; 199} 200 201static void 202set_match_destroy(const struct xt_mtdtor_param *par) 203{ 204 struct xt_set_info_match *info = par->matchinfo; 205 206 ip_set_nfnl_put(info->match_set.index); 207} 208 209static unsigned int 210set_target(struct sk_buff *skb, const struct xt_action_param *par) 211{ 212 const struct xt_set_info_target *info = par->targinfo; 213 214 if (info->add_set.index != IPSET_INVALID_ID) 215 ip_set_add(info->add_set.index, 216 skb, par->family, 217 info->add_set.dim, 218 info->add_set.flags); 219 if (info->del_set.index != IPSET_INVALID_ID) 220 ip_set_del(info->del_set.index, 221 skb, par->family, 222 info->add_set.dim, 223 info->del_set.flags); 224 225 return XT_CONTINUE; 226} 227 228static int 229set_target_checkentry(const struct xt_tgchk_param *par) 230{ 231 const struct xt_set_info_target *info = par->targinfo; 232 ip_set_id_t index; 233 234 if (info->add_set.index != IPSET_INVALID_ID) { 235 index = ip_set_nfnl_get_byindex(info->add_set.index); 236 if (index == IPSET_INVALID_ID) { 237 pr_warning("Cannot find add_set index %u as target\n", 238 info->add_set.index); 239 return -ENOENT; 240 } 241 } 242 243 if (info->del_set.index != IPSET_INVALID_ID) { 244 index = ip_set_nfnl_get_byindex(info->del_set.index); 245 if (index == IPSET_INVALID_ID) { 246 pr_warning("Cannot find del_set index %u as target\n", 247 info->del_set.index); 248 return -ENOENT; 249 } 250 } 251 if (info->add_set.dim > IPSET_DIM_MAX || 252 info->del_set.flags > IPSET_DIM_MAX) { 253 pr_warning("Protocol error: SET target dimension " 254 "is over the limit!\n"); 255 return -ERANGE; 256 } 257 258 return 0; 259} 260 261static void 262set_target_destroy(const struct xt_tgdtor_param *par) 263{ 264 const struct xt_set_info_target *info = par->targinfo; 265 266 if (info->add_set.index != IPSET_INVALID_ID) 267 ip_set_nfnl_put(info->add_set.index); 268 if (info->del_set.index != IPSET_INVALID_ID) 269 ip_set_nfnl_put(info->del_set.index); 270} 271 272static struct xt_match set_matches[] __read_mostly = { 273 { 274 .name = "set", 275 .family = NFPROTO_IPV4, 276 .revision = 0, 277 .match = set_match_v0, 278 .matchsize = sizeof(struct xt_set_info_match_v0), 279 .checkentry = set_match_v0_checkentry, 280 .destroy = set_match_v0_destroy, 281 .me = THIS_MODULE 282 }, 283 { 284 .name = "set", 285 .family = NFPROTO_IPV4, 286 .revision = 1, 287 .match = set_match, 288 .matchsize = sizeof(struct xt_set_info_match), 289 .checkentry = set_match_checkentry, 290 .destroy = set_match_destroy, 291 .me = THIS_MODULE 292 }, 293 { 294 .name = "set", 295 .family = NFPROTO_IPV6, 296 .revision = 1, 297 .match = set_match, 298 .matchsize = sizeof(struct xt_set_info_match), 299 .checkentry = set_match_checkentry, 300 .destroy = set_match_destroy, 301 .me = THIS_MODULE 302 }, 303}; 304 305static struct xt_target set_targets[] __read_mostly = { 306 { 307 .name = "SET", 308 .revision = 0, 309 .family = NFPROTO_IPV4, 310 .target = set_target_v0, 311 .targetsize = sizeof(struct xt_set_info_target_v0), 312 .checkentry = set_target_v0_checkentry, 313 .destroy = set_target_v0_destroy, 314 .me = THIS_MODULE 315 }, 316 { 317 .name = "SET", 318 .revision = 1, 319 .family = NFPROTO_IPV4, 320 .target = set_target, 321 .targetsize = sizeof(struct xt_set_info_target), 322 .checkentry = set_target_checkentry, 323 .destroy = set_target_destroy, 324 .me = THIS_MODULE 325 }, 326 { 327 .name = "SET", 328 .revision = 1, 329 .family = NFPROTO_IPV6, 330 .target = set_target, 331 .targetsize = sizeof(struct xt_set_info_target), 332 .checkentry = set_target_checkentry, 333 .destroy = set_target_destroy, 334 .me = THIS_MODULE 335 }, 336}; 337 338static int __init xt_set_init(void) 339{ 340 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); 341 342 if (!ret) { 343 ret = xt_register_targets(set_targets, 344 ARRAY_SIZE(set_targets)); 345 if (ret) 346 xt_unregister_matches(set_matches, 347 ARRAY_SIZE(set_matches)); 348 } 349 return ret; 350} 351 352static void __exit xt_set_fini(void) 353{ 354 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); 355 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); 356} 357 358module_init(xt_set_init); 359module_exit(xt_set_fini);