mirror of
https://github.com/torvalds/linux.git
synced 2024-11-30 16:11:38 +00:00
f1bd7d659e
Both esp4 and esp6 used to assume that the SKB payload is encrypted and therefore the inner_network and inner_transport offsets are not relevant. When doing crypto offload in the NIC, this is no longer the case and the NIC driver needs these offsets so it can do TX TCP checksum offloading. This patch sets the inner_network and inner_transport members of the SKB, as well as encapsulation, to reflect the actual positions of these headers, and removes them only once encryption is done on the payload. Signed-off-by: Ilan Tayari <ilant@mellanox.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
154 lines
3.8 KiB
C
154 lines
3.8 KiB
C
/*
|
|
* xfrm4_mode_tunnel.c - Tunnel mode encapsulation for IPv4.
|
|
*
|
|
* Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
|
|
*/
|
|
|
|
#include <linux/gfp.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/stringify.h>
|
|
#include <net/dst.h>
|
|
#include <net/inet_ecn.h>
|
|
#include <net/ip.h>
|
|
#include <net/xfrm.h>
|
|
|
|
static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
|
|
{
|
|
struct iphdr *inner_iph = ipip_hdr(skb);
|
|
|
|
if (INET_ECN_is_ce(XFRM_MODE_SKB_CB(skb)->tos))
|
|
IP_ECN_set_ce(inner_iph);
|
|
}
|
|
|
|
/* Add encapsulation header.
|
|
*
|
|
* The top IP header will be constructed per RFC 2401.
|
|
*/
|
|
static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
|
|
{
|
|
struct dst_entry *dst = skb_dst(skb);
|
|
struct iphdr *top_iph;
|
|
int flags;
|
|
|
|
skb_set_inner_network_header(skb, skb_network_offset(skb));
|
|
skb_set_inner_transport_header(skb, skb_transport_offset(skb));
|
|
|
|
skb_set_network_header(skb, -x->props.header_len);
|
|
skb->mac_header = skb->network_header +
|
|
offsetof(struct iphdr, protocol);
|
|
skb->transport_header = skb->network_header + sizeof(*top_iph);
|
|
top_iph = ip_hdr(skb);
|
|
|
|
top_iph->ihl = 5;
|
|
top_iph->version = 4;
|
|
|
|
top_iph->protocol = xfrm_af2proto(skb_dst(skb)->ops->family);
|
|
|
|
/* DS disclosing depends on XFRM_SA_XFLAG_DONT_ENCAP_DSCP */
|
|
if (x->props.extra_flags & XFRM_SA_XFLAG_DONT_ENCAP_DSCP)
|
|
top_iph->tos = 0;
|
|
else
|
|
top_iph->tos = XFRM_MODE_SKB_CB(skb)->tos;
|
|
top_iph->tos = INET_ECN_encapsulate(top_iph->tos,
|
|
XFRM_MODE_SKB_CB(skb)->tos);
|
|
|
|
flags = x->props.flags;
|
|
if (flags & XFRM_STATE_NOECN)
|
|
IP_ECN_clear(top_iph);
|
|
|
|
top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
|
|
0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
|
|
|
|
top_iph->ttl = ip4_dst_hoplimit(dst->child);
|
|
|
|
top_iph->saddr = x->props.saddr.a4;
|
|
top_iph->daddr = x->id.daddr.a4;
|
|
ip_select_ident(dev_net(dst->dev), skb, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
|
|
{
|
|
int err = -EINVAL;
|
|
|
|
if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP)
|
|
goto out;
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
|
goto out;
|
|
|
|
err = skb_unclone(skb, GFP_ATOMIC);
|
|
if (err)
|
|
goto out;
|
|
|
|
if (x->props.flags & XFRM_STATE_DECAP_DSCP)
|
|
ipv4_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipip_hdr(skb));
|
|
if (!(x->props.flags & XFRM_STATE_NOECN))
|
|
ipip_ecn_decapsulate(skb);
|
|
|
|
skb_reset_network_header(skb);
|
|
skb_mac_header_rebuild(skb);
|
|
|
|
err = 0;
|
|
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
static struct sk_buff *xfrm4_mode_tunnel_gso_segment(struct xfrm_state *x,
|
|
struct sk_buff *skb,
|
|
netdev_features_t features)
|
|
{
|
|
__skb_push(skb, skb->mac_len);
|
|
return skb_mac_gso_segment(skb, features);
|
|
|
|
}
|
|
|
|
static void xfrm4_mode_tunnel_xmit(struct xfrm_state *x, struct sk_buff *skb)
|
|
{
|
|
struct xfrm_offload *xo = xfrm_offload(skb);
|
|
|
|
if (xo->flags & XFRM_GSO_SEGMENT) {
|
|
skb->network_header = skb->network_header - x->props.header_len;
|
|
skb->transport_header = skb->network_header +
|
|
sizeof(struct iphdr);
|
|
}
|
|
|
|
skb_reset_mac_len(skb);
|
|
pskb_pull(skb, skb->mac_len + x->props.header_len);
|
|
}
|
|
|
|
static struct xfrm_mode xfrm4_tunnel_mode = {
|
|
.input2 = xfrm4_mode_tunnel_input,
|
|
.input = xfrm_prepare_input,
|
|
.output2 = xfrm4_mode_tunnel_output,
|
|
.output = xfrm4_prepare_output,
|
|
.gso_segment = xfrm4_mode_tunnel_gso_segment,
|
|
.xmit = xfrm4_mode_tunnel_xmit,
|
|
.owner = THIS_MODULE,
|
|
.encap = XFRM_MODE_TUNNEL,
|
|
.flags = XFRM_MODE_FLAG_TUNNEL,
|
|
};
|
|
|
|
static int __init xfrm4_mode_tunnel_init(void)
|
|
{
|
|
return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET);
|
|
}
|
|
|
|
static void __exit xfrm4_mode_tunnel_exit(void)
|
|
{
|
|
int err;
|
|
|
|
err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET);
|
|
BUG_ON(err);
|
|
}
|
|
|
|
module_init(xfrm4_mode_tunnel_init);
|
|
module_exit(xfrm4_mode_tunnel_exit);
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL);
|