Hi Eelco.

Sorry for the delayed review.

On Thu, Jul 06, 2017 at 02:48:47PM +0200, Eelco Chaudron wrote:
> 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 <[email protected]>
> ---
>  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;
> +}
> +

I found parse_ipv6_ext_hdrs() in lib/flow.c. It seems like we should be
able to make use of that. It'd be nice if this was common between
tunnels and normal IPv6.

>  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
> 
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to