diff options
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ah6.c | 26 |
1 files changed, 21 insertions, 5 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 81e496a2e008..89298124c136 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c | |||
@@ -346,6 +346,10 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
346 | struct ip_auth_hdr *ah; | 346 | struct ip_auth_hdr *ah; |
347 | struct ah_data *ahp; | 347 | struct ah_data *ahp; |
348 | struct tmp_ext *iph_ext; | 348 | struct tmp_ext *iph_ext; |
349 | int seqhi_len = 0; | ||
350 | __be32 *seqhi; | ||
351 | int sglists = 0; | ||
352 | struct scatterlist *seqhisg; | ||
349 | 353 | ||
350 | ahp = x->data; | 354 | ahp = x->data; |
351 | ahash = ahp->ahash; | 355 | ahash = ahp->ahash; |
@@ -359,15 +363,22 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb) | |||
359 | if (extlen) | 363 | if (extlen) |
360 | extlen += sizeof(*iph_ext); | 364 | extlen += sizeof(*iph_ext); |
361 | 365 | ||
366 | if (x->props.flags & XFRM_STATE_ESN) { | ||
367 | sglists = 1; | ||
368 | seqhi_len = sizeof(*seqhi); | ||
369 | } | ||
362 | err = -ENOMEM; | 370 | err = -ENOMEM; |
363 | iph_base = ah_alloc_tmp(ahash, nfrags, IPV6HDR_BASELEN + extlen); | 371 | iph_base = ah_alloc_tmp(ahash, nfrags + sglists, IPV6HDR_BASELEN + |
372 | extlen + seqhi_len); | ||
364 | if (!iph_base) | 373 | if (!iph_base) |
365 | goto out; | 374 | goto out; |
366 | 375 | ||
367 | iph_ext = ah_tmp_ext(iph_base); | 376 | iph_ext = ah_tmp_ext(iph_base); |
368 | icv = ah_tmp_icv(ahash, iph_ext, extlen); | 377 | seqhi = (__be32 *)((char *)iph_ext + extlen); |
378 | icv = ah_tmp_icv(ahash, seqhi, seqhi_len); | ||
369 | req = ah_tmp_req(ahash, icv); | 379 | req = ah_tmp_req(ahash, icv); |
370 | sg = ah_req_sg(ahash, req); | 380 | sg = ah_req_sg(ahash, req); |
381 | seqhisg = sg + nfrags; | ||
371 | 382 | ||
372 | ah = ip_auth_hdr(skb); | 383 | ah = ip_auth_hdr(skb); |
373 | memset(ah->auth_data, 0, ahp->icv_trunc_len); | 384 | 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) | |||
411 | ah->spi = x->id.spi; | 422 | ah->spi = x->id.spi; |
412 | ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); | 423 | ah->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low); |
413 | 424 | ||
414 | sg_init_table(sg, nfrags); | 425 | sg_init_table(sg, nfrags + sglists); |
415 | skb_to_sgvec(skb, sg, 0, skb->len); | 426 | skb_to_sgvec_nomark(skb, sg, 0, skb->len); |
416 | 427 | ||
417 | ahash_request_set_crypt(req, sg, icv, skb->len); | 428 | if (x->props.flags & XFRM_STATE_ESN) { |
429 | /* Attach seqhi sg right after packet payload */ | ||
430 | *seqhi = htonl(XFRM_SKB_CB(skb)->seq.output.hi); | ||
431 | sg_set_buf(seqhisg, seqhi, seqhi_len); | ||
432 | } | ||
433 | ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len); | ||
418 | ahash_request_set_callback(req, 0, ah6_output_done, skb); | 434 | ahash_request_set_callback(req, 0, ah6_output_done, skb); |
419 | 435 | ||
420 | AH_SKB_CB(skb)->tmp = iph_base; | 436 | AH_SKB_CB(skb)->tmp = iph_base; |