[ovs-dev] [PATCH v2 11/17] userspace: add non-tap (l3) support to GRE vports

Yang, Yi Y yi.y.yang at intel.com
Tue Jan 3 01:10:47 UTC 2017


Jan, which patch is the final solution you mentioned? Has It been merged into net-next? Or it isn't ready at all?  From my understanding, the final solution you mentioned will also wait for long time to merge, it is just to add packet_type match field, this won't have any big impact on current patchset from user perspective.

-----Original Message-----
From: Jan Scheurich [mailto:jan.scheurich at web.de] 
Sent: Friday, December 30, 2016 6:59 PM
To: Yang, Yi Y <yi.y.yang at intel.com>; dev at openvswitch.org
Cc: Simon Horman <simon.horman at netronome.com>; Jiri Benc <jbenc at redhat.com>
Subject: Re: [ovs-dev] [PATCH v2 11/17] userspace: add non-tap (l3) support to GRE vports

This patch is not in line with the ongoing work to support L3 tunnels on legacy (non packet type-aware) OVS bridges as specified in https://docs.google.com/document/d/1oWMYUH8sjZJzWa72o2q9kU0N6pNE-rwZcLH3-kbbDR8/edit?usp=sharing

To avoid extensive rework, we suggest to replace the patch with the final solution based on explicit packet_type fields in dp_packet and struct flow.

Regards, Jan

On 2016-12-28 13:26, Yi Yang wrote:
> Add support for layer 3 GRE vports (non-tap aka non-VTEP).
>
> This makes use of a vport mode configuration for the existing 
> (tap/VTEP) GRE vports.
>
> In order to differentiate packets for two different types of GRE 
> vports a new flow key attribute, OVS_KEY_ATTR_NEXT_BASE_LAYER, is 
> used.  It is intended that this attribute is only used in userspace as 
> there appears to be no need for it to be used in the kernel datapath.
>
> It is envisaged that this attribute may be used for other 
> encapsulation protocols that support both layer3 and layer2 inner-packets.
>
> Signed-off-by: Simon Horman <simon.horman at netronome.com>
> Signed-off-by: Jiri Benc <jbenc at redhat.com>
> Signed-off-by: Yi Yang <yi.y.yang at intel.com>
> ---
>   datapath/linux/compat/include/linux/openvswitch.h |  3 ++
>   include/openvswitch/flow.h                        | 12 ++++--
>   lib/flow.c                                        | 34 ++++++++++++----
>   lib/match.c                                       |  6 ++-
>   lib/netdev-linux.c                                |  3 +-
>   lib/netdev-native-tnl.c                           | 26 +++++++++---
>   lib/netdev-vport.c                                | 22 ++++++++--
>   lib/netdev.h                                      |  1 +
>   lib/nx-match.c                                    |  2 +-
>   lib/odp-execute.c                                 |  2 +
>   lib/odp-util.c                                    | 22 ++++++++++
>   lib/odp-util.h                                    |  4 +-
>   lib/ofp-util.c                                    |  2 +-
>   lib/tnl-ports.c                                   | 49 +++++++++++++++++------
>   lib/tnl-ports.h                                   |  3 +-
>   ofproto/ofproto-dpif-rid.h                        |  2 +-
>   ofproto/ofproto-dpif-sflow.c                      |  1 +
>   ofproto/ofproto-dpif-xlate.c                      |  2 +-
>   ofproto/ofproto-dpif.c                            |  2 +
>   ofproto/tunnel.c                                  |  4 +-
>   tests/tunnel-push-pop-ipv6.at                     | 12 ++++--
>   tests/tunnel-push-pop.at                          | 26 ++++++++++--
>   vswitchd/vswitch.xml                              | 13 ++++++
>   23 files changed, 202 insertions(+), 51 deletions(-)
>
> diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
> b/datapath/linux/compat/include/linux/openvswitch.h
> index af4ee5c..e477d35 100644
> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -360,6 +360,9 @@ enum ovs_key_attr {
>   #ifdef __KERNEL__
>   	/* Only used within kernel data path. */
>   	OVS_KEY_ATTR_TUNNEL_INFO,  /* struct ovs_tunnel_info */
> +#else
> +	/* Only used within user-space data path. */
> +	OVS_KEY_ATTR_NEXT_BASE_LAYER, /* base layer of encapsulated packet 
> +*/
>   #endif
>   	__OVS_KEY_ATTR_MAX
>   };
> diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h 
> index 93ed37e..46ef87e 100644
> --- a/include/openvswitch/flow.h
> +++ b/include/openvswitch/flow.h
> @@ -23,7 +23,7 @@
>   /* This sequence number should be incremented whenever anything involving flows
>    * or the wildcarding of flows changes.  This will cause build assertion
>    * failures in places which likely need to be updated. */ -#define 
> FLOW_WC_SEQ 37
> +#define FLOW_WC_SEQ 38
>   
>   /* Number of Open vSwitch extension 32-bit registers. */
>   #define FLOW_N_REGS 16
> @@ -138,6 +138,10 @@ struct flow {
>       ovs_be16 tp_dst;            /* TCP/UDP/SCTP destination port/ICMP code. */
>       ovs_be32 igmp_group_ip4;    /* IGMP group IPv4 address.
>                                    * Keep last for BUILD_ASSERT_DECL 
> below. */
> +
> +    uint8_t next_base_layer;    /* Fields of encapsulated packet, if any,
> +                                 * start at this layer */
> +    uint8_t pad4[7];
>   };
>   BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0);
>   BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0); 
> @@ -145,9 +149,9 @@ BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
>   #define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t))
>   
>   /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */ 
> -BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
> -                  == sizeof(struct flow_tnl) + 256
> -                  && FLOW_WC_SEQ == 37);
> +BUILD_ASSERT_DECL(OFFSETOFEND(struct flow, pad4)
> +                  == sizeof(struct flow_tnl) + 264
> +                  && FLOW_WC_SEQ == 38);
>   
>   /* Incremental points at which flow classification may be performed in
>    * segments.
> diff --git a/lib/flow.c b/lib/flow.c
> index ac22d55..a65e154 100644
> --- a/lib/flow.c
> +++ b/lib/flow.c
> @@ -125,7 +125,7 @@ struct mf_ctx {
>    * away.  Some GCC versions gave warnings on ALWAYS_INLINE, so these are
>    * defined as macros. */
>   
> -#if (FLOW_WC_SEQ != 37)
> +#if (FLOW_WC_SEQ != 38)
>   #define MINIFLOW_ASSERT(X) ovs_assert(X)
>   BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime "
>                  "assertions enabled. Consider updating FLOW_WC_SEQ after "
> @@ -846,6 +846,20 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
>                   miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
>                   miniflow_pad_to_64(mf, tp_dst);
>               }
> +        } else if (OVS_LIKELY(nw_proto == IPPROTO_GRE)) {
> +            if (OVS_LIKELY(size >= sizeof(struct gre_base_hdr))) {
> +                const struct gre_base_hdr *gre = data_pull(&data, &size,
> +                                                           sizeof *gre);
> +                if (gre->protocol == htons(ETH_TYPE_TEB)) {
> +                    /* No need to store a zero value for next_base_layer
> +                     * in the miniflow which would cost an extra word of
> +                     * storage. */
> +                    BUILD_ASSERT(LAYER_2 == 0);
> +                } else {
> +                    miniflow_push_uint8(mf, next_base_layer, LAYER_3);
> +                    miniflow_pad_to_64(mf, next_base_layer);
> +                }
> +            }
>           }
>       }
>    out:
> @@ -894,7 +908,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
>   {
>       int i;
>   
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       match_init_catchall(flow_metadata);
>       if (flow->tunnel.tun_id != htonll(0)) { @@ -1304,7 +1318,7 @@ 
> void flow_wildcards_init_for_packet(struct flow_wildcards *wc,
>       memset(&wc->masks, 0x0, sizeof wc->masks);
>   
>       /* Update this function whenever struct flow changes. */
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       if (flow_tnl_dst_is_set(&flow->tunnel)) {
>           if (flow->tunnel.flags & FLOW_TNL_F_KEY) { @@ -1425,7 
> +1439,7 @@ void
>   flow_wc_map(const struct flow *flow, struct flowmap *map)
>   {
>       /* Update this function whenever struct flow changes. */
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       flowmap_init(map);
>   
> @@ -1473,6 +1487,8 @@ flow_wc_map(const struct flow *flow, struct 
> flowmap *map)
>   
>           if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_IGMP)) {
>               FLOWMAP_SET(map, igmp_group_ip4);
> +        } else if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_GRE)) {
> +            FLOWMAP_SET(map, next_base_layer);
>           } else {
>               FLOWMAP_SET(map, tcp_flags);
>           }
> @@ -1491,6 +1507,8 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
>               FLOWMAP_SET(map, nd_target);
>               FLOWMAP_SET(map, arp_sha);
>               FLOWMAP_SET(map, arp_tha);
> +        } else if (OVS_UNLIKELY(flow->nw_proto == IPPROTO_GRE)) {
> +            FLOWMAP_SET(map, next_base_layer);
>           } else {
>               FLOWMAP_SET(map, tcp_flags);
>           }
> @@ -1512,7 +1530,7 @@ void
>   flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
>   {
>       /* Update this function whenever struct flow changes. */
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
>       memset(&wc->masks.regs, 0, sizeof wc->masks.regs); @@ -1657,7 
> +1675,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards *wc, int idx,
>   uint32_t
>   miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
>   {
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>       uint32_t hash = basis;
>   
>       if (flow) {
> @@ -1704,7 +1722,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst);
>   uint32_t
>   flow_hash_5tuple(const struct flow *flow, uint32_t basis)
>   {
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>       uint32_t hash = basis;
>   
>       if (flow) {
> @@ -2172,7 +2190,7 @@ flow_push_mpls(struct flow *flow, int n, 
> ovs_be16 mpls_eth_type,
>   
>           if (clear_flow_L3) {
>               /* Clear all L3 and L4 fields and dp_hash. */
> -            BUILD_ASSERT(FLOW_WC_SEQ == 37);
> +            BUILD_ASSERT(FLOW_WC_SEQ == 38);
>               memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
>                      sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
>               flow->dp_hash = 0;
> diff --git a/lib/match.c b/lib/match.c index c551e57..65d5b8e 100644
> --- a/lib/match.c
> +++ b/lib/match.c
> @@ -1082,7 +1082,7 @@ match_format(const struct match *match, struct 
> ds *s, int priority)
>   
>       int i;
>   
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       if (priority != OFP_DEFAULT_PRIORITY) {
>           ds_put_format(s, "%spriority=%s%d,", @@ -1358,6 +1358,10 @@ 
> match_format(const struct match *match, struct ds *s, int priority)
>                               TCP_FLAGS(OVS_BE16_MAX));
>       }
>   
> +    if (wc->masks.next_base_layer) {
> +        ds_put_format(s, "next_base_layer=%"PRIu8",", f->next_base_layer);
> +    }
> +
>       if (s->length > start_len) {
>           ds_chomp(s, ',');
>       }
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index 
> 30189b4..786e727 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -5507,7 +5507,8 @@ get_etheraddr(const char *netdev_name, struct eth_addr *ea)
>           return error;
>       }
>       hwaddr_family = ifr.ifr_hwaddr.sa_family;
> -    if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER) {
> +    if (hwaddr_family != AF_UNSPEC && hwaddr_family != ARPHRD_ETHER &&
> +        hwaddr_family != ARPHRD_NONE) {
>           VLOG_INFO("%s device has unknown hardware address family %d",
>                     netdev_name, hwaddr_family);
>           return EINVAL;
> diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index 
> ce2582f..bf52e9d 100644
> --- a/lib/netdev-native-tnl.c
> +++ b/lib/netdev-native-tnl.c
> @@ -153,6 +153,9 @@ netdev_tnl_push_ip_header(struct dp_packet 
> *packet,
>   
>       memcpy(eth, header, size);
>   
> +    dp_packet_reset_offsets(packet);
> +    packet->l3_ofs = sizeof (struct eth_header);
> +
>       if (netdev_tnl_is_header_ipv6(header)) {
>           ip6 = netdev_tnl_ipv6_hdr(eth);
>           *ip_tot_size -= IPV6_HEADER_LEN; @@ -350,10 +353,6 @@ 
> parse_gre_header(struct dp_packet *packet,
>           return -EINVAL;
>       }
>   
> -    if (greh->protocol != htons(ETH_TYPE_TEB)) {
> -        return -EINVAL;
> -    }
> -
>       hlen = ulen + gre_header_len(greh->flags);
>       if (hlen > dp_packet_size(packet)) {
>           return -EINVAL;
> @@ -383,6 +382,12 @@ parse_gre_header(struct dp_packet *packet,
>           options++;
>       }
>   
> +    if (greh->protocol == htons(ETH_TYPE_TEB)) {
> +        packet->md.packet_ethertype = htons(0);
> +    } else {
> +        packet->md.packet_ethertype = greh->protocol;
> +    }
> +
>       return hlen;
>   }
>   
> @@ -408,6 +413,12 @@ netdev_gre_pop_header(struct dp_packet *packet)
>   
>       dp_packet_reset_packet(packet, hlen);
>   
> +    if (eth_type_mpls(packet->md.packet_ethertype)) {
> +        packet->l2_5_ofs = 0;
> +    } else if (packet->md.packet_ethertype) {
> +        packet->l3_ofs = 0;
> +    }
> +
>       return packet;
>   err:
>       dp_packet_delete(packet);
> @@ -446,7 +457,12 @@ netdev_gre_build_header(const struct netdev 
> *netdev,
>   
>       greh = netdev_tnl_ip_build_header(data, params, IPPROTO_GRE);
>   
> -    greh->protocol = htons(ETH_TYPE_TEB);
> +    if (tnl_cfg->is_layer3) {
> +        greh->protocol = params->flow->dl_type;
> +    } else {
> +        greh->protocol = htons(ETH_TYPE_TEB);
> +    }
> +
>       greh->flags = 0;
>   
>       options = (ovs_16aligned_be32 *) (greh + 1); diff --git 
> a/lib/netdev-vport.c b/lib/netdev-vport.c index 02a246a..9fcdc64 
> 100644
> --- a/lib/netdev-vport.c
> +++ b/lib/netdev-vport.c
> @@ -96,9 +96,13 @@ netdev_vport_is_patch(const struct netdev *netdev)
>   bool
>   netdev_vport_is_layer3(const struct netdev *dev)
>   {
> -    const char *type = netdev_get_type(dev);
> +    if (is_vport_class(netdev_get_class(dev))) {
> +        struct netdev_vport *vport = netdev_vport_cast(dev);
> +
> +        return vport->tnl_cfg.is_layer3;
> +    }
>   
> -    return (!strcmp("lisp", type));
> +    return false;
>   }
>   
>   static bool
> @@ -402,13 +406,14 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
>       struct netdev_vport *dev = netdev_vport_cast(dev_);
>       const char *name = netdev_get_name(dev_);
>       const char *type = netdev_get_type(dev_);
> -    bool needs_dst_port, has_csum;
> +    bool needs_dst_port, has_csum, optional_layer3;
>       uint16_t dst_proto = 0, src_proto = 0;
>       struct netdev_tunnel_config tnl_cfg;
>       struct smap_node *node;
>   
>       has_csum = strstr(type, "gre") || strstr(type, "geneve") ||
>                  strstr(type, "stt") || strstr(type, "vxlan");
> +    optional_layer3 = !strcmp(type, "gre");
>       memset(&tnl_cfg, 0, sizeof tnl_cfg);
>   
>       /* Add a default destination port for tunnel ports if none 
> specified. */ @@ -422,6 +427,7 @@ set_tunnel_config(struct netdev 
> *dev_, const struct smap *args)
>   
>       if (!strcmp(type, "lisp")) {
>           tnl_cfg.dst_port = htons(LISP_DST_PORT);
> +        tnl_cfg.is_layer3 = true;
>       }
>   
>       if (!strcmp(type, "stt")) {
> @@ -505,6 +511,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args)
>               }
>   
>               free(str);
> +        } else if (!strcmp(node->key, "layer3") && optional_layer3) {
> +            if (!strcmp(node->value, "true")) {
> +                tnl_cfg.is_layer3 = true;
> +            }
>           } else {
>               VLOG_WARN("%s: unknown %s argument '%s'", name, type, node->key);
>           }
> @@ -552,6 +562,7 @@ static int
>   get_tunnel_config(const struct netdev *dev, struct smap *args)
>   {
>       struct netdev_vport *netdev = netdev_vport_cast(dev);
> +    const char *type = netdev_get_type(dev);
>       struct netdev_tunnel_config tnl_cfg;
>   
>       ovs_mutex_lock(&netdev->mutex);
> @@ -605,7 +616,6 @@ get_tunnel_config(const struct netdev *dev, struct 
> smap *args)
>   
>       if (tnl_cfg.dst_port) {
>           uint16_t dst_port = ntohs(tnl_cfg.dst_port);
> -        const char *type = netdev_get_type(dev);
>   
>           if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
>               (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) 
> || @@ -619,6 +629,10 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
>           smap_add(args, "csum", "true");
>       }
>   
> +    if (tnl_cfg.is_layer3 && !strcmp("gre", type)) {
> +        smap_add(args, "layer3", "true");
> +    }
> +
>       if (!tnl_cfg.dont_fragment) {
>           smap_add(args, "df_default", "false");
>       }
> diff --git a/lib/netdev.h b/lib/netdev.h index a667fe3..b3ef596 100644
> --- a/lib/netdev.h
> +++ b/lib/netdev.h
> @@ -98,6 +98,7 @@ struct netdev_tunnel_config {
>   
>       bool csum;
>       bool dont_fragment;
> +    bool is_layer3;
>   };
>   
>   void netdev_run(void);
> diff --git a/lib/nx-match.c b/lib/nx-match.c index da2919f..a6b7e2f 
> 100644
> --- a/lib/nx-match.c
> +++ b/lib/nx-match.c
> @@ -930,7 +930,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
>       int match_len;
>       int i;
>   
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       /* Metadata. */
>       if (match->wc.masks.dp_hash) {
> diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 
> d7aec06..b239336 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -375,6 +375,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
>       case OVS_KEY_ATTR_CT_ZONE:
>       case OVS_KEY_ATTR_CT_MARK:
>       case OVS_KEY_ATTR_CT_LABELS:
> +    case OVS_KEY_ATTR_NEXT_BASE_LAYER:
>       case __OVS_KEY_ATTR_MAX:
>       default:
>           OVS_NOT_REACHED();
> @@ -474,6 +475,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
>       case OVS_KEY_ATTR_ICMP:
>       case OVS_KEY_ATTR_ICMPV6:
>       case OVS_KEY_ATTR_TCP_FLAGS:
> +    case OVS_KEY_ATTR_NEXT_BASE_LAYER:
>       case __OVS_KEY_ATTR_MAX:
>       default:
>           OVS_NOT_REACHED();
> diff --git a/lib/odp-util.c b/lib/odp-util.c index 6725294..6c4df51 
> 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -166,6 +166,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
>       case OVS_KEY_ATTR_MPLS: return "mpls";
>       case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
>       case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
> +    case OVS_KEY_ATTR_NEXT_BASE_LAYER: return "next_base_layer";
>   
>       case __OVS_KEY_ATTR_MAX:
>       default:
> @@ -1862,6 +1863,7 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
>       [OVS_KEY_ATTR_CT_ZONE]   = { .len = 2 },
>       [OVS_KEY_ATTR_CT_MARK]   = { .len = 4 },
>       [OVS_KEY_ATTR_CT_LABELS] = { .len = sizeof(struct 
> ovs_key_ct_labels) },
> +    [OVS_KEY_ATTR_NEXT_BASE_LAYER] = { .len = 1 },
>   };
>   
>   /* Returns the correct length of the payload for a flow key 
> attribute of the @@ -2986,6 +2988,13 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
>           ds_chomp(ds, ',');
>           break;
>       }
> +
> +    case OVS_KEY_ATTR_NEXT_BASE_LAYER: {
> +        const uint8_t *mask = ma ? nl_attr_get(ma) : NULL;
> +        format_u8u(ds, "type", nl_attr_get_u8(a), mask, verbose);
> +        break;
> +    }
> +
>       case OVS_KEY_ATTR_UNSPEC:
>       case __OVS_KEY_ATTR_MAX:
>       default:
> @@ -4465,6 +4474,11 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
>               sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP,
>                                                  sizeof *sctp_key);
>               get_tp_key(data, sctp_key);
> +        } else if (flow->nw_proto == IPPROTO_GRE) {
> +            if (parms->support.next_base_layer) {
> +                nl_msg_put_u8(buf, OVS_KEY_ATTR_NEXT_BASE_LAYER,
> +                              data->next_base_layer);
> +            }
>           } else if (flow->dl_type == htons(ETH_TYPE_IP)
>                   && flow->nw_proto == IPPROTO_ICMP) {
>               struct ovs_key_icmp *icmp_key; @@ -5029,6 +5043,14 @@ 
> parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
>               put_tp_key(sctp_key, flow);
>               expected_bit = OVS_KEY_ATTR_SCTP;
>           }
> +    } else if (src_flow->nw_proto == IPPROTO_GRE
> +               && (src_flow->dl_type == htons(ETH_TYPE_IP) ||
> +                   src_flow->dl_type == htons(ETH_TYPE_IPV6))
> +               && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
> +        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NEXT_BASE_LAYER)) {
> +            flow->next_base_layer = nl_attr_get_u8(attrs[OVS_KEY_ATTR_NEXT_BASE_LAYER]);
> +            expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NEXT_BASE_LAYER;
> +        }
>       } else if (src_flow->nw_proto == IPPROTO_ICMP
>                  && src_flow->dl_type == htons(ETH_TYPE_IP)
>                  && !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) { diff 
> --git a/lib/odp-util.h b/lib/odp-util.h index f391e2a..41348cc 100644
> --- a/lib/odp-util.h
> +++ b/lib/odp-util.h
> @@ -142,7 +142,7 @@ void odp_portno_names_destroy(struct hmap *portno_names);
>    * add another field and forget to adjust this value.
>    */
>   #define ODPUTIL_FLOW_KEY_BYTES 640
> -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>   /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
>    * key.  An array of "struct nlattr" might not, in theory, be 
> sufficiently @@ -185,6 +185,8 @@ struct odp_support {
>        * 'ct_state'.  The above 'ct_state' member must be true for this
>        * to make sense */
>       bool ct_state_nat;
> +
> +    bool next_base_layer;
>   };
>   
>   struct odp_flow_key_parms {
> diff --git a/lib/ofp-util.c b/lib/ofp-util.c index d5d4b7d..273f18c 
> 100644
> --- a/lib/ofp-util.c
> +++ b/lib/ofp-util.c
> @@ -101,7 +101,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
>   void
>   ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
>   {
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>       /* Initialize most of wc. */
>       flow_wildcards_init_catchall(wc); diff --git a/lib/tnl-ports.c 
> b/lib/tnl-ports.c index ffa1389..8f82ac9 100644
> --- a/lib/tnl-ports.c
> +++ b/lib/tnl-ports.c
> @@ -27,6 +27,7 @@
>   #include "hash.h"
>   #include "openvswitch/list.h"
>   #include "netdev.h"
> +#include "netdev-vport.h"
>   #include "openvswitch/ofpbuf.h"
>   #include "ovs-thread.h"
>   #include "odp-util.h"
> @@ -53,6 +54,7 @@ struct tnl_port {
>       odp_port_t port;
>       ovs_be16 tp_port;
>       uint8_t nw_proto;
> +    bool is_layer3;
>       char dev_name[IFNAMSIZ];
>       struct ovs_list node;
>   };
> @@ -83,7 +85,8 @@ tnl_port_free(struct tnl_port_in *p)
>   
>   static void
>   tnl_port_init_flow(struct flow *flow, struct eth_addr mac,
> -                   struct in6_addr *addr, uint8_t nw_proto, ovs_be16 tp_port)
> +                   struct in6_addr *addr, uint8_t nw_proto, ovs_be16 tp_port,
> +                   bool is_layer3)
>   {
>       memset(flow, 0, sizeof *flow);
>   
> @@ -98,18 +101,20 @@ tnl_port_init_flow(struct flow *flow, struct 
> eth_addr mac,
>   
>       flow->nw_proto = nw_proto;
>       flow->tp_dst = tp_port;
> +    flow->next_base_layer = is_layer3 ? LAYER_3 : LAYER_2;
>   }
>   
>   static void
>   map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
> -           uint8_t nw_proto, ovs_be16 tp_port, const char dev_name[])
> +           uint8_t nw_proto, ovs_be16 tp_port, const char dev_name[],
> +           bool is_layer3)
>   {
>       const struct cls_rule *cr;
>       struct tnl_port_in *p;
>       struct match match;
>   
>       memset(&match, 0, sizeof match);
> -    tnl_port_init_flow(&match.flow, mac, addr, nw_proto, tp_port);
> +    tnl_port_init_flow(&match.flow, mac, addr, nw_proto, tp_port, 
> + is_layer3);
>   
>       do {
>           cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, 
> NULL); @@ -130,6 +135,11 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr,
>            * doesn't make sense to match on UDP port numbers. */
>           if (tp_port) {
>               match.wc.masks.tp_dst = OVS_BE16_MAX;
> +        } else {
> +            /* Match base layer for GRE tunnels as it may
> +             * be used to differentiate them.
> +             */
> +            match.wc.masks.next_base_layer = UINT8_MAX;
>           }
>           if (IN6_IS_ADDR_V4MAPPED(addr)) {
>               match.wc.masks.nw_dst = OVS_BE32_MAX; @@ -149,14 +159,15 
> @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr 
> *addr,
>   
>   static void
>   map_insert_ipdev__(struct ip_device *ip_dev, char dev_name[],
> -                   odp_port_t port, uint8_t nw_proto, ovs_be16 tp_port)
> +                   odp_port_t port, uint8_t nw_proto, ovs_be16 tp_port,
> +                   bool is_layer3)
>   {
>       if (ip_dev->n_addr) {
>           int i;
>   
>           for (i = 0; i < ip_dev->n_addr; i++) {
>               map_insert(port, ip_dev->mac, &ip_dev->addr[i],
> -                       nw_proto, tp_port, dev_name);
> +                       nw_proto, tp_port, dev_name, is_layer3);
>           }
>       }
>   }
> @@ -181,7 +192,7 @@ tnl_type_to_nw_proto(const char type[])
>   
>   void
>   tnl_port_map_insert(odp_port_t port, ovs_be16 tp_port,
> -                    const char dev_name[], const char type[])
> +                    const char dev_name[], const char type[], bool 
> + is_layer3)
>   {
>       struct tnl_port *p;
>       struct ip_device *ip_dev;
> @@ -194,7 +205,8 @@ tnl_port_map_insert(odp_port_t port, ovs_be16 
> tp_port,
>   
>       ovs_mutex_lock(&mutex);
>       LIST_FOR_EACH(p, node, &port_list) {
> -        if (tp_port == p->tp_port && p->nw_proto == nw_proto) {
> +        if (tp_port == p->tp_port && p->nw_proto == nw_proto &&
> +            p->is_layer3 == is_layer3) {
>                goto out;
>           }
>       }
> @@ -203,11 +215,13 @@ tnl_port_map_insert(odp_port_t port, ovs_be16 tp_port,
>       p->port = port;
>       p->tp_port = tp_port;
>       p->nw_proto = nw_proto;
> +    p->is_layer3 = is_layer3;
>       ovs_strlcpy(p->dev_name, dev_name, sizeof p->dev_name);
>       ovs_list_insert(&port_list, &p->node);
>   
>       LIST_FOR_EACH(ip_dev, node, &addr_list) {
> -        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
> +        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto,
> +                           p->tp_port, p->is_layer3);
>       }
>   
>   out:
> @@ -228,12 +242,12 @@ tnl_port_unref(const struct cls_rule *cr)
>   
>   static void
>   map_delete(struct eth_addr mac, struct in6_addr *addr,
> -           ovs_be16 tp_port, uint8_t nw_proto)
> +           ovs_be16 tp_port, uint8_t nw_proto, bool is_layer3)
>   {
>       const struct cls_rule *cr;
>       struct flow flow;
>   
> -    tnl_port_init_flow(&flow, mac, addr, nw_proto, tp_port);
> +    tnl_port_init_flow(&flow, mac, addr, nw_proto, tp_port, 
> + is_layer3);
>   
>       cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL);
>       tnl_port_unref(cr);
> @@ -242,11 +256,14 @@ map_delete(struct eth_addr mac, struct in6_addr *addr,
>   static void
>   ipdev_map_delete(struct ip_device *ip_dev, ovs_be16 tp_port, uint8_t nw_proto)
>   {
> +    bool is_layer3 = netdev_vport_is_layer3(ip_dev->dev);
> +
>       if (ip_dev->n_addr) {
>           int i;
>   
>           for (i = 0; i < ip_dev->n_addr; i++) {
> -            map_delete(ip_dev->mac, &ip_dev->addr[i], tp_port, nw_proto);
> +            map_delete(ip_dev->mac, &ip_dev->addr[i], tp_port, nw_proto,
> +                       is_layer3);
>           }
>       }
>   }
> @@ -352,7 +369,12 @@ tnl_port_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>       }
>   
>       LIST_FOR_EACH(p, node, &port_list) {
> -        ds_put_format(&ds, "%s (%"PRIu32")\n", p->dev_name, p->port);
> +        /* A layer3 and non-layer3 tunnel port may share the same ODP port.
> +         * To allow differentiation and avoid displaying otherwise
> +         * duplicated ouput append " (layer3)" when showing layer-3
> +         * tunnel ports. */
> +        ds_put_format(&ds, "%s (%"PRIu32")%s\n", p->dev_name, p->port,
> +                      p->is_layer3 ? " (layer3)" : "");
>       }
>   
>   out:
> @@ -367,7 +389,8 @@ map_insert_ipdev(struct ip_device *ip_dev)
>       struct tnl_port *p;
>   
>       LIST_FOR_EACH(p, node, &port_list) {
> -        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto, p->tp_port);
> +        map_insert_ipdev__(ip_dev, p->dev_name, p->port, p->nw_proto,
> +                           p->tp_port, p->is_layer3);
>       }
>   }
>   
> diff --git a/lib/tnl-ports.h b/lib/tnl-ports.h index 58b048a..fb57673 
> 100644
> --- a/lib/tnl-ports.h
> +++ b/lib/tnl-ports.h
> @@ -27,7 +27,8 @@
>   odp_port_t tnl_port_map_lookup(struct flow *flow, struct 
> flow_wildcards *wc);
>   
>   void tnl_port_map_insert(odp_port_t port, ovs_be16 udp_port,
> -                         const char dev_name[], const char type[]);
> +                         const char dev_name[], const char type[],
> +                         bool is_layer3);
>   
>   void tnl_port_map_delete(ovs_be16 udp_port, const char type[]);
>   void tnl_port_map_insert_ipdev(const char dev[]); diff --git 
> a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h index 
> f622278..8ccd68b 100644
> --- a/ofproto/ofproto-dpif-rid.h
> +++ b/ofproto/ofproto-dpif-rid.h
> @@ -99,7 +99,7 @@ struct rule;
>   /* Metadata for restoring pipeline context after recirculation.  Helpers
>    * are inlined below to keep them together with the definition for easier
>    * updates. */
> -BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>   
>   struct frozen_metadata {
>       /* Metadata in struct flow. */
> diff --git a/ofproto/ofproto-dpif-sflow.c 
> b/ofproto/ofproto-dpif-sflow.c index f970a57..8f51161 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1023,6 +1023,7 @@ sflow_read_set_action(const struct nlattr *attr,
>       case OVS_KEY_ATTR_CT_MARK:
>       case OVS_KEY_ATTR_CT_LABELS:
>       case OVS_KEY_ATTR_UNSPEC:
> +    case OVS_KEY_ATTR_NEXT_BASE_LAYER:
>       case __OVS_KEY_ATTR_MAX:
>       default:
>           break;
> diff --git a/ofproto/ofproto-dpif-xlate.c 
> b/ofproto/ofproto-dpif-xlate.c index 1e0bcea..f8a573b 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -2923,7 +2923,7 @@ compose_output_action__(struct xlate_ctx *ctx, 
> ofp_port_t ofp_port,
>   
>       /* If 'struct flow' gets additional metadata, we'll need to zero it out
>        * before traversing a patch port. */
> -    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 37);
> +    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 38);
>       memset(&flow_tnl, 0, sizeof flow_tnl);
>   
>       if (!xport) {
> diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 
> a70a491..50c1bb0 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1325,6 +1325,8 @@ check_support(struct dpif_backer *backer)
>       backer->support.odp.ct_label = check_ct_label(backer);
>   
>       backer->support.odp.ct_state_nat = check_ct_state_nat(backer);
> +
> +    backer->support.odp.next_base_layer = 
> + backer->support.tnl_push_pop;
>   }
>   
>   static int
> diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c index 
> ce727f4..0dc9fe3 100644
> --- a/ofproto/tunnel.c
> +++ b/ofproto/tunnel.c
> @@ -26,6 +26,7 @@
>   #include "hash.h"
>   #include "openvswitch/hmap.h"
>   #include "netdev.h"
> +#include "netdev-vport.h"
>   #include "odp-util.h"
>   #include "openvswitch/ofpbuf.h"
>   #include "packets.h"
> @@ -192,7 +193,8 @@ tnl_port_add__(const struct ofport_dpif *ofport, const struct netdev *netdev,
>           const char *type;
>   
>           type = netdev_get_type(netdev);
> -        tnl_port_map_insert(odp_port, cfg->dst_port, name, type);
> +        tnl_port_map_insert(odp_port, cfg->dst_port, name, type,
> +                            cfg->is_layer3);
>   
>       }
>       return true;
> diff --git a/tests/tunnel-push-pop-ipv6.at 
> b/tests/tunnel-push-pop-ipv6.at index 3f3d5ee..0e4cb91 100644
> --- a/tests/tunnel-push-pop-ipv6.at
> +++ b/tests/tunnel-push-pop-ipv6.at
> @@ -12,6 +12,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
>                          options:remote_ip=2001:cafe::93 options:out_key=flow options:csum=true ofport_request=4\
>                       -- add-port int-br t4 -- set Interface t4 type=geneve \
>                          options:remote_ip=flow options:key=123 
> ofport_request=5\
> +                    -- add-port int-br t5 -- set Interface t5 type=gre \
> +                       options:remote_ip=2001:cafe::92 
> + options:key=455 options:layer3=true ofport_request=6\
>                          ], [0])
>   
>   AT_CHECK([ovs-appctl dpif/show], [0], [dnl @@ -25,6 +27,7 @@ 
> dummy at ovs-dummy: hit:0 missed:0
>   		t2 2/4789: (vxlan: key=123, remote_ip=2001:cafe::92)
>   		t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=2001:cafe::93)
>   		t4 5/6081: (geneve: key=123, remote_ip=flow)
> +		t5 6/3: (gre: key=455, layer3=true, remote_ip=2001:cafe::92)
>   ])
>   
>   dnl First setup dummy interface IP address, then add the route @@ 
> -65,6 +68,7 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
>   Listening ports:
>   genev_sys_6081 (6081)
>   gre_sys (3)
> +gre_sys (3) (layer3)
>   vxlan_sys_4789 (4789)
>   ])
>   
> @@ -130,12 +134,12 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
>     port  3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
>   ])
>   
> -dnl Check GRE only accepts encapsulated Ethernet frames 
> -AT_CHECK([ovs-appctl netdev-dummy/receive p0 
> 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe00000000000000000
> 00000922001cafe00000000000000000000008820000800000001c8fe71d883724fbeb
> 6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030
> af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f2021222
> 32425262728292a2b2c2d2e2f3031323334353637'])
> +dnl Check decapsulation of L3GRE packet AT_CHECK([ovs-appctl 
> +netdev-dummy/receive p0 
> +'aa55aa550000001b213cab6486dd60000000005a2f402001cafe0000000000000000
> +000000922001cafe00000000000000000000008820000800000001c745000054ba200
> +000400184861e0000011e00000200004227e75400030af3195500000000f265010000
> +000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2
> +f3031323334353637'])
>   ovs-appctl time/warp 1000
>   
> -AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
> -  port  3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
> +AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  6'], [0], [dnl
> +  port  6: rx pkts=1, bytes=84, drop=?, errs=?, frame=?, over=?, crc=?
>   ])
>   
>   dnl Check decapsulation of Geneve packet with options diff --git 
> a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at index 
> 0e596f2..f1f956d 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -12,6 +12,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
>                          options:remote_ip=1.1.2.93 options:out_key=flow options:csum=true ofport_request=4\
>                       -- add-port int-br t4 -- set Interface t4 type=geneve \
>                          options:remote_ip=flow options:key=123 
> ofport_request=5\
> +                    -- add-port int-br t5 -- set Interface t5 type=gre \
> +                       options:remote_ip=1.1.2.92 options:key=455 
> + options:layer3=true ofport_request=6\
>                          ], [0])
>   
>   AT_CHECK([ovs-appctl dpif/show], [0], [dnl @@ -25,6 +27,7 @@ 
> dummy at ovs-dummy: hit:0 missed:0
>   		t2 2/4789: (vxlan: key=123, remote_ip=1.1.2.92)
>   		t3 4/4789: (vxlan: csum=true, out_key=flow, remote_ip=1.1.2.93)
>   		t4 5/6081: (geneve: key=123, remote_ip=flow)
> +		t5 6/3: (gre: key=455, layer3=true, remote_ip=1.1.2.92)
>   ])
>   
>   dnl First setup dummy interface IP address, then add the route @@ 
> -70,6 +73,7 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
>   Listening ports:
>   genev_sys_6081 (6081)
>   gre_sys (3)
> +gre_sys (3) (layer3)
>   vxlan_sys_4789 (4789)
>   ])
>   
> @@ -108,8 +112,14 @@ AT_CHECK([tail -1 stdout], [0],
>   dnl Check GRE tunnel push
>   AT_CHECK([ovs-ofctl add-flow int-br action=3])
>   AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 
> 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(
> 0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)
> '], [0], [stdout])
> +AT_CHECK([tail -1 stdout], [0], [Datapath actions: 
> +tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,
> +src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,
> +proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),ke
> +y=0x1c8)),out_port(100))
> +])
> +
> +dnl Check L3GRE tunnel push
> +AT_CHECK([ovs-ofctl add-flow int-br action=6]) AT_CHECK([ovs-appctl 
> +ofproto/trace ovs-dummy 
> +'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type
> +(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=n
> +o)'], [0], [stdout])
>   AT_CHECK([tail -1 stdout], [0],
> -  [Datapath actions: 
> tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:44:34:b6,s
> rc=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,pr
> oto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0
> x1c8)),out_port(100))
> +  [Datapath actions: 
> + pop_eth,tnl_push(tnl_port(3),header(size=42,type=3,eth(dst=f8:bc:12:
> + 44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst
> + =1.1.2.92,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto
> + =0x800),key=0x1c7)),out_port(100))
>   ])
>   
>   dnl Check Geneve tunnel push
> @@ -135,12 +145,20 @@ AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
>     port  3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
>   ])
>   
> -dnl Check GRE only accepts encapsulated Ethernet frames 
> -AT_CHECK([ovs-appctl netdev-dummy/receive p0 
> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c010102582
> 0000800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0
> 000011e00000200004227e75400030af3195500000000f265010000000000101112131
> 415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363
> 7'])
> +dnl Check decapsulation of L3GRE packet AT_CHECK([ovs-appctl 
> +netdev-dummy/receive p0 
> +'aa55aa550000001b213cab6408004500007079464000402fba630101025c01010258
> +20000800000001c745000054ba200000400184861e0000011e00000200004227e7540
> +0030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f20
> +2122232425262728292a2b2c2d2e2f3031323334353637'])
>   ovs-appctl time/warp 1000
>   
> -AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  3'], [0], [dnl
> +AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  6'], [0], [dnl
> +  port  6: rx pkts=1, bytes=84, drop=?, errs=?, frame=?, over=?, crc=?
> +])
> +
> +dnl Check GREL3 only accepts non-fragmented packets?
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 
> +'aa55aa550000001b213cab6408004500007e79464000402fba550101025c01010258
> +20000800000001c7fe71d883724fbeb6f4e1494a080045000054ba200000400184861
> +e0000011e00000200004227e75400030af3195500000000f265010000000000101112
> +131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343
> +53637'])
> +
> +AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[36]]' | sort], 
> +[0], [dnl
>     port  3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
> +  port  6: rx pkts=1, bytes=84, drop=?, errs=?, frame=?, over=?, crc=?
>   ])
>   
>   dnl Check decapsulation of Geneve packet with options diff --git 
> a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index b4af5a5..931d39c 
> 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -2246,6 +2246,19 @@
>   
>         </group>
>   
> +      <group title="Tunnel Options: gre only">
> +        <p>
> +          <code>gre</code> interfaces support these options.
> +        </p>
> +
> +        <column name="options" key="layer3" type='{"type": "boolean"}'>
> +          <p>
> +            Optional.  Packets are sent and received without an Ethernet
> +	        header present.
> +	      </p>
> +        </column>
> +      </group>
> +
>         <group title="Tunnel Options: gre, geneve, and vxlan">
>           <p>
>             <code>gre</code>, <code>geneve</code>, and



More information about the dev mailing list