[ovs-dev] [PATCH 1/1] userspace: Add IPv6 extension header parsing for tunnels

Eelco Chaudron echaudro at redhat.com
Thu Jul 6 12:48:47 UTC 2017


While OVS userspace datapath (OVS-DPDK) supports GREv6, it does not
inter-operate with a native Linux ip6gretap tunnel. This is because
the Linux driver uses IPv6 optional headers for the Tunnel
Encapsulation Limit (rfc 2473, section 6.6).

OVS userspace simply does not parse these IPv6 extension headers
inside netdev_tnl_ip_extract_tnl_md(), as such popping the tunnel
leaves extra bytes resulting in a mangled decapsulated frame.

The change below will parse the IPv6 "next header" chain, and return
the offset to the upper layer protocol.

Signed-off-by: Eelco Chaudron <echaudro at redhat.com>
---
 lib/netdev-native-tnl.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 90 insertions(+), 1 deletion(-)

diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 7f3cf98..9a97b7f 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -57,6 +57,80 @@ static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 uint16_t tnl_udp_port_min = 32768;
 uint16_t tnl_udp_port_max = 61000;
 
+static int
+netdev_tnl_ip6_get_upperlayer_offset(struct ovs_16aligned_ip6_hdr *ip6)
+{
+    uint8_t  ext = ip6->ip6_nxt;
+    int      ext_hdr_offset = IPV6_HEADER_LEN;
+    uint16_t bytes_left = ntohs(ip6->ip6_plen);
+
+    while (true) {
+        size_t          ext_size = 0;
+        struct ip6_ext *ext_hdr = (struct ip6_ext *) ((uint8_t *)ip6 + \
+                                                      ext_hdr_offset);
+
+        if (bytes_left < 8) {
+            /*
+             * We need at least 8 bytes of data, which is the minimal extension
+             * header length. The upper layer headers are also minimal 8 bytes.
+             */
+            break;
+        }
+
+        switch (ext) {
+        case IPPROTO_TCP:
+        case IPPROTO_UDP:
+        case IPPROTO_GRE:
+            /*
+             * If its any of the upper layer protocols we support for tunnels
+             * return the offset.
+             */
+            return ext_hdr_offset;
+
+        case IPPROTO_HOPOPTS:
+        case IPPROTO_ROUTING:
+        case IPPROTO_DSTOPTS:
+            /*
+             * Silently ignore these extensions, and their options.
+             */
+            ext_size = (ext_hdr->ip6e_len + 1) * 8;
+            if (bytes_left < ext_size) {
+                return 0;
+            }
+            break;
+
+        case IPPROTO_FRAGMENT:
+            /*
+             * Currently IPv6 reassembly is not supported, so do not process
+             * fragmented packets.
+             */
+            return 0;
+
+        case IPPROTO_AH:
+            /*
+             * Currently authentication is not supported, so do not process
+             * the packet further.
+             */
+            return 0;
+
+        default:
+            /*
+             * Drop all packets with an unsupported transport layer,
+             * or unknown extension header.
+             */
+            return 0;
+        }
+
+        ext = ext_hdr->ip6e_nxt;
+        bytes_left -= ext_size;
+        ext_hdr_offset += ext_size;
+    }
+    /*
+     * If we end up here the packet is invalid, so drop it...
+     */
+    return 0;
+}
+
 void *
 netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
                   unsigned int *hlen)
@@ -115,6 +189,7 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
 
     } else if (IP_VER(ip->ip_ihl_ver) == 6) {
         ovs_be32 tc_flow = get_16aligned_be32(&ip6->ip6_flow);
+        unsigned int upper_layer_offset = 0;
 
         memcpy(tnl->ipv6_src.s6_addr, ip6->ip6_src.be16, sizeof ip6->ip6_src);
         memcpy(tnl->ipv6_dst.s6_addr, ip6->ip6_dst.be16, sizeof ip6->ip6_dst);
@@ -122,7 +197,21 @@ netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
         tnl->ip_tos = ntohl(tc_flow) >> 20;
         tnl->ip_ttl = ip6->ip6_hlim;
 
-        *hlen += IPV6_HEADER_LEN;
+        if ((l3_size - IPV6_HEADER_LEN) >= ntohs(ip6->ip6_plen)) {
+            /*
+             * Make sure the remaining buffer is big enough to contain
+             * the entire payload.
+             */
+            upper_layer_offset = netdev_tnl_ip6_get_upperlayer_offset(ip6);
+        }
+
+        if (upper_layer_offset <= 0) {
+            VLOG_WARN_RL(&err_rl,
+                         "ipv6 packet has unsupported extension headers");
+            return NULL;
+        }
+
+        *hlen += upper_layer_offset;
 
     } else {
         VLOG_WARN_RL(&err_rl, "ipv4 packet has invalid version (%d)",
-- 
2.7.5



More information about the dev mailing list