summaryrefslogtreecommitdiffstats
path: root/net/ipv6/reassembly.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-06-30 00:21:34 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-06-30 00:21:34 +0000
commit3917ac5846dd0f9ad1238166f90caab9912052e6 (patch)
tree1c298935def4f29edb39192365a65d73de999155 /net/ipv6/reassembly.c
parentaf2f803c8b2d469fe38e4a7ce952658dfcb6681a (diff)
o Merge with Linux 2.1.100.
o Cleanup the machine dependencies of floppy and rtc. The driver for the Dallas thingy in the Indy is still missing. o Handle allocation of zero'd pages correct for R4000SC / R4400SC. o Page colouring shit to match the virtual and physical colour of all mapped pages. This tends to produce extreme fragmentation problems, so it's deactivated for now. Users of R4000SC / R4400SC may re-enable the code in arch/mips/mm/init.c by removing the definition of CONF_GIVE_A_SHIT_ABOUT_COLOURS. Should get them somewhat further - but don't shake to hard ... o Fixed ptrace(2)-ing of syscalls, strace is now working again. o Fix the interrupt forwarding from the keyboard driver to the psaux driver, PS/2 mice are now working on the Indy. The fix is somewhat broken as it prevents generic kernels for Indy and machines which handle things different. o Things I can't remember.
Diffstat (limited to 'net/ipv6/reassembly.c')
-rw-r--r--net/ipv6/reassembly.c79
1 files changed, 61 insertions, 18 deletions
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 55fecc676..e78cf97a2 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: reassembly.c,v 1.9 1998/02/12 07:43:48 davem Exp $
+ * $Id: reassembly.c,v 1.10 1998/04/30 16:24:32 freitag Exp $
*
* Based on: net/ipv4/ip_fragment.c
*
@@ -15,6 +15,11 @@
* 2 of the License, or (at your option) any later version.
*/
+/*
+ * Fixes:
+ * Andi Kleen Make it work with multiple hosts.
+ * More RFC compliance.
+ */
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -39,8 +44,9 @@
static struct frag_queue ipv6_frag_queue = {
&ipv6_frag_queue, &ipv6_frag_queue,
- 0, {0}, NULL, NULL,
- 0
+ 0, {{{0}}}, {{{0}}},
+ {0}, NULL, NULL,
+ 0, 0, NULL
};
static void create_frag_entry(struct sk_buff *skb,
@@ -72,12 +78,11 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb,
* one it's the kmalloc for a struct ipv6_frag.
* Feel free to try other alternatives...
*/
- reasm_queue(fq, *skb, fhdr);
-
if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
fq->last_in = 1;
fq->nhptr = nhptr;
}
+ reasm_queue(fq, *skb, fhdr);
if (fq->last_in) {
if ((nh = reasm_frag_1(fq, skb)))
@@ -90,18 +95,27 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb,
return 0;
}
-int ipv6_reassembly(struct sk_buff **skb, struct device *dev, __u8 *nhptr,
+int ipv6_reassembly(struct sk_buff **skbp, struct device *dev, __u8 *nhptr,
struct ipv6_options *opt)
{
- struct frag_hdr *fhdr = (struct frag_hdr *) ((*skb)->h.raw);
+ struct sk_buff *skb = *skbp;
+ struct frag_hdr *fhdr = (struct frag_hdr *) (skb->h.raw);
struct frag_queue *fq;
-
+ struct ipv6hdr *hdr;
+
+ if ((u8 *)(fhdr+1) > skb->tail) {
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw);
+ return 0;
+ }
+ hdr = skb->nh.ipv6h;
for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
- if (fq->id == fhdr->identification)
- return reasm_frag(fq, skb, nhptr,fhdr);
+ if (fq->id == fhdr->identification &&
+ !ipv6_addr_cmp(&hdr->saddr, &fq->saddr) &&
+ !ipv6_addr_cmp(&hdr->daddr, &fq->daddr))
+ return reasm_frag(fq, skbp, nhptr,fhdr);
}
- create_frag_entry(*skb, dev, nhptr, fhdr);
+ create_frag_entry(skb, dev, nhptr, fhdr);
return 0;
}
@@ -154,6 +168,7 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
struct frag_hdr *fhdr)
{
struct frag_queue *fq;
+ struct ipv6hdr *hdr;
fq = (struct frag_queue *) kmalloc(sizeof(struct frag_queue),
GFP_ATOMIC);
@@ -167,6 +182,10 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
fq->id = fhdr->identification;
+ hdr = skb->nh.ipv6h;
+ ipv6_addr_copy(&fq->saddr, &hdr->saddr);
+ ipv6_addr_copy(&fq->daddr, &hdr->daddr);
+
fq->dev = dev;
/* init_timer has been done by the memset */
@@ -193,14 +212,14 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
- struct frag_hdr *fhdr)
+ struct frag_hdr *fhdr)
{
struct ipv6_frag *nfp, *fp, **bptr;
nfp = (struct ipv6_frag *) kmalloc(sizeof(struct ipv6_frag),
GFP_ATOMIC);
- if (nfp == NULL) {
+ if (nfp == NULL) {
kfree_skb(skb);
return;
}
@@ -209,6 +228,10 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
+ if ((u32)nfp->offset + (u32)nfp->len > 65536) {
+ icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off);
+ goto err;
+ }
nfp->skb = skb;
nfp->fhdr = fhdr;
@@ -224,19 +247,37 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
}
if (fp && fp->offset == nfp->offset) {
- if (fp->len != nfp->len) {
- /* this cannot happen */
+ if (nfp->len != fp->len) {
printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
}
/* duplicate. discard it. */
- kfree_skb(skb);
- kfree(nfp);
- return;
+ goto err;
}
*bptr = nfp;
nfp->next = fp;
+
+#ifdef STRICT_RFC
+ if (fhdr->frag_off & __constant_htons(0x0001)) {
+ /* Check if the fragment is rounded to 8 bytes.
+ * Required by the RFC.
+ */
+ if (nfp->len & 0x7) {
+ printk(KERN_DEBUG "fragment not rounded to 8bytes\n");
+
+ icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
+ &skb->nh.ipv6h->payload_len);
+ goto err;
+ }
+ }
+#endif
+
+ return;
+
+err:
+ kfree(nfp);
+ kfree_skb(skb);
}
/*
@@ -303,6 +344,8 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
/*
* FIXME: If we don't have a checksum we ought to be able
* to defragment and checksum in this pass. [AC]
+ * Note that we don't really know yet whether the protocol
+ * needs checksums at all. It might still be a good idea. -AK
*/
for(fp = fq->fragments; fp; ) {
struct ipv6_frag *back;