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 21 MODULE_LICENSE("GPL"); 22 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); 23 MODULE_DESCRIPTION("Xtables: IP set match and target module"); 24 MODULE_ALIAS("xt_SET"); 25 MODULE_ALIAS("ipt_set"); 26 MODULE_ALIAS("ip6t_set"); 27 MODULE_ALIAS("ipt_SET"); 28 MODULE_ALIAS("ip6t_SET"); 29 30 static inline int 31 match_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 41 static bool 42 set_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 52 static void 53 compat_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 68 static int 69 set_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 94 static void 95 set_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 102 static unsigned int 103 set_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 119 static int 120 set_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 162 static void 163 set_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 175 static bool 176 set_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 186 static int 187 set_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 209 static void 210 set_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 217 static unsigned int 218 set_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 236 static int 237 set_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 275 static void 276 set_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 286 static 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 319 static 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 352 static 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 366 static 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 372 module_init(xt_set_init); 373 module_exit(xt_set_fini); 374