@@ -30,4 +30,5 @@ openvswitchinclude_HEADERS = \
include/openvswitch/version.h \
include/openvswitch/vconn.h \
include/openvswitch/vlog.h \
- include/openvswitch/vxlangpe.h
+ include/openvswitch/vxlangpe.h \
+ include/openvswitch/nsh.h
@@ -23,7 +23,7 @@
/* 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
@@ -123,6 +123,9 @@ struct flow {
union flow_vlan_hdr vlans[FLOW_MAX_VLAN_HEADERS]; /* VLANs */
ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)]; /* MPLS label stack
(with padding). */
+
+ struct flow_nsh nsh; /* Network Service Header keys */
+
/* L3 (64-bit aligned) */
ovs_be32 nw_src; /* IPv4 source address or ARP SPA. */
ovs_be32 nw_dst; /* IPv4 destination address or ARP TPA. */
@@ -154,13 +157,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.
new file mode 100644
@@ -0,0 +1,150 @@
+#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-01
+ */
+struct nsh_base {
+#if defined(WORDS_BIGENDIAN)
+ uint8_t version:2;
+ uint8_t oam_flag:1;
+ uint8_t context_flag:1;
+ uint8_t reserved_flags1:4;
+
+ uint8_t reserved_flags2:2;
+ uint8_t length:6;
+#else
+ uint8_t reserved_flags1:4;
+ uint8_t context_flag:1;
+ uint8_t oam_flag:1;
+ uint8_t version:2;
+
+ uint8_t length:6;
+ uint8_t reserved_flags2:2;
+#endif
+ uint8_t md_type;
+ uint8_t next_proto;
+ union {
+ struct {
+ uint8_t svc_path[3];
+ uint8_t svc_idx;
+ };
+ ovs_be32 path_hdr;
+ };
+};
+
+/**
+ * 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_ctx {
+ ovs_be16 md_class;
+ uint8_t type;
+ uint8_t length;
+ uint8_t md_value[];
+};
+
+/**
+ * struct nshdr - Network Service header
+ * @base: Network Service Base Header.
+ * @ctx: Network Service Context Header.
+ */
+struct nsh_hdr {
+ struct nsh_base base;
+ ovs_be32 ctx[0]; /* Mandatory/optional Context Header */
+};
+
+/* Network Service Header keys
+ * Only fields for metadata type I are defined, for metadata type II,
+ * tun_opts will be reused.
+ */
+OVS_PACKED(
+OVS_ALIGNED_STRUCT(4, ovs_nsh_key) {
+ uint8_t flags;
+ uint8_t mdtype; /* NSH metadata type */
+ uint8_t np; /* NSH next protocol */
+ uint8_t si; /* NSH service index */
+ uint32_t spi; /* NSH service path id */
+ uint32_t c1; /* NSH context C1-C4 */
+ uint32_t c2;
+ uint32_t c3;
+ uint32_t c4;
+}); /* Minimize padding. */
+
+#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
+
+/* Used for masking nsp and nsi values in field nsp below */
+#define NSH_M_NSP 0x00FFFFFF
+#define NSH_M_NSI 0xFF000000
+
+/* sizeof(struct nsh_hdr) + sizeof(struct nsh_md1_ctx) */
+#define NSH_M_TYPE1_LEN 24
+#define NSH_LEN_MAX 256
+
+static inline struct nsh_md1_ctx *nsh_md1_ctx(const struct nsh_hdr *nsh)
+{
+ return (struct nsh_md1_ctx *) (nsh + 1);
+}
+
+static inline struct nsh_md2_ctx *nsh_md2_ctx(const struct nsh_hdr *nsh)
+{
+ return (struct nsh_md2_ctx *) (nsh + 1);
+}
+
+#endif
@@ -69,4 +69,23 @@ union flow_vlan_hdr {
};
};
+/* 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 "
@@ -530,6 +531,38 @@ 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);
}
+static 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 length = 0;
+
+ memset(key, 0, sizeof(struct flow_nsh));
+
+ length = nsh->base.length << 2;
+ if (length > NSH_LEN_MAX)
+ return -EINVAL;
+
+ key->mdtype = nsh->base.md_type;
+ key->np = nsh->base.next_proto;
+ key->si = nsh->base.svc_idx;
+ key->spi = nsh->base.path_hdr << 8;
+
+ if (nsh->base.md_type == NSH_M_TYPE1) {
+ const struct nsh_md1_ctx *md1_ctx = nsh_md1_ctx(nsh);
+ key->c1 = md1_ctx->c1;
+ key->c2 = md1_ctx->c2;
+ key->c3 = md1_ctx->c3;
+ key->c4 = md1_ctx->c4;
+ } else if (nsh->base.md_type == NSH_M_TYPE2) {
+ /* TODO */
+ }
+
+ data_pull(datap, sizep, length);
+
+ return 0;
+}
+
/* Initializes 'flow' members from 'packet' and 'md', taking the packet type
* into account.
*
@@ -685,6 +718,22 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
/* Network layer. */
packet->l3_ofs = (char *)data - frame;
+ /* Network Service Header */
+ 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) {
+ /* TODO */
+ }
+ }
+ goto out;
+ }
+
nw_frag = 0;
if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) {
const struct ip_header *nh = data;
@@ -949,7 +998,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)) {
@@ -1389,7 +1438,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) {
@@ -1436,6 +1485,22 @@ 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);
@@ -1528,7 +1593,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);
@@ -1623,7 +1688,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);
@@ -1767,7 +1832,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) {
@@ -1814,7 +1879,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) {
@@ -2400,7 +2465,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;
@@ -905,7 +905,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;
@@ -1176,7 +1176,7 @@ match_format(const struct match *match, struct ds *s, int priority)
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,",
@@ -988,7 +988,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);
/* OpenFlow Packet Type. Must be first. */
if (match->wc.masks.packet_type) {
@@ -143,7 +143,7 @@ void odp_portno_names_destroy(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
@@ -101,7 +101,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);
@@ -370,6 +370,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)
{
@@ -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. */
@@ -3494,7 +3494,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)) {