diff --git a/NEWS b/NEWS
index 4985dba..0fa530c 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,8 @@ v2.11.0 - xx xxx xxxx
      * Add support for Auto load balancing of PMDs (experimental)
      * Added new per-port configurable option to manage EMC:
        'other_config:emc-enable'.
+     * ICMPv6 ND enhancements: support for match and set ND options type
+       and reserved fields.
    - Add 'symmetric_l3' hash function.
    - OVS now honors 'updelay' and 'downdelay' for bonds with LACP configured.
    - ovs-vswitchd:
diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
index 3592594..e159a1d 100755
--- a/build-aux/extract-ofp-fields
+++ b/build-aux/extract-ofp-fields
@@ -71,6 +71,7 @@ OXM_CLASSES = {"NXM_OF_":        (0,          0x0000, 'extension'),
                "OXM_OF_":        (0,          0x8000, 'standard'),
                "OXM_OF_PKT_REG": (0,          0x8001, 'standard'),
                "ONFOXM_ET_":     (0x4f4e4600, 0xffff, 'standard'),
+               "ERICOXM_OF_":    (0,          0x1000, 'extension'),
 
                # This is the experimenter OXM class for Nicira, which is the
                # one that OVS would be using instead of NXM_OF_ and NXM_NX_
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 9b087f1..d5aa09d 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -375,6 +375,7 @@ enum ovs_key_attr {
 #ifndef __KERNEL__
 	/* Only used within userspace data path. */
 	OVS_KEY_ATTR_PACKET_TYPE,  /* be32 packet type */
+	OVS_KEY_ATTR_ND_EXTENSIONS, /* struct ovs_key_nd_extensions */
 #endif
 
 	__OVS_KEY_ATTR_MAX
@@ -489,6 +490,13 @@ struct ovs_key_nd {
 	__u8	nd_tll[ETH_ALEN];
 };
 
+#ifndef __KERNEL__
+struct ovs_key_nd_extensions {
+    __be32  nd_reserved;
+    __u8    nd_options_type;
+};
+#endif
+
 #define OVS_CT_LABELS_LEN_32	4
 #define OVS_CT_LABELS_LEN	(OVS_CT_LABELS_LEN_32 * sizeof(__u32))
 struct ovs_key_ct_labels {
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index 5d2cf09..57b6c92 100644
--- a/include/openvswitch/flow.h
+++ b/include/openvswitch/flow.h
@@ -144,7 +144,8 @@ struct flow {
     struct in6_addr nd_target;  /* IPv6 neighbor discovery (ND) target. */
     struct eth_addr arp_sha;    /* ARP/ND source hardware address. */
     struct eth_addr arp_tha;    /* ARP/ND target hardware address. */
-    ovs_be16 tcp_flags;         /* TCP flags. With L3 to avoid matching L4. */
+    ovs_be16 tcp_flags;         /* TCP flags/ICMPv6 ND options type.
+                                 * With L3 to avoid matching L4. */
     ovs_be16 pad2;              /* Pad to 64 bits. */
     struct ovs_key_nsh nsh;     /* Network Service Header keys */
 
@@ -153,7 +154,8 @@ struct flow {
     ovs_be16 tp_dst;            /* TCP/UDP/SCTP destination port/ICMP code. */
     ovs_be16 ct_tp_src;         /* CT original tuple source port/ICMP type. */
     ovs_be16 ct_tp_dst;         /* CT original tuple dst port/ICMP code. */
-    ovs_be32 igmp_group_ip4;    /* IGMP group IPv4 address.
+    ovs_be32 igmp_group_ip4;    /* IGMP group IPv4 address/ICMPv6 ND reserved
+                                 * field.
                                  * Keep last for BUILD_ASSERT_DECL below. */
     ovs_be32 pad3;              /* Pad to 64 bits. */
 };
diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index e8c80dd..05ecee7 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -219,6 +219,9 @@ void match_set_nd_target(struct match *, const struct in6_addr *);
 void match_set_nd_target_masked(struct match *, const struct in6_addr *,
                                 const struct in6_addr *);
 
+void match_set_nd_reserved(struct match *, ovs_be32);
+void match_set_nd_options_type(struct match *, uint8_t);
+
 bool match_equal(const struct match *, const struct match *);
 uint32_t match_hash(const struct match *, uint32_t basis);
 
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 4a5a126..b7fd6cb 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -1796,6 +1796,34 @@ enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_ND_TLL,
 
+    /* "nd_reserved".
+     *
+     * The reserved field in IPv6 Neighbor Discovery message.
+     *
+     * Type: be32.
+     * Maskable: no.
+     * Formatting: decimal.
+     * Prerequisites: ND.
+     * Access: read/write.
+     * NXM: none.
+     * OXM: ERICOXM_OF_ICMPV6_ND_RESERVED(1) since v2.11.
+     */
+    MFF_ND_RESERVED,
+
+    /* "nd_options_type".
+     *
+     * The type of the option in IPv6 Neighbor Discovery message.
+     *
+     * Type: u8.
+     * Maskable: no.
+     * Formatting: decimal.
+     * Prerequisites: ND.
+     * Access: read/write.
+     * NXM: none.
+     * OXM: ERICOXM_OF_ICMPV6_ND_OPTIONS_TYPE(2) since v2.11.
+     */
+    MFF_ND_OPTIONS_TYPE,
+
 /* ## ---- ## */
 /* ## NSH  ## */
 /* ## ---- ## */
diff --git a/lib/flow.c b/lib/flow.c
index c6e4778..ae0a8a3 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -396,13 +396,14 @@ parse_ethertype(const void **datap, size_t *sizep)
 }
 
 /* Returns 'true' if the packet is an ND packet. In that case the '*nd_target'
- * and 'arp_buf[]' are filled in.  If the packet is not an ND pacet, 'false' is
- * returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */
+ * and 'arp_buf[]' are filled in.  If the packet is not an ND packet, 'false'
+ * is returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */
 static inline bool
 parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
-             const struct in6_addr **nd_target,
-             struct eth_addr arp_buf[2])
+             uint32_t *rso_flags, const struct in6_addr **nd_target,
+             struct eth_addr arp_buf[2], uint8_t *opt_type)
 {
+    const uint32_t *reserved;
     if (icmp->icmp6_code != 0 ||
         (icmp->icmp6_type != ND_NEIGHBOR_SOLICIT &&
          icmp->icmp6_type != ND_NEIGHBOR_ADVERT)) {
@@ -411,6 +412,15 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
 
     arp_buf[0] = eth_addr_zero;
     arp_buf[1] = eth_addr_zero;
+    *opt_type = 0;
+
+    reserved = data_try_pull(datap, sizep, sizeof(uint32_t));
+    if (OVS_UNLIKELY(!reserved)) {
+        /* Invalid ND packet. */
+        return false;
+    }
+    *rso_flags = *reserved;
+
     *nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
     if (OVS_UNLIKELY(!*nd_target)) {
         return true;
@@ -432,12 +442,20 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
         if (lla_opt->type == ND_OPT_SOURCE_LINKADDR && opt_len == 8) {
             if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) {
                 arp_buf[0] = lla_opt->mac;
+                /* We use only first option type present in ND packet. */
+                if (*opt_type == 0) {
+                    *opt_type = lla_opt->type;
+                }
             } else {
                 goto invalid;
             }
         } else if (lla_opt->type == ND_OPT_TARGET_LINKADDR && opt_len == 8) {
             if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) {
                 arp_buf[1] = lla_opt->mac;
+                /* We use only first option type present in ND packet. */
+                if (*opt_type == 0) {
+                    *opt_type = lla_opt->type;
+                }
             } else {
                 goto invalid;
             }
@@ -987,18 +1005,38 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
             if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) {
                 const struct in6_addr *nd_target;
                 struct eth_addr arp_buf[2];
-                const struct icmp6_hdr *icmp = data_pull(&data, &size,
-                                                         sizeof *icmp);
-                if (parse_icmpv6(&data, &size, icmp, &nd_target, arp_buf)) {
+                /* This will populate whether we received Option 1
+                 * or Option 2. */
+                uint8_t opt_type;
+                /* This holds the ND Reserved field. */
+                uint32_t rso_flags;
+                const struct icmp6_hdr *icmp = data_pull(&data,
+                                               &size,ICMP6_HEADER_LEN);
+                if (parse_icmpv6(&data, &size, icmp,
+                                 &rso_flags, &nd_target, arp_buf, &opt_type)) {
                     if (nd_target) {
                         miniflow_push_words(mf, nd_target, nd_target,
                                             sizeof *nd_target / sizeof(uint64_t));
                     }
                     miniflow_push_macs(mf, arp_sha, arp_buf);
-                    miniflow_pad_to_64(mf, arp_tha);
+                    /* Populate options field and set the padding
+                     * accordingly. */
+                    if (opt_type != 0) {
+                        miniflow_push_be16(mf, tcp_flags, htons(opt_type));
+                        /* Pad to align with 64 bits.
+                         * This will zero out the pad3 field. */
+                        miniflow_pad_to_64(mf, tcp_flags);
+                    } else {
+                        /* Pad to align with 64 bits.
+                         * This will zero out the tcp_flags & pad3 field. */
+                        miniflow_pad_to_64(mf, arp_tha);
+                    }
                     miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
                     miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
                     miniflow_pad_to_64(mf, tp_dst);
+                    /* Fill ND reserved field. */
+                    miniflow_push_be32(mf, igmp_group_ip4, htonl(rso_flags));
+                    miniflow_pad_to_64(mf, igmp_group_ip4);
                 } else {
                     /* ICMPv6 but not ND. */
                     miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
@@ -1927,6 +1965,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);
+            FLOWMAP_SET(map, tcp_flags);
+            FLOWMAP_SET(map, igmp_group_ip4);
         } else {
             FLOWMAP_SET(map, ct_nw_proto);
             FLOWMAP_SET(map, ct_ipv6_src);
@@ -2968,6 +3008,8 @@ flow_compose_l4(struct dp_packet *p, const struct flow *flow,
             struct icmp6_hdr *icmp = dp_packet_put_zeros(p, sizeof *icmp);
             icmp->icmp6_type = ntohs(flow->tp_src);
             icmp->icmp6_code = ntohs(flow->tp_dst);
+            uint32_t *reserved = &icmp->icmp6_dataun.icmp6_un_data32[0];
+            *reserved = ntohl(flow->igmp_group_ip4);
 
             if (icmp->icmp6_code == 0 &&
                 (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
diff --git a/lib/match.c b/lib/match.c
index a1407a8..052daee 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -1080,6 +1080,19 @@ match_set_nd_target_masked(struct match *match,
     match->wc.masks.nd_target = *mask;
 }
 
+void
+match_set_nd_reserved (struct match *match, ovs_be32 value)
+{
+   match->flow.igmp_group_ip4 = value;
+   match->wc.masks.igmp_group_ip4 = OVS_BE32_MAX;
+}
+
+void
+match_set_nd_options_type(struct match *match, uint8_t option)
+{
+    match_set_tcp_flags(match, htons(option));
+}
+
 /* Returns true if 'a' and 'b' wildcard the same fields and have the same
  * values for fixed fields, otherwise false. */
 bool
@@ -1688,6 +1701,14 @@ match_format(const struct match *match,
                             &wc->masks.nd_target);
         format_eth_masked(s, "nd_sll", f->arp_sha, wc->masks.arp_sha);
         format_eth_masked(s, "nd_tll", f->arp_tha, wc->masks.arp_tha);
+        if (wc->masks.igmp_group_ip4) {
+            format_be32_masked(s,"nd_reserved", f->igmp_group_ip4,
+                               wc->masks.igmp_group_ip4);
+        }
+        if (wc->masks.tcp_flags) {
+            format_be16_masked(s,"nd_options_type", f->tcp_flags,
+                               wc->masks.tcp_flags);
+        }
     } else {
         format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
         format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index bc48d63..6e4f09f 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -344,6 +344,11 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_ND_TARGET:
         return ipv6_mask_is_any(&wc->masks.nd_target);
 
+    case MFF_ND_RESERVED:
+        return !wc->masks.igmp_group_ip4;
+    case MFF_ND_OPTIONS_TYPE:
+        return !wc->masks.tcp_flags;
+
     case MFF_IP_FRAG:
         return !(wc->masks.nw_frag & FLOW_NW_FRAG_MASK);
 
@@ -571,6 +576,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ND_TARGET:
     case MFF_ND_SLL:
     case MFF_ND_TLL:
+    case MFF_ND_RESERVED:
+    case MFF_ND_OPTIONS_TYPE:
         return true;
 
     case MFF_IN_PORT_OXM:
@@ -909,9 +916,14 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
         break;
 
     case MFF_TCP_FLAGS:
+    case MFF_ND_OPTIONS_TYPE:
         value->be16 = flow->tcp_flags;
         break;
 
+    case MFF_ND_RESERVED:
+        value->be32 = flow->igmp_group_ip4;
+        break;
+
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
         value->u8 = ntohs(flow->tp_src);
@@ -1259,6 +1271,14 @@ mf_set_value(const struct mf_field *mf,
         match_set_nd_target(match, &value->ipv6);
         break;
 
+    case MFF_ND_RESERVED:
+        match_set_nd_reserved(match, value->be32);
+        break;
+
+    case MFF_ND_OPTIONS_TYPE:
+        match_set_nd_options_type(match, value->u8);
+        break;
+
     case MFF_NSH_FLAGS:
         MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8);
         break;
@@ -1668,6 +1688,14 @@ mf_set_flow_value(const struct mf_field *mf,
         flow->nd_target = value->ipv6;
         break;
 
+    case MFF_ND_RESERVED:
+        flow->igmp_group_ip4 = value->be32;
+        break;
+
+    case MFF_ND_OPTIONS_TYPE:
+        flow->tcp_flags = htons(value->u8);
+        break;
+
     case MFF_NSH_FLAGS:
         flow->nsh.flags = value->u8;
         break;
@@ -1823,6 +1851,8 @@ mf_is_pipeline_field(const struct mf_field *mf)
     case MFF_ND_TARGET:
     case MFF_ND_SLL:
     case MFF_ND_TLL:
+    case MFF_ND_RESERVED:
+    case MFF_ND_OPTIONS_TYPE:
     case MFF_NSH_FLAGS:
     case MFF_NSH_TTL:
     case MFF_NSH_MDTYPE:
@@ -2150,6 +2180,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         match->wc.masks.arp_tha = eth_addr_zero;
         break;
 
+    case MFF_ND_RESERVED:
+        match->wc.masks.igmp_group_ip4 = htonl(0);
+        match->flow.igmp_group_ip4 = htonl(0);
+        break;
+
     case MFF_TCP_SRC:
     case MFF_UDP_SRC:
     case MFF_SCTP_SRC:
@@ -2169,6 +2204,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         break;
 
     case MFF_TCP_FLAGS:
+    case MFF_ND_OPTIONS_TYPE:
         match->wc.masks.tcp_flags = htons(0);
         match->flow.tcp_flags = htons(0);
         break;
@@ -2285,6 +2321,8 @@ mf_set(const struct mf_field *mf,
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
     case MFF_ICMPV6_CODE:
+    case MFF_ND_RESERVED:
+    case MFF_ND_OPTIONS_TYPE:
         return OFPUTIL_P_NONE;
 
     case MFF_DP_HASH:
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 0b5c1d0..31db761 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -4586,6 +4586,18 @@ r r c c c.
            title="ICMPv6 Neighbor Discovery Source Ethernet Address"/>
     <field id="MFF_ND_TLL"
            title="ICMPv6 Neighbor Discovery Target Ethernet Address"/>
+    <field id="MFF_ND_RESERVED"
+           title="ICMPv6 Neighbor Discovery Reserved Field"/>
+      <p>
+        This is used to set the R,S,O bits in Neighbor Advertisement Messages
+      </p>
+    <field id="MFF_ND_OPTIONS_TYPE"
+          title="ICMPv6 Neighbor Discovery Options Type Field"/>
+    <p>
+       A value of 1 indicates that the option is Source Link Layer.
+       A value of 2 indicates that the options is Target Link Layer.
+       See RFC 4861 for further details.
+    </p>
   </group>
 
   <h1>References</h1>
diff --git a/lib/nx-match.c b/lib/nx-match.c
index fd3eac0..e7bd895 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -990,8 +990,16 @@ nxm_put_ip(struct nxm_put_ctx *ctx,
                           ntohs(flow->tp_dst));
             }
             if (is_nd(flow, NULL)) {
+                if (match->wc.masks.igmp_group_ip4) {
+                    nxm_put_32(ctx, MFF_ND_RESERVED, oxm,
+                           flow->igmp_group_ip4);
+                }
                 nxm_put_ipv6(ctx, MFF_ND_TARGET, oxm,
                              &flow->nd_target, &match->wc.masks.nd_target);
+                if (match->wc.masks.tcp_flags) {
+                   nxm_put_8(ctx, MFF_ND_OPTIONS_TYPE, oxm,
+                             ntohs(flow->tcp_flags));
+                }
                 if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
                     nxm_put_eth_masked(ctx, MFF_ND_SLL, oxm,
                                        flow->arp_sha, match->wc.masks.arp_sha);
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 3b6890e..5d07133 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -228,6 +228,23 @@ set_arp(struct dp_packet *packet, const struct ovs_key_arp *key,
 }
 
 static void
+odp_set_nd_ext(struct dp_packet *packet, const struct ovs_key_nd_extensions
+        *key, const struct ovs_key_nd_extensions *mask)
+{
+    const struct ovs_nd_msg *ns = dp_packet_l4(packet);
+    ovs_16aligned_be32 reserved = ns->rso_flags;
+    uint8_t opt_type = ns->options[0].type;
+
+    if (mask->nd_reserved) {
+        put_16aligned_be32(&reserved, key->nd_reserved);
+    }
+    if (mask->nd_options_type) {
+        opt_type = key->nd_options_type;
+    }
+    packet_set_nd_ext(packet, reserved, opt_type);
+}
+
+static void
 odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
            const struct ovs_key_nd *mask)
 {
@@ -438,6 +455,16 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
         }
         break;
 
+    case OVS_KEY_ATTR_ND_EXTENSIONS:
+        if (OVS_LIKELY(dp_packet_get_nd_payload(packet))) {
+            const struct ovs_key_nd_extensions *nd_ext_key
+                 = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd_extensions));
+            ovs_16aligned_be32 rso_flags;
+            put_16aligned_be32(&rso_flags, nd_ext_key->nd_reserved);
+            packet_set_nd_ext(packet, rso_flags, nd_ext_key->nd_options_type);
+        }
+        break;
+
     case OVS_KEY_ATTR_DP_HASH:
         md->dp_hash = nl_attr_get_u32(a);
         break;
@@ -540,6 +567,11 @@ odp_execute_masked_set_action(struct dp_packet *packet,
                    get_mask(a, struct ovs_key_nd));
         break;
 
+    case OVS_KEY_ATTR_ND_EXTENSIONS:
+        odp_set_nd_ext(packet, nl_attr_get(a),
+                       get_mask(a, struct ovs_key_nd_extensions));
+        break;
+
     case OVS_KEY_ATTR_DP_HASH:
         md->dp_hash = nl_attr_get_u32(a)
             | (md->dp_hash & ~*get_mask(a, uint32_t));
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 778c00e..929b178 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -173,6 +173,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
     case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
     case OVS_KEY_ATTR_ARP: return "arp";
     case OVS_KEY_ATTR_ND: return "nd";
+    case OVS_KEY_ATTR_ND_EXTENSIONS: return "nd_ext";
     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";
@@ -2500,6 +2501,7 @@ const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
     [OVS_KEY_ATTR_ICMPV6]    = { .len = sizeof(struct ovs_key_icmpv6) },
     [OVS_KEY_ATTR_ARP]       = { .len = sizeof(struct ovs_key_arp) },
     [OVS_KEY_ATTR_ND]        = { .len = sizeof(struct ovs_key_nd) },
+    [OVS_KEY_ATTR_ND_EXTENSIONS] = { .len = sizeof(struct ovs_key_nd_extensions) },
     [OVS_KEY_ATTR_CT_STATE]  = { .len = 4 },
     [OVS_KEY_ATTR_CT_ZONE]   = { .len = 2 },
     [OVS_KEY_ATTR_CT_MARK]   = { .len = 4 },
@@ -2950,6 +2952,7 @@ odp_mask_is_constant__(enum ovs_key_attr attr, const void *mask, size_t size,
     case OVS_KEY_ATTR_ICMP:
     case OVS_KEY_ATTR_ICMPV6:
     case OVS_KEY_ATTR_ND:
+    case OVS_KEY_ATTR_ND_EXTENSIONS:
     case OVS_KEY_ATTR_SKB_MARK:
     case OVS_KEY_ATTR_TUNNEL:
     case OVS_KEY_ATTR_SCTP:
@@ -4044,6 +4047,21 @@ format_odp_key_attr__(const struct nlattr *a, const struct nlattr *ma,
         ds_chomp(ds, ',');
         break;
     }
+    case OVS_KEY_ATTR_ND_EXTENSIONS: {
+        const struct ovs_key_nd_extensions *mask = ma ? nl_attr_get(ma) : NULL;
+        const struct ovs_key_nd_extensions *key = nl_attr_get(a);
+
+        bool first = true;
+        format_be32_masked(ds, &first, "nd_reserved", key->nd_reserved,
+                           OVS_BE32_MAX);
+        ds_put_char(ds, ',');
+
+        format_u8u(ds, "nd_options_type", key->nd_options_type,
+                   MASK(mask, nd_options_type), verbose);
+
+        ds_chomp(ds, ',');
+        break;
+    }
     case OVS_KEY_ATTR_NSH: {
         format_odp_nsh_attr(a, ma, ds);
         break;
@@ -5550,6 +5568,11 @@ parse_odp_key_mask_attr(struct parse_odp_context *context, const char *s,
         SCAN_FIELD("tll=", eth, nd_tll);
     } SCAN_END(OVS_KEY_ATTR_ND);
 
+    SCAN_BEGIN("nd_ext(", struct ovs_key_nd_extensions) {
+        SCAN_FIELD("nd_reserved=", be32, nd_reserved);
+        SCAN_FIELD("nd_options_type=", u8, nd_options_type);
+    } SCAN_END(OVS_KEY_ATTR_ND_EXTENSIONS);
+
     struct packet_type {
         ovs_be16 ns;
         ovs_be16 id;
@@ -5927,14 +5950,25 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
                  * xlate_wc_finish() for details. */
                 && (!export_mask || (data->tp_src == htons(0xff)
                                      && data->tp_dst == htons(0xff)))) {
-
                 struct ovs_key_nd *nd_key;
-
+                struct ovs_key_nd_extensions *nd_ext_key;
                 nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
                                                     sizeof *nd_key);
                 nd_key->nd_target = data->nd_target;
                 nd_key->nd_sll = data->arp_sha;
                 nd_key->nd_tll = data->arp_tha;
+
+                /* Add ND Extensions Attr only if reserved field
+                 * or options type is set. */
+                if (data->igmp_group_ip4 != 0 ||
+                    data->tcp_flags != 0) {
+                    nd_ext_key =
+                         nl_msg_put_unspec_uninit(buf,
+                                            OVS_KEY_ATTR_ND_EXTENSIONS,
+                                            sizeof *nd_ext_key);
+                    nd_ext_key->nd_reserved = data->igmp_group_ip4;
+                    nd_ext_key->nd_options_type = ntohs(data->tcp_flags);
+                }
             }
         }
     }
@@ -6118,6 +6152,7 @@ odp_key_to_dp_packet(const struct nlattr *key, size_t key_len,
         case OVS_KEY_ATTR_ICMPV6:
         case OVS_KEY_ATTR_ARP:
         case OVS_KEY_ATTR_ND:
+        case OVS_KEY_ATTR_ND_EXTENSIONS:
         case OVS_KEY_ATTR_SCTP:
         case OVS_KEY_ATTR_TCP_FLAGS:
         case OVS_KEY_ATTR_MPLS:
@@ -6557,6 +6592,35 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                         }
                     }
                 }
+                if (present_attrs &
+                    (UINT64_C(1) << OVS_KEY_ATTR_ND_EXTENSIONS)) {
+                    const struct ovs_key_nd_extensions *nd_ext_key;
+                    if (!is_mask) {
+                        *expected_attrs |=
+                                UINT64_C(1) << OVS_KEY_ATTR_ND_EXTENSIONS;
+                    }
+
+                    nd_ext_key =
+                        nl_attr_get(attrs[OVS_KEY_ATTR_ND_EXTENSIONS]);
+                    flow->igmp_group_ip4 = nd_ext_key->nd_reserved;
+                    flow->tcp_flags = htons(nd_ext_key->nd_options_type);
+
+                    if (is_mask) {
+                        /* Even though 'tp_src' and 'tp_dst' are 16 bits wide,
+                         * ICMP type and code are 8 bits wide.  Therefore, an
+                         * exact match looks like htons(0xff), not
+                         * htons(0xffff).  See xlate_wc_finish() for details.
+                         * */
+                        if (!is_all_zeros(nd_ext_key, sizeof *nd_ext_key) &&
+                            (flow->tp_src != htons(0xff) ||
+                             flow->tp_dst != htons(0xff))) {
+                            return ODP_FIT_ERROR;
+                        } else {
+                            *expected_attrs |=
+                                UINT64_C(1) << OVS_KEY_ATTR_ND_EXTENSIONS;
+                        }
+                    }
+                }
             }
         }
     } else if (src_flow->nw_proto == IPPROTO_IGMP
@@ -7460,6 +7524,24 @@ put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
     flow->arp_tha = nd->nd_tll;
 }
 
+static void
+get_nd_extensions_key(const struct flow *flow,
+                      struct ovs_key_nd_extensions *nd_ext)
+{
+    /* ND Extensions key has padding, clear it. */
+    memset(nd_ext, 0, sizeof *nd_ext);
+    nd_ext->nd_reserved = flow->igmp_group_ip4;
+    nd_ext->nd_options_type = ntohs(flow->tcp_flags);
+}
+
+static void
+put_nd_extensions_key(const struct ovs_key_nd_extensions *nd_ext,
+                      struct flow *flow)
+{
+    flow->igmp_group_ip4 = nd_ext->nd_reserved;
+    flow->tcp_flags = htons(nd_ext->nd_options_type);
+}
+
 static enum slow_path_reason
 commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
                      struct ofpbuf *odp_actions,
@@ -7482,10 +7564,33 @@ commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
 }
 
 static enum slow_path_reason
+commit_set_nd_extensions_action(const struct flow *flow,
+                                struct flow *base_flow,
+                                struct ofpbuf *odp_actions,
+                                struct flow_wildcards *wc, bool use_masked)
+{
+    struct ovs_key_nd_extensions key, mask, base;
+
+    get_nd_extensions_key(flow, &key);
+    get_nd_extensions_key(base_flow, &base);
+    get_nd_extensions_key(&wc->masks, &mask);
+
+    if (commit(OVS_KEY_ATTR_ND_EXTENSIONS, use_masked, &key,
+               &base, &mask, sizeof key, odp_actions)) {
+        put_nd_extensions_key(&base, base_flow);
+        put_nd_extensions_key(&mask, &wc->masks);
+        return SLOW_ACTION;
+    }
+    return 0;
+}
+
+static enum slow_path_reason
 commit_set_nw_action(const struct flow *flow, struct flow *base,
                      struct ofpbuf *odp_actions, struct flow_wildcards *wc,
                      bool use_masked)
 {
+    uint32_t reason;
+
     /* Check if 'flow' really has an L3 header. */
     if (!flow->nw_proto) {
         return 0;
@@ -7498,7 +7603,16 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
 
     case ETH_TYPE_IPV6:
         commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
-        return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);
+        if (base->nw_proto == IPPROTO_ICMPV6) {
+            /* Commit extended attrs first to make sure
+               correct options are added.*/
+            reason = commit_set_nd_extensions_action(flow, base,
+                                         odp_actions, wc, use_masked);
+            reason |= commit_set_nd_action(flow, base, odp_actions,
+                                         wc, use_masked);
+            return reason;
+        }
+        break;
 
     case ETH_TYPE_ARP:
         return commit_set_arp_action(flow, base, odp_actions, wc);
diff --git a/lib/packets.c b/lib/packets.c
index 2d6f77b..f5a2005 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1282,6 +1282,39 @@ packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
 }
 
 void
+packet_set_nd_ext(struct dp_packet *packet, const ovs_16aligned_be32 rso_flags,
+                  const uint8_t opt_type)
+{
+    struct ovs_nd_msg *ns;
+    struct ovs_nd_lla_opt *opt;
+    int bytes_remain = dp_packet_l4_size(packet);
+    struct ovs_16aligned_ip6_hdr * nh = dp_packet_l3(packet);
+    uint32_t pseudo_hdr_csum = 0;
+
+    if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
+        return;
+    }
+
+    if (nh) {
+        pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
+    }
+
+    ns = dp_packet_l4(packet);
+    opt = &ns->options[0];
+
+    /* set RSO flags and option type */
+    ns->rso_flags = rso_flags;
+    opt->type = opt_type;
+
+    /* recalculate checksum */
+    ovs_be16 *csum_value = &(ns->icmph.icmp6_cksum);
+    *csum_value = 0;
+    *csum_value = csum_finish(csum_continue(pseudo_hdr_csum,
+                              &(ns->icmph), bytes_remain));
+
+}
+
+void
 packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
               const struct eth_addr sll, const struct eth_addr tll)
 {
diff --git a/lib/packets.h b/lib/packets.h
index 09a0ac3..fcd90b3 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1543,6 +1543,9 @@ void packet_set_sctp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst);
 void packet_set_icmp(struct dp_packet *, uint8_t type, uint8_t code);
 void packet_set_nd(struct dp_packet *, const struct in6_addr *target,
                    const struct eth_addr sll, const struct eth_addr tll);
+void packet_set_nd_ext(struct dp_packet *packet,
+                       const ovs_16aligned_be32 rso_flags,
+                       const uint8_t opt_type);
 
 void packet_format_tcp_flags(struct ds *, uint16_t);
 const char *packet_tcp_flag_to_string(uint32_t flag);
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index 7da3175..bc4ffee 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1055,6 +1055,7 @@ sflow_read_set_action(const struct nlattr *attr,
     case OVS_KEY_ATTR_ICMPV6:
     case OVS_KEY_ATTR_ARP:
     case OVS_KEY_ATTR_ND:
+    case OVS_KEY_ATTR_ND_EXTENSIONS:
     case OVS_KEY_ATTR_CT_STATE:
     case OVS_KEY_ATTR_CT_ZONE:
     case OVS_KEY_ATTR_CT_MARK:
diff --git a/tests/odp.at b/tests/odp.at
index 86a918e..558c0c6 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -22,6 +22,7 @@ in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4,tip=5.6.7.8,op=1,sha=00:0f:10:11:12:13,tha=00:14:15:16:17:18)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
+in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=58,tclass=0,hlimit=128,frag=no),icmpv6(type=136,code=0),nd(target=::3,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e),nd_ext(nd_reserved=0x0,nd_options_type=2)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=3,ttl=64,bos=1)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=1)
 in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8847),mpls(label=100,tc=7,ttl=100,bos=0)
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 3021f93..baae70c 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2459,7 +2459,7 @@ head_table () {
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
         supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_metadata0 dnl
 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
-metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
+metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nd_reserved nd_options_type nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
     matching:
       dp_hash: arbitrary mask
       recirc_id: exact match or wildcard
@@ -2625,6 +2625,8 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
       nd_target: arbitrary mask
       nd_sll: arbitrary mask
       nd_tll: arbitrary mask
+      nd_reserved: exact match or wildcard
+      nd_options_type: exact match or wildcard
       nsh_flags: arbitrary mask
       nsh_mdtype: exact match or wildcard
       nsh_np: exact match or wildcard
