diff options
author | David S. Miller <davem@davemloft.net> | 2011-07-05 04:43:20 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-07-06 01:34:52 -0400 |
commit | 7736d33f4262d437c51ed7a28114eacbfca236ff (patch) | |
tree | fdefc344c66940773982d23203a7fc07aa94e1ef /net/packet | |
parent | 595fc71baa1e80420fe89a400ff2d9cc099d22fc (diff) |
packet: Add pre-defragmentation support for ipv4 fanouts.
The skb->rxhash cannot be properly computed if the
packet is a fragment. To alleviate this, allow the
AF_PACKET client to ask for defragmentation to be
done at demux time.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/packet')
-rw-r--r-- | net/packet/af_packet.c | 50 |
1 files changed, 48 insertions, 2 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 3350f1d3c9aa..7ba6871a1942 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
@@ -223,7 +223,7 @@ struct packet_fanout { | |||
223 | unsigned int num_members; | 223 | unsigned int num_members; |
224 | u16 id; | 224 | u16 id; |
225 | u8 type; | 225 | u8 type; |
226 | u8 pad; | 226 | u8 defrag; |
227 | atomic_t rr_cur; | 227 | atomic_t rr_cur; |
228 | struct list_head list; | 228 | struct list_head list; |
229 | struct sock *arr[PACKET_FANOUT_MAX]; | 229 | struct sock *arr[PACKET_FANOUT_MAX]; |
@@ -447,6 +447,41 @@ static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb | |||
447 | return f->arr[cur]; | 447 | return f->arr[cur]; |
448 | } | 448 | } |
449 | 449 | ||
450 | static struct sk_buff *fanout_check_defrag(struct sk_buff *skb) | ||
451 | { | ||
452 | const struct iphdr *iph; | ||
453 | u32 len; | ||
454 | |||
455 | if (skb->protocol != htons(ETH_P_IP)) | ||
456 | return skb; | ||
457 | |||
458 | if (!pskb_may_pull(skb, sizeof(struct iphdr))) | ||
459 | return skb; | ||
460 | |||
461 | iph = ip_hdr(skb); | ||
462 | if (iph->ihl < 5 || iph->version != 4) | ||
463 | return skb; | ||
464 | if (!pskb_may_pull(skb, iph->ihl*4)) | ||
465 | return skb; | ||
466 | iph = ip_hdr(skb); | ||
467 | len = ntohs(iph->tot_len); | ||
468 | if (skb->len < len || len < (iph->ihl * 4)) | ||
469 | return skb; | ||
470 | |||
471 | if (ip_is_fragment(ip_hdr(skb))) { | ||
472 | skb = skb_clone(skb, GFP_ATOMIC); | ||
473 | if (skb) { | ||
474 | if (pskb_trim_rcsum(skb, len)) | ||
475 | return skb; | ||
476 | memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); | ||
477 | if (ip_defrag(skb, IP_DEFRAG_AF_PACKET)) | ||
478 | return NULL; | ||
479 | skb->rxhash = 0; | ||
480 | } | ||
481 | } | ||
482 | return skb; | ||
483 | } | ||
484 | |||
450 | static int packet_rcv_fanout_hash(struct sk_buff *skb, struct net_device *dev, | 485 | static int packet_rcv_fanout_hash(struct sk_buff *skb, struct net_device *dev, |
451 | struct packet_type *pt, struct net_device *orig_dev) | 486 | struct packet_type *pt, struct net_device *orig_dev) |
452 | { | 487 | { |
@@ -461,6 +496,12 @@ static int packet_rcv_fanout_hash(struct sk_buff *skb, struct net_device *dev, | |||
461 | return 0; | 496 | return 0; |
462 | } | 497 | } |
463 | 498 | ||
499 | if (f->defrag) { | ||
500 | skb = fanout_check_defrag(skb); | ||
501 | if (!skb) | ||
502 | return 0; | ||
503 | } | ||
504 | |||
464 | skb_get_rxhash(skb); | 505 | skb_get_rxhash(skb); |
465 | 506 | ||
466 | sk = fanout_demux_hash(f, skb, num); | 507 | sk = fanout_demux_hash(f, skb, num); |
@@ -519,10 +560,12 @@ static void __fanout_unlink(struct sock *sk, struct packet_sock *po) | |||
519 | spin_unlock(&f->lock); | 560 | spin_unlock(&f->lock); |
520 | } | 561 | } |
521 | 562 | ||
522 | static int fanout_add(struct sock *sk, u16 id, u8 type) | 563 | static int fanout_add(struct sock *sk, u16 id, u16 type_flags) |
523 | { | 564 | { |
524 | struct packet_sock *po = pkt_sk(sk); | 565 | struct packet_sock *po = pkt_sk(sk); |
525 | struct packet_fanout *f, *match; | 566 | struct packet_fanout *f, *match; |
567 | u8 type = type_flags & 0xff; | ||
568 | u8 defrag = (type_flags & PACKET_FANOUT_FLAG_DEFRAG) ? 1 : 0; | ||
526 | int err; | 569 | int err; |
527 | 570 | ||
528 | switch (type) { | 571 | switch (type) { |
@@ -548,12 +591,15 @@ static int fanout_add(struct sock *sk, u16 id, u8 type) | |||
548 | break; | 591 | break; |
549 | } | 592 | } |
550 | } | 593 | } |
594 | if (match && match->defrag != defrag) | ||
595 | return -EINVAL; | ||
551 | if (!match) { | 596 | if (!match) { |
552 | match = kzalloc(sizeof(*match), GFP_KERNEL); | 597 | match = kzalloc(sizeof(*match), GFP_KERNEL); |
553 | if (match) { | 598 | if (match) { |
554 | write_pnet(&match->net, sock_net(sk)); | 599 | write_pnet(&match->net, sock_net(sk)); |
555 | match->id = id; | 600 | match->id = id; |
556 | match->type = type; | 601 | match->type = type; |
602 | match->defrag = defrag; | ||
557 | atomic_set(&match->rr_cur, 0); | 603 | atomic_set(&match->rr_cur, 0); |
558 | INIT_LIST_HEAD(&match->list); | 604 | INIT_LIST_HEAD(&match->list); |
559 | spin_lock_init(&match->lock); | 605 | spin_lock_init(&match->lock); |