@@ -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*/
@@ -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)
@@ -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,
@@ -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();
}
@@ -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();
@@ -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:
@@ -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));
}
}
@@ -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";
}
@@ -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();
}
@@ -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'.
@@ -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
@@ -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"
@@ -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",