Merge branch 'tnl-ipv4-ipv6'
authorDavid S. Miller <davem@davemloft.net>
Sat, 29 Aug 2015 20:07:55 +0000 (13:07 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 29 Aug 2015 20:07:55 +0000 (13:07 -0700)
Jiri Benc says:

====================
tunnels: fix incorrect IPv4/v6 headers interpretation

With tunneling, it is currently possible to get an IPv6 header and interpret
it as an IPv4 header, or to interpret an IPv6 address as an IPv4 address
(and vice versa). This leads to things like sending packets to incorrect
address, IPv6 flow label being interpreted as IP packet length, etc.

Fix several places where this can happen.

Most of this is net-next only. The third patch affects net, too, but it
doesn't seem there's anything in user space that sets the attribute at all
currently, thus net-next is fine.

Changelog:
v2: fixed geneve after incorrect rebase on top of Pravin's patches
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
14 files changed:
drivers/net/geneve.c
drivers/net/vxlan.c
include/net/dst_metadata.h
include/net/ip_tunnels.h
include/net/udp_tunnel.h
net/core/filter.c
net/ipv4/fou.c
net/ipv4/ip_gre.c
net/ipv4/ip_tunnel_core.c
net/ipv4/route.c
net/ipv6/ip6_udp_tunnel.c
net/ipv6/route.c
net/openvswitch/flow.c
net/openvswitch/vport.c

index 4357bae732d738f275788dde25cdc72a9ae0b622..3908a22f23d187cfbd4e87e376b93181b85efb06 100644 (file)
@@ -623,10 +623,12 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)
 
        if (geneve->collect_md) {
                info = skb_tunnel_info(skb);
-               if (unlikely(info && info->mode != IP_TUNNEL_INFO_TX)) {
+               if (unlikely(info && !(info->mode & IP_TUNNEL_INFO_TX))) {
                        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);
index 30e56cb5888495df97b90844a0a7281713f0d56e..6c5269aea5447b22f9cc69477a75048127285a36 100644 (file)
@@ -1903,6 +1903,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
                                  dev->name);
                        goto drop;
                }
+               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);
@@ -2113,7 +2115,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        if (vxlan->flags & VXLAN_F_COLLECT_METADATA &&
-           info && info->mode == IP_TUNNEL_INFO_TX) {
+           info && info->mode & IP_TUNNEL_INFO_TX) {
                vxlan_xmit_one(skb, dev, NULL, false);
                return NETDEV_TX_OK;
        }
@@ -2528,6 +2530,7 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
                udp_conf.family = AF_INET6;
                udp_conf.use_udp6_rx_checksums =
                    !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX);
+               udp_conf.ipv6_v6only = 1;
        } else {
                udp_conf.family = AF_INET;
        }
index 60c03326c0876da848a31b8d45ac0bb0dfc10f4f..d32f49cc621d22935572e15b6a68a9958878d2ea 100644 (file)
@@ -59,7 +59,6 @@ static inline struct metadata_dst *tun_rx_dst(__be16 flags,
                return NULL;
 
        info = &tun_dst->u.tun_info;
-       info->mode = IP_TUNNEL_INFO_RX;
        info->key.tun_flags = flags;
        info->key.tun_id = tunnel_id;
        info->key.tp_src = 0;
@@ -106,6 +105,7 @@ static inline struct metadata_dst *ipv6_tun_rx_dst(struct sk_buff *skb,
        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;
        return tun_dst;
 }
 
index 224e4ecec91b71eb626e8f37e3785e6ded52c92c..2b4fa06e91bdee117f78d87a782e6024570bd296 100644 (file)
@@ -4,6 +4,7 @@
 #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>
@@ -50,13 +51,9 @@ struct ip_tunnel_key {
        __be16                  tp_dst;
 };
 
-/* Indicates whether the tunnel info structure represents receive
- * or transmit tunnel parameters.
- */
-enum {
-       IP_TUNNEL_INFO_RX,
-       IP_TUNNEL_INFO_TX,
-};
+/* 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;
@@ -213,6 +210,8 @@ static inline void __ip_tunnel_info_init(struct ip_tunnel_info *tun_info,
 
        tun_info->options = opts;
        tun_info->options_len = opts_len;
+
+       tun_info->mode = 0;
 }
 
 static inline void ip_tunnel_info_init(struct ip_tunnel_info *tun_info,
@@ -226,6 +225,12 @@ static inline void ip_tunnel_info_init(struct ip_tunnel_info *tun_info,
                              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);
index 35041d0fc21ededc45dd341449771b821d399da9..cb2f89f20f5c95d4566745cf02aa97d064fd1099 100644 (file)
@@ -31,7 +31,8 @@ struct udp_port_cfg {
        __be16                  peer_udp_port;
        unsigned int            use_udp_checksums:1,
                                use_udp6_tx_checksums:1,
-                               use_udp6_rx_checksums:1;
+                               use_udp6_rx_checksums:1,
+                               ipv6_v6only:1;
 };
 
 int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
index 66500d490995190ab8c890cf1ca2eaa0ef78a8e6..13079f03902e7674b7cd71bf01ca1d12844c758b 100644 (file)
@@ -1493,6 +1493,8 @@ static u64 bpf_skb_get_tunnel_key(u64 r1, u64 r2, u64 size, u64 flags, u64 r5)
 
        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);
index 2d1646cff0572054cc2982b764d817094c002146..e0fcbbbcfe54d0b35b46597b5f217075ffbc745c 100644 (file)
@@ -566,7 +566,7 @@ static int parse_nl_config(struct genl_info *info,
        if (info->attrs[FOU_ATTR_AF]) {
                u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
 
-               if (family != AF_INET && family != AF_INET6)
+               if (family != AF_INET)
                        return -EINVAL;
 
                cfg->udp_config.family = family;
index faf1cde6f8da2486b5f6dbd955b8f0196e1af9f1..bd0679d90519b170dc98369e9b438e4c31b152b9 100644 (file)
@@ -511,7 +511,8 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev)
        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;
index 934f2ac8ad610838b8a6611a4fecac5849f3afef..0c756ade1cf715ac74b593714597a8aa4401ea6e 100644 (file)
@@ -356,7 +356,7 @@ static int ip6_tun_build_state(struct net_device *dev, struct nlattr *attr,
        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;
 
index 6b91879e9cbe5e574ff562306472111a2fb66377..5f4a5565ad8b32ef7d10619364a86713d52f38e0 100644 (file)
@@ -1696,7 +1696,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
         */
 
        tun_info = skb_tunnel_info(skb);
-       if (tun_info && tun_info->mode == IP_TUNNEL_INFO_RX)
+       if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
                fl4.flowi4_tun_key.tun_id = tun_info->key.tun_id;
        else
                fl4.flowi4_tun_key.tun_id = 0;
index e1a1136bda7c8f15e589c92e7ea752ddceee70f4..14dacf1df529d9602cc497976db0868f68d9fbb7 100644 (file)
@@ -23,6 +23,15 @@ int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
        if (err < 0)
                goto error;
 
+       if (cfg->ipv6_v6only) {
+               int val = 1;
+
+               err = kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+                                       (char *) &val, sizeof(val));
+               if (err < 0)
+                       goto error;
+       }
+
        udp6_addr.sin6_family = AF_INET6;
        memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
               sizeof(udp6_addr.sin6_addr));
index df3e353a012d081699df247482c9932a9f69ee9c..308dd5f9158f17bd140a413d5fd63d6d99b25d16 100644 (file)
@@ -1174,7 +1174,7 @@ void ip6_route_input(struct sk_buff *skb)
        };
 
        tun_info = skb_tunnel_info(skb);
-       if (tun_info && tun_info->mode == IP_TUNNEL_INFO_RX)
+       if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
                fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
        skb_dst_drop(skb);
        skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
index 5a3195e538ce588ca6889bce445f61d02cf59894..9760dc43bdb99815dc4af7dd8e2c1a34a184a27b 100644 (file)
@@ -688,6 +688,8 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *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) {
index e2dc9dac59e689bd0edbb9ddc7f0d6932f97da7e..40164037928e7e8a28a98a9ca7f48c51cdefe343 100644 (file)
@@ -587,6 +587,8 @@ int ovs_tunnel_get_egress_info(struct ip_tunnel_info *egress_tun_info,
 
        if (unlikely(!tun_info))
                return -EINVAL;
+       if (ip_tunnel_info_af(tun_info) != AF_INET)
+               return -EINVAL;
 
        tun_key = &tun_info->key;