From 72adc18af784915e7cc4c207655a38e49e33232e Mon Sep 17 00:00:00 2001 From: Jett Jacobs <62487934+jettjacobs@users.noreply.github.com> Date: Mon, 27 Sep 2021 13:44:01 -0400 Subject: [PATCH 01/16] Added Skeleton NF --- examples/skeleton/Makefile | 67 +++++++++ examples/skeleton/README.md | 31 ++++ examples/skeleton/go.sh | 20 +++ examples/skeleton/skeleton.c | 282 +++++++++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 examples/skeleton/Makefile create mode 100644 examples/skeleton/README.md create mode 100644 examples/skeleton/go.sh create mode 100644 examples/skeleton/skeleton.c diff --git a/examples/skeleton/Makefile b/examples/skeleton/Makefile new file mode 100644 index 000000000..edd7e5211 --- /dev/null +++ b/examples/skeleton/Makefile @@ -0,0 +1,67 @@ +# openNetVM +# https://github.com/sdnfv/openNetVM +# +# BSD LICENSE +# +# Copyright(c) +# 2015-2017 George Washington University +# 2015-2017 University of California Riverside +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ifeq ($(RTE_SDK),) +$(error "Please define RTE_SDK environment variable") +endif + + +# Default target, can be overriden by command line or environment +include $(RTE_SDK)/mk/rte.vars.mk +RTE_TARGET ?= x86_64-native-linuxapp-gcc + +# binary name +APP = skeleton + +# all source are stored in SRCS-y +SRCS-y := skeleton.c + +ONVM= $(SRCDIR)/../../onvm + +CFLAGS += $(WERROR_FLAGS) -O3 $(USER_FLAGS) + +CFLAGS += -I$(ONVM)/onvm_nflib +CFLAGS += -I$(ONVM)/lib +LDFLAGS += $(ONVM)/onvm_nflib/$(RTE_TARGET)/libonvm.a +LDFLAGS += $(ONVM)/lib/$(RTE_TARGET)/lib/libonvmhelper.a -lm + +# workaround for a gcc bug with noreturn attribute +# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603 +ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y) +CFLAGS_main.o += -Wno-return-type +endif + +include $(RTE_SDK)/mk/rte.extapp.mk diff --git a/examples/skeleton/README.md b/examples/skeleton/README.md new file mode 100644 index 000000000..bef504178 --- /dev/null +++ b/examples/skeleton/README.md @@ -0,0 +1,31 @@ +Skeleton +== +This is an example NF that acts as a basic skeleton NF. + +Compilation and Execution +-- +``` +cd examples +make +cd skeleton +./go.sh SERVICE_ID [PRINT_DELAY] + +OR + +./go.sh -F CONFIG_FILE -- -- [-p PRINT_DELAY] + +OR + +sudo ./build/skeleton -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- [-p PRINT_DELAY] +``` + +App Specific Arguments +-- + - `-p `: number of packets between each print, e.g. `-p 1` prints every packets. + +Config File Support +-- +This NF supports the NF generating arguments from a config file. For +additional reading, see [Examples.md](../../docs/Examples.md) + +See `../example_config.json` for all possible options that can be set. diff --git a/examples/skeleton/go.sh b/examples/skeleton/go.sh new file mode 100644 index 000000000..03fc3bb36 --- /dev/null +++ b/examples/skeleton/go.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +#The go.sh script is a convinient way to run start_nf.sh without specifying NF_NAME + +NF_DIR=${PWD##*/} + +if [ ! -f ../start_nf.sh ]; then + echo "ERROR: The ./go.sh script can only be used from the NF folder" + echo "If running from other directory use examples/start_nf.sh" + exit 1 +fi + +# only check for running manager if not in Docker +if [[ -z $(pgrep -u root -f "/onvm/onvm_mgr/.*/onvm_mgr") ]] && ! grep -q "docker" /proc/1/cgroup +then + echo "NF cannot start without a running manager" + exit 1 +fi + +../start_nf.sh "$NF_DIR" "$@" diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c new file mode 100644 index 000000000..85349e6eb --- /dev/null +++ b/examples/skeleton/skeleton.c @@ -0,0 +1,282 @@ +/********************************************************************* + * openNetVM + * https://sdnfv.github.io + * + * BSD LICENSE + * + * Copyright(c) + * 2015-2021 George Washington University + * 2015-2021 University of California Riverside + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * skeleton.c - Template NF for development. + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "onvm_nflib.h" +#include "onvm_pkt_helper.h" + +// Define NF Tag Appropriately +#define NF_TAG "skeleton" + +/* number of package between each print */ +static uint32_t print_delay = 1000000; + +// State structs hold any variables which are universal to all files within the NF +// The following struct is an example from text_messaging NF: +struct skeleton_state { + // Insert stored data which is not specific to any function (similar to global data) + int tests_passed; + int test_phase; + int test_msg_count; + int ring_count; + int mempool_count; + uint16_t address; + struct rte_mempool* msg_pool; + struct rte_ring *msg_q; + int test_status[3]; + uint64_t start_time; +}; + +/* + * Print a usage message + */ +static void +usage(const char *progname) { + printf("Usage:\n"); + printf("%s [EAL args] -- [NF_LIB args] -- -p \n", progname); + printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); + printf("Flags:\n"); + printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); +} + +/* + * Parse the application arguments. + */ +static int +parse_app_args(int argc, char *argv[], const char *progname) { + int c; + + // Handles command line arguments + while ((c = getopt(argc, argv, "p:")) != -1) { + switch (c) { + case 'p': + print_delay = strtoul(optarg, NULL, 10); + break; + case '?': + usage(progname); + if (optopt == 'p') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (isprint(optopt)) + RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); + else + RTE_LOG(INFO, APP, "Unknown option character `\\x%x'.\n", optopt); + return -1; + default: + usage(progname); + return -1; + } + } + return optind; +} + +// Function for NF with displays - Example from bridge.c +static void +do_stats_display(struct rte_mbuf *pkt) { + + // [EDIT CODE IF NF USES DISPLAY] + + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; + static uint64_t pkt_process = 0; + + struct rte_ipv4_hdr *ip; + + pkt_process += print_delay; + + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + + printf("PACKETS\n"); + printf("-----\n"); + printf("Port : %d\n", pkt->port); + printf("Size : %d\n", pkt->pkt_len); + printf("Type : %d\n", pkt->packet_type); + printf("Number of packet processed : %" PRIu64 "\n", pkt_process); + + ip = onvm_pkt_ipv4_hdr(pkt); + if (ip != NULL) { + onvm_pkt_print(pkt); + } else { + printf("Not IP4\n"); + } + + printf("\n\n"); +} + + +static int +packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + + // Timed display counter - keep/edit if NF uses display + static uint32_t counter = 0; + if (counter++ == print_delay) { + do_stats_display(pkt); + counter = 0; + } + + // [EDIT PACKET HANDLER CODE BELOW] + + /* Important Variables + meta->action + Defines what to do with the packet. Actions defined in onvm_common.h + meta->destination + Defines the service ID of the NF which will receive the packet. + */ + if (pkt->port == 0) { + meta->destination = 1; + } else { + meta->destination = 0; + } + + // Send the packet out of the NIC port + meta->action = ONVM_NF_ACTION_OUT; + + // Return 0 on success, -1 on failure + return 0; +} + +static int +action(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx){ + + // Gets called every run loop even if there weren't any packets + + // [ADD ACTION CODE] - Remove __attribute__((unused)) + return 0; +} + +static void +setup(__attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx){ + + // Gets called once before NF will get any packets/messages + + // Excerpt from text_messaging setup; note nf_local_ctx->nf->data with corresponding struct + struct rte_mempool *pktmbuf_pool; + struct test_msg_data *test_state; + static struct rte_mempool *nf_msg_pool; + pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); + if (pktmbuf_pool == NULL) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); + } + nf_msg_pool = rte_mempool_lookup(_NF_MSG_POOL_NAME); + if (nf_msg_pool == NULL) + rte_exit(EXIT_FAILURE, "No NF Message mempool - bye\n"); + + nf_local_ctx->nf->data = (void *)test_state; + + // [REMOVE EXCERPT, ADD SETUP CODE] +} + +static void +handle_msg(__attribute__((unused))void *msg_data, __attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx) { + + // Gets called if there is a message sent to the NF + + // [ADD MESSAGE HANDLER CODE] - Remove __attribute__((unused)) +} + +int +main(int argc, char *argv[]) { + + // Handles command line arguments + int arg_offset; + // Local context holds NF and status + struct onvm_nf_local_ctx *nf_local_ctx; + // Function table holds pointers to NF setup, msg_handler, user_actions, and pkt_handler + struct onvm_nf_function_table *nf_function_table; + // Handles command line arguments + const char *progname = argv[0]; + + // Initializing local context and start default signal handler + nf_local_ctx = onvm_nflib_init_nf_local_ctx(); + onvm_nflib_start_signal_handler(nf_local_ctx, NULL); + + // [EDIT FUNCTION TABLE AS NEEDED] + // Address declarations of NF functions for function table: + nf_function_table = onvm_nflib_init_nf_function_table(); + nf_function_table->pkt_handler = &packet_handler; + nf_function_table->setup = &setup; + nf_function_table->user_actions = &action; + nf_function_table->msg_handler = &handle_msg; + + // If a termination signal is received or initiation is interrupted, exit + if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { + onvm_nflib_stop(nf_local_ctx); + if (arg_offset == ONVM_SIGNAL_TERMINATION) { + printf("Exiting due to user termination\n"); + return 0; + } else { + rte_exit(EXIT_FAILURE, "Failed ONVM init\n"); + } + } + + // Command line arguments + argc -= arg_offset; + argv += arg_offset; + + // Invalid command-line argument handling + if (parse_app_args(argc, argv, progname) < 0) { + onvm_nflib_stop(nf_local_ctx); + rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); + } + + // Begin running NF + onvm_nflib_run(nf_local_ctx); + + // Once the NF has stopped running, free and stop + onvm_nflib_stop(nf_local_ctx); + printf("If we reach here, program is ending\n"); + return 0; +} From d5600b0d71a4df306d0808e7f0fd28722c3dd7db Mon Sep 17 00:00:00 2001 From: Jett Jacobs <62487934+jettjacobs@users.noreply.github.com> Date: Mon, 27 Sep 2021 13:46:30 -0400 Subject: [PATCH 02/16] Update Makefile --- examples/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 3ce592312..9e6c92872 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -40,7 +40,7 @@ $(error "Please define RTE_SDK environment variable") endif # To add new examples, append the directory name to this variable -examples = bridge basic_monitor simple_forward speed_tester flow_table test_flow_dir aes_encrypt aes_decrypt flow_tracker load_balancer arp_response nf_router scaling_example load_generator payload_scan firewall simple_fwd_tb l2fwd +examples = bridge basic_monitor simple_forward speed_tester flow_table test_flow_dir aes_encrypt aes_decrypt flow_tracker load_balancer arp_response nf_router scaling_example load_generator payload_scan firewall simple_fwd_tb l2fwd skeleton ifeq ($(NDPI_HOME),) $(warning "Skipping ndpi_stats NF as NDPI_HOME is not set") From b7efec66d53ac9263e40f339102f8ba0c5da6a4d Mon Sep 17 00:00:00 2001 From: Jett Jacobs <62487934+jettjacobs@users.noreply.github.com> Date: Mon, 27 Sep 2021 14:21:51 -0400 Subject: [PATCH 03/16] Update skeleton.c --- examples/skeleton/skeleton.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 85349e6eb..33d4f5004 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -63,7 +63,7 @@ static uint32_t print_delay = 1000000; // State structs hold any variables which are universal to all files within the NF -// The following struct is an example from text_messaging NF: +// The following struct is an example from test_messaging NF: struct skeleton_state { // Insert stored data which is not specific to any function (similar to global data) int tests_passed; @@ -201,7 +201,7 @@ setup(__attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx){ // Gets called once before NF will get any packets/messages - // Excerpt from text_messaging setup; note nf_local_ctx->nf->data with corresponding struct + // Excerpt from test_messaging setup; note nf_local_ctx->nf->data with corresponding struct struct rte_mempool *pktmbuf_pool; struct test_msg_data *test_state; static struct rte_mempool *nf_msg_pool; From 80fc4755ac0e96e9d25997eb75e03331f9bce75c Mon Sep 17 00:00:00 2001 From: Jett Jacobs Date: Sat, 2 Oct 2021 13:54:30 -0400 Subject: [PATCH 04/16] Skeleton NF 1.2 --- examples/skeleton/skeleton.c | 59 ++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 33d4f5004..8f7f99a16 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -76,6 +76,7 @@ struct skeleton_state { struct rte_ring *msg_q; int test_status[3]; uint64_t start_time; + uint32_t counter; }; /* @@ -155,12 +156,16 @@ do_stats_display(struct rte_mbuf *pkt) { } +// Gets called every time a packet arrives static int packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { - // Timed display counter - keep/edit if NF uses display - static uint32_t counter = 0; + /* + Timed display counter (counter stored in skeleton_state - keep/edit if NF uses display) + After the reception of each packet, the counter is incremented. Once we reach a defined + number of packets, display and reset the counter + */ if (counter++ == print_delay) { do_stats_display(pkt); counter = 0; @@ -174,49 +179,57 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, meta->destination Defines the service ID of the NF which will receive the packet. */ + + /* Skeleton NF simply bridges two ports, swapping packets between them. [APP LOGIC HERE] */ + if (pkt->port == 0) { meta->destination = 1; } else { meta->destination = 0; } - // Send the packet out of the NIC port + // Specify an action meta->action = ONVM_NF_ACTION_OUT; + + /* ONVM_NF_ACTION_* Options + ONVM_NF_ACTION_DROP - drop packet + NVM_NF_ACTION_NEXT - to whatever the next action is configured by the SDN controller in the flow table + ONVM_NF_ACTION_TONF - send to the NF specified in the argument field (assume it is on the same host) + ONVM_NF_ACTION_OUT - send the packet out the NIC port set in the argument field + */ // Return 0 on success, -1 on failure return 0; } +// Gets called every run loop even if there weren't any packets static int action(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx){ - // Gets called every run loop even if there weren't any packets - // [ADD ACTION CODE] - Remove __attribute__((unused)) return 0; } +// Gets called once before NF will get any packets/messages static void setup(__attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx){ - // Gets called once before NF will get any packets/messages - - // Excerpt from test_messaging setup; note nf_local_ctx->nf->data with corresponding struct - struct rte_mempool *pktmbuf_pool; - struct test_msg_data *test_state; - static struct rte_mempool *nf_msg_pool; - pktmbuf_pool = rte_mempool_lookup(PKTMBUF_POOL_NAME); - if (pktmbuf_pool == NULL) { - onvm_nflib_stop(nf_local_ctx); - rte_exit(EXIT_FAILURE, "Cannot find mbuf pool!\n"); - } - nf_msg_pool = rte_mempool_lookup(_NF_MSG_POOL_NAME); - if (nf_msg_pool == NULL) - rte_exit(EXIT_FAILURE, "No NF Message mempool - bye\n"); - - nf_local_ctx->nf->data = (void *)test_state; - - // [REMOVE EXCERPT, ADD SETUP CODE] + // Declare and initialize struct skeleton_state to hold all data within the NF. + // Assign the nf_local_ctx->nf->data pointer to the struct. + // Initialize packet counter + struct skeleton_state *skeleton_data; + skeleton_data = (struct skeleton_state *) rte_zmalloc ("skeleton", sizeof(struct skeleton_state), 0); + nf_local_ctx->nf->data = (void *) skeleton_data; + nf_local_ctx->nf->data->counter = 0; + + /* + void* rte_malloc + const char * type, A string identifying the type of allocated objects. Can be NULL. + size_t size, Size (in bytes) to be allocated. + unsigned align If 0, the return is a pointer that is suitably aligned for any kind of variable + */ +) + // [ADD SETUP CODE] } static void From d5d1c8fcf230b351e0ef16ec391a9a31220c48a2 Mon Sep 17 00:00:00 2001 From: Jett Jacobs Date: Fri, 8 Oct 2021 13:42:01 -0400 Subject: [PATCH 05/16] Skeleton NF 1.3 --- examples/skeleton/Makefile | 2 +- examples/skeleton/skeleton.c | 161 ++++++++++++++++++----------------- 2 files changed, 85 insertions(+), 78 deletions(-) diff --git a/examples/skeleton/Makefile b/examples/skeleton/Makefile index edd7e5211..6505a1485 100644 --- a/examples/skeleton/Makefile +++ b/examples/skeleton/Makefile @@ -43,7 +43,7 @@ endif include $(RTE_SDK)/mk/rte.vars.mk RTE_TARGET ?= x86_64-native-linuxapp-gcc -# binary name +# binary name [EDIT & ADD NF TAG TO "examples" VARIABLE WITHIN MAKEFILE OF EXAMPLES DIRECTORY] APP = skeleton # all source are stored in SRCS-y diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 8f7f99a16..c31bb24a7 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -35,7 +35,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * skeleton.c - Template NF for development. + * skeleton.c - Template NF for development. [EDIT] ********************************************************************/ #include @@ -56,25 +56,20 @@ #include "onvm_nflib.h" #include "onvm_pkt_helper.h" -// Define NF Tag Appropriately +// Define NF Tag Appropriately [EDIT] #define NF_TAG "skeleton" -/* number of package between each print */ -static uint32_t print_delay = 1000000; - -// State structs hold any variables which are universal to all files within the NF -// The following struct is an example from test_messaging NF: +/* + * State structs [EDIT] + * Holds any variables which are universal to all files within the NF + * Listed below are common variables used within the struct + */ struct skeleton_state { - // Insert stored data which is not specific to any function (similar to global data) int tests_passed; int test_phase; - int test_msg_count; - int ring_count; - int mempool_count; + int pkt_process uint16_t address; - struct rte_mempool* msg_pool; - struct rte_ring *msg_q; - int test_status[3]; + uint32_t print_delay; uint64_t start_time; uint32_t counter; }; @@ -85,7 +80,9 @@ struct skeleton_state { static void usage(const char *progname) { printf("Usage:\n"); - printf("%s [EAL args] -- [NF_LIB args] -- -p \n", progname); + printf("%s [EAL args] -- [NF_LIB args]\n", progname); + + // Additional Usage Messages [EDIT] printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); printf("Flags:\n"); printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); @@ -98,7 +95,6 @@ static int parse_app_args(int argc, char *argv[], const char *progname) { int c; - // Handles command line arguments while ((c = getopt(argc, argv, "p:")) != -1) { switch (c) { case 'p': @@ -121,19 +117,20 @@ parse_app_args(int argc, char *argv[], const char *progname) { return optind; } -// Function for NF with displays - Example from bridge.c +/* + * Function for NFs with Displays + * [EDIT IF NF USES DISPLAY; ELSE DELETE] + */ static void do_stats_display(struct rte_mbuf *pkt) { - // [EDIT CODE IF NF USES DISPLAY] - const char clr[] = {27, '[', '2', 'J', '\0'}; const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; - static uint64_t pkt_process = 0; + skeleton_data->pkt_process = 0; struct rte_ipv4_hdr *ip; - pkt_process += print_delay; + skeleton_data->pkt_process += skeleton_data->print_delay; /* Clear screen and move to top left */ printf("%s%s", clr, topLeft); @@ -156,31 +153,29 @@ do_stats_display(struct rte_mbuf *pkt) { } -// Gets called every time a packet arrives +/* + * Handles each packet upon arrival [EDIT] + */ static int packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { /* - Timed display counter (counter stored in skeleton_state - keep/edit if NF uses display) - After the reception of each packet, the counter is incremented. Once we reach a defined - number of packets, display and reset the counter - */ + * Timed display counter (counter stored in skeleton_state - keep/edit if NF uses display) + * After the reception of each packet, the counter is incremented. Once we reach a defined + * number of packets, display and reset the counter + */ if (counter++ == print_delay) { do_stats_display(pkt); counter = 0; } - - // [EDIT PACKET HANDLER CODE BELOW] - /* Important Variables - meta->action - Defines what to do with the packet. Actions defined in onvm_common.h - meta->destination - Defines the service ID of the NF which will receive the packet. - */ - - /* Skeleton NF simply bridges two ports, swapping packets between them. [APP LOGIC HERE] */ + /* + * meta->action + * Defines what to do with the packet. Actions defined in onvm_common.h + * meta->destination + * Defines the service ID of the NF which will receive the packet. + */ if (pkt->port == 0) { meta->destination = 1; @@ -188,83 +183,95 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, meta->destination = 0; } - // Specify an action + /* + * Specify an action: + * ONVM_NF_ACTION_DROP - Drop packet + * NVM_NF_ACTION_NEXT - Go to whatever the next action is configured by the SDN controller in the flow table + * ONVM_NF_ACTION_TONF - Send the packet to the NF specified in the argument field (assume it is on the same host) + * ONVM_NF_ACTION_OUT - Send the packet out the NIC port set in the argument field + */ meta->action = ONVM_NF_ACTION_OUT; - - /* ONVM_NF_ACTION_* Options - ONVM_NF_ACTION_DROP - drop packet - NVM_NF_ACTION_NEXT - to whatever the next action is configured by the SDN controller in the flow table - ONVM_NF_ACTION_TONF - send to the NF specified in the argument field (assume it is on the same host) - ONVM_NF_ACTION_OUT - send the packet out the NIC port set in the argument field - */ - - // Return 0 on success, -1 on failure + + /* Return 0 on success, -1 on failure */ return 0; } -// Gets called every run loop even if there weren't any packets +/* + * Performs action continuously; called every run loop regardless of packet reception [EDIT] + */ static int action(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx){ - // [ADD ACTION CODE] - Remove __attribute__((unused)) + /* Remove __attribute__((unused)) once implemented */ + return 0; } -// Gets called once before NF will get any packets/messages +/* + * Initial setup function; called before NF receives any packets/messages [EDIT] + */ static void setup(__attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx){ - // Declare and initialize struct skeleton_state to hold all data within the NF. - // Assign the nf_local_ctx->nf->data pointer to the struct. - // Initialize packet counter + /* + * (1) Declare and initialize state struct to hold non-local data within the NF. Use rte_zmalloc + * void * rte_zmalloc (const char * type, size_t size, unsigned align) + * type: A string identifying the type of allocated objects. Can be NULL. + * size: Size (in bytes) to be allocated. + * align: If 0, the return is a pointer that is suitably aligned for any kind of variable + * (2) Assign the nf_local_ctx->nf->data pointer to the struct. + * (3) Initialize other predefined data or counters; examples include: + * nf_local_ctx->nf->data->counter = 0; + * skeleton_data->print_delay = 10000 + */ + struct skeleton_state *skeleton_data; skeleton_data = (struct skeleton_state *) rte_zmalloc ("skeleton", sizeof(struct skeleton_state), 0); nf_local_ctx->nf->data = (void *) skeleton_data; - nf_local_ctx->nf->data->counter = 0; - - /* - void* rte_malloc - const char * type, A string identifying the type of allocated objects. Can be NULL. - size_t size, Size (in bytes) to be allocated. - unsigned align If 0, the return is a pointer that is suitably aligned for any kind of variable - */ -) - // [ADD SETUP CODE] } +/* + * Handles each message upon arrival [EDIT] + */ static void handle_msg(__attribute__((unused))void *msg_data, __attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx) { - // Gets called if there is a message sent to the NF - - // [ADD MESSAGE HANDLER CODE] - Remove __attribute__((unused)) + /* Remove __attribute__((unused)) once implemented */ + } +/* + * Creates function table and local context. Runs NF. + */ int main(int argc, char *argv[]) { - // Handles command line arguments + /* Handles command line arguments */ int arg_offset; - // Local context holds NF and status + + /* Local context holds NF and status */ struct onvm_nf_local_ctx *nf_local_ctx; - // Function table holds pointers to NF setup, msg_handler, user_actions, and pkt_handler + + /* Declare function table: Holds pointers to the NF methods - setup, msg_handler, user_actions, and pkt_handler */ struct onvm_nf_function_table *nf_function_table; - // Handles command line arguments + + /* Handles command line arguments */ const char *progname = argv[0]; - // Initializing local context and start default signal handler + /* Initialize local context and start default signal handler */ nf_local_ctx = onvm_nflib_init_nf_local_ctx(); onvm_nflib_start_signal_handler(nf_local_ctx, NULL); - // [EDIT FUNCTION TABLE AS NEEDED] - // Address declarations of NF functions for function table: + /* Initialize function table and respective pointers to NF methods: + * [EDIT] + */ nf_function_table = onvm_nflib_init_nf_function_table(); nf_function_table->pkt_handler = &packet_handler; nf_function_table->setup = &setup; nf_function_table->user_actions = &action; nf_function_table->msg_handler = &handle_msg; - // If a termination signal is received or initiation is interrupted, exit + /* If a termination signal is received or initiation is interrupted, exit */ if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { onvm_nflib_stop(nf_local_ctx); if (arg_offset == ONVM_SIGNAL_TERMINATION) { @@ -275,20 +282,20 @@ main(int argc, char *argv[]) { } } - // Command line arguments + /* Command line arguments */ argc -= arg_offset; argv += arg_offset; - // Invalid command-line argument handling + /* Invalid command-line argument handling */ if (parse_app_args(argc, argv, progname) < 0) { onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } - // Begin running NF + /* Begin running NF */ onvm_nflib_run(nf_local_ctx); - // Once the NF has stopped running, free and stop + /* Once the NF has stopped running, free and stop */ onvm_nflib_stop(nf_local_ctx); printf("If we reach here, program is ending\n"); return 0; From 322dee45f9d79f9491b5ba2fc5e5534df6b72ae7 Mon Sep 17 00:00:00 2001 From: Jett Jacobs Date: Fri, 8 Oct 2021 13:43:39 -0400 Subject: [PATCH 06/16] Comment Adjustment --- examples/skeleton/skeleton.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index c31bb24a7..4be1175c9 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -262,9 +262,7 @@ main(int argc, char *argv[]) { nf_local_ctx = onvm_nflib_init_nf_local_ctx(); onvm_nflib_start_signal_handler(nf_local_ctx, NULL); - /* Initialize function table and respective pointers to NF methods: - * [EDIT] - */ + /* Initialize function table and respective pointers to NF methods: */ nf_function_table = onvm_nflib_init_nf_function_table(); nf_function_table->pkt_handler = &packet_handler; nf_function_table->setup = &setup; From b37b0099a311830063afb5d81e86bedaf915f194 Mon Sep 17 00:00:00 2001 From: Jett Jacobs Date: Fri, 8 Oct 2021 13:51:55 -0400 Subject: [PATCH 07/16] Comment Adjustment 2 --- examples/skeleton/skeleton.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 4be1175c9..2d8f76246 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -177,6 +177,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, * Defines the service ID of the NF which will receive the packet. */ + /* The following example bridges two ports, swapping packets between them. [EDIT APP LOGIC BELOW] */ if (pkt->port == 0) { meta->destination = 1; } else { From 0e37e189f4b176250417617896454475c598a079 Mon Sep 17 00:00:00 2001 From: Jett Jacobs Date: Fri, 8 Oct 2021 13:58:25 -0400 Subject: [PATCH 08/16] Comment Adjustment 3 --- examples/skeleton/skeleton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 2d8f76246..29447af1d 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -177,7 +177,7 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, * Defines the service ID of the NF which will receive the packet. */ - /* The following example bridges two ports, swapping packets between them. [EDIT APP LOGIC BELOW] */ + /* The following example bridges two ports, swapping packets between them. [EDIT] */ if (pkt->port == 0) { meta->destination = 1; } else { From b67c270b51d803d4c22689d99a71d407b8a853b3 Mon Sep 17 00:00:00 2001 From: jett Date: Wed, 13 Oct 2021 15:39:57 -0500 Subject: [PATCH 09/16] modified skeleton --- examples/skeleton/go.sh | 0 examples/skeleton/skeleton.c | 144 +++++++++++++++++++---------------- 2 files changed, 77 insertions(+), 67 deletions(-) mode change 100644 => 100755 examples/skeleton/go.sh diff --git a/examples/skeleton/go.sh b/examples/skeleton/go.sh old mode 100644 new mode 100755 diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 29447af1d..97edf0cd4 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -52,6 +52,7 @@ #include #include #include +#include #include "onvm_nflib.h" #include "onvm_pkt_helper.h" @@ -65,13 +66,10 @@ * Listed below are common variables used within the struct */ struct skeleton_state { - int tests_passed; - int test_phase; - int pkt_process - uint16_t address; - uint32_t print_delay; + uint64_t start_time; - uint32_t counter; + uint32_t print_delay; + // uint32_t counter; }; /* @@ -97,9 +95,9 @@ parse_app_args(int argc, char *argv[], const char *progname) { while ((c = getopt(argc, argv, "p:")) != -1) { switch (c) { - case 'p': - print_delay = strtoul(optarg, NULL, 10); - break; + // case 'p': + // print_delay = strtoul(optarg, NULL, 10); + // break; case '?': usage(progname); if (optopt == 'p') @@ -121,80 +119,83 @@ parse_app_args(int argc, char *argv[], const char *progname) { * Function for NFs with Displays * [EDIT IF NF USES DISPLAY; ELSE DELETE] */ -static void -do_stats_display(struct rte_mbuf *pkt) { +// static void +// do_stats_display(struct rte_mbuf *pkt) { - const char clr[] = {27, '[', '2', 'J', '\0'}; - const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; - skeleton_data->pkt_process = 0; +// const char clr[] = {27, '[', '2', 'J', '\0'}; +// const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; +// static uint64_t pkt_process = 0; - struct rte_ipv4_hdr *ip; +// struct rte_ipv4_hdr *ip; - skeleton_data->pkt_process += skeleton_data->print_delay; +// pkt_process += 1000000; - /* Clear screen and move to top left */ - printf("%s%s", clr, topLeft); +// /* Clear screen and move to top left */ +// printf("%s%s", clr, topLeft); - printf("PACKETS\n"); - printf("-----\n"); - printf("Port : %d\n", pkt->port); - printf("Size : %d\n", pkt->pkt_len); - printf("Type : %d\n", pkt->packet_type); - printf("Number of packet processed : %" PRIu64 "\n", pkt_process); +// printf("PACKETS\n"); +// printf("-----\n"); +// printf("Port : %d\n", pkt->port); +// printf("Size : %d\n", pkt->pkt_len); +// printf("Type : %d\n", pkt->packet_type); +// printf("Number of packet processed : %" PRIu64 "\n", pkt_process); - ip = onvm_pkt_ipv4_hdr(pkt); - if (ip != NULL) { - onvm_pkt_print(pkt); - } else { - printf("Not IP4\n"); - } +// ip = onvm_pkt_ipv4_hdr(pkt); +// if (ip != NULL) { +// onvm_pkt_print(pkt); +// } else { +// printf("Not IP4\n"); +// } - printf("\n\n"); -} +// printf("\n\n"); +// } /* * Handles each packet upon arrival [EDIT] */ static int -packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, +packet_handler(__attribute__((unused))struct rte_mbuf *pkt, __attribute__((unused)) struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { - /* - * Timed display counter (counter stored in skeleton_state - keep/edit if NF uses display) - * After the reception of each packet, the counter is incremented. Once we reach a defined - * number of packets, display and reset the counter - */ - if (counter++ == print_delay) { - do_stats_display(pkt); - counter = 0; - } + meta->action = ONVM_NF_ACTION_DROP; + return 0; - /* - * meta->action - * Defines what to do with the packet. Actions defined in onvm_common.h - * meta->destination - * Defines the service ID of the NF which will receive the packet. - */ - - /* The following example bridges two ports, swapping packets between them. [EDIT] */ - if (pkt->port == 0) { - meta->destination = 1; - } else { - meta->destination = 0; - } + // /* + // * Timed display counter (counter stored in skeleton_state - keep/edit if NF uses display) + // * After the reception of each packet, the counter is incremented. Once we reach a defined + // * number of packets, display and reset the counter + // */ + // if (counter++ == print_delay) { + // do_stats_display(pkt); + // counter = 0; + // } - /* - * Specify an action: - * ONVM_NF_ACTION_DROP - Drop packet - * NVM_NF_ACTION_NEXT - Go to whatever the next action is configured by the SDN controller in the flow table - * ONVM_NF_ACTION_TONF - Send the packet to the NF specified in the argument field (assume it is on the same host) - * ONVM_NF_ACTION_OUT - Send the packet out the NIC port set in the argument field - */ - meta->action = ONVM_NF_ACTION_OUT; - - /* Return 0 on success, -1 on failure */ - return 0; + // /* + // * meta->action + // * Defines what to do with the packet. Actions defined in onvm_common.h + // * meta->destination + // * Defines the service ID of the NF which will receive the packet. + // */ + + // /* The following example bridges two ports, swapping packets between them. [EDIT] */ + // if (pkt->port == 0) { + // meta->destination = 1; + // } else { + // meta->destination = 0; + // } + + // /* + // * Specify an action: + // * ONVM_NF_ACTION_DROP - Drop packet + // * NVM_NF_ACTION_NEXT - Go to whatever the next action is configured by the SDN controller in the flow table + // * ONVM_NF_ACTION_TONF - Send the packet to the NF specified in the argument field (assume it is on the same host) + // * ONVM_NF_ACTION_OUT - Send the packet out the NIC port set in the argument field + // */ + // meta->action = ONVM_NF_ACTION_OUT; + + // /* Return 0 on success, -1 on failure */ + // return 0; } /* @@ -203,7 +204,13 @@ packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, static int action(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx){ - /* Remove __attribute__((unused)) once implemented */ + struct skeleton_state *skeleton_data; + skeleton_data = (struct skeleton_state *)nf_local_ctx->nf->data; + + __uint64_t time = (rte_get_tsc_cycles() - skeleton_data->start_time) / rte_get_timer_hz(); + if (time%5 == 0) { + printf("%ld\n",time); + } return 0; } @@ -229,6 +236,9 @@ setup(__attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx){ struct skeleton_state *skeleton_data; skeleton_data = (struct skeleton_state *) rte_zmalloc ("skeleton", sizeof(struct skeleton_state), 0); nf_local_ctx->nf->data = (void *) skeleton_data; + + skeleton_data->start_time = rte_get_tsc_cycles(); + skeleton_data->print_delay = 5; } /* From 6fdcc09a76562065dde6ca9d911485d0779120ea Mon Sep 17 00:00:00 2001 From: jett Date: Fri, 29 Oct 2021 12:22:12 -0600 Subject: [PATCH 10/16] Added timer/packet-based print counters --- examples/skeleton/skeleton.c | 185 ++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 91 deletions(-) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 97edf0cd4..0ef834417 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -67,9 +67,11 @@ */ struct skeleton_state { + int displayType; uint64_t start_time; - uint32_t print_delay; - // uint32_t counter; + uint32_t delay; + uint32_t current_time; + uint32_t packets_processed; }; /* @@ -83,21 +85,29 @@ usage(const char *progname) { // Additional Usage Messages [EDIT] printf("%s -F [EAL args] -- [NF_LIB args] -- [NF args]\n\n", progname); printf("Flags:\n"); - printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); + printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); } /* * Parse the application arguments. */ static int -parse_app_args(int argc, char *argv[], const char *progname) { +parse_app_args(int argc, char *argv[], const char *progname, struct skeleton_state *skeleton_data) { int c; - - while ((c = getopt(argc, argv, "p:")) != -1) { + + /* User can specify how they would like to display data. */ + + skeleton_data->displayType = -1; // displayType -1 = No Display + while ((c = getopt(argc, argv, "p:v:")) != -1) { switch (c) { - // case 'p': - // print_delay = strtoul(optarg, NULL, 10); - // break; + case 'p': + skeleton_data->delay = strtoul(optarg, NULL, 10); + skeleton_data->displayType = 0; // displayType 0 = Time-Based Delays + break; + case 'v': + skeleton_data->delay = strtoul(optarg, NULL, 10); + skeleton_data->displayType = 1; // displayType 1 = Packet-Based Delays + break; case '?': usage(progname); if (optopt == 'p') @@ -119,99 +129,89 @@ parse_app_args(int argc, char *argv[], const char *progname) { * Function for NFs with Displays * [EDIT IF NF USES DISPLAY; ELSE DELETE] */ -// static void -// do_stats_display(struct rte_mbuf *pkt) { +static void +do_stats_display(struct skeleton_state *skeleton_data) { -// const char clr[] = {27, '[', '2', 'J', '\0'}; -// const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; -// static uint64_t pkt_process = 0; - -// struct rte_ipv4_hdr *ip; - -// pkt_process += 1000000; + const char clr[] = {27, '[', '2', 'J', '\0'}; + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; -// /* Clear screen and move to top left */ -// printf("%s%s", clr, topLeft); + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); -// printf("PACKETS\n"); -// printf("-----\n"); -// printf("Port : %d\n", pkt->port); -// printf("Size : %d\n", pkt->pkt_len); -// printf("Type : %d\n", pkt->packet_type); -// printf("Number of packet processed : %" PRIu64 "\n", pkt_process); + printf("PACKETS\n"); + printf("-----\n"); + printf("Number of packet processed : %d\n", skeleton_data->packets_processed); + printf("Time : %d\n", skeleton_data->current_time); -// ip = onvm_pkt_ipv4_hdr(pkt); -// if (ip != NULL) { -// onvm_pkt_print(pkt); -// } else { -// printf("Not IP4\n"); -// } - -// printf("\n\n"); -// } + printf("\n\n"); +} /* * Handles each packet upon arrival [EDIT] */ static int -packet_handler(__attribute__((unused))struct rte_mbuf *pkt, __attribute__((unused)) struct onvm_pkt_meta *meta, - __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { +packet_handler(__attribute__((unused)) struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, + struct onvm_nf_local_ctx *nf_local_ctx) { - meta->action = ONVM_NF_ACTION_DROP; - return 0; + struct skeleton_state *skeleton_data; + skeleton_data = (struct skeleton_state *)nf_local_ctx->nf->data; + skeleton_data->packets_processed++; - // /* - // * Timed display counter (counter stored in skeleton_state - keep/edit if NF uses display) - // * After the reception of each packet, the counter is incremented. Once we reach a defined - // * number of packets, display and reset the counter - // */ - // if (counter++ == print_delay) { - // do_stats_display(pkt); - // counter = 0; - // } + /* + * Timed display current_time (current_time stored in skeleton_state - keep/edit if NF uses display) + * After the reception of each packet, the current_time is incremented. Once we reach a defined + * number of packets, display and reset the current_time + */ - // /* - // * meta->action - // * Defines what to do with the packet. Actions defined in onvm_common.h - // * meta->destination - // * Defines the service ID of the NF which will receive the packet. - // */ - - // /* The following example bridges two ports, swapping packets between them. [EDIT] */ - // if (pkt->port == 0) { - // meta->destination = 1; - // } else { - // meta->destination = 0; - // } + if (skeleton_data->displayType == 1) { + if (skeleton_data->packets_processed % skeleton_data->delay == 0) { + do_stats_display(skeleton_data); + } + } - // /* - // * Specify an action: - // * ONVM_NF_ACTION_DROP - Drop packet - // * NVM_NF_ACTION_NEXT - Go to whatever the next action is configured by the SDN controller in the flow table - // * ONVM_NF_ACTION_TONF - Send the packet to the NF specified in the argument field (assume it is on the same host) - // * ONVM_NF_ACTION_OUT - Send the packet out the NIC port set in the argument field - // */ - // meta->action = ONVM_NF_ACTION_OUT; - - // /* Return 0 on success, -1 on failure */ - // return 0; + /* + * meta->action + * Defines what to do with the packet. Actions defined in onvm_common.h + * meta->destination + * Defines the service ID of the NF which will receive the packet. + * + * Possible actions: + * ONVM_NF_ACTION_DROP - Drop packet + * NVM_NF_ACTION_NEXT - Go to whatever the next action is configured by the SDN controller in the flow table + * ONVM_NF_ACTION_TONF - Send the packet to the NF specified in the argument field (assume it is on the same host) + * ONVM_NF_ACTION_OUT - Send the packet out the NIC port set in the argument field + */ + meta->action = ONVM_NF_ACTION_DROP; + + /* Return 0 on success, -1 on failure */ + return 0; } /* * Performs action continuously; called every run loop regardless of packet reception [EDIT] */ static int -action(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx){ +action(struct onvm_nf_local_ctx *nf_local_ctx){ struct skeleton_state *skeleton_data; skeleton_data = (struct skeleton_state *)nf_local_ctx->nf->data; + /* + * Get the current time, and, if the user has specified a timer-based print delay, + * check whether it is time to print + */ __uint64_t time = (rte_get_tsc_cycles() - skeleton_data->start_time) / rte_get_timer_hz(); - if (time%5 == 0) { - printf("%ld\n",time); + if (skeleton_data->displayType == 0) { + __uint64_t delay = skeleton_data->delay; + __uint64_t current_time = skeleton_data->current_time; + if ((time%delay == 0) && (time != current_time)) { + do_stats_display(skeleton_data); + } } + /* Update the current time */ + skeleton_data->current_time = time; return 0; } @@ -219,26 +219,17 @@ action(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx){ * Initial setup function; called before NF receives any packets/messages [EDIT] */ static void -setup(__attribute__((unused))struct onvm_nf_local_ctx *nf_local_ctx){ +setup(struct onvm_nf_local_ctx *nf_local_ctx){ /* - * (1) Declare and initialize state struct to hold non-local data within the NF. Use rte_zmalloc - * void * rte_zmalloc (const char * type, size_t size, unsigned align) - * type: A string identifying the type of allocated objects. Can be NULL. - * size: Size (in bytes) to be allocated. - * align: If 0, the return is a pointer that is suitably aligned for any kind of variable - * (2) Assign the nf_local_ctx->nf->data pointer to the struct. - * (3) Initialize other predefined data or counters; examples include: - * nf_local_ctx->nf->data->counter = 0; - * skeleton_data->print_delay = 10000 + * Initialize variables within state struct that must be defined + * before receiving packets/messages */ - - struct skeleton_state *skeleton_data; - skeleton_data = (struct skeleton_state *) rte_zmalloc ("skeleton", sizeof(struct skeleton_state), 0); - nf_local_ctx->nf->data = (void *) skeleton_data; + struct skeleton_state *skeleton_data = nf_local_ctx->nf->data; skeleton_data->start_time = rte_get_tsc_cycles(); - skeleton_data->print_delay = 5; + skeleton_data->current_time = 0; + skeleton_data->packets_processed = 0; } /* @@ -295,8 +286,20 @@ main(int argc, char *argv[]) { argc -= arg_offset; argv += arg_offset; + /* + * (1) Declare and initialize state struct to hold non-local data within the NF. Use rte_zmalloc + * void * rte_zmalloc (const char * type, size_t size, unsigned align) + * type: A string identifying the type of allocated objects. Can be NULL. + * size: Size (in bytes) to be allocated. + * align: If 0, the return is a pointer that is suitably aligned for any kind of variable + * (2) Assign the nf_local_ctx->nf->data pointer to the struct. + */ + struct skeleton_state *skeleton_data; + skeleton_data = (struct skeleton_state *) rte_zmalloc ("skeleton", sizeof(struct skeleton_state), 0); + nf_local_ctx->nf->data = (void *) skeleton_data; + /* Invalid command-line argument handling */ - if (parse_app_args(argc, argv, progname) < 0) { + if (parse_app_args(argc, argv, progname, skeleton_data) < 0) { onvm_nflib_stop(nf_local_ctx); rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n"); } From 84c791754a13086f9f95adc754be2e3cfcb2dd86 Mon Sep 17 00:00:00 2001 From: jett Date: Fri, 29 Oct 2021 13:35:35 -0600 Subject: [PATCH 11/16] [UPDATE] Command-Line Bugs --- examples/skeleton/README.md | 5 +++-- examples/skeleton/skeleton.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/skeleton/README.md b/examples/skeleton/README.md index bef504178..ce15f37c8 100644 --- a/examples/skeleton/README.md +++ b/examples/skeleton/README.md @@ -8,7 +8,7 @@ Compilation and Execution cd examples make cd skeleton -./go.sh SERVICE_ID [PRINT_DELAY] +./go.sh SERVICE_ID [-p TIME_PRINT_DELAY | -v PACKET_PRINT_DELAY] OR @@ -21,7 +21,8 @@ sudo ./build/skeleton -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- App Specific Arguments -- - - `-p `: number of packets between each print, e.g. `-p 1` prints every packets. + - `-p `: time between each print, e.g. `-p 1` prints after every second. + - `-v `: number of packets between each print, e.g. `-v 1` prints after every packet. Config File Support -- diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 0ef834417..274aabe92 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -122,7 +122,7 @@ parse_app_args(int argc, char *argv[], const char *progname, struct skeleton_sta return -1; } } - return optind; + return optind;ƒ } /* From 50a5524cda1cdd7b2dc00d38cacbbce1488bafaa Mon Sep 17 00:00:00 2001 From: jett Date: Fri, 29 Oct 2021 14:04:39 -0600 Subject: [PATCH 12/16] Compilation Fix --- examples/skeleton/skeleton.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 274aabe92..0ef834417 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -122,7 +122,7 @@ parse_app_args(int argc, char *argv[], const char *progname, struct skeleton_sta return -1; } } - return optind;ƒ + return optind; } /* From ffe76d71c4b770fb9fed9f4ae67aeec269bfe8ed Mon Sep 17 00:00:00 2001 From: jett Date: Fri, 29 Oct 2021 15:22:53 -0600 Subject: [PATCH 13/16] [UPDATE] Command-Line Argument Debugging --- examples/skeleton/README.md | 2 +- examples/skeleton/skeleton.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/examples/skeleton/README.md b/examples/skeleton/README.md index ce15f37c8..fdd66f5d8 100644 --- a/examples/skeleton/README.md +++ b/examples/skeleton/README.md @@ -16,7 +16,7 @@ OR OR -sudo ./build/skeleton -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- [-p PRINT_DELAY] +sudo ./build/skeleton -l CORELIST -n 3 --proc-type=secondary -- -r SERVICE_ID -- [-p TIME_PRINT_DELAY | -v PACKET_PRINT_DELAY] ``` App Specific Arguments diff --git a/examples/skeleton/skeleton.c b/examples/skeleton/skeleton.c index 0ef834417..36044b552 100644 --- a/examples/skeleton/skeleton.c +++ b/examples/skeleton/skeleton.c @@ -102,16 +102,26 @@ parse_app_args(int argc, char *argv[], const char *progname, struct skeleton_sta switch (c) { case 'p': skeleton_data->delay = strtoul(optarg, NULL, 10); + if (skeleton_data->delay < 1) { + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + return -1; + } skeleton_data->displayType = 0; // displayType 0 = Time-Based Delays - break; + return optind; case 'v': skeleton_data->delay = strtoul(optarg, NULL, 10); + if (skeleton_data->delay < 1) { + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + return -1; + } skeleton_data->displayType = 1; // displayType 1 = Packet-Based Delays - break; + return optind; case '?': usage(progname); if (optopt == 'p') RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); + else if (optopt == 'v') + RTE_LOG(INFO, APP, "Option -%c requires an argument.\n", optopt); else if (isprint(optopt)) RTE_LOG(INFO, APP, "Unknown option `-%c'.\n", optopt); else @@ -206,7 +216,9 @@ action(struct onvm_nf_local_ctx *nf_local_ctx){ __uint64_t delay = skeleton_data->delay; __uint64_t current_time = skeleton_data->current_time; if ((time%delay == 0) && (time != current_time)) { + skeleton_data->current_time = time; do_stats_display(skeleton_data); + return 0; } } @@ -230,6 +242,9 @@ setup(struct onvm_nf_local_ctx *nf_local_ctx){ skeleton_data->start_time = rte_get_tsc_cycles(); skeleton_data->current_time = 0; skeleton_data->packets_processed = 0; + if (skeleton_data->displayType > -1) { + do_stats_display(skeleton_data); + } } /* From 5fc7c1528543df8f74e0401580fca20912bd78bc Mon Sep 17 00:00:00 2001 From: jiyan Date: Thu, 4 Aug 2022 21:08:15 +0300 Subject: [PATCH 14/16] update topology pdf --- docs/loadbalancer/ONVM_LB_TopologyDoc.pdf | Bin 123081 -> 114960 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/loadbalancer/ONVM_LB_TopologyDoc.pdf b/docs/loadbalancer/ONVM_LB_TopologyDoc.pdf index f852ae7c42435ed7c0389a2f3f606fe10c44feb4..b3c0d0c012cf5bffb7e060c7936e9f65feaf40b2 100644 GIT binary patch delta 26016 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

&9kf<#CJU_nu#j- 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 delta 26934 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 From 512d24a076dd62106332579b420a8b4ab1ac4a3d Mon Sep 17 00:00:00 2001 From: Jiyan <54191383+xwedea@users.noreply.github.com> Date: Thu, 4 Aug 2022 21:35:53 +0300 Subject: [PATCH 15/16] Update index.rst --- docs/loadbalancer/index.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/loadbalancer/index.rst b/docs/loadbalancer/index.rst index d29c3e5c8..021eadd80 100644 --- a/docs/loadbalancer/index.rst +++ b/docs/loadbalancer/index.rst @@ -7,7 +7,7 @@ Getting Started Overview ----------------- -- In the following tutorial, we will explore a means of deploying and testing ONVM’s example Layer-3 round-robin load balancer. To do this, we will instantiate a Cloudlab experiment using the :code:`ONVM_LoadBalancer` profile; this topology (shown below) includes two backend servers and a single client, in addition to the ONVM load balancer. +- In the following tutorial, we will explore a means of deploying and testing ONVM’s load balancer. To do this, we will instantiate a Cloudlab experiment using the :code:`ONVM_LoadBalancer` profile; this topology (shown below) includes two backend servers and a single client, in addition to the ONVM load balancer. .. image:: ../images/lb-1.png @@ -15,18 +15,23 @@ Overview Cloudlab Node Setup ----------------- -- Open Cloudlab and start a new experiment. When prompted to choose a profile, select :code:`ONVM_LoadBalancer`. For this tutorial, set the number of backend servers to 2, and set the OS Image to :code:`ONVM UBUNTU20.04`. With regards to defining the physical node type, we will leave this blank and expect the default c220g2 nodes. In the following section (under "Finalize"), select the :code:`Cloudlab Wisconsin` cluster, and all remaining inputs are discretionary. +- Open Cloudlab and start a new experiment. When prompted to choose a profile, select :code:`ONVM_LoadBalancer`. For this tutorial, set the number of backend servers to 2, and set the OS Image to :code:`ONVM & Sledge UBUNTU20.04`. With regards to defining the physical node type, we will leave this blank and expect the default c220g2 nodes. In the following section (under "Finalize"), select the :code:`Cloudlab Wisconsin` cluster, and all remaining inputs are discretionary. - Begin by SSHing into each node within the experiment, and download the **Load Balancer Topology Template** `here `_. If you are using any Apple product to complete this tutorial, avoid using Preview as your PDF editor; autofill scripts will not apply. Google Chrome or Adobe Acrobat are viable alternatives. - For every node, use :code:`ifconfig` to view all available network interfaces. Record the appropriate name, IPv4 (inet), and MAC address (ether) for each network interface in the Topology Template, as shown below. Note that the client side and server side nodes should be on a different IP subnets. The ONVM_LB node requires the use of two ports: one for connection to the client and one for connecting to the servers. It is recommended that you use the 10-Gigabit SFI/SFP+ network connections. Port IDs will be handled later. .. image:: ../images/lb-2.png -- In the ONVM LB node, set up the environment using :code:`setup_cloudlab.sh` in the scripts directory. Once the ports have been successfully bound to the DPDK-compatible driver, start the manager with at least two available ports. Listed below are the abbreviated steps for binding available ports to the DPDK-bound driver. To start the manager, you may use :code:`./onvm/go.sh -k 3 -n 0xFF -s stdout`. - - #. Unbind the connected NICs: :code:`sudo ifconfig down` where represents the interface name (eg. :code:`enp6s0f0`) +Running ONVM Manager +----------------- + +- In the ONVM LB node + + #. Unbind both of the connected NICs: :code:`sudo ifconfig down` where represents the interface name (eg. :code:`enp6s0f0`) #. Navigate to the :code:`/local/onvm/openNetVM/scripts` directory and bind the NICs to DPDK using the command :code:`source ./setup_cloudlab.sh` #. Ensure that you see the two NICs in the section defining “Network devices using DPDK-compatible driver.” If you only see one NIC, it’s possible that you did not unbind the other NIC from the kernel driver using :code:`sudo ifconfig down`. Repeat step (i). - #. Navigate back to the openNetVM folder (:code:`cd ..`) and compile the Manager using :code:`cd onvm && make && cd ..` + #. Navigate back to the openNetVM folder (:code:`cd ..`), and compile the Manager using :code:`cd onvm && make && cd ..` + #. Navigate to examples folder :code:`cd examples && make && cd ..` + #. Run the manager :code:`./onvm/go.sh -k 3 -n 0xFF -s stdout` - At the top of the manager display (pictured below), you can observe two (or more) port IDs and their associated MAC addresses. Use these ID mappings to complete the Port ID sections of the **Topology Template**. From 4e674b0042cffae9e940e91c8d59c4657daebcf3 Mon Sep 17 00:00:00 2001 From: Robin Andreasen Date: Thu, 4 Aug 2022 17:10:03 -0400 Subject: [PATCH 16/16] policy and config instuction updates --- docs/loadbalancer/ONVM_LB_TopologyDoc.pdf | Bin 114960 -> 152461 bytes docs/loadbalancer/index.rst | 7 ++++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/loadbalancer/ONVM_LB_TopologyDoc.pdf b/docs/loadbalancer/ONVM_LB_TopologyDoc.pdf index b3c0d0c012cf5bffb7e060c7936e9f65feaf40b2..bdba5f5ed574cf0f789bc3be6aa6f6f3c00c5b8f 100644 GIT binary patch delta 30231 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%0D}a8puk!Ns6m!+jRo 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. If you are using any Apple product to complete this tutorial, avoid using Preview as your PDF editor; autofill scripts will not apply. Google Chrome or Adobe Acrobat are viable alternatives. -- For every node, use :code:`ifconfig` to view all available network interfaces. Record the appropriate name, IPv4 (inet), and MAC address (ether) for each network interface in the Topology Template, as shown below. Note that the client side and server side nodes should be on a different IP subnets. The ONVM_LB node requires the use of two ports: one for connection to the client and one for connecting to the servers. It is recommended that you use the 10-Gigabit SFI/SFP+ network connections. Port IDs will be handled later. +- For every node, use :code:`ifconfig` to view all available network interfaces. Record the appropriate name, IPv4 (inet), and MAC address (ether) for each network interface in the Topology Template, as shown below. Note that the client side and server side nodes should be on a different IP subnets and the first 3 values of the client ip must match it's serverside onvm port. The first 3 values of the servers and their onvm port must also match. The ONVM_LB node requires the use of two ports: one for connection to the client and one for connecting to the servers. It is recommended that you use the 10-Gigabit SFI/SFP+ network connections. Port IDs will be handled later. .. image:: ../images/lb-2.png @@ -39,10 +39,11 @@ Running ONVM Manager - Now that the **Topology Template** is complete, all commands within the PDF document should be populated. To complete our LB configuration, we must: - #. Specify the backend servers’ port information in **server.conf** + #. Specify the backend servers’ port information in **server.json** #. Define a static route in each of the backend servers to specify the correct gateway for which traffic will enter. -- To specify the server information for the load balancer, go to :code:`/examples/load_balancer/server.conf` and copy the information that is shown in the bottom left quadrant of your **Topology Template**. This includes the *"LIST_SIZE 2"* and the IP+MAC address of each port. +- To specify the server information for the load balancer, go to :code:`/examples/load_balancer/server.json` and copy the json code that is shown in the bottom left quadrant of your **Topology Template**. This includes the *"list_size 2"*, the policy, and the IP + MAC address + weight of each port. +- The Load Balancer currently supports random ("random"), weighted random ("weighted_random"), and round robin ("rrobin") packet distribution policies. The policy may be selected in the json using the strings provided in parenthesis. Weight fields are only required for weighted random policy. - To define the static routes, navigate to the two backend nodes (Server1 and Server2) and execute the respective commands shown on the bottom-center area of the **Topology Template**. This includes the :code:`sudo ip route add *` command for each server. Running The Load Balancer