summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-08-29 20:07:55 (GMT)
committerDavid S. Miller <davem@davemloft.net>2015-08-29 20:07:55 (GMT)
commit6d74232410a9f3ed0e1fc0f5e538598f21e93982 (patch)
tree388c9e94c26bdd40875562a58b84a54213ca7ac7
parentf6d3c19274c74ff17174df8078e0a14df003667f (diff)
parenta43a9ef6a2e510fec61176ff2c34fab3e7d581da (diff)
downloadlinux-6d74232410a9f3ed0e1fc0f5e538598f21e93982.tar.xz
Merge branch 'tnl-ipv4-ipv6'
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>
-rw-r--r--drivers/net/geneve.c4
-rw-r--r--drivers/net/vxlan.c5
-rw-r--r--include/net/dst_metadata.h2
-rw-r--r--include/net/ip_tunnels.h19
-rw-r--r--include/net/udp_tunnel.h3
-rw-r--r--net/core/filter.c2
-rw-r--r--net/ipv4/fou.c2
-rw-r--r--net/ipv4/ip_gre.c3
-rw-r--r--net/ipv4/ip_tunnel_core.c2
-rw-r--r--net/ipv4/route.c2
-rw-r--r--net/ipv6/ip6_udp_tunnel.c9
-rw-r--r--net/ipv6/route.c2
-rw-r--r--net/openvswitch/flow.c2
-rw-r--r--net/openvswitch/vport.c2
14 files changed, 43 insertions, 16 deletions
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 4357bae..3908a22 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -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);
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 30e56cb..6c5269a 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -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;
}
diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h
index 60c0332..d32f49c 100644
--- a/include/net/dst_metadata.h
+++ b/include/net/dst_metadata.h
@@ -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;
}
diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 224e4ec..2b4fa06 100644
--- a/include/net/ip_tunnels.h
+++ b/include/net/ip_tunnels.h
@@ -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);
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 35041d0..cb2f89f 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -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,
diff --git a/net/core/filter.c b/net/core/filter.c
index 66500d4..13079f0 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -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);
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 2d1646c..e0fcbbb 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -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;
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index faf1cde..bd0679d 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -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;
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 934f2ac..0c756ad 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -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;
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 6b91879..5f4a556 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -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;
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index e1a1136..14dacf1 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -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));
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index df3e353..308dd5f 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -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));
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 5a3195e..9760dc4 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -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) {
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index e2dc9da..4016403 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -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;