[ovs-dev] [PATCH 5/6] Generic encap and decap support for NSH

Yi Yang yi.y.yang at intel.com
Wed Jul 5 03:45:42 UTC 2017


From: Jan Scheurich <jan.scheurich at ericsson.com>

This commit adds translation and netdev datapath support for generic
encap and decap actions for the NSH MD1 header. The generic encap and
decap actions are mapped to specific encap_nsh and decap_nsh actions
in the datapath.

The translation follows that general scheme that decap() of an NSH
packet triggers recirculation after decapsulation, while encap(nsh)
just modifies struct flow and sets the ctx->pending_encap flag to
generate the encap_nsh action at the next commit to be able to include
subsequent set_field actions for NSH headers.

Support for the flexible MD2 format using TLV properties is foreseen
in encap(nsh), but not yet fully implemented.

The CLI syntax for encap of NSH is
encap(hdr=nsh,prop(class=nsh,type=md_type,val=1))
encap(hdr=nsh,prop(class=nsh,type=md_type,val=2),
      prop(class=nsh,type=tlv,val(<tlv_class>,<tlv_type>,<hex_string>)))

Signed-off-by: Jan Scheurich <jan.scheurich at ericsson.com>
Signed-off-by: Yi Yang <yi.y.yang at intel.com>
---
 datapath/linux/compat/include/linux/openvswitch.h |  23 +++
 include/openvswitch/nsh.h                         |  12 +-
 include/openvswitch/ofp-ed-props.h                |  38 ++++
 lib/dpif-netdev.c                                 |   2 +
 lib/dpif.c                                        |   2 +
 lib/odp-execute.c                                 |  15 ++
 lib/odp-util.c                                    | 219 +++++++++++++++++++++-
 lib/ofp-actions.c                                 |   9 +-
 lib/ofp-ed-props.c                                | 178 +++++++++++++++++-
 lib/packets.c                                     |  71 +++++++
 lib/packets.h                                     |   4 +
 ofproto/ofproto-dpif-sflow.c                      |   2 +
 ofproto/ofproto-dpif-xlate.c                      | 132 ++++++++++++-
 13 files changed, 694 insertions(+), 13 deletions(-)

diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index f5814c5..b2b7d52 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -795,6 +795,25 @@ struct ovs_action_push_eth {
 	struct ovs_key_ethernet addresses;
 };
 
+#define OVS_ENCAP_NSH_MAX_MD_LEN 256
+/*
+ * struct ovs_action_encap_nsh - %OVS_ACTION_ATTR_ENCAP_NSH
+ * @flags: NSH header flags.
+ * @mdtype: NSH metadata type.
+ * @mdlen: Length of NSH metadata in bytes.
+ * @np: NSH next_protocol: Inner packet type.
+ * @path_hdr: NSH service path id and service index.
+ * @metadata: NSH metadata for MD type 1 or 2
+ */
+struct ovs_action_encap_nsh {
+    uint8_t flags;
+    uint8_t mdtype;
+    uint8_t mdlen;
+    uint8_t np;
+    __be32 path_hdr;
+    uint8_t metadata[OVS_ENCAP_NSH_MAX_MD_LEN];
+};
+
 /**
  * enum ovs_nat_attr - Attributes for %OVS_CT_ATTR_NAT.
  *
@@ -870,6 +889,8 @@ enum ovs_nat_attr {
  * @OVS_ACTION_ATTR_PUSH_ETH: Push a new outermost Ethernet header onto the
  * packet.
  * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the packet.
+ * @OVS_ACTION_ATTR_ENCAP_NSH: encap NSH action to push NSH header.
+ * @OVS_ACTION_ATTR_DECAP_NSH: decap NSH action to remove NSH header.
  *
  * Only a single header can be set with a single %OVS_ACTION_ATTR_SET.  Not all
  * fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -905,6 +926,8 @@ enum ovs_action_attr {
 	OVS_ACTION_ATTR_TRUNC,        /* u32 struct ovs_action_trunc. */
 	OVS_ACTION_ATTR_PUSH_ETH,     /* struct ovs_action_push_eth. */
 	OVS_ACTION_ATTR_POP_ETH,      /* No argument. */
+	OVS_ACTION_ATTR_ENCAP_NSH,    /* struct ovs_action_encap_nsh. */
+	OVS_ACTION_ATTR_DECAP_NSH,    /* No argument. */
 
 #ifndef __KERNEL__
 	OVS_ACTION_ATTR_TUNNEL_PUSH,   /* struct ovs_action_push_tnl*/
diff --git a/include/openvswitch/nsh.h b/include/openvswitch/nsh.h
index 532438b..b0bff01 100644
--- a/include/openvswitch/nsh.h
+++ b/include/openvswitch/nsh.h
@@ -91,6 +91,8 @@ struct nsh_hdr {
 #define NSH_P_IPV4        0x01
 #define NSH_P_IPV6        0x02
 #define NSH_P_ETHERNET    0x03
+#define NSH_P_NSH         0x04
+#define NSH_P_MPLS        0x05
 
 /* MD Type Registry. */
 #define NSH_M_TYPE1     0x01
@@ -98,8 +100,14 @@ struct nsh_hdr {
 #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
+/* NSH Metadata Length. */
+#define NSH_M_TYPE1_MDLEN 16
+
+/* NSH Base Header Length */
+#define NSH_BASE_HDR_LEN  8
+
+/* NSH MD Type 1 header Length. */
+#define NSH_M_TYPE1_LEN   24
 
 static inline uint16_t
 nsh_hdr_len(const struct nsh_hdr *nsh)
diff --git a/include/openvswitch/ofp-ed-props.h b/include/openvswitch/ofp-ed-props.h
index 1db7e56..28c02d9 100644
--- a/include/openvswitch/ofp-ed-props.h
+++ b/include/openvswitch/ofp-ed-props.h
@@ -26,6 +26,7 @@ enum ofp_ed_prop_class {
     OFPPPC_MPLS  = 1,            /* MPLS property  class. */
     OFPPPC_GRE   = 2,            /* GRE property  class. */
     OFPPPC_GTP   = 3,            /* GTP property  class. */
+    OFPPPC_NSH   = 4,            /* NSH property  class */
     /*  new values go here  */
     OFPPPC_EXPERIMENTER=0xffff,  /* Experimenter property class.
                                   * First 32 bits of property data is exp
@@ -33,6 +34,13 @@ enum ofp_ed_prop_class {
                                   * property data. */
 };
 
+enum ofp_ed_nsh_prop_type {
+    OFPPPT_PROP_NSH_NONE = 0,    /* unused */
+    OFPPPT_PROP_NSH_MDTYPE = 1,  /* property MDTYPE in NSH */
+    OFPPPT_PROP_NSH_TLV = 2,     /* property TLV in NSH */
+    /*  new values go here  */
+};
+
 /*
  * External representation of encap/decap properties.
  * These must be padded to a multiple of 4 bytes.
@@ -44,6 +52,21 @@ struct ofp_ed_prop_header {
     uint8_t len;
 };
 
+struct ofp_ed_prop_nsh_md_type {
+    struct ofp_ed_prop_header header;
+    uint8_t md_type;         /* NSH MD type .*/
+    uint8_t pad[3];          /* Padding to 8 bytes. */
+};
+
+struct ofp_ed_prop_nsh_tlv {
+    struct ofp_ed_prop_header header;
+    ovs_be16 tlv_class;      /* Metadata class. */
+    uint8_t tlv_type;        /* Metadata type including C bit. */
+    uint8_t tlv_len;         /* Metadata value length (0-127). */
+    uint8_t data[0];         /* tlv_len octets of metadata value,
+                              * padded to a multiple of 8 bytes. */
+};
+
 /*
  * Internal representation of encap/decap properties
  */
@@ -54,6 +77,21 @@ struct ofpact_ed_prop {
     uint8_t len;
 };
 
+struct ofpact_ed_prop_nsh_md_type {
+    struct ofpact_ed_prop header;
+    uint8_t md_type;         /* NSH MD type .*/
+    uint8_t pad[3];          /* Padding to 8 bytes. */
+};
+
+struct ofpact_ed_prop_nsh_tlv {
+    struct ofpact_ed_prop header;
+    ovs_be16 tlv_class;      /* Metadata class. */
+    uint8_t tlv_type;        /* Metadata type including C bit. */
+    uint8_t tlv_len;         /* Metadata value length (0-127). */
+    uint8_t data[0];         /* tlv_len octets of metadata value,
+                              * padded to a multiple of 8 bytes.
+                              */
+};
 enum ofperr decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
                            struct ofpbuf *out, size_t *remaining);
 enum ofperr encode_ed_prop(const struct ofpact_ed_prop **prop,
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 4e29085..3ebe572 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -5297,6 +5297,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_PUSH_ETH:
     case OVS_ACTION_ATTR_POP_ETH:
     case OVS_ACTION_ATTR_CLONE:
+    case OVS_ACTION_ATTR_ENCAP_NSH:
+    case OVS_ACTION_ATTR_DECAP_NSH:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
     }
diff --git a/lib/dpif.c b/lib/dpif.c
index 45c1890..b79270a 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1254,6 +1254,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
     case OVS_ACTION_ATTR_PUSH_ETH:
     case OVS_ACTION_ATTR_POP_ETH:
     case OVS_ACTION_ATTR_CLONE:
+    case OVS_ACTION_ATTR_ENCAP_NSH:
+    case OVS_ACTION_ATTR_DECAP_NSH:
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
         OVS_NOT_REACHED();
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 30385f6..12f517e 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -662,6 +662,8 @@ requires_datapath_assistance(const struct nlattr *a)
     case OVS_ACTION_ATTR_PUSH_ETH:
     case OVS_ACTION_ATTR_POP_ETH:
     case OVS_ACTION_ATTR_CLONE:
+    case OVS_ACTION_ATTR_ENCAP_NSH:
+    case OVS_ACTION_ATTR_DECAP_NSH:
         return false;
 
     case OVS_ACTION_ATTR_UNSPEC:
@@ -819,6 +821,19 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
             }
             break;
 
+        case OVS_ACTION_ATTR_ENCAP_NSH: {
+            const struct ovs_action_encap_nsh *enc_nsh = nl_attr_get(a);
+            DP_PACKET_BATCH_FOR_EACH (packet, batch) {
+                encap_nsh(packet, enc_nsh);
+            }
+            break;
+        }
+        case OVS_ACTION_ATTR_DECAP_NSH:
+            DP_PACKET_BATCH_FOR_EACH (packet, batch) {
+                decap_nsh(packet);
+            }
+            break;
+
         case OVS_ACTION_ATTR_OUTPUT:
         case OVS_ACTION_ATTR_TUNNEL_PUSH:
         case OVS_ACTION_ATTR_TUNNEL_POP:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 14bdf96..20373ec 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -129,6 +129,8 @@ odp_action_len(uint16_t type)
     case OVS_ACTION_ATTR_PUSH_ETH: return sizeof(struct ovs_action_push_eth);
     case OVS_ACTION_ATTR_POP_ETH: return 0;
     case OVS_ACTION_ATTR_CLONE: return ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_ENCAP_NSH: return ATTR_LEN_VARIABLE;
+    case OVS_ACTION_ATTR_DECAP_NSH: return 0;
 
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
@@ -337,6 +339,47 @@ format_nsh_key_mask(struct ds *ds, const struct ovs_key_nsh *key,
     }
 }
 
+static void
+format_odp_encap_nsh_action(struct ds *ds, const struct nlattr *attr)
+{
+    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+    if (nl_attr_type(attr) != OVS_ACTION_ATTR_ENCAP_NSH) {
+        return;
+    }
+    ds_put_cstr(ds, "encap_nsh(");
+
+    size_t len = nl_attr_get_size(attr);
+    if (len) {
+        const struct ovs_action_encap_nsh *encap_nsh = nl_attr_get(attr);
+        struct ovs_key_nsh nsh_key;
+
+        memset(&nsh_key, 0, sizeof(nsh_key));
+        nsh_key.flags = encap_nsh->flags;
+        nsh_key.mdtype = encap_nsh->mdtype;
+        nsh_key.np = encap_nsh->np;
+        nsh_key.path_hdr = encap_nsh->path_hdr;
+
+        switch (encap_nsh->mdtype) {
+        case NSH_M_TYPE1: {
+            struct nsh_md1_ctx *md1 = (struct nsh_md1_ctx *) encap_nsh->metadata;
+            nsh_key.c1 = md1->c1;
+            nsh_key.c2 = md1->c2;
+            nsh_key.c3 = md1->c3;
+            nsh_key.c4 = md1->c4;
+            break;
+        }
+        case NSH_M_TYPE2:
+            /* TODO */
+            break;
+        default:
+            VLOG_ERR_RL(&rl, "Invalid MD type: %d", encap_nsh->mdtype);
+            break;
+        }
+        format_nsh_key(ds, &nsh_key);
+    }
+    ds_put_format(ds, ")");
+}
+
 static const char *
 slow_path_reason_to_string(uint32_t reason)
 {
@@ -632,7 +675,7 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
                       gnh->oam ? "oam," : "",
                       gnh->critical ? "crit," : "",
                       ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
- 
+
         if (gnh->opt_len) {
             ds_put_cstr(ds, ",options(");
             format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
@@ -1019,6 +1062,12 @@ format_odp_action(struct ds *ds, const struct nlattr *a,
     case OVS_ACTION_ATTR_CLONE:
         format_odp_clone_action(ds, a, portno_names);
         break;
+    case OVS_ACTION_ATTR_ENCAP_NSH:
+        format_odp_encap_nsh_action(ds, a);
+        break;
+    case OVS_ACTION_ATTR_DECAP_NSH:
+        ds_put_cstr(ds, "decap_nsh()");
+        break;
     case OVS_ACTION_ATTR_UNSPEC:
     case __OVS_ACTION_ATTR_MAX:
     default:
@@ -1737,6 +1786,99 @@ find_end:
 }
 
 static int
+parse_odp_encap_nsh_action(const char *s, struct ofpbuf *actions)
+{
+    int n = 0;
+    int ret = 0;
+    struct ovs_action_encap_nsh encap_nsh;
+    uint32_t spi;
+    uint8_t si;
+    uint32_t cd;
+
+    if (!ovs_scan_len(s, &n, "encap_nsh(")) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    /* The default is NSH_M_TYPE1 */
+    encap_nsh.flags = 0;
+    encap_nsh.mdtype = NSH_M_TYPE1;
+    encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
+    encap_nsh.path_hdr = htonl(255);
+    memset(encap_nsh.metadata, 0, NSH_M_TYPE1_MDLEN);
+
+    for (;;) {
+        n += strspn(s + n, delimiters);
+        if (s[n] == ')') {
+            break;
+        }
+
+        if (ovs_scan_len(s, &n, "flags=%"SCNi8, &encap_nsh.flags)) {
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "mdtype=%"SCNi8, &encap_nsh.mdtype)) {
+            switch (encap_nsh.mdtype) {
+            case NSH_M_TYPE1:
+                encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
+                break;
+            case NSH_M_TYPE2:
+                /* TODO */
+                encap_nsh.mdlen = 0;
+                break;
+            default:
+                ret = -EINVAL;
+                goto out;
+            }
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "np=%"SCNi8, &encap_nsh.np)) {
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "spi=%"SCNi32, &spi)) {
+            encap_nsh.path_hdr = htonl(((spi << NSH_SPI_SHIFT) & NSH_SPI_MASK) |
+                                       (ntohl(encap_nsh.path_hdr) & ~NSH_SPI_MASK));
+            continue;
+        }
+        if (ovs_scan_len(s, &n, "si=%"SCNi8, &si)) {
+            encap_nsh.path_hdr = htonl((si << NSH_SI_SHIFT) |
+                                       (ntohl(encap_nsh.path_hdr) & ~NSH_SI_MASK));
+            continue;
+        }
+        if (encap_nsh.mdtype == NSH_M_TYPE1) {
+            struct nsh_md1_ctx *md1 = (struct nsh_md1_ctx *) encap_nsh.metadata;
+            if (ovs_scan_len(s, &n, "c1=0x%"SCNx32, &cd)) {
+                md1->c1 = htonl(cd);
+                continue;
+            }
+            if (ovs_scan_len(s, &n, "c2=0x%"SCNx32, &cd)) {
+                md1->c2 = htonl(cd);
+                continue;
+            }
+            if (ovs_scan_len(s, &n, "c3=0x%"SCNx32, &cd)) {
+                md1->c3 = htonl(cd);
+                continue;
+            }
+            if (ovs_scan_len(s, &n, "c4=0x%"SCNx32, &cd)) {
+                md1->c4 = htonl(cd);
+                continue;
+            }
+        }
+        else if (encap_nsh.mdtype == NSH_M_TYPE2) {
+            /* TODO */
+            continue;
+        }
+    }
+out:
+    if (ret < 0) {
+        return ret;
+    } else {
+        size_t size = sizeof(encap_nsh);
+        nl_msg_put_unspec(actions, OVS_ACTION_ATTR_ENCAP_NSH, &encap_nsh, size);
+        return n;
+    }
+}
+
+static int
 parse_action_list(const char *s, const struct simap *port_names,
                   struct ofpbuf *actions)
 {
@@ -1938,6 +2080,24 @@ parse_odp_action(const char *s, const struct simap *port_names,
     }
 
     {
+        if (!strncmp(s, "encap_nsh(", 10)) {
+            int retval = parse_odp_encap_nsh_action(s, actions);
+            if (retval < 0) {
+                return retval;
+            }
+            return retval + 1;
+        }
+    }
+
+    {
+        int n;
+        if (ovs_scan(s, "decap_nsh()%n", &n)) {
+            nl_msg_put_flag(actions, OVS_ACTION_ATTR_DECAP_NSH);
+            return n;
+        }
+    }
+
+    {
         uint32_t port;
         int n;
 
@@ -2301,7 +2461,6 @@ odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
     if (attr == OVS_KEY_ATTR_TUNNEL) {
         return false;
     }
-
     if (attr == OVS_KEY_ATTR_ARP) {
         /* ARP key has padding, ignore it. */
         BUILD_ASSERT_DECL(sizeof(struct ovs_key_arp) == 24);
@@ -2403,6 +2562,7 @@ format_eth(struct ds *ds, const char *name, const struct eth_addr key,
     }
 }
 
+
 static void
 format_be64(struct ds *ds, const char *name, ovs_be64 key,
             const ovs_be64 *mask, bool verbose)
@@ -6562,6 +6722,45 @@ commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
 }
 
 static void
+odp_put_decap_nsh_action(struct ofpbuf *odp_actions)
+{
+    nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_DECAP_NSH);
+}
+
+static void
+odp_put_encap_nsh_action(struct ofpbuf *odp_actions,
+                         const struct flow *flow)
+{
+    struct ovs_action_encap_nsh encap_nsh;
+
+    encap_nsh.flags = flow->nsh.flags;
+    encap_nsh.mdtype = flow->nsh.mdtype;
+    encap_nsh.np = flow->nsh.np;
+    encap_nsh.path_hdr = htonl((ntohl(flow->nsh.spi) << NSH_SPI_SHIFT) | flow->nsh.si);
+
+    switch (encap_nsh.mdtype) {
+    case NSH_M_TYPE1: {
+        struct nsh_md1_ctx *md1 = (struct nsh_md1_ctx *) encap_nsh.metadata;
+        encap_nsh.mdlen = NSH_M_TYPE1_MDLEN;
+        md1->c1 = flow->nsh.c1;
+        md1->c2 = flow->nsh.c2;
+        md1->c3 = flow->nsh.c3;
+        md1->c4 = flow->nsh.c4;
+        break;
+    }
+    case NSH_M_TYPE2:
+        /* TODO */
+        encap_nsh.mdlen = 0;
+        break;
+    default:
+        encap_nsh.mdlen = 0;
+        break;
+    }
+    nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_ENCAP_NSH,
+                      &encap_nsh, sizeof(encap_nsh));
+}
+
+static void
 commit_packet_type_change(const struct flow *flow,
                           struct flow *base_flow,
                           struct ofpbuf *odp_actions,
@@ -6583,12 +6782,21 @@ commit_packet_type_change(const struct flow *flow,
             base_flow->dl_dst = flow->dl_dst;
             break;
         }
+        case PT_NSH:
+            /* encap_nsh */
+            odp_put_encap_nsh_action(odp_actions, flow);
+            base_flow->packet_type = flow->packet_type;
+            memcpy(&base_flow->nsh, &flow->nsh, sizeof base_flow->nsh);
+            /* TODO: Do we need to copy all the other fields too? */
+            break;
         default:
             /* Only the above protocols are supported for encap. The check
              * is done at action decoding. */
             OVS_NOT_REACHED();
         }
     } else {
+        /* This is a decap case. It can only happen after translation of a
+         * decap action. */
         if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE &&
             base_flow->packet_type == htonl(PT_ETH)) {
             /* pop_eth */
@@ -6596,9 +6804,10 @@ commit_packet_type_change(const struct flow *flow,
             base_flow->packet_type = flow->packet_type;
             base_flow->dl_src = eth_addr_zero;
             base_flow->dl_dst = eth_addr_zero;
-        } else {
-            /* All other cases are handled through recirculation. */
-            OVS_NOT_REACHED();
+        } else if (ntohl(base_flow->packet_type) == PT_NSH) {
+                odp_put_decap_nsh_action(odp_actions);
+                base_flow->packet_type = flow->packet_type;
+                memcpy(&base_flow->nsh, &flow->nsh, sizeof(base_flow->nsh));
         }
     }
 
diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c
index a50d476..c797317 100644
--- a/lib/ofp-actions.c
+++ b/lib/ofp-actions.c
@@ -4055,6 +4055,7 @@ decode_OFPAT_RAW13_ENCAP(const struct ofp_action_encap *oae,
     encap->ofpact.raw = OFPAT_RAW13_ENCAP;
     switch (ntohl(oae->new_pkt_type)) {
     case PT_ETH:
+    case PT_NSH:
         /* Add supported encap header types here. */
         break;
     default:
@@ -4101,6 +4102,8 @@ parse_encap_header(const char *hdr, ovs_be32 *packet_type)
 {
     if (strcmp(hdr, "ethernet") == 0) {
         *packet_type = htonl(PT_ETH);
+    } else if (strcmp(hdr, "nsh") == 0) {
+        *packet_type = htonl(PT_NSH);
     } else {
         return false;
     }
@@ -4165,10 +4168,8 @@ parse_ENCAP(char *arg,
     struct ofpact_encap *encap;
     char *key, *value, *str;
     char *error = NULL;
-    size_t start_ofs;
     int n_props = 0;
 
-    start_ofs = out->size;
     encap = ofpact_put_ENCAP(out);
     encap->hdr_size = 0;
     /* Parse encap header type. */
@@ -4186,7 +4187,7 @@ parse_ENCAP(char *arg,
         return error;
     }
     /* ofbuf out may have been re-allocated. */
-    encap = (struct ofpact_encap *) ((char *)out->data + start_ofs);
+    encap = out->header;
     encap->n_props = n_props;
     ofpact_finish_ENCAP(out, &encap);
     return NULL;
@@ -4198,6 +4199,8 @@ format_encap_pkt_type(const ovs_be32 pkt_type)
     switch (ntohl(pkt_type)) {
     case PT_ETH:
         return "ethernet";
+    case PT_NSH:
+        return "nsh";
     default:
         return "UNKNOWN";
     }
diff --git a/lib/ofp-ed-props.c b/lib/ofp-ed-props.c
index 8e58f6f..2d58fd4 100644
--- a/lib/ofp-ed-props.c
+++ b/lib/ofp-ed-props.c
@@ -30,14 +30,53 @@ decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
                size_t *remaining)
 {
     uint16_t prop_class = ntohs((*ofp_prop)->prop_class);
+    uint8_t prop_type = (*ofp_prop)->type;
     size_t len = (*ofp_prop)->len;
     size_t pad_len = ROUND_UP(len, 8);
 
     switch (prop_class) {
+    case OFPPPC_NSH: {
+        switch (prop_type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            struct ofp_ed_prop_nsh_md_type *opnmt =
+                    (struct ofp_ed_prop_nsh_md_type *) *ofp_prop;
+            if (len > sizeof(*opnmt) || len > *remaining) {
+                return OFPERR_OFPBAC_BAD_ED_PROP;
+            }
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                    ofpbuf_put_uninit(out, sizeof(*pnmt));
+            pnmt->header.prop_class = prop_class;
+            pnmt->header.type = prop_type;
+            pnmt->header.len = len;
+            pnmt->md_type = opnmt->md_type;
+            break;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            struct ofp_ed_prop_nsh_tlv *opnt =
+                    (struct ofp_ed_prop_nsh_tlv *) *ofp_prop;
+            size_t tlv_pad_len = ROUND_UP(opnt->tlv_len, 8);
+            if (len != sizeof(*opnt) + tlv_pad_len || len > *remaining) {
+                return OFPERR_OFPBAC_BAD_ED_PROP;
+            }
+            struct ofpact_ed_prop_nsh_tlv *pnt =
+                    ofpbuf_put_uninit(out, sizeof(*pnt));
+            pnt->header.prop_class = prop_class;
+            pnt->header.type = prop_type;
+            pnt->header.len = len;
+            pnt->tlv_class = opnt->tlv_class;
+            pnt->tlv_type = opnt->tlv_type;
+            pnt->tlv_len = opnt->tlv_len;
+            ofpbuf_put(out, opnt->data, tlv_pad_len);
+            break;
+        }
+        default:
+            return OFPERR_OFPBAC_UNKNOWN_ED_PROP;
+        }
+        break;
+    }
     default:
         return OFPERR_OFPBAC_UNKNOWN_ED_PROP;
     }
-
     *remaining -= pad_len;
     *ofp_prop = (const struct ofp_ed_prop_header *)
             ((char *)(*ofp_prop) + pad_len);
@@ -51,6 +90,43 @@ encode_ed_prop(const struct ofpact_ed_prop **prop,
     size_t prop_len;
 
     switch ((*prop)->prop_class) {
+    case OFPPPC_NSH: {
+        switch ((*prop)->type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                     (struct ofpact_ed_prop_nsh_md_type *) *prop;
+            struct ofp_ed_prop_nsh_md_type *opnmt =
+                    ofpbuf_put_uninit(out, sizeof(*opnmt));
+            opnmt->header.prop_class = htons((*prop)->prop_class);
+            opnmt->header.type = (*prop)->type;
+            opnmt->header.len =
+                    offsetof(struct ofp_ed_prop_nsh_md_type, pad);
+            opnmt->md_type = pnmt->md_type;
+            prop_len = sizeof(*pnmt);
+            break;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            struct ofpact_ed_prop_nsh_tlv *pnt =
+                    (struct ofpact_ed_prop_nsh_tlv *) *prop;
+            struct ofp_ed_prop_nsh_tlv *opnt;
+            size_t tlv_pad_len = ROUND_UP(pnt->tlv_len, 8);
+            size_t len = sizeof(*opnt) + tlv_pad_len;
+            opnt = ofpbuf_put_uninit(out, len);
+            opnt->header.prop_class = htons((*prop)->prop_class);
+            opnt->header.type = (*prop)->type;
+            opnt->header.len = len;
+            opnt->tlv_class = pnt->tlv_class;
+            opnt->tlv_type = pnt->tlv_type;
+            opnt->tlv_len = pnt->tlv_len;
+            memcpy(opnt->data, pnt->data, tlv_pad_len);
+            prop_len = sizeof(*pnt) + tlv_pad_len;
+            break;
+        }
+        default:
+            return OFPERR_OFPBAC_BAD_ARGUMENT;
+        }
+        break;
+    }
     default:
         return OFPERR_OFPBAC_BAD_ARGUMENT;
     }
@@ -71,6 +147,8 @@ parse_ed_prop_class(const char *str OVS_UNUSED,
         *prop_class = OFPPPC_GRE;
     } else if (!strcmp(str,"gtp")) {
         *prop_class = OFPPPC_GTP;
+    } else if (!strcmp(str,"nsh")) {
+        *prop_class = OFPPPC_NSH;
     } else {
         return false;
     }
@@ -83,6 +161,16 @@ parse_ed_prop_type(uint16_t prop_class,
                    uint8_t *type OVS_UNUSED)
 {
     switch (prop_class) {
+    case OFPPPC_NSH:
+        if (!strcmp(str, "md_type")) {
+            *type = OFPPPT_PROP_NSH_MDTYPE;
+            return true;
+        } else if (!strcmp(str, "tlv")) {
+            *type = OFPPPT_PROP_NSH_TLV;
+            return true;
+        } else {
+            return false;
+        }
     default:
         return false;
     }
@@ -99,12 +187,68 @@ char *
 parse_ed_prop_value(uint16_t prop_class, uint8_t prop_type OVS_UNUSED,
                     const char *value, struct ofpbuf *out OVS_UNUSED)
 {
+    char *error = NULL;
 
     if (value == NULL || *value == '\0') {
         return xstrdup("Value missing for encap property");
     }
 
     switch (prop_class) {
+    case OFPPPC_NSH:
+        switch (prop_type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            /* Format: "<md_type>:uint8_t". */
+            uint8_t md_type;
+            error = str_to_u8(value, "md_type", &md_type);
+            if (error != NULL) {
+                return error;
+            }
+            if (md_type < 1 || md_type > 2) {
+                return xstrdup("invalid md_type");
+            }
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                    ofpbuf_put_uninit(out, sizeof(*pnmt));
+            pnmt->header.prop_class = prop_class;
+            pnmt->header.type = prop_type;
+            pnmt->header.len =
+                    offsetof(struct ofp_ed_prop_nsh_md_type, pad);
+            pnmt->md_type = md_type;
+            break;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            /* Format: "<tlv_class>:ovs_be16,<tlv_type>:uint8_t,<tlv_val>:hex_string" */
+            struct ofpact_ed_prop_nsh_tlv *pnt;
+            uint16_t tlv_class;
+            uint8_t tlv_type;
+            char buf[256];
+            size_t tlv_value_len, padding;
+            size_t start_ofs = out->size;
+
+            if (!ovs_scan(value, "0x%"SCNx16",%"SCNu8",0x%251[0-9a-fA-F]",
+                          &tlv_class, &tlv_type, buf)) {
+                return xasprintf("Invalid NSH TLV header: %s", value);
+            }
+            ofpbuf_put_uninit(out, sizeof(*pnt));
+            ofpbuf_put_hex(out, buf, &tlv_value_len);
+            pnt = (struct ofpact_ed_prop_nsh_tlv *)
+                    ((char *)out->data + start_ofs);
+            padding = ROUND_UP(tlv_value_len, 8) - tlv_value_len;
+            pnt->header.prop_class = prop_class;
+            pnt->header.type = prop_type;
+            pnt->header.len = sizeof(*pnt) + tlv_value_len + padding;
+            pnt->tlv_class = htons(tlv_class);
+            pnt->tlv_type = tlv_type;
+            pnt->tlv_len = tlv_value_len;
+            if (padding > 0) {
+                ofpbuf_put_zeros(out, padding);
+            }
+            break;
+        }
+        default:
+            /* Unsupported property types rejected before. */
+            OVS_NOT_REACHED();
+        }
+        break;
     default:
         /* Unsupported property classes rejected before. */
         OVS_NOT_REACHED();
@@ -125,6 +269,8 @@ format_ed_prop_class(const struct ofpact_ed_prop *prop)
         return "gre";
     case OFPPPC_GTP:
         return "gtp";
+    case OFPPPC_NSH:
+        return "nsh";
     default:
         OVS_NOT_REACHED();
     }
@@ -134,6 +280,16 @@ char *
 format_ed_prop_type(const struct ofpact_ed_prop *prop)
 {
     switch (prop->prop_class) {
+    case OFPPPC_NSH:
+        switch (prop->type) {
+        case OFPPPT_PROP_NSH_MDTYPE:
+            return "md_type";
+        case OFPPPT_PROP_NSH_TLV:
+            return "tlv";
+        default:
+            OVS_NOT_REACHED();
+        }
+        break;
     default:
         OVS_NOT_REACHED();
     }
@@ -144,6 +300,26 @@ format_ed_prop_value(struct ds *s OVS_UNUSED,
                      const struct ofpact_ed_prop *prop)
 {
     switch (prop->prop_class) {
+    case OFPPPC_NSH:
+        switch (prop->type) {
+        case OFPPPT_PROP_NSH_MDTYPE: {
+            struct ofpact_ed_prop_nsh_md_type *pnmt =
+                    (struct ofpact_ed_prop_nsh_md_type *) prop;
+            ds_put_format(s, "val=%d", pnmt->md_type);
+            return;
+        }
+        case OFPPPT_PROP_NSH_TLV: {
+            struct ofpact_ed_prop_nsh_tlv *pnt =
+                    (struct ofpact_ed_prop_nsh_tlv *) prop;
+            ds_put_format(s, "val(0x%04x,%d,",
+                          ntohs(pnt->tlv_class), pnt->tlv_type);
+            ds_put_hex(s, pnt->data, pnt->tlv_len);
+            ds_put_cstr(s,")");
+            return;
+        }
+        default:
+            OVS_NOT_REACHED();
+        }
     default:
         OVS_NOT_REACHED();
     }
diff --git a/lib/packets.c b/lib/packets.c
index d51c91a..0592596 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -402,6 +402,77 @@ pop_mpls(struct dp_packet *packet, ovs_be16 ethtype)
     }
 }
 
+void
+encap_nsh(struct dp_packet *packet, const struct ovs_action_encap_nsh *encap)
+{
+    struct nsh_hdr *nsh;
+    size_t length = NSH_BASE_HDR_LEN + encap->mdlen;
+    uint8_t next_proto;
+
+    switch(ntohl(packet->packet_type)) {
+        case PT_ETH:
+            next_proto = NSH_P_ETHERNET;
+            break;
+        case PT_IPV4:
+            next_proto = NSH_P_IPV4;
+            break;
+        case PT_IPV6:
+            next_proto = NSH_P_IPV6;
+            break;
+        default:
+            OVS_NOT_REACHED();
+    }
+
+    nsh = (struct nsh_hdr *) dp_packet_push_uninit(packet, length);
+    nsh->ver_flags_len = htons(encap->flags << NSH_FLAGS_SHIFT | length >> 2);
+    nsh->next_proto = next_proto;
+    nsh->path_hdr = encap->path_hdr;
+    nsh->md_type = encap->mdtype;
+    switch (nsh->md_type) {
+        case NSH_M_TYPE1:
+            nsh->md1 = *(struct nsh_md1_ctx *) encap->metadata;
+            break;
+        case NSH_M_TYPE2:
+            /* TODO */
+            break;
+        default:
+            OVS_NOT_REACHED();
+    }
+
+    packet->packet_type = htonl(PT_NSH);
+    dp_packet_reset_offsets(packet);
+    packet->l3_ofs = 0;
+}
+
+void
+decap_nsh(struct dp_packet *packet)
+{
+    struct nsh_hdr *nsh = (struct nsh_hdr *) dp_packet_l3(packet);
+    size_t length;
+    uint32_t next_pt;
+
+    if (packet->packet_type == htonl(PT_NSH) && nsh) {
+        switch(nsh->next_proto) {
+            case NSH_P_ETHERNET:
+                next_pt = PT_ETH;
+                break;
+            case NSH_P_IPV4:
+                next_pt = PT_IPV4;
+                break;
+            case NSH_P_IPV6:
+                next_pt = PT_IPV6;
+                break;
+            default:
+                OVS_NOT_REACHED();
+        }
+
+        length = nsh_hdr_len(nsh);
+        dp_packet_reset_packet(packet, length);
+        packet->packet_type = htonl(next_pt);
+        /* Packet must be recirculated for further processing. */
+    }
+}
+
 /* Converts hex digits in 'hex' to an Ethernet packet in '*packetp'.  The
  * caller must free '*packetp'.  On success, returns NULL.  On failure, returns
  * an error message and stores NULL in '*packetp'.
diff --git a/lib/packets.h b/lib/packets.h
index 9a85675..19571b1 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -407,6 +407,10 @@ void push_eth(struct dp_packet *packet, const struct eth_addr *dst,
               const struct eth_addr *src);
 void pop_eth(struct dp_packet *packet);
 
+void encap_nsh(struct dp_packet *packet,
+               const struct ovs_action_encap_nsh *encap_nsh);
+void decap_nsh(struct dp_packet *packet);
+
 #define LLC_DSAP_SNAP 0xaa
 #define LLC_SSAP_SNAP 0xaa
 #define LLC_CNTL_SNAP 3
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index e4cca65..baa3526 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1199,6 +1199,8 @@ dpif_sflow_read_actions(const struct flow *flow,
 	    break;
 	case OVS_ACTION_ATTR_SAMPLE:
 	case OVS_ACTION_ATTR_CLONE:
+	case OVS_ACTION_ATTR_ENCAP_NSH:
+	case OVS_ACTION_ATTR_DECAP_NSH:
 	case OVS_ACTION_ATTR_UNSPEC:
 	case __OVS_ACTION_ATTR_MAX:
 	default:
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index cdab421..238e620 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -55,6 +55,7 @@
 #include "openvswitch/meta-flow.h"
 #include "openvswitch/list.h"
 #include "openvswitch/ofp-actions.h"
+#include "openvswitch/ofp-ed-props.h"
 #include "openvswitch/vlog.h"
 #include "ovs-lldp.h"
 #include "ovs-router.h"
@@ -4152,6 +4153,8 @@ xlate_fixup_actions(struct ofpbuf *b, const struct nlattr *actions,
         case OVS_ACTION_ATTR_CT:
         case OVS_ACTION_ATTR_PUSH_ETH:
         case OVS_ACTION_ATTR_POP_ETH:
+        case OVS_ACTION_ATTR_ENCAP_NSH:
+        case OVS_ACTION_ATTR_DECAP_NSH:
         case OVS_ACTION_ATTR_METER:
             ofpbuf_put(b, a, nl_attr_len_pad(a, left));
             break;
@@ -5465,6 +5468,103 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
 }
 
 static void
+rewrite_flow_encap_nsh(struct xlate_ctx *ctx,
+                       const struct ofpact_encap *encap,
+                       struct flow *flow,
+                       struct flow_wildcards *wc)
+{
+    ovs_be32 packet_type = flow->packet_type;
+    const uint8_t *ptr = (uint8_t *) encap->props;
+    size_t len, padding;
+    uint8_t md_type = NSH_M_TYPE1;
+    uint8_t np = 0;
+    uint8_t buf[512], *p = buf;
+    int i;
+
+    /* Parse the optional NSH encap TLV properties, if any. */
+    for (i=0; i<encap->n_props; i++) {
+        struct ofpact_ed_prop *prop_ptr = (struct ofpact_ed_prop *) ptr;
+        if (prop_ptr->prop_class == OFPPPC_NSH) {
+            switch (prop_ptr->type) {
+                case OFPPPT_PROP_NSH_MDTYPE: {
+                    struct ofpact_ed_prop_nsh_md_type *prop_md_type =
+                            (struct ofpact_ed_prop_nsh_md_type *) prop_ptr;
+                    md_type = prop_md_type->md_type;
+                    break;
+                }
+                case OFPPPT_PROP_NSH_TLV: {
+                    struct nsh_md2_tlv *md2_ctx = (struct nsh_md2_tlv *) p;
+                    struct ofpact_ed_prop_nsh_tlv *tlv_prop =
+                            (struct ofpact_ed_prop_nsh_tlv *) prop_ptr;
+                    md2_ctx->md_class = tlv_prop->tlv_class;
+                    md2_ctx->type = tlv_prop->tlv_type;
+                    md2_ctx->length = tlv_prop->tlv_len;
+                    len = ROUND_UP(md2_ctx->length, 4);
+                    padding = len - md2_ctx->length;
+                    if (p + len < buf + 512) {
+                        memcpy(md2_ctx->md_value, tlv_prop->data, md2_ctx->length);
+                        p += md2_ctx->length;
+                        memset(p, 0, padding);
+                        p += padding;
+                    }
+                    break;
+                }
+                default:
+                    /* No other NSH encap properties defined yet. */
+                    break;
+            }
+        }
+        ptr += ROUND_UP(prop_ptr->len, 8);
+    }
+
+    /* Determine the Next Protocol field for NSH header. */
+    switch (ntohl(packet_type)) {
+        case PT_ETH:
+            np = NSH_P_ETHERNET;
+            break;
+        case PT_IPV4:
+            np = NSH_P_IPV4;
+            break;
+        case PT_IPV6:
+            np = NSH_P_IPV6;
+            break;
+        case PT_NSH:
+            np = NSH_P_NSH;
+            break;
+        default:
+            xlate_report_debug(ctx, OFT_ACTION,
+                               "encap(nsh) for unsupported packet type %x",
+                               ntohl(packet_type));
+            ctx->error = 1;
+            return;
+    }
+    /* Note that we have matched on packet_type! */
+    wc->masks.packet_type = OVS_BE32_MAX;
+
+    /* Reset all current flow packet headers. */
+    memset(&flow->dl_dst, 0, sizeof(struct flow) - offsetof(struct flow, dl_dst));
+
+    /* Populate the flow with the new NSH header. */
+    flow->packet_type = htonl(PT_NSH);
+    flow->dl_type = htons(ETH_TYPE_NSH);
+    flow->nsh.flags = 0;    /* */
+    flow->nsh.np = np;
+    flow->nsh.spi = 0;
+    flow->nsh.si = 255;
+
+    if (md_type == NSH_M_TYPE1) {
+        flow->nsh.mdtype = NSH_M_TYPE1;
+        flow->nsh.c1 = 0;
+        flow->nsh.c2 = 0;
+        flow->nsh.c3 = 0;
+        flow->nsh.c4 = 0;
+    } else if (md_type == NSH_M_TYPE2) {
+        flow->nsh.mdtype = NSH_M_TYPE1;
+        /* TODO: Deal with TLV context headers stored in buf[512]. */
+    }
+}
+
+static void
 xlate_generic_encap_action(struct xlate_ctx *ctx,
                            const struct ofpact_encap *encap)
 {
@@ -5480,9 +5580,11 @@ xlate_generic_encap_action(struct xlate_ctx *ctx,
         case PT_ETH:
             rewrite_flow_encap_ethernet(ctx, flow, wc);
             break;
+        case PT_NSH:
+            rewrite_flow_encap_nsh(ctx, encap, flow, wc);
+            break;
         default:
-            /* TODO: Error handling: Should not happen if the PT is checked
-             * at decoding */
+            OVS_NOT_REACHED();
             break;
     }
 
@@ -5511,6 +5613,32 @@ xlate_generic_decap_action(struct xlate_ctx *ctx,
             flow->packet_type = htonl(PACKET_TYPE(OFPHTN_ETHERTYPE,
                                                   ntohs(flow->dl_type)));
             return false;
+        case PT_NSH:
+            /* decap_nsh */
+            /* Delay generating decap_nsh to the next commit. */
+            switch (flow->nsh.np) {
+            case NSH_P_ETHERNET:
+                flow->packet_type = htonl(PT_ETH);
+                break;
+            case NSH_P_IPV4:
+                flow->packet_type = htonl(PT_IPV4);
+                break;
+            case NSH_P_IPV6:
+                flow->packet_type = htonl(PT_IPV6);
+                break;
+            case NSH_P_NSH:
+                flow->packet_type = htonl(PT_NSH);
+                break;
+            default:
+                xlate_report_debug(ctx, OFT_ACTION,
+                                   "decap() NSH Next Protocol not supported"
+                                   " %x", flow->nsh.np);
+                /* TODO: Error handling: drop packet. */
+                ctx->error = 1;
+                return false;
+                break;
+            }
+            return true;
         default:
             xlate_report_debug(ctx, OFT_ACTION,
                                "decap() for unsupported packet type %x",
-- 
2.1.0



More information about the dev mailing list