@@ -46,6 +46,7 @@ PREREQS = {"none": "MFP_NONE",
"IPv4": "MFP_IPV4",
"IPv6": "MFP_IPV6",
"IPv4/IPv6": "MFP_IP_ANY",
+ "NSH": "MFP_NSH",
"CT": "MFP_CT_VALID",
"MPLS": "MFP_MPLS",
"TCP": "MFP_TCP",
@@ -68,6 +69,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
@@ -360,6 +360,9 @@ enum ovs_key_attr {
OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking labels */
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, /* struct ovs_key_ct_tuple_ipv4 */
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, /* struct ovs_key_ct_tuple_ipv6 */
+#ifndef __KERNEL__
+ OVS_KEY_ATTR_NSH, /* struct ovs_key_nsh */
+#endif
#ifdef __KERNEL__
/* Only used within kernel data path. */
@@ -491,6 +494,18 @@ struct ovs_key_ct_labels {
};
};
+struct ovs_key_nsh {
+ __u8 flags;
+ __u8 mdtype;
+ __u8 np;
+ __u8 pad;
+ __be32 path_hdr;
+ __be32 c1;
+ __be32 c2;
+ __be32 c3;
+ __be32 c4;
+};
+
/* OVS_KEY_ATTR_CT_STATE flags */
#define OVS_CS_F_NEW 0x01 /* Beginning of a new connection. */
#define OVS_CS_F_ESTABLISHED 0x02 /* Part of an existing connection. */
@@ -30,7 +30,8 @@ openvswitchinclude_HEADERS = \
include/openvswitch/uuid.h \
include/openvswitch/version.h \
include/openvswitch/vconn.h \
- include/openvswitch/vlog.h
+ include/openvswitch/vlog.h \
+ include/openvswitch/nsh.h
if HAVE_CXX
# OVS does not use C++ itself, but it provides public header files
@@ -27,7 +27,7 @@ extern "C" {
/* This sequence number should be incremented whenever anything involving flows
* or the wildcarding of flows changes. This will cause build assertion
* failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 39
+#define FLOW_WC_SEQ 40
/* Number of Open vSwitch extension 32-bit registers. */
#define FLOW_N_REGS 16
@@ -146,6 +146,7 @@ struct flow {
struct eth_addr arp_tha; /* ARP/ND target hardware address. */
ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
ovs_be16 pad2; /* Pad to 64 bits. */
+ struct flow_nsh nsh; /* Network Service Header keys */
/* L4 (64-bit aligned) */
ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */
@@ -158,13 +159,14 @@ struct flow {
};
BUILD_ASSERT_DECL(sizeof(struct flow) % sizeof(uint64_t) == 0);
BUILD_ASSERT_DECL(sizeof(struct flow_tnl) % sizeof(uint64_t) == 0);
+BUILD_ASSERT_DECL(sizeof(struct flow_nsh) % sizeof(uint64_t) == 0);
#define FLOW_U64S (sizeof(struct flow) / sizeof(uint64_t))
/* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
- == sizeof(struct flow_tnl) + 300
- && FLOW_WC_SEQ == 39);
+ == sizeof(struct flow_tnl) + sizeof(struct flow_nsh) + 300
+ && FLOW_WC_SEQ == 40);
/* Incremental points at which flow classification may be performed in
* segments.
@@ -46,6 +46,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);
@@ -1739,6 +1739,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: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * 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: no.
+ * Formatting: decimal.
+ * Prerequisites: NSH.
+ * Access: read-only.
+ * NXM: none.
+ * 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: no.
+ * Formatting: decimal.
+ * Prerequisites: NSH.
+ * Access: read-only.
+ * NXM: none.
+ * 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: no.
+ * Formatting: hexadecimal.
+ * Prerequisites: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * 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: no.
+ * Formatting: decimal.
+ * Prerequisites: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * 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: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * 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: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * 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: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * 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: NSH.
+ * Access: read/write.
+ * NXM: none.
+ * OXM: OXM_NSH_C4(9) since OF1.3 and v2.8.
+ */
+ MFF_NSH_C4,
+
MFF_N_IDS
};
@@ -1838,6 +1969,7 @@ enum OVS_PACKED_ENUM mf_prereqs {
MFP_IPV4,
MFP_IPV6,
MFP_IP_ANY,
+ MFP_NSH,
/* L2.5 requirements. */
MFP_MPLS,
new file mode 100644
@@ -0,0 +1,130 @@
+#ifndef __OPENVSWITCH_NSH_H
+#define __OPENVSWITCH_NSH_H 1
+
+#include "openvswitch/types.h"
+
+/*
+ * Network Service Header:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Ver|O|C|R|R|R|R|R|R| Length | MD Type | Next Proto |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Service Path ID | Service Index |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * ~ Mandatory/Optional Context Header ~
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * Ver = The version field is used to ensure backward compatibility
+ * going forward with future NSH updates. It MUST be set to 0x0
+ * by the sender, in this first revision of NSH.
+ *
+ * O = OAM. when set to 0x1 indicates that this packet is an operations
+ * and management (OAM) packet. The receiving SFF and SFs nodes
+ * MUST examine the payload and take appropriate action.
+ *
+ * C = context. Indicates that a critical metadata TLV is present.
+ *
+ * Length : total length, in 4-byte words, of NSH including the Base
+ * Header, the Service Path Header and the optional variable
+ * TLVs.
+ * MD Type: indicates the format of NSH beyond the mandatory Base Header
+ * and the Service Path Header.
+ *
+ * Next Protocol: indicates the protocol type of the original packet. A
+ * new IANA registry will be created for protocol type.
+ *
+ * Service Path Identifier (SPI): identifies a service path.
+ * Participating nodes MUST use this identifier for Service
+ * Function Path selection.
+ *
+ * Service Index (SI): provides location within the SFP.
+ *
+ * [0] https://tools.ietf.org/html/draft-ietf-sfc-nsh-13
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * struct nsh_md1_ctx - Keeps track of NSH context data
+ * @nshc<1-4>: NSH Contexts.
+ */
+struct nsh_md1_ctx {
+ ovs_be32 c1;
+ ovs_be32 c2;
+ ovs_be32 c3;
+ ovs_be32 c4;
+};
+
+struct nsh_md2_tlv {
+ ovs_be16 md_class;
+ uint8_t type;
+ uint8_t length;
+ uint8_t md_value[];
+};
+
+struct nsh_hdr {
+ ovs_be16 ver_flags_len;
+ uint8_t md_type;
+ uint8_t next_proto;
+ ovs_be32 path_hdr;
+ union {
+ struct nsh_md1_ctx md1;
+ struct nsh_md2_tlv md2[0];
+ };
+};
+
+/* Masking NSH header fields. */
+#define NSH_VER_MASK 0xc000
+#define NSH_VER_SHIFT 12
+#define NSH_FLAGS_MASK 0x3fc0
+#define NSH_FLAGS_SHIFT 6
+#define NSH_LEN_MASK 0x003f
+#define NSH_LEN_SHIFT 0
+
+#define NSH_SPI_MASK 0xffffff00
+#define NSH_SPI_SHIFT 8
+#define NSH_SI_MASK 0x000000ff
+#define NSH_SI_SHIFT 0
+
+#define NSH_DST_PORT 4790 /* UDP Port for NSH on VXLAN. */
+#define ETH_P_NSH 0x894F /* Ethertype for NSH. */
+
+/* NSH Base Header Next Protocol. */
+#define NSH_P_IPV4 0x01
+#define NSH_P_IPV6 0x02
+#define NSH_P_ETHERNET 0x03
+
+/* MD Type Registry. */
+#define NSH_M_TYPE1 0x01
+#define NSH_M_TYPE2 0x02
+#define NSH_M_EXP1 0xFE
+#define NSH_M_EXP2 0xFF
+
+/* sizeof(struct nsh_hdr) + sizeof(struct nsh_md1_ctx). */
+#define NSH_M_TYPE1_LEN 24
+
+static inline uint16_t
+nsh_hdr_len(const struct nsh_hdr *nsh)
+{
+ return 4 * (ntohs(nsh->ver_flags_len) & NSH_LEN_MASK) >> NSH_LEN_SHIFT;
+}
+
+static inline struct nsh_md1_ctx *
+nsh_md1_ctx(struct nsh_hdr *nsh)
+{
+ return &nsh->md1;
+}
+
+static inline struct nsh_md2_tlv *
+nsh_md2_ctx(struct nsh_hdr *nsh)
+{
+ return nsh->md2;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
@@ -77,4 +77,23 @@ union flow_vlan_hdr {
}
#endif
+/* Network Service Header keys */
+struct flow_nsh {
+ uint8_t flags;
+ uint8_t mdtype;
+ uint8_t np;
+ uint8_t si;
+ ovs_be32 spi;
+ ovs_be32 c1;
+ ovs_be32 c2;
+ ovs_be32 c3;
+ ovs_be32 c4;
+};
+
+/* NSH flags */
+#define FLOW_NSH_F_OAM (1 << 0)
+#define FLOW_NSH_F_CTX (1 << 1)
+
+#define FLOW_NSH_F_MASK ((1 << 2) - 1)
+
#endif /* packets.h */
@@ -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,55 @@ 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);
}
+int
+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;
+
+ 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;
+ length = (ver_flags_len & NSH_LEN_MASK) >> NSH_LEN_SHIFT;
+
+ if (version != 0) {
+ return false;
+ }
+
+ key->flags = flags;
+ key->mdtype = nsh->md_type;
+ key->np = nsh->next_proto;
+
+ path_hdr = ntohl(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:
+ if (length != 6) {
+ return -EINVAL;
+ }
+ key->c1 = nsh->md1.c1;
+ key->c2 = nsh->md1.c2;
+ key->c3 = nsh->md1.c3;
+ key->c4 = nsh->md1.c4;
+ break;
+ case NSH_M_TYPE2:
+ /* Don't support MD type 2 yet, so return false */
+ default:
+ return false;
+ }
+
+ /* NSH header length is in 4 byte words. */
+ data_pull(datap, sizep, 4 * length);
+
+ return true;
+}
+
/* Initializes 'flow' members from 'packet' and 'md', taking the packet type
* into account.
*
@@ -817,6 +867,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 +1015,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 +1579,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 +1678,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. */
}
@@ -1654,7 +1729,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 +1815,16 @@ 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.c1);
+ FLOWMAP_SET(map, nsh.c2);
+ FLOWMAP_SET(map, nsh.c3);
+ FLOWMAP_SET(map, nsh.c4);
}
}
@@ -1749,7 +1834,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 +1978,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 +2025,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 +2614,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;
@@ -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);
+int 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;
@@ -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,7 +1144,23 @@ 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, ',');
+ }
+}
+
+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%08"PRIx32, ntohl(value));
+ } else {
+ ds_put_format(s, "0x%08"PRIx32"/0x%08"PRIx32,
ntohl(value), ntohl(mask));
}
ds_put_char(s, ',');
@@ -1218,6 +1250,30 @@ 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)
+{
+ if (m->nsh.flags)
+ format_uint8_masked(s, "nsh_flags", f->nsh.flags, m->nsh.flags);
+ if (m->nsh.mdtype)
+ format_uint8_masked(s, "nsh_mdtype", f->nsh.mdtype, m->nsh.mdtype);
+ if (m->nsh.np)
+ format_uint8_masked(s, "nsh_np", f->nsh.np, m->nsh.np);
+ if (m->nsh.spi)
+ format_be32_masked(s, "nsh_spi", f->nsh.spi, m->nsh.spi);
+ if (m->nsh.si)
+ format_uint8_masked(s, "nsh_si", f->nsh.si, m->nsh.si);
+
+ if (m->nsh.c1)
+ format_be32_masked_hex(s, "nsh_c1", f->nsh.c1, m->nsh.c1);
+ if (m->nsh.c2)
+ format_be32_masked_hex(s, "nsh_c2", f->nsh.c2, m->nsh.c2);
+ if (m->nsh.c3)
+ format_be32_masked_hex(s, "nsh_c3", f->nsh.c3, m->nsh.c3);
+ if (m->nsh.c4)
+ format_be32_masked_hex(s, "nsh_c4", f->nsh.c4, m->nsh.c4);
+}
+
/* 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 +1291,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 +1377,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 +1514,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);
@@ -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();
@@ -423,6 +442,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 +607,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 +895,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 +1216,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 +1613,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();
@@ -1651,6 +1767,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 +2110,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();
@@ -2222,6 +2375,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();
@@ -1292,6 +1292,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
@@ -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,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(&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);
+ nxm_put_32m(&ctx, MFF_NSH_C1, oxm, flow->nsh.c1,
+ match->wc.masks.nsh.c1);
+ nxm_put_32m(&ctx, MFF_NSH_C2, oxm, flow->nsh.c2,
+ match->wc.masks.nsh.c2);
+ nxm_put_32m(&ctx, MFF_NSH_C3, oxm, flow->nsh.c3,
+ match->wc.masks.nsh.c3);
+ nxm_put_32m(&ctx, 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++) {
@@ -295,6 +295,8 @@ 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:
+ 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 +421,8 @@ odp_execute_masked_set_action(struct dp_packet *packet,
get_mask(a, struct ovs_key_ethernet));
break;
+ case OVS_KEY_ATTR_NSH:
+ break;
case OVS_KEY_ATTR_IPV4:
odp_set_ipv4(packet, nl_attr_get(a),
get_mask(a, struct ovs_key_ipv4));
@@ -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,95 @@ 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=%d", (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:
+ ds_put_format(ds, ",c1=0x%08x", ntohl(key->c1));
+ ds_put_format(ds, ",c2=0x%08x", ntohl(key->c2));
+ ds_put_format(ds, ",c3=0x%08x", ntohl(key->c3));
+ ds_put_format(ds, ",c4=0x%08x", ntohl(key->c4));
+ 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%08"PRIx32, ntohl(value));
+ } else {
+ ds_put_format(s, "0x%08"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->c1, mask->c1);
+ format_be32_masked(ds, &first, "c2", key->c2, mask->c2);
+ format_be32_masked(ds, &first, "c3", key->c3, mask->c3);
+ format_be32_masked(ds, &first, "c4", key->c4, mask->c4);
+ }
+}
+
static const char *
slow_path_reason_to_string(uint32_t reason)
{
@@ -1970,6 +2060,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 +2313,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:
@@ -3188,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:
@@ -3606,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_;
@@ -4408,6 +4529,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, c1);
+ SCAN_FIELD("c2=", be32, c2);
+ SCAN_FIELD("c3=", be32, c3);
+ SCAN_FIELD("c4=", be32, c4);
+ } SCAN_END(OVS_KEY_ATTR_NSH);
+
/* Encap open-coded. */
if (!strncmp(s, "encap(", 6)) {
const char *start = s;
@@ -4516,6 +4648,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 {
@@ -4692,6 +4828,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)) {
@@ -4935,6 +5077,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;
@@ -5245,6 +5388,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;
}
@@ -6287,6 +6445,58 @@ 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) {
+ nsh->c1 = flow->nsh.c1;
+ nsh->c2 = flow->nsh.c2;
+ nsh->c3 = flow->nsh.c3;
+ nsh->c4 = flow->nsh.c4;
+ } else {
+ switch (nsh->mdtype) {
+ case NSH_M_TYPE1:
+ nsh->c1 = flow->nsh.c1;
+ nsh->c2 = flow->nsh.c2;
+ nsh->c3 = flow->nsh.c3;
+ nsh->c4 = flow->nsh.c4;
+ 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:
+ flow->nsh.c1 = nsh->c1;
+ flow->nsh.c2 = nsh->c2;
+ flow->nsh.c3 = nsh->c3;
+ flow->nsh.c4 = nsh->c4;
+ break;
+ case NSH_M_TYPE2:
+ /* TODO: MD type 2 */
+ flow->nsh.c1 = 0;
+ flow->nsh.c2 = 0;
+ flow->nsh.c3 = 0;
+ flow->nsh.c4 = 0;
+ break;
+ }
+}
+
/* 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));
@@ -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
@@ -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);
@@ -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"
@@ -378,6 +379,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)
{
@@ -1279,6 +1281,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. */
};
@@ -99,7 +99,7 @@ struct rule;
/* Metadata for restoring pipeline context after recirculation. Helpers
* are inlined below to keep them together with the definition for easier
* updates. */
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
struct frozen_metadata {
/* Metadata in struct flow. */
@@ -1050,6 +1050,7 @@ sflow_read_set_action(const struct nlattr *attr,
case OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6:
case OVS_KEY_ATTR_UNSPEC:
case OVS_KEY_ATTR_PACKET_TYPE:
+ case OVS_KEY_ATTR_NSH:
case __OVS_KEY_ATTR_MAX:
default:
break;
@@ -3727,7 +3727,7 @@ compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
/* If 'struct flow' gets additional metadata, we'll need to zero it out
* before traversing a patch port. */
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 40);
memset(&flow_tnl, 0, sizeof flow_tnl);
if (!check_output_prerequisites(ctx, xport, flow, check_stp)) {
@@ -2386,7 +2386,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_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
+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
matching:
dp_hash: arbitrary mask
recirc_id: exact match or wildcard
@@ -2548,6 +2548,15 @@ 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
+ nsh_flags: arbitrary mask
+ nsh_mdtype: exact match or wildcard
+ nsh_np: exact match or wildcard
+ nsh_spi: exact match or wildcard
+ nsh_si: exact match or wildcard
+ nsh_c1: arbitrary mask
+ nsh_c2: arbitrary mask
+ nsh_c3: arbitrary mask
+ nsh_c4: arbitrary mask
' $1
}