Commit f203b76d authored by Steffen Klassert's avatar Steffen Klassert

xfrm: Add virtual xfrm interfaces

This patch adds support for virtual xfrm interfaces.
Packets that are routed through such an interface
are guaranteed to be IPsec transformed or dropped.
It is a generic virtual interface that ensures IPsec
transformation, no need to know what happens behind
the interface. This means that we can tunnel IPv4 and
IPv6 through the same interface and support all xfrm
modes (tunnel, transport and beet) on it.
Co-developed-by: default avatarLorenzo Colitti <lorenzo@google.com>
Co-developed-by: default avatarBenedict Wong <benedictwong@google.com>
Signed-off-by: default avatarLorenzo Colitti <lorenzo@google.com>
Signed-off-by: default avatarBenedict Wong <benedictwong@google.com>
Signed-off-by: default avatarSteffen Klassert <steffen.klassert@secunet.com>
Acked-by: default avatarShannon Nelson <shannon.nelson@oracle.com>
Tested-by: default avatarBenedict Wong <benedictwong@google.com>
Tested-by: default avatarAntony Antony <antony@phenome.org>
Reviewed-by: default avatarEyal Birger <eyal.birger@gmail.com>
parent 7e652640
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <net/ipv6.h> #include <net/ipv6.h>
#include <net/ip6_fib.h> #include <net/ip6_fib.h>
#include <net/flow.h> #include <net/flow.h>
#include <net/gro_cells.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
...@@ -293,6 +294,13 @@ struct xfrm_replay { ...@@ -293,6 +294,13 @@ struct xfrm_replay {
int (*overflow)(struct xfrm_state *x, struct sk_buff *skb); int (*overflow)(struct xfrm_state *x, struct sk_buff *skb);
}; };
struct xfrm_if_cb {
struct xfrm_if *(*decode_session)(struct sk_buff *skb);
};
void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb);
void xfrm_if_unregister_cb(void);
struct net_device; struct net_device;
struct xfrm_type; struct xfrm_type;
struct xfrm_dst; struct xfrm_dst;
...@@ -1039,6 +1047,22 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst) ...@@ -1039,6 +1047,22 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev); void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
struct xfrm_if_parms {
char name[IFNAMSIZ]; /* name of XFRM device */
int link; /* ifindex of underlying L2 interface */
u32 if_id; /* interface identifyer */
};
struct xfrm_if {
struct xfrm_if __rcu *next; /* next interface in list */
struct net_device *dev; /* virtual device associated with interface */
struct net_device *phydev; /* physical device */
struct net *net; /* netns for packet i/o */
struct xfrm_if_parms p; /* interface parms */
struct gro_cells gro_cells;
};
struct xfrm_offload { struct xfrm_offload {
/* Output sequence number for replay protection on offloading. */ /* Output sequence number for replay protection on offloading. */
struct { struct {
......
...@@ -459,6 +459,16 @@ enum { ...@@ -459,6 +459,16 @@ enum {
#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1) #define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
/* XFRM section */
enum {
IFLA_XFRM_UNSPEC,
IFLA_XFRM_LINK,
IFLA_XFRM_IF_ID,
__IFLA_XFRM_MAX
};
#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
enum macsec_validation_type { enum macsec_validation_type {
MACSEC_VALIDATE_DISABLED = 0, MACSEC_VALIDATE_DISABLED = 0,
MACSEC_VALIDATE_CHECK = 1, MACSEC_VALIDATE_CHECK = 1,
......
...@@ -25,6 +25,14 @@ config XFRM_USER ...@@ -25,6 +25,14 @@ config XFRM_USER
If unsure, say Y. If unsure, say Y.
config XFRM_INTERFACE
tristate "Transformation virtual interface"
depends on XFRM && IPV6
---help---
This provides a virtual interface to route IPsec traffic.
If unsure, say N.
config XFRM_SUB_POLICY config XFRM_SUB_POLICY
bool "Transformation sub policy support" bool "Transformation sub policy support"
depends on XFRM depends on XFRM
......
...@@ -10,3 +10,4 @@ obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o ...@@ -10,3 +10,4 @@ obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
obj-$(CONFIG_XFRM_USER) += xfrm_user.o obj-$(CONFIG_XFRM_USER) += xfrm_user.o
obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
obj-$(CONFIG_XFRM_INTERFACE) += xfrm_interface.o
...@@ -320,6 +320,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -320,6 +320,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
seq = 0; seq = 0;
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) { if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
goto drop; goto drop;
} }
...@@ -328,12 +329,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) ...@@ -328,12 +329,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
XFRM_SPI_SKB_CB(skb)->daddroff); XFRM_SPI_SKB_CB(skb)->daddroff);
do { do {
if (skb->sp->len == XFRM_MAX_DEPTH) { if (skb->sp->len == XFRM_MAX_DEPTH) {
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
goto drop; goto drop;
} }
x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family); x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);
if (x == NULL) { if (x == NULL) {
secpath_reset(skb);
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES); XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
xfrm_audit_state_notfound(skb, family, spi, seq); xfrm_audit_state_notfound(skb, family, spi, seq);
goto drop; goto drop;
......
This diff is collapsed.
...@@ -47,6 +47,9 @@ struct xfrm_flo { ...@@ -47,6 +47,9 @@ struct xfrm_flo {
static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst); static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst);
static struct work_struct *xfrm_pcpu_work __read_mostly; static struct work_struct *xfrm_pcpu_work __read_mostly;
static DEFINE_SPINLOCK(xfrm_if_cb_lock);
static struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly;
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1] static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
__read_mostly; __read_mostly;
...@@ -119,6 +122,12 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa ...@@ -119,6 +122,12 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa
return afinfo; return afinfo;
} }
/* Called with rcu_read_lock(). */
static const struct xfrm_if_cb *xfrm_if_get_cb(void)
{
return rcu_dereference(xfrm_if_cb);
}
struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
const xfrm_address_t *saddr, const xfrm_address_t *saddr,
const xfrm_address_t *daddr, const xfrm_address_t *daddr,
...@@ -2083,6 +2092,11 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, ...@@ -2083,6 +2092,11 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
if (IS_ERR(xdst)) { if (IS_ERR(xdst)) {
err = PTR_ERR(xdst); err = PTR_ERR(xdst);
if (err == -EREMOTE) {
xfrm_pols_put(pols, num_pols);
return NULL;
}
if (err != -EAGAIN) if (err != -EAGAIN)
goto error; goto error;
goto make_dummy_bundle; goto make_dummy_bundle;
...@@ -2176,6 +2190,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, ...@@ -2176,6 +2190,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
if (IS_ERR(xdst)) { if (IS_ERR(xdst)) {
xfrm_pols_put(pols, num_pols); xfrm_pols_put(pols, num_pols);
err = PTR_ERR(xdst); err = PTR_ERR(xdst);
if (err == -EREMOTE)
goto nopol;
goto dropdst; goto dropdst;
} else if (xdst == NULL) { } else if (xdst == NULL) {
num_xfrms = 0; num_xfrms = 0;
...@@ -2368,12 +2385,20 @@ int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, ...@@ -2368,12 +2385,20 @@ int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
unsigned int family, int reverse) unsigned int family, int reverse)
{ {
const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
const struct xfrm_if_cb *ifcb = xfrm_if_get_cb();
struct xfrm_if *xi;
int err; int err;
if (unlikely(afinfo == NULL)) if (unlikely(afinfo == NULL))
return -EAFNOSUPPORT; return -EAFNOSUPPORT;
afinfo->decode_session(skb, fl, reverse); afinfo->decode_session(skb, fl, reverse);
if (ifcb) {
xi = ifcb->decode_session(skb);
if (xi)
fl->flowi_xfrm.if_id = xi->p.if_id;
}
err = security_xfrm_decode_session(skb, &fl->flowi_secid); err = security_xfrm_decode_session(skb, &fl->flowi_secid);
rcu_read_unlock(); rcu_read_unlock();
return err; return err;
...@@ -2828,6 +2853,21 @@ void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo) ...@@ -2828,6 +2853,21 @@ void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo)
} }
EXPORT_SYMBOL(xfrm_policy_unregister_afinfo); EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb)
{
spin_lock(&xfrm_if_cb_lock);
rcu_assign_pointer(xfrm_if_cb, ifcb);
spin_unlock(&xfrm_if_cb_lock);
}
EXPORT_SYMBOL(xfrm_if_register_cb);
void xfrm_if_unregister_cb(void)
{
RCU_INIT_POINTER(xfrm_if_cb, NULL);
synchronize_rcu();
}
EXPORT_SYMBOL(xfrm_if_unregister_cb);
#ifdef CONFIG_XFRM_STATISTICS #ifdef CONFIG_XFRM_STATISTICS
static int __net_init xfrm_statistics_init(struct net *net) static int __net_init xfrm_statistics_init(struct net *net)
{ {
...@@ -3008,6 +3048,9 @@ void __init xfrm_init(void) ...@@ -3008,6 +3048,9 @@ void __init xfrm_init(void)
xfrm_dev_init(); xfrm_dev_init();
seqcount_init(&xfrm_policy_hash_generation); seqcount_init(&xfrm_policy_hash_generation);
xfrm_input_init(); xfrm_input_init();
RCU_INIT_POINTER(xfrm_if_cb, NULL);
synchronize_rcu();
} }
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment