Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e1f40ec authored by David S. Miller's avatar David S. Miller
Browse files


Steffen Klassert says:

====================
This is the rework of the IPsec virtual tunnel interface
for ipv4 to support inter address family tunneling and
namespace crossing. The only change to the last RFC version
is a compile fix for an odd configuration where CONFIG_XFRM
is set but CONFIG_INET is not set.

1) Add and use a IPsec protocol multiplexer.

2) Add xfrm_tunnel_skb_cb to the skb common buffer
   to store a receive callback there.

3) Make vti work with i_key set by not including the i_key
   when comupting the hash for the tunnel lookup in case of
   vti tunnels.

4) Update ip_vti to use it's own receive hook.

5) Remove xfrm_tunnel_notifier, this is replaced by the IPsec
   protocol multiplexer.

6) We need to be protocol family indepenent, so use the on xfrm_lookup
   returned dst_entry instead of the ipv4 rtable in vti_tunnel_xmit().

7) Add support for inter address family tunneling.

8) Check if the tunnel endpoints of the xfrm state and the vti interface
   are matching and return an error otherwise.

8) Enable namespace crossing tor vti devices.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e1fbf260 895de9a3
Loading
Loading
Loading
Loading
+68 −15
Original line number Original line Diff line number Diff line
@@ -599,16 +599,27 @@ struct xfrm_mgr {
int xfrm_register_km(struct xfrm_mgr *km);
int xfrm_register_km(struct xfrm_mgr *km);
int xfrm_unregister_km(struct xfrm_mgr *km);
int xfrm_unregister_km(struct xfrm_mgr *km);


struct xfrm_tunnel_skb_cb {
	union {
		struct inet_skb_parm h4;
		struct inet6_skb_parm h6;
	} header;

	union {
		struct ip_tunnel *ip4;
		struct ip6_tnl *ip6;
	} tunnel;
};

#define XFRM_TUNNEL_SKB_CB(__skb) ((struct xfrm_tunnel_skb_cb *)&((__skb)->cb[0]))

/*
/*
 * This structure is used for the duration where packets are being
 * This structure is used for the duration where packets are being
 * transformed by IPsec.  As soon as the packet leaves IPsec the
 * transformed by IPsec.  As soon as the packet leaves IPsec the
 * area beyond the generic IP part may be overwritten.
 * area beyond the generic IP part may be overwritten.
 */
 */
struct xfrm_skb_cb {
struct xfrm_skb_cb {
	union {
	struct xfrm_tunnel_skb_cb header;
		struct inet_skb_parm h4;
		struct inet6_skb_parm h6;
        } header;


        /* Sequence number for replay protection. */
        /* Sequence number for replay protection. */
	union {
	union {
@@ -630,10 +641,7 @@ struct xfrm_skb_cb {
 * to transmit header information to the mode input/output functions.
 * to transmit header information to the mode input/output functions.
 */
 */
struct xfrm_mode_skb_cb {
struct xfrm_mode_skb_cb {
	union {
	struct xfrm_tunnel_skb_cb header;
		struct inet_skb_parm h4;
		struct inet6_skb_parm h6;
	} header;


	/* Copied from header for IPv4, always set to zero and DF for IPv6. */
	/* Copied from header for IPv4, always set to zero and DF for IPv6. */
	__be16 id;
	__be16 id;
@@ -665,10 +673,7 @@ struct xfrm_mode_skb_cb {
 * related information.
 * related information.
 */
 */
struct xfrm_spi_skb_cb {
struct xfrm_spi_skb_cb {
	union {
	struct xfrm_tunnel_skb_cb header;
		struct inet_skb_parm h4;
		struct inet6_skb_parm h6;
	} header;


	unsigned int daddroff;
	unsigned int daddroff;
	unsigned int family;
	unsigned int family;
@@ -1347,6 +1352,18 @@ struct xfrm_algo_desc {
	struct sadb_alg desc;
	struct sadb_alg desc;
};
};


/* XFRM protocol handlers.  */
struct xfrm4_protocol {
	int (*handler)(struct sk_buff *skb);
	int (*input_handler)(struct sk_buff *skb, int nexthdr, __be32 spi,
			     int encap_type);
	int (*cb_handler)(struct sk_buff *skb, int err);
	int (*err_handler)(struct sk_buff *skb, u32 info);

	struct xfrm4_protocol __rcu *next;
	int priority;
};

/* XFRM tunnel handlers.  */
/* XFRM tunnel handlers.  */
struct xfrm_tunnel {
struct xfrm_tunnel {
	int (*handler)(struct sk_buff *skb);
	int (*handler)(struct sk_buff *skb);
@@ -1498,18 +1515,22 @@ int xfrm4_rcv(struct sk_buff *skb);


static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi)
{
{
	return xfrm4_rcv_encap(skb, nexthdr, spi, 0);
	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
	XFRM_SPI_SKB_CB(skb)->family = AF_INET;
	XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
	return xfrm_input(skb, nexthdr, spi, 0);
}
}


int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb);
int xfrm4_output(struct sk_buff *skb);
int xfrm4_output(struct sk_buff *skb);
int xfrm4_output_finish(struct sk_buff *skb);
int xfrm4_output_finish(struct sk_buff *skb);
int xfrm4_rcv_cb(struct sk_buff *skb, u8 protocol, int err);
int xfrm4_protocol_register(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm4_protocol_deregister(struct xfrm4_protocol *handler, unsigned char protocol);
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family);
int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler);
int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler);
int xfrm6_extract_header(struct sk_buff *skb);
int xfrm6_extract_header(struct sk_buff *skb);
@@ -1752,4 +1773,36 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m)
	return ret;
	return ret;
}
}


static inline int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family,
			      u8 protocol, int err)
{
	switch(family) {
#ifdef CONFIG_INET
	case AF_INET:
		return xfrm4_rcv_cb(skb, protocol, err);
#endif
	}
	return 0;
}

static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
				    unsigned int family)
{
	bool tunnel = false;

	switch(family) {
	case AF_INET:
		if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4)
			tunnel = true;
		break;
	case AF_INET6:
		if (XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6)
			tunnel = true;
		break;
	}
	if (tunnel && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL))
		return -EINVAL;

	return 0;
}
#endif	/* _NET_XFRM_H */
#endif	/* _NET_XFRM_H */
+1 −1
Original line number Original line Diff line number Diff line
@@ -55,4 +55,4 @@ obj-$(CONFIG_MEMCG_KMEM) += tcp_memcontrol.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o


obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
		      xfrm4_output.o
		      xfrm4_output.o xfrm4_protocol.o
+16 −9
Original line number Original line Diff line number Diff line
@@ -428,7 +428,7 @@ out:
	return err;
	return err;
}
}


static void ah4_err(struct sk_buff *skb, u32 info)
static int ah4_err(struct sk_buff *skb, u32 info)
{
{
	struct net *net = dev_net(skb->dev);
	struct net *net = dev_net(skb->dev);
	const struct iphdr *iph = (const struct iphdr *)skb->data;
	const struct iphdr *iph = (const struct iphdr *)skb->data;
@@ -438,23 +438,25 @@ static void ah4_err(struct sk_buff *skb, u32 info)
	switch (icmp_hdr(skb)->type) {
	switch (icmp_hdr(skb)->type) {
	case ICMP_DEST_UNREACH:
	case ICMP_DEST_UNREACH:
		if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
		if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
			return;
			return 0;
	case ICMP_REDIRECT:
	case ICMP_REDIRECT:
		break;
		break;
	default:
	default:
		return;
		return 0;
	}
	}


	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
			      ah->spi, IPPROTO_AH, AF_INET);
			      ah->spi, IPPROTO_AH, AF_INET);
	if (!x)
	if (!x)
		return;
		return 0;


	if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
	if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
		ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
		ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
	else
	else
		ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
		ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
	xfrm_state_put(x);
	xfrm_state_put(x);

	return 0;
}
}


static int ah_init_state(struct xfrm_state *x)
static int ah_init_state(struct xfrm_state *x)
@@ -536,6 +538,10 @@ static void ah_destroy(struct xfrm_state *x)
	kfree(ahp);
	kfree(ahp);
}
}


static int ah4_rcv_cb(struct sk_buff *skb, int err)
{
	return 0;
}


static const struct xfrm_type ah_type =
static const struct xfrm_type ah_type =
{
{
@@ -549,11 +555,12 @@ static const struct xfrm_type ah_type =
	.output		= ah_output
	.output		= ah_output
};
};


static const struct net_protocol ah4_protocol = {
static struct xfrm4_protocol ah4_protocol = {
	.handler	=	xfrm4_rcv,
	.handler	=	xfrm4_rcv,
	.input_handler	=	xfrm_input,
	.cb_handler	=	ah4_rcv_cb,
	.err_handler	=	ah4_err,
	.err_handler	=	ah4_err,
	.no_policy	=	1,
	.priority	=	0,
	.netns_ok	=	1,
};
};


static int __init ah4_init(void)
static int __init ah4_init(void)
@@ -562,7 +569,7 @@ static int __init ah4_init(void)
		pr_info("%s: can't add xfrm type\n", __func__);
		pr_info("%s: can't add xfrm type\n", __func__);
		return -EAGAIN;
		return -EAGAIN;
	}
	}
	if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
	if (xfrm4_protocol_register(&ah4_protocol, IPPROTO_AH) < 0) {
		pr_info("%s: can't add protocol\n", __func__);
		pr_info("%s: can't add protocol\n", __func__);
		xfrm_unregister_type(&ah_type, AF_INET);
		xfrm_unregister_type(&ah_type, AF_INET);
		return -EAGAIN;
		return -EAGAIN;
@@ -572,7 +579,7 @@ static int __init ah4_init(void)


static void __exit ah4_fini(void)
static void __exit ah4_fini(void)
{
{
	if (inet_del_protocol(&ah4_protocol, IPPROTO_AH) < 0)
	if (xfrm4_protocol_deregister(&ah4_protocol, IPPROTO_AH) < 0)
		pr_info("%s: can't remove protocol\n", __func__);
		pr_info("%s: can't remove protocol\n", __func__);
	if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
	if (xfrm_unregister_type(&ah_type, AF_INET) < 0)
		pr_info("%s: can't remove xfrm type\n", __func__);
		pr_info("%s: can't remove xfrm type\n", __func__);
+17 −9
Original line number Original line Diff line number Diff line
@@ -473,7 +473,7 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu)
		 net_adj) & ~(blksize - 1)) + net_adj - 2;
		 net_adj) & ~(blksize - 1)) + net_adj - 2;
}
}


static void esp4_err(struct sk_buff *skb, u32 info)
static int esp4_err(struct sk_buff *skb, u32 info)
{
{
	struct net *net = dev_net(skb->dev);
	struct net *net = dev_net(skb->dev);
	const struct iphdr *iph = (const struct iphdr *)skb->data;
	const struct iphdr *iph = (const struct iphdr *)skb->data;
@@ -483,23 +483,25 @@ static void esp4_err(struct sk_buff *skb, u32 info)
	switch (icmp_hdr(skb)->type) {
	switch (icmp_hdr(skb)->type) {
	case ICMP_DEST_UNREACH:
	case ICMP_DEST_UNREACH:
		if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
		if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
			return;
			return 0;
	case ICMP_REDIRECT:
	case ICMP_REDIRECT:
		break;
		break;
	default:
	default:
		return;
		return 0;
	}
	}


	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
	x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
			      esph->spi, IPPROTO_ESP, AF_INET);
			      esph->spi, IPPROTO_ESP, AF_INET);
	if (!x)
	if (!x)
		return;
		return 0;


	if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
	if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
		ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
		ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
	else
	else
		ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
		ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
	xfrm_state_put(x);
	xfrm_state_put(x);

	return 0;
}
}


static void esp_destroy(struct xfrm_state *x)
static void esp_destroy(struct xfrm_state *x)
@@ -672,6 +674,11 @@ error:
	return err;
	return err;
}
}


static int esp4_rcv_cb(struct sk_buff *skb, int err)
{
	return 0;
}

static const struct xfrm_type esp_type =
static const struct xfrm_type esp_type =
{
{
	.description	= "ESP4",
	.description	= "ESP4",
@@ -685,11 +692,12 @@ static const struct xfrm_type esp_type =
	.output		= esp_output
	.output		= esp_output
};
};


static const struct net_protocol esp4_protocol = {
static struct xfrm4_protocol esp4_protocol = {
	.handler	=	xfrm4_rcv,
	.handler	=	xfrm4_rcv,
	.input_handler	=	xfrm_input,
	.cb_handler	=	esp4_rcv_cb,
	.err_handler	=	esp4_err,
	.err_handler	=	esp4_err,
	.no_policy	=	1,
	.priority	=	0,
	.netns_ok	=	1,
};
};


static int __init esp4_init(void)
static int __init esp4_init(void)
@@ -698,7 +706,7 @@ static int __init esp4_init(void)
		pr_info("%s: can't add xfrm type\n", __func__);
		pr_info("%s: can't add xfrm type\n", __func__);
		return -EAGAIN;
		return -EAGAIN;
	}
	}
	if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
	if (xfrm4_protocol_register(&esp4_protocol, IPPROTO_ESP) < 0) {
		pr_info("%s: can't add protocol\n", __func__);
		pr_info("%s: can't add protocol\n", __func__);
		xfrm_unregister_type(&esp_type, AF_INET);
		xfrm_unregister_type(&esp_type, AF_INET);
		return -EAGAIN;
		return -EAGAIN;
@@ -708,7 +716,7 @@ static int __init esp4_init(void)


static void __exit esp4_fini(void)
static void __exit esp4_fini(void)
{
{
	if (inet_del_protocol(&esp4_protocol, IPPROTO_ESP) < 0)
	if (xfrm4_protocol_deregister(&esp4_protocol, IPPROTO_ESP) < 0)
		pr_info("%s: can't remove protocol\n", __func__);
		pr_info("%s: can't remove protocol\n", __func__);
	if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
	if (xfrm_unregister_type(&esp_type, AF_INET) < 0)
		pr_info("%s: can't remove xfrm type\n", __func__);
		pr_info("%s: can't remove xfrm type\n", __func__);
+5 −1
Original line number Original line Diff line number Diff line
@@ -280,13 +280,17 @@ static struct hlist_head *ip_bucket(struct ip_tunnel_net *itn,
{
{
	unsigned int h;
	unsigned int h;
	__be32 remote;
	__be32 remote;
	__be32 i_key = parms->i_key;


	if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr))
	if (parms->iph.daddr && !ipv4_is_multicast(parms->iph.daddr))
		remote = parms->iph.daddr;
		remote = parms->iph.daddr;
	else
	else
		remote = 0;
		remote = 0;


	h = ip_tunnel_hash(parms->i_key, remote);
	if (!(parms->i_flags & TUNNEL_KEY) && (parms->i_flags & VTI_ISVTI))
		i_key = 0;

	h = ip_tunnel_hash(i_key, remote);
	return &itn->tunnels[h];
	return &itn->tunnels[h];
}
}


Loading