diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 221c4d6..10d960b 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -273,20 +273,25 @@ odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
 /* Set the NSH header. Assumes the NSH header is present and matches the
  * MD format of the key. The slow path must take case of that. */
 static void
-odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
-            const struct ovs_key_nsh *mask)
+odp_set_nsh(struct dp_packet *packet, const struct nlattr *a, bool has_mask)
 {
+    struct ovs_key_nsh key, mask;
     struct nsh_hdr *nsh = dp_packet_l3(packet);
     uint8_t mdtype = nsh_md_type(nsh);
     ovs_be32 path_hdr;
 
-    if (!mask) {
-        nsh_set_flags_and_ttl(nsh, key->flags, key->ttl);
-        put_16aligned_be32(&nsh->path_hdr, key->path_hdr);
+    if (has_mask)
+        odp_nsh_key_from_attr(a, &key, &mask);
+    else
+        odp_nsh_key_from_attr(a, &key, NULL);
+
+    if (!has_mask) {
+        nsh_set_flags_and_ttl(nsh, key.flags, key.ttl);
+        put_16aligned_be32(&nsh->path_hdr, key.path_hdr);
         switch (mdtype) {
             case NSH_M_TYPE1:
                 for (int i = 0; i < 4; i++) {
-                    put_16aligned_be32(&nsh->md1.context[i], key->context[i]);
+                    put_16aligned_be32(&nsh->md1.context[i], key.context[i]);
                 }
                 break;
             case NSH_M_TYPE2:
@@ -298,19 +303,19 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
         uint8_t flags = nsh_get_flags(nsh);
         uint8_t ttl = nsh_get_ttl(nsh);
 
-        flags = key->flags | (flags & ~mask->flags);
-        ttl = key->ttl | (ttl & ~mask->ttl);
+        flags = key.flags | (flags & ~mask.flags);
+        ttl = key.ttl | (ttl & ~mask.ttl);
         nsh_set_flags_and_ttl(nsh, flags, ttl);
 
         uint32_t spi = ntohl(nsh_get_spi(nsh));
         uint8_t si = nsh_get_si(nsh);
-        uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask->path_hdr);
-        uint8_t si_mask = nsh_path_hdr_to_si(mask->path_hdr);
+        uint32_t spi_mask = nsh_path_hdr_to_spi_uint32(mask.path_hdr);
+        uint8_t si_mask = nsh_path_hdr_to_si(mask.path_hdr);
         if (spi_mask == 0x00ffffff) {
             spi_mask = UINT32_MAX;
         }
-        spi = nsh_path_hdr_to_spi_uint32(key->path_hdr) | (spi & ~spi_mask);
-        si = nsh_path_hdr_to_si(key->path_hdr) | (si & ~si_mask);
+        spi = nsh_path_hdr_to_spi_uint32(key.path_hdr) | (spi & ~spi_mask);
+        si = nsh_path_hdr_to_si(key.path_hdr) | (si & ~si_mask);
         path_hdr = nsh_get_path_hdr(nsh);
         nsh_path_hdr_set_spi(&path_hdr, htonl(spi));
         nsh_path_hdr_set_si(&path_hdr, si);
@@ -319,8 +324,8 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
             case NSH_M_TYPE1:
                 for (int i = 0; i < 4; i++) {
                     ovs_be32 p = get_16aligned_be32(&nsh->md1.context[i]);
-                    ovs_be32 k = key->context[i];
-                    ovs_be32 m = mask->context[i];
+                    ovs_be32 k = key.context[i];
+                    ovs_be32 m = mask.context[i];
                     put_16aligned_be32(&nsh->md1.context[i], k | (p & ~m));
                 }
                 break;
@@ -358,9 +363,7 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
         break;
 
     case OVS_KEY_ATTR_NSH: {
-        struct ovs_key_nsh nsh;
-        odp_nsh_key_from_attr(a, &nsh);
-        odp_set_nsh(packet, &nsh, NULL);
+        odp_set_nsh(packet, a, false);
         break;
     }
 
@@ -489,22 +492,7 @@ odp_execute_masked_set_action(struct dp_packet *packet,
         break;
 
     case OVS_KEY_ATTR_NSH: {
-        struct ovs_key_nsh nsh, nsh_mask;
-        size_t size = nl_attr_get_size(a) / 2;
-        struct {
-            struct nlattr nla;
-            uint8_t data[size];
-        } attr, mask;
-
-        mask.nla.nla_type = attr.nla.nla_type = nl_attr_type(a);
-        mask.nla.nla_len = attr.nla.nla_len = NLA_HDRLEN + size;
-        memcpy(attr.data, (char *)(a + 1), size);
-        memcpy(mask.data, (char *)(a + 1) + size, size);
-
-        odp_nsh_key_from_attr(&attr.nla, &nsh);
-        odp_nsh_key_from_attr(&mask.nla, &nsh_mask);
-        odp_set_nsh(packet, &nsh, &nsh_mask);
-
+        odp_set_nsh(packet, a, true);
         break;
     }
 
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 7597814..922667f 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -937,6 +937,68 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
     }
 }
 
+static const struct attr_len_tbl
+ovs_nsh_key_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {
+    [OVS_NSH_KEY_ATTR_BASE]     = { .len = 8 },
+    [OVS_NSH_KEY_ATTR_MD1]      = { .len = 16 },
+    [OVS_NSH_KEY_ATTR_MD2]      = { .len = ATTR_LEN_VARIABLE },
+};
+
+static void
+format_odp_set_nsh(struct ds *ds, const struct nlattr *attr)
+{
+    unsigned int left;
+    const struct nlattr *a;
+    struct ovs_key_nsh nsh;
+    struct ovs_key_nsh nsh_mask;
+
+    memset(&nsh, 0, sizeof nsh);
+    memset(&nsh_mask, 0xff, sizeof nsh_mask);
+
+    NL_NESTED_FOR_EACH (a, left, attr) {
+        enum ovs_nsh_key_attr type = nl_attr_type(a);
+        size_t len = nl_attr_get_size(a);
+
+        if (type >= OVS_NSH_KEY_ATTR_MAX) {
+            return;
+        }
+
+        int expected_len = ovs_nsh_key_attr_lens[type].len;
+        if ((expected_len != ATTR_LEN_VARIABLE) && (len != 2 * expected_len)) {
+            return;
+        }
+
+        switch (type) {
+        case OVS_NSH_KEY_ATTR_UNSPEC:
+            break;
+        case OVS_NSH_KEY_ATTR_BASE: {
+            const struct ovs_nsh_key_base *base = nl_attr_get(a);
+            const struct ovs_nsh_key_base *base_mask = base + 1;
+            memcpy(&nsh, base, sizeof(*base));
+            memcpy(&nsh_mask, base_mask, sizeof(*base_mask));
+            break;
+        }
+        case OVS_NSH_KEY_ATTR_MD1: {
+            const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
+            const struct ovs_nsh_key_md1 *md1_mask = md1 + 1;
+            memcpy(&nsh.context, &md1->context, sizeof(*md1));
+            memcpy(&nsh_mask.context, &md1_mask->context, sizeof(*md1_mask));
+            break;
+        }
+        case OVS_NSH_KEY_ATTR_MD2:
+        case __OVS_NSH_KEY_ATTR_MAX:
+        default:
+            /* No support for matching other metadata formats yet. */
+            break;
+        }
+    }
+
+    ds_put_cstr(ds, "set(nsh(");
+    format_nsh_key_mask(ds, &nsh, &nsh_mask);
+    ds_put_cstr(ds, "))");
+}
+
+
 static void
 format_odp_action(struct ds *ds, const struct nlattr *a,
                   const struct hmap *portno_names)
@@ -987,6 +1049,11 @@ format_odp_action(struct ds *ds, const struct nlattr *a,
         break;
     case OVS_ACTION_ATTR_SET_MASKED:
         a = nl_attr_get(a);
+        /* OVS_KEY_ATTR_NSH is nested attribute, so it needs special process */
+        if (nl_attr_type(a) == OVS_KEY_ATTR_NSH) {
+            format_odp_set_nsh(ds, a);
+            break;
+        }
         size = nl_attr_get_size(a) / 2;
         ds_put_cstr(ds, "set(");
 
@@ -2258,13 +2325,6 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +
     [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = 16 },
 };
 
-static const struct attr_len_tbl
-ovs_nsh_key_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = {
-    [OVS_NSH_KEY_ATTR_BASE]     = { .len = 8 },
-    [OVS_NSH_KEY_ATTR_MD1]      = { .len = 16 },
-    [OVS_NSH_KEY_ATTR_MD2]      = { .len = ATTR_LEN_VARIABLE },
-};
-
 const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
     [OVS_KEY_ATTR_ENCAP]     = { .len = ATTR_LEN_NESTED },
     [OVS_KEY_ATTR_PRIORITY]  = { .len = 4 },
@@ -2429,7 +2489,8 @@ odp_nsh_hdr_from_attr(const struct nlattr *attr,
 }
 
 enum odp_key_fitness
-odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh)
+odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh,
+                      struct ovs_key_nsh *nsh_mask)
 {
     unsigned int left;
     const struct nlattr *a;
@@ -2442,7 +2503,14 @@ odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh)
         int expected_len = odp_key_attr_len(ovs_nsh_key_attr_lens,
                                             OVS_NSH_KEY_ATTR_MAX, type);
 
-        if (len != expected_len && expected_len >= 0) {
+        /* the attribute can have mask, len is 2 * expected_len for that case.          */
+        if ((len != expected_len) && (len != 2 * expected_len) &&
+            (expected_len >= 0)) {
+            return ODP_FIT_ERROR;
+        }
+
+        if ((nsh_mask && (expected_len >= 0) && (len != 2 * expected_len)) ||
+            (!nsh_mask && (expected_len >= 0) && (len == 2 * expected_len))) {
             return ODP_FIT_ERROR;
         }
 
@@ -2450,12 +2518,21 @@ odp_nsh_key_from_attr(const struct nlattr *attr, struct ovs_key_nsh *nsh)
         case OVS_NSH_KEY_ATTR_BASE: {
             const struct ovs_nsh_key_base *base = nl_attr_get(a);
             memcpy(nsh, base, sizeof(*base));
+            if (nsh_mask && (len == 2 * sizeof(*base))) {
+               const struct ovs_nsh_key_base *base_mask = base + 1;
+               memcpy(nsh_mask, base_mask, sizeof(*base_mask));
+            }
             break;
         }
         case OVS_NSH_KEY_ATTR_MD1: {
             const struct ovs_nsh_key_md1 *md1 = nl_attr_get(a);
             has_md1 = true;
             memcpy(&nsh->context, &md1->context, sizeof(*md1));
+            if (len == 2 * sizeof(*md1)) {
+                const struct ovs_nsh_key_md1 *md1_mask = md1 + 1;
+                memcpy(&nsh_mask->context, &md1_mask->context,
+                       sizeof(*md1_mask));
+            }
             break;
         }
         case OVS_NSH_KEY_ATTR_MD2:
@@ -3195,6 +3272,8 @@ format_odp_nsh_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
         }
 
         switch (type) {
+        case OVS_NSH_KEY_ATTR_UNSPEC:
+            break;
         case OVS_NSH_KEY_ATTR_BASE: {
             const struct ovs_nsh_key_base *base = nl_attr_get(a);
             const struct ovs_nsh_key_base *base_mask
@@ -5938,7 +6017,7 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NSH;
         }
         if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NSH)) {
-            odp_nsh_key_from_attr(attrs[OVS_KEY_ATTR_NSH], &flow->nsh);
+            odp_nsh_key_from_attr(attrs[OVS_KEY_ATTR_NSH], &flow->nsh, NULL);
             if (is_mask) {
                 check_start = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);
                 check_len = nl_attr_get_size(attrs[OVS_KEY_ATTR_NSH]);
@@ -7040,23 +7119,28 @@ commit_nsh(const struct ovs_key_nsh * flow_nsh, bool use_masked_set,
         /* OVS_KEY_ATTR_NSH keys */
         nsh_key_ofs = nl_msg_start_nested(odp_actions, OVS_KEY_ATTR_NSH);
 
+        /* put value and mask for OVS_NSH_KEY_ATTR_BASE */
         char *data = nl_msg_put_unspec_uninit(odp_actions,
                                               OVS_NSH_KEY_ATTR_BASE,
-                                              sizeof(nsh_base));
+                                              2 * sizeof(nsh_base));
         const char *lkey = (char *)&nsh_base, *lmask = (char *)&nsh_base_mask;
         size_t lkey_size = sizeof(nsh_base);
 
         while (lkey_size--) {
             *data++ = *lkey++ & *lmask++;
         }
+        lmask = (char *)&nsh_base_mask;
+        memcpy(data, lmask, sizeof(nsh_base_mask));
 
         switch (key->mdtype) {
         case NSH_M_TYPE1:
             memcpy(&md1.context, &key->context, sizeof(md1));
             memcpy(&md1_mask.context, &mask->context, sizeof(md1_mask));
+
+            /* put value and mask for OVS_NSH_KEY_ATTR_MD1 */
             data = nl_msg_put_unspec_uninit(odp_actions,
                                             OVS_NSH_KEY_ATTR_MD1,
-                                            sizeof(md1));
+                                            2 * sizeof(md1));
             lkey = (char *)&md1;
             lmask = (char *)&md1_mask;
             lkey_size = sizeof(md1);
@@ -7064,26 +7148,6 @@ commit_nsh(const struct ovs_key_nsh * flow_nsh, bool use_masked_set,
             while (lkey_size--) {
                 *data++ = *lkey++ & *lmask++;
             }
-            break;
-        case NSH_M_TYPE2:
-        default:
-            /* No match support for other MD formats yet. */
-            break;
-        }
-
-        /* OVS_KEY_ATTR_NSH masks */
-        data = nl_msg_put_unspec_uninit(odp_actions,
-                                        OVS_NSH_KEY_ATTR_BASE,
-                                        sizeof(nsh_base_mask));
-        lmask = (char *)&nsh_base_mask;
-
-        memcpy(data, lmask, sizeof(nsh_base_mask));
-
-        switch (key->mdtype) {
-        case NSH_M_TYPE1:
-            data = nl_msg_put_unspec_uninit(odp_actions,
-                                            OVS_NSH_KEY_ATTR_MD1,
-                                            sizeof(md1_mask));
             lmask = (char *)&md1_mask;
             memcpy(data, lmask, sizeof(md1_mask));
             break;
@@ -7092,6 +7156,7 @@ commit_nsh(const struct ovs_key_nsh * flow_nsh, bool use_masked_set,
             /* No match support for other MD formats yet. */
             break;
         }
+
         nl_msg_end_nested(odp_actions, nsh_key_ofs);
 
         nl_msg_end_nested(odp_actions, offset);
diff --git a/lib/odp-util.h b/lib/odp-util.h
index fafea62..d99727b 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -159,9 +159,10 @@ struct odputil_keybuf {
 enum odp_key_fitness odp_tun_key_from_attr(const struct nlattr *,
                                            struct flow_tnl *);
 enum odp_key_fitness odp_nsh_key_from_attr(const struct nlattr *,
+                                           struct ovs_key_nsh *,
                                            struct ovs_key_nsh *);
 enum odp_key_fitness odp_nsh_hdr_from_attr(const struct nlattr *,
-                                           struct nsh_hdr *, size_t size);
+                                           struct nsh_hdr *, size_t);
 
 int odp_ufid_from_string(const char *s_, ovs_u128 *ufid);
 void odp_format_ufid(const ovs_u128 *ufid, struct ds *);
