diff options
Diffstat (limited to 'net/ipv6/ah6.c')
| -rw-r--r-- | net/ipv6/ah6.c | 86 | 
1 files changed, 63 insertions, 23 deletions
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 73784c3d464..72a4930bdc0 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -12,8 +12,7 @@   * GNU General Public License for more details.   *   * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * along with this program; if not, see <http://www.gnu.org/licenses/>.   *   * Authors   * @@ -347,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; @@ -360,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); @@ -412,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; @@ -515,6 +530,10 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)  	int nexthdr;  	int nfrags;  	int err = -ENOMEM; +	int seqhi_len = 0; +	__be32 *seqhi; +	int sglists = 0; +	struct scatterlist *seqhisg;  	if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))  		goto out; @@ -551,14 +570,22 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)  	skb_push(skb, hdr_len); -	work_iph = ah_alloc_tmp(ahash, nfrags, hdr_len + 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, hdr_len + +				ahp->icv_trunc_len + seqhi_len);  	if (!work_iph)  		goto out; -	auth_data = ah_tmp_auth(work_iph, hdr_len); -	icv = ah_tmp_icv(ahash, auth_data, ahp->icv_trunc_len); +	auth_data = ah_tmp_auth((u8 *)work_iph, hdr_len); +	seqhi = (__be32 *)(auth_data + ahp->icv_trunc_len); +	icv = ah_tmp_icv(ahash, seqhi, seqhi_len);  	req = ah_tmp_req(ahash, icv);  	sg = ah_req_sg(ahash, req); +	seqhisg = sg + nfrags;  	memcpy(work_iph, ip6h, hdr_len);  	memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); @@ -573,10 +600,16 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)  	ip6h->flow_lbl[2] = 0;  	ip6h->hop_limit   = 0; -	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); + +	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); +	ahash_request_set_crypt(req, sg, icv, skb->len + seqhi_len);  	ahash_request_set_callback(req, 0, ah6_input_done, skb);  	AH_SKB_CB(skb)->tmp = work_iph; @@ -610,28 +643,29 @@ out:  	return err;  } -static void ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, -		    u8 type, u8 code, int offset, __be32 info) +static int ah6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, +		   u8 type, u8 code, int offset, __be32 info)  {  	struct net *net = dev_net(skb->dev);  	struct ipv6hdr *iph = (struct ipv6hdr*)skb->data;  	struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+offset);  	struct xfrm_state *x; -	if (type != ICMPV6_DEST_UNREACH && -	    type != ICMPV6_PKT_TOOBIG && +	if (type != ICMPV6_PKT_TOOBIG &&  	    type != NDISC_REDIRECT) -		return; +		return 0;  	x = xfrm_state_lookup(net, skb->mark, (xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET6);  	if (!x) -		return; +		return 0;  	if (type == NDISC_REDIRECT)  		ip6_redirect(skb, net, skb->dev->ifindex, 0);  	else  		ip6_update_pmtu(skb, net, info, 0, 0);  	xfrm_state_put(x); + +	return 0;  }  static int ah6_init_state(struct xfrm_state *x) @@ -716,6 +750,11 @@ static void ah6_destroy(struct xfrm_state *x)  	kfree(ahp);  } +static int ah6_rcv_cb(struct sk_buff *skb, int err) +{ +	return 0; +} +  static const struct xfrm_type ah6_type =  {  	.description	= "AH6", @@ -729,10 +768,11 @@ static const struct xfrm_type ah6_type =  	.hdr_offset	= xfrm6_find_1stfragopt,  }; -static const struct inet6_protocol ah6_protocol = { +static struct xfrm6_protocol ah6_protocol = {  	.handler	=	xfrm6_rcv, +	.cb_handler	=	ah6_rcv_cb,  	.err_handler	=	ah6_err, -	.flags		=	INET6_PROTO_NOPOLICY, +	.priority	=	0,  };  static int __init ah6_init(void) @@ -742,7 +782,7 @@ static int __init ah6_init(void)  		return -EAGAIN;  	} -	if (inet6_add_protocol(&ah6_protocol, IPPROTO_AH) < 0) { +	if (xfrm6_protocol_register(&ah6_protocol, IPPROTO_AH) < 0) {  		pr_info("%s: can't add protocol\n", __func__);  		xfrm_unregister_type(&ah6_type, AF_INET6);  		return -EAGAIN; @@ -753,7 +793,7 @@ static int __init ah6_init(void)  static void __exit ah6_fini(void)  { -	if (inet6_del_protocol(&ah6_protocol, IPPROTO_AH) < 0) +	if (xfrm6_protocol_deregister(&ah6_protocol, IPPROTO_AH) < 0)  		pr_info("%s: can't remove protocol\n", __func__);  	if (xfrm_unregister_type(&ah6_type, AF_INET6) < 0)  | 
