{IPv6,xfrm} Add ESN support for AH egress part
[linux-2.6-block.git] / net / ipv6 / ah6.c
index 81e496a2e0083c42fe94729a486647aa23c8aed0..89298124c136c5248667d9aa6364ce3ba539f067 100644 (file)
@@ -346,6 +346,10 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
        struct ip_auth_hdr *ah;
        struct ah_data *ahp;
        struct tmp_ext *iph_ext;
+       int seqhi_len = 0;
+       __be32 *seqhi;
+       int sglists = 0;
+       struct scatterlist *seqhisg;
 
        ahp = x->data;
        ahash = ahp->ahash;
@@ -359,15 +363,22 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
        if (extlen)
                extlen += sizeof(*iph_ext);
 
+       if (x->props.flags & XFRM_STATE_ESN) {
+               sglists = 1;
+               seqhi_len = sizeof(*seqhi);
+       }
        err = -ENOMEM;
-       iph_base = ah_alloc_tmp(ahash, nfrags, IPV6HDR_BASELEN + extlen);
+       iph_base = ah_alloc_tmp(ahash, nfrags + sglists, IPV6HDR_BASELEN +
+                               extlen + seqhi_len);
        if (!iph_base)
                goto out;
 
        iph_ext = ah_tmp_ext(iph_base);
-       icv = ah_tmp_icv(ahash, iph_ext, extlen);
+       seqhi = (__be32 *)((char *)iph_ext + extlen);
+       icv = ah_tmp_icv(ahash, seqhi, seqhi_len);
        req = ah_tmp_req(ahash, icv);
        sg = ah_req_sg(ahash, req);
+       seqhisg = sg + nfrags;
 
        ah = ip_auth_hdr(skb);
        memset(ah->auth_data, 0, ahp->icv_trunc_len);
@@ -411,10 +422,15 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
        ah->spi = x->id.spi;
        ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
 
-       sg_init_table(sg, nfrags);
-       skb_to_sgvec(skb, sg, 0, skb->len);
+       sg_init_table(sg, nfrags + sglists);
+       skb_to_sgvec_nomark(skb, sg, 0, skb->len);
 
-       ahash_request_set_crypt(req, sg, icv, skb->len);
+       if (x->props.flags & XFRM_STATE_ESN) {
+               /* Attach seqhi sg right after packet payload */
+               *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
+               sg_set_buf(seqhisg, seqhi, seqhi_len);
+       }
+       ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);
        ahash_request_set_callback(req, 0, ah6_output_done, skb);
 
        AH_SKB_CB(skb)->tmp = iph_base;