summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJan Scheurich <jan.scheurich@ericsson.com>2017-08-05 13:41:08 +0800
committerBen Pfaff <blp@ovn.org>2017-08-07 11:26:09 -0700
commit3d2fbd70bda514f7327970b859663f34f994290c (patch)
tree5a5fb77fbc30f598c56c5af1d666a70bf815a796 /lib
parent05f282c2e16126577de7704b16f9dd591b927904 (diff)
downloadopenvswitch-3d2fbd70bda514f7327970b859663f34f994290c.tar.gz
userspace: Add support for NSH MD1 match fields
This patch adds support for NSH packet header fields to the OVS control plane and the userspace datapath. Initially we support the fields of the NSH base header as defined in https://www.ietf.org/id/draft-ietf-sfc-nsh-13.txt and the fixed context headers specified for metadata format MD1. The variable length MD2 format is parsed but the TLV context headers are not yet available for matching. The NSH fields are modelled as experimenter fields with the dedicated experimenter class 0x005ad650 proposed for NSH in ONF. The following fields are defined: NXOXM code ofctl name Size Comment ===================================================================== NXOXM_NSH_FLAGS nsh_flags 8 Bits 2-9 of 1st NSH word (0x005ad650,1) NXOXM_NSH_MDTYPE nsh_mdtype 8 Bits 16-23 (0x005ad650,2) NXOXM_NSH_NEXTPROTO nsh_np 8 Bits 24-31 (0x005ad650,3) NXOXM_NSH_SPI nsh_spi 24 Bits 0-23 of 2nd NSH word (0x005ad650,4) NXOXM_NSH_SI nsh_si 8 Bits 24-31 (0x005ad650,5) NXOXM_NSH_C1 nsh_c1 32 Maskable, nsh_mdtype==1 (0x005ad650,6) NXOXM_NSH_C2 nsh_c2 32 Maskable, nsh_mdtype==1 (0x005ad650,7) NXOXM_NSH_C3 nsh_c3 32 Maskable, nsh_mdtype==1 (0x005ad650,8) NXOXM_NSH_C4 nsh_c4 32 Maskable, nsh_mdtype==1 (0x005ad650,9) Co-authored-by: Johnson Li <johnson.li@intel.com> Signed-off-by: Yi Yang <yi.y.yang@intel.com> Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/flow.c103
-rw-r--r--lib/flow.h3
-rw-r--r--lib/match.c53
-rw-r--r--lib/meta-flow.c154
-rw-r--r--lib/meta-flow.xml67
-rw-r--r--lib/nx-match.c17
-rw-r--r--lib/odp-execute.c61
-rw-r--r--lib/odp-util.c243
-rw-r--r--lib/odp-util.h2
-rw-r--r--lib/ofp-util.c2
-rw-r--r--lib/packets.h3
11 files changed, 689 insertions, 19 deletions
diff --git a/lib/flow.c b/lib/flow.c
index 2ce3e12ff..b2b10aa48 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -40,6 +40,7 @@
#include "random.h"
#include "unaligned.h"
#include "util.h"
+#include "openvswitch/nsh.h"
COVERAGE_DEFINE(flow_extract);
COVERAGE_DEFINE(miniflow_malloc);
@@ -125,7 +126,7 @@ struct mf_ctx {
* away. Some GCC versions gave warnings on ALWAYS_INLINE, so these are
* defined as macros. */
-#if (FLOW_WC_SEQ != 39)
+#if (FLOW_WC_SEQ != 40)
#define MINIFLOW_ASSERT(X) ovs_assert(X)
BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime "
"assertions enabled. Consider updating FLOW_WC_SEQ after "
@@ -528,6 +529,63 @@ parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
return parse_ipv6_ext_hdrs__(datap, sizep, nw_proto, nw_frag);
}
+bool
+parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key)
+{
+ const struct nsh_hdr *nsh = (const struct nsh_hdr *) *datap;
+ uint16_t ver_flags_len;
+ uint8_t version, length, flags;
+ uint32_t path_hdr;
+
+ /* Check if it is long enough for NSH header, doesn't support
+ * MD type 2 yet
+ */
+ if (OVS_UNLIKELY(*sizep < NSH_M_TYPE1_LEN)) {
+ return false;
+ }
+
+ memset(key, 0, sizeof(struct flow_nsh));
+
+ ver_flags_len = ntohs(nsh->ver_flags_len);
+ version = (ver_flags_len & NSH_VER_MASK) >> NSH_VER_SHIFT;
+ flags = (ver_flags_len & NSH_FLAGS_MASK) >> NSH_FLAGS_SHIFT;
+
+ /* NSH header length is in 4 byte words. */
+ length = ((ver_flags_len & NSH_LEN_MASK) >> NSH_LEN_SHIFT) << 2;
+
+ if (version != 0) {
+ return false;
+ }
+
+ if (length != NSH_M_TYPE1_LEN) {
+ return false;
+ }
+
+ key->flags = flags;
+ key->mdtype = nsh->md_type;
+ key->np = nsh->next_proto;
+
+ path_hdr = ntohl(get_16aligned_be32(&nsh->path_hdr));
+ key->si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
+ key->spi = htonl((path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
+
+ switch (key->mdtype) {
+ case NSH_M_TYPE1:
+ for (size_t i = 0; i < 4; i++) {
+ key->c[i] = get_16aligned_be32(&nsh->md1.c[i]);
+ }
+ break;
+ case NSH_M_TYPE2:
+ /* Don't support MD type 2 yet, so return false */
+ default:
+ return false;
+ }
+
+ data_pull(datap, sizep, length);
+
+ return true;
+}
+
/* Initializes 'flow' members from 'packet' and 'md', taking the packet type
* into account.
*
@@ -817,6 +875,21 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
miniflow_push_macs(mf, arp_sha, arp_buf);
miniflow_pad_to_64(mf, arp_tha);
}
+ } else if (dl_type == htons(ETH_TYPE_NSH)) {
+ struct flow_nsh nsh;
+
+ if (OVS_LIKELY(parse_nsh(&data, &size, &nsh))) {
+ if (nsh.mdtype == NSH_M_TYPE1) {
+ miniflow_push_words(mf, nsh, &nsh,
+ sizeof(struct flow_nsh) /
+ sizeof(uint64_t));
+ }
+ else if (nsh.mdtype == NSH_M_TYPE2) {
+ /* parse_nsh has stopped it from arriving here for
+ * MD type 2, will add MD type 2 support code here later
+ */
+ }
+ }
}
goto out;
}
@@ -950,7 +1023,7 @@ flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
{
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
match_init_catchall(flow_metadata);
if (flow->tunnel.tun_id != htonll(0)) {
@@ -1514,7 +1587,7 @@ flow_wildcards_init_for_packet(struct flow_wildcards *wc,
memset(&wc->masks, 0x0, sizeof wc->masks);
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
if (flow_tnl_dst_is_set(&flow->tunnel)) {
if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
@@ -1613,6 +1686,13 @@ 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.c);
} else {
return; /* Unknown ethertype. */
}
@@ -1654,7 +1734,7 @@ void
flow_wc_map(const struct flow *flow, struct flowmap *map)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
flowmap_init(map);
@@ -1740,6 +1820,13 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
FLOWMAP_SET(map, nw_proto);
FLOWMAP_SET(map, arp_sha);
FLOWMAP_SET(map, arp_tha);
+ } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
+ FLOWMAP_SET(map, nsh.flags);
+ FLOWMAP_SET(map, nsh.mdtype);
+ FLOWMAP_SET(map, nsh.np);
+ FLOWMAP_SET(map, nsh.spi);
+ FLOWMAP_SET(map, nsh.si);
+ FLOWMAP_SET(map, nsh.c);
}
}
@@ -1749,7 +1836,7 @@ void
flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
@@ -1893,7 +1980,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards *wc, int idx,
uint32_t
miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
uint32_t hash = basis;
if (flow) {
@@ -1940,7 +2027,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst);
uint32_t
flow_hash_5tuple(const struct flow *flow, uint32_t basis)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
uint32_t hash = basis;
if (flow) {
@@ -2529,7 +2616,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
if (clear_flow_L3) {
/* Clear all L3 and L4 fields and dp_hash. */
- BUILD_ASSERT(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT(FLOW_WC_SEQ == 40);
memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
flow->dp_hash = 0;
diff --git a/lib/flow.h b/lib/flow.h
index 0c6069c66..6ae5a674d 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -129,6 +129,7 @@ bool flow_compose(struct dp_packet *, const struct flow *, size_t);
bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,
uint8_t *nw_frag);
ovs_be16 parse_dl_type(const struct eth_header *data_, size_t size);
+bool parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key);
static inline uint64_t
flow_get_xreg(const struct flow *flow, int idx)
@@ -914,7 +915,7 @@ static inline void
pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow)
{
/* Update this function whenever struct flow changes. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
md->recirc_id = flow->recirc_id;
md->dp_hash = flow->dp_hash;
diff --git a/lib/match.c b/lib/match.c
index f5288e31f..125a291b6 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'. */
@@ -1089,6 +1090,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)
{
@@ -1128,6 +1144,22 @@ 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%08"PRIx32"/0x%08"PRIx32,
+ ntohl(value), ntohl(mask));
+ }
+ ds_put_char(s, ',');
+ }
+}
+
+static void
+format_be32_masked_hex(struct ds *s, const char *name,
+ ovs_be32 value, ovs_be32 mask)
+{
+ if (mask != htonl(0)) {
+ ds_put_format(s, "%s%s=%s", colors.param, name, colors.end);
+ if (mask == OVS_BE32_MAX) {
+ ds_put_format(s, "0x%"PRIx32, ntohl(value));
+ } else {
ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
ntohl(value), ntohl(mask));
}
@@ -1218,6 +1250,22 @@ format_ct_label_masked(struct ds *s, const ovs_u128 *key, const ovs_u128 *mask)
}
}
+static void
+format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m)
+{
+ format_uint8_masked(s, "nsh_flags", f->nsh.flags, m->nsh.flags);
+ format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, m->nsh.mdtype);
+ format_uint8_masked(s, "nsh_np", f->nsh.np, m->nsh.np);
+ format_be32_masked_hex(s, "nsh_spi", f->nsh.spi, m->nsh.spi);
+ format_uint8_masked(s, "nsh_si", f->nsh.si, m->nsh.si);
+ if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) {
+ format_be32_masked_hex(s, "nsh_c1", f->nsh.c[0], m->nsh.c[0]);
+ format_be32_masked_hex(s, "nsh_c2", f->nsh.c[1], m->nsh.c[1]);
+ format_be32_masked_hex(s, "nsh_c3", f->nsh.c[2], m->nsh.c[2]);
+ format_be32_masked_hex(s, "nsh_c4", f->nsh.c[3], m->nsh.c[3]);
+ }
+}
+
/* Appends a string representation of 'match' to 's'. If 'priority' is
* different from OFP_DEFAULT_PRIORITY, includes it in 's'. If 'port_map' is
* nonnull, uses it to translate port numbers to names in output. */
@@ -1235,7 +1283,7 @@ match_format(const struct match *match,
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
if (priority != OFP_DEFAULT_PRIORITY) {
ds_put_format(s, "%spriority=%s%d,",
@@ -1321,7 +1369,6 @@ match_format(const struct match *match,
}
if (wc->masks.dl_type) {
- dl_type = f->dl_type;
skip_type = true;
if (dl_type == htons(ETH_TYPE_IP)) {
if (wc->masks.nw_proto) {
@@ -1459,6 +1506,8 @@ match_format(const struct match *match,
dl_type == htons(ETH_TYPE_RARP)) {
format_ip_netmask(s, "arp_spa", f->nw_src, wc->masks.nw_src);
format_ip_netmask(s, "arp_tpa", f->nw_dst, wc->masks.nw_dst);
+ } else if (dl_type == htons(ETH_TYPE_NSH)) {
+ format_nsh_masked(s, f, &wc->masks);
} else {
format_ip_netmask(s, "nw_src", f->nw_src, wc->masks.nw_src);
format_ip_netmask(s, "nw_dst", f->nw_dst, wc->masks.nw_dst);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index d0980814c..64a8cf120 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -359,6 +359,22 @@ 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:
+ case MFF_NSH_C2:
+ case MFF_NSH_C3:
+ case MFF_NSH_C4:
+ return !wc->masks.nsh.c[mf->id - MFF_NSH_C1];
+
case MFF_N_IDS:
default:
OVS_NOT_REACHED();
@@ -423,6 +439,8 @@ mf_are_prereqs_ok__(const struct mf_field *mf, const struct flow *flow,
return eth_type_mpls(dl_type);
case MFP_IP_ANY:
return is_ip_any(flow);
+ case MFP_NSH:
+ return dl_type == htons(ETH_TYPE_NSH);
case MFP_CT_VALID:
return is_ct_valid(flow, mask, wc);
case MFP_TCP:
@@ -586,6 +604,21 @@ 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:
+ return true;
+ case MFF_NSH_MDTYPE:
+ return (value->u8 == 1 || value->u8 == 2);
+ case MFF_NSH_NP:
+ return true;
+ case MFF_NSH_SPI:
+ return !(value->be32 & htonl(0xFF000000));
+ 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 +896,28 @@ 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:
+ case MFF_NSH_C2:
+ case MFF_NSH_C3:
+ case MFF_NSH_C4:
+ value->be32 = flow->nsh.c[mf->id - MFF_NSH_C1];
+ break;
+
case MFF_N_IDS:
default:
OVS_NOT_REACHED();
@@ -1156,6 +1211,28 @@ 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:
+ case MFF_NSH_C2:
+ case MFF_NSH_C3:
+ case MFF_NSH_C4:
+ MATCH_SET_FIELD_BE32(match, nsh.c[mf->id - MFF_NSH_C1], value->be32);
+ break;
+
case MFF_N_IDS:
default:
OVS_NOT_REACHED();
@@ -1525,6 +1602,28 @@ 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:
+ case MFF_NSH_C2:
+ case MFF_NSH_C3:
+ case MFF_NSH_C4:
+ flow->nsh.c[mf->id - MFF_NSH_C1] = value->be32;
+ break;
+
case MFF_N_IDS:
default:
OVS_NOT_REACHED();
@@ -1651,6 +1750,15 @@ mf_is_pipeline_field(const struct mf_field *mf)
case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
+ 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 false;
case MFF_N_IDS:
@@ -1985,6 +2093,29 @@ 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:
+ case MFF_NSH_C2:
+ case MFF_NSH_C3:
+ case MFF_NSH_C4:
+ MATCH_SET_FIELD_MASKED(match, nsh.c[mf->id - MFF_NSH_C1],
+ htonl(0), htonl(0));
+ break;
+
case MFF_N_IDS:
default:
OVS_NOT_REACHED();
@@ -2222,6 +2353,29 @@ 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:
+ case MFF_NSH_C2:
+ case MFF_NSH_C3:
+ case MFF_NSH_C4:
+ MATCH_SET_FIELD_MASKED(match, nsh.c[mf->id - MFF_NSH_C1],
+ 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 634ab69e4..3066c671b 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -689,11 +689,7 @@ tcp,tp_src=0x07c0/0xfff0
using the first 32 bits of the body as an <code>experimenter</code> field
whose most significant byte is zero and whose remaining bytes are an
Organizationally Unique Identifier (OUI) assigned by the IEEE [IEEE OUI],
- as shown below. OpenFlow says that support for experimenter fields is
- optional. Open vSwitch 2.4 and later does support them, primarily so that
- it can support the <code>ONFOXM_ET_</code>* code points defined by official
- Open Networking Foundation extensions to OpenFlow 1.3 in e.g. [TCP Flags
- Match Field Extension].
+ as shown below.
</p>
<diagram>
@@ -717,6 +713,46 @@ tcp,tp_src=0x07c0/0xfff0
</diagram>
<p>
+ OpenFlow says that support for experimenter fields is optional. Open
+ vSwitch 2.4 and later does support them, so that it can support the
+ following experimenter classes:
+ </p>
+
+ <dl>
+ <dt>0x4f4e4600 (<code>ONFOXM_ET</code>)</dt>
+ <dd>
+ Used by official Open Networking Foundation extensions to OpenFlow 1.3 in
+ e.g. [TCP Flags Match Field Extension].
+ </dd>
+
+ <dt>0x005ad650 (<code>NXOXM_NSH</code>)</dt>
+ <dd>
+ Used by Open vSwitch for NSH extensions, in the absence of an official
+ ONF-assigned class. (This OUI is randomly generated.)
+ </dd>
+ </dl>
+
+ <p>
+ OpenFlow says that support for experimenter fields is optional. Open
+ vSwitch 2.4 and later does support them, so that it can support the
+ following experimenter classes:
+ </p>
+
+ <dl>
+ <dt>0x4f4e4600 (<code>ONFOXM_ET</code>)</dt>
+ <dd>
+ Used by official Open Networking Foundation extensions to OpenFlow 1.3 in
+ e.g. [TCP Flags Match Field Extension].
+ </dd>
+
+ <dt>0x005ad650 (<code>NXOXM_NSH</code>)</dt>
+ <dd>
+ Used by Open vSwitch for NSH extensions, in the absence of an official
+ ONF-assigned class. (This OUI is randomly generated.)
+ </dd>
+ </dl>
+
+ <p>
Taken as a unit, <code>class</code> (or <code>vendor</code>),
<code>field</code>, and <code>experimenter</code> (when present) uniquely
identify a particular field.
@@ -1292,6 +1328,27 @@ tcp,tp_src=0x07c0/0xfff0
</field>
</group>
+ <group title="Network Service Header">
+ <field id="MFF_NSH_FLAGS"
+ title="flags field (8 bits)"/>
+ <field id="MFF_NSH_MDTYPE"
+ title="mdtype field (8 bits)"/>
+ <field id="MFF_NSH_NP"
+ title="np (next protocol) field (8 bits)"/>
+ <field id="MFF_NSH_SPI"
+ title="spi (service path identifier) field (24 bits)"/>
+ <field id="MFF_NSH_SI"
+ title="si (service index) field (8 bits)"/>
+ <field id="MFF_NSH_C1"
+ title="c1 (Network Platform Context) field (32 bits)"/>
+ <field id="MFF_NSH_C2"
+ title="c2 (Network Shared Context) field (32 bits)"/>
+ <field id="MFF_NSH_C3"
+ title="c3 (Service Platform Context) field (32 bits)"/>
+ <field id="MFF_NSH_C4"
+ title="c4 (Service Shared Context) field (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 571755a75..b782e8c59 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1025,7 +1025,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
int match_len;
int i;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
struct nxm_put_ctx ctx = { .output = b, .implied_ethernet = false };
@@ -1154,6 +1154,21 @@ 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(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
+ match->wc.masks.nsh.flags);
+ nxm_put_8m(&ctx, MFF_NSH_MDTYPE, oxm, flow->nsh.mdtype,
+ match->wc.masks.nsh.mdtype);
+ nxm_put_8m(&ctx, MFF_NSH_NP, oxm, flow->nsh.np,
+ match->wc.masks.nsh.np);
+ nxm_put_32m(&ctx, MFF_NSH_SPI, oxm, flow->nsh.spi,
+ match->wc.masks.nsh.spi);
+ nxm_put_8m(&ctx, MFF_NSH_SI, oxm, flow->nsh.si, match->wc.masks.nsh.si);
+ for (int i = 0; i < 4; i++) {
+ nxm_put_32m(&ctx, MFF_NSH_C1 + i, oxm, flow->nsh.c[i],
+ match->wc.masks.nsh.c[i]);
+ }
+
/* Registers. */
if (oxm < OFP15_VERSION) {
for (i = 0; i < FLOW_N_REGS; i++) {
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 03120bf06..e631c6836 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -270,6 +270,58 @@ 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)
+{
+ struct nsh_hdr *nsh = dp_packet_l3(packet);
+
+ if (!mask) {
+ nsh->ver_flags_len = htons(key->flags << NSH_FLAGS_SHIFT) |
+ (nsh->ver_flags_len & ~htons(NSH_FLAGS_MASK));
+ put_16aligned_be32(&nsh->path_hdr, key->path_hdr);
+ switch (nsh->md_type) {
+ case NSH_M_TYPE1:
+ for (int i = 0; i < 4; i++) {
+ put_16aligned_be32(&nsh->md1.c[i], key->c[i]);
+ }
+ break;
+ case NSH_M_TYPE2:
+ /* TODO */
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+ } else {
+ uint8_t flags = (ntohs(nsh->ver_flags_len) & NSH_FLAGS_MASK) >>
+ NSH_FLAGS_SHIFT;
+ flags = key->flags | (flags & ~mask->flags);
+ nsh->ver_flags_len = htons(flags << NSH_FLAGS_SHIFT) |
+ (nsh->ver_flags_len & ~htons(NSH_FLAGS_MASK));
+
+ ovs_be32 path_hdr = get_16aligned_be32(&nsh->path_hdr);
+ path_hdr = key->path_hdr | (path_hdr & ~mask->path_hdr);
+ put_16aligned_be32(&nsh->path_hdr, path_hdr);
+ switch (nsh->md_type) {
+ case NSH_M_TYPE1:
+ for (int i = 0; i < 4; i++) {
+ ovs_be32 p = get_16aligned_be32(&nsh->md1.c[i]);
+ ovs_be32 k = key->c[i];
+ ovs_be32 m = mask->c[i];
+ put_16aligned_be32(&nsh->md1.c[i], k | (p & ~m));
+ }
+ break;
+ case NSH_M_TYPE2:
+ /* TODO */
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+ }
+}
+
static void
odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
{
@@ -295,6 +347,10 @@ odp_execute_set_action(struct dp_packet *packet, const struct nlattr *a)
odp_eth_set_addrs(packet, nl_attr_get(a), NULL);
break;
+ case OVS_KEY_ATTR_NSH:
+ odp_set_nsh(packet, nl_attr_get(a), NULL);
+ break;
+
case OVS_KEY_ATTR_IPV4:
ipv4_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4));
packet_set_ipv4(packet, ipv4_key->ipv4_src,
@@ -419,6 +475,11 @@ odp_execute_masked_set_action(struct dp_packet *packet,
get_mask(a, struct ovs_key_ethernet));
break;
+ case OVS_KEY_ATTR_NSH:
+ odp_set_nsh(packet, nl_attr_get(a),
+ get_mask(a, struct ovs_key_nsh));
+ break;
+
case OVS_KEY_ATTR_IPV4:
odp_set_ipv4(packet, nl_attr_get(a),
get_mask(a, struct ovs_key_ipv4));
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 728e325ce..fa4871c4b 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -175,6 +175,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
case OVS_KEY_ATTR_PACKET_TYPE: return "packet_type";
+ case OVS_KEY_ATTR_NSH: return "nsh";
case __OVS_KEY_ATTR_MAX:
default:
@@ -247,6 +248,98 @@ format_odp_clone_action(struct ds *ds, const struct nlattr *attr,
ds_put_format(ds, ")");
}
+static void
+format_nsh_key(struct ds *ds, const struct ovs_key_nsh *key)
+{
+ ds_put_format(ds, "flags=%d", key->flags);
+ ds_put_format(ds, ",mdtype=%d", key->mdtype);
+ ds_put_format(ds, ",np=%d", key->np);
+ ds_put_format(ds, ",spi=0x%x",
+ (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
+ ds_put_format(ds, ",si=%d",
+ (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT);
+
+ switch (key->mdtype) {
+ case NSH_M_TYPE1:
+ for (int i = 0; i < 4; i++) {
+ ds_put_format(ds, ",c%d=0x%x", i + 1, ntohl(key->c[i]));
+ }
+ break;
+ case NSH_M_TYPE2:
+ /* TODO */
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+}
+
+static void
+format_uint8_masked(struct ds *s, bool *first, const char *name,
+ uint8_t value, uint8_t mask)
+{
+ if (mask != 0) {
+ if (!*first) {
+ ds_put_char(s, ',');
+ }
+ ds_put_format(s, "%s=", name);
+ if (mask == UINT8_MAX) {
+ ds_put_format(s, "%"PRIu8, value);
+ } else {
+ ds_put_format(s, "0x%02"PRIx8"/0x%02"PRIx8, value, mask);
+ }
+ *first = false;
+ }
+}
+
+static void
+format_be32_masked(struct ds *s, bool *first, const char *name,
+ ovs_be32 value, ovs_be32 mask)
+{
+ if (mask != htonl(0)) {
+ if (!*first) {
+ ds_put_char(s, ',');
+ }
+ ds_put_format(s, "%s=", name);
+ if (mask == OVS_BE32_MAX) {
+ ds_put_format(s, "0x%"PRIx32, ntohl(value));
+ } else {
+ ds_put_format(s, "0x%"PRIx32"/0x%08"PRIx32,
+ ntohl(value), ntohl(mask));
+ }
+ *first = false;
+ }
+}
+
+static void
+format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,
+ const struct ovs_key_nsh *mask)
+{
+ if (!mask) {
+ format_nsh_key(ds, key);
+ } else {
+ bool first = true;
+ uint32_t spi = (ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
+ uint32_t spi_mask = (ntohl(mask->path_hdr) & NSH_SPI_MASK) >>
+ NSH_SPI_SHIFT;
+ if (spi_mask == 0x00ffffff) {
+ spi_mask = UINT32_MAX;
+ }
+ uint8_t si = (ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
+ uint8_t si_mask = (ntohl(mask->path_hdr) & NSH_SI_MASK) >>
+ NSH_SI_SHIFT;
+
+ format_uint8_masked(ds, &first, "flags", key->flags, mask->flags);
+ format_uint8_masked(ds, &first, "mdtype", key->mdtype, mask->mdtype);
+ format_uint8_masked(ds, &first, "np", key->np, mask->np);
+ format_be32_masked(ds, &first, "spi", htonl(spi), htonl(spi_mask));
+ format_uint8_masked(ds, &first, "si", si, si_mask);
+ format_be32_masked(ds, &first, "c1", key->c[0], mask->c[0]);
+ format_be32_masked(ds, &first, "c2", key->c[1], mask->c[1]);
+ format_be32_masked(ds, &first, "c3", key->c[2], mask->c[2]);
+ format_be32_masked(ds, &first, "c4", key->c[3], mask->c[3]);
+ }
+}
+
static const char *
slow_path_reason_to_string(uint32_t reason)
{
@@ -1970,6 +2063,7 @@ static const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] =
[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4] = { .len = sizeof(struct ovs_key_ct_tuple_ipv4) },
[OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = { .len = sizeof(struct ovs_key_ct_tuple_ipv6) },
[OVS_KEY_ATTR_PACKET_TYPE] = { .len = 4 },
+ [OVS_KEY_ATTR_NSH] = { .len = sizeof(struct ovs_key_nsh) },
};
/* Returns the correct length of the payload for a flow key attribute of the
@@ -2222,6 +2316,7 @@ odp_mask_is_constant__(enum ovs_key_attr attr, const void *mask, size_t size,
case OVS_KEY_ATTR_CT_MARK:
case OVS_KEY_ATTR_CT_LABELS:
case OVS_KEY_ATTR_PACKET_TYPE:
+ case OVS_KEY_ATTR_NSH:
return is_all_byte(mask, size, u8);
case OVS_KEY_ATTR_TCP_FLAGS:
@@ -3185,6 +3280,12 @@ format_odp_key_attr__(const struct nlattr *a, const struct nlattr *ma,
ds_chomp(ds, ',');
break;
}
+ case OVS_KEY_ATTR_NSH: {
+ const struct ovs_key_nsh *mask = ma ? nl_attr_get(ma) : NULL;
+ const struct ovs_key_nsh *key = nl_attr_get(a);
+ format_nsh_key_mask(ds, key, mask);
+ break;
+ }
case OVS_KEY_ATTR_UNSPEC:
case __OVS_KEY_ATTR_MAX:
default:
@@ -3603,6 +3704,29 @@ scan_be16(const char *s, ovs_be16 *key, ovs_be16 *mask)
}
static int
+scan_be32(const char *s, ovs_be32 *key, ovs_be32 *mask)
+{
+ uint32_t key_, mask_;
+ int n;
+
+ if (ovs_scan(s, "%"SCNi32"%n", &key_, &n)) {
+ int len = n;
+
+ *key = htonl(key_);
+ if (mask) {
+ if (ovs_scan(s + len, "/%"SCNi32"%n", &mask_, &n)) {
+ len += n;
+ *mask = htonl(mask_);
+ } else {
+ *mask = OVS_BE32_MAX;
+ }
+ }
+ return len;
+ }
+ return 0;
+}
+
+static int
scan_be64(const char *s, ovs_be64 *key, ovs_be64 *mask)
{
uint64_t key_, mask_;
@@ -4403,6 +4527,17 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
SCAN_FIELD("id=", be16, id);
} SCAN_END(OVS_KEY_ATTR_PACKET_TYPE);
+ SCAN_BEGIN("nsh(", struct ovs_key_nsh) {
+ SCAN_FIELD("flags=", u8, flags);
+ SCAN_FIELD("mdtype=", u8, mdtype);
+ SCAN_FIELD("np=", u8, np);
+ SCAN_FIELD("path_hdr=", be32, path_hdr);
+ SCAN_FIELD("c1=", be32, c[0]);
+ SCAN_FIELD("c2=", be32, c[1]);
+ SCAN_FIELD("c3=", be32, c[2]);
+ SCAN_FIELD("c4=", be32, c[2]);
+ } SCAN_END(OVS_KEY_ATTR_NSH);
+
/* Encap open-coded. */
if (!strncmp(s, "encap(", 6)) {
const char *start = s;
@@ -4511,6 +4646,10 @@ static void get_arp_key(const struct flow *, struct ovs_key_arp *);
static void put_arp_key(const struct ovs_key_arp *, struct flow *);
static void get_nd_key(const struct flow *, struct ovs_key_nd *);
static void put_nd_key(const struct ovs_key_nd *, struct flow *);
+static void get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh,
+ bool is_mask);
+static void put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,
+ bool is_mask);
/* These share the same layout. */
union ovs_key_tp {
@@ -4687,6 +4826,12 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
for (i = 0; i < n; i++) {
mpls_key[i].mpls_lse = data->mpls_lse[i];
}
+ } else if (flow->dl_type == htons(ETH_TYPE_NSH)) {
+ struct ovs_key_nsh *nsh_key;
+
+ nsh_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_NSH,
+ sizeof *nsh_key);
+ get_nsh_key(data, nsh_key, export_mask);
}
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
@@ -4930,6 +5075,7 @@ odp_key_to_dp_packet(const struct nlattr *key, size_t key_len,
case OVS_KEY_ATTR_TCP_FLAGS:
case OVS_KEY_ATTR_MPLS:
case OVS_KEY_ATTR_PACKET_TYPE:
+ case OVS_KEY_ATTR_NSH:
case __OVS_KEY_ATTR_MAX:
default:
break;
@@ -5240,6 +5386,21 @@ parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
expected_bit = OVS_KEY_ATTR_ARP;
}
}
+ } else if (src_flow->dl_type == htons(ETH_TYPE_NSH)) {
+ if (!is_mask) {
+ expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_NSH;
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_NSH)) {
+ const struct ovs_key_nsh *nsh_key;
+
+ nsh_key = nl_attr_get(attrs[OVS_KEY_ATTR_NSH]);
+ put_nsh_key(nsh_key, flow, false);
+ if (is_mask) {
+ check_start = nsh_key;
+ check_len = sizeof *nsh_key;
+ expected_bit = OVS_KEY_ATTR_NSH;
+ }
+ }
} else {
goto done;
}
@@ -6282,6 +6443,87 @@ commit_set_nw_action(const struct flow *flow, struct flow *base,
return 0;
}
+static void
+get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask)
+{
+ nsh->flags = flow->nsh.flags;
+ nsh->mdtype = flow->nsh.mdtype;
+ nsh->np = flow->nsh.np;
+ nsh->path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) |
+ flow->nsh.si);
+ if (is_mask) {
+ for (int i = 0; i < 4; i++) {
+ nsh->c[i] = flow->nsh.c[i];
+ }
+ } else {
+ switch (nsh->mdtype) {
+ case NSH_M_TYPE1:
+ for (int i = 0; i < 4; i++) {
+ nsh->c[i] = flow->nsh.c[i];
+ }
+ break;
+ case NSH_M_TYPE2:
+ /* TODO: MD type 2 */
+ break;
+ }
+ }
+}
+
+static void
+put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,
+ bool is_mask OVS_UNUSED)
+{
+ flow->nsh.flags = nsh->flags;
+ flow->nsh.mdtype = nsh->mdtype;
+ flow->nsh.np = nsh->np;
+ flow->nsh.spi = htonl((ntohl(nsh->path_hdr) & NSH_SPI_MASK) >>
+ NSH_SPI_SHIFT);
+ flow->nsh.si = (ntohl(nsh->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT;
+ switch (nsh->mdtype) {
+ case NSH_M_TYPE1:
+ for (int i = 0; i < 4; i++) {
+ flow->nsh.c[i] = nsh->c[i];
+ }
+ break;
+ case NSH_M_TYPE2:
+ /* TODO: MD type 2 */
+ memset(flow->nsh.c, 0, sizeof flow->nsh.c);
+ break;
+ }
+}
+
+static void
+commit_set_nsh_action(const struct flow *flow, struct flow *base_flow,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc,
+ bool use_masked)
+{
+ struct ovs_key_nsh key, mask, base;
+
+ if (flow->dl_type != htons(ETH_TYPE_NSH) ||
+ !memcmp(&base_flow->nsh, &flow->nsh, sizeof base_flow->nsh)) {
+ return;
+ }
+
+ /* Check that mdtype and np remain unchanged. */
+ ovs_assert(flow->nsh.mdtype == base_flow->nsh.mdtype &&
+ flow->nsh.np == base_flow->nsh.np);
+
+ get_nsh_key(flow, &key, false);
+ get_nsh_key(base_flow, &base, false);
+ get_nsh_key(&wc->masks, &mask, true);
+ mask.mdtype = 0; /* Not writable. */
+ mask.np = 0; /* Not writable. */
+
+ if (commit(OVS_KEY_ATTR_NSH, use_masked, &key, &base, &mask, sizeof key,
+ odp_actions)) {
+ put_nsh_key(&base, base_flow, false);
+ if (mask.mdtype != 0) { /* Mask was changed by commit(). */
+ put_nsh_key(&mask, &wc->masks, true);
+ }
+ }
+}
+
/* TCP, UDP, and SCTP keys have the same layout. */
BUILD_ASSERT_DECL(sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_udp) &&
sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_sctp));
@@ -6445,6 +6687,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base,
commit_mpls_action(flow, base, odp_actions);
mpls_done = true;
}
+ commit_set_nsh_action(flow, base, odp_actions, wc, use_masked);
slow1 = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
commit_set_port_action(flow, base, odp_actions, wc, use_masked);
slow2 = commit_set_icmp_action(flow, base, odp_actions, wc);
diff --git a/lib/odp-util.h b/lib/odp-util.h
index bdb23545b..111cd9cff 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -147,7 +147,7 @@ void odp_portno_name_format(const struct hmap *portno_names,
* add another field and forget to adjust this value.
*/
#define ODPUTIL_FLOW_KEY_BYTES 640
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
/* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
* key. An array of "struct nlattr" might not, in theory, be sufficiently
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 354a6ce0e..86dd5cb61 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -102,7 +102,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
diff --git a/lib/packets.h b/lib/packets.h
index 297a8a94a..5083e6a83 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -25,6 +25,7 @@
#include "openvswitch/geneve.h"
#include "openvswitch/packets.h"
#include "openvswitch/types.h"
+#include "openvswitch/nsh.h"
#include "odp-netlink.h"
#include "random.h"
#include "hash.h"
@@ -397,6 +398,7 @@ ovs_be32 set_mpls_lse_values(uint8_t ttl, uint8_t tc, uint8_t bos,
#define ETH_TYPE_RARP 0x8035
#define ETH_TYPE_MPLS 0x8847
#define ETH_TYPE_MPLS_MCAST 0x8848
+#define ETH_TYPE_NSH 0x894f
static inline bool eth_type_mpls(ovs_be16 eth_type)
{
@@ -1298,6 +1300,7 @@ enum packet_type {
PT_IPV6 = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_IPV6),
PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS),
PT_MPLS_MC = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS_MCAST),
+ PT_NSH = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_NSH),
PT_UNKNOWN = PACKET_TYPE(0xffff, 0xffff), /* Unknown packet type. */
};