Hi Akhil, Can you please review this patch?
Thanks Ankur >-----Original Message----- >From: Ankur Dwivedi <adwiv...@marvell.com> >Sent: Wednesday, June 10, 2020 2:39 PM >To: dev@dpdk.org >Cc: praveen.she...@intel.com; konstantin.anan...@intel.com; >radu.nico...@intel.com; akhil.go...@nxp.com; Anoob Joseph ><ano...@marvell.com>; Ankur Dwivedi <adwiv...@marvell.com> >Subject: [PATCH v2] examples/ipsec-secgw: enable rte_flow based packet >distribution > >From: Anoob Joseph <ano...@marvell.com> > >RTE_FLOW API allows hardware parsing and steering of packets to specific >queues which helps in distributing ingress traffic across various cores. >Adding 'flow' rules allows user to specify the distribution required. > >Signed-off-by: Anoob Joseph <ano...@marvell.com> >--- >v2: >* Removed Change-Id > > doc/guides/sample_app_ug/ipsec_secgw.rst | 78 ++++++++- > examples/ipsec-secgw/Makefile | 1 + > examples/ipsec-secgw/flow.c | 285 >+++++++++++++++++++++++++++++++ > examples/ipsec-secgw/flow.h | 15 ++ > examples/ipsec-secgw/ipsec-secgw.c | 3 + > examples/ipsec-secgw/ipsec-secgw.h | 7 + > examples/ipsec-secgw/ipsec.h | 7 - > examples/ipsec-secgw/meson.build | 15 +- > examples/ipsec-secgw/parser.c | 46 +++++ > examples/ipsec-secgw/parser.h | 7 +- > 10 files changed, 450 insertions(+), 14 deletions(-) create mode 100644 >examples/ipsec-secgw/flow.c create mode 100644 examples/ipsec- >secgw/flow.h > >diff --git a/doc/guides/sample_app_ug/ipsec_secgw.rst >b/doc/guides/sample_app_ug/ipsec_secgw.rst >index 81c5d43..434f484 100644 >--- a/doc/guides/sample_app_ug/ipsec_secgw.rst >+++ b/doc/guides/sample_app_ug/ipsec_secgw.rst >@@ -348,7 +348,7 @@ Configurations > -------------- > > The following sections provide the syntax of configurations to initialize > -your >SP, SA, Routing and Neighbour tables. >+your SP, SA, Routing, Flow and Neighbour tables. > Configurations shall be specified in the configuration file to be passed to > the >application. The file is then parsed by the application. The successful >parsing >will result in the appropriate rules being applied to the tables @@ -369,7 >+369,7 @@ General rule syntax > > The parse treats one line in the configuration file as one configuration item >(unless the line concatenation symbol exists). Every configuration -item shall >follow the syntax of either SP, SA, Routing or Neighbour >+item shall follow the syntax of either SP, SA, Routing, Flow or >+Neighbour > rules specified below. > > The configuration parser supports the following special symbols: >@@ -808,6 +808,80 @@ Example SP rules: > > rt ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 0 > >+Flow rule syntax >+^^^^^^^^^^^^^^^^ >+ >+Flow rule enables the usage of hardware classification capabilities to >+match specific ingress traffic and redirect the packets to the >+specified queue. This feature is optional and relies on hardware ``rte_flow`` >support. >+ >+The flow rule syntax is shown as follows: >+ >+.. code-block:: console >+ >+ flow <ip_ver> <src_ip> <dst_ip> <port> <queue> >+ >+ >+where each options means: >+ >+``<ip_ver>`` >+ >+ * IP protocol version >+ >+ * Optional: No >+ >+ * Available options: >+ >+ * *ipv4*: IP protocol version 4 >+ * *ipv6*: IP protocol version 6 >+ >+``<src_ip>`` >+ >+ * The source IP address and mask >+ >+ * Optional: Yes, default address 0.0.0.0 and mask of 0 will be used >+ >+ * Syntax: >+ >+ * *src X.X.X.X/Y* for IPv4 >+ * *src XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/Y* for IPv6 >+ >+``<dst_ip>`` >+ >+ * The destination IP address and mask >+ >+ * Optional: Yes, default address 0.0.0.0 and mask of 0 will be used >+ >+ * Syntax: >+ >+ * *dst X.X.X.X/Y* for IPv4 >+ * *dst XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX/Y* for IPv6 >+ >+``<port>`` >+ >+ * The traffic input port id >+ >+ * Optional: yes, default input port 0 will be used >+ >+ * Syntax: *port X* >+ >+``<queue>`` >+ >+ * The traffic input queue id >+ >+ * Optional: yes, default input queue 0 will be used >+ >+ * Syntax: *queue X* >+ >+Example flow rules: >+ >+.. code-block:: console >+ >+ flow ipv4 dst 172.16.1.5/32 port 0 queue 0 >+ >+ flow ipv6 dst 1111:1111:1111:1111:1111:1111:1111:5555/116 port 1 >+ queue 0 >+ >+ > Neighbour rule syntax > ^^^^^^^^^^^^^^^^^^^^^ > >diff --git a/examples/ipsec-secgw/Makefile b/examples/ipsec-secgw/Makefile >index c4a272a..dbae152 100644 >--- a/examples/ipsec-secgw/Makefile >+++ b/examples/ipsec-secgw/Makefile >@@ -18,6 +18,7 @@ SRCS-y += ipsec_process.c SRCS-y += ipsec-secgw.c >SRCS-y += ipsec_worker.c SRCS-y += event_helper.c >+SRCS-y += flow.c > > CFLAGS += -gdwarf-2 > >diff --git a/examples/ipsec-secgw/flow.c b/examples/ipsec-secgw/flow.c new >file mode 100644 index 0000000..69f8405 >--- /dev/null >+++ b/examples/ipsec-secgw/flow.c >@@ -0,0 +1,285 @@ >+/* SPDX-License-Identifier: BSD-3-Clause >+ * Copyright (C) 2020 Marvell International Ltd. >+ */ >+ >+#include <stdio.h> >+ >+#include <rte_common.h> >+#include <rte_flow.h> >+#include <rte_ip.h> >+ >+#include "flow.h" >+#include "ipsec-secgw.h" >+#include "parser.h" >+ >+#define FLOW_RULES_MAX 128 >+ >+struct flow_rule_entry { >+ uint8_t is_ipv4; >+ RTE_STD_C11 >+ union { >+ struct { >+ struct rte_flow_item_ipv4 spec; >+ struct rte_flow_item_ipv4 mask; >+ } ipv4; >+ struct { >+ struct rte_flow_item_ipv6 spec; >+ struct rte_flow_item_ipv6 mask; >+ } ipv6; >+ }; >+ uint16_t port; >+ uint16_t queue; >+ struct rte_flow *flow; >+} flow_rule_tbl[FLOW_RULES_MAX]; >+ >+int nb_flow_rule; >+ >+static void >+ipv4_hdr_print(struct rte_ipv4_hdr *hdr) { >+ char a, b, c, d; >+ >+ uint32_t_to_char(rte_bswap32(hdr->src_addr), &a, &b, &c, &d); >+ printf("src: %3hhu.%3hhu.%3hhu.%3hhu \t", a, b, c, d); >+ >+ uint32_t_to_char(rte_bswap32(hdr->dst_addr), &a, &b, &c, &d); >+ printf("dst: %3hhu.%3hhu.%3hhu.%3hhu", a, b, c, d); } >+ >+static int >+ipv4_addr_cpy(rte_be32_t *spec, rte_be32_t *mask, char *token, >+ struct parse_status *status) >+{ >+ struct in_addr ip; >+ uint32_t depth; >+ >+ APP_CHECK(parse_ipv4_addr(token, &ip, &depth) == 0, status, >+ "unrecognized input \"%s\", expect valid ipv4 addr", token); >+ if (status->status < 0) >+ return -1; >+ >+ if (depth > 32) >+ return -1; >+ >+ memcpy(mask, &rte_flow_item_ipv4_mask.hdr.src_addr, sizeof(ip)); >+ >+ *spec = ip.s_addr; >+ if (depth < 32) >+ *mask = *mask << (32-depth); >+ >+ return 0; >+} >+ >+static void >+ipv6_hdr_print(struct rte_ipv6_hdr *hdr) { >+ uint8_t *addr; >+ >+ addr = hdr->src_addr; >+ printf("src: %4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx \t", >+ (uint16_t)((addr[0] << 8) | addr[1]), >+ (uint16_t)((addr[2] << 8) | addr[3]), >+ (uint16_t)((addr[4] << 8) | addr[5]), >+ (uint16_t)((addr[6] << 8) | addr[7]), >+ (uint16_t)((addr[8] << 8) | addr[9]), >+ (uint16_t)((addr[10] << 8) | addr[11]), >+ (uint16_t)((addr[12] << 8) | addr[13]), >+ (uint16_t)((addr[14] << 8) | addr[15])); >+ >+ addr = hdr->dst_addr; >+ printf("dst: %4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx:%4hx", >+ (uint16_t)((addr[0] << 8) | addr[1]), >+ (uint16_t)((addr[2] << 8) | addr[3]), >+ (uint16_t)((addr[4] << 8) | addr[5]), >+ (uint16_t)((addr[6] << 8) | addr[7]), >+ (uint16_t)((addr[8] << 8) | addr[9]), >+ (uint16_t)((addr[10] << 8) | addr[11]), >+ (uint16_t)((addr[12] << 8) | addr[13]), >+ (uint16_t)((addr[14] << 8) | addr[15])); } >+ >+static int >+ipv6_addr_cpy(uint8_t *spec, uint8_t *mask, char *token, >+ struct parse_status *status) >+{ >+ struct in6_addr ip; >+ uint32_t depth, i; >+ >+ APP_CHECK(parse_ipv6_addr(token, &ip, &depth) == 0, status, >+ "unrecognized input \"%s\", expect valid ipv6 address", >token); >+ if (status->status < 0) >+ return -1; >+ >+ memcpy(mask, &rte_flow_item_ipv6_mask.hdr.src_addr, sizeof(ip)); >+ memcpy(spec, ip.s6_addr, sizeof(struct in6_addr)); >+ >+ for (i = 0; i < depth && (i%8 <= sizeof(struct in6_addr)); i++) >+ mask[i/8] &= ~(1 << (7-i%8)); >+ >+ return 0; >+} >+ >+void >+parse_flow_tokens(char **tokens, uint32_t n_tokens, >+ struct parse_status *status) >+{ >+ struct flow_rule_entry *rule; >+ uint32_t ti; >+ >+ if (nb_flow_rule >= FLOW_RULES_MAX) { >+ printf("Too many flow rules\n"); >+ return; >+ } >+ >+ rule = &flow_rule_tbl[nb_flow_rule]; >+ memset(rule, 0, sizeof(*rule)); >+ >+ if (strcmp(tokens[0], "ipv4") == 0) { >+ rule->is_ipv4 = 1; >+ } else if (strcmp(tokens[0], "ipv6") == 0) { >+ rule->is_ipv4 = 0; >+ } else { >+ APP_CHECK(0, status, "unrecognized input \"%s\"", tokens[0]); >+ return; >+ } >+ >+ for (ti = 1; ti < n_tokens; ti++) { >+ if (strcmp(tokens[ti], "src") == 0) { >+ INCREMENT_TOKEN_INDEX(ti, n_tokens, status); >+ if (status->status < 0) >+ return; >+ >+ if (rule->is_ipv4) { >+ if (ipv4_addr_cpy(&rule- >>ipv4.spec.hdr.src_addr, >+ &rule- >>ipv4.mask.hdr.src_addr, >+ tokens[ti], status)) >+ return; >+ } else { >+ if (ipv6_addr_cpy(rule- >>ipv6.spec.hdr.src_addr, >+ rule- >>ipv6.mask.hdr.src_addr, >+ tokens[ti], status)) >+ return; >+ } >+ } >+ if (strcmp(tokens[ti], "dst") == 0) { >+ INCREMENT_TOKEN_INDEX(ti, n_tokens, status); >+ if (status->status < 0) >+ return; >+ >+ if (rule->is_ipv4) { >+ if (ipv4_addr_cpy(&rule- >>ipv4.spec.hdr.dst_addr, >+ &rule- >>ipv4.mask.hdr.dst_addr, >+ tokens[ti], status)) >+ return; >+ } else { >+ if (ipv6_addr_cpy(rule- >>ipv6.spec.hdr.dst_addr, >+ rule- >>ipv6.mask.hdr.dst_addr, >+ tokens[ti], status)) >+ return; >+ } >+ } >+ >+ if (strcmp(tokens[ti], "port") == 0) { >+ INCREMENT_TOKEN_INDEX(ti, n_tokens, status); >+ if (status->status < 0) >+ return; >+ APP_CHECK_TOKEN_IS_NUM(tokens, ti, status); >+ if (status->status < 0) >+ return; >+ >+ rule->port = atoi(tokens[ti]); >+ >+ INCREMENT_TOKEN_INDEX(ti, n_tokens, status); >+ if (status->status < 0) >+ return; >+ APP_CHECK_TOKEN_IS_NUM(tokens, ti, status); >+ if (status->status < 0) >+ return; >+ >+ rule->queue = atoi(tokens[ti]); >+ } >+ } >+ >+ nb_flow_rule++; >+} >+ >+#define MAX_RTE_FLOW_PATTERN (3) >+#define MAX_RTE_FLOW_ACTIONS (2) >+ >+static void >+flow_init_single(struct flow_rule_entry *rule) { >+ struct rte_flow_item pattern[MAX_RTE_FLOW_PATTERN] = {}; >+ struct rte_flow_action action[MAX_RTE_FLOW_ACTIONS] = {}; >+ struct rte_flow_attr attr = {}; >+ struct rte_flow_error err; >+ int ret; >+ >+ attr.egress = 0; >+ attr.ingress = 1; >+ >+ action[0].type = RTE_FLOW_ACTION_TYPE_QUEUE; >+ action[0].conf = &(struct rte_flow_action_queue) { >+ .index = rule->queue, >+ }; >+ action[1].type = RTE_FLOW_ACTION_TYPE_END; >+ >+ pattern[0].type = RTE_FLOW_ITEM_TYPE_ETH; >+ >+ if (rule->is_ipv4) { >+ pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV4; >+ pattern[1].spec = &rule->ipv4.spec; >+ pattern[1].mask = &rule->ipv4.mask; >+ } else { >+ pattern[1].type = RTE_FLOW_ITEM_TYPE_IPV6; >+ pattern[1].spec = &rule->ipv6.spec; >+ pattern[1].mask = &rule->ipv6.mask; >+ } >+ >+ pattern[2].type = RTE_FLOW_ITEM_TYPE_END; >+ >+ ret = rte_flow_validate(rule->port, &attr, pattern, action, &err); >+ if (ret < 0) { >+ RTE_LOG(ERR, IPSEC, "Flow validation failed %s\n", >err.message); >+ return; >+ } >+ >+ rule->flow = rte_flow_create(rule->port, &attr, pattern, action, &err); >+ if (rule->flow == NULL) >+ RTE_LOG(ERR, IPSEC, "Flow creation return %s\n", >err.message); } >+ >+void >+flow_init(void) >+{ >+ struct flow_rule_entry *rule; >+ int i; >+ >+ for (i = 0; i < nb_flow_rule; i++) { >+ rule = &flow_rule_tbl[i]; >+ flow_init_single(rule); >+ } >+ >+ for (i = 0; i < nb_flow_rule; i++) { >+ rule = &flow_rule_tbl[i]; >+ if (rule->is_ipv4) { >+ printf("Flow #%3d: spec ipv4 ", i); >+ ipv4_hdr_print(&rule->ipv4.spec.hdr); >+ printf("\n"); >+ printf(" mask ipv4 "); >+ ipv4_hdr_print(&rule->ipv4.mask.hdr); >+ } else { >+ printf("Flow #%3d: spec ipv6 ", i); >+ ipv6_hdr_print(&rule->ipv6.spec.hdr); >+ printf("\n"); >+ printf(" mask ipv6 "); >+ ipv6_hdr_print(&rule->ipv6.mask.hdr); >+ } >+ >+ printf("\tPort: %d, Queue: %d", rule->port, rule->queue); >+ >+ if (rule->flow == NULL) >+ printf(" [UNSUPPORTED]"); >+ printf("\n"); >+ } >+} >diff --git a/examples/ipsec-secgw/flow.h b/examples/ipsec-secgw/flow.h new >file mode 100644 index 0000000..1b1b477 >--- /dev/null >+++ b/examples/ipsec-secgw/flow.h >@@ -0,0 +1,15 @@ >+/* SPDX-License-Identifier: BSD-3-Clause >+ * Copyright (C) 2020 Marvell International Ltd. >+ */ >+ >+#ifndef _FLOW_H_ >+#define _FLOW_H_ >+ >+#include "parser.h" >+ >+void parse_flow_tokens(char **tokens, uint32_t n_tokens, >+ struct parse_status *status); >+ >+void flow_init(void); >+ >+#endif /* _FLOW_H_ */ >diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec- >secgw/ipsec-secgw.c >index f777ce2..d19688d 100644 >--- a/examples/ipsec-secgw/ipsec-secgw.c >+++ b/examples/ipsec-secgw/ipsec-secgw.c >@@ -49,6 +49,7 @@ > #include <rte_ip_frag.h> > > #include "event_helper.h" >+#include "flow.h" > #include "ipsec.h" > #include "ipsec_worker.h" > #include "parser.h" >@@ -2914,6 +2915,8 @@ struct lcore_conf { > } > } > >+ flow_init(); >+ > check_all_ports_link_status(enabled_port_mask); > > /* launch per-lcore init on every lcore */ diff --git a/examples/ipsec- >secgw/ipsec-secgw.h b/examples/ipsec-secgw/ipsec-secgw.h >index 4b53cb5..412d727 100644 >--- a/examples/ipsec-secgw/ipsec-secgw.h >+++ b/examples/ipsec-secgw/ipsec-secgw.h >@@ -34,6 +34,13 @@ > ((uint64_t)(a) & 0xff)) > #endif > >+#define uint32_t_to_char(ip, a, b, c, d) do {\ >+ *a = (uint8_t)(ip >> 24 & 0xff);\ >+ *b = (uint8_t)(ip >> 16 & 0xff);\ >+ *c = (uint8_t)(ip >> 8 & 0xff);\ >+ *d = (uint8_t)(ip & 0xff);\ >+ } while (0) >+ > #define ETHADDR(a, b, c, d, e, f) (__BYTES_TO_UINT64(a, b, c, d, e, f, 0, 0)) > > struct traffic_type { >diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h >index 2f69199..7031e28 100644 >--- a/examples/ipsec-secgw/ipsec.h >+++ b/examples/ipsec-secgw/ipsec.h >@@ -28,13 +28,6 @@ > #define IV_OFFSET (sizeof(struct rte_crypto_op) + \ > sizeof(struct rte_crypto_sym_op)) > >-#define uint32_t_to_char(ip, a, b, c, d) do {\ >- *a = (uint8_t)(ip >> 24 & 0xff);\ >- *b = (uint8_t)(ip >> 16 & 0xff);\ >- *c = (uint8_t)(ip >> 8 & 0xff);\ >- *d = (uint8_t)(ip & 0xff);\ >- } while (0) >- > #define DEFAULT_MAX_CATEGORIES 1 > > #define INVALID_SPI (0) >diff --git a/examples/ipsec-secgw/meson.build b/examples/ipsec- >secgw/meson.build >index f9ba2a2..d0373da 100644 >--- a/examples/ipsec-secgw/meson.build >+++ b/examples/ipsec-secgw/meson.build >@@ -9,6 +9,17 @@ > deps += ['security', 'lpm', 'acl', 'hash', 'ip_frag', 'ipsec', 'eventdev'] >allow_experimental_apis = true sources = files( >- 'esp.c', 'event_helper.c', 'ipsec.c', 'ipsec_process.c', >'ipsec-secgw.c', >- 'ipsec_worker.c', 'parser.c', 'rt.c', 'sa.c', 'sad.c', 'sp4.c', 'sp6.c' >+ 'esp.c', >+ 'event_helper.c', >+ 'flow.c', >+ 'ipsec.c', >+ 'ipsec_process.c', >+ 'ipsec-secgw.c', >+ 'ipsec_worker.c', >+ 'parser.c', >+ 'rt.c', >+ 'sa.c', >+ 'sad.c', >+ 'sp4.c', >+ 'sp6.c' > ) >diff --git a/examples/ipsec-secgw/parser.c b/examples/ipsec-secgw/parser.c >index 65eb7e9..8f66660 100644 >--- a/examples/ipsec-secgw/parser.c >+++ b/examples/ipsec-secgw/parser.c >@@ -11,6 +11,7 @@ > #include <cmdline_socket.h> > #include <cmdline.h> > >+#include "flow.h" > #include "ipsec.h" > #include "parser.h" > >@@ -484,6 +485,49 @@ struct cfg_rt_add_cfg_item { > }, > }; > >+/* flow add parse */ >+struct cfg_flow_add_cfg_item { >+ cmdline_fixed_string_t flow_keyword; >+ cmdline_multi_string_t multi_string; >+}; >+ >+static void >+cfg_flow_add_cfg_item_parsed(void *parsed_result, >+ __rte_unused struct cmdline *cl, void *data) { >+ struct cfg_flow_add_cfg_item *params = parsed_result; >+ char *tokens[32]; >+ uint32_t n_tokens = RTE_DIM(tokens); >+ struct parse_status *status = (struct parse_status *)data; >+ >+ APP_CHECK(parse_tokenize_string( >+ params->multi_string, tokens, &n_tokens) == 0, >+ status, "too many arguments\n"); >+ if (status->status < 0) >+ return; >+ >+ parse_flow_tokens(tokens, n_tokens, status); } >+ >+static cmdline_parse_token_string_t cfg_flow_add_flow_str = >+ TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, >+ flow_keyword, "flow"); >+ >+static cmdline_parse_token_string_t cfg_flow_add_multi_str = >+ TOKEN_STRING_INITIALIZER(struct cfg_flow_add_cfg_item, >multi_string, >+ TOKEN_STRING_MULTI); >+ >+cmdline_parse_inst_t cfg_flow_add_rule = { >+ .f = cfg_flow_add_cfg_item_parsed, >+ .data = NULL, >+ .help_str = "", >+ .tokens = { >+ (void *) &cfg_flow_add_flow_str, >+ (void *) &cfg_flow_add_multi_str, >+ NULL, >+ }, >+}; >+ > /* neigh add parse */ > struct cfg_neigh_add_item { > cmdline_fixed_string_t neigh; >@@ -538,6 +582,7 @@ struct cfg_neigh_add_item { > (cmdline_parse_inst_t *)&cfg_sp_add_rule, > (cmdline_parse_inst_t *)&cfg_sa_add_rule, > (cmdline_parse_inst_t *)&cfg_rt_add_rule, >+ (cmdline_parse_inst_t *)&cfg_flow_add_rule, > (cmdline_parse_inst_t *)&cfg_neigh_add_rule, > NULL, > }; >@@ -564,6 +609,7 @@ struct cfg_neigh_add_item { > cfg_sp_add_rule.data = &status; > cfg_sa_add_rule.data = &status; > cfg_rt_add_rule.data = &status; >+ cfg_flow_add_rule.data = &status; > cfg_neigh_add_rule.data = &status; > > do { >diff --git a/examples/ipsec-secgw/parser.h b/examples/ipsec-secgw/parser.h >index 6e764fe..a0ff7e1 100644 >--- a/examples/ipsec-secgw/parser.h >+++ b/examples/ipsec-secgw/parser.h >@@ -2,12 +2,13 @@ > * Copyright(c) 2016 Intel Corporation > */ > >+#ifndef __PARSER_H >+#define __PARSER_H >+ > #include <sys/types.h> > #include <netinet/in.h> > #include <netinet/ip.h> >- >-#ifndef __PARSER_H >-#define __PARSER_H >+#include <string.h> > > struct parse_status { > int status; >-- >1.9.3