diff --git a/examples/test_flow_dir/README.md b/examples/test_flow_dir/README.md index 5200b67f0..132081b2f 100644 --- a/examples/test_flow_dir/README.md +++ b/examples/test_flow_dir/README.md @@ -3,6 +3,8 @@ Flow Director Test This NF demonstrates how to use ONVM's Flow Director. When a packet arrives the NF checks whether it is from a flow that already has a service chain rule. If not, it creates a new rule so the packet will be sent to the destination NF indicated on the command line. Packets that match a rule are processed with the ONVM_NF_ACTION_NEXT action. +This NF demonstrates how to populate the Manager's flow table as well as how to create service chain rules for specific flows. To enable this functionality use the -s flag. + Compilation and Execution -- ``` @@ -24,6 +26,7 @@ App Specific Arguments -- - `-d `: destination service ID to foward to - `-p `: number of packets between each print, e.g. `-p 1` prints every packets. + - `s`: Prepopulate sample flow table rules. Config File Support -- diff --git a/examples/test_flow_dir/test_flow_dir.c b/examples/test_flow_dir/test_flow_dir.c index 5d196314d..756049023 100644 --- a/examples/test_flow_dir/test_flow_dir.c +++ b/examples/test_flow_dir/test_flow_dir.c @@ -66,11 +66,18 @@ #define NF_TAG "test_flow_dir" -/* number of package between each print */ +/* Number of packets between each print. */ static uint32_t print_delay = 1000000; static uint32_t destination; +/* Populate flow disabled by default */ +static int populate_flow = 0; + +static struct onvm_flow_entry *flow_entry = NULL; + +/* Pointer to the manager flow table */ +extern struct onvm_ft* sdn_ft; /* * Print a usage message */ @@ -82,6 +89,8 @@ usage(const char *progname) { printf("Flags:\n"); printf(" - `-d `: destination service ID to foward to\n"); printf(" - `-p `: number of packets between each print, e.g. `-p 1` prints every packets.\n"); + printf(" - -s : Prepopulate sample flow table rules. \n"); + } /* @@ -91,7 +100,7 @@ static int parse_app_args(int argc, char *argv[], const char *progname) { int c; - while ((c = getopt(argc, argv, "d:p:")) != -1) { + while ((c = getopt(argc, argv, "d:p:s")) != -1) { switch (c) { case 'd': destination = strtoul(optarg, NULL, 10); @@ -99,6 +108,9 @@ parse_app_args(int argc, char *argv[], const char *progname) { case 'p': print_delay = strtoul(optarg, NULL, 10); break; + case 's': + populate_flow = 1; + break; case '?': usage(progname); if (optopt == 'd') @@ -151,32 +163,96 @@ do_stats_display(struct rte_mbuf *pkt) { } } +/* + * This function populates a set of flows to the sdn flow table. + * For each new flow a service chain is created and appended. + * Each service chain is of max 4 length. This function is not enabled by default. + * Users may update the predefined struct with their own rules here. + */ + +static void +populate_sample_ipv4(void) { + struct test_add_key { + struct onvm_ft_ipv4_5tuple key; + uint8_t destinations[ONVM_MAX_CHAIN_LENGTH]; + }; + + struct test_add_key keys[] = { + {{RTE_IPV4(100, 10, 0, 0), RTE_IPV4(100, 10, 0, 0), 1, 1, IPPROTO_TCP}, {2,3}}, + {{RTE_IPV4(102, 10, 0, 0), RTE_IPV4(101, 10, 0, 1), 0, 1, IPPROTO_TCP}, {4,3,2,1}}, + {{RTE_IPV4(103, 10, 0, 0), RTE_IPV4(102, 10, 0, 1), 0, 1, IPPROTO_TCP}, {2,1}}, + {{RTE_IPV4(10, 11, 1, 17), RTE_IPV4(10, 11, 1, 17), 1234, 1234, IPPROTO_UDP}, {4,3,2}}, + }; + + uint32_t num_keys = RTE_DIM(keys); + for (uint32_t i = 0; i < num_keys; i++) { + struct onvm_ft_ipv4_5tuple key; + key = keys[i].key; + int ret = onvm_ft_lookup_key(sdn_ft, &key, (char **)&flow_entry); + if (ret >= 0) { + continue; + } else { + printf("\nAdding Key: "); + _onvm_ft_print_key(&key); + ret = onvm_ft_add_key(sdn_ft, &key, (char **)&flow_entry); + if (ret < 0) { + printf("Unable to add key."); + continue; + } + printf("Creating new service chain.\n"); + flow_entry->sc = onvm_sc_create(); + uint32_t num_dest = RTE_DIM(keys[i].destinations); + for (uint32_t j = 0; j < num_dest; j++) { + if (keys[i].destinations[j] == 0) { + continue; + } + printf("Appending Destination: %d \n", keys[i].destinations[j]); + onvm_sc_append_entry(flow_entry->sc, ONVM_NF_ACTION_TONF, keys[i].destinations[j]); + } + } + } +} + +static void +nf_setup(__attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { + flow_entry = (struct onvm_flow_entry *) rte_calloc(NULL, 1, sizeof(struct onvm_flow_entry), 0); + if (flow_entry == NULL) { + rte_exit(EXIT_FAILURE, "Unable to allocate flow entry\n"); + } +} + static int packet_handler(struct rte_mbuf *pkt, struct onvm_pkt_meta *meta, __attribute__((unused)) struct onvm_nf_local_ctx *nf_local_ctx) { static uint32_t counter = 0; - struct onvm_flow_entry *flow_entry = NULL; int ret; + if (!onvm_pkt_is_ipv4(pkt)) { + meta->action = ONVM_NF_ACTION_DROP; + return 0; + } + if (++counter == print_delay) { do_stats_display(pkt); counter = 0; } - ret = onvm_flow_dir_get_pkt(pkt, &flow_entry); + struct onvm_ft_ipv4_5tuple key; + onvm_ft_fill_key(&key, pkt); + ret = onvm_ft_lookup_key(sdn_ft, &key, (char **)&flow_entry); if (ret >= 0) { meta->action = ONVM_NF_ACTION_NEXT; } else { - ret = onvm_flow_dir_add_pkt(pkt, &flow_entry); + ret = onvm_ft_add_key(sdn_ft, &key, (char **)&flow_entry); if (ret < 0) { meta->action = ONVM_NF_ACTION_DROP; meta->destination = 0; return 0; } - memset(flow_entry, 0, sizeof(struct onvm_flow_entry)); flow_entry->sc = onvm_sc_create(); onvm_sc_append_entry(flow_entry->sc, ONVM_NF_ACTION_TONF, destination); - // onvm_sc_print(flow_entry->sc); + meta->action = ONVM_NF_ACTION_TONF; + meta->destination = destination; } return 0; } @@ -193,6 +269,7 @@ main(int argc, char *argv[]) { nf_function_table = onvm_nflib_init_nf_function_table(); nf_function_table->pkt_handler = &packet_handler; + nf_function_table->setup = &nf_setup; if ((arg_offset = onvm_nflib_init(argc, argv, NF_TAG, nf_local_ctx, nf_function_table)) < 0) { onvm_nflib_stop(nf_local_ctx); @@ -214,9 +291,16 @@ main(int argc, char *argv[]) { /* Map the sdn_ft table */ onvm_flow_dir_nf_init(); + if (populate_flow) { + printf("Populating flow table. \n"); + populate_sample_ipv4(); + } + onvm_nflib_run(nf_local_ctx); onvm_nflib_stop(nf_local_ctx); + rte_free(flow_entry); + flow_entry = NULL; printf("If we reach here, program is ending\n"); return 0; } diff --git a/onvm/onvm_mgr/onvm_init.c b/onvm/onvm_mgr/onvm_init.c index a0d9e7527..2e94366c9 100644 --- a/onvm/onvm_mgr/onvm_init.c +++ b/onvm/onvm_mgr/onvm_init.c @@ -251,7 +251,7 @@ init(int argc, char *argv[]) { printf("Chain length can not be larger than the maximum chain length\n"); exit(1); } - printf("Default service chain: send to sdn NF\n"); + printf("Default service chain: send to NF with service ID 1\n"); /* set up service chain pointer shared to NFs*/ mz_scp = rte_memzone_reserve(MZ_SCP_INFO, sizeof(struct onvm_service_chain *), rte_socket_id(), NO_FLAGS); diff --git a/onvm/onvm_nflib/onvm_common.h b/onvm/onvm_nflib/onvm_common.h index e2b8adcc1..73f43821f 100755 --- a/onvm/onvm_nflib/onvm_common.h +++ b/onvm/onvm_nflib/onvm_common.h @@ -79,6 +79,8 @@ #define ONVM_NF_ACTION_TONF 2 // send to the NF specified in the argument field (assume it is on the same host) #define ONVM_NF_ACTION_OUT 3 // send the packet out the NIC port set in the argument field +#define ACTION_NEXT_DEST_ID 255 // Destination service ID that will set the manager to perform flow table lookup. + #define PKT_WAKEUP_THRESHOLD 1 // for shared core mode, how many packets are required to wake up the NF #define MSG_WAKEUP_THRESHOLD 1 // for shared core mode, how many messages on an NF's ring are required to wake up the NF diff --git a/onvm/onvm_nflib/onvm_flow_table.h b/onvm/onvm_nflib/onvm_flow_table.h index 6373aa0a6..65134dca1 100644 --- a/onvm/onvm_nflib/onvm_flow_table.h +++ b/onvm/onvm_nflib/onvm_flow_table.h @@ -126,10 +126,10 @@ onvm_ft_free(struct onvm_ft *table); static inline void _onvm_ft_print_key(struct onvm_ft_ipv4_5tuple *key) { - printf("IP: %" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, key->src_addr & 0xFF, (key->src_addr >> 8) & 0xFF, - (key->src_addr >> 16) & 0xFF, (key->src_addr >> 24) & 0xFF); - printf("-%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", key->dst_addr & 0xFF, (key->dst_addr >> 8) & 0xFF, - (key->dst_addr >> 16) & 0xFF, (key->dst_addr >> 24) & 0xFF); + printf("IP: %" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8, (key->src_addr >> 24) & 0xFF, + (key->src_addr >> 16) & 0xFF, (key->src_addr >> 8) & 0xFF, key->src_addr & 0xFF); + printf("-%" PRIu8 ".%" PRIu8 ".%" PRIu8 ".%" PRIu8 " ", (key->dst_addr >> 24) & 0xFF, + (key->dst_addr >> 16) & 0xFF, (key->dst_addr >> 8) & 0xFF, key->dst_addr & 0xFF); printf("Port: %d %d Proto: %d\n", key->src_port, key->dst_port, key->proto); } @@ -150,16 +150,16 @@ onvm_ft_fill_key(struct onvm_ft_ipv4_5tuple *key, struct rte_mbuf *pkt) { ipv4_hdr = onvm_pkt_ipv4_hdr(pkt); memset(key, 0, sizeof(struct onvm_ft_ipv4_5tuple)); key->proto = ipv4_hdr->next_proto_id; - key->src_addr = ipv4_hdr->src_addr; - key->dst_addr = ipv4_hdr->dst_addr; + key->src_addr = rte_be_to_cpu_32(ipv4_hdr->src_addr); + key->dst_addr = rte_be_to_cpu_32(ipv4_hdr->dst_addr); if (key->proto == IP_PROTOCOL_TCP) { tcp_hdr = onvm_pkt_tcp_hdr(pkt); - key->src_port = tcp_hdr->src_port; - key->dst_port = tcp_hdr->dst_port; + key->src_port = rte_be_to_cpu_16(tcp_hdr->src_port); + key->dst_port = rte_be_to_cpu_16(tcp_hdr->dst_port); } else if (key->proto == IP_PROTOCOL_UDP) { udp_hdr = onvm_pkt_udp_hdr(pkt); - key->src_port = udp_hdr->src_port; - key->dst_port = udp_hdr->dst_port; + key->src_port = rte_be_to_cpu_16(udp_hdr->src_port); + key->dst_port = rte_be_to_cpu_16(udp_hdr->dst_port); } else { key->src_port = 0; key->dst_port = 0; @@ -224,10 +224,10 @@ onvm_softrss(struct onvm_ft_ipv4_5tuple *key) { rte_convert_rss_key((uint32_t *)rss_symmetric_key, (uint32_t *)rss_key_be, RTE_DIM(rss_symmetric_key)); - tuple.v4.src_addr = rte_be_to_cpu_32(key->src_addr); - tuple.v4.dst_addr = rte_be_to_cpu_32(key->dst_addr); - tuple.v4.sport = rte_be_to_cpu_16(key->src_port); - tuple.v4.dport = rte_be_to_cpu_16(key->dst_port); + tuple.v4.src_addr = key->src_addr; + tuple.v4.dst_addr = key->dst_addr; + tuple.v4.sport = key->src_port; + tuple.v4.dport = key->dst_port; rss_l3l4 = rte_softrss_be((uint32_t *)&tuple, RTE_THASH_V4_L4_LEN, rss_key_be); diff --git a/onvm/onvm_nflib/onvm_nflib.c b/onvm/onvm_nflib/onvm_nflib.c index 2e5ecb33b..75e526239 100755 --- a/onvm/onvm_nflib/onvm_nflib.c +++ b/onvm/onvm_nflib/onvm_nflib.c @@ -555,6 +555,9 @@ onvm_nflib_start_nf(struct onvm_nf_local_ctx *nf_local_ctx, struct onvm_nf_init_ int onvm_nflib_run(struct onvm_nf_local_ctx *nf_local_ctx) { + /* Map the sdn_ft table */ + onvm_flow_dir_nf_init(); + int ret; pthread_t main_loop_thread; diff --git a/onvm/onvm_nflib/onvm_pkt_common.c b/onvm/onvm_nflib/onvm_pkt_common.c index 2a6d88c90..cb039e3a2 100644 --- a/onvm/onvm_nflib/onvm_pkt_common.c +++ b/onvm/onvm_nflib/onvm_pkt_common.c @@ -102,9 +102,8 @@ onvm_pkt_process_tx_batch(struct queue_mgr *tx_mgr, struct rte_mbuf *pkts[], uin // and ! is 1. nf->stats.act_drop++; nf->stats.tx += !onvm_pkt_drop(pkts[i]); - } else if (meta->action == ONVM_NF_ACTION_NEXT) { - /* TODO: Here we drop the packet : there will be a flow table - in the future to know what to do with the packet next */ + } else if (meta->action == ONVM_NF_ACTION_NEXT || meta->destination == ACTION_NEXT_DEST_ID) { + // Perform next action is configured by the manager's flow table nf->stats.act_next++; onvm_pkt_process_next_action(tx_mgr, pkts[i], nf); } else if (meta->action == ONVM_NF_ACTION_TONF) {