diff options
author | Florian Westphal <fw@strlen.de> | 2011-01-18 09:27:28 -0500 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-01-18 09:27:28 -0500 |
commit | f15850861860636c905b33a9a5be3dcbc2b0d56a (patch) | |
tree | 463d73647de2a43138bdd8259c259137a3bb3e3b /net | |
parent | 5f2cafe73671d865af88494159f3e8c1b322e1c5 (diff) |
netfilter: nfnetlink_queue: return error number to caller
instead of returning -1 on error, return an error number to allow the
caller to handle some errors differently.
ECANCELED is used to indicate that the hook is going away and should be
ignored.
A followup patch will introduce more 'ignore this hook' conditions,
(depending on queue settings) and will move kfree_skb responsibility
to the caller.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/core.c | 6 | ||||
-rw-r--r-- | net/netfilter/nf_queue.c | 44 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue.c | 22 |
3 files changed, 49 insertions, 23 deletions
diff --git a/net/netfilter/core.c b/net/netfilter/core.c index e69d537362c7..91d66d2f8cd9 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c | |||
@@ -179,9 +179,11 @@ next_hook: | |||
179 | if (ret == 0) | 179 | if (ret == 0) |
180 | ret = -EPERM; | 180 | ret = -EPERM; |
181 | } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { | 181 | } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) { |
182 | if (!nf_queue(skb, elem, pf, hook, indev, outdev, okfn, | 182 | ret = nf_queue(skb, elem, pf, hook, indev, outdev, okfn, |
183 | verdict >> NF_VERDICT_BITS)) | 183 | verdict >> NF_VERDICT_BITS); |
184 | if (ret == -ECANCELED) | ||
184 | goto next_hook; | 185 | goto next_hook; |
186 | ret = 0; | ||
185 | } | 187 | } |
186 | rcu_read_unlock(); | 188 | rcu_read_unlock(); |
187 | return ret; | 189 | return ret; |
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 1876f7411561..ad25c7e726bc 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c | |||
@@ -125,7 +125,7 @@ static int __nf_queue(struct sk_buff *skb, | |||
125 | int (*okfn)(struct sk_buff *), | 125 | int (*okfn)(struct sk_buff *), |
126 | unsigned int queuenum) | 126 | unsigned int queuenum) |
127 | { | 127 | { |
128 | int status; | 128 | int status = -ENOENT; |
129 | struct nf_queue_entry *entry = NULL; | 129 | struct nf_queue_entry *entry = NULL; |
130 | #ifdef CONFIG_BRIDGE_NETFILTER | 130 | #ifdef CONFIG_BRIDGE_NETFILTER |
131 | struct net_device *physindev; | 131 | struct net_device *physindev; |
@@ -146,8 +146,10 @@ static int __nf_queue(struct sk_buff *skb, | |||
146 | goto err_unlock; | 146 | goto err_unlock; |
147 | 147 | ||
148 | entry = kmalloc(sizeof(*entry) + afinfo->route_key_size, GFP_ATOMIC); | 148 | entry = kmalloc(sizeof(*entry) + afinfo->route_key_size, GFP_ATOMIC); |
149 | if (!entry) | 149 | if (!entry) { |
150 | status = -ENOMEM; | ||
150 | goto err_unlock; | 151 | goto err_unlock; |
152 | } | ||
151 | 153 | ||
152 | *entry = (struct nf_queue_entry) { | 154 | *entry = (struct nf_queue_entry) { |
153 | .skb = skb, | 155 | .skb = skb, |
@@ -163,9 +165,8 @@ static int __nf_queue(struct sk_buff *skb, | |||
163 | if (!try_module_get(entry->elem->owner)) { | 165 | if (!try_module_get(entry->elem->owner)) { |
164 | rcu_read_unlock(); | 166 | rcu_read_unlock(); |
165 | kfree(entry); | 167 | kfree(entry); |
166 | return 0; | 168 | return -ECANCELED; |
167 | } | 169 | } |
168 | |||
169 | /* Bump dev refs so they don't vanish while packet is out */ | 170 | /* Bump dev refs so they don't vanish while packet is out */ |
170 | if (indev) | 171 | if (indev) |
171 | dev_hold(indev); | 172 | dev_hold(indev); |
@@ -192,14 +193,14 @@ static int __nf_queue(struct sk_buff *skb, | |||
192 | goto err; | 193 | goto err; |
193 | } | 194 | } |
194 | 195 | ||
195 | return 1; | 196 | return 0; |
196 | 197 | ||
197 | err_unlock: | 198 | err_unlock: |
198 | rcu_read_unlock(); | 199 | rcu_read_unlock(); |
199 | err: | 200 | err: |
200 | kfree_skb(skb); | 201 | kfree_skb(skb); |
201 | kfree(entry); | 202 | kfree(entry); |
202 | return 1; | 203 | return status; |
203 | } | 204 | } |
204 | 205 | ||
205 | int nf_queue(struct sk_buff *skb, | 206 | int nf_queue(struct sk_buff *skb, |
@@ -211,6 +212,8 @@ int nf_queue(struct sk_buff *skb, | |||
211 | unsigned int queuenum) | 212 | unsigned int queuenum) |
212 | { | 213 | { |
213 | struct sk_buff *segs; | 214 | struct sk_buff *segs; |
215 | int err; | ||
216 | unsigned int queued; | ||
214 | 217 | ||
215 | if (!skb_is_gso(skb)) | 218 | if (!skb_is_gso(skb)) |
216 | return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn, | 219 | return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn, |
@@ -227,19 +230,32 @@ int nf_queue(struct sk_buff *skb, | |||
227 | 230 | ||
228 | segs = skb_gso_segment(skb, 0); | 231 | segs = skb_gso_segment(skb, 0); |
229 | kfree_skb(skb); | 232 | kfree_skb(skb); |
233 | /* Does not use PTR_ERR to limit the number of error codes that can be | ||
234 | * returned by nf_queue. For instance, callers rely on -ECANCELED to mean | ||
235 | * 'ignore this hook'. | ||
236 | */ | ||
230 | if (IS_ERR(segs)) | 237 | if (IS_ERR(segs)) |
231 | return 1; | 238 | return -EINVAL; |
232 | 239 | ||
240 | queued = 0; | ||
241 | err = 0; | ||
233 | do { | 242 | do { |
234 | struct sk_buff *nskb = segs->next; | 243 | struct sk_buff *nskb = segs->next; |
235 | 244 | ||
236 | segs->next = NULL; | 245 | segs->next = NULL; |
237 | if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn, | 246 | if (err == 0) |
238 | queuenum)) | 247 | err = __nf_queue(segs, elem, pf, hook, indev, |
248 | outdev, okfn, queuenum); | ||
249 | if (err == 0) | ||
250 | queued++; | ||
251 | else | ||
239 | kfree_skb(segs); | 252 | kfree_skb(segs); |
240 | segs = nskb; | 253 | segs = nskb; |
241 | } while (segs); | 254 | } while (segs); |
242 | return 1; | 255 | |
256 | if (unlikely(err && queued)) | ||
257 | err = 0; | ||
258 | return err; | ||
243 | } | 259 | } |
244 | 260 | ||
245 | void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) | 261 | void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) |
@@ -247,6 +263,7 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) | |||
247 | struct sk_buff *skb = entry->skb; | 263 | struct sk_buff *skb = entry->skb; |
248 | struct list_head *elem = &entry->elem->list; | 264 | struct list_head *elem = &entry->elem->list; |
249 | const struct nf_afinfo *afinfo; | 265 | const struct nf_afinfo *afinfo; |
266 | int err; | ||
250 | 267 | ||
251 | rcu_read_lock(); | 268 | rcu_read_lock(); |
252 | 269 | ||
@@ -280,9 +297,10 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) | |||
280 | local_bh_enable(); | 297 | local_bh_enable(); |
281 | break; | 298 | break; |
282 | case NF_QUEUE: | 299 | case NF_QUEUE: |
283 | if (!__nf_queue(skb, elem, entry->pf, entry->hook, | 300 | err = __nf_queue(skb, elem, entry->pf, entry->hook, |
284 | entry->indev, entry->outdev, entry->okfn, | 301 | entry->indev, entry->outdev, entry->okfn, |
285 | verdict >> NF_VERDICT_BITS)) | 302 | verdict >> NF_VERDICT_BITS); |
303 | if (err == -ECANCELED) | ||
286 | goto next_hook; | 304 | goto next_hook; |
287 | break; | 305 | break; |
288 | case NF_STOLEN: | 306 | case NF_STOLEN: |
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 68e67d19724d..b83123f12b42 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c | |||
@@ -387,25 +387,31 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | |||
387 | { | 387 | { |
388 | struct sk_buff *nskb; | 388 | struct sk_buff *nskb; |
389 | struct nfqnl_instance *queue; | 389 | struct nfqnl_instance *queue; |
390 | int err; | 390 | int err = -ENOBUFS; |
391 | 391 | ||
392 | /* rcu_read_lock()ed by nf_hook_slow() */ | 392 | /* rcu_read_lock()ed by nf_hook_slow() */ |
393 | queue = instance_lookup(queuenum); | 393 | queue = instance_lookup(queuenum); |
394 | if (!queue) | 394 | if (!queue) { |
395 | err = -ESRCH; | ||
395 | goto err_out; | 396 | goto err_out; |
397 | } | ||
396 | 398 | ||
397 | if (queue->copy_mode == NFQNL_COPY_NONE) | 399 | if (queue->copy_mode == NFQNL_COPY_NONE) { |
400 | err = -EINVAL; | ||
398 | goto err_out; | 401 | goto err_out; |
402 | } | ||
399 | 403 | ||
400 | nskb = nfqnl_build_packet_message(queue, entry); | 404 | nskb = nfqnl_build_packet_message(queue, entry); |
401 | if (nskb == NULL) | 405 | if (nskb == NULL) { |
406 | err = -ENOMEM; | ||
402 | goto err_out; | 407 | goto err_out; |
403 | 408 | } | |
404 | spin_lock_bh(&queue->lock); | 409 | spin_lock_bh(&queue->lock); |
405 | 410 | ||
406 | if (!queue->peer_pid) | 411 | if (!queue->peer_pid) { |
412 | err = -EINVAL; | ||
407 | goto err_out_free_nskb; | 413 | goto err_out_free_nskb; |
408 | 414 | } | |
409 | if (queue->queue_total >= queue->queue_maxlen) { | 415 | if (queue->queue_total >= queue->queue_maxlen) { |
410 | queue->queue_dropped++; | 416 | queue->queue_dropped++; |
411 | if (net_ratelimit()) | 417 | if (net_ratelimit()) |
@@ -432,7 +438,7 @@ err_out_free_nskb: | |||
432 | err_out_unlock: | 438 | err_out_unlock: |
433 | spin_unlock_bh(&queue->lock); | 439 | spin_unlock_bh(&queue->lock); |
434 | err_out: | 440 | err_out: |
435 | return -1; | 441 | return err; |
436 | } | 442 | } |
437 | 443 | ||
438 | static int | 444 | static int |