get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/105136/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 105136,
    "url": "http://patches.dpdk.org/api/patches/105136/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20211214141242.3383831-7-ronan.randles@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20211214141242.3383831-7-ronan.randles@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20211214141242.3383831-7-ronan.randles@intel.com",
    "date": "2021-12-14T14:12:36",
    "name": "[06/12] gen: add parsing infrastructure and Ether protocol",
    "commit_ref": null,
    "pull_url": null,
    "state": "not-applicable",
    "archived": true,
    "hash": "e030776380598c4399ca5fd4719a3b19a79df7fc",
    "submitter": {
        "id": 2439,
        "url": "http://patches.dpdk.org/api/people/2439/?format=api",
        "name": "Ronan Randles",
        "email": "ronan.randles@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20211214141242.3383831-7-ronan.randles@intel.com/mbox/",
    "series": [
        {
            "id": 20944,
            "url": "http://patches.dpdk.org/api/series/20944/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=20944",
            "date": "2021-12-14T14:12:30",
            "name": "add packet generator library and example app",
            "version": 1,
            "mbox": "http://patches.dpdk.org/series/20944/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/105136/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/105136/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 2C6EBA00C3;\n\tTue, 14 Dec 2021 15:13:32 +0100 (CET)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 036F941174;\n\tTue, 14 Dec 2021 15:12:59 +0100 (CET)",
            "from mga07.intel.com (mga07.intel.com [134.134.136.100])\n by mails.dpdk.org (Postfix) with ESMTP id 0DCC541160\n for <dev@dpdk.org>; Tue, 14 Dec 2021 15:12:56 +0100 (CET)",
            "from orsmga006.jf.intel.com ([10.7.209.51])\n by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 14 Dec 2021 06:12:56 -0800",
            "from silpixa00401120.ir.intel.com ([10.55.129.95])\n by orsmga006.jf.intel.com with ESMTP; 14 Dec 2021 06:12:55 -0800"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=simple/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1639491177; x=1671027177;\n h=from:to:cc:subject:date:message-id:in-reply-to:\n references:mime-version:content-transfer-encoding;\n bh=ypT+HuKf923gPNTXD4Isc9GzIvf2Asw/23Jt7emBTNs=;\n b=fdBPadcxPVNr6mFVdH8T+nhUTcrnpMQq+3RGxisU4mWvnG0hlgqe3z5Z\n dZOwRZgsm4J4qB/1TUfFW82yiIHFZIxEVwu8oNLoLosXZeWUp/Cgd2Bnc\n PmmPTLppMr1f8IP0wUM7/HffSP9BZ8lnJiwooC5DVfHazBsuiFhM9Nu8t\n Ti+9DkV0eGgWOVlZIlkgQGBCjYcm8Xmwm9+KCCKZe+/wCgP0a5NHfO8DI\n yDtOoIUgJLkFctiC/RMIlpRj/h9KrUULeNKtTFnhgK2T9zn9MDczfQ4wq\n tLZHSqPSpVAOV0f5EAauUIBgAblETNSxH0KUCgtyiI4mPr/7a1nNLOXY6 A==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6200,9189,10197\"; a=\"302362329\"",
            "E=Sophos;i=\"5.88,205,1635231600\"; d=\"scan'208\";a=\"302362329\"",
            "E=Sophos;i=\"5.88,205,1635231600\"; d=\"scan'208\";a=\"465104141\""
        ],
        "X-ExtLoop1": "1",
        "From": "Ronan Randles <ronan.randles@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "harry.van.haaren@intel.com",
        "Subject": "[PATCH 06/12] gen: add parsing infrastructure and Ether protocol",
        "Date": "Tue, 14 Dec 2021 14:12:36 +0000",
        "Message-Id": "<20211214141242.3383831-7-ronan.randles@intel.com>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<20211214141242.3383831-1-ronan.randles@intel.com>",
        "References": "<20211214141242.3383831-1-ronan.randles@intel.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "From: Harry van Haaren <harry.van.haaren@intel.com>\n\nThis commit adds parsing infrastructure and support\nfor Ether parsing. Appropriate unit tests are also added\n\nSigned-off-by: Harry van Haaren <harry.van.haaren@intel.com>\n---\n app/test/test_gen.c |  29 +++++\n lib/gen/meson.build |   2 +-\n lib/gen/rte_gen.c   | 281 ++++++++++++++++++++++++++++++++++++++++++++\n lib/gen/rte_gen.h   |  14 ++-\n lib/gen/version.map |   1 +\n 5 files changed, 325 insertions(+), 2 deletions(-)",
    "diff": "diff --git a/app/test/test_gen.c b/app/test/test_gen.c\nindex b60ceaef8a..324582d0a5 100644\n--- a/app/test/test_gen.c\n+++ b/app/test/test_gen.c\n@@ -112,6 +112,34 @@ test_gen_packet_set_raw(void)\n \treturn 0;\n }\n \n+static int\n+test_gen_packet_parse_string(void)\n+{\n+\tstruct rte_gen *gen = rte_gen_create(mp);\n+\tTEST_ASSERT_FAIL(gen, \"Expected valid pointer after create()\");\n+\n+\tstruct str_parse_t {\n+\t\tconst char *str;\n+\t} pkt_strings[] = {\n+\t\t{ .str = \"Ether()\"},\n+\t\t{ .str = \"Ether()/\"},\n+\t\t{ .str = \"/Ether()\"},\n+\t\t{ .str = \"/Ether()/\"}\n+\t};\n+\n+\tuint32_t i;\n+\tfor (i = 0; i < RTE_DIM(pkt_strings); i++) {\n+\t\tconst char *pkt_str = pkt_strings[i].str;\n+\t\tint32_t err = rte_gen_packet_parse_string(gen, pkt_str, NULL);\n+\t\tTEST_ASSERT_EQUAL(err, 0, \"Expected string %s to parse.\",\n+\t\t\t\tpkt_str);\n+\t}\n+\n+\trte_gen_destroy(gen);\n+\treturn 0;\n+}\n+\n+\n static struct unit_test_suite gen_suite  = {\n \t.suite_name = \"gen: packet generator unit test suite\",\n \t.setup = testsuite_setup,\n@@ -121,6 +149,7 @@ static struct unit_test_suite gen_suite  = {\n \t\tTEST_CASE_ST(NULL, NULL, test_gen_basic_rxtx),\n \t\tTEST_CASE_ST(NULL, NULL, test_gen_loop_rxtx),\n \t\tTEST_CASE_ST(NULL, NULL, test_gen_packet_set_raw),\n+\t\tTEST_CASE_ST(NULL, NULL, test_gen_packet_parse_string),\n \t\tTEST_CASES_END() /**< NULL terminate unit test array */\n \t}\n };\ndiff --git a/lib/gen/meson.build b/lib/gen/meson.build\nindex 753984cbba..b3a55564f4 100644\n--- a/lib/gen/meson.build\n+++ b/lib/gen/meson.build\n@@ -3,4 +3,4 @@\n \n sources = files('rte_gen.c')\n headers = files('rte_gen.h')\n-deps += ['mempool', 'mbuf']\n+deps += ['mempool', 'mbuf', 'net']\ndiff --git a/lib/gen/rte_gen.c b/lib/gen/rte_gen.c\nindex 432be65f1a..ab73120791 100644\n--- a/lib/gen/rte_gen.c\n+++ b/lib/gen/rte_gen.c\n@@ -9,12 +9,18 @@\n #include <rte_hexdump.h>\n #include <rte_log.h>\n \n+#include <rte_ether.h>\n+\n RTE_LOG_REGISTER(gen_logtype, lib.gen, NOTICE);\n \n #define TGEN_LOG(level, fmt, args...)\t\t\t\t\\\n \trte_log(RTE_LOG_ ## level, gen_logtype, \"%s(): \" fmt,\t\\\n \t\t__func__, ## args)\n \n+/* Don't prefix with function name, breaks the Scapy style formatting. */\n+#define TGEN_LOG_PROTOCOL(level, fmt, args...)\t\t\t\\\n+\trte_log(RTE_LOG_ ## level, gen_logtype, fmt, ## args)\n+\n #define GEN_MAX_BURST 32\n #define GEN_INIT_PKT_SIZE 64\n \n@@ -126,3 +132,278 @@ rte_gen_tx_burst(struct rte_gen *gen,\n \n \treturn nb_pkts;\n }\n+\n+enum GEN_PROTO {\n+\tGEN_PROTO_INVALID,\n+\tGEN_PROTO_ETHER,\n+\n+\t/* Must be last. */\n+\tGEN_PROTO_COUNT,\n+};\n+\n+typedef void (*gen_log_func)(void *data, const char *indent);\n+\n+/* Structure for holding offset and function pointers for protocol. */\n+struct protocol_meta {\n+\t/* Byte offset into packet where this protocol starts. */\n+\tuint32_t offset;\n+\t/* Function to call to log the packet's information. */\n+\tgen_log_func log_func;\n+};\n+\n+/* Allow up to 32 nexted '/' characters in the protocol string. */\n+#define GEN_PROTO_PARSE_MAX 16\n+\n+/* Structure to hold state required while parsing. */\n+struct gen_parser {\n+\t/* Mbuf the parsed data is being put into. */\n+\tstruct rte_mbuf *mbuf;\n+\tuint8_t *mbuf_data;\n+\n+\t/* Offset into the packet data to parse to next. */\n+\tuint32_t buf_write_offset;\n+\n+\t/* Parsing state. */\n+\tuint8_t parse_iter;\n+\tchar indent_str[(GEN_PROTO_PARSE_MAX * 2) + 1];\n+\n+\t/* String being parsed. */\n+\tchar *parse_string;\n+\tchar *parse_strtok_save_ptr;\n+\n+\t/* Store metadata for parse/display of protocols.  */\n+\tstruct protocol_meta proto_meta[GEN_PROTO_PARSE_MAX];\n+\n+\t/* Per protocol hit counters. */\n+\tuint32_t proto_hit_counters[GEN_PROTO_COUNT];\n+};\n+\n+/* Forward declaration of recursive parsing function.\n+ * @param inner reports back the inner protocol that was handled. This is often\n+ * required for the outer protocol to indicate what the inner protocol is.\n+ */\n+static int32_t\n+gen_parser_parse_next(struct gen_parser *parser, enum GEN_PROTO *inner);\n+\n+/* Return void pointer to the position in the data buffer to parse into. */\n+static inline void *\n+gen_parser_get_data_ptr(struct gen_parser *parser)\n+{\n+\treturn &parser->mbuf_data[parser->buf_write_offset];\n+}\n+\n+/* Initialize a parser structure. */\n+static int32_t\n+gen_parser_init(struct gen_parser *parser, struct rte_gen *gen,\n+\t\tconst char *pkt_string)\n+{\n+\t/* Initialize own memory to zero. */\n+\tmemset(parser, 0, sizeof(*parser));\n+\n+\t/* Duplicate string for tokenizing string. */\n+\tparser->parse_string = strdup(pkt_string);\n+\tif (!parser->parse_string)\n+\t\tgoto error;\n+\n+\t/* Allocate mbuf to parse packet into. */\n+\tparser->mbuf = rte_pktmbuf_alloc(gen->mp);\n+\tif (!parser->mbuf)\n+\t\tgoto error;\n+\n+\tparser->mbuf_data = rte_pktmbuf_mtod(parser->mbuf, uint8_t *);\n+\n+\treturn 0;\n+\n+error:\n+\tfree(parser->parse_string);\n+\treturn -ENOMEM;\n+}\n+\n+static void\n+gen_log_ether(void *data, const char *indent)\n+{\n+\tstruct rte_ether_hdr *eth = data;\n+\tchar src[64];\n+\tchar dst[64];\n+\n+\trte_ether_format_addr(src, 64, &eth->src_addr);\n+\trte_ether_format_addr(dst, 64, &eth->dst_addr);\n+\tconst char *type_str;\n+\tswitch (rte_be_to_cpu_16(eth->ether_type)) {\n+\tcase RTE_ETHER_TYPE_IPV4:\n+\t\ttype_str = \"IPv4\";\n+\t\tbreak;\n+\tdefault:\n+\t\ttype_str = \"0x9000\";\n+\t\tbreak;\n+\t};\n+\tTGEN_LOG_PROTOCOL(DEBUG,\n+\t\t\"###[ Ethernet ]###\\n%sdst= %s\\n%ssrc= %s\\n%stype= %s\\n\",\n+\t\tindent, dst, indent, src, indent, type_str);\n+}\n+\n+/* Ether(...) string detected, supports parameters:\n+ * - dst : Destination MAC in 00:11:22:33:44:55 or 0011:2233:4455 forms.\n+ * - src : Source MAC in the same forms.\n+ * Note:\n+ * - type is set based on the next header\n+ */\n+static int32_t\n+gen_parse_ether(struct gen_parser *parser, char *protocol_str)\n+{\n+\tstruct rte_ether_hdr *eth = gen_parser_get_data_ptr(parser);\n+\n+\tchar *dst_ptr = strstr(protocol_str, \"dst=\");\n+\tif (dst_ptr) {\n+\t\tchar *dup = strdup(dst_ptr);\n+\t\trte_ether_unformat_addr(&dup[4], &eth->dst_addr);\n+\t\tfree(dup);\n+\t} else\n+\t\trte_ether_unformat_addr(\"ff:ff:ff:ff:ff:ff\", &eth->dst_addr);\n+\n+\tchar *src_ptr = strstr(protocol_str, \"src=\");\n+\tif (src_ptr)\n+\t\trte_ether_unformat_addr(&src_ptr[4], &eth->src_addr);\n+\telse\n+\t\trte_ether_unformat_addr(\"00:00:00:00:00:00\", &eth->src_addr);\n+\n+\t/* Move up write pointer in packet. */\n+\tparser->buf_write_offset += sizeof(*eth);\n+\n+\t/* Recurse and handle inner protocol. */\n+\tenum GEN_PROTO inner;\n+\tint32_t err = gen_parser_parse_next(parser, &inner);\n+\tif (err) {\n+\t\tTGEN_LOG(ERR, \"parser parse next() error %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\tswitch (inner) {\n+\tdefault:\n+\t\teth->ether_type = rte_cpu_to_be_16(0x9000);\n+\t\tbreak;\n+\t};\n+\treturn 0;\n+}\n+\n+/* (Name, Function-pointer) pairs for supported parse types */\n+typedef int32_t (*gen_parse_func)(struct gen_parser *parser,\n+\t\t\t\tchar *protocol_str);\n+\n+struct gen_parse_func_t {\n+\tconst char *name;\n+\tenum GEN_PROTO proto;\n+\tgen_parse_func parse_func;\n+\tgen_log_func log_func;\n+};\n+\n+/* Mapping from string to function to parse that protocol. */\n+static struct gen_parse_func_t gen_protocols[] = {\n+\t{\n+\t\t.name = \"Ether(\",\n+\t\t.proto = GEN_PROTO_ETHER,\n+\t\t.parse_func = gen_parse_ether,\n+\t\t.log_func = gen_log_ether,\n+\t}\n+};\n+\n+/* Function to tokenize and parse each segment of a string.\n+ * @param outer indicates the protocol before this one.\n+ * @param inner returns the protocol that is parsed here/now.\n+ */\n+static int32_t\n+gen_parser_parse_next(struct gen_parser *parser,\n+\t\t\tenum GEN_PROTO *inner_proto)\n+{\n+\t/* Tokenize the input string based on '/' character. */\n+\tchar *tok_str = (parser->parse_iter == 0) ?\n+\t\t\t\t\tparser->parse_string : NULL;\n+\tparser->parse_string = strtok_r(tok_str, \"/\",\n+\t\t\t\t\t&parser->parse_strtok_save_ptr);\n+\n+\t/* End protocol parsing recursion when parse_string is NULL, or max\n+\t * protocol recursion depth is reached.\n+\t */\n+\tif (!parser->parse_string ||\n+\t\t\tparser->parse_iter >= GEN_PROTO_PARSE_MAX) {\n+\t\tstruct rte_mbuf *mbuf = parser->mbuf;\n+\t\tmbuf->data_len = parser->buf_write_offset;\n+\t\tmbuf->pkt_len = parser->buf_write_offset;\n+\t\tTGEN_LOG(DEBUG, \"packet length %d\\n\", mbuf->pkt_len);\n+\t\treturn 0;\n+\t}\n+\n+\tuint32_t i;\n+\t/* Loop over protocols, and identify the parse function to call. */\n+\tfor (i = 0; i < RTE_DIM(gen_protocols); i++) {\n+\t\tconst char *proto = gen_protocols[i].name;\n+\t\tuint32_t proto_len = strlen(proto);\n+\t\tif (strncmp(proto, parser->parse_string, proto_len))\n+\t\t\tcontinue;\n+\n+\t\t/* Store the log function pointer to output later. */\n+\t\tuint32_t iter = parser->parse_iter;\n+\t\tparser->proto_hit_counters[i]++;\n+\t\tstruct protocol_meta *meta = &parser->proto_meta[iter];\n+\t\tmeta->log_func = gen_protocols[i].log_func;\n+\t\tmeta->offset = parser->buf_write_offset;\n+\n+\t\t/* Handle protocol recursively. */\n+\t\tparser->parse_iter++;\n+\t\tint err = gen_protocols[i].parse_func(parser,\n+\t\t\t\t\t\t\tparser->parse_string);\n+\t\t*inner_proto = gen_protocols[i].proto;\n+\n+\t\treturn err;\n+\t}\n+\n+\tTGEN_LOG(ERR, \"parser does not understand protocol %s\\n\",\n+\t\tparser->parse_string);\n+\treturn -1;\n+}\n+\n+int32_t\n+rte_gen_packet_parse_string(struct rte_gen *gen,\n+\t\t\t    const char *pkt_string,\n+\t\t\t    struct rte_mbuf **old_mbuf_to_user)\n+{\n+\tstruct gen_parser parser;\n+\tint32_t err = gen_parser_init(&parser, gen, pkt_string);\n+\tif (err) {\n+\t\tTGEN_LOG(ERR, \"error with parser_init(), %d\\n\", err);\n+\t\treturn -1;\n+\t};\n+\n+\t/* Recursively parse each protocol. */\n+\tenum GEN_PROTO inner;\n+\terr = gen_parser_parse_next(&parser, &inner);\n+\tif (err) {\n+\t\tTGEN_LOG(ERR, \"Error in parsing packet string. \"\n+\t\t\t\"Set \\\"gen\\\" log level to debug for more info.\\n\");\n+\t\treturn -1;\n+\t}\n+\n+\tuint32_t i;\n+\t/* Iterate the per protocol stored metadata to log output. */\n+\tfor (i = 0; i < parser.parse_iter; i++) {\n+\t\tsnprintf(parser.indent_str, 2 + i * 2,\n+\t\t\t\"                               \" /* 32 spaces. */);\n+\t\tvoid *buf_off = parser.mbuf_data + parser.proto_meta[i].offset;\n+\t\tparser.proto_meta[i].log_func(buf_off, parser.indent_str);\n+\t}\n+\n+\tif (inner != GEN_PROTO_ETHER) {\n+\t\tTGEN_LOG(WARNING,\n+\t\t\t\"Outer protocol of frame is not Ethernet.\\n\");\n+\t}\n+\n+\t/* Free the currently in use mbuf. */\n+\tif (old_mbuf_to_user)\n+\t\t*old_mbuf_to_user = gen->base_pkt;\n+\telse\n+\t\trte_pktmbuf_free(gen->base_pkt);\n+\n+\t/* TODO: HVH design race-condition above vs rx/tx*/\n+\tgen->base_pkt = parser.mbuf;\n+\treturn 0;\n+}\ndiff --git a/lib/gen/rte_gen.h b/lib/gen/rte_gen.h\nindex c8d85a5b72..93b3346436 100644\n--- a/lib/gen/rte_gen.h\n+++ b/lib/gen/rte_gen.h\n@@ -89,12 +89,24 @@ rte_gen_tx_burst(struct rte_gen *gen,\n  * @retval 0 Success.\n  * @retval -ENOMEM No memory available.\n  */\n-int32_t\n __rte_experimental\n+int32_t\n rte_gen_packet_set_raw(struct rte_gen *gen,\n \t\t       const uint8_t *raw_data,\n \t\t       uint32_t raw_data_size);\n \n+/* Parse a string description of a packet.\n+ *\n+ * The optional out parameter supplies the previously being sent mbuf to\n+ * the user to be freed later. If this argument is not provided, then the\n+ * mbuf is freed by this function.\n+ */\n+__rte_experimental\n+int32_t\n+rte_gen_packet_parse_string(struct rte_gen *gen,\n+\t\t\t    const char *pkt_string,\n+\t\t\t    struct rte_mbuf **old_mbuf_to_user);\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/lib/gen/version.map b/lib/gen/version.map\nindex d75e0b4bac..e335c608ee 100644\n--- a/lib/gen/version.map\n+++ b/lib/gen/version.map\n@@ -6,4 +6,5 @@ EXPERIMENTAL {\n \trte_gen_rx_burst;\n \trte_gen_tx_burst;\n \trte_gen_packet_set_raw;\n+\trte_gen_packet_parse_string;\n };\n",
    "prefixes": [
        "06/12"
    ]
}