@@ -798,6 +798,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.
*
@@ -873,6 +892,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
@@ -908,6 +929,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*/
@@ -95,6 +95,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
@@ -102,8 +104,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)
@@ -27,9 +27,10 @@ extern "C" {
enum ofp_ed_prop_class {
OFPPPC_BASIC = 0, /* ONF Basic class. */
- OFPPPC_MPLS = 1, /* MPLS property class. */
- OFPPPC_GRE = 2, /* GRE property class. */
- OFPPPC_GTP = 3, /* GTP property 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 */
/* Experimenter property class.
*
@@ -39,6 +40,12 @@ enum ofp_ed_prop_class {
OFPPPC_EXPERIMENTER=0xffff
};
+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 */
+};
+
/*
* External representation of encap/decap properties.
* These must be padded to a multiple of 8 bytes.
@@ -49,6 +56,22 @@ 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). */
+
+ /* tlv_len octets of metadata value, padded to a multiple of 8 bytes. */
+ uint8_t data[0];
+};
+
/*
* Internal representation of encap/decap properties
*/
@@ -58,6 +81,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). */
+
+ /* tlv_len octets of metadata value, padded to a multiple of 8 bytes. */
+ uint8_t data[0];
+};
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,
@@ -5407,6 +5407,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();
}
@@ -1255,6 +1255,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();
@@ -1158,9 +1158,9 @@ format_be32_masked_hex(struct ds *s, const char *name,
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));
+ ds_put_format(s, "0x%"PRIx32, ntohl(value));
} else {
- ds_put_format(s, "0x%08"PRIx32"/0x%08"PRIx32,
+ ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
ntohl(value), ntohl(mask));
}
ds_put_char(s, ',');
@@ -1263,23 +1263,25 @@ format_nsh_masked(struct ds *s, const struct flow *f, const struct flow *m)
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);
+ format_be32_masked_hex(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);
+ if (m->nsh.mdtype == UINT8_MAX && f->nsh.mdtype == NSH_M_TYPE1) {
+ 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);
+ }
}
}
@@ -296,10 +296,9 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
memcpy(&nsh->md1, &key->c1, sizeof(struct nsh_md1_ctx));
break;
case NSH_M_TYPE2:
- /* TODO */
- break;
default:
- OVS_NOT_REACHED();
+ /* No support for setting any other metadata format yet. */
+ break;
}
} else {
flags = (ntohs(nsh->ver_flags_len) & NSH_FLAGS_MASK) >>
@@ -313,20 +312,19 @@ odp_set_nsh(struct dp_packet *packet, const struct ovs_key_nsh *key,
path_hdr);
switch (nsh->md_type) {
case NSH_M_TYPE1:
- len = sizeof(struct nsh_md1_ctx) >> 1;
/* Avoid the 16/32 bit alignment hassle. */
+ len = sizeof(struct nsh_md1_ctx) >> 1;
p = (uint16_t *) &nsh->md1.c1;
k = (uint16_t *) &key->c1;
m = (uint16_t *) &mask->c1;
- for (i=0; i<len; i++, p++, k++, m++) {
+ for (i = 0; i < len; i++, p++, k++, m++) {
*p = *k | (*p & ~*m);
}
break;
case NSH_M_TYPE2:
- /* TODO */
- break;
default:
- OVS_NOT_REACHED();
+ /* No support for setting any other metadata format yet. */
+ break;
}
}
}
@@ -663,6 +661,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:
@@ -827,6 +827,26 @@ 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: {
+ size_t i, num = batch->count;
+
+ DP_PACKET_BATCH_REFILL_FOR_EACH (i, num, packet, batch) {
+ if (decap_nsh(packet)) {
+ dp_packet_batch_refill(batch, packet, i);
+ } else {
+ dp_packet_delete(packet);
+ }
+ }
+ break;
+ }
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -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:
@@ -254,23 +256,22 @@ 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",
+ ds_put_format(ds, ",spi=0x%x",
(ntohl(key->path_hdr) & NSH_SPI_MASK) >> NSH_SPI_SHIFT);
ds_put_format(ds, ",si=%d",
(ntohl(key->path_hdr) & NSH_SI_MASK) >> NSH_SI_SHIFT);
switch (key->mdtype) {
case NSH_M_TYPE1:
- 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));
+ ds_put_format(ds, ",c1=0x%x", ntohl(key->c1));
+ ds_put_format(ds, ",c2=0x%x", ntohl(key->c2));
+ ds_put_format(ds, ",c3=0x%x", ntohl(key->c3));
+ ds_put_format(ds, ",c4=0x%x", ntohl(key->c4));
break;
case NSH_M_TYPE2:
- /* TODO */
- break;
default:
- OVS_NOT_REACHED();
+ /* No support for matching other metadata formats yet. */
+ break;
}
}
@@ -302,9 +303,9 @@ format_be32_masked(struct ds *s, bool *first, const char *name,
}
ds_put_format(s, "%s=", name);
if (mask == OVS_BE32_MAX) {
- ds_put_format(s, "0x%08"PRIx32, ntohl(value));
+ ds_put_format(s, "0x%"PRIx32, ntohl(value));
} else {
- ds_put_format(s, "0x%08"PRIx32"/0x%08"PRIx32,
+ ds_put_format(s, "0x%"PRIx32"/0x%"PRIx32,
ntohl(value), ntohl(mask));
}
*first = false;
@@ -341,6 +342,40 @@ 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 ovs_action_encap_nsh *encap_nsh)
+ {
+ uint32_t path_hdr = ntohl(encap_nsh->path_hdr);
+ uint32_t spi = (path_hdr & NSH_SPI_MASK) >> NSH_SPI_SHIFT;
+ uint8_t si = (path_hdr & NSH_SI_MASK) >> NSH_SI_SHIFT;
+
+ ds_put_cstr(ds, "encap_nsh(");
+ ds_put_format(ds, "flags=%d", encap_nsh->flags);
+ ds_put_format(ds, ",mdtype=%d", encap_nsh->mdtype);
+ ds_put_format(ds, ",np=%d", encap_nsh->np);
+ ds_put_format(ds, ",spi=0x%x", spi);
+ ds_put_format(ds, ",si=%d", si);
+ switch (encap_nsh->mdtype) {
+ case NSH_M_TYPE1: {
+ struct nsh_md1_ctx *md1_ctx =
+ ALIGNED_CAST(struct nsh_md1_ctx *, encap_nsh->metadata);
+ ds_put_format(ds, ",c1=0x%x", ntohl(md1_ctx->c1));
+ ds_put_format(ds, ",c2=0x%x", ntohl(md1_ctx->c2));
+ ds_put_format(ds, ",c3=0x%x", ntohl(md1_ctx->c3));
+ ds_put_format(ds, ",c4=0x%x", ntohl(md1_ctx->c4));
+ break;
+ }
+ case NSH_M_TYPE2:
+ ds_put_cstr(ds, ",md2=");
+ ds_put_hex(ds, encap_nsh->metadata, encap_nsh->mdlen);
+ break;
+ default:
+ OVS_NOT_REACHED();
+ }
+ ds_put_format(ds, ")");
+}
+
static const char *
slow_path_reason_to_string(uint32_t reason)
{
@@ -636,7 +671,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,
@@ -1023,6 +1058,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, nl_attr_get(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:
@@ -1741,6 +1782,113 @@ 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:
+ /* This is the default format. */;
+ break;
+ case NSH_M_TYPE2:
+ /* Length will be updated later. */
+ 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=0x%"SCNx32, &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) {
+ struct ofpbuf b;
+ char buf[512];
+ size_t mdlen;
+ if (ovs_scan_len(s, &n, "md2=0x%511[0-9a-fA-F]", buf)) {
+ ofpbuf_use_stub(&b, encap_nsh.metadata,
+ OVS_ENCAP_NSH_MAX_MD_LEN);
+ ofpbuf_put_hex(&b, buf, &mdlen);
+ encap_nsh.mdlen = mdlen;
+ ofpbuf_uninit(&b);
+ }
+ continue;
+ }
+ }
+out:
+ if (ret < 0) {
+ return ret;
+ } else {
+ size_t size = offsetof(struct ovs_action_encap_nsh, metadata)
+ + ROUND_UP(encap_nsh.mdlen, 4);
+ 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)
{
@@ -1942,6 +2090,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;
@@ -2455,6 +2621,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)
@@ -6469,7 +6636,8 @@ get_nsh_key(const struct flow *flow, struct ovs_key_nsh *nsh, bool is_mask)
nsh->c4 = flow->nsh.c4;
break;
case NSH_M_TYPE2:
- /* TODO: MD type 2 */
+ default:
+ /* No match support for other MD formats yet. */
break;
}
}
@@ -6493,11 +6661,12 @@ put_nsh_key(const struct ovs_key_nsh *nsh, struct flow *flow,
flow->nsh.c4 = nsh->c4;
break;
case NSH_M_TYPE2:
- /* TODO: MD type 2 */
+ default:
flow->nsh.c1 = 0;
flow->nsh.c2 = 0;
flow->nsh.c3 = 0;
flow->nsh.c4 = 0;
+ /* No match support for other MD formats yet. */
break;
}
}
@@ -6629,11 +6798,58 @@ 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 ofpbuf *encap_data)
+{
+ 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:
+ if (encap_data) {
+ ovs_assert(encap_data->size < OVS_ENCAP_NSH_MAX_MD_LEN);
+ encap_nsh.mdlen = encap_data->size;
+ memcpy(encap_nsh.metadata, encap_data->data, encap_data->size);
+ } else {
+ 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,
struct flow_wildcards *wc,
- bool pending_encap)
+ bool pending_encap,
+ struct ofpbuf *encap_data)
{
if (flow->packet_type == base_flow->packet_type) {
return;
@@ -6650,22 +6866,40 @@ 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, encap_data);
+ base_flow->packet_type = flow->packet_type;
+ /* Update all packet headers in base_flow. */
+ memcpy(&base_flow->dl_dst, &flow->dl_dst,
+ sizeof(*flow) - offsetof(struct flow, dl_dst));
+ break;
default:
- /* Only the above protocols are supported for encap. The check
- * is done at action decoding. */
+ /* Only the above protocols are supported for encap.
+ * The check is done at action translation. */
OVS_NOT_REACHED();
}
} else {
+ /* This is an explicit or implicit decap case. */
if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE &&
base_flow->packet_type == htonl(PT_ETH)) {
- /* pop_eth */
+ /* Generate pop_eth and continue without recirculation. */
odp_put_pop_eth_action(odp_actions);
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();
+ /* All other decap cases require recirculation.
+ * No need to update the base flow here. */
+ switch (ntohl(base_flow->packet_type)) {
+ case PT_NSH:
+ /* decap_nsh. */
+ odp_put_decap_nsh_action(odp_actions);
+ break;
+ default:
+ /* Checks are done during translation. */
+ OVS_NOT_REACHED();
+ }
}
}
@@ -6684,12 +6918,14 @@ commit_packet_type_change(const struct flow *flow,
enum slow_path_reason
commit_odp_actions(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
- bool use_masked, bool pending_encap)
+ bool use_masked, bool pending_encap,
+ struct ofpbuf *encap_data)
{
enum slow_path_reason slow1, slow2;
bool mpls_done = false;
- commit_packet_type_change(flow, base, odp_actions, wc, pending_encap);
+ commit_packet_type_change(flow, base, odp_actions, wc,
+ pending_encap, encap_data);
commit_set_ether_action(flow, base, odp_actions, wc, use_masked);
/* Make packet a non-MPLS packet before committing L3/4 actions,
* which would otherwise do nothing. */
@@ -277,7 +277,8 @@ enum slow_path_reason commit_odp_actions(const struct flow *,
struct ofpbuf *odp_actions,
struct flow_wildcards *wc,
bool use_masked,
- bool pending_encap);
+ bool pending_encap,
+ struct ofpbuf *encap_data);
/* ofproto-dpif interface.
*
@@ -4055,6 +4055,7 @@ decode_NXAST_RAW_ENCAP(const struct nx_action_encap *nae,
encap->ofpact.raw = NXAST_RAW_ENCAP;
switch (ntohl(nae->new_pkt_type)) {
case PT_ETH:
+ case PT_NSH:
/* Add supported encap header types here. */
break;
default:
@@ -4103,6 +4104,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;
}
@@ -4178,6 +4181,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";
}
@@ -30,6 +30,7 @@ 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);
@@ -38,6 +39,45 @@ decode_ed_prop(const struct ofp_ed_prop_header **ofp_prop,
}
switch (prop_class) {
+ case OFPPPC_NSH: {
+ switch (prop_type) {
+ case OFPPPT_PROP_NSH_MDTYPE: {
+ struct ofp_ed_prop_nsh_md_type *opnmt =
+ ALIGNED_CAST(struct ofp_ed_prop_nsh_md_type *, *ofp_prop);
+ if (len > sizeof(*opnmt) || len > *remaining) {
+ return OFPERR_NXBAC_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 =
+ ALIGNED_CAST(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_NXBAC_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_NXBAC_UNKNOWN_ED_PROP;
+ }
+ break;
+ }
default:
return OFPERR_NXBAC_UNKNOWN_ED_PROP;
}
@@ -55,6 +95,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 =
+ ALIGNED_CAST(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 =
+ ALIGNED_CAST(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;
}
@@ -78,6 +155,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;
}
@@ -90,6 +169,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;
}
@@ -106,12 +195,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: "<class>:ovs_be16,<type>:uint8_t,<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 = ALIGNED_CAST(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();
@@ -132,6 +277,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();
}
@@ -141,6 +288,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();
}
@@ -151,6 +308,28 @@ format_ed_prop(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 =
+ ALIGNED_CAST(struct ofpact_ed_prop_nsh_md_type *, prop);
+ ds_put_format(s, "%s=%d", format_ed_prop_type(prop),
+ pnmt->md_type);
+ return;
+ }
+ case OFPPPT_PROP_NSH_TLV: {
+ struct ofpact_ed_prop_nsh_tlv *pnt =
+ ALIGNED_CAST(struct ofpact_ed_prop_nsh_tlv *, prop);
+ ds_put_format(s, "%s(0x%04x,%d,",
+ format_ed_prop_type(prop),
+ 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();
}
@@ -402,6 +402,88 @@ 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;
+ case PT_NSH:
+ next_proto = NSH_P_NSH;
+ 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 = *ALIGNED_CAST(struct nsh_md1_ctx *, encap->metadata);
+ break;
+ case NSH_M_TYPE2: {
+ /* The MD2 metadata in encap is already padded to 4 bytes. */
+ size_t len = ROUND_UP(encap->mdlen, 4);
+ memcpy(nsh->md2, encap->metadata, len);
+ break;
+ }
+ default:
+ OVS_NOT_REACHED();
+ }
+
+ packet->packet_type = htonl(PT_NSH);
+ dp_packet_reset_offsets(packet);
+ packet->l3_ofs = 0;
+}
+
+bool
+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;
+ case NSH_P_NSH:
+ next_pt = PT_NSH;
+ break;
+ default:
+ /* Unknown inner packet type. Drop packet. */
+ return false;
+ }
+
+ length = nsh_hdr_len(nsh);
+ dp_packet_reset_packet(packet, length);
+ packet->packet_type = htonl(next_pt);
+ /* Packet must be recirculated for further processing. */
+ }
+ return true;
+}
+
/* 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'.
@@ -415,6 +415,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);
+bool decap_nsh(struct dp_packet *packet);
+
#define LLC_DSAP_SNAP 0xaa
#define LLC_SSAP_SNAP 0xaa
#define LLC_CNTL_SNAP 3
@@ -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:
@@ -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"
@@ -234,8 +235,10 @@ struct xlate_ctx {
bool in_action_set; /* Currently translating action_set, if true. */
bool in_packet_out; /* Currently translating a packet_out msg, if
* true. */
- bool pending_encap; /* Waiting to commit a pending encap
- * action, if true. */
+ bool pending_encap; /* True when waiting to commit a pending
+ * encap action. */
+ struct ofpbuf *encap_data; /* May contain a pointer to an ofpbuf with
+ * context for the datapath encap action.*/
uint8_t table_id; /* OpenFlow table ID where flow was found. */
ovs_be64 rule_cookie; /* Cookie of the rule being translated. */
@@ -3468,8 +3471,11 @@ xlate_commit_actions(struct xlate_ctx *ctx)
ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
ctx->odp_actions, ctx->wc,
- use_masked, ctx->pending_encap);
+ use_masked, ctx->pending_encap,
+ ctx->encap_data);
ctx->pending_encap = false;
+ ofpbuf_delete(ctx->encap_data);
+ ctx->encap_data = NULL;
}
static void
@@ -4386,6 +4392,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;
@@ -5711,20 +5719,131 @@ rewrite_flow_encap_ethernet(struct xlate_ctx *ctx,
flow->dl_dst = eth_addr_zero;
flow->dl_type = ethertype;
} else {
+ /* Error handling: drop packet. */
xlate_report_debug(ctx, OFT_ACTION,
- "encap(ethernet) unsupported for packet type "
- "ethernet");
- /* TODO: Error handling: drop packet. */
+ "Dropping packet as encap(ethernet) is not "
+ "supported for packet type ethernet.");
ctx->error = 1;
}
}
+/* For an MD2 NSH header returns a pointer to an ofpbuf with the encoded
+ * MD2 TLVs provided as encap properties to the encap operation. This
+ * will be stored as encap_data in the ctx and copied into the encap_nsh
+ * action at the next commit. */
+static struct ofpbuf *
+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 char *ptr = (char *) encap->props;
+ struct ofpbuf *buf = ofpbuf_new(256);
+ uint8_t md_type = NSH_M_TYPE1;
+ uint8_t np = 0;
+ int i;
+
+ /* Scan 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 ofpact_ed_prop_nsh_tlv *tlv_prop =
+ (struct ofpact_ed_prop_nsh_tlv *) prop_ptr;
+ struct nsh_md2_tlv *md2_ctx =
+ ofpbuf_put_uninit(buf, sizeof(*md2_ctx));
+ md2_ctx->md_class = tlv_prop->tlv_class;
+ md2_ctx->type = tlv_prop->tlv_type;
+ md2_ctx->length = tlv_prop->tlv_len;
+ size_t len = ROUND_UP(md2_ctx->length, 4);
+ size_t padding = len - md2_ctx->length;
+ ofpbuf_put(buf, tlv_prop->data, md2_ctx->length);
+ ofpbuf_put_zeros(buf, padding);
+ break;
+ }
+ default:
+ /* No other NSH encap properties defined yet. */
+ break;
+ }
+ }
+ ptr += ROUND_UP(prop_ptr->len, 8);
+ }
+ if (buf->size == 0 || buf->size >= OVS_ENCAP_NSH_MAX_MD_LEN) {
+ ofpbuf_delete(buf);
+ buf = NULL;
+ }
+
+ /* 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:
+ /* Error handling: drop packet. */
+ xlate_report_debug(ctx, OFT_ACTION,
+ "Dropping packet as encap(nsh) is not "
+ "supported for packet type (%d,0x%x)",
+ pt_ns(packet_type), pt_ns_type(packet_type));
+ ctx->error = 1;
+ return buf;
+ }
+ /* 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;
+ if (buf) {
+ /* Drop any MD2 context TLVs. */
+ ofpbuf_delete(buf);
+ buf = NULL;
+ }
+ } else if (md_type == NSH_M_TYPE2) {
+ flow->nsh.mdtype = NSH_M_TYPE2;
+ }
+
+ return buf;
+}
+
static void
xlate_generic_encap_action(struct xlate_ctx *ctx,
const struct ofpact_encap *encap)
{
struct flow *flow = &ctx->xin->flow;
struct flow_wildcards *wc = ctx->wc;
+ struct ofpbuf *encap_data = NULL;
/* Ensure that any pending actions on the inner packet are applied before
* rewriting the flow */
@@ -5735,15 +5854,19 @@ xlate_generic_encap_action(struct xlate_ctx *ctx,
case PT_ETH:
rewrite_flow_encap_ethernet(ctx, flow, wc);
break;
+ case PT_NSH:
+ encap_data = rewrite_flow_encap_nsh(ctx, encap, flow, wc);
+ break;
default:
- /* TODO: Error handling: Should not happen if the PT is checked
- * at decoding */
+ /* New packet type was checked during decoding. */
+ OVS_NOT_REACHED();
break;
}
if (!ctx->error) {
/* The actual encap datapath action will be generated at next commit. */
ctx->pending_encap = true;
+ ctx->encap_data = encap_data;
}
}
@@ -5774,11 +5897,42 @@ xlate_generic_decap_action(struct xlate_ctx *ctx,
ctx->wc->masks.dl_type = OVS_BE16_MAX;
}
return false;
+ case PT_NSH:
+ /* The decap_nsh action is generated at the commit executed as
+ * part of freezing the ctx for recirculation. Here we just set
+ * the new packet type based on the NSH next protocol field. */
+ 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:
+ /* Error handling: drop packet. */
+ xlate_report_debug(ctx, OFT_ACTION,
+ "Dropping packet as NSH next protocol %d "
+ "is not supported", flow->nsh.np);
+ ctx->error = 1;
+ return false;
+ break;
+ }
+ ctx->wc->masks.nsh.np = UINT8_MAX;
+ /* Trigger recirculation. */
+ return true;
default:
- xlate_report_debug(ctx, OFT_ACTION,
- "decap() for unsupported packet type %x",
- ntohl(flow->packet_type));
- /* TODO: Error handling: drop packet. */
+ /* Error handling: drop packet. */
+ xlate_report_debug(
+ ctx, OFT_ACTION,
+ "Dropping packet as the decap() does not support "
+ "packet type (%d,0x%x)",
+ pt_ns(flow->packet_type), pt_ns_type(flow->packet_type));
ctx->error = 1;
return false;
}
@@ -6624,6 +6778,7 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
.in_action_set = false,
.in_packet_out = xin->in_packet_out,
.pending_encap = false,
+ .encap_data = NULL,
.table_id = 0,
.rule_cookie = OVS_BE64_MAX,
@@ -6971,6 +7126,7 @@ exit:
ofpbuf_uninit(&ctx.action_set);
ofpbuf_uninit(&ctx.frozen_actions);
ofpbuf_uninit(&scratch_actions);
+ ofpbuf_delete(ctx.encap_data);
/* Make sure we return a "drop flow" in case of an error. */
if (ctx.error) {
@@ -17,25 +17,25 @@ AT_CHECK([
ovs-ofctl -Oopenflow13 add-flows br0 flows.txt
ovs-ofctl -Oopenflow13 dump-flows br0 | ofctl_strip | sort | grep actions
], [0], [dnl
- in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=1193046,nsh_si=255,nsh_c1=0x11223344 actions=set_field:128->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2
+ in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344 actions=set_field:128->nsh_flags,set_field:254->nsh_si,set_field:0x44332211->nsh_c1,output:2
])
AT_CHECK([
ovs-appctl ofproto/trace br0 'in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00'
], [0], [dnl
-Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=1193046,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
bridge("br0")
-------------
- 0. in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=1193046,nsh_si=255,nsh_c1=0x11223344, priority 32768
+ 0. in_port=1,dl_type=0x894f,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344, priority 32768
set_field:128->nsh_flags
set_field:254->nsh_si
set_field:0x44332211->nsh_c1
output:2
-Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=128,nsh_mdtype=1,nsh_np=3,nsh_spi=1193046,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
-Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=1193046,nsh_si=255,nsh_c1=0x11223344
-Datapath actions: set(nsh(flags=128,spi=0x00123456,si=254,c1=0x44332211)),2
+Final flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x894f,nsh_flags=128,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=254,nsh_c1=0x44332211,nsh_c2=0x55667788,nsh_c3=0x99aabbcc,nsh_c4=0xddeeff00,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
+Megaflow: recirc_id=0,eth,in_port=1,dl_type=0x894f,nsh_flags=0,nsh_mdtype=1,nsh_np=3,nsh_spi=0x123456,nsh_si=255,nsh_c1=0x11223344
+Datapath actions: set(nsh(flags=128,spi=0x123456,si=254,c1=0x44332211)),2
])
OVS_VSWITCHD_STOP