aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2009-06-03 04:32:06 -0400
committerpablo <pablo@1984.(none)>2009-06-03 04:32:06 -0400
commite34d5c1a4f9919a81b4ea4591d7383245f35cb8e (patch)
tree0a19bfe3e162b2b8de9b9414dcc2bef0a07e7133
parent17e6e4eac070607a35464ea7e2c5eceac32e5eca (diff)
netfilter: conntrack: replace notify chain by function pointer
This patch removes the notify chain infrastructure and replace it by a simple function pointer. This issue has been mentioned in the mailing list several times: the use of the notify chain adds too much overhead for something that is only used by ctnetlink. This patch also changes nfnetlink_send(). It seems that gfp_any() returns GFP_KERNEL for user-context request, like those via ctnetlink, inside the RCU read-side section which is not valid. Using GFP_KERNEL is also evil since netlink may schedule(), this leads to "scheduling while atomic" bug reports. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/linux/netfilter/nfnetlink.h2
-rw-r--r--include/net/netfilter/nf_conntrack_ecache.h68
-rw-r--r--net/netfilter/nf_conntrack_ecache.c83
-rw-r--r--net/netfilter/nf_conntrack_netlink.c42
-rw-r--r--net/netfilter/nfnetlink.c5
5 files changed, 139 insertions, 61 deletions
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index c600083cbdf5..2214e5161461 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -75,7 +75,7 @@ extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
75 75
76extern int nfnetlink_has_listeners(unsigned int group); 76extern int nfnetlink_has_listeners(unsigned int group);
77extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, 77extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group,
78 int echo); 78 int echo, gfp_t flags);
79extern void nfnetlink_set_err(u32 pid, u32 group, int error); 79extern void nfnetlink_set_err(u32 pid, u32 group, int error);
80extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags); 80extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags);
81 81
diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index 2e17a2d0eb3b..1afb907e015a 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -6,7 +6,6 @@
6#define _NF_CONNTRACK_ECACHE_H 6#define _NF_CONNTRACK_ECACHE_H
7#include <net/netfilter/nf_conntrack.h> 7#include <net/netfilter/nf_conntrack.h>
8 8
9#include <linux/notifier.h>
10#include <linux/interrupt.h> 9#include <linux/interrupt.h>
11#include <net/net_namespace.h> 10#include <net/net_namespace.h>
12#include <net/netfilter/nf_conntrack_expect.h> 11#include <net/netfilter/nf_conntrack_expect.h>
@@ -69,9 +68,13 @@ struct nf_ct_event {
69 int report; 68 int report;
70}; 69};
71 70
72extern struct atomic_notifier_head nf_conntrack_chain; 71struct nf_ct_event_notifier {
73extern int nf_conntrack_register_notifier(struct notifier_block *nb); 72 int (*fcn)(unsigned int events, struct nf_ct_event *item);
74extern int nf_conntrack_unregister_notifier(struct notifier_block *nb); 73};
74
75extern struct nf_ct_event_notifier *nf_conntrack_event_cb;
76extern int nf_conntrack_register_notifier(struct nf_ct_event_notifier *nb);
77extern void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *nb);
75 78
76extern void nf_ct_deliver_cached_events(const struct nf_conn *ct); 79extern void nf_ct_deliver_cached_events(const struct nf_conn *ct);
77extern void __nf_ct_event_cache_init(struct nf_conn *ct); 80extern void __nf_ct_event_cache_init(struct nf_conn *ct);
@@ -97,13 +100,23 @@ nf_conntrack_event_report(enum ip_conntrack_events event,
97 u32 pid, 100 u32 pid,
98 int report) 101 int report)
99{ 102{
100 struct nf_ct_event item = { 103 struct nf_ct_event_notifier *notify;
101 .ct = ct, 104
102 .pid = pid, 105 rcu_read_lock();
103 .report = report 106 notify = rcu_dereference(nf_conntrack_event_cb);
104 }; 107 if (notify == NULL)
105 if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) 108 goto out_unlock;
106 atomic_notifier_call_chain(&nf_conntrack_chain, event, &item); 109
110 if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct)) {
111 struct nf_ct_event item = {
112 .ct = ct,
113 .pid = pid,
114 .report = report
115 };
116 notify->fcn(event, &item);
117 }
118out_unlock:
119 rcu_read_unlock();
107} 120}
108 121
109static inline void 122static inline void
@@ -118,9 +131,13 @@ struct nf_exp_event {
118 int report; 131 int report;
119}; 132};
120 133
121extern struct atomic_notifier_head nf_ct_expect_chain; 134struct nf_exp_event_notifier {
122extern int nf_ct_expect_register_notifier(struct notifier_block *nb); 135 int (*fcn)(unsigned int events, struct nf_exp_event *item);
123extern int nf_ct_expect_unregister_notifier(struct notifier_block *nb); 136};
137
138extern struct nf_exp_event_notifier *nf_expect_event_cb;
139extern int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *nb);
140extern void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *nb);
124 141
125static inline void 142static inline void
126nf_ct_expect_event_report(enum ip_conntrack_expect_events event, 143nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
@@ -128,12 +145,23 @@ nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
128 u32 pid, 145 u32 pid,
129 int report) 146 int report)
130{ 147{
131 struct nf_exp_event item = { 148 struct nf_exp_event_notifier *notify;
132 .exp = exp, 149
133 .pid = pid, 150 rcu_read_lock();
134 .report = report 151 notify = rcu_dereference(nf_expect_event_cb);
135 }; 152 if (notify == NULL)
136 atomic_notifier_call_chain(&nf_ct_expect_chain, event, &item); 153 goto out_unlock;
154
155 {
156 struct nf_exp_event item = {
157 .exp = exp,
158 .pid = pid,
159 .report = report
160 };
161 notify->fcn(event, &item);
162 }
163out_unlock:
164 rcu_read_unlock();
137} 165}
138 166
139static inline void 167static inline void
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index dee4190209cc..5516b3e64b43 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -16,24 +16,32 @@
16#include <linux/stddef.h> 16#include <linux/stddef.h>
17#include <linux/err.h> 17#include <linux/err.h>
18#include <linux/percpu.h> 18#include <linux/percpu.h>
19#include <linux/notifier.h>
20#include <linux/kernel.h> 19#include <linux/kernel.h>
21#include <linux/netdevice.h> 20#include <linux/netdevice.h>
22 21
23#include <net/netfilter/nf_conntrack.h> 22#include <net/netfilter/nf_conntrack.h>
24#include <net/netfilter/nf_conntrack_core.h> 23#include <net/netfilter/nf_conntrack_core.h>
25 24
26ATOMIC_NOTIFIER_HEAD(nf_conntrack_chain); 25static DEFINE_MUTEX(nf_ct_ecache_mutex);
27EXPORT_SYMBOL_GPL(nf_conntrack_chain);
28 26
29ATOMIC_NOTIFIER_HEAD(nf_ct_expect_chain); 27struct nf_ct_event_notifier *nf_conntrack_event_cb __read_mostly;
30EXPORT_SYMBOL_GPL(nf_ct_expect_chain); 28EXPORT_SYMBOL_GPL(nf_conntrack_event_cb);
29
30struct nf_exp_event_notifier *nf_expect_event_cb __read_mostly;
31EXPORT_SYMBOL_GPL(nf_expect_event_cb);
31 32
32/* deliver cached events and clear cache entry - must be called with locally 33/* deliver cached events and clear cache entry - must be called with locally
33 * disabled softirqs */ 34 * disabled softirqs */
34static inline void 35static inline void
35__nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache) 36__nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
36{ 37{
38 struct nf_ct_event_notifier *notify;
39
40 rcu_read_lock();
41 notify = rcu_dereference(nf_conntrack_event_cb);
42 if (notify == NULL)
43 goto out_unlock;
44
37 if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct) 45 if (nf_ct_is_confirmed(ecache->ct) && !nf_ct_is_dying(ecache->ct)
38 && ecache->events) { 46 && ecache->events) {
39 struct nf_ct_event item = { 47 struct nf_ct_event item = {
@@ -42,14 +50,15 @@ __nf_ct_deliver_cached_events(struct nf_conntrack_ecache *ecache)
42 .report = 0 50 .report = 0
43 }; 51 };
44 52
45 atomic_notifier_call_chain(&nf_conntrack_chain, 53 notify->fcn(ecache->events, &item);
46 ecache->events,
47 &item);
48 } 54 }
49 55
50 ecache->events = 0; 56 ecache->events = 0;
51 nf_ct_put(ecache->ct); 57 nf_ct_put(ecache->ct);
52 ecache->ct = NULL; 58 ecache->ct = NULL;
59
60out_unlock:
61 rcu_read_unlock();
53} 62}
54 63
55/* Deliver all cached events for a particular conntrack. This is called 64/* Deliver all cached events for a particular conntrack. This is called
@@ -111,26 +120,68 @@ void nf_conntrack_ecache_fini(struct net *net)
111 free_percpu(net->ct.ecache); 120 free_percpu(net->ct.ecache);
112} 121}
113 122
114int nf_conntrack_register_notifier(struct notifier_block *nb) 123int nf_conntrack_register_notifier(struct nf_ct_event_notifier *new)
115{ 124{
116 return atomic_notifier_chain_register(&nf_conntrack_chain, nb); 125 int ret = 0;
126 struct nf_ct_event_notifier *notify;
127
128 mutex_lock(&nf_ct_ecache_mutex);
129 notify = rcu_dereference(nf_conntrack_event_cb);
130 if (notify != NULL) {
131 ret = -EBUSY;
132 goto out_unlock;
133 }
134 rcu_assign_pointer(nf_conntrack_event_cb, new);
135 mutex_unlock(&nf_ct_ecache_mutex);
136 return ret;
137
138out_unlock:
139 mutex_unlock(&nf_ct_ecache_mutex);
140 return ret;
117} 141}
118EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier); 142EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
119 143
120int nf_conntrack_unregister_notifier(struct notifier_block *nb) 144void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *new)
121{ 145{
122 return atomic_notifier_chain_unregister(&nf_conntrack_chain, nb); 146 struct nf_ct_event_notifier *notify;
147
148 mutex_lock(&nf_ct_ecache_mutex);
149 notify = rcu_dereference(nf_conntrack_event_cb);
150 BUG_ON(notify != new);
151 rcu_assign_pointer(nf_conntrack_event_cb, NULL);
152 mutex_unlock(&nf_ct_ecache_mutex);
123} 153}
124EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier); 154EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
125 155
126int nf_ct_expect_register_notifier(struct notifier_block *nb) 156int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *new)
127{ 157{
128 return atomic_notifier_chain_register(&nf_ct_expect_chain, nb); 158 int ret = 0;
159 struct nf_exp_event_notifier *notify;
160
161 mutex_lock(&nf_ct_ecache_mutex);
162 notify = rcu_dereference(nf_expect_event_cb);
163 if (notify != NULL) {
164 ret = -EBUSY;
165 goto out_unlock;
166 }
167 rcu_assign_pointer(nf_expect_event_cb, new);
168 mutex_unlock(&nf_ct_ecache_mutex);
169 return ret;
170
171out_unlock:
172 mutex_unlock(&nf_ct_ecache_mutex);
173 return ret;
129} 174}
130EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier); 175EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier);
131 176
132int nf_ct_expect_unregister_notifier(struct notifier_block *nb) 177void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *new)
133{ 178{
134 return atomic_notifier_chain_unregister(&nf_ct_expect_chain, nb); 179 struct nf_exp_event_notifier *notify;
180
181 mutex_lock(&nf_ct_ecache_mutex);
182 notify = rcu_dereference(nf_expect_event_cb);
183 BUG_ON(notify != new);
184 rcu_assign_pointer(nf_expect_event_cb, NULL);
185 mutex_unlock(&nf_ct_ecache_mutex);
135} 186}
136EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier); 187EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index b1b9e4fb7ded..4448b062de0c 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -27,7 +27,6 @@
27#include <linux/netlink.h> 27#include <linux/netlink.h>
28#include <linux/spinlock.h> 28#include <linux/spinlock.h>
29#include <linux/interrupt.h> 29#include <linux/interrupt.h>
30#include <linux/notifier.h>
31 30
32#include <linux/netfilter.h> 31#include <linux/netfilter.h>
33#include <net/netlink.h> 32#include <net/netlink.h>
@@ -454,13 +453,12 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct)
454 ; 453 ;
455} 454}
456 455
457static int ctnetlink_conntrack_event(struct notifier_block *this, 456static int
458 unsigned long events, void *ptr) 457ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
459{ 458{
460 struct nlmsghdr *nlh; 459 struct nlmsghdr *nlh;
461 struct nfgenmsg *nfmsg; 460 struct nfgenmsg *nfmsg;
462 struct nlattr *nest_parms; 461 struct nlattr *nest_parms;
463 struct nf_ct_event *item = (struct nf_ct_event *)ptr;
464 struct nf_conn *ct = item->ct; 462 struct nf_conn *ct = item->ct;
465 struct sk_buff *skb; 463 struct sk_buff *skb;
466 unsigned int type; 464 unsigned int type;
@@ -468,7 +466,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
468 466
469 /* ignore our fake conntrack entry */ 467 /* ignore our fake conntrack entry */
470 if (ct == &nf_conntrack_untracked) 468 if (ct == &nf_conntrack_untracked)
471 return NOTIFY_DONE; 469 return 0;
472 470
473 if (events & IPCT_DESTROY) { 471 if (events & IPCT_DESTROY) {
474 type = IPCTNL_MSG_CT_DELETE; 472 type = IPCTNL_MSG_CT_DELETE;
@@ -481,10 +479,10 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
481 type = IPCTNL_MSG_CT_NEW; 479 type = IPCTNL_MSG_CT_NEW;
482 group = NFNLGRP_CONNTRACK_UPDATE; 480 group = NFNLGRP_CONNTRACK_UPDATE;
483 } else 481 } else
484 return NOTIFY_DONE; 482 return 0;
485 483
486 if (!item->report && !nfnetlink_has_listeners(group)) 484 if (!item->report && !nfnetlink_has_listeners(group))
487 return NOTIFY_DONE; 485 return 0;
488 486
489 skb = nlmsg_new(ctnetlink_nlmsg_size(ct), GFP_ATOMIC); 487 skb = nlmsg_new(ctnetlink_nlmsg_size(ct), GFP_ATOMIC);
490 if (skb == NULL) 488 if (skb == NULL)
@@ -560,8 +558,8 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
560 rcu_read_unlock(); 558 rcu_read_unlock();
561 559
562 nlmsg_end(skb, nlh); 560 nlmsg_end(skb, nlh);
563 nfnetlink_send(skb, item->pid, group, item->report); 561 nfnetlink_send(skb, item->pid, group, item->report, GFP_ATOMIC);
564 return NOTIFY_DONE; 562 return 0;
565 563
566nla_put_failure: 564nla_put_failure:
567 rcu_read_unlock(); 565 rcu_read_unlock();
@@ -570,7 +568,7 @@ nlmsg_failure:
570 kfree_skb(skb); 568 kfree_skb(skb);
571errout: 569errout:
572 nfnetlink_set_err(0, group, -ENOBUFS); 570 nfnetlink_set_err(0, group, -ENOBUFS);
573 return NOTIFY_DONE; 571 return 0;
574} 572}
575#endif /* CONFIG_NF_CONNTRACK_EVENTS */ 573#endif /* CONFIG_NF_CONNTRACK_EVENTS */
576 574
@@ -1507,12 +1505,11 @@ nla_put_failure:
1507} 1505}
1508 1506
1509#ifdef CONFIG_NF_CONNTRACK_EVENTS 1507#ifdef CONFIG_NF_CONNTRACK_EVENTS
1510static int ctnetlink_expect_event(struct notifier_block *this, 1508static int
1511 unsigned long events, void *ptr) 1509ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item)
1512{ 1510{
1513 struct nlmsghdr *nlh; 1511 struct nlmsghdr *nlh;
1514 struct nfgenmsg *nfmsg; 1512 struct nfgenmsg *nfmsg;
1515 struct nf_exp_event *item = (struct nf_exp_event *)ptr;
1516 struct nf_conntrack_expect *exp = item->exp; 1513 struct nf_conntrack_expect *exp = item->exp;
1517 struct sk_buff *skb; 1514 struct sk_buff *skb;
1518 unsigned int type; 1515 unsigned int type;
@@ -1522,11 +1519,11 @@ static int ctnetlink_expect_event(struct notifier_block *this,
1522 type = IPCTNL_MSG_EXP_NEW; 1519 type = IPCTNL_MSG_EXP_NEW;
1523 flags = NLM_F_CREATE|NLM_F_EXCL; 1520 flags = NLM_F_CREATE|NLM_F_EXCL;
1524 } else 1521 } else
1525 return NOTIFY_DONE; 1522 return 0;
1526 1523
1527 if (!item->report && 1524 if (!item->report &&
1528 !nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW)) 1525 !nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW))
1529 return NOTIFY_DONE; 1526 return 0;
1530 1527
1531 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 1528 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
1532 if (skb == NULL) 1529 if (skb == NULL)
@@ -1548,8 +1545,9 @@ static int ctnetlink_expect_event(struct notifier_block *this,
1548 rcu_read_unlock(); 1545 rcu_read_unlock();
1549 1546
1550 nlmsg_end(skb, nlh); 1547 nlmsg_end(skb, nlh);
1551 nfnetlink_send(skb, item->pid, NFNLGRP_CONNTRACK_EXP_NEW, item->report); 1548 nfnetlink_send(skb, item->pid, NFNLGRP_CONNTRACK_EXP_NEW,
1552 return NOTIFY_DONE; 1549 item->report, GFP_ATOMIC);
1550 return 0;
1553 1551
1554nla_put_failure: 1552nla_put_failure:
1555 rcu_read_unlock(); 1553 rcu_read_unlock();
@@ -1558,7 +1556,7 @@ nlmsg_failure:
1558 kfree_skb(skb); 1556 kfree_skb(skb);
1559errout: 1557errout:
1560 nfnetlink_set_err(0, 0, -ENOBUFS); 1558 nfnetlink_set_err(0, 0, -ENOBUFS);
1561 return NOTIFY_DONE; 1559 return 0;
1562} 1560}
1563#endif 1561#endif
1564static int ctnetlink_exp_done(struct netlink_callback *cb) 1562static int ctnetlink_exp_done(struct netlink_callback *cb)
@@ -1864,12 +1862,12 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
1864} 1862}
1865 1863
1866#ifdef CONFIG_NF_CONNTRACK_EVENTS 1864#ifdef CONFIG_NF_CONNTRACK_EVENTS
1867static struct notifier_block ctnl_notifier = { 1865static struct nf_ct_event_notifier ctnl_notifier = {
1868 .notifier_call = ctnetlink_conntrack_event, 1866 .fcn = ctnetlink_conntrack_event,
1869}; 1867};
1870 1868
1871static struct notifier_block ctnl_notifier_exp = { 1869static struct nf_exp_event_notifier ctnl_notifier_exp = {
1872 .notifier_call = ctnetlink_expect_event, 1870 .fcn = ctnetlink_expect_event,
1873}; 1871};
1874#endif 1872#endif
1875 1873
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 9dbd5709aad7..92761a988375 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -107,9 +107,10 @@ int nfnetlink_has_listeners(unsigned int group)
107} 107}
108EXPORT_SYMBOL_GPL(nfnetlink_has_listeners); 108EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
109 109
110int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group, int echo) 110int nfnetlink_send(struct sk_buff *skb, u32 pid,
111 unsigned group, int echo, gfp_t flags)
111{ 112{
112 return nlmsg_notify(nfnl, skb, pid, group, echo, gfp_any()); 113 return nlmsg_notify(nfnl, skb, pid, group, echo, flags);
113} 114}
114EXPORT_SYMBOL_GPL(nfnetlink_send); 115EXPORT_SYMBOL_GPL(nfnetlink_send);
115 116