diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-07-19 05:44:17 -0400 |
---|---|---|
committer | Patrick McHardy <kaber@trash.net> | 2011-07-19 05:44:17 -0400 |
commit | 5863702a3421b0d2a63a473cf96afeb9fe09070d (patch) | |
tree | d462b775b7702a4ae44ec50ec28a2217d5ab2338 /net | |
parent | 84a797dd0b9f7130357b70577fcbda8e638c71a7 (diff) |
netfilter: nfnetlink_queue: assert monotonic packet ids
Packet identifier is currently setup in nfqnl_build_packet_message(),
using one atomic_inc_return().
Problem is that since several cpus might concurrently call
nfqnl_enqueue_packet() for the same queue, we can deliver packets to
consumer in non monotonic way (packet N+1 being delivered after packet
N)
This patch moves the packet id setup from nfqnl_build_packet_message()
to nfqnl_enqueue_packet() to guarantee correct delivery order.
This also removes one atomic operation.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
CC: Florian Westphal <fw@strlen.de>
CC: Pablo Neira Ayuso <pablo@netfilter.org>
CC: Eric Leblond <eric@regit.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/nfnetlink_queue.c | 26 |
1 files changed, 15 insertions, 11 deletions
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index c645b87915b8..3b2af8cb7de9 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c | |||
@@ -58,7 +58,7 @@ struct nfqnl_instance { | |||
58 | */ | 58 | */ |
59 | spinlock_t lock; | 59 | spinlock_t lock; |
60 | unsigned int queue_total; | 60 | unsigned int queue_total; |
61 | atomic_t id_sequence; /* 'sequence' of pkt ids */ | 61 | unsigned int id_sequence; /* 'sequence' of pkt ids */ |
62 | struct list_head queue_list; /* packets in queue */ | 62 | struct list_head queue_list; /* packets in queue */ |
63 | }; | 63 | }; |
64 | 64 | ||
@@ -213,13 +213,15 @@ nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) | |||
213 | 213 | ||
214 | static struct sk_buff * | 214 | static struct sk_buff * |
215 | nfqnl_build_packet_message(struct nfqnl_instance *queue, | 215 | nfqnl_build_packet_message(struct nfqnl_instance *queue, |
216 | struct nf_queue_entry *entry) | 216 | struct nf_queue_entry *entry, |
217 | __be32 **packet_id_ptr) | ||
217 | { | 218 | { |
218 | sk_buff_data_t old_tail; | 219 | sk_buff_data_t old_tail; |
219 | size_t size; | 220 | size_t size; |
220 | size_t data_len = 0; | 221 | size_t data_len = 0; |
221 | struct sk_buff *skb; | 222 | struct sk_buff *skb; |
222 | struct nfqnl_msg_packet_hdr pmsg; | 223 | struct nlattr *nla; |
224 | struct nfqnl_msg_packet_hdr *pmsg; | ||
223 | struct nlmsghdr *nlh; | 225 | struct nlmsghdr *nlh; |
224 | struct nfgenmsg *nfmsg; | 226 | struct nfgenmsg *nfmsg; |
225 | struct sk_buff *entskb = entry->skb; | 227 | struct sk_buff *entskb = entry->skb; |
@@ -272,12 +274,11 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, | |||
272 | nfmsg->version = NFNETLINK_V0; | 274 | nfmsg->version = NFNETLINK_V0; |
273 | nfmsg->res_id = htons(queue->queue_num); | 275 | nfmsg->res_id = htons(queue->queue_num); |
274 | 276 | ||
275 | entry->id = atomic_inc_return(&queue->id_sequence); | 277 | nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); |
276 | pmsg.packet_id = htonl(entry->id); | 278 | pmsg = nla_data(nla); |
277 | pmsg.hw_protocol = entskb->protocol; | 279 | pmsg->hw_protocol = entskb->protocol; |
278 | pmsg.hook = entry->hook; | 280 | pmsg->hook = entry->hook; |
279 | 281 | *packet_id_ptr = &pmsg->packet_id; | |
280 | NLA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg); | ||
281 | 282 | ||
282 | indev = entry->indev; | 283 | indev = entry->indev; |
283 | if (indev) { | 284 | if (indev) { |
@@ -388,6 +389,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | |||
388 | struct sk_buff *nskb; | 389 | struct sk_buff *nskb; |
389 | struct nfqnl_instance *queue; | 390 | struct nfqnl_instance *queue; |
390 | int err = -ENOBUFS; | 391 | int err = -ENOBUFS; |
392 | __be32 *packet_id_ptr; | ||
391 | 393 | ||
392 | /* rcu_read_lock()ed by nf_hook_slow() */ | 394 | /* rcu_read_lock()ed by nf_hook_slow() */ |
393 | queue = instance_lookup(queuenum); | 395 | queue = instance_lookup(queuenum); |
@@ -401,7 +403,7 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | |||
401 | goto err_out; | 403 | goto err_out; |
402 | } | 404 | } |
403 | 405 | ||
404 | nskb = nfqnl_build_packet_message(queue, entry); | 406 | nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); |
405 | if (nskb == NULL) { | 407 | if (nskb == NULL) { |
406 | err = -ENOMEM; | 408 | err = -ENOMEM; |
407 | goto err_out; | 409 | goto err_out; |
@@ -420,6 +422,8 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | |||
420 | queue->queue_total); | 422 | queue->queue_total); |
421 | goto err_out_free_nskb; | 423 | goto err_out_free_nskb; |
422 | } | 424 | } |
425 | entry->id = ++queue->id_sequence; | ||
426 | *packet_id_ptr = htonl(entry->id); | ||
423 | 427 | ||
424 | /* nfnetlink_unicast will either free the nskb or add it to a socket */ | 428 | /* nfnetlink_unicast will either free the nskb or add it to a socket */ |
425 | err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); | 429 | err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); |
@@ -852,7 +856,7 @@ static int seq_show(struct seq_file *s, void *v) | |||
852 | inst->peer_pid, inst->queue_total, | 856 | inst->peer_pid, inst->queue_total, |
853 | inst->copy_mode, inst->copy_range, | 857 | inst->copy_mode, inst->copy_range, |
854 | inst->queue_dropped, inst->queue_user_dropped, | 858 | inst->queue_dropped, inst->queue_user_dropped, |
855 | atomic_read(&inst->id_sequence), 1); | 859 | inst->id_sequence, 1); |
856 | } | 860 | } |
857 | 861 | ||
858 | static const struct seq_operations nfqnl_seq_ops = { | 862 | static const struct seq_operations nfqnl_seq_ops = { |