diff options
author | Alexey Dobriyan <adobriyan@gmail.com> | 2010-01-18 02:33:28 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2010-01-18 02:33:28 -0500 |
commit | e89fc3f1b06d9241f65e580b002789abaa6d11ac (patch) | |
tree | 565133f45c0d50ee0f631c134f08309256426831 | |
parent | 7d07d5632b672c892a65882c2a119345fd9596c9 (diff) |
netfilter: xt_hashlimit: netns support
Make hashtable per-netns.
Make proc files per-netns.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | net/netfilter/xt_hashlimit.c | 141 |
1 files changed, 98 insertions, 43 deletions
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 944fd11c8989..fb7fcb773a3f 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #endif | 26 | #endif |
27 | 27 | ||
28 | #include <net/net_namespace.h> | 28 | #include <net/net_namespace.h> |
29 | #include <net/netns/generic.h> | ||
29 | 30 | ||
30 | #include <linux/netfilter/x_tables.h> | 31 | #include <linux/netfilter/x_tables.h> |
31 | #include <linux/netfilter_ipv4/ip_tables.h> | 32 | #include <linux/netfilter_ipv4/ip_tables.h> |
@@ -40,9 +41,19 @@ MODULE_DESCRIPTION("Xtables: per hash-bucket rate-limit match"); | |||
40 | MODULE_ALIAS("ipt_hashlimit"); | 41 | MODULE_ALIAS("ipt_hashlimit"); |
41 | MODULE_ALIAS("ip6t_hashlimit"); | 42 | MODULE_ALIAS("ip6t_hashlimit"); |
42 | 43 | ||
44 | struct hashlimit_net { | ||
45 | struct hlist_head htables; | ||
46 | struct proc_dir_entry *ipt_hashlimit; | ||
47 | struct proc_dir_entry *ip6t_hashlimit; | ||
48 | }; | ||
49 | |||
50 | static int hashlimit_net_id; | ||
51 | static inline struct hashlimit_net *hashlimit_pernet(struct net *net) | ||
52 | { | ||
53 | return net_generic(net, hashlimit_net_id); | ||
54 | } | ||
55 | |||
43 | /* need to declare this at the top */ | 56 | /* need to declare this at the top */ |
44 | static struct proc_dir_entry *hashlimit_procdir4; | ||
45 | static struct proc_dir_entry *hashlimit_procdir6; | ||
46 | static const struct file_operations dl_file_ops; | 57 | static const struct file_operations dl_file_ops; |
47 | 58 | ||
48 | /* hash table crap */ | 59 | /* hash table crap */ |
@@ -93,13 +104,13 @@ struct xt_hashlimit_htable { | |||
93 | 104 | ||
94 | /* seq_file stuff */ | 105 | /* seq_file stuff */ |
95 | struct proc_dir_entry *pde; | 106 | struct proc_dir_entry *pde; |
107 | struct net *net; | ||
96 | 108 | ||
97 | struct hlist_head hash[0]; /* hashtable itself */ | 109 | struct hlist_head hash[0]; /* hashtable itself */ |
98 | }; | 110 | }; |
99 | 111 | ||
100 | static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */ | 112 | static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */ |
101 | static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */ | 113 | static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */ |
102 | static HLIST_HEAD(hashlimit_htables); | ||
103 | static struct kmem_cache *hashlimit_cachep __read_mostly; | 114 | static struct kmem_cache *hashlimit_cachep __read_mostly; |
104 | 115 | ||
105 | static inline bool dst_cmp(const struct dsthash_ent *ent, | 116 | static inline bool dst_cmp(const struct dsthash_ent *ent, |
@@ -185,8 +196,9 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent) | |||
185 | } | 196 | } |
186 | static void htable_gc(unsigned long htlong); | 197 | static void htable_gc(unsigned long htlong); |
187 | 198 | ||
188 | static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family) | 199 | static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_int8_t family) |
189 | { | 200 | { |
201 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); | ||
190 | struct xt_hashlimit_htable *hinfo; | 202 | struct xt_hashlimit_htable *hinfo; |
191 | unsigned int size; | 203 | unsigned int size; |
192 | unsigned int i; | 204 | unsigned int i; |
@@ -239,26 +251,29 @@ static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family) | |||
239 | spin_lock_init(&hinfo->lock); | 251 | spin_lock_init(&hinfo->lock); |
240 | hinfo->pde = proc_create_data(minfo->name, 0, | 252 | hinfo->pde = proc_create_data(minfo->name, 0, |
241 | (family == NFPROTO_IPV4) ? | 253 | (family == NFPROTO_IPV4) ? |
242 | hashlimit_procdir4 : hashlimit_procdir6, | 254 | hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit, |
243 | &dl_file_ops, hinfo); | 255 | &dl_file_ops, hinfo); |
244 | if (!hinfo->pde) { | 256 | if (!hinfo->pde) { |
245 | vfree(hinfo); | 257 | vfree(hinfo); |
246 | return -1; | 258 | return -1; |
247 | } | 259 | } |
260 | hinfo->net = net; | ||
248 | 261 | ||
249 | setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo); | 262 | setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo); |
250 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); | 263 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); |
251 | add_timer(&hinfo->timer); | 264 | add_timer(&hinfo->timer); |
252 | 265 | ||
253 | spin_lock_bh(&hashlimit_lock); | 266 | spin_lock_bh(&hashlimit_lock); |
254 | hlist_add_head(&hinfo->node, &hashlimit_htables); | 267 | hlist_add_head(&hinfo->node, &hashlimit_net->htables); |
255 | spin_unlock_bh(&hashlimit_lock); | 268 | spin_unlock_bh(&hashlimit_lock); |
256 | 269 | ||
257 | return 0; | 270 | return 0; |
258 | } | 271 | } |
259 | 272 | ||
260 | static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) | 273 | static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo, |
274 | u_int8_t family) | ||
261 | { | 275 | { |
276 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); | ||
262 | struct xt_hashlimit_htable *hinfo; | 277 | struct xt_hashlimit_htable *hinfo; |
263 | unsigned int size; | 278 | unsigned int size; |
264 | unsigned int i; | 279 | unsigned int i; |
@@ -301,19 +316,20 @@ static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) | |||
301 | 316 | ||
302 | hinfo->pde = proc_create_data(minfo->name, 0, | 317 | hinfo->pde = proc_create_data(minfo->name, 0, |
303 | (family == NFPROTO_IPV4) ? | 318 | (family == NFPROTO_IPV4) ? |
304 | hashlimit_procdir4 : hashlimit_procdir6, | 319 | hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit, |
305 | &dl_file_ops, hinfo); | 320 | &dl_file_ops, hinfo); |
306 | if (hinfo->pde == NULL) { | 321 | if (hinfo->pde == NULL) { |
307 | vfree(hinfo); | 322 | vfree(hinfo); |
308 | return -1; | 323 | return -1; |
309 | } | 324 | } |
325 | hinfo->net = net; | ||
310 | 326 | ||
311 | setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo); | 327 | setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo); |
312 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); | 328 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); |
313 | add_timer(&hinfo->timer); | 329 | add_timer(&hinfo->timer); |
314 | 330 | ||
315 | spin_lock_bh(&hashlimit_lock); | 331 | spin_lock_bh(&hashlimit_lock); |
316 | hlist_add_head(&hinfo->node, &hashlimit_htables); | 332 | hlist_add_head(&hinfo->node, &hashlimit_net->htables); |
317 | spin_unlock_bh(&hashlimit_lock); | 333 | spin_unlock_bh(&hashlimit_lock); |
318 | 334 | ||
319 | return 0; | 335 | return 0; |
@@ -364,24 +380,30 @@ static void htable_gc(unsigned long htlong) | |||
364 | 380 | ||
365 | static void htable_destroy(struct xt_hashlimit_htable *hinfo) | 381 | static void htable_destroy(struct xt_hashlimit_htable *hinfo) |
366 | { | 382 | { |
383 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(hinfo->net); | ||
384 | struct proc_dir_entry *parent; | ||
385 | |||
367 | del_timer_sync(&hinfo->timer); | 386 | del_timer_sync(&hinfo->timer); |
368 | 387 | ||
369 | /* remove proc entry */ | 388 | if (hinfo->family == NFPROTO_IPV4) |
370 | remove_proc_entry(hinfo->pde->name, | 389 | parent = hashlimit_net->ipt_hashlimit; |
371 | hinfo->family == NFPROTO_IPV4 ? hashlimit_procdir4 : | 390 | else |
372 | hashlimit_procdir6); | 391 | parent = hashlimit_net->ip6t_hashlimit; |
392 | remove_proc_entry(hinfo->pde->name, parent); | ||
373 | htable_selective_cleanup(hinfo, select_all); | 393 | htable_selective_cleanup(hinfo, select_all); |
374 | vfree(hinfo); | 394 | vfree(hinfo); |
375 | } | 395 | } |
376 | 396 | ||
377 | static struct xt_hashlimit_htable *htable_find_get(const char *name, | 397 | static struct xt_hashlimit_htable *htable_find_get(struct net *net, |
398 | const char *name, | ||
378 | u_int8_t family) | 399 | u_int8_t family) |
379 | { | 400 | { |
401 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); | ||
380 | struct xt_hashlimit_htable *hinfo; | 402 | struct xt_hashlimit_htable *hinfo; |
381 | struct hlist_node *pos; | 403 | struct hlist_node *pos; |
382 | 404 | ||
383 | spin_lock_bh(&hashlimit_lock); | 405 | spin_lock_bh(&hashlimit_lock); |
384 | hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) { | 406 | hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) { |
385 | if (!strcmp(name, hinfo->pde->name) && | 407 | if (!strcmp(name, hinfo->pde->name) && |
386 | hinfo->family == family) { | 408 | hinfo->family == family) { |
387 | atomic_inc(&hinfo->use); | 409 | atomic_inc(&hinfo->use); |
@@ -665,6 +687,7 @@ hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) | |||
665 | 687 | ||
666 | static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) | 688 | static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) |
667 | { | 689 | { |
690 | struct net *net = par->net; | ||
668 | struct xt_hashlimit_info *r = par->matchinfo; | 691 | struct xt_hashlimit_info *r = par->matchinfo; |
669 | 692 | ||
670 | /* Check for overflow. */ | 693 | /* Check for overflow. */ |
@@ -694,8 +717,8 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) | |||
694 | * the list of htable's in htable_create(), since then we would | 717 | * the list of htable's in htable_create(), since then we would |
695 | * create duplicate proc files. -HW */ | 718 | * create duplicate proc files. -HW */ |
696 | mutex_lock(&hlimit_mutex); | 719 | mutex_lock(&hlimit_mutex); |
697 | r->hinfo = htable_find_get(r->name, par->match->family); | 720 | r->hinfo = htable_find_get(net, r->name, par->match->family); |
698 | if (!r->hinfo && htable_create_v0(r, par->match->family) != 0) { | 721 | if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) { |
699 | mutex_unlock(&hlimit_mutex); | 722 | mutex_unlock(&hlimit_mutex); |
700 | return false; | 723 | return false; |
701 | } | 724 | } |
@@ -706,6 +729,7 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) | |||
706 | 729 | ||
707 | static bool hashlimit_mt_check(const struct xt_mtchk_param *par) | 730 | static bool hashlimit_mt_check(const struct xt_mtchk_param *par) |
708 | { | 731 | { |
732 | struct net *net = par->net; | ||
709 | struct xt_hashlimit_mtinfo1 *info = par->matchinfo; | 733 | struct xt_hashlimit_mtinfo1 *info = par->matchinfo; |
710 | 734 | ||
711 | /* Check for overflow. */ | 735 | /* Check for overflow. */ |
@@ -735,8 +759,8 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par) | |||
735 | * the list of htable's in htable_create(), since then we would | 759 | * the list of htable's in htable_create(), since then we would |
736 | * create duplicate proc files. -HW */ | 760 | * create duplicate proc files. -HW */ |
737 | mutex_lock(&hlimit_mutex); | 761 | mutex_lock(&hlimit_mutex); |
738 | info->hinfo = htable_find_get(info->name, par->match->family); | 762 | info->hinfo = htable_find_get(net, info->name, par->match->family); |
739 | if (!info->hinfo && htable_create(info, par->match->family) != 0) { | 763 | if (!info->hinfo && htable_create(net, info, par->match->family) != 0) { |
740 | mutex_unlock(&hlimit_mutex); | 764 | mutex_unlock(&hlimit_mutex); |
741 | return false; | 765 | return false; |
742 | } | 766 | } |
@@ -953,10 +977,61 @@ static const struct file_operations dl_file_ops = { | |||
953 | .release = seq_release | 977 | .release = seq_release |
954 | }; | 978 | }; |
955 | 979 | ||
980 | static int __net_init hashlimit_proc_net_init(struct net *net) | ||
981 | { | ||
982 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); | ||
983 | |||
984 | hashlimit_net->ipt_hashlimit = proc_mkdir("ipt_hashlimit", net->proc_net); | ||
985 | if (!hashlimit_net->ipt_hashlimit) | ||
986 | return -ENOMEM; | ||
987 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
988 | hashlimit_net->ip6t_hashlimit = proc_mkdir("ip6t_hashlimit", net->proc_net); | ||
989 | if (!hashlimit_net->ip6t_hashlimit) { | ||
990 | proc_net_remove(net, "ipt_hashlimit"); | ||
991 | return -ENOMEM; | ||
992 | } | ||
993 | #endif | ||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static void __net_exit hashlimit_proc_net_exit(struct net *net) | ||
998 | { | ||
999 | proc_net_remove(net, "ipt_hashlimit"); | ||
1000 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
1001 | proc_net_remove(net, "ip6t_hashlimit"); | ||
1002 | #endif | ||
1003 | } | ||
1004 | |||
1005 | static int __net_init hashlimit_net_init(struct net *net) | ||
1006 | { | ||
1007 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); | ||
1008 | |||
1009 | INIT_HLIST_HEAD(&hashlimit_net->htables); | ||
1010 | return hashlimit_proc_net_init(net); | ||
1011 | } | ||
1012 | |||
1013 | static void __net_exit hashlimit_net_exit(struct net *net) | ||
1014 | { | ||
1015 | struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); | ||
1016 | |||
1017 | BUG_ON(!hlist_empty(&hashlimit_net->htables)); | ||
1018 | hashlimit_proc_net_exit(net); | ||
1019 | } | ||
1020 | |||
1021 | static struct pernet_operations hashlimit_net_ops = { | ||
1022 | .init = hashlimit_net_init, | ||
1023 | .exit = hashlimit_net_exit, | ||
1024 | .id = &hashlimit_net_id, | ||
1025 | .size = sizeof(struct hashlimit_net), | ||
1026 | }; | ||
1027 | |||
956 | static int __init hashlimit_mt_init(void) | 1028 | static int __init hashlimit_mt_init(void) |
957 | { | 1029 | { |
958 | int err; | 1030 | int err; |
959 | 1031 | ||
1032 | err = register_pernet_subsys(&hashlimit_net_ops); | ||
1033 | if (err < 0) | ||
1034 | return err; | ||
960 | err = xt_register_matches(hashlimit_mt_reg, | 1035 | err = xt_register_matches(hashlimit_mt_reg, |
961 | ARRAY_SIZE(hashlimit_mt_reg)); | 1036 | ARRAY_SIZE(hashlimit_mt_reg)); |
962 | if (err < 0) | 1037 | if (err < 0) |
@@ -970,41 +1045,21 @@ static int __init hashlimit_mt_init(void) | |||
970 | printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n"); | 1045 | printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n"); |
971 | goto err2; | 1046 | goto err2; |
972 | } | 1047 | } |
973 | hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", init_net.proc_net); | 1048 | return 0; |
974 | if (!hashlimit_procdir4) { | 1049 | |
975 | printk(KERN_ERR "xt_hashlimit: unable to create proc dir " | ||
976 | "entry\n"); | ||
977 | goto err3; | ||
978 | } | ||
979 | err = 0; | ||
980 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
981 | hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", init_net.proc_net); | ||
982 | if (!hashlimit_procdir6) { | ||
983 | printk(KERN_ERR "xt_hashlimit: unable to create proc dir " | ||
984 | "entry\n"); | ||
985 | err = -ENOMEM; | ||
986 | } | ||
987 | #endif | ||
988 | if (!err) | ||
989 | return 0; | ||
990 | remove_proc_entry("ipt_hashlimit", init_net.proc_net); | ||
991 | err3: | ||
992 | kmem_cache_destroy(hashlimit_cachep); | ||
993 | err2: | 1050 | err2: |
994 | xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); | 1051 | xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); |
995 | err1: | 1052 | err1: |
1053 | unregister_pernet_subsys(&hashlimit_net_ops); | ||
996 | return err; | 1054 | return err; |
997 | 1055 | ||
998 | } | 1056 | } |
999 | 1057 | ||
1000 | static void __exit hashlimit_mt_exit(void) | 1058 | static void __exit hashlimit_mt_exit(void) |
1001 | { | 1059 | { |
1002 | remove_proc_entry("ipt_hashlimit", init_net.proc_net); | ||
1003 | #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) | ||
1004 | remove_proc_entry("ip6t_hashlimit", init_net.proc_net); | ||
1005 | #endif | ||
1006 | kmem_cache_destroy(hashlimit_cachep); | 1060 | kmem_cache_destroy(hashlimit_cachep); |
1007 | xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); | 1061 | xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); |
1062 | unregister_pernet_subsys(&hashlimit_net_ops); | ||
1008 | } | 1063 | } |
1009 | 1064 | ||
1010 | module_init(hashlimit_mt_init); | 1065 | module_init(hashlimit_mt_init); |