#include <linux/icmpv6.h>
#include <linux/random.h>
#include <linux/jhash.h>
+#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/snmp.h>
static LIST_HEAD(ip6_frag_lru_list);
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)
{
hlist_del(&fq->list);
fq_kill(fq);
- dev = dev_get_by_index(fq->iif);
+ dev = dev_get_by_index(&init_net, fq->iif);
if (!dev)
goto out;
}
-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 sk_buff *prev, *next;
+ struct net_device *dev;
int offset, end;
if (fq->last_in & COMPLETE)
goto err;
offset = ntohs(fhdr->frag_off) & ~0x7;
- end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
- ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
+ end = offset + (ntohs(ipv6_hdr(skb)->payload_len) -
+ ((u8 *)(fhdr + 1) - (u8 *)(ipv6_hdr(skb) + 1)));
if ((unsigned int)end > IPV6_MAXPLEN) {
IP6_INC_STATS_BH(ip6_dst_idev(skb->dst),
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
((u8 *)&fhdr->frag_off -
skb_network_header(skb)));
- return;
+ return -1;
}
if (skb->ip_summed == CHECKSUM_COMPLETE) {
IPSTATS_MIB_INHDRERRORS);
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
offsetof(struct ipv6hdr, payload_len));
- return;
+ return -1;
}
if (end > fq->len) {
/* Some bits beyond end -> corruption. */
else
fq->fragments = skb;
- if (skb->dev)
- fq->iif = skb->dev->ifindex;
- skb->dev = NULL;
+ dev = skb->dev;
+ if (dev) {
+ fq->iif = dev->ifindex;
+ skb->dev = NULL;
+ }
fq->stamp = skb->tstamp;
fq->meat += skb->len;
atomic_add(skb->truesize, &ip6_frag_mem);
fq->nhoffset = nhoff;
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);
list_move_tail(&fq->lru_list, &ip6_frag_lru_list);
write_unlock(&ip6_frag_lock);
- return;
+ return -1;
err:
IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMFAILS);
kfree_skb(skb);
+ return -1;
}
/*
* queue is eligible for reassembly i.e. it is not COMPLETE,
* 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 sk_buff *fp, *head = fq->fragments;
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(FRAG6_CB(head)->offset == 0);
/* We have to remove fragment header from datagram and to relocate
* header in order to calculate ICV correctly. */
nhoff = fq->nhoffset;
- skb_network_header(head)[nhoff] = head->h.raw[0];
+ skb_network_header(head)[nhoff] = skb_transport_header(head)[0];
memmove(head->head + sizeof(struct frag_hdr), head->head,
(head->data - head->head) - sizeof(struct frag_hdr));
- head->mac.raw += sizeof(struct frag_hdr);
- head->nh.raw += sizeof(struct frag_hdr);
+ head->mac_header += sizeof(struct frag_hdr);
+ head->network_header += sizeof(struct frag_hdr);
skb_shinfo(head)->frag_list = head->next;
- head->h.raw = head->data;
+ skb_reset_transport_header(head);
skb_push(head, head->data - skb_network_header(head));
atomic_sub(head->truesize, &ip6_frag_mem);
head->next = NULL;
head->dev = dev;
head->tstamp = fq->stamp;
- head->nh.ipv6h->payload_len = htons(payload_len);
+ ipv6_hdr(head)->payload_len = htons(payload_len);
IP6CB(head)->nhoff = nhoff;
- *skb_in = head;
-
/* Yes, and fold redundant checksum back. 8) */
if (head->ip_summed == CHECKSUM_COMPLETE)
head->csum = csum_partial(skb_network_header(head),
- head->h.raw - head->nh.raw,
+ skb_network_header_len(head),
head->csum);
rcu_read_lock();
static int ipv6_frag_rcv(struct sk_buff **skbp)
{
struct sk_buff *skb = *skbp;
- struct net_device *dev = skb->dev;
struct frag_hdr *fhdr;
struct frag_queue *fq;
- struct ipv6hdr *hdr;
-
- hdr = skb->nh.ipv6h;
+ struct ipv6hdr *hdr = ipv6_hdr(skb);
IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMREQDS);
/* Jumbo payload inhibits frag. header */
if (hdr->payload_len==0) {
IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+ skb_network_header_len(skb));
return -1;
}
- if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) {
+ if (!pskb_may_pull(skb, (skb_transport_offset(skb) +
+ sizeof(struct frag_hdr)))) {
IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS);
- icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+ skb_network_header_len(skb));
return -1;
}
- hdr = skb->nh.ipv6h;
- fhdr = (struct frag_hdr *)skb->h.raw;
+ hdr = ipv6_hdr(skb);
+ fhdr = (struct frag_hdr *)skb_transport_header(skb);
if (!(fhdr->frag_off & htons(0xFFF9))) {
/* It is not a fragmented frame */
- skb->h.raw += sizeof(struct frag_hdr);
+ skb->transport_header += sizeof(struct frag_hdr);
IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_REASMOKS);
IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr,
ip6_dst_idev(skb->dst))) != NULL) {
- int ret = -1;
+ int ret;
spin_lock(&fq->lock);
- 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);
+ ret = ip6_frag_queue(fq, skb, fhdr, IP6CB(skb)->nhoff);
spin_unlock(&fq->lock);
fq_put(fq, NULL);