[ovs-dev,v1,2/5] userspace: add meta flow keys and match fields for NSH

Submitted by Yang, Yi Y on April 20, 2017, 2:42 a.m.

Details

Message ID 1492656131-110100-3-git-send-email-yi.y.yang@intel.com
State New
Headers show

Commit Message

Yang, Yi Y April 20, 2017, 2:42 a.m.
Signed-off-by: Mengke Liu <mengke.liu@intel.com>
Signed-off-by: Ricky Li <ricky.li@intel.com>
Signed-off-by: Johnson Li <johnson.li@intel.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
---
 build-aux/extract-ofp-fields    |   1 +
 include/openvswitch/match.h     |  12 +++
 include/openvswitch/meta-flow.h | 131 +++++++++++++++++++++++++++++++
 lib/flow.c                      |  26 +++---
 lib/match.c                     |  47 ++++++++++-
 lib/meta-flow.c                 | 170 ++++++++++++++++++++++++++++++++++++++++
 lib/meta-flow.xml               |  21 +++++
 lib/nx-match.c                  |  18 +++++
 8 files changed, 409 insertions(+), 17 deletions(-)

Patch hide | download patch | download mbox

diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
index d5b8a82..1390aec 100755
--- a/build-aux/extract-ofp-fields
+++ b/build-aux/extract-ofp-fields
@@ -67,6 +67,7 @@  OXM_CLASSES = {"NXM_OF_":        (0,          0x0000),
                "NXM_NX_":        (0,          0x0001),
                "OXM_OF_":        (0,          0x8000),
                "OXM_OF_PKT_REG": (0,          0x8001),
+               "OXM_NSH_":       (0,          0x8004),
                "ONFOXM_ET_":     (0x4f4e4600, 0xffff),
 
                # This is the experimenter OXM class for Nicira, which is the
diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index ce06919..5804168 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -40,6 +40,18 @@  struct match {
 /* Initializer for a "struct match" that matches every packet. */
 #define MATCH_CATCHALL_INITIALIZER { .flow = { .dl_type = 0 } }
 
+#define MATCH_SET_FIELD_MASKED(match, field, value, msk)      \
+    do {                                                      \
+        (match)->wc.masks.field = (msk);                      \
+        (match)->flow.field = (value) & (msk);                \
+    } while (0)
+
+#define MATCH_SET_FIELD_UINT8(match, field, value)            \
+    MATCH_SET_FIELD_MASKED(match, field, value, UINT8_MAX)
+
+#define MATCH_SET_FIELD_BE32(match, field, value)             \
+    MATCH_SET_FIELD_MASKED(match, field, value, OVS_BE32_MAX)
+
 void match_init(struct match *,
                 const struct flow *, const struct flow_wildcards *);
 void match_wc_init(struct match *match, const struct flow *flow);
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index 172cc3b..5bdb63f 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -1729,6 +1729,137 @@  enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_ND_TLL,
 
+/* ## ---- ## */
+/* ## NSH  ## */
+/* ## ---- ## */
+
+    /* "nsh_flags".
+     *
+     * flags field in NSH base header (8 bits).
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_FLAGS(126) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_FLAGS(1) since OF1.3 and v2.8.
+     */
+    MFF_NSH_FLAGS,
+
+    /* "nsh_mdtype".
+     *
+     * mdtype field in NSH base header (8 bits).
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_MDTYPE(127) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_MDTYPE(2) since OF1.3 and v2.8.
+     */
+    MFF_NSH_MDTYPE,
+
+    /* "nsh_np".
+     *
+     * np (next protocol) field in NSH base header (8 bits)
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_NP(128) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_NP(3) since OF1.3 and v2.8.
+     */
+    MFF_NSH_NP,
+
+    /* "nsh_spi" (aka "nsp").
+     *
+     * spi (service path identifier) field in NSH base
+     * header (24 bits).
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_SPI(129) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_SPI(4) since OF1.3 and v2.8.
+     */
+    MFF_NSH_SPI,
+
+    /* "nsh_si" (aka "nsi").
+     *
+     * si (service index) field in NSH base header (8 bits).
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_SI(130) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_SI(5) since OF1.3 and v2.8.
+     */
+    MFF_NSH_SI,
+
+    /* "nsh_c1" (aka "nshc1").
+     *
+     * c1 (Network Platform Context) field in NSH context header (32 bits)
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C1(131) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_C1(6) since OF1.3 and v2.8.
+     */
+    MFF_NSH_C1,
+
+    /* "nsh_c2" (aka "nshc2").
+     *
+     * c2 (Network Shared Context) field in NSH context header (32 bits)
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C2(132) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_C2(7) since OF1.3 and v2.8.
+     */
+    MFF_NSH_C2,
+
+    /* "nsh_c3" (aka "nshc3").
+     *
+     * c3 (Service Platform Context) field in NSH context header (32 bits)
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C3(133) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_C3(8) since OF1.3 and v2.8.
+     */
+    MFF_NSH_C3,
+
+    /* "nsh_c4" (aka "nshc4").
+     *
+     * c4 (Service Shared Context) field in NSH context header (32 bits)
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: hexadecimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: NXM_NX_NSH_C4(134) since OF1.3 and v2.8.
+     * OXM: OXM_NSH_C4(9) since OF1.3 and v2.8.
+     */
+    MFF_NSH_C4,
+
     MFF_N_IDS
 };
 
diff --git a/lib/flow.c b/lib/flow.c
index adfea1e..0500852 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -1485,22 +1485,6 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
     WC_MASK_FIELD(wc, dp_hash);
     WC_MASK_FIELD(wc, in_port);
 
-    if (flow->nsh.mdtype) {
-        WC_MASK_FIELD(wc, nsh.flags);
-        WC_MASK_FIELD(wc, nsh.mdtype);
-        WC_MASK_FIELD(wc, nsh.np);
-        WC_MASK_FIELD(wc, nsh.si);
-        WC_MASK_FIELD(wc, nsh.spi);
-        if (flow->nsh.mdtype == NSH_M_TYPE1) {
-            WC_MASK_FIELD(wc, nsh.c1);
-            WC_MASK_FIELD(wc, nsh.c2);
-            WC_MASK_FIELD(wc, nsh.c3);
-            WC_MASK_FIELD(wc, nsh.c4);
-        } else if (flow->nsh.mdtype == NSH_M_TYPE2) {
-            /* TODO */
-        }
-    }
-
     /* actset_output wildcarded. */
     if (flow->packet_type == htonl(PT_ETH)) {
         WC_MASK_FIELD(wc, dl_dst);
@@ -1552,6 +1536,16 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
             }
         }
         return;
+    } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
+        WC_MASK_FIELD(wc, nsh.flags);
+        WC_MASK_FIELD(wc, nsh.mdtype);
+        WC_MASK_FIELD(wc, nsh.np);
+        WC_MASK_FIELD(wc, nsh.spi);
+        WC_MASK_FIELD(wc, nsh.si);
+        WC_MASK_FIELD(wc, nsh.c1);
+        WC_MASK_FIELD(wc, nsh.c2);
+        WC_MASK_FIELD(wc, nsh.c3);
+        WC_MASK_FIELD(wc, nsh.c4);
     } else {
         return; /* Unknown ethertype. */
     }
diff --git a/lib/match.c b/lib/match.c
index f819ebb..32ac058 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -24,6 +24,7 @@ 
 #include "openvswitch/ofp-util.h"
 #include "packets.h"
 #include "tun-metadata.h"
+#include "openvswitch/nsh.h"
 
 /* Converts the flow in 'flow' into a match in 'match', with the given
  * 'wildcards'. */
@@ -1033,6 +1034,21 @@  format_ipv6_netmask(struct ds *s, const char *name,
 }
 
 static void
+format_uint8_masked(struct ds *s, const char *name,
+                   uint8_t value, uint8_t mask)
+{
+    if (mask != 0) {
+        ds_put_format(s, "%s%s=%s", colors.param, name, colors.end);
+        if (mask == UINT8_MAX) {
+            ds_put_format(s, "%"PRIu8, value);
+        } else {
+            ds_put_format(s, "0x%02"PRIx8"/0x%02"PRIx8, value, mask);
+        }
+        ds_put_char(s, ',');
+    }
+}
+
+static void
 format_uint16_masked(struct ds *s, const char *name,
                    uint16_t value, uint16_t mask)
 {
@@ -1072,7 +1088,7 @@  format_be32_masked(struct ds *s, const char *name,
         if (mask == OVS_BE32_MAX) {
             ds_put_format(s, "%"PRIu32, ntohl(value));
         } else {
-            ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
+            ds_put_format(s, "0x%08"PRIx32"/0x%08"PRIx32,
                           ntohl(value), ntohl(mask));
         }
         ds_put_char(s, ',');
@@ -1162,6 +1178,33 @@  format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
     }
 }
 
+static void
+format_flow_nsh(struct ds *s, const struct match *match)
+{
+    const struct flow_wildcards *wc = &match->wc;
+    const struct flow *f = &match->flow;
+
+    if (wc->masks.nsh.flags)
+        format_uint8_masked(s, "nsh_flags", f->nsh.flags, wc->masks.nsh.flags);
+    if (wc->masks.nsh.mdtype)
+        format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, wc->masks.nsh.mdtype);
+    if (wc->masks.nsh.np)
+        format_uint8_masked(s, "nsh_np", f->nsh.np, wc->masks.nsh.np);
+    if (wc->masks.nsh.spi)
+        format_be32_masked(s, "nsh_spi", f->nsh.spi, wc->masks.nsh.spi);
+    if (wc->masks.nsh.si)
+        format_uint8_masked(s, "nsh_si", f->nsh.si, wc->masks.nsh.si);
+
+    if (wc->masks.nsh.c1)
+        format_be32_masked(s, "nsh_c1", f->nsh.c1, wc->masks.nsh.c1);
+    if (wc->masks.nsh.c2)
+        format_be32_masked(s, "nsh_c2", f->nsh.c2, wc->masks.nsh.c2);
+    if (wc->masks.nsh.c3)
+        format_be32_masked(s, "nsh_c3", f->nsh.c3, wc->masks.nsh.c3);
+    if (wc->masks.nsh.c4)
+        format_be32_masked(s, "nsh_c4", f->nsh.c4, wc->masks.nsh.c4);
+}
+
 /* Appends a string representation of 'match' to 's'.  If 'priority' is
  * different from OFP_DEFAULT_PRIORITY, includes it in 's'. */
 void
@@ -1313,6 +1356,8 @@  match_format(const struct match *match, struct ds *s, int priority)
             } else {
                 ds_put_format(s, "%sipv6%s,", colors.value, colors.end);
             }
+        } else if (f->dl_type == htons(ETH_TYPE_NSH)) {
+            format_flow_nsh(s, match);
         } else if (dl_type == htons(ETH_TYPE_ARP)) {
             ds_put_format(s, "%sarp%s,", colors.value, colors.end);
         } else if (dl_type == htons(ETH_TYPE_RARP)) {
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index eac7fbc..eb91440 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -359,6 +359,25 @@  mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_TCP_FLAGS:
         return !wc->masks.tcp_flags;
 
+    case MFF_NSH_FLAGS:
+        return !wc->masks.nsh.flags;
+    case MFF_NSH_MDTYPE:
+        return !wc->masks.nsh.mdtype;
+    case MFF_NSH_NP:
+        return !wc->masks.nsh.np;
+    case MFF_NSH_SPI:
+        return !wc->masks.nsh.spi;
+    case MFF_NSH_SI:
+        return !wc->masks.nsh.si;
+    case MFF_NSH_C1:
+        return !wc->masks.nsh.c1;
+    case MFF_NSH_C2:
+        return !wc->masks.nsh.c2;
+    case MFF_NSH_C3:
+        return !wc->masks.nsh.c3;
+    case MFF_NSH_C4:
+        return !wc->masks.nsh.c4;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -586,6 +605,17 @@  mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_CT_STATE:
         return !(value->be32 & ~htonl(CS_SUPPORTED_MASK));
 
+    case MFF_NSH_FLAGS:
+    case MFF_NSH_MDTYPE:
+    case MFF_NSH_NP:
+    case MFF_NSH_SPI:
+    case MFF_NSH_SI:
+    case MFF_NSH_C1:
+    case MFF_NSH_C2:
+    case MFF_NSH_C3:
+    case MFF_NSH_C4:
+        return true;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -863,6 +893,34 @@  mf_get_value(const struct mf_field *mf, const struct flow *flow,
         value->ipv6 = flow->nd_target;
         break;
 
+    case MFF_NSH_FLAGS:
+        value->u8 = flow->nsh.flags;
+        break;
+    case MFF_NSH_MDTYPE:
+        value->u8 = flow->nsh.mdtype;
+        break;
+    case MFF_NSH_NP:
+        value->u8 = flow->nsh.np;
+        break;
+    case MFF_NSH_SPI:
+        value->be32 = flow->nsh.spi;
+        break;
+    case MFF_NSH_SI:
+        value->u8 = flow->nsh.si;
+        break;
+    case MFF_NSH_C1:
+        value->be32 = flow->nsh.c1;
+        break;
+    case MFF_NSH_C2:
+        value->be32 = flow->nsh.c2;
+        break;
+    case MFF_NSH_C3:
+        value->be32 = flow->nsh.c3;
+        break;
+    case MFF_NSH_C4:
+        value->be32 = flow->nsh.c4;
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1156,6 +1214,34 @@  mf_set_value(const struct mf_field *mf,
         match_set_nd_target(match, &value->ipv6);
         break;
 
+    case MFF_NSH_FLAGS:
+        MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8);
+        break;
+    case MFF_NSH_MDTYPE:
+        MATCH_SET_FIELD_UINT8(match, nsh.mdtype, value->u8);
+        break;
+    case MFF_NSH_NP:
+        MATCH_SET_FIELD_UINT8(match, nsh.np, value->u8);
+        break;
+    case MFF_NSH_SPI:
+        MATCH_SET_FIELD_BE32(match, nsh.spi, value->be32);
+        break;
+    case MFF_NSH_SI:
+        MATCH_SET_FIELD_UINT8(match, nsh.si, value->u8);
+        break;
+    case MFF_NSH_C1:
+        MATCH_SET_FIELD_BE32(match, nsh.c1, value->be32);
+        break;
+    case MFF_NSH_C2:
+        MATCH_SET_FIELD_BE32(match, nsh.c2, value->be32);
+        break;
+    case MFF_NSH_C3:
+        MATCH_SET_FIELD_BE32(match, nsh.c3, value->be32);
+        break;
+    case MFF_NSH_C4:
+        MATCH_SET_FIELD_BE32(match, nsh.c4, value->be32);
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1525,6 +1611,34 @@  mf_set_flow_value(const struct mf_field *mf,
         flow->nd_target = value->ipv6;
         break;
 
+    case MFF_NSH_FLAGS:
+        flow->nsh.flags = value->u8;
+        break;
+    case MFF_NSH_MDTYPE:
+        flow->nsh.mdtype = value->u8;
+        break;
+    case MFF_NSH_NP:
+        flow->nsh.np = value->u8;
+        break;
+    case MFF_NSH_SPI:
+        flow->nsh.spi = value->be32;
+        break;
+    case MFF_NSH_SI:
+        flow->nsh.si = value->u8;
+        break;
+    case MFF_NSH_C1:
+        flow->nsh.c1 = value->be32;
+        break;
+    case MFF_NSH_C2:
+        flow->nsh.c2 = value->be32;
+        break;
+    case MFF_NSH_C3:
+        flow->nsh.c3 = value->be32;
+        break;
+    case MFF_NSH_C4:
+        flow->nsh.c4 = value->be32;
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -1894,6 +2008,34 @@  mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
         memset(&match->flow.nd_target, 0, sizeof match->flow.nd_target);
         break;
 
+    case MFF_NSH_FLAGS:
+        MATCH_SET_FIELD_MASKED(match, nsh.flags, 0, 0);
+        break;
+    case MFF_NSH_MDTYPE:
+        MATCH_SET_FIELD_MASKED(match, nsh.mdtype, 0, 0);
+        break;
+    case MFF_NSH_NP:
+        MATCH_SET_FIELD_MASKED(match, nsh.np, 0, 0);
+        break;
+    case MFF_NSH_SPI:
+        MATCH_SET_FIELD_MASKED(match, nsh.spi, htonl(0), htonl(0));
+        break;
+    case MFF_NSH_SI:
+        MATCH_SET_FIELD_MASKED(match, nsh.si, 0, 0);
+        break;
+    case MFF_NSH_C1:
+        MATCH_SET_FIELD_MASKED(match, nsh.c1, htonl(0), htonl(0));
+        break;
+    case MFF_NSH_C2:
+        MATCH_SET_FIELD_MASKED(match, nsh.c2, htonl(0), htonl(0));
+        break;
+    case MFF_NSH_C3:
+        MATCH_SET_FIELD_MASKED(match, nsh.c3, htonl(0), htonl(0));
+        break;
+    case MFF_NSH_C4:
+        MATCH_SET_FIELD_MASKED(match, nsh.c4, htonl(0), htonl(0));
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
@@ -2131,6 +2273,34 @@  mf_set(const struct mf_field *mf,
         match_set_tcp_flags_masked(match, value->be16, mask->be16);
         break;
 
+    case MFF_NSH_FLAGS:
+        MATCH_SET_FIELD_MASKED(match, nsh.flags, value->u8, mask->u8);
+        break;
+    case MFF_NSH_MDTYPE:
+        MATCH_SET_FIELD_MASKED(match, nsh.mdtype, value->u8, mask->u8);
+        break;
+    case MFF_NSH_NP:
+        MATCH_SET_FIELD_MASKED(match, nsh.np, value->u8, mask->u8);
+        break;
+    case MFF_NSH_SPI:
+        MATCH_SET_FIELD_MASKED(match, nsh.spi, value->be32, mask->be32);
+        break;
+    case MFF_NSH_SI:
+        MATCH_SET_FIELD_MASKED(match, nsh.si, value->u8, mask->u8);
+        break;
+    case MFF_NSH_C1:
+        MATCH_SET_FIELD_MASKED(match, nsh.c1, value->be32, mask->be32);
+        break;
+    case MFF_NSH_C2:
+        MATCH_SET_FIELD_MASKED(match, nsh.c2, value->be32, mask->be32);
+        break;
+    case MFF_NSH_C3:
+        MATCH_SET_FIELD_MASKED(match, nsh.c3, value->be32, mask->be32);
+        break;
+    case MFF_NSH_C4:
+        MATCH_SET_FIELD_MASKED(match, nsh.c4, value->be32, mask->be32);
+        break;
+
     case MFF_N_IDS:
     default:
         OVS_NOT_REACHED();
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 3183968..fca36f9 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -1276,6 +1276,27 @@  tcp,tp_src=0x07c0/0xfff0
     </field>
   </group>
 
+  <group title="Network Service Header">
+    <field id="MFF_NSH_FLAGS"
+        title="flags field in NSH base header (8 bits)"/>
+    <field id="MFF_NSH_MDTYPE"
+        title="mdtype field in NSH base header (8 bits)"/>
+    <field id="MFF_NSH_NP"
+        title="np (next protocol) field in NSH base header (8 bits)"/>
+    <field id="MFF_NSH_SPI"
+        title="spi (service path identifier) field in NSH base header (24 bits)"/>
+    <field id="MFF_NSH_SI"
+        title="si (service index) field in NSH base header (8 bits)"/>
+    <field id="MFF_NSH_C1"
+        title="c1 (Network Platform Context) field in NSH context header (32 bits)"/>
+    <field id="MFF_NSH_C2"
+        title="c2 (Network Shared Context) field in NSH context header (32 bits)"/>
+    <field id="MFF_NSH_C3"
+        title="c3 (Service Platform Context) field in NSH context header (32 bits)"/>
+    <field id="MFF_NSH_C4"
+        title="c4 (Service Shared Context) field in NSH context header (32 bits)"/>
+  </group>
+
   <group title="Tunnel">
     <p>
       The fields in this group relate to tunnels, which Open vSwitch
diff --git a/lib/nx-match.c b/lib/nx-match.c
index b42e85f..1698b7b 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1115,6 +1115,24 @@  nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
                flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags);
     tun_metadata_to_nx_match(b, oxm, match);
 
+    /* Network Service Header */
+    nxm_put_8m(b, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
+            match->wc.masks.nsh.flags);
+    nxm_put_8m(b, MFF_NSH_MDTYPE, oxm, flow->nsh.mdtype,
+            match->wc.masks.nsh.mdtype);
+    nxm_put_8m(b, MFF_NSH_NP, oxm, flow->nsh.np,
+            match->wc.masks.nsh.np);
+    nxm_put_32m(b, MFF_NSH_SPI, oxm, flow->nsh.spi, match->wc.masks.nsh.spi);
+    nxm_put_8m(b, MFF_NSH_SI, oxm, flow->nsh.si, match->wc.masks.nsh.si);
+    nxm_put_32m(b, MFF_NSH_C1, oxm, flow->nsh.c1,
+            match->wc.masks.nsh.c1);
+    nxm_put_32m(b, MFF_NSH_C2, oxm, flow->nsh.c2,
+            match->wc.masks.nsh.c2);
+    nxm_put_32m(b, MFF_NSH_C3, oxm, flow->nsh.c3,
+            match->wc.masks.nsh.c3);
+    nxm_put_32m(b, MFF_NSH_C4, oxm, flow->nsh.c4,
+            match->wc.masks.nsh.c4);
+
     /* Registers. */
     if (oxm < OFP15_VERSION) {
         for (i = 0; i < FLOW_N_REGS; i++) {