Commit c14b78e7 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso

netfilter: nfnetlink: add mutex per subsystem

This patch replaces the global lock to one lock per subsystem.
The per-subsystem lock avoids that processes operating
with different subsystems are synchronized.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent 5474f57f
...@@ -34,8 +34,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne ...@@ -34,8 +34,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne
extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
extern void nfnl_lock(void); extern void nfnl_lock(__u8 subsys_id);
extern void nfnl_unlock(void); extern void nfnl_unlock(__u8 subsys_id);
#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
......
...@@ -88,14 +88,14 @@ find_set_type(const char *name, u8 family, u8 revision) ...@@ -88,14 +88,14 @@ find_set_type(const char *name, u8 family, u8 revision)
static bool static bool
load_settype(const char *name) load_settype(const char *name)
{ {
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_IPSET);
pr_debug("try to load ip_set_%s\n", name); pr_debug("try to load ip_set_%s\n", name);
if (request_module("ip_set_%s", name) < 0) { if (request_module("ip_set_%s", name) < 0) {
pr_warning("Can't find ip_set type %s\n", name); pr_warning("Can't find ip_set type %s\n", name);
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
return false; return false;
} }
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
return true; return true;
} }
...@@ -532,7 +532,7 @@ ip_set_nfnl_get(const char *name) ...@@ -532,7 +532,7 @@ ip_set_nfnl_get(const char *name)
ip_set_id_t i, index = IPSET_INVALID_ID; ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s; struct ip_set *s;
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
for (i = 0; i < ip_set_max; i++) { for (i = 0; i < ip_set_max; i++) {
s = nfnl_set(i); s = nfnl_set(i);
if (s != NULL && STREQ(s->name, name)) { if (s != NULL && STREQ(s->name, name)) {
...@@ -541,7 +541,7 @@ ip_set_nfnl_get(const char *name) ...@@ -541,7 +541,7 @@ ip_set_nfnl_get(const char *name)
break; break;
} }
} }
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_IPSET);
return index; return index;
} }
...@@ -561,13 +561,13 @@ ip_set_nfnl_get_byindex(ip_set_id_t index) ...@@ -561,13 +561,13 @@ ip_set_nfnl_get_byindex(ip_set_id_t index)
if (index > ip_set_max) if (index > ip_set_max)
return IPSET_INVALID_ID; return IPSET_INVALID_ID;
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(index); set = nfnl_set(index);
if (set) if (set)
__ip_set_get(set); __ip_set_get(set);
else else
index = IPSET_INVALID_ID; index = IPSET_INVALID_ID;
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_IPSET);
return index; return index;
} }
...@@ -584,11 +584,11 @@ void ...@@ -584,11 +584,11 @@ void
ip_set_nfnl_put(ip_set_id_t index) ip_set_nfnl_put(ip_set_id_t index)
{ {
struct ip_set *set; struct ip_set *set;
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(index); set = nfnl_set(index);
if (set != NULL) if (set != NULL)
__ip_set_put(set); __ip_set_put(set);
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_IPSET);
} }
EXPORT_SYMBOL_GPL(ip_set_nfnl_put); EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
...@@ -1763,10 +1763,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) ...@@ -1763,10 +1763,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
goto done; goto done;
} }
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
find_set_and_id(req_get->set.name, &id); find_set_and_id(req_get->set.name, &id);
req_get->set.index = id; req_get->set.index = id;
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy; goto copy;
} }
case IP_SET_OP_GET_BYINDEX: { case IP_SET_OP_GET_BYINDEX: {
...@@ -1778,11 +1778,11 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) ...@@ -1778,11 +1778,11 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(req_get->set.index); set = nfnl_set(req_get->set.index);
strncpy(req_get->set.name, set ? set->name : "", strncpy(req_get->set.name, set ? set->name : "",
IPSET_MAXNAMELEN); IPSET_MAXNAMELEN);
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy; goto copy;
} }
default: default:
......
...@@ -1256,13 +1256,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, ...@@ -1256,13 +1256,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
if (!parse_nat_setup) { if (!parse_nat_setup) {
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
rcu_read_unlock(); rcu_read_unlock();
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
if (request_module("nf-nat") < 0) { if (request_module("nf-nat") < 0) {
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock(); rcu_read_lock();
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock(); rcu_read_lock();
if (nfnetlink_parse_nat_setup_hook) if (nfnetlink_parse_nat_setup_hook)
return -EAGAIN; return -EAGAIN;
...@@ -1274,13 +1274,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct, ...@@ -1274,13 +1274,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
if (err == -EAGAIN) { if (err == -EAGAIN) {
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
rcu_read_unlock(); rcu_read_unlock();
nfnl_unlock(); nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) { if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) {
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock(); rcu_read_lock();
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
nfnl_lock(); nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock(); rcu_read_lock();
#else #else
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
......
...@@ -36,8 +36,10 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER); ...@@ -36,8 +36,10 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
static char __initdata nfversion[] = "0.30"; static char __initdata nfversion[] = "0.30";
static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT]; static struct {
static DEFINE_MUTEX(nfnl_mutex); struct mutex mutex;
const struct nfnetlink_subsystem __rcu *subsys;
} table[NFNL_SUBSYS_COUNT];
static const int nfnl_group2type[NFNLGRP_MAX+1] = { static const int nfnl_group2type[NFNLGRP_MAX+1] = {
[NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK, [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK,
...@@ -48,27 +50,32 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = { ...@@ -48,27 +50,32 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
}; };
void nfnl_lock(void) void nfnl_lock(__u8 subsys_id)
{ {
mutex_lock(&nfnl_mutex); mutex_lock(&table[subsys_id].mutex);
} }
EXPORT_SYMBOL_GPL(nfnl_lock); EXPORT_SYMBOL_GPL(nfnl_lock);
void nfnl_unlock(void) void nfnl_unlock(__u8 subsys_id)
{ {
mutex_unlock(&nfnl_mutex); mutex_unlock(&table[subsys_id].mutex);
} }
EXPORT_SYMBOL_GPL(nfnl_unlock); EXPORT_SYMBOL_GPL(nfnl_unlock);
static struct mutex *nfnl_get_lock(__u8 subsys_id)
{
return &table[subsys_id].mutex;
}
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
{ {
nfnl_lock(); nfnl_lock(n->subsys_id);
if (subsys_table[n->subsys_id]) { if (table[n->subsys_id].subsys) {
nfnl_unlock(); nfnl_unlock(n->subsys_id);
return -EBUSY; return -EBUSY;
} }
rcu_assign_pointer(subsys_table[n->subsys_id], n); rcu_assign_pointer(table[n->subsys_id].subsys, n);
nfnl_unlock(); nfnl_unlock(n->subsys_id);
return 0; return 0;
} }
...@@ -76,9 +83,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_subsys_register); ...@@ -76,9 +83,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
{ {
nfnl_lock(); nfnl_lock(n->subsys_id);
subsys_table[n->subsys_id] = NULL; table[n->subsys_id].subsys = NULL;
nfnl_unlock(); nfnl_unlock(n->subsys_id);
synchronize_rcu(); synchronize_rcu();
return 0; return 0;
} }
...@@ -91,7 +98,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t ...@@ -91,7 +98,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t
if (subsys_id >= NFNL_SUBSYS_COUNT) if (subsys_id >= NFNL_SUBSYS_COUNT)
return NULL; return NULL;
return rcu_dereference(subsys_table[subsys_id]); return rcu_dereference(table[subsys_id].subsys);
} }
static inline const struct nfnl_callback * static inline const struct nfnl_callback *
...@@ -175,6 +182,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -175,6 +182,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
struct nlattr *attr = (void *)nlh + min_len; struct nlattr *attr = (void *)nlh + min_len;
int attrlen = nlh->nlmsg_len - min_len; int attrlen = nlh->nlmsg_len - min_len;
__u8 subsys_id = NFNL_SUBSYS_ID(type);
err = nla_parse(cda, ss->cb[cb_id].attr_count, err = nla_parse(cda, ss->cb[cb_id].attr_count,
attr, attrlen, ss->cb[cb_id].policy); attr, attrlen, ss->cb[cb_id].policy);
...@@ -189,10 +197,9 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -189,10 +197,9 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
rcu_read_unlock(); rcu_read_unlock();
} else { } else {
rcu_read_unlock(); rcu_read_unlock();
nfnl_lock(); nfnl_lock(subsys_id);
if (rcu_dereference_protected( if (rcu_dereference_protected(table[subsys_id].subsys,
subsys_table[NFNL_SUBSYS_ID(type)], lockdep_is_held(nfnl_get_lock(subsys_id))) != ss ||
lockdep_is_held(&nfnl_mutex)) != ss ||
nfnetlink_find_client(type, ss) != nc) nfnetlink_find_client(type, ss) != nc)
err = -EAGAIN; err = -EAGAIN;
else if (nc->call) else if (nc->call)
...@@ -200,7 +207,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -200,7 +207,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
(const struct nlattr **)cda); (const struct nlattr **)cda);
else else
err = -EINVAL; err = -EINVAL;
nfnl_unlock(); nfnl_unlock(subsys_id);
} }
if (err == -EAGAIN) if (err == -EAGAIN)
goto replay; goto replay;
...@@ -267,6 +274,11 @@ static struct pernet_operations nfnetlink_net_ops = { ...@@ -267,6 +274,11 @@ static struct pernet_operations nfnetlink_net_ops = {
static int __init nfnetlink_init(void) static int __init nfnetlink_init(void)
{ {
int i;
for (i=0; i<NFNL_SUBSYS_COUNT; i++)
mutex_init(&table[i].mutex);
pr_info("Netfilter messages via NETLINK v%s.\n", nfversion); pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
return register_pernet_subsys(&nfnetlink_net_ops); return register_pernet_subsys(&nfnetlink_net_ops);
} }
......
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