[dpdk-dev] [PATCH v6 3/3] app/testpmd: add MPLSoGRE encapsulation

Ori Kam orika at mellanox.com
Mon Oct 22 19:38:11 CEST 2018


Example for MPLSoGRE tunnel:
ETH / IPV4 / GRE / MPLS / IP / L4..L7

In order to encapsulate such a tunnel there is a need to remove L2 of
the inner packet and encap the remaining tunnel, this is done by
applying 2 rte flow commands l2_decap followed by mplsogre_encap.
Both commands must appear in the same flow, and from the point of the
packet it both actions are applyed at the same time. (There is no part
where a packet doesn't have L2 header).

Decapsulating such a tunnel works the other way, first we need to decap
the outer tunnel header and then apply the new L2.
So the commands will be mplsogre_decap / l2_encap

Due to the complex encapsulation of MPLSoGRE flow action and
based on the fact testpmd does not allocate memory, this patch adds a
new command in testpmd to initialise a global structure containing the
necessary information to make the outer layer of the packet.  This same
global structures will then be used by the flow commands in testpmd when
the action mplsogre_encap, mplsogre_decap, will be parsed, at this point,
the conversion into such action becomes trivial.

Signed-off-by: Ori Kam <orika at mellanox.com>

---
v6:
 * fix compilation issue.
---
 app/test-pmd/cmdline.c                      | 227 ++++++++++++++++++++++++++
 app/test-pmd/cmdline_flow.c                 | 239 ++++++++++++++++++++++++++--
 app/test-pmd/testpmd.h                      |  22 +++
 doc/guides/testpmd_app_ug/testpmd_funcs.rst | 103 ++++++++++++
 4 files changed, 580 insertions(+), 11 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index e807dbb..6e14345 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -15436,6 +15436,229 @@ static void cmd_set_l2_decap_parsed(void *parsed_result,
 	},
 };
 
+/** Set MPLSoGRE encapsulation details */
+struct cmd_set_mplsogre_encap_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t mplsogre;
+	cmdline_fixed_string_t pos_token;
+	cmdline_fixed_string_t ip_version;
+	uint32_t vlan_present:1;
+	uint32_t label;
+	cmdline_ipaddr_t ip_src;
+	cmdline_ipaddr_t ip_dst;
+	uint16_t tci;
+	struct ether_addr eth_src;
+	struct ether_addr eth_dst;
+};
+
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result, set,
+				 "set");
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_mplsogre_encap =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result, mplsogre,
+				 "mplsogre_encap");
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_mplsogre_encap_with_vlan =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 mplsogre, "mplsogre_encap-with-vlan");
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_ip_version =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "ip-version");
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_ip_version_value =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 ip_version, "ipv4#ipv6");
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_label =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "label");
+cmdline_parse_token_num_t cmd_set_mplsogre_encap_label_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_mplsogre_encap_result, label,
+			      UINT32);
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_ip_src =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "ip-src");
+cmdline_parse_token_ipaddr_t cmd_set_mplsogre_encap_ip_src_value =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_set_mplsogre_encap_result, ip_src);
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_ip_dst =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "ip-dst");
+cmdline_parse_token_ipaddr_t cmd_set_mplsogre_encap_ip_dst_value =
+	TOKEN_IPADDR_INITIALIZER(struct cmd_set_mplsogre_encap_result, ip_dst);
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_vlan =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "vlan-tci");
+cmdline_parse_token_num_t cmd_set_mplsogre_encap_vlan_value =
+	TOKEN_NUM_INITIALIZER(struct cmd_set_mplsogre_encap_result, tci,
+			      UINT16);
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_eth_src =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "eth-src");
+cmdline_parse_token_etheraddr_t cmd_set_mplsogre_encap_eth_src_value =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				    eth_src);
+cmdline_parse_token_string_t cmd_set_mplsogre_encap_eth_dst =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				 pos_token, "eth-dst");
+cmdline_parse_token_etheraddr_t cmd_set_mplsogre_encap_eth_dst_value =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_mplsogre_encap_result,
+				    eth_dst);
+
+static void cmd_set_mplsogre_encap_parsed(void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_set_mplsogre_encap_result *res = parsed_result;
+	union {
+		uint32_t mplsogre_label;
+		uint8_t label[3];
+	} id = {
+		.mplsogre_label =
+			rte_cpu_to_be_32(res->label) & RTE_BE32(0x00ffffff),
+	};
+
+	if (strcmp(res->mplsogre, "mplsogre_encap") == 0)
+		mplsogre_encap_conf.select_vlan = 0;
+	else if (strcmp(res->mplsogre, "mplsogre_encap-with-vlan") == 0)
+		mplsogre_encap_conf.select_vlan = 1;
+	if (strcmp(res->ip_version, "ipv4") == 0)
+		mplsogre_encap_conf.select_ipv4 = 1;
+	else if (strcmp(res->ip_version, "ipv6") == 0)
+		mplsogre_encap_conf.select_ipv4 = 0;
+	else
+		return;
+	rte_memcpy(mplsogre_encap_conf.label, &id.label[1], 3);
+	if (mplsogre_encap_conf.select_ipv4) {
+		IPV4_ADDR_TO_UINT(res->ip_src, mplsogre_encap_conf.ipv4_src);
+		IPV4_ADDR_TO_UINT(res->ip_dst, mplsogre_encap_conf.ipv4_dst);
+	} else {
+		IPV6_ADDR_TO_ARRAY(res->ip_src, mplsogre_encap_conf.ipv6_src);
+		IPV6_ADDR_TO_ARRAY(res->ip_dst, mplsogre_encap_conf.ipv6_dst);
+	}
+	if (mplsogre_encap_conf.select_vlan)
+		mplsogre_encap_conf.vlan_tci = rte_cpu_to_be_16(res->tci);
+	rte_memcpy(mplsogre_encap_conf.eth_src, res->eth_src.addr_bytes,
+		   ETHER_ADDR_LEN);
+	rte_memcpy(mplsogre_encap_conf.eth_dst, res->eth_dst.addr_bytes,
+		   ETHER_ADDR_LEN);
+}
+
+cmdline_parse_inst_t cmd_set_mplsogre_encap = {
+	.f = cmd_set_mplsogre_encap_parsed,
+	.data = NULL,
+	.help_str = "set mplsogre_encap ip-version ipv4|ipv6 label <label>"
+		" ip-src <ip-src> ip-dst <ip-dst> eth-src <eth-src>"
+		" eth-dst <eth-dst>",
+	.tokens = {
+		(void *)&cmd_set_mplsogre_encap_set,
+		(void *)&cmd_set_mplsogre_encap_mplsogre_encap,
+		(void *)&cmd_set_mplsogre_encap_ip_version,
+		(void *)&cmd_set_mplsogre_encap_ip_version_value,
+		(void *)&cmd_set_mplsogre_encap_label,
+		(void *)&cmd_set_mplsogre_encap_label_value,
+		(void *)&cmd_set_mplsogre_encap_ip_src,
+		(void *)&cmd_set_mplsogre_encap_ip_src_value,
+		(void *)&cmd_set_mplsogre_encap_ip_dst,
+		(void *)&cmd_set_mplsogre_encap_ip_dst_value,
+		(void *)&cmd_set_mplsogre_encap_eth_src,
+		(void *)&cmd_set_mplsogre_encap_eth_src_value,
+		(void *)&cmd_set_mplsogre_encap_eth_dst,
+		(void *)&cmd_set_mplsogre_encap_eth_dst_value,
+		NULL,
+	},
+};
+
+cmdline_parse_inst_t cmd_set_mplsogre_encap_with_vlan = {
+	.f = cmd_set_mplsogre_encap_parsed,
+	.data = NULL,
+	.help_str = "set mplsogre_encap-with-vlan ip-version ipv4|ipv6"
+		" label <label> ip-src <ip-src> ip-dst <ip-dst>"
+		" vlan-tci <vlan-tci> eth-src <eth-src> eth-dst <eth-dst>",
+	.tokens = {
+		(void *)&cmd_set_mplsogre_encap_set,
+		(void *)&cmd_set_mplsogre_encap_mplsogre_encap_with_vlan,
+		(void *)&cmd_set_mplsogre_encap_ip_version,
+		(void *)&cmd_set_mplsogre_encap_ip_version_value,
+		(void *)&cmd_set_mplsogre_encap_label,
+		(void *)&cmd_set_mplsogre_encap_label_value,
+		(void *)&cmd_set_mplsogre_encap_ip_src,
+		(void *)&cmd_set_mplsogre_encap_ip_src_value,
+		(void *)&cmd_set_mplsogre_encap_ip_dst,
+		(void *)&cmd_set_mplsogre_encap_ip_dst_value,
+		(void *)&cmd_set_mplsogre_encap_vlan,
+		(void *)&cmd_set_mplsogre_encap_vlan_value,
+		(void *)&cmd_set_mplsogre_encap_eth_src,
+		(void *)&cmd_set_mplsogre_encap_eth_src_value,
+		(void *)&cmd_set_mplsogre_encap_eth_dst,
+		(void *)&cmd_set_mplsogre_encap_eth_dst_value,
+		NULL,
+	},
+};
+
+/** Set MPLSoGRE decapsulation details */
+struct cmd_set_mplsogre_decap_result {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t mplsogre;
+	cmdline_fixed_string_t pos_token;
+	cmdline_fixed_string_t ip_version;
+	uint32_t vlan_present:1;
+};
+
+cmdline_parse_token_string_t cmd_set_mplsogre_decap_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_decap_result, set,
+				 "set");
+cmdline_parse_token_string_t cmd_set_mplsogre_decap_mplsogre_decap =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_decap_result, mplsogre,
+				 "mplsogre_decap");
+cmdline_parse_token_string_t cmd_set_mplsogre_decap_mplsogre_decap_with_vlan =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_decap_result,
+				 mplsogre, "mplsogre_decap-with-vlan");
+cmdline_parse_token_string_t cmd_set_mplsogre_decap_ip_version =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_decap_result,
+				 pos_token, "ip-version");
+cmdline_parse_token_string_t cmd_set_mplsogre_decap_ip_version_value =
+	TOKEN_STRING_INITIALIZER(struct cmd_set_mplsogre_decap_result,
+				 ip_version, "ipv4#ipv6");
+
+static void cmd_set_mplsogre_decap_parsed(void *parsed_result,
+	__attribute__((unused)) struct cmdline *cl,
+	__attribute__((unused)) void *data)
+{
+	struct cmd_set_mplsogre_decap_result *res = parsed_result;
+
+	if (strcmp(res->mplsogre, "mplsogre_decap") == 0)
+		mplsogre_decap_conf.select_vlan = 0;
+	else if (strcmp(res->mplsogre, "mplsogre_decap-with-vlan") == 0)
+		mplsogre_decap_conf.select_vlan = 1;
+	if (strcmp(res->ip_version, "ipv4") == 0)
+		mplsogre_decap_conf.select_ipv4 = 1;
+	else if (strcmp(res->ip_version, "ipv6") == 0)
+		mplsogre_decap_conf.select_ipv4 = 0;
+}
+
+cmdline_parse_inst_t cmd_set_mplsogre_decap = {
+	.f = cmd_set_mplsogre_decap_parsed,
+	.data = NULL,
+	.help_str = "set mplsogre_decap ip-version ipv4|ipv6",
+	.tokens = {
+		(void *)&cmd_set_mplsogre_decap_set,
+		(void *)&cmd_set_mplsogre_decap_mplsogre_decap,
+		(void *)&cmd_set_mplsogre_decap_ip_version,
+		(void *)&cmd_set_mplsogre_decap_ip_version_value,
+		NULL,
+	},
+};
+
+cmdline_parse_inst_t cmd_set_mplsogre_decap_with_vlan = {
+	.f = cmd_set_mplsogre_decap_parsed,
+	.data = NULL,
+	.help_str = "set mplsogre_decap-with-vlan ip-version ipv4|ipv6",
+	.tokens = {
+		(void *)&cmd_set_mplsogre_decap_set,
+		(void *)&cmd_set_mplsogre_decap_mplsogre_decap_with_vlan,
+		(void *)&cmd_set_mplsogre_decap_ip_version,
+		(void *)&cmd_set_mplsogre_decap_ip_version_value,
+		NULL,
+	},
+};
+
 /** Set MPLSoUDP encapsulation details */
 struct cmd_set_mplsoudp_encap_result {
 	cmdline_fixed_string_t set;
@@ -18317,6 +18540,10 @@ struct cmd_config_per_queue_tx_offload_result {
 	(cmdline_parse_inst_t *)&cmd_set_l2_encap_with_vlan,
 	(cmdline_parse_inst_t *)&cmd_set_l2_decap,
 	(cmdline_parse_inst_t *)&cmd_set_l2_decap_with_vlan,
+	(cmdline_parse_inst_t *)&cmd_set_mplsogre_encap,
+	(cmdline_parse_inst_t *)&cmd_set_mplsogre_encap_with_vlan,
+	(cmdline_parse_inst_t *)&cmd_set_mplsogre_decap,
+	(cmdline_parse_inst_t *)&cmd_set_mplsogre_decap_with_vlan,
 	(cmdline_parse_inst_t *)&cmd_set_mplsoudp_encap,
 	(cmdline_parse_inst_t *)&cmd_set_mplsoudp_encap_with_vlan,
 	(cmdline_parse_inst_t *)&cmd_set_mplsoudp_decap,
diff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c
index b876918..3d7d040 100644
--- a/app/test-pmd/cmdline_flow.c
+++ b/app/test-pmd/cmdline_flow.c
@@ -245,6 +245,8 @@ enum index {
 	ACTION_NVGRE_DECAP,
 	ACTION_L2_ENCAP,
 	ACTION_L2_DECAP,
+	ACTION_MPLSOGRE_ENCAP,
+	ACTION_MPLSOGRE_DECAP,
 	ACTION_MPLSOUDP_ENCAP,
 	ACTION_MPLSOUDP_DECAP,
 	ACTION_SET_IPV4_SRC,
@@ -851,6 +853,8 @@ struct parse_action_priv {
 	ACTION_NVGRE_DECAP,
 	ACTION_L2_ENCAP,
 	ACTION_L2_DECAP,
+	ACTION_MPLSOGRE_ENCAP,
+	ACTION_MPLSOGRE_DECAP,
 	ACTION_MPLSOUDP_ENCAP,
 	ACTION_MPLSOUDP_DECAP,
 	ACTION_SET_IPV4_SRC,
@@ -1038,6 +1042,12 @@ static int parse_vc_action_l2_encap(struct context *, const struct token *,
 static int parse_vc_action_l2_decap(struct context *, const struct token *,
 				    const char *, unsigned int, void *,
 				    unsigned int);
+static int parse_vc_action_mplsogre_encap(struct context *,
+					  const struct token *, const char *,
+					  unsigned int, void *, unsigned int);
+static int parse_vc_action_mplsogre_decap(struct context *,
+					  const struct token *, const char *,
+					  unsigned int, void *, unsigned int);
 static int parse_vc_action_mplsoudp_encap(struct context *,
 					  const struct token *, const char *,
 					  unsigned int, void *, unsigned int);
@@ -2562,19 +2572,10 @@ static int comp_vc_action_rss_queue(struct context *, const struct token *,
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc,
 	},
-	[ACTION_MPLSOUDP_ENCAP] = {
-		.name = "mplsoudp_encap",
-		.help = "mplsoudp encapsulation, uses configuration set by"
-			" \"set mplsoudp_encap\"",
-		.priv = PRIV_ACTION(RAW_ENCAP,
-				    sizeof(struct action_raw_encap_data)),
-		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
-		.call = parse_vc_action_mplsoudp_encap,
-	},
 	[ACTION_L2_ENCAP] = {
 		.name = "l2_encap",
-		.help = "l2 encapsulation, uses configuration set by"
-			" \"set l2_encp\"",
+		.help = "l2 encap, uses configuration set by"
+			" \"set l2_encap\"",
 		.priv = PRIV_ACTION(RAW_ENCAP,
 				    sizeof(struct action_raw_encap_data)),
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
@@ -2589,6 +2590,33 @@ static int comp_vc_action_rss_queue(struct context *, const struct token *,
 		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
 		.call = parse_vc_action_l2_decap,
 	},
+	[ACTION_MPLSOGRE_ENCAP] = {
+		.name = "mplsogre_encap",
+		.help = "mplsogre encapsulation, uses configuration set by"
+			" \"set mplsogre_encap\"",
+		.priv = PRIV_ACTION(RAW_ENCAP,
+				    sizeof(struct action_raw_encap_data)),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc_action_mplsogre_encap,
+	},
+	[ACTION_MPLSOGRE_DECAP] = {
+		.name = "mplsogre_decap",
+		.help = "mplsogre decapsulation, uses configuration set by"
+			" \"set mplsogre_decap\"",
+		.priv = PRIV_ACTION(RAW_DECAP,
+				    sizeof(struct action_raw_decap_data)),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc_action_mplsogre_decap,
+	},
+	[ACTION_MPLSOUDP_ENCAP] = {
+		.name = "mplsoudp_encap",
+		.help = "mplsoudp encapsulation, uses configuration set by"
+			" \"set mplsoudp_encap\"",
+		.priv = PRIV_ACTION(RAW_ENCAP,
+				    sizeof(struct action_raw_encap_data)),
+		.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+		.call = parse_vc_action_mplsoudp_encap,
+	},
 	[ACTION_MPLSOUDP_DECAP] = {
 		.name = "mplsoudp_decap",
 		.help = "mplsoudp decapsulation, uses configuration set by"
@@ -3579,6 +3607,195 @@ static int comp_vc_action_rss_queue(struct context *, const struct token *,
 	return ret;
 }
 
+#define ETHER_TYPE_MPLS_UNICAST 0x8847
+
+/** Parse MPLSOGRE encap action. */
+static int
+parse_vc_action_mplsogre_encap(struct context *ctx, const struct token *token,
+			       const char *str, unsigned int len,
+			       void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_action *action;
+	struct action_raw_encap_data *action_encap_data;
+	struct rte_flow_item_eth eth = { .type = 0, };
+	struct rte_flow_item_vlan vlan = {
+		.tci = mplsogre_encap_conf.vlan_tci,
+		.inner_type = 0,
+	};
+	struct rte_flow_item_ipv4 ipv4 = {
+		.hdr =  {
+			.src_addr = mplsogre_encap_conf.ipv4_src,
+			.dst_addr = mplsogre_encap_conf.ipv4_dst,
+			.next_proto_id = IPPROTO_GRE,
+		},
+	};
+	struct rte_flow_item_ipv6 ipv6 = {
+		.hdr =  {
+			.proto = IPPROTO_GRE,
+		},
+	};
+	struct rte_flow_item_gre gre = {
+		.protocol = rte_cpu_to_be_16(ETHER_TYPE_MPLS_UNICAST),
+	};
+	struct rte_flow_item_mpls mpls;
+	uint8_t *header;
+	int ret;
+
+	ret = parse_vc(ctx, token, str, len, buf, size);
+	if (ret < 0)
+		return ret;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return ret;
+	if (!out->args.vc.actions_n)
+		return -1;
+	action = &out->args.vc.actions[out->args.vc.actions_n - 1];
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data;
+	ctx->objmask = NULL;
+	/* Copy the headers to the buffer. */
+	action_encap_data = ctx->object;
+	*action_encap_data = (struct action_raw_encap_data) {
+		.conf = (struct rte_flow_action_raw_encap){
+			.data = action_encap_data->data,
+		},
+		.data = {},
+		.preserve = {},
+	};
+	header = action_encap_data->data;
+	if (mplsogre_encap_conf.select_vlan)
+		eth.type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+	else if (mplsogre_encap_conf.select_ipv4)
+		eth.type = rte_cpu_to_be_16(ETHER_TYPE_IPv4);
+	else
+		eth.type = rte_cpu_to_be_16(ETHER_TYPE_IPv6);
+	memcpy(eth.dst.addr_bytes,
+	       mplsogre_encap_conf.eth_dst, ETHER_ADDR_LEN);
+	memcpy(eth.src.addr_bytes,
+	       mplsogre_encap_conf.eth_src, ETHER_ADDR_LEN);
+	memcpy(header, &eth, sizeof(eth));
+	header += sizeof(eth);
+	if (mplsogre_encap_conf.select_vlan) {
+		if (mplsogre_encap_conf.select_ipv4)
+			vlan.inner_type = rte_cpu_to_be_16(ETHER_TYPE_IPv4);
+		else
+			vlan.inner_type = rte_cpu_to_be_16(ETHER_TYPE_IPv6);
+		memcpy(header, &vlan, sizeof(vlan));
+		header += sizeof(vlan);
+	}
+	if (mplsogre_encap_conf.select_ipv4) {
+		memcpy(header, &ipv4, sizeof(ipv4));
+		header += sizeof(ipv4);
+	} else {
+		memcpy(&ipv6.hdr.src_addr,
+		       &mplsogre_encap_conf.ipv6_src,
+		       sizeof(mplsogre_encap_conf.ipv6_src));
+		memcpy(&ipv6.hdr.dst_addr,
+		       &mplsogre_encap_conf.ipv6_dst,
+		       sizeof(mplsogre_encap_conf.ipv6_dst));
+		memcpy(header, &ipv6, sizeof(ipv6));
+		header += sizeof(ipv6);
+	}
+	memcpy(header, &gre, sizeof(gre));
+	header += sizeof(gre);
+	memcpy(mpls.label_tc_s, mplsogre_encap_conf.label,
+	       RTE_DIM(mplsogre_encap_conf.label));
+	memcpy(header, &mpls, sizeof(mpls));
+	header += sizeof(mpls);
+	action_encap_data->conf.size = header -
+		action_encap_data->data;
+	action->conf = &action_encap_data->conf;
+	return ret;
+}
+
+/** Parse MPLSOGRE decap action. */
+static int
+parse_vc_action_mplsogre_decap(struct context *ctx, const struct token *token,
+			       const char *str, unsigned int len,
+			       void *buf, unsigned int size)
+{
+	struct buffer *out = buf;
+	struct rte_flow_action *action;
+	struct action_raw_decap_data *action_decap_data;
+	struct rte_flow_item_eth eth = { .type = 0, };
+	struct rte_flow_item_vlan vlan = {.tci = 0};
+	struct rte_flow_item_ipv4 ipv4 = {
+		.hdr =  {
+			.next_proto_id = IPPROTO_GRE,
+		},
+	};
+	struct rte_flow_item_ipv6 ipv6 = {
+		.hdr =  {
+			.proto = IPPROTO_GRE,
+		},
+	};
+	struct rte_flow_item_gre gre = {
+		.protocol = rte_cpu_to_be_16(ETHER_TYPE_MPLS_UNICAST),
+	};
+	struct rte_flow_item_mpls mpls;
+	uint8_t *header;
+	int ret;
+
+	ret = parse_vc(ctx, token, str, len, buf, size);
+	if (ret < 0)
+		return ret;
+	/* Nothing else to do if there is no buffer. */
+	if (!out)
+		return ret;
+	if (!out->args.vc.actions_n)
+		return -1;
+	action = &out->args.vc.actions[out->args.vc.actions_n - 1];
+	/* Point to selected object. */
+	ctx->object = out->args.vc.data;
+	ctx->objmask = NULL;
+	/* Copy the headers to the buffer. */
+	action_decap_data = ctx->object;
+	*action_decap_data = (struct action_raw_decap_data) {
+		.conf = (struct rte_flow_action_raw_decap){
+			.data = action_decap_data->data,
+		},
+		.data = {},
+	};
+	header = action_decap_data->data;
+	if (mplsogre_decap_conf.select_vlan)
+		eth.type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+	else if (mplsogre_encap_conf.select_ipv4)
+		eth.type = rte_cpu_to_be_16(ETHER_TYPE_IPv4);
+	else
+		eth.type = rte_cpu_to_be_16(ETHER_TYPE_IPv6);
+	memcpy(eth.dst.addr_bytes,
+	       mplsogre_encap_conf.eth_dst, ETHER_ADDR_LEN);
+	memcpy(eth.src.addr_bytes,
+	       mplsogre_encap_conf.eth_src, ETHER_ADDR_LEN);
+	memcpy(header, &eth, sizeof(eth));
+	header += sizeof(eth);
+	if (mplsogre_encap_conf.select_vlan) {
+		if (mplsogre_encap_conf.select_ipv4)
+			vlan.inner_type = rte_cpu_to_be_16(ETHER_TYPE_IPv4);
+		else
+			vlan.inner_type = rte_cpu_to_be_16(ETHER_TYPE_IPv6);
+		memcpy(header, &vlan, sizeof(vlan));
+		header += sizeof(vlan);
+	}
+	if (mplsogre_encap_conf.select_ipv4) {
+		memcpy(header, &ipv4, sizeof(ipv4));
+		header += sizeof(ipv4);
+	} else {
+		memcpy(header, &ipv6, sizeof(ipv6));
+		header += sizeof(ipv6);
+	}
+	memcpy(header, &gre, sizeof(gre));
+	header += sizeof(gre);
+	memset(&mpls, 0, sizeof(mpls));
+	memcpy(header, &mpls, sizeof(mpls));
+	header += sizeof(mpls);
+	action_decap_data->conf.size = header -
+		action_decap_data->data;
+	action->conf = &action_decap_data->conf;
+	return ret;
+}
+
 /** Parse MPLSOUDP encap action. */
 static int
 parse_vc_action_mplsoudp_encap(struct context *ctx, const struct token *token,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 12daee5..0738105 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -523,6 +523,28 @@ struct l2_decap_conf {
 };
 struct l2_decap_conf l2_decap_conf;
 
+/* MPLSoGRE encap parameters. */
+struct mplsogre_encap_conf {
+	uint32_t select_ipv4:1;
+	uint32_t select_vlan:1;
+	uint8_t label[3];
+	rte_be32_t ipv4_src;
+	rte_be32_t ipv4_dst;
+	uint8_t ipv6_src[16];
+	uint8_t ipv6_dst[16];
+	rte_be16_t vlan_tci;
+	uint8_t eth_src[ETHER_ADDR_LEN];
+	uint8_t eth_dst[ETHER_ADDR_LEN];
+};
+struct mplsogre_encap_conf mplsogre_encap_conf;
+
+/* MPLSoGRE decap parameters. */
+struct mplsogre_decap_conf {
+	uint32_t select_ipv4:1;
+	uint32_t select_vlan:1;
+};
+struct mplsogre_decap_conf mplsogre_decap_conf;
+
 /* MPLSoUDP encap parameters. */
 struct mplsoudp_encap_conf {
 	uint32_t select_ipv4:1;
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index fb26315..8d60bf0 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -1607,6 +1607,35 @@ flow rule using the action l2_decap will use the last configuration set.
 To have a different encapsulation header, one of those commands must be called
 before the flow rule creation.
 
+Config MPLSoGRE Encap outer layers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configure the outer layer to encapsulate a packet inside a MPLSoGRE tunnel::
+
+ set mplsogre_encap ip-version (ipv4|ipv6) label (label) \
+        ip-src (ip-src) ip-dst (ip-dst) eth-src (eth-src) eth-dst (eth-dst)
+ set mplsogre_encap-with-vlan ip-version (ipv4|ipv6) label (label) \
+        ip-src (ip-src) ip-dst (ip-dst) vlan-tci (vlan-tci) \
+        eth-src (eth-src) eth-dst (eth-dst)
+
+Those command will set an internal configuration inside testpmd, any following
+flow rule using the action mplsogre_encap will use the last configuration set.
+To have a different encapsulation header, one of those commands must be called
+before the flow rule creation.
+
+Config MPLSoGRE Decap outer layers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configure the outer layer to decapsulate MPLSoGRE packet::
+
+ set mplsogre_decap ip-version (ipv4|ipv6)
+ set mplsogre_decap-with-vlan ip-version (ipv4|ipv6)
+
+Those command will set an internal configuration inside testpmd, any following
+flow rule using the action mplsogre_decap will use the last configuration set.
+To have a different decapsulation header, one of those commands must be called
+before the flow rule creation.
+
 Config MPLSoUDP Encap outer layers
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -3834,6 +3863,12 @@ This section lists supported actions and their attributes, if any.
 - ``l2_decap``: Performs a L2 decapsulation, L2 configuration
   is done through `Config L2 Decap`_.
 
+- ``mplsogre_encap``: Performs a MPLSoGRE encapsulation, outer layer
+  configuration is done through `Config MPLSoGRE Encap outer layers`_.
+
+- ``mplsogre_decap``: Performs a MPLSoGRE decapsulation, outer layer
+  configuration is done through `Config MPLSoGRE Decap outer layers`_.
+
 - ``mplsoudp_encap``: Performs a MPLSoUDP encapsulation, outer layer
   configuration is done through `Config MPLSoUDP Encap outer layers`_.
 
@@ -4237,6 +4272,74 @@ L2 with VXLAN header::
  testpmd> flow create 0 egress pattern eth / end actions l2_encap / mplsoudp_encap /
          queue index 0 / end
 
+Sample MPLSoGRE encapsulation rule
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+MPLSoGRE encapsulation outer layer has default value pre-configured in testpmd
+source code, those can be changed by using the following commands
+
+IPv4 MPLSoGRE outer header::
+
+ testpmd> set mplsogre_encap ip-version ipv4 label 4
+        ip-src 127.0.0.1 ip-dst 128.0.0.1 eth-src 11:11:11:11:11:11
+        eth-dst 22:22:22:22:22:22
+ testpmd> flow create 0 egress pattern eth / end actions l2_decap /
+        mplsogre_encap / end
+
+IPv4 MPLSoGRE with VLAN outer header::
+
+ testpmd> set mplsogre_encap-with-vlan ip-version ipv4 label 4
+        ip-src 127.0.0.1 ip-dst 128.0.0.1 vlan-tci 34
+        eth-src 11:11:11:11:11:11 eth-dst 22:22:22:22:22:22
+ testpmd> flow create 0 egress pattern eth / end actions l2_decap /
+        mplsogre_encap / end
+
+IPv6 MPLSoGRE outer header::
+
+ testpmd> set mplsogre_encap ip-version ipv6 mask 4
+        ip-src ::1 ip-dst ::2222 eth-src 11:11:11:11:11:11
+        eth-dst 22:22:22:22:22:22
+ testpmd> flow create 0 egress pattern eth / end actions l2_decap /
+        mplsogre_encap / end
+
+IPv6 MPLSoGRE with VLAN outer header::
+
+ testpmd> set mplsogre_encap-with-vlan ip-version ipv6 mask 4
+        ip-src ::1 ip-dst ::2222 vlan-tci 34
+        eth-src 11:11:11:11:11:11 eth-dst 22:22:22:22:22:22
+ testpmd> flow create 0 egress pattern eth / end actions l2_decap /
+        mplsogre_encap / end
+
+Sample MPLSoGRE decapsulation rule
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+MPLSoGRE decapsulation outer layer has default value pre-configured in testpmd
+source code, those can be changed by using the following commands
+
+IPv4 MPLSoGRE outer header::
+
+ testpmd> set mplsogre_decap ip-version ipv4
+ testpmd> flow create 0 ingress pattern eth / ipv4 / gre / mpls / end actions
+        mplsogre_decap / l2_encap / end
+
+IPv4 MPLSoGRE with VLAN outer header::
+
+ testpmd> set mplsogre_decap-with-vlan ip-version ipv4
+ testpmd> flow create 0 ingress pattern eth / vlan / ipv4 / gre / mpls / end
+        actions mplsogre_decap / l2_encap / end
+
+IPv6 MPLSoGRE outer header::
+
+ testpmd> set mplsogre_decap ip-version ipv6
+ testpmd> flow create 0 ingress pattern eth / ipv6 / gre / mpls / end
+        actions mplsogre_decap / l2_encap / end
+
+IPv6 MPLSoGRE with VLAN outer header::
+
+ testpmd> set mplsogre_decap-with-vlan ip-version ipv6
+ testpmd> flow create 0 ingress pattern eth / vlan / ipv6 / gre / mpls / end
+        actions mplsogre_decap / l2_encap / end
+
 Sample MPLSoUDP encapsulation rule
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-- 
1.8.3.1



More information about the dev mailing list