[ovs-dev,v1,1/5] userspace: Add NSH keys to struct flow

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

Details

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

Commit Message

Yang, Yi Y April 20, 2017, 2:42 a.m.
Signed-off-by: Mengke Liu <mengke.liu@intel.com>
Signed-off-by: Ricky Li <ricky.li@intel.com>
Signed-off-by: Johnson Li <johnson.li@intel.com>
Signed-off-by: Yi Yang <yi.y.yang@intel.com>
---
 include/openvswitch/automake.mk |   3 +-
 include/openvswitch/flow.h      |  10 ++-
 include/openvswitch/nsh.h       | 150 ++++++++++++++++++++++++++++++++++++++++
 include/openvswitch/packets.h   |  19 +++++
 lib/flow.c                      |  81 +++++++++++++++++++---
 lib/flow.h                      |   2 +-
 lib/match.c                     |   2 +-
 lib/nx-match.c                  |   2 +-
 lib/odp-util.h                  |   2 +-
 lib/ofp-util.c                  |   2 +-
 lib/packets.h                   |   1 +
 ofproto/ofproto-dpif-rid.h      |   2 +-
 ofproto/ofproto-dpif-xlate.c    |   2 +-
 13 files changed, 259 insertions(+), 19 deletions(-)
 create mode 100644 include/openvswitch/nsh.h

Patch hide | download patch | download mbox

diff --git a/include/openvswitch/automake.mk b/include/openvswitch/automake.mk
index c125f1e..d7a499d 100644
--- a/include/openvswitch/automake.mk
+++ b/include/openvswitch/automake.mk
@@ -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
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index 36a2a85..63ec7aa 100644
--- a/include/openvswitch/flow.h
+++ b/include/openvswitch/flow.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.
diff --git a/include/openvswitch/nsh.h b/include/openvswitch/nsh.h
new file mode 100644
index 0000000..d48380c
--- /dev/null
+++ b/include/openvswitch/nsh.h
@@ -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
diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
index f13d634..1592847 100644
--- a/include/openvswitch/packets.h
+++ b/include/openvswitch/packets.h
@@ -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 */
diff --git a/lib/flow.c b/lib/flow.c
index f50c73b..adfea1e 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 "
@@ -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;
diff --git a/lib/flow.h b/lib/flow.h
index fcc7d13..d29dcb1 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -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;
diff --git a/lib/match.c b/lib/match.c
index eddbc7d..f819ebb 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -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,",
diff --git a/lib/nx-match.c b/lib/nx-match.c
index f9d8565..b42e85f 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -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) {
diff --git a/lib/odp-util.h b/lib/odp-util.h
index 98aa445..1d7dcc4 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -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
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index 75ee220..5e910f2 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -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);
diff --git a/lib/packets.h b/lib/packets.h
index 4b51496..34d56b4 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -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)
 {
diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h
index ab9b1b7..14b346d 100644
--- a/ofproto/ofproto-dpif-rid.h
+++ b/ofproto/ofproto-dpif-rid.h
@@ -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. */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 8625445..731c376 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -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)) {