tunnels: fix incorrect IPv4/v6 headers interpretation
Subsystems (6)
v2 cover 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
Jiri Benc (4):
ip_tunnels: convert the mode field of ip_tunnel_info to flags
ip_tunnels: record IP version in tunnel info
fou: reject IPv6 config
vxlan: do not receive IPv4 packets on IPv6 socket
drivers/net/geneve.c | 4 +++- drivers/net/vxlan.c | 5 ++++- include/net/dst_metadata.h | 2 +- include/net/ip_tunnels.h | 19 ++++++++++++------- include/net/udp_tunnel.h | 3 ++- net/core/filter.c | 2 ++ net/ipv4/fou.c | 2 +- net/ipv4/ip_gre.c | 3 ++- net/ipv4/ip_tunnel_core.c | 2 +- net/ipv4/route.c | 2 +- net/ipv6/ip6_udp_tunnel.c | 9 +++++++++ net/ipv6/route.c | 2 +- net/openvswitch/flow.c | 2 ++ net/openvswitch/vport.c | 2 ++ 14 files changed, 43 insertions(+), 16 deletions(-)
Signature · 1 line
1.8.3.1
PATCH v2 1/4 ip_tunnels: convert the mode field of ip_tunnel_info to flags
The mode field holds a single bit of information only (whether the ip_tunnel_info struct is for rx or tx). Change the mode field to bit flags. This allows more mode flags to be added.
---
v2: converted geneve fully, too
--- drivers/net/geneve.c | 2 +- drivers/net/vxlan.c | 2 +- include/net/dst_metadata.h | 1 - include/net/ip_tunnels.h | 9 ++------- net/ipv4/ip_gre.c | 2 +- net/ipv4/route.c | 2 +- net/ipv6/route.c | 2 +- 7 files changed, 7 insertions(+), 13 deletions(-)
drivers/net/geneve.c +1 -1
@@ -623,7 +623,7 @@ 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; }
drivers/net/vxlan.c +1 -1
@@ -2113,7 +2113,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 ; }
include/net/dst_metadata.h +0 -1
@@ -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 ;
include/net/ip_tunnels.h +2 -7
@@ -50,13 +50,8 @@ 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 */ struct ip_tunnel_info {struct ip_tunnel_key key;
net/ipv4/ip_gre.c +1 -1
@@ -511,7 +511,7 @@ 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 ))) goto err_free_skb; key = &tun_info->key;
net/ipv4/route.c +1 -1
@@ -1693,7 +1693,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 ;
net/ipv6/route.c +1 -1
@@ -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));
Signature · 1 line
1.8.3.1
PATCH v2 2/4 ip_tunnels: record IP version in tunnel info
In reply to Jiri BencThere'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.
---
v2: moved the af check in geneve before the first usage of tunnel info data
--- drivers/net/geneve.c | 2 ++ drivers/net/vxlan.c | 2 ++ include/net/dst_metadata.h | 1 + include/net/ip_tunnels.h | 10 ++++++++++ net/core/filter.c | 2 ++ net/ipv4/ip_gre.c | 3 ++- net/ipv4/ip_tunnel_core.c | 2 +- net/openvswitch/flow.c | 2 ++ net/openvswitch/vport.c | 2 ++ 9 files changed, 24 insertions(+), 2 deletions(-)
drivers/net/geneve.c +2 -0
@@ -627,6 +627,8 @@ static netdev_tx_t geneve_xmit(struct sk_buff *skb, struct net_device *dev)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);
drivers/net/vxlan.c +2 -0
@@ -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);
include/net/dst_metadata.h +1 -0
@@ -105,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; }
include/net/ip_tunnels.h +10 -0
@@ -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>
@@ -52,6 +53,7 @@ struct ip_tunnel_key {/* 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;
@@ -208,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,
@@ -221,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);
net/core/filter.c +2 -0
@@ -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);
net/ipv4/ip_gre.c +2 -1
@@ -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;
net/ipv4/ip_tunnel_core.c +1 -1
@@ -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 ;
net/openvswitch/flow.c +2 -0
@@ -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) {
net/openvswitch/vport.c +2 -0
@@ -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;
Signature · 1 line
1.8.3.1
PATCH v2 3/4 fou: reject IPv6 config
In reply to Jiri Bencfou does not really support IPv6 encapsulation. After an UDP socket is created in fou_create, the encap_rcv callback is set either to fou_udp_recv or to gue_udp_recv. Both of those unconditionally assume that the received packet has an IPv4 header and access the data at network_header as it was an IPv4 header. This leads to IPv6 flow label being interpreted as IP packet length, etc.
Disallow fou tunnel to be configured as IPv6 until real IPv6 support is added to fou.
---
v2: no change
--- net/ipv4/fou.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/ipv4/fou.c +1 -1
@@ -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;
Signature · 1 line
1.8.3.1
PATCH v2 4/4 vxlan: do not receive IPv4 packets on IPv6 socket
In reply to Jiri BencBy default (subject to the sysctl settings), IPv6 sockets listen also for IPv4 traffic. Vxlan is not prepared for that and expects IPv6 header in packets received through an IPv6 socket.
In addition, it's currently not possible to have both IPv4 and IPv6 vxlan tunnel on the same port (unless bindv6only sysctl is enabled), as it's not possible to create and bind both IPv4 and IPv6 vxlan interfaces and there's no way to specify both IPv4 and IPv6 remote/group IP addresses.
Set IPV6_V6ONLY on vxlan sockets to fix both of these issues. This is not done globally in udp_tunnel, as l2tp and tipc seems to work okay when receiving IPv4 packets on IPv6 socket and people may rely on this behavior. The other tunnels (geneve and fou) do not support IPv6.
---
v2: no change
--- drivers/net/vxlan.c | 1 + include/net/udp_tunnel.h | 3 ++- net/ipv6/ip6_udp_tunnel.c | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-)
drivers/net/vxlan.c +1 -0
@@ -2530,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 ; }
include/net/udp_tunnel.h +2 -1
@@ -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,
net/ipv6/ip6_udp_tunnel.c +9 -0
@@ -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));
Signature · 1 line
1.8.3.1
Re: [PATCH v2 net-next 1/4] ip_tunnels: convert the mode field of ip_tunnel_info to flags
In reply to Jiri BencQuoted 6 lines · Jiri Benc
The mode field holds a single bit of information only (whether the
ip_tunnel_infostruct is for rx or tx). Change the mode field to bit flags. This allows more mode flags to be added.Signed-off-by Jiri Benc <[email protected]>
Re: [PATCH v2 net-next 2/4] ip_tunnels: record IP version in tunnel info
In reply to Jiri Bencnet/openvswitch/vport.c int ovs_tunnel_get_egress_info(struct ip_tunnel_info *egress_tun_info, · line 593
Quoted 12 lines · Jiri Benc
net/openvswitch/vport.c +2
-0@@ -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;
Are you sure this chunk is needed? It looks like OVS_CB()->egress_tun_info can only be set via execute_set_action() which can only be of a supported AF type.
Otherwise this looks great.
Re: [PATCH v2 net-next 4/4] vxlan: do not receive IPv4 packets on IPv6 socket
In reply to Jiri BencQuoted 9 lines · Jiri Benc
By default (subject to the sysctl settings), IPv6 sockets listen also for IPv4 traffic. Vxlan is not prepared for that and expects IPv6 header in packets received through an IPv6 socket.
In addition, it's currently not possible to have both IPv4 and IPv6 vxlan tunnel on the same port (unless bindv6only sysctl is enabled), as it's not possible to create and bind both IPv4 and IPv6 vxlan interfaces and there's no way to specify both IPv4 and IPv6 remote/group IP addresses.
Quoted 6 lines
Set
IPV6_V6ONLYon vxlan sockets to fix both of these issues. This is not done globally inudp_tunnel, as l2tp and tipc seems to work okay when receiving IPv4 packets on IPv6 socket and people may rely on this behavior. The other tunnels (geneve and fou) do not support IPv6.Signed-off-by Jiri Benc <[email protected]>
Can we fix VXLAN instead? It should be possible to receive and send both v4 and v6 frames and represent it with metadata on a single socket.
Maybe set the flag if IFLA_VXLAN_COLLECT_METADATA is not set?
Re: [PATCH v2 net-next 1/4] ip_tunnels: convert the mode field of ip_tunnel_info to flags
In reply to Jiri BencQuoted 9 lines · Jiri Benc
The mode field holds a single bit of information only (whether the
ip_tunnel_infostruct is for rx or tx). Change the mode field to bit flags. This allows more mode flags to be added.Signed-off-by Jiri Benc <[email protected]>---
v2: converted geneve fully, too
---
Re: [PATCH v2 net-next 2/4] ip_tunnels: record IP version in tunnel info
In reply to Jiri BencQuoted 8 lines · Jiri Benc
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_keycontains IPv4 or IPv6 data. Store this inip_tunnel_info. Reject packets at appropriate places if they are supposed to be encapsulated into an incompatible protocol.
Quoted 5 lines
Signed-off-by Jiri Benc <[email protected]>---
v2: moved the af check in geneve before the first usage of tunnel info data
---
Re: [PATCH v2 net-next 2/4] ip_tunnels: record IP version in tunnel info
In reply to Thomas GrafQuoted 12 lines · Thomas Graf
Quoted 11 lines · Jiri Benc
net/openvswitch/vport.c +2
-0@@ -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 ;
Quoted 5 lines
net/openvswitch/vport.c int ovs_tunnel_get_egress_info(struct ip_tunnel_info *egress_tun_info, · line 593
Quoted 1 line
net/openvswitch/vport.c +0
-0tun_key = &tun_info->key;Are you sure this chunk is needed? It looks like OVS_CB()->egress_tun_info can only be set via
execute_set_action()which can only be of a supported AF type.
It does no harm and I'm going to add IPv6 tunneling support to ovs soon. This is a nice reminder that this function is IPv4 only and needs to be extended.
Quoted 3 lines
Otherwise this looks great.
Acked-by Thomas Graf <[email protected]>
Thanks!
Jiri
Signature · 1 line
Jiri Benc
Re: [PATCH v2 net-next 4/4] vxlan: do not receive IPv4 packets on IPv6 socket
In reply to Thomas GrafQuoted 2 lines · Thomas Graf
Can we fix VXLAN instead? It should be possible to receive and send both v4 and v6 frames and represent it with metadata on a single socket.
That's actually what I intend to do next. But I need the flag and this patch for that. If IPv6 is enabled, I'll open two sockets within a single vxlan_sock for metadata based vxlan, IPv4 one and IPv6 one. It would be easier to just open an IPv6 socket and receive both IPv4 and IPv6 traffic through it but unfortunately, this depends on bindv6only sysctl. We don't want to have IPv4 vxlan broken if bindv6only is set. Therefore, we need two sockets, and the IPv6 one needs to be v6only.
Jiri
Signature · 1 line
Jiri Benc
David Miller
maintainerFrom: Jiri Benc <[email protected]>
Date: Fri, 28 Aug 2015 20:48:18 +0200
Quoted 5 lines
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.
Quoted 8 lines
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
Series applied, thanks Jiri.
Re: [PATCH v2 net-next 4/4] vxlan: do not receive IPv4 packets on IPv6 socket
In reply to Jiri BencQuoted 11 lines · Jiri Benc
Quoted 2 lines · Thomas Graf
Can we fix VXLAN instead? It should be possible to receive and send both v4 and v6 frames and represent it with metadata on a single socket.
That's actually what I intend to do next. But I need the flag and this patch for that. If IPv6 is enabled, I'll open two sockets within a single
vxlan_sockfor metadata based vxlan, IPv4 one and IPv6 one. It would be easier to just open an IPv6 socket and receive both IPv4 and IPv6 traffic through it but unfortunately, this depends on bindv6only sysctl. We don't want to have IPv4 vxlan broken if bindv6only is set. Therefore, we need two sockets, and the IPv6 one needs to be v6only.
Awesome, sounds great.