Subject: [PATCH] fix esp6 extension headers handling

From: Mitsuru KANDA / 神田 充<[email-masked]>
Date: Sat, 07 Jun 2003 03:17:10 +0900

Hello,

At Thu, 05 Jun 2003 15:59:32 +0300,
Henrik Petander <[email-masked]> wrote:
> 
> David S. Miller wrote:
> >    From: Henrik Petander <[email-masked]>
> >    Date: Thu, 05 Jun 2003 15:25:14 +0300
> >    
> >    A possible fix is to change the pointer into an offset from the start of 
> >    the packet and use the offset later to set the nexthdr value in the 
> >    extension header.
> > 
> > Please indicate the version of the sources you are looking
> > at when making reports.
> 
> Sure, esp6.c bitkeeper version was 1.16. Also a fix to the bug report: 
> the problem is with esp6 and not with ah6, which does not use the 
> get_offset function.
> 
> Henrik
> 

The attached diff fixes esp6 extension headers handling bug 
which reported by Henrik.

I introduced ip6_find_1stfragopt() instead of get_offset().

# ip6_found_nexthdr() is just renamed ip6_find_1stfragopt()
# in order to represent collect functionality.

Regards,
-mk

Index: linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/include/net/ipv6.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/include/net/ipv6.h,v
retrieving revision 1.1.1.12
retrieving revision 1.1.1.12.8.1
diff -u -r1.1.1.12 -r1.1.1.12.8.1
--- linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/include/net/ipv6.h	31 May 2003 07:30:34 -0000	1.1.1.12
+++ linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/include/net/ipv6.h	6 Jun 2003 15:43:46 -0000	1.1.1.12.8.1
@@ -315,7 +315,7 @@
 					       unsigned length,
 					       struct ipv6_txoptions *opt,
 					       int hlimit, int flags);
-extern int			ip6_found_nexthdr(struct sk_buff *skb, u8 **nexthdr);
+extern int			ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
 
 extern int			ip6_append_data(struct sock *sk,
 						int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb),
Index: linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/esp6.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/esp6.c,v
retrieving revision 1.1.1.13
retrieving revision 1.1.1.13.12.1
diff -u -r1.1.1.13 -r1.1.1.13.12.1
--- linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/esp6.c	26 May 2003 08:04:11 -0000	1.1.1.13
+++ linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/esp6.c	6 Jun 2003 16:23:01 -0000	1.1.1.13.12.1
@@ -39,57 +39,6 @@
 
 #define MAX_SG_ONSTACK 4
 
-/* BUGS:
- * - we assume replay seqno is always present.
- */
-
-/* Move to common area: it is shared with AH. */
-/* Common with AH after some work on arguments. */
-
-static int get_offset(u8 *packet, u32 packet_len, u8 *nexthdr, struct ipv6_opt_hdr **prevhdr)
-{
-	u16 offset = sizeof(struct ipv6hdr);
-	struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(packet + offset);
-	u8 nextnexthdr;
-
-	*nexthdr = ((struct ipv6hdr*)packet)->nexthdr;
-
-	while (offset + 1 < packet_len) {
-
-		switch (*nexthdr) {
-
-		case NEXTHDR_HOP:
-		case NEXTHDR_ROUTING:
-			offset += ipv6_optlen(exthdr);
-			*nexthdr = exthdr->nexthdr;
-			*prevhdr = exthdr;
-			exthdr = (struct ipv6_opt_hdr*)(packet + offset);
-			break;
-
-		case NEXTHDR_DEST:
-			nextnexthdr =
-				((struct ipv6_opt_hdr*)(packet + offset + ipv6_optlen(exthdr)))->nexthdr;
-			/* XXX We know the option is inner dest opt
-			   with next next header check. */
-			if (nextnexthdr != NEXTHDR_HOP &&
-		    	    nextnexthdr != NEXTHDR_ROUTING &&
-			    nextnexthdr != NEXTHDR_DEST) {
-					return offset;
-			}
-			offset += ipv6_optlen(exthdr);
-			*nexthdr = exthdr->nexthdr;
-			*prevhdr = exthdr;
-			exthdr = (struct ipv6_opt_hdr*)(packet + offset);
-			break;
-
-		default :
-			return offset;
-		}
-	}
-
-	return offset;
-}
-
 int esp6_output(struct sk_buff *skb)
 {
 	int err;
@@ -101,12 +50,12 @@
 	struct crypto_tfm *tfm;
 	struct esp_data *esp;
 	struct sk_buff *trailer;
-	struct ipv6_opt_hdr *prevhdr = NULL;
 	int blksize;
 	int clen;
 	int alen;
 	int nfrags;
-	u8 nexthdr;
+	u8 *prevhdr;
+	u8 nexthdr = 0;
 
 	/* First, if the skb is not checksummed, complete checksum. */
 	if (skb->ip_summed == CHECKSUM_HW && skb_checksum_help(skb) == NULL) {
@@ -123,7 +72,9 @@
 	/* Strip IP header in transport mode. Save it. */
 
 	if (!x->props.mode) {
-		hdr_len = get_offset(skb->nh.raw, skb->len, &nexthdr, &prevhdr);
+		hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
+		nexthdr = *prevhdr;
+		*prevhdr = IPPROTO_ESP;
 		iph = kmalloc(hdr_len, GFP_ATOMIC);
 		if (!iph) {
 			err = -ENOMEM;
@@ -178,18 +129,12 @@
 		ipv6_addr_copy(&top_iph->daddr,
 			       (struct in6_addr *)&x->id.daddr);
 	} else { 
-		/* XXX exthdr */
 		esph = (struct ipv6_esp_hdr*)skb_push(skb, x->props.header_len);
 		skb->h.raw = (unsigned char*)esph;
 		top_iph = (struct ipv6hdr*)skb_push(skb, hdr_len);
 		memcpy(top_iph, iph, hdr_len);
 		kfree(iph);
 		top_iph->payload_len = htons(skb->len + alen - sizeof(struct ipv6hdr));
-		if (prevhdr) {
-			prevhdr->nexthdr = IPPROTO_ESP;
-		} else {
-			top_iph->nexthdr = IPPROTO_ESP;
-		}
 		*(u8*)(trailer->tail - 1) = nexthdr;
 	}
 
@@ -302,6 +247,7 @@
 		struct scatterlist sgbuf[nfrags>MAX_SG_ONSTACK ? 0 : nfrags];
 		struct scatterlist *sg = sgbuf;
 		u8 padlen;
+		u8 *prevhdr;
 
 		if (unlikely(nfrags > MAX_SG_ONSTACK)) {
 			sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
@@ -325,11 +271,13 @@
 		}
 		/* ... check padding bits here. Silly. :-) */ 
 
-		ret_nexthdr = ((struct ipv6hdr*)tmp_hdr)->nexthdr = nexthdr[1];
 		pskb_trim(skb, skb->len - alen - padlen - 2);
 		skb->h.raw = skb_pull(skb, sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen);
 		skb->nh.raw += sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen;
 		memcpy(skb->nh.raw, tmp_hdr, hdr_len);
+		skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
+		ip6_find_1stfragopt(skb, &prevhdr);
+		ret_nexthdr = *prevhdr = nexthdr[1];
 	}
 	kfree(tmp_hdr);
 	return ret_nexthdr;
Index: linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ip6_output.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ip6_output.c,v
retrieving revision 1.1.1.16
retrieving revision 1.1.1.16.16.1
diff -u -r1.1.1.16 -r1.1.1.16.16.1
--- linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ip6_output.c	26 May 2003 08:04:10 -0000	1.1.1.16
+++ linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ip6_output.c	6 Jun 2003 15:43:34 -0000	1.1.1.16.16.1
@@ -887,7 +887,7 @@
 #endif
 }
 
-int ip6_found_nexthdr(struct sk_buff *skb, u8 **nexthdr)
+int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 {
 	u16 offset = sizeof(struct ipv6hdr);
 	struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.ipv6h + 1);
@@ -929,7 +929,7 @@
 	u8 *prevhdr, nexthdr = 0;
 
 	dev = rt->u.dst.dev;
-	hlen = ip6_found_nexthdr(skb, &prevhdr);
+	hlen = ip6_find_1stfragopt(skb, &prevhdr);
 	nexthdr = *prevhdr;
 
 	mtu = dst_pmtu(&rt->u.dst) - hlen - sizeof(struct frag_hdr);
Index: linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ipcomp6.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ipcomp6.c,v
retrieving revision 1.1.1.2
retrieving revision 1.1.1.2.14.1
diff -u -r1.1.1.2 -r1.1.1.2.14.1
--- linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ipcomp6.c	21 May 2003 13:15:20 -0000	1.1.1.2
+++ linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ipcomp6.c	6 Jun 2003 15:43:34 -0000	1.1.1.2.14.1
@@ -105,7 +105,7 @@
 	iph = skb->nh.ipv6h;
 	iph->payload_len = htons(skb->len);
 	
-	ip6_found_nexthdr(skb, &prevhdr);
+	ip6_find_1stfragopt(skb, &prevhdr);
 	*prevhdr = nexthdr;
 out:
 	if (tmp_hdr)
@@ -160,7 +160,7 @@
 		skb->nh.raw = skb->data; /* == top_iph */
 		skb->h.raw = skb->nh.raw + hdr_len;
 	} else {
-		hdr_len = ip6_found_nexthdr(skb, &prevhdr);
+		hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
 		nexthdr = *prevhdr;
 	}
 
@@ -203,7 +203,7 @@
 
 	top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
 	skb->nh.raw = skb->data; /* top_iph */
-	ip6_found_nexthdr(skb, &prevhdr); 
+	ip6_find_1stfragopt(skb, &prevhdr); 
 	*prevhdr = IPPROTO_COMP;
 
 	ipch = (struct ipv6_comp_hdr *)((unsigned char *)top_iph + hdr_len);
Index: linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ipv6_syms.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux25/net/ipv6/ipv6_syms.c,v
retrieving revision 1.1.1.12
retrieving revision 1.1.1.12.16.4
diff -u -r1.1.1.12 -r1.1.1.12.16.4
--- linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ipv6_syms.c	26 May 2003 08:04:11 -0000	1.1.1.12
+++ linux25-b2_5_70+CS1_1314_IPSEC6_CLEANUP/net/ipv6/ipv6_syms.c	6 Jun 2003 17:38:20 -0000	1.1.1.12.16.4
@@ -35,6 +35,6 @@
 EXPORT_SYMBOL(in6addr_any);
 EXPORT_SYMBOL(in6addr_loopback);
 EXPORT_SYMBOL(in6_dev_finish_destroy);
-EXPORT_SYMBOL(ip6_found_nexthdr);
+EXPORT_SYMBOL(ip6_find_1stfragopt);
 EXPORT_SYMBOL(xfrm6_rcv);
 EXPORT_SYMBOL(xfrm6_clear_mutable_options);