app/testpmd: add MPLSoUDP encapsulation
authorOri Kam <orika@mellanox.com>
Mon, 22 Oct 2018 17:38:10 +0000 (17:38 +0000)
committerFerruh Yigit <ferruh.yigit@intel.com>
Fri, 26 Oct 2018 20:14:05 +0000 (22:14 +0200)
MPLSoUDP is an example for L3 tunnel encapsulation.

L3 tunnel type is a tunnel that is missing the layer 2 header of the
inner packet.

Example for MPLSoUDP tunnel:
ETH / IPV4 / UDP / 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 mplsoudp_encap.
Both commands must appear in the same flow, and from the point of the
packet it both actions are applied 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 mplsoudp_decap / l2_encap

Due to the complex encapsulation of MPLSoUDP and L2  flow actions and
based on the fact testpmd does not allocate memory, this patch adds a
new command in testpmd to initialise a global structures 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 mplsoudp_encap, mplsoudp_decap, l2_encap, l2_decap, will be
parsed, at this point, the conversion into such action becomes trivial.

The l2_encap and l2_decap actions can also be used for other L3 tunnel
types.

Signed-off-by: Ori Kam <orika@mellanox.com>
Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
app/test-pmd/cmdline.c
app/test-pmd/cmdline_flow.c
app/test-pmd/testpmd.h
doc/guides/testpmd_app_ug/testpmd_funcs.rst

index fc74b95..acf3ccc 100644 (file)
@@ -15282,6 +15282,408 @@ cmdline_parse_inst_t cmd_set_nvgre_with_vlan = {
        },
 };
 
+/** Set L2 encapsulation details */
+struct cmd_set_l2_encap_result {
+       cmdline_fixed_string_t set;
+       cmdline_fixed_string_t l2_encap;
+       cmdline_fixed_string_t pos_token;
+       cmdline_fixed_string_t ip_version;
+       uint32_t vlan_present:1;
+       uint16_t tci;
+       struct ether_addr eth_src;
+       struct ether_addr eth_dst;
+};
+
+cmdline_parse_token_string_t cmd_set_l2_encap_set =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, set, "set");
+cmdline_parse_token_string_t cmd_set_l2_encap_l2_encap =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, l2_encap, "l2_encap");
+cmdline_parse_token_string_t cmd_set_l2_encap_l2_encap_with_vlan =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, l2_encap,
+                                "l2_encap-with-vlan");
+cmdline_parse_token_string_t cmd_set_l2_encap_ip_version =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, pos_token,
+                                "ip-version");
+cmdline_parse_token_string_t cmd_set_l2_encap_ip_version_value =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, ip_version,
+                                "ipv4#ipv6");
+cmdline_parse_token_string_t cmd_set_l2_encap_vlan =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, pos_token,
+                                "vlan-tci");
+cmdline_parse_token_num_t cmd_set_l2_encap_vlan_value =
+       TOKEN_NUM_INITIALIZER(struct cmd_set_l2_encap_result, tci, UINT16);
+cmdline_parse_token_string_t cmd_set_l2_encap_eth_src =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, pos_token,
+                                "eth-src");
+cmdline_parse_token_etheraddr_t cmd_set_l2_encap_eth_src_value =
+       TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_l2_encap_result, eth_src);
+cmdline_parse_token_string_t cmd_set_l2_encap_eth_dst =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_encap_result, pos_token,
+                                "eth-dst");
+cmdline_parse_token_etheraddr_t cmd_set_l2_encap_eth_dst_value =
+       TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_l2_encap_result, eth_dst);
+
+static void cmd_set_l2_encap_parsed(void *parsed_result,
+       __attribute__((unused)) struct cmdline *cl,
+       __attribute__((unused)) void *data)
+{
+       struct cmd_set_l2_encap_result *res = parsed_result;
+
+       if (strcmp(res->l2_encap, "l2_encap") == 0)
+               l2_encap_conf.select_vlan = 0;
+       else if (strcmp(res->l2_encap, "l2_encap-with-vlan") == 0)
+               l2_encap_conf.select_vlan = 1;
+       if (strcmp(res->ip_version, "ipv4") == 0)
+               l2_encap_conf.select_ipv4 = 1;
+       else if (strcmp(res->ip_version, "ipv6") == 0)
+               l2_encap_conf.select_ipv4 = 0;
+       else
+               return;
+       if (l2_encap_conf.select_vlan)
+               l2_encap_conf.vlan_tci = rte_cpu_to_be_16(res->tci);
+       rte_memcpy(l2_encap_conf.eth_src, res->eth_src.addr_bytes,
+                  ETHER_ADDR_LEN);
+       rte_memcpy(l2_encap_conf.eth_dst, res->eth_dst.addr_bytes,
+                  ETHER_ADDR_LEN);
+}
+
+cmdline_parse_inst_t cmd_set_l2_encap = {
+       .f = cmd_set_l2_encap_parsed,
+       .data = NULL,
+       .help_str = "set l2_encap ip-version ipv4|ipv6"
+               " eth-src <eth-src> eth-dst <eth-dst>",
+       .tokens = {
+               (void *)&cmd_set_l2_encap_set,
+               (void *)&cmd_set_l2_encap_l2_encap,
+               (void *)&cmd_set_l2_encap_ip_version,
+               (void *)&cmd_set_l2_encap_ip_version_value,
+               (void *)&cmd_set_l2_encap_eth_src,
+               (void *)&cmd_set_l2_encap_eth_src_value,
+               (void *)&cmd_set_l2_encap_eth_dst,
+               (void *)&cmd_set_l2_encap_eth_dst_value,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_set_l2_encap_with_vlan = {
+       .f = cmd_set_l2_encap_parsed,
+       .data = NULL,
+       .help_str = "set l2_encap-with-vlan ip-version ipv4|ipv6"
+               " vlan-tci <vlan-tci> eth-src <eth-src> eth-dst <eth-dst>",
+       .tokens = {
+               (void *)&cmd_set_l2_encap_set,
+               (void *)&cmd_set_l2_encap_l2_encap_with_vlan,
+               (void *)&cmd_set_l2_encap_ip_version,
+               (void *)&cmd_set_l2_encap_ip_version_value,
+               (void *)&cmd_set_l2_encap_vlan,
+               (void *)&cmd_set_l2_encap_vlan_value,
+               (void *)&cmd_set_l2_encap_eth_src,
+               (void *)&cmd_set_l2_encap_eth_src_value,
+               (void *)&cmd_set_l2_encap_eth_dst,
+               (void *)&cmd_set_l2_encap_eth_dst_value,
+               NULL,
+       },
+};
+
+/** Set L2 decapsulation details */
+struct cmd_set_l2_decap_result {
+       cmdline_fixed_string_t set;
+       cmdline_fixed_string_t l2_decap;
+       cmdline_fixed_string_t pos_token;
+       uint32_t vlan_present:1;
+};
+
+cmdline_parse_token_string_t cmd_set_l2_decap_set =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_decap_result, set, "set");
+cmdline_parse_token_string_t cmd_set_l2_decap_l2_decap =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_decap_result, l2_decap,
+                                "l2_decap");
+cmdline_parse_token_string_t cmd_set_l2_decap_l2_decap_with_vlan =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_l2_decap_result, l2_decap,
+                                "l2_decap-with-vlan");
+
+static void cmd_set_l2_decap_parsed(void *parsed_result,
+       __attribute__((unused)) struct cmdline *cl,
+       __attribute__((unused)) void *data)
+{
+       struct cmd_set_l2_decap_result *res = parsed_result;
+
+       if (strcmp(res->l2_decap, "l2_decap") == 0)
+               l2_decap_conf.select_vlan = 0;
+       else if (strcmp(res->l2_decap, "l2_decap-with-vlan") == 0)
+               l2_decap_conf.select_vlan = 1;
+}
+
+cmdline_parse_inst_t cmd_set_l2_decap = {
+       .f = cmd_set_l2_decap_parsed,
+       .data = NULL,
+       .help_str = "set l2_decap",
+       .tokens = {
+               (void *)&cmd_set_l2_decap_set,
+               (void *)&cmd_set_l2_decap_l2_decap,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_set_l2_decap_with_vlan = {
+       .f = cmd_set_l2_decap_parsed,
+       .data = NULL,
+       .help_str = "set l2_decap-with-vlan",
+       .tokens = {
+               (void *)&cmd_set_l2_decap_set,
+               (void *)&cmd_set_l2_decap_l2_decap_with_vlan,
+               NULL,
+       },
+};
+
+/** Set MPLSoUDP encapsulation details */
+struct cmd_set_mplsoudp_encap_result {
+       cmdline_fixed_string_t set;
+       cmdline_fixed_string_t mplsoudp;
+       cmdline_fixed_string_t pos_token;
+       cmdline_fixed_string_t ip_version;
+       uint32_t vlan_present:1;
+       uint32_t label;
+       uint16_t udp_src;
+       uint16_t udp_dst;
+       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_mplsoudp_encap_set =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result, set,
+                                "set");
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_mplsoudp_encap =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result, mplsoudp,
+                                "mplsoudp_encap");
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_mplsoudp_encap_with_vlan =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                mplsoudp, "mplsoudp_encap-with-vlan");
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_ip_version =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "ip-version");
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_ip_version_value =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                ip_version, "ipv4#ipv6");
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_label =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "label");
+cmdline_parse_token_num_t cmd_set_mplsoudp_encap_label_value =
+       TOKEN_NUM_INITIALIZER(struct cmd_set_mplsoudp_encap_result, label,
+                             UINT32);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_udp_src =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "udp-src");
+cmdline_parse_token_num_t cmd_set_mplsoudp_encap_udp_src_value =
+       TOKEN_NUM_INITIALIZER(struct cmd_set_mplsoudp_encap_result, udp_src,
+                             UINT16);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_udp_dst =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "udp-dst");
+cmdline_parse_token_num_t cmd_set_mplsoudp_encap_udp_dst_value =
+       TOKEN_NUM_INITIALIZER(struct cmd_set_mplsoudp_encap_result, udp_dst,
+                             UINT16);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_ip_src =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "ip-src");
+cmdline_parse_token_ipaddr_t cmd_set_mplsoudp_encap_ip_src_value =
+       TOKEN_IPADDR_INITIALIZER(struct cmd_set_mplsoudp_encap_result, ip_src);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_ip_dst =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "ip-dst");
+cmdline_parse_token_ipaddr_t cmd_set_mplsoudp_encap_ip_dst_value =
+       TOKEN_IPADDR_INITIALIZER(struct cmd_set_mplsoudp_encap_result, ip_dst);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_vlan =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "vlan-tci");
+cmdline_parse_token_num_t cmd_set_mplsoudp_encap_vlan_value =
+       TOKEN_NUM_INITIALIZER(struct cmd_set_mplsoudp_encap_result, tci,
+                             UINT16);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_eth_src =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "eth-src");
+cmdline_parse_token_etheraddr_t cmd_set_mplsoudp_encap_eth_src_value =
+       TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                   eth_src);
+cmdline_parse_token_string_t cmd_set_mplsoudp_encap_eth_dst =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                pos_token, "eth-dst");
+cmdline_parse_token_etheraddr_t cmd_set_mplsoudp_encap_eth_dst_value =
+       TOKEN_ETHERADDR_INITIALIZER(struct cmd_set_mplsoudp_encap_result,
+                                   eth_dst);
+
+static void cmd_set_mplsoudp_encap_parsed(void *parsed_result,
+       __attribute__((unused)) struct cmdline *cl,
+       __attribute__((unused)) void *data)
+{
+       struct cmd_set_mplsoudp_encap_result *res = parsed_result;
+       union {
+               uint32_t mplsoudp_label;
+               uint8_t label[3];
+       } id = {
+               .mplsoudp_label =
+                       rte_cpu_to_be_32(res->label) & RTE_BE32(0x00ffffff),
+       };
+
+       if (strcmp(res->mplsoudp, "mplsoudp_encap") == 0)
+               mplsoudp_encap_conf.select_vlan = 0;
+       else if (strcmp(res->mplsoudp, "mplsoudp_encap-with-vlan") == 0)
+               mplsoudp_encap_conf.select_vlan = 1;
+       if (strcmp(res->ip_version, "ipv4") == 0)
+               mplsoudp_encap_conf.select_ipv4 = 1;
+       else if (strcmp(res->ip_version, "ipv6") == 0)
+               mplsoudp_encap_conf.select_ipv4 = 0;
+       else
+               return;
+       rte_memcpy(mplsoudp_encap_conf.label, &id.label[1], 3);
+       mplsoudp_encap_conf.udp_src = rte_cpu_to_be_16(res->udp_src);
+       mplsoudp_encap_conf.udp_dst = rte_cpu_to_be_16(res->udp_dst);
+       if (mplsoudp_encap_conf.select_ipv4) {
+               IPV4_ADDR_TO_UINT(res->ip_src, mplsoudp_encap_conf.ipv4_src);
+               IPV4_ADDR_TO_UINT(res->ip_dst, mplsoudp_encap_conf.ipv4_dst);
+       } else {
+               IPV6_ADDR_TO_ARRAY(res->ip_src, mplsoudp_encap_conf.ipv6_src);
+               IPV6_ADDR_TO_ARRAY(res->ip_dst, mplsoudp_encap_conf.ipv6_dst);
+       }
+       if (mplsoudp_encap_conf.select_vlan)
+               mplsoudp_encap_conf.vlan_tci = rte_cpu_to_be_16(res->tci);
+       rte_memcpy(mplsoudp_encap_conf.eth_src, res->eth_src.addr_bytes,
+                  ETHER_ADDR_LEN);
+       rte_memcpy(mplsoudp_encap_conf.eth_dst, res->eth_dst.addr_bytes,
+                  ETHER_ADDR_LEN);
+}
+
+cmdline_parse_inst_t cmd_set_mplsoudp_encap = {
+       .f = cmd_set_mplsoudp_encap_parsed,
+       .data = NULL,
+       .help_str = "set mplsoudp_encap ip-version ipv4|ipv6 label <label>"
+               " udp-src <udp-src> udp-dst <udp-dst> ip-src <ip-src>"
+               " ip-dst <ip-dst> eth-src <eth-src> eth-dst <eth-dst>",
+       .tokens = {
+               (void *)&cmd_set_mplsoudp_encap_set,
+               (void *)&cmd_set_mplsoudp_encap_mplsoudp_encap,
+               (void *)&cmd_set_mplsoudp_encap_ip_version,
+               (void *)&cmd_set_mplsoudp_encap_ip_version_value,
+               (void *)&cmd_set_mplsoudp_encap_label,
+               (void *)&cmd_set_mplsoudp_encap_label_value,
+               (void *)&cmd_set_mplsoudp_encap_udp_src,
+               (void *)&cmd_set_mplsoudp_encap_udp_src_value,
+               (void *)&cmd_set_mplsoudp_encap_udp_dst,
+               (void *)&cmd_set_mplsoudp_encap_udp_dst_value,
+               (void *)&cmd_set_mplsoudp_encap_ip_src,
+               (void *)&cmd_set_mplsoudp_encap_ip_src_value,
+               (void *)&cmd_set_mplsoudp_encap_ip_dst,
+               (void *)&cmd_set_mplsoudp_encap_ip_dst_value,
+               (void *)&cmd_set_mplsoudp_encap_eth_src,
+               (void *)&cmd_set_mplsoudp_encap_eth_src_value,
+               (void *)&cmd_set_mplsoudp_encap_eth_dst,
+               (void *)&cmd_set_mplsoudp_encap_eth_dst_value,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_set_mplsoudp_encap_with_vlan = {
+       .f = cmd_set_mplsoudp_encap_parsed,
+       .data = NULL,
+       .help_str = "set mplsoudp_encap-with-vlan ip-version ipv4|ipv6"
+               " label <label> udp-src <udp-src> udp-dst <udp-dst>"
+               " ip-src <ip-src> ip-dst <ip-dst> vlan-tci <vlan-tci>"
+               " eth-src <eth-src> eth-dst <eth-dst>",
+       .tokens = {
+               (void *)&cmd_set_mplsoudp_encap_set,
+               (void *)&cmd_set_mplsoudp_encap_mplsoudp_encap_with_vlan,
+               (void *)&cmd_set_mplsoudp_encap_ip_version,
+               (void *)&cmd_set_mplsoudp_encap_ip_version_value,
+               (void *)&cmd_set_mplsoudp_encap_label,
+               (void *)&cmd_set_mplsoudp_encap_label_value,
+               (void *)&cmd_set_mplsoudp_encap_udp_src,
+               (void *)&cmd_set_mplsoudp_encap_udp_src_value,
+               (void *)&cmd_set_mplsoudp_encap_udp_dst,
+               (void *)&cmd_set_mplsoudp_encap_udp_dst_value,
+               (void *)&cmd_set_mplsoudp_encap_ip_src,
+               (void *)&cmd_set_mplsoudp_encap_ip_src_value,
+               (void *)&cmd_set_mplsoudp_encap_ip_dst,
+               (void *)&cmd_set_mplsoudp_encap_ip_dst_value,
+               (void *)&cmd_set_mplsoudp_encap_vlan,
+               (void *)&cmd_set_mplsoudp_encap_vlan_value,
+               (void *)&cmd_set_mplsoudp_encap_eth_src,
+               (void *)&cmd_set_mplsoudp_encap_eth_src_value,
+               (void *)&cmd_set_mplsoudp_encap_eth_dst,
+               (void *)&cmd_set_mplsoudp_encap_eth_dst_value,
+               NULL,
+       },
+};
+
+/** Set MPLSoUDP decapsulation details */
+struct cmd_set_mplsoudp_decap_result {
+       cmdline_fixed_string_t set;
+       cmdline_fixed_string_t mplsoudp;
+       cmdline_fixed_string_t pos_token;
+       cmdline_fixed_string_t ip_version;
+       uint32_t vlan_present:1;
+};
+
+cmdline_parse_token_string_t cmd_set_mplsoudp_decap_set =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_decap_result, set,
+                                "set");
+cmdline_parse_token_string_t cmd_set_mplsoudp_decap_mplsoudp_decap =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_decap_result, mplsoudp,
+                                "mplsoudp_decap");
+cmdline_parse_token_string_t cmd_set_mplsoudp_decap_mplsoudp_decap_with_vlan =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_decap_result,
+                                mplsoudp, "mplsoudp_decap-with-vlan");
+cmdline_parse_token_string_t cmd_set_mplsoudp_decap_ip_version =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_decap_result,
+                                pos_token, "ip-version");
+cmdline_parse_token_string_t cmd_set_mplsoudp_decap_ip_version_value =
+       TOKEN_STRING_INITIALIZER(struct cmd_set_mplsoudp_decap_result,
+                                ip_version, "ipv4#ipv6");
+
+static void cmd_set_mplsoudp_decap_parsed(void *parsed_result,
+       __attribute__((unused)) struct cmdline *cl,
+       __attribute__((unused)) void *data)
+{
+       struct cmd_set_mplsoudp_decap_result *res = parsed_result;
+
+       if (strcmp(res->mplsoudp, "mplsoudp_decap") == 0)
+               mplsoudp_decap_conf.select_vlan = 0;
+       else if (strcmp(res->mplsoudp, "mplsoudp_decap-with-vlan") == 0)
+               mplsoudp_decap_conf.select_vlan = 1;
+       if (strcmp(res->ip_version, "ipv4") == 0)
+               mplsoudp_decap_conf.select_ipv4 = 1;
+       else if (strcmp(res->ip_version, "ipv6") == 0)
+               mplsoudp_decap_conf.select_ipv4 = 0;
+}
+
+cmdline_parse_inst_t cmd_set_mplsoudp_decap = {
+       .f = cmd_set_mplsoudp_decap_parsed,
+       .data = NULL,
+       .help_str = "set mplsoudp_decap ip-version ipv4|ipv6",
+       .tokens = {
+               (void *)&cmd_set_mplsoudp_decap_set,
+               (void *)&cmd_set_mplsoudp_decap_mplsoudp_decap,
+               (void *)&cmd_set_mplsoudp_decap_ip_version,
+               (void *)&cmd_set_mplsoudp_decap_ip_version_value,
+               NULL,
+       },
+};
+
+cmdline_parse_inst_t cmd_set_mplsoudp_decap_with_vlan = {
+       .f = cmd_set_mplsoudp_decap_parsed,
+       .data = NULL,
+       .help_str = "set mplsoudp_decap-with-vlan ip-version ipv4|ipv6",
+       .tokens = {
+               (void *)&cmd_set_mplsoudp_decap_set,
+               (void *)&cmd_set_mplsoudp_decap_mplsoudp_decap_with_vlan,
+               (void *)&cmd_set_mplsoudp_decap_ip_version,
+               (void *)&cmd_set_mplsoudp_decap_ip_version_value,
+               NULL,
+       },
+};
+
 /* Strict link priority scheduling mode setting */
 static void
 cmd_strict_link_prio_parsed(
@@ -18014,6 +18416,14 @@ cmdline_parse_ctx_t main_ctx[] = {
        (cmdline_parse_inst_t *)&cmd_set_vxlan_with_vlan,
        (cmdline_parse_inst_t *)&cmd_set_nvgre,
        (cmdline_parse_inst_t *)&cmd_set_nvgre_with_vlan,
+       (cmdline_parse_inst_t *)&cmd_set_l2_encap,
+       (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_mplsoudp_encap,
+       (cmdline_parse_inst_t *)&cmd_set_mplsoudp_encap_with_vlan,
+       (cmdline_parse_inst_t *)&cmd_set_mplsoudp_decap,
+       (cmdline_parse_inst_t *)&cmd_set_mplsoudp_decap_with_vlan,
        (cmdline_parse_inst_t *)&cmd_ddp_add,
        (cmdline_parse_inst_t *)&cmd_ddp_del,
        (cmdline_parse_inst_t *)&cmd_ddp_get_list,
index 7932b54..2e771f4 100644 (file)
@@ -245,6 +245,10 @@ enum index {
        ACTION_VXLAN_DECAP,
        ACTION_NVGRE_ENCAP,
        ACTION_NVGRE_DECAP,
+       ACTION_L2_ENCAP,
+       ACTION_L2_DECAP,
+       ACTION_MPLSOUDP_ENCAP,
+       ACTION_MPLSOUDP_DECAP,
        ACTION_SET_IPV4_SRC,
        ACTION_SET_IPV4_SRC_IPV4_SRC,
        ACTION_SET_IPV4_DST,
@@ -317,6 +321,22 @@ struct action_nvgre_encap_data {
        struct rte_flow_item_nvgre item_nvgre;
 };
 
+/** Maximum data size in struct rte_flow_action_raw_encap. */
+#define ACTION_RAW_ENCAP_MAX_DATA 128
+
+/** Storage for struct rte_flow_action_raw_encap including external data. */
+struct action_raw_encap_data {
+       struct rte_flow_action_raw_encap conf;
+       uint8_t data[ACTION_RAW_ENCAP_MAX_DATA];
+       uint8_t preserve[ACTION_RAW_ENCAP_MAX_DATA];
+};
+
+/** Storage for struct rte_flow_action_raw_decap including external data. */
+struct action_raw_decap_data {
+       struct rte_flow_action_raw_decap conf;
+       uint8_t data[ACTION_RAW_ENCAP_MAX_DATA];
+};
+
 /** Maximum number of subsequent tokens and arguments on the stack. */
 #define CTX_STACK_SIZE 16
 
@@ -850,6 +870,10 @@ static const enum index next_action[] = {
        ACTION_VXLAN_DECAP,
        ACTION_NVGRE_ENCAP,
        ACTION_NVGRE_DECAP,
+       ACTION_L2_ENCAP,
+       ACTION_L2_DECAP,
+       ACTION_MPLSOUDP_ENCAP,
+       ACTION_MPLSOUDP_DECAP,
        ACTION_SET_IPV4_SRC,
        ACTION_SET_IPV4_DST,
        ACTION_SET_IPV6_SRC,
@@ -1051,6 +1075,18 @@ static int parse_vc_action_vxlan_encap(struct context *, const struct token *,
 static int parse_vc_action_nvgre_encap(struct context *, const struct token *,
                                       const char *, unsigned int, void *,
                                       unsigned int);
+static int parse_vc_action_l2_encap(struct context *, const struct token *,
+                                   const char *, unsigned int, void *,
+                                   unsigned int);
+static int parse_vc_action_l2_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);
+static int parse_vc_action_mplsoudp_decap(struct context *,
+                                         const struct token *, const char *,
+                                         unsigned int, void *, unsigned int);
 static int parse_destroy(struct context *, const struct token *,
                         const char *, unsigned int,
                         void *, unsigned int);
@@ -2583,6 +2619,42 @@ static const struct token token_list[] = {
                .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\"",
+               .priv = PRIV_ACTION(RAW_ENCAP,
+                                   sizeof(struct action_raw_encap_data)),
+               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+               .call = parse_vc_action_l2_encap,
+       },
+       [ACTION_L2_DECAP] = {
+               .name = "l2_decap",
+               .help = "l2 decap, uses configuration set by"
+                       " \"set l2_decap\"",
+               .priv = PRIV_ACTION(RAW_DECAP,
+                                   sizeof(struct action_raw_decap_data)),
+               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+               .call = parse_vc_action_l2_decap,
+       },
+       [ACTION_MPLSOUDP_DECAP] = {
+               .name = "mplsoudp_decap",
+               .help = "mplsoudp decapsulation, uses configuration set by"
+                       " \"set mplsoudp_decap\"",
+               .priv = PRIV_ACTION(RAW_DECAP,
+                                   sizeof(struct action_raw_decap_data)),
+               .next = NEXT(NEXT_ENTRY(ACTION_NEXT)),
+               .call = parse_vc_action_mplsoudp_decap,
+       },
        [ACTION_SET_IPV4_SRC] = {
                .name = "set_ipv4_src",
                .help = "Set a new IPv4 source address in the outermost"
@@ -3503,6 +3575,314 @@ parse_vc_action_nvgre_encap(struct context *ctx, const struct token *token,
        return ret;
 }
 
+/** Parse l2 encap action. */
+static int
+parse_vc_action_l2_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 = mplsoudp_encap_conf.vlan_tci,
+               .inner_type = 0,
+       };
+       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 = {},
+       };
+       header = action_encap_data->data;
+       if (l2_encap_conf.select_vlan)
+               eth.type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+       else if (l2_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,
+              l2_encap_conf.eth_dst, ETHER_ADDR_LEN);
+       memcpy(eth.src.addr_bytes,
+              l2_encap_conf.eth_src, ETHER_ADDR_LEN);
+       memcpy(header, &eth, sizeof(eth));
+       header += sizeof(eth);
+       if (l2_encap_conf.select_vlan) {
+               if (l2_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);
+       }
+       action_encap_data->conf.size = header -
+               action_encap_data->data;
+       action->conf = &action_encap_data->conf;
+       return ret;
+}
+
+/** Parse l2 decap action. */
+static int
+parse_vc_action_l2_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 = mplsoudp_encap_conf.vlan_tci,
+               .inner_type = 0,
+       };
+       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 (l2_decap_conf.select_vlan)
+               eth.type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+       memcpy(header, &eth, sizeof(eth));
+       header += sizeof(eth);
+       if (l2_decap_conf.select_vlan) {
+               memcpy(header, &vlan, sizeof(vlan));
+               header += sizeof(vlan);
+       }
+       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,
+                              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 = mplsoudp_encap_conf.vlan_tci,
+               .inner_type = 0,
+       };
+       struct rte_flow_item_ipv4 ipv4 = {
+               .hdr =  {
+                       .src_addr = mplsoudp_encap_conf.ipv4_src,
+                       .dst_addr = mplsoudp_encap_conf.ipv4_dst,
+                       .next_proto_id = IPPROTO_UDP,
+               },
+       };
+       struct rte_flow_item_ipv6 ipv6 = {
+               .hdr =  {
+                       .proto = IPPROTO_UDP,
+               },
+       };
+       struct rte_flow_item_udp udp = {
+               .hdr = {
+                       .src_port = mplsoudp_encap_conf.udp_src,
+                       .dst_port = mplsoudp_encap_conf.udp_dst,
+               },
+       };
+       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 (mplsoudp_encap_conf.select_vlan)
+               eth.type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+       else if (mplsoudp_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,
+              mplsoudp_encap_conf.eth_dst, ETHER_ADDR_LEN);
+       memcpy(eth.src.addr_bytes,
+              mplsoudp_encap_conf.eth_src, ETHER_ADDR_LEN);
+       memcpy(header, &eth, sizeof(eth));
+       header += sizeof(eth);
+       if (mplsoudp_encap_conf.select_vlan) {
+               if (mplsoudp_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 (mplsoudp_encap_conf.select_ipv4) {
+               memcpy(header, &ipv4, sizeof(ipv4));
+               header += sizeof(ipv4);
+       } else {
+               memcpy(&ipv6.hdr.src_addr,
+                      &mplsoudp_encap_conf.ipv6_src,
+                      sizeof(mplsoudp_encap_conf.ipv6_src));
+               memcpy(&ipv6.hdr.dst_addr,
+                      &mplsoudp_encap_conf.ipv6_dst,
+                      sizeof(mplsoudp_encap_conf.ipv6_dst));
+               memcpy(header, &ipv6, sizeof(ipv6));
+               header += sizeof(ipv6);
+       }
+       memcpy(header, &udp, sizeof(udp));
+       header += sizeof(udp);
+       memcpy(mpls.label_tc_s, mplsoudp_encap_conf.label,
+              RTE_DIM(mplsoudp_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 MPLSOUDP decap action. */
+static int
+parse_vc_action_mplsoudp_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_UDP,
+               },
+       };
+       struct rte_flow_item_ipv6 ipv6 = {
+               .hdr =  {
+                       .proto = IPPROTO_UDP,
+               },
+       };
+       struct rte_flow_item_udp udp = {
+               .hdr = {
+                       .dst_port = rte_cpu_to_be_16(6635),
+               },
+       };
+       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 (mplsoudp_decap_conf.select_vlan)
+               eth.type = rte_cpu_to_be_16(ETHER_TYPE_VLAN);
+       else if (mplsoudp_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,
+              mplsoudp_encap_conf.eth_dst, ETHER_ADDR_LEN);
+       memcpy(eth.src.addr_bytes,
+              mplsoudp_encap_conf.eth_src, ETHER_ADDR_LEN);
+       memcpy(header, &eth, sizeof(eth));
+       header += sizeof(eth);
+       if (mplsoudp_encap_conf.select_vlan) {
+               if (mplsoudp_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 (mplsoudp_encap_conf.select_ipv4) {
+               memcpy(header, &ipv4, sizeof(ipv4));
+               header += sizeof(ipv4);
+       } else {
+               memcpy(header, &ipv6, sizeof(ipv6));
+               header += sizeof(ipv6);
+       }
+       memcpy(header, &udp, sizeof(udp));
+       header += sizeof(udp);
+       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 tokens for destroy command. */
 static int
 parse_destroy(struct context *ctx, const struct token *token,
index c07548e..adfe090 100644 (file)
@@ -511,6 +511,46 @@ struct nvgre_encap_conf {
 };
 struct nvgre_encap_conf nvgre_encap_conf;
 
+/* L2 encap parameters. */
+struct l2_encap_conf {
+       uint32_t select_ipv4:1;
+       uint32_t select_vlan:1;
+       rte_be16_t vlan_tci;
+       uint8_t eth_src[ETHER_ADDR_LEN];
+       uint8_t eth_dst[ETHER_ADDR_LEN];
+};
+struct l2_encap_conf l2_encap_conf;
+
+/* L2 decap parameters. */
+struct l2_decap_conf {
+       uint32_t select_vlan:1;
+};
+struct l2_decap_conf l2_decap_conf;
+
+/* MPLSoUDP encap parameters. */
+struct mplsoudp_encap_conf {
+       uint32_t select_ipv4:1;
+       uint32_t select_vlan:1;
+       uint8_t label[3];
+       rte_be16_t udp_src;
+       rte_be16_t udp_dst;
+       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 mplsoudp_encap_conf mplsoudp_encap_conf;
+
+/* MPLSoUDP decap parameters. */
+struct mplsoudp_decap_conf {
+       uint32_t select_ipv4:1;
+       uint32_t select_vlan:1;
+};
+struct mplsoudp_decap_conf mplsoudp_decap_conf;
+
 static inline unsigned int
 lcore_num(void)
 {
index 3854271..a996d56 100644 (file)
@@ -1592,6 +1592,63 @@ flow rule using the action nvgre_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 L2 Encap
+~~~~~~~~~~~~~~~
+
+Configure the l2 to be used when encapsulating a packet with L2::
+
+ set l2_encap ip-version (ipv4|ipv6) eth-src (eth-src) eth-dst (eth-dst)
+ set l2_encap-with-vlan ip-version (ipv4|ipv6) vlan-tci (vlan-tci) \
+        eth-src (eth-src) eth-dst (eth-dst)
+
+Those commands will set an internal configuration inside testpmd, any following
+flow rule using the action l2_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 L2 Decap
+~~~~~~~~~~~~~~~
+
+Configure the l2 to be removed when decapsulating a packet with L2::
+
+ set l2_decap ip-version (ipv4|ipv6)
+ set l2_decap-with-vlan ip-version (ipv4|ipv6)
+
+Those commands will set an internal configuration inside testpmd, any following
+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 MPLSoUDP Encap outer layers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configure the outer layer to encapsulate a packet inside a MPLSoUDP tunnel::
+
+ set mplsoudp_encap ip-version (ipv4|ipv6) label (label) udp-src (udp-src) \
+        udp-dst (udp-dst) ip-src (ip-src) ip-dst (ip-dst) \
+        eth-src (eth-src) eth-dst (eth-dst)
+ set mplsoudp_encap-with-vlan ip-version (ipv4|ipv6) label (label) \
+        udp-src (udp-src) udp-dst (udp-dst) 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 mplsoudp_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 MPLSoUDP Decap outer layers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Configure the outer layer to decapsulate MPLSoUDP packet::
+
+ set mplsoudp_decap ip-version (ipv4|ipv6)
+ set mplsoudp_decap-with-vlan ip-version (ipv4|ipv6)
+
+Those command will set an internal configuration inside testpmd, any following
+flow rule using the action mplsoudp_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.
+
 Port Functions
 --------------
 
@@ -3795,6 +3852,18 @@ This section lists supported actions and their attributes, if any.
 - ``nvgre_decap``: Performs a decapsulation action by stripping all headers of
   the NVGRE tunnel network overlay from the matched flow.
 
+- ``l2_encap``: Performs a L2 encapsulation, L2 configuration
+  is done through `Config L2 Encap`_.
+
+- ``l2_decap``: Performs a L2 decapsulation, L2 configuration
+  is done through `Config L2 Decap`_.
+
+- ``mplsoudp_encap``: Performs a MPLSoUDP encapsulation, outer layer
+  configuration is done through `Config MPLSoUDP Encap outer layers`_.
+
+- ``mplsoudp_decap``: Performs a MPLSoUDP decapsulation, outer layer
+  configuration is done through `Config MPLSoUDP Decap outer layers`_.
+
 - ``set_ipv4_src``: Set a new IPv4 source address in the outermost IPv4 header.
 
   - ``ipv4_addr``: New IPv4 source address.
@@ -4167,6 +4236,112 @@ IPv6 NVGRE outer header::
  testpmd> flow create 0 ingress pattern end actions nvgre_encap /
         queue index 0 / end
 
+Sample L2 encapsulation rule
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+L2 encapsulation has default value pre-configured in testpmd
+source code, those can be changed by using the following commands
+
+L2 header::
+
+ testpmd> set l2_encap ip-version ipv4
+        eth-src 11:11:11:11:11:11 eth-dst 22:22:22:22:22:22
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / mpls / end actions
+        mplsoudp_decap / l2_encap / end
+
+L2 with VXLAN header::
+
+ testpmd> set l2_encap-with-vlan ip-version ipv4 vlan-tci 34
+         eth-src 11:11:11:11:11:11 eth-dst 22:22:22:22:22:22
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / mpls / end actions
+        mplsoudp_decap / l2_encap / end
+
+Sample L2 decapsulation rule
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+L2 decapsulation has default value pre-configured in testpmd
+source code, those can be changed by using the following commands
+
+L2 header::
+
+ testpmd> set l2_decap
+ testpmd> flow create 0 egress pattern eth / end actions l2_decap / mplsoudp_encap /
+        queue index 0 / end
+
+L2 with VXLAN header::
+
+ testpmd> set l2_encap-with-vlan
+ testpmd> flow create 0 egress pattern eth / end actions l2_encap / mplsoudp_encap /
+         queue index 0 / end
+
+Sample MPLSoUDP encapsulation rule
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+MPLSoUDP encapsulation outer layer has default value pre-configured in testpmd
+source code, those can be changed by using the following commands
+
+IPv4 MPLSoUDP outer header::
+
+ testpmd> set mplsoudp_encap ip-version ipv4 label 4 udp-src 5 udp-dst 10
+        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 /
+        mplsoudp_encap / end
+
+IPv4 MPLSoUDP with VLAN outer header::
+
+ testpmd> set mplsoudp_encap-with-vlan ip-version ipv4 label 4 udp-src 5
+        udp-dst 10 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 /
+        mplsoudp_encap / end
+
+IPv6 MPLSoUDP outer header::
+
+ testpmd> set mplsoudp_encap ip-version ipv6 mask 4 udp-src 5 udp-dst 10
+        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 /
+        mplsoudp_encap / end
+
+IPv6 MPLSoUDP with VLAN outer header::
+
+ testpmd> set mplsoudp_encap-with-vlan ip-version ipv6 mask 4 udp-src 5
+        udp-dst 10 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 /
+        mplsoudp_encap / end
+
+Sample MPLSoUDP decapsulation rule
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+MPLSoUDP decapsulation outer layer has default value pre-configured in testpmd
+source code, those can be changed by using the following commands
+
+IPv4 MPLSoUDP outer header::
+
+ testpmd> set mplsoudp_decap ip-version ipv4
+ testpmd> flow create 0 ingress pattern eth / ipv4 / udp / mpls / end actions
+        mplsoudp_decap / l2_encap / end
+
+IPv4 MPLSoUDP with VLAN outer header::
+
+ testpmd> set mplsoudp_decap-with-vlan ip-version ipv4
+ testpmd> flow create 0 ingress pattern eth / vlan / ipv4 / udp / mpls / end
+        actions mplsoudp_decap / l2_encap / end
+
+IPv6 MPLSoUDP outer header::
+
+ testpmd> set mplsoudp_decap ip-version ipv6
+ testpmd> flow create 0 ingress pattern eth / ipv6 / udp / mpls / end
+        actions mplsoudp_decap / l2_encap / end
+
+IPv6 MPLSoUDP with VLAN outer header::
+
+ testpmd> set mplsoudp_decap-with-vlan ip-version ipv6
+ testpmd> flow create 0 ingress pattern eth / vlan / ipv6 / udp / mpls / end
+        actions mplsoudp_decap / l2_encap / end
+
 BPF Functions
 --------------