Commit 7d07d563 authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Patrick McHardy

netfilter: xt_recent: netns support

Make recent table list 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 f54e9367
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/inet.h> #include <linux/inet.h>
#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/xt_recent.h> #include <linux/netfilter/xt_recent.h>
...@@ -78,15 +79,26 @@ struct recent_table { ...@@ -78,15 +79,26 @@ struct recent_table {
struct list_head iphash[0]; struct list_head iphash[0];
}; };
static LIST_HEAD(tables); struct recent_net {
struct list_head tables;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *xt_recent;
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
struct proc_dir_entry *ipt_recent;
#endif
#endif
};
static int recent_net_id;
static inline struct recent_net *recent_pernet(struct net *net)
{
return net_generic(net, recent_net_id);
}
static DEFINE_SPINLOCK(recent_lock); static DEFINE_SPINLOCK(recent_lock);
static DEFINE_MUTEX(recent_mutex); static DEFINE_MUTEX(recent_mutex);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
static struct proc_dir_entry *proc_old_dir;
#endif
static struct proc_dir_entry *recent_proc_dir;
static const struct file_operations recent_old_fops, recent_mt_fops; static const struct file_operations recent_old_fops, recent_mt_fops;
#endif #endif
...@@ -172,11 +184,12 @@ static void recent_entry_update(struct recent_table *t, struct recent_entry *e) ...@@ -172,11 +184,12 @@ static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
list_move_tail(&e->lru_list, &t->lru_list); list_move_tail(&e->lru_list, &t->lru_list);
} }
static struct recent_table *recent_table_lookup(const char *name) static struct recent_table *recent_table_lookup(struct recent_net *recent_net,
const char *name)
{ {
struct recent_table *t; struct recent_table *t;
list_for_each_entry(t, &tables, list) list_for_each_entry(t, &recent_net->tables, list)
if (!strcmp(t->name, name)) if (!strcmp(t->name, name))
return t; return t;
return NULL; return NULL;
...@@ -195,6 +208,8 @@ static void recent_table_flush(struct recent_table *t) ...@@ -195,6 +208,8 @@ static void recent_table_flush(struct recent_table *t)
static bool static bool
recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{ {
struct net *net = dev_net(par->in ? par->in : par->out);
struct recent_net *recent_net = recent_pernet(net);
const struct xt_recent_mtinfo *info = par->matchinfo; const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t; struct recent_table *t;
struct recent_entry *e; struct recent_entry *e;
...@@ -227,7 +242,7 @@ recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -227,7 +242,7 @@ recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
ttl++; ttl++;
spin_lock_bh(&recent_lock); spin_lock_bh(&recent_lock);
t = recent_table_lookup(info->name); t = recent_table_lookup(recent_net, info->name);
e = recent_entry_lookup(t, &addr, par->match->family, e = recent_entry_lookup(t, &addr, par->match->family,
(info->check_set & XT_RECENT_TTL) ? ttl : 0); (info->check_set & XT_RECENT_TTL) ? ttl : 0);
if (e == NULL) { if (e == NULL) {
...@@ -271,6 +286,7 @@ recent_mt(const struct sk_buff *skb, const struct xt_match_param *par) ...@@ -271,6 +286,7 @@ recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
static bool recent_mt_check(const struct xt_mtchk_param *par) static bool recent_mt_check(const struct xt_mtchk_param *par)
{ {
struct recent_net *recent_net = recent_pernet(par->net);
const struct xt_recent_mtinfo *info = par->matchinfo; const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t; struct recent_table *t;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
...@@ -297,7 +313,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) ...@@ -297,7 +313,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
return false; return false;
mutex_lock(&recent_mutex); mutex_lock(&recent_mutex);
t = recent_table_lookup(info->name); t = recent_table_lookup(recent_net, info->name);
if (t != NULL) { if (t != NULL) {
t->refcnt++; t->refcnt++;
ret = true; ret = true;
...@@ -314,7 +330,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) ...@@ -314,7 +330,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
for (i = 0; i < ip_list_hash_size; i++) for (i = 0; i < ip_list_hash_size; i++)
INIT_LIST_HEAD(&t->iphash[i]); INIT_LIST_HEAD(&t->iphash[i]);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
pde = proc_create_data(t->name, ip_list_perms, recent_proc_dir, pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent,
&recent_mt_fops, t); &recent_mt_fops, t);
if (pde == NULL) { if (pde == NULL) {
kfree(t); kfree(t);
...@@ -323,10 +339,10 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) ...@@ -323,10 +339,10 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
pde->uid = ip_list_uid; pde->uid = ip_list_uid;
pde->gid = ip_list_gid; pde->gid = ip_list_gid;
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT #ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
pde = proc_create_data(t->name, ip_list_perms, proc_old_dir, pde = proc_create_data(t->name, ip_list_perms, recent_net->ipt_recent,
&recent_old_fops, t); &recent_old_fops, t);
if (pde == NULL) { if (pde == NULL) {
remove_proc_entry(t->name, proc_old_dir); remove_proc_entry(t->name, recent_net->xt_recent);
kfree(t); kfree(t);
goto out; goto out;
} }
...@@ -335,7 +351,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) ...@@ -335,7 +351,7 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
#endif #endif
#endif #endif
spin_lock_bh(&recent_lock); spin_lock_bh(&recent_lock);
list_add_tail(&t->list, &tables); list_add_tail(&t->list, &recent_net->tables);
spin_unlock_bh(&recent_lock); spin_unlock_bh(&recent_lock);
ret = true; ret = true;
out: out:
...@@ -345,20 +361,21 @@ static bool recent_mt_check(const struct xt_mtchk_param *par) ...@@ -345,20 +361,21 @@ static bool recent_mt_check(const struct xt_mtchk_param *par)
static void recent_mt_destroy(const struct xt_mtdtor_param *par) static void recent_mt_destroy(const struct xt_mtdtor_param *par)
{ {
struct recent_net *recent_net = recent_pernet(par->net);
const struct xt_recent_mtinfo *info = par->matchinfo; const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t; struct recent_table *t;
mutex_lock(&recent_mutex); mutex_lock(&recent_mutex);
t = recent_table_lookup(info->name); t = recent_table_lookup(recent_net, info->name);
if (--t->refcnt == 0) { if (--t->refcnt == 0) {
spin_lock_bh(&recent_lock); spin_lock_bh(&recent_lock);
list_del(&t->list); list_del(&t->list);
spin_unlock_bh(&recent_lock); spin_unlock_bh(&recent_lock);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT #ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
remove_proc_entry(t->name, proc_old_dir); remove_proc_entry(t->name, recent_net->ipt_recent);
#endif #endif
remove_proc_entry(t->name, recent_proc_dir); remove_proc_entry(t->name, recent_net->xt_recent);
#endif #endif
recent_table_flush(t); recent_table_flush(t);
kfree(t); kfree(t);
...@@ -607,8 +624,65 @@ static const struct file_operations recent_mt_fops = { ...@@ -607,8 +624,65 @@ static const struct file_operations recent_mt_fops = {
.release = seq_release_private, .release = seq_release_private,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int __net_init recent_proc_net_init(struct net *net)
{
struct recent_net *recent_net = recent_pernet(net);
recent_net->xt_recent = proc_mkdir("xt_recent", net->proc_net);
if (!recent_net->xt_recent)
return -ENOMEM;
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
recent_net->ipt_recent = proc_mkdir("ipt_recent", net->proc_net);
if (!recent_net->ipt_recent) {
proc_net_remove(net, "xt_recent");
return -ENOMEM;
}
#endif
return 0;
}
static void __net_exit recent_proc_net_exit(struct net *net)
{
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
proc_net_remove(net, "ipt_recent");
#endif
proc_net_remove(net, "xt_recent");
}
#else
static inline int recent_proc_net_init(struct net *net)
{
return 0;
}
static inline void recent_proc_net_exit(struct net *net)
{
}
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
static int __net_init recent_net_init(struct net *net)
{
struct recent_net *recent_net = recent_pernet(net);
INIT_LIST_HEAD(&recent_net->tables);
return recent_proc_net_init(net);
}
static void __net_exit recent_net_exit(struct net *net)
{
struct recent_net *recent_net = recent_pernet(net);
BUG_ON(!list_empty(&recent_net->tables));
recent_proc_net_exit(net);
}
static struct pernet_operations recent_net_ops = {
.init = recent_net_init,
.exit = recent_net_exit,
.id = &recent_net_id,
.size = sizeof(struct recent_net),
};
static struct xt_match recent_mt_reg[] __read_mostly = { static struct xt_match recent_mt_reg[] __read_mostly = {
{ {
.name = "recent", .name = "recent",
...@@ -640,39 +714,19 @@ static int __init recent_mt_init(void) ...@@ -640,39 +714,19 @@ static int __init recent_mt_init(void)
return -EINVAL; return -EINVAL;
ip_list_hash_size = 1 << fls(ip_list_tot); ip_list_hash_size = 1 << fls(ip_list_tot);
err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); err = register_pernet_subsys(&recent_net_ops);
#ifdef CONFIG_PROC_FS
if (err) if (err)
return err; return err;
recent_proc_dir = proc_mkdir("xt_recent", init_net.proc_net); err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
if (recent_proc_dir == NULL) { if (err)
xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); unregister_pernet_subsys(&recent_net_ops);
err = -ENOMEM;
}
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
if (err < 0)
return err;
proc_old_dir = proc_mkdir("ipt_recent", init_net.proc_net);
if (proc_old_dir == NULL) {
remove_proc_entry("xt_recent", init_net.proc_net);
xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
err = -ENOMEM;
}
#endif
#endif
return err; return err;
} }
static void __exit recent_mt_exit(void) static void __exit recent_mt_exit(void)
{ {
BUG_ON(!list_empty(&tables));
xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg)); xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
#ifdef CONFIG_PROC_FS unregister_pernet_subsys(&recent_net_ops);
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
remove_proc_entry("ipt_recent", init_net.proc_net);
#endif
remove_proc_entry("xt_recent", init_net.proc_net);
#endif
} }
module_init(recent_mt_init); module_init(recent_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