]> err.no Git - linux-2.6/commitdiff
[IPSEC]: Fix BEET output
authorHerbert Xu <herbert@gondor.apana.org.au>
Wed, 26 Mar 2008 23:51:09 +0000 (16:51 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 26 Mar 2008 23:51:09 +0000 (16:51 -0700)
The IPv6 BEET output function is incorrectly including the inner
header in the payload to be protected.  This causes a crash as
the packet doesn't actually have that many bytes for a second
header.

The IPv4 BEET output on the other hand is broken when it comes
to handling an inner IPv6 header since it always assumes an
inner IPv4 header.

This patch fixes both by making sure that neither BEET output
function touches the inner header at all.  All access is now
done through the protocol-independent cb structure.  Two new
attributes are added to make this work, the IP header length
and the IPv4 option length.  They're filled in by the inner
mode's output function.

Thanks to Joakim Koskela for finding this problem.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/xfrm.h
net/ipv4/xfrm4_mode_beet.c
net/ipv4/xfrm4_state.c
net/ipv6/xfrm6_mode_beet.c
net/ipv6/xfrm6_state.c

index 4e6f9568cbe7271bed9df9085c9328cfe7547a6b..0d255ae008b6f5510474954da780af5eaf079365 100644 (file)
@@ -552,6 +552,9 @@ struct xfrm_mode_skb_cb {
        __be16 id;
        __be16 frag_off;
 
+       /* IP header length (excluding options or extension headers). */
+       u8 ihl;
+
        /* TOS for IPv4, class for IPv6. */
        u8 tos;
 
@@ -561,6 +564,9 @@ struct xfrm_mode_skb_cb {
        /* Protocol for IPv4, NH for IPv6. */
        u8 protocol;
 
+       /* Option length for IPv4, zero for IPv6. */
+       u8 optlen;
+
        /* Used by IPv6 only, zero for IPv4. */
        u8 flow_lbl[3];
 };
index b47030ba162bc97df715d11fc779f59df1db16e5..9c798abce736c5afdb78aaccb3d26f1ce08ad307 100644 (file)
@@ -39,13 +39,11 @@ static void xfrm4_beet_make_header(struct sk_buff *skb)
 static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
 {
        struct ip_beet_phdr *ph;
-       struct iphdr *iph, *top_iph;
+       struct iphdr *top_iph;
        int hdrlen, optlen;
 
-       iph = ip_hdr(skb);
-
        hdrlen = 0;
-       optlen = iph->ihl * 4 - sizeof(*iph);
+       optlen = XFRM_MODE_SKB_CB(skb)->optlen;
        if (unlikely(optlen))
                hdrlen += IPV4_BEET_PHMAXLEN - (optlen & 4);
 
@@ -53,11 +51,12 @@ static int xfrm4_beet_output(struct xfrm_state *x, struct sk_buff *skb)
                                    hdrlen);
        skb->mac_header = skb->network_header +
                          offsetof(struct iphdr, protocol);
-       skb->transport_header = skb->network_header + sizeof(*iph);
+       skb->transport_header = skb->network_header + sizeof(*top_iph);
 
        xfrm4_beet_make_header(skb);
 
-       ph = (struct ip_beet_phdr *)__skb_pull(skb, sizeof(*iph) - hdrlen);
+       ph = (struct ip_beet_phdr *)
+               __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl - hdrlen);
 
        top_iph = ip_hdr(skb);
 
index fdeebe68a379781853e4a0ea6f4197195af8cb89..07735ed280d720f32ac7269c5099c1e840d866d7 100644 (file)
@@ -52,10 +52,12 @@ int xfrm4_extract_header(struct sk_buff *skb)
 {
        struct iphdr *iph = ip_hdr(skb);
 
+       XFRM_MODE_SKB_CB(skb)->ihl = sizeof(*iph);
        XFRM_MODE_SKB_CB(skb)->id = iph->id;
        XFRM_MODE_SKB_CB(skb)->frag_off = iph->frag_off;
        XFRM_MODE_SKB_CB(skb)->tos = iph->tos;
        XFRM_MODE_SKB_CB(skb)->ttl = iph->ttl;
+       XFRM_MODE_SKB_CB(skb)->optlen = iph->ihl * 4 - sizeof(*iph);
        memset(XFRM_MODE_SKB_CB(skb)->flow_lbl, 0,
               sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));
 
index 0527d11c1ae37a304964cd5c85baf4d99e3b6fbe..d6ce400f585f69675a17bd96ae4036492dcf1efb 100644 (file)
@@ -45,6 +45,7 @@ static int xfrm6_beet_output(struct xfrm_state *x, struct sk_buff *skb)
        skb->mac_header = skb->network_header +
                          offsetof(struct ipv6hdr, nexthdr);
        skb->transport_header = skb->network_header + sizeof(*top_iph);
+       __skb_pull(skb, XFRM_MODE_SKB_CB(skb)->ihl);
 
        xfrm6_beet_make_header(skb);
 
index dc817e035e2338cb60fa78ec2e0cf9319eef986d..ff1e1db8e236009910ed6d9258eef5f961b8fb08 100644 (file)
@@ -174,10 +174,12 @@ int xfrm6_extract_header(struct sk_buff *skb)
 {
        struct ipv6hdr *iph = ipv6_hdr(skb);
 
+       XFRM_MODE_SKB_CB(skb)->ihl = sizeof(*iph);
        XFRM_MODE_SKB_CB(skb)->id = 0;
        XFRM_MODE_SKB_CB(skb)->frag_off = htons(IP_DF);
        XFRM_MODE_SKB_CB(skb)->tos = ipv6_get_dsfield(iph);
        XFRM_MODE_SKB_CB(skb)->ttl = iph->hop_limit;
+       XFRM_MODE_SKB_CB(skb)->optlen = 0;
        memcpy(XFRM_MODE_SKB_CB(skb)->flow_lbl, iph->flow_lbl,
               sizeof(XFRM_MODE_SKB_CB(skb)->flow_lbl));