*  s, Recur, Flags, Version fields only S (bit 03) is set to 1. The
  *  other fields are set to zero, so only a sequence number follows.
  *
- *  ERSPAN Type II header (8 octets [42:49])
+ *  ERSPAN Version 1 (Type II) header (8 octets [42:49])
  *  0                   1                   2                   3
  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB
  */
 
-#define ERSPAN_VERSION 0x1
+#define ERSPAN_VERSION 0x1     /* ERSPAN type II */
 
 #define VER_MASK       0xf000
 #define VLAN_MASK      0x0fff
        ERSPAN_ENCAP_INFRAME = 0x3,     /* VLAN tag perserved in frame */
 };
 
+#define ERSPAN_V1_MDSIZE       4
+#define ERSPAN_V2_MDSIZE       8
 struct erspan_metadata {
-       __be32 index;   /* type II */
+       union {
+               __be32 index;   /* Version 1 (type II)*/
+       } u;
 };
 
-struct erspanhdr {
+struct erspan_base_hdr {
        __be16 ver_vlan;
 #define VER_OFFSET  12
        __be16 session_id;
 #define COS_OFFSET  13
 #define EN_OFFSET   11
 #define T_OFFSET    10
-       struct erspan_metadata md;
 };
 
+static inline int erspan_hdr_len(int version)
+{
+       return sizeof(struct erspan_base_hdr) +
+              (version == 1 ? ERSPAN_V1_MDSIZE : ERSPAN_V2_MDSIZE);
+}
+
 static inline u8 tos_to_cos(u8 tos)
 {
        u8 dscp, cos;
 {
        struct ethhdr *eth = eth_hdr(skb);
        enum erspan_encap_type enc_type;
-       struct erspanhdr *ershdr;
+       struct erspan_base_hdr *ershdr;
+       struct erspan_metadata *ersmd;
        struct qtag_prefix {
                __be16 eth_type;
                __be16 tci;
                enc_type = ERSPAN_ENCAP_INFRAME;
        }
 
-       skb_push(skb, sizeof(*ershdr));
-       ershdr = (struct erspanhdr *)skb->data;
-       memset(ershdr, 0, sizeof(*ershdr));
+       skb_push(skb, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
+       ershdr = (struct erspan_base_hdr *)skb->data;
+       memset(ershdr, 0, sizeof(*ershdr) + ERSPAN_V1_MDSIZE);
 
+       /* Build base header */
        ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) |
                                 (ERSPAN_VERSION << VER_OFFSET));
        ershdr->session_id = htons((u16)(ntohl(id) & ID_MASK) |
                           ((tos_to_cos(tos) << COS_OFFSET) & COS_MASK) |
                           (enc_type << EN_OFFSET & EN_MASK) |
                           ((truncate << T_OFFSET) & T_MASK));
-       ershdr->md.index = htonl(index & INDEX_MASK);
+
+       /* Build metadata */
+       ersmd = (struct erspan_metadata *)(ershdr + 1);
+       ersmd->u.index = htonl(index & INDEX_MASK);
 }
 
 #endif
 
 {
        struct net *net = dev_net(skb->dev);
        struct metadata_dst *tun_dst = NULL;
+       struct erspan_base_hdr *ershdr;
+       struct erspan_metadata *pkt_md;
        struct ip_tunnel_net *itn;
        struct ip_tunnel *tunnel;
-       struct erspanhdr *ershdr;
        const struct iphdr *iph;
-       __be32 index;
+       int ver;
        int len;
 
        itn = net_generic(net, erspan_net_id);
        len = gre_hdr_len + sizeof(*ershdr);
 
+       /* Check based hdr len */
        if (unlikely(!pskb_may_pull(skb, len)))
                return -ENOMEM;
 
        iph = ip_hdr(skb);
-       ershdr = (struct erspanhdr *)(skb->data + gre_hdr_len);
+       ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
+       ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET;
 
        /* The original GRE header does not have key field,
         * Use ERSPAN 10-bit session ID as key.
         */
        tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
-       index = ershdr->md.index;
+       pkt_md = (struct erspan_metadata *)(ershdr + 1);
        tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex,
                                  tpi->flags | TUNNEL_KEY,
                                  iph->saddr, iph->daddr, tpi->key);
 
        if (tunnel) {
+               len = gre_hdr_len + erspan_hdr_len(ver);
+               if (unlikely(!pskb_may_pull(skb, len)))
+                       return -ENOMEM;
+
                if (__iptunnel_pull_header(skb,
-                                          gre_hdr_len + sizeof(*ershdr),
+                                          len,
                                           htons(ETH_P_TEB),
                                           false, false) < 0)
                        goto drop;
                        if (!md)
                                return PACKET_REJECT;
 
-                       md->index = index;
+                       memcpy(md, pkt_md, sizeof(*md));
                        info = &tun_dst->u.tun_info;
                        info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
                        info->options_len = sizeof(*md);
                } else {
-                       tunnel->index = ntohl(index);
+                       tunnel->index = ntohl(pkt_md->u.index);
                }
 
                skb_reset_mac_header(skb);
        key = &tun_info->key;
 
        /* ERSPAN has fixed 8 byte GRE header */
-       tunnel_hlen = 8 + sizeof(struct erspanhdr);
+       tunnel_hlen = 8 + sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
 
        rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen);
        if (!rt)
                goto err_free_rt;
 
        erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
-                           ntohl(md->index), truncate, true);
+                           ntohl(md->u.index), truncate, true);
 
        gre_build_header(skb, 8, TUNNEL_SEQ,
                         htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++));
        tunnel->tun_hlen = 8;
        tunnel->parms.iph.protocol = IPPROTO_GRE;
        tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-                      sizeof(struct erspanhdr);
+                      sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
        t_hlen = tunnel->hlen + sizeof(struct iphdr);
 
        dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4;
 
 static int ip6erspan_rcv(struct sk_buff *skb, int gre_hdr_len,
                         struct tnl_ptk_info *tpi)
 {
+       struct erspan_base_hdr *ershdr;
+       struct erspan_metadata *pkt_md;
        const struct ipv6hdr *ipv6h;
-       struct erspanhdr *ershdr;
        struct ip6_tnl *tunnel;
-       __be32 index;
+       u8 ver;
 
        ipv6h = ipv6_hdr(skb);
-       ershdr = (struct erspanhdr *)skb->data;
+       ershdr = (struct erspan_base_hdr *)skb->data;
 
        if (unlikely(!pskb_may_pull(skb, sizeof(*ershdr))))
                return PACKET_REJECT;
 
+       ver = (ntohs(ershdr->ver_vlan) & VER_MASK) >> VER_OFFSET;
        tpi->key = cpu_to_be32(ntohs(ershdr->session_id) & ID_MASK);
-       index = ershdr->md.index;
+       pkt_md = (struct erspan_metadata *)(ershdr + 1);
 
        tunnel = ip6gre_tunnel_lookup(skb->dev,
                                      &ipv6h->saddr, &ipv6h->daddr, tpi->key,
                                      tpi->proto);
        if (tunnel) {
-               if (__iptunnel_pull_header(skb, sizeof(*ershdr),
+               int len = erspan_hdr_len(ver);
+
+               if (unlikely(!pskb_may_pull(skb, len)))
+                       return -ENOMEM;
+
+               if (__iptunnel_pull_header(skb, len,
                                           htons(ETH_P_TEB),
                                           false, false) < 0)
                        return PACKET_REJECT;
                        if (!md)
                                return PACKET_REJECT;
 
-                       md->index = index;
+                       memcpy(md, pkt_md, sizeof(*md));
                        info->key.tun_flags |= TUNNEL_ERSPAN_OPT;
                        info->options_len = sizeof(*md);
 
                        ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error);
 
                } else {
-                       tunnel->parms.index = ntohl(index);
+                       tunnel->parms.index = ntohl(pkt_md->u.index);
                        ip6_tnl_rcv(tunnel, skb, tpi, NULL, log_ecn_error);
                }
 
                        goto tx_err;
 
                erspan_build_header(skb, tunnel_id_to_key32(key->tun_id),
-                                   ntohl(md->index), truncate, false);
+                                   ntohl(md->u.index), truncate, false);
 
        } else {
                switch (skb->protocol) {
 
        tunnel->tun_hlen = 8;
        tunnel->hlen = tunnel->tun_hlen + tunnel->encap_hlen +
-                      sizeof(struct erspanhdr);
+                      sizeof(struct erspan_base_hdr) + ERSPAN_V1_MDSIZE;
        t_hlen = tunnel->hlen + sizeof(struct ipv6hdr);
 
        dev->hard_header_len = LL_MAX_HEADER + t_hlen;
 
        BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts));
 
        memset(&opts, 0, sizeof(opts));
-       opts.index = nla_get_be32(attr);
+       opts.u.index = nla_get_be32(attr);
 
        /* Index has only 20-bit */
-       if (ntohl(opts.index) & ~INDEX_MASK) {
+       if (ntohl(opts.u.index) & ~INDEX_MASK) {
                OVS_NLERR(log, "ERSPAN index number %x too large.",
-                         ntohl(opts.index));
+                         ntohl(opts.u.index));
                return -EINVAL;
        }
 
                        return -EMSGSIZE;
                else if (output->tun_flags & TUNNEL_ERSPAN_OPT &&
                         nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,
-                                     ((struct erspan_metadata *)tun_opts)->index))
+                                     ((struct erspan_metadata *)tun_opts)->u.index))
                        return -EMSGSIZE;
        }