This commit allows userspace to configure keyed VTI tunnels by adding a IFLA_VTI_FLAGS attribute and a VTI_KEYED flag. When set, the flag causes the tunnel parameter i_flags to be set to TUNNEL_KEY.
Creating both a non-keyed VTI and a keyed VTI on the same IP src+dst pair is not useful. Because non-keyed VTIs always accept packets, in such a configuration the keyed VTI would not receive any traffic. This is disallowed by modifying the ip_tunnel_find and vti6_locate functions to treat VTIs on the same src+dst pair as identical unless they are both keyed (in which case they can coexist, by design). So attempts to create such duplicate tunnels - or to change one tunnel in such a way that it would duplicate another - will fail with EEXIST. Signed-off-by: Lorenzo Colitti <lore...@google.com> --- include/uapi/linux/if_tunnel.h | 4 ++++ net/ipv4/ip_tunnel.c | 10 +++++++++- net/ipv4/ip_vti.c | 26 +++++++++++++++++++++++--- net/ipv6/ip6_vti.c | 33 +++++++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h index 1b3d148c45..b431b1c209 100644 --- a/include/uapi/linux/if_tunnel.h +++ b/include/uapi/linux/if_tunnel.h @@ -148,6 +148,9 @@ enum { /* VTI-mode i_flags */ #define VTI_ISVTI ((__force __be16)0x0001) +/* VTI netlink iflags. */ +#define VTI_KEYED 0x0001 + enum { IFLA_VTI_UNSPEC, IFLA_VTI_LINK, @@ -156,6 +159,7 @@ enum { IFLA_VTI_LOCAL, IFLA_VTI_REMOTE, IFLA_VTI_FWMARK, + IFLA_VTI_FLAGS, __IFLA_VTI_MAX, }; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index f45968bb81..9a0a56b491 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -84,6 +84,14 @@ static bool ip_tunnel_key_match(const struct ip_tunnel_parm *p, return !(flags & TUNNEL_KEY); } +static bool ip_tunnel_match(const struct ip_tunnel_parm *p, + __be32 flags, u8 lookup_flags, __be32 key) +{ + return ip_tunnel_key_match(p, flags, lookup_flags, key) || + ((p->i_flags & flags & VTI_ISVTI) && + !(p->i_flags & flags & TUNNEL_KEY)); +} + /* Fallback tunnel: no source, no destination, no key, no options Tunnel hash table: @@ -242,7 +250,7 @@ static struct ip_tunnel *ip_tunnel_find(struct ip_tunnel_net *itn, remote == t->parms.iph.daddr && link == t->parms.link && type == t->dev->type && - ip_tunnel_key_match(&t->parms, flags, 0, key)) + ip_tunnel_match(&t->parms, flags, 0, key)) break; } return t; diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 9d28433a60..1f52719228 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -385,6 +385,16 @@ static int vti4_err(struct sk_buff *skb, u32 info) return tunnel ? 0 : -1; } +static __be16 vti_flags_to_tnl_flags(__u16 flags) +{ + return VTI_ISVTI | ((flags & VTI_KEYED) ? TUNNEL_KEY : 0); +} + +static __u16 tnl_flags_to_vti_flags(__be16 i_flags) +{ + return (i_flags & TUNNEL_KEY) ? VTI_KEYED : 0; +} + static int vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { @@ -525,6 +535,8 @@ static void vti_netlink_parms(struct nlattr *data[], struct ip_tunnel_parm *parms, __u32 *fwmark) { + __u16 flags = 0; + memset(parms, 0, sizeof(*parms)); parms->iph.protocol = IPPROTO_IPIP; @@ -532,8 +544,6 @@ static void vti_netlink_parms(struct nlattr *data[], if (!data) return; - parms->i_flags = VTI_ISVTI; - if (data[IFLA_VTI_LINK]) parms->link = nla_get_u32(data[IFLA_VTI_LINK]); @@ -551,6 +561,11 @@ static void vti_netlink_parms(struct nlattr *data[], if (data[IFLA_VTI_FWMARK]) *fwmark = nla_get_u32(data[IFLA_VTI_FWMARK]); + + if (data[IFLA_VTI_FLAGS]) + flags = nla_get_u16(data[IFLA_VTI_FLAGS]); + + parms->i_flags = vti_flags_to_tnl_flags(flags); } static int vti_newlink(struct net *src_net, struct net_device *dev, @@ -591,6 +606,8 @@ static size_t vti_get_size(const struct net_device *dev) nla_total_size(4) + /* IFLA_VTI_FWMARK */ nla_total_size(4) + + /* IFLA_VTI_FLAGS */ + nla_total_size(2) + 0; } @@ -604,7 +621,9 @@ static int vti_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_be32(skb, IFLA_VTI_OKEY, p->o_key) || nla_put_in_addr(skb, IFLA_VTI_LOCAL, p->iph.saddr) || nla_put_in_addr(skb, IFLA_VTI_REMOTE, p->iph.daddr) || - nla_put_u32(skb, IFLA_VTI_FWMARK, t->fwmark)) + nla_put_u32(skb, IFLA_VTI_FWMARK, t->fwmark) || + nla_put_u16(skb, IFLA_VTI_FLAGS, + tnl_flags_to_vti_flags(p->i_flags))) return -EMSGSIZE; return 0; @@ -617,6 +636,7 @@ static const struct nla_policy vti_policy[IFLA_VTI_MAX + 1] = { [IFLA_VTI_LOCAL] = { .len = FIELD_SIZEOF(struct iphdr, saddr) }, [IFLA_VTI_REMOTE] = { .len = FIELD_SIZEOF(struct iphdr, daddr) }, [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, + [IFLA_VTI_FLAGS] = { .type = NLA_U16 }, }; static struct rtnl_link_ops vti_link_ops __read_mostly = { diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index bf64821b8a..18c2695dc3 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -86,6 +86,13 @@ static bool vti6_match_key(const struct ip6_tnl *t, __be32 key, bool in) return !(flags & TUNNEL_KEY) || tunnel_key == key; } +static bool vti6_match_tunnel(const struct ip6_tnl *t, struct __ip6_tnl_parm *p) +{ + return !(t->parms.i_flags & TUNNEL_KEY) || + !(p->i_flags & TUNNEL_KEY) || + vti6_match_key(t, p->i_key, true); +} + /** * vti6_tnl_lookup - fetch tunnel matching the end-point addresses and key * @net: network namespace @@ -280,7 +287,7 @@ static struct ip6_tnl *vti6_locate(struct net *net, struct __ip6_tnl_parm *p, tp = &t->next) { if (ipv6_addr_equal(local, &t->parms.laddr) && ipv6_addr_equal(remote, &t->parms.raddr) && - vti6_match_key(t, p->i_key, true)) { + vti6_match_tunnel(t, p)) { if (create) return NULL; @@ -990,9 +997,21 @@ static int vti6_validate(struct nlattr *tb[], struct nlattr *data[], return 0; } +static __be16 vti_flags_to_tnl_flags(__u16 i_flags) +{ + return VTI_ISVTI | ((i_flags & VTI_KEYED) ? TUNNEL_KEY : 0); +} + +static __u16 tnl_flags_to_vti_flags(__be16 i_flags) +{ + return (i_flags & TUNNEL_KEY) ? VTI_KEYED : 0; +} + static void vti6_netlink_parms(struct nlattr *data[], struct __ip6_tnl_parm *parms) { + __u16 flags = 0; + memset(parms, 0, sizeof(*parms)); if (!data) @@ -1015,6 +1034,11 @@ static void vti6_netlink_parms(struct nlattr *data[], if (data[IFLA_VTI_FWMARK]) parms->fwmark = nla_get_u32(data[IFLA_VTI_FWMARK]); + + if (data[IFLA_VTI_FLAGS]) + flags = nla_get_u16(data[IFLA_VTI_FLAGS]); + + parms->i_flags = vti_flags_to_tnl_flags(flags); } static int vti6_newlink(struct net *src_net, struct net_device *dev, @@ -1084,6 +1108,8 @@ static size_t vti6_get_size(const struct net_device *dev) nla_total_size(4) + /* IFLA_VTI_FWMARK */ nla_total_size(4) + + /* IFLA_VTI_FLAGS */ + nla_total_size(2) + 0; } @@ -1097,7 +1123,9 @@ static int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_in6_addr(skb, IFLA_VTI_REMOTE, &parm->raddr) || nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) || nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key) || - nla_put_u32(skb, IFLA_VTI_FWMARK, parm->fwmark)) + nla_put_u32(skb, IFLA_VTI_FWMARK, parm->fwmark) || + nla_put_u16(skb, IFLA_VTI_FLAGS, + tnl_flags_to_vti_flags(parm->i_flags))) goto nla_put_failure; return 0; @@ -1112,6 +1140,7 @@ static const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = { [IFLA_VTI_IKEY] = { .type = NLA_U32 }, [IFLA_VTI_OKEY] = { .type = NLA_U32 }, [IFLA_VTI_FWMARK] = { .type = NLA_U32 }, + [IFLA_VTI_FLAGS] = { .type = NLA_U16 }, }; static struct rtnl_link_ops vti6_link_ops __read_mostly = { -- 2.15.1.620.gb9897f4670-goog