Commit 3499abb2 authored by Andreas Schultz's avatar Andreas Schultz Committed by Pablo Neira Ayuso

netfilter: nfacct: per network namespace support

- Move the nfnl_acct_list into the network namespace, initialize
  and destroy it per namespace
- Keep track of refcnt on nfacct objects, the old logic does not
  longer work with a per namespace list
- Adjust xt_nfacct to pass the namespace when registring objects
Signed-off-by: default avatarAndreas Schultz <aschultz@tpip.net>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent d2168e84
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define _NFNL_ACCT_H_ #define _NFNL_ACCT_H_
#include <uapi/linux/netfilter/nfnetlink_acct.h> #include <uapi/linux/netfilter/nfnetlink_acct.h>
#include <net/net_namespace.h>
enum { enum {
NFACCT_NO_QUOTA = -1, NFACCT_NO_QUOTA = -1,
...@@ -11,7 +12,7 @@ enum { ...@@ -11,7 +12,7 @@ enum {
struct nf_acct; struct nf_acct;
struct nf_acct *nfnl_acct_find_get(const char *filter_name); struct nf_acct *nfnl_acct_find_get(struct net *net, const char *filter_name);
void nfnl_acct_put(struct nf_acct *acct); void nfnl_acct_put(struct nf_acct *acct);
void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct); void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct);
extern int nfnl_acct_overquota(const struct sk_buff *skb, extern int nfnl_acct_overquota(const struct sk_buff *skb,
......
...@@ -118,6 +118,9 @@ struct net { ...@@ -118,6 +118,9 @@ struct net {
#endif #endif
struct sock *nfnl; struct sock *nfnl;
struct sock *nfnl_stash; struct sock *nfnl_stash;
#if IS_ENABLED(CONFIG_NETFILTER_NETLINK_ACCT)
struct list_head nfnl_acct_list;
#endif
#endif #endif
#ifdef CONFIG_WEXT_CORE #ifdef CONFIG_WEXT_CORE
struct sk_buff_head wext_nlevents; struct sk_buff_head wext_nlevents;
......
...@@ -27,8 +27,6 @@ MODULE_LICENSE("GPL"); ...@@ -27,8 +27,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure"); MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure");
static LIST_HEAD(nfnl_acct_list);
struct nf_acct { struct nf_acct {
atomic64_t pkts; atomic64_t pkts;
atomic64_t bytes; atomic64_t bytes;
...@@ -53,6 +51,7 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, ...@@ -53,6 +51,7 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlattr * const tb[]) const struct nlmsghdr *nlh, const struct nlattr * const tb[])
{ {
struct nf_acct *nfacct, *matching = NULL; struct nf_acct *nfacct, *matching = NULL;
struct net *net = sock_net(nfnl);
char *acct_name; char *acct_name;
unsigned int size = 0; unsigned int size = 0;
u32 flags = 0; u32 flags = 0;
...@@ -64,7 +63,7 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, ...@@ -64,7 +63,7 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
if (strlen(acct_name) == 0) if (strlen(acct_name) == 0)
return -EINVAL; return -EINVAL;
list_for_each_entry(nfacct, &nfnl_acct_list, head) { list_for_each_entry(nfacct, &net->nfnl_acct_list, head) {
if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0)
continue; continue;
...@@ -124,7 +123,7 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, ...@@ -124,7 +123,7 @@ nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb,
be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS])));
} }
atomic_set(&nfacct->refcnt, 1); atomic_set(&nfacct->refcnt, 1);
list_add_tail_rcu(&nfacct->head, &nfnl_acct_list); list_add_tail_rcu(&nfacct->head, &net->nfnl_acct_list);
return 0; return 0;
} }
...@@ -185,6 +184,7 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ...@@ -185,6 +184,7 @@ nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
static int static int
nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct net *net = sock_net(skb->sk);
struct nf_acct *cur, *last; struct nf_acct *cur, *last;
const struct nfacct_filter *filter = cb->data; const struct nfacct_filter *filter = cb->data;
...@@ -196,7 +196,7 @@ nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -196,7 +196,7 @@ nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[1] = 0; cb->args[1] = 0;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) {
if (last) { if (last) {
if (cur != last) if (cur != last)
continue; continue;
...@@ -257,6 +257,7 @@ static int ...@@ -257,6 +257,7 @@ static int
nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlattr * const tb[]) const struct nlmsghdr *nlh, const struct nlattr * const tb[])
{ {
struct net *net = sock_net(nfnl);
int ret = -ENOENT; int ret = -ENOENT;
struct nf_acct *cur; struct nf_acct *cur;
char *acct_name; char *acct_name;
...@@ -283,7 +284,7 @@ nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, ...@@ -283,7 +284,7 @@ nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb,
return -EINVAL; return -EINVAL;
acct_name = nla_data(tb[NFACCT_NAME]); acct_name = nla_data(tb[NFACCT_NAME]);
list_for_each_entry(cur, &nfnl_acct_list, head) { list_for_each_entry(cur, &net->nfnl_acct_list, head) {
struct sk_buff *skb2; struct sk_buff *skb2;
if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
...@@ -336,19 +337,20 @@ static int ...@@ -336,19 +337,20 @@ static int
nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlattr * const tb[]) const struct nlmsghdr *nlh, const struct nlattr * const tb[])
{ {
struct net *net = sock_net(nfnl);
char *acct_name; char *acct_name;
struct nf_acct *cur; struct nf_acct *cur;
int ret = -ENOENT; int ret = -ENOENT;
if (!tb[NFACCT_NAME]) { if (!tb[NFACCT_NAME]) {
list_for_each_entry(cur, &nfnl_acct_list, head) list_for_each_entry(cur, &net->nfnl_acct_list, head)
nfnl_acct_try_del(cur); nfnl_acct_try_del(cur);
return 0; return 0;
} }
acct_name = nla_data(tb[NFACCT_NAME]); acct_name = nla_data(tb[NFACCT_NAME]);
list_for_each_entry(cur, &nfnl_acct_list, head) { list_for_each_entry(cur, &net->nfnl_acct_list, head) {
if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0)
continue; continue;
...@@ -394,12 +396,12 @@ static const struct nfnetlink_subsystem nfnl_acct_subsys = { ...@@ -394,12 +396,12 @@ static const struct nfnetlink_subsystem nfnl_acct_subsys = {
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT);
struct nf_acct *nfnl_acct_find_get(const char *acct_name) struct nf_acct *nfnl_acct_find_get(struct net *net, const char *acct_name)
{ {
struct nf_acct *cur, *acct = NULL; struct nf_acct *cur, *acct = NULL;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) {
if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0)
continue; continue;
...@@ -422,7 +424,9 @@ EXPORT_SYMBOL_GPL(nfnl_acct_find_get); ...@@ -422,7 +424,9 @@ EXPORT_SYMBOL_GPL(nfnl_acct_find_get);
void nfnl_acct_put(struct nf_acct *acct) void nfnl_acct_put(struct nf_acct *acct)
{ {
atomic_dec(&acct->refcnt); if (atomic_dec_and_test(&acct->refcnt))
kfree_rcu(acct, rcu_head);
module_put(THIS_MODULE); module_put(THIS_MODULE);
} }
EXPORT_SYMBOL_GPL(nfnl_acct_put); EXPORT_SYMBOL_GPL(nfnl_acct_put);
...@@ -478,34 +482,59 @@ int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) ...@@ -478,34 +482,59 @@ int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct)
} }
EXPORT_SYMBOL_GPL(nfnl_acct_overquota); EXPORT_SYMBOL_GPL(nfnl_acct_overquota);
static int __net_init nfnl_acct_net_init(struct net *net)
{
INIT_LIST_HEAD(&net->nfnl_acct_list);
return 0;
}
static void __net_exit nfnl_acct_net_exit(struct net *net)
{
struct nf_acct *cur, *tmp;
list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head) {
list_del_rcu(&cur->head);
if (atomic_dec_and_test(&cur->refcnt))
kfree_rcu(cur, rcu_head);
}
}
static struct pernet_operations nfnl_acct_ops = {
.init = nfnl_acct_net_init,
.exit = nfnl_acct_net_exit,
};
static int __init nfnl_acct_init(void) static int __init nfnl_acct_init(void)
{ {
int ret; int ret;
ret = register_pernet_subsys(&nfnl_acct_ops);
if (ret < 0) {
pr_err("nfnl_acct_init: failed to register pernet ops\n");
goto err_out;
}
pr_info("nfnl_acct: registering with nfnetlink.\n"); pr_info("nfnl_acct: registering with nfnetlink.\n");
ret = nfnetlink_subsys_register(&nfnl_acct_subsys); ret = nfnetlink_subsys_register(&nfnl_acct_subsys);
if (ret < 0) { if (ret < 0) {
pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); pr_err("nfnl_acct_init: cannot register with nfnetlink.\n");
goto err_out; goto cleanup_pernet;
} }
return 0; return 0;
cleanup_pernet:
unregister_pernet_subsys(&nfnl_acct_ops);
err_out: err_out:
return ret; return ret;
} }
static void __exit nfnl_acct_exit(void) static void __exit nfnl_acct_exit(void)
{ {
struct nf_acct *cur, *tmp;
pr_info("nfnl_acct: unregistering from nfnetlink.\n"); pr_info("nfnl_acct: unregistering from nfnetlink.\n");
nfnetlink_subsys_unregister(&nfnl_acct_subsys); nfnetlink_subsys_unregister(&nfnl_acct_subsys);
unregister_pernet_subsys(&nfnl_acct_ops);
list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) {
list_del_rcu(&cur->head);
/* We are sure that our objects have no clients at this point,
* it's safe to release them all without checking refcnt. */
kfree_rcu(cur, rcu_head);
}
} }
module_init(nfnl_acct_init); module_init(nfnl_acct_init);
......
...@@ -37,7 +37,7 @@ nfacct_mt_checkentry(const struct xt_mtchk_param *par) ...@@ -37,7 +37,7 @@ nfacct_mt_checkentry(const struct xt_mtchk_param *par)
struct xt_nfacct_match_info *info = par->matchinfo; struct xt_nfacct_match_info *info = par->matchinfo;
struct nf_acct *nfacct; struct nf_acct *nfacct;
nfacct = nfnl_acct_find_get(info->name); nfacct = nfnl_acct_find_get(par->net, info->name);
if (nfacct == NULL) { if (nfacct == NULL) {
pr_info("xt_nfacct: accounting object with name `%s' " pr_info("xt_nfacct: accounting object with name `%s' "
"does not exists\n", info->name); "does not exists\n", info->name);
......
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