Commit f61944ef authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[IPV6]: Make ipv6_frag_rcv return the same packet

This patch implements the same change taht was done to ip_defrag.  It
makes ipv6_frag_rcv return the last packet received of a train of fragments
rather than the head of that sequence.

This allows us to get rid of the sk_buff ** argument later.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3db05fea
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/icmpv6.h> #include <linux/icmpv6.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/skbuff.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/snmp.h> #include <net/snmp.h>
...@@ -107,6 +108,9 @@ static u32 ip6_frag_hash_rnd; ...@@ -107,6 +108,9 @@ static u32 ip6_frag_hash_rnd;
static LIST_HEAD(ip6_frag_lru_list); static LIST_HEAD(ip6_frag_lru_list);
int ip6_frag_nqueues = 0; int ip6_frag_nqueues = 0;
static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
struct net_device *dev);
static __inline__ void __fq_unlink(struct frag_queue *fq) static __inline__ void __fq_unlink(struct frag_queue *fq)
{ {
hlist_del(&fq->list); hlist_del(&fq->list);
...@@ -420,10 +424,11 @@ fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst, ...@@ -420,10 +424,11 @@ fq_find(__be32 id, struct in6_addr *src, struct in6_addr *dst,
} }
static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, static int ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
struct frag_hdr *fhdr, int nhoff) struct frag_hdr *fhdr, int nhoff)
{ {
struct sk_buff *prev, *next; struct sk_buff *prev, *next;
struct net_device *dev;
int offset, end; int offset, end;
if (fq->last_in & COMPLETE) if (fq->last_in & COMPLETE)
...@@ -439,7 +444,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, ...@@ -439,7 +444,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
((u8 *)&fhdr->frag_off - ((u8 *)&fhdr->frag_off -
skb_network_header(skb))); skb_network_header(skb)));
return; return -1;
} }
if (skb->ip_summed == CHECKSUM_COMPLETE) { if (skb->ip_summed == CHECKSUM_COMPLETE) {
...@@ -471,7 +476,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, ...@@ -471,7 +476,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
IPSTATS_MIB_INHDRERRORS); IPSTATS_MIB_INHDRERRORS);
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
offsetof(struct ipv6hdr, payload_len)); offsetof(struct ipv6hdr, payload_len));
return; return -1;
} }
if (end > fq->len) { if (end > fq->len) {
/* Some bits beyond end -> corruption. */ /* Some bits beyond end -> corruption. */
...@@ -564,9 +569,11 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, ...@@ -564,9 +569,11 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
else else
fq->fragments = skb; fq->fragments = skb;
if (skb->dev) dev = skb->dev;
fq->iif = skb->dev->ifindex; if (dev) {
fq->iif = dev->ifindex;
skb->dev = NULL; skb->dev = NULL;
}
fq->stamp = skb->tstamp; fq->stamp = skb->tstamp;
fq->meat += skb->len; fq->meat += skb->len;
atomic_add(skb->truesize, &ip6_frag_mem); atomic_add(skb->truesize, &ip6_frag_mem);
...@@ -578,14 +585,19 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, ...@@ -578,14 +585,19 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
fq->nhoffset = nhoff; fq->nhoffset = nhoff;
fq->last_in |= FIRST_IN; fq->last_in |= FIRST_IN;
} }
if (fq->last_in == (FIRST_IN | LAST_IN) && fq->meat == fq->len)
return ip6_frag_reasm(fq, prev, dev);
write_lock(&ip6_frag_lock); write_lock(&ip6_frag_lock);
list_move_tail(&fq->lru_list, &ip6_frag_lru_list); list_move_tail(&fq->lru_list, &ip6_frag_lru_list);
write_unlock(&ip6_frag_lock); write_unlock(&ip6_frag_lock);
return; return -1;
err: err:
IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMFAILS); IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMFAILS);
kfree_skb(skb); kfree_skb(skb);
return -1;
} }
/* /*
...@@ -597,7 +609,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, ...@@ -597,7 +609,7 @@ static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb,
* queue is eligible for reassembly i.e. it is not COMPLETE, * queue is eligible for reassembly i.e. it is not COMPLETE,
* the last and the first frames arrived and all the bits are here. * the last and the first frames arrived and all the bits are here.
*/ */
static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
struct net_device *dev) struct net_device *dev)
{ {
struct sk_buff *fp, *head = fq->fragments; struct sk_buff *fp, *head = fq->fragments;
...@@ -606,6 +618,24 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, ...@@ -606,6 +618,24 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
fq_kill(fq); fq_kill(fq);
/* Make the one we just received the head. */
if (prev) {
head = prev->next;
fp = skb_clone(head, GFP_ATOMIC);
if (!fp)
goto out_oom;
fp->next = head->next;
prev->next = fp;
skb_morph(head, fq->fragments);
head->next = fq->fragments->next;
kfree_skb(fq->fragments);
fq->fragments = head;
}
BUG_TRAP(head != NULL); BUG_TRAP(head != NULL);
BUG_TRAP(FRAG6_CB(head)->offset == 0); BUG_TRAP(FRAG6_CB(head)->offset == 0);
...@@ -674,8 +704,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, ...@@ -674,8 +704,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
ipv6_hdr(head)->payload_len = htons(payload_len); ipv6_hdr(head)->payload_len = htons(payload_len);
IP6CB(head)->nhoff = nhoff; IP6CB(head)->nhoff = nhoff;
*skb_in = head;
/* Yes, and fold redundant checksum back. 8) */ /* Yes, and fold redundant checksum back. 8) */
if (head->ip_summed == CHECKSUM_COMPLETE) if (head->ip_summed == CHECKSUM_COMPLETE)
head->csum = csum_partial(skb_network_header(head), head->csum = csum_partial(skb_network_header(head),
...@@ -705,7 +733,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, ...@@ -705,7 +733,6 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
static int ipv6_frag_rcv(struct sk_buff **skbp) static int ipv6_frag_rcv(struct sk_buff **skbp)
{ {
struct sk_buff *skb = *skbp; struct sk_buff *skb = *skbp;
struct net_device *dev = skb->dev;
struct frag_hdr *fhdr; struct frag_hdr *fhdr;
struct frag_queue *fq; struct frag_queue *fq;
struct ipv6hdr *hdr = ipv6_hdr(skb); struct ipv6hdr *hdr = ipv6_hdr(skb);
...@@ -744,15 +771,11 @@ static int ipv6_frag_rcv(struct sk_buff **skbp) ...@@ -744,15 +771,11 @@ static int ipv6_frag_rcv(struct sk_buff **skbp)
if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr, if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr,
ip6_dst_idev(skb->dst))) != NULL) { ip6_dst_idev(skb->dst))) != NULL) {
int ret = -1; int ret;
spin_lock(&fq->lock); spin_lock(&fq->lock);
ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff); ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff);
if (fq->last_in == (FIRST_IN|LAST_IN) &&
fq->meat == fq->len)
ret = ip6_frag_reasm(fq, skbp, dev);
spin_unlock(&fq->lock); spin_unlock(&fq->lock);
fq_put(fq, NULL); fq_put(fq, NULL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment