This patch has been superseded by patch series
https://mail.openvswitch.org/pipermail/ovs-dev/2017-February/328391.html, 
specifically
https://mail.openvswitch.org/pipermail/ovs-dev/2017-February/328395.html

Please exclude from review.

Regards, Jan

> -----Original Message-----
> From: ovs-dev-boun...@openvswitch.org 
> [mailto:ovs-dev-boun...@openvswitch.org] On Behalf Of Yi Yang
> Sent: Monday, 06 February, 2017 14:05
> To: d...@openvswitch.org
> Cc: Simon Horman <simon.hor...@netronome.com>; Jiri Benc <jb...@redhat.com>
> Subject: [ovs-dev] [PATCH v3 11/16] userspace: add non-tap (l3) support to 
> GRE vports
> 
> 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.hor...@netronome.com>
> Signed-off-by: Jiri Benc <jb...@redhat.com>
> Signed-off-by: Yi Yang <yi.y.y...@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                          | 28 ++++++++++---
>  vswitchd/vswitch.xml                              | 13 ++++++
>  23 files changed, 203 insertions(+), 52 deletions(-)
> 
> diff --git a/datapath/linux/compat/include/linux/openvswitch.h 
> b/datapath/linux/compat/include/linux/openvswitch.h
> index 7afdc4d..5631bb5 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 a1ad2af..4a72f73 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 4566201..50e26c0 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -5506,7 +5506,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 c730e72..52c1c9f 100644
> --- a/lib/netdev-native-tnl.c
> +++ b/lib/netdev-native-tnl.c
> @@ -155,6 +155,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;
> @@ -355,10 +358,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;
> @@ -388,6 +387,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;
>  }
> 
> @@ -413,6 +418,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);
> @@ -451,7 +462,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 2d0aa43..8653e96 100644
> --- a/lib/netdev-vport.c
> +++ b/lib/netdev-vport.c
> @@ -97,9 +97,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
> @@ -406,7 +410,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap 
> *args, char **errp)
>      const char *name = netdev_get_name(dev_);
>      const char *type = netdev_get_type(dev_);
>      struct ds errors = DS_EMPTY_INITIALIZER;
> -    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;
> @@ -414,6 +418,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap 
> *args, char **errp)
> 
>      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. */
> @@ -427,6 +432,7 @@ set_tunnel_config(struct netdev *dev_, const struct smap 
> *args, char **errp)
> 
>      if (!strcmp(type, "lisp")) {
>          tnl_cfg.dst_port = htons(LISP_DST_PORT);
> +        tnl_cfg.is_layer3 = true;
>      }
> 
>      if (!strcmp(type, "stt")) {
> @@ -514,6 +520,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap 
> *args, char **errp)
>          } else if (!strcmp(node->key, "egress_pkt_mark")) {
>              tnl_cfg.egress_pkt_mark = strtoul(node->value, NULL, 10);
>              tnl_cfg.set_egress_pkt_mark = true;
> +        } else if (!strcmp(node->key, "layer3") && optional_layer3) {
> +            if (!strcmp(node->value, "true")) {
> +                tnl_cfg.is_layer3 = true;
> +            }
>          } else {
>              ds_put_format(&errors, "%s: unknown %s argument '%s'\n",
>                            name, type, node->key);
> @@ -583,6 +593,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);
> @@ -636,7 +647,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) ||
> @@ -650,6 +660,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 d6c07c1..416d2b7 100644
> --- a/lib/netdev.h
> +++ b/lib/netdev.h
> @@ -100,6 +100,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 124e56b..6dc11f2 100644
> --- a/lib/nx-match.c
> +++ b/lib/nx-match.c
> @@ -962,7 +962,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 c0d6a20..b0df366 100644
> --- a/lib/odp-execute.c
> +++ b/lib/odp-execute.c
> @@ -384,6 +384,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();
> @@ -483,6 +484,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 3b039e8..58e05d2 100644
> --- a/lib/odp-util.c
> +++ b/lib/odp-util.c
> @@ -170,6 +170,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:
> @@ -1911,6 +1912,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
> @@ -3027,6 +3029,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:
> @@ -4499,6 +4508,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;
> @@ -5062,6 +5076,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 b8872b5..9a8322f 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 dfe54ff..2c05c36 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 beb4b1f..c6b7bb6 100644
> --- a/ofproto/ofproto-dpif-sflow.c
> +++ b/ofproto/ofproto-dpif-sflow.c
> @@ -1026,6 +1026,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 a6772eb..2b7e277 100644
> --- a/ofproto/ofproto-dpif-xlate.c
> +++ b/ofproto/ofproto-dpif-xlate.c
> @@ -3089,7 +3089,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 0b46d06..e361271 100644
> --- a/ofproto/ofproto-dpif.c
> +++ b/ofproto/ofproto-dpif.c
> @@ -1210,6 +1210,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 e285d54..cd00f83 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@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
> 'aa55aa550000001b213cab6486dd60000000006a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000
> 800000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f26
> 5010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +dnl Check decapsulation of L3GRE packet
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'aa55aa550000001b213cab6486dd60000000005a2f402001cafe0000000000000000000000922001cafe00000000000000000000008820000
> 800000001c745000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f26501000000000010111213141516
> 1718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>  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 e4687fc..8eb389b 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -14,6 +14,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 
> type=vxlan \
>                         options:remote_ip=flow options:key=123 
> ofport_request=5\
>                      -- add-port int-br t5 -- set Interface t5 type=geneve \
>                         options:remote_ip=1.1.2.93 options:out_key=flow 
> options:egress_pkt_mark=1234 ofport_request=6\
> +                    -- add-port int-br t6 -- set Interface t6 type=gre \
> +                       options:remote_ip=1.1.2.92 options:key=455 
> options:layer3=true ofport_request=7\
>                         ], [0])
> 
>  AT_CHECK([ovs-appctl dpif/show], [0], [dnl
> @@ -28,6 +30,7 @@ dummy@ovs-dummy: hit:0 missed:0
>               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/6081: (geneve: egress_pkt_mark=1234, out_key=flow, 
> remote_ip=1.1.2.93)
> +             t6 7/3: (gre: key=455, layer3=true, remote_ip=1.1.2.92)
>  ])
> 
>  dnl First setup dummy interface IP address, then add the route
> @@ -76,6 +79,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)
>  ])
> 
> @@ -120,8 +124,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.9
> 2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1c8)),out_port(100))
> +])
> +
> +dnl Check L3GRE tunnel push
> +AT_CHECK([ovs-ofctl add-flow int-br action=7])
> +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.9
> 2,proto=47,tos=0,ttl=64,frag=0x4000),gre((flags=0x2000,proto=0x6558),key=0x1c8)),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,ds
> t=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
> @@ -133,7 +143,7 @@ AT_CHECK([tail -1 stdout], [0],
> 
>  dnl Check Geneve tunnel push with pkt-mark
>  AT_CHECK([ovs-ofctl add-flow int-br "actions=set_tunnel:234,6"])
> -AT_CHECK([ovs-appctl ofproto/trace ovs-dummy
> 'in_port(2),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([ovs-appctl ofproto/trace ovs-dummy
> 'in_port(2),eth(src=f8:bc:12:44:34:b7,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:
> set(skb_mark(0x4d2)),tnl_push(tnl_port(6081),header(size=50,type=5,eth(dst=f8:bc:12:44:34:b7,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv
> 4(src=1.1.2.88,dst=1.1.2.93,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(vni=0xea)),out_port(100))
>  ])
> @@ -154,12 +164,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
> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c8fe71d883724fbeb6f4e1494a080045
> 000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1
> d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +dnl Check decapsulation of L3GRE packet
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0
> 'aa55aa550000001b213cab6408004500007079464000402fba630101025c0101025820000800000001c745000054ba200000400184861e00
> 00011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292
> a2b2c2d2e2f3031323334353637'])
>  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  7'], [0], [dnl
> +  port  7: 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
> 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820000800000001c7fe71d883724fbeb6f4e1494a080045
> 000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1
> d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> +
> +AT_CHECK([ovs-ofctl dump-ports int-br | grep 'port  [[37]]' | sort], [0], 
> [dnl
>    port  3: rx pkts=1, bytes=98, drop=?, errs=?, frame=?, over=?, crc=?
> +  port  7: 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 146a816..e860661 100644
> --- a/vswitchd/vswitch.xml
> +++ b/vswitchd/vswitch.xml
> @@ -2252,6 +2252,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
> --
> 2.1.0
> 
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to