From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 2C6EBA00C3; Tue, 14 Dec 2021 15:13:32 +0100 (CET) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 036F941174; Tue, 14 Dec 2021 15:12:59 +0100 (CET) Received: from mga07.intel.com (mga07.intel.com [134.134.136.100]) by mails.dpdk.org (Postfix) with ESMTP id 0DCC541160 for ; Tue, 14 Dec 2021 15:12:56 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1639491177; x=1671027177; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ypT+HuKf923gPNTXD4Isc9GzIvf2Asw/23Jt7emBTNs=; b=fdBPadcxPVNr6mFVdH8T+nhUTcrnpMQq+3RGxisU4mWvnG0hlgqe3z5Z dZOwRZgsm4J4qB/1TUfFW82yiIHFZIxEVwu8oNLoLosXZeWUp/Cgd2Bnc PmmPTLppMr1f8IP0wUM7/HffSP9BZ8lnJiwooC5DVfHazBsuiFhM9Nu8t Ti+9DkV0eGgWOVlZIlkgQGBCjYcm8Xmwm9+KCCKZe+/wCgP0a5NHfO8DI yDtOoIUgJLkFctiC/RMIlpRj/h9KrUULeNKtTFnhgK2T9zn9MDczfQ4wq tLZHSqPSpVAOV0f5EAauUIBgAblETNSxH0KUCgtyiI4mPr/7a1nNLOXY6 A==; X-IronPort-AV: E=McAfee;i="6200,9189,10197"; a="302362329" X-IronPort-AV: E=Sophos;i="5.88,205,1635231600"; d="scan'208";a="302362329" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 14 Dec 2021 06:12:56 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.88,205,1635231600"; d="scan'208";a="465104141" Received: from silpixa00401120.ir.intel.com ([10.55.129.95]) by orsmga006.jf.intel.com with ESMTP; 14 Dec 2021 06:12:55 -0800 From: Ronan Randles 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 List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Harry van Haaren This commit adds parsing infrastructure and support for Ether parsing. Appropriate unit tests are also added Signed-off-by: Harry van Haaren --- 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 #include +#include + 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, ð->src_addr); + rte_ether_format_addr(dst, 64, ð->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], ð->dst_addr); + free(dup); + } else + rte_ether_unformat_addr("ff:ff:ff:ff:ff:ff", ð->dst_addr); + + char *src_ptr = strstr(protocol_str, "src="); + if (src_ptr) + rte_ether_unformat_addr(&src_ptr[4], ð->src_addr); + else + rte_ether_unformat_addr("00:00:00:00:00:00", ð->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