1 /* Copyright (C) 2000-2002 Joakim Axelsson <[email protected]> 2 * Patrick Schaaf <[email protected]> 3 * Martin Josefsson <[email protected]> 4 * Copyright (C) 2003-2011 Jozsef Kadlecsik <[email protected]> 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 <[email protected]>"); 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 return -ERANGE; 85 } 86 87 /* Fill out compatibility data */ 88 compat_flags(&info->match_set); 89 90 return 0; 91 } 92 93 static void 94 set_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 101 static unsigned int 102 set_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 118 static int 119 set_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 155 static void 156 set_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 168 static bool 169 set_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 179 static int 180 set_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 201 static void 202 set_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 209 static unsigned int 210 set_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 228 static int 229 set_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 261 static void 262 set_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 272 static 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 305 static 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 338 static 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 352 static 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 358 module_init(xt_set_init); 359 module_exit(xt_set_fini); 360