1267 lines · 28.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C)2002 USAGI/WIDE Project * * Authors * *	Mitsuru KANDA @USAGI       : IPv6 Support *	Kazunori MIYAZAWA @USAGI   : *	Kunihiro Ishiguro <kunihiro@ipinfusion.com> * *	This file is derived from net/ipv4/esp.c */ #define pr_fmt(fmt) "IPv6: " fmt #include <crypto/aead.h>#include <crypto/authenc.h>#include <linux/err.h>#include <linux/module.h>#include <net/ip.h>#include <net/xfrm.h>#include <net/esp.h>#include <linux/scatterlist.h>#include <linux/kernel.h>#include <linux/pfkeyv2.h>#include <linux/random.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <net/ip6_checksum.h>#include <net/ip6_route.h>#include <net/icmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/udp.h>#include <linux/icmpv6.h>#include <net/tcp.h>#include <net/espintcp.h>#include <net/inet6_hashtables.h>#include <linux/skbuff_ref.h> #include <linux/highmem.h> struct esp_skb_cb {	struct xfrm_skb_cb xfrm;	void *tmp;}; struct esp_output_extra {	__be32 seqhi;	u32 esphoff;}; #define ESP_SKB_CB(__skb) ((struct esp_skb_cb *)&((__skb)->cb[0])) /* * Allocate an AEAD request structure with extra space for SG and IV. * * For alignment considerations the upper 32 bits of the sequence number are * placed at the front, if present. Followed by the IV, the request and finally * the SG list. * * TODO: Use spare space in skb for this where possible. */static void *esp_alloc_tmp(struct crypto_aead *aead, int nfrags, int seqihlen){	unsigned int len; 	len = seqihlen; 	len += crypto_aead_ivsize(aead); 	if (len) {		len += crypto_aead_alignmask(aead) &		       ~(crypto_tfm_ctx_alignment() - 1);		len = ALIGN(len, crypto_tfm_ctx_alignment());	} 	len += sizeof(struct aead_request) + crypto_aead_reqsize(aead);	len = ALIGN(len, __alignof__(struct scatterlist)); 	len += sizeof(struct scatterlist) * nfrags; 	return kmalloc(len, GFP_ATOMIC);} static inline void *esp_tmp_extra(void *tmp){	return PTR_ALIGN(tmp, __alignof__(struct esp_output_extra));} static inline u8 *esp_tmp_iv(struct crypto_aead *aead, void *tmp, int seqhilen){	return crypto_aead_ivsize(aead) ?	       PTR_ALIGN((u8 *)tmp + seqhilen,			 crypto_aead_alignmask(aead) + 1) : tmp + seqhilen;} static inline struct aead_request *esp_tmp_req(struct crypto_aead *aead, u8 *iv){	struct aead_request *req; 	req = (void *)PTR_ALIGN(iv + crypto_aead_ivsize(aead),				crypto_tfm_ctx_alignment());	aead_request_set_tfm(req, aead);	return req;} static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead,					     struct aead_request *req){	return (void *)ALIGN((unsigned long)(req + 1) +			     crypto_aead_reqsize(aead),			     __alignof__(struct scatterlist));} static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb, bool already_unref){	struct crypto_aead *aead = x->data;	int extralen = 0;	u8 *iv;	struct aead_request *req;	struct scatterlist *sg; 	if (x->props.flags & XFRM_STATE_ESN)		extralen += sizeof(struct esp_output_extra); 	iv = esp_tmp_iv(aead, tmp, extralen);	req = esp_tmp_req(aead, iv); 	/* Unref skb_frag_pages in the src scatterlist if necessary.	 * Skip the first sg which comes from skb->data.	 */	if (already_unref || req->src != req->dst) {		struct scatterlist *src = already_unref ? esp_req_sg(aead, req) : req->src; 		for (sg = sg_next(src); sg; sg = sg_next(sg))			skb_page_unref(page_to_netmem(sg_page(sg)),				       skb->pp_recycle);	}} #ifdef CONFIG_INET6_ESPINTCPstatic struct sock *esp6_find_tcp_sk(struct xfrm_state *x){	struct xfrm_encap_tmpl *encap = x->encap;	struct net *net = xs_net(x);	__be16 sport, dport;	struct sock *sk; 	spin_lock_bh(&x->lock);	sport = encap->encap_sport;	dport = encap->encap_dport;	spin_unlock_bh(&x->lock); 	sk = __inet6_lookup_established(net, &x->id.daddr.in6, dport,					&x->props.saddr.in6, ntohs(sport), 0, 0);	if (!sk)		return ERR_PTR(-ENOENT); 	if (!tcp_is_ulp_esp(sk)) {		sock_put(sk);		return ERR_PTR(-EINVAL);	} 	return sk;} static int esp_output_tcp_finish(struct xfrm_state *x, struct sk_buff *skb){	struct sock *sk;	int err; 	rcu_read_lock(); 	sk = esp6_find_tcp_sk(x);	err = PTR_ERR_OR_ZERO(sk);	if (err) {		kfree_skb(skb);		goto out;	} 	bh_lock_sock(sk);	if (sock_owned_by_user(sk))		err = espintcp_queue_out(sk, skb);	else		err = espintcp_push_skb(sk, skb);	bh_unlock_sock(sk); 	sock_put(sk); out:	rcu_read_unlock();	return err;} static int esp_output_tcp_encap_cb(struct net *net, struct sock *sk,				   struct sk_buff *skb){	struct dst_entry *dst = skb_dst(skb);	struct xfrm_state *x = dst->xfrm; 	return esp_output_tcp_finish(x, skb);} static int esp_output_tail_tcp(struct xfrm_state *x, struct sk_buff *skb){	int err; 	local_bh_disable();	err = xfrm_trans_queue_net(xs_net(x), skb, esp_output_tcp_encap_cb);	local_bh_enable(); 	/* EINPROGRESS just happens to do the right thing.  It	 * actually means that the skb has been consumed and	 * isn't coming back.	 */	return err ?: -EINPROGRESS;}#elsestatic int esp_output_tail_tcp(struct xfrm_state *x, struct sk_buff *skb){	WARN_ON(1);	return -EOPNOTSUPP;}#endif static void esp_output_encap_csum(struct sk_buff *skb){	/* UDP encap with IPv6 requires a valid checksum */	if (*skb_mac_header(skb) == IPPROTO_UDP) {		struct udphdr *uh = udp_hdr(skb);		struct ipv6hdr *ip6h = ipv6_hdr(skb);		/* esp6_output_udp_encap limits len to U16_MAX. */		int len = udp_get_len_short(uh);		unsigned int offset = skb_transport_offset(skb);		__wsum csum = skb_checksum(skb, offset, skb->len - offset, 0); 		uh->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,					    len, IPPROTO_UDP, csum);		if (uh->check == 0)			uh->check = CSUM_MANGLED_0;	}} static void esp_output_done(void *data, int err){	struct sk_buff *skb = data;	struct xfrm_offload *xo = xfrm_offload(skb);	void *tmp;	struct xfrm_state *x; 	if (xo && (xo->flags & XFRM_DEV_RESUME)) {		struct sec_path *sp = skb_sec_path(skb); 		x = sp->xvec[sp->len - 1];	} else {		x = skb_dst(skb)->xfrm;	} 	tmp = ESP_SKB_CB(skb)->tmp;	esp_ssg_unref(x, tmp, skb, false);	kfree(tmp); 	esp_output_encap_csum(skb); 	if (xo && (xo->flags & XFRM_DEV_RESUME)) {		if (err) {			XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR);			kfree_skb(skb);			return;		} 		skb_push(skb, skb->data - skb_mac_header(skb));		secpath_reset(skb);		xfrm_dev_resume(skb);	} else {		if (!err &&		    x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) {			err = esp_output_tail_tcp(x, skb);			if (err != -EINPROGRESS)				kfree_skb(skb);		} else {			xfrm_output_resume(skb_to_full_sk(skb), skb, err);		}	}} /* Move ESP header back into place. */static void esp_restore_header(struct sk_buff *skb, unsigned int offset){	struct ip_esp_hdr *esph = (void *)(skb->data + offset);	void *tmp = ESP_SKB_CB(skb)->tmp;	__be32 *seqhi = esp_tmp_extra(tmp); 	esph->seq_no = esph->spi;	esph->spi = *seqhi;} static void esp_output_restore_header(struct sk_buff *skb){	void *tmp = ESP_SKB_CB(skb)->tmp;	struct esp_output_extra *extra = esp_tmp_extra(tmp); 	esp_restore_header(skb, skb_transport_offset(skb) + extra->esphoff -				sizeof(__be32));} static struct ip_esp_hdr *esp_output_set_esn(struct sk_buff *skb,					     struct xfrm_state *x,					     struct ip_esp_hdr *esph,					     struct esp_output_extra *extra){	/* For ESN we move the header forward by 4 bytes to	 * accommodate the high bits.  We will move it back after	 * encryption.	 */	if ((x->props.flags & XFRM_STATE_ESN)) {		__u32 seqhi;		struct xfrm_offload *xo = xfrm_offload(skb); 		if (xo)			seqhi = xo->seq.hi;		else			seqhi = XFRM_SKB_CB(skb)->seq.output.hi; 		extra->esphoff = (unsigned char *)esph -				 skb_transport_header(skb);		esph = (struct ip_esp_hdr *)((unsigned char *)esph - 4);		extra->seqhi = esph->spi;		esph->seq_no = htonl(seqhi);	} 	esph->spi = x->id.spi; 	return esph;} static void esp_output_done_esn(void *data, int err){	struct sk_buff *skb = data; 	esp_output_restore_header(skb);	esp_output_done(data, err);} static struct ip_esp_hdr *esp6_output_udp_encap(struct sk_buff *skb,					       int encap_type,					       struct esp_info *esp,					       __be16 sport,					       __be16 dport){	struct udphdr *uh;	unsigned int len; 	len = skb->len + esp->tailen - skb_transport_offset(skb);	if (len > U16_MAX)		return ERR_PTR(-EMSGSIZE); 	uh = (struct udphdr *)esp->esph;	uh->source = sport;	uh->dest = dport;	udp_set_len_short(uh, len);	uh->check = 0; 	*skb_mac_header(skb) = IPPROTO_UDP; 	return (struct ip_esp_hdr *)(uh + 1);} #ifdef CONFIG_INET6_ESPINTCPstatic struct ip_esp_hdr *esp6_output_tcp_encap(struct xfrm_state *x,						struct sk_buff *skb,						struct esp_info *esp){	__be16 *lenp = (void *)esp->esph;	struct ip_esp_hdr *esph;	unsigned int len;	struct sock *sk; 	len = skb->len + esp->tailen - skb_transport_offset(skb);	if (len > IP_MAX_MTU)		return ERR_PTR(-EMSGSIZE); 	rcu_read_lock();	sk = esp6_find_tcp_sk(x);	rcu_read_unlock(); 	if (IS_ERR(sk))		return ERR_CAST(sk); 	sock_put(sk); 	*lenp = htons(len);	esph = (struct ip_esp_hdr *)(lenp + 1); 	return esph;}#elsestatic struct ip_esp_hdr *esp6_output_tcp_encap(struct xfrm_state *x,						struct sk_buff *skb,						struct esp_info *esp){	return ERR_PTR(-EOPNOTSUPP);}#endif static int esp6_output_encap(struct xfrm_state *x, struct sk_buff *skb,			    struct esp_info *esp){	struct xfrm_encap_tmpl *encap = x->encap;	struct ip_esp_hdr *esph;	__be16 sport, dport;	int encap_type; 	spin_lock_bh(&x->lock);	sport = encap->encap_sport;	dport = encap->encap_dport;	encap_type = encap->encap_type;	spin_unlock_bh(&x->lock); 	switch (encap_type) {	default:	case UDP_ENCAP_ESPINUDP:		esph = esp6_output_udp_encap(skb, encap_type, esp, sport, dport);		break;	case TCP_ENCAP_ESPINTCP:		esph = esp6_output_tcp_encap(x, skb, esp);		break;	} 	if (IS_ERR(esph))		return PTR_ERR(esph); 	esp->esph = esph; 	return 0;} int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp){	u8 *tail;	int nfrags;	int esph_offset;	struct page *page;	struct sk_buff *trailer;	int tailen = esp->tailen; 	if (x->encap) {		int err = esp6_output_encap(x, skb, esp); 		if (err < 0)			return err;	} 	if (ALIGN(skb->data_len + tailen, L1_CACHE_BYTES) >	    PAGE_SIZE)		goto cow; 	if (!skb_cloned(skb)) {		if (tailen <= skb_tailroom(skb)) {			nfrags = 1;			trailer = skb;			tail = skb_tail_pointer(trailer); 			goto skip_cow;		} else if ((skb_shinfo(skb)->nr_frags < MAX_SKB_FRAGS)			   && !skb_has_frag_list(skb)) {			int allocsize;			struct sock *sk = skb->sk;			struct page_frag *pfrag = &x->xfrag; 			esp->inplace = false; 			allocsize = ALIGN(tailen, L1_CACHE_BYTES); 			spin_lock_bh(&x->lock); 			if (unlikely(!skb_page_frag_refill(allocsize, pfrag, GFP_ATOMIC))) {				spin_unlock_bh(&x->lock);				goto cow;			} 			page = pfrag->page;			get_page(page); 			tail = page_address(page) + pfrag->offset; 			esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto); 			nfrags = skb_shinfo(skb)->nr_frags; 			__skb_fill_page_desc(skb, nfrags, page, pfrag->offset,					     tailen);			skb_shinfo(skb)->nr_frags = ++nfrags; 			pfrag->offset = pfrag->offset + allocsize; 			spin_unlock_bh(&x->lock); 			nfrags++;

Lines 1–500 of 1267.