DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 00/12] add packet generator library and example app
@ 2021-12-14 14:12 Ronan Randles
  2021-12-14 14:12 ` [PATCH 01/12] net: add string to IPv4 parse function Ronan Randles
                   ` (15 more replies)
  0 siblings, 16 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

This patchset introduces a Gen library for DPDK. This library provides an easy
way to generate traffic in order to test software based network components.

This library enables the basic functionality required in the traffic generator.
This includes: raw data setting, packet Tx and Rx, creation and destruction of a
 Gen instance and various types of data parsing.
This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
library.

A sample app is included in "examples/generator" which shows the use of the gen
library in making a traffic generator. This can be used to generate traffic by
running the dpdk-generator generator executable. This sample app supports
runtime stats reporting (/gen/stats) and line rate limiting
(/gen/mpps,<target traffic rate in mpps>) through telemetry.py.

As more features are added to the gen library, the sample application will
become more powerful through the "/gen/packet" string parameter
(currently supports IP and Ether address setting). This will allow every
application to generate more complex traffic types in the future without
changing API.

Harry van Haaren (6):
  gen: add files for initial traffic generation library
  gen: add basic Rx and Tx routines and tests
  gen: add raw packet data API and tests
  gen: add parsing infrastructure and Ether protocol
  gen: add gen IP parsing
  examples/generator: import code from basicfwd.c

Ronan Randles (6):
  net: add string to IPv4 parse function
  net: add function to pretty print IPv4
  examples/generator: enable gen library for traffic gen
  examples/generator: telemetry support
  examples/generator: link status check added
  examples/generator: line rate limiting

 app/test/meson.build           |   4 +
 app/test/test_gen.c            | 184 +++++++++++
 app/test/test_net.c            |  87 ++++++
 doc/api/doxy-api-index.md      |   3 +-
 doc/api/doxy-api.conf.in       |   1 +
 examples/generator/main.c      | 483 ++++++++++++++++++++++++++++
 examples/generator/meson.build |  13 +
 examples/meson.build           |   1 +
 lib/gen/meson.build            |   6 +
 lib/gen/rte_gen.c              | 553 +++++++++++++++++++++++++++++++++
 lib/gen/rte_gen.h              | 114 +++++++
 lib/gen/version.map            |  10 +
 lib/meson.build                |   1 +
 lib/net/meson.build            |   1 +
 lib/net/rte_ip.c               |  58 ++++
 lib/net/rte_ip.h               |  38 +++
 lib/net/version.map            |   9 +
 17 files changed, 1565 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_gen.c
 create mode 100644 app/test/test_net.c
 create mode 100644 examples/generator/main.c
 create mode 100644 examples/generator/meson.build
 create mode 100644 lib/gen/meson.build
 create mode 100644 lib/gen/rte_gen.c
 create mode 100644 lib/gen/rte_gen.h
 create mode 100644 lib/gen/version.map
 create mode 100644 lib/net/rte_ip.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 01/12] net: add string to IPv4 parse function
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 17:31   ` Morten Brørup
  2022-01-19 14:20   ` Thomas Monjalon
  2021-12-14 14:12 ` [PATCH 02/12] net: add function to pretty print IPv4 Ronan Randles
                   ` (14 subsequent siblings)
  15 siblings, 2 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

Added function that accepts ip string as a parameter and returns an ip
address represented by a uint32_t. Relevant unit test for this function
is also included.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/meson.build |  2 ++
 app/test/test_net.c  | 61 ++++++++++++++++++++++++++++++++++++++++++++
 lib/net/meson.build  |  1 +
 lib/net/rte_ip.c     | 43 +++++++++++++++++++++++++++++++
 lib/net/rte_ip.h     | 18 +++++++++++++
 lib/net/version.map  |  8 ++++++
 6 files changed, 133 insertions(+)
 create mode 100644 app/test/test_net.c
 create mode 100644 lib/net/rte_ip.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 2b480adfba..4cf540fc74 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -100,6 +100,7 @@ test_sources = files(
         'test_meter.c',
         'test_mcslock.c',
         'test_mp_secondary.c',
+        'test_net.c',
         'test_per_lcore.c',
         'test_pflock.c',
         'test_pmd_perf.c',
@@ -177,6 +178,7 @@ test_deps = [
         'ipsec',
         'lpm',
         'member',
+        'net',
         'node',
         'pipeline',
         'port',
diff --git a/app/test/test_net.c b/app/test/test_net.c
new file mode 100644
index 0000000000..2cb7d3e1c9
--- /dev/null
+++ b/app/test/test_net.c
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <rte_ip.h>
+#include <rte_common.h>
+#include "test.h"
+
+static int
+test_rte_ip_parse_addr(void)
+{
+	printf("Running IP parsing tests...\n");
+
+	struct str_ip_t {
+		const char *str;
+		uint32_t exp_output;
+		uint32_t expected_to_fail;
+	} str_ip_tests[] = {
+		{ .str = "1.2.3.4", .exp_output = RTE_IPV4(1, 2, 3, 4)},
+		{ .str = "192.168.255.255", .exp_output =
+				RTE_IPV4(192, 168, 255, 255)},
+		{ .str = "172.16.0.9", .exp_output =
+				RTE_IPV4(172, 16, 0, 9)},
+		{ .str = "1.2.3", .expected_to_fail = 1},
+		{ .str = "1.2.3.4.5", .expected_to_fail = 1},
+		{ .str = "fail.1.2.3", .expected_to_fail = 1},
+		{ .str = "", .expected_to_fail = 1},
+		{ .str = "1.2.3.fail", .expected_to_fail = 1}
+	};
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(str_ip_tests); i++) {
+		uint32_t test_addr;
+		int32_t err = rte_ip_parse_addr(str_ip_tests[i].str,
+							&test_addr);
+		if (!test_addr) {
+			if (str_ip_tests[i].expected_to_fail != 1)
+				return -1;
+		}
+
+		if (err || test_addr != str_ip_tests[i].exp_output) {
+			if (str_ip_tests[i].expected_to_fail != 1)
+				return -1;
+		}
+	}
+
+
+	return 0;
+}
+
+static int
+test_net_tests(void)
+{
+	int ret = test_rte_ip_parse_addr();
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(net_autotest, test_net_tests);
diff --git a/lib/net/meson.build b/lib/net/meson.build
index e899846578..b2577a7592 100644
--- a/lib/net/meson.build
+++ b/lib/net/meson.build
@@ -26,6 +26,7 @@ headers = files(
 sources = files(
         'rte_arp.c',
         'rte_ether.c',
+        'rte_ip.c',
         'rte_net.c',
         'rte_net_crc.c',
 )
diff --git a/lib/net/rte_ip.c b/lib/net/rte_ip.c
new file mode 100644
index 0000000000..b859dfb640
--- /dev/null
+++ b/lib/net/rte_ip.c
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <string.h>
+#include <rte_ip.h>
+
+int32_t
+rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr)
+{
+	int32_t ret = 0;
+	char *current_position;
+
+	if (src_ip == NULL)
+		return -1;
+
+	char *tok = strdup(src_ip);
+	if (tok == NULL)
+		return -1;
+
+	char *current_digit = strtok_r(tok, ".", &current_position);
+
+	*output_addr = 0;
+	uint32_t i = 0;
+	while (current_digit) {
+		uint32_t shift = ((3 - i) * 8);
+		unsigned long parsed_value = strtoul(current_digit, NULL, 0)
+								<< shift;
+
+		if (parsed_value == 0 && strcmp(current_digit, "0"))
+			break;
+
+		*output_addr |= parsed_value;
+		current_digit = strtok_r(NULL, ".", &current_position);
+		i++;
+
+	}
+	if (i != 4)
+		return -1;
+
+	free(tok);
+	return ret;
+}
diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
index c575250852..188054fda4 100644
--- a/lib/net/rte_ip.h
+++ b/lib/net/rte_ip.h
@@ -426,6 +426,24 @@ rte_ipv4_udptcp_cksum_verify(const struct rte_ipv4_hdr *ipv4_hdr,
 	return 0;
 }
 
+/**
+ * IP address parser.
+ *
+ * @param src_ip
+ *   The IP address to be parsed.
+ * @param output_addr
+ *   The array in which the parsed digits will be saved.
+ *
+ * @retval 0
+ *   Success.
+ * @retval -1
+ *   Failure due to invalid input arguments.
+ */
+
+__rte_experimental
+int32_t
+rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
+
 /**
  * IPv6 Header
  */
diff --git a/lib/net/version.map b/lib/net/version.map
index 4f4330d1c4..3e5a307e48 100644
--- a/lib/net/version.map
+++ b/lib/net/version.map
@@ -12,3 +12,11 @@ DPDK_22 {
 
 	local: *;
 };
+
+EXPERIMENTAL {
+	global:
+
+	rte_net_skip_ip6_ext;
+	rte_ether_unformat_addr;
+	rte_ip_parse_addr;
+};
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
  2021-12-14 14:12 ` [PATCH 01/12] net: add string to IPv4 parse function Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 16:08   ` Stephen Hemminger
  2021-12-14 17:31   ` Morten Brørup
  2021-12-14 14:12 ` [PATCH 03/12] gen: add files for initial traffic generation library Ronan Randles
                   ` (13 subsequent siblings)
  15 siblings, 2 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

This function accepts an uint32_t representation of an IP address and
produces a string representation stored in a char * buffer. Realavent
unit tests also included.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_net.c | 26 ++++++++++++++++++++++++++
 lib/net/rte_ip.c    | 15 +++++++++++++++
 lib/net/rte_ip.h    | 20 ++++++++++++++++++++
 lib/net/version.map |  1 +
 4 files changed, 62 insertions(+)

diff --git a/app/test/test_net.c b/app/test/test_net.c
index 2cb7d3e1c9..75beeea671 100644
--- a/app/test/test_net.c
+++ b/app/test/test_net.c
@@ -46,7 +46,32 @@ test_rte_ip_parse_addr(void)
 				return -1;
 		}
 	}
+	return 0;
+}
+
+static int
+test_rte_ip_print_addr(void)
+{
+	printf("Running IP printing tests...\n");
+	char buffer[128];
 
+	struct ip_str_t {
+		uint32_t ip_addr;
+		const char *exp_output;
+	} ip_str_tests[] = {
+		{ .ip_addr = 16909060, .exp_output = "1.2.3.4"},
+		{ .ip_addr = 3232301055, . exp_output = "192.168.255.255"},
+		{ .ip_addr = 2886729737, .exp_output = "172.16.0.9"}
+	};
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(ip_str_tests); i++) {
+		int32_t err = rte_ip_print_addr(ip_str_tests[i].ip_addr,
+								buffer, 128);
+
+		if (err || strcmp(buffer, ip_str_tests[i].exp_output))
+			return -1;
+	}
 
 	return 0;
 }
@@ -55,6 +80,7 @@ static int
 test_net_tests(void)
 {
 	int ret = test_rte_ip_parse_addr();
+	ret += test_rte_ip_print_addr();
 	return ret;
 }
 
diff --git a/lib/net/rte_ip.c b/lib/net/rte_ip.c
index b859dfb640..fbd9161317 100644
--- a/lib/net/rte_ip.c
+++ b/lib/net/rte_ip.c
@@ -41,3 +41,18 @@ rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr)
 	free(tok);
 	return ret;
 }
+
+int32_t
+rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t buffer_size)
+{
+	if (buffer == NULL)
+		return -1;
+
+	snprintf(buffer, buffer_size, "%u.%u.%u.%u",
+		(ip_addr >> 24),
+		(ip_addr >> 16) & UINT8_MAX,
+		(ip_addr >> 8) & UINT8_MAX,
+		 ip_addr & UINT8_MAX);
+
+	return 0;
+}
diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
index 188054fda4..e46f0b41ba 100644
--- a/lib/net/rte_ip.h
+++ b/lib/net/rte_ip.h
@@ -444,6 +444,26 @@ __rte_experimental
 int32_t
 rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
 
+
+/**
+ * Print IP address from 32 bit int into char * buffer.
+ *
+ * @param ip_addr
+ *   ip address to be printed.
+ * @param buffer
+ *   The buffer the string will be saved into.
+ * @param buffer_size
+ *   size of buffer to be used.
+ *
+ * @retval 0
+ *   Success.
+ * @retval -1
+ *   Failure due to invalid input arguments.
+ */
+__rte_experimental
+int32_t
+rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t buffer_size);
+
 /**
  * IPv6 Header
  */
diff --git a/lib/net/version.map b/lib/net/version.map
index 3e5a307e48..25028f28ff 100644
--- a/lib/net/version.map
+++ b/lib/net/version.map
@@ -19,4 +19,5 @@ EXPERIMENTAL {
 	rte_net_skip_ip6_ext;
 	rte_ether_unformat_addr;
 	rte_ip_parse_addr;
+	rte_ip_print_addr;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 03/12] gen: add files for initial traffic generation library
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
  2021-12-14 14:12 ` [PATCH 01/12] net: add string to IPv4 parse function Ronan Randles
  2021-12-14 14:12 ` [PATCH 02/12] net: add function to pretty print IPv4 Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 04/12] gen: add basic Rx and Tx routines and tests Ronan Randles
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds empty files to the DPDK build for a traffic
generation library, including the bare create and destroy functions.
Unit testing infrastructure is added for the create function.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 app/test/meson.build      |  2 ++
 app/test/test_gen.c       | 55 +++++++++++++++++++++++++++++++++++++++
 doc/api/doxy-api-index.md |  3 ++-
 doc/api/doxy-api.conf.in  |  1 +
 lib/gen/meson.build       |  5 ++++
 lib/gen/rte_gen.c         | 33 +++++++++++++++++++++++
 lib/gen/rte_gen.h         | 44 +++++++++++++++++++++++++++++++
 lib/gen/version.map       |  6 +++++
 lib/meson.build           |  1 +
 9 files changed, 149 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_gen.c
 create mode 100644 lib/gen/meson.build
 create mode 100644 lib/gen/rte_gen.c
 create mode 100644 lib/gen/rte_gen.h
 create mode 100644 lib/gen/version.map

diff --git a/app/test/meson.build b/app/test/meson.build
index 4cf540fc74..0e4f3b810d 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -66,6 +66,7 @@ test_sources = files(
         'test_fib6_perf.c',
         'test_func_reentrancy.c',
         'test_flow_classify.c',
+        'test_gen.c',
         'test_graph.c',
         'test_graph_perf.c',
         'test_hash.c',
@@ -173,6 +174,7 @@ test_deps = [
         'eventdev',
         'fib',
         'flow_classify',
+        'gen',
         'graph',
         'hash',
         'ipsec',
diff --git a/app/test/test_gen.c b/app/test/test_gen.c
new file mode 100644
index 0000000000..f53f4a6608
--- /dev/null
+++ b/app/test/test_gen.c
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <rte_common.h>
+#include <rte_gen.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static struct rte_mempool *mp;
+
+static int
+testsuite_setup(void)
+{
+	if (!mp) {
+		mp = rte_pktmbuf_pool_create("test_gen_mp", 8192, 256, 0, 2048,
+						SOCKET_ID_ANY);
+	}
+	return mp ? TEST_SUCCESS : TEST_FAILED;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_mempool_free(mp);
+}
+
+static int
+test_gen_create(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+static struct unit_test_suite gen_suite  = {
+	.suite_name = "gen: packet generator unit test suite",
+	.setup = testsuite_setup,
+	.teardown = testsuite_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(NULL, NULL, test_gen_create),
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_gen_suite(void)
+{
+	return unit_test_suite_runner(&gen_suite);
+}
+
+REGISTER_TEST_COMMAND(gen_autotest, test_gen_suite);
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 4245b9635c..f7ddadd21a 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -220,7 +220,8 @@ The public API headers are grouped by topics:
   [log]                (@ref rte_log.h),
   [errno]              (@ref rte_errno.h),
   [trace]              (@ref rte_trace.h),
-  [trace_point]        (@ref rte_trace_point.h)
+  [trace_point]        (@ref rte_trace_point.h),
+  [gen]                (@ref rte_gen.h)
 
 - **misc**:
   [EAL config]         (@ref rte_eal.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index db2ca9b6ed..6344e949d9 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -41,6 +41,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/eventdev \
                           @TOPDIR@/lib/fib \
                           @TOPDIR@/lib/flow_classify \
+                          @TOPDIR@/lib/gen \
                           @TOPDIR@/lib/gpudev \
                           @TOPDIR@/lib/graph \
                           @TOPDIR@/lib/gro \
diff --git a/lib/gen/meson.build b/lib/gen/meson.build
new file mode 100644
index 0000000000..3c5d854645
--- /dev/null
+++ b/lib/gen/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2021 Intel Corporation
+
+sources = files('rte_gen.c')
+headers = files('rte_gen.h')
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
new file mode 100644
index 0000000000..d993772422
--- /dev/null
+++ b/lib/gen/rte_gen.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include "rte_gen.h"
+
+#include <rte_malloc.h>
+
+/** Structure that represents a traffic generator. */
+struct rte_gen {
+	/* Mempool that buffers are retrieved from. */
+	struct rte_mempool *mp;
+};
+
+/* Allocate and initialize a traffic generator instance. */
+struct rte_gen *
+rte_gen_create(struct rte_mempool *mempool)
+{
+	struct rte_gen *gen = rte_zmalloc(NULL, sizeof(*gen), 0);
+	if (gen == NULL)
+		return NULL;
+
+	gen->mp = mempool;
+
+	return gen;
+}
+
+/* Free a traffic generator instance. */
+void
+rte_gen_destroy(struct rte_gen *gen)
+{
+	rte_free(gen);
+}
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
new file mode 100644
index 0000000000..5b30430f9e
--- /dev/null
+++ b/lib/gen/rte_gen.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#ifndef _RTE_GEN_H_
+#define _RTE_GEN_H_
+
+/**
+ * @file
+ * RTE gen
+ *
+ * A library for the generation of packets, to allow easy generation
+ * of various flows of packets.
+ */
+
+#include <stdint.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** Structure that represents a logical traffic generator. */
+struct rte_gen;
+
+/* Forward declarations for DPDK componeents. */
+struct rte_mempool;
+
+/* Allocate and initialize a traffic generator instance. */
+__rte_experimental
+struct rte_gen *
+rte_gen_create(struct rte_mempool *mempool);
+
+/* Free a traffic generator instance. */
+__rte_experimental
+void
+rte_gen_destroy(struct rte_gen *gen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GEN_H_ */
diff --git a/lib/gen/version.map b/lib/gen/version.map
new file mode 100644
index 0000000000..d8a26eb53a
--- /dev/null
+++ b/lib/gen/version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_gen_create;
+	rte_gen_destroy;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 018976df17..5b28cd3a89 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -35,6 +35,7 @@ libraries = [
         'efd',
         'eventdev',
         'gpudev',
+        'gen',
         'gro',
         'gso',
         'ip_frag',
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 04/12] gen: add basic Rx and Tx routines and tests
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (2 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 03/12] gen: add files for initial traffic generation library Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 05/12] gen: add raw packet data API " Ronan Randles
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit introduces functions for basic mbuf rx and tx.
This commit also contains a unit test that calls rte_gen_rx_burst
and rte_gen_tx_burst to send bursts of 32 packets repeatedly in
order to verify their functionality

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_gen.c | 46 ++++++++++++++++++++++++++++++++++++++++++
 lib/gen/meson.build |  1 +
 lib/gen/rte_gen.c   | 43 +++++++++++++++++++++++++++++++++++++++
 lib/gen/rte_gen.h   | 49 +++++++++++++++++++++++++++++++++++++++++++++
 lib/gen/version.map |  2 ++
 5 files changed, 141 insertions(+)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index f53f4a6608..ceacd8c7c4 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -8,6 +8,8 @@
 
 #include "test.h"
 
+#define BURST_MAX 32
+
 static struct rte_mempool *mp;
 
 static int
@@ -36,12 +38,56 @@ test_gen_create(void)
 	return 0;
 }
 
+static int
+test_gen_basic_rxtx(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	struct rte_mbuf *bufs[BURST_MAX];
+	uint16_t nb_rx = rte_gen_rx_burst(gen, bufs, BURST_MAX);
+	TEST_ASSERT_EQUAL(nb_rx, BURST_MAX, "Expected rx packet burst.");
+
+	uint64_t latency[BURST_MAX];
+	uint16_t nb_tx = rte_gen_tx_burst(gen, bufs, latency, BURST_MAX);
+	TEST_ASSERT_EQUAL(nb_tx, BURST_MAX, "Expected tx packet burst.");
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+static int
+test_gen_loop_rxtx(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	uint32_t total_sent = 0;
+
+	while (total_sent < 1000000) {
+		struct rte_mbuf *bufs[BURST_MAX];
+		uint16_t nb_rx = rte_gen_rx_burst(gen, bufs, BURST_MAX);
+		TEST_ASSERT_EQUAL(nb_rx, BURST_MAX, "Expected rx packet burst.");
+
+		uint64_t latency[BURST_MAX];
+		uint16_t nb_tx = rte_gen_tx_burst(gen, bufs, latency, nb_rx);
+		TEST_ASSERT_EQUAL(nb_tx, BURST_MAX, "Expected tx packet burst.");
+
+		total_sent += nb_tx;
+	}
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
 static struct unit_test_suite gen_suite  = {
 	.suite_name = "gen: packet generator unit test suite",
 	.setup = testsuite_setup,
 	.teardown = testsuite_teardown,
 	.unit_test_cases = {
 		TEST_CASE_ST(NULL, NULL, test_gen_create),
+		TEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),
+		TEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/gen/meson.build b/lib/gen/meson.build
index 3c5d854645..753984cbba 100644
--- a/lib/gen/meson.build
+++ b/lib/gen/meson.build
@@ -3,3 +3,4 @@
 
 sources = files('rte_gen.c')
 headers = files('rte_gen.h')
+deps += ['mempool', 'mbuf']
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index d993772422..f0ad57fa81 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -4,8 +4,11 @@
 
 #include "rte_gen.h"
 
+#include <rte_mbuf.h>
 #include <rte_malloc.h>
 
+#define GEN_MAX_BURST 32
+
 /** Structure that represents a traffic generator. */
 struct rte_gen {
 	/* Mempool that buffers are retrieved from. */
@@ -31,3 +34,43 @@ rte_gen_destroy(struct rte_gen *gen)
 {
 	rte_free(gen);
 }
+
+uint16_t
+rte_gen_rx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **rx_pkts,
+		 const uint16_t nb_pkts)
+{
+	/* Get a bulk of nb_pkts from the mempool. */
+	int err = rte_mempool_get_bulk(gen->mp, (void **)rx_pkts, nb_pkts);
+	if (err)
+		return 0;
+
+	const uint32_t pkt_len = 64;
+
+	uint32_t i;
+	for (i = 0; i < nb_pkts; i++) {
+		struct rte_mbuf *m = rx_pkts[i];
+		uint8_t *pkt_data = rte_pktmbuf_mtod(m, uint8_t *);
+
+		memset(pkt_data, 0, pkt_len);
+
+		m->pkt_len  = pkt_len;
+		m->data_len = pkt_len;
+	}
+
+	return nb_pkts;
+}
+
+uint16_t
+rte_gen_tx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **tx_pkts,
+		 uint64_t *pkt_latencies,
+		 const uint16_t nb_pkts)
+{
+	RTE_SET_USED(gen);
+	RTE_SET_USED(pkt_latencies);
+
+	rte_pktmbuf_free_bulk(tx_pkts, nb_pkts);
+
+	return nb_pkts;
+}
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
index 5b30430f9e..09ee1e8872 100644
--- a/lib/gen/rte_gen.h
+++ b/lib/gen/rte_gen.h
@@ -25,6 +25,7 @@ extern "C" {
 struct rte_gen;
 
 /* Forward declarations for DPDK componeents. */
+struct rte_mbuf;
 struct rte_mempool;
 
 /* Allocate and initialize a traffic generator instance. */
@@ -37,6 +38,54 @@ __rte_experimental
 void
 rte_gen_destroy(struct rte_gen *gen);
 
+/**
+ * Call to receive a burst of generated packets
+ *
+ * @param gen
+ *   Gen instance to be used.
+ * @param rx_pkts
+ *   mbuf where packets will be generated.
+ * @param nb_pkts
+ *   number of packets to be generated
+ *
+ * @retval nb_pkts
+ *   On success the number of rx'ed packets will be returned
+ * @retval 0
+ *   Failure.
+ */
+__rte_experimental
+uint16_t
+rte_gen_rx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **rx_pkts,
+		 const uint16_t nb_pkts);
+
+/** Call to transmit a burst of traffic back to the generator.
+ * This allows the generator to calculate stats/properties of the stream.
+ *
+ * If the pkt_latencies parameter is not NULL, it is expected to be a pointer
+ * to an array of uint64_t values that has nb_pkts in length. Each individual
+ * packet latency will be stored to the array.
+ *
+ * @param gen
+ *   Gen instance to be used.
+ * @param tx_pkts
+ *   mbuf to be used to tx packets
+ * @param pkt_latencies
+ *   Array to store latencies of sent packets
+ * @param nb_pkts
+ *   The number of packets to be tx'ed
+ *
+ * @retval nb_pkts
+ *   On success the number of packets tx'ed is returned
+ */
+__rte_experimental
+uint16_t
+rte_gen_tx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **tx_pkts,
+		 uint64_t *pkt_latencies,
+		 const uint16_t nb_pkts);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/gen/version.map b/lib/gen/version.map
index d8a26eb53a..bdd25add6f 100644
--- a/lib/gen/version.map
+++ b/lib/gen/version.map
@@ -3,4 +3,6 @@ EXPERIMENTAL {
 
 	rte_gen_create;
 	rte_gen_destroy;
+	rte_gen_rx_burst;
+	rte_gen_tx_burst;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (3 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 04/12] gen: add basic Rx and Tx routines and tests Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-15 12:40   ` Jerin Jacob
  2021-12-14 14:12 ` [PATCH 06/12] gen: add parsing infrastructure and Ether protocol Ronan Randles
                   ` (10 subsequent siblings)
  15 siblings, 1 reply; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds a new API to gen, allowing the caller to set
raw packet data with a size. Tests are added to test the new
API with randomized packet data.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 app/test/test_gen.c | 33 ++++++++++++++++++++++++
 lib/gen/rte_gen.c   | 62 +++++++++++++++++++++++++++++++++++++++++----
 lib/gen/rte_gen.h   |  9 +++++++
 lib/gen/version.map |  1 +
 4 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index ceacd8c7c4..b60ceaef8a 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -5,6 +5,7 @@
 #include <rte_common.h>
 #include <rte_gen.h>
 #include <rte_mbuf.h>
+#include <rte_random.h>
 
 #include "test.h"
 
@@ -75,6 +76,37 @@ test_gen_loop_rxtx(void)
 
 		total_sent += nb_tx;
 	}
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+static int
+test_gen_packet_set_raw(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	/* Set a raw packet payload, and ensure the next received packet has
+	 * that packet data as contents and size.
+	 */
+	uint64_t pkt_data[8];
+	uint32_t i;
+	for (i = 0; i < 8; i++)
+		pkt_data[i] = rte_rand();
+
+	int32_t err = rte_gen_packet_set_raw(gen, (void *)pkt_data, 64);
+	TEST_ASSERT_EQUAL(err, 0, "Expected set raw() to return success.");
+
+	struct rte_mbuf *bufs[BURST_MAX];
+	uint16_t nb_rx = rte_gen_rx_burst(gen, bufs, 1);
+	TEST_ASSERT_EQUAL(nb_rx, 1, "Expected rx packet burst.");
+
+	void *mbuf_data = rte_pktmbuf_mtod(bufs[0], void *);
+	int32_t data_equal = memcmp(pkt_data, mbuf_data, 64) == 0;
+	TEST_ASSERT_EQUAL(data_equal, 1,
+		"Expected packet data equal to input data.");
+
+	rte_pktmbuf_free(bufs[0]);
 
 	rte_gen_destroy(gen);
 	return 0;
@@ -88,6 +120,7 @@ static struct unit_test_suite gen_suite  = {
 		TEST_CASE_ST(NULL, NULL, test_gen_create),
 		TEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),
 		TEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),
+		TEST_CASE_ST(NULL, NULL, test_gen_packet_set_raw),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index f0ad57fa81..432be65f1a 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -6,13 +6,25 @@
 
 #include <rte_mbuf.h>
 #include <rte_malloc.h>
+#include <rte_hexdump.h>
+#include <rte_log.h>
+
+RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
+
+#define TGEN_LOG(level, fmt, args...)				\
+	rte_log(RTE_LOG_ ## level, gen_logtype, "%s(): " fmt,	\
+		__func__, ## args)
 
 #define GEN_MAX_BURST 32
+#define GEN_INIT_PKT_SIZE 64
 
 /** Structure that represents a traffic generator. */
 struct rte_gen {
 	/* Mempool that buffers are retrieved from. */
 	struct rte_mempool *mp;
+
+	/* Packet template to send. */
+	struct rte_mbuf *base_pkt;
 };
 
 /* Allocate and initialize a traffic generator instance. */
@@ -25,6 +37,15 @@ rte_gen_create(struct rte_mempool *mempool)
 
 	gen->mp = mempool;
 
+	uint8_t data[GEN_INIT_PKT_SIZE];
+	memset(data, 0, GEN_INIT_PKT_SIZE);
+	int32_t err = rte_gen_packet_set_raw(gen, data, GEN_INIT_PKT_SIZE);
+	if (err) {
+		TGEN_LOG(ERR, "Failed to set initial packet\n");
+		rte_free(gen);
+		return NULL;
+	}
+
 	return gen;
 }
 
@@ -32,9 +53,37 @@ rte_gen_create(struct rte_mempool *mempool)
 void
 rte_gen_destroy(struct rte_gen *gen)
 {
+	rte_pktmbuf_free(gen->base_pkt);
 	rte_free(gen);
 }
 
+int32_t
+rte_gen_packet_set_raw(struct rte_gen *gen,
+		       const uint8_t *raw_data,
+		       uint32_t raw_data_size)
+{
+
+	struct rte_mbuf *new_pkt = rte_pktmbuf_alloc(gen->mp);
+	if (!new_pkt) {
+		TGEN_LOG(ERR, "Failed to retireve mbuf for parser\n");
+		return -ENOMEM;
+	}
+
+	uint8_t *base_data = rte_pktmbuf_mtod(new_pkt, uint8_t *);
+	new_pkt->pkt_len = raw_data_size;
+	new_pkt->data_len = raw_data_size;
+	rte_memcpy(base_data, raw_data, raw_data_size);
+
+	/* If old packet exists, free it. */
+	struct rte_mbuf *old_pkt = gen->base_pkt;
+	gen->base_pkt = new_pkt;
+
+	if (old_pkt)
+		rte_pktmbuf_free(old_pkt);
+
+	return 0;
+}
+
 uint16_t
 rte_gen_rx_burst(struct rte_gen *gen,
 		 struct rte_mbuf **rx_pkts,
@@ -45,17 +94,20 @@ rte_gen_rx_burst(struct rte_gen *gen,
 	if (err)
 		return 0;
 
-	const uint32_t pkt_len = 64;
+	if (!gen->base_pkt)
+		return 0;
+
+	const uint32_t base_size = gen->base_pkt->pkt_len;
+	const uint8_t *base_data = rte_pktmbuf_mtod(gen->base_pkt, uint8_t *);
 
 	uint32_t i;
 	for (i = 0; i < nb_pkts; i++) {
 		struct rte_mbuf *m = rx_pkts[i];
 		uint8_t *pkt_data = rte_pktmbuf_mtod(m, uint8_t *);
 
-		memset(pkt_data, 0, pkt_len);
-
-		m->pkt_len  = pkt_len;
-		m->data_len = pkt_len;
+		rte_memcpy(pkt_data, base_data, base_size);
+		m->pkt_len = base_size;
+		m->data_len = base_size;
 	}
 
 	return nb_pkts;
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
index 09ee1e8872..c8d85a5b72 100644
--- a/lib/gen/rte_gen.h
+++ b/lib/gen/rte_gen.h
@@ -85,6 +85,15 @@ rte_gen_tx_burst(struct rte_gen *gen,
 		 uint64_t *pkt_latencies,
 		 const uint16_t nb_pkts);
 
+/* Update the packet being sent to the provided raw data.
+ * @retval 0 Success.
+ * @retval -ENOMEM No memory available.
+ */
+int32_t
+__rte_experimental
+rte_gen_packet_set_raw(struct rte_gen *gen,
+		       const uint8_t *raw_data,
+		       uint32_t raw_data_size);
 
 #ifdef __cplusplus
 }
diff --git a/lib/gen/version.map b/lib/gen/version.map
index bdd25add6f..d75e0b4bac 100644
--- a/lib/gen/version.map
+++ b/lib/gen/version.map
@@ -5,4 +5,5 @@ EXPERIMENTAL {
 	rte_gen_destroy;
 	rte_gen_rx_burst;
 	rte_gen_tx_burst;
+	rte_gen_packet_set_raw;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 06/12] gen: add parsing infrastructure and Ether protocol
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (4 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 05/12] gen: add raw packet data API " Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 07/12] gen: add gen IP parsing Ronan Randles
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds parsing infrastructure and support
for Ether parsing. Appropriate unit tests are also added

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 app/test/test_gen.c |  29 +++++
 lib/gen/meson.build |   2 +-
 lib/gen/rte_gen.c   | 281 ++++++++++++++++++++++++++++++++++++++++++++
 lib/gen/rte_gen.h   |  14 ++-
 lib/gen/version.map |   1 +
 5 files changed, 325 insertions(+), 2 deletions(-)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index b60ceaef8a..324582d0a5 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -112,6 +112,34 @@ test_gen_packet_set_raw(void)
 	return 0;
 }
 
+static int
+test_gen_packet_parse_string(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	struct str_parse_t {
+		const char *str;
+	} pkt_strings[] = {
+		{ .str = "Ether()"},
+		{ .str = "Ether()/"},
+		{ .str = "/Ether()"},
+		{ .str = "/Ether()/"}
+	};
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(pkt_strings); i++) {
+		const char *pkt_str = pkt_strings[i].str;
+		int32_t err = rte_gen_packet_parse_string(gen, pkt_str, NULL);
+		TEST_ASSERT_EQUAL(err, 0, "Expected string %s to parse.",
+				pkt_str);
+	}
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+
 static struct unit_test_suite gen_suite  = {
 	.suite_name = "gen: packet generator unit test suite",
 	.setup = testsuite_setup,
@@ -121,6 +149,7 @@ static struct unit_test_suite gen_suite  = {
 		TEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),
 		TEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),
 		TEST_CASE_ST(NULL, NULL, test_gen_packet_set_raw),
+		TEST_CASE_ST(NULL, NULL, test_gen_packet_parse_string),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/gen/meson.build b/lib/gen/meson.build
index 753984cbba..b3a55564f4 100644
--- a/lib/gen/meson.build
+++ b/lib/gen/meson.build
@@ -3,4 +3,4 @@
 
 sources = files('rte_gen.c')
 headers = files('rte_gen.h')
-deps += ['mempool', 'mbuf']
+deps += ['mempool', 'mbuf', 'net']
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index 432be65f1a..ab73120791 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -9,12 +9,18 @@
 #include <rte_hexdump.h>
 #include <rte_log.h>
 
+#include <rte_ether.h>
+
 RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
 
 #define TGEN_LOG(level, fmt, args...)				\
 	rte_log(RTE_LOG_ ## level, gen_logtype, "%s(): " fmt,	\
 		__func__, ## args)
 
+/* Don't prefix with function name, breaks the Scapy style formatting. */
+#define TGEN_LOG_PROTOCOL(level, fmt, args...)			\
+	rte_log(RTE_LOG_ ## level, gen_logtype, fmt, ## args)
+
 #define GEN_MAX_BURST 32
 #define GEN_INIT_PKT_SIZE 64
 
@@ -126,3 +132,278 @@ rte_gen_tx_burst(struct rte_gen *gen,
 
 	return nb_pkts;
 }
+
+enum GEN_PROTO {
+	GEN_PROTO_INVALID,
+	GEN_PROTO_ETHER,
+
+	/* Must be last. */
+	GEN_PROTO_COUNT,
+};
+
+typedef void (*gen_log_func)(void *data, const char *indent);
+
+/* Structure for holding offset and function pointers for protocol. */
+struct protocol_meta {
+	/* Byte offset into packet where this protocol starts. */
+	uint32_t offset;
+	/* Function to call to log the packet's information. */
+	gen_log_func log_func;
+};
+
+/* Allow up to 32 nexted '/' characters in the protocol string. */
+#define GEN_PROTO_PARSE_MAX 16
+
+/* Structure to hold state required while parsing. */
+struct gen_parser {
+	/* Mbuf the parsed data is being put into. */
+	struct rte_mbuf *mbuf;
+	uint8_t *mbuf_data;
+
+	/* Offset into the packet data to parse to next. */
+	uint32_t buf_write_offset;
+
+	/* Parsing state. */
+	uint8_t parse_iter;
+	char indent_str[(GEN_PROTO_PARSE_MAX * 2) + 1];
+
+	/* String being parsed. */
+	char *parse_string;
+	char *parse_strtok_save_ptr;
+
+	/* Store metadata for parse/display of protocols.  */
+	struct protocol_meta proto_meta[GEN_PROTO_PARSE_MAX];
+
+	/* Per protocol hit counters. */
+	uint32_t proto_hit_counters[GEN_PROTO_COUNT];
+};
+
+/* Forward declaration of recursive parsing function.
+ * @param inner reports back the inner protocol that was handled. This is often
+ * required for the outer protocol to indicate what the inner protocol is.
+ */
+static int32_t
+gen_parser_parse_next(struct gen_parser *parser, enum GEN_PROTO *inner);
+
+/* Return void pointer to the position in the data buffer to parse into. */
+static inline void *
+gen_parser_get_data_ptr(struct gen_parser *parser)
+{
+	return &parser->mbuf_data[parser->buf_write_offset];
+}
+
+/* Initialize a parser structure. */
+static int32_t
+gen_parser_init(struct gen_parser *parser, struct rte_gen *gen,
+		const char *pkt_string)
+{
+	/* Initialize own memory to zero. */
+	memset(parser, 0, sizeof(*parser));
+
+	/* Duplicate string for tokenizing string. */
+	parser->parse_string = strdup(pkt_string);
+	if (!parser->parse_string)
+		goto error;
+
+	/* Allocate mbuf to parse packet into. */
+	parser->mbuf = rte_pktmbuf_alloc(gen->mp);
+	if (!parser->mbuf)
+		goto error;
+
+	parser->mbuf_data = rte_pktmbuf_mtod(parser->mbuf, uint8_t *);
+
+	return 0;
+
+error:
+	free(parser->parse_string);
+	return -ENOMEM;
+}
+
+static void
+gen_log_ether(void *data, const char *indent)
+{
+	struct rte_ether_hdr *eth = data;
+	char src[64];
+	char dst[64];
+
+	rte_ether_format_addr(src, 64, &eth->src_addr);
+	rte_ether_format_addr(dst, 64, &eth->dst_addr);
+	const char *type_str;
+	switch (rte_be_to_cpu_16(eth->ether_type)) {
+	case RTE_ETHER_TYPE_IPV4:
+		type_str = "IPv4";
+		break;
+	default:
+		type_str = "0x9000";
+		break;
+	};
+	TGEN_LOG_PROTOCOL(DEBUG,
+		"###[ Ethernet ]###\n%sdst= %s\n%ssrc= %s\n%stype= %s\n",
+		indent, dst, indent, src, indent, type_str);
+}
+
+/* Ether(...) string detected, supports parameters:
+ * - dst : Destination MAC in 00:11:22:33:44:55 or 0011:2233:4455 forms.
+ * - src : Source MAC in the same forms.
+ * Note:
+ * - type is set based on the next header
+ */
+static int32_t
+gen_parse_ether(struct gen_parser *parser, char *protocol_str)
+{
+	struct rte_ether_hdr *eth = gen_parser_get_data_ptr(parser);
+
+	char *dst_ptr = strstr(protocol_str, "dst=");
+	if (dst_ptr) {
+		char *dup = strdup(dst_ptr);
+		rte_ether_unformat_addr(&dup[4], &eth->dst_addr);
+		free(dup);
+	} else
+		rte_ether_unformat_addr("ff:ff:ff:ff:ff:ff", &eth->dst_addr);
+
+	char *src_ptr = strstr(protocol_str, "src=");
+	if (src_ptr)
+		rte_ether_unformat_addr(&src_ptr[4], &eth->src_addr);
+	else
+		rte_ether_unformat_addr("00:00:00:00:00:00", &eth->src_addr);
+
+	/* Move up write pointer in packet. */
+	parser->buf_write_offset += sizeof(*eth);
+
+	/* Recurse and handle inner protocol. */
+	enum GEN_PROTO inner;
+	int32_t err = gen_parser_parse_next(parser, &inner);
+	if (err) {
+		TGEN_LOG(ERR, "parser parse next() error %d\n", err);
+		return err;
+	}
+
+	switch (inner) {
+	default:
+		eth->ether_type = rte_cpu_to_be_16(0x9000);
+		break;
+	};
+	return 0;
+}
+
+/* (Name, Function-pointer) pairs for supported parse types */
+typedef int32_t (*gen_parse_func)(struct gen_parser *parser,
+				char *protocol_str);
+
+struct gen_parse_func_t {
+	const char *name;
+	enum GEN_PROTO proto;
+	gen_parse_func parse_func;
+	gen_log_func log_func;
+};
+
+/* Mapping from string to function to parse that protocol. */
+static struct gen_parse_func_t gen_protocols[] = {
+	{
+		.name = "Ether(",
+		.proto = GEN_PROTO_ETHER,
+		.parse_func = gen_parse_ether,
+		.log_func = gen_log_ether,
+	}
+};
+
+/* Function to tokenize and parse each segment of a string.
+ * @param outer indicates the protocol before this one.
+ * @param inner returns the protocol that is parsed here/now.
+ */
+static int32_t
+gen_parser_parse_next(struct gen_parser *parser,
+			enum GEN_PROTO *inner_proto)
+{
+	/* Tokenize the input string based on '/' character. */
+	char *tok_str = (parser->parse_iter == 0) ?
+					parser->parse_string : NULL;
+	parser->parse_string = strtok_r(tok_str, "/",
+					&parser->parse_strtok_save_ptr);
+
+	/* End protocol parsing recursion when parse_string is NULL, or max
+	 * protocol recursion depth is reached.
+	 */
+	if (!parser->parse_string ||
+			parser->parse_iter >= GEN_PROTO_PARSE_MAX) {
+		struct rte_mbuf *mbuf = parser->mbuf;
+		mbuf->data_len = parser->buf_write_offset;
+		mbuf->pkt_len = parser->buf_write_offset;
+		TGEN_LOG(DEBUG, "packet length %d\n", mbuf->pkt_len);
+		return 0;
+	}
+
+	uint32_t i;
+	/* Loop over protocols, and identify the parse function to call. */
+	for (i = 0; i < RTE_DIM(gen_protocols); i++) {
+		const char *proto = gen_protocols[i].name;
+		uint32_t proto_len = strlen(proto);
+		if (strncmp(proto, parser->parse_string, proto_len))
+			continue;
+
+		/* Store the log function pointer to output later. */
+		uint32_t iter = parser->parse_iter;
+		parser->proto_hit_counters[i]++;
+		struct protocol_meta *meta = &parser->proto_meta[iter];
+		meta->log_func = gen_protocols[i].log_func;
+		meta->offset = parser->buf_write_offset;
+
+		/* Handle protocol recursively. */
+		parser->parse_iter++;
+		int err = gen_protocols[i].parse_func(parser,
+							parser->parse_string);
+		*inner_proto = gen_protocols[i].proto;
+
+		return err;
+	}
+
+	TGEN_LOG(ERR, "parser does not understand protocol %s\n",
+		parser->parse_string);
+	return -1;
+}
+
+int32_t
+rte_gen_packet_parse_string(struct rte_gen *gen,
+			    const char *pkt_string,
+			    struct rte_mbuf **old_mbuf_to_user)
+{
+	struct gen_parser parser;
+	int32_t err = gen_parser_init(&parser, gen, pkt_string);
+	if (err) {
+		TGEN_LOG(ERR, "error with parser_init(), %d\n", err);
+		return -1;
+	};
+
+	/* Recursively parse each protocol. */
+	enum GEN_PROTO inner;
+	err = gen_parser_parse_next(&parser, &inner);
+	if (err) {
+		TGEN_LOG(ERR, "Error in parsing packet string. "
+			"Set \"gen\" log level to debug for more info.\n");
+		return -1;
+	}
+
+	uint32_t i;
+	/* Iterate the per protocol stored metadata to log output. */
+	for (i = 0; i < parser.parse_iter; i++) {
+		snprintf(parser.indent_str, 2 + i * 2,
+			"                               " /* 32 spaces. */);
+		void *buf_off = parser.mbuf_data + parser.proto_meta[i].offset;
+		parser.proto_meta[i].log_func(buf_off, parser.indent_str);
+	}
+
+	if (inner != GEN_PROTO_ETHER) {
+		TGEN_LOG(WARNING,
+			"Outer protocol of frame is not Ethernet.\n");
+	}
+
+	/* Free the currently in use mbuf. */
+	if (old_mbuf_to_user)
+		*old_mbuf_to_user = gen->base_pkt;
+	else
+		rte_pktmbuf_free(gen->base_pkt);
+
+	/* TODO: HVH design race-condition above vs rx/tx*/
+	gen->base_pkt = parser.mbuf;
+	return 0;
+}
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
index c8d85a5b72..93b3346436 100644
--- a/lib/gen/rte_gen.h
+++ b/lib/gen/rte_gen.h
@@ -89,12 +89,24 @@ rte_gen_tx_burst(struct rte_gen *gen,
  * @retval 0 Success.
  * @retval -ENOMEM No memory available.
  */
-int32_t
 __rte_experimental
+int32_t
 rte_gen_packet_set_raw(struct rte_gen *gen,
 		       const uint8_t *raw_data,
 		       uint32_t raw_data_size);
 
+/* Parse a string description of a packet.
+ *
+ * The optional out parameter supplies the previously being sent mbuf to
+ * the user to be freed later. If this argument is not provided, then the
+ * mbuf is freed by this function.
+ */
+__rte_experimental
+int32_t
+rte_gen_packet_parse_string(struct rte_gen *gen,
+			    const char *pkt_string,
+			    struct rte_mbuf **old_mbuf_to_user);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/gen/version.map b/lib/gen/version.map
index d75e0b4bac..e335c608ee 100644
--- a/lib/gen/version.map
+++ b/lib/gen/version.map
@@ -6,4 +6,5 @@ EXPERIMENTAL {
 	rte_gen_rx_burst;
 	rte_gen_tx_burst;
 	rte_gen_packet_set_raw;
+	rte_gen_packet_parse_string;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 07/12] gen: add gen IP parsing
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (5 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 06/12] gen: add parsing infrastructure and Ether protocol Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 08/12] examples/generator: import code from basicfwd.c Ronan Randles
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds support for the parsing of "IP(src=...,dst=...)"
strings. Parse string API improvement for app RCU.
Appropriate unit tests also added.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_gen.c |  29 +++++++--
 lib/gen/rte_gen.c   | 146 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 170 insertions(+), 5 deletions(-)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index 324582d0a5..7b67835c80 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -117,26 +117,47 @@ test_gen_packet_parse_string(void)
 {
 	struct rte_gen *gen = rte_gen_create(mp);
 	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
-
 	struct str_parse_t {
 		const char *str;
+		uint32_t expected_to_fail;
 	} pkt_strings[] = {
 		{ .str = "Ether()"},
 		{ .str = "Ether()/"},
 		{ .str = "/Ether()"},
-		{ .str = "/Ether()/"}
+		{ .str = "/Ether()/"},
+		{ .str = "Ether()/IP()"},
+		{ .str = "Ether()/IP(src=1.2.3.4,dst=5.6.7.8)"},
+		{ .str = "Ether()/IP(src=1.2.3.4,dst=192.168.255.255)"},
+		{ .str = "Ether()/IP(dst=172.16.0.9,src=1.2.3.4)"},
+		{ .str = "Ether()/IP(src=1.2.3.4)"},
+		{ .str = "Ether()/IP(srdst=5.6.7.8)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=1.2.3.4,ds=)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=1.2.3.4,dst=)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=,dst=5.6.7.8)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(sr=,dst=5.6.7.8)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=1.2.3.fail,dst=5.6.7.8)",
+					.expected_to_fail = 1},
 	};
 
 	uint32_t i;
 	for (i = 0; i < RTE_DIM(pkt_strings); i++) {
 		const char *pkt_str = pkt_strings[i].str;
 		int32_t err = rte_gen_packet_parse_string(gen, pkt_str, NULL);
-		TEST_ASSERT_EQUAL(err, 0, "Expected string %s to parse.",
-				pkt_str);
+
+		if (err && pkt_strings[i].expected_to_fail != 1) {
+			printf("Expected string %s to parse.", pkt_str);
+			return -1;
+		}
+		/* False pass if reached with no err when e_t_f = 1 */
+		if (!err && pkt_strings[i].expected_to_fail) {
+			printf("False Pass on string: %s\n", pkt_str);
+			return -1;
+		}
 	}
 
 	rte_gen_destroy(gen);
 	return 0;
+
 }
 
 
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index ab73120791..3dac436cff 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -10,6 +10,7 @@
 #include <rte_log.h>
 
 #include <rte_ether.h>
+#include <rte_ip.h>
 
 RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
 
@@ -136,6 +137,7 @@ rte_gen_tx_burst(struct rte_gen *gen,
 enum GEN_PROTO {
 	GEN_PROTO_INVALID,
 	GEN_PROTO_ETHER,
+	GEN_PROTO_IPV4,
 
 	/* Must be last. */
 	GEN_PROTO_COUNT,
@@ -219,6 +221,140 @@ gen_parser_init(struct gen_parser *parser, struct rte_gen *gen,
 	return -ENOMEM;
 }
 
+static void
+gen_log_ipv4(void *data, const char *indent)
+{
+	struct rte_ipv4_hdr *ip = data;
+
+	const char *proto_str;
+	switch (ip->next_proto_id) {
+	case 0:
+		proto_str = "hopopt";
+		break;
+	default:
+		proto_str = "unknown next proto";
+		break;
+	}
+
+	TGEN_LOG_PROTOCOL(DEBUG,
+		"###[ IP ]###\n%sversion = %d\n%sihl = %d\n%stos = %d\n"
+		"%slen = %d\n%sid = %d\n%sflags = 0x%x\n%sfrag = %d\n"
+		"%sttl = %d\n%sproto = %s (%d)\n%schksum 0x%x\n%ssrc = 0x%x\n"
+		"%sdst = 0x%x\n%soptions = %s\n",
+		indent, ip->version_ihl >> 4,
+		indent, ip->version_ihl & RTE_IPV4_HDR_IHL_MASK,
+		indent, ip->type_of_service,
+		indent, rte_be_to_cpu_16(ip->total_length),
+		indent, rte_be_to_cpu_16(ip->packet_id), /* TODO: Scapy ID? */
+		indent, rte_be_to_cpu_16(ip->packet_id), /*TODO: Scapy Flags?*/
+		indent, rte_be_to_cpu_16(ip->fragment_offset),
+		indent, ip->time_to_live,
+		indent, proto_str, ip->next_proto_id,
+		indent, rte_be_to_cpu_16(ip->hdr_checksum),
+		indent, rte_be_to_cpu_32(ip->src_addr),
+		indent, rte_be_to_cpu_32(ip->dst_addr),
+		indent, "notImplemented");
+}
+
+static int32_t
+gen_parse_ipv4_params(char *protocol_str, struct rte_ipv4_hdr *ip)
+{
+	/* Strings to look for. */
+	static const char * const items[] = {
+		"src=",
+		"dst=",
+	};
+	const uint32_t num_items = RTE_DIM(items);
+
+	char *tok_ptr;
+	uint32_t err = 0;
+	uint32_t i;
+	for (i = 0; i < num_items; i++) {
+		/* Print input string into local buffer for processing. */
+		char buffer[1024];
+		int chars_printed = snprintf(buffer, 1024, "%s", protocol_str);
+		if (chars_printed >= 1024)
+			return -1;
+
+		/* Find substring (e.g. src=) if not found skip to next one. */
+		char *start = strstr(buffer, items[i]);
+		char check_previous[32];
+		if (start != NULL) {
+			snprintf(check_previous, 32, "%.1s", start - 1);
+			if (strcmp(&check_previous[0], "(") &&
+						strcmp(&check_previous[0], ","))
+				return -EINVAL;
+		}
+
+		if (!start) {
+			if (!strstr(buffer, ","))
+				continue;
+			else
+				return -EINVAL;
+		}
+		/* get from start of string till first , character. */
+		char *item = strtok_r(start, ",", &tok_ptr);
+
+		if (strcmp(item, items[i]) == 0)
+			return -EINVAL;
+		/* skip past the src= prefix. We know string is long enough as
+		 * otherwise strstr() wouldn't have matched it.
+		 */
+		item = &item[4];
+
+		if (strcmp(items[i], "src=") == 0) {
+			err = rte_ip_parse_addr(item, &ip->src_addr);
+			ip->src_addr = rte_cpu_to_be_32(ip->src_addr);
+		} else {
+			err = rte_ip_parse_addr(item, &ip->dst_addr);
+			ip->dst_addr = rte_cpu_to_be_32(ip->dst_addr);
+		}
+		if (err) {
+			TGEN_LOG(ERR, "parser ip_parse_addr error %d\n", err);
+			return err;
+		}
+	}
+	return 0;
+}
+
+static int32_t
+gen_parse_ipv4(struct gen_parser *parser, char *protocol_str)
+{
+	struct rte_ipv4_hdr *ip = gen_parser_get_data_ptr(parser);
+	memset(ip, 0, sizeof(*ip));
+	ip->version_ihl = RTE_IPV4_VHL_DEF;
+
+	/* default addrs */
+	ip->src_addr = RTE_IPV4(127, 0, 0, 1);
+	ip->dst_addr = RTE_IPV4(127, 0, 0, 1);
+
+	uint32_t err = 0;
+	if (strcmp("IP()", protocol_str))
+		err = gen_parse_ipv4_params(protocol_str, ip);
+
+	if (err) {
+		TGEN_LOG(ERR, "parser parse ipv4 params error %d\n", err);
+		return err;
+	}
+
+	/* Move up write pointer in packet, recurse to next. */
+	enum GEN_PROTO inner;
+	parser->buf_write_offset += rte_ipv4_hdr_len(ip);
+		err = gen_parser_parse_next(parser, &inner);
+	if (err) {
+		TGEN_LOG(ERR, "parser parse next() error %d\n", err);
+		return err;
+	}
+
+	switch (inner) {
+	default:
+		/* Default protocol is hopopt (0). */
+		break;
+	};
+
+	return 0;
+}
+
 static void
 gen_log_ether(void *data, const char *indent)
 {
@@ -304,7 +440,14 @@ static struct gen_parse_func_t gen_protocols[] = {
 		.proto = GEN_PROTO_ETHER,
 		.parse_func = gen_parse_ether,
 		.log_func = gen_log_ether,
-	}
+	},
+	{
+		.name = "IP(",
+		.proto = GEN_PROTO_IPV4,
+		.parse_func = gen_parse_ipv4,
+		.log_func = gen_log_ipv4,
+	},
+
 };
 
 /* Function to tokenize and parse each segment of a string.
@@ -380,6 +523,7 @@ rte_gen_packet_parse_string(struct rte_gen *gen,
 	if (err) {
 		TGEN_LOG(ERR, "Error in parsing packet string. "
 			"Set \"gen\" log level to debug for more info.\n");
+		rte_pktmbuf_free(parser.mbuf);
 		return -1;
 	}
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 08/12] examples/generator: import code from basicfwd.c
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (6 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 07/12] gen: add gen IP parsing Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 09/12] examples/generator: enable gen library for traffic gen Ronan Randles
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

Import files from basicfwd.c to act as starting point for
gen library sample app

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c      | 226 +++++++++++++++++++++++++++++++++
 examples/generator/meson.build |  12 ++
 2 files changed, 238 insertions(+)
 create mode 100644 examples/generator/main.c
 create mode 100644 examples/generator/meson.build

diff --git a/examples/generator/main.c b/examples/generator/main.c
new file mode 100644
index 0000000000..0082f588b4
--- /dev/null
+++ b/examples/generator/main.c
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2015 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+
+#define RX_RING_SIZE 1024
+#define TX_RING_SIZE 1024
+
+#define NUM_MBUFS 8191
+#define MBUF_CACHE_SIZE 256
+#define BURST_SIZE 32
+
+/* Configuration of ethernet ports. 8<  */
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = {
+		.max_lro_pkt_size = RTE_ETHER_MAX_LEN,
+	},
+};
+/* >8 End of configuration of ethernet ports. */
+
+/* basicfwd.c: Basic DPDK skeleton forwarding example. */
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+
+/* Main functional part of port initialization. 8< */
+static inline int
+port_init(uint16_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1, tx_rings = 1;
+	uint16_t nb_rxd = RX_RING_SIZE;
+	uint16_t nb_txd = TX_RING_SIZE;
+	int retval;
+	uint16_t q;
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_txconf txconf;
+
+	if (!rte_eth_dev_is_valid_port(port))
+		return -1;
+
+	retval = rte_eth_dev_info_get(port, &dev_info);
+	if (retval != 0) {
+		printf("Error during getting device (port %u) info: %s\n",
+				port, strerror(-retval));
+		return retval;
+	}
+
+	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+		port_conf.txmode.offloads |=
+			DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+		if (retval < 0)
+			return retval;
+	}
+
+	txconf = dev_info.default_txconf;
+	txconf.offloads = port_conf.txmode.offloads;
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		retval = rte_eth_tx_queue_setup(port, q, nb_txd,
+				rte_eth_dev_socket_id(port), &txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Starting Ethernet port. 8< */
+	retval = rte_eth_dev_start(port);
+	/* >8 End of starting of ethernet port. */
+	if (retval < 0)
+		return retval;
+
+	/* Display the port MAC address. */
+	struct rte_ether_addr addr;
+	retval = rte_eth_macaddr_get(port, &addr);
+	if (retval != 0)
+		return retval;
+
+	printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
+			   " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
+			port, RTE_ETHER_ADDR_BYTES(&addr));
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	retval = rte_eth_promiscuous_enable(port);
+	/* End of setting RX port in promiscuous mode. */
+	if (retval != 0)
+		return retval;
+
+	return 0;
+}
+/* >8 End of main functional part of port initialization. */
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+
+ /* Basic forwarding application lcore. 8< */
+static __rte_noreturn void
+lcore_main(void)
+{
+	uint16_t port;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	RTE_ETH_FOREACH_DEV(port)
+		if (rte_eth_dev_socket_id(port) >= 0 &&
+				rte_eth_dev_socket_id(port) !=
+						(int)rte_socket_id())
+			printf("WARNING, port %u is on remote NUMA node to "
+					"polling thread.\n\tPerformance will "
+					"not be optimal.\n", port);
+
+	printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Main work of application loop. 8< */
+	for (;;) {
+		/*
+		 * Receive packets on a port and forward them on the paired
+		 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
+		 */
+		RTE_ETH_FOREACH_DEV(port) {
+
+			/* Get burst of RX packets, from first port of pair. */
+			struct rte_mbuf *bufs[BURST_SIZE];
+			const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
+					bufs, BURST_SIZE);
+
+			if (unlikely(nb_rx == 0))
+				continue;
+
+			/* Send burst of TX packets, to second port of pair. */
+			const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
+					bufs, nb_rx);
+
+			/* Free any unsent packets. */
+			if (unlikely(nb_tx < nb_rx)) {
+				uint16_t buf;
+				for (buf = nb_tx; buf < nb_rx; buf++)
+					rte_pktmbuf_free(bufs[buf]);
+			}
+		}
+	}
+	/* >8 End of loop. */
+}
+/* >8 End Basic forwarding application lcore. */
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	struct rte_mempool *mbuf_pool;
+	unsigned int nb_ports;
+	uint16_t portid;
+
+	/* Initializion the Environment Abstraction Layer (EAL). 8< */
+	int ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+	/* >8 End of initializion the Environment Abstraction Layer (EAL). */
+
+	argc -= ret;
+	argv += ret;
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count_avail();
+	if (nb_ports < 2 || (nb_ports & 1))
+		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+
+	/* Allocates mempool to hold the mbufs. 8< */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+	/* >8 End of allocating mempool to hold mbuf. */
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initializing all ports. 8< */
+	RTE_ETH_FOREACH_DEV(portid)
+		if (port_init(portid, mbuf_pool) != 0)
+			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
+					portid);
+	/* >8 End of initializing all ports. */
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the main core only. Called on single lcore. 8< */
+	lcore_main();
+	/* >8 End of called on single lcore. */
+
+	/* clean up the EAL */
+	rte_eal_cleanup();
+
+	return 0;
+}
diff --git a/examples/generator/meson.build b/examples/generator/meson.build
new file mode 100644
index 0000000000..441678bbe5
--- /dev/null
+++ b/examples/generator/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Intel Corporation
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+allow_experimental_apis = true
+sources = files(
+        'main.c',
+)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 09/12] examples/generator: enable gen library for traffic gen
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (7 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 08/12] examples/generator: import code from basicfwd.c Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 10/12] examples/generator: telemetry support Ronan Randles
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

This commit shows the steps necessary to enable traffic generation
using the gen library

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c      | 175 ++++++++++++++++++++++-----------
 examples/generator/meson.build |   1 +
 examples/meson.build           |   1 +
 3 files changed, 120 insertions(+), 57 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 0082f588b4..1ddf4c1603 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -1,14 +1,18 @@
 /* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2010-2015 Intel Corporation
+ * Copyright(c) 2021 Intel Corporation
  */
 
 #include <stdint.h>
 #include <inttypes.h>
+#include <unistd.h>
+#include <signal.h>
+
 #include <rte_eal.h>
 #include <rte_ethdev.h>
 #include <rte_cycles.h>
 #include <rte_lcore.h>
 #include <rte_mbuf.h>
+#include <rte_gen.h>
 
 #define RX_RING_SIZE 1024
 #define TX_RING_SIZE 1024
@@ -16,23 +20,23 @@
 #define NUM_MBUFS 8191
 #define MBUF_CACHE_SIZE 256
 #define BURST_SIZE 32
+#define MIN_THREADS 3
 
-/* Configuration of ethernet ports. 8<  */
 static const struct rte_eth_conf port_conf_default = {
 	.rxmode = {
 		.max_lro_pkt_size = RTE_ETHER_MAX_LEN,
 	},
 };
-/* >8 End of configuration of ethernet ports. */
 
-/* basicfwd.c: Basic DPDK skeleton forwarding example. */
+static volatile int done;
+static struct rte_mempool *mbuf_pool;
+struct rte_gen *gen;
+
+static void handle_sigint(int sig);
 
-/*
- * Initializes a given port using global settings and with the RX buffers
+/* Initializes a given port using global settings and with the RX buffers
  * coming from the mbuf_pool passed as a parameter.
  */
-
-/* Main functional part of port initialization. 8< */
 static inline int
 port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 {
@@ -68,6 +72,12 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 	if (retval != 0)
 		return retval;
 
+	int lcore_available_count = rte_lcore_count();
+	if (lcore_available_count < MIN_THREADS) {
+		printf("Not enough threads available\n");
+		return -1;
+	}
+
 	/* Allocate and set up 1 RX queue per Ethernet port. */
 	for (q = 0; q < rx_rings; q++) {
 		retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
@@ -86,9 +96,8 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 			return retval;
 	}
 
-	/* Starting Ethernet port. 8< */
+	/* Start the Ethernet port. */
 	retval = rte_eth_dev_start(port);
-	/* >8 End of starting of ethernet port. */
 	if (retval < 0)
 		return retval;
 
@@ -104,27 +113,67 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 
 	/* Enable RX in promiscuous mode for the Ethernet device. */
 	retval = rte_eth_promiscuous_enable(port);
-	/* End of setting RX port in promiscuous mode. */
 	if (retval != 0)
 		return retval;
 
 	return 0;
 }
-/* >8 End of main functional part of port initialization. */
 
-/*
- * The lcore main. This is the main thread that does the work, reading from
+/* The lcore main. This is the main thread that does the work, reading from
  * an input port and writing to an output port.
  */
+static int
+lcore_producer(__rte_unused void *arg)
+{
+	uint16_t port;
+
+	/* Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	RTE_ETH_FOREACH_DEV(port)
+		if (rte_eth_dev_socket_id(port) >= 0 &&
+				rte_eth_dev_socket_id(port) !=
+						(int)rte_socket_id())
+			printf("WARNING, port %u is on remote NUMA node to "
+					"polling thread.\n\tPerformance will "
+					"not be optimal.\n", port);
 
- /* Basic forwarding application lcore. 8< */
-static __rte_noreturn void
-lcore_main(void)
+	/* Run until the application is quit or killed. */
+	while (!done) {
+		struct rte_mbuf *bufs[BURST_SIZE];
+		int i;
+		/* Receive packets from gen and then tx them over port */
+		RTE_ETH_FOREACH_DEV(port) {
+			int nb_recieved = rte_gen_rx_burst(gen, bufs,
+							BURST_SIZE);
+			for (i = 0; i < nb_recieved; i++) {
+				bufs[i]->pkt_len = 64;
+				bufs[i]->data_len = 64;
+			}
+
+			uint16_t nb_tx = rte_eth_tx_burst(port, 0, bufs,
+							nb_recieved);
+			if (nb_tx != nb_recieved)
+				rte_pktmbuf_free_bulk(&bufs[nb_tx],
+							(nb_recieved - nb_tx));
+
+			if (unlikely(nb_tx == 0))
+				continue;
+
+		}
+	}
+	return 0;
+}
+
+/* The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static int
+lcore_consumer(__rte_unused void *arg)
 {
 	uint16_t port;
 
-	/*
-	 * Check that the port is on the same NUMA node as the polling thread
+	/* Check that the port is on the same NUMA node as the polling thread
 	 * for best performance.
 	 */
 	RTE_ETH_FOREACH_DEV(port)
@@ -135,57 +184,53 @@ lcore_main(void)
 					"polling thread.\n\tPerformance will "
 					"not be optimal.\n", port);
 
-	printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
-			rte_lcore_id());
+	/* Run until the application is quit or killed. */
+	while (!done) {
+		struct rte_mbuf *bufs[BURST_SIZE];
 
-	/* Main work of application loop. 8< */
-	for (;;) {
-		/*
-		 * Receive packets on a port and forward them on the paired
-		 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
+		/* Receive packets over port and then tx them to gen library
+		 * for stats
 		 */
 		RTE_ETH_FOREACH_DEV(port) {
+			uint64_t latency[BURST_SIZE];
+			uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs,
+							BURST_SIZE);
+			rte_gen_tx_burst(gen, bufs, latency, nb_rx);
 
-			/* Get burst of RX packets, from first port of pair. */
-			struct rte_mbuf *bufs[BURST_SIZE];
-			const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
-					bufs, BURST_SIZE);
+			int nb_sent = rte_gen_tx_burst(gen, bufs,
+							latency, nb_rx);
+			if (nb_sent != nb_rx)
+				rte_panic("invalid tx quantity\n");
 
 			if (unlikely(nb_rx == 0))
 				continue;
 
-			/* Send burst of TX packets, to second port of pair. */
-			const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
-					bufs, nb_rx);
-
-			/* Free any unsent packets. */
-			if (unlikely(nb_tx < nb_rx)) {
-				uint16_t buf;
-				for (buf = nb_tx; buf < nb_rx; buf++)
-					rte_pktmbuf_free(bufs[buf]);
-			}
 		}
 	}
-	/* >8 End of loop. */
+	return 0;
 }
-/* >8 End Basic forwarding application lcore. */
 
-/*
- * The main function, which does initialization and calls the per-lcore
+void handle_sigint(int sig)
+{
+	RTE_SET_USED(sig);
+	printf("\nExiting...\n");
+	done = 1;
+}
+
+/* The main function, which does initialization and calls the per-lcore
  * functions.
  */
 int
 main(int argc, char *argv[])
 {
-	struct rte_mempool *mbuf_pool;
+	signal(SIGINT, handle_sigint);
 	unsigned int nb_ports;
 	uint16_t portid;
 
-	/* Initializion the Environment Abstraction Layer (EAL). 8< */
+	/* Initialize the Environment Abstraction Layer (EAL). */
 	int ret = rte_eal_init(argc, argv);
 	if (ret < 0)
 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
-	/* >8 End of initializion the Environment Abstraction Layer (EAL). */
 
 	argc -= ret;
 	argv += ret;
@@ -196,28 +241,44 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
 
 	/* Creates a new mempool in memory to hold the mbufs. */
-
-	/* Allocates mempool to hold the mbufs. 8< */
 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
 		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
-	/* >8 End of allocating mempool to hold mbuf. */
 
 	if (mbuf_pool == NULL)
 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
 
-	/* Initializing all ports. 8< */
+	/* Initialize all ports. */
 	RTE_ETH_FOREACH_DEV(portid)
 		if (port_init(portid, mbuf_pool) != 0)
 			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
 					portid);
-	/* >8 End of initializing all ports. */
 
-	if (rte_lcore_count() > 1)
-		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+	gen = rte_gen_create(mbuf_pool);
+	if (!gen)
+		rte_panic("Gen failed to initialize\n");
+
+	int err = rte_gen_packet_parse_string(gen, "Ether()/IP()", NULL);
+	if (err)
+		rte_panic("Failed to parse input args");
+
+	/* launch lcore functions */
+	uint32_t lcore_count = 0;
+	uint32_t lcore_id = 0;
+	RTE_LCORE_FOREACH_WORKER(lcore_id) {
+		if (lcore_count == 0)
+			rte_eal_remote_launch(lcore_producer, NULL, lcore_id);
+		else if (lcore_count == 1)
+			rte_eal_remote_launch(lcore_consumer, NULL, lcore_id);
+		else
+			break;
+
+		lcore_count++;
+	}
+	/* Stall the main thread until all other threads have returned. */
+	rte_eal_mp_wait_lcore();
 
-	/* Call lcore_main on the main core only. Called on single lcore. 8< */
-	lcore_main();
-	/* >8 End of called on single lcore. */
+	/* All threads returned, safe to destroy gen instance */
+	rte_gen_destroy(gen);
 
 	/* clean up the EAL */
 	rte_eal_cleanup();
diff --git a/examples/generator/meson.build b/examples/generator/meson.build
index 441678bbe5..15d84674a5 100644
--- a/examples/generator/meson.build
+++ b/examples/generator/meson.build
@@ -10,3 +10,4 @@ allow_experimental_apis = true
 sources = files(
         'main.c',
 )
+deps += 'gen'
\ No newline at end of file
diff --git a/examples/meson.build b/examples/meson.build
index bac9b76007..79ce36d02b 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -18,6 +18,7 @@ all_examples = [
         'fips_validation',
         'flow_classify',
         'flow_filtering',
+        'generator',
         'helloworld',
         'ip_fragmentation',
         'ip_pipeline',
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 10/12] examples/generator: telemetry support
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (8 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 09/12] examples/generator: enable gen library for traffic gen Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 11/12] examples/generator: link status check added Ronan Randles
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

This commit adds telemetry introducing the callback functions
and returning measured values

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c | 159 ++++++++++++++++++++++++++++++++------
 1 file changed, 135 insertions(+), 24 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 1ddf4c1603..2525d34b6e 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -13,6 +13,7 @@
 #include <rte_lcore.h>
 #include <rte_mbuf.h>
 #include <rte_gen.h>
+#include <rte_telemetry.h>
 
 #define RX_RING_SIZE 1024
 #define TX_RING_SIZE 1024
@@ -32,6 +33,29 @@ static volatile int done;
 static struct rte_mempool *mbuf_pool;
 struct rte_gen *gen;
 
+struct gen_args {
+	/* Inputs */
+	struct rte_gen *gen;
+
+	/* Outputs */
+	uint64_t tx_total_packets;
+	uint64_t rx_total_packets;
+	uint64_t rx_missed_total;
+	uint64_t tx_failed;
+	uint64_t last_tx_total;
+	uint64_t measured_tx_pps;
+
+
+} __rte_cache_aligned;
+/* Expose a struct as a global so the telemetry callbacks can access the
+ * data required to provide stats etc.
+ */
+struct telemetry_userdata {
+	struct gen_args *prod;
+	struct gen_args *cons;
+};
+static struct telemetry_userdata telemetry_userdata;
+
 static void handle_sigint(int sig);
 
 /* Initializes a given port using global settings and with the RX buffers
@@ -123,10 +147,11 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
  * an input port and writing to an output port.
  */
 static int
-lcore_producer(__rte_unused void *arg)
+lcore_producer(void *arg)
 {
+	struct gen_args *args = arg;
+	struct rte_gen *gen = args->gen;
 	uint16_t port;
-
 	/* Check that the port is on the same NUMA node as the polling thread
 	 * for best performance.
 	 */
@@ -138,25 +163,34 @@ lcore_producer(__rte_unused void *arg)
 					"polling thread.\n\tPerformance will "
 					"not be optimal.\n", port);
 
+	uint64_t tsc_hz = rte_get_tsc_hz();
+	uint64_t last_tsc_reading = 0;
+	uint64_t last_tx_total = 0;
+
 	/* Run until the application is quit or killed. */
 	while (!done) {
 		struct rte_mbuf *bufs[BURST_SIZE];
-		int i;
+		uint16_t nb_tx = 0;
 		/* Receive packets from gen and then tx them over port */
 		RTE_ETH_FOREACH_DEV(port) {
-			int nb_recieved = rte_gen_rx_burst(gen, bufs,
+			int nb_generated = rte_gen_rx_burst(gen, bufs,
 							BURST_SIZE);
-			for (i = 0; i < nb_recieved; i++) {
-				bufs[i]->pkt_len = 64;
-				bufs[i]->data_len = 64;
-			}
 
-			uint16_t nb_tx = rte_eth_tx_burst(port, 0, bufs,
-							nb_recieved);
-			if (nb_tx != nb_recieved)
-				rte_pktmbuf_free_bulk(&bufs[nb_tx],
-							(nb_recieved - nb_tx));
+			uint64_t start_tsc = rte_rdtsc();
+			if (start_tsc > last_tsc_reading + tsc_hz) {
+				args->measured_tx_pps = args->tx_total_packets -
+								last_tx_total;
+				last_tx_total = args->tx_total_packets;
+				last_tsc_reading = start_tsc;
+			}
+			nb_tx = rte_eth_tx_burst(port, 0, bufs, nb_generated);
+			args->tx_total_packets += nb_tx;
 
+			uint64_t tx_failed = nb_generated - nb_tx;
+			if (nb_tx != nb_generated) {
+				rte_pktmbuf_free_bulk(&bufs[nb_tx], tx_failed);
+				args->tx_failed += tx_failed;
+			}
 			if (unlikely(nb_tx == 0))
 				continue;
 
@@ -169,10 +203,11 @@ lcore_producer(__rte_unused void *arg)
  * an input port and writing to an output port.
  */
 static int
-lcore_consumer(__rte_unused void *arg)
+lcore_consumer(void *arg)
 {
+	struct gen_args *args = arg;
+	struct rte_gen *gen = args->gen;
 	uint16_t port;
-
 	/* Check that the port is on the same NUMA node as the polling thread
 	 * for best performance.
 	 */
@@ -195,16 +230,16 @@ lcore_consumer(__rte_unused void *arg)
 			uint64_t latency[BURST_SIZE];
 			uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs,
 							BURST_SIZE);
-			rte_gen_tx_burst(gen, bufs, latency, nb_rx);
+			if (unlikely(nb_rx == 0))
+				continue;
+
+			args->rx_total_packets += nb_rx;
 
 			int nb_sent = rte_gen_tx_burst(gen, bufs,
 							latency, nb_rx);
 			if (nb_sent != nb_rx)
 				rte_panic("invalid tx quantity\n");
 
-			if (unlikely(nb_rx == 0))
-				continue;
-
 		}
 	}
 	return 0;
@@ -217,6 +252,58 @@ void handle_sigint(int sig)
 	done = 1;
 }
 
+static int
+tele_gen_packet(const char *cmd, const char *params, struct rte_tel_data *d)
+{
+	RTE_SET_USED(cmd);
+	RTE_SET_USED(params);
+	RTE_SET_USED(d);
+
+	rte_tel_data_string(d, "Ether()/IP()");
+	return 0;
+}
+
+static int
+tele_gen_mpps(const char *cmd, const char *params, struct rte_tel_data *d)
+{
+	struct gen_args *args = telemetry_userdata.prod;
+	RTE_SET_USED(cmd);
+	if (params) {
+		rte_tel_data_add_dict_int(d, "TEST",
+					(args->measured_tx_pps/1000000));
+	}
+	rte_tel_data_start_dict(d);
+	rte_tel_data_add_dict_int(d, "mpps",
+					(args->measured_tx_pps/1000000));
+	return 0;
+}
+
+static int
+tele_gen_stats(const char *cmd, const char *params, struct rte_tel_data *d)
+{
+	RTE_SET_USED(cmd);
+	RTE_SET_USED(params);
+
+	struct gen_args *args_prod = telemetry_userdata.prod;
+	struct gen_args *args_cons = telemetry_userdata.cons;
+	rte_tel_data_start_dict(d);
+	static const char * const stats[] = {
+		"tx_total_packets",
+		"rx_total_packets",
+		"measured_tx_pps"
+	};
+
+	uint64_t values[RTE_DIM(stats)] = {0};
+	values[0] = args_prod->tx_total_packets;
+	values[1] = args_cons->rx_total_packets;
+	values[2] = args_prod->measured_tx_pps;
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(stats); i++)
+		rte_tel_data_add_dict_int(d, stats[i], values[i]);
+
+	return 0;
+}
 /* The main function, which does initialization and calls the per-lcore
  * functions.
  */
@@ -224,6 +311,10 @@ int
 main(int argc, char *argv[])
 {
 	signal(SIGINT, handle_sigint);
+
+	#define CORE_COUNT 2
+	struct gen_args core_launch_args[CORE_COUNT];
+
 	unsigned int nb_ports;
 	uint16_t portid;
 
@@ -253,7 +344,7 @@ main(int argc, char *argv[])
 			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
 					portid);
 
-	gen = rte_gen_create(mbuf_pool);
+	struct rte_gen *gen = rte_gen_create(mbuf_pool);
 	if (!gen)
 		rte_panic("Gen failed to initialize\n");
 
@@ -261,19 +352,39 @@ main(int argc, char *argv[])
 	if (err)
 		rte_panic("Failed to parse input args");
 
+	memset(core_launch_args, 0, sizeof(struct gen_args) * CORE_COUNT);
 	/* launch lcore functions */
 	uint32_t lcore_count = 0;
 	uint32_t lcore_id = 0;
 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
-		if (lcore_count == 0)
-			rte_eal_remote_launch(lcore_producer, NULL, lcore_id);
-		else if (lcore_count == 1)
-			rte_eal_remote_launch(lcore_consumer, NULL, lcore_id);
+		core_launch_args[lcore_count].gen = gen;
+		if (lcore_count == 0) {
+			telemetry_userdata.prod =
+						&core_launch_args[lcore_count];
+			rte_eal_remote_launch(lcore_producer,
+					      telemetry_userdata.prod,
+					      lcore_id);
+		} else if (lcore_count == 1) {
+			telemetry_userdata.cons =
+						&core_launch_args[lcore_count];
+			rte_eal_remote_launch(lcore_consumer,
+					      telemetry_userdata.cons,
+					      lcore_id);
+		}
 		else
 			break;
 
 		lcore_count++;
 	}
+
+	/* Export stats via Telemetry */
+	rte_telemetry_register_cmd("/gen/stats", tele_gen_stats,
+			"Return statistics of the Gen instance.");
+	rte_telemetry_register_cmd("/gen/packet", tele_gen_packet,
+			"Return the Gen string packet being sent.");
+	rte_telemetry_register_cmd("/gen/mpps", tele_gen_mpps,
+			"Get/Set the mpps rate");
+
 	/* Stall the main thread until all other threads have returned. */
 	rte_eal_mp_wait_lcore();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 11/12] examples/generator: link status check added
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (9 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 10/12] examples/generator: telemetry support Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 14:12 ` [PATCH 12/12] examples/generator: line rate limiting Ronan Randles
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

This commit brings in a link status check so that the generator will
only start sending packets once there is something on the other end of
the link.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c | 70 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 67 insertions(+), 3 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 2525d34b6e..f11110e528 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -27,9 +27,13 @@ static const struct rte_eth_conf port_conf_default = {
 	.rxmode = {
 		.max_lro_pkt_size = RTE_ETHER_MAX_LEN,
 	},
+	.intr_conf = {
+		.lsc = 1, /**< lsc interrupt */
+	},
 };
 
 static volatile int done;
+static volatile int link_status[RTE_MAX_ETHPORTS];
 static struct rte_mempool *mbuf_pool;
 struct rte_gen *gen;
 
@@ -58,6 +62,30 @@ static struct telemetry_userdata telemetry_userdata;
 
 static void handle_sigint(int sig);
 
+static int
+link_status_change_cb(uint16_t port_id, enum rte_eth_event_type type,
+		      void *param, void *ret_param)
+{
+	if (unlikely(port_id >= RTE_DIM(link_status)))
+		rte_panic("got LSC interrupt for unknown port id\n");
+
+	RTE_SET_USED(type);
+	RTE_SET_USED(param);
+	RTE_SET_USED(ret_param);
+
+	struct rte_eth_link link;
+	int ret = rte_eth_link_get_nowait(port_id, &link);
+	if (ret < 0) {
+		printf("Failed link get on port %d: %s\n",
+		       port_id, rte_strerror(-ret));
+		return ret;
+	}
+
+	printf("Link status change port %i\n", port_id);
+	link_status[port_id] = link.link_status;
+	return 0;
+}
+
 /* Initializes a given port using global settings and with the RX buffers
  * coming from the mbuf_pool passed as a parameter.
  */
@@ -101,6 +129,9 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 		printf("Not enough threads available\n");
 		return -1;
 	}
+	/* Register the LinkStatusChange callback */
+	rte_eth_dev_callback_register(port, RTE_ETH_EVENT_INTR_LSC,
+				      link_status_change_cb, NULL);
 
 	/* Allocate and set up 1 RX queue per Ethernet port. */
 	for (q = 0; q < rx_rings; q++) {
@@ -140,6 +171,16 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 	if (retval != 0)
 		return retval;
 
+	struct rte_eth_link link;
+	int ret = rte_eth_link_get_nowait(port, &link);
+	if (ret < 0) {
+		printf("Failed link get on port %d: %s\n", port,
+							rte_strerror(-ret));
+		return ret;
+	}
+
+	link_status[port] = link.link_status;
+
 	return 0;
 }
 
@@ -166,12 +207,26 @@ lcore_producer(void *arg)
 	uint64_t tsc_hz = rte_get_tsc_hz();
 	uint64_t last_tsc_reading = 0;
 	uint64_t last_tx_total = 0;
+	uint16_t nb_tx = 0;
+
+	/* Ensure all available ports are up before generating packets */
+	uint16_t nb_eth_ports = rte_eth_dev_count_avail();
+	uint16_t nb_links_up = 0;
+	while (!done && nb_links_up < nb_eth_ports) {
+		if (link_status[nb_links_up])
+			nb_links_up++;
+
+		rte_delay_us_block(100);
+	}
+	if (!done)
+		printf("Generating packets...\n");
 
 	/* Run until the application is quit or killed. */
 	while (!done) {
+
 		struct rte_mbuf *bufs[BURST_SIZE];
-		uint16_t nb_tx = 0;
 		/* Receive packets from gen and then tx them over port */
+
 		RTE_ETH_FOREACH_DEV(port) {
 			int nb_generated = rte_gen_rx_burst(gen, bufs,
 							BURST_SIZE);
@@ -219,8 +274,19 @@ lcore_consumer(void *arg)
 					"polling thread.\n\tPerformance will "
 					"not be optimal.\n", port);
 
+	/* Ensure all available ports are up before generating packets */
+	uint16_t nb_eth_ports = rte_eth_dev_count_avail();
+	uint16_t nb_links_up = 0;
+	while (!done && nb_links_up < nb_eth_ports) {
+		if (link_status[nb_links_up])
+			nb_links_up++;
+
+		rte_delay_us_block(100);
+	}
+
 	/* Run until the application is quit or killed. */
 	while (!done) {
+
 		struct rte_mbuf *bufs[BURST_SIZE];
 
 		/* Receive packets over port and then tx them to gen library
@@ -257,8 +323,6 @@ tele_gen_packet(const char *cmd, const char *params, struct rte_tel_data *d)
 {
 	RTE_SET_USED(cmd);
 	RTE_SET_USED(params);
-	RTE_SET_USED(d);
-
 	rte_tel_data_string(d, "Ether()/IP()");
 	return 0;
 }
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 12/12] examples/generator: line rate limiting
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (10 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 11/12] examples/generator: link status check added Ronan Randles
@ 2021-12-14 14:12 ` Ronan Randles
  2021-12-14 16:10   ` Stephen Hemminger
  2021-12-14 14:57 ` [PATCH 00/12] add packet generator library and example app Bruce Richardson
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 62+ messages in thread
From: Ronan Randles @ 2021-12-14 14:12 UTC (permalink / raw)
  To: dev; +Cc: harry.van.haaren, Ronan Randles

This commit introduces line rate limiting using a token passing method.
The target traffic rate default is currently hard coded, this can be set
using telemetry.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c | 77 +++++++++++++++++++++++++--------------
 1 file changed, 49 insertions(+), 28 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index f11110e528..3847cde755 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -40,6 +40,7 @@ struct rte_gen *gen;
 struct gen_args {
 	/* Inputs */
 	struct rte_gen *gen;
+	uint64_t target_tx_pps;
 
 	/* Outputs */
 	uint64_t tx_total_packets;
@@ -205,9 +206,11 @@ lcore_producer(void *arg)
 					"not be optimal.\n", port);
 
 	uint64_t tsc_hz = rte_get_tsc_hz();
+	float tsc_hz_f = (float)tsc_hz;
 	uint64_t last_tsc_reading = 0;
 	uint64_t last_tx_total = 0;
 	uint16_t nb_tx = 0;
+	float tokens = 0;
 
 	/* Ensure all available ports are up before generating packets */
 	uint16_t nb_eth_ports = rte_eth_dev_count_avail();
@@ -221,34 +224,52 @@ lcore_producer(void *arg)
 	if (!done)
 		printf("Generating packets...\n");
 
+	uint64_t token_last_add_tsc = rte_rdtsc();
+
 	/* Run until the application is quit or killed. */
 	while (!done) {
-
 		struct rte_mbuf *bufs[BURST_SIZE];
-		/* Receive packets from gen and then tx them over port */
-
-		RTE_ETH_FOREACH_DEV(port) {
-			int nb_generated = rte_gen_rx_burst(gen, bufs,
-							BURST_SIZE);
-
-			uint64_t start_tsc = rte_rdtsc();
-			if (start_tsc > last_tsc_reading + tsc_hz) {
-				args->measured_tx_pps = args->tx_total_packets -
-								last_tx_total;
-				last_tx_total = args->tx_total_packets;
-				last_tsc_reading = start_tsc;
+		/* Track time since last token add and calculate number
+		 * of tokens to give per second to implement line rate limiting
+		 */
+		uint64_t now = rte_rdtsc();
+		uint64_t tsc_delta = now - token_last_add_tsc;
+		float token_scalar = (float)tsc_delta / tsc_hz_f;
+		float add_tokens = args->target_tx_pps * token_scalar;
+		/* If there are tokens to be added and we haven't exceeded
+		 * the target rate then we add tokens
+		 */
+		if (add_tokens > 1.f) {
+			if (tokens < args->target_tx_pps) {
+				tokens += add_tokens;
+				token_last_add_tsc = now;
 			}
-			nb_tx = rte_eth_tx_burst(port, 0, bufs, nb_generated);
-			args->tx_total_packets += nb_tx;
-
-			uint64_t tx_failed = nb_generated - nb_tx;
-			if (nb_tx != nb_generated) {
-				rte_pktmbuf_free_bulk(&bufs[nb_tx], tx_failed);
-				args->tx_failed += tx_failed;
+		}
+		/* Receive packets from gen and then tx them over port */
+		if (tokens >= BURST_SIZE) {
+			RTE_ETH_FOREACH_DEV(port) {
+				int nb_generated = rte_gen_rx_burst(gen, bufs,
+								BURST_SIZE);
+
+				uint64_t start_tsc = rte_rdtsc();
+				if (start_tsc > last_tsc_reading + tsc_hz) {
+					args->measured_tx_pps =
+					args->tx_total_packets - last_tx_total;
+					last_tx_total = args->tx_total_packets;
+					last_tsc_reading = start_tsc;
+				}
+				nb_tx = rte_eth_tx_burst(port, 0, bufs,
+								nb_generated);
+				args->tx_total_packets += nb_tx;
+				tokens -= nb_tx;
+
+				uint64_t tx_failed = nb_generated - nb_tx;
+				if (nb_tx != nb_generated) {
+					rte_pktmbuf_free_bulk(&bufs[nb_tx],
+								tx_failed);
+					args->tx_failed += tx_failed;
+				}
 			}
-			if (unlikely(nb_tx == 0))
-				continue;
-
 		}
 	}
 	return 0;
@@ -298,7 +319,6 @@ lcore_consumer(void *arg)
 							BURST_SIZE);
 			if (unlikely(nb_rx == 0))
 				continue;
-
 			args->rx_total_packets += nb_rx;
 
 			int nb_sent = rte_gen_tx_burst(gen, bufs,
@@ -333,12 +353,11 @@ tele_gen_mpps(const char *cmd, const char *params, struct rte_tel_data *d)
 	struct gen_args *args = telemetry_userdata.prod;
 	RTE_SET_USED(cmd);
 	if (params) {
-		rte_tel_data_add_dict_int(d, "TEST",
-					(args->measured_tx_pps/1000000));
+		args->target_tx_pps = atoi(params) * 1000000;
+		printf("Packet rate set: %li\n", args->target_tx_pps);
 	}
 	rte_tel_data_start_dict(d);
-	rte_tel_data_add_dict_int(d, "mpps",
-					(args->measured_tx_pps/1000000));
+	rte_tel_data_add_dict_int(d, "mpps", args->target_tx_pps);
 	return 0;
 }
 
@@ -425,6 +444,8 @@ main(int argc, char *argv[])
 		if (lcore_count == 0) {
 			telemetry_userdata.prod =
 						&core_launch_args[lcore_count];
+			/* Default traffic rate */
+			telemetry_userdata.prod->target_tx_pps = 20000000;
 			rte_eal_remote_launch(lcore_producer,
 					      telemetry_userdata.prod,
 					      lcore_id);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 00/12] add packet generator library and example app
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (11 preceding siblings ...)
  2021-12-14 14:12 ` [PATCH 12/12] examples/generator: line rate limiting Ronan Randles
@ 2021-12-14 14:57 ` Bruce Richardson
  2021-12-14 15:59   ` Randles, Ronan
  2022-01-12 16:18   ` Morten Brørup
  2021-12-15 12:31 ` Jerin Jacob
                   ` (2 subsequent siblings)
  15 siblings, 2 replies; 62+ messages in thread
From: Bruce Richardson @ 2021-12-14 14:57 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dev, harry.van.haaren

On Tue, Dec 14, 2021 at 02:12:30PM +0000, Ronan Randles wrote:
> This patchset introduces a Gen library for DPDK. This library provides an easy
> way to generate traffic in order to test software based network components.
> 
> This library enables the basic functionality required in the traffic generator.
> This includes: raw data setting, packet Tx and Rx, creation and destruction of a
>  Gen instance and various types of data parsing.
> This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
> functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
> library.
> 
> A sample app is included in "examples/generator" which shows the use of the gen
> library in making a traffic generator. This can be used to generate traffic by
> running the dpdk-generator generator executable. This sample app supports
> runtime stats reporting (/gen/stats) and line rate limiting
> (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> 
> As more features are added to the gen library, the sample application will
> become more powerful through the "/gen/packet" string parameter
> (currently supports IP and Ether address setting). This will allow every
> application to generate more complex traffic types in the future without
> changing API.
> 
> Harry van Haaren (6):
>   gen: add files for initial traffic generation library
>   gen: add basic Rx and Tx routines and tests
>   gen: add raw packet data API and tests
>   gen: add parsing infrastructure and Ether protocol
>   gen: add gen IP parsing
>   examples/generator: import code from basicfwd.c
> 
> Ronan Randles (6):
>   net: add string to IPv4 parse function
>   net: add function to pretty print IPv4
>   examples/generator: enable gen library for traffic gen
>   examples/generator: telemetry support
>   examples/generator: link status check added
>   examples/generator: line rate limiting
> 
>  app/test/meson.build           |   4 +
>  app/test/test_gen.c            | 184 +++++++++++
>  app/test/test_net.c            |  87 ++++++
>  doc/api/doxy-api-index.md      |   3 +-
>  doc/api/doxy-api.conf.in       |   1 +
>  examples/generator/main.c      | 483 ++++++++++++++++++++++++++++
>  examples/generator/meson.build |  13 +
>  examples/meson.build           |   1 +
>  lib/gen/meson.build            |   6 +
>  lib/gen/rte_gen.c              | 553 +++++++++++++++++++++++++++++++++
>  lib/gen/rte_gen.h              | 114 +++++++
>  lib/gen/version.map            |  10 +
>  lib/meson.build                |   1 +
>  lib/net/meson.build            |   1 +
>  lib/net/rte_ip.c               |  58 ++++
>  lib/net/rte_ip.h               |  38 +++
>  lib/net/version.map            |   9 +
>  17 files changed, 1565 insertions(+), 1 deletion(-)

I think this is great to see, and sounds a good addition to DPDK. One thing
to address in any v2 is to add more documentation for both the library and
the example app. You need a chapter on the lib added to the programmers
guide to help others use the library from their code, and a chapter on the
generator example in the example apps guide.

More general question - if we do have a traffic generator in DPDK, would it
be better in the "app" rather than the examples one? If it's only going to
ever stay a simple example of using the lib, examples might be fine, but I
suspect that it will get quite complicated if people start using it and
adding more features, in which case a move to the "app" folder might be
more appropriate. Thoughts?

/Bruce

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 00/12] add packet generator library and example app
  2021-12-14 14:57 ` [PATCH 00/12] add packet generator library and example app Bruce Richardson
@ 2021-12-14 15:59   ` Randles, Ronan
  2022-01-12 16:18   ` Morten Brørup
  1 sibling, 0 replies; 62+ messages in thread
From: Randles, Ronan @ 2021-12-14 15:59 UTC (permalink / raw)
  To: Richardson, Bruce, Van Haaren, Harry; +Cc: dev

I'd be inclined to agree with you on the move to the app dir. The example app could be turned into more of a fully-fledged app than just an example app. I think the final decision on that would fall to @Van Haaren, Harry on that

-----Original Message-----
From: Richardson, Bruce <bruce.richardson@intel.com> 
Sent: Tuesday, December 14, 2021 2:58 PM
To: Randles, Ronan <ronan.randles@intel.com>
Cc: dev@dpdk.org; Van Haaren, Harry <harry.van.haaren@intel.com>
Subject: Re: [PATCH 00/12] add packet generator library and example app

On Tue, Dec 14, 2021 at 02:12:30PM +0000, Ronan Randles wrote:
> This patchset introduces a Gen library for DPDK. This library provides 
> an easy way to generate traffic in order to test software based network components.
> 
> This library enables the basic functionality required in the traffic generator.
> This includes: raw data setting, packet Tx and Rx, creation and 
> destruction of a  Gen instance and various types of data parsing.
> This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing 
> functionality is also added in "lib/net/rte_ip.c", this is then used 
> in the gen library.
> 
> A sample app is included in "examples/generator" which shows the use 
> of the gen library in making a traffic generator. This can be used to 
> generate traffic by running the dpdk-generator generator executable. 
> This sample app supports runtime stats reporting (/gen/stats) and line 
> rate limiting (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> 
> As more features are added to the gen library, the sample application 
> will become more powerful through the "/gen/packet" string parameter 
> (currently supports IP and Ether address setting). This will allow 
> every application to generate more complex traffic types in the future 
> without changing API.
> 
> Harry van Haaren (6):
>   gen: add files for initial traffic generation library
>   gen: add basic Rx and Tx routines and tests
>   gen: add raw packet data API and tests
>   gen: add parsing infrastructure and Ether protocol
>   gen: add gen IP parsing
>   examples/generator: import code from basicfwd.c
> 
> Ronan Randles (6):
>   net: add string to IPv4 parse function
>   net: add function to pretty print IPv4
>   examples/generator: enable gen library for traffic gen
>   examples/generator: telemetry support
>   examples/generator: link status check added
>   examples/generator: line rate limiting
> 
>  app/test/meson.build           |   4 +
>  app/test/test_gen.c            | 184 +++++++++++
>  app/test/test_net.c            |  87 ++++++
>  doc/api/doxy-api-index.md      |   3 +-
>  doc/api/doxy-api.conf.in       |   1 +
>  examples/generator/main.c      | 483 ++++++++++++++++++++++++++++
>  examples/generator/meson.build |  13 +
>  examples/meson.build           |   1 +
>  lib/gen/meson.build            |   6 +
>  lib/gen/rte_gen.c              | 553 +++++++++++++++++++++++++++++++++
>  lib/gen/rte_gen.h              | 114 +++++++
>  lib/gen/version.map            |  10 +
>  lib/meson.build                |   1 +
>  lib/net/meson.build            |   1 +
>  lib/net/rte_ip.c               |  58 ++++
>  lib/net/rte_ip.h               |  38 +++
>  lib/net/version.map            |   9 +
>  17 files changed, 1565 insertions(+), 1 deletion(-)

I think this is great to see, and sounds a good addition to DPDK. One thing to address in any v2 is to add more documentation for both the library and the example app. You need a chapter on the lib added to the programmers guide to help others use the library from their code, and a chapter on the generator example in the example apps guide.

More general question - if we do have a traffic generator in DPDK, would it be better in the "app" rather than the examples one? If it's only going to ever stay a simple example of using the lib, examples might be fine, but I suspect that it will get quite complicated if people start using it and adding more features, in which case a move to the "app" folder might be more appropriate. Thoughts?

/Bruce

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-14 14:12 ` [PATCH 02/12] net: add function to pretty print IPv4 Ronan Randles
@ 2021-12-14 16:08   ` Stephen Hemminger
  2021-12-14 17:42     ` Morten Brørup
  2021-12-14 17:31   ` Morten Brørup
  1 sibling, 1 reply; 62+ messages in thread
From: Stephen Hemminger @ 2021-12-14 16:08 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dev, harry.van.haaren

On Tue, 14 Dec 2021 14:12:32 +0000
Ronan Randles <ronan.randles@intel.com> wrote:

> This function accepts an uint32_t representation of an IP address and
> produces a string representation stored in a char * buffer. Realavent
> unit tests also included.
> 
> Signed-off-by: Ronan Randles <ronan.randles@intel.com>

Do we really have to reinvent getnameinfo()?
Is this for Windows?

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 12/12] examples/generator: line rate limiting
  2021-12-14 14:12 ` [PATCH 12/12] examples/generator: line rate limiting Ronan Randles
@ 2021-12-14 16:10   ` Stephen Hemminger
  0 siblings, 0 replies; 62+ messages in thread
From: Stephen Hemminger @ 2021-12-14 16:10 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dev, harry.van.haaren

On Tue, 14 Dec 2021 14:12:42 +0000
Ronan Randles <ronan.randles@intel.com> wrote:

> +		/* Track time since last token add and calculate number
> +		 * of tokens to give per second to implement line rate limiting
> +		 */
> +		uint64_t now = rte_rdtsc();
> +		uint64_t tsc_delta = now - token_last_add_tsc;
> +		float token_scalar = (float)tsc_delta / tsc_hz_f;
> +		float add_tokens = args->target_tx_pps * token_scalar;

floating point math is slow, could you do this fixed point with
reciprocal divide?

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 01/12] net: add string to IPv4 parse function
  2021-12-14 14:12 ` [PATCH 01/12] net: add string to IPv4 parse function Ronan Randles
@ 2021-12-14 17:31   ` Morten Brørup
  2021-12-15  9:27     ` Bruce Richardson
  2022-01-19 14:20   ` Thomas Monjalon
  1 sibling, 1 reply; 62+ messages in thread
From: Morten Brørup @ 2021-12-14 17:31 UTC (permalink / raw)
  To: Ronan Randles, dev; +Cc: harry.van.haaren

> From: Ronan Randles [mailto:ronan.randles@intel.com]
> Sent: Tuesday, 14 December 2021 15.13
> 
> Added function that accepts ip string as a parameter and returns an ip
> address represented by a uint32_t. Relevant unit test for this function
> is also included.
> 
> Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
> Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> ---

[snip]

> diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> index c575250852..188054fda4 100644
> --- a/lib/net/rte_ip.h
> +++ b/lib/net/rte_ip.h
> @@ -426,6 +426,24 @@ rte_ipv4_udptcp_cksum_verify(const struct
> rte_ipv4_hdr *ipv4_hdr,
>  	return 0;
>  }
> 
> +/**
> + * IP address parser.
> + *
> + * @param src_ip
> + *   The IP address to be parsed.
> + * @param output_addr
> + *   The array in which the parsed digits will be saved.
> + *
> + * @retval 0
> + *   Success.
> + * @retval -1
> + *   Failure due to invalid input arguments.
> + */
> +
> +__rte_experimental
> +int32_t
> +rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> +

Good initiative!

This should set a precedent for to/from string functions, so be careful about names and calling conventions.

I have a few suggestions:

The function should take a parameter to tell if the input string must be zero-terminated or not. This is highly useful for parsing subnet strings (e.g. "192.0.2.0/24") and IP range strings (e.g. "192.0.2.2-192.0.2.253").

The return value should be the number of characters read from the input string, and still -1 on error. With this modification, also consider using the return type ssize_t instead of int32_t.

-Morten


^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-14 14:12 ` [PATCH 02/12] net: add function to pretty print IPv4 Ronan Randles
  2021-12-14 16:08   ` Stephen Hemminger
@ 2021-12-14 17:31   ` Morten Brørup
  2021-12-15  1:06     ` Ananyev, Konstantin
  1 sibling, 1 reply; 62+ messages in thread
From: Morten Brørup @ 2021-12-14 17:31 UTC (permalink / raw)
  To: Ronan Randles, dev; +Cc: harry.van.haaren

> From: Ronan Randles [mailto:ronan.randles@intel.com]
> Sent: Tuesday, 14 December 2021 15.13
> 
> This function accepts an uint32_t representation of an IP address and
> produces a string representation stored in a char * buffer. Realavent
> unit tests also included.
> 
> Signed-off-by: Ronan Randles <ronan.randles@intel.com>

[snip]

> diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> index 188054fda4..e46f0b41ba 100644
> --- a/lib/net/rte_ip.h
> +++ b/lib/net/rte_ip.h
> @@ -444,6 +444,26 @@ __rte_experimental
>  int32_t
>  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> 
> +
> +/**
> + * Print IP address from 32 bit int into char * buffer.
> + *
> + * @param ip_addr
> + *   ip address to be printed.
> + * @param buffer
> + *   The buffer the string will be saved into.
> + * @param buffer_size
> + *   size of buffer to be used.
> + *
> + * @retval 0
> + *   Success.
> + * @retval -1
> + *   Failure due to invalid input arguments.
> + */
> +__rte_experimental
> +int32_t
> +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> buffer_size);
> +

In continuation of my email reply about the IPv4 parse function...

I have a few suggestions to the IPv4 print function too:

The return value should be the number of characters written to the output string, and still -1 on error. With this modification, you could use the return type ssize_t instead of int32_t.

Furthermore, I would prefer having the parameters in the same order as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please also notice the suggested changed type for the size, and the const added to the ip_addr.

-Morten


^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-14 16:08   ` Stephen Hemminger
@ 2021-12-14 17:42     ` Morten Brørup
  0 siblings, 0 replies; 62+ messages in thread
From: Morten Brørup @ 2021-12-14 17:42 UTC (permalink / raw)
  To: Ronan Randles, harry.van.haaren; +Cc: dev, Stephen Hemminger

> From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> Sent: Tuesday, 14 December 2021 17.09
> 
> On Tue, 14 Dec 2021 14:12:32 +0000
> Ronan Randles <ronan.randles@intel.com> wrote:
> 
> > This function accepts an uint32_t representation of an IP address and
> > produces a string representation stored in a char * buffer. Realavent
> > unit tests also included.
> >
> > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> 
> Do we really have to reinvent getnameinfo()?
> Is this for Windows?

For general inspiration:

We have an in-house to/from string library, where our xx_to_str() functions return char * and can take a NULL pointer as the buffer parameter to make it use a buffer from an small cyclic pool of string buffers in the library.

Probably not good from an academic standpoint, and not good for a generic library, but we know how many of these strings are being used simultaneously in our applications, so the pool is not overrun in reality. And it makes the code using these functions much shorter and readable.

-Morten


^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-14 17:31   ` Morten Brørup
@ 2021-12-15  1:06     ` Ananyev, Konstantin
  2021-12-15  3:20       ` Stephen Hemminger
  0 siblings, 1 reply; 62+ messages in thread
From: Ananyev, Konstantin @ 2021-12-15  1:06 UTC (permalink / raw)
  To: Morten Brørup, Randles, Ronan, dev; +Cc: Van Haaren, Harry



> -----Original Message-----
> From: Morten Brørup <mb@smartsharesystems.com>
> Sent: Tuesday, December 14, 2021 5:31 PM
> To: Randles, Ronan <ronan.randles@intel.com>; dev@dpdk.org
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>
> Subject: RE: [PATCH 02/12] net: add function to pretty print IPv4
> 
> > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > Sent: Tuesday, 14 December 2021 15.13
> >
> > This function accepts an uint32_t representation of an IP address and
> > produces a string representation stored in a char * buffer. Realavent
> > unit tests also included.
> >
> > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> 
> [snip]
> 
> > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > index 188054fda4..e46f0b41ba 100644
> > --- a/lib/net/rte_ip.h
> > +++ b/lib/net/rte_ip.h
> > @@ -444,6 +444,26 @@ __rte_experimental
> >  int32_t
> >  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> >
> > +
> > +/**
> > + * Print IP address from 32 bit int into char * buffer.
> > + *
> > + * @param ip_addr
> > + *   ip address to be printed.
> > + * @param buffer
> > + *   The buffer the string will be saved into.
> > + * @param buffer_size
> > + *   size of buffer to be used.
> > + *
> > + * @retval 0
> > + *   Success.
> > + * @retval -1
> > + *   Failure due to invalid input arguments.
> > + */
> > +__rte_experimental
> > +int32_t
> > +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> > buffer_size);
> > +
> 
> In continuation of my email reply about the IPv4 parse function...
> 
> I have a few suggestions to the IPv4 print function too:
> 
> The return value should be the number of characters written to the output string, and still -1 on error. With this modification, you could
> use the return type ssize_t instead of int32_t.
> 
> Furthermore, I would prefer having the parameters in the same order as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please
> also notice the suggested changed type for the size, and the const added to the ip_addr.
> 
Honestly, I don't understand why we need to introduce such functions
inside DPDK at all.
What's wrong with existing standard ones: inet_ntop() and inet_pton()?



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-15  1:06     ` Ananyev, Konstantin
@ 2021-12-15  3:20       ` Stephen Hemminger
  2021-12-15  7:23         ` Morten Brørup
  0 siblings, 1 reply; 62+ messages in thread
From: Stephen Hemminger @ 2021-12-15  3:20 UTC (permalink / raw)
  To: Ananyev, Konstantin
  Cc: Morten Brørup, Randles, Ronan, dev, Van Haaren, Harry

On Wed, 15 Dec 2021 01:06:14 +0000
"Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote:

> > -----Original Message-----
> > From: Morten Brørup <mb@smartsharesystems.com>
> > Sent: Tuesday, December 14, 2021 5:31 PM
> > To: Randles, Ronan <ronan.randles@intel.com>; dev@dpdk.org
> > Cc: Van Haaren, Harry <harry.van.haaren@intel.com>
> > Subject: RE: [PATCH 02/12] net: add function to pretty print IPv4
> >   
> > > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > > Sent: Tuesday, 14 December 2021 15.13
> > >
> > > This function accepts an uint32_t representation of an IP address and
> > > produces a string representation stored in a char * buffer. Realavent
> > > unit tests also included.
> > >
> > > Signed-off-by: Ronan Randles <ronan.randles@intel.com>  
> > 
> > [snip]
> >   
> > > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > > index 188054fda4..e46f0b41ba 100644
> > > --- a/lib/net/rte_ip.h
> > > +++ b/lib/net/rte_ip.h
> > > @@ -444,6 +444,26 @@ __rte_experimental
> > >  int32_t
> > >  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > >
> > > +
> > > +/**
> > > + * Print IP address from 32 bit int into char * buffer.
> > > + *
> > > + * @param ip_addr
> > > + *   ip address to be printed.
> > > + * @param buffer
> > > + *   The buffer the string will be saved into.
> > > + * @param buffer_size
> > > + *   size of buffer to be used.
> > > + *
> > > + * @retval 0
> > > + *   Success.
> > > + * @retval -1
> > > + *   Failure due to invalid input arguments.
> > > + */
> > > +__rte_experimental
> > > +int32_t
> > > +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> > > buffer_size);
> > > +  
> > 
> > In continuation of my email reply about the IPv4 parse function...
> > 
> > I have a few suggestions to the IPv4 print function too:
> > 
> > The return value should be the number of characters written to the output string, and still -1 on error. With this modification, you could
> > use the return type ssize_t instead of int32_t.
> > 
> > Furthermore, I would prefer having the parameters in the same order as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please
> > also notice the suggested changed type for the size, and the const added to the ip_addr.
> >   
> Honestly, I don't understand why we need to introduce such functions
> inside DPDK at all.
> What's wrong with existing standard ones: inet_ntop() and inet_pton()?

Agreed, I see no added value in reinventing here


^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-15  3:20       ` Stephen Hemminger
@ 2021-12-15  7:23         ` Morten Brørup
  2021-12-15 13:06           ` Ananyev, Konstantin
  0 siblings, 1 reply; 62+ messages in thread
From: Morten Brørup @ 2021-12-15  7:23 UTC (permalink / raw)
  To: Stephen Hemminger, Ananyev, Konstantin
  Cc: Randles, Ronan, dev, Van Haaren, Harry

> From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> Sent: Wednesday, 15 December 2021 04.21
> 
> On Wed, 15 Dec 2021 01:06:14 +0000
> "Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote:
> 
> > > -----Original Message-----
> > > From: Morten Brørup <mb@smartsharesystems.com>
> > > Sent: Tuesday, December 14, 2021 5:31 PM
> > > To: Randles, Ronan <ronan.randles@intel.com>; dev@dpdk.org
> > > Cc: Van Haaren, Harry <harry.van.haaren@intel.com>
> > > Subject: RE: [PATCH 02/12] net: add function to pretty print IPv4
> > >
> > > > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > > > Sent: Tuesday, 14 December 2021 15.13
> > > >
> > > > This function accepts an uint32_t representation of an IP address
> and
> > > > produces a string representation stored in a char * buffer.
> Realavent
> > > > unit tests also included.
> > > >
> > > > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> > >
> > > [snip]
> > >
> > > > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > > > index 188054fda4..e46f0b41ba 100644
> > > > --- a/lib/net/rte_ip.h
> > > > +++ b/lib/net/rte_ip.h
> > > > @@ -444,6 +444,26 @@ __rte_experimental
> > > >  int32_t
> > > >  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > > >
> > > > +
> > > > +/**
> > > > + * Print IP address from 32 bit int into char * buffer.
> > > > + *
> > > > + * @param ip_addr
> > > > + *   ip address to be printed.
> > > > + * @param buffer
> > > > + *   The buffer the string will be saved into.
> > > > + * @param buffer_size
> > > > + *   size of buffer to be used.
> > > > + *
> > > > + * @retval 0
> > > > + *   Success.
> > > > + * @retval -1
> > > > + *   Failure due to invalid input arguments.
> > > > + */
> > > > +__rte_experimental
> > > > +int32_t
> > > > +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> > > > buffer_size);
> > > > +
> > >
> > > In continuation of my email reply about the IPv4 parse function...
> > >
> > > I have a few suggestions to the IPv4 print function too:
> > >
> > > The return value should be the number of characters written to the
> output string, and still -1 on error. With this modification, you could
> > > use the return type ssize_t instead of int32_t.
> > >
> > > Furthermore, I would prefer having the parameters in the same order
> as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please
> > > also notice the suggested changed type for the size, and the const
> added to the ip_addr.
> > >
> > Honestly, I don't understand why we need to introduce such functions
> > inside DPDK at all.
> > What's wrong with existing standard ones: inet_ntop() and
> inet_pton()?
> 
> Agreed, I see no added value in reinventing here

I think that DPDK functions for converting all sorts of types to/from strings would be useful; not only IP addresses, but also MAC addresses, TCP/UDP port numbers and VLAN IDs.

If you don't like IP address string conversion functions in the net library, DPDK could have a string conversions library. That library could expose a multitude of APIs for the same purpose, so the application can use the API that best fits each application use.



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/12] net: add string to IPv4 parse function
  2021-12-14 17:31   ` Morten Brørup
@ 2021-12-15  9:27     ` Bruce Richardson
  2021-12-15  9:35       ` Morten Brørup
  0 siblings, 1 reply; 62+ messages in thread
From: Bruce Richardson @ 2021-12-15  9:27 UTC (permalink / raw)
  To: Morten Brørup; +Cc: Ronan Randles, dev, harry.van.haaren

On Tue, Dec 14, 2021 at 06:31:06PM +0100, Morten Brørup wrote:
> > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > Sent: Tuesday, 14 December 2021 15.13
> > 
> > Added function that accepts ip string as a parameter and returns an ip
> > address represented by a uint32_t. Relevant unit test for this function
> > is also included.
> > 
> > Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
> > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> > ---
> 
> [snip]
> 
> > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > index c575250852..188054fda4 100644
> > --- a/lib/net/rte_ip.h
> > +++ b/lib/net/rte_ip.h
> > @@ -426,6 +426,24 @@ rte_ipv4_udptcp_cksum_verify(const struct
> > rte_ipv4_hdr *ipv4_hdr,
> >  	return 0;
> >  }
> > 
> > +/**
> > + * IP address parser.
> > + *
> > + * @param src_ip
> > + *   The IP address to be parsed.
> > + * @param output_addr
> > + *   The array in which the parsed digits will be saved.
> > + *
> > + * @retval 0
> > + *   Success.
> > + * @retval -1
> > + *   Failure due to invalid input arguments.
> > + */
> > +
> > +__rte_experimental
> > +int32_t
> > +rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > +
> 
> Good initiative!
> 
> This should set a precedent for to/from string functions, so be careful about names and calling conventions.
> 
> I have a few suggestions:
> 
> The function should take a parameter to tell if the input string must be zero-terminated or not. This is highly useful for parsing subnet strings (e.g. "192.0.2.0/24") and IP range strings (e.g. "192.0.2.2-192.0.2.253").
> 
> The return value should be the number of characters read from the input string, and still -1 on error. With this modification, also consider using the return type ssize_t instead of int32_t.
> 
Interesting point on the "must be zero-terminated" parameter. However, if
the return value is the number of characters read, then the user can check
for null-termination very easily after the call, and not need the
parameter. Therefore, I would suggest having the function always assume
chars after the address and let the user check for null if needed
afterwards, to keep function simpler.

/Bruce

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 01/12] net: add string to IPv4 parse function
  2021-12-15  9:27     ` Bruce Richardson
@ 2021-12-15  9:35       ` Morten Brørup
  2021-12-15 10:11         ` Bruce Richardson
  0 siblings, 1 reply; 62+ messages in thread
From: Morten Brørup @ 2021-12-15  9:35 UTC (permalink / raw)
  To: Bruce Richardson; +Cc: Ronan Randles, dev, harry.van.haaren

> From: Bruce Richardson [mailto:bruce.richardson@intel.com]
> Sent: Wednesday, 15 December 2021 10.27
> 
> On Tue, Dec 14, 2021 at 06:31:06PM +0100, Morten Brørup wrote:
> > > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > > Sent: Tuesday, 14 December 2021 15.13
> > >
> > > Added function that accepts ip string as a parameter and returns an
> ip
> > > address represented by a uint32_t. Relevant unit test for this
> function
> > > is also included.
> > >
> > > Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
> > > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> > > ---
> >
> > [snip]
> >
> > > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > > index c575250852..188054fda4 100644
> > > --- a/lib/net/rte_ip.h
> > > +++ b/lib/net/rte_ip.h
> > > @@ -426,6 +426,24 @@ rte_ipv4_udptcp_cksum_verify(const struct
> > > rte_ipv4_hdr *ipv4_hdr,
> > >  	return 0;
> > >  }
> > >
> > > +/**
> > > + * IP address parser.
> > > + *
> > > + * @param src_ip
> > > + *   The IP address to be parsed.
> > > + * @param output_addr
> > > + *   The array in which the parsed digits will be saved.
> > > + *
> > > + * @retval 0
> > > + *   Success.
> > > + * @retval -1
> > > + *   Failure due to invalid input arguments.
> > > + */
> > > +
> > > +__rte_experimental
> > > +int32_t
> > > +rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > > +
> >
> > Good initiative!
> >
> > This should set a precedent for to/from string functions, so be
> careful about names and calling conventions.
> >
> > I have a few suggestions:
> >
> > The function should take a parameter to tell if the input string must
> be zero-terminated or not. This is highly useful for parsing subnet
> strings (e.g. "192.0.2.0/24") and IP range strings (e.g. "192.0.2.2-
> 192.0.2.253").
> >
> > The return value should be the number of characters read from the
> input string, and still -1 on error. With this modification, also
> consider using the return type ssize_t instead of int32_t.
> >
> Interesting point on the "must be zero-terminated" parameter. However,
> if
> the return value is the number of characters read, then the user can
> check
> for null-termination very easily after the call, and not need the
> parameter. Therefore, I would suggest having the function always assume
> chars after the address and let the user check for null if needed
> afterwards, to keep function simpler.

That would require additional lines of code in the application. I would rather have that additional code inside the utility function. There is no need to keep the function simple.



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/12] net: add string to IPv4 parse function
  2021-12-15  9:35       ` Morten Brørup
@ 2021-12-15 10:11         ` Bruce Richardson
  0 siblings, 0 replies; 62+ messages in thread
From: Bruce Richardson @ 2021-12-15 10:11 UTC (permalink / raw)
  To: Morten Brørup; +Cc: Ronan Randles, dev, harry.van.haaren

On Wed, Dec 15, 2021 at 10:35:44AM +0100, Morten Brørup wrote:
> > From: Bruce Richardson [mailto:bruce.richardson@intel.com]
> > Sent: Wednesday, 15 December 2021 10.27
> > 
> > On Tue, Dec 14, 2021 at 06:31:06PM +0100, Morten Brørup wrote:
> > > > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > > > Sent: Tuesday, 14 December 2021 15.13
> > > >
> > > > Added function that accepts ip string as a parameter and returns an
> > ip
> > > > address represented by a uint32_t. Relevant unit test for this
> > function
> > > > is also included.
> > > >
> > > > Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
> > > > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> > > > ---
> > >
> > > [snip]
> > >
> > > > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > > > index c575250852..188054fda4 100644
> > > > --- a/lib/net/rte_ip.h
> > > > +++ b/lib/net/rte_ip.h
> > > > @@ -426,6 +426,24 @@ rte_ipv4_udptcp_cksum_verify(const struct
> > > > rte_ipv4_hdr *ipv4_hdr,
> > > >  	return 0;
> > > >  }
> > > >
> > > > +/**
> > > > + * IP address parser.
> > > > + *
> > > > + * @param src_ip
> > > > + *   The IP address to be parsed.
> > > > + * @param output_addr
> > > > + *   The array in which the parsed digits will be saved.
> > > > + *
> > > > + * @retval 0
> > > > + *   Success.
> > > > + * @retval -1
> > > > + *   Failure due to invalid input arguments.
> > > > + */
> > > > +
> > > > +__rte_experimental
> > > > +int32_t
> > > > +rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > > > +
> > >
> > > Good initiative!
> > >
> > > This should set a precedent for to/from string functions, so be
> > careful about names and calling conventions.
> > >
> > > I have a few suggestions:
> > >
> > > The function should take a parameter to tell if the input string must
> > be zero-terminated or not. This is highly useful for parsing subnet
> > strings (e.g. "192.0.2.0/24") and IP range strings (e.g. "192.0.2.2-
> > 192.0.2.253").
> > >
> > > The return value should be the number of characters read from the
> > input string, and still -1 on error. With this modification, also
> > consider using the return type ssize_t instead of int32_t.
> > >
> > Interesting point on the "must be zero-terminated" parameter. However,
> > if
> > the return value is the number of characters read, then the user can
> > check
> > for null-termination very easily after the call, and not need the
> > parameter. Therefore, I would suggest having the function always assume
> > chars after the address and let the user check for null if needed
> > afterwards, to keep function simpler.
> 
> That would require additional lines of code in the application. I would rather have that additional code inside the utility function. There is no need to keep the function simple.
>
Well, it's only an extra item in the error-check conditional, but point
taken. I have no strong opinion here.

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 00/12] add packet generator library and example app
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (12 preceding siblings ...)
  2021-12-14 14:57 ` [PATCH 00/12] add packet generator library and example app Bruce Richardson
@ 2021-12-15 12:31 ` Jerin Jacob
  2021-12-15 14:07   ` Bruce Richardson
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
  2022-01-21 14:44 ` [PATCH 00/12] add packet generator library and example app Xueming(Steven) Li
  15 siblings, 1 reply; 62+ messages in thread
From: Jerin Jacob @ 2021-12-15 12:31 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dpdk-dev, Van Haaren, Harry

On Tue, Dec 14, 2021 at 7:42 PM Ronan Randles <ronan.randles@intel.com> wrote:
>
> This patchset introduces a Gen library for DPDK. This library provides an easy
> way to generate traffic in order to test software based network components.
>
> This library enables the basic functionality required in the traffic generator.
> This includes: raw data setting, packet Tx and Rx, creation and destruction of a
>  Gen instance and various types of data parsing.
> This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
> functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
> library.
>
> A sample app is included in "examples/generator" which shows the use of the gen
> library in making a traffic generator. This can be used to generate traffic by
> running the dpdk-generator generator executable. This sample app supports
> runtime stats reporting (/gen/stats) and line rate limiting
> (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
>
>
>  lib/gen/rte_gen.h              | 114 +++++++

Please check Doxygen syntax across the file. rte_gen_create(),
rte_gen_destroy(),
rte_gen_packet_parse_string() etc missing proper doxygen synax.

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-14 14:12 ` [PATCH 05/12] gen: add raw packet data API " Ronan Randles
@ 2021-12-15 12:40   ` Jerin Jacob
  2021-12-17 11:40     ` Van Haaren, Harry
  0 siblings, 1 reply; 62+ messages in thread
From: Jerin Jacob @ 2021-12-15 12:40 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dpdk-dev, Van Haaren, Harry

On Tue, Dec 14, 2021 at 7:43 PM Ronan Randles <ronan.randles@intel.com> wrote:
>
> From: Harry van Haaren <harry.van.haaren@intel.com>
>
> This commit adds a new API to gen, allowing the caller to set
> raw packet data with a size. Tests are added to test the new
> API with randomized packet data.
>
> Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
> ---
>  }
>
> @@ -32,9 +53,37 @@ rte_gen_create(struct rte_mempool *mempool)
>  void
>  rte_gen_destroy(struct rte_gen *gen)
>  {
> +       rte_pktmbuf_free(gen->base_pkt);
>         rte_free(gen);
>  }
>

> +
>  uint16_t
>  rte_gen_rx_burst(struct rte_gen *gen,
>                  struct rte_mbuf **rx_pkts,
> @@ -45,17 +94,20 @@ rte_gen_rx_burst(struct rte_gen *gen,
>         if (err)
>                 return 0;
>
> -       const uint32_t pkt_len = 64;
> +       if (!gen->base_pkt)
> +               return 0;
> +
> +       const uint32_t base_size = gen->base_pkt->pkt_len;
> +       const uint8_t *base_data = rte_pktmbuf_mtod(gen->base_pkt, uint8_t *);

I think, the very next feature will be generating packets for
incrementing IP addresses or so. In this case, one packet-based
template will not work.
May we worth consider that use case into API framework first and add support
later for implementation as it may change the complete land space of API to have
better performance. Options like struct rte_gen logical object can have
N templates instead of one is an option on the table. :-)


>
>         uint32_t i;
>         for (i = 0; i < nb_pkts; i++) {
>                 struct rte_mbuf *m = rx_pkts[i];
>                 uint8_t *pkt_data = rte_pktmbuf_mtod(m, uint8_t *);
>
> -               memset(pkt_data, 0, pkt_len);
> -
> -               m->pkt_len  = pkt_len;
> -               m->data_len = pkt_len;
> +               rte_memcpy(pkt_data, base_data, base_size);
> +               m->pkt_len = base_size;
> +               m->data_len = base_size;
>         }
>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-15  7:23         ` Morten Brørup
@ 2021-12-15 13:06           ` Ananyev, Konstantin
  2022-01-19 14:24             ` Thomas Monjalon
  0 siblings, 1 reply; 62+ messages in thread
From: Ananyev, Konstantin @ 2021-12-15 13:06 UTC (permalink / raw)
  To: Morten Brørup, Stephen Hemminger
  Cc: Randles, Ronan, dev, Van Haaren, Harry



 
> > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > Sent: Wednesday, 15 December 2021 04.21
> >
> > On Wed, 15 Dec 2021 01:06:14 +0000
> > "Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote:
> >
> > > > -----Original Message-----
> > > > From: Morten Brørup <mb@smartsharesystems.com>
> > > > Sent: Tuesday, December 14, 2021 5:31 PM
> > > > To: Randles, Ronan <ronan.randles@intel.com>; dev@dpdk.org
> > > > Cc: Van Haaren, Harry <harry.van.haaren@intel.com>
> > > > Subject: RE: [PATCH 02/12] net: add function to pretty print IPv4
> > > >
> > > > > From: Ronan Randles [mailto:ronan.randles@intel.com]
> > > > > Sent: Tuesday, 14 December 2021 15.13
> > > > >
> > > > > This function accepts an uint32_t representation of an IP address
> > and
> > > > > produces a string representation stored in a char * buffer.
> > Realavent
> > > > > unit tests also included.
> > > > >
> > > > > Signed-off-by: Ronan Randles <ronan.randles@intel.com>
> > > >
> > > > [snip]
> > > >
> > > > > diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
> > > > > index 188054fda4..e46f0b41ba 100644
> > > > > --- a/lib/net/rte_ip.h
> > > > > +++ b/lib/net/rte_ip.h
> > > > > @@ -444,6 +444,26 @@ __rte_experimental
> > > > >  int32_t
> > > > >  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > > > >
> > > > > +
> > > > > +/**
> > > > > + * Print IP address from 32 bit int into char * buffer.
> > > > > + *
> > > > > + * @param ip_addr
> > > > > + *   ip address to be printed.
> > > > > + * @param buffer
> > > > > + *   The buffer the string will be saved into.
> > > > > + * @param buffer_size
> > > > > + *   size of buffer to be used.
> > > > > + *
> > > > > + * @retval 0
> > > > > + *   Success.
> > > > > + * @retval -1
> > > > > + *   Failure due to invalid input arguments.
> > > > > + */
> > > > > +__rte_experimental
> > > > > +int32_t
> > > > > +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> > > > > buffer_size);
> > > > > +
> > > >
> > > > In continuation of my email reply about the IPv4 parse function...
> > > >
> > > > I have a few suggestions to the IPv4 print function too:
> > > >
> > > > The return value should be the number of characters written to the
> > output string, and still -1 on error. With this modification, you could
> > > > use the return type ssize_t instead of int32_t.
> > > >
> > > > Furthermore, I would prefer having the parameters in the same order
> > as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please
> > > > also notice the suggested changed type for the size, and the const
> > added to the ip_addr.
> > > >
> > > Honestly, I don't understand why we need to introduce such functions
> > > inside DPDK at all.
> > > What's wrong with existing standard ones: inet_ntop() and
> > inet_pton()?
> >
> > Agreed, I see no added value in reinventing here
> 
> I think that DPDK functions for converting all sorts of types to/from strings would be useful; not only IP addresses, but also MAC addresses,
> TCP/UDP port numbers and VLAN IDs.

For MACs we already have:
rte_ether_format_addr()/rte_ether_unformat_addr()

> 
> If you don't like IP address string conversion functions in the net library, DPDK could have a string conversions library. That library could
> expose a multitude of APIs for the same purpose, so the application can use the API that best fits each application use.

I don’t mind to add new functions into net lib, if they are useful ones.
But for that particular case, I just don't see what is the reason to
develop and maintain our own functions while existing analogues:
- are well known, widely adopted and field proven
- do provide the same or even more comprehensive functionality
  


 


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 00/12] add packet generator library and example app
  2021-12-15 12:31 ` Jerin Jacob
@ 2021-12-15 14:07   ` Bruce Richardson
  0 siblings, 0 replies; 62+ messages in thread
From: Bruce Richardson @ 2021-12-15 14:07 UTC (permalink / raw)
  To: Jerin Jacob; +Cc: Ronan Randles, dpdk-dev, Van Haaren, Harry

On Wed, Dec 15, 2021 at 06:01:23PM +0530, Jerin Jacob wrote:
> On Tue, Dec 14, 2021 at 7:42 PM Ronan Randles <ronan.randles@intel.com> wrote:
> >
> > This patchset introduces a Gen library for DPDK. This library provides an easy
> > way to generate traffic in order to test software based network components.
> >
> > This library enables the basic functionality required in the traffic generator.
> > This includes: raw data setting, packet Tx and Rx, creation and destruction of a
> >  Gen instance and various types of data parsing.
> > This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
> > functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
> > library.
> >
> > A sample app is included in "examples/generator" which shows the use of the gen
> > library in making a traffic generator. This can be used to generate traffic by
> > running the dpdk-generator generator executable. This sample app supports
> > runtime stats reporting (/gen/stats) and line rate limiting
> > (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> >
> >
> >  lib/gen/rte_gen.h              | 114 +++++++
> 
> Please check Doxygen syntax across the file. rte_gen_create(),
> rte_gen_destroy(),
> rte_gen_packet_parse_string() etc missing proper doxygen synax.

If you do a build with "-Denable_docs=true -Dwerror" meson options set,
these should all be flagged on build. [If they aren't, we should fix!]

/Bruce

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-15 12:40   ` Jerin Jacob
@ 2021-12-17 11:40     ` Van Haaren, Harry
  2021-12-17 16:19       ` Thomas Monjalon
  2021-12-20 13:21       ` Jerin Jacob
  0 siblings, 2 replies; 62+ messages in thread
From: Van Haaren, Harry @ 2021-12-17 11:40 UTC (permalink / raw)
  To: Jerin Jacob, Randles, Ronan, Thomas Monjalon; +Cc: dpdk-dev

+CC Thomas;

> -----Original Message-----
> From: Jerin Jacob <jerinjacobk@gmail.com>
> Sent: Wednesday, December 15, 2021 12:41 PM
> To: Randles, Ronan <ronan.randles@intel.com>
> Cc: dpdk-dev <dev@dpdk.org>; Van Haaren, Harry
> <harry.van.haaren@intel.com>
> Subject: Re: [PATCH 05/12] gen: add raw packet data API and tests
> 
> On Tue, Dec 14, 2021 at 7:43 PM Ronan Randles <ronan.randles@intel.com>
> wrote:
> >
> > From: Harry van Haaren <harry.van.haaren@intel.com>

<snip some patch contents>

> > +       const uint32_t base_size = gen->base_pkt->pkt_len;
> > +       const uint8_t *base_data = rte_pktmbuf_mtod(gen->base_pkt, uint8_t
> *);
> 
> I think, the very next feature will be generating packets for
> incrementing IP addresses or so.

Hah, yes! It’s a logical next step, and indeed we have POC code internally that Ronan
and I have worked on that does this :) I've been using this internal POC of
testing of OVS for ~ a year now, and it provides a pretty nice workflow for me.

> In this case, one packet-based template will not work.

Why not? I agree that "pre-calculating" all packets will not work, but the approach
we have taken for this library is different. See below;

> May we worth consider that use case into API framework first and add support
> later for implementation as it may change the complete land space of API to have
> better performance. Options like struct rte_gen logical object can have
> N templates instead of one is an option on the table. :-)

Agree - more complex usages have been designed for too. Let me explain;

1) A single gen instance uses a single template, and has "modifiers" that allow
manipulation of the packet before sending. The gen->base_pkt is copied to the
destination mbuf, and then updated by the modifiers. This approach is much better
to allow for huge flow-counts (> 1 million?) as pre-calculating and storing 1 million
packets is a waste of memory, and causes a lot of mem-IO for the datapath core.

2) The "modifiers" approach allows any number of things to be changed, with little
mem-IO, and variable CPU cycle cost based on the modifiers themselves.
If the CPU cycle cost of generating packets is too high, just add more cores :)

3) There are also some smarts we can apply for pre-calculating only a small amount of
data per packet (e.g. uniformly-random distributed src ip). The memory footprint is
lower than pre-calc of whole packets, and the runtime overhead of uniform-random
is moved to configure time instead of on the datapath.

4) Dynamically generating packets by modification of templates allows for cool things
to be added, e.g. adding timestamps to packets, and calculating latency can
be done using the modifier concept and a protocol string "Ether()/IP()/UDP()/TSC()".
If the packet is being decapped by the target application, the string params can provide
context for where to "retrieve" the TSC from on RX in the generator: "TSC(rx_offset=30)".
I've found this approach to be very flexible and nice, so am a big fan :)

5) In order to have multiple streams of totally-different traffic types (read "from multiple templates")
the user can initialize multiple rte_gen instances. This allows applications that require multi-stream traffic
to achieve that too, with the same abstraction as a single template stream. Initially the generator app is just
providing a single stream, but this application can be expanded to many usages over the next year before 22.11 :)

I could ramble on a bit more, but mostly diminishing returns I think... I'll just use this email as a reply to Thomas' tweet;
https://twitter.com/tmonjalo/status/1337313985662771201

Regards, -Harry

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-17 11:40     ` Van Haaren, Harry
@ 2021-12-17 16:19       ` Thomas Monjalon
  2021-12-20 10:21         ` Van Haaren, Harry
  2021-12-20 13:21       ` Jerin Jacob
  1 sibling, 1 reply; 62+ messages in thread
From: Thomas Monjalon @ 2021-12-17 16:19 UTC (permalink / raw)
  To: Randles, Ronan, Van Haaren, Harry; +Cc: Jerin Jacob, dev, bruce.richardson

17/12/2021 12:40, Van Haaren, Harry:
> I could ramble on a bit more, but mostly diminishing returns I think...
> I'll just use this email as a reply to Thomas' tweet;
> https://twitter.com/tmonjalo/status/1337313985662771201

My original question was to know available applications,
not integrating such application in the DPDK repository.

I may me miss something obvious,
but I don't understand why trying to add a user app inside DPDK repo.



^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-17 16:19       ` Thomas Monjalon
@ 2021-12-20 10:21         ` Van Haaren, Harry
  2022-01-19 14:56           ` Thomas Monjalon
  0 siblings, 1 reply; 62+ messages in thread
From: Van Haaren, Harry @ 2021-12-20 10:21 UTC (permalink / raw)
  To: Thomas Monjalon, Randles, Ronan; +Cc: Jerin Jacob, dev, Richardson, Bruce

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Friday, December 17, 2021 4:19 PM
> To: Randles, Ronan <ronan.randles@intel.com>; Van Haaren, Harry
> <harry.van.haaren@intel.com>
> Cc: Jerin Jacob <jerinjacobk@gmail.com>; dev@dpdk.org; Richardson, Bruce
> <bruce.richardson@intel.com>
> Subject: Re: [PATCH 05/12] gen: add raw packet data API and tests
> 
> 17/12/2021 12:40, Van Haaren, Harry:
> > I could ramble on a bit more, but mostly diminishing returns I think...
> > I'll just use this email as a reply to Thomas' tweet;
> > https://twitter.com/tmonjalo/status/1337313985662771201
> 
> My original question was to know available applications,
> not integrating such application in the DPDK repository.
> 
> I may me miss something obvious,
> but I don't understand why trying to add a user app inside DPDK repo.

There are likely a few points-of-view on this particular topic; and I'm glad
you mention it so we can discuss it clearly here.

There are two main parts to this patchset, the first is a packet generation library,
with an easy to use string-based syntax. The *library* is designed to be extended in
future to a range of "useful stuff" to do while generating packets. The packet generation
*application* should have minimal features, and focus on ease-of-use (as suggested below).

In order to test the DPDK code, we need a variety of unit tests, and a sample-application to show
users how to use the library (as well as docs etc). For me, the interesting part is that it is a small
step from a simple sample-app just for testing to a minimal tool for high-rate packet generation. 

I think many users of DPDK first install DPDK, then wish for a tool to generate high traffic rates
to test DPDK, and end up with a usability problem; DPDK does not include a usable packet generator.

To highlight this point; our own DPDK Docs simply ignore the requirement of packet-generation to
actually have packets processed by skeleton: http://doc.dpdk.org/guides/sample_app_ug/skeleton.html 
Our "quick start" on the website uses PCAP vdevs (avoiding the problem)  https://core.dpdk.org/doc/quick-start/
Even searching the entire docs for "generate packet" doesn't give any relevant/useful results:
http://doc.dpdk.org/guides/search.html?q=generate+packet&check_keywords=yes&area=default# 

Users could internet-search & find pktgen, moongen, trex, or similar tools. These tools are fantastic for experienced
developers such as devs on this mailing list - we should *NOT* replicate these complex tools in DPDK itself. However,
building any tool outside of DPDK repo requires more effort; another git-clone, another set of dependencies to install,
perhaps another build-system to get used to. Particularly for people starting out with DPDK (who are likely finding
it difficult to learn the various hugepage/PCI-binding etc), this is yet another problem to solve, or to give up.

So my proposal is as follows; let us add a simple DPDK traffic generator to DPDK. We can define its scope
and its intended use, limiting the scope and capabilities. As before, I do NOT think it a good idea to build a
complex and feature-rich packet generator. I do feel it useful to have an easy-to-use application in DPDK that
is particularly designed for generating specific packets, at specific line-rates, and reports mpps returned.

Thoughts on adding an small scope-limited application to DPDK enabling ease-of-packet-generation for new users?

Regards, -Harry

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-17 11:40     ` Van Haaren, Harry
  2021-12-17 16:19       ` Thomas Monjalon
@ 2021-12-20 13:21       ` Jerin Jacob
  2022-01-21 14:20         ` Xueming(Steven) Li
  1 sibling, 1 reply; 62+ messages in thread
From: Jerin Jacob @ 2021-12-20 13:21 UTC (permalink / raw)
  To: Van Haaren, Harry; +Cc: Randles, Ronan, Thomas Monjalon, dpdk-dev

On Fri, Dec 17, 2021 at 5:10 PM Van Haaren, Harry
<harry.van.haaren@intel.com> wrote:
>
> +CC Thomas;
>
> > -----Original Message-----
> > From: Jerin Jacob <jerinjacobk@gmail.com>
> > Sent: Wednesday, December 15, 2021 12:41 PM
> > To: Randles, Ronan <ronan.randles@intel.com>
> > Cc: dpdk-dev <dev@dpdk.org>; Van Haaren, Harry
> > <harry.van.haaren@intel.com>
> > Subject: Re: [PATCH 05/12] gen: add raw packet data API and tests
> >
> > On Tue, Dec 14, 2021 at 7:43 PM Ronan Randles <ronan.randles@intel.com>
> > wrote:
> > >
> > > From: Harry van Haaren <harry.van.haaren@intel.com>
>
> <snip some patch contents>
>
> > > +       const uint32_t base_size = gen->base_pkt->pkt_len;
> > > +       const uint8_t *base_data = rte_pktmbuf_mtod(gen->base_pkt, uint8_t
> > *);
> >
> > I think, the very next feature will be generating packets for
> > incrementing IP addresses or so.
>
> Hah, yes! It’s a logical next step, and indeed we have POC code internally that Ronan
> and I have worked on that does this :) I've been using this internal POC of
> testing of OVS for ~ a year now, and it provides a pretty nice workflow for me.
>
> > In this case, one packet-based template will not work.
>
> Why not? I agree that "pre-calculating" all packets will not work, but the approach
> we have taken for this library is different. See below;
>
> > May we worth consider that use case into API framework first and add support
> > later for implementation as it may change the complete land space of API to have
> > better performance. Options like struct rte_gen logical object can have
> > N templates instead of one is an option on the table. :-)
>
> Agree - more complex usages have been designed for too. Let me explain;
>
> 1) A single gen instance uses a single template, and has "modifiers" that allow
> manipulation of the packet before sending. The gen->base_pkt is copied to the
> destination mbuf, and then updated by the modifiers. This approach is much better
> to allow for huge flow-counts (> 1 million?) as pre-calculating and storing 1 million
> packets is a waste of memory, and causes a lot of mem-IO for the datapath core.
>
> 2) The "modifiers" approach allows any number of things to be changed, with little
> mem-IO, and variable CPU cycle cost based on the modifiers themselves.
> If the CPU cycle cost of generating packets is too high, just add more cores :)
>
> 3) There are also some smarts we can apply for pre-calculating only a small amount of
> data per packet (e.g. uniformly-random distributed src ip). The memory footprint is
> lower than pre-calc of whole packets, and the runtime overhead of uniform-random
> is moved to configure time instead of on the datapath.
>
> 4) Dynamically generating packets by modification of templates allows for cool things
> to be added, e.g. adding timestamps to packets, and calculating latency can
> be done using the modifier concept and a protocol string "Ether()/IP()/UDP()/TSC()".
> If the packet is being decapped by the target application, the string params can provide
> context for where to "retrieve" the TSC from on RX in the generator: "TSC(rx_offset=30)".
> I've found this approach to be very flexible and nice, so am a big fan :)
>
> 5) In order to have multiple streams of totally-different traffic types (read "from multiple templates")
> the user can initialize multiple rte_gen instances. This allows applications that require multi-stream traffic
> to achieve that too, with the same abstraction as a single template stream. Initially the generator app is just
> providing a single stream, but this application can be expanded to many usages over the next year before 22.11 :)

OK. I thought "modifiers" will need some sort of critical section in
multiple producer use cases. If so,
one option could be N streams in one gen instance vs N gen instance.
Just my 2c. Anyway, you folks can decide
on one option vs another. Only my concern was including such features
affect the prototype of existing APIs or not?
In either case, No strong opinion.


>
> I could ramble on a bit more, but mostly diminishing returns I think... I'll just use this email as a reply to Thomas' tweet;
> https://twitter.com/tmonjalo/status/1337313985662771201
>
> Regards, -Harry

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 00/12] add packet generator library and example app
  2021-12-14 14:57 ` [PATCH 00/12] add packet generator library and example app Bruce Richardson
  2021-12-14 15:59   ` Randles, Ronan
@ 2022-01-12 16:18   ` Morten Brørup
  1 sibling, 0 replies; 62+ messages in thread
From: Morten Brørup @ 2022-01-12 16:18 UTC (permalink / raw)
  To: Bruce Richardson, Ronan Randles; +Cc: dev, harry.van.haaren

> From: Bruce Richardson [mailto:bruce.richardson@intel.com]
> Sent: Tuesday, 14 December 2021 15.58
> 
> On Tue, Dec 14, 2021 at 02:12:30PM +0000, Ronan Randles wrote:
> > This patchset introduces a Gen library for DPDK. This library
> provides an easy
> > way to generate traffic in order to test software based network
> components.
> >
> > This library enables the basic functionality required in the traffic
> generator.
> > This includes: raw data setting, packet Tx and Rx, creation and
> destruction of a
> >  Gen instance and various types of data parsing.
> > This functionality is implemented in "lib/gen/rte_gen.c". IPv4
> parsing
> > functionality is also added in "lib/net/rte_ip.c", this is then used
> in the gen
> > library.
> >
> > A sample app is included in "examples/generator" which shows the use
> of the gen
> > library in making a traffic generator. This can be used to generate
> traffic by
> > running the dpdk-generator generator executable. This sample app
> supports
> > runtime stats reporting (/gen/stats) and line rate limiting
> > (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> >
> > As more features are added to the gen library, the sample application
> will
> > become more powerful through the "/gen/packet" string parameter
> > (currently supports IP and Ether address setting). This will allow
> every
> > application to generate more complex traffic types in the future
> without
> > changing API.
> >
> 
> I think this is great to see, and sounds a good addition to DPDK. One
> thing
> to address in any v2 is to add more documentation for both the library
> and
> the example app. You need a chapter on the lib added to the programmers
> guide to help others use the library from their code, and a chapter on
> the
> generator example in the example apps guide.
> 
> More general question - if we do have a traffic generator in DPDK,
> would it
> be better in the "app" rather than the examples one? If it's only going
> to
> ever stay a simple example of using the lib, examples might be fine,
> but I
> suspect that it will get quite complicated if people start using it and
> adding more features, in which case a move to the "app" folder might be
> more appropriate. Thoughts?
> 
> /Bruce

If adding a traffic generator lib/app to DPDK itself, it should be able to evolve freely, unencumbered by the DPDK ABI/API stability requirements.

Also, it MUST be optional when building DPDK for production purposes. Consider the security perspective: If a network appliance based on DPDK is compromised by a hacker, you don't want it to include a traffic generator.

-Morten


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 01/12] net: add string to IPv4 parse function
  2021-12-14 14:12 ` [PATCH 01/12] net: add string to IPv4 parse function Ronan Randles
  2021-12-14 17:31   ` Morten Brørup
@ 2022-01-19 14:20   ` Thomas Monjalon
  1 sibling, 0 replies; 62+ messages in thread
From: Thomas Monjalon @ 2022-01-19 14:20 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dev, harry.van.haaren

14/12/2021 15:12, Ronan Randles:
> --- a/lib/net/rte_ip.h
> +++ b/lib/net/rte_ip.h
> +/**
> + * IP address parser.
> + *
> + * @param src_ip
> + *   The IP address to be parsed.
> + * @param output_addr
> + *   The array in which the parsed digits will be saved.
> + *
> + * @retval 0
> + *   Success.
> + * @retval -1
> + *   Failure due to invalid input arguments.
> + */
> +
> +__rte_experimental
> +int32_t
> +rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);

Is it similar to inet_aton() ?
Does it support IPv6? If not, why not adding a number 4 in the name?




^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 02/12] net: add function to pretty print IPv4
  2021-12-15 13:06           ` Ananyev, Konstantin
@ 2022-01-19 14:24             ` Thomas Monjalon
  2022-01-19 14:41               ` Van Haaren, Harry
  0 siblings, 1 reply; 62+ messages in thread
From: Thomas Monjalon @ 2022-01-19 14:24 UTC (permalink / raw)
  To: Morten Brørup, Stephen Hemminger, Randles, Ronan,
	Van Haaren, Harry, Ananyev, Konstantin
  Cc: dev

15/12/2021 14:06, Ananyev, Konstantin:
> 
> > > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > > Sent: Wednesday, 15 December 2021 04.21
> > >
> > > On Wed, 15 Dec 2021 01:06:14 +0000
> > > "Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote:
> > > > > > --- a/lib/net/rte_ip.h
> > > > > > +++ b/lib/net/rte_ip.h
> > > > > > @@ -444,6 +444,26 @@ __rte_experimental
> > > > > >  int32_t
> > > > > >  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > > > > >
> > > > > > +
> > > > > > +/**
> > > > > > + * Print IP address from 32 bit int into char * buffer.
> > > > > > + *
> > > > > > + * @param ip_addr
> > > > > > + *   ip address to be printed.
> > > > > > + * @param buffer
> > > > > > + *   The buffer the string will be saved into.
> > > > > > + * @param buffer_size
> > > > > > + *   size of buffer to be used.
> > > > > > + *
> > > > > > + * @retval 0
> > > > > > + *   Success.
> > > > > > + * @retval -1
> > > > > > + *   Failure due to invalid input arguments.
> > > > > > + */
> > > > > > +__rte_experimental
> > > > > > +int32_t
> > > > > > +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> > > > > > buffer_size);
> > > > > > +
> > > > >
> > > > > In continuation of my email reply about the IPv4 parse function...
> > > > >
> > > > > I have a few suggestions to the IPv4 print function too:
> > > > >
> > > > > The return value should be the number of characters written to the
> > > output string, and still -1 on error. With this modification, you could
> > > > > use the return type ssize_t instead of int32_t.
> > > > >
> > > > > Furthermore, I would prefer having the parameters in the same order
> > > as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please
> > > > > also notice the suggested changed type for the size, and the const
> > > added to the ip_addr.
> > > > >
> > > > Honestly, I don't understand why we need to introduce such functions
> > > > inside DPDK at all.
> > > > What's wrong with existing standard ones: inet_ntop() and
> > > inet_pton()?
> > >
> > > Agreed, I see no added value in reinventing here
> > 
> > I think that DPDK functions for converting all sorts of types to/from strings would be useful; not only IP addresses, but also MAC addresses,
> > TCP/UDP port numbers and VLAN IDs.
> 
> For MACs we already have:
> rte_ether_format_addr()/rte_ether_unformat_addr()
> 
> > 
> > If you don't like IP address string conversion functions in the net library, DPDK could have a string conversions library. That library could
> > expose a multitude of APIs for the same purpose, so the application can use the API that best fits each application use.
> 
> I don’t mind to add new functions into net lib, if they are useful ones.
> But for that particular case, I just don't see what is the reason to
> develop and maintain our own functions while existing analogues:
> - are well known, widely adopted and field proven
> - do provide the same or even more comprehensive functionality

+1
Waiting for an answer from the authors. One month silence so far.




^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 02/12] net: add function to pretty print IPv4
  2022-01-19 14:24             ` Thomas Monjalon
@ 2022-01-19 14:41               ` Van Haaren, Harry
  0 siblings, 0 replies; 62+ messages in thread
From: Van Haaren, Harry @ 2022-01-19 14:41 UTC (permalink / raw)
  To: Thomas Monjalon, Morten Brørup, Stephen Hemminger, Randles,
	Ronan, Ananyev, Konstantin
  Cc: dev

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Wednesday, January 19, 2022 2:24 PM
> To: Morten Brørup <mb@smartsharesystems.com>; Stephen Hemminger
> <stephen@networkplumber.org>; Randles, Ronan <ronan.randles@intel.com>;
> Van Haaren, Harry <harry.van.haaren@intel.com>; Ananyev, Konstantin
> <konstantin.ananyev@intel.com>
> Cc: dev@dpdk.org
> Subject: Re: [PATCH 02/12] net: add function to pretty print IPv4
> 
> 15/12/2021 14:06, Ananyev, Konstantin:
> >
> > > > From: Stephen Hemminger [mailto:stephen@networkplumber.org]
> > > > Sent: Wednesday, 15 December 2021 04.21
> > > >
> > > > On Wed, 15 Dec 2021 01:06:14 +0000
> > > > "Ananyev, Konstantin" <konstantin.ananyev@intel.com> wrote:
> > > > > > > --- a/lib/net/rte_ip.h
> > > > > > > +++ b/lib/net/rte_ip.h
> > > > > > > @@ -444,6 +444,26 @@ __rte_experimental
> > > > > > >  int32_t
> > > > > > >  rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
> > > > > > >
> > > > > > > +
> > > > > > > +/**
> > > > > > > + * Print IP address from 32 bit int into char * buffer.
> > > > > > > + *
> > > > > > > + * @param ip_addr
> > > > > > > + *   ip address to be printed.
> > > > > > > + * @param buffer
> > > > > > > + *   The buffer the string will be saved into.
> > > > > > > + * @param buffer_size
> > > > > > > + *   size of buffer to be used.
> > > > > > > + *
> > > > > > > + * @retval 0
> > > > > > > + *   Success.
> > > > > > > + * @retval -1
> > > > > > > + *   Failure due to invalid input arguments.
> > > > > > > + */
> > > > > > > +__rte_experimental
> > > > > > > +int32_t
> > > > > > > +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t
> > > > > > > buffer_size);
> > > > > > > +
> > > > > >
> > > > > > In continuation of my email reply about the IPv4 parse function...
> > > > > >
> > > > > > I have a few suggestions to the IPv4 print function too:
> > > > > >
> > > > > > The return value should be the number of characters written to the
> > > > output string, and still -1 on error. With this modification, you could
> > > > > > use the return type ssize_t instead of int32_t.
> > > > > >
> > > > > > Furthermore, I would prefer having the parameters in the same order
> > > > as snprintf(): char *str, size_t size, const uint32_t ip_addr. Please
> > > > > > also notice the suggested changed type for the size, and the const
> > > > added to the ip_addr.
> > > > > >
> > > > > Honestly, I don't understand why we need to introduce such functions
> > > > > inside DPDK at all.
> > > > > What's wrong with existing standard ones: inet_ntop() and
> > > > inet_pton()?
> > > >
> > > > Agreed, I see no added value in reinventing here
> > >
> > > I think that DPDK functions for converting all sorts of types to/from strings
> would be useful; not only IP addresses, but also MAC addresses,
> > > TCP/UDP port numbers and VLAN IDs.
> >
> > For MACs we already have:
> > rte_ether_format_addr()/rte_ether_unformat_addr()
> >
> > >
> > > If you don't like IP address string conversion functions in the net library, DPDK
> could have a string conversions library. That library could
> > > expose a multitude of APIs for the same purpose, so the application can use
> the API that best fits each application use.
> >
> > I don’t mind to add new functions into net lib, if they are useful ones.
> > But for that particular case, I just don't see what is the reason to
> > develop and maintain our own functions while existing analogues:
> > - are well known, widely adopted and field proven
> > - do provide the same or even more comprehensive functionality
> 
> +1
> Waiting for an answer from the authors. One month silence so far.

Hi All,

Ronan and I are working on a V2 patchset, and hope to share it in the next days.

Personally I think there is value in DPDK having various functionality "built in", and
if that functionality is available for e.g. Ether, I see no reason why IPv4 or other
protocols shouldn't have that functionality available. If I was a newcomer to DPDK,
I would find it difficult to understand why I can format/unformat Ether with DPDK,
but had to use Linux/Windows functions to format/unformat IP/UDP.

I liked Morten's feedback around real-world usage, however we have not got around
to implementing that functionality in the V2 patchset. We intended to propose that
change in signature/extra-functionality being added to V3.

I believe the gen library was discussed briefly at last tech-board call (unfortunately I was not
available to participate). Perhaps a quick chat/discussion at the next tech-board would be helpful
to identify community stance on the library? At that point the V2 should be available too.

Regards, -Harry


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-20 10:21         ` Van Haaren, Harry
@ 2022-01-19 14:56           ` Thomas Monjalon
  2022-01-20 10:21             ` Van Haaren, Harry
  0 siblings, 1 reply; 62+ messages in thread
From: Thomas Monjalon @ 2022-01-19 14:56 UTC (permalink / raw)
  To: Randles, Ronan, Van Haaren, Harry
  Cc: dev, Jerin Jacob, dev, Richardson, Bruce

20/12/2021 11:21, Van Haaren, Harry:
> From: Thomas Monjalon <thomas@monjalon.net>
> > 17/12/2021 12:40, Van Haaren, Harry:
> > > I could ramble on a bit more, but mostly diminishing returns I think...
> > > I'll just use this email as a reply to Thomas' tweet;
> > > https://twitter.com/tmonjalo/status/1337313985662771201
> > 
> > My original question was to know available applications,
> > not integrating such application in the DPDK repository.
> > 
> > I may me miss something obvious,
> > but I don't understand why trying to add a user app inside DPDK repo.
> 
> There are likely a few points-of-view on this particular topic; and I'm glad
> you mention it so we can discuss it clearly here.
> 
> There are two main parts to this patchset, the first is a packet generation library,
> with an easy to use string-based syntax. The *library* is designed to be extended in
> future to a range of "useful stuff" to do while generating packets.

The text syntax would be specific to this application
and not usable somewhere else, so it doesn't make sense as a lib.

> The packet generation
> *application* should have minimal features, and focus on ease-of-use (as suggested below).

It would be either a limited application,
or an ever-growing application.
If the latter, it should not be in the main DPDK repository in my opinion.

By the way, I don't think it is the responsibility of DPDK to generate packets.
I would prefer having an application using the already known scapy
or a graphical interface like Ostinato.
There are tons of approach to define packets to send (pCraft is another one).
DPDK should only manage the Tx part, and optionally Rx of forwarded packets.

> In order to test the DPDK code, we need a variety of unit tests, and a sample-application to show
> users how to use the library (as well as docs etc). For me, the interesting part is that it is a small
> step from a simple sample-app just for testing to a minimal tool for high-rate packet generation.
> 
> I think many users of DPDK first install DPDK, then wish for a tool to generate high traffic rates
> to test DPDK, and end up with a usability problem; DPDK does not include a usable packet generator.

I don't see any usability problem in using an external well known tool.
Learning a new tool provided by DPDK *is* a usabilty difficulty.

> To highlight this point; our own DPDK Docs simply ignore the requirement of packet-generation to
> actually have packets processed by skeleton: http://doc.dpdk.org/guides/sample_app_ug/skeleton.html 
> Our "quick start" on the website uses PCAP vdevs (avoiding the problem)  https://core.dpdk.org/doc/quick-start/
> Even searching the entire docs for "generate packet" doesn't give any relevant/useful results:
> http://doc.dpdk.org/guides/search.html?q=generate+packet&check_keywords=yes&area=default# 
> 
> Users could internet-search & find pktgen, moongen, trex, or similar tools. These tools are fantastic for experienced
> developers such as devs on this mailing list - we should *NOT* replicate these complex tools in DPDK itself. However,
> building any tool outside of DPDK repo requires more effort; another git-clone, another set of dependencies to install,
> perhaps another build-system to get used to. Particularly for people starting out with DPDK (who are likely finding
> it difficult to learn the various hugepage/PCI-binding etc), this is yet another problem to solve, or to give up.
> 
> So my proposal is as follows; let us add a simple DPDK traffic generator to DPDK. We can define its scope
> and its intended use, limiting the scope and capabilities. As before, I do NOT think it a good idea to build a
> complex and feature-rich packet generator. I do feel it useful to have an easy-to-use application in DPDK that
> is particularly designed for generating specific packets, at specific line-rates, and reports mpps returned.
> 
> Thoughts on adding an small scope-limited application to DPDK enabling ease-of-packet-generation for new users?

So you want a simple packet generator for simple benchmarks?
And for complex benchmarks, we use another tool?



^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 05/12] gen: add raw packet data API and tests
  2022-01-19 14:56           ` Thomas Monjalon
@ 2022-01-20 10:21             ` Van Haaren, Harry
  2022-01-21 10:45               ` Van Haaren, Harry
  0 siblings, 1 reply; 62+ messages in thread
From: Van Haaren, Harry @ 2022-01-20 10:21 UTC (permalink / raw)
  To: Thomas Monjalon, Randles, Ronan; +Cc: dev, Jerin Jacob, dev, Richardson, Bruce

> -----Original Message-----
> From: Thomas Monjalon <thomas@monjalon.net>
> Sent: Wednesday, January 19, 2022 2:56 PM
> To: Randles, Ronan <ronan.randles@intel.com>; Van Haaren, Harry
> <harry.van.haaren@intel.com>
> Cc: dev@dpdk.org; Jerin Jacob <jerinjacobk@gmail.com>; dev@dpdk.org;
> Richardson, Bruce <bruce.richardson@intel.com>
> Subject: Re: [PATCH 05/12] gen: add raw packet data API and tests
> 
> 20/12/2021 11:21, Van Haaren, Harry:
> > From: Thomas Monjalon <thomas@monjalon.net>
> > > 17/12/2021 12:40, Van Haaren, Harry:
> > > > I could ramble on a bit more, but mostly diminishing returns I think...
> > > > I'll just use this email as a reply to Thomas' tweet;
> > > > https://twitter.com/tmonjalo/status/1337313985662771201
> > >
> > > My original question was to know available applications,
> > > not integrating such application in the DPDK repository.
> > >
> > > I may me miss something obvious,
> > > but I don't understand why trying to add a user app inside DPDK repo.
> >
> > There are likely a few points-of-view on this particular topic; and I'm glad
> > you mention it so we can discuss it clearly here.
> >
> > There are two main parts to this patchset, the first is a packet generation
> library,
> > with an easy to use string-based syntax. The *library* is designed to be
> extended in
> > future to a range of "useful stuff" to do while generating packets.
> 
> The text syntax would be specific to this application
> and not usable somewhere else, so it doesn't make sense as a lib.

The text string format "Ether()/IP(src=1.2.3.4)/UDP()" is not application specific,
and the items being parsed can be easily added to; that's what I meant above.


> > The packet generation
> > *application* should have minimal features, and focus on ease-of-use (as
> suggested below).
> 
> It would be either a limited application,
> or an ever-growing application.
> If the latter, it should not be in the main DPDK repository in my opinion.
> 
> By the way, I don't think it is the responsibility of DPDK to generate packets.
> I would prefer having an application using the already known scapy
> or a graphical interface like Ostinato.
> There are tons of approach to define packets to send (pCraft is another one).
> DPDK should only manage the Tx part, and optionally Rx of forwarded packets.

Scapy did not meet the performance requirements for infrastructure testing use-cases.
The goal of gen lib is to provide a toolbox for high-speed "scapy-like" packet generation,
the above tools didn't seem like the right fit for my needs; hence Gen library.

> > In order to test the DPDK code, we need a variety of unit tests, and a sample-
> application to show
> > users how to use the library (as well as docs etc). For me, the interesting part is
> that it is a small
> > step from a simple sample-app just for testing to a minimal tool for high-rate
> packet generation.
> >
> > I think many users of DPDK first install DPDK, then wish for a tool to generate
> high traffic rates
> > to test DPDK, and end up with a usability problem; DPDK does not include a
> usable packet generator.
> 
> I don't see any usability problem in using an external well known tool.
> Learning a new tool provided by DPDK *is* a usabilty difficulty.
> 
> > To highlight this point; our own DPDK Docs simply ignore the requirement of
> packet-generation to
> > actually have packets processed by skeleton:
> http://doc.dpdk.org/guides/sample_app_ug/skeleton.html
> > Our "quick start" on the website uses PCAP vdevs (avoiding the problem)
> https://core.dpdk.org/doc/quick-start/
> > Even searching the entire docs for "generate packet" doesn't give any
> relevant/useful results:
> >
> http://doc.dpdk.org/guides/search.html?q=generate+packet&check_keywords=
> yes&area=default#
> >
> > Users could internet-search & find pktgen, moongen, trex, or similar tools.
> These tools are fantastic for experienced
> > developers such as devs on this mailing list - we should *NOT* replicate these
> complex tools in DPDK itself. However,
> > building any tool outside of DPDK repo requires more effort; another git-clone,
> another set of dependencies to install,
> > perhaps another build-system to get used to. Particularly for people starting
> out with DPDK (who are likely finding
> > it difficult to learn the various hugepage/PCI-binding etc), this is yet another
> problem to solve, or to give up.
> >
> > So my proposal is as follows; let us add a simple DPDK traffic generator to DPDK.
> We can define its scope
> > and its intended use, limiting the scope and capabilities. As before, I do NOT
> think it a good idea to build a
> > complex and feature-rich packet generator. I do feel it useful to have an easy-
> to-use application in DPDK that
> > is particularly designed for generating specific packets, at specific line-rates, and
> reports mpps returned.
> >
> > Thoughts on adding an small scope-limited application to DPDK enabling ease-
> of-packet-generation for new users?
> 
> So you want a simple packet generator for simple benchmarks?
> And for complex benchmarks, we use another tool?

In short, yes. Make simple things easy (gen lib in DPDK itself), and make difficult things
possible (by using TRex, Ostinato, Warp17, MoonGen, Pktgen, pCraft, Scapy, or other tools).

But to summarize, it seems there are multiple questions/concerns around merging the
gen library into DPDK. That's OK, this is not a critical feature to upstream, I thought it
might be useful to the wider community.

As some rework was done from the V1 to a V2, adding some protocols and generally
advancing the library, Ronan and I will post the V2 to list to make it publicly available.
We can mark the V2 as "not applicable" in patchwork once sent.

I will likely continue to use the Gen library for my own packet-generation & testing
needs. If in future somebody is interested in Gen library send me an email!

Regards, -Harry

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 00/15] add packet generator library and example app
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (13 preceding siblings ...)
  2021-12-15 12:31 ` Jerin Jacob
@ 2022-01-21 10:31 ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 01/15] net: add string to IPv4 parse function Ronan Randles
                     ` (14 more replies)
  2022-01-21 14:44 ` [PATCH 00/12] add packet generator library and example app Xueming(Steven) Li
  15 siblings, 15 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

V2
  - Add UDP protocol support
  - Add VXLAN protocol support
  - Removed gen/packet telemetry function as it was unimplemented
  - Fixed IPv4 endianness issue

As I am coming to the end of my internship I will now longer be working on this
project. As discussed on the thread [1] this patchset will be marked
non-applicable on patchwork. We are posting the V2 here to make the rework
publicly available.
  - Ronan

[1] DPDK Mailing list discussion around Gen library upstreaming;
http://mails.dpdk.org/archives/dev/2022-January/232845.html


This patchset introduces a Gen library for DPDK. This library provides an easy
way to generate traffic in order to test software based network components.

This library enables the basic functionality required in the traffic generator.
This includes: raw data setting, packet Tx and Rx, creation and destruction of a
 Gen instance and various types of data parsing.
This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
library.

A sample app is included in "examples/generator" which shows the use of the gen
library in making a traffic generator. This can be used to generate traffic by
running the dpdk-generator generator executable. This sample app supports
runtime stats reporting (/gen/stats) and line rate limiting
(/gen/mpps,<target traffic rate in mpps>) through telemetry.py.

As more features are added to the gen library, the sample application will
become more powerful through the "/gen/packet" string parameter
(currently supports IP and Ether address setting). This will allow every
application to generate more complex traffic types in the future without
changing API.

Harry van Haaren (6):
  gen: add files for initial traffic generation library
  gen: add basic Rx and Tx routines and tests
  gen: add raw packet data API and tests
  gen: add parsing infrastructure and Ether protocol
  gen: add gen IP parsing
  examples/generator: import code from basicfwd.c

Ronan Randles (9):
  net: add string to IPv4 parse function
  net: add function to pretty print IPv4
  examples/generator: enable gen library for traffic gen
  examples/generator: telemetry support
  examples/generator: link status check added
  examples/generator: line rate limiting
  gen: add UDP support
  net/vxlan: instance flag endianness refactored
  gen: add VXLAN support

 app/test/meson.build           |   4 +
 app/test/test_gen.c            | 186 +++++++++
 app/test/test_net.c            |  87 +++++
 doc/api/doxy-api-index.md      |   3 +-
 doc/api/doxy-api.conf.in       |   1 +
 examples/generator/main.c      | 470 ++++++++++++++++++++++
 examples/generator/meson.build |  13 +
 examples/meson.build           |   1 +
 lib/gen/meson.build            |   6 +
 lib/gen/rte_gen.c              | 694 +++++++++++++++++++++++++++++++++
 lib/gen/rte_gen.h              | 114 ++++++
 lib/gen/version.map            |  10 +
 lib/meson.build                |   1 +
 lib/net/meson.build            |   1 +
 lib/net/rte_ip.c               |  58 +++
 lib/net/rte_ip.h               |  38 ++
 lib/net/rte_vxlan.h            |  10 +-
 lib/net/version.map            |   7 +
 18 files changed, 1702 insertions(+), 2 deletions(-)
 create mode 100644 app/test/test_gen.c
 create mode 100644 app/test/test_net.c
 create mode 100644 examples/generator/main.c
 create mode 100644 examples/generator/meson.build
 create mode 100644 lib/gen/meson.build
 create mode 100644 lib/gen/rte_gen.c
 create mode 100644 lib/gen/rte_gen.h
 create mode 100644 lib/gen/version.map
 create mode 100644 lib/net/rte_ip.c

-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 01/15] net: add string to IPv4 parse function
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 02/15] net: add function to pretty print IPv4 Ronan Randles
                     ` (13 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles, Harry van Haaren

Added function that accepts ip string as a parameter and returns an ip
address represented by a uint32_t. Relevant unit test for this function
is also included.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/meson.build |  2 ++
 app/test/test_net.c  | 61 ++++++++++++++++++++++++++++++++++++++++++++
 lib/net/meson.build  |  1 +
 lib/net/rte_ip.c     | 43 +++++++++++++++++++++++++++++++
 lib/net/rte_ip.h     | 18 +++++++++++++
 lib/net/version.map  |  6 +++++
 6 files changed, 131 insertions(+)
 create mode 100644 app/test/test_net.c
 create mode 100644 lib/net/rte_ip.c

diff --git a/app/test/meson.build b/app/test/meson.build
index 344a609a4d..8cdfb783a9 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -100,6 +100,7 @@ test_sources = files(
         'test_meter.c',
         'test_mcslock.c',
         'test_mp_secondary.c',
+        'test_net.c',
         'test_per_lcore.c',
         'test_pflock.c',
         'test_pmd_perf.c',
@@ -177,6 +178,7 @@ test_deps = [
         'ipsec',
         'lpm',
         'member',
+        'net',
         'node',
         'pipeline',
         'port',
diff --git a/app/test/test_net.c b/app/test/test_net.c
new file mode 100644
index 0000000000..2cb7d3e1c9
--- /dev/null
+++ b/app/test/test_net.c
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+#include <rte_ip.h>
+#include <rte_common.h>
+#include "test.h"
+
+static int
+test_rte_ip_parse_addr(void)
+{
+	printf("Running IP parsing tests...\n");
+
+	struct str_ip_t {
+		const char *str;
+		uint32_t exp_output;
+		uint32_t expected_to_fail;
+	} str_ip_tests[] = {
+		{ .str = "1.2.3.4", .exp_output = RTE_IPV4(1, 2, 3, 4)},
+		{ .str = "192.168.255.255", .exp_output =
+				RTE_IPV4(192, 168, 255, 255)},
+		{ .str = "172.16.0.9", .exp_output =
+				RTE_IPV4(172, 16, 0, 9)},
+		{ .str = "1.2.3", .expected_to_fail = 1},
+		{ .str = "1.2.3.4.5", .expected_to_fail = 1},
+		{ .str = "fail.1.2.3", .expected_to_fail = 1},
+		{ .str = "", .expected_to_fail = 1},
+		{ .str = "1.2.3.fail", .expected_to_fail = 1}
+	};
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(str_ip_tests); i++) {
+		uint32_t test_addr;
+		int32_t err = rte_ip_parse_addr(str_ip_tests[i].str,
+							&test_addr);
+		if (!test_addr) {
+			if (str_ip_tests[i].expected_to_fail != 1)
+				return -1;
+		}
+
+		if (err || test_addr != str_ip_tests[i].exp_output) {
+			if (str_ip_tests[i].expected_to_fail != 1)
+				return -1;
+		}
+	}
+
+
+	return 0;
+}
+
+static int
+test_net_tests(void)
+{
+	int ret = test_rte_ip_parse_addr();
+	return ret;
+}
+
+REGISTER_TEST_COMMAND(net_autotest, test_net_tests);
diff --git a/lib/net/meson.build b/lib/net/meson.build
index e899846578..b2577a7592 100644
--- a/lib/net/meson.build
+++ b/lib/net/meson.build
@@ -26,6 +26,7 @@ headers = files(
 sources = files(
         'rte_arp.c',
         'rte_ether.c',
+        'rte_ip.c',
         'rte_net.c',
         'rte_net_crc.c',
 )
diff --git a/lib/net/rte_ip.c b/lib/net/rte_ip.c
new file mode 100644
index 0000000000..b859dfb640
--- /dev/null
+++ b/lib/net/rte_ip.c
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <string.h>
+#include <rte_ip.h>
+
+int32_t
+rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr)
+{
+	int32_t ret = 0;
+	char *current_position;
+
+	if (src_ip == NULL)
+		return -1;
+
+	char *tok = strdup(src_ip);
+	if (tok == NULL)
+		return -1;
+
+	char *current_digit = strtok_r(tok, ".", &current_position);
+
+	*output_addr = 0;
+	uint32_t i = 0;
+	while (current_digit) {
+		uint32_t shift = ((3 - i) * 8);
+		unsigned long parsed_value = strtoul(current_digit, NULL, 0)
+								<< shift;
+
+		if (parsed_value == 0 && strcmp(current_digit, "0"))
+			break;
+
+		*output_addr |= parsed_value;
+		current_digit = strtok_r(NULL, ".", &current_position);
+		i++;
+
+	}
+	if (i != 4)
+		return -1;
+
+	free(tok);
+	return ret;
+}
diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
index c575250852..188054fda4 100644
--- a/lib/net/rte_ip.h
+++ b/lib/net/rte_ip.h
@@ -426,6 +426,24 @@ rte_ipv4_udptcp_cksum_verify(const struct rte_ipv4_hdr *ipv4_hdr,
 	return 0;
 }
 
+/**
+ * IP address parser.
+ *
+ * @param src_ip
+ *   The IP address to be parsed.
+ * @param output_addr
+ *   The array in which the parsed digits will be saved.
+ *
+ * @retval 0
+ *   Success.
+ * @retval -1
+ *   Failure due to invalid input arguments.
+ */
+
+__rte_experimental
+int32_t
+rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
+
 /**
  * IPv6 Header
  */
diff --git a/lib/net/version.map b/lib/net/version.map
index 4f4330d1c4..c530d1a4e4 100644
--- a/lib/net/version.map
+++ b/lib/net/version.map
@@ -12,3 +12,9 @@ DPDK_22 {
 
 	local: *;
 };
+
+EXPERIMENTAL {
+	global:
+
+	rte_ip_parse_addr;
+};
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 02/15] net: add function to pretty print IPv4
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 01/15] net: add string to IPv4 parse function Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 16:20     ` Stephen Hemminger
  2022-01-21 10:31   ` [PATCH v2 03/15] gen: add files for initial traffic generation library Ronan Randles
                     ` (12 subsequent siblings)
  14 siblings, 1 reply; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This function accepts an uint32_t representation of an IP address and
produces a string representation stored in a char * buffer. Realavent
unit tests also included.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_net.c | 26 ++++++++++++++++++++++++++
 lib/net/rte_ip.c    | 15 +++++++++++++++
 lib/net/rte_ip.h    | 20 ++++++++++++++++++++
 lib/net/version.map |  1 +
 4 files changed, 62 insertions(+)

diff --git a/app/test/test_net.c b/app/test/test_net.c
index 2cb7d3e1c9..75beeea671 100644
--- a/app/test/test_net.c
+++ b/app/test/test_net.c
@@ -46,7 +46,32 @@ test_rte_ip_parse_addr(void)
 				return -1;
 		}
 	}
+	return 0;
+}
+
+static int
+test_rte_ip_print_addr(void)
+{
+	printf("Running IP printing tests...\n");
+	char buffer[128];
 
+	struct ip_str_t {
+		uint32_t ip_addr;
+		const char *exp_output;
+	} ip_str_tests[] = {
+		{ .ip_addr = 16909060, .exp_output = "1.2.3.4"},
+		{ .ip_addr = 3232301055, . exp_output = "192.168.255.255"},
+		{ .ip_addr = 2886729737, .exp_output = "172.16.0.9"}
+	};
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(ip_str_tests); i++) {
+		int32_t err = rte_ip_print_addr(ip_str_tests[i].ip_addr,
+								buffer, 128);
+
+		if (err || strcmp(buffer, ip_str_tests[i].exp_output))
+			return -1;
+	}
 
 	return 0;
 }
@@ -55,6 +80,7 @@ static int
 test_net_tests(void)
 {
 	int ret = test_rte_ip_parse_addr();
+	ret += test_rte_ip_print_addr();
 	return ret;
 }
 
diff --git a/lib/net/rte_ip.c b/lib/net/rte_ip.c
index b859dfb640..fbd9161317 100644
--- a/lib/net/rte_ip.c
+++ b/lib/net/rte_ip.c
@@ -41,3 +41,18 @@ rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr)
 	free(tok);
 	return ret;
 }
+
+int32_t
+rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t buffer_size)
+{
+	if (buffer == NULL)
+		return -1;
+
+	snprintf(buffer, buffer_size, "%u.%u.%u.%u",
+		(ip_addr >> 24),
+		(ip_addr >> 16) & UINT8_MAX,
+		(ip_addr >> 8) & UINT8_MAX,
+		 ip_addr & UINT8_MAX);
+
+	return 0;
+}
diff --git a/lib/net/rte_ip.h b/lib/net/rte_ip.h
index 188054fda4..e46f0b41ba 100644
--- a/lib/net/rte_ip.h
+++ b/lib/net/rte_ip.h
@@ -444,6 +444,26 @@ __rte_experimental
 int32_t
 rte_ip_parse_addr(const char *src_ip, uint32_t *output_addr);
 
+
+/**
+ * Print IP address from 32 bit int into char * buffer.
+ *
+ * @param ip_addr
+ *   ip address to be printed.
+ * @param buffer
+ *   The buffer the string will be saved into.
+ * @param buffer_size
+ *   size of buffer to be used.
+ *
+ * @retval 0
+ *   Success.
+ * @retval -1
+ *   Failure due to invalid input arguments.
+ */
+__rte_experimental
+int32_t
+rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t buffer_size);
+
 /**
  * IPv6 Header
  */
diff --git a/lib/net/version.map b/lib/net/version.map
index c530d1a4e4..7fe9892d03 100644
--- a/lib/net/version.map
+++ b/lib/net/version.map
@@ -17,4 +17,5 @@ EXPERIMENTAL {
 	global:
 
 	rte_ip_parse_addr;
+	rte_ip_print_addr;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 03/15] gen: add files for initial traffic generation library
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 01/15] net: add string to IPv4 parse function Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 02/15] net: add function to pretty print IPv4 Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 04/15] gen: add basic Rx and Tx routines and tests Ronan Randles
                     ` (11 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Harry van Haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds empty files to the DPDK build for a traffic
generation library, including the bare create and destroy functions.
Unit testing infrastructure is added for the create function.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/meson.build      |  2 ++
 app/test/test_gen.c       | 55 +++++++++++++++++++++++++++++++++++++++
 doc/api/doxy-api-index.md |  3 ++-
 doc/api/doxy-api.conf.in  |  1 +
 lib/gen/meson.build       |  5 ++++
 lib/gen/rte_gen.c         | 33 +++++++++++++++++++++++
 lib/gen/rte_gen.h         | 44 +++++++++++++++++++++++++++++++
 lib/gen/version.map       |  6 +++++
 lib/meson.build           |  1 +
 9 files changed, 149 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_gen.c
 create mode 100644 lib/gen/meson.build
 create mode 100644 lib/gen/rte_gen.c
 create mode 100644 lib/gen/rte_gen.h
 create mode 100644 lib/gen/version.map

diff --git a/app/test/meson.build b/app/test/meson.build
index 8cdfb783a9..c282493d10 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -66,6 +66,7 @@ test_sources = files(
         'test_fib6_perf.c',
         'test_func_reentrancy.c',
         'test_flow_classify.c',
+        'test_gen.c',
         'test_graph.c',
         'test_graph_perf.c',
         'test_hash.c',
@@ -173,6 +174,7 @@ test_deps = [
         'eventdev',
         'fib',
         'flow_classify',
+        'gen',
         'graph',
         'hash',
         'ipsec',
diff --git a/app/test/test_gen.c b/app/test/test_gen.c
new file mode 100644
index 0000000000..f53f4a6608
--- /dev/null
+++ b/app/test/test_gen.c
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <rte_common.h>
+#include <rte_gen.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static struct rte_mempool *mp;
+
+static int
+testsuite_setup(void)
+{
+	if (!mp) {
+		mp = rte_pktmbuf_pool_create("test_gen_mp", 8192, 256, 0, 2048,
+						SOCKET_ID_ANY);
+	}
+	return mp ? TEST_SUCCESS : TEST_FAILED;
+}
+
+static void
+testsuite_teardown(void)
+{
+	rte_mempool_free(mp);
+}
+
+static int
+test_gen_create(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+static struct unit_test_suite gen_suite  = {
+	.suite_name = "gen: packet generator unit test suite",
+	.setup = testsuite_setup,
+	.teardown = testsuite_teardown,
+	.unit_test_cases = {
+		TEST_CASE_ST(NULL, NULL, test_gen_create),
+		TEST_CASES_END() /**< NULL terminate unit test array */
+	}
+};
+
+static int
+test_gen_suite(void)
+{
+	return unit_test_suite_runner(&gen_suite);
+}
+
+REGISTER_TEST_COMMAND(gen_autotest, test_gen_suite);
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 4245b9635c..f7ddadd21a 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -220,7 +220,8 @@ The public API headers are grouped by topics:
   [log]                (@ref rte_log.h),
   [errno]              (@ref rte_errno.h),
   [trace]              (@ref rte_trace.h),
-  [trace_point]        (@ref rte_trace_point.h)
+  [trace_point]        (@ref rte_trace_point.h),
+  [gen]                (@ref rte_gen.h)
 
 - **misc**:
   [EAL config]         (@ref rte_eal.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index db2ca9b6ed..6344e949d9 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -41,6 +41,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/eventdev \
                           @TOPDIR@/lib/fib \
                           @TOPDIR@/lib/flow_classify \
+                          @TOPDIR@/lib/gen \
                           @TOPDIR@/lib/gpudev \
                           @TOPDIR@/lib/graph \
                           @TOPDIR@/lib/gro \
diff --git a/lib/gen/meson.build b/lib/gen/meson.build
new file mode 100644
index 0000000000..3c5d854645
--- /dev/null
+++ b/lib/gen/meson.build
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2021 Intel Corporation
+
+sources = files('rte_gen.c')
+headers = files('rte_gen.h')
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
new file mode 100644
index 0000000000..d993772422
--- /dev/null
+++ b/lib/gen/rte_gen.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include "rte_gen.h"
+
+#include <rte_malloc.h>
+
+/** Structure that represents a traffic generator. */
+struct rte_gen {
+	/* Mempool that buffers are retrieved from. */
+	struct rte_mempool *mp;
+};
+
+/* Allocate and initialize a traffic generator instance. */
+struct rte_gen *
+rte_gen_create(struct rte_mempool *mempool)
+{
+	struct rte_gen *gen = rte_zmalloc(NULL, sizeof(*gen), 0);
+	if (gen == NULL)
+		return NULL;
+
+	gen->mp = mempool;
+
+	return gen;
+}
+
+/* Free a traffic generator instance. */
+void
+rte_gen_destroy(struct rte_gen *gen)
+{
+	rte_free(gen);
+}
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
new file mode 100644
index 0000000000..5b30430f9e
--- /dev/null
+++ b/lib/gen/rte_gen.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#ifndef _RTE_GEN_H_
+#define _RTE_GEN_H_
+
+/**
+ * @file
+ * RTE gen
+ *
+ * A library for the generation of packets, to allow easy generation
+ * of various flows of packets.
+ */
+
+#include <stdint.h>
+#include <rte_compat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/** Structure that represents a logical traffic generator. */
+struct rte_gen;
+
+/* Forward declarations for DPDK componeents. */
+struct rte_mempool;
+
+/* Allocate and initialize a traffic generator instance. */
+__rte_experimental
+struct rte_gen *
+rte_gen_create(struct rte_mempool *mempool);
+
+/* Free a traffic generator instance. */
+__rte_experimental
+void
+rte_gen_destroy(struct rte_gen *gen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_GEN_H_ */
diff --git a/lib/gen/version.map b/lib/gen/version.map
new file mode 100644
index 0000000000..d8a26eb53a
--- /dev/null
+++ b/lib/gen/version.map
@@ -0,0 +1,6 @@
+EXPERIMENTAL {
+	global:
+
+	rte_gen_create;
+	rte_gen_destroy;
+};
diff --git a/lib/meson.build b/lib/meson.build
index fbaa6ef7c2..bdddc059ca 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -35,6 +35,7 @@ libraries = [
         'efd',
         'eventdev',
         'gpudev',
+        'gen',
         'gro',
         'gso',
         'ip_frag',
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 04/15] gen: add basic Rx and Tx routines and tests
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (2 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 03/15] gen: add files for initial traffic generation library Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 05/15] gen: add raw packet data API " Ronan Randles
                     ` (10 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Harry van Haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit introduces functions for basic mbuf rx and tx.
This commit also contains a unit test that calls rte_gen_rx_burst
and rte_gen_tx_burst to send bursts of 32 packets repeatedly in
order to verify their functionality

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_gen.c | 46 ++++++++++++++++++++++++++++++++++++++++++
 lib/gen/meson.build |  1 +
 lib/gen/rte_gen.c   | 43 +++++++++++++++++++++++++++++++++++++++
 lib/gen/rte_gen.h   | 49 +++++++++++++++++++++++++++++++++++++++++++++
 lib/gen/version.map |  2 ++
 5 files changed, 141 insertions(+)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index f53f4a6608..ceacd8c7c4 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -8,6 +8,8 @@
 
 #include "test.h"
 
+#define BURST_MAX 32
+
 static struct rte_mempool *mp;
 
 static int
@@ -36,12 +38,56 @@ test_gen_create(void)
 	return 0;
 }
 
+static int
+test_gen_basic_rxtx(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	struct rte_mbuf *bufs[BURST_MAX];
+	uint16_t nb_rx = rte_gen_rx_burst(gen, bufs, BURST_MAX);
+	TEST_ASSERT_EQUAL(nb_rx, BURST_MAX, "Expected rx packet burst.");
+
+	uint64_t latency[BURST_MAX];
+	uint16_t nb_tx = rte_gen_tx_burst(gen, bufs, latency, BURST_MAX);
+	TEST_ASSERT_EQUAL(nb_tx, BURST_MAX, "Expected tx packet burst.");
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+static int
+test_gen_loop_rxtx(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	uint32_t total_sent = 0;
+
+	while (total_sent < 1000000) {
+		struct rte_mbuf *bufs[BURST_MAX];
+		uint16_t nb_rx = rte_gen_rx_burst(gen, bufs, BURST_MAX);
+		TEST_ASSERT_EQUAL(nb_rx, BURST_MAX, "Expected rx packet burst.");
+
+		uint64_t latency[BURST_MAX];
+		uint16_t nb_tx = rte_gen_tx_burst(gen, bufs, latency, nb_rx);
+		TEST_ASSERT_EQUAL(nb_tx, BURST_MAX, "Expected tx packet burst.");
+
+		total_sent += nb_tx;
+	}
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
 static struct unit_test_suite gen_suite  = {
 	.suite_name = "gen: packet generator unit test suite",
 	.setup = testsuite_setup,
 	.teardown = testsuite_teardown,
 	.unit_test_cases = {
 		TEST_CASE_ST(NULL, NULL, test_gen_create),
+		TEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),
+		TEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/gen/meson.build b/lib/gen/meson.build
index 3c5d854645..753984cbba 100644
--- a/lib/gen/meson.build
+++ b/lib/gen/meson.build
@@ -3,3 +3,4 @@
 
 sources = files('rte_gen.c')
 headers = files('rte_gen.h')
+deps += ['mempool', 'mbuf']
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index d993772422..f0ad57fa81 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -4,8 +4,11 @@
 
 #include "rte_gen.h"
 
+#include <rte_mbuf.h>
 #include <rte_malloc.h>
 
+#define GEN_MAX_BURST 32
+
 /** Structure that represents a traffic generator. */
 struct rte_gen {
 	/* Mempool that buffers are retrieved from. */
@@ -31,3 +34,43 @@ rte_gen_destroy(struct rte_gen *gen)
 {
 	rte_free(gen);
 }
+
+uint16_t
+rte_gen_rx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **rx_pkts,
+		 const uint16_t nb_pkts)
+{
+	/* Get a bulk of nb_pkts from the mempool. */
+	int err = rte_mempool_get_bulk(gen->mp, (void **)rx_pkts, nb_pkts);
+	if (err)
+		return 0;
+
+	const uint32_t pkt_len = 64;
+
+	uint32_t i;
+	for (i = 0; i < nb_pkts; i++) {
+		struct rte_mbuf *m = rx_pkts[i];
+		uint8_t *pkt_data = rte_pktmbuf_mtod(m, uint8_t *);
+
+		memset(pkt_data, 0, pkt_len);
+
+		m->pkt_len  = pkt_len;
+		m->data_len = pkt_len;
+	}
+
+	return nb_pkts;
+}
+
+uint16_t
+rte_gen_tx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **tx_pkts,
+		 uint64_t *pkt_latencies,
+		 const uint16_t nb_pkts)
+{
+	RTE_SET_USED(gen);
+	RTE_SET_USED(pkt_latencies);
+
+	rte_pktmbuf_free_bulk(tx_pkts, nb_pkts);
+
+	return nb_pkts;
+}
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
index 5b30430f9e..09ee1e8872 100644
--- a/lib/gen/rte_gen.h
+++ b/lib/gen/rte_gen.h
@@ -25,6 +25,7 @@ extern "C" {
 struct rte_gen;
 
 /* Forward declarations for DPDK componeents. */
+struct rte_mbuf;
 struct rte_mempool;
 
 /* Allocate and initialize a traffic generator instance. */
@@ -37,6 +38,54 @@ __rte_experimental
 void
 rte_gen_destroy(struct rte_gen *gen);
 
+/**
+ * Call to receive a burst of generated packets
+ *
+ * @param gen
+ *   Gen instance to be used.
+ * @param rx_pkts
+ *   mbuf where packets will be generated.
+ * @param nb_pkts
+ *   number of packets to be generated
+ *
+ * @retval nb_pkts
+ *   On success the number of rx'ed packets will be returned
+ * @retval 0
+ *   Failure.
+ */
+__rte_experimental
+uint16_t
+rte_gen_rx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **rx_pkts,
+		 const uint16_t nb_pkts);
+
+/** Call to transmit a burst of traffic back to the generator.
+ * This allows the generator to calculate stats/properties of the stream.
+ *
+ * If the pkt_latencies parameter is not NULL, it is expected to be a pointer
+ * to an array of uint64_t values that has nb_pkts in length. Each individual
+ * packet latency will be stored to the array.
+ *
+ * @param gen
+ *   Gen instance to be used.
+ * @param tx_pkts
+ *   mbuf to be used to tx packets
+ * @param pkt_latencies
+ *   Array to store latencies of sent packets
+ * @param nb_pkts
+ *   The number of packets to be tx'ed
+ *
+ * @retval nb_pkts
+ *   On success the number of packets tx'ed is returned
+ */
+__rte_experimental
+uint16_t
+rte_gen_tx_burst(struct rte_gen *gen,
+		 struct rte_mbuf **tx_pkts,
+		 uint64_t *pkt_latencies,
+		 const uint16_t nb_pkts);
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/gen/version.map b/lib/gen/version.map
index d8a26eb53a..bdd25add6f 100644
--- a/lib/gen/version.map
+++ b/lib/gen/version.map
@@ -3,4 +3,6 @@ EXPERIMENTAL {
 
 	rte_gen_create;
 	rte_gen_destroy;
+	rte_gen_rx_burst;
+	rte_gen_tx_burst;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 05/15] gen: add raw packet data API and tests
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (3 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 04/15] gen: add basic Rx and Tx routines and tests Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 06/15] gen: add parsing infrastructure and Ether protocol Ronan Randles
                     ` (9 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Harry van Haaren

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds a new API to gen, allowing the caller to set
raw packet data with a size. Tests are added to test the new
API with randomized packet data.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 app/test/test_gen.c | 33 ++++++++++++++++++++++++
 lib/gen/rte_gen.c   | 62 +++++++++++++++++++++++++++++++++++++++++----
 lib/gen/rte_gen.h   |  9 +++++++
 lib/gen/version.map |  1 +
 4 files changed, 100 insertions(+), 5 deletions(-)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index ceacd8c7c4..b60ceaef8a 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -5,6 +5,7 @@
 #include <rte_common.h>
 #include <rte_gen.h>
 #include <rte_mbuf.h>
+#include <rte_random.h>
 
 #include "test.h"
 
@@ -75,6 +76,37 @@ test_gen_loop_rxtx(void)
 
 		total_sent += nb_tx;
 	}
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+static int
+test_gen_packet_set_raw(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	/* Set a raw packet payload, and ensure the next received packet has
+	 * that packet data as contents and size.
+	 */
+	uint64_t pkt_data[8];
+	uint32_t i;
+	for (i = 0; i < 8; i++)
+		pkt_data[i] = rte_rand();
+
+	int32_t err = rte_gen_packet_set_raw(gen, (void *)pkt_data, 64);
+	TEST_ASSERT_EQUAL(err, 0, "Expected set raw() to return success.");
+
+	struct rte_mbuf *bufs[BURST_MAX];
+	uint16_t nb_rx = rte_gen_rx_burst(gen, bufs, 1);
+	TEST_ASSERT_EQUAL(nb_rx, 1, "Expected rx packet burst.");
+
+	void *mbuf_data = rte_pktmbuf_mtod(bufs[0], void *);
+	int32_t data_equal = memcmp(pkt_data, mbuf_data, 64) == 0;
+	TEST_ASSERT_EQUAL(data_equal, 1,
+		"Expected packet data equal to input data.");
+
+	rte_pktmbuf_free(bufs[0]);
 
 	rte_gen_destroy(gen);
 	return 0;
@@ -88,6 +120,7 @@ static struct unit_test_suite gen_suite  = {
 		TEST_CASE_ST(NULL, NULL, test_gen_create),
 		TEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),
 		TEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),
+		TEST_CASE_ST(NULL, NULL, test_gen_packet_set_raw),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index f0ad57fa81..8a54c4bcdb 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -6,13 +6,25 @@
 
 #include <rte_mbuf.h>
 #include <rte_malloc.h>
+#include <rte_hexdump.h>
+#include <rte_log.h>
+
+RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
+
+#define GEN_LOG(level, fmt, args...)				\
+	rte_log(RTE_LOG_ ## level, gen_logtype, "%s(): " fmt,	\
+		__func__, ## args)
 
 #define GEN_MAX_BURST 32
+#define GEN_INIT_PKT_SIZE 64
 
 /** Structure that represents a traffic generator. */
 struct rte_gen {
 	/* Mempool that buffers are retrieved from. */
 	struct rte_mempool *mp;
+
+	/* Packet template to send. */
+	struct rte_mbuf *base_pkt;
 };
 
 /* Allocate and initialize a traffic generator instance. */
@@ -25,6 +37,15 @@ rte_gen_create(struct rte_mempool *mempool)
 
 	gen->mp = mempool;
 
+	uint8_t data[GEN_INIT_PKT_SIZE];
+	memset(data, 0, GEN_INIT_PKT_SIZE);
+	int32_t err = rte_gen_packet_set_raw(gen, data, GEN_INIT_PKT_SIZE);
+	if (err) {
+		GEN_LOG(ERR, "Failed to set initial packet\n");
+		rte_free(gen);
+		return NULL;
+	}
+
 	return gen;
 }
 
@@ -32,9 +53,37 @@ rte_gen_create(struct rte_mempool *mempool)
 void
 rte_gen_destroy(struct rte_gen *gen)
 {
+	rte_pktmbuf_free(gen->base_pkt);
 	rte_free(gen);
 }
 
+int32_t
+rte_gen_packet_set_raw(struct rte_gen *gen,
+		       const uint8_t *raw_data,
+		       uint32_t raw_data_size)
+{
+
+	struct rte_mbuf *new_pkt = rte_pktmbuf_alloc(gen->mp);
+	if (!new_pkt) {
+		GEN_LOG(ERR, "Failed to retireve mbuf for parser\n");
+		return -ENOMEM;
+	}
+
+	uint8_t *base_data = rte_pktmbuf_mtod(new_pkt, uint8_t *);
+	new_pkt->pkt_len = raw_data_size;
+	new_pkt->data_len = raw_data_size;
+	rte_memcpy(base_data, raw_data, raw_data_size);
+
+	/* If old packet exists, free it. */
+	struct rte_mbuf *old_pkt = gen->base_pkt;
+	gen->base_pkt = new_pkt;
+
+	if (old_pkt)
+		rte_pktmbuf_free(old_pkt);
+
+	return 0;
+}
+
 uint16_t
 rte_gen_rx_burst(struct rte_gen *gen,
 		 struct rte_mbuf **rx_pkts,
@@ -45,17 +94,20 @@ rte_gen_rx_burst(struct rte_gen *gen,
 	if (err)
 		return 0;
 
-	const uint32_t pkt_len = 64;
+	if (!gen->base_pkt)
+		return 0;
+
+	const uint32_t base_size = gen->base_pkt->pkt_len;
+	const uint8_t *base_data = rte_pktmbuf_mtod(gen->base_pkt, uint8_t *);
 
 	uint32_t i;
 	for (i = 0; i < nb_pkts; i++) {
 		struct rte_mbuf *m = rx_pkts[i];
 		uint8_t *pkt_data = rte_pktmbuf_mtod(m, uint8_t *);
 
-		memset(pkt_data, 0, pkt_len);
-
-		m->pkt_len  = pkt_len;
-		m->data_len = pkt_len;
+		rte_memcpy(pkt_data, base_data, base_size);
+		m->pkt_len = base_size;
+		m->data_len = base_size;
 	}
 
 	return nb_pkts;
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
index 09ee1e8872..9119331673 100644
--- a/lib/gen/rte_gen.h
+++ b/lib/gen/rte_gen.h
@@ -85,6 +85,15 @@ rte_gen_tx_burst(struct rte_gen *gen,
 		 uint64_t *pkt_latencies,
 		 const uint16_t nb_pkts);
 
+/* Update the packet being sent to the provided raw data.
+ * @retval 0 Success.
+ * @retval -ENOMEM No memory available.
+ */
+__rte_experimental
+int32_t
+rte_gen_packet_set_raw(struct rte_gen *gen,
+		       const uint8_t *raw_data,
+		       uint32_t raw_data_size);
 
 #ifdef __cplusplus
 }
diff --git a/lib/gen/version.map b/lib/gen/version.map
index bdd25add6f..d75e0b4bac 100644
--- a/lib/gen/version.map
+++ b/lib/gen/version.map
@@ -5,4 +5,5 @@ EXPERIMENTAL {
 	rte_gen_destroy;
 	rte_gen_rx_burst;
 	rte_gen_tx_burst;
+	rte_gen_packet_set_raw;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 06/15] gen: add parsing infrastructure and Ether protocol
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (4 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 05/15] gen: add raw packet data API " Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 07/15] gen: add gen IP parsing Ronan Randles
                     ` (8 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Harry van Haaren

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds parsing infrastructure and support
for Ether parsing. Appropriate unit tests are also added

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
---
 app/test/test_gen.c |  29 +++++
 lib/gen/meson.build |   2 +-
 lib/gen/rte_gen.c   | 299 ++++++++++++++++++++++++++++++++++++++++++++
 lib/gen/rte_gen.h   |  12 ++
 lib/gen/version.map |   1 +
 5 files changed, 342 insertions(+), 1 deletion(-)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index b60ceaef8a..324582d0a5 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -112,6 +112,34 @@ test_gen_packet_set_raw(void)
 	return 0;
 }
 
+static int
+test_gen_packet_parse_string(void)
+{
+	struct rte_gen *gen = rte_gen_create(mp);
+	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
+
+	struct str_parse_t {
+		const char *str;
+	} pkt_strings[] = {
+		{ .str = "Ether()"},
+		{ .str = "Ether()/"},
+		{ .str = "/Ether()"},
+		{ .str = "/Ether()/"}
+	};
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(pkt_strings); i++) {
+		const char *pkt_str = pkt_strings[i].str;
+		int32_t err = rte_gen_packet_parse_string(gen, pkt_str, NULL);
+		TEST_ASSERT_EQUAL(err, 0, "Expected string %s to parse.",
+				pkt_str);
+	}
+
+	rte_gen_destroy(gen);
+	return 0;
+}
+
+
 static struct unit_test_suite gen_suite  = {
 	.suite_name = "gen: packet generator unit test suite",
 	.setup = testsuite_setup,
@@ -121,6 +149,7 @@ static struct unit_test_suite gen_suite  = {
 		TEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),
 		TEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),
 		TEST_CASE_ST(NULL, NULL, test_gen_packet_set_raw),
+		TEST_CASE_ST(NULL, NULL, test_gen_packet_parse_string),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/gen/meson.build b/lib/gen/meson.build
index 753984cbba..b3a55564f4 100644
--- a/lib/gen/meson.build
+++ b/lib/gen/meson.build
@@ -3,4 +3,4 @@
 
 sources = files('rte_gen.c')
 headers = files('rte_gen.h')
-deps += ['mempool', 'mbuf']
+deps += ['mempool', 'mbuf', 'net']
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index 8a54c4bcdb..4d3fe4017f 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -9,12 +9,18 @@
 #include <rte_hexdump.h>
 #include <rte_log.h>
 
+#include <rte_ether.h>
+
 RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
 
 #define GEN_LOG(level, fmt, args...)				\
 	rte_log(RTE_LOG_ ## level, gen_logtype, "%s(): " fmt,	\
 		__func__, ## args)
 
+/* Don't prefix with function name, breaks the Scapy style formatting. */
+#define GEN_LOG_PROTOCOL(level, fmt, args...)			\
+	rte_log(RTE_LOG_ ## level, gen_logtype, fmt, ## args)
+
 #define GEN_MAX_BURST 32
 #define GEN_INIT_PKT_SIZE 64
 
@@ -126,3 +132,296 @@ rte_gen_tx_burst(struct rte_gen *gen,
 
 	return nb_pkts;
 }
+
+enum GEN_PROTO {
+	GEN_PROTO_INVALID,
+	GEN_PROTO_ETHER,
+
+	/* Must be last. */
+	GEN_PROTO_COUNT,
+};
+
+typedef void (*gen_log_func)(void *data, const char *indent);
+
+/* Structure for holding offset and function pointers for protocol. */
+struct protocol_meta {
+	/* Byte offset into packet where this protocol starts. */
+	uint32_t offset;
+	/* Function to call to log the packet's information. */
+	gen_log_func log_func;
+};
+
+/* Allow up to 32 nexted '/' characters in the protocol string. */
+#define GEN_PROTO_PARSE_MAX 16
+
+/* Structure to hold state required while parsing. */
+struct gen_parser {
+	/* Mbuf the parsed data is being put into. */
+	struct rte_mbuf *mbuf;
+	uint8_t *mbuf_data;
+
+	/* Offset into the packet data to parse to next. */
+	uint32_t buf_write_offset;
+
+	/* Parsing state. */
+	uint8_t parse_iter;
+	char indent_str[(GEN_PROTO_PARSE_MAX * 2) + 1];
+
+	/* String being parsed. */
+	char *parse_string;
+	char *parse_strtok_save_ptr;
+
+	/* Store metadata for parse/display of protocols.  */
+	struct protocol_meta proto_meta[GEN_PROTO_PARSE_MAX];
+
+	/* Per protocol hit counters. */
+	uint32_t proto_hit_counters[GEN_PROTO_COUNT];
+};
+
+/* Forward declaration of recursive parsing function.
+ * @param inner reports back the inner protocol that was handled. This is often
+ * required for the outer protocol to indicate what the inner protocol is.
+ */
+static int32_t
+gen_parser_parse_next(struct gen_parser *parser, enum GEN_PROTO *inner);
+
+/* Return void pointer to the position in the data buffer to parse into. */
+static inline void *
+gen_parser_get_data_ptr(struct gen_parser *parser)
+{
+	return &parser->mbuf_data[parser->buf_write_offset];
+}
+
+/* Initialize a parser structure. */
+static int32_t
+gen_parser_init(struct gen_parser *parser, struct rte_gen *gen,
+		const char *pkt_string)
+{
+	/* Initialize own memory to zero. */
+	memset(parser, 0, sizeof(*parser));
+
+	/* Duplicate string for tokenizing string. */
+	parser->parse_string = strdup(pkt_string);
+	if (!parser->parse_string)
+		goto error;
+
+	/* Allocate mbuf to parse packet into. */
+	parser->mbuf = rte_pktmbuf_alloc(gen->mp);
+	if (!parser->mbuf)
+		goto error;
+
+	parser->mbuf_data = rte_pktmbuf_mtod(parser->mbuf, uint8_t *);
+
+	return 0;
+
+error:
+	free(parser->parse_string);
+	return -ENOMEM;
+}
+
+static void
+gen_log_ether(void *data, const char *indent)
+{
+	struct rte_ether_hdr *eth = data;
+	char src[64];
+	char dst[64];
+
+	rte_ether_format_addr(src, 64, &eth->src_addr);
+	rte_ether_format_addr(dst, 64, &eth->dst_addr);
+	const char *type_str;
+	switch (rte_be_to_cpu_16(eth->ether_type)) {
+	case RTE_ETHER_TYPE_IPV4:
+		type_str = "IPv4";
+		break;
+	default:
+		type_str = "0x9000";
+		break;
+	};
+	GEN_LOG_PROTOCOL(DEBUG,
+		"###[ Ethernet ]###\n%sdst= %s\n%ssrc= %s\n%stype= %s\n",
+		indent, dst, indent, src, indent, type_str);
+}
+
+/* Ether(...) string detected, supports parameters:
+ * - dst : Destination MAC in 00:11:22:33:44:55 or 0011:2233:4455 forms.
+ * - src : Source MAC in the same forms.
+ * Note:
+ * - type is set based on the next header
+ */
+static int32_t
+gen_parse_ether(struct gen_parser *parser, char *protocol_str)
+{
+	struct rte_ether_hdr *eth = gen_parser_get_data_ptr(parser);
+
+	char *dst_ptr = strstr(protocol_str, "dst=");
+	if (dst_ptr) {
+		char *dup = strdup(dst_ptr);
+		rte_ether_unformat_addr(&dup[4], &eth->dst_addr);
+		free(dup);
+	} else
+		rte_ether_unformat_addr("ff:ff:ff:ff:ff:ff", &eth->dst_addr);
+
+	char *src_ptr = strstr(protocol_str, "src=");
+	if (src_ptr)
+		rte_ether_unformat_addr(&src_ptr[4], &eth->src_addr);
+	else
+		rte_ether_unformat_addr("00:00:00:00:00:00", &eth->src_addr);
+
+	/* Move up write pointer in packet. */
+	parser->buf_write_offset += sizeof(*eth);
+
+	/* Recurse and handle inner protocol. */
+	enum GEN_PROTO inner;
+	int32_t err = gen_parser_parse_next(parser, &inner);
+	if (err) {
+		GEN_LOG(ERR, "parser parse next() error %d\n", err);
+		return err;
+	}
+
+	switch (inner) {
+	default:
+		eth->ether_type = rte_cpu_to_be_16(0x9000);
+		break;
+	};
+	return 0;
+}
+
+/* (Name, Function-pointer) pairs for supported parse types */
+typedef int32_t (*gen_parse_func)(struct gen_parser *parser,
+				char *protocol_str);
+
+struct gen_parse_func_t {
+	const char *name;
+	enum GEN_PROTO proto;
+	gen_parse_func parse_func;
+	gen_log_func log_func;
+};
+
+/* Mapping from string to function to parse that protocol. */
+static struct gen_parse_func_t gen_protocols[] = {
+	{
+		.name = "Ether(",
+		.proto = GEN_PROTO_ETHER,
+		.parse_func = gen_parse_ether,
+		.log_func = gen_log_ether,
+	}
+};
+
+/* Function to tokenize and parse each segment of a string.
+ * @param outer indicates the protocol before this one.
+ * @param inner returns the protocol that is parsed here/now.
+ */
+static int32_t
+gen_parser_parse_next(struct gen_parser *parser,
+			enum GEN_PROTO *inner_proto)
+{
+	/* Tokenize the input string based on '/' character. */
+	char *tok_str = (parser->parse_iter == 0) ?
+					parser->parse_string : NULL;
+	parser->parse_string = strtok_r(tok_str, "/",
+					&parser->parse_strtok_save_ptr);
+
+	/* End protocol parsing recursion when parse_string is NULL, or max
+	 * protocol recursion depth is reached.
+	 */
+	if (!parser->parse_string ||
+			parser->parse_iter >= GEN_PROTO_PARSE_MAX) {
+		struct rte_mbuf *mbuf = parser->mbuf;
+		mbuf->data_len = parser->buf_write_offset;
+		mbuf->pkt_len = parser->buf_write_offset;
+		GEN_LOG(DEBUG, "packet length %d\n", mbuf->pkt_len);
+		return 0;
+	}
+
+	uint32_t i;
+	/* Loop over protocols, and identify the parse function to call. */
+	for (i = 0; i < RTE_DIM(gen_protocols); i++) {
+		const char *proto = gen_protocols[i].name;
+		uint32_t proto_len = strlen(proto);
+		if (strncmp(proto, parser->parse_string, proto_len))
+			continue;
+
+		/* Store the log function pointer to output later. */
+		uint32_t iter = parser->parse_iter;
+		parser->proto_hit_counters[i]++;
+		struct protocol_meta *meta = &parser->proto_meta[iter];
+
+		if (gen_protocols[i].log_func == NULL) {
+			GEN_LOG(ERR, "Missing log function, failed to log %s\n",
+									proto);
+			return -1;
+		}
+		meta->log_func = gen_protocols[i].log_func;
+		meta->offset = parser->buf_write_offset;
+
+		if (gen_protocols[i].parse_func == NULL) {
+			GEN_LOG(ERR, "Missing parse function, failed to parse %s\n"
+								, proto);
+			return -1;
+		}
+		/* Handle protocol recursively. */
+		parser->parse_iter++;
+		int err = gen_protocols[i].parse_func(parser,
+							parser->parse_string);
+		*inner_proto = gen_protocols[i].proto;
+
+		return err;
+	}
+
+	GEN_LOG(ERR, "parser does not understand protocol %s\n",
+		parser->parse_string);
+	return -1;
+}
+
+int32_t
+rte_gen_packet_parse_string(struct rte_gen *gen,
+			    const char *pkt_string,
+			    struct rte_mbuf **old_mbuf_to_user)
+{
+	struct gen_parser parser;
+	int32_t err = gen_parser_init(&parser, gen, pkt_string);
+	if (err) {
+		GEN_LOG(ERR, "error with parser_init(), %d\n", err);
+		return -1;
+	};
+
+	/* Recursively parse each protocol. */
+	enum GEN_PROTO inner;
+	err = gen_parser_parse_next(&parser, &inner);
+	if (err) {
+		GEN_LOG(ERR, "Error in parsing packet string. "
+			"Set \"gen\" log level to debug for more info.\n");
+		rte_pktmbuf_free(parser.mbuf);
+		return -1;
+	}
+
+	uint32_t i;
+	/* Iterate the per protocol stored metadata to log output. */
+	for (i = 0; i < parser.parse_iter; i++) {
+		snprintf(parser.indent_str, 2 + i * 2,
+			"                               " /* 32 spaces. */);
+
+		if (gen_protocols[i].log_func == NULL) {
+			GEN_LOG(ERR, "Missing log function\n");
+			return -1;
+		}
+
+		void *buf_off = parser.mbuf_data + parser.proto_meta[i].offset;
+		parser.proto_meta[i].log_func(buf_off, parser.indent_str);
+	}
+
+	if (inner != GEN_PROTO_ETHER) {
+		GEN_LOG(WARNING,
+			"Outer protocol of frame is not Ethernet.\n");
+	}
+
+	/* Free the currently in use mbuf. */
+	if (old_mbuf_to_user)
+		*old_mbuf_to_user = gen->base_pkt;
+	else
+		rte_pktmbuf_free(gen->base_pkt);
+
+	/* TODO: HVH design race-condition above vs rx/tx*/
+	gen->base_pkt = parser.mbuf;
+	return 0;
+}
diff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h
index 9119331673..93b3346436 100644
--- a/lib/gen/rte_gen.h
+++ b/lib/gen/rte_gen.h
@@ -95,6 +95,18 @@ rte_gen_packet_set_raw(struct rte_gen *gen,
 		       const uint8_t *raw_data,
 		       uint32_t raw_data_size);
 
+/* Parse a string description of a packet.
+ *
+ * The optional out parameter supplies the previously being sent mbuf to
+ * the user to be freed later. If this argument is not provided, then the
+ * mbuf is freed by this function.
+ */
+__rte_experimental
+int32_t
+rte_gen_packet_parse_string(struct rte_gen *gen,
+			    const char *pkt_string,
+			    struct rte_mbuf **old_mbuf_to_user);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/gen/version.map b/lib/gen/version.map
index d75e0b4bac..e335c608ee 100644
--- a/lib/gen/version.map
+++ b/lib/gen/version.map
@@ -6,4 +6,5 @@ EXPERIMENTAL {
 	rte_gen_rx_burst;
 	rte_gen_tx_burst;
 	rte_gen_packet_set_raw;
+	rte_gen_packet_parse_string;
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 07/15] gen: add gen IP parsing
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (5 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 06/15] gen: add parsing infrastructure and Ether protocol Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 08/15] examples/generator: import code from basicfwd.c Ronan Randles
                     ` (7 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Harry van Haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

This commit adds support for the parsing of "IP(src=...,dst=...)"
strings. Parse string API improvement for app RCU.
Appropriate unit tests also added.

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_gen.c |  29 +++++++--
 lib/gen/rte_gen.c   | 153 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 177 insertions(+), 5 deletions(-)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index 324582d0a5..7b67835c80 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -117,26 +117,47 @@ test_gen_packet_parse_string(void)
 {
 	struct rte_gen *gen = rte_gen_create(mp);
 	TEST_ASSERT_FAIL(gen, "Expected valid pointer after create()");
-
 	struct str_parse_t {
 		const char *str;
+		uint32_t expected_to_fail;
 	} pkt_strings[] = {
 		{ .str = "Ether()"},
 		{ .str = "Ether()/"},
 		{ .str = "/Ether()"},
-		{ .str = "/Ether()/"}
+		{ .str = "/Ether()/"},
+		{ .str = "Ether()/IP()"},
+		{ .str = "Ether()/IP(src=1.2.3.4,dst=5.6.7.8)"},
+		{ .str = "Ether()/IP(src=1.2.3.4,dst=192.168.255.255)"},
+		{ .str = "Ether()/IP(dst=172.16.0.9,src=1.2.3.4)"},
+		{ .str = "Ether()/IP(src=1.2.3.4)"},
+		{ .str = "Ether()/IP(srdst=5.6.7.8)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=1.2.3.4,ds=)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=1.2.3.4,dst=)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=,dst=5.6.7.8)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(sr=,dst=5.6.7.8)", .expected_to_fail = 1},
+		{ .str = "Ether()/IP(src=1.2.3.fail,dst=5.6.7.8)",
+					.expected_to_fail = 1},
 	};
 
 	uint32_t i;
 	for (i = 0; i < RTE_DIM(pkt_strings); i++) {
 		const char *pkt_str = pkt_strings[i].str;
 		int32_t err = rte_gen_packet_parse_string(gen, pkt_str, NULL);
-		TEST_ASSERT_EQUAL(err, 0, "Expected string %s to parse.",
-				pkt_str);
+
+		if (err && pkt_strings[i].expected_to_fail != 1) {
+			printf("Expected string %s to parse.", pkt_str);
+			return -1;
+		}
+		/* False pass if reached with no err when e_t_f = 1 */
+		if (!err && pkt_strings[i].expected_to_fail) {
+			printf("False Pass on string: %s\n", pkt_str);
+			return -1;
+		}
 	}
 
 	rte_gen_destroy(gen);
 	return 0;
+
 }
 
 
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index 4d3fe4017f..4020150712 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -10,6 +10,7 @@
 #include <rte_log.h>
 
 #include <rte_ether.h>
+#include <rte_ip.h>
 
 RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
 
@@ -136,6 +137,7 @@ rte_gen_tx_burst(struct rte_gen *gen,
 enum GEN_PROTO {
 	GEN_PROTO_INVALID,
 	GEN_PROTO_ETHER,
+	GEN_PROTO_IPV4,
 
 	/* Must be last. */
 	GEN_PROTO_COUNT,
@@ -219,6 +221,145 @@ gen_parser_init(struct gen_parser *parser, struct rte_gen *gen,
 	return -ENOMEM;
 }
 
+static void
+gen_log_ipv4(void *data, const char *indent)
+{
+	struct rte_ipv4_hdr *ip = data;
+
+	const char *proto_str;
+	switch (ip->next_proto_id) {
+	case 0:
+		proto_str = "hopopt";
+		break;
+	default:
+		proto_str = "unknown next proto";
+		break;
+	}
+
+	GEN_LOG_PROTOCOL(DEBUG,
+		"###[ IP ]###\n%sversion = %d\n%sihl = %d\n%stos = %d\n"
+		"%slen = %d\n%sid = %d\n%sflags = 0x%x\n%sfrag = %d\n"
+		"%sttl = %d\n%sproto = %s (%d)\n%schksum 0x%x\n%ssrc = 0x%x\n"
+		"%sdst = 0x%x\n%soptions = %s\n",
+		indent, ip->version_ihl >> 4,
+		indent, ip->version_ihl & RTE_IPV4_HDR_IHL_MASK,
+		indent, ip->type_of_service,
+		indent, rte_be_to_cpu_16(ip->total_length),
+		indent, rte_be_to_cpu_16(ip->packet_id), /* TODO: Scapy ID? */
+		indent, rte_be_to_cpu_16(ip->packet_id), /*TODO: Scapy Flags?*/
+		indent, rte_be_to_cpu_16(ip->fragment_offset),
+		indent, ip->time_to_live,
+		indent, proto_str, ip->next_proto_id,
+		indent, rte_be_to_cpu_16(ip->hdr_checksum),
+		indent, rte_be_to_cpu_32(ip->src_addr),
+		indent, rte_be_to_cpu_32(ip->dst_addr),
+		indent, "notImplemented");
+}
+
+static int32_t
+gen_parse_ipv4_params(char *protocol_str, struct rte_ipv4_hdr *ip)
+{
+	/* Strings to look for. */
+	static const char * const items[] = {
+		"src=",
+		"dst=",
+	};
+	const uint32_t num_items = RTE_DIM(items);
+
+	char *tok_ptr;
+	uint32_t err = 0;
+	uint32_t i;
+	for (i = 0; i < num_items; i++) {
+		/* Print input string into local buffer for processing. */
+		char buffer[1024];
+		int chars_printed = snprintf(buffer, 1024, "%s", protocol_str);
+		if (chars_printed >= 1024)
+			return -1;
+
+		/* Find substring (e.g. src=) if not found skip to next one. */
+		char *start = strstr(buffer, items[i]);
+		char check_previous[32];
+		if (start != NULL) {
+			snprintf(check_previous, 32, "%.1s", start - 1);
+			if (strcmp(&check_previous[0], "(") &&
+						strcmp(&check_previous[0], ","))
+				return -EINVAL;
+		}
+
+		if (!start) {
+			if (!strstr(buffer, ","))
+				continue;
+			else
+				return -EINVAL;
+		}
+		/* get from start of string till first , character. */
+		char *item = strtok_r(start, ",", &tok_ptr);
+
+		if (strcmp(item, items[i]) == 0)
+			return -EINVAL;
+		/* skip past the src= prefix. We know string is long enough as
+		 * otherwise strstr() wouldn't have matched it.
+		 */
+		item = &item[4];
+
+		if (strcmp(items[i], "src=") == 0) {
+			err = rte_ip_parse_addr(item, &ip->src_addr);
+			ip->src_addr = rte_cpu_to_be_32(ip->src_addr);
+		} else {
+			err = rte_ip_parse_addr(item, &ip->dst_addr);
+			ip->dst_addr = rte_cpu_to_be_32(ip->dst_addr);
+		}
+		if (err) {
+			GEN_LOG(ERR, "parser ip_parse_addr error %d\n", err);
+			return err;
+		}
+	}
+	return 0;
+}
+
+static int32_t
+gen_parse_ipv4(struct gen_parser *parser, char *protocol_str)
+{
+	struct rte_ipv4_hdr *ip = gen_parser_get_data_ptr(parser);
+	uint32_t pre_ip_len = parser->buf_write_offset;
+	memset(ip, 0, sizeof(*ip));
+	ip->version_ihl = RTE_IPV4_VHL_DEF;
+	ip->time_to_live = 64;
+	ip->packet_id = rte_cpu_to_be_16(1);
+
+	/* default addrs */
+	ip->src_addr = rte_cpu_to_be_32(RTE_IPV4(127, 0, 0, 1));
+	ip->dst_addr = rte_cpu_to_be_32(RTE_IPV4(127, 0, 0, 1));
+
+	uint32_t err = 0;
+	if (strcmp("IP()", protocol_str))
+		err = gen_parse_ipv4_params(protocol_str, ip);
+
+	if (err) {
+		GEN_LOG(ERR, "parser parse ipv4 params error %d\n", err);
+		return err;
+	}
+	/* Move up write pointer in packet. */
+	parser->buf_write_offset += sizeof(*ip);
+
+	/* Move up write pointer in packet, recurse to next. */
+	enum GEN_PROTO inner;
+	err = gen_parser_parse_next(parser, &inner);
+	if (err) {
+		GEN_LOG(ERR, "parser parse next() error %d\n", err);
+		return err;
+	}
+
+	switch (inner) {
+	default:
+		/* Default protocol is hopopt (0). */
+		break;
+	};
+
+	ip->total_length = rte_cpu_to_be_16(parser->mbuf->pkt_len - pre_ip_len);
+	return 0;
+}
+
 static void
 gen_log_ether(void *data, const char *indent)
 {
@@ -279,6 +420,9 @@ gen_parse_ether(struct gen_parser *parser, char *protocol_str)
 	}
 
 	switch (inner) {
+	case GEN_PROTO_IPV4:
+		eth->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
+		break;
 	default:
 		eth->ether_type = rte_cpu_to_be_16(0x9000);
 		break;
@@ -304,7 +448,14 @@ static struct gen_parse_func_t gen_protocols[] = {
 		.proto = GEN_PROTO_ETHER,
 		.parse_func = gen_parse_ether,
 		.log_func = gen_log_ether,
-	}
+	},
+	{
+		.name = "IP(",
+		.proto = GEN_PROTO_IPV4,
+		.parse_func = gen_parse_ipv4,
+		.log_func = gen_log_ipv4,
+	},
+
 };
 
 /* Function to tokenize and parse each segment of a string.
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 08/15] examples/generator: import code from basicfwd.c
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (6 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 07/15] gen: add gen IP parsing Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 09/15] examples/generator: enable gen library for traffic gen Ronan Randles
                     ` (6 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Harry van Haaren, Ronan Randles

From: Harry van Haaren <harry.van.haaren@intel.com>

Import files from basicfwd.c to act as starting point for
gen library sample app

Signed-off-by: Harry van Haaren <harry.van.haaren@intel.com>
Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c      | 226 +++++++++++++++++++++++++++++++++
 examples/generator/meson.build |  12 ++
 2 files changed, 238 insertions(+)
 create mode 100644 examples/generator/main.c
 create mode 100644 examples/generator/meson.build

diff --git a/examples/generator/main.c b/examples/generator/main.c
new file mode 100644
index 0000000000..0082f588b4
--- /dev/null
+++ b/examples/generator/main.c
@@ -0,0 +1,226 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2015 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <inttypes.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_cycles.h>
+#include <rte_lcore.h>
+#include <rte_mbuf.h>
+
+#define RX_RING_SIZE 1024
+#define TX_RING_SIZE 1024
+
+#define NUM_MBUFS 8191
+#define MBUF_CACHE_SIZE 256
+#define BURST_SIZE 32
+
+/* Configuration of ethernet ports. 8<  */
+static const struct rte_eth_conf port_conf_default = {
+	.rxmode = {
+		.max_lro_pkt_size = RTE_ETHER_MAX_LEN,
+	},
+};
+/* >8 End of configuration of ethernet ports. */
+
+/* basicfwd.c: Basic DPDK skeleton forwarding example. */
+
+/*
+ * Initializes a given port using global settings and with the RX buffers
+ * coming from the mbuf_pool passed as a parameter.
+ */
+
+/* Main functional part of port initialization. 8< */
+static inline int
+port_init(uint16_t port, struct rte_mempool *mbuf_pool)
+{
+	struct rte_eth_conf port_conf = port_conf_default;
+	const uint16_t rx_rings = 1, tx_rings = 1;
+	uint16_t nb_rxd = RX_RING_SIZE;
+	uint16_t nb_txd = TX_RING_SIZE;
+	int retval;
+	uint16_t q;
+	struct rte_eth_dev_info dev_info;
+	struct rte_eth_txconf txconf;
+
+	if (!rte_eth_dev_is_valid_port(port))
+		return -1;
+
+	retval = rte_eth_dev_info_get(port, &dev_info);
+	if (retval != 0) {
+		printf("Error during getting device (port %u) info: %s\n",
+				port, strerror(-retval));
+		return retval;
+	}
+
+	if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
+		port_conf.txmode.offloads |=
+			DEV_TX_OFFLOAD_MBUF_FAST_FREE;
+
+	/* Configure the Ethernet device. */
+	retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
+	if (retval != 0)
+		return retval;
+
+	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
+	if (retval != 0)
+		return retval;
+
+	/* Allocate and set up 1 RX queue per Ethernet port. */
+	for (q = 0; q < rx_rings; q++) {
+		retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
+				rte_eth_dev_socket_id(port), NULL, mbuf_pool);
+		if (retval < 0)
+			return retval;
+	}
+
+	txconf = dev_info.default_txconf;
+	txconf.offloads = port_conf.txmode.offloads;
+	/* Allocate and set up 1 TX queue per Ethernet port. */
+	for (q = 0; q < tx_rings; q++) {
+		retval = rte_eth_tx_queue_setup(port, q, nb_txd,
+				rte_eth_dev_socket_id(port), &txconf);
+		if (retval < 0)
+			return retval;
+	}
+
+	/* Starting Ethernet port. 8< */
+	retval = rte_eth_dev_start(port);
+	/* >8 End of starting of ethernet port. */
+	if (retval < 0)
+		return retval;
+
+	/* Display the port MAC address. */
+	struct rte_ether_addr addr;
+	retval = rte_eth_macaddr_get(port, &addr);
+	if (retval != 0)
+		return retval;
+
+	printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
+			   " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
+			port, RTE_ETHER_ADDR_BYTES(&addr));
+
+	/* Enable RX in promiscuous mode for the Ethernet device. */
+	retval = rte_eth_promiscuous_enable(port);
+	/* End of setting RX port in promiscuous mode. */
+	if (retval != 0)
+		return retval;
+
+	return 0;
+}
+/* >8 End of main functional part of port initialization. */
+
+/*
+ * The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+
+ /* Basic forwarding application lcore. 8< */
+static __rte_noreturn void
+lcore_main(void)
+{
+	uint16_t port;
+
+	/*
+	 * Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	RTE_ETH_FOREACH_DEV(port)
+		if (rte_eth_dev_socket_id(port) >= 0 &&
+				rte_eth_dev_socket_id(port) !=
+						(int)rte_socket_id())
+			printf("WARNING, port %u is on remote NUMA node to "
+					"polling thread.\n\tPerformance will "
+					"not be optimal.\n", port);
+
+	printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
+			rte_lcore_id());
+
+	/* Main work of application loop. 8< */
+	for (;;) {
+		/*
+		 * Receive packets on a port and forward them on the paired
+		 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
+		 */
+		RTE_ETH_FOREACH_DEV(port) {
+
+			/* Get burst of RX packets, from first port of pair. */
+			struct rte_mbuf *bufs[BURST_SIZE];
+			const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
+					bufs, BURST_SIZE);
+
+			if (unlikely(nb_rx == 0))
+				continue;
+
+			/* Send burst of TX packets, to second port of pair. */
+			const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
+					bufs, nb_rx);
+
+			/* Free any unsent packets. */
+			if (unlikely(nb_tx < nb_rx)) {
+				uint16_t buf;
+				for (buf = nb_tx; buf < nb_rx; buf++)
+					rte_pktmbuf_free(bufs[buf]);
+			}
+		}
+	}
+	/* >8 End of loop. */
+}
+/* >8 End Basic forwarding application lcore. */
+
+/*
+ * The main function, which does initialization and calls the per-lcore
+ * functions.
+ */
+int
+main(int argc, char *argv[])
+{
+	struct rte_mempool *mbuf_pool;
+	unsigned int nb_ports;
+	uint16_t portid;
+
+	/* Initializion the Environment Abstraction Layer (EAL). 8< */
+	int ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+	/* >8 End of initializion the Environment Abstraction Layer (EAL). */
+
+	argc -= ret;
+	argv += ret;
+
+	/* Check that there is an even number of ports to send/receive on. */
+	nb_ports = rte_eth_dev_count_avail();
+	if (nb_ports < 2 || (nb_ports & 1))
+		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
+
+	/* Creates a new mempool in memory to hold the mbufs. */
+
+	/* Allocates mempool to hold the mbufs. 8< */
+	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
+		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+	/* >8 End of allocating mempool to hold mbuf. */
+
+	if (mbuf_pool == NULL)
+		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
+
+	/* Initializing all ports. 8< */
+	RTE_ETH_FOREACH_DEV(portid)
+		if (port_init(portid, mbuf_pool) != 0)
+			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
+					portid);
+	/* >8 End of initializing all ports. */
+
+	if (rte_lcore_count() > 1)
+		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+
+	/* Call lcore_main on the main core only. Called on single lcore. 8< */
+	lcore_main();
+	/* >8 End of called on single lcore. */
+
+	/* clean up the EAL */
+	rte_eal_cleanup();
+
+	return 0;
+}
diff --git a/examples/generator/meson.build b/examples/generator/meson.build
new file mode 100644
index 0000000000..441678bbe5
--- /dev/null
+++ b/examples/generator/meson.build
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Intel Corporation
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+allow_experimental_apis = true
+sources = files(
+        'main.c',
+)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 09/15] examples/generator: enable gen library for traffic gen
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (7 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 08/15] examples/generator: import code from basicfwd.c Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 10/15] examples/generator: telemetry support Ronan Randles
                     ` (5 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This commit shows the steps necessary to enable traffic generation
using the gen library

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c      | 175 ++++++++++++++++++++++-----------
 examples/generator/meson.build |   1 +
 examples/meson.build           |   1 +
 3 files changed, 120 insertions(+), 57 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 0082f588b4..1ddf4c1603 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -1,14 +1,18 @@
 /* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2010-2015 Intel Corporation
+ * Copyright(c) 2021 Intel Corporation
  */
 
 #include <stdint.h>
 #include <inttypes.h>
+#include <unistd.h>
+#include <signal.h>
+
 #include <rte_eal.h>
 #include <rte_ethdev.h>
 #include <rte_cycles.h>
 #include <rte_lcore.h>
 #include <rte_mbuf.h>
+#include <rte_gen.h>
 
 #define RX_RING_SIZE 1024
 #define TX_RING_SIZE 1024
@@ -16,23 +20,23 @@
 #define NUM_MBUFS 8191
 #define MBUF_CACHE_SIZE 256
 #define BURST_SIZE 32
+#define MIN_THREADS 3
 
-/* Configuration of ethernet ports. 8<  */
 static const struct rte_eth_conf port_conf_default = {
 	.rxmode = {
 		.max_lro_pkt_size = RTE_ETHER_MAX_LEN,
 	},
 };
-/* >8 End of configuration of ethernet ports. */
 
-/* basicfwd.c: Basic DPDK skeleton forwarding example. */
+static volatile int done;
+static struct rte_mempool *mbuf_pool;
+struct rte_gen *gen;
+
+static void handle_sigint(int sig);
 
-/*
- * Initializes a given port using global settings and with the RX buffers
+/* Initializes a given port using global settings and with the RX buffers
  * coming from the mbuf_pool passed as a parameter.
  */
-
-/* Main functional part of port initialization. 8< */
 static inline int
 port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 {
@@ -68,6 +72,12 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 	if (retval != 0)
 		return retval;
 
+	int lcore_available_count = rte_lcore_count();
+	if (lcore_available_count < MIN_THREADS) {
+		printf("Not enough threads available\n");
+		return -1;
+	}
+
 	/* Allocate and set up 1 RX queue per Ethernet port. */
 	for (q = 0; q < rx_rings; q++) {
 		retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
@@ -86,9 +96,8 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 			return retval;
 	}
 
-	/* Starting Ethernet port. 8< */
+	/* Start the Ethernet port. */
 	retval = rte_eth_dev_start(port);
-	/* >8 End of starting of ethernet port. */
 	if (retval < 0)
 		return retval;
 
@@ -104,27 +113,67 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 
 	/* Enable RX in promiscuous mode for the Ethernet device. */
 	retval = rte_eth_promiscuous_enable(port);
-	/* End of setting RX port in promiscuous mode. */
 	if (retval != 0)
 		return retval;
 
 	return 0;
 }
-/* >8 End of main functional part of port initialization. */
 
-/*
- * The lcore main. This is the main thread that does the work, reading from
+/* The lcore main. This is the main thread that does the work, reading from
  * an input port and writing to an output port.
  */
+static int
+lcore_producer(__rte_unused void *arg)
+{
+	uint16_t port;
+
+	/* Check that the port is on the same NUMA node as the polling thread
+	 * for best performance.
+	 */
+	RTE_ETH_FOREACH_DEV(port)
+		if (rte_eth_dev_socket_id(port) >= 0 &&
+				rte_eth_dev_socket_id(port) !=
+						(int)rte_socket_id())
+			printf("WARNING, port %u is on remote NUMA node to "
+					"polling thread.\n\tPerformance will "
+					"not be optimal.\n", port);
 
- /* Basic forwarding application lcore. 8< */
-static __rte_noreturn void
-lcore_main(void)
+	/* Run until the application is quit or killed. */
+	while (!done) {
+		struct rte_mbuf *bufs[BURST_SIZE];
+		int i;
+		/* Receive packets from gen and then tx them over port */
+		RTE_ETH_FOREACH_DEV(port) {
+			int nb_recieved = rte_gen_rx_burst(gen, bufs,
+							BURST_SIZE);
+			for (i = 0; i < nb_recieved; i++) {
+				bufs[i]->pkt_len = 64;
+				bufs[i]->data_len = 64;
+			}
+
+			uint16_t nb_tx = rte_eth_tx_burst(port, 0, bufs,
+							nb_recieved);
+			if (nb_tx != nb_recieved)
+				rte_pktmbuf_free_bulk(&bufs[nb_tx],
+							(nb_recieved - nb_tx));
+
+			if (unlikely(nb_tx == 0))
+				continue;
+
+		}
+	}
+	return 0;
+}
+
+/* The lcore main. This is the main thread that does the work, reading from
+ * an input port and writing to an output port.
+ */
+static int
+lcore_consumer(__rte_unused void *arg)
 {
 	uint16_t port;
 
-	/*
-	 * Check that the port is on the same NUMA node as the polling thread
+	/* Check that the port is on the same NUMA node as the polling thread
 	 * for best performance.
 	 */
 	RTE_ETH_FOREACH_DEV(port)
@@ -135,57 +184,53 @@ lcore_main(void)
 					"polling thread.\n\tPerformance will "
 					"not be optimal.\n", port);
 
-	printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n",
-			rte_lcore_id());
+	/* Run until the application is quit or killed. */
+	while (!done) {
+		struct rte_mbuf *bufs[BURST_SIZE];
 
-	/* Main work of application loop. 8< */
-	for (;;) {
-		/*
-		 * Receive packets on a port and forward them on the paired
-		 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc.
+		/* Receive packets over port and then tx them to gen library
+		 * for stats
 		 */
 		RTE_ETH_FOREACH_DEV(port) {
+			uint64_t latency[BURST_SIZE];
+			uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs,
+							BURST_SIZE);
+			rte_gen_tx_burst(gen, bufs, latency, nb_rx);
 
-			/* Get burst of RX packets, from first port of pair. */
-			struct rte_mbuf *bufs[BURST_SIZE];
-			const uint16_t nb_rx = rte_eth_rx_burst(port, 0,
-					bufs, BURST_SIZE);
+			int nb_sent = rte_gen_tx_burst(gen, bufs,
+							latency, nb_rx);
+			if (nb_sent != nb_rx)
+				rte_panic("invalid tx quantity\n");
 
 			if (unlikely(nb_rx == 0))
 				continue;
 
-			/* Send burst of TX packets, to second port of pair. */
-			const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0,
-					bufs, nb_rx);
-
-			/* Free any unsent packets. */
-			if (unlikely(nb_tx < nb_rx)) {
-				uint16_t buf;
-				for (buf = nb_tx; buf < nb_rx; buf++)
-					rte_pktmbuf_free(bufs[buf]);
-			}
 		}
 	}
-	/* >8 End of loop. */
+	return 0;
 }
-/* >8 End Basic forwarding application lcore. */
 
-/*
- * The main function, which does initialization and calls the per-lcore
+void handle_sigint(int sig)
+{
+	RTE_SET_USED(sig);
+	printf("\nExiting...\n");
+	done = 1;
+}
+
+/* The main function, which does initialization and calls the per-lcore
  * functions.
  */
 int
 main(int argc, char *argv[])
 {
-	struct rte_mempool *mbuf_pool;
+	signal(SIGINT, handle_sigint);
 	unsigned int nb_ports;
 	uint16_t portid;
 
-	/* Initializion the Environment Abstraction Layer (EAL). 8< */
+	/* Initialize the Environment Abstraction Layer (EAL). */
 	int ret = rte_eal_init(argc, argv);
 	if (ret < 0)
 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
-	/* >8 End of initializion the Environment Abstraction Layer (EAL). */
 
 	argc -= ret;
 	argv += ret;
@@ -196,28 +241,44 @@ main(int argc, char *argv[])
 		rte_exit(EXIT_FAILURE, "Error: number of ports must be even\n");
 
 	/* Creates a new mempool in memory to hold the mbufs. */
-
-	/* Allocates mempool to hold the mbufs. 8< */
 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
 		MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
-	/* >8 End of allocating mempool to hold mbuf. */
 
 	if (mbuf_pool == NULL)
 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
 
-	/* Initializing all ports. 8< */
+	/* Initialize all ports. */
 	RTE_ETH_FOREACH_DEV(portid)
 		if (port_init(portid, mbuf_pool) != 0)
 			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
 					portid);
-	/* >8 End of initializing all ports. */
 
-	if (rte_lcore_count() > 1)
-		printf("\nWARNING: Too many lcores enabled. Only 1 used.\n");
+	gen = rte_gen_create(mbuf_pool);
+	if (!gen)
+		rte_panic("Gen failed to initialize\n");
+
+	int err = rte_gen_packet_parse_string(gen, "Ether()/IP()", NULL);
+	if (err)
+		rte_panic("Failed to parse input args");
+
+	/* launch lcore functions */
+	uint32_t lcore_count = 0;
+	uint32_t lcore_id = 0;
+	RTE_LCORE_FOREACH_WORKER(lcore_id) {
+		if (lcore_count == 0)
+			rte_eal_remote_launch(lcore_producer, NULL, lcore_id);
+		else if (lcore_count == 1)
+			rte_eal_remote_launch(lcore_consumer, NULL, lcore_id);
+		else
+			break;
+
+		lcore_count++;
+	}
+	/* Stall the main thread until all other threads have returned. */
+	rte_eal_mp_wait_lcore();
 
-	/* Call lcore_main on the main core only. Called on single lcore. 8< */
-	lcore_main();
-	/* >8 End of called on single lcore. */
+	/* All threads returned, safe to destroy gen instance */
+	rte_gen_destroy(gen);
 
 	/* clean up the EAL */
 	rte_eal_cleanup();
diff --git a/examples/generator/meson.build b/examples/generator/meson.build
index 441678bbe5..15d84674a5 100644
--- a/examples/generator/meson.build
+++ b/examples/generator/meson.build
@@ -10,3 +10,4 @@ allow_experimental_apis = true
 sources = files(
         'main.c',
 )
+deps += 'gen'
\ No newline at end of file
diff --git a/examples/meson.build b/examples/meson.build
index 268422a257..c6765a151d 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -18,6 +18,7 @@ all_examples = [
         'fips_validation',
         'flow_classify',
         'flow_filtering',
+        'generator',
         'helloworld',
         'ip_fragmentation',
         'ip_pipeline',
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 10/15] examples/generator: telemetry support
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (8 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 09/15] examples/generator: enable gen library for traffic gen Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 11/15] examples/generator: link status check added Ronan Randles
                     ` (4 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This commit adds telemetry introducing the callback functions
and returning measured values

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c | 142 +++++++++++++++++++++++++++++++-------
 1 file changed, 118 insertions(+), 24 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 1ddf4c1603..1ac3caafcc 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -13,6 +13,7 @@
 #include <rte_lcore.h>
 #include <rte_mbuf.h>
 #include <rte_gen.h>
+#include <rte_telemetry.h>
 
 #define RX_RING_SIZE 1024
 #define TX_RING_SIZE 1024
@@ -32,6 +33,27 @@ static volatile int done;
 static struct rte_mempool *mbuf_pool;
 struct rte_gen *gen;
 
+struct gen_args {
+	/* Inputs */
+	struct rte_gen *gen;
+
+	/* Outputs */
+	uint64_t tx_total_packets;
+	uint64_t rx_total_packets;
+	uint64_t rx_missed_total;
+	uint64_t tx_failed;
+	uint64_t last_tx_total;
+	uint64_t measured_tx_pps;
+} __rte_cache_aligned;
+/* Expose a struct as a global so the telemetry callbacks can access the
+ * data required to provide stats etc.
+ */
+struct telemetry_userdata {
+	struct gen_args *prod;
+	struct gen_args *cons;
+};
+static struct telemetry_userdata telemetry_userdata;
+
 static void handle_sigint(int sig);
 
 /* Initializes a given port using global settings and with the RX buffers
@@ -123,10 +145,11 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
  * an input port and writing to an output port.
  */
 static int
-lcore_producer(__rte_unused void *arg)
+lcore_producer(void *arg)
 {
+	struct gen_args *args = arg;
+	struct rte_gen *gen = args->gen;
 	uint16_t port;
-
 	/* Check that the port is on the same NUMA node as the polling thread
 	 * for best performance.
 	 */
@@ -138,25 +161,34 @@ lcore_producer(__rte_unused void *arg)
 					"polling thread.\n\tPerformance will "
 					"not be optimal.\n", port);
 
+	uint64_t tsc_hz = rte_get_tsc_hz();
+	uint64_t last_tsc_reading = 0;
+	uint64_t last_tx_total = 0;
+
 	/* Run until the application is quit or killed. */
 	while (!done) {
 		struct rte_mbuf *bufs[BURST_SIZE];
-		int i;
+		uint16_t nb_tx = 0;
 		/* Receive packets from gen and then tx them over port */
 		RTE_ETH_FOREACH_DEV(port) {
-			int nb_recieved = rte_gen_rx_burst(gen, bufs,
+			int nb_generated = rte_gen_rx_burst(gen, bufs,
 							BURST_SIZE);
-			for (i = 0; i < nb_recieved; i++) {
-				bufs[i]->pkt_len = 64;
-				bufs[i]->data_len = 64;
-			}
 
-			uint16_t nb_tx = rte_eth_tx_burst(port, 0, bufs,
-							nb_recieved);
-			if (nb_tx != nb_recieved)
-				rte_pktmbuf_free_bulk(&bufs[nb_tx],
-							(nb_recieved - nb_tx));
+			uint64_t start_tsc = rte_rdtsc();
+			if (start_tsc > last_tsc_reading + tsc_hz) {
+				args->measured_tx_pps = args->tx_total_packets -
+								last_tx_total;
+				last_tx_total = args->tx_total_packets;
+				last_tsc_reading = start_tsc;
+			}
+			nb_tx = rte_eth_tx_burst(port, 0, bufs, nb_generated);
+			args->tx_total_packets += nb_tx;
 
+			uint64_t tx_failed = nb_generated - nb_tx;
+			if (nb_tx != nb_generated) {
+				rte_pktmbuf_free_bulk(&bufs[nb_tx], tx_failed);
+				args->tx_failed += tx_failed;
+			}
 			if (unlikely(nb_tx == 0))
 				continue;
 
@@ -169,10 +201,11 @@ lcore_producer(__rte_unused void *arg)
  * an input port and writing to an output port.
  */
 static int
-lcore_consumer(__rte_unused void *arg)
+lcore_consumer(void *arg)
 {
+	struct gen_args *args = arg;
+	struct rte_gen *gen = args->gen;
 	uint16_t port;
-
 	/* Check that the port is on the same NUMA node as the polling thread
 	 * for best performance.
 	 */
@@ -195,16 +228,16 @@ lcore_consumer(__rte_unused void *arg)
 			uint64_t latency[BURST_SIZE];
 			uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs,
 							BURST_SIZE);
-			rte_gen_tx_burst(gen, bufs, latency, nb_rx);
+			if (unlikely(nb_rx == 0))
+				continue;
+
+			args->rx_total_packets += nb_rx;
 
 			int nb_sent = rte_gen_tx_burst(gen, bufs,
 							latency, nb_rx);
 			if (nb_sent != nb_rx)
 				rte_panic("invalid tx quantity\n");
 
-			if (unlikely(nb_rx == 0))
-				continue;
-
 		}
 	}
 	return 0;
@@ -217,6 +250,45 @@ void handle_sigint(int sig)
 	done = 1;
 }
 
+static int
+tele_gen_mpps(const char *cmd, const char *params, struct rte_tel_data *d)
+{
+	RTE_SET_USED(cmd);
+	RTE_SET_USED(params);
+
+	struct gen_args *args = telemetry_userdata.prod;
+	rte_tel_data_start_dict(d);
+	rte_tel_data_add_dict_int(d, "pps",
+					(args->measured_tx_pps));
+	return 0;
+}
+
+static int
+tele_gen_stats(const char *cmd, const char *params, struct rte_tel_data *d)
+{
+	RTE_SET_USED(cmd);
+	RTE_SET_USED(params);
+
+	struct gen_args *args_prod = telemetry_userdata.prod;
+	struct gen_args *args_cons = telemetry_userdata.cons;
+	rte_tel_data_start_dict(d);
+	static const char * const stats[] = {
+		"tx_total_packets",
+		"rx_total_packets",
+		"measured_tx_pps"
+	};
+
+	uint64_t values[RTE_DIM(stats)] = {0};
+	values[0] = args_prod->tx_total_packets;
+	values[1] = args_cons->rx_total_packets;
+	values[2] = args_prod->measured_tx_pps;
+
+	uint32_t i;
+	for (i = 0; i < RTE_DIM(stats); i++)
+		rte_tel_data_add_dict_int(d, stats[i], values[i]);
+
+	return 0;
+}
 /* The main function, which does initialization and calls the per-lcore
  * functions.
  */
@@ -224,6 +296,10 @@ int
 main(int argc, char *argv[])
 {
 	signal(SIGINT, handle_sigint);
+
+	#define CORE_COUNT 2
+	struct gen_args core_launch_args[CORE_COUNT];
+
 	unsigned int nb_ports;
 	uint16_t portid;
 
@@ -253,7 +329,7 @@ main(int argc, char *argv[])
 			rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
 					portid);
 
-	gen = rte_gen_create(mbuf_pool);
+	struct rte_gen *gen = rte_gen_create(mbuf_pool);
 	if (!gen)
 		rte_panic("Gen failed to initialize\n");
 
@@ -261,19 +337,37 @@ main(int argc, char *argv[])
 	if (err)
 		rte_panic("Failed to parse input args");
 
+	memset(core_launch_args, 0, sizeof(struct gen_args) * CORE_COUNT);
 	/* launch lcore functions */
 	uint32_t lcore_count = 0;
 	uint32_t lcore_id = 0;
 	RTE_LCORE_FOREACH_WORKER(lcore_id) {
-		if (lcore_count == 0)
-			rte_eal_remote_launch(lcore_producer, NULL, lcore_id);
-		else if (lcore_count == 1)
-			rte_eal_remote_launch(lcore_consumer, NULL, lcore_id);
+		core_launch_args[lcore_count].gen = gen;
+		if (lcore_count == 0) {
+			telemetry_userdata.prod =
+						&core_launch_args[lcore_count];
+			rte_eal_remote_launch(lcore_producer,
+					      telemetry_userdata.prod,
+					      lcore_id);
+		} else if (lcore_count == 1) {
+			telemetry_userdata.cons =
+						&core_launch_args[lcore_count];
+			rte_eal_remote_launch(lcore_consumer,
+					      telemetry_userdata.cons,
+					      lcore_id);
+		}
 		else
 			break;
 
 		lcore_count++;
 	}
+
+	/* Export stats via Telemetry */
+	rte_telemetry_register_cmd("/gen/stats", tele_gen_stats,
+			"Return statistics of the Gen instance.");
+	rte_telemetry_register_cmd("/gen/mpps", tele_gen_mpps,
+			"Get/Set the mpps rate");
+
 	/* Stall the main thread until all other threads have returned. */
 	rte_eal_mp_wait_lcore();
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 11/15] examples/generator: link status check added
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (9 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 10/15] examples/generator: telemetry support Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 12/15] examples/generator: line rate limiting Ronan Randles
                     ` (3 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This commit brings in a link status check so that the generator will
only start sending packets once there is something on the other end of
the link.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c | 68 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 67 insertions(+), 1 deletion(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 1ac3caafcc..0834a094a4 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -27,9 +27,13 @@ static const struct rte_eth_conf port_conf_default = {
 	.rxmode = {
 		.max_lro_pkt_size = RTE_ETHER_MAX_LEN,
 	},
+	.intr_conf = {
+		.lsc = 1, /**< lsc interrupt */
+	},
 };
 
 static volatile int done;
+static volatile int link_status[RTE_MAX_ETHPORTS];
 static struct rte_mempool *mbuf_pool;
 struct rte_gen *gen;
 
@@ -56,6 +60,30 @@ static struct telemetry_userdata telemetry_userdata;
 
 static void handle_sigint(int sig);
 
+static int
+link_status_change_cb(uint16_t port_id, enum rte_eth_event_type type,
+		      void *param, void *ret_param)
+{
+	if (unlikely(port_id >= RTE_DIM(link_status)))
+		rte_panic("got LSC interrupt for unknown port id\n");
+
+	RTE_SET_USED(type);
+	RTE_SET_USED(param);
+	RTE_SET_USED(ret_param);
+
+	struct rte_eth_link link;
+	int ret = rte_eth_link_get_nowait(port_id, &link);
+	if (ret < 0) {
+		printf("Failed link get on port %d: %s\n",
+		       port_id, rte_strerror(-ret));
+		return ret;
+	}
+
+	printf("Link status change port %i\n", port_id);
+	link_status[port_id] = link.link_status;
+	return 0;
+}
+
 /* Initializes a given port using global settings and with the RX buffers
  * coming from the mbuf_pool passed as a parameter.
  */
@@ -99,6 +127,9 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 		printf("Not enough threads available\n");
 		return -1;
 	}
+	/* Register the LinkStatusChange callback */
+	rte_eth_dev_callback_register(port, RTE_ETH_EVENT_INTR_LSC,
+				      link_status_change_cb, NULL);
 
 	/* Allocate and set up 1 RX queue per Ethernet port. */
 	for (q = 0; q < rx_rings; q++) {
@@ -138,9 +169,33 @@ port_init(uint16_t port, struct rte_mempool *mbuf_pool)
 	if (retval != 0)
 		return retval;
 
+	struct rte_eth_link link;
+	int ret = rte_eth_link_get_nowait(port, &link);
+	if (ret < 0) {
+		printf("Failed link get on port %d: %s\n", port,
+							rte_strerror(-ret));
+		return ret;
+	}
+
+	link_status[port] = link.link_status;
+
 	return 0;
 }
 
+static void
+gen_wait_for_links_up(void)
+{
+	/* Ensure all available ports are up before generating packets */
+	uint16_t nb_eth_ports = rte_eth_dev_count_avail();
+	uint16_t nb_links_up = 0;
+	while (!done && nb_links_up < nb_eth_ports) {
+		if (link_status[nb_links_up])
+			nb_links_up++;
+
+		rte_delay_us_block(100);
+	}
+}
+
 /* The lcore main. This is the main thread that does the work, reading from
  * an input port and writing to an output port.
  */
@@ -164,12 +219,19 @@ lcore_producer(void *arg)
 	uint64_t tsc_hz = rte_get_tsc_hz();
 	uint64_t last_tsc_reading = 0;
 	uint64_t last_tx_total = 0;
+	uint16_t nb_tx = 0;
+
+	/* Wait for links to come up before generating packets */
+	gen_wait_for_links_up();
+	if (!done)
+		printf("Generating packets...\n");
 
 	/* Run until the application is quit or killed. */
 	while (!done) {
+
 		struct rte_mbuf *bufs[BURST_SIZE];
-		uint16_t nb_tx = 0;
 		/* Receive packets from gen and then tx them over port */
+
 		RTE_ETH_FOREACH_DEV(port) {
 			int nb_generated = rte_gen_rx_burst(gen, bufs,
 							BURST_SIZE);
@@ -217,8 +279,12 @@ lcore_consumer(void *arg)
 					"polling thread.\n\tPerformance will "
 					"not be optimal.\n", port);
 
+	/* Wait for links to come up before generating packets */
+	gen_wait_for_links_up();
+
 	/* Run until the application is quit or killed. */
 	while (!done) {
+
 		struct rte_mbuf *bufs[BURST_SIZE];
 
 		/* Receive packets over port and then tx them to gen library
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 12/15] examples/generator: line rate limiting
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (10 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 11/15] examples/generator: link status check added Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 13/15] gen: add UDP support Ronan Randles
                     ` (2 subsequent siblings)
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This commit introduces line rate limiting using a token passing method.
The target traffic rate default is currently hard coded, this can be set
using telemetry.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 examples/generator/main.c | 79 +++++++++++++++++++++++++--------------
 1 file changed, 51 insertions(+), 28 deletions(-)

diff --git a/examples/generator/main.c b/examples/generator/main.c
index 0834a094a4..16292f06db 100644
--- a/examples/generator/main.c
+++ b/examples/generator/main.c
@@ -40,6 +40,7 @@ struct rte_gen *gen;
 struct gen_args {
 	/* Inputs */
 	struct rte_gen *gen;
+	uint64_t target_tx_pps;
 
 	/* Outputs */
 	uint64_t tx_total_packets;
@@ -195,7 +196,6 @@ gen_wait_for_links_up(void)
 		rte_delay_us_block(100);
 	}
 }
-
 /* The lcore main. This is the main thread that does the work, reading from
  * an input port and writing to an output port.
  */
@@ -217,43 +217,63 @@ lcore_producer(void *arg)
 					"not be optimal.\n", port);
 
 	uint64_t tsc_hz = rte_get_tsc_hz();
+	float tsc_hz_f = (float)tsc_hz;
 	uint64_t last_tsc_reading = 0;
 	uint64_t last_tx_total = 0;
 	uint16_t nb_tx = 0;
+	float tokens = 0;
 
 	/* Wait for links to come up before generating packets */
 	gen_wait_for_links_up();
 	if (!done)
 		printf("Generating packets...\n");
 
+	uint64_t token_last_add_tsc = rte_rdtsc();
+
 	/* Run until the application is quit or killed. */
 	while (!done) {
-
 		struct rte_mbuf *bufs[BURST_SIZE];
-		/* Receive packets from gen and then tx them over port */
-
-		RTE_ETH_FOREACH_DEV(port) {
-			int nb_generated = rte_gen_rx_burst(gen, bufs,
-							BURST_SIZE);
-
-			uint64_t start_tsc = rte_rdtsc();
-			if (start_tsc > last_tsc_reading + tsc_hz) {
-				args->measured_tx_pps = args->tx_total_packets -
-								last_tx_total;
-				last_tx_total = args->tx_total_packets;
-				last_tsc_reading = start_tsc;
+		/* Track time since last token add and calculate number
+		 * of tokens to give per second to implement line rate limiting
+		 */
+		uint64_t now = rte_rdtsc();
+		uint64_t tsc_delta = now - token_last_add_tsc;
+		float token_scalar = (float)tsc_delta / tsc_hz_f;
+		float add_tokens = args->target_tx_pps * token_scalar;
+		/* If there are tokens to be added and we haven't exceeded
+		 * the target rate then we add tokens
+		 */
+		if (add_tokens > 1.f) {
+			if (tokens < args->target_tx_pps) {
+				tokens += add_tokens;
+				token_last_add_tsc = now;
 			}
-			nb_tx = rte_eth_tx_burst(port, 0, bufs, nb_generated);
-			args->tx_total_packets += nb_tx;
-
-			uint64_t tx_failed = nb_generated - nb_tx;
-			if (nb_tx != nb_generated) {
-				rte_pktmbuf_free_bulk(&bufs[nb_tx], tx_failed);
-				args->tx_failed += tx_failed;
+		}
+		/* Receive packets from gen and then tx them over port */
+		if (tokens >= BURST_SIZE) {
+			RTE_ETH_FOREACH_DEV(port) {
+				int nb_generated = rte_gen_rx_burst(gen, bufs,
+								BURST_SIZE);
+
+				uint64_t start_tsc = rte_rdtsc();
+				if (start_tsc > last_tsc_reading + tsc_hz) {
+					args->measured_tx_pps =
+					args->tx_total_packets - last_tx_total;
+					last_tx_total = args->tx_total_packets;
+					last_tsc_reading = start_tsc;
+				}
+				nb_tx = rte_eth_tx_burst(port, 0, bufs,
+								nb_generated);
+				args->tx_total_packets += nb_tx;
+				tokens -= nb_tx;
+
+				uint64_t tx_failed = nb_generated - nb_tx;
+				if (nb_tx != nb_generated) {
+					rte_pktmbuf_free_bulk(&bufs[nb_tx],
+								tx_failed);
+					args->tx_failed += tx_failed;
+				}
 			}
-			if (unlikely(nb_tx == 0))
-				continue;
-
 		}
 	}
 	return 0;
@@ -296,7 +316,6 @@ lcore_consumer(void *arg)
 							BURST_SIZE);
 			if (unlikely(nb_rx == 0))
 				continue;
-
 			args->rx_total_packets += nb_rx;
 
 			int nb_sent = rte_gen_tx_burst(gen, bufs,
@@ -320,12 +339,14 @@ static int
 tele_gen_mpps(const char *cmd, const char *params, struct rte_tel_data *d)
 {
 	RTE_SET_USED(cmd);
-	RTE_SET_USED(params);
 
 	struct gen_args *args = telemetry_userdata.prod;
+	if (params) {
+		args->target_tx_pps = atoi(params) * 1000000;
+		printf("Packet rate set: %li\n", args->target_tx_pps);
+	}
 	rte_tel_data_start_dict(d);
-	rte_tel_data_add_dict_int(d, "pps",
-					(args->measured_tx_pps));
+	rte_tel_data_add_dict_int(d, "pps", args->target_tx_pps);
 	return 0;
 }
 
@@ -412,6 +433,8 @@ main(int argc, char *argv[])
 		if (lcore_count == 0) {
 			telemetry_userdata.prod =
 						&core_launch_args[lcore_count];
+			/* Default traffic rate */
+			telemetry_userdata.prod->target_tx_pps = 20000000;
 			rte_eal_remote_launch(lcore_producer,
 					      telemetry_userdata.prod,
 					      lcore_id);
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 13/15] gen: add UDP support
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (11 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 12/15] examples/generator: line rate limiting Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 14/15] net/vxlan: instance flag endianness refactored Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 15/15] gen: add VXLAN support Ronan Randles
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This commit adds UDP parsing and logging. Unit tested by the adition of
"UDP()" string to 'test_gen_packet_parse_string()'

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_gen.c |  1 +
 lib/gen/rte_gen.c   | 66 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index 7b67835c80..a0b00d6a6e 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -137,6 +137,7 @@ test_gen_packet_parse_string(void)
 		{ .str = "Ether()/IP(sr=,dst=5.6.7.8)", .expected_to_fail = 1},
 		{ .str = "Ether()/IP(src=1.2.3.fail,dst=5.6.7.8)",
 					.expected_to_fail = 1},
+		{ .str = "Ether()/IP()/UDP()"},
 	};
 
 	uint32_t i;
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index 4020150712..3a67ce8b6c 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -11,6 +11,7 @@
 
 #include <rte_ether.h>
 #include <rte_ip.h>
+#include <rte_udp.h>
 
 RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
 
@@ -138,6 +139,7 @@ enum GEN_PROTO {
 	GEN_PROTO_INVALID,
 	GEN_PROTO_ETHER,
 	GEN_PROTO_IPV4,
+	GEN_PROTO_UDP,
 
 	/* Must be last. */
 	GEN_PROTO_COUNT,
@@ -231,6 +233,9 @@ gen_log_ipv4(void *data, const char *indent)
 	case 0:
 		proto_str = "hopopt";
 		break;
+	case IPPROTO_UDP:
+		proto_str = "UDP";
+		break;
 	default:
 		proto_str = "unknown next proto";
 		break;
@@ -351,6 +356,12 @@ gen_parse_ipv4(struct gen_parser *parser, char *protocol_str)
 	}
 
 	switch (inner) {
+	case GEN_PROTO_UDP:
+		ip->next_proto_id = IPPROTO_UDP;
+		struct rte_udp_hdr *udp = gen_parser_get_data_ptr(parser);
+		udp->dgram_cksum = 0;
+		break;
+
 	default:
 		/* Default protocol is hopopt (0). */
 		break;
@@ -430,6 +441,55 @@ gen_parse_ether(struct gen_parser *parser, char *protocol_str)
 	return 0;
 }
 
+static void
+gen_log_udp(void *data, const char *indent)
+{
+	struct rte_udp_hdr *udp = data;
+
+	GEN_LOG_PROTOCOL(DEBUG,
+		"###[ UDP ]###\n%ssport= %u\n%sdport= %u\n%s"
+		"len= %u\n%schksum= %u\n",
+		indent, rte_be_to_cpu_16(udp->src_port),
+		indent,	rte_be_to_cpu_16(udp->dst_port),
+		indent,	rte_be_to_cpu_16(udp->dgram_len),
+		indent,	rte_be_to_cpu_16(udp->dgram_cksum));
+
+}
+
+static int32_t
+gen_parse_udp(struct gen_parser *parser, char *protocol_str)
+{
+	RTE_SET_USED(protocol_str);
+	struct rte_udp_hdr *udp = gen_parser_get_data_ptr(parser);
+	uint32_t pre_udp_len = parser->buf_write_offset;
+	memset(udp, 0, sizeof(*udp));
+
+	/* Move up write pointer in packet. */
+	parser->buf_write_offset += sizeof(*udp);
+
+	/* Recurse and handle inner protocol. */
+	enum GEN_PROTO inner;
+	int err = gen_parser_parse_next(parser, &inner);
+
+	switch (inner) {
+	default:
+		/* default to DNS like other packet generation tools */
+		udp->src_port = rte_cpu_to_be_16(53);
+		udp->dst_port = rte_cpu_to_be_16(53);
+		break;
+	};
+
+	/* Minimum len is the UDP header itself (8 bytes) or more */
+	int32_t total_len = parser->mbuf->data_len;
+	int32_t dgram_len = total_len - pre_udp_len;
+	if (dgram_len < 8)
+		printf("error parsing dgram len, %d\n", dgram_len);
+
+	udp->dgram_len = rte_cpu_to_be_16(dgram_len);
+
+	return err;
+}
+
 /* (Name, Function-pointer) pairs for supported parse types */
 typedef int32_t (*gen_parse_func)(struct gen_parser *parser,
 				char *protocol_str);
@@ -455,6 +515,12 @@ static struct gen_parse_func_t gen_protocols[] = {
 		.parse_func = gen_parse_ipv4,
 		.log_func = gen_log_ipv4,
 	},
+	{
+		.name = "UDP(",
+		.proto = GEN_PROTO_UDP,
+		.parse_func = gen_parse_udp,
+		.log_func = gen_log_udp,
+	}
 
 };
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 14/15] net/vxlan: instance flag endianness refactored
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (12 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 13/15] gen: add UDP support Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  2022-01-21 10:31   ` [PATCH v2 15/15] gen: add VXLAN support Ronan Randles
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This patch improves the setting of the instance flag in the
rte_vxlan_hdr struct, by using a byte to represent vx_flag_bits.
Previously it was exposed to the user via a rte_be32_t value, which
required handling endianness at the application level. The code uses a
union to ensure that existing code continues to work as before, while
allowing the improved more usable method to co-exist.

A new #define is introduced to represent the instance bit, which must be
set if a vxlan header contains a valid VNI field, see
https://datatracker.ietf.org/doc/html/rfc7348 for details

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 lib/net/rte_vxlan.h | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lib/net/rte_vxlan.h b/lib/net/rte_vxlan.h
index 929fa7a1dd..86b6d8a3ae 100644
--- a/lib/net/rte_vxlan.h
+++ b/lib/net/rte_vxlan.h
@@ -24,6 +24,7 @@ extern "C" {
 /** VXLAN default port. */
 #define RTE_VXLAN_DEFAULT_PORT 4789
 #define RTE_VXLAN_GPE_DEFAULT_PORT 4790
+#define RTE_VXLAN_FLAGS_I (1 << 3)
 
 /**
  * VXLAN protocol header.
@@ -31,7 +32,14 @@ extern "C" {
  * Reserved fields (24 bits and 8 bits)
  */
 struct rte_vxlan_hdr {
-	rte_be32_t vx_flags; /**< flag (8) + Reserved (24). */
+	RTE_STD_C11
+	union {
+		struct {
+			uint8_t vx_flag_bits;
+			uint8_t reserved[3];
+		};
+		rte_be32_t vx_flags; /**< flag (8) + Reserved (24). */
+	};
 	rte_be32_t vx_vni;   /**< VNI (24) + Reserved (8). */
 } __rte_packed;
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v2 15/15] gen: add VXLAN support
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
                     ` (13 preceding siblings ...)
  2022-01-21 10:31   ` [PATCH v2 14/15] net/vxlan: instance flag endianness refactored Ronan Randles
@ 2022-01-21 10:31   ` Ronan Randles
  14 siblings, 0 replies; 62+ messages in thread
From: Ronan Randles @ 2022-01-21 10:31 UTC (permalink / raw)
  To: dev; +Cc: Ronan Randles

This commit adds VXLAN parsing and logging functionality. Unit test with
additional test case to test_gen_packet_parse_string.

Signed-off-by: Ronan Randles <ronan.randles@intel.com>
---
 app/test/test_gen.c |  1 +
 lib/gen/rte_gen.c   | 50 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/app/test/test_gen.c b/app/test/test_gen.c
index a0b00d6a6e..f05851831a 100644
--- a/app/test/test_gen.c
+++ b/app/test/test_gen.c
@@ -138,6 +138,7 @@ test_gen_packet_parse_string(void)
 		{ .str = "Ether()/IP(src=1.2.3.fail,dst=5.6.7.8)",
 					.expected_to_fail = 1},
 		{ .str = "Ether()/IP()/UDP()"},
+		{ .str = "Ether()/IP()/UDP()/VXLAN()"},
 	};
 
 	uint32_t i;
diff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c
index 3a67ce8b6c..431ffc5b2a 100644
--- a/lib/gen/rte_gen.c
+++ b/lib/gen/rte_gen.c
@@ -12,6 +12,7 @@
 #include <rte_ether.h>
 #include <rte_ip.h>
 #include <rte_udp.h>
+#include <rte_vxlan.h>
 
 RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);
 
@@ -140,6 +141,7 @@ enum GEN_PROTO {
 	GEN_PROTO_ETHER,
 	GEN_PROTO_IPV4,
 	GEN_PROTO_UDP,
+	GEN_PROTO_VXLAN,
 
 	/* Must be last. */
 	GEN_PROTO_COUNT,
@@ -472,6 +474,10 @@ gen_parse_udp(struct gen_parser *parser, char *protocol_str)
 	int err = gen_parser_parse_next(parser, &inner);
 
 	switch (inner) {
+	case GEN_PROTO_VXLAN:
+		udp->src_port = rte_cpu_to_be_16(RTE_VXLAN_DEFAULT_PORT);
+		udp->dst_port = rte_cpu_to_be_16(RTE_VXLAN_DEFAULT_PORT);
+		break;
 	default:
 		/* default to DNS like other packet generation tools */
 		udp->src_port = rte_cpu_to_be_16(53);
@@ -490,6 +496,44 @@ gen_parse_udp(struct gen_parser *parser, char *protocol_str)
 	return err;
 }
 
+static int32_t
+gen_parse_vxlan(struct gen_parser *parser, char *protocol_str)
+{
+	RTE_SET_USED(protocol_str);
+	struct rte_vxlan_hdr *vxlan = gen_parser_get_data_ptr(parser);
+	memset(vxlan, 0, sizeof(*vxlan));
+
+	uint32_t input_vni = 1000;
+	vxlan->vx_vni = rte_cpu_to_be_32(input_vni << 8);
+
+	/* Move up write pointer in packet. */
+	parser->buf_write_offset += sizeof(*vxlan);
+
+	enum GEN_PROTO inner;
+	int err = gen_parser_parse_next(parser, &inner);
+
+	vxlan->vx_flag_bits = RTE_VXLAN_FLAGS_I;
+
+	switch (inner) {
+	default:
+	/* Not supporting VXLAN-GPE and next proto today. */
+	break;
+	}
+
+	return err;
+}
+
+static void
+gen_log_vxlan(void *data, const char *indent)
+{
+	struct rte_vxlan_hdr *vxlan = data;
+
+	GEN_LOG_PROTOCOL(DEBUG,
+			"###[ VXLAN ]###\n%svx_flags= %u\n%svx_vni= %u\n",
+			indent, rte_be_to_cpu_32(vxlan->vx_flags),
+			indent, rte_be_to_cpu_32(vxlan->vx_vni));
+}
+
 /* (Name, Function-pointer) pairs for supported parse types */
 typedef int32_t (*gen_parse_func)(struct gen_parser *parser,
 				char *protocol_str);
@@ -520,6 +564,12 @@ static struct gen_parse_func_t gen_protocols[] = {
 		.proto = GEN_PROTO_UDP,
 		.parse_func = gen_parse_udp,
 		.log_func = gen_log_udp,
+	},
+	{
+		.name = "VXLAN(",
+		.proto = GEN_PROTO_VXLAN,
+		.parse_func = gen_parse_vxlan,
+		.log_func = gen_log_vxlan,
 	}
 
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 05/12] gen: add raw packet data API and tests
  2022-01-20 10:21             ` Van Haaren, Harry
@ 2022-01-21 10:45               ` Van Haaren, Harry
  0 siblings, 0 replies; 62+ messages in thread
From: Van Haaren, Harry @ 2022-01-21 10:45 UTC (permalink / raw)
  To: Thomas Monjalon, Randles, Ronan; +Cc: dev, Jerin Jacob, dev, Richardson, Bruce

> -----Original Message-----
> From: Van Haaren, Harry
> Sent: Thursday, January 20, 2022 10:21 AM
> To: 'Thomas Monjalon' <thomas@monjalon.net>; Randles, Ronan
> <ronan.randles@intel.com>
> Cc: dev@dpdk.org; Jerin Jacob <jerinjacobk@gmail.com>; dev@dpdk.org;
> Richardson, Bruce <bruce.richardson@intel.com>
> Subject: RE: [PATCH 05/12] gen: add raw packet data API and tests

<snip previous discussion>

> But to summarize, it seems there are multiple questions/concerns around
> merging the gen library into DPDK. That's OK, this is not a critical feature to
> upstream, I thought it might be useful to the wider community.
> 
> As some rework was done from the V1 to a V2, adding some protocols and
> generally
> advancing the library, Ronan and I will post the V2 to list to make it publicly
> available.
> We can mark the V2 as "not applicable" in patchwork once sent.

As promised, the reworked V2 patches are available here;
http://patches.dpdk.org/project/dpdk/cover/20220121103122.2926856-1-ronan.randles@intel.com/

They are marked as "not applicable" in patchwork, so will not be candidates
for merge, but are available for interested folks to work with.

I'd like to thank Ronan for his work on the Gen library during his internship,
it is useful tool for my testing usages, and I expect to continue using it in future!

Thanks community for reviews and suggestions on improving post-v1.

Regards, -Harry

<snip rest of email>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 05/12] gen: add raw packet data API and tests
  2021-12-20 13:21       ` Jerin Jacob
@ 2022-01-21 14:20         ` Xueming(Steven) Li
  0 siblings, 0 replies; 62+ messages in thread
From: Xueming(Steven) Li @ 2022-01-21 14:20 UTC (permalink / raw)
  To: jerinjacobk, harry.van.haaren
  Cc: NBU-Contact-Thomas Monjalon (EXTERNAL), dev, ronan.randles

On Mon, 2021-12-20 at 18:51 +0530, Jerin Jacob wrote:
> On Fri, Dec 17, 2021 at 5:10 PM Van Haaren, Harry
> <harry.van.haaren@intel.com> wrote:
> > 
> > +CC Thomas;
> > 
> > > -----Original Message-----
> > > From: Jerin Jacob <jerinjacobk@gmail.com>
> > > Sent: Wednesday, December 15, 2021 12:41 PM
> > > To: Randles, Ronan <ronan.randles@intel.com>
> > > Cc: dpdk-dev <dev@dpdk.org>; Van Haaren, Harry
> > > <harry.van.haaren@intel.com>
> > > Subject: Re: [PATCH 05/12] gen: add raw packet data API and tests
> > > 
> > > On Tue, Dec 14, 2021 at 7:43 PM Ronan Randles <ronan.randles@intel.com>
> > > wrote:
> > > > 
> > > > From: Harry van Haaren <harry.van.haaren@intel.com>
> > 
> > <snip some patch contents>
> > 
> > > > +       const uint32_t base_size = gen->base_pkt->pkt_len;
> > > > +       const uint8_t *base_data = rte_pktmbuf_mtod(gen->base_pkt, uint8_t
> > > *);
> > > 
> > > I think, the very next feature will be generating packets for
> > > incrementing IP addresses or so.
> > 
> > Hah, yes! It’s a logical next step, and indeed we have POC code internally that Ronan
> > and I have worked on that does this :) I've been using this internal POC of
> > testing of OVS for ~ a year now, and it provides a pretty nice workflow for me.
> > 
> > > In this case, one packet-based template will not work.
> > 
> > Why not? I agree that "pre-calculating" all packets will not work, but the approach
> > we have taken for this library is different. See below;
> > 
> > > May we worth consider that use case into API framework first and add support
> > > later for implementation as it may change the complete land space of API to have
> > > better performance. Options like struct rte_gen logical object can have
> > > N templates instead of one is an option on the table. :-)
> > 
> > Agree - more complex usages have been designed for too. Let me explain;
> > 
> > 1) A single gen instance uses a single template, and has "modifiers" that allow
> > manipulation of the packet before sending. The gen->base_pkt is copied to the
> > destination mbuf, and then updated by the modifiers. This approach is much better
> > to allow for huge flow-counts (> 1 million?) as pre-calculating and storing 1 million
> > packets is a waste of memory, and causes a lot of mem-IO for the datapath core.
> > 
> > 2) The "modifiers" approach allows any number of things to be changed, with little
> > mem-IO, and variable CPU cycle cost based on the modifiers themselves.
> > If the CPU cycle cost of generating packets is too high, just add more cores :)
> > 
> > 3) There are also some smarts we can apply for pre-calculating only a small amount of
> > data per packet (e.g. uniformly-random distributed src ip). The memory footprint is
> > lower than pre-calc of whole packets, and the runtime overhead of uniform-random
> > is moved to configure time instead of on the datapath.
> > 
> > 4) Dynamically generating packets by modification of templates allows for cool things
> > to be added, e.g. adding timestamps to packets, and calculating latency can
> > be done using the modifier concept and a protocol string "Ether()/IP()/UDP()/TSC()".
> > If the packet is being decapped by the target application, the string params can provide
> > context for where to "retrieve" the TSC from on RX in the generator: "TSC(rx_offset=30)".
> > I've found this approach to be very flexible and nice, so am a big fan :)
> > 
> > 5) In order to have multiple streams of totally-different traffic types (read "from multiple templates")
> > the user can initialize multiple rte_gen instances. This allows applications that require multi-stream traffic
> > to achieve that too, with the same abstraction as a single template stream. Initially the generator app is just
> > providing a single stream, but this application can be expanded to many usages over the next year before 22.11 :)
> 
> OK. I thought "modifiers" will need some sort of critical section in
> multiple producer use cases. If so,
> one option could be N streams in one gen instance vs N gen instance.
> Just my 2c. Anyway, you folks can decide
> on one option vs another. Only my concern was including such features
> affect the prototype of existing APIs or not?
> In either case, No strong opinion.

Sometimes we need RSS with limited streams, modifies need to copy
template data, then modify accordingly, involves more cycles and data
cache, not a good choice for performance test.

By cloning a list of templates, just copy the mbuf headers, seems more
efficient for such case.

Agree modifiers a flexible way to do things more powerful, hopefully we
have them all :)

> 
> 
> > 
> > I could ramble on a bit more, but mostly diminishing returns I think... I'll just use this email as a reply to Thomas' tweet;
> > https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftwitter.com%2Ftmonjalo%2Fstatus%2F1337313985662771201&amp;data=04%7C01%7Cxuemingl%40nvidia.com%7Cedc55ae350504eaebe0b08d9c3bbb464%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C637756033240578829%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000&amp;sdata=JVkpoweUrPoEWf7rWg1tSG4qiO9IKTtnw30x%2BqBZb%2FI%3D&amp;reserved=0
> > 
> > Regards, -Harry


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 00/12] add packet generator library and example app
  2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
                   ` (14 preceding siblings ...)
  2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
@ 2022-01-21 14:44 ` Xueming(Steven) Li
  2022-01-21 15:24   ` Van Haaren, Harry
  15 siblings, 1 reply; 62+ messages in thread
From: Xueming(Steven) Li @ 2022-01-21 14:44 UTC (permalink / raw)
  To: dev, ronan.randles; +Cc: harry.van.haaren

On Tue, 2021-12-14 at 14:12 +0000, Ronan Randles wrote:
> This patchset introduces a Gen library for DPDK. This library provides an easy
> way to generate traffic in order to test software based network components.
> 
> This library enables the basic functionality required in the traffic generator.
> This includes: raw data setting, packet Tx and Rx, creation and destruction of a
>  Gen instance and various types of data parsing.
> This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
> functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
> library.
> 
> A sample app is included in "examples/generator" which shows the use of the gen
> library in making a traffic generator. This can be used to generate traffic by
> running the dpdk-generator generator executable. This sample app supports
> runtime stats reporting (/gen/stats) and line rate limiting
> (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> 
> As more features are added to the gen library, the sample application will
> become more powerful through the "/gen/packet" string parameter
> (currently supports IP and Ether address setting). This will allow every
> application to generate more complex traffic types in the future without
> changing API.

A good start, thanks.

Testpmd can do txonly, rxonly, forwarding, but no rx+tx only :) I guess
that's the key of a GEN. testpmd also has "--stats-period" to show
statistics in non-interactive mode, very close to a GEN, but not
enough. Testpmd can toggle a lot of settings like offloading, that's an
advantage. Do you see an chance to make the lib part of testpmd?

I tried to leverage Scapy syntax into testpmd [1][2]: to set fancy
template - useful to test flows, or dump packet using different scapy
detail level. unfotunately I don't have time to finish it. Parsing
packet from string is boring, any consideration on this?

[1]: https://github.com/steevenlee/dpdk/commits/upstream_scapy
[2]: doc
https://github.com/steevenlee/dpdk/blob/875b8407f769465837513d29a495af3cc1a29765/doc/guides/howto/scapy.rst

> 
> Harry van Haaren (6):
>   gen: add files for initial traffic generation library
>   gen: add basic Rx and Tx routines and tests
>   gen: add raw packet data API and tests
>   gen: add parsing infrastructure and Ether protocol
>   gen: add gen IP parsing
>   examples/generator: import code from basicfwd.c
> 
> Ronan Randles (6):
>   net: add string to IPv4 parse function
>   net: add function to pretty print IPv4
>   examples/generator: enable gen library for traffic gen
>   examples/generator: telemetry support
>   examples/generator: link status check added
>   examples/generator: line rate limiting
> 
>  app/test/meson.build           |   4 +
>  app/test/test_gen.c            | 184 +++++++++++
>  app/test/test_net.c            |  87 ++++++
>  doc/api/doxy-api-index.md      |   3 +-
>  doc/api/doxy-api.conf.in       |   1 +
>  examples/generator/main.c      | 483 ++++++++++++++++++++++++++++
>  examples/generator/meson.build |  13 +
>  examples/meson.build           |   1 +
>  lib/gen/meson.build            |   6 +
>  lib/gen/rte_gen.c              | 553 +++++++++++++++++++++++++++++++++
>  lib/gen/rte_gen.h              | 114 +++++++
>  lib/gen/version.map            |  10 +
>  lib/meson.build                |   1 +
>  lib/net/meson.build            |   1 +
>  lib/net/rte_ip.c               |  58 ++++
>  lib/net/rte_ip.h               |  38 +++
>  lib/net/version.map            |   9 +
>  17 files changed, 1565 insertions(+), 1 deletion(-)
>  create mode 100644 app/test/test_gen.c
>  create mode 100644 app/test/test_net.c
>  create mode 100644 examples/generator/main.c
>  create mode 100644 examples/generator/meson.build
>  create mode 100644 lib/gen/meson.build
>  create mode 100644 lib/gen/rte_gen.c
>  create mode 100644 lib/gen/rte_gen.h
>  create mode 100644 lib/gen/version.map
>  create mode 100644 lib/net/rte_ip.c
> 


^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 00/12] add packet generator library and example app
  2022-01-21 14:44 ` [PATCH 00/12] add packet generator library and example app Xueming(Steven) Li
@ 2022-01-21 15:24   ` Van Haaren, Harry
  2022-01-24 10:48     ` Ananyev, Konstantin
  0 siblings, 1 reply; 62+ messages in thread
From: Van Haaren, Harry @ 2022-01-21 15:24 UTC (permalink / raw)
  To: Xueming(Steven) Li, dev, Randles, Ronan

> -----Original Message-----
> From: Xueming(Steven) Li <xuemingl@nvidia.com>
> Sent: Friday, January 21, 2022 2:45 PM
> To: dev@dpdk.org; Randles, Ronan <ronan.randles@intel.com>
> Cc: Van Haaren, Harry <harry.van.haaren@intel.com>
> Subject: Re: [PATCH 00/12] add packet generator library and example app
> 
> On Tue, 2021-12-14 at 14:12 +0000, Ronan Randles wrote:
> > This patchset introduces a Gen library for DPDK. This library provides an easy
> > way to generate traffic in order to test software based network components.
> >
> > This library enables the basic functionality required in the traffic generator.
> > This includes: raw data setting, packet Tx and Rx, creation and destruction of a
> >  Gen instance and various types of data parsing.
> > This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
> > functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
> > library.
> >
> > A sample app is included in "examples/generator" which shows the use of the
> gen
> > library in making a traffic generator. This can be used to generate traffic by
> > running the dpdk-generator generator executable. This sample app supports
> > runtime stats reporting (/gen/stats) and line rate limiting
> > (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> >
> > As more features are added to the gen library, the sample application will
> > become more powerful through the "/gen/packet" string parameter
> > (currently supports IP and Ether address setting). This will allow every
> > application to generate more complex traffic types in the future without
> > changing API.
> 
> A good start, thanks.
> 
> Testpmd can do txonly, rxonly, forwarding, but no rx+tx only :) I guess
> that's the key of a GEN. testpmd also has "--stats-period" to show
> statistics in non-interactive mode, very close to a GEN, but not
> enough. Testpmd can toggle a lot of settings like offloading, that's an
> advantage. Do you see an chance to make the lib part of testpmd?
> 
> I tried to leverage Scapy syntax into testpmd [1][2]: to set fancy
> template - useful to test flows, or dump packet using different scapy
> detail level. unfotunately I don't have time to finish it. Parsing
> packet from string is boring, any consideration on this?

Hi Steven,

Yes I recall the patchset to extend DPDK with python/scapy itself, and indeed this was my first approach too!
I build a proof-of-concept for enabling a "run-python-script-inside-eth-null-PMD-rx-function" approach.
Although this worked (in a clunky way, using packet.__bytes__() in python script..), the performance was
extremely low. An optimization was to pre-calculate e.g. 1 million packets, and leave them all in memory,
which causes memory waste, and lack of performance due to memory-boundedness in the CPU.

There is a fundamental blocking issue here - the generation/creation of packets is slow. Even updating a
single flow, or single parameter of the packet, could/would require recalculation of checksums... so leads
to the C code needing to know the L2/L3/L4 offsets. Even then, the re-calculation over 1-million packets
puts a lot of memory pressure - the packets cannot be re-used (from mempool lcore cache) if they need
to be "unique" through pre-computation, resulting in cache-trashing.

As a solution, moving to a native/C language based creation of a "base packet" (template) with "modifiers"
seemed a good fit. It allows best-case performance (mempool cache) and lowest compute per-packet.
The "modifiers" are "pay for what you need" approach to flows/updates/etc, allowing user to choose
the right balance of traffic-complexity vs CPU-cost to generate it.

All in all; handling string-parsing in C is not fun - but by paying that price we can gain many other things...

I think that a "gen" engine for TestPMD could be interesting for certain use-cases yes. As your links below,
the "expect" behaviour is a particularly nice way of working for testing! There is an implementation for
net/null PMD to create packets with Gen library, but is not ready for upstream so was not included in V2.

For multi-flow latency testing, I'm not sure if TestPMD is the ideal tool, as it does not prioritize RX/latency
at all costs: e.g lcores perform both RX and TX functionality. The dedicated "example/generator" is a sample
application that provides the basis for a more latency/jitter focused application, however there is much work
to do there to make it a production-ready tool.

Note that at the moment, the status of the Gen work is as follows;
- Upstream discussion: http://mails.dpdk.org/archives/dev/2022-January/232965.html
- V2 patch: http://patches.dpdk.org/project/dpdk/cover/20220121103122.2926856-1-ronan.randles@intel.com/

Regards, -Harry

PS: Side note around Python/Scapy: its an *awesome* tool. I like it a lot, but for DPDK levels of performance,
and in a latency/performance critical use-case, I'm not convinced it is the right tool to use.


> [1]: https://github.com/steevenlee/dpdk/commits/upstream_scapy
> [2]: doc
> https://github.com/steevenlee/dpdk/blob/875b8407f769465837513d29a495af3cc
> 1a29765/doc/guides/howto/scapy.rst
> 
> >
> > Harry van Haaren (6):
> >   gen: add files for initial traffic generation library
> >   gen: add basic Rx and Tx routines and tests
> >   gen: add raw packet data API and tests
> >   gen: add parsing infrastructure and Ether protocol
> >   gen: add gen IP parsing
> >   examples/generator: import code from basicfwd.c
> >
> > Ronan Randles (6):
> >   net: add string to IPv4 parse function
> >   net: add function to pretty print IPv4
> >   examples/generator: enable gen library for traffic gen
> >   examples/generator: telemetry support
> >   examples/generator: link status check added
> >   examples/generator: line rate limiting
> >
> >  app/test/meson.build           |   4 +
> >  app/test/test_gen.c            | 184 +++++++++++
> >  app/test/test_net.c            |  87 ++++++
> >  doc/api/doxy-api-index.md      |   3 +-
> >  doc/api/doxy-api.conf.in       |   1 +
> >  examples/generator/main.c      | 483 ++++++++++++++++++++++++++++
> >  examples/generator/meson.build |  13 +
> >  examples/meson.build           |   1 +
> >  lib/gen/meson.build            |   6 +
> >  lib/gen/rte_gen.c              | 553 +++++++++++++++++++++++++++++++++
> >  lib/gen/rte_gen.h              | 114 +++++++
> >  lib/gen/version.map            |  10 +
> >  lib/meson.build                |   1 +
> >  lib/net/meson.build            |   1 +
> >  lib/net/rte_ip.c               |  58 ++++
> >  lib/net/rte_ip.h               |  38 +++
> >  lib/net/version.map            |   9 +
> >  17 files changed, 1565 insertions(+), 1 deletion(-)
> >  create mode 100644 app/test/test_gen.c
> >  create mode 100644 app/test/test_net.c
> >  create mode 100644 examples/generator/main.c
> >  create mode 100644 examples/generator/meson.build
> >  create mode 100644 lib/gen/meson.build
> >  create mode 100644 lib/gen/rte_gen.c
> >  create mode 100644 lib/gen/rte_gen.h
> >  create mode 100644 lib/gen/version.map
> >  create mode 100644 lib/net/rte_ip.c
> >


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v2 02/15] net: add function to pretty print IPv4
  2022-01-21 10:31   ` [PATCH v2 02/15] net: add function to pretty print IPv4 Ronan Randles
@ 2022-01-21 16:20     ` Stephen Hemminger
  0 siblings, 0 replies; 62+ messages in thread
From: Stephen Hemminger @ 2022-01-21 16:20 UTC (permalink / raw)
  To: Ronan Randles; +Cc: dev

On Fri, 21 Jan 2022 10:31:09 +0000
Ronan Randles <ronan.randles@intel.com> wrote:

> +int32_t
> +rte_ip_print_addr(uint32_t ip_addr, char *buffer, uint32_t buffer_size)
> +{
> +	if (buffer == NULL)
> +		return -1;
> +
> +	snprintf(buffer, buffer_size, "%u.%u.%u.%u",
> +		(ip_addr >> 24),
> +		(ip_addr >> 16) & UINT8_MAX,
> +		(ip_addr >> 8) & UINT8_MAX,
> +		 ip_addr & UINT8_MAX);
> +
> +	return 0;

NAK
Broken on big endian machines.
Once again, why does DPDK need to reinvent everything?

^ permalink raw reply	[flat|nested] 62+ messages in thread

* RE: [PATCH 00/12] add packet generator library and example app
  2022-01-21 15:24   ` Van Haaren, Harry
@ 2022-01-24 10:48     ` Ananyev, Konstantin
  0 siblings, 0 replies; 62+ messages in thread
From: Ananyev, Konstantin @ 2022-01-24 10:48 UTC (permalink / raw)
  To: Van Haaren, Harry, Xueming(Steven) Li, dev, Randles, Ronan



> > On Tue, 2021-12-14 at 14:12 +0000, Ronan Randles wrote:
> > > This patchset introduces a Gen library for DPDK. This library provides an easy
> > > way to generate traffic in order to test software based network components.
> > >
> > > This library enables the basic functionality required in the traffic generator.
> > > This includes: raw data setting, packet Tx and Rx, creation and destruction of a
> > >  Gen instance and various types of data parsing.
> > > This functionality is implemented in "lib/gen/rte_gen.c". IPv4 parsing
> > > functionality is also added in "lib/net/rte_ip.c", this is then used in the gen
> > > library.
> > >
> > > A sample app is included in "examples/generator" which shows the use of the
> > gen
> > > library in making a traffic generator. This can be used to generate traffic by
> > > running the dpdk-generator generator executable. This sample app supports
> > > runtime stats reporting (/gen/stats) and line rate limiting
> > > (/gen/mpps,<target traffic rate in mpps>) through telemetry.py.
> > >
> > > As more features are added to the gen library, the sample application will
> > > become more powerful through the "/gen/packet" string parameter
> > > (currently supports IP and Ether address setting). This will allow every
> > > application to generate more complex traffic types in the future without
> > > changing API.
> >
> > A good start, thanks.
> >
> > Testpmd can do txonly, rxonly, forwarding, but no rx+tx only :) I guess
> > that's the key of a GEN. testpmd also has "--stats-period" to show
> > statistics in non-interactive mode, very close to a GEN, but not
> > enough. Testpmd can toggle a lot of settings like offloading, that's an
> > advantage. Do you see an chance to make the lib part of testpmd?
> >
> > I tried to leverage Scapy syntax into testpmd [1][2]: to set fancy
> > template - useful to test flows, or dump packet using different scapy
> > detail level. unfotunately I don't have time to finish it. Parsing
> > packet from string is boring, any consideration on this?
> 
> Hi Steven,
> 
> Yes I recall the patchset to extend DPDK with python/scapy itself, and indeed this was my first approach too!
> I build a proof-of-concept for enabling a "run-python-script-inside-eth-null-PMD-rx-function" approach.
> Although this worked (in a clunky way, using packet.__bytes__() in python script..), the performance was
> extremely low. An optimization was to pre-calculate e.g. 1 million packets, and leave them all in memory,
> which causes memory waste, and lack of performance due to memory-boundedness in the CPU.
> 
> There is a fundamental blocking issue here - the generation/creation of packets is slow. Even updating a
> single flow, or single parameter of the packet, could/would require recalculation of checksums... so leads
> to the C code needing to know the L2/L3/L4 offsets. Even then, the re-calculation over 1-million packets
> puts a lot of memory pressure - the packets cannot be re-used (from mempool lcore cache) if they need
> to be "unique" through pre-computation, resulting in cache-trashing.
> 
> As a solution, moving to a native/C language based creation of a "base packet" (template) with "modifiers"
> seemed a good fit. It allows best-case performance (mempool cache) and lowest compute per-packet.
> The "modifiers" are "pay for what you need" approach to flows/updates/etc, allowing user to choose
> the right balance of traffic-complexity vs CPU-cost to generate it.
> 
> All in all; handling string-parsing in C is not fun - but by paying that price we can gain many other things...

Just to throw in an alternate thought:
Instead of trying to re-implement scapy's abilities and face all related complexities (string parsings, etc.) -
might be choose another way: allow user to write his own packet fillers via eBPF?   
I.E: user write and compile eBPF program that will fill l2/l3/l4 headers, set mbuf flags, etc.
Then it will be loaded by packet-generator and executed for each outgoing packet.
That way user will have full flexibility and extensibility in defining his outgoing traffic.  
Again from developer/maintainer point of view such packet-gen app will probably require
much less effort.    
Though yes - user will need to do more work on his own - write actual packet filler code.

> 
> I think that a "gen" engine for TestPMD could be interesting for certain use-cases yes. As your links below,
> the "expect" behaviour is a particularly nice way of working for testing! There is an implementation for
> net/null PMD to create packets with Gen library, but is not ready for upstream so was not included in V2.
> 
> For multi-flow latency testing, I'm not sure if TestPMD is the ideal tool, as it does not prioritize RX/latency
> at all costs: e.g lcores perform both RX and TX functionality. The dedicated "example/generator" is a sample
> application that provides the basis for a more latency/jitter focused application, however there is much work
> to do there to make it a production-ready tool.
> 
> Note that at the moment, the status of the Gen work is as follows;
> - Upstream discussion: http://mails.dpdk.org/archives/dev/2022-January/232965.html
> - V2 patch: http://patches.dpdk.org/project/dpdk/cover/20220121103122.2926856-1-ronan.randles@intel.com/
> 
> Regards, -Harry
> 
> PS: Side note around Python/Scapy: its an *awesome* tool. I like it a lot, but for DPDK levels of performance,
> and in a latency/performance critical use-case, I'm not convinced it is the right tool to use.
> 
> 
> > [1]: https://github.com/steevenlee/dpdk/commits/upstream_scapy
> > [2]: doc
> > https://github.com/steevenlee/dpdk/blob/875b8407f769465837513d29a495af3cc
> > 1a29765/doc/guides/howto/scapy.rst
> >
> > >
> > > Harry van Haaren (6):
> > >   gen: add files for initial traffic generation library
> > >   gen: add basic Rx and Tx routines and tests
> > >   gen: add raw packet data API and tests
> > >   gen: add parsing infrastructure and Ether protocol
> > >   gen: add gen IP parsing
> > >   examples/generator: import code from basicfwd.c
> > >
> > > Ronan Randles (6):
> > >   net: add string to IPv4 parse function
> > >   net: add function to pretty print IPv4
> > >   examples/generator: enable gen library for traffic gen
> > >   examples/generator: telemetry support
> > >   examples/generator: link status check added
> > >   examples/generator: line rate limiting
> > >
> > >  app/test/meson.build           |   4 +
> > >  app/test/test_gen.c            | 184 +++++++++++
> > >  app/test/test_net.c            |  87 ++++++
> > >  doc/api/doxy-api-index.md      |   3 +-
> > >  doc/api/doxy-api.conf.in       |   1 +
> > >  examples/generator/main.c      | 483 ++++++++++++++++++++++++++++
> > >  examples/generator/meson.build |  13 +
> > >  examples/meson.build           |   1 +
> > >  lib/gen/meson.build            |   6 +
> > >  lib/gen/rte_gen.c              | 553 +++++++++++++++++++++++++++++++++
> > >  lib/gen/rte_gen.h              | 114 +++++++
> > >  lib/gen/version.map            |  10 +
> > >  lib/meson.build                |   1 +
> > >  lib/net/meson.build            |   1 +
> > >  lib/net/rte_ip.c               |  58 ++++
> > >  lib/net/rte_ip.h               |  38 +++
> > >  lib/net/version.map            |   9 +
> > >  17 files changed, 1565 insertions(+), 1 deletion(-)
> > >  create mode 100644 app/test/test_gen.c
> > >  create mode 100644 app/test/test_net.c
> > >  create mode 100644 examples/generator/main.c
> > >  create mode 100644 examples/generator/meson.build
> > >  create mode 100644 lib/gen/meson.build
> > >  create mode 100644 lib/gen/rte_gen.c
> > >  create mode 100644 lib/gen/rte_gen.h
> > >  create mode 100644 lib/gen/version.map
> > >  create mode 100644 lib/net/rte_ip.c
> > >


^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2022-01-24 10:49 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-14 14:12 [PATCH 00/12] add packet generator library and example app Ronan Randles
2021-12-14 14:12 ` [PATCH 01/12] net: add string to IPv4 parse function Ronan Randles
2021-12-14 17:31   ` Morten Brørup
2021-12-15  9:27     ` Bruce Richardson
2021-12-15  9:35       ` Morten Brørup
2021-12-15 10:11         ` Bruce Richardson
2022-01-19 14:20   ` Thomas Monjalon
2021-12-14 14:12 ` [PATCH 02/12] net: add function to pretty print IPv4 Ronan Randles
2021-12-14 16:08   ` Stephen Hemminger
2021-12-14 17:42     ` Morten Brørup
2021-12-14 17:31   ` Morten Brørup
2021-12-15  1:06     ` Ananyev, Konstantin
2021-12-15  3:20       ` Stephen Hemminger
2021-12-15  7:23         ` Morten Brørup
2021-12-15 13:06           ` Ananyev, Konstantin
2022-01-19 14:24             ` Thomas Monjalon
2022-01-19 14:41               ` Van Haaren, Harry
2021-12-14 14:12 ` [PATCH 03/12] gen: add files for initial traffic generation library Ronan Randles
2021-12-14 14:12 ` [PATCH 04/12] gen: add basic Rx and Tx routines and tests Ronan Randles
2021-12-14 14:12 ` [PATCH 05/12] gen: add raw packet data API " Ronan Randles
2021-12-15 12:40   ` Jerin Jacob
2021-12-17 11:40     ` Van Haaren, Harry
2021-12-17 16:19       ` Thomas Monjalon
2021-12-20 10:21         ` Van Haaren, Harry
2022-01-19 14:56           ` Thomas Monjalon
2022-01-20 10:21             ` Van Haaren, Harry
2022-01-21 10:45               ` Van Haaren, Harry
2021-12-20 13:21       ` Jerin Jacob
2022-01-21 14:20         ` Xueming(Steven) Li
2021-12-14 14:12 ` [PATCH 06/12] gen: add parsing infrastructure and Ether protocol Ronan Randles
2021-12-14 14:12 ` [PATCH 07/12] gen: add gen IP parsing Ronan Randles
2021-12-14 14:12 ` [PATCH 08/12] examples/generator: import code from basicfwd.c Ronan Randles
2021-12-14 14:12 ` [PATCH 09/12] examples/generator: enable gen library for traffic gen Ronan Randles
2021-12-14 14:12 ` [PATCH 10/12] examples/generator: telemetry support Ronan Randles
2021-12-14 14:12 ` [PATCH 11/12] examples/generator: link status check added Ronan Randles
2021-12-14 14:12 ` [PATCH 12/12] examples/generator: line rate limiting Ronan Randles
2021-12-14 16:10   ` Stephen Hemminger
2021-12-14 14:57 ` [PATCH 00/12] add packet generator library and example app Bruce Richardson
2021-12-14 15:59   ` Randles, Ronan
2022-01-12 16:18   ` Morten Brørup
2021-12-15 12:31 ` Jerin Jacob
2021-12-15 14:07   ` Bruce Richardson
2022-01-21 10:31 ` [PATCH v2 00/15] " Ronan Randles
2022-01-21 10:31   ` [PATCH v2 01/15] net: add string to IPv4 parse function Ronan Randles
2022-01-21 10:31   ` [PATCH v2 02/15] net: add function to pretty print IPv4 Ronan Randles
2022-01-21 16:20     ` Stephen Hemminger
2022-01-21 10:31   ` [PATCH v2 03/15] gen: add files for initial traffic generation library Ronan Randles
2022-01-21 10:31   ` [PATCH v2 04/15] gen: add basic Rx and Tx routines and tests Ronan Randles
2022-01-21 10:31   ` [PATCH v2 05/15] gen: add raw packet data API " Ronan Randles
2022-01-21 10:31   ` [PATCH v2 06/15] gen: add parsing infrastructure and Ether protocol Ronan Randles
2022-01-21 10:31   ` [PATCH v2 07/15] gen: add gen IP parsing Ronan Randles
2022-01-21 10:31   ` [PATCH v2 08/15] examples/generator: import code from basicfwd.c Ronan Randles
2022-01-21 10:31   ` [PATCH v2 09/15] examples/generator: enable gen library for traffic gen Ronan Randles
2022-01-21 10:31   ` [PATCH v2 10/15] examples/generator: telemetry support Ronan Randles
2022-01-21 10:31   ` [PATCH v2 11/15] examples/generator: link status check added Ronan Randles
2022-01-21 10:31   ` [PATCH v2 12/15] examples/generator: line rate limiting Ronan Randles
2022-01-21 10:31   ` [PATCH v2 13/15] gen: add UDP support Ronan Randles
2022-01-21 10:31   ` [PATCH v2 14/15] net/vxlan: instance flag endianness refactored Ronan Randles
2022-01-21 10:31   ` [PATCH v2 15/15] gen: add VXLAN support Ronan Randles
2022-01-21 14:44 ` [PATCH 00/12] add packet generator library and example app Xueming(Steven) Li
2022-01-21 15:24   ` Van Haaren, Harry
2022-01-24 10:48     ` Ananyev, Konstantin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).