{IPv4,xfrm} Add ESN support for AH ingress part
[linux-drm-fsl-dcu.git] / net / ipv4 / ah4.c
index c6accacc3a2be404533032d27eefcf099426894d..54b965ddcb1973ec368dcf52c61eb229d0f35a26 100644 (file)
@@ -309,6 +309,10 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
        struct ip_auth_hdr *ah;
        struct ah_data *ahp;
        int err = -ENOMEM;
+       int seqhi_len = 0;
+       __be32 *seqhi;
+       int sglists = 0;
+       struct scatterlist *seqhisg;
 
        if (!pskb_may_pull(skb, sizeof(*ah)))
                goto out;
@@ -349,14 +353,22 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
        iph = ip_hdr(skb);
        ihl = ip_hdrlen(skb);
 
-       work_iph = ah_alloc_tmp(ahash, nfrags, ihl + ahp->icv_trunc_len);
+       if (x->props.flags & XFRM_STATE_ESN) {
+               sglists = 1;
+               seqhi_len = sizeof(*seqhi);
+       }
+
+       work_iph = ah_alloc_tmp(ahash, nfrags + sglists, ihl +
+                               ahp->icv_trunc_len + seqhi_len);
        if (!work_iph)
                goto out;
 
-       auth_data = ah_tmp_auth(work_iph, ihl);
+       seqhi = (__be32 *)((char *)work_iph + ihl);
+       auth_data = ah_tmp_auth(seqhi, seqhi_len);
        icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len);
        req = ah_tmp_req(ahash, icv);
        sg = ah_req_sg(ahash, req);
+       seqhisg = sg + nfrags;
 
        memcpy(work_iph, iph, ihl);
        memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
@@ -375,10 +387,15 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
 
        skb_push(skb, ihl);
 
-       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 = XFRM_SKB_CB(skb)->seq.input.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, ah_input_done, skb);
 
        AH_SKB_CB(skb)->tmp = work_iph;