Skip to content

Commit

Permalink
xfrm: Add an IPsec hardware offloading API
Browse files Browse the repository at this point in the history
This patch adds all the bits that are needed to do
IPsec hardware offload for IPsec states and ESP packets.
We add xfrmdev_ops to the net_device. xfrmdev_ops has
function pointers that are needed to manage the xfrm
states in the hardware and to do a per packet
offloading decision.

Joint work with:
Ilan Tayari <[email protected]>
Guy Shapiro <[email protected]>
Yossi Kuperman <[email protected]>

Signed-off-by: Guy Shapiro <[email protected]>
Signed-off-by: Ilan Tayari <[email protected]>
Signed-off-by: Yossi Kuperman <[email protected]>
Signed-off-by: Steffen Klassert <[email protected]>
  • Loading branch information
klassert committed Apr 14, 2017
1 parent c35fe41 commit d77e38e
Show file tree
Hide file tree
Showing 14 changed files with 424 additions and 24 deletions.
14 changes: 14 additions & 0 deletions include/linux/netdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,16 @@ struct netdev_xdp {
};
};

#ifdef CONFIG_XFRM_OFFLOAD
struct xfrmdev_ops {
int (*xdo_dev_state_add) (struct xfrm_state *x);
void (*xdo_dev_state_delete) (struct xfrm_state *x);
void (*xdo_dev_state_free) (struct xfrm_state *x);
bool (*xdo_dev_offload_ok) (struct sk_buff *skb,
struct xfrm_state *x);
};
#endif

/*
* This structure defines the management hooks for network devices.
* The following hooks can be defined; unless noted otherwise, they are
Expand Down Expand Up @@ -1697,6 +1707,10 @@ struct net_device {
const struct ndisc_ops *ndisc_ops;
#endif

#ifdef CONFIG_XFRM
const struct xfrmdev_ops *xfrmdev_ops;
#endif

const struct header_ops *header_ops;

unsigned int flags;
Expand Down
65 changes: 64 additions & 1 deletion include/net/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,13 @@ struct xfrm_state_walk {
struct xfrm_address_filter *filter;
};

struct xfrm_state_offload {
struct net_device *dev;
unsigned long offload_handle;
unsigned int num_exthdrs;
u8 flags;
};

/* Full description of state of transformer. */
struct xfrm_state {
possible_net_t xs_net;
Expand Down Expand Up @@ -207,6 +214,8 @@ struct xfrm_state {
struct xfrm_lifetime_cur curlft;
struct tasklet_hrtimer mtimer;

struct xfrm_state_offload xso;

/* used to fix curlft->add_time when changing date */
long saved_tmo;

Expand Down Expand Up @@ -1453,7 +1462,6 @@ struct xfrm6_tunnel {
void xfrm_init(void);
void xfrm4_init(void);
int xfrm_state_init(struct net *net);
void xfrm_dev_init(void);
void xfrm_state_fini(struct net *net);
void xfrm4_state_init(void);
void xfrm4_protocol_init(void);
Expand Down Expand Up @@ -1559,6 +1567,7 @@ struct xfrmk_spdinfo {
struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq);
int xfrm_state_delete(struct xfrm_state *x);
int xfrm_state_flush(struct net *net, u8 proto, bool task_valid);
int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
Expand Down Expand Up @@ -1641,6 +1650,11 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
}
#endif

struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
const xfrm_address_t *saddr,
const xfrm_address_t *daddr,
int family);

struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp);

void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type);
Expand Down Expand Up @@ -1846,6 +1860,55 @@ static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb)
}
#endif

#ifdef CONFIG_XFRM_OFFLOAD
void __net_init xfrm_dev_init(void);
int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
struct xfrm_user_offload *xuo);
bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);

static inline void xfrm_dev_state_delete(struct xfrm_state *x)
{
struct xfrm_state_offload *xso = &x->xso;

if (xso->dev)
xso->dev->xfrmdev_ops->xdo_dev_state_delete(x);
}

static inline void xfrm_dev_state_free(struct xfrm_state *x)
{
struct xfrm_state_offload *xso = &x->xso;
struct net_device *dev = xso->dev;

if (dev && dev->xfrmdev_ops) {
dev->xfrmdev_ops->xdo_dev_state_free(x);
xso->dev = NULL;
dev_put(dev);
}
}
#else
static inline void __net_init xfrm_dev_init(void)
{
}

static inline int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo)
{
return 0;
}

static inline void xfrm_dev_state_delete(struct xfrm_state *x)
{
}

static inline void xfrm_dev_state_free(struct xfrm_state *x)
{
}

static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
{
return false;
}
#endif

static inline int xfrm_mark_get(struct nlattr **attrs, struct xfrm_mark *m)
{
if (attrs[XFRMA_MARK])
Expand Down
8 changes: 8 additions & 0 deletions include/uapi/linux/xfrm.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ enum xfrm_attr_type_t {
XFRMA_PROTO, /* __u8 */
XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */
XFRMA_PAD,
XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */
__XFRMA_MAX

#define XFRMA_MAX (__XFRMA_MAX - 1)
Expand Down Expand Up @@ -494,6 +495,13 @@ struct xfrm_address_filter {
__u8 dplen;
};

struct xfrm_user_offload {
int ifindex;
__u8 flags;
};
#define XFRM_OFFLOAD_IPV6 1
#define XFRM_OFFLOAD_INBOUND 2

#ifndef __KERNEL__
/* backwards compatibility for userspace */
#define XFRMGRP_ACQUIRE 1
Expand Down
7 changes: 3 additions & 4 deletions net/ipv4/esp4.c
Original file line number Diff line number Diff line change
Expand Up @@ -435,9 +435,6 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
aead_request_set_crypt(req, sg, dsg, ivlen + clen, iv);
aead_request_set_ad(req, assoclen);

seqno = cpu_to_be64(XFRM_SKB_CB(skb)->seq.output.low +
((u64)XFRM_SKB_CB(skb)->seq.output.hi << 32));

memset(iv, 0, ivlen);
memcpy(iv + ivlen - min(ivlen, 8), (u8 *)&seqno + 8 - min(ivlen, 8),
min(ivlen, 8));
Expand Down Expand Up @@ -470,6 +467,7 @@ static int esp_input_done2(struct sk_buff *skb, int err)
{
const struct iphdr *iph;
struct xfrm_state *x = xfrm_input_state(skb);
struct xfrm_offload *xo = xfrm_offload(skb);
struct crypto_aead *aead = x->data;
int alen = crypto_aead_authsize(aead);
int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
Expand All @@ -478,7 +476,8 @@ static int esp_input_done2(struct sk_buff *skb, int err)
u8 nexthdr[2];
int padlen;

kfree(ESP_SKB_CB(skb)->tmp);
if (!xo || (xo && !(xo->flags & CRYPTO_DONE)))
kfree(ESP_SKB_CB(skb)->tmp);

if (unlikely(err))
goto out;
Expand Down
3 changes: 2 additions & 1 deletion net/ipv4/xfrm4_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
goto out;

mtu = dst_mtu(skb_dst(skb));
if (skb->len > mtu) {
if ((!skb_is_gso(skb) && skb->len > mtu) ||
(skb_is_gso(skb) && skb_gso_network_seglen(skb) > ip_skb_dst_mtu(skb->sk, skb))) {
skb->protocol = htons(ETH_P_IP);

if (skb->sk)
Expand Down
4 changes: 3 additions & 1 deletion net/ipv6/esp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb)
static int esp_input_done2(struct sk_buff *skb, int err)
{
struct xfrm_state *x = xfrm_input_state(skb);
struct xfrm_offload *xo = xfrm_offload(skb);
struct crypto_aead *aead = x->data;
int alen = crypto_aead_authsize(aead);
int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead);
Expand All @@ -458,7 +459,8 @@ static int esp_input_done2(struct sk_buff *skb, int err)
int padlen;
u8 nexthdr[2];

kfree(ESP_SKB_CB(skb)->tmp);
if (!xo || (xo && !(xo->flags & CRYPTO_DONE)))
kfree(ESP_SKB_CB(skb)->tmp);

if (unlikely(err))
goto out;
Expand Down
9 changes: 7 additions & 2 deletions net/ipv6/xfrm6_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,16 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
int mtu, ret = 0;
struct dst_entry *dst = skb_dst(skb);

if (skb->ignore_df)
goto out;

mtu = dst_mtu(dst);
if (mtu < IPV6_MIN_MTU)
mtu = IPV6_MIN_MTU;

if (!skb->ignore_df && skb->len > mtu) {
if ((!skb_is_gso(skb) && skb->len > mtu) ||
(skb_is_gso(skb) &&
skb_gso_network_seglen(skb) > ip6_skb_dst_mtu(skb))) {
skb->dev = dst->dev;
skb->protocol = htons(ETH_P_IPV6);

Expand All @@ -89,7 +94,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
ret = -EMSGSIZE;
}

out:
return ret;
}

Expand Down
3 changes: 2 additions & 1 deletion net/xfrm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
xfrm_input.o xfrm_output.o \
xfrm_sysctl.o xfrm_replay.o xfrm_device.o
xfrm_sysctl.o xfrm_replay.o
obj-$(CONFIG_XFRM_OFFLOAD) += xfrm_device.o
obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
obj-$(CONFIG_XFRM_USER) += xfrm_user.o
Expand Down
Loading

0 comments on commit d77e38e

Please sign in to comment.