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