1218 lines · 27.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only#define pr_fmt(fmt) "IPsec: " 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/rtnetlink.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/in6.h>#include <net/icmp.h>#include <net/protocol.h>#include <net/udp.h>#include <net/tcp.h>#include <net/espintcp.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 IV is placed at the front, followed * by 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 extralen){	unsigned int len; 	len = extralen; 	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 extralen){	return crypto_aead_ivsize(aead) ?	       PTR_ALIGN((u8 *)tmp + extralen,			 crypto_aead_alignmask(aead) + 1) : tmp + extralen;} 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_INET_ESPINTCPstatic struct sock *esp_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 = inet_lookup_established(net, x->id.daddr.a4, dport,				     x->props.saddr.a4, sport, 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 = esp_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_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); 	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_extra(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 *esp_output_udp_encap(struct sk_buff *skb,					       int encap_type,					       struct esp_info *esp,					       __be16 sport,					       __be16 dport){	struct udphdr *uh;	unsigned int len;	struct xfrm_offload *xo = xfrm_offload(skb); 	len = skb->len + esp->tailen - skb_transport_offset(skb);	if (len + sizeof(struct iphdr) > IP_MAX_MTU)		return ERR_PTR(-EMSGSIZE); 	uh = (struct udphdr *)esp->esph;	uh->source = sport;	uh->dest = dport;	udp_set_len_short(uh, len);	uh->check = 0; 	/* For IPv4 ESP with UDP encapsulation, if xo is not null, the skb is in the crypto offload	 * data path, which means that esp_output_udp_encap is called outside of the XFRM stack.	 * In this case, the mac header doesn't point to the IPv4 protocol field, so don't set it.	 */	if (!xo || encap_type != UDP_ENCAP_ESPINUDP)		*skb_mac_header(skb) = IPPROTO_UDP; 	return (struct ip_esp_hdr *)(uh + 1);} #ifdef CONFIG_INET_ESPINTCPstatic struct ip_esp_hdr *esp_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 = esp_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 *esp_output_tcp_encap(struct xfrm_state *x,						    struct sk_buff *skb,						    struct esp_info *esp){	return ERR_PTR(-EOPNOTSUPP);}#endif static int esp_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 = esp_output_udp_encap(skb, encap_type, esp, sport, dport);		break;	case TCP_ENCAP_ESPINTCP:		esph = esp_output_tcp_encap(x, skb, esp);		break;	} 	if (IS_ERR(esph))		return PTR_ERR(esph); 	esp->esph = esph; 	return 0;} int esp_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; 	/* this is non-NULL only with TCP/UDP Encapsulation */	if (x->encap) {		int err = esp_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++; 			skb_len_add(skb, tailen);			if (sk && sk_fullsock(sk))				refcount_add(tailen, &sk->sk_wmem_alloc); 			goto out;		}	} cow:	esph_offset = (unsigned char *)esp->esph - skb_transport_header(skb); 	nfrags = skb_cow_data(skb, tailen, &trailer);	if (nfrags < 0)		goto out;	tail = skb_tail_pointer(trailer);	esp->esph = (struct ip_esp_hdr *)(skb_transport_header(skb) + esph_offset); skip_cow:	esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto);	pskb_put(skb, trailer, tailen); out:	return nfrags;}EXPORT_SYMBOL_GPL(esp_output_head); int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp){	u8 *iv;

Lines 1–500 of 1218.