Commit e89fc3f1 authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Patrick McHardy

netfilter: xt_hashlimit: netns support

Make hashtable per-netns.
Make proc files per-netns.
Signed-off-by: default avatarAlexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 7d07d563
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#endif #endif
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <linux/netfilter/x_tables.h> #include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv4/ip_tables.h>
...@@ -40,9 +41,19 @@ MODULE_DESCRIPTION("Xtables: per hash-bucket rate-limit match"); ...@@ -40,9 +41,19 @@ MODULE_DESCRIPTION("Xtables: per hash-bucket rate-limit match");
MODULE_ALIAS("ipt_hashlimit"); MODULE_ALIAS("ipt_hashlimit");
MODULE_ALIAS("ip6t_hashlimit"); MODULE_ALIAS("ip6t_hashlimit");
struct hashlimit_net {
struct hlist_head htables;
struct proc_dir_entry *ipt_hashlimit;
struct proc_dir_entry *ip6t_hashlimit;
};
static int hashlimit_net_id;
static inline struct hashlimit_net *hashlimit_pernet(struct net *net)
{
return net_generic(net, hashlimit_net_id);
}
/* need to declare this at the top */ /* need to declare this at the top */
static struct proc_dir_entry *hashlimit_procdir4;
static struct proc_dir_entry *hashlimit_procdir6;
static const struct file_operations dl_file_ops; static const struct file_operations dl_file_ops;
/* hash table crap */ /* hash table crap */
...@@ -93,13 +104,13 @@ struct xt_hashlimit_htable { ...@@ -93,13 +104,13 @@ struct xt_hashlimit_htable {
/* seq_file stuff */ /* seq_file stuff */
struct proc_dir_entry *pde; struct proc_dir_entry *pde;
struct net *net;
struct hlist_head hash[0]; /* hashtable itself */ struct hlist_head hash[0]; /* hashtable itself */
}; };
static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */ static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */
static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */ static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */
static HLIST_HEAD(hashlimit_htables);
static struct kmem_cache *hashlimit_cachep __read_mostly; static struct kmem_cache *hashlimit_cachep __read_mostly;
static inline bool dst_cmp(const struct dsthash_ent *ent, 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,8 +196,9 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent)
} }
static void htable_gc(unsigned long htlong); static void htable_gc(unsigned long htlong);
static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family) static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_int8_t family)
{ {
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo; struct xt_hashlimit_htable *hinfo;
unsigned int size; unsigned int size;
unsigned int i; unsigned int i;
...@@ -239,26 +251,29 @@ static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family) ...@@ -239,26 +251,29 @@ static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family)
spin_lock_init(&hinfo->lock); spin_lock_init(&hinfo->lock);
hinfo->pde = proc_create_data(minfo->name, 0, hinfo->pde = proc_create_data(minfo->name, 0,
(family == NFPROTO_IPV4) ? (family == NFPROTO_IPV4) ?
hashlimit_procdir4 : hashlimit_procdir6, hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
&dl_file_ops, hinfo); &dl_file_ops, hinfo);
if (!hinfo->pde) { if (!hinfo->pde) {
vfree(hinfo); vfree(hinfo);
return -1; return -1;
} }
hinfo->net = net;
setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo); setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo);
hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
add_timer(&hinfo->timer); add_timer(&hinfo->timer);
spin_lock_bh(&hashlimit_lock); spin_lock_bh(&hashlimit_lock);
hlist_add_head(&hinfo->node, &hashlimit_htables); hlist_add_head(&hinfo->node, &hashlimit_net->htables);
spin_unlock_bh(&hashlimit_lock); spin_unlock_bh(&hashlimit_lock);
return 0; return 0;
} }
static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo,
u_int8_t family)
{ {
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo; struct xt_hashlimit_htable *hinfo;
unsigned int size; unsigned int size;
unsigned int i; unsigned int i;
...@@ -301,19 +316,20 @@ static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) ...@@ -301,19 +316,20 @@ static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family)
hinfo->pde = proc_create_data(minfo->name, 0, hinfo->pde = proc_create_data(minfo->name, 0,
(family == NFPROTO_IPV4) ? (family == NFPROTO_IPV4) ?
hashlimit_procdir4 : hashlimit_procdir6, hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
&dl_file_ops, hinfo); &dl_file_ops, hinfo);
if (hinfo->pde == NULL) { if (hinfo->pde == NULL) {
vfree(hinfo); vfree(hinfo);
return -1; return -1;
} }
hinfo->net = net;
setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo); setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo);
hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
add_timer(&hinfo->timer); add_timer(&hinfo->timer);
spin_lock_bh(&hashlimit_lock); spin_lock_bh(&hashlimit_lock);
hlist_add_head(&hinfo->node, &hashlimit_htables); hlist_add_head(&hinfo->node, &hashlimit_net->htables);
spin_unlock_bh(&hashlimit_lock); spin_unlock_bh(&hashlimit_lock);
return 0; return 0;
...@@ -364,24 +380,30 @@ static void htable_gc(unsigned long htlong) ...@@ -364,24 +380,30 @@ static void htable_gc(unsigned long htlong)
static void htable_destroy(struct xt_hashlimit_htable *hinfo) static void htable_destroy(struct xt_hashlimit_htable *hinfo)
{ {
struct hashlimit_net *hashlimit_net = hashlimit_pernet(hinfo->net);
struct proc_dir_entry *parent;
del_timer_sync(&hinfo->timer); del_timer_sync(&hinfo->timer);
/* remove proc entry */ if (hinfo->family == NFPROTO_IPV4)
remove_proc_entry(hinfo->pde->name, parent = hashlimit_net->ipt_hashlimit;
hinfo->family == NFPROTO_IPV4 ? hashlimit_procdir4 : else
hashlimit_procdir6); parent = hashlimit_net->ip6t_hashlimit;
remove_proc_entry(hinfo->pde->name, parent);
htable_selective_cleanup(hinfo, select_all); htable_selective_cleanup(hinfo, select_all);
vfree(hinfo); vfree(hinfo);
} }
static struct xt_hashlimit_htable *htable_find_get(const char *name, static struct xt_hashlimit_htable *htable_find_get(struct net *net,
const char *name,
u_int8_t family) u_int8_t family)
{ {
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo; struct xt_hashlimit_htable *hinfo;
struct hlist_node *pos; struct hlist_node *pos;
spin_lock_bh(&hashlimit_lock); spin_lock_bh(&hashlimit_lock);
hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) { hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) {
if (!strcmp(name, hinfo->pde->name) && if (!strcmp(name, hinfo->pde->name) &&
hinfo->family == family) { hinfo->family == family) {
atomic_inc(&hinfo->use); atomic_inc(&hinfo->use);
...@@ -665,6 +687,7 @@ hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -665,6 +687,7 @@ hashlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
{ {
struct net *net = par->net;
struct xt_hashlimit_info *r = par->matchinfo; struct xt_hashlimit_info *r = par->matchinfo;
/* Check for overflow. */ /* Check for overflow. */
...@@ -694,8 +717,8 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) ...@@ -694,8 +717,8 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
* the list of htable's in htable_create(), since then we would * the list of htable's in htable_create(), since then we would
* create duplicate proc files. -HW */ * create duplicate proc files. -HW */
mutex_lock(&hlimit_mutex); mutex_lock(&hlimit_mutex);
r->hinfo = htable_find_get(r->name, par->match->family); r->hinfo = htable_find_get(net, r->name, par->match->family);
if (!r->hinfo && htable_create_v0(r, par->match->family) != 0) { if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) {
mutex_unlock(&hlimit_mutex); mutex_unlock(&hlimit_mutex);
return false; return false;
} }
...@@ -706,6 +729,7 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) ...@@ -706,6 +729,7 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
static bool hashlimit_mt_check(const struct xt_mtchk_param *par) static bool hashlimit_mt_check(const struct xt_mtchk_param *par)
{ {
struct net *net = par->net;
struct xt_hashlimit_mtinfo1 *info = par->matchinfo; struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
/* Check for overflow. */ /* Check for overflow. */
...@@ -735,8 +759,8 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par) ...@@ -735,8 +759,8 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par)
* the list of htable's in htable_create(), since then we would * the list of htable's in htable_create(), since then we would
* create duplicate proc files. -HW */ * create duplicate proc files. -HW */
mutex_lock(&hlimit_mutex); mutex_lock(&hlimit_mutex);
info->hinfo = htable_find_get(info->name, par->match->family); info->hinfo = htable_find_get(net, info->name, par->match->family);
if (!info->hinfo && htable_create(info, par->match->family) != 0) { if (!info->hinfo && htable_create(net, info, par->match->family) != 0) {
mutex_unlock(&hlimit_mutex); mutex_unlock(&hlimit_mutex);
return false; return false;
} }
...@@ -953,10 +977,61 @@ static const struct file_operations dl_file_ops = { ...@@ -953,10 +977,61 @@ static const struct file_operations dl_file_ops = {
.release = seq_release .release = seq_release
}; };
static int __net_init hashlimit_proc_net_init(struct net *net)
{
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
hashlimit_net->ipt_hashlimit = proc_mkdir("ipt_hashlimit", net->proc_net);
if (!hashlimit_net->ipt_hashlimit)
return -ENOMEM;
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
hashlimit_net->ip6t_hashlimit = proc_mkdir("ip6t_hashlimit", net->proc_net);
if (!hashlimit_net->ip6t_hashlimit) {
proc_net_remove(net, "ipt_hashlimit");
return -ENOMEM;
}
#endif
return 0;
}
static void __net_exit hashlimit_proc_net_exit(struct net *net)
{
proc_net_remove(net, "ipt_hashlimit");
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
proc_net_remove(net, "ip6t_hashlimit");
#endif
}
static int __net_init hashlimit_net_init(struct net *net)
{
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
INIT_HLIST_HEAD(&hashlimit_net->htables);
return hashlimit_proc_net_init(net);
}
static void __net_exit hashlimit_net_exit(struct net *net)
{
struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
BUG_ON(!hlist_empty(&hashlimit_net->htables));
hashlimit_proc_net_exit(net);
}
static struct pernet_operations hashlimit_net_ops = {
.init = hashlimit_net_init,
.exit = hashlimit_net_exit,
.id = &hashlimit_net_id,
.size = sizeof(struct hashlimit_net),
};
static int __init hashlimit_mt_init(void) static int __init hashlimit_mt_init(void)
{ {
int err; int err;
err = register_pernet_subsys(&hashlimit_net_ops);
if (err < 0)
return err;
err = xt_register_matches(hashlimit_mt_reg, err = xt_register_matches(hashlimit_mt_reg,
ARRAY_SIZE(hashlimit_mt_reg)); ARRAY_SIZE(hashlimit_mt_reg));
if (err < 0) if (err < 0)
...@@ -970,41 +1045,21 @@ static int __init hashlimit_mt_init(void) ...@@ -970,41 +1045,21 @@ static int __init hashlimit_mt_init(void)
printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n"); printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n");
goto err2; goto err2;
} }
hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", init_net.proc_net); return 0;
if (!hashlimit_procdir4) {
printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
"entry\n");
goto err3;
}
err = 0;
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", init_net.proc_net);
if (!hashlimit_procdir6) {
printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
"entry\n");
err = -ENOMEM;
}
#endif
if (!err)
return 0;
remove_proc_entry("ipt_hashlimit", init_net.proc_net);
err3:
kmem_cache_destroy(hashlimit_cachep);
err2: err2:
xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
err1: err1:
unregister_pernet_subsys(&hashlimit_net_ops);
return err; return err;
} }
static void __exit hashlimit_mt_exit(void) static void __exit hashlimit_mt_exit(void)
{ {
remove_proc_entry("ipt_hashlimit", init_net.proc_net);
#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
remove_proc_entry("ip6t_hashlimit", init_net.proc_net);
#endif
kmem_cache_destroy(hashlimit_cachep); kmem_cache_destroy(hashlimit_cachep);
xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
unregister_pernet_subsys(&hashlimit_net_ops);
} }
module_init(hashlimit_mt_init); module_init(hashlimit_mt_init);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment