Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+95 −41
Original line number Original line Diff line number Diff line
@@ -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 {
	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)
	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)
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)
		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)


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)
		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)
	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)
	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)
#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)


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 = {
	.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)
		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);