@@ -652,6 +652,17 @@ struct ovs_action_push_vlan {
__be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */
};
+/**
+ * struct ovs_action_push_nsh - %OVS_ACTION_ATTR_PUSH_NSH action argument.
+ * @len: length of NSH header. Differs since different metadata type.
+ * @header: RAW value for the NSH header.
+ */
+#define NSH_HEADER_LEN_MAX 256
+struct ovs_action_push_nsh {
+ uint16_t len;
+ uint8_t header[NSH_HEADER_LEN_MAX]; /* NSH header */
+};
+
/* Data path hash algorithm for computing Datapath hash.
*
* The algorithm type only specifies the fields in a flow
@@ -807,6 +818,9 @@ enum ovs_nat_attr {
* ovs_action_push_tnl.
* @OVS_ACTION_ATTR_TUNNEL_POP: Lookup tunnel port by port-no passed and pop
* tunnel header.
+ * @OVS_ACTION_ATTR_PUSH_NSH: Append a Network Service Header before
+ * original packet.
+ * @OVS_ACTION_ATTR_POP_NSH: Strip the Network Service Header from packet.
*/
enum ovs_action_attr {
@@ -827,6 +841,8 @@ enum ovs_action_attr {
* bits. */
OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */
OVS_ACTION_ATTR_TRUNC, /* u32 struct ovs_action_trunc. */
+ OVS_ACTION_ATTR_PUSH_NSH, /* struct ovs_action_push_nsh. */
+ OVS_ACTION_ATTR_POP_NSH, /* No argument. */
#ifndef __KERNEL__
OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
@@ -4197,6 +4197,8 @@ dp_execute_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_UNSPEC:
case OVS_ACTION_ATTR_TRUNC:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
}
@@ -1169,6 +1169,8 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet_batch *packets_,
case OVS_ACTION_ATTR_SET_MASKED:
case OVS_ACTION_ATTR_SAMPLE:
case OVS_ACTION_ATTR_TRUNC:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
@@ -506,6 +506,8 @@ requires_datapath_assistance(const struct nlattr *a)
case OVS_ACTION_ATTR_PUSH_MPLS:
case OVS_ACTION_ATTR_POP_MPLS:
case OVS_ACTION_ATTR_TRUNC:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
return false;
case OVS_ACTION_ATTR_UNSPEC:
@@ -639,6 +641,10 @@ odp_execute_actions(void *dp, struct dp_packet_batch *batch, bool steal,
break;
}
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+ break;
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -121,6 +121,8 @@ odp_action_len(uint16_t type)
case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE;
+ case OVS_ACTION_ATTR_PUSH_NSH: return sizeof(struct ovs_action_push_nsh);
+ case OVS_ACTION_ATTR_POP_NSH: return 0;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
@@ -551,6 +553,69 @@ format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port);
}
+static void
+format_push_nsh_header(struct ds *ds, const struct ovs_action_push_nsh * nsh)
+{
+ const struct nsh_header *nsh_hdr = (struct nsh_header *) (nsh->header);
+
+ ds_put_format(ds, "flags=0x%"PRIx8",md_type=%"PRIu8
+ ",next_proto=%"PRIu8",nsp=0x%"PRIx32",nsi=0x%"PRIx8",",
+ nsh_hdr->base.flags,
+ nsh_hdr->base.md_type,
+ nsh_hdr->base.next_proto,
+ ntohl(nsh_hdr->base.sfp << 8),
+ (nsh_hdr->base.sfp >> 24));
+
+ if (nsh_hdr->base.md_type == NSH_MD_TYPE1) {
+ const struct nsh_md1_ctx *ctx = (struct nsh_md1_ctx *)(nsh_hdr + 1);
+ ds_put_format(ds, "nshc1=0x%"PRIx32",nshc2=0x%"PRIx32
+ ",nshc3=0x%"PRIx32",nshc4=0x%"PRIx32,
+ ntohl(ctx->nshc1),
+ ntohl(ctx->nshc2),
+ ntohl(ctx->nshc3),
+ ntohl(ctx->nshc4));
+#if 0
+ } else if (nsh_hdr->base.md_type == NSH_MD_TYPE2) {
+ /* MD type 2 prototype with TUN_METADATA APIs. */
+ const struct nsh_md2_ctx *ctx = (struct nsh_md2_ctx *)(nsh_hdr + 1);
+ int md_len = (nsh_hdr->base.length << 2) - sizeof(struct nsh_header);
+
+ while (md_len > 0) {
+ unsigned int len;
+ uint8_t data_len;
+
+ if (md_len < sizeof *ctx)
+ return;
+
+ data_len = ctx->length << 2;
+ len = data_len + sizeof *ctx;
+ if (len > md_len)
+ return;
+
+ ds_put_char(ds, '{');
+ ds_put_format(ds, "class=0x%"PRIx16",type=0x%"PRIx8",len=%"PRIu8",",
+ ctx->md_class, ctx->type, data_len);
+ ds_put_hex(ds, ctx + 1, data_len);
+ ds_put_char(ds, '}');
+
+ ctx += len / sizeof(*ctx);
+ md_len -= len;
+ }
+#endif
+ }
+}
+
+static void
+format_push_nsh_action(struct ds *ds, const struct nlattr *attr)
+{
+ struct ovs_action_push_nsh *data;
+
+ data = (struct ovs_action_push_nsh *)nl_attr_get(attr);
+ ds_put_cstr(ds, "push_nsh(");
+ format_push_nsh_header(ds, data);
+ ds_put_char(ds, ')');
+}
+
static const struct nl_policy ovs_nat_policy[] = {
[OVS_NAT_ATTR_SRC] = { .type = NL_A_FLAG, .optional = true, },
[OVS_NAT_ATTR_DST] = { .type = NL_A_FLAG, .optional = true, },
@@ -860,6 +925,14 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
case OVS_ACTION_ATTR_CT:
format_odp_conntrack_action(ds, a);
break;
+
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ format_push_nsh_action(ds, a);
+ break;
+ case OVS_ACTION_ATTR_POP_NSH:
+ ds_put_cstr(ds, "pop_nsh");
+ break;
+
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
default:
@@ -1647,6 +1720,72 @@ parse_odp_action(const char *s, const struct simap *port_names,
}
{
+ struct ovs_action_push_nsh push;
+ struct nsh_header *nsh_hdr = (struct nsh_header *)push.header;
+ ovs_be32 nsp = 0;
+ int n = -1, m = -1;
+ uint16_t length = 0;
+ uint8_t flags = 0, nsi = 0, md_type = 0, next_proto = 0;
+
+ if (ovs_scan_len(s, &n, "push_nsh(flags=0x%"SCNx8
+ ",md_type=%"SCNi8",next_proto=%"SCNi8
+ ",nsp=0x%"SCNx32",nsi=0x%"SCNx8,
+ &flags, &md_type, &next_proto, &nsp, &nsi)) {
+
+ if (md_type == NSH_MD_TYPE1) {
+ struct nsh_md1_ctx *ctx = NULL;
+ ovs_be32 nshc1 = 0, nshc2 = 0, nshc3 = 0, nshc4 = 0;
+
+ if (!ovs_scan_len(s, &m, ",nshc1=0x%"SCNx32",nshc2=0x%"SCNx32
+ ",nshc3=0x%"SCNx32",nshc4=0x%"SCNx32")",
+ &nshc1, &nshc2, &nshc3, &nshc4)) {
+ nshc1 = 0;
+ nshc2 = 0;
+ nshc3 = 0;
+ nshc4 = 0;
+ }
+
+ nsh_hdr->base.flags = flags;
+ nsh_hdr->base.md_type = NSH_MD_TYPE1;
+ nsh_hdr->base.next_proto = next_proto;
+ nsh_hdr->base.sfp = (nsp >> 8) | (nsi << 24);
+
+ ctx = (struct nsh_md1_ctx *)(nsh_hdr + 1);
+ ctx->nshc1 = nshc1;
+ ctx->nshc2 = nshc2;
+ ctx->nshc3 = nshc3;
+ ctx->nshc4 = nshc4;
+ length = NSH_TYPE1_LEN;
+#if 0
+ } else if (md_type == NSH_MD_TYPE2) {
+ /* MD type 2 prototype with TUN_METADATA APIs. */
+ struct geneve_scan options;
+ struct nsh_md2_ctx *ctx = NULL;
+ nsh_hdr->base.flags = flags;
+ nsh_hdr->base.md_type = NSH_MD_TYPE2;
+ nsh_hdr->base.next_proto = next_proto;
+ nsh_hdr->base.sfp = (nsp >> 8) | (nsi << 24);
+
+ m = scan_geneve(s + n, &options, NULL);
+ ctx = (struct nsh_md2_ctx *)(nsh_hdr + 1);
+ memcpy(ctx, options.d, options.len);
+ length = options.len + sizeof *nsh_hdr;
+#endif
+ }
+
+ nsh_hdr->base.length = length >> 2;
+ push.len = length;
+ nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_NSH,
+ &push, sizeof push);
+ return n + m;
+ }
+ }
+
+ if (!strncmp(s, "pop_nsh", 7)) {
+ nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_NSH);
+ return 7;
+ }
+ {
double percentage;
int n = -1;
@@ -1177,6 +1177,8 @@ dpif_sflow_read_actions(const struct flow *flow,
}
case OVS_ACTION_ATTR_SAMPLE:
case OVS_ACTION_ATTR_UNSPEC:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
case __OVS_ACTION_ATTR_MAX:
default:
break;
Signed-off-by: Johnson Li <johnson.li@intel.com>