aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netdevice.h2
-rw-r--r--include/linux/netfilter.h63
-rw-r--r--include/linux/netfilter_ingress.h17
-rw-r--r--include/net/netfilter/nf_queue.h3
-rw-r--r--include/net/netns/netfilter.h2
-rw-r--r--net/bridge/br_netfilter_hooks.c19
-rw-r--r--net/netfilter/core.c141
-rw-r--r--net/netfilter/nf_internals.h10
-rw-r--r--net/netfilter/nf_queue.c18
-rw-r--r--net/netfilter/nfnetlink_queue.c8
10 files changed, 167 insertions, 116 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 67bb978470dc..41f49f5ab62a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1783,7 +1783,7 @@ struct net_device {
1783#endif 1783#endif
1784 struct netdev_queue __rcu *ingress_queue; 1784 struct netdev_queue __rcu *ingress_queue;
1785#ifdef CONFIG_NETFILTER_INGRESS 1785#ifdef CONFIG_NETFILTER_INGRESS
1786 struct list_head nf_hooks_ingress; 1786 struct nf_hook_entry __rcu *nf_hooks_ingress;
1787#endif 1787#endif
1788 1788
1789 unsigned char broadcast[MAX_ADDR_LEN]; 1789 unsigned char broadcast[MAX_ADDR_LEN];
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index ad444f0b4ed0..44e20dac98a9 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -55,12 +55,34 @@ struct nf_hook_state {
55 struct net_device *out; 55 struct net_device *out;
56 struct sock *sk; 56 struct sock *sk;
57 struct net *net; 57 struct net *net;
58 struct list_head *hook_list; 58 struct nf_hook_entry __rcu *hook_entries;
59 int (*okfn)(struct net *, struct sock *, struct sk_buff *); 59 int (*okfn)(struct net *, struct sock *, struct sk_buff *);
60}; 60};
61 61
62typedef unsigned int nf_hookfn(void *priv,
63 struct sk_buff *skb,
64 const struct nf_hook_state *state);
65struct nf_hook_ops {
66 struct list_head list;
67
68 /* User fills in from here down. */
69 nf_hookfn *hook;
70 struct net_device *dev;
71 void *priv;
72 u_int8_t pf;
73 unsigned int hooknum;
74 /* Hooks are ordered in ascending priority. */
75 int priority;
76};
77
78struct nf_hook_entry {
79 struct nf_hook_entry __rcu *next;
80 struct nf_hook_ops ops;
81 const struct nf_hook_ops *orig_ops;
82};
83
62static inline void nf_hook_state_init(struct nf_hook_state *p, 84static inline void nf_hook_state_init(struct nf_hook_state *p,
63 struct list_head *hook_list, 85 struct nf_hook_entry *hook_entry,
64 unsigned int hook, 86 unsigned int hook,
65 int thresh, u_int8_t pf, 87 int thresh, u_int8_t pf,
66 struct net_device *indev, 88 struct net_device *indev,
@@ -76,26 +98,11 @@ static inline void nf_hook_state_init(struct nf_hook_state *p,
76 p->out = outdev; 98 p->out = outdev;
77 p->sk = sk; 99 p->sk = sk;
78 p->net = net; 100 p->net = net;
79 p->hook_list = hook_list; 101 RCU_INIT_POINTER(p->hook_entries, hook_entry);
80 p->okfn = okfn; 102 p->okfn = okfn;
81} 103}
82 104
83typedef unsigned int nf_hookfn(void *priv,
84 struct sk_buff *skb,
85 const struct nf_hook_state *state);
86
87struct nf_hook_ops {
88 struct list_head list;
89 105
90 /* User fills in from here down. */
91 nf_hookfn *hook;
92 struct net_device *dev;
93 void *priv;
94 u_int8_t pf;
95 unsigned int hooknum;
96 /* Hooks are ordered in ascending priority. */
97 int priority;
98};
99 106
100struct nf_sockopt_ops { 107struct nf_sockopt_ops {
101 struct list_head list; 108 struct list_head list;
@@ -161,7 +168,8 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
161 int (*okfn)(struct net *, struct sock *, struct sk_buff *), 168 int (*okfn)(struct net *, struct sock *, struct sk_buff *),
162 int thresh) 169 int thresh)
163{ 170{
164 struct list_head *hook_list; 171 struct nf_hook_entry *hook_head;
172 int ret = 1;
165 173
166#ifdef HAVE_JUMP_LABEL 174#ifdef HAVE_JUMP_LABEL
167 if (__builtin_constant_p(pf) && 175 if (__builtin_constant_p(pf) &&
@@ -170,22 +178,19 @@ static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook,
170 return 1; 178 return 1;
171#endif 179#endif
172 180
173 hook_list = &net->nf.hooks[pf][hook]; 181 rcu_read_lock();
174 182 hook_head = rcu_dereference(net->nf.hooks[pf][hook]);
175 if (!list_empty(hook_list)) { 183 if (hook_head) {
176 struct nf_hook_state state; 184 struct nf_hook_state state;
177 int ret;
178 185
179 /* We may already have this, but read-locks nest anyway */ 186 nf_hook_state_init(&state, hook_head, hook, thresh,
180 rcu_read_lock();
181 nf_hook_state_init(&state, hook_list, hook, thresh,
182 pf, indev, outdev, sk, net, okfn); 187 pf, indev, outdev, sk, net, okfn);
183 188
184 ret = nf_hook_slow(skb, &state); 189 ret = nf_hook_slow(skb, &state);
185 rcu_read_unlock();
186 return ret;
187 } 190 }
188 return 1; 191 rcu_read_unlock();
192
193 return ret;
189} 194}
190 195
191static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, 196static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
diff --git a/include/linux/netfilter_ingress.h b/include/linux/netfilter_ingress.h
index 6965ba09eba7..33e37fb41d5d 100644
--- a/include/linux/netfilter_ingress.h
+++ b/include/linux/netfilter_ingress.h
@@ -11,23 +11,30 @@ static inline bool nf_hook_ingress_active(const struct sk_buff *skb)
11 if (!static_key_false(&nf_hooks_needed[NFPROTO_NETDEV][NF_NETDEV_INGRESS])) 11 if (!static_key_false(&nf_hooks_needed[NFPROTO_NETDEV][NF_NETDEV_INGRESS]))
12 return false; 12 return false;
13#endif 13#endif
14 return !list_empty(&skb->dev->nf_hooks_ingress); 14 return rcu_access_pointer(skb->dev->nf_hooks_ingress);
15} 15}
16 16
17/* caller must hold rcu_read_lock */ 17/* caller must hold rcu_read_lock */
18static inline int nf_hook_ingress(struct sk_buff *skb) 18static inline int nf_hook_ingress(struct sk_buff *skb)
19{ 19{
20 struct nf_hook_entry *e = rcu_dereference(skb->dev->nf_hooks_ingress);
20 struct nf_hook_state state; 21 struct nf_hook_state state;
21 22
22 nf_hook_state_init(&state, &skb->dev->nf_hooks_ingress, 23 /* Must recheck the ingress hook head, in the event it became NULL
23 NF_NETDEV_INGRESS, INT_MIN, NFPROTO_NETDEV, 24 * after the check in nf_hook_ingress_active evaluated to true.
24 skb->dev, NULL, NULL, dev_net(skb->dev), NULL); 25 */
26 if (unlikely(!e))
27 return 0;
28
29 nf_hook_state_init(&state, e, NF_NETDEV_INGRESS, INT_MIN,
30 NFPROTO_NETDEV, skb->dev, NULL, NULL,
31 dev_net(skb->dev), NULL);
25 return nf_hook_slow(skb, &state); 32 return nf_hook_slow(skb, &state);
26} 33}
27 34
28static inline void nf_hook_ingress_init(struct net_device *dev) 35static inline void nf_hook_ingress_init(struct net_device *dev)
29{ 36{
30 INIT_LIST_HEAD(&dev->nf_hooks_ingress); 37 RCU_INIT_POINTER(dev->nf_hooks_ingress, NULL);
31} 38}
32#else /* CONFIG_NETFILTER_INGRESS */ 39#else /* CONFIG_NETFILTER_INGRESS */
33static inline int nf_hook_ingress_active(struct sk_buff *skb) 40static inline int nf_hook_ingress_active(struct sk_buff *skb)
diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h
index 8fe85b98b5c8..2280cfe86c56 100644
--- a/include/net/netfilter/nf_queue.h
+++ b/include/net/netfilter/nf_queue.h
@@ -11,7 +11,6 @@ struct nf_queue_entry {
11 struct sk_buff *skb; 11 struct sk_buff *skb;
12 unsigned int id; 12 unsigned int id;
13 13
14 struct nf_hook_ops *elem;
15 struct nf_hook_state state; 14 struct nf_hook_state state;
16 u16 size; /* sizeof(entry) + saved route keys */ 15 u16 size; /* sizeof(entry) + saved route keys */
17 16
@@ -25,7 +24,7 @@ struct nf_queue_handler {
25 int (*outfn)(struct nf_queue_entry *entry, 24 int (*outfn)(struct nf_queue_entry *entry,
26 unsigned int queuenum); 25 unsigned int queuenum);
27 void (*nf_hook_drop)(struct net *net, 26 void (*nf_hook_drop)(struct net *net,
28 struct nf_hook_ops *ops); 27 const struct nf_hook_entry *hooks);
29}; 28};
30 29
31void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh); 30void nf_register_queue_handler(struct net *net, const struct nf_queue_handler *qh);
diff --git a/include/net/netns/netfilter.h b/include/net/netns/netfilter.h
index 36d723579af2..58487b1cc99a 100644
--- a/include/net/netns/netfilter.h
+++ b/include/net/netns/netfilter.h
@@ -16,6 +16,6 @@ struct netns_nf {
16#ifdef CONFIG_SYSCTL 16#ifdef CONFIG_SYSCTL
17 struct ctl_table_header *nf_log_dir_header; 17 struct ctl_table_header *nf_log_dir_header;
18#endif 18#endif
19 struct list_head hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; 19 struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
20}; 20};
21#endif 21#endif
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index 6029af47377d..2fe9345c1407 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -1002,28 +1002,21 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net,
1002 int (*okfn)(struct net *, struct sock *, 1002 int (*okfn)(struct net *, struct sock *,
1003 struct sk_buff *)) 1003 struct sk_buff *))
1004{ 1004{
1005 struct nf_hook_ops *elem; 1005 struct nf_hook_entry *elem;
1006 struct nf_hook_state state; 1006 struct nf_hook_state state;
1007 struct list_head *head;
1008 int ret; 1007 int ret;
1009 1008
1010 head = &net->nf.hooks[NFPROTO_BRIDGE][hook]; 1009 elem = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]);
1011 1010
1012 list_for_each_entry_rcu(elem, head, list) { 1011 while (elem && (elem->ops.priority <= NF_BR_PRI_BRNF))
1013 struct nf_hook_ops *next; 1012 elem = rcu_dereference(elem->next);
1014 1013
1015 next = list_entry_rcu(list_next_rcu(&elem->list), 1014 if (!elem)
1016 struct nf_hook_ops, list);
1017 if (next->priority <= NF_BR_PRI_BRNF)
1018 continue;
1019 }
1020
1021 if (&elem->list == head)
1022 return okfn(net, sk, skb); 1015 return okfn(net, sk, skb);
1023 1016
1024 /* We may already have this, but read-locks nest anyway */ 1017 /* We may already have this, but read-locks nest anyway */
1025 rcu_read_lock(); 1018 rcu_read_lock();
1026 nf_hook_state_init(&state, head, hook, NF_BR_PRI_BRNF + 1, 1019 nf_hook_state_init(&state, elem, hook, NF_BR_PRI_BRNF + 1,
1027 NFPROTO_BRIDGE, indev, outdev, sk, net, okfn); 1020 NFPROTO_BRIDGE, indev, outdev, sk, net, okfn);
1028 1021
1029 ret = nf_hook_slow(skb, &state); 1022 ret = nf_hook_slow(skb, &state);
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 67b74287535d..72fc514ec676 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -22,6 +22,7 @@
22#include <linux/proc_fs.h> 22#include <linux/proc_fs.h>
23#include <linux/mutex.h> 23#include <linux/mutex.h>
24#include <linux/slab.h> 24#include <linux/slab.h>
25#include <linux/rcupdate.h>
25#include <net/net_namespace.h> 26#include <net/net_namespace.h>
26#include <net/sock.h> 27#include <net/sock.h>
27 28
@@ -61,33 +62,50 @@ EXPORT_SYMBOL(nf_hooks_needed);
61#endif 62#endif
62 63
63static DEFINE_MUTEX(nf_hook_mutex); 64static DEFINE_MUTEX(nf_hook_mutex);
65#define nf_entry_dereference(e) \
66 rcu_dereference_protected(e, lockdep_is_held(&nf_hook_mutex))
64 67
65static struct list_head *nf_find_hook_list(struct net *net, 68static struct nf_hook_entry *nf_hook_entry_head(struct net *net,
66 const struct nf_hook_ops *reg) 69 const struct nf_hook_ops *reg)
67{ 70{
68 struct list_head *hook_list = NULL; 71 struct nf_hook_entry *hook_head = NULL;
69 72
70 if (reg->pf != NFPROTO_NETDEV) 73 if (reg->pf != NFPROTO_NETDEV)
71 hook_list = &net->nf.hooks[reg->pf][reg->hooknum]; 74 hook_head = nf_entry_dereference(net->nf.hooks[reg->pf]
75 [reg->hooknum]);
72 else if (reg->hooknum == NF_NETDEV_INGRESS) { 76 else if (reg->hooknum == NF_NETDEV_INGRESS) {
73#ifdef CONFIG_NETFILTER_INGRESS 77#ifdef CONFIG_NETFILTER_INGRESS
74 if (reg->dev && dev_net(reg->dev) == net) 78 if (reg->dev && dev_net(reg->dev) == net)
75 hook_list = &reg->dev->nf_hooks_ingress; 79 hook_head =
80 nf_entry_dereference(
81 reg->dev->nf_hooks_ingress);
76#endif 82#endif
77 } 83 }
78 return hook_list; 84 return hook_head;
79} 85}
80 86
81struct nf_hook_entry { 87/* must hold nf_hook_mutex */
82 const struct nf_hook_ops *orig_ops; 88static void nf_set_hooks_head(struct net *net, const struct nf_hook_ops *reg,
83 struct nf_hook_ops ops; 89 struct nf_hook_entry *entry)
84}; 90{
91 switch (reg->pf) {
92 case NFPROTO_NETDEV:
93 /* We already checked in nf_register_net_hook() that this is
94 * used from ingress.
95 */
96 rcu_assign_pointer(reg->dev->nf_hooks_ingress, entry);
97 break;
98 default:
99 rcu_assign_pointer(net->nf.hooks[reg->pf][reg->hooknum],
100 entry);
101 break;
102 }
103}
85 104
86int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) 105int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
87{ 106{
88 struct list_head *hook_list; 107 struct nf_hook_entry *hooks_entry;
89 struct nf_hook_entry *entry; 108 struct nf_hook_entry *entry;
90 struct nf_hook_ops *elem;
91 109
92 if (reg->pf == NFPROTO_NETDEV && 110 if (reg->pf == NFPROTO_NETDEV &&
93 (reg->hooknum != NF_NETDEV_INGRESS || 111 (reg->hooknum != NF_NETDEV_INGRESS ||
@@ -100,19 +118,30 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
100 118
101 entry->orig_ops = reg; 119 entry->orig_ops = reg;
102 entry->ops = *reg; 120 entry->ops = *reg;
121 entry->next = NULL;
122
123 mutex_lock(&nf_hook_mutex);
124 hooks_entry = nf_hook_entry_head(net, reg);
103 125
104 hook_list = nf_find_hook_list(net, reg); 126 if (hooks_entry && hooks_entry->orig_ops->priority > reg->priority) {
105 if (!hook_list) { 127 /* This is the case where we need to insert at the head */
106 kfree(entry); 128 entry->next = hooks_entry;
107 return -ENOENT; 129 hooks_entry = NULL;
108 } 130 }
109 131
110 mutex_lock(&nf_hook_mutex); 132 while (hooks_entry &&
111 list_for_each_entry(elem, hook_list, list) { 133 reg->priority >= hooks_entry->orig_ops->priority &&
112 if (reg->priority < elem->priority) 134 nf_entry_dereference(hooks_entry->next)) {
113 break; 135 hooks_entry = nf_entry_dereference(hooks_entry->next);
136 }
137
138 if (hooks_entry) {
139 entry->next = nf_entry_dereference(hooks_entry->next);
140 rcu_assign_pointer(hooks_entry->next, entry);
141 } else {
142 nf_set_hooks_head(net, reg, entry);
114 } 143 }
115 list_add_rcu(&entry->ops.list, elem->list.prev); 144
116 mutex_unlock(&nf_hook_mutex); 145 mutex_unlock(&nf_hook_mutex);
117#ifdef CONFIG_NETFILTER_INGRESS 146#ifdef CONFIG_NETFILTER_INGRESS
118 if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) 147 if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS)
@@ -127,24 +156,33 @@ EXPORT_SYMBOL(nf_register_net_hook);
127 156
128void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) 157void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
129{ 158{
130 struct list_head *hook_list; 159 struct nf_hook_entry *hooks_entry;
131 struct nf_hook_entry *entry;
132 struct nf_hook_ops *elem;
133
134 hook_list = nf_find_hook_list(net, reg);
135 if (!hook_list)
136 return;
137 160
138 mutex_lock(&nf_hook_mutex); 161 mutex_lock(&nf_hook_mutex);
139 list_for_each_entry(elem, hook_list, list) { 162 hooks_entry = nf_hook_entry_head(net, reg);
140 entry = container_of(elem, struct nf_hook_entry, ops); 163 if (hooks_entry->orig_ops == reg) {
141 if (entry->orig_ops == reg) { 164 nf_set_hooks_head(net, reg,
142 list_del_rcu(&entry->ops.list); 165 nf_entry_dereference(hooks_entry->next));
143 break; 166 goto unlock;
167 }
168 while (hooks_entry && nf_entry_dereference(hooks_entry->next)) {
169 struct nf_hook_entry *next =
170 nf_entry_dereference(hooks_entry->next);
171 struct nf_hook_entry *nnext;
172
173 if (next->orig_ops != reg) {
174 hooks_entry = next;
175 continue;
144 } 176 }
177 nnext = nf_entry_dereference(next->next);
178 rcu_assign_pointer(hooks_entry->next, nnext);
179 hooks_entry = next;
180 break;
145 } 181 }
182
183unlock:
146 mutex_unlock(&nf_hook_mutex); 184 mutex_unlock(&nf_hook_mutex);
147 if (&elem->list == hook_list) { 185 if (!hooks_entry) {
148 WARN(1, "nf_unregister_net_hook: hook not found!\n"); 186 WARN(1, "nf_unregister_net_hook: hook not found!\n");
149 return; 187 return;
150 } 188 }
@@ -156,10 +194,10 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg)
156 static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); 194 static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]);
157#endif 195#endif
158 synchronize_net(); 196 synchronize_net();
159 nf_queue_nf_hook_drop(net, &entry->ops); 197 nf_queue_nf_hook_drop(net, hooks_entry);
160 /* other cpu might still process nfqueue verdict that used reg */ 198 /* other cpu might still process nfqueue verdict that used reg */
161 synchronize_net(); 199 synchronize_net();
162 kfree(entry); 200 kfree(hooks_entry);
163} 201}
164EXPORT_SYMBOL(nf_unregister_net_hook); 202EXPORT_SYMBOL(nf_unregister_net_hook);
165 203
@@ -258,10 +296,9 @@ void nf_unregister_hooks(struct nf_hook_ops *reg, unsigned int n)
258} 296}
259EXPORT_SYMBOL(nf_unregister_hooks); 297EXPORT_SYMBOL(nf_unregister_hooks);
260 298
261unsigned int nf_iterate(struct list_head *head, 299unsigned int nf_iterate(struct sk_buff *skb,
262 struct sk_buff *skb,
263 struct nf_hook_state *state, 300 struct nf_hook_state *state,
264 struct nf_hook_ops **elemp) 301 struct nf_hook_entry **entryp)
265{ 302{
266 unsigned int verdict; 303 unsigned int verdict;
267 304
@@ -269,20 +306,23 @@ unsigned int nf_iterate(struct list_head *head,
269 * The caller must not block between calls to this 306 * The caller must not block between calls to this
270 * function because of risk of continuing from deleted element. 307 * function because of risk of continuing from deleted element.
271 */ 308 */
272 list_for_each_entry_continue_rcu((*elemp), head, list) { 309 while (*entryp) {
273 if (state->thresh > (*elemp)->priority) 310 if (state->thresh > (*entryp)->ops.priority) {
311 *entryp = rcu_dereference((*entryp)->next);
274 continue; 312 continue;
313 }
275 314
276 /* Optimization: we don't need to hold module 315 /* Optimization: we don't need to hold module
277 reference here, since function can't sleep. --RR */ 316 reference here, since function can't sleep. --RR */
278repeat: 317repeat:
279 verdict = (*elemp)->hook((*elemp)->priv, skb, state); 318 verdict = (*entryp)->ops.hook((*entryp)->ops.priv, skb, state);
280 if (verdict != NF_ACCEPT) { 319 if (verdict != NF_ACCEPT) {
281#ifdef CONFIG_NETFILTER_DEBUG 320#ifdef CONFIG_NETFILTER_DEBUG
282 if (unlikely((verdict & NF_VERDICT_MASK) 321 if (unlikely((verdict & NF_VERDICT_MASK)
283 > NF_MAX_VERDICT)) { 322 > NF_MAX_VERDICT)) {
284 NFDEBUG("Evil return from %p(%u).\n", 323 NFDEBUG("Evil return from %p(%u).\n",
285 (*elemp)->hook, state->hook); 324 (*entryp)->ops.hook, state->hook);
325 *entryp = rcu_dereference((*entryp)->next);
286 continue; 326 continue;
287 } 327 }
288#endif 328#endif
@@ -290,6 +330,7 @@ repeat:
290 return verdict; 330 return verdict;
291 goto repeat; 331 goto repeat;
292 } 332 }
333 *entryp = rcu_dereference((*entryp)->next);
293 } 334 }
294 return NF_ACCEPT; 335 return NF_ACCEPT;
295} 336}
@@ -299,13 +340,13 @@ repeat:
299 * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */ 340 * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */
300int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state) 341int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state)
301{ 342{
302 struct nf_hook_ops *elem; 343 struct nf_hook_entry *entry;
303 unsigned int verdict; 344 unsigned int verdict;
304 int ret = 0; 345 int ret = 0;
305 346
306 elem = list_entry_rcu(state->hook_list, struct nf_hook_ops, list); 347 entry = rcu_dereference(state->hook_entries);
307next_hook: 348next_hook:
308 verdict = nf_iterate(state->hook_list, skb, state, &elem); 349 verdict = nf_iterate(skb, state, &entry);
309 if (verdict == NF_ACCEPT || verdict == NF_STOP) { 350 if (verdict == NF_ACCEPT || verdict == NF_STOP) {
310 ret = 1; 351 ret = 1;
311 } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) { 352 } else if ((verdict & NF_VERDICT_MASK) == NF_DROP) {
@@ -314,8 +355,10 @@ next_hook:
314 if (ret == 0) 355 if (ret == 0)
315 ret = -EPERM; 356 ret = -EPERM;
316 } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { 357 } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
317 int err = nf_queue(skb, elem, state, 358 int err;
318 verdict >> NF_VERDICT_QBITS); 359
360 RCU_INIT_POINTER(state->hook_entries, entry);
361 err = nf_queue(skb, state, verdict >> NF_VERDICT_QBITS);
319 if (err < 0) { 362 if (err < 0) {
320 if (err == -ESRCH && 363 if (err == -ESRCH &&
321 (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) 364 (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
@@ -442,7 +485,7 @@ static int __net_init netfilter_net_init(struct net *net)
442 485
443 for (i = 0; i < ARRAY_SIZE(net->nf.hooks); i++) { 486 for (i = 0; i < ARRAY_SIZE(net->nf.hooks); i++) {
444 for (h = 0; h < NF_MAX_HOOKS; h++) 487 for (h = 0; h < NF_MAX_HOOKS; h++)
445 INIT_LIST_HEAD(&net->nf.hooks[i][h]); 488 RCU_INIT_POINTER(net->nf.hooks[i][h], NULL);
446 } 489 }
447 490
448#ifdef CONFIG_PROC_FS 491#ifdef CONFIG_PROC_FS
diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h
index 065522564ac6..e0adb5959342 100644
--- a/net/netfilter/nf_internals.h
+++ b/net/netfilter/nf_internals.h
@@ -13,13 +13,13 @@
13 13
14 14
15/* core.c */ 15/* core.c */
16unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, 16unsigned int nf_iterate(struct sk_buff *skb, struct nf_hook_state *state,
17 struct nf_hook_state *state, struct nf_hook_ops **elemp); 17 struct nf_hook_entry **entryp);
18 18
19/* nf_queue.c */ 19/* nf_queue.c */
20int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, 20int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
21 struct nf_hook_state *state, unsigned int queuenum); 21 unsigned int queuenum);
22void nf_queue_nf_hook_drop(struct net *net, struct nf_hook_ops *ops); 22void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry);
23int __init netfilter_queue_init(void); 23int __init netfilter_queue_init(void);
24 24
25/* nf_log.c */ 25/* nf_log.c */
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index b19ad20a705c..96964a0070e1 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -96,14 +96,14 @@ void nf_queue_entry_get_refs(struct nf_queue_entry *entry)
96} 96}
97EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs); 97EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
98 98
99void nf_queue_nf_hook_drop(struct net *net, struct nf_hook_ops *ops) 99void nf_queue_nf_hook_drop(struct net *net, const struct nf_hook_entry *entry)
100{ 100{
101 const struct nf_queue_handler *qh; 101 const struct nf_queue_handler *qh;
102 102
103 rcu_read_lock(); 103 rcu_read_lock();
104 qh = rcu_dereference(net->nf.queue_handler); 104 qh = rcu_dereference(net->nf.queue_handler);
105 if (qh) 105 if (qh)
106 qh->nf_hook_drop(net, ops); 106 qh->nf_hook_drop(net, entry);
107 rcu_read_unlock(); 107 rcu_read_unlock();
108} 108}
109 109
@@ -112,7 +112,6 @@ void nf_queue_nf_hook_drop(struct net *net, struct nf_hook_ops *ops)
112 * through nf_reinject(). 112 * through nf_reinject().
113 */ 113 */
114int nf_queue(struct sk_buff *skb, 114int nf_queue(struct sk_buff *skb,
115 struct nf_hook_ops *elem,
116 struct nf_hook_state *state, 115 struct nf_hook_state *state,
117 unsigned int queuenum) 116 unsigned int queuenum)
118{ 117{
@@ -141,7 +140,6 @@ int nf_queue(struct sk_buff *skb,
141 140
142 *entry = (struct nf_queue_entry) { 141 *entry = (struct nf_queue_entry) {
143 .skb = skb, 142 .skb = skb,
144 .elem = elem,
145 .state = *state, 143 .state = *state,
146 .size = sizeof(*entry) + afinfo->route_key_size, 144 .size = sizeof(*entry) + afinfo->route_key_size,
147 }; 145 };
@@ -165,11 +163,15 @@ err:
165 163
166void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) 164void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
167{ 165{
166 struct nf_hook_entry *hook_entry;
168 struct sk_buff *skb = entry->skb; 167 struct sk_buff *skb = entry->skb;
169 struct nf_hook_ops *elem = entry->elem;
170 const struct nf_afinfo *afinfo; 168 const struct nf_afinfo *afinfo;
169 struct nf_hook_ops *elem;
171 int err; 170 int err;
172 171
172 hook_entry = rcu_dereference(entry->state.hook_entries);
173 elem = &hook_entry->ops;
174
173 nf_queue_entry_release_refs(entry); 175 nf_queue_entry_release_refs(entry);
174 176
175 /* Continue traversal iff userspace said ok... */ 177 /* Continue traversal iff userspace said ok... */
@@ -186,8 +188,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
186 188
187 if (verdict == NF_ACCEPT) { 189 if (verdict == NF_ACCEPT) {
188 next_hook: 190 next_hook:
189 verdict = nf_iterate(entry->state.hook_list, 191 verdict = nf_iterate(skb, &entry->state, &hook_entry);
190 skb, &entry->state, &elem);
191 } 192 }
192 193
193 switch (verdict & NF_VERDICT_MASK) { 194 switch (verdict & NF_VERDICT_MASK) {
@@ -198,7 +199,8 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
198 local_bh_enable(); 199 local_bh_enable();
199 break; 200 break;
200 case NF_QUEUE: 201 case NF_QUEUE:
201 err = nf_queue(skb, elem, &entry->state, 202 RCU_INIT_POINTER(entry->state.hook_entries, hook_entry);
203 err = nf_queue(skb, &entry->state,
202 verdict >> NF_VERDICT_QBITS); 204 verdict >> NF_VERDICT_QBITS);
203 if (err < 0) { 205 if (err < 0) {
204 if (err == -ESRCH && 206 if (err == -ESRCH &&
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 7caa8b082c41..af832c526048 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -917,12 +917,14 @@ static struct notifier_block nfqnl_dev_notifier = {
917 .notifier_call = nfqnl_rcv_dev_event, 917 .notifier_call = nfqnl_rcv_dev_event,
918}; 918};
919 919
920static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long ops_ptr) 920static int nf_hook_cmp(struct nf_queue_entry *entry, unsigned long entry_ptr)
921{ 921{
922 return entry->elem == (struct nf_hook_ops *)ops_ptr; 922 return rcu_access_pointer(entry->state.hook_entries) ==
923 (struct nf_hook_entry *)entry_ptr;
923} 924}
924 925
925static void nfqnl_nf_hook_drop(struct net *net, struct nf_hook_ops *hook) 926static void nfqnl_nf_hook_drop(struct net *net,
927 const struct nf_hook_entry *hook)
926{ 928{
927 struct nfnl_queue_net *q = nfnl_queue_pernet(net); 929 struct nfnl_queue_net *q = nfnl_queue_pernet(net);
928 int i; 930 int i;