at v3.0-rc6 373 lines 9.9 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 ip_set_nfnl_put(info->match_set.index); 85 return -ERANGE; 86 } 87 88 /* Fill out compatibility data */ 89 compat_flags(&info->match_set); 90 91 return 0; 92} 93 94static void 95set_match_v0_destroy(const struct xt_mtdtor_param *par) 96{ 97 struct xt_set_info_match_v0 *info = par->matchinfo; 98 99 ip_set_nfnl_put(info->match_set.index); 100} 101 102static unsigned int 103set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) 104{ 105 const struct xt_set_info_target_v0 *info = par->targinfo; 106 107 if (info->add_set.index != IPSET_INVALID_ID) 108 ip_set_add(info->add_set.index, skb, par->family, 109 info->add_set.u.compat.dim, 110 info->add_set.u.compat.flags); 111 if (info->del_set.index != IPSET_INVALID_ID) 112 ip_set_del(info->del_set.index, skb, par->family, 113 info->del_set.u.compat.dim, 114 info->del_set.u.compat.flags); 115 116 return XT_CONTINUE; 117} 118 119static int 120set_target_v0_checkentry(const struct xt_tgchk_param *par) 121{ 122 struct xt_set_info_target_v0 *info = par->targinfo; 123 ip_set_id_t index; 124 125 if (info->add_set.index != IPSET_INVALID_ID) { 126 index = ip_set_nfnl_get_byindex(info->add_set.index); 127 if (index == IPSET_INVALID_ID) { 128 pr_warning("Cannot find add_set index %u as target\n", 129 info->add_set.index); 130 return -ENOENT; 131 } 132 } 133 134 if (info->del_set.index != IPSET_INVALID_ID) { 135 index = ip_set_nfnl_get_byindex(info->del_set.index); 136 if (index == IPSET_INVALID_ID) { 137 pr_warning("Cannot find del_set index %u as target\n", 138 info->del_set.index); 139 if (info->add_set.index != IPSET_INVALID_ID) 140 ip_set_nfnl_put(info->add_set.index); 141 return -ENOENT; 142 } 143 } 144 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || 145 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { 146 pr_warning("Protocol error: SET target dimension " 147 "is over the limit!\n"); 148 if (info->add_set.index != IPSET_INVALID_ID) 149 ip_set_nfnl_put(info->add_set.index); 150 if (info->del_set.index != IPSET_INVALID_ID) 151 ip_set_nfnl_put(info->del_set.index); 152 return -ERANGE; 153 } 154 155 /* Fill out compatibility data */ 156 compat_flags(&info->add_set); 157 compat_flags(&info->del_set); 158 159 return 0; 160} 161 162static void 163set_target_v0_destroy(const struct xt_tgdtor_param *par) 164{ 165 const struct xt_set_info_target_v0 *info = par->targinfo; 166 167 if (info->add_set.index != IPSET_INVALID_ID) 168 ip_set_nfnl_put(info->add_set.index); 169 if (info->del_set.index != IPSET_INVALID_ID) 170 ip_set_nfnl_put(info->del_set.index); 171} 172 173/* Revision 1: current interface to netfilter/iptables */ 174 175static bool 176set_match(const struct sk_buff *skb, struct xt_action_param *par) 177{ 178 const struct xt_set_info_match *info = par->matchinfo; 179 180 return match_set(info->match_set.index, skb, par->family, 181 info->match_set.dim, 182 info->match_set.flags, 183 info->match_set.flags & IPSET_INV_MATCH); 184} 185 186static int 187set_match_checkentry(const struct xt_mtchk_param *par) 188{ 189 struct xt_set_info_match *info = par->matchinfo; 190 ip_set_id_t index; 191 192 index = ip_set_nfnl_get_byindex(info->match_set.index); 193 194 if (index == IPSET_INVALID_ID) { 195 pr_warning("Cannot find set indentified by id %u to match\n", 196 info->match_set.index); 197 return -ENOENT; 198 } 199 if (info->match_set.dim > IPSET_DIM_MAX) { 200 pr_warning("Protocol error: set match dimension " 201 "is over the limit!\n"); 202 ip_set_nfnl_put(info->match_set.index); 203 return -ERANGE; 204 } 205 206 return 0; 207} 208 209static void 210set_match_destroy(const struct xt_mtdtor_param *par) 211{ 212 struct xt_set_info_match *info = par->matchinfo; 213 214 ip_set_nfnl_put(info->match_set.index); 215} 216 217static unsigned int 218set_target(struct sk_buff *skb, const struct xt_action_param *par) 219{ 220 const struct xt_set_info_target *info = par->targinfo; 221 222 if (info->add_set.index != IPSET_INVALID_ID) 223 ip_set_add(info->add_set.index, 224 skb, par->family, 225 info->add_set.dim, 226 info->add_set.flags); 227 if (info->del_set.index != IPSET_INVALID_ID) 228 ip_set_del(info->del_set.index, 229 skb, par->family, 230 info->del_set.dim, 231 info->del_set.flags); 232 233 return XT_CONTINUE; 234} 235 236static int 237set_target_checkentry(const struct xt_tgchk_param *par) 238{ 239 const struct xt_set_info_target *info = par->targinfo; 240 ip_set_id_t index; 241 242 if (info->add_set.index != IPSET_INVALID_ID) { 243 index = ip_set_nfnl_get_byindex(info->add_set.index); 244 if (index == IPSET_INVALID_ID) { 245 pr_warning("Cannot find add_set index %u as target\n", 246 info->add_set.index); 247 return -ENOENT; 248 } 249 } 250 251 if (info->del_set.index != IPSET_INVALID_ID) { 252 index = ip_set_nfnl_get_byindex(info->del_set.index); 253 if (index == IPSET_INVALID_ID) { 254 pr_warning("Cannot find del_set index %u as target\n", 255 info->del_set.index); 256 if (info->add_set.index != IPSET_INVALID_ID) 257 ip_set_nfnl_put(info->add_set.index); 258 return -ENOENT; 259 } 260 } 261 if (info->add_set.dim > IPSET_DIM_MAX || 262 info->del_set.dim > IPSET_DIM_MAX) { 263 pr_warning("Protocol error: SET target dimension " 264 "is over the limit!\n"); 265 if (info->add_set.index != IPSET_INVALID_ID) 266 ip_set_nfnl_put(info->add_set.index); 267 if (info->del_set.index != IPSET_INVALID_ID) 268 ip_set_nfnl_put(info->del_set.index); 269 return -ERANGE; 270 } 271 272 return 0; 273} 274 275static void 276set_target_destroy(const struct xt_tgdtor_param *par) 277{ 278 const struct xt_set_info_target *info = par->targinfo; 279 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} 285 286static struct xt_match set_matches[] __read_mostly = { 287 { 288 .name = "set", 289 .family = NFPROTO_IPV4, 290 .revision = 0, 291 .match = set_match_v0, 292 .matchsize = sizeof(struct xt_set_info_match_v0), 293 .checkentry = set_match_v0_checkentry, 294 .destroy = set_match_v0_destroy, 295 .me = THIS_MODULE 296 }, 297 { 298 .name = "set", 299 .family = NFPROTO_IPV4, 300 .revision = 1, 301 .match = set_match, 302 .matchsize = sizeof(struct xt_set_info_match), 303 .checkentry = set_match_checkentry, 304 .destroy = set_match_destroy, 305 .me = THIS_MODULE 306 }, 307 { 308 .name = "set", 309 .family = NFPROTO_IPV6, 310 .revision = 1, 311 .match = set_match, 312 .matchsize = sizeof(struct xt_set_info_match), 313 .checkentry = set_match_checkentry, 314 .destroy = set_match_destroy, 315 .me = THIS_MODULE 316 }, 317}; 318 319static struct xt_target set_targets[] __read_mostly = { 320 { 321 .name = "SET", 322 .revision = 0, 323 .family = NFPROTO_IPV4, 324 .target = set_target_v0, 325 .targetsize = sizeof(struct xt_set_info_target_v0), 326 .checkentry = set_target_v0_checkentry, 327 .destroy = set_target_v0_destroy, 328 .me = THIS_MODULE 329 }, 330 { 331 .name = "SET", 332 .revision = 1, 333 .family = NFPROTO_IPV4, 334 .target = set_target, 335 .targetsize = sizeof(struct xt_set_info_target), 336 .checkentry = set_target_checkentry, 337 .destroy = set_target_destroy, 338 .me = THIS_MODULE 339 }, 340 { 341 .name = "SET", 342 .revision = 1, 343 .family = NFPROTO_IPV6, 344 .target = set_target, 345 .targetsize = sizeof(struct xt_set_info_target), 346 .checkentry = set_target_checkentry, 347 .destroy = set_target_destroy, 348 .me = THIS_MODULE 349 }, 350}; 351 352static int __init xt_set_init(void) 353{ 354 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); 355 356 if (!ret) { 357 ret = xt_register_targets(set_targets, 358 ARRAY_SIZE(set_targets)); 359 if (ret) 360 xt_unregister_matches(set_matches, 361 ARRAY_SIZE(set_matches)); 362 } 363 return ret; 364} 365 366static void __exit xt_set_fini(void) 367{ 368 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); 369 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); 370} 371 372module_init(xt_set_init); 373module_exit(xt_set_fini);