[ovs-dev] IPv6: Add support for match and set nd_options_type and reserved fields.

Message ID 1537282752-17158-1-git-send-email-vishal.deep.ajmera@ericsson.com
State New
Headers show
Series
  • [ovs-dev] IPv6: Add support for match and set nd_options_type and reserved fields.
Related show

Commit Message

Vishal Deep Ajmera Sept. 18, 2018, 2:59 p.m.
Currently OVS supports all ARP protocol fields as OXM match fields
to implement the relevant ARP procedures for IPv4. This includes support
for matching copying and setting ARP fields. In IPv6 ARP has been
replaced by ICMPv6 neighbor discovery (ND) procedures, neighbor
advertisement and neighbor solicitation.

The support for ICMPv6 fields in OVS is not complete for the use cases
equivalent to ARP in IPv4. OVS lacks support for matching, copying and
setting the “ND option type” and “ND reserved” fields. Without these user
cannot implement all ICMPv6 ND procedures for IPv6 support.

This commit adds additional OXM fields to OVS for ICMPv6 “ND option type“
and ICMPv6 “ND reserved” using the OXM extension mechanism.

This allows support for parsing these fields from an ICMPv6 packet header
and extending the OpenFlow protocol with specifications for these new OXM
fields for matching, copying and setting.

Signed-off-by: Ashvin Lakshmikantha <ashvin.lakshmikantha@ericsson.com>
Signed-off-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
Co-authored-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
---
 build-aux/extract-ofp-fields                      |  2 +-
 datapath/linux/compat/include/linux/openvswitch.h | 10 +++--
 include/openvswitch/match.h                       |  3 ++
 include/openvswitch/meta-flow.h                   | 28 ++++++++++++
 lib/flow.c                                        | 54 ++++++++++++++++++++---
 lib/match.c                                       | 16 +++++++
 lib/meta-flow.c                                   | 37 ++++++++++++++++
 lib/meta-flow.xml                                 | 13 ++++++
 lib/nx-match.c                                    |  8 ++++
 lib/odp-execute.c                                 | 21 +++++++--
 lib/odp-util.c                                    | 19 +++++++-
 lib/packets.c                                     | 36 +++++++++------
 lib/packets.h                                     |  3 +-
 tests/odp.at                                      | 14 +++---
 tests/ofproto-dpif.at                             |  8 ++--
 tests/ofproto.at                                  |  4 +-
 16 files changed, 234 insertions(+), 42 deletions(-)

Comments

Ben Pfaff Sept. 18, 2018, 8:06 a.m. | #1
On Tue, Sep 18, 2018 at 08:29:12PM +0530, Vishal Deep Ajmera wrote:
> Currently OVS supports all ARP protocol fields as OXM match fields
> to implement the relevant ARP procedures for IPv4. This includes support
> for matching copying and setting ARP fields. In IPv6 ARP has been
> replaced by ICMPv6 neighbor discovery (ND) procedures, neighbor
> advertisement and neighbor solicitation.
> 
> The support for ICMPv6 fields in OVS is not complete for the use cases
> equivalent to ARP in IPv4. OVS lacks support for matching, copying and
> setting the “ND option type” and “ND reserved” fields. Without these user
> cannot implement all ICMPv6 ND procedures for IPv6 support.
> 
> This commit adds additional OXM fields to OVS for ICMPv6 “ND option type“
> and ICMPv6 “ND reserved” using the OXM extension mechanism.
> 
> This allows support for parsing these fields from an ICMPv6 packet header
> and extending the OpenFlow protocol with specifications for these new OXM
> fields for matching, copying and setting.
> 
> Signed-off-by: Ashvin Lakshmikantha <ashvin.lakshmikantha@ericsson.com>
> Signed-off-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>
> Co-authored-by: Vishal Deep Ajmera <vishal.deep.ajmera@ericsson.com>

Thanks for working on making OVS better support IPv6.

The following change stood out to me.  It appears to break ABI
compatibility in the kernel datapath.  Is there some reason it's OK?  I
especially don't understand why it adds a nested inner struct.

> --- a/datapath/linux/compat/include/linux/openvswitch.h
> +++ b/datapath/linux/compat/include/linux/openvswitch.h
> @@ -484,9 +484,13 @@ struct ovs_key_arp {
>  };
>  
>  struct ovs_key_nd {
> -	__be32	nd_target[4];
> -	__u8	nd_sll[ETH_ALEN];
> -	__u8	nd_tll[ETH_ALEN];
> +        __be32  nd_reserved;
> +        __be32  nd_target[4];
> +        __u8    nd_options_type;
> +        struct {
> +           __u8        nd_sll[ETH_ALEN];
> +           __u8        nd_tll[ETH_ALEN];
> +        };
>  };

I have not otherwise reviewed this patch.

Thanks,

Ben.
0-day Robot Sept. 18, 2018, 8:29 a.m. | #2
Bleep bloop.  Greetings Vishal Deep Ajmera, I am a robot and I have tried out your patch.
Thanks for your contribution.

I encountered some error that I wasn't expecting.  See the details below.


checkpatch:
ERROR: Author should not be also be co-author.
Lines checked: 793, Warnings: 0, Errors: 1


build:
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H -I.    -I ./include -I ./include -I ./lib -I ./lib    -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror   -g -O2 -MT lib/entropy.lo -MD -MP -MF $depbase.Tpo -c -o lib/entropy.lo lib/entropy.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I ./include -I ./include -I ./lib -I ./lib -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror -g -O2 -MT lib/entropy.lo -MD -MP -MF lib/.deps/entropy.Tpo -c lib/entropy.c -o lib/entropy.o
depbase=`echo lib/fat-rwlock.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H -I.    -I ./include -I ./include -I ./lib -I ./lib    -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror   -g -O2 -MT lib/fat-rwlock.lo -MD -MP -MF $depbase.Tpo -c -o lib/fat-rwlock.lo lib/fat-rwlock.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I ./include -I ./include -I ./lib -I ./lib -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror -g -O2 -MT lib/fat-rwlock.lo -MD -MP -MF lib/.deps/fat-rwlock.Tpo -c lib/fat-rwlock.c -o lib/fat-rwlock.o
depbase=`echo lib/fatal-signal.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H -I.    -I ./include -I ./include -I ./lib -I ./lib    -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror   -g -O2 -MT lib/fatal-signal.lo -MD -MP -MF $depbase.Tpo -c -o lib/fatal-signal.lo lib/fatal-signal.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I ./include -I ./include -I ./lib -I ./lib -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror -g -O2 -MT lib/fatal-signal.lo -MD -MP -MF lib/.deps/fatal-signal.Tpo -c lib/fatal-signal.c -o lib/fatal-signal.o
depbase=`echo lib/flow.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H -I.    -I ./include -I ./include -I ./lib -I ./lib    -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror   -g -O2 -MT lib/flow.lo -MD -MP -MF $depbase.Tpo -c -o lib/flow.lo lib/flow.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -std=gnu99 -DHAVE_CONFIG_H -I. -I ./include -I ./include -I ./lib -I ./lib -Wstrict-prototypes -Wall -Wextra -Wno-sign-compare -Wpointer-arith -Wformat -Wformat-security -Wswitch-enum -Wunused-parameter -Wbad-function-cast -Wcast-align -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-field-initializers -fno-strict-aliasing -Wshadow -Wno-null-pointer-arithmetic -Werror -Werror -g -O2 -MT lib/flow.lo -MD -MP -MF lib/.deps/flow.Tpo -c lib/flow.c -o lib/flow.o
lib/flow.c:47:31: error: 'minirl' defined but not used [-Werror=unused-variable]
 static struct vlog_rate_limit minirl = VLOG_RATE_LIMIT_INIT(1, 5);
                               ^
cc1: error: unrecognized command line option "-Wno-null-pointer-arithmetic" [-Werror]
cc1: all warnings being treated as errors
make[2]: *** [lib/flow.lo] Error 1
make[2]: Leaving directory `/var/lib/jenkins/jobs/upstream_build_from_pw/workspace'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/var/lib/jenkins/jobs/upstream_build_from_pw/workspace'
make: *** [all] Error 2


Please check this out.  If you feel there has been an error, please email aconole@bytheb.org

Thanks,
0-day Robot
Vishal Deep Ajmera Sept. 18, 2018, 10:19 a.m. | #3
> 
> Thanks for working on making OVS better support IPv6.
> 
> The following change stood out to me.  It appears to break ABI compatibility
> in the kernel datapath.  Is there some reason it's OK?  I especially don't
> understand why it adds a nested inner struct.
> 
Thanks Ben. Yes, the inner structure is not required. Earlier we defined it as union and 
later changed it to structure. Instead those fields can be simply defined outside of inner 
structure. I will fix this in v2 patch.

Warm Regards,
Vishal
Ben Pfaff Sept. 18, 2018, 10:21 a.m. | #4
On Tue, Sep 18, 2018 at 10:19:20AM +0000, Vishal Deep Ajmera wrote:
> > 
> > Thanks for working on making OVS better support IPv6.
> > 
> > The following change stood out to me.  It appears to break ABI compatibility
> > in the kernel datapath.  Is there some reason it's OK?  I especially don't
> > understand why it adds a nested inner struct.
> > 
> Thanks Ben. Yes, the inner structure is not required. Earlier we defined it as union and 
> later changed it to structure. Instead those fields can be simply defined outside of inner 
> structure. I will fix this in v2 patch.

That is good to know, but the inner struct is not the main issue as I
see it.  The main issue is that the change breaks the ABI.  This change
must be implemented in some way that does not break the ABI.
Vishal Deep Ajmera Sept. 18, 2018, 2:53 p.m. | #5
> > Thanks Ben. Yes, the inner structure is not required. Earlier we
> > defined it as union and later changed it to structure. Instead those
> > fields can be simply defined outside of inner structure. I will fix this in v2
> patch.
> 
> That is good to know, but the inner struct is not the main issue as I see it.
> The main issue is that the change breaks the ABI.  This change must be
> implemented in some way that does not break the ABI.

Hi Ben,
Are there any guidelines or mechanism available to extend such structures which are 
defined in openvswitch.h with new fields for supporting new features?

We primarily need this feature for 'netdev' datapath and so kernel implementation need
not change.

Warm Regards,
Vishal Ajmera
Vishal Deep Ajmera Sept. 25, 2018, 9:34 a.m. | #6
> >
> > That is good to know, but the inner struct is not the main issue as I see it.
> > The main issue is that the change breaks the ABI.  This change must be
> > implemented in some way that does not break the ABI.
> 
> Hi Ben,
> Are there any guidelines or mechanism available to extend such structures which
> are defined in openvswitch.h with new fields for supporting new features?
> 
> We primarily need this feature for 'netdev' datapath and so kernel
> implementation need not change.
> 
Hi,

Can you help us in this regard? How do I extend this data structure to include more fields namely 'nd_options_type' and 'reserved'? As I mentioned we need this support only for netdev data path. Is it possible using some #defines?

Warm Regards,
Vishal Ajmera
Ben Pfaff Sept. 25, 2018, 3:49 p.m. | #7
On Tue, Sep 18, 2018 at 02:53:00PM +0000, Vishal Deep Ajmera wrote:
> 
> > > Thanks Ben. Yes, the inner structure is not required. Earlier we
> > > defined it as union and later changed it to structure. Instead those
> > > fields can be simply defined outside of inner structure. I will fix this in v2
> > patch.
> > 
> > That is good to know, but the inner struct is not the main issue as I see it.
> > The main issue is that the change breaks the ABI.  This change must be
> > implemented in some way that does not break the ABI.
> 
> Hi Ben,
> Are there any guidelines or mechanism available to extend such structures which are 
> defined in openvswitch.h with new fields for supporting new features?
> 
> We primarily need this feature for 'netdev' datapath and so kernel implementation need
> not change.

Usually, you'd add additional attributes instead of extending existing
ones.
Ben Pfaff Oct. 9, 2018, 6:26 p.m. | #8
On Tue, Oct 09, 2018 at 10:28:28AM +0000, Ashvin Lakshmikantha wrote:
>                In this specific case, it appears that the original design of the data structure is inconsistent with RFC 4861. The RFC 4861 mandates the ND message to be of the following type:
> 
> 
> 
> [cid:image005.jpg@01D45FE8.E7EB35B0]
> 
> 
> 
> 
> 
> Continuing, the options field have the following data structure:
> 
> 
> 
> [cid:image006.jpg@01D45FE8.E7EB35B0]
> 
> 
> 
> 
> 
> However, if we look at the ovs_nd_key data structure, it appears like this:
> 
> 
> 
> struct ovs_key_nd {
> 
>                __be32  nd_target[4];
> 
>                __u8      nd_sll[ETH_ALEN];
> 
>                __u8      nd_tll[ETH_ALEN];
> 
> };
> 
> 
> 
> A ND message DOES NOT have both ND_SLL and ND_TLL fields. It most commonly has only one of them. Also, a ND message has a RESERVED field (4 Bytes) and Type/Length of Options (2 Bytes).
> 
> 
> 
> In the original design, the author has removed RESERVED/Type/Length fields from ovs_nd_key and replaced it with an extra ND_TLL field. This is incorrect.
> 
> 
> 
> Our proposal fixes the original design and in addition provides a new capability to modify RESERVED field and Type Field.

The "key" structures do not exactly mirror the headers themselves.

No matter what you want, you can't break the existing ABI.  Period.
Find another way.

Patch

diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
index 3592594..3558601 100755
--- a/build-aux/extract-ofp-fields
+++ b/build-aux/extract-ofp-fields
@@ -71,7 +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_
                # if OVS didn't have those grandfathered in.  It is currently
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index aaeb034..9c7077f 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -484,9 +484,13 @@  struct ovs_key_arp {
 };
 
 struct ovs_key_nd {
-	__be32	nd_target[4];
-	__u8	nd_sll[ETH_ALEN];
-	__u8	nd_tll[ETH_ALEN];
+        __be32  nd_reserved;
+        __be32  nd_target[4];
+        __u8    nd_options_type;
+        struct {
+           __u8        nd_sll[ETH_ALEN];
+           __u8        nd_tll[ETH_ALEN];
+        };
 };
 
 #define OVS_CT_LABELS_LEN_32	4
diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index e8c80dd..800c55d 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -68,6 +68,8 @@  void match_zero_wildcarded_fields(struct match *);
 void match_set_dp_hash(struct match *, uint32_t value);
 void match_set_dp_hash_masked(struct match *, uint32_t value, uint32_t mask);
 
+void match_set_nd_reserved(struct match *, uint32_t value);
+
 void match_set_recirc_id(struct match *, uint32_t value);
 
 void match_set_conj_id(struct match *, uint32_t value);
@@ -199,6 +201,7 @@  void match_set_nw_frag(struct match *, uint8_t nw_frag);
 void match_set_nw_frag_masked(struct match *, uint8_t nw_frag, uint8_t mask);
 void match_set_icmp_type(struct match *, uint8_t);
 void match_set_icmp_code(struct match *, uint8_t);
+void match_set_nd_options_type(struct match *, uint8_t);
 void match_set_arp_sha(struct match *, const struct eth_addr);
 void match_set_arp_sha_masked(struct match *,
                               const struct eth_addr arp_sha,
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 627c8a3..9ef23df 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 128f640..2c1fe8b 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -41,6 +41,10 @@ 
 #include "unaligned.h"
 #include "util.h"
 #include "openvswitch/nsh.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(flow);
+static struct vlog_rate_limit minirl = VLOG_RATE_LIMIT_INIT(1, 5);
 
 COVERAGE_DEFINE(flow_extract);
 COVERAGE_DEFINE(miniflow_malloc);
@@ -398,7 +402,7 @@  parse_ethertype(const void **datap, size_t *sizep)
 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])
+             struct eth_addr arp_buf[2], uint8_t *opt_type)
 {
     if (icmp->icmp6_code != 0 ||
         (icmp->icmp6_type != ND_NEIGHBOR_SOLICIT &&
@@ -408,6 +412,9 @@  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] = 0;
+    opt_type[1] = 0;
+
     *nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
     if (OVS_UNLIKELY(!*nd_target)) {
         return true;
@@ -429,12 +436,14 @@  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;
+                opt_type[0] = 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;
+                opt_type[1] = lla_opt->type;
             } else {
                 goto invalid;
             }
@@ -982,18 +991,46 @@  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[2];
+                /* This holds the ND Reserved field. */
+                uint32_t *rso_flags;
+                const struct icmp6_hdr *icmp = data_pull(&data,
+                                               &size,ICMP6_HEADER_LEN);
+                rso_flags = (uint32_t *) data_pull(&data,
+                                               &size, sizeof(uint32_t));
+                if (parse_icmpv6(&data, &size, icmp,
+                                 &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] != 0) {
+                        miniflow_push_be16(mf, tcp_flags, htons(opt_type[0]));
+                        /* Pad to align with 64 bits.
+                         * This will zero out the pad3 field. */
+                        miniflow_pad_to_64(mf, tcp_flags);
+                    } else if (opt_type[1] != 0) {
+                        miniflow_push_be16(mf, tcp_flags, htons(opt_type[1]));
+                        /* 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));
@@ -1840,6 +1877,9 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
             WC_MASK_FIELD(wc, tcp_flags);
         } else if (flow->nw_proto == IPPROTO_IGMP) {
             WC_MASK_FIELD(wc, igmp_group_ip4);
+        } else if (flow->nw_proto == IPPROTO_ICMPV6) {
+            WC_MASK_FIELD(wc, tcp_flags);
+            WC_MASK_FIELD(wc, igmp_group_ip4);
         }
     }
 }
@@ -1922,6 +1962,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);
@@ -2918,6 +2960,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..4af7ad1 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -517,6 +517,18 @@  match_set_ct_tp_dst_masked(struct match *match, ovs_be16 port, ovs_be16 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));
+}
+
+void
 match_set_ct_ipv6_src(struct match *match, const struct in6_addr *src)
 {
     match->flow.ct_ipv6_src = *src;
@@ -1688,6 +1700,10 @@  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);
+        format_be32_masked(s,"nd_reserved", f->igmp_group_ip4,
+                           wc->masks.igmp_group_ip4);
+        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 b6d9e92..8059725 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -366,6 +366,11 @@  mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
         return !wc->masks.tp_dst;
+    case MFF_ND_RESERVED:
+        return !wc->masks.igmp_group_ip4;
+    case MFF_ND_OPTIONS_TYPE:
+        return !wc->masks.tcp_flags;
+
     case MFF_TCP_FLAGS:
         return !wc->masks.tcp_flags;
 
@@ -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:
@@ -922,6 +929,12 @@  mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->u8 = ntohs(flow->tp_dst);
         break;
 
+    case MFF_ND_RESERVED:
+        value->be32 = ntohl(flow->igmp_group_ip4);
+        break;
+    case MFF_ND_OPTIONS_TYPE:
+        value->u8 = ntohs(flow->tcp_flags);
+        break;
     case MFF_ND_TARGET:
         value->ipv6 = flow->nd_target;
         break;
@@ -1255,6 +1268,12 @@  mf_set_value(const struct mf_field *mf,
         match_set_icmp_code(match, value->u8);
         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_ND_TARGET:
         match_set_nd_target(match, &value->ipv6);
         break;
@@ -1664,6 +1683,12 @@  mf_set_flow_value(const struct mf_field *mf,
         flow->tp_dst = htons(value->u8);
         break;
 
+    case MFF_ND_RESERVED:
+        flow->igmp_group_ip4 = htonl(value->be32);
+        break;
+    case MFF_ND_OPTIONS_TYPE:
+        flow->tcp_flags = htons(value->u8);
+        break;
     case MFF_ND_TARGET:
         flow->nd_target = value->ipv6;
         break;
@@ -1823,6 +1848,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:
@@ -2167,6 +2194,14 @@  mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         match->wc.masks.tp_dst = htons(0);
         match->flow.tp_dst = htons(0);
         break;
+    case MFF_ND_RESERVED:
+        match->wc.masks.igmp_group_ip4 = htonl(0);
+        match->flow.igmp_group_ip4 = htonl(0);
+        break;
+   case MFF_ND_OPTIONS_TYPE:
+        match->wc.masks.tcp_flags = htons(0);
+        match->flow.tcp_flags = htons(0);
+        break;
 
     case MFF_TCP_FLAGS:
         match->wc.masks.tcp_flags = htons(0);
@@ -2285,6 +2320,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..af69ade 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -4586,6 +4586,19 @@  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 8f98032..a898567 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,
+                           ntohl(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 5831d1f..77c94b8 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -239,16 +239,25 @@  odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
         struct in6_addr tgt_buf;
         struct eth_addr sll_buf = eth_addr_zero;
         struct eth_addr tll_buf = eth_addr_zero;
+        uint32_t reserved = get_16aligned_be32(&ns->rso_flags);
+        uint8_t nd_options_type = lla_opt->type;
+
+        if (mask->nd_reserved) {
+            reserved = key->nd_reserved;
+        }
+        if (mask->nd_options_type) {
+            nd_options_type = key->nd_options_type;
+        }
 
         while (bytes_remain >= ND_LLA_OPT_LEN && lla_opt->len != 0) {
-            if (lla_opt->type == ND_OPT_SOURCE_LINKADDR
+            if (nd_options_type == ND_OPT_SOURCE_LINKADDR
                 && lla_opt->len == 1) {
                 sll_buf = lla_opt->mac;
                 ether_addr_copy_masked(&sll_buf, key->nd_sll, mask->nd_sll);
 
                 /* A packet can only contain one SLL or TLL option */
                 break;
-            } else if (lla_opt->type == ND_OPT_TARGET_LINKADDR
+            } else if (nd_options_type == ND_OPT_TARGET_LINKADDR
                        && lla_opt->len == 1) {
                 tll_buf = lla_opt->mac;
                 ether_addr_copy_masked(&tll_buf, key->nd_tll, mask->nd_tll);
@@ -265,7 +274,9 @@  odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
                       mask_ipv6_addr(ns->target.be32, &key->nd_target,
                                      &mask->nd_target, &tgt_buf),
                       sll_buf,
-                      tll_buf);
+                      tll_buf,
+                      reserved,
+                      nd_options_type);
     }
 }
 
@@ -434,7 +445,9 @@  odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
             const struct ovs_key_nd *nd_key
                    = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd));
             packet_set_nd(packet, &nd_key->nd_target, nd_key->nd_sll,
-                          nd_key->nd_tll);
+                          nd_key->nd_tll, nd_key->nd_reserved,
+                          nd_key->nd_options_type);
+
         }
         break;
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 890c71b..02351ad 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -2484,7 +2484,7 @@  const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
     [OVS_KEY_ATTR_ICMP]      = { .len = sizeof(struct ovs_key_icmp) },
     [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]        = { .len = ATTR_LEN_VARIABLE },
     [OVS_KEY_ATTR_CT_STATE]  = { .len = 4 },
     [OVS_KEY_ATTR_CT_ZONE]   = { .len = 2 },
     [OVS_KEY_ATTR_CT_MARK]   = { .len = 4 },
@@ -4018,9 +4018,14 @@  format_odp_key_attr__(const struct nlattr *a, const struct nlattr *ma,
     case OVS_KEY_ATTR_ND: {
         const struct ovs_key_nd *mask = ma ? nl_attr_get(ma) : NULL;
         const struct ovs_key_nd *key = nl_attr_get(a);
-
+        bool first = true;
+        format_be32_masked(ds, &first, "nd_reserved", htonl(key->nd_reserved),
+                           htonl(OVS_BE32_MAX));
+        ds_put_char(ds, ',');
         format_in6_addr(ds, "target", &key->nd_target, MASK(mask, nd_target),
                         verbose);
+        format_u8u(ds,"nd_options_type", key->nd_options_type,
+                   MASK(mask,nd_options_type), verbose);
         format_eth(ds, "sll", key->nd_sll, MASK(mask, nd_sll), verbose);
         format_eth(ds, "tll", key->nd_tll, MASK(mask, nd_tll), verbose);
 
@@ -5493,7 +5498,9 @@  parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
     } SCAN_END(OVS_KEY_ATTR_ARP);
 
     SCAN_BEGIN("nd(", struct ovs_key_nd) {
+        SCAN_FIELD("nd_reserved=",be32,nd_reserved);
         SCAN_FIELD("target=", in6_addr, nd_target);
+        SCAN_FIELD("nd_options_type=",u8,nd_options_type);
         SCAN_FIELD("sll=", eth, nd_sll);
         SCAN_FIELD("tll=", eth, nd_tll);
     } SCAN_END(OVS_KEY_ATTR_ND);
@@ -5858,7 +5865,9 @@  odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
 
                 nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
                                                     sizeof *nd_key);
+                nd_key->nd_reserved = ntohl(data->igmp_group_ip4);
                 nd_key->nd_target = data->nd_target;
+                nd_key->nd_options_type = ntohs(data->tcp_flags);
                 nd_key->nd_sll = data->arp_sha;
                 nd_key->nd_tll = data->arp_tha;
             }
@@ -6465,7 +6474,9 @@  parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
                     const struct ovs_key_nd *nd_key;
 
                     nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]);
+                    flow->igmp_group_ip4 = htonl(nd_key->nd_reserved);
                     flow->nd_target = nd_key->nd_target;
+                    flow->tcp_flags = htons(nd_key->nd_options_type);
                     flow->arp_sha = nd_key->nd_sll;
                     flow->arp_tha = nd_key->nd_tll;
                     if (is_mask) {
@@ -7364,7 +7375,9 @@  commit_set_icmp_action(const struct flow *flow, struct flow *base_flow,
 static void
 get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
 {
+    nd->nd_reserved = ntohl(flow->igmp_group_ip4);
     nd->nd_target = flow->nd_target;
+    nd->nd_options_type = ntohs(flow->tcp_flags);
     /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
     nd->nd_sll = flow->arp_sha;
     nd->nd_tll = flow->arp_tha;
@@ -7373,8 +7386,10 @@  get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
 static void
 put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
 {
+    flow->igmp_group_ip4 =htonl(nd->nd_reserved);
     flow->nd_target = nd->nd_target;
     /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
+    flow->tcp_flags = htons(nd->nd_options_type);
     flow->arp_sha = nd->nd_sll;
     flow->arp_tha = nd->nd_tll;
 }
diff --git a/lib/packets.c b/lib/packets.c
index 38bfb60..81b03dd 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1283,11 +1283,19 @@  packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
 
 void
 packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
-              const struct eth_addr sll, const struct eth_addr tll)
+              const struct eth_addr sll, const struct eth_addr tll,
+              uint32_t reserved, uint8_t nd_options_type)
 {
     struct ovs_nd_msg *ns;
     struct ovs_nd_lla_opt *opt;
     int bytes_remain = dp_packet_l4_size(packet);
+    int msg_size = bytes_remain;
+    struct ovs_16aligned_ip6_hdr * nh = dp_packet_l3(packet);
+    uint32_t pseudo_hdr_csum = 0;
+
+    if (nh) {
+        pseudo_hdr_csum = packet_csum_pseudoheader6(nh);
+    }
 
     if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
         return;
@@ -1296,6 +1304,7 @@  packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
     ns = dp_packet_l4(packet);
     opt = &ns->options[0];
     bytes_remain -= sizeof(*ns);
+    put_16aligned_be32(&(ns->rso_flags),reserved);
 
     if (memcmp(&ns->target, target, sizeof(ovs_be32[4]))) {
         packet_set_ipv6_addr(packet, IPPROTO_ICMPV6, ns->target.be32, target,
@@ -1303,21 +1312,17 @@  packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
     }
 
     while (bytes_remain >= ND_LLA_OPT_LEN && opt->len != 0) {
-        if (opt->type == ND_OPT_SOURCE_LINKADDR && opt->len == 1) {
+        if (nd_options_type == ND_OPT_SOURCE_LINKADDR && opt->len == 1) {
             if (!eth_addr_equals(opt->mac, sll)) {
-                ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
-
-                *csum = recalc_csum48(*csum, opt->mac, sll);
+                opt->type = 1;
                 opt->mac = sll;
             }
-
             /* A packet can only contain one SLL or TLL option */
             break;
-        } else if (opt->type == ND_OPT_TARGET_LINKADDR && opt->len == 1) {
+        } else if (nd_options_type == ND_OPT_TARGET_LINKADDR
+                   && opt->len == 1) {
             if (!eth_addr_equals(opt->mac, tll)) {
-                ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
-
-                *csum = recalc_csum48(*csum, opt->mac, tll);
+                opt->type = 2;
                 opt->mac = tll;
             }
 
@@ -1328,6 +1333,11 @@  packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
         opt += opt->len;
         bytes_remain -= opt->len * ND_LLA_OPT_LEN;
     }
+
+    ovs_be16 *csum_value = &(ns->icmph.icmp6_cksum);
+    *csum_value = 0;
+    *csum_value = csum_finish(csum_continue(pseudo_hdr_csum,
+                              &(ns->icmph), msg_size));
 }
 
 const char *
@@ -1505,13 +1515,12 @@  compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src,
 
     ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT;
     ns->icmph.icmp6_code = 0;
-    put_16aligned_be32(&ns->rso_flags, htonl(0));
 
     lla_opt = &ns->options[0];
     lla_opt->type = ND_OPT_SOURCE_LINKADDR;
     lla_opt->len = 1;
 
-    packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero);
+    packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero, 0, 1);
 
     ns->icmph.icmp6_cksum = 0;
     icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
@@ -1536,13 +1545,12 @@  compose_nd_na(struct dp_packet *b,
 
     na->icmph.icmp6_type = ND_NEIGHBOR_ADVERT;
     na->icmph.icmp6_code = 0;
-    put_16aligned_be32(&na->rso_flags, rso_flags);
 
     lla_opt = &na->options[0];
     lla_opt->type = ND_OPT_TARGET_LINKADDR;
     lla_opt->len = 1;
 
-    packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src);
+    packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src, rso_flags, 2);
 
     na->icmph.icmp6_cksum = 0;
     icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
diff --git a/lib/packets.h b/lib/packets.h
index 09a0ac3..18590f3 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1542,7 +1542,8 @@  void packet_set_udp_port(struct dp_packet *, ovs_be16 src, ovs_be16 dst);
 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);
+                   const struct eth_addr sll, const struct eth_addr tll,
+                   const ovs_be32 reserved, const uint8_t nd_options_type);
 
 void packet_format_tcp_flags(struct ds *, uint16_t);
 const char *packet_tcp_flag_to_string(uint32_t flag);
diff --git a/tests/odp.at b/tests/odp.at
index aad89e8..90df12b 100644
--- a/tests/odp.at
+++ b/tests/odp.at
@@ -19,9 +19,9 @@  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=6,tclass=0,hlimit=128,frag=no),tcp(src=80,dst=8080)
 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=17,tclass=0,hlimit=128,frag=no),udp(src=6630,dst=22)
 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=1,code=2)
-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(nd_reserved=0x0,target=::3,nd_options_type=0,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(nd_reserved=0x0,target=::3,nd_options_type=0,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(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)
@@ -124,12 +124,12 @@  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=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)
 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=17,tclass=0,hlimit=128,frag=no),udp(src=6630/0xff00,dst=22/0xff)
 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=1/0xf0,code=2)
-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=135,code=0),nd(target=::3/::250)
-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=135,code=0),nd(target=::3/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00)
-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/::250,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00)
-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/::250,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00)
+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=135,code=0),nd(nd_reserved=0x0,target=::3/::250,nd_options_type=0)
+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=135,code=0),nd(nd_reserved=0x0,target=::3/::250,nd_options_type=0,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00)
+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(nd_reserved=0x0,target=::3/::250,nd_options_type=0,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00)
+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(nd_reserved=0x0,target=::3/::250,nd_options_type=0,sll=00:05:06:07:08:09/ff:ff:ff:ff:ff:00,tll=00:0a:0b:0c:0d:0e/ff:ff:ff:ff:ff:00)
 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/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00)
-skb_mark(0x1234/0xfff0),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)
+skb_mark(0x1234/0xfff0),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(nd_reserved=0x0,target=::3,nd_options_type=0,sll=00:05:06:07:08:09,tll=00:0a:0b:0c:0d:0e)
 ])
 
 (echo '# Valid forms without tun_id or VLAN header.'
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 362c58d..9790dbc 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -269,7 +269,7 @@  AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
 AT_CHECK([ovs-appctl ofproto/trace br0 'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,icmp6,ipv6_src=fe80::1,ipv6_dst=fe80::2,nw_tos=0,nw_ttl=128,icmpv6_type=135,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11'], [0], [stdout])
 AT_CHECK([tail -4 stdout], [0],
   [Megaflow: recirc_id=0,eth,icmp6,in_port=1,nw_frag=no,icmp_type=0x87/0xff,icmp_code=0x0/0xff,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11
-Datapath actions: 10,set(nd(target=fe80::4,sll=cc:cc:cc:cc:cc:cc)),11,set(nd(target=fe80::3,sll=aa:aa:aa:aa:aa:aa)),13
+Datapath actions: 10,set(nd(nd_reserved=0x0,target=fe80::4,sll=cc:cc:cc:cc:cc:cc)),11,set(nd(nd_reserved=0x0,target=fe80::3,sll=aa:aa:aa:aa:aa:aa)),13
 This flow is handled by the userspace slow path because it:
   - Uses action(s) not supported by datapath.
 ])
@@ -9152,7 +9152,7 @@  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via no_match) data_len=86 (unbuffered)
-icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00 icmp6_csum:68bd
+icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00,nd_reserved=0,nd_options_type=1 icmp6_csum:68bd
 ])
 
 OVS_VSWITCHD_STOP
@@ -9203,7 +9203,7 @@  OVS_APP_EXIT_AND_WAIT([ovs-ofctl])
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 in_port=1 (via action) data_len=86 (unbuffered)
-icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::1,nd_sll=32:21:14:86:11:74,nd_tll=00:00:00:00:00:00 icmp6_csum:19d3
+icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::1,nd_sll=32:21:14:86:11:74,nd_tll=00:00:00:00:00:00,nd_reserved=0,nd_options_type=1 icmp6_csum:19d3
 ])
 
 OVS_VSWITCHD_STOP
@@ -9764,7 +9764,7 @@  OVS_WAIT_UNTIL([ovs-appctl -t ovs-ofctl exit])
 
 AT_CHECK([cat ofctl_monitor.log], [0], [dnl
 NXT_PACKET_IN (xid=0x0): cookie=0x0 total_len=86 ct_state=inv|trk,ipv6,in_port=2 (via action) data_len=86 (unbuffered)
-icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00 icmp6_csum:68bd
+icmp6,vlan_tci=0x0000,dl_src=00:00:86:05:80:da,dl_dst=00:60:97:07:69:ea,ipv6_src=fe80::200:86ff:fe05:80da,ipv6_dst=fe80::260:97ff:fe07:69ea,ipv6_label=0x00000,nw_tos=0,nw_ecn=0,nw_ttl=255,icmp_type=135,icmp_code=0,nd_target=fe80::260:97ff:fe07:69ea,nd_sll=00:00:86:05:80:da,nd_tll=00:00:00:00:00:00,nd_reserved=0,nd_options_type=1 icmp6_csum:68bd
 ])
 
 OVS_VSWITCHD_STOP
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 9819bc5..6a876c0 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2456,7 +2456,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
@@ -2622,6 +2622,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