forked from mirrors/linux
		
	esp: Add gso handlers for esp4 and esp6
This patch extends the xfrm_type by an encap function pointer and implements esp4_gso_encap and esp6_gso_encap. These functions doing the basic esp encapsulation for a GSO packet. In case the GSO packet needs to be segmented in software, we add gso_segment functions. This codepath is going to be used on esp hardware offloads. Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
This commit is contained in:
		
							parent
							
								
									383d0350f2
								
							
						
					
					
						commit
						7862b4058b
					
				
					 5 changed files with 203 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -161,11 +161,19 @@ static struct ip_esp_hdr *esp_output_set_extra(struct sk_buff *skb,
 | 
			
		|||
	 * 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(XFRM_SKB_CB(skb)->seq.output.hi);
 | 
			
		||||
		esph->seq_no = htonl(seqhi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	esph->spi = x->id.spi;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,97 @@ static struct sk_buff **esp4_gro_receive(struct sk_buff **head,
 | 
			
		|||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void esp4_gso_encap(struct xfrm_state *x, struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct ip_esp_hdr *esph;
 | 
			
		||||
	struct iphdr *iph = ip_hdr(skb);
 | 
			
		||||
	struct xfrm_offload *xo = xfrm_offload(skb);
 | 
			
		||||
	int proto = iph->protocol;
 | 
			
		||||
 | 
			
		||||
	skb_push(skb, -skb_network_offset(skb));
 | 
			
		||||
	esph = ip_esp_hdr(skb);
 | 
			
		||||
	*skb_mac_header(skb) = IPPROTO_ESP;
 | 
			
		||||
 | 
			
		||||
	esph->spi = x->id.spi;
 | 
			
		||||
	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
 | 
			
		||||
 | 
			
		||||
	xo->proto = proto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct sk_buff *esp4_gso_segment(struct sk_buff *skb,
 | 
			
		||||
				        netdev_features_t features)
 | 
			
		||||
{
 | 
			
		||||
	__u32 seq;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	struct sk_buff *skb2;
 | 
			
		||||
	struct xfrm_state *x;
 | 
			
		||||
	struct ip_esp_hdr *esph;
 | 
			
		||||
	struct crypto_aead *aead;
 | 
			
		||||
	struct sk_buff *segs = ERR_PTR(-EINVAL);
 | 
			
		||||
	netdev_features_t esp_features = features;
 | 
			
		||||
	struct xfrm_offload *xo = xfrm_offload(skb);
 | 
			
		||||
 | 
			
		||||
	if (!xo)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	seq = xo->seq.low;
 | 
			
		||||
 | 
			
		||||
	x = skb->sp->xvec[skb->sp->len - 1];
 | 
			
		||||
	aead = x->data;
 | 
			
		||||
	esph = ip_esp_hdr(skb);
 | 
			
		||||
 | 
			
		||||
	if (esph->spi != x->id.spi)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	__skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead));
 | 
			
		||||
 | 
			
		||||
	skb->encap_hdr_csum = 1;
 | 
			
		||||
 | 
			
		||||
	if (!(features & NETIF_F_HW_ESP))
 | 
			
		||||
		esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK);
 | 
			
		||||
 | 
			
		||||
	segs = x->outer_mode->gso_segment(x, skb, esp_features);
 | 
			
		||||
	if (IS_ERR_OR_NULL(segs))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	__skb_pull(skb, skb->data - skb_mac_header(skb));
 | 
			
		||||
 | 
			
		||||
	skb2 = segs;
 | 
			
		||||
	do {
 | 
			
		||||
		struct sk_buff *nskb = skb2->next;
 | 
			
		||||
 | 
			
		||||
		xo = xfrm_offload(skb2);
 | 
			
		||||
		xo->flags |= XFRM_GSO_SEGMENT;
 | 
			
		||||
		xo->seq.low = seq;
 | 
			
		||||
		xo->seq.hi = xfrm_replay_seqhi(x, seq);
 | 
			
		||||
 | 
			
		||||
		if(!(features & NETIF_F_HW_ESP))
 | 
			
		||||
			xo->flags |= CRYPTO_FALLBACK;
 | 
			
		||||
 | 
			
		||||
		x->outer_mode->xmit(x, skb2);
 | 
			
		||||
 | 
			
		||||
		err = x->type_offload->xmit(x, skb2, esp_features);
 | 
			
		||||
		if (err) {
 | 
			
		||||
			kfree_skb_list(segs);
 | 
			
		||||
			return ERR_PTR(err);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!skb_is_gso(skb2))
 | 
			
		||||
			seq++;
 | 
			
		||||
		else
 | 
			
		||||
			seq += skb_shinfo(skb2)->gso_segs;
 | 
			
		||||
 | 
			
		||||
		skb_push(skb2, skb2->mac_len);
 | 
			
		||||
		skb2 = nskb;
 | 
			
		||||
	} while (skb2);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	return segs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct crypto_aead *aead = x->data;
 | 
			
		||||
| 
						 | 
				
			
			@ -173,6 +264,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_
 | 
			
		|||
static const struct net_offload esp4_offload = {
 | 
			
		||||
	.callbacks = {
 | 
			
		||||
		.gro_receive = esp4_gro_receive,
 | 
			
		||||
		.gso_segment = esp4_gso_segment,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +274,7 @@ static const struct xfrm_type_offload esp_type_offload = {
 | 
			
		|||
	.proto	     	= IPPROTO_ESP,
 | 
			
		||||
	.input_tail	= esp_input_tail,
 | 
			
		||||
	.xmit		= esp_xmit,
 | 
			
		||||
	.encap		= esp4_gso_encap,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init esp4_offload_init(void)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -179,9 +179,14 @@ static struct ip_esp_hdr *esp_output_set_esn(struct sk_buff *skb,
 | 
			
		|||
	 * encryption.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((x->props.flags & XFRM_STATE_ESN)) {
 | 
			
		||||
		struct xfrm_offload *xo = xfrm_offload(skb);
 | 
			
		||||
 | 
			
		||||
		esph = (void *)(skb_transport_header(skb) - sizeof(__be32));
 | 
			
		||||
		*seqhi = esph->spi;
 | 
			
		||||
		esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
 | 
			
		||||
		if (xo)
 | 
			
		||||
			esph->seq_no = htonl(xo->seq.hi);
 | 
			
		||||
		else
 | 
			
		||||
			esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.hi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	esph->spi = x->id.spi;
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +228,6 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
 | 
			
		|||
	struct sk_buff *trailer;
 | 
			
		||||
	int tailen = esp->tailen;
 | 
			
		||||
 | 
			
		||||
	*skb_mac_header(skb) = IPPROTO_ESP;
 | 
			
		||||
	esph = ip_esp_hdr(skb);
 | 
			
		||||
 | 
			
		||||
	if (!skb_cloned(skb)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -86,6 +86,97 @@ static struct sk_buff **esp6_gro_receive(struct sk_buff **head,
 | 
			
		|||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void esp6_gso_encap(struct xfrm_state *x, struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct ip_esp_hdr *esph;
 | 
			
		||||
	struct ipv6hdr *iph = ipv6_hdr(skb);
 | 
			
		||||
	struct xfrm_offload *xo = xfrm_offload(skb);
 | 
			
		||||
	int proto = iph->nexthdr;
 | 
			
		||||
 | 
			
		||||
	skb_push(skb, -skb_network_offset(skb));
 | 
			
		||||
	esph = ip_esp_hdr(skb);
 | 
			
		||||
	*skb_mac_header(skb) = IPPROTO_ESP;
 | 
			
		||||
 | 
			
		||||
	esph->spi = x->id.spi;
 | 
			
		||||
	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
 | 
			
		||||
 | 
			
		||||
	xo->proto = proto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct sk_buff *esp6_gso_segment(struct sk_buff *skb,
 | 
			
		||||
				        netdev_features_t features)
 | 
			
		||||
{
 | 
			
		||||
	__u32 seq;
 | 
			
		||||
	int err = 0;
 | 
			
		||||
	struct sk_buff *skb2;
 | 
			
		||||
	struct xfrm_state *x;
 | 
			
		||||
	struct ip_esp_hdr *esph;
 | 
			
		||||
	struct crypto_aead *aead;
 | 
			
		||||
	struct sk_buff *segs = ERR_PTR(-EINVAL);
 | 
			
		||||
	netdev_features_t esp_features = features;
 | 
			
		||||
	struct xfrm_offload *xo = xfrm_offload(skb);
 | 
			
		||||
 | 
			
		||||
	if (xo)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	seq = xo->seq.low;
 | 
			
		||||
 | 
			
		||||
	x = skb->sp->xvec[skb->sp->len - 1];
 | 
			
		||||
	aead = x->data;
 | 
			
		||||
	esph = ip_esp_hdr(skb);
 | 
			
		||||
 | 
			
		||||
	if (esph->spi != x->id.spi)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	__skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead));
 | 
			
		||||
 | 
			
		||||
	skb->encap_hdr_csum = 1;
 | 
			
		||||
 | 
			
		||||
	if (!(features & NETIF_F_HW_ESP))
 | 
			
		||||
		esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK);
 | 
			
		||||
 | 
			
		||||
	segs = x->outer_mode->gso_segment(x, skb, esp_features);
 | 
			
		||||
	if (IS_ERR_OR_NULL(segs))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	__skb_pull(skb, skb->data - skb_mac_header(skb));
 | 
			
		||||
 | 
			
		||||
	skb2 = segs;
 | 
			
		||||
	do {
 | 
			
		||||
		struct sk_buff *nskb = skb2->next;
 | 
			
		||||
 | 
			
		||||
		xo = xfrm_offload(skb2);
 | 
			
		||||
		xo->flags |= XFRM_GSO_SEGMENT;
 | 
			
		||||
		xo->seq.low = seq;
 | 
			
		||||
		xo->seq.hi = xfrm_replay_seqhi(x, seq);
 | 
			
		||||
 | 
			
		||||
		if(!(features & NETIF_F_HW_ESP))
 | 
			
		||||
			xo->flags |= CRYPTO_FALLBACK;
 | 
			
		||||
 | 
			
		||||
		x->outer_mode->xmit(x, skb2);
 | 
			
		||||
 | 
			
		||||
		err = x->type_offload->xmit(x, skb2, esp_features);
 | 
			
		||||
		if (err) {
 | 
			
		||||
			kfree_skb_list(segs);
 | 
			
		||||
			return ERR_PTR(err);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!skb_is_gso(skb2))
 | 
			
		||||
			seq++;
 | 
			
		||||
		else
 | 
			
		||||
			seq += skb_shinfo(skb2)->gso_segs;
 | 
			
		||||
 | 
			
		||||
		skb_push(skb2, skb2->mac_len);
 | 
			
		||||
		skb2 = nskb;
 | 
			
		||||
	} while (skb2);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	return segs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int esp6_input_tail(struct xfrm_state *x, struct sk_buff *skb)
 | 
			
		||||
{
 | 
			
		||||
	struct crypto_aead *aead = x->data;
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +267,7 @@ static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features
 | 
			
		|||
static const struct net_offload esp6_offload = {
 | 
			
		||||
	.callbacks = {
 | 
			
		||||
		.gro_receive = esp6_gro_receive,
 | 
			
		||||
		.gso_segment = esp6_gso_segment,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +277,7 @@ static const struct xfrm_type_offload esp6_type_offload = {
 | 
			
		|||
	.proto	     	= IPPROTO_ESP,
 | 
			
		||||
	.input_tail	= esp6_input_tail,
 | 
			
		||||
	.xmit		= esp6_xmit,
 | 
			
		||||
	.encap		= esp6_gso_encap,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init esp6_offload_init(void)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,8 @@ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq)
 | 
			
		|||
 | 
			
		||||
	return seq_hi;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORT_SYMBOL(xfrm_replay_seqhi);
 | 
			
		||||
;
 | 
			
		||||
static void xfrm_replay_notify(struct xfrm_state *x, int event)
 | 
			
		||||
{
 | 
			
		||||
	struct km_event c;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue