There's currently nothing preventing directing packets with IPv6
encapsulation data to IPv4 tunnels (and vice versa). If this happens,
IPv6 addresses are incorrectly interpreted as IPv4 ones.
Track whether the given ip_tunnel_key contains IPv4 or IPv6 data. Store this
in ip_tunnel_info. Reject packets at appropriate places if they are supposed
to be encapsulated into an incompatible protocol.
Signed-off-by: Jiri Benc <jbenc@redhat.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Acked-by: Thomas Graf <tgraf@suug.ch>
Acked-by: Pravin B Shelar <pshelar@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
netdev_dbg(dev, "no tunnel metadata\n");
goto tx_error;
}
netdev_dbg(dev, "no tunnel metadata\n");
goto tx_error;
}
+ if (info && ip_tunnel_info_af(info) != AF_INET)
+ goto tx_error;
}
rt = geneve_get_rt(skb, dev, &fl4, info);
}
rt = geneve_get_rt(skb, dev, &fl4, info);
+ if (family != ip_tunnel_info_af(info))
+ goto drop;
dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
vni = be64_to_cpu(info->key.tun_id);
dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
vni = be64_to_cpu(info->key.tun_id);
info->key.u.ipv6.dst = ip6h->daddr;
info->key.tos = ipv6_get_dsfield(ip6h);
info->key.ttl = ip6h->hop_limit;
info->key.u.ipv6.dst = ip6h->daddr;
info->key.tos = ipv6_get_dsfield(ip6h);
info->key.ttl = ip6h->hop_limit;
+ info->mode = IP_TUNNEL_INFO_IPV6;
#include <linux/if_tunnel.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_tunnel.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
+#include <linux/socket.h>
#include <linux/types.h>
#include <linux/u64_stats_sync.h>
#include <net/dsfield.h>
#include <linux/types.h>
#include <linux/u64_stats_sync.h>
#include <net/dsfield.h>
/* Flags for ip_tunnel_info mode. */
#define IP_TUNNEL_INFO_TX 0x01 /* represents tx tunnel parameters */
/* Flags for ip_tunnel_info mode. */
#define IP_TUNNEL_INFO_TX 0x01 /* represents tx tunnel parameters */
+#define IP_TUNNEL_INFO_IPV6 0x02 /* key contains IPv6 addresses */
struct ip_tunnel_info {
struct ip_tunnel_key key;
struct ip_tunnel_info {
struct ip_tunnel_key key;
tun_info->options = opts;
tun_info->options_len = opts_len;
tun_info->options = opts;
tun_info->options_len = opts_len;
}
static inline void ip_tunnel_info_init(struct ip_tunnel_info *tun_info,
}
static inline void ip_tunnel_info_init(struct ip_tunnel_info *tun_info,
tun_id, tun_flags, opts, opts_len);
}
tun_id, tun_flags, opts, opts_len);
}
+static inline unsigned short ip_tunnel_info_af(const struct ip_tunnel_info
+ *tun_info)
+{
+ return tun_info->mode & IP_TUNNEL_INFO_IPV6 ? AF_INET6 : AF_INET;
+}
+
#ifdef CONFIG_INET
int ip_tunnel_init(struct net_device *dev);
#ifdef CONFIG_INET
int ip_tunnel_init(struct net_device *dev);
if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags || !info))
return -EINVAL;
if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags || !info))
return -EINVAL;
+ if (ip_tunnel_info_af(info) != AF_INET)
+ return -EINVAL;
to->tunnel_id = be64_to_cpu(info->key.tun_id);
to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
to->tunnel_id = be64_to_cpu(info->key.tun_id);
to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
int err;
tun_info = skb_tunnel_info(skb);
int err;
tun_info = skb_tunnel_info(skb);
- if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX)))
+ if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) ||
+ ip_tunnel_info_af(tun_info) != AF_INET))
goto err_free_skb;
key = &tun_info->key;
goto err_free_skb;
key = &tun_info->key;
if (tb[LWTUNNEL_IP6_FLAGS])
tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP6_FLAGS]);
if (tb[LWTUNNEL_IP6_FLAGS])
tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP6_FLAGS]);
- tun_info->mode = IP_TUNNEL_INFO_TX;
+ tun_info->mode = IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_IPV6;
tun_info->options = NULL;
tun_info->options_len = 0;
tun_info->options = NULL;
tun_info->options_len = 0;
{
/* Extract metadata from packet. */
if (tun_info) {
{
/* Extract metadata from packet. */
if (tun_info) {
+ if (ip_tunnel_info_af(tun_info) != AF_INET)
+ return -EINVAL;
memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key));
if (tun_info->options) {
memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key));
if (tun_info->options) {
if (unlikely(!tun_info))
return -EINVAL;
if (unlikely(!tun_info))
return -EINVAL;
+ if (ip_tunnel_info_af(tun_info) != AF_INET)
+ return -EINVAL;
tun_key = &tun_info->key;
tun_key = &tun_info->key;