You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
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: Lorenzo Colitti <lorenzo@google.com> Co-developed-by: Benedict Wong <benedictwong@google.com> Signed-off-by: Lorenzo Colitti <lorenzo@google.com> Signed-off-by: Benedict Wong <benedictwong@google.com> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> Acked-by: Shannon Nelson <shannon.nelson@oracle.com> Tested-by: Benedict Wong <benedictwong@google.com> Tested-by: Antony Antony <antony@phenome.org> Reviewed-by: Eyal Birger <eyal.birger@gmail.com>
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_fib.h>
|
||||
#include <net/flow.h>
|
||||
#include <net/gro_cells.h>
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
@@ -293,6 +294,13 @@ struct xfrm_replay {
|
||||
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 xfrm_type;
|
||||
struct xfrm_dst;
|
||||
@@ -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);
|
||||
|
||||
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 {
|
||||
/* Output sequence number for replay protection on offloading. */
|
||||
struct {
|
||||
|
||||
@@ -459,6 +459,16 @@ enum {
|
||||
|
||||
#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 {
|
||||
MACSEC_VALIDATE_DISABLED = 0,
|
||||
MACSEC_VALIDATE_CHECK = 1,
|
||||
|
||||
@@ -25,6 +25,14 @@ config XFRM_USER
|
||||
|
||||
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
|
||||
bool "Transformation sub policy support"
|
||||
depends on XFRM
|
||||
|
||||
@@ -10,3 +10,4 @@ obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
|
||||
obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
|
||||
obj-$(CONFIG_XFRM_USER) += xfrm_user.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)
|
||||
|
||||
seq = 0;
|
||||
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
||||
goto drop;
|
||||
}
|
||||
@@ -328,12 +329,14 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
||||
XFRM_SPI_SKB_CB(skb)->daddroff);
|
||||
do {
|
||||
if (skb->sp->len == XFRM_MAX_DEPTH) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);
|
||||
if (x == NULL) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
|
||||
xfrm_audit_state_notfound(skb, family, spi, seq);
|
||||
goto drop;
|
||||
|
||||
972
net/xfrm/xfrm_interface.c
Normal file
972
net/xfrm/xfrm_interface.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,9 @@ struct xfrm_flo {
|
||||
|
||||
static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst);
|
||||
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 struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
|
||||
__read_mostly;
|
||||
@@ -119,6 +122,12 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa
|
||||
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,
|
||||
const xfrm_address_t *saddr,
|
||||
const xfrm_address_t *daddr,
|
||||
@@ -2083,6 +2092,11 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
|
||||
|
||||
if (IS_ERR(xdst)) {
|
||||
err = PTR_ERR(xdst);
|
||||
if (err == -EREMOTE) {
|
||||
xfrm_pols_put(pols, num_pols);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (err != -EAGAIN)
|
||||
goto error;
|
||||
goto make_dummy_bundle;
|
||||
@@ -2176,6 +2190,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
if (IS_ERR(xdst)) {
|
||||
xfrm_pols_put(pols, num_pols);
|
||||
err = PTR_ERR(xdst);
|
||||
if (err == -EREMOTE)
|
||||
goto nopol;
|
||||
|
||||
goto dropdst;
|
||||
} else if (xdst == NULL) {
|
||||
num_xfrms = 0;
|
||||
@@ -2368,12 +2385,20 @@ int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
|
||||
unsigned int family, int reverse)
|
||||
{
|
||||
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;
|
||||
|
||||
if (unlikely(afinfo == NULL))
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
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);
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
@@ -2828,6 +2853,21 @@ void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *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
|
||||
static int __net_init xfrm_statistics_init(struct net *net)
|
||||
{
|
||||
@@ -3008,6 +3048,9 @@ void __init xfrm_init(void)
|
||||
xfrm_dev_init();
|
||||
seqcount_init(&xfrm_policy_hash_generation);
|
||||
xfrm_input_init();
|
||||
|
||||
RCU_INIT_POINTER(xfrm_if_cb, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
|
||||
Reference in New Issue
Block a user