mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	route: Per route IP tunnel metadata via lightweight tunnel
This introduces a new IP tunnel lightweight tunnel type which allows to specify IP tunnel instructions per route. Only IPv4 is supported at this point. Signed-off-by: Thomas Graf <tgraf@suug.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									1b7179d3ad
								
							
						
					
					
						commit
						3093fbe7ff
					
				
					 8 changed files with 157 additions and 5 deletions
				
			
		| 
						 | 
				
			
			@ -1935,7 +1935,7 @@ static void vxlan_encap_bypass(struct sk_buff *skb, struct vxlan_dev *src_vxlan,
 | 
			
		|||
static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 | 
			
		||||
			   struct vxlan_rdst *rdst, bool did_rsc)
 | 
			
		||||
{
 | 
			
		||||
	struct ip_tunnel_info *info = skb_tunnel_info(skb);
 | 
			
		||||
	struct ip_tunnel_info *info;
 | 
			
		||||
	struct vxlan_dev *vxlan = netdev_priv(dev);
 | 
			
		||||
	struct sock *sk = vxlan->vn_sock->sock->sk;
 | 
			
		||||
	struct rtable *rt = NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -1952,6 +1952,9 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 | 
			
		|||
	int err;
 | 
			
		||||
	u32 flags = vxlan->flags;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Support IPv6 */
 | 
			
		||||
	info = skb_tunnel_info(skb, AF_INET);
 | 
			
		||||
 | 
			
		||||
	if (rdst) {
 | 
			
		||||
		dst_port = rdst->remote_port ? rdst->remote_port : vxlan->dst_port;
 | 
			
		||||
		vni = rdst->remote_vni;
 | 
			
		||||
| 
						 | 
				
			
			@ -2141,12 +2144,15 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 | 
			
		|||
static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct vxlan_dev *vxlan = netdev_priv(dev);
 | 
			
		||||
	const struct ip_tunnel_info *info = skb_tunnel_info(skb);
 | 
			
		||||
	const struct ip_tunnel_info *info;
 | 
			
		||||
	struct ethhdr *eth;
 | 
			
		||||
	bool did_rsc = false;
 | 
			
		||||
	struct vxlan_rdst *rdst, *fdst = NULL;
 | 
			
		||||
	struct vxlan_fdb *f;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Support IPv6 */
 | 
			
		||||
	info = skb_tunnel_info(skb, AF_INET);
 | 
			
		||||
 | 
			
		||||
	skb_reset_mac_header(skb);
 | 
			
		||||
	eth = eth_hdr(skb);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,13 +23,23 @@ static inline struct metadata_dst *skb_metadata_dst(struct sk_buff *skb)
 | 
			
		|||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct ip_tunnel_info *skb_tunnel_info(struct sk_buff *skb)
 | 
			
		||||
static inline struct ip_tunnel_info *skb_tunnel_info(struct sk_buff *skb,
 | 
			
		||||
						     int family)
 | 
			
		||||
{
 | 
			
		||||
	struct metadata_dst *md_dst = skb_metadata_dst(skb);
 | 
			
		||||
	struct rtable *rt;
 | 
			
		||||
 | 
			
		||||
	if (md_dst)
 | 
			
		||||
		return &md_dst->u.tun_info;
 | 
			
		||||
 | 
			
		||||
	switch (family) {
 | 
			
		||||
	case AF_INET:
 | 
			
		||||
		rt = (struct rtable *)skb_dst(skb);
 | 
			
		||||
		if (rt && rt->rt_lwtstate)
 | 
			
		||||
			return lwt_tun_info(rt->rt_lwtstate);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,9 +9,9 @@
 | 
			
		|||
#include <net/dsfield.h>
 | 
			
		||||
#include <net/gro_cells.h>
 | 
			
		||||
#include <net/inet_ecn.h>
 | 
			
		||||
#include <net/ip.h>
 | 
			
		||||
#include <net/netns/generic.h>
 | 
			
		||||
#include <net/rtnetlink.h>
 | 
			
		||||
#include <net/lwtunnel.h>
 | 
			
		||||
 | 
			
		||||
#if IS_ENABLED(CONFIG_IPV6)
 | 
			
		||||
#include <net/ipv6.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -298,6 +298,11 @@ static inline void *ip_tunnel_info_opts(struct ip_tunnel_info *info, size_t n)
 | 
			
		|||
	return info + 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct ip_tunnel_info *lwt_tun_info(struct lwtunnel_state *lwtstate)
 | 
			
		||||
{
 | 
			
		||||
	return (struct ip_tunnel_info *)lwtstate->data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* CONFIG_INET */
 | 
			
		||||
 | 
			
		||||
#endif /* __NET_IP_TUNNELS_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
enum lwtunnel_encap_types {
 | 
			
		||||
	LWTUNNEL_ENCAP_NONE,
 | 
			
		||||
	LWTUNNEL_ENCAP_MPLS,
 | 
			
		||||
	LWTUNNEL_ENCAP_IP,
 | 
			
		||||
	__LWTUNNEL_ENCAP_MAX,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -286,6 +286,21 @@ enum rt_class_t {
 | 
			
		|||
 | 
			
		||||
/* Routing message attributes */
 | 
			
		||||
 | 
			
		||||
enum ip_tunnel_t {
 | 
			
		||||
	IP_TUN_UNSPEC,
 | 
			
		||||
	IP_TUN_ID,
 | 
			
		||||
	IP_TUN_DST,
 | 
			
		||||
	IP_TUN_SRC,
 | 
			
		||||
	IP_TUN_TTL,
 | 
			
		||||
	IP_TUN_TOS,
 | 
			
		||||
	IP_TUN_SPORT,
 | 
			
		||||
	IP_TUN_DPORT,
 | 
			
		||||
	IP_TUN_FLAGS,
 | 
			
		||||
	__IP_TUN_MAX,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define IP_TUN_MAX (__IP_TUN_MAX - 1)
 | 
			
		||||
 | 
			
		||||
enum rtattr_type_t {
 | 
			
		||||
	RTA_UNSPEC,
 | 
			
		||||
	RTA_DST,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,3 +190,117 @@ struct rtnl_link_stats64 *ip_tunnel_get_stats64(struct net_device *dev,
 | 
			
		|||
	return tot;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(ip_tunnel_get_stats64);
 | 
			
		||||
 | 
			
		||||
static const struct nla_policy ip_tun_policy[IP_TUN_MAX + 1] = {
 | 
			
		||||
	[IP_TUN_ID]		= { .type = NLA_U64 },
 | 
			
		||||
	[IP_TUN_DST]		= { .type = NLA_U32 },
 | 
			
		||||
	[IP_TUN_SRC]		= { .type = NLA_U32 },
 | 
			
		||||
	[IP_TUN_TTL]		= { .type = NLA_U8 },
 | 
			
		||||
	[IP_TUN_TOS]		= { .type = NLA_U8 },
 | 
			
		||||
	[IP_TUN_SPORT]		= { .type = NLA_U16 },
 | 
			
		||||
	[IP_TUN_DPORT]		= { .type = NLA_U16 },
 | 
			
		||||
	[IP_TUN_FLAGS]		= { .type = NLA_U16 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int ip_tun_build_state(struct net_device *dev, struct nlattr *attr,
 | 
			
		||||
			      struct lwtunnel_state **ts)
 | 
			
		||||
{
 | 
			
		||||
	struct ip_tunnel_info *tun_info;
 | 
			
		||||
	struct lwtunnel_state *new_state;
 | 
			
		||||
	struct nlattr *tb[IP_TUN_MAX + 1];
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	err = nla_parse_nested(tb, IP_TUN_MAX, attr, ip_tun_policy);
 | 
			
		||||
	if (err < 0)
 | 
			
		||||
		return err;
 | 
			
		||||
 | 
			
		||||
	new_state = lwtunnel_state_alloc(sizeof(*tun_info));
 | 
			
		||||
	if (!new_state)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	new_state->type = LWTUNNEL_ENCAP_IP;
 | 
			
		||||
 | 
			
		||||
	tun_info = lwt_tun_info(new_state);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_ID])
 | 
			
		||||
		tun_info->key.tun_id = nla_get_u64(tb[IP_TUN_ID]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_DST])
 | 
			
		||||
		tun_info->key.ipv4_dst = nla_get_be32(tb[IP_TUN_DST]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_SRC])
 | 
			
		||||
		tun_info->key.ipv4_src = nla_get_be32(tb[IP_TUN_SRC]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_TTL])
 | 
			
		||||
		tun_info->key.ipv4_ttl = nla_get_u8(tb[IP_TUN_TTL]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_TOS])
 | 
			
		||||
		tun_info->key.ipv4_tos = nla_get_u8(tb[IP_TUN_TOS]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_SPORT])
 | 
			
		||||
		tun_info->key.tp_src = nla_get_be16(tb[IP_TUN_SPORT]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_DPORT])
 | 
			
		||||
		tun_info->key.tp_dst = nla_get_be16(tb[IP_TUN_DPORT]);
 | 
			
		||||
 | 
			
		||||
	if (tb[IP_TUN_FLAGS])
 | 
			
		||||
		tun_info->key.tun_flags = nla_get_u16(tb[IP_TUN_FLAGS]);
 | 
			
		||||
 | 
			
		||||
	tun_info->mode = IP_TUNNEL_INFO_TX;
 | 
			
		||||
	tun_info->options = NULL;
 | 
			
		||||
	tun_info->options_len = 0;
 | 
			
		||||
 | 
			
		||||
	*ts = new_state;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ip_tun_fill_encap_info(struct sk_buff *skb,
 | 
			
		||||
				  struct lwtunnel_state *lwtstate)
 | 
			
		||||
{
 | 
			
		||||
	struct ip_tunnel_info *tun_info = lwt_tun_info(lwtstate);
 | 
			
		||||
 | 
			
		||||
	if (nla_put_u64(skb, IP_TUN_ID, tun_info->key.tun_id) ||
 | 
			
		||||
	    nla_put_be32(skb, IP_TUN_DST, tun_info->key.ipv4_dst) ||
 | 
			
		||||
	    nla_put_be32(skb, IP_TUN_SRC, tun_info->key.ipv4_src) ||
 | 
			
		||||
	    nla_put_u8(skb, IP_TUN_TOS, tun_info->key.ipv4_tos) ||
 | 
			
		||||
	    nla_put_u8(skb, IP_TUN_TTL, tun_info->key.ipv4_ttl) ||
 | 
			
		||||
	    nla_put_u16(skb, IP_TUN_SPORT, tun_info->key.tp_src) ||
 | 
			
		||||
	    nla_put_u16(skb, IP_TUN_DPORT, tun_info->key.tp_dst) ||
 | 
			
		||||
	    nla_put_u16(skb, IP_TUN_FLAGS, tun_info->key.tun_flags))
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ip_tun_encap_nlsize(struct lwtunnel_state *lwtstate)
 | 
			
		||||
{
 | 
			
		||||
	return nla_total_size(8)	/* IP_TUN_ID */
 | 
			
		||||
		+ nla_total_size(4)	/* IP_TUN_DST */
 | 
			
		||||
		+ nla_total_size(4)	/* IP_TUN_SRC */
 | 
			
		||||
		+ nla_total_size(1)	/* IP_TUN_TOS */
 | 
			
		||||
		+ nla_total_size(1)	/* IP_TUN_TTL */
 | 
			
		||||
		+ nla_total_size(2)	/* IP_TUN_SPORT */
 | 
			
		||||
		+ nla_total_size(2)	/* IP_TUN_DPORT */
 | 
			
		||||
		+ nla_total_size(2);	/* IP_TUN_FLAGS */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct lwtunnel_encap_ops ip_tun_lwt_ops = {
 | 
			
		||||
	.build_state = ip_tun_build_state,
 | 
			
		||||
	.fill_encap = ip_tun_fill_encap_info,
 | 
			
		||||
	.get_encap_size = ip_tun_encap_nlsize,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init ip_tunnel_core_init(void)
 | 
			
		||||
{
 | 
			
		||||
	lwtunnel_encap_add_ops(&ip_tun_lwt_ops, LWTUNNEL_ENCAP_IP);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
module_init(ip_tunnel_core_init);
 | 
			
		||||
 | 
			
		||||
static void __exit ip_tunnel_core_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	lwtunnel_encap_del_ops(&ip_tun_lwt_ops, LWTUNNEL_ENCAP_IP);
 | 
			
		||||
}
 | 
			
		||||
module_exit(ip_tunnel_core_exit);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1693,7 +1693,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 | 
			
		|||
	   by fib_lookup.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	tun_info = skb_tunnel_info(skb);
 | 
			
		||||
	tun_info = skb_tunnel_info(skb, AF_INET);
 | 
			
		||||
	if (tun_info && tun_info->mode == IP_TUNNEL_INFO_RX)
 | 
			
		||||
		fl4.flowi4_tun_key.tun_id = tun_info->key.tun_id;
 | 
			
		||||
	else
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@
 | 
			
		|||
#include <linux/skbuff.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/u64_stats_sync.h>
 | 
			
		||||
#include <net/route.h>
 | 
			
		||||
 | 
			
		||||
#include "datapath.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue