aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2013-02-04 19:50:26 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2013-02-04 22:07:35 -0500
commitc14b78e7decd0d1d5add6a4604feb8609fe920a9 (patch)
tree820b4a0a2f3702581452e05d53d44097cb240d81
parent5474f57f7d686ac918355419cb71496f835aaf5d (diff)
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: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/linux/netfilter/nfnetlink.h4
-rw-r--r--net/netfilter/ipset/ip_set_core.c26
-rw-r--r--net/netfilter/nf_conntrack_netlink.c12
-rw-r--r--net/netfilter/nfnetlink.c52
4 files changed, 53 insertions, 41 deletions
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 4966ddec039b..ecbb8e495912 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -34,8 +34,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne
34extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); 34extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
35extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); 35extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
36 36
37extern void nfnl_lock(void); 37extern void nfnl_lock(__u8 subsys_id);
38extern void nfnl_unlock(void); 38extern void nfnl_unlock(__u8 subsys_id);
39 39
40#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ 40#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
41 MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) 41 MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 6d6d8f2b033e..f82b2e606cfd 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -88,14 +88,14 @@ find_set_type(const char *name, u8 family, u8 revision)
88static bool 88static bool
89load_settype(const char *name) 89load_settype(const char *name)
90{ 90{
91 nfnl_unlock(); 91 nfnl_unlock(NFNL_SUBSYS_IPSET);
92 pr_debug("try to load ip_set_%s\n", name); 92 pr_debug("try to load ip_set_%s\n", name);
93 if (request_module("ip_set_%s", name) < 0) { 93 if (request_module("ip_set_%s", name) < 0) {
94 pr_warning("Can't find ip_set type %s\n", name); 94 pr_warning("Can't find ip_set type %s\n", name);
95 nfnl_lock(); 95 nfnl_lock(NFNL_SUBSYS_IPSET);
96 return false; 96 return false;
97 } 97 }
98 nfnl_lock(); 98 nfnl_lock(NFNL_SUBSYS_IPSET);
99 return true; 99 return true;
100} 100}
101 101
@@ -532,7 +532,7 @@ ip_set_nfnl_get(const char *name)
532 ip_set_id_t i, index = IPSET_INVALID_ID; 532 ip_set_id_t i, index = IPSET_INVALID_ID;
533 struct ip_set *s; 533 struct ip_set *s;
534 534
535 nfnl_lock(); 535 nfnl_lock(NFNL_SUBSYS_IPSET);
536 for (i = 0; i < ip_set_max; i++) { 536 for (i = 0; i < ip_set_max; i++) {
537 s = nfnl_set(i); 537 s = nfnl_set(i);
538 if (s != NULL && STREQ(s->name, name)) { 538 if (s != NULL && STREQ(s->name, name)) {
@@ -541,7 +541,7 @@ ip_set_nfnl_get(const char *name)
541 break; 541 break;
542 } 542 }
543 } 543 }
544 nfnl_unlock(); 544 nfnl_unlock(NFNL_SUBSYS_IPSET);
545 545
546 return index; 546 return index;
547} 547}
@@ -561,13 +561,13 @@ ip_set_nfnl_get_byindex(ip_set_id_t index)
561 if (index > ip_set_max) 561 if (index > ip_set_max)
562 return IPSET_INVALID_ID; 562 return IPSET_INVALID_ID;
563 563
564 nfnl_lock(); 564 nfnl_lock(NFNL_SUBSYS_IPSET);
565 set = nfnl_set(index); 565 set = nfnl_set(index);
566 if (set) 566 if (set)
567 __ip_set_get(set); 567 __ip_set_get(set);
568 else 568 else
569 index = IPSET_INVALID_ID; 569 index = IPSET_INVALID_ID;
570 nfnl_unlock(); 570 nfnl_unlock(NFNL_SUBSYS_IPSET);
571 571
572 return index; 572 return index;
573} 573}
@@ -584,11 +584,11 @@ void
584ip_set_nfnl_put(ip_set_id_t index) 584ip_set_nfnl_put(ip_set_id_t index)
585{ 585{
586 struct ip_set *set; 586 struct ip_set *set;
587 nfnl_lock(); 587 nfnl_lock(NFNL_SUBSYS_IPSET);
588 set = nfnl_set(index); 588 set = nfnl_set(index);
589 if (set != NULL) 589 if (set != NULL)
590 __ip_set_put(set); 590 __ip_set_put(set);
591 nfnl_unlock(); 591 nfnl_unlock(NFNL_SUBSYS_IPSET);
592} 592}
593EXPORT_SYMBOL_GPL(ip_set_nfnl_put); 593EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
594 594
@@ -1763,10 +1763,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
1763 goto done; 1763 goto done;
1764 } 1764 }
1765 req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; 1765 req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
1766 nfnl_lock(); 1766 nfnl_lock(NFNL_SUBSYS_IPSET);
1767 find_set_and_id(req_get->set.name, &id); 1767 find_set_and_id(req_get->set.name, &id);
1768 req_get->set.index = id; 1768 req_get->set.index = id;
1769 nfnl_unlock(); 1769 nfnl_unlock(NFNL_SUBSYS_IPSET);
1770 goto copy; 1770 goto copy;
1771 } 1771 }
1772 case IP_SET_OP_GET_BYINDEX: { 1772 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 ret = -EINVAL; 1778 ret = -EINVAL;
1779 goto done; 1779 goto done;
1780 } 1780 }
1781 nfnl_lock(); 1781 nfnl_lock(NFNL_SUBSYS_IPSET);
1782 set = nfnl_set(req_get->set.index); 1782 set = nfnl_set(req_get->set.index);
1783 strncpy(req_get->set.name, set ? set->name : "", 1783 strncpy(req_get->set.name, set ? set->name : "",
1784 IPSET_MAXNAMELEN); 1784 IPSET_MAXNAMELEN);
1785 nfnl_unlock(); 1785 nfnl_unlock(NFNL_SUBSYS_IPSET);
1786 goto copy; 1786 goto copy;
1787 } 1787 }
1788 default: 1788 default:
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 2334cc5d2b16..d490a300ce2b 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1256,13 +1256,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
1256 if (!parse_nat_setup) { 1256 if (!parse_nat_setup) {
1257#ifdef CONFIG_MODULES 1257#ifdef CONFIG_MODULES
1258 rcu_read_unlock(); 1258 rcu_read_unlock();
1259 nfnl_unlock(); 1259 nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
1260 if (request_module("nf-nat") < 0) { 1260 if (request_module("nf-nat") < 0) {
1261 nfnl_lock(); 1261 nfnl_lock(NFNL_SUBSYS_CTNETLINK);
1262 rcu_read_lock(); 1262 rcu_read_lock();
1263 return -EOPNOTSUPP; 1263 return -EOPNOTSUPP;
1264 } 1264 }
1265 nfnl_lock(); 1265 nfnl_lock(NFNL_SUBSYS_CTNETLINK);
1266 rcu_read_lock(); 1266 rcu_read_lock();
1267 if (nfnetlink_parse_nat_setup_hook) 1267 if (nfnetlink_parse_nat_setup_hook)
1268 return -EAGAIN; 1268 return -EAGAIN;
@@ -1274,13 +1274,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
1274 if (err == -EAGAIN) { 1274 if (err == -EAGAIN) {
1275#ifdef CONFIG_MODULES 1275#ifdef CONFIG_MODULES
1276 rcu_read_unlock(); 1276 rcu_read_unlock();
1277 nfnl_unlock(); 1277 nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
1278 if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) { 1278 if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) {
1279 nfnl_lock(); 1279 nfnl_lock(NFNL_SUBSYS_CTNETLINK);
1280 rcu_read_lock(); 1280 rcu_read_lock();
1281 return -EOPNOTSUPP; 1281 return -EOPNOTSUPP;
1282 } 1282 }
1283 nfnl_lock(); 1283 nfnl_lock(NFNL_SUBSYS_CTNETLINK);
1284 rcu_read_lock(); 1284 rcu_read_lock();
1285#else 1285#else
1286 err = -EOPNOTSUPP; 1286 err = -EOPNOTSUPP;
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 58a09b7c3f6d..d578ec251712 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -36,8 +36,10 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
36 36
37static char __initdata nfversion[] = "0.30"; 37static char __initdata nfversion[] = "0.30";
38 38
39static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT]; 39static struct {
40static DEFINE_MUTEX(nfnl_mutex); 40 struct mutex mutex;
41 const struct nfnetlink_subsystem __rcu *subsys;
42} table[NFNL_SUBSYS_COUNT];
41 43
42static const int nfnl_group2type[NFNLGRP_MAX+1] = { 44static const int nfnl_group2type[NFNLGRP_MAX+1] = {
43 [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK, 45 [NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK,
@@ -48,27 +50,32 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
48 [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP, 50 [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
49}; 51};
50 52
51void nfnl_lock(void) 53void nfnl_lock(__u8 subsys_id)
52{ 54{
53 mutex_lock(&nfnl_mutex); 55 mutex_lock(&table[subsys_id].mutex);
54} 56}
55EXPORT_SYMBOL_GPL(nfnl_lock); 57EXPORT_SYMBOL_GPL(nfnl_lock);
56 58
57void nfnl_unlock(void) 59void nfnl_unlock(__u8 subsys_id)
58{ 60{
59 mutex_unlock(&nfnl_mutex); 61 mutex_unlock(&table[subsys_id].mutex);
60} 62}
61EXPORT_SYMBOL_GPL(nfnl_unlock); 63EXPORT_SYMBOL_GPL(nfnl_unlock);
62 64
65static struct mutex *nfnl_get_lock(__u8 subsys_id)
66{
67 return &table[subsys_id].mutex;
68}
69
63int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n) 70int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
64{ 71{
65 nfnl_lock(); 72 nfnl_lock(n->subsys_id);
66 if (subsys_table[n->subsys_id]) { 73 if (table[n->subsys_id].subsys) {
67 nfnl_unlock(); 74 nfnl_unlock(n->subsys_id);
68 return -EBUSY; 75 return -EBUSY;
69 } 76 }
70 rcu_assign_pointer(subsys_table[n->subsys_id], n); 77 rcu_assign_pointer(table[n->subsys_id].subsys, n);
71 nfnl_unlock(); 78 nfnl_unlock(n->subsys_id);
72 79
73 return 0; 80 return 0;
74} 81}
@@ -76,9 +83,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
76 83
77int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n) 84int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
78{ 85{
79 nfnl_lock(); 86 nfnl_lock(n->subsys_id);
80 subsys_table[n->subsys_id] = NULL; 87 table[n->subsys_id].subsys = NULL;
81 nfnl_unlock(); 88 nfnl_unlock(n->subsys_id);
82 synchronize_rcu(); 89 synchronize_rcu();
83 return 0; 90 return 0;
84} 91}
@@ -91,7 +98,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t
91 if (subsys_id >= NFNL_SUBSYS_COUNT) 98 if (subsys_id >= NFNL_SUBSYS_COUNT)
92 return NULL; 99 return NULL;
93 100
94 return rcu_dereference(subsys_table[subsys_id]); 101 return rcu_dereference(table[subsys_id].subsys);
95} 102}
96 103
97static inline const struct nfnl_callback * 104static inline const struct nfnl_callback *
@@ -175,6 +182,7 @@ replay:
175 struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; 182 struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
176 struct nlattr *attr = (void *)nlh + min_len; 183 struct nlattr *attr = (void *)nlh + min_len;
177 int attrlen = nlh->nlmsg_len - min_len; 184 int attrlen = nlh->nlmsg_len - min_len;
185 __u8 subsys_id = NFNL_SUBSYS_ID(type);
178 186
179 err = nla_parse(cda, ss->cb[cb_id].attr_count, 187 err = nla_parse(cda, ss->cb[cb_id].attr_count,
180 attr, attrlen, ss->cb[cb_id].policy); 188 attr, attrlen, ss->cb[cb_id].policy);
@@ -189,10 +197,9 @@ replay:
189 rcu_read_unlock(); 197 rcu_read_unlock();
190 } else { 198 } else {
191 rcu_read_unlock(); 199 rcu_read_unlock();
192 nfnl_lock(); 200 nfnl_lock(subsys_id);
193 if (rcu_dereference_protected( 201 if (rcu_dereference_protected(table[subsys_id].subsys,
194 subsys_table[NFNL_SUBSYS_ID(type)], 202 lockdep_is_held(nfnl_get_lock(subsys_id))) != ss ||
195 lockdep_is_held(&nfnl_mutex)) != ss ||
196 nfnetlink_find_client(type, ss) != nc) 203 nfnetlink_find_client(type, ss) != nc)
197 err = -EAGAIN; 204 err = -EAGAIN;
198 else if (nc->call) 205 else if (nc->call)
@@ -200,7 +207,7 @@ replay:
200 (const struct nlattr **)cda); 207 (const struct nlattr **)cda);
201 else 208 else
202 err = -EINVAL; 209 err = -EINVAL;
203 nfnl_unlock(); 210 nfnl_unlock(subsys_id);
204 } 211 }
205 if (err == -EAGAIN) 212 if (err == -EAGAIN)
206 goto replay; 213 goto replay;
@@ -267,6 +274,11 @@ static struct pernet_operations nfnetlink_net_ops = {
267 274
268static int __init nfnetlink_init(void) 275static int __init nfnetlink_init(void)
269{ 276{
277 int i;
278
279 for (i=0; i<NFNL_SUBSYS_COUNT; i++)
280 mutex_init(&table[i].mutex);
281
270 pr_info("Netfilter messages via NETLINK v%s.\n", nfversion); 282 pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
271 return register_pernet_subsys(&nfnetlink_net_ops); 283 return register_pernet_subsys(&nfnetlink_net_ops);
272} 284}