examples/server_node_efd: renamed from flow_distributor
authorPablo de Lara <pablo.de.lara.guarch@intel.com>
Tue, 24 Jan 2017 09:06:14 +0000 (09:06 +0000)
committerThomas Monjalon <thomas.monjalon@6wind.com>
Mon, 30 Jan 2017 16:26:11 +0000 (17:26 +0100)
To avoid confusion with distributor app, this commit
renames the flow-distributor sample app to server_node_efd,
since it shows how to use the EFD library and it is based
on a server/nodes model.

Signed-off-by: Pablo de Lara <pablo.de.lara.guarch@intel.com>
28 files changed:
MAINTAINERS
doc/api/examples.dox
doc/guides/sample_app_ug/flow_distributor.rst [deleted file]
doc/guides/sample_app_ug/img/flow_distributor.svg [deleted file]
doc/guides/sample_app_ug/img/server_node_efd.svg [new file with mode: 0644]
doc/guides/sample_app_ug/index.rst
doc/guides/sample_app_ug/server_node_efd.rst [new file with mode: 0644]
examples/Makefile
examples/flow_distributor/Makefile [deleted file]
examples/flow_distributor/distributor/Makefile [deleted file]
examples/flow_distributor/distributor/args.c [deleted file]
examples/flow_distributor/distributor/args.h [deleted file]
examples/flow_distributor/distributor/init.c [deleted file]
examples/flow_distributor/distributor/init.h [deleted file]
examples/flow_distributor/distributor/main.c [deleted file]
examples/flow_distributor/node/Makefile [deleted file]
examples/flow_distributor/node/node.c [deleted file]
examples/flow_distributor/shared/common.h [deleted file]
examples/server_node_efd/Makefile [new file with mode: 0644]
examples/server_node_efd/node/Makefile [new file with mode: 0644]
examples/server_node_efd/node/node.c [new file with mode: 0644]
examples/server_node_efd/server/Makefile [new file with mode: 0644]
examples/server_node_efd/server/args.c [new file with mode: 0644]
examples/server_node_efd/server/args.h [new file with mode: 0644]
examples/server_node_efd/server/init.c [new file with mode: 0644]
examples/server_node_efd/server/init.h [new file with mode: 0644]
examples/server_node_efd/server/main.c [new file with mode: 0644]
examples/server_node_efd/shared/common.h [new file with mode: 0644]

index f071138..cfbec21 100644 (file)
@@ -551,8 +551,8 @@ M: Pablo de Lara Guarch <pablo.de.lara.guarch@intel.com>
 F: lib/librte_efd/
 F: doc/guides/prog_guide/efd_lib.rst
 F: app/test/test_efd*
-F: examples/flow_distributor/
-F: doc/guides/sample_app_ug/flow_distributor.rst
+F: examples/server_node_efd/
+F: doc/guides/sample_app_ug/server_node_efd.rst
 
 Hashes
 M: Bruce Richardson <bruce.richardson@intel.com>
index c13e574..9a48f5f 100644 (file)
 @example load_balancer/init.c
 @example load_balancer/main.c
 @example load_balancer/runtime.c
-@example flow_distributor/distributor/args.c
-@example flow_distributor/distributor/init.c
-@example flow_distributor/distributor/main.c
-@example flow_distributor/node/node.c
 @example multi_process/client_server_mp/mp_client/client.c
 @example multi_process/client_server_mp/mp_server/args.c
 @example multi_process/client_server_mp/mp_server/init.c
 @example quota_watermark/qw/init.c
 @example quota_watermark/qw/main.c
 @example rxtx_callbacks/main.c
+@example server_node_efd/server/args.c
+@example server_node_efd/server/init.c
+@example server_node_efd/server/main.c
+@example server_node_efd/node/node.c
 @example skeleton/basicfwd.c
 @example tep_termination/main.c
 @example tep_termination/vxlan.c
diff --git a/doc/guides/sample_app_ug/flow_distributor.rst b/doc/guides/sample_app_ug/flow_distributor.rst
deleted file mode 100644 (file)
index a12df76..0000000
+++ /dev/null
@@ -1,494 +0,0 @@
-..  BSD LICENSE
-    Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
-    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.
-    * Neither the name of Intel Corporation nor the names of its
-    contributors may 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.
-
-Flow Distributor Sample Application
-===================================
-
-This sample application demonstrates the use of EFD library as a flow-level
-load balancer, for more information about the EFD Library please refer to the
-DPDK programmer's guide.
-
-This sample application is a variant of the
-:ref:`client-server sample application <multi_process_app>`
-where a specific target node is specified for every and each flow
-(not in a round-robin fashion as the original load balancing sample application).
-
-Overview
---------
-
-The architecture of the EFD flow-based load balancer sample application is
-presented in the following figure.
-
-.. _figure_efd_sample_app_overview:
-
-.. figure:: img/flow_distributor.*
-
-   Using EFD as a Flow-Level Load Balancer
-
-As shown in :numref:`figure_efd_sample_app_overview`,
-the sample application consists of a front-end node (distributor)
-using the EFD library to create a load-balancing table for flows,
-for each flow a target backend worker node is specified. The EFD table does not
-store the flow key (unlike a regular hash table), and hence, it can
-individually load-balance millions of flows (number of targets * maximum number
-of flows fit in a flow table per target) while still fitting in CPU cache.
-
-It should be noted that although they are referred to as nodes, the frontend
-distributor and worker nodes are processes running on the same platform.
-
-Front-end Distributor
-~~~~~~~~~~~~~~~~~~~~~
-
-Upon initializing, the frontend distributor node (process) creates a flow
-distributor table (based on the EFD library) which is populated with flow
-information and its intended target node.
-
-The sample application assigns a specific target node_id (process) for each of
-the IP destination addresses as follows:
-
-.. code-block:: c
-
-    node_id = i % num_nodes; /* Target node id is generated */
-    ip_dst = rte_cpu_to_be_32(i); /* Specific ip destination address is
-                                     assigned to this target node */
-
-then the pair of <key,target> is inserted into the flow distribution table.
-
-The main loop of the the distributor node receives a burst of packets, then for
-each packet, a flow key (IP destination address) is extracted. The flow
-distributor table is looked up and the target node id is returned.  Packets are
-then enqueued to the specified target node id.
-
-It should be noted that flow distributor table is not a membership test table.
-I.e. if the key has already been inserted the target node id will be correct,
-but for new keys the flow distributor table will return a value (which can be
-valid).
-
-Backend Worker Nodes
-~~~~~~~~~~~~~~~~~~~~
-
-Upon initializing, the worker node (process) creates a flow table (a regular
-hash table that stores the key default size 1M flows) which is populated with
-only the flow information that is serviced at this node. This flow key is
-essential to point out new keys that have not been inserted before.
-
-The worker node's main loop is simply receiving packets then doing a hash table
-lookup. If a match occurs then statistics are updated for flows serviced by
-this node. If no match is found in the local hash table then this indicates
-that this is a new flow, which is dropped.
-
-
-Compiling the Application
--------------------------
-
-The sequence of steps used to build the application is:
-
-#.  Export the required environment variables:
-
-    .. code-block:: console
-
-        export RTE_SDK=/path/to/rte_sdk
-        export RTE_TARGET=x86_64-native-linuxapp-gcc
-
-#.  Build the application executable file:
-
-    .. code-block:: console
-
-        cd ${RTE_SDK}/examples/flow_distributor/
-        make
-
-    For more details on how to build the DPDK libraries and sample
-    applications,
-    please refer to the *DPDK Getting Started Guide.*
-
-
-Running the Application
------------------------
-
-The application has two binaries to be run: the front-end distributor
-and the back-end node.
-
-The frontend distributor (distributor) has the following command line options::
-
-    ./distributor [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS
-
-Where,
-
-* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
-* ``-n NUM_NODES:`` Number of back-end nodes that will be used
-* ``-f NUM_FLOWS:`` Number of flows to be added in the EFD table (1 million, by default)
-
-The back-end node (node) has the following command line options::
-
-    ./node [EAL options] -- -n NODE_ID
-
-Where,
-
-* ``-n NODE_ID:`` Node ID, which cannot be equal or higher than NUM_MODES
-
-
-First, the distributor app must be launched, with the number of nodes that will be run.
-Once it has been started, the node instances can be run, with different NODE_ID.
-These instances have to be run as secondary processes, with ``--proc-type=secondary``
-in the EAL options, which will attach to the primary process memory, and therefore,
-they can access the queues created by the primary process to distribute packets.
-
-To successfully run the application, the command line used to start the
-application has to be in sync with the traffic flows configured on the traffic
-generator side.
-
-For examples of application command lines and traffic generator flows, please
-refer to the DPDK Test Report. For more details on how to set up and run the
-sample applications provided with DPDK package, please refer to the
-:ref:`DPDK Getting Started Guide for Linux <linux_gsg>` and
-:ref:`DPDK Getting Started Guide for FreeBSD <freebsd_gsg>`.
-
-
-Explanation
------------
-
-As described in previous sections, there are two processes in this example.
-
-The first process, the front-end distributor, creates and populates the EFD table,
-which is used to distribute packets to nodes, which the number of flows
-specified in the command line (1 million, by default).
-
-
-.. code-block:: c
-
-    static void
-    create_flow_distributor_table(void)
-    {
-        uint8_t socket_id = rte_socket_id();
-
-        /* create table */
-        efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
-                        1 << socket_id, socket_id);
-
-        if (efd_table == NULL)
-            rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
-    }
-
-    static void
-    populate_flow_distributor_table(void)
-    {
-        unsigned int i;
-        int32_t ret;
-        uint32_t ip_dst;
-        uint8_t socket_id = rte_socket_id();
-        uint64_t node_id;
-
-        /* Add flows in table */
-        for (i = 0; i < num_flows; i++) {
-            node_id = i % num_nodes;
-
-            ip_dst = rte_cpu_to_be_32(i);
-            ret = rte_efd_update(efd_table, socket_id,
-                            (void *)&ip_dst, (efd_value_t)node_id);
-            if (ret < 0)
-                rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
-                                    "flow distributor table\n", i);
-        }
-
-        printf("EFD table: Adding 0x%x keys\n", num_flows);
-    }
-
-After initialization, packets are received from the enabled ports, and the IPv4
-address from the packets is used as a key to look up in the EFD table,
-which tells the node where the packet has to be distributed.
-
-.. code-block:: c
-
-    static void
-    process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
-            uint16_t rx_count, unsigned int socket_id)
-    {
-        uint16_t i;
-        uint8_t node;
-        efd_value_t data[EFD_BURST_MAX];
-        const void *key_ptrs[EFD_BURST_MAX];
-
-        struct ipv4_hdr *ipv4_hdr;
-        uint32_t ipv4_dst_ip[EFD_BURST_MAX];
-
-        for (i = 0; i < rx_count; i++) {
-            /* Handle IPv4 header.*/
-            ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
-                    sizeof(struct ether_hdr));
-            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
-            key_ptrs[i] = (void *)&ipv4_dst_ip[i];
-        }
-
-        rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
-                    (const void **) key_ptrs, data);
-        for (i = 0; i < rx_count; i++) {
-            node = (uint8_t) ((uintptr_t)data[i]);
-
-            if (node >= num_nodes) {
-                /*
-                 * Node is out of range, which means that
-                 * flow has not been inserted
-                 */
-                flow_dist_stats.drop++;
-                rte_pktmbuf_free(pkts[i]);
-            } else {
-                flow_dist_stats.distributed++;
-                enqueue_rx_packet(node, pkts[i]);
-            }
-        }
-
-        for (i = 0; i < num_nodes; i++)
-            flush_rx_queue(i);
-    }
-
-The burst of packets received is enqueued in temporary buffers (per node),
-and enqueued in the shared ring between the distributor and the node.
-After this, a new burst of packets is received and this process is
-repeated infinitely.
-
-.. code-block:: c
-
-    static void
-    flush_rx_queue(uint16_t node)
-    {
-        uint16_t j;
-        struct node *cl;
-
-        if (cl_rx_buf[node].count == 0)
-            return;
-
-        cl = &nodes[node];
-        if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
-                cl_rx_buf[node].count) != 0){
-            for (j = 0; j < cl_rx_buf[node].count; j++)
-                rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
-            cl->stats.rx_drop += cl_rx_buf[node].count;
-        } else
-            cl->stats.rx += cl_rx_buf[node].count;
-
-        cl_rx_buf[node].count = 0;
-    }
-
-The second process, the back-end node, receives the packets from the shared
-ring with the distributor and send them out, if they belong to the node.
-
-At initialization, it attaches to the distributor process memory, to have
-access to the shared ring, parameters and statistics.
-
-.. code-block:: c
-
-    rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
-    if (rx_ring == NULL)
-        rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
-                "is distributor process running?\n");
-
-    mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
-    if (mp == NULL)
-        rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
-
-    mz = rte_memzone_lookup(MZ_SHARED_INFO);
-    if (mz == NULL)
-        rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
-    info = mz->addr;
-    tx_stats = &(info->tx_stats[node_id]);
-    filter_stats = &(info->filter_stats[node_id]);
-
-Then, the hash table that contains the flows that will be handled
-by the node is created and populated.
-
-.. code-block:: c
-
-    static struct rte_hash *
-    create_hash_table(const struct shared_info *info)
-    {
-        uint32_t num_flows_node = info->num_flows / info->num_nodes;
-        char name[RTE_HASH_NAMESIZE];
-        struct rte_hash *h;
-
-        /* create table */
-        struct rte_hash_parameters hash_params = {
-            .entries = num_flows_node * 2, /* table load = 50% */
-            .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
-            .socket_id = rte_socket_id(),
-            .hash_func_init_val = 0,
-        };
-
-        snprintf(name, sizeof(name), "hash_table_%d", node_id);
-        hash_params.name = name;
-        h = rte_hash_create(&hash_params);
-
-        if (h == NULL)
-            rte_exit(EXIT_FAILURE,
-                    "Problem creating the hash table for node %d\n",
-                    node_id);
-        return h;
-    }
-
-    static void
-    populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
-    {
-        unsigned int i;
-        int32_t ret;
-        uint32_t ip_dst;
-        uint32_t num_flows_node = 0;
-        uint64_t target_node;
-
-        /* Add flows in table */
-        for (i = 0; i < info->num_flows; i++) {
-            target_node = i % info->num_nodes;
-            if (target_node != node_id)
-                continue;
-
-            ip_dst = rte_cpu_to_be_32(i);
-
-            ret = rte_hash_add_key(h, (void *) &ip_dst);
-            if (ret < 0)
-                rte_exit(EXIT_FAILURE, "Unable to add entry %u "
-                        "in hash table\n", i);
-            else
-                num_flows_node++;
-
-        }
-
-        printf("Hash table: Adding 0x%x keys\n", num_flows_node);
-    }
-
-After initialization, packets are dequeued from the shared ring
-(from the distributor) and, like in the distributor process,
-the IPv4 address from the packets is used as a key to look up in the hash table.
-If there is a hit, packet is stored in a buffer, to be eventually transmitted
-in one of the enabled ports. If key is not there, packet is dropped, since the
-flow is not handled by the node.
-
-.. code-block:: c
-
-    static inline void
-    handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
-    {
-        struct ipv4_hdr *ipv4_hdr;
-        uint32_t ipv4_dst_ip[PKT_READ_SIZE];
-        const void *key_ptrs[PKT_READ_SIZE];
-        unsigned int i;
-        int32_t positions[PKT_READ_SIZE] = {0};
-
-        for (i = 0; i < num_packets; i++) {
-            /* Handle IPv4 header.*/
-            ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
-                    sizeof(struct ether_hdr));
-            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
-            key_ptrs[i] = &ipv4_dst_ip[i];
-        }
-        /* Check if packets belongs to any flows handled by this node */
-        rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
-
-        for (i = 0; i < num_packets; i++) {
-            if (likely(positions[i] >= 0)) {
-                filter_stats->passed++;
-                transmit_packet(bufs[i]);
-            } else {
-                filter_stats->drop++;
-                /* Drop packet, as flow is not handled by this node */
-                rte_pktmbuf_free(bufs[i]);
-            }
-        }
-    }
-
-Finally, note that both processes updates statistics, such as transmitted, received
-and dropped packets, which are shown and refreshed by the distributor app.
-
-.. code-block:: c
-
-    static void
-    do_stats_display(void)
-    {
-        unsigned int i, j;
-        const char clr[] = {27, '[', '2', 'J', '\0'};
-        const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
-        uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
-        uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
-
-        /* to get TX stats, we need to do some summing calculations */
-        memset(port_tx, 0, sizeof(port_tx));
-        memset(port_tx_drop, 0, sizeof(port_tx_drop));
-        memset(node_tx, 0, sizeof(node_tx));
-        memset(node_tx_drop, 0, sizeof(node_tx_drop));
-
-        for (i = 0; i < num_nodes; i++) {
-            const struct tx_stats *tx = &info->tx_stats[i];
-
-            for (j = 0; j < info->num_ports; j++) {
-                const uint64_t tx_val = tx->tx[info->id[j]];
-                const uint64_t drop_val = tx->tx_drop[info->id[j]];
-
-                port_tx[j] += tx_val;
-                port_tx_drop[j] += drop_val;
-                node_tx[i] += tx_val;
-                node_tx_drop[i] += drop_val;
-            }
-        }
-
-        /* Clear screen and move to top left */
-        printf("%s%s", clr, topLeft);
-
-        printf("PORTS\n");
-        printf("-----\n");
-        for (i = 0; i < info->num_ports; i++)
-            printf("Port %u: '%s'\t", (unsigned int)info->id[i],
-                    get_printable_mac_addr(info->id[i]));
-        printf("\n\n");
-        for (i = 0; i < info->num_ports; i++) {
-            printf("Port %u - rx: %9"PRIu64"\t"
-                    "tx: %9"PRIu64"\n",
-                    (unsigned int)info->id[i], info->rx_stats.rx[i],
-                    port_tx[i]);
-        }
-
-        printf("\nFLOW DISTRIBUTOR\n");
-        printf("-----\n");
-        printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
-                flow_dist_stats.distributed, flow_dist_stats.drop);
-
-        printf("\nNODES\n");
-        printf("-------\n");
-        for (i = 0; i < num_nodes; i++) {
-            const unsigned long long rx = nodes[i].stats.rx;
-            const unsigned long long rx_drop = nodes[i].stats.rx_drop;
-            const struct filter_stats *filter = &info->filter_stats[i];
-
-            printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
-                    "            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
-                    "            filter_passed: %9"PRIu64", "
-                    "filter_drop: %9"PRIu64"\n",
-                    i, rx, rx_drop, node_tx[i], node_tx_drop[i],
-                    filter->passed, filter->drop);
-        }
-
-        printf("\n");
-    }
diff --git a/doc/guides/sample_app_ug/img/flow_distributor.svg b/doc/guides/sample_app_ug/img/flow_distributor.svg
deleted file mode 100644 (file)
index 9aee30b..0000000
+++ /dev/null
@@ -1,1254 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Generated by Microsoft Visio, SVG Export efd_i6.svg Page-1 -->
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
-               xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.2496in" height="5.89673in"
-               viewBox="0 0 593.971 424.565" xml:space="preserve" color-interpolation-filters="sRGB" class="st27">
-       <v:documentProperties v:langID="1033" v:viewMarkup="false">
-               <v:userDefs>
-                       <v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
-                       <v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
-               </v:userDefs>
-       </v:documentProperties>
-
-       <style type="text/css">
-       <![CDATA[
-               .st1 {visibility:visible}
-               .st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
-               .st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
-               .st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
-               .st5 {fill:#feffff;font-family:Calibri;font-size:0.75em}
-               .st6 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
-               .st7 {fill:none;stroke:#2e75b5;stroke-width:2.25}
-               .st8 {fill:#305497;stroke:#2e75b5;stroke-width:1}
-               .st9 {fill:#feffff;font-family:Calibri;font-size:0.833336em;font-weight:bold}
-               .st10 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2)}
-               .st11 {fill:#5b9bd5}
-               .st12 {stroke:#c7c8c8;stroke-width:0.25}
-               .st13 {fill:#acccea;stroke:#c7c8c8;stroke-width:0.25}
-               .st14 {fill:#feffff;font-family:Calibri;font-size:1.00001em;font-weight:bold}
-               .st15 {fill:#ed7d31;stroke:#c7c8c8;stroke-width:0.25}
-               .st16 {fill:#deebf6;stroke:#c7c8c8;stroke-width:0.25}
-               .st17 {marker-end:url(#mrkr5-212);stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
-               .st18 {fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1;stroke-width:0.28409090909091}
-               .st19 {fill:none;stroke:#2e75b5;stroke-width:1}
-               .st20 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em}
-               .st21 {fill:none;stroke:none;stroke-width:0.25}
-               .st22 {font-size:1em}
-               .st23 {fill:#ffffff}
-               .st24 {stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
-               .st25 {marker-end:url(#mrkr5-444);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
-               .st26 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
-               .st27 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
-       ]]>
-       </style>
-
-       <defs id="Markers">
-               <g id="lend5">
-                       <path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
-               </g>
-               <marker id="mrkr5-212" class="st18" v:arrowType="5" v:arrowSize="2" v:setback="5.8" refX="-5.8" orient="auto"
-                               markerUnits="strokeWidth" overflow="visible">
-                       <use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
-               </marker>
-               <marker id="mrkr5-444" class="st26" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
-                               markerUnits="strokeWidth" overflow="visible">
-                       <use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
-               </marker>
-       </defs>
-       <defs id="Filters">
-               <filter id="filter_2">
-                       <feGaussianBlur stdDeviation="2"/>
-               </filter>
-       </defs>
-       <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
-               <v:userDefs>
-                       <v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
-               </v:userDefs>
-               <title>Page-1</title>
-               <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
-               <g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(319.501,-335.688)">
-                       <title>Rectangle.58</title>
-                       <desc>Key 1</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow3-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>           </g>
-               <g id="shape4-7" v:mID="4" v:groupContext="shape" transform="translate(353.251,-335.688)">
-                       <title>Rectangle.59</title>
-                       <desc>Action 1</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow4-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>                </g>
-               <g id="shape5-13" v:mID="5" v:groupContext="shape" transform="translate(400.501,-335.688)">
-                       <title>Rectangle.60</title>
-                       <desc>Key 2</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow5-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>           </g>
-               <g id="shape6-19" v:mID="6" v:groupContext="shape" transform="translate(434.251,-335.688)">
-                       <title>Rectangle.61</title>
-                       <desc>Action 2</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow6-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>                </g>
-               <g id="shape7-25" v:mID="7" v:groupContext="shape" transform="translate(481.501,-335.688)">
-                       <title>Rectangle.62</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow7-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape8-30" v:mID="8" v:groupContext="shape" transform="translate(515.251,-335.688)">
-                       <title>Rectangle.63</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow8-31" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape9-35" v:mID="9" v:groupContext="shape" transform="translate(319.501,-313.188)">
-                       <title>Rectangle.64</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow9-36" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape10-40" v:mID="10" v:groupContext="shape" transform="translate(353.251,-313.188)">
-                       <title>Rectangle.65</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow10-41" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape11-45" v:mID="11" v:groupContext="shape" transform="translate(400.501,-313.188)">
-                       <title>Rectangle.66</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow11-46" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape12-50" v:mID="12" v:groupContext="shape" transform="translate(434.251,-313.188)">
-                       <title>Rectangle.67</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow12-51" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape13-55" v:mID="13" v:groupContext="shape" transform="translate(481.501,-313.188)">
-                       <title>Rectangle.68</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow13-56" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape14-60" v:mID="14" v:groupContext="shape" transform="translate(515.251,-313.188)">
-                       <title>Rectangle.69</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow14-61" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(319.501,-277.188)">
-                       <title>Rectangle.70</title>
-                       <desc>Key x</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow15-66" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>           </g>
-               <g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(353.251,-277.188)">
-                       <title>Rectangle.71</title>
-                       <desc>Action x</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow16-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>                </g>
-               <g id="shape17-77" v:mID="17" v:groupContext="shape" transform="translate(400.501,-277.188)">
-                       <title>Rectangle.72</title>
-                       <desc>Key y</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow17-78" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>           </g>
-               <g id="shape18-83" v:mID="18" v:groupContext="shape" transform="translate(434.251,-277.188)">
-                       <title>Rectangle.73</title>
-                       <desc>Action y</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow18-84" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>                </g>
-               <g id="shape19-89" v:mID="19" v:groupContext="shape" transform="translate(481.501,-277.188)">
-                       <title>Rectangle.74</title>
-                       <desc>Key z</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow19-90" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>            </g>
-               <g id="shape20-95" v:mID="20" v:groupContext="shape" transform="translate(515.251,-277.188)">
-                       <title>Rectangle.75</title>
-                       <desc>Action z</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow20-96" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>                </g>
-               <g id="shape21-101" v:mID="21" v:groupContext="shape" transform="translate(319.501,-240.687)">
-                       <title>Rectangle.76</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow21-102" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape22-106" v:mID="22" v:groupContext="shape" transform="translate(353.251,-240.687)">
-                       <title>Rectangle.77</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow22-107" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape23-111" v:mID="23" v:groupContext="shape" transform="translate(400.501,-240.687)">
-                       <title>Rectangle.78</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow23-112" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape24-116" v:mID="24" v:groupContext="shape" transform="translate(434.251,-240.687)">
-                       <title>Rectangle.79</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow24-117" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape25-121" v:mID="25" v:groupContext="shape" transform="translate(481.501,-240.687)">
-                       <title>Rectangle.80</title>
-                       <desc>Key N</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow25-122" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>           </g>
-               <g id="shape26-127" v:mID="26" v:groupContext="shape" transform="translate(515.251,-240.687)">
-                       <title>Rectangle.81</title>
-                       <desc>Action N</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow26-128" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>                </g>
-               <g id="shape27-133" v:mID="27" v:groupContext="shape" transform="translate(317.251,-231.687)">
-                       <title>Rectangle.82</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow27-134" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
-                       </g>
-                       <rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
-               </g>
-               <g id="shape28-138" v:mID="28" v:groupContext="shape" transform="translate(328.501,-362.688)">
-                       <title>Sheet.28</title>
-                       <desc>Local Table for N Specific Flows Serviced at Node 1</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
-                       <rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
-                       <text x="5.77" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node 1</text>             </g>
-               <g id="group34-141" transform="translate(66.0294,-165.569)" v:mID="34" v:groupContext="group">
-                       <v:custProps>
-                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
-                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Device)"/>
-                               <v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Load balancer)"/>
-                       </v:custProps>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
-                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
-                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
-                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
-                       </v:userDefs>
-                       <title>Load balancer</title>
-                       <g id="shape35-142" v:mID="35" v:groupContext="shape" transform="translate(0,-7.33146)">
-                               <title>Sheet.35</title>
-                               <g id="shadow35-143" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st10"/>
-                                       <path d="M0 377.86 L72 377.86" class="st6"/>
-                                       <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st6"/>
-                               </g>
-                               <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st11"/>
-                               <path d="M0 377.86 L72 377.86" class="st12"/>
-                               <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st12"/>
-                       </g>
-                       <g id="shape36-152" v:mID="36" v:groupContext="shape" transform="translate(8.03054,-12.9324)">
-                               <title>Sheet.36</title>
-                               <g id="shadow36-153" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97
-                                                                413.34 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83
-                                                                L51.34 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37
-                                                                L38.13 400.48 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16
-                                                                L46.49 392.01 ZM27.71 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42
-                                                                27.71 415.42 C32.75 415.42 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71
-                                                                400.04 C31.15 400.04 33.96 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54
-                                                                21.46 409.74 21.46 406.29 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56
-                                                                L11.6 407.56 L8.62 410.51 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z"
-                                                       class="st2"/>
-                               </g>
-                               <path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97 413.34
-                                                        L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83 L51.34
-                                                        410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37 L38.13 400.48
-                                                        L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16 L46.49 392.01 ZM27.71
-                                                        397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42 27.71 415.42 C32.75 415.42
-                                                        36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71 400.04 C31.15 400.04 33.96
-                                                        402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54 21.46 409.74 21.46 406.29
-                                                        C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56 L11.6 407.56 L8.62 410.51
-                                                        L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z" class="st13"/>
-                       </g>
-               </g>
-               <g id="shape37-157" v:mID="37" v:groupContext="shape" transform="translate(21.0294,-45.4375)">
-                       <title>Rectangle</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow37-158" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="336.433" width="135" height="88.1315" class="st2"/>
-                       </g>
-                       <rect x="0" y="336.433" width="135" height="88.1315" class="st3"/>
-               </g>
-               <g id="shape38-162" v:mID="38" v:groupContext="shape" transform="translate(34.693,-126.438)">
-                       <title>Sheet.38</title>
-                       <desc>EFD Table</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="49.3364" cy="415.565" width="98.68" height="18"/>
-                       <rect x="0" y="406.565" width="98.6728" height="18" class="st8"/>
-                       <text x="24.87" y="419.17" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>EFD Table</text>             </g>
-               <g id="shape39-165" v:mID="39" v:groupContext="shape" transform="translate(30.0294,-99.4375)">
-                       <title>Rectangle.39</title>
-                       <desc>Group_id</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
-                       <g id="shadow39-166" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
-                       <text x="7.87" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group_id</text>                </g>
-               <g id="shape40-171" v:mID="40" v:groupContext="shape" transform="translate(93.193,-99.4375)">
-                       <title>Rectangle.40</title>
-                       <desc>Hash index</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
-                       <g id="shadow40-172" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
-                       <text x="4.64" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash index</text>              </g>
-               <g id="shape41-177" v:mID="41" v:groupContext="shape" transform="translate(30.193,-82.4275)">
-                       <title>Rectangle.41</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow41-178" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
-                       </g>
-                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
-               </g>
-               <g id="shape42-182" v:mID="42" v:groupContext="shape" transform="translate(30.193,-66.8125)">
-                       <title>Rectangle.42</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow42-183" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
-                       </g>
-                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
-               </g>
-               <g id="shape43-187" v:mID="43" v:groupContext="shape" transform="translate(30.1112,-52.1875)">
-                       <title>Rectangle.43</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow43-188" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
-                       </g>
-                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
-               </g>
-               <g id="shape44-192" v:mID="44" v:groupContext="shape" transform="translate(93.0294,-81.4375)">
-                       <title>Rectangle.44</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow44-193" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
-                       </g>
-                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
-               </g>
-               <g id="shape45-197" v:mID="45" v:groupContext="shape" transform="translate(93.193,-66.8125)">
-                       <title>Rectangle.45</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow45-198" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
-                       </g>
-                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
-               </g>
-               <g id="shape46-202" v:mID="46" v:groupContext="shape" transform="translate(93.193,-52.1875)">
-                       <title>Rectangle.46</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow46-203" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
-                       </g>
-                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
-               </g>
-               <g id="shape47-207" v:mID="47" v:groupContext="shape" transform="translate(374.924,544.022) rotate(135)">
-                       <title>Sheet.47</title>
-                       <path d="M-0 417.75 A40.674 18.0151 -156.2 0 0 40.24 422.15 L40.49 421.89" class="st17"/>
-               </g>
-               <g id="shape48-213" v:mID="48" v:groupContext="shape" transform="translate(21.0294,-19)">
-                       <title>Sheet.48</title>
-                       <desc>Supports X*N Flows</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
-                       <rect x="0" y="406.565" width="135" height="18" class="st19"/>
-                       <text x="19.05" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports X*N Flows</text>            </g>
-               <g id="shape49-216" v:mID="49" v:groupContext="shape" transform="translate(48.0294,-229.938)">
-                       <title>Sheet.49</title>
-                       <desc>Frontend Server or Load Balancer</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="54" cy="400.94" width="108" height="47.25"/>
-                       <rect x="0" y="377.315" width="108" height="47.25" class="st21"/>
-                       <text x="14.56" y="397.34" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Frontend Server<v:newlineChar/><tspan
-                                               x="13.16" dy="1.2em" class="st22">or Load Balancer </tspan> </text>             </g>
-               <g id="group51-220" transform="translate(223.876,-310.938)" v:mID="51" v:groupContext="group">
-                       <v:custProps>
-                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
-                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
-                               <v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                       </v:custProps>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
-                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
-                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
-                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
-                       </v:userDefs>
-                       <title>Server</title>
-                       <g id="shape52-221" v:mID="52" v:groupContext="shape" transform="translate(13.0183,0)">
-                               <title>Sheet.52</title>
-                               <g id="shadow52-222" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
-                               </g>
-                               <rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
-                       </g>
-                       <g id="shape53-226" v:mID="53" v:groupContext="shape" transform="translate(47.371,-30.7354)">
-                               <title>Sheet.53</title>
-                               <g id="shadow53-227" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
-                               </g>
-                               <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
-                       </g>
-                       <g id="shape54-231" v:mID="54" v:groupContext="shape" transform="translate(30.51,-11.8022)">
-                               <title>Sheet.54</title>
-                               <v:userDefs>
-                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
-                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
-                               </v:userDefs>
-                               <g id="shadow54-232" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
-                                                                L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
-                                                       class="st10"/>
-                               </g>
-                               <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
-                                                        417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
-                       </g>
-               </g>
-               <g id="shape59-239" v:mID="59" v:groupContext="shape" transform="translate(277.876,-373.938)">
-                       <title>Sheet.59</title>
-                       <path d="M-0 424.56 A111.108 53.2538 42.31 0 1 93.83 421.21 L94.14 421.41" class="st17"/>
-               </g>
-               <g id="shape60-244" v:mID="60" v:groupContext="shape" transform="translate(205.876,-283.938)">
-                       <title>Sheet.60</title>
-                       <desc>Backend Server 1</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
-                       <rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
-                       <text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 1</text>              </g>
-               <g id="group61-247" transform="translate(223.876,-207.438)" v:mID="61" v:groupContext="group">
-                       <v:custProps>
-                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
-                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
-                               <v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                       </v:custProps>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
-                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
-                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
-                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
-                       </v:userDefs>
-                       <title>Server.61</title>
-                       <g id="shape62-248" v:mID="62" v:groupContext="shape" transform="translate(13.0183,0)">
-                               <title>Sheet.62</title>
-                               <g id="shadow62-249" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
-                               </g>
-                               <rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
-                       </g>
-                       <g id="shape63-253" v:mID="63" v:groupContext="shape" transform="translate(47.371,-30.7354)">
-                               <title>Sheet.63</title>
-                               <g id="shadow63-254" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
-                               </g>
-                               <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
-                       </g>
-                       <g id="shape64-258" v:mID="64" v:groupContext="shape" transform="translate(30.51,-11.8022)">
-                               <title>Sheet.64</title>
-                               <v:userDefs>
-                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
-                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
-                               </v:userDefs>
-                               <g id="shadow64-259" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
-                                                                L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
-                                                       class="st10"/>
-                               </g>
-                               <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
-                                                        417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
-                       </g>
-               </g>
-               <g id="shape65-266" v:mID="65" v:groupContext="shape" transform="translate(205.876,-180.437)">
-                       <title>Sheet.65</title>
-                       <desc>Backend Server 2</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
-                       <rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
-                       <text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 2</text>              </g>
-               <g id="group66-269" transform="translate(219.029,-58.9375)" v:mID="66" v:groupContext="group">
-                       <v:custProps>
-                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
-                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
-                                               v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
-                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
-                               <v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
-                                               v:ask="false" v:langID="1033" v:cal="0"/>
-                       </v:custProps>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
-                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
-                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
-                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
-                       </v:userDefs>
-                       <title>Server.66</title>
-                       <g id="shape67-270" v:mID="67" v:groupContext="shape" transform="translate(13.0183,0)">
-                               <title>Sheet.67</title>
-                               <g id="shadow67-271" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
-                               </g>
-                               <rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
-                       </g>
-                       <g id="shape68-275" v:mID="68" v:groupContext="shape" transform="translate(47.371,-30.7354)">
-                               <title>Sheet.68</title>
-                               <g id="shadow68-276" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
-                               </g>
-                               <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
-                       </g>
-                       <g id="shape69-280" v:mID="69" v:groupContext="shape" transform="translate(30.51,-11.8022)">
-                               <title>Sheet.69</title>
-                               <v:userDefs>
-                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
-                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
-                               </v:userDefs>
-                               <g id="shadow69-281" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                                       <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
-                                                                L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
-                                                       class="st10"/>
-                               </g>
-                               <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
-                                                        417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
-                       </g>
-               </g>
-               <g id="shape70-288" v:mID="70" v:groupContext="shape" transform="translate(201.029,-26.056)">
-                       <title>Sheet.70</title>
-                       <desc>Backend Server X</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
-                       <rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
-                       <text x="11.86" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server X</text>              </g>
-               <g id="shape71-291" v:mID="71" v:groupContext="shape" transform="translate(684.44,239.627) rotate(90)">
-                       <title>Sheet.71</title>
-                       <path d="M0 424.56 L45 424.56" class="st24"/>
-               </g>
-               <g id="shape72-294" v:mID="72" v:groupContext="shape" transform="translate(6.85967,-22.443) rotate(-38.1076)">
-                       <title>Sheet.72</title>
-                       <path d="M-0 424.56 A96.1331 44.4001 55.03 0 1 68.24 420.56 L68.51 420.79" class="st17"/>
-               </g>
-               <g id="shape73-299" v:mID="73" v:groupContext="shape" transform="translate(328.501,-135.937)">
-                       <title>Rectangle.73</title>
-                       <desc>Key 1</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow73-300" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>           </g>
-               <g id="shape74-305" v:mID="74" v:groupContext="shape" transform="translate(362.251,-135.937)">
-                       <title>Rectangle.74</title>
-                       <desc>Action 1</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow74-306" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>                </g>
-               <g id="shape75-311" v:mID="75" v:groupContext="shape" transform="translate(409.501,-135.937)">
-                       <title>Rectangle.75</title>
-                       <desc>Key 2</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow75-312" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>           </g>
-               <g id="shape76-317" v:mID="76" v:groupContext="shape" transform="translate(443.251,-135.937)">
-                       <title>Rectangle.76</title>
-                       <desc>Action 2</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow76-318" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>                </g>
-               <g id="shape77-323" v:mID="77" v:groupContext="shape" transform="translate(490.501,-135.937)">
-                       <title>Rectangle.77</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow77-324" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape78-328" v:mID="78" v:groupContext="shape" transform="translate(524.251,-135.937)">
-                       <title>Rectangle.78</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow78-329" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape79-333" v:mID="79" v:groupContext="shape" transform="translate(328.501,-113.437)">
-                       <title>Rectangle.79</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow79-334" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape80-338" v:mID="80" v:groupContext="shape" transform="translate(362.251,-113.437)">
-                       <title>Rectangle.80</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow80-339" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape81-343" v:mID="81" v:groupContext="shape" transform="translate(409.501,-113.437)">
-                       <title>Rectangle.81</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow81-344" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape82-348" v:mID="82" v:groupContext="shape" transform="translate(443.251,-113.437)">
-                       <title>Rectangle.82</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow82-349" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape83-353" v:mID="83" v:groupContext="shape" transform="translate(490.501,-113.437)">
-                       <title>Rectangle.83</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow83-354" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape84-358" v:mID="84" v:groupContext="shape" transform="translate(524.251,-113.437)">
-                       <title>Rectangle.84</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow84-359" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape85-363" v:mID="85" v:groupContext="shape" transform="translate(328.501,-77.4375)">
-                       <title>Rectangle.85</title>
-                       <desc>Key x</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow85-364" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>           </g>
-               <g id="shape86-369" v:mID="86" v:groupContext="shape" transform="translate(362.251,-77.4375)">
-                       <title>Rectangle.86</title>
-                       <desc>Action x</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow86-370" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>                </g>
-               <g id="shape87-375" v:mID="87" v:groupContext="shape" transform="translate(409.501,-77.4375)">
-                       <title>Rectangle.87</title>
-                       <desc>Key y</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow87-376" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>           </g>
-               <g id="shape88-381" v:mID="88" v:groupContext="shape" transform="translate(443.251,-77.4375)">
-                       <title>Rectangle.88</title>
-                       <desc>Action y</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow88-382" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>                </g>
-               <g id="shape89-387" v:mID="89" v:groupContext="shape" transform="translate(490.501,-77.4375)">
-                       <title>Rectangle.89</title>
-                       <desc>Key z</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow89-388" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>            </g>
-               <g id="shape90-393" v:mID="90" v:groupContext="shape" transform="translate(524.251,-77.4375)">
-                       <title>Rectangle.90</title>
-                       <desc>Action z</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow90-394" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>                </g>
-               <g id="shape91-399" v:mID="91" v:groupContext="shape" transform="translate(328.501,-40.9375)">
-                       <title>Rectangle.91</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow91-400" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape92-404" v:mID="92" v:groupContext="shape" transform="translate(362.251,-40.9375)">
-                       <title>Rectangle.92</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow92-405" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape93-409" v:mID="93" v:groupContext="shape" transform="translate(409.501,-40.9375)">
-                       <title>Rectangle.93</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow93-410" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-               </g>
-               <g id="shape94-414" v:mID="94" v:groupContext="shape" transform="translate(443.251,-40.9375)">
-                       <title>Rectangle.94</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow94-415" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-               </g>
-               <g id="shape95-419" v:mID="95" v:groupContext="shape" transform="translate(490.501,-40.9375)">
-                       <title>Rectangle.95</title>
-                       <desc>Key N</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
-                       <g id="shadow95-420" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
-                       <text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>           </g>
-               <g id="shape96-425" v:mID="96" v:groupContext="shape" transform="translate(524.251,-40.9375)">
-                       <title>Rectangle.96</title>
-                       <desc>Action N</desc>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
-                       <g id="shadow96-426" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
-                       </g>
-                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
-                       <text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>                </g>
-               <g id="shape97-431" v:mID="97" v:groupContext="shape" transform="translate(326.251,-31.9375)">
-                       <title>Rectangle.97</title>
-                       <v:userDefs>
-                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
-                       </v:userDefs>
-                       <g id="shadow97-432" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
-                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
-                               <rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
-                       </g>
-                       <rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
-               </g>
-               <g id="shape98-436" v:mID="98" v:groupContext="shape" transform="translate(337.501,-162.938)">
-                       <title>Sheet.98</title>
-                       <desc>Local Table for N Specific Flows Serviced at Node X</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
-                       <rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
-                       <text x="5.55" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node X</text>             </g>
-               <g id="shape99-439" v:mID="99" v:groupContext="shape" transform="translate(-204.342,-29.4449) rotate(-53.7462)">
-                       <title>Sheet.99</title>
-                       <path d="M0 424.56 L160.37 424.56" class="st25"/>
-               </g>
-               <g id="shape100-445" v:mID="100" v:groupContext="shape" transform="translate(-37.6568,-164.882) rotate(-24.444)">
-                       <title>Sheet.100</title>
-                       <path d="M0 424.56 L101.71 424.56" class="st25"/>
-               </g>
-               <g id="shape101-450" v:mID="101" v:groupContext="shape" transform="translate(464.049,-50.8578) rotate(50.099)">
-                       <title>Sheet.101</title>
-                       <path d="M0 424.56 L139.8 424.56" class="st25"/>
-               </g>
-               <g id="shape102-455" v:mID="102" v:groupContext="shape" transform="translate(372.376,-207.438)">
-                       <title>Sheet.102</title>
-                       <desc>Supports N Flows</desc>
-                       <v:textBlock v:margins="rect(4,4,4,4)"/>
-                       <v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
-                       <rect x="0" y="406.565" width="135" height="18" class="st19"/>
-                       <text x="25.15" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports N Flows</text>              </g>
-       </g>
-</svg>
diff --git a/doc/guides/sample_app_ug/img/server_node_efd.svg b/doc/guides/sample_app_ug/img/server_node_efd.svg
new file mode 100644 (file)
index 0000000..9aee30b
--- /dev/null
@@ -0,0 +1,1254 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by Microsoft Visio, SVG Export efd_i6.svg Page-1 -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
+               xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.2496in" height="5.89673in"
+               viewBox="0 0 593.971 424.565" xml:space="preserve" color-interpolation-filters="sRGB" class="st27">
+       <v:documentProperties v:langID="1033" v:viewMarkup="false">
+               <v:userDefs>
+                       <v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
+                       <v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
+               </v:userDefs>
+       </v:documentProperties>
+
+       <style type="text/css">
+       <![CDATA[
+               .st1 {visibility:visible}
+               .st2 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+               .st3 {fill:#5b9bd5;stroke:#c7c8c8;stroke-width:0.25}
+               .st4 {fill:#feffff;font-family:Calibri;font-size:0.833336em}
+               .st5 {fill:#feffff;font-family:Calibri;font-size:0.75em}
+               .st6 {fill:none;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
+               .st7 {fill:none;stroke:#2e75b5;stroke-width:2.25}
+               .st8 {fill:#305497;stroke:#2e75b5;stroke-width:1}
+               .st9 {fill:#feffff;font-family:Calibri;font-size:0.833336em;font-weight:bold}
+               .st10 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2)}
+               .st11 {fill:#5b9bd5}
+               .st12 {stroke:#c7c8c8;stroke-width:0.25}
+               .st13 {fill:#acccea;stroke:#c7c8c8;stroke-width:0.25}
+               .st14 {fill:#feffff;font-family:Calibri;font-size:1.00001em;font-weight:bold}
+               .st15 {fill:#ed7d31;stroke:#c7c8c8;stroke-width:0.25}
+               .st16 {fill:#deebf6;stroke:#c7c8c8;stroke-width:0.25}
+               .st17 {marker-end:url(#mrkr5-212);stroke:#ff0000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
+               .st18 {fill:#ff0000;fill-opacity:1;stroke:#ff0000;stroke-opacity:1;stroke-width:0.28409090909091}
+               .st19 {fill:none;stroke:#2e75b5;stroke-width:1}
+               .st20 {fill:#5b9bd5;font-family:Calibri;font-size:1.00001em}
+               .st21 {fill:none;stroke:none;stroke-width:0.25}
+               .st22 {font-size:1em}
+               .st23 {fill:#ffffff}
+               .st24 {stroke:#5b9bd5;stroke-dasharray:1.5,3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+               .st25 {marker-end:url(#mrkr5-444);stroke:#5b9bd5;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+               .st26 {fill:#5b9bd5;fill-opacity:1;stroke:#5b9bd5;stroke-opacity:1;stroke-width:0.37313432835821}
+               .st27 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+       ]]>
+       </style>
+
+       <defs id="Markers">
+               <g id="lend5">
+                       <path d="M 2 1 L 0 0 L 1.98117 -0.993387 C 1.67173 -0.364515 1.67301 0.372641 1.98465 1.00043 " style="stroke:none"/>
+               </g>
+               <marker id="mrkr5-212" class="st18" v:arrowType="5" v:arrowSize="2" v:setback="5.8" refX="-5.8" orient="auto"
+                               markerUnits="strokeWidth" overflow="visible">
+                       <use xlink:href="#lend5" transform="scale(-3.52,-3.52) "/>
+               </marker>
+               <marker id="mrkr5-444" class="st26" v:arrowType="5" v:arrowSize="2" v:setback="4.69" refX="-4.69" orient="auto"
+                               markerUnits="strokeWidth" overflow="visible">
+                       <use xlink:href="#lend5" transform="scale(-2.68,-2.68) "/>
+               </marker>
+       </defs>
+       <defs id="Filters">
+               <filter id="filter_2">
+                       <feGaussianBlur stdDeviation="2"/>
+               </filter>
+       </defs>
+       <g v:mID="0" v:index="1" v:groupContext="foregroundPage">
+               <v:userDefs>
+                       <v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
+               </v:userDefs>
+               <title>Page-1</title>
+               <v:pageProperties v:drawingScale="1" v:pageScale="1" v:drawingUnits="0" v:shadowOffsetX="9" v:shadowOffsetY="-9"/>
+               <g id="shape3-1" v:mID="3" v:groupContext="shape" transform="translate(319.501,-335.688)">
+                       <title>Rectangle.58</title>
+                       <desc>Key 1</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow3-2" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>           </g>
+               <g id="shape4-7" v:mID="4" v:groupContext="shape" transform="translate(353.251,-335.688)">
+                       <title>Rectangle.59</title>
+                       <desc>Action 1</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow4-8" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>                </g>
+               <g id="shape5-13" v:mID="5" v:groupContext="shape" transform="translate(400.501,-335.688)">
+                       <title>Rectangle.60</title>
+                       <desc>Key 2</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow5-14" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>           </g>
+               <g id="shape6-19" v:mID="6" v:groupContext="shape" transform="translate(434.251,-335.688)">
+                       <title>Rectangle.61</title>
+                       <desc>Action 2</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow6-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>                </g>
+               <g id="shape7-25" v:mID="7" v:groupContext="shape" transform="translate(481.501,-335.688)">
+                       <title>Rectangle.62</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow7-26" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape8-30" v:mID="8" v:groupContext="shape" transform="translate(515.251,-335.688)">
+                       <title>Rectangle.63</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow8-31" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape9-35" v:mID="9" v:groupContext="shape" transform="translate(319.501,-313.188)">
+                       <title>Rectangle.64</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow9-36" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape10-40" v:mID="10" v:groupContext="shape" transform="translate(353.251,-313.188)">
+                       <title>Rectangle.65</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow10-41" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape11-45" v:mID="11" v:groupContext="shape" transform="translate(400.501,-313.188)">
+                       <title>Rectangle.66</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow11-46" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape12-50" v:mID="12" v:groupContext="shape" transform="translate(434.251,-313.188)">
+                       <title>Rectangle.67</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow12-51" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape13-55" v:mID="13" v:groupContext="shape" transform="translate(481.501,-313.188)">
+                       <title>Rectangle.68</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow13-56" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape14-60" v:mID="14" v:groupContext="shape" transform="translate(515.251,-313.188)">
+                       <title>Rectangle.69</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow14-61" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(319.501,-277.188)">
+                       <title>Rectangle.70</title>
+                       <desc>Key x</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow15-66" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>           </g>
+               <g id="shape16-71" v:mID="16" v:groupContext="shape" transform="translate(353.251,-277.188)">
+                       <title>Rectangle.71</title>
+                       <desc>Action x</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow16-72" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>                </g>
+               <g id="shape17-77" v:mID="17" v:groupContext="shape" transform="translate(400.501,-277.188)">
+                       <title>Rectangle.72</title>
+                       <desc>Key y</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow17-78" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>           </g>
+               <g id="shape18-83" v:mID="18" v:groupContext="shape" transform="translate(434.251,-277.188)">
+                       <title>Rectangle.73</title>
+                       <desc>Action y</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow18-84" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>                </g>
+               <g id="shape19-89" v:mID="19" v:groupContext="shape" transform="translate(481.501,-277.188)">
+                       <title>Rectangle.74</title>
+                       <desc>Key z</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow19-90" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>            </g>
+               <g id="shape20-95" v:mID="20" v:groupContext="shape" transform="translate(515.251,-277.188)">
+                       <title>Rectangle.75</title>
+                       <desc>Action z</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow20-96" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>                </g>
+               <g id="shape21-101" v:mID="21" v:groupContext="shape" transform="translate(319.501,-240.687)">
+                       <title>Rectangle.76</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow21-102" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape22-106" v:mID="22" v:groupContext="shape" transform="translate(353.251,-240.687)">
+                       <title>Rectangle.77</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow22-107" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape23-111" v:mID="23" v:groupContext="shape" transform="translate(400.501,-240.687)">
+                       <title>Rectangle.78</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow23-112" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape24-116" v:mID="24" v:groupContext="shape" transform="translate(434.251,-240.687)">
+                       <title>Rectangle.79</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow24-117" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape25-121" v:mID="25" v:groupContext="shape" transform="translate(481.501,-240.687)">
+                       <title>Rectangle.80</title>
+                       <desc>Key N</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow25-122" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>           </g>
+               <g id="shape26-127" v:mID="26" v:groupContext="shape" transform="translate(515.251,-240.687)">
+                       <title>Rectangle.81</title>
+                       <desc>Action N</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow26-128" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>                </g>
+               <g id="shape27-133" v:mID="27" v:groupContext="shape" transform="translate(317.251,-231.687)">
+                       <title>Rectangle.82</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow27-134" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+                       </g>
+                       <rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+               </g>
+               <g id="shape28-138" v:mID="28" v:groupContext="shape" transform="translate(328.501,-362.688)">
+                       <title>Sheet.28</title>
+                       <desc>Local Table for N Specific Flows Serviced at Node 1</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+                       <rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+                       <text x="5.77" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node 1</text>             </g>
+               <g id="group34-141" transform="translate(66.0294,-165.569)" v:mID="34" v:groupContext="group">
+                       <v:custProps>
+                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Device)"/>
+                               <v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Load balancer)"/>
+                       </v:custProps>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+                       </v:userDefs>
+                       <title>Load balancer</title>
+                       <g id="shape35-142" v:mID="35" v:groupContext="shape" transform="translate(0,-7.33146)">
+                               <title>Sheet.35</title>
+                               <g id="shadow35-143" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st10"/>
+                                       <path d="M0 377.86 L72 377.86" class="st6"/>
+                                       <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st6"/>
+                               </g>
+                               <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23 Z" class="st11"/>
+                               <path d="M0 377.86 L72 377.86" class="st12"/>
+                               <path d="M54 367.23 L18 367.23 L0 377.86 L0 424.56 L72 424.56 L72 377.86 L54 367.23" class="st12"/>
+                       </g>
+                       <g id="shape36-152" v:mID="36" v:groupContext="shape" transform="translate(8.03054,-12.9324)">
+                               <title>Sheet.36</title>
+                               <g id="shadow36-153" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97
+                                                                413.34 L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83
+                                                                L51.34 410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37
+                                                                L38.13 400.48 L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16
+                                                                L46.49 392.01 ZM27.71 397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42
+                                                                27.71 415.42 C32.75 415.42 36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71
+                                                                400.04 C31.15 400.04 33.96 402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54
+                                                                21.46 409.74 21.46 406.29 C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56
+                                                                L11.6 407.56 L8.62 410.51 L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z"
+                                                       class="st2"/>
+                               </g>
+                               <path d="M45.34 421.81 L41.2 422.66 L44.12 424.56 L49.68 423.16 L48.75 417.51 L45.8 415.59 L46.69 419.68 L36.97 413.34
+                                                        L35.6 415.45 L45.34 421.81 ZM50.83 405.36 L39.2 405.36 L39.2 407.88 L50.8 407.88 L47.82 410.83 L51.34
+                                                        410.83 L55.21 406.61 L51.32 402.39 L47.83 402.39 L50.83 405.36 ZM46.49 392.01 L36.75 398.37 L38.13 400.48
+                                                        L47.84 394.14 L46.96 398.23 L49.91 396.31 L50.84 390.66 L45.28 389.26 L42.36 391.16 L46.49 392.01 ZM27.71
+                                                        397.16 C22.66 397.16 18.58 401.25 18.58 406.29 C18.58 411.33 22.66 415.42 27.71 415.42 C32.75 415.42
+                                                        36.84 411.33 36.84 406.29 C36.84 401.25 32.75 397.16 27.71 397.16 ZM27.71 400.04 C31.15 400.04 33.96
+                                                        402.84 33.96 406.29 C33.96 409.74 31.15 412.54 27.71 412.54 C24.26 412.54 21.46 409.74 21.46 406.29
+                                                        C21.46 402.84 24.26 400.04 27.71 400.04 ZM11.64 405.04 L0 405.04 L0 407.56 L11.6 407.56 L8.62 410.51
+                                                        L12.14 410.51 L16.01 406.29 L12.12 402.07 L8.64 402.07 L11.64 405.04 Z" class="st13"/>
+                       </g>
+               </g>
+               <g id="shape37-157" v:mID="37" v:groupContext="shape" transform="translate(21.0294,-45.4375)">
+                       <title>Rectangle</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow37-158" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="336.433" width="135" height="88.1315" class="st2"/>
+                       </g>
+                       <rect x="0" y="336.433" width="135" height="88.1315" class="st3"/>
+               </g>
+               <g id="shape38-162" v:mID="38" v:groupContext="shape" transform="translate(34.693,-126.438)">
+                       <title>Sheet.38</title>
+                       <desc>EFD Table</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="49.3364" cy="415.565" width="98.68" height="18"/>
+                       <rect x="0" y="406.565" width="98.6728" height="18" class="st8"/>
+                       <text x="24.87" y="419.17" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>EFD Table</text>             </g>
+               <g id="shape39-165" v:mID="39" v:groupContext="shape" transform="translate(30.0294,-99.4375)">
+                       <title>Rectangle.39</title>
+                       <desc>Group_id</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+                       <g id="shadow39-166" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+                       <text x="7.87" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Group_id</text>                </g>
+               <g id="shape40-171" v:mID="40" v:groupContext="shape" transform="translate(93.193,-99.4375)">
+                       <title>Rectangle.40</title>
+                       <desc>Hash index</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="26.9182" cy="415.565" width="53.84" height="18"/>
+                       <g id="shadow40-172" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="53.8364" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="53.8364" height="18" class="st15"/>
+                       <text x="4.64" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Hash index</text>              </g>
+               <g id="shape41-177" v:mID="41" v:groupContext="shape" transform="translate(30.193,-82.4275)">
+                       <title>Rectangle.41</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow41-178" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+                       </g>
+                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+               </g>
+               <g id="shape42-182" v:mID="42" v:groupContext="shape" transform="translate(30.193,-66.8125)">
+                       <title>Rectangle.42</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow42-183" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+                       </g>
+                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+               </g>
+               <g id="shape43-187" v:mID="43" v:groupContext="shape" transform="translate(30.1112,-52.1875)">
+                       <title>Rectangle.43</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow43-188" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+                       </g>
+                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+               </g>
+               <g id="shape44-192" v:mID="44" v:groupContext="shape" transform="translate(93.0294,-81.4375)">
+                       <title>Rectangle.44</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow44-193" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+                       </g>
+                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+               </g>
+               <g id="shape45-197" v:mID="45" v:groupContext="shape" transform="translate(93.193,-66.8125)">
+                       <title>Rectangle.45</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow45-198" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+                       </g>
+                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+               </g>
+               <g id="shape46-202" v:mID="46" v:groupContext="shape" transform="translate(93.193,-52.1875)">
+                       <title>Rectangle.46</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow46-203" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="413.315" width="53.8364" height="11.25" class="st2"/>
+                       </g>
+                       <rect x="0" y="413.315" width="53.8364" height="11.25" class="st16"/>
+               </g>
+               <g id="shape47-207" v:mID="47" v:groupContext="shape" transform="translate(374.924,544.022) rotate(135)">
+                       <title>Sheet.47</title>
+                       <path d="M-0 417.75 A40.674 18.0151 -156.2 0 0 40.24 422.15 L40.49 421.89" class="st17"/>
+               </g>
+               <g id="shape48-213" v:mID="48" v:groupContext="shape" transform="translate(21.0294,-19)">
+                       <title>Sheet.48</title>
+                       <desc>Supports X*N Flows</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+                       <rect x="0" y="406.565" width="135" height="18" class="st19"/>
+                       <text x="19.05" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports X*N Flows</text>            </g>
+               <g id="shape49-216" v:mID="49" v:groupContext="shape" transform="translate(48.0294,-229.938)">
+                       <title>Sheet.49</title>
+                       <desc>Frontend Server or Load Balancer</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="54" cy="400.94" width="108" height="47.25"/>
+                       <rect x="0" y="377.315" width="108" height="47.25" class="st21"/>
+                       <text x="14.56" y="397.34" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Frontend Server<v:newlineChar/><tspan
+                                               x="13.16" dy="1.2em" class="st22">or Load Balancer </tspan> </text>             </g>
+               <g id="group51-220" transform="translate(223.876,-310.938)" v:mID="51" v:groupContext="group">
+                       <v:custProps>
+                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+                               <v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                       </v:custProps>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+                       </v:userDefs>
+                       <title>Server</title>
+                       <g id="shape52-221" v:mID="52" v:groupContext="shape" transform="translate(13.0183,0)">
+                               <title>Sheet.52</title>
+                               <g id="shadow52-222" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+                               </g>
+                               <rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+                       </g>
+                       <g id="shape53-226" v:mID="53" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+                               <title>Sheet.53</title>
+                               <g id="shadow53-227" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+                               </g>
+                               <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+                       </g>
+                       <g id="shape54-231" v:mID="54" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+                               <title>Sheet.54</title>
+                               <v:userDefs>
+                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+                               </v:userDefs>
+                               <g id="shadow54-232" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+                                                                L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+                                                       class="st10"/>
+                               </g>
+                               <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+                                                        417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+                       </g>
+               </g>
+               <g id="shape59-239" v:mID="59" v:groupContext="shape" transform="translate(277.876,-373.938)">
+                       <title>Sheet.59</title>
+                       <path d="M-0 424.56 A111.108 53.2538 42.31 0 1 93.83 421.21 L94.14 421.41" class="st17"/>
+               </g>
+               <g id="shape60-244" v:mID="60" v:groupContext="shape" transform="translate(205.876,-283.938)">
+                       <title>Sheet.60</title>
+                       <desc>Backend Server 1</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+                       <rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+                       <text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 1</text>              </g>
+               <g id="group61-247" transform="translate(223.876,-207.438)" v:mID="61" v:groupContext="group">
+                       <v:custProps>
+                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+                               <v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                       </v:custProps>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+                       </v:userDefs>
+                       <title>Server.61</title>
+                       <g id="shape62-248" v:mID="62" v:groupContext="shape" transform="translate(13.0183,0)">
+                               <title>Sheet.62</title>
+                               <g id="shadow62-249" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+                               </g>
+                               <rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+                       </g>
+                       <g id="shape63-253" v:mID="63" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+                               <title>Sheet.63</title>
+                               <g id="shadow63-254" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+                               </g>
+                               <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+                       </g>
+                       <g id="shape64-258" v:mID="64" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+                               <title>Sheet.64</title>
+                               <v:userDefs>
+                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+                               </v:userDefs>
+                               <g id="shadow64-259" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+                                                                L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+                                                       class="st10"/>
+                               </g>
+                               <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+                                                        417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+                       </g>
+               </g>
+               <g id="shape65-266" v:mID="65" v:groupContext="shape" transform="translate(205.876,-180.437)">
+                       <title>Sheet.65</title>
+                       <desc>Backend Server 2</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+                       <rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+                       <text x="11.93" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server 2</text>              </g>
+               <g id="group66-269" transform="translate(219.029,-58.9375)" v:mID="66" v:groupContext="group">
+                       <v:custProps>
+                               <v:cp v:nameU="AssetNumber" v:lbl="Asset Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SerialNumber" v:lbl="Serial Number" v:prompt="" v:type="0" v:format="" v:sortKey="Asset"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Location" v:lbl="Location" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Building" v:lbl="Building" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Room" v:lbl="Room" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Manufacturer" v:lbl="Manufacturer" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductNumber" v:lbl="Product Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="PartNumber" v:lbl="Part Number" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ProductDescription" v:lbl="Product Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Equipment" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkName" v:lbl="Network Name" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="IPAddress" v:lbl="IP Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="SubnetMask" v:lbl="Subnet Mask" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="AdminInterface" v:lbl="Administrative Interface" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NumberofPorts" v:lbl="Number of Ports" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CommunityString" v:lbl="Community String" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="NetworkDescription" v:lbl="Network Description" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Network" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="MACAddress" v:lbl="MAC Address" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Memory" v:lbl="Memory" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="OperatingSystem" v:lbl="Operating System" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
+                                               v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="HardDriveSize" v:lbl="Hard Drive Capacity" v:prompt="" v:type="0" v:format=""
+                                               v:sortKey="Workstation" v:invis="false" v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="Department" v:lbl="Department" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                               <v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Equipment)"/>
+                               <v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0" v:val="VT4(Server)"/>
+                               <v:cp v:nameU="BelongsTo" v:lbl="Belongs To" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
+                                               v:ask="false" v:langID="1033" v:cal="0"/>
+                       </v:custProps>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
+                               <v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
+                               <v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
+                               <v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
+                       </v:userDefs>
+                       <title>Server.66</title>
+                       <g id="shape67-270" v:mID="67" v:groupContext="shape" transform="translate(13.0183,0)">
+                               <title>Sheet.67</title>
+                               <g id="shadow67-271" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <rect x="0" y="352.565" width="45.9634" height="72" class="st2"/>
+                               </g>
+                               <rect x="0" y="352.565" width="45.9634" height="72" class="st3"/>
+                       </g>
+                       <g id="shape68-275" v:mID="68" v:groupContext="shape" transform="translate(47.371,-30.7354)">
+                               <title>Sheet.68</title>
+                               <g id="shadow68-276" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st2"/>
+                               </g>
+                               <ellipse cx="2.77848" cy="421.786" rx="2.77848" ry="2.77848" class="st13"/>
+                       </g>
+                       <g id="shape69-280" v:mID="69" v:groupContext="shape" transform="translate(30.51,-11.8022)">
+                               <title>Sheet.69</title>
+                               <v:userDefs>
+                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
+                                       <v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
+                               </v:userDefs>
+                               <g id="shadow69-281" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                               transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                                       <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31
+                                                                L-0 417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z"
+                                                       class="st10"/>
+                               </g>
+                               <path d="M-0 424.56 L22.42 424.56 L22.42 422.76 L-0 422.76 L-0 424.56 ZM-0 419.11 L22.42 419.11 L22.42 417.31 L-0
+                                                        417.31 L-0 419.11 ZM-0 413.65 L22.42 413.65 L22.42 411.84 L-0 411.84 L-0 413.65 Z" class="st23"/>
+                       </g>
+               </g>
+               <g id="shape70-288" v:mID="70" v:groupContext="shape" transform="translate(201.029,-26.056)">
+                       <title>Sheet.70</title>
+                       <desc>Backend Server X</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="54" cy="408.124" width="108" height="32.8815"/>
+                       <rect x="0" y="391.683" width="108" height="32.8815" class="st21"/>
+                       <text x="11.86" y="411.72" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Backend Server X</text>              </g>
+               <g id="shape71-291" v:mID="71" v:groupContext="shape" transform="translate(684.44,239.627) rotate(90)">
+                       <title>Sheet.71</title>
+                       <path d="M0 424.56 L45 424.56" class="st24"/>
+               </g>
+               <g id="shape72-294" v:mID="72" v:groupContext="shape" transform="translate(6.85967,-22.443) rotate(-38.1076)">
+                       <title>Sheet.72</title>
+                       <path d="M-0 424.56 A96.1331 44.4001 55.03 0 1 68.24 420.56 L68.51 420.79" class="st17"/>
+               </g>
+               <g id="shape73-299" v:mID="73" v:groupContext="shape" transform="translate(328.501,-135.937)">
+                       <title>Rectangle.73</title>
+                       <desc>Key 1</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow73-300" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 1</text>           </g>
+               <g id="shape74-305" v:mID="74" v:groupContext="shape" transform="translate(362.251,-135.937)">
+                       <title>Rectangle.74</title>
+                       <desc>Action 1</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow74-306" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 1</text>                </g>
+               <g id="shape75-311" v:mID="75" v:groupContext="shape" transform="translate(409.501,-135.937)">
+                       <title>Rectangle.75</title>
+                       <desc>Key 2</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow75-312" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="4.74" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key 2</text>           </g>
+               <g id="shape76-317" v:mID="76" v:groupContext="shape" transform="translate(443.251,-135.937)">
+                       <title>Rectangle.76</title>
+                       <desc>Action 2</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow76-318" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.62" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action 2</text>                </g>
+               <g id="shape77-323" v:mID="77" v:groupContext="shape" transform="translate(490.501,-135.937)">
+                       <title>Rectangle.77</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow77-324" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape78-328" v:mID="78" v:groupContext="shape" transform="translate(524.251,-135.937)">
+                       <title>Rectangle.78</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow78-329" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape79-333" v:mID="79" v:groupContext="shape" transform="translate(328.501,-113.437)">
+                       <title>Rectangle.79</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow79-334" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape80-338" v:mID="80" v:groupContext="shape" transform="translate(362.251,-113.437)">
+                       <title>Rectangle.80</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow80-339" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape81-343" v:mID="81" v:groupContext="shape" transform="translate(409.501,-113.437)">
+                       <title>Rectangle.81</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow81-344" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape82-348" v:mID="82" v:groupContext="shape" transform="translate(443.251,-113.437)">
+                       <title>Rectangle.82</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow82-349" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape83-353" v:mID="83" v:groupContext="shape" transform="translate(490.501,-113.437)">
+                       <title>Rectangle.83</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow83-354" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape84-358" v:mID="84" v:groupContext="shape" transform="translate(524.251,-113.437)">
+                       <title>Rectangle.84</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow84-359" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape85-363" v:mID="85" v:groupContext="shape" transform="translate(328.501,-77.4375)">
+                       <title>Rectangle.85</title>
+                       <desc>Key x</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow85-364" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.11" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key x</text>           </g>
+               <g id="shape86-369" v:mID="86" v:groupContext="shape" transform="translate(362.251,-77.4375)">
+                       <title>Rectangle.86</title>
+                       <desc>Action x</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow86-370" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.99" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action x</text>                </g>
+               <g id="shape87-375" v:mID="87" v:groupContext="shape" transform="translate(409.501,-77.4375)">
+                       <title>Rectangle.87</title>
+                       <desc>Key y</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow87-376" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.01" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key y</text>           </g>
+               <g id="shape88-381" v:mID="88" v:groupContext="shape" transform="translate(443.251,-77.4375)">
+                       <title>Rectangle.88</title>
+                       <desc>Action y</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow88-382" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="4.89" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action y</text>                </g>
+               <g id="shape89-387" v:mID="89" v:groupContext="shape" transform="translate(490.501,-77.4375)">
+                       <title>Rectangle.89</title>
+                       <desc>Key z</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow89-388" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.3" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key z</text>            </g>
+               <g id="shape90-393" v:mID="90" v:groupContext="shape" transform="translate(524.251,-77.4375)">
+                       <title>Rectangle.90</title>
+                       <desc>Action z</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow90-394" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="5.18" y="418.56" class="st4" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action z</text>                </g>
+               <g id="shape91-399" v:mID="91" v:groupContext="shape" transform="translate(328.501,-40.9375)">
+                       <title>Rectangle.91</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow91-400" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape92-404" v:mID="92" v:groupContext="shape" transform="translate(362.251,-40.9375)">
+                       <title>Rectangle.92</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow92-405" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape93-409" v:mID="93" v:groupContext="shape" transform="translate(409.501,-40.9375)">
+                       <title>Rectangle.93</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow93-410" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+               </g>
+               <g id="shape94-414" v:mID="94" v:groupContext="shape" transform="translate(443.251,-40.9375)">
+                       <title>Rectangle.94</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow94-415" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+               </g>
+               <g id="shape95-419" v:mID="95" v:groupContext="shape" transform="translate(490.501,-40.9375)">
+                       <title>Rectangle.95</title>
+                       <desc>Key N</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="15.75" cy="415.565" width="31.5" height="18"/>
+                       <g id="shadow95-420" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="31.5" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="31.5" height="18" class="st3"/>
+                       <text x="5.21" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Key N</text>           </g>
+               <g id="shape96-425" v:mID="96" v:groupContext="shape" transform="translate(524.251,-40.9375)">
+                       <title>Rectangle.96</title>
+                       <desc>Action N</desc>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="21.375" cy="415.565" width="42.75" height="18"/>
+                       <g id="shadow96-426" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="406.565" width="42.75" height="18" class="st2"/>
+                       </g>
+                       <rect x="0" y="406.565" width="42.75" height="18" class="st3"/>
+                       <text x="5.67" y="418.26" class="st5" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Action N</text>                </g>
+               <g id="shape97-431" v:mID="97" v:groupContext="shape" transform="translate(326.251,-31.9375)">
+                       <title>Rectangle.97</title>
+                       <v:userDefs>
+                               <v:ud v:nameU="visVersion" v:val="VT0(15):26"/>
+                       </v:userDefs>
+                       <g id="shadow97-432" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
+                                       transform="matrix(1,0,0,1,0.345598,1.97279)" class="st1">
+                               <rect x="0" y="289.065" width="245.25" height="135.5" class="st6"/>
+                       </g>
+                       <rect x="0" y="289.065" width="245.25" height="135.5" class="st7"/>
+               </g>
+               <g id="shape98-436" v:mID="98" v:groupContext="shape" transform="translate(337.501,-162.938)">
+                       <title>Sheet.98</title>
+                       <desc>Local Table for N Specific Flows Serviced at Node X</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="110.423" cy="418.94" width="220.85" height="11.25"/>
+                       <rect x="0" y="413.315" width="220.846" height="11.25" class="st8"/>
+                       <text x="5.55" y="421.94" class="st9" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Local Table for N Specific Flows Serviced at Node X</text>             </g>
+               <g id="shape99-439" v:mID="99" v:groupContext="shape" transform="translate(-204.342,-29.4449) rotate(-53.7462)">
+                       <title>Sheet.99</title>
+                       <path d="M0 424.56 L160.37 424.56" class="st25"/>
+               </g>
+               <g id="shape100-445" v:mID="100" v:groupContext="shape" transform="translate(-37.6568,-164.882) rotate(-24.444)">
+                       <title>Sheet.100</title>
+                       <path d="M0 424.56 L101.71 424.56" class="st25"/>
+               </g>
+               <g id="shape101-450" v:mID="101" v:groupContext="shape" transform="translate(464.049,-50.8578) rotate(50.099)">
+                       <title>Sheet.101</title>
+                       <path d="M0 424.56 L139.8 424.56" class="st25"/>
+               </g>
+               <g id="shape102-455" v:mID="102" v:groupContext="shape" transform="translate(372.376,-207.438)">
+                       <title>Sheet.102</title>
+                       <desc>Supports N Flows</desc>
+                       <v:textBlock v:margins="rect(4,4,4,4)"/>
+                       <v:textRect cx="67.5" cy="415.565" width="135" height="18"/>
+                       <rect x="0" y="406.565" width="135" height="18" class="st19"/>
+                       <text x="25.15" y="419.17" class="st20" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Supports N Flows</text>              </g>
+       </g>
+</svg>
index 260f6a5..99ad8ea 100644 (file)
@@ -57,7 +57,7 @@ Sample Applications User Guides
     l3_forward_virtual
     link_status_intr
     load_balancer
-    flow_distributor
+    server_node_efd
     multi_process
     qos_metering
     qos_scheduler
@@ -133,6 +133,8 @@ Sample Applications User Guides
 
 :numref:`figure_ptpclient_highlevel` :ref:`figure_ptpclient_highlevel`
 
+:numref:`figure_efd_sample_app_overview` :ref:`figure_efd_sample_app_overview`
+
 **Tables**
 
 :numref:`table_qos_metering_1` :ref:`table_qos_metering_1`
diff --git a/doc/guides/sample_app_ug/server_node_efd.rst b/doc/guides/sample_app_ug/server_node_efd.rst
new file mode 100644 (file)
index 0000000..9b69cfe
--- /dev/null
@@ -0,0 +1,494 @@
+..  BSD LICENSE
+    Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+    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.
+    * Neither the name of Intel Corporation nor the names of its
+    contributors may 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.
+
+Server-Node EFD Sample Application
+==================================
+
+This sample application demonstrates the use of EFD library as a flow-level
+load balancer, for more information about the EFD Library please refer to the
+DPDK programmer's guide.
+
+This sample application is a variant of the
+:ref:`client-server sample application <multi_process_app>`
+where a specific target node is specified for every and each flow
+(not in a round-robin fashion as the original load balancing sample application).
+
+Overview
+--------
+
+The architecture of the EFD flow-based load balancer sample application is
+presented in the following figure.
+
+.. _figure_efd_sample_app_overview:
+
+.. figure:: img/server_node_efd.*
+
+   Using EFD as a Flow-Level Load Balancer
+
+As shown in :numref:`figure_efd_sample_app_overview`,
+the sample application consists of a front-end node (server)
+using the EFD library to create a load-balancing table for flows,
+for each flow a target backend worker node is specified. The EFD table does not
+store the flow key (unlike a regular hash table), and hence, it can
+individually load-balance millions of flows (number of targets * maximum number
+of flows fit in a flow table per target) while still fitting in CPU cache.
+
+It should be noted that although they are referred to as nodes, the frontend
+server and worker nodes are processes running on the same platform.
+
+Front-end Server
+~~~~~~~~~~~~~~~~
+
+Upon initializing, the frontend server node (process) creates a flow
+distributor table (based on the EFD library) which is populated with flow
+information and its intended target node.
+
+The sample application assigns a specific target node_id (process) for each of
+the IP destination addresses as follows:
+
+.. code-block:: c
+
+    node_id = i % num_nodes; /* Target node id is generated */
+    ip_dst = rte_cpu_to_be_32(i); /* Specific ip destination address is
+                                     assigned to this target node */
+
+then the pair of <key,target> is inserted into the flow distribution table.
+
+The main loop of the server process receives a burst of packets, then for
+each packet, a flow key (IP destination address) is extracted. The flow
+distributor table is looked up and the target node id is returned.  Packets are
+then enqueued to the specified target node id.
+
+It should be noted that flow distributor table is not a membership test table.
+I.e. if the key has already been inserted the target node id will be correct,
+but for new keys the flow distributor table will return a value (which can be
+valid).
+
+Backend Worker Nodes
+~~~~~~~~~~~~~~~~~~~~
+
+Upon initializing, the worker node (process) creates a flow table (a regular
+hash table that stores the key default size 1M flows) which is populated with
+only the flow information that is serviced at this node. This flow key is
+essential to point out new keys that have not been inserted before.
+
+The worker node's main loop is simply receiving packets then doing a hash table
+lookup. If a match occurs then statistics are updated for flows serviced by
+this node. If no match is found in the local hash table then this indicates
+that this is a new flow, which is dropped.
+
+
+Compiling the Application
+-------------------------
+
+The sequence of steps used to build the application is:
+
+#.  Export the required environment variables:
+
+    .. code-block:: console
+
+        export RTE_SDK=/path/to/rte_sdk
+        export RTE_TARGET=x86_64-native-linuxapp-gcc
+
+#.  Build the application executable file:
+
+    .. code-block:: console
+
+        cd ${RTE_SDK}/examples/server_node_efd/
+        make
+
+    For more details on how to build the DPDK libraries and sample
+    applications,
+    please refer to the *DPDK Getting Started Guide.*
+
+
+Running the Application
+-----------------------
+
+The application has two binaries to be run: the front-end server
+and the back-end node.
+
+The frontend server (server) has the following command line options::
+
+    ./server [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS
+
+Where,
+
+* ``-p PORTMASK:`` Hexadecimal bitmask of ports to configure
+* ``-n NUM_NODES:`` Number of back-end nodes that will be used
+* ``-f NUM_FLOWS:`` Number of flows to be added in the EFD table (1 million, by default)
+
+The back-end node (node) has the following command line options::
+
+    ./node [EAL options] -- -n NODE_ID
+
+Where,
+
+* ``-n NODE_ID:`` Node ID, which cannot be equal or higher than NUM_MODES
+
+
+First, the server app must be launched, with the number of nodes that will be run.
+Once it has been started, the node instances can be run, with different NODE_ID.
+These instances have to be run as secondary processes, with ``--proc-type=secondary``
+in the EAL options, which will attach to the primary process memory, and therefore,
+they can access the queues created by the primary process to distribute packets.
+
+To successfully run the application, the command line used to start the
+application has to be in sync with the traffic flows configured on the traffic
+generator side.
+
+For examples of application command lines and traffic generator flows, please
+refer to the DPDK Test Report. For more details on how to set up and run the
+sample applications provided with DPDK package, please refer to the
+:ref:`DPDK Getting Started Guide for Linux <linux_gsg>` and
+:ref:`DPDK Getting Started Guide for FreeBSD <freebsd_gsg>`.
+
+
+Explanation
+-----------
+
+As described in previous sections, there are two processes in this example.
+
+The first process, the front-end server, creates and populates the EFD table,
+which is used to distribute packets to nodes, which the number of flows
+specified in the command line (1 million, by default).
+
+
+.. code-block:: c
+
+    static void
+    create_efd_table(void)
+    {
+        uint8_t socket_id = rte_socket_id();
+
+        /* create table */
+        efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
+                        1 << socket_id, socket_id);
+
+        if (efd_table == NULL)
+            rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
+    }
+
+    static void
+    populate_efd_table(void)
+    {
+        unsigned int i;
+        int32_t ret;
+        uint32_t ip_dst;
+        uint8_t socket_id = rte_socket_id();
+        uint64_t node_id;
+
+        /* Add flows in table */
+        for (i = 0; i < num_flows; i++) {
+            node_id = i % num_nodes;
+
+            ip_dst = rte_cpu_to_be_32(i);
+            ret = rte_efd_update(efd_table, socket_id,
+                            (void *)&ip_dst, (efd_value_t)node_id);
+            if (ret < 0)
+                rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
+                                    "EFD table\n", i);
+        }
+
+        printf("EFD table: Adding 0x%x keys\n", num_flows);
+    }
+
+After initialization, packets are received from the enabled ports, and the IPv4
+address from the packets is used as a key to look up in the EFD table,
+which tells the node where the packet has to be distributed.
+
+.. code-block:: c
+
+    static void
+    process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
+            uint16_t rx_count, unsigned int socket_id)
+    {
+        uint16_t i;
+        uint8_t node;
+        efd_value_t data[EFD_BURST_MAX];
+        const void *key_ptrs[EFD_BURST_MAX];
+
+        struct ipv4_hdr *ipv4_hdr;
+        uint32_t ipv4_dst_ip[EFD_BURST_MAX];
+
+        for (i = 0; i < rx_count; i++) {
+            /* Handle IPv4 header.*/
+            ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
+                    sizeof(struct ether_hdr));
+            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+            key_ptrs[i] = (void *)&ipv4_dst_ip[i];
+        }
+
+        rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
+                    (const void **) key_ptrs, data);
+        for (i = 0; i < rx_count; i++) {
+            node = (uint8_t) ((uintptr_t)data[i]);
+
+            if (node >= num_nodes) {
+                /*
+                 * Node is out of range, which means that
+                 * flow has not been inserted
+                 */
+                flow_dist_stats.drop++;
+                rte_pktmbuf_free(pkts[i]);
+            } else {
+                flow_dist_stats.distributed++;
+                enqueue_rx_packet(node, pkts[i]);
+            }
+        }
+
+        for (i = 0; i < num_nodes; i++)
+            flush_rx_queue(i);
+    }
+
+The burst of packets received is enqueued in temporary buffers (per node),
+and enqueued in the shared ring between the server and the node.
+After this, a new burst of packets is received and this process is
+repeated infinitely.
+
+.. code-block:: c
+
+    static void
+    flush_rx_queue(uint16_t node)
+    {
+        uint16_t j;
+        struct node *cl;
+
+        if (cl_rx_buf[node].count == 0)
+            return;
+
+        cl = &nodes[node];
+        if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
+                cl_rx_buf[node].count) != 0){
+            for (j = 0; j < cl_rx_buf[node].count; j++)
+                rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
+            cl->stats.rx_drop += cl_rx_buf[node].count;
+        } else
+            cl->stats.rx += cl_rx_buf[node].count;
+
+        cl_rx_buf[node].count = 0;
+    }
+
+The second process, the back-end node, receives the packets from the shared
+ring with the server and send them out, if they belong to the node.
+
+At initialization, it attaches to the server process memory, to have
+access to the shared ring, parameters and statistics.
+
+.. code-block:: c
+
+    rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
+    if (rx_ring == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
+                "is server process running?\n");
+
+    mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+    if (mp == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+    mz = rte_memzone_lookup(MZ_SHARED_INFO);
+    if (mz == NULL)
+        rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
+    info = mz->addr;
+    tx_stats = &(info->tx_stats[node_id]);
+    filter_stats = &(info->filter_stats[node_id]);
+
+Then, the hash table that contains the flows that will be handled
+by the node is created and populated.
+
+.. code-block:: c
+
+    static struct rte_hash *
+    create_hash_table(const struct shared_info *info)
+    {
+        uint32_t num_flows_node = info->num_flows / info->num_nodes;
+        char name[RTE_HASH_NAMESIZE];
+        struct rte_hash *h;
+
+        /* create table */
+        struct rte_hash_parameters hash_params = {
+            .entries = num_flows_node * 2, /* table load = 50% */
+            .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
+            .socket_id = rte_socket_id(),
+            .hash_func_init_val = 0,
+        };
+
+        snprintf(name, sizeof(name), "hash_table_%d", node_id);
+        hash_params.name = name;
+        h = rte_hash_create(&hash_params);
+
+        if (h == NULL)
+            rte_exit(EXIT_FAILURE,
+                    "Problem creating the hash table for node %d\n",
+                    node_id);
+        return h;
+    }
+
+    static void
+    populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
+    {
+        unsigned int i;
+        int32_t ret;
+        uint32_t ip_dst;
+        uint32_t num_flows_node = 0;
+        uint64_t target_node;
+
+        /* Add flows in table */
+        for (i = 0; i < info->num_flows; i++) {
+            target_node = i % info->num_nodes;
+            if (target_node != node_id)
+                continue;
+
+            ip_dst = rte_cpu_to_be_32(i);
+
+            ret = rte_hash_add_key(h, (void *) &ip_dst);
+            if (ret < 0)
+                rte_exit(EXIT_FAILURE, "Unable to add entry %u "
+                        "in hash table\n", i);
+            else
+                num_flows_node++;
+
+        }
+
+        printf("Hash table: Adding 0x%x keys\n", num_flows_node);
+    }
+
+After initialization, packets are dequeued from the shared ring
+(from the server) and, like in the server process,
+the IPv4 address from the packets is used as a key to look up in the hash table.
+If there is a hit, packet is stored in a buffer, to be eventually transmitted
+in one of the enabled ports. If key is not there, packet is dropped, since the
+flow is not handled by the node.
+
+.. code-block:: c
+
+    static inline void
+    handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
+    {
+        struct ipv4_hdr *ipv4_hdr;
+        uint32_t ipv4_dst_ip[PKT_READ_SIZE];
+        const void *key_ptrs[PKT_READ_SIZE];
+        unsigned int i;
+        int32_t positions[PKT_READ_SIZE] = {0};
+
+        for (i = 0; i < num_packets; i++) {
+            /* Handle IPv4 header.*/
+            ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
+                    sizeof(struct ether_hdr));
+            ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+            key_ptrs[i] = &ipv4_dst_ip[i];
+        }
+        /* Check if packets belongs to any flows handled by this node */
+        rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
+
+        for (i = 0; i < num_packets; i++) {
+            if (likely(positions[i] >= 0)) {
+                filter_stats->passed++;
+                transmit_packet(bufs[i]);
+            } else {
+                filter_stats->drop++;
+                /* Drop packet, as flow is not handled by this node */
+                rte_pktmbuf_free(bufs[i]);
+            }
+        }
+    }
+
+Finally, note that both processes updates statistics, such as transmitted, received
+and dropped packets, which are shown and refreshed by the server app.
+
+.. code-block:: c
+
+    static void
+    do_stats_display(void)
+    {
+        unsigned int i, j;
+        const char clr[] = {27, '[', '2', 'J', '\0'};
+        const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+        uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
+        uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
+
+        /* to get TX stats, we need to do some summing calculations */
+        memset(port_tx, 0, sizeof(port_tx));
+        memset(port_tx_drop, 0, sizeof(port_tx_drop));
+        memset(node_tx, 0, sizeof(node_tx));
+        memset(node_tx_drop, 0, sizeof(node_tx_drop));
+
+        for (i = 0; i < num_nodes; i++) {
+            const struct tx_stats *tx = &info->tx_stats[i];
+
+            for (j = 0; j < info->num_ports; j++) {
+                const uint64_t tx_val = tx->tx[info->id[j]];
+                const uint64_t drop_val = tx->tx_drop[info->id[j]];
+
+                port_tx[j] += tx_val;
+                port_tx_drop[j] += drop_val;
+                node_tx[i] += tx_val;
+                node_tx_drop[i] += drop_val;
+            }
+        }
+
+        /* Clear screen and move to top left */
+        printf("%s%s", clr, topLeft);
+
+        printf("PORTS\n");
+        printf("-----\n");
+        for (i = 0; i < info->num_ports; i++)
+            printf("Port %u: '%s'\t", (unsigned int)info->id[i],
+                    get_printable_mac_addr(info->id[i]));
+        printf("\n\n");
+        for (i = 0; i < info->num_ports; i++) {
+            printf("Port %u - rx: %9"PRIu64"\t"
+                    "tx: %9"PRIu64"\n",
+                    (unsigned int)info->id[i], info->rx_stats.rx[i],
+                    port_tx[i]);
+        }
+
+        printf("\nSERVER\n");
+        printf("-----\n");
+        printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
+                flow_dist_stats.distributed, flow_dist_stats.drop);
+
+        printf("\nNODES\n");
+        printf("-------\n");
+        for (i = 0; i < num_nodes; i++) {
+            const unsigned long long rx = nodes[i].stats.rx;
+            const unsigned long long rx_drop = nodes[i].stats.rx_drop;
+            const struct filter_stats *filter = &info->filter_stats[i];
+
+            printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
+                    "            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
+                    "            filter_passed: %9"PRIu64", "
+                    "filter_drop: %9"PRIu64"\n",
+                    i, rx, rx_drop, node_tx[i], node_tx_drop[i],
+                    filter->passed, filter->drop);
+        }
+
+        printf("\n");
+    }
index b404982..da2bfdd 100644 (file)
@@ -45,7 +45,7 @@ DIRS-y += dpdk_qat
 endif
 DIRS-y += ethtool
 DIRS-y += exception_path
-DIRS-$(CONFIG_RTE_LIBRTE_EFD) += flow_distributor
+DIRS-$(CONFIG_RTE_LIBRTE_EFD) += server_node_efd
 DIRS-y += helloworld
 DIRS-$(CONFIG_RTE_LIBRTE_PIPELINE) += ip_pipeline
 ifeq ($(CONFIG_RTE_LIBRTE_LPM),y)
diff --git a/examples/flow_distributor/Makefile b/examples/flow_distributor/Makefile
deleted file mode 100644 (file)
index 5bae706..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#   BSD LICENSE
-#
-#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
-#   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.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may 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 overridden by command line or environment
-RTE_TARGET ?= x86_64-native-linuxapp-gcc
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += distributor
-DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += node
-
-include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/flow_distributor/distributor/Makefile b/examples/flow_distributor/distributor/Makefile
deleted file mode 100644 (file)
index 8714151..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#   BSD LICENSE
-#
-#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
-#   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.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may 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 overridden by command line or environment
-RTE_TARGET ?= x86_64-native-linuxapp-gcc
-
-include $(RTE_SDK)/mk/rte.vars.mk
-
-ifneq ($(CONFIG_RTE_EXEC_ENV), "linuxapp")
-$(error This application can only operate in a linuxapp environment, \
-please change the definition of the RTE_TARGET environment variable)
-endif
-
-# binary name
-APP = distributor
-
-# all source are stored in SRCS-y
-SRCS-y := main.c init.c args.c
-
-INC := $(wildcard *.h)
-
-CFLAGS += $(WERROR_FLAGS) -O3
-CFLAGS += -I$(SRCDIR)/../shared
-
-include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/flow_distributor/distributor/args.c b/examples/flow_distributor/distributor/args.c
deleted file mode 100644 (file)
index ee29203..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <getopt.h>
-#include <stdarg.h>
-#include <errno.h>
-
-#include <rte_memory.h>
-#include <rte_string_fns.h>
-
-#include "common.h"
-#include "args.h"
-#include "init.h"
-
-/* 1M flows by default */
-#define DEFAULT_NUM_FLOWS    0x100000
-
-/* global var for number of nodes - extern in header */
-uint8_t num_nodes;
-/* global var for number of flows - extern in header */
-uint32_t num_flows = DEFAULT_NUM_FLOWS;
-
-static const char *progname;
-
-/**
- * Prints out usage information to stdout
- */
-static void
-usage(void)
-{
-       printf("%s [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS\n"
-               " -p PORTMASK: hexadecimal bitmask of ports to use\n"
-               " -n NUM_NODES: number of node processes to use\n"
-               " -f NUM_FLOWS: number of flows to be added in the EFD table\n",
-               progname);
-}
-
-/**
- * The ports to be used by the application are passed in
- * the form of a bitmask. This function parses the bitmask
- * and places the port numbers to be used into the port[]
- * array variable
- */
-static int
-parse_portmask(uint8_t max_ports, const char *portmask)
-{
-       char *end = NULL;
-       unsigned long pm;
-       uint8_t count = 0;
-
-       if (portmask == NULL || *portmask == '\0')
-               return -1;
-
-       /* convert parameter to a number and verify */
-       pm = strtoul(portmask, &end, 16);
-       if (end == NULL || *end != '\0' || pm == 0)
-               return -1;
-
-       /* loop through bits of the mask and mark ports */
-       while (pm != 0) {
-               if (pm & 0x01) { /* bit is set in mask, use port */
-                       if (count >= max_ports)
-                               printf("WARNING: requested port %u not present"
-                               " - ignoring\n", (unsigned int)count);
-                       else
-                           info->id[info->num_ports++] = count;
-               }
-               pm = (pm >> 1);
-               count++;
-       }
-
-       return 0;
-}
-
-/**
- * Take the number of nodes parameter passed to the app
- * and convert to a number to store in the num_nodes variable
- */
-static int
-parse_num_nodes(const char *nodes)
-{
-       char *end = NULL;
-       unsigned long temp;
-
-       if (nodes == NULL || *nodes == '\0')
-               return -1;
-
-       temp = strtoul(nodes, &end, 10);
-       if (end == NULL || *end != '\0' || temp == 0)
-               return -1;
-
-       num_nodes = (uint8_t)temp;
-       return 0;
-}
-
-static int
-parse_num_flows(const char *flows)
-{
-       char *end = NULL;
-
-       /* parse hexadecimal string */
-       num_flows = strtoul(flows, &end, 16);
-       if ((flows[0] == '\0') || (end == NULL) || (*end != '\0'))
-               return -1;
-
-       if (num_flows == 0)
-               return -1;
-
-       return 0;
-}
-
-/**
- * The application specific arguments follow the DPDK-specific
- * arguments which are stripped by the DPDK init. This function
- * processes these application arguments, printing usage info
- * on error.
- */
-int
-parse_app_args(uint8_t max_ports, int argc, char *argv[])
-{
-       int option_index, opt;
-       char **argvopt = argv;
-       static struct option lgopts[] = { /* no long options */
-               {NULL, 0, 0, 0 }
-       };
-       progname = argv[0];
-
-       while ((opt = getopt_long(argc, argvopt, "n:f:p:", lgopts,
-                       &option_index)) != EOF) {
-               switch (opt) {
-               case 'p':
-                       if (parse_portmask(max_ports, optarg) != 0) {
-                               usage();
-                               return -1;
-                       }
-                       break;
-               case 'n':
-                       if (parse_num_nodes(optarg) != 0) {
-                               usage();
-                               return -1;
-                       }
-                       break;
-               case 'f':
-                       if (parse_num_flows(optarg) != 0) {
-                               usage();
-                               return -1;
-                       }
-                       break;
-               default:
-                       printf("ERROR: Unknown option '%c'\n", opt);
-                       usage();
-                       return -1;
-               }
-       }
-
-       if (info->num_ports == 0 || num_nodes == 0) {
-               usage();
-               return -1;
-       }
-
-       if (info->num_ports % 2 != 0) {
-               printf("ERROR: application requires an even "
-                               "number of ports to use\n");
-               return -1;
-       }
-       return 0;
-}
diff --git a/examples/flow_distributor/distributor/args.h b/examples/flow_distributor/distributor/args.h
deleted file mode 100644 (file)
index cacf395..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#ifndef _ARGS_H_
-#define _ARGS_H_
-
-int parse_app_args(uint8_t max_ports, int argc, char *argv[]);
-
-#endif /* ifndef _ARGS_H_ */
diff --git a/examples/flow_distributor/distributor/init.c b/examples/flow_distributor/distributor/init.c
deleted file mode 100644 (file)
index 3b0aa85..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/queue.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <inttypes.h>
-
-#include <rte_common.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_byteorder.h>
-#include <rte_atomic.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_debug.h>
-#include <rte_ring.h>
-#include <rte_log.h>
-#include <rte_mempool.h>
-#include <rte_memcpy.h>
-#include <rte_mbuf.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_malloc.h>
-#include <rte_string_fns.h>
-#include <rte_cycles.h>
-#include <rte_efd.h>
-#include <rte_hash.h>
-
-#include "common.h"
-#include "args.h"
-#include "init.h"
-
-#define MBUFS_PER_NODE 1536
-#define MBUFS_PER_PORT 1536
-#define MBUF_CACHE_SIZE 512
-
-#define RTE_MP_RX_DESC_DEFAULT 512
-#define RTE_MP_TX_DESC_DEFAULT 512
-#define NODE_QUEUE_RINGSIZE 128
-
-#define NO_FLAGS 0
-
-/* The mbuf pool for packet rx */
-struct rte_mempool *pktmbuf_pool;
-
-/* array of info/queues for nodes */
-struct node *nodes;
-
-/* Flow distributor table */
-struct rte_efd_table *efd_table;
-
-/* Shared info between distributor and nodes */
-struct shared_info *info;
-
-/**
- * Initialise the mbuf pool for packet reception for the NIC, and any other
- * buffer pools needed by the app - currently none.
- */
-static int
-init_mbuf_pools(void)
-{
-       const unsigned int num_mbufs = (num_nodes * MBUFS_PER_NODE) +
-                       (info->num_ports * MBUFS_PER_PORT);
-
-       /*
-        * Don't pass single-producer/single-consumer flags to mbuf create as it
-        * seems faster to use a cache instead
-        */
-       printf("Creating mbuf pool '%s' [%u mbufs] ...\n",
-                       PKTMBUF_POOL_NAME, num_mbufs);
-       pktmbuf_pool = rte_pktmbuf_pool_create(PKTMBUF_POOL_NAME, num_mbufs,
-               MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
-
-       return pktmbuf_pool == NULL; /* 0  on success */
-}
-
-/**
- * Initialise an individual port:
- * - configure number of rx and tx rings
- * - set up each rx ring, to pull from the main mbuf pool
- * - set up each tx ring
- * - start the port and report its status to stdout
- */
-static int
-init_port(uint8_t port_num)
-{
-       /* for port configuration all features are off by default */
-       const struct rte_eth_conf port_conf = {
-               .rxmode = {
-                       .mq_mode = ETH_MQ_RX_RSS
-               }
-       };
-       const uint16_t rx_rings = 1, tx_rings = num_nodes;
-       const uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT;
-       const uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT;
-
-       uint16_t q;
-       int retval;
-
-       printf("Port %u init ... ", (unsigned int)port_num);
-       fflush(stdout);
-
-       /*
-        * Standard DPDK port initialisation - config port, then set up
-        * rx and tx rings.
-        */
-       retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, &port_conf);
-       if (retval != 0)
-               return retval;
-
-       for (q = 0; q < rx_rings; q++) {
-               retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size,
-                               rte_eth_dev_socket_id(port_num),
-                               NULL, pktmbuf_pool);
-               if (retval < 0)
-                       return retval;
-       }
-
-       for (q = 0; q < tx_rings; q++) {
-               retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size,
-                               rte_eth_dev_socket_id(port_num),
-                               NULL);
-               if (retval < 0)
-                       return retval;
-       }
-
-       rte_eth_promiscuous_enable(port_num);
-
-       retval = rte_eth_dev_start(port_num);
-       if (retval < 0)
-               return retval;
-
-       printf("done:\n");
-
-       return 0;
-}
-
-/**
- * Set up the DPDK rings which will be used to pass packets, via
- * pointers, between the multi-process distributor and node processes.
- * Each node needs one RX queue.
- */
-static int
-init_shm_rings(void)
-{
-       unsigned int i;
-       unsigned int socket_id;
-       const char *q_name;
-       const unsigned int ringsize = NODE_QUEUE_RINGSIZE;
-
-       nodes = rte_malloc("node details",
-               sizeof(*nodes) * num_nodes, 0);
-       if (nodes == NULL)
-               rte_exit(EXIT_FAILURE, "Cannot allocate memory for "
-                               "node program details\n");
-
-       for (i = 0; i < num_nodes; i++) {
-               /* Create an RX queue for each node */
-               socket_id = rte_socket_id();
-               q_name = get_rx_queue_name(i);
-               nodes[i].rx_q = rte_ring_create(q_name,
-                               ringsize, socket_id,
-                               RING_F_SP_ENQ | RING_F_SC_DEQ);
-               if (nodes[i].rx_q == NULL)
-                       rte_exit(EXIT_FAILURE, "Cannot create rx ring queue "
-                                       "for node %u\n", i);
-       }
-       return 0;
-}
-
-/*
- * Create flow distributor table which will contain all the flows
- * that will be distributed among the nodes
- */
-static void
-create_flow_distributor_table(void)
-{
-       uint8_t socket_id = rte_socket_id();
-
-       /* create table */
-       efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
-                       1 << socket_id, socket_id);
-
-       if (efd_table == NULL)
-               rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
-}
-
-static void
-populate_flow_distributor_table(void)
-{
-       unsigned int i;
-       int32_t ret;
-       uint32_t ip_dst;
-       uint8_t socket_id = rte_socket_id();
-       uint64_t node_id;
-
-       /* Add flows in table */
-       for (i = 0; i < num_flows; i++) {
-               node_id = i % num_nodes;
-
-               ip_dst = rte_cpu_to_be_32(i);
-               ret = rte_efd_update(efd_table, socket_id,
-                               (void *)&ip_dst, (efd_value_t)node_id);
-               if (ret < 0)
-                       rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
-                                       "flow distributor table\n", i);
-       }
-
-       printf("EFD table: Adding 0x%x keys\n", num_flows);
-}
-
-/* Check the link status of all ports in up to 9s, and print them finally */
-static void
-check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
-{
-#define CHECK_INTERVAL 100 /* 100ms */
-#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
-       uint8_t portid, count, all_ports_up, print_flag = 0;
-       struct rte_eth_link link;
-
-       printf("\nChecking link status");
-       fflush(stdout);
-       for (count = 0; count <= MAX_CHECK_TIME; count++) {
-               all_ports_up = 1;
-               for (portid = 0; portid < port_num; portid++) {
-                       if ((port_mask & (1 << info->id[portid])) == 0)
-                               continue;
-                       memset(&link, 0, sizeof(link));
-                       rte_eth_link_get_nowait(info->id[portid], &link);
-                       /* print link status if flag set */
-                       if (print_flag == 1) {
-                               if (link.link_status)
-                                       printf("Port %d Link Up - speed %u "
-                                               "Mbps - %s\n", info->id[portid],
-                                               (unsigned int)link.link_speed,
-                               (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
-                                       ("full-duplex") : ("half-duplex\n"));
-                               else
-                                       printf("Port %d Link Down\n",
-                                               (uint8_t)info->id[portid]);
-                               continue;
-                       }
-                       /* clear all_ports_up flag if any link down */
-                       if (link.link_status == ETH_LINK_DOWN) {
-                               all_ports_up = 0;
-                               break;
-                       }
-               }
-               /* after finally printing all link status, get out */
-               if (print_flag == 1)
-                       break;
-
-               if (all_ports_up == 0) {
-                       printf(".");
-                       fflush(stdout);
-                       rte_delay_ms(CHECK_INTERVAL);
-               }
-
-               /* set the print_flag if all ports up or timeout */
-               if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
-                       print_flag = 1;
-                       printf("done\n");
-               }
-       }
-}
-
-/**
- * Main init function for the multi-process distributor app,
- * calls subfunctions to do each stage of the initialisation.
- */
-int
-init(int argc, char *argv[])
-{
-       int retval;
-       const struct rte_memzone *mz;
-       uint8_t i, total_ports;
-
-       /* init EAL, parsing EAL args */
-       retval = rte_eal_init(argc, argv);
-       if (retval < 0)
-               return -1;
-       argc -= retval;
-       argv += retval;
-
-       /* get total number of ports */
-       total_ports = rte_eth_dev_count();
-
-       /* set up array for port data */
-       mz = rte_memzone_reserve(MZ_SHARED_INFO, sizeof(*info),
-                               rte_socket_id(), NO_FLAGS);
-       if (mz == NULL)
-               rte_exit(EXIT_FAILURE, "Cannot reserve memory zone "
-                               "for port information\n");
-       memset(mz->addr, 0, sizeof(*info));
-       info = mz->addr;
-
-       /* parse additional, application arguments */
-       retval = parse_app_args(total_ports, argc, argv);
-       if (retval != 0)
-               return -1;
-
-       /* initialise mbuf pools */
-       retval = init_mbuf_pools();
-       if (retval != 0)
-               rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
-
-       /* now initialise the ports we will use */
-       for (i = 0; i < info->num_ports; i++) {
-               retval = init_port(info->id[i]);
-               if (retval != 0)
-                       rte_exit(EXIT_FAILURE, "Cannot initialise port %u\n",
-                                       (unsigned int) i);
-       }
-
-       check_all_ports_link_status(info->num_ports, (~0x0));
-
-       /* initialise the node queues/rings for inter-eu comms */
-       init_shm_rings();
-
-       /* Create the flow distributor table */
-       create_flow_distributor_table();
-
-       /* Populate the flow distributor table */
-       populate_flow_distributor_table();
-
-       /* Share the total number of nodes */
-       info->num_nodes = num_nodes;
-
-       /* Share the total number of flows */
-       info->num_flows = num_flows;
-       return 0;
-}
diff --git a/examples/flow_distributor/distributor/init.h b/examples/flow_distributor/distributor/init.h
deleted file mode 100644 (file)
index d11aacf..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#ifndef _INIT_H_
-#define _INIT_H_
-
-/*
- * #include <rte_ring.h>
- * #include "args.h"
- */
-
-/*
- * Define a node structure with all needed info, including
- * stats from the nodes.
- */
-struct node {
-       struct rte_ring *rx_q;
-       unsigned int node_id;
-       /* these stats hold how many packets the node will actually receive,
-        * and how many packets were dropped because the node's queue was full.
-        * The port-info stats, in contrast, record how many packets were received
-        * or transmitted on an actual NIC port.
-        */
-       struct {
-               uint64_t rx;
-               uint64_t rx_drop;
-       } stats;
-};
-
-extern struct rte_efd_table *efd_table;
-extern struct node *nodes;
-
-/*
- * shared information between distributor and nodes: number of clients,
- * port numbers, rx and tx stats etc.
- */
-extern struct shared_info *info;
-
-extern struct rte_mempool *pktmbuf_pool;
-extern uint8_t num_nodes;
-extern unsigned int num_sockets;
-extern uint32_t num_flows;
-
-int init(int argc, char *argv[]);
-
-#endif /* ifndef _INIT_H_ */
diff --git a/examples/flow_distributor/distributor/main.c b/examples/flow_distributor/distributor/main.c
deleted file mode 100644 (file)
index f97f003..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <inttypes.h>
-#include <inttypes.h>
-#include <sys/queue.h>
-#include <errno.h>
-#include <netinet/ip.h>
-
-#include <rte_common.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_byteorder.h>
-#include <rte_launch.h>
-#include <rte_per_lcore.h>
-#include <rte_lcore.h>
-#include <rte_branch_prediction.h>
-#include <rte_atomic.h>
-#include <rte_ring.h>
-#include <rte_log.h>
-#include <rte_debug.h>
-#include <rte_mempool.h>
-#include <rte_memcpy.h>
-#include <rte_mbuf.h>
-#include <rte_ether.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_ethdev.h>
-#include <rte_byteorder.h>
-#include <rte_malloc.h>
-#include <rte_string_fns.h>
-#include <rte_efd.h>
-#include <rte_ip.h>
-
-#include "common.h"
-#include "args.h"
-#include "init.h"
-
-/*
- * When doing reads from the NIC or the node queues,
- * use this batch size
- */
-#define PACKET_READ_SIZE 32
-
-/*
- * Local buffers to put packets in, used to send packets in bursts to the
- * nodes
- */
-struct node_rx_buf {
-       struct rte_mbuf *buffer[PACKET_READ_SIZE];
-       uint16_t count;
-};
-
-struct flow_distributor_stats {
-       uint64_t distributed;
-       uint64_t drop;
-} flow_dist_stats;
-
-/* One buffer per node rx queue - dynamically allocate array */
-static struct node_rx_buf *cl_rx_buf;
-
-static const char *
-get_printable_mac_addr(uint8_t port)
-{
-       static const char err_address[] = "00:00:00:00:00:00";
-       static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)];
-       struct ether_addr mac;
-
-       if (unlikely(port >= RTE_MAX_ETHPORTS))
-               return err_address;
-       if (unlikely(addresses[port][0] == '\0')) {
-               rte_eth_macaddr_get(port, &mac);
-               snprintf(addresses[port], sizeof(addresses[port]),
-                               "%02x:%02x:%02x:%02x:%02x:%02x\n",
-                               mac.addr_bytes[0], mac.addr_bytes[1],
-                               mac.addr_bytes[2], mac.addr_bytes[3],
-                               mac.addr_bytes[4], mac.addr_bytes[5]);
-       }
-       return addresses[port];
-}
-
-/*
- * This function displays the recorded statistics for each port
- * and for each node. It uses ANSI terminal codes to clear
- * screen when called. It is called from a single non-master
- * thread in the distributor process, when the process is run with more
- * than one lcore enabled.
- */
-static void
-do_stats_display(void)
-{
-       unsigned int i, j;
-       const char clr[] = {27, '[', '2', 'J', '\0'};
-       const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
-       uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
-       uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
-
-       /* to get TX stats, we need to do some summing calculations */
-       memset(port_tx, 0, sizeof(port_tx));
-       memset(port_tx_drop, 0, sizeof(port_tx_drop));
-       memset(node_tx, 0, sizeof(node_tx));
-       memset(node_tx_drop, 0, sizeof(node_tx_drop));
-
-       for (i = 0; i < num_nodes; i++) {
-               const struct tx_stats *tx = &info->tx_stats[i];
-
-               for (j = 0; j < info->num_ports; j++) {
-                       const uint64_t tx_val = tx->tx[info->id[j]];
-                       const uint64_t drop_val = tx->tx_drop[info->id[j]];
-
-                       port_tx[j] += tx_val;
-                       port_tx_drop[j] += drop_val;
-                       node_tx[i] += tx_val;
-                       node_tx_drop[i] += drop_val;
-               }
-       }
-
-       /* Clear screen and move to top left */
-       printf("%s%s", clr, topLeft);
-
-       printf("PORTS\n");
-       printf("-----\n");
-       for (i = 0; i < info->num_ports; i++)
-               printf("Port %u: '%s'\t", (unsigned int)info->id[i],
-                               get_printable_mac_addr(info->id[i]));
-       printf("\n\n");
-       for (i = 0; i < info->num_ports; i++) {
-               printf("Port %u - rx: %9"PRIu64"\t"
-                               "tx: %9"PRIu64"\n",
-                               (unsigned int)info->id[i], info->rx_stats.rx[i],
-                               port_tx[i]);
-       }
-
-       printf("\nFLOW DISTRIBUTOR\n");
-       printf("-----\n");
-       printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
-                       flow_dist_stats.distributed, flow_dist_stats.drop);
-
-       printf("\nNODES\n");
-       printf("-------\n");
-       for (i = 0; i < num_nodes; i++) {
-               const unsigned long long rx = nodes[i].stats.rx;
-               const unsigned long long rx_drop = nodes[i].stats.rx_drop;
-               const struct filter_stats *filter = &info->filter_stats[i];
-
-               printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
-                               "            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
-                               "            filter_passed: %9"PRIu64", "
-                               "filter_drop: %9"PRIu64"\n",
-                               i, rx, rx_drop, node_tx[i], node_tx_drop[i],
-                               filter->passed, filter->drop);
-       }
-
-       printf("\n");
-}
-
-/*
- * The function called from each non-master lcore used by the process.
- * The test_and_set function is used to randomly pick a single lcore on which
- * the code to display the statistics will run. Otherwise, the code just
- * repeatedly sleeps.
- */
-static int
-sleep_lcore(__attribute__((unused)) void *dummy)
-{
-       /* Used to pick a display thread - static, so zero-initialised */
-       static rte_atomic32_t display_stats;
-
-       /* Only one core should display stats */
-       if (rte_atomic32_test_and_set(&display_stats)) {
-               const unsigned int sleeptime = 1;
-
-               printf("Core %u displaying statistics\n", rte_lcore_id());
-
-               /* Longer initial pause so above printf is seen */
-               sleep(sleeptime * 3);
-
-               /* Loop forever: sleep always returns 0 or <= param */
-               while (sleep(sleeptime) <= sleeptime)
-                       do_stats_display();
-       }
-       return 0;
-}
-
-/*
- * Function to set all the node statistic values to zero.
- * Called at program startup.
- */
-static void
-clear_stats(void)
-{
-       unsigned int i;
-
-       for (i = 0; i < num_nodes; i++)
-               nodes[i].stats.rx = nodes[i].stats.rx_drop = 0;
-}
-
-/*
- * send a burst of traffic to a node, assuming there are packets
- * available to be sent to this node
- */
-static void
-flush_rx_queue(uint16_t node)
-{
-       uint16_t j;
-       struct node *cl;
-
-       if (cl_rx_buf[node].count == 0)
-               return;
-
-       cl = &nodes[node];
-       if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
-                       cl_rx_buf[node].count) != 0){
-               for (j = 0; j < cl_rx_buf[node].count; j++)
-                       rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
-               cl->stats.rx_drop += cl_rx_buf[node].count;
-       } else
-               cl->stats.rx += cl_rx_buf[node].count;
-
-       cl_rx_buf[node].count = 0;
-}
-
-/*
- * marks a packet down to be sent to a particular node process
- */
-static inline void
-enqueue_rx_packet(uint8_t node, struct rte_mbuf *buf)
-{
-       cl_rx_buf[node].buffer[cl_rx_buf[node].count++] = buf;
-}
-
-/*
- * This function takes a group of packets and routes them
- * individually to the node process. Very simply round-robins the packets
- * without checking any of the packet contents.
- */
-static void
-process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
-               uint16_t rx_count, unsigned int socket_id)
-{
-       uint16_t i;
-       uint8_t node;
-       efd_value_t data[RTE_EFD_BURST_MAX];
-       const void *key_ptrs[RTE_EFD_BURST_MAX];
-
-       struct ipv4_hdr *ipv4_hdr;
-       uint32_t ipv4_dst_ip[RTE_EFD_BURST_MAX];
-
-       for (i = 0; i < rx_count; i++) {
-               /* Handle IPv4 header.*/
-               ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
-                               sizeof(struct ether_hdr));
-               ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
-               key_ptrs[i] = (void *)&ipv4_dst_ip[i];
-       }
-
-       rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
-                               (const void **) key_ptrs, data);
-       for (i = 0; i < rx_count; i++) {
-               node = (uint8_t) ((uintptr_t)data[i]);
-
-               if (node >= num_nodes) {
-                       /*
-                        * Node is out of range, which means that
-                        * flow has not been inserted
-                        */
-                       flow_dist_stats.drop++;
-                       rte_pktmbuf_free(pkts[i]);
-               } else {
-                       flow_dist_stats.distributed++;
-                       enqueue_rx_packet(node, pkts[i]);
-               }
-       }
-
-       for (i = 0; i < num_nodes; i++)
-               flush_rx_queue(i);
-}
-
-/*
- * Function called by the master lcore of the DPDK process.
- */
-static void
-do_packet_forwarding(void)
-{
-       unsigned int port_num = 0; /* indexes the port[] array */
-       unsigned int socket_id = rte_socket_id();
-
-       for (;;) {
-               struct rte_mbuf *buf[PACKET_READ_SIZE];
-               uint16_t rx_count;
-
-               /* read a port */
-               rx_count = rte_eth_rx_burst(info->id[port_num], 0,
-                               buf, PACKET_READ_SIZE);
-               info->rx_stats.rx[port_num] += rx_count;
-
-               /* Now process the NIC packets read */
-               if (likely(rx_count > 0))
-                       process_packets(port_num, buf, rx_count, socket_id);
-
-               /* move to next port */
-               if (++port_num == info->num_ports)
-                       port_num = 0;
-       }
-}
-
-int
-main(int argc, char *argv[])
-{
-       /* initialise the system */
-       if (init(argc, argv) < 0)
-               return -1;
-       RTE_LOG(INFO, APP, "Finished Process Init.\n");
-
-       cl_rx_buf = calloc(num_nodes, sizeof(cl_rx_buf[0]));
-
-       /* clear statistics */
-       clear_stats();
-
-       /* put all other cores to sleep bar master */
-       rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MASTER);
-
-       do_packet_forwarding();
-       return 0;
-}
diff --git a/examples/flow_distributor/node/Makefile b/examples/flow_distributor/node/Makefile
deleted file mode 100644 (file)
index 8cf7b65..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#   BSD LICENSE
-#
-#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
-#   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.
-#     * Neither the name of Intel Corporation nor the names of its
-#       contributors may 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 overridden by command line or environment
-include $(RTE_SDK)/mk/rte.vars.mk
-
-# binary name
-APP = node
-
-# all source are stored in SRCS-y
-SRCS-y := node.c
-
-CFLAGS += $(WERROR_FLAGS) -O3
-CFLAGS += -I$(SRCDIR)/../shared
-
-include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/flow_distributor/node/node.c b/examples/flow_distributor/node/node.c
deleted file mode 100644 (file)
index 1f1e7e7..0000000
+++ /dev/null
@@ -1,417 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/queue.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <string.h>
-
-#include <rte_common.h>
-#include <rte_malloc.h>
-#include <rte_memory.h>
-#include <rte_memzone.h>
-#include <rte_eal.h>
-#include <rte_atomic.h>
-#include <rte_branch_prediction.h>
-#include <rte_log.h>
-#include <rte_per_lcore.h>
-#include <rte_launch.h>
-#include <rte_lcore.h>
-#include <rte_ring.h>
-#include <rte_launch.h>
-#include <rte_lcore.h>
-#include <rte_debug.h>
-#include <rte_mempool.h>
-#include <rte_mbuf.h>
-#include <rte_interrupts.h>
-#include <rte_pci.h>
-#include <rte_ether.h>
-#include <rte_ethdev.h>
-#include <rte_string_fns.h>
-#include <rte_ip.h>
-
-#include "common.h"
-
-/* Number of packets to attempt to read from queue */
-#define PKT_READ_SIZE  ((uint16_t)32)
-
-/*
- * Our node id number - tells us which rx queue to read, and NIC TX
- * queue to write to.
- */
-static uint8_t node_id;
-
-#define MBQ_CAPACITY 32
-
-/* maps input ports to output ports for packets */
-static uint8_t output_ports[RTE_MAX_ETHPORTS];
-
-/* buffers up a set of packet that are ready to send */
-struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
-
-/* shared data from distributor. We update statistics here */
-static struct tx_stats *tx_stats;
-
-static struct filter_stats *filter_stats;
-
-/*
- * print a usage message
- */
-static void
-usage(const char *progname)
-{
-       printf("Usage: %s [EAL args] -- -n <node_id>\n\n", progname);
-}
-
-/*
- * Convert the node id number from a string to an int.
- */
-static int
-parse_node_num(const char *node)
-{
-       char *end = NULL;
-       unsigned long temp;
-
-       if (node == NULL || *node == '\0')
-               return -1;
-
-       temp = strtoul(node, &end, 10);
-       if (end == NULL || *end != '\0')
-               return -1;
-
-       node_id = (uint8_t)temp;
-       return 0;
-}
-
-/*
- * Parse the application arguments to the node app.
- */
-static int
-parse_app_args(int argc, char *argv[])
-{
-       int option_index, opt;
-       char **argvopt = argv;
-       const char *progname = NULL;
-       static struct option lgopts[] = { /* no long options */
-               {NULL, 0, 0, 0 }
-       };
-       progname = argv[0];
-
-       while ((opt = getopt_long(argc, argvopt, "n:", lgopts,
-               &option_index)) != EOF) {
-               switch (opt) {
-               case 'n':
-                       if (parse_node_num(optarg) != 0) {
-                               usage(progname);
-                               return -1;
-                       }
-                       break;
-               default:
-                       usage(progname);
-                       return -1;
-               }
-       }
-       return 0;
-}
-
-/*
- * Tx buffer error callback
- */
-static void
-flush_tx_error_callback(struct rte_mbuf **unsent, uint16_t count,
-               void *userdata) {
-       int i;
-       uint8_t port_id = (uintptr_t)userdata;
-
-       tx_stats->tx_drop[port_id] += count;
-
-       /* free the mbufs which failed from transmit */
-       for (i = 0; i < count; i++)
-               rte_pktmbuf_free(unsent[i]);
-
-}
-
-static void
-configure_tx_buffer(uint8_t port_id, uint16_t size)
-{
-       int ret;
-
-       /* Initialize TX buffers */
-       tx_buffer[port_id] = rte_zmalloc_socket("tx_buffer",
-                       RTE_ETH_TX_BUFFER_SIZE(size), 0,
-                       rte_eth_dev_socket_id(port_id));
-       if (tx_buffer[port_id] == NULL)
-               rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx "
-                               "on port %u\n", (unsigned int) port_id);
-
-       rte_eth_tx_buffer_init(tx_buffer[port_id], size);
-
-       ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[port_id],
-                       flush_tx_error_callback, (void *)(intptr_t)port_id);
-       if (ret < 0)
-               rte_exit(EXIT_FAILURE, "Cannot set error callback for "
-                       "tx buffer on port %u\n", (unsigned int) port_id);
-}
-
-/*
- * set up output ports so that all traffic on port gets sent out
- * its paired port. Index using actual port numbers since that is
- * what comes in the mbuf structure.
- */
-static void
-configure_output_ports(const struct shared_info *info)
-{
-       int i;
-
-       if (info->num_ports > RTE_MAX_ETHPORTS)
-               rte_exit(EXIT_FAILURE, "Too many ethernet ports. "
-                               "RTE_MAX_ETHPORTS = %u\n",
-                               (unsigned int)RTE_MAX_ETHPORTS);
-       for (i = 0; i < info->num_ports - 1; i += 2) {
-               uint8_t p1 = info->id[i];
-               uint8_t p2 = info->id[i+1];
-
-               output_ports[p1] = p2;
-               output_ports[p2] = p1;
-
-               configure_tx_buffer(p1, MBQ_CAPACITY);
-               configure_tx_buffer(p2, MBQ_CAPACITY);
-
-       }
-}
-
-/*
- * Create the hash table that will contain the flows that
- * the node will handle, which will be used to decide if packet
- * is transmitted or dropped.
- */
-static struct rte_hash *
-create_hash_table(const struct shared_info *info)
-{
-       uint32_t num_flows_node = info->num_flows / info->num_nodes;
-       char name[RTE_HASH_NAMESIZE];
-       struct rte_hash *h;
-
-       /* create table */
-       struct rte_hash_parameters hash_params = {
-               .entries = num_flows_node * 2, /* table load = 50% */
-               .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
-               .socket_id = rte_socket_id(),
-               .hash_func_init_val = 0,
-       };
-
-       snprintf(name, sizeof(name), "hash_table_%d", node_id);
-       hash_params.name = name;
-       h = rte_hash_create(&hash_params);
-
-       if (h == NULL)
-               rte_exit(EXIT_FAILURE,
-                               "Problem creating the hash table for node %d\n",
-                               node_id);
-       return h;
-}
-
-static void
-populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
-{
-       unsigned int i;
-       int32_t ret;
-       uint32_t ip_dst;
-       uint32_t num_flows_node = 0;
-       uint64_t target_node;
-
-       /* Add flows in table */
-       for (i = 0; i < info->num_flows; i++) {
-               target_node = i % info->num_nodes;
-               if (target_node != node_id)
-                       continue;
-
-               ip_dst = rte_cpu_to_be_32(i);
-
-               ret = rte_hash_add_key(h, (void *) &ip_dst);
-               if (ret < 0)
-                       rte_exit(EXIT_FAILURE, "Unable to add entry %u "
-                                       "in hash table\n", i);
-               else
-                       num_flows_node++;
-
-       }
-
-       printf("Hash table: Adding 0x%x keys\n", num_flows_node);
-}
-
-/*
- * This function performs routing of packets
- * Just sends each input packet out an output port based solely on the input
- * port it arrived on.
- */
-static inline void
-transmit_packet(struct rte_mbuf *buf)
-{
-       int sent;
-       const uint8_t in_port = buf->port;
-       const uint8_t out_port = output_ports[in_port];
-       struct rte_eth_dev_tx_buffer *buffer = tx_buffer[out_port];
-
-       sent = rte_eth_tx_buffer(out_port, node_id, buffer, buf);
-       if (sent)
-               tx_stats->tx[out_port] += sent;
-
-}
-
-static inline void
-handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
-{
-       struct ipv4_hdr *ipv4_hdr;
-       uint32_t ipv4_dst_ip[PKT_READ_SIZE];
-       const void *key_ptrs[PKT_READ_SIZE];
-       unsigned int i;
-       int32_t positions[PKT_READ_SIZE] = {0};
-
-       for (i = 0; i < num_packets; i++) {
-               /* Handle IPv4 header.*/
-               ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
-                               sizeof(struct ether_hdr));
-               ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
-               key_ptrs[i] = &ipv4_dst_ip[i];
-       }
-       /* Check if packets belongs to any flows handled by this node */
-       rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
-
-       for (i = 0; i < num_packets; i++) {
-               if (likely(positions[i] >= 0)) {
-                       filter_stats->passed++;
-                       transmit_packet(bufs[i]);
-               } else {
-                       filter_stats->drop++;
-                       /* Drop packet, as flow is not handled by this node */
-                       rte_pktmbuf_free(bufs[i]);
-               }
-       }
-}
-
-/*
- * Application main function - loops through
- * receiving and processing packets. Never returns
- */
-int
-main(int argc, char *argv[])
-{
-       const struct rte_memzone *mz;
-       struct rte_ring *rx_ring;
-       struct rte_hash *h;
-       struct rte_mempool *mp;
-       struct shared_info *info;
-       int need_flush = 0; /* indicates whether we have unsent packets */
-       int retval;
-       void *pkts[PKT_READ_SIZE];
-       uint16_t sent;
-
-       retval = rte_eal_init(argc, argv);
-       if (retval  < 0)
-               return -1;
-       argc -= retval;
-       argv += retval;
-
-       if (parse_app_args(argc, argv) < 0)
-               rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n");
-
-       if (rte_eth_dev_count() == 0)
-               rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
-
-       rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
-       if (rx_ring == NULL)
-               rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
-                               "is distributor process running?\n");
-
-       mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
-       if (mp == NULL)
-               rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
-
-       mz = rte_memzone_lookup(MZ_SHARED_INFO);
-       if (mz == NULL)
-               rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
-       info = mz->addr;
-       tx_stats = &(info->tx_stats[node_id]);
-       filter_stats = &(info->filter_stats[node_id]);
-
-       configure_output_ports(info);
-
-       h = create_hash_table(info);
-
-       populate_hash_table(h, info);
-
-       RTE_LOG(INFO, APP, "Finished Process Init.\n");
-
-       printf("\nNode process %d handling packets\n", node_id);
-       printf("[Press Ctrl-C to quit ...]\n");
-
-       for (;;) {
-               uint16_t  rx_pkts = PKT_READ_SIZE;
-               uint8_t port;
-
-               /*
-                * Try dequeuing max possible packets first, if that fails,
-                * get the most we can. Loop body should only execute once,
-                * maximum
-                */
-               while (rx_pkts > 0 &&
-                               unlikely(rte_ring_dequeue_bulk(rx_ring, pkts,
-                                       rx_pkts) != 0))
-                       rx_pkts = (uint16_t)RTE_MIN(rte_ring_count(rx_ring),
-                                       PKT_READ_SIZE);
-
-               if (unlikely(rx_pkts == 0)) {
-                       if (need_flush)
-                               for (port = 0; port < info->num_ports; port++) {
-                                       sent = rte_eth_tx_buffer_flush(
-                                                       info->id[port],
-                                                       node_id,
-                                                       tx_buffer[port]);
-                                       if (unlikely(sent))
-                                               tx_stats->tx[port] += sent;
-                               }
-                       need_flush = 0;
-                       continue;
-               }
-
-               handle_packets(h, (struct rte_mbuf **)pkts, rx_pkts);
-
-               need_flush = 1;
-       }
-}
diff --git a/examples/flow_distributor/shared/common.h b/examples/flow_distributor/shared/common.h
deleted file mode 100644 (file)
index 5dcffd6..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
- *   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.
- *     * Neither the name of Intel Corporation nor the names of its
- *       contributors may 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.
- */
-
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-#include <rte_hash_crc.h>
-#include <rte_hash.h>
-
-#define MAX_NODES             16
-/*
- * Shared port info, including statistics information for display by distributor.
- * Structure will be put in a memzone.
- * - All port id values share one cache line as this data will be read-only
- * during operation.
- * - All rx statistic values share cache lines, as this data is written only
- * by the distributor process. (rare reads by stats display)
- * - The tx statistics have values for all ports per cache line, but the stats
- * themselves are written by the nodes, so we have a distinct set, on different
- * cache lines for each node to use.
- */
-struct rx_stats {
-       uint64_t rx[RTE_MAX_ETHPORTS];
-} __rte_cache_aligned;
-
-struct tx_stats {
-       uint64_t tx[RTE_MAX_ETHPORTS];
-       uint64_t tx_drop[RTE_MAX_ETHPORTS];
-} __rte_cache_aligned;
-
-struct filter_stats {
-       uint64_t drop;
-       uint64_t passed;
-} __rte_cache_aligned;
-
-struct shared_info {
-       uint8_t num_nodes;
-       uint8_t num_ports;
-       uint32_t num_flows;
-       uint8_t id[RTE_MAX_ETHPORTS];
-       struct rx_stats rx_stats;
-       struct tx_stats tx_stats[MAX_NODES];
-       struct filter_stats filter_stats[MAX_NODES];
-};
-
-/* define common names for structures shared between distributor and node */
-#define MP_NODE_RXQ_NAME "MProc_Node_%u_RX"
-#define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool"
-#define MZ_SHARED_INFO "MProc_shared_info"
-
-/*
- * Given the rx queue name template above, get the queue name
- */
-static inline const char *
-get_rx_queue_name(unsigned int id)
-{
-       /*
-        * Buffer for return value. Size calculated by %u being replaced
-        * by maximum 3 digits (plus an extra byte for safety)
-        */
-       static char buffer[sizeof(MP_NODE_RXQ_NAME) + 2];
-
-       snprintf(buffer, sizeof(buffer) - 1, MP_NODE_RXQ_NAME, id);
-       return buffer;
-}
-
-#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
-
-#endif
diff --git a/examples/server_node_efd/Makefile b/examples/server_node_efd/Makefile
new file mode 100644 (file)
index 0000000..6977ef9
--- /dev/null
@@ -0,0 +1,44 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   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.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may 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 overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += server
+DIRS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) += node
+
+include $(RTE_SDK)/mk/rte.extsubdir.mk
diff --git a/examples/server_node_efd/node/Makefile b/examples/server_node_efd/node/Makefile
new file mode 100644 (file)
index 0000000..8cf7b65
--- /dev/null
@@ -0,0 +1,48 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   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.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may 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 overridden by command line or environment
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = node
+
+# all source are stored in SRCS-y
+SRCS-y := node.c
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/server_node_efd/node/node.c b/examples/server_node_efd/node/node.c
new file mode 100644 (file)
index 0000000..a6c0c70
--- /dev/null
@@ -0,0 +1,417 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <rte_common.h>
+#include <rte_malloc.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_atomic.h>
+#include <rte_branch_prediction.h>
+#include <rte_log.h>
+#include <rte_per_lcore.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_ring.h>
+#include <rte_launch.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_mempool.h>
+#include <rte_mbuf.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_string_fns.h>
+#include <rte_ip.h>
+
+#include "common.h"
+
+/* Number of packets to attempt to read from queue */
+#define PKT_READ_SIZE  ((uint16_t)32)
+
+/*
+ * Our node id number - tells us which rx queue to read, and NIC TX
+ * queue to write to.
+ */
+static uint8_t node_id;
+
+#define MBQ_CAPACITY 32
+
+/* maps input ports to output ports for packets */
+static uint8_t output_ports[RTE_MAX_ETHPORTS];
+
+/* buffers up a set of packet that are ready to send */
+struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
+
+/* shared data from server. We update statistics here */
+static struct tx_stats *tx_stats;
+
+static struct filter_stats *filter_stats;
+
+/*
+ * print a usage message
+ */
+static void
+usage(const char *progname)
+{
+       printf("Usage: %s [EAL args] -- -n <node_id>\n\n", progname);
+}
+
+/*
+ * Convert the node id number from a string to an int.
+ */
+static int
+parse_node_num(const char *node)
+{
+       char *end = NULL;
+       unsigned long temp;
+
+       if (node == NULL || *node == '\0')
+               return -1;
+
+       temp = strtoul(node, &end, 10);
+       if (end == NULL || *end != '\0')
+               return -1;
+
+       node_id = (uint8_t)temp;
+       return 0;
+}
+
+/*
+ * Parse the application arguments to the node app.
+ */
+static int
+parse_app_args(int argc, char *argv[])
+{
+       int option_index, opt;
+       char **argvopt = argv;
+       const char *progname = NULL;
+       static struct option lgopts[] = { /* no long options */
+               {NULL, 0, 0, 0 }
+       };
+       progname = argv[0];
+
+       while ((opt = getopt_long(argc, argvopt, "n:", lgopts,
+               &option_index)) != EOF) {
+               switch (opt) {
+               case 'n':
+                       if (parse_node_num(optarg) != 0) {
+                               usage(progname);
+                               return -1;
+                       }
+                       break;
+               default:
+                       usage(progname);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Tx buffer error callback
+ */
+static void
+flush_tx_error_callback(struct rte_mbuf **unsent, uint16_t count,
+               void *userdata) {
+       int i;
+       uint8_t port_id = (uintptr_t)userdata;
+
+       tx_stats->tx_drop[port_id] += count;
+
+       /* free the mbufs which failed from transmit */
+       for (i = 0; i < count; i++)
+               rte_pktmbuf_free(unsent[i]);
+
+}
+
+static void
+configure_tx_buffer(uint8_t port_id, uint16_t size)
+{
+       int ret;
+
+       /* Initialize TX buffers */
+       tx_buffer[port_id] = rte_zmalloc_socket("tx_buffer",
+                       RTE_ETH_TX_BUFFER_SIZE(size), 0,
+                       rte_eth_dev_socket_id(port_id));
+       if (tx_buffer[port_id] == NULL)
+               rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx "
+                               "on port %u\n", (unsigned int) port_id);
+
+       rte_eth_tx_buffer_init(tx_buffer[port_id], size);
+
+       ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[port_id],
+                       flush_tx_error_callback, (void *)(intptr_t)port_id);
+       if (ret < 0)
+               rte_exit(EXIT_FAILURE, "Cannot set error callback for "
+                       "tx buffer on port %u\n", (unsigned int) port_id);
+}
+
+/*
+ * set up output ports so that all traffic on port gets sent out
+ * its paired port. Index using actual port numbers since that is
+ * what comes in the mbuf structure.
+ */
+static void
+configure_output_ports(const struct shared_info *info)
+{
+       int i;
+
+       if (info->num_ports > RTE_MAX_ETHPORTS)
+               rte_exit(EXIT_FAILURE, "Too many ethernet ports. "
+                               "RTE_MAX_ETHPORTS = %u\n",
+                               (unsigned int)RTE_MAX_ETHPORTS);
+       for (i = 0; i < info->num_ports - 1; i += 2) {
+               uint8_t p1 = info->id[i];
+               uint8_t p2 = info->id[i+1];
+
+               output_ports[p1] = p2;
+               output_ports[p2] = p1;
+
+               configure_tx_buffer(p1, MBQ_CAPACITY);
+               configure_tx_buffer(p2, MBQ_CAPACITY);
+
+       }
+}
+
+/*
+ * Create the hash table that will contain the flows that
+ * the node will handle, which will be used to decide if packet
+ * is transmitted or dropped.
+ */
+static struct rte_hash *
+create_hash_table(const struct shared_info *info)
+{
+       uint32_t num_flows_node = info->num_flows / info->num_nodes;
+       char name[RTE_HASH_NAMESIZE];
+       struct rte_hash *h;
+
+       /* create table */
+       struct rte_hash_parameters hash_params = {
+               .entries = num_flows_node * 2, /* table load = 50% */
+               .key_len = sizeof(uint32_t), /* Store IPv4 dest IP address */
+               .socket_id = rte_socket_id(),
+               .hash_func_init_val = 0,
+       };
+
+       snprintf(name, sizeof(name), "hash_table_%d", node_id);
+       hash_params.name = name;
+       h = rte_hash_create(&hash_params);
+
+       if (h == NULL)
+               rte_exit(EXIT_FAILURE,
+                               "Problem creating the hash table for node %d\n",
+                               node_id);
+       return h;
+}
+
+static void
+populate_hash_table(const struct rte_hash *h, const struct shared_info *info)
+{
+       unsigned int i;
+       int32_t ret;
+       uint32_t ip_dst;
+       uint32_t num_flows_node = 0;
+       uint64_t target_node;
+
+       /* Add flows in table */
+       for (i = 0; i < info->num_flows; i++) {
+               target_node = i % info->num_nodes;
+               if (target_node != node_id)
+                       continue;
+
+               ip_dst = rte_cpu_to_be_32(i);
+
+               ret = rte_hash_add_key(h, (void *) &ip_dst);
+               if (ret < 0)
+                       rte_exit(EXIT_FAILURE, "Unable to add entry %u "
+                                       "in hash table\n", i);
+               else
+                       num_flows_node++;
+
+       }
+
+       printf("Hash table: Adding 0x%x keys\n", num_flows_node);
+}
+
+/*
+ * This function performs routing of packets
+ * Just sends each input packet out an output port based solely on the input
+ * port it arrived on.
+ */
+static inline void
+transmit_packet(struct rte_mbuf *buf)
+{
+       int sent;
+       const uint8_t in_port = buf->port;
+       const uint8_t out_port = output_ports[in_port];
+       struct rte_eth_dev_tx_buffer *buffer = tx_buffer[out_port];
+
+       sent = rte_eth_tx_buffer(out_port, node_id, buffer, buf);
+       if (sent)
+               tx_stats->tx[out_port] += sent;
+
+}
+
+static inline void
+handle_packets(struct rte_hash *h, struct rte_mbuf **bufs, uint16_t num_packets)
+{
+       struct ipv4_hdr *ipv4_hdr;
+       uint32_t ipv4_dst_ip[PKT_READ_SIZE];
+       const void *key_ptrs[PKT_READ_SIZE];
+       unsigned int i;
+       int32_t positions[PKT_READ_SIZE] = {0};
+
+       for (i = 0; i < num_packets; i++) {
+               /* Handle IPv4 header.*/
+               ipv4_hdr = rte_pktmbuf_mtod_offset(bufs[i], struct ipv4_hdr *,
+                               sizeof(struct ether_hdr));
+               ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+               key_ptrs[i] = &ipv4_dst_ip[i];
+       }
+       /* Check if packets belongs to any flows handled by this node */
+       rte_hash_lookup_bulk(h, key_ptrs, num_packets, positions);
+
+       for (i = 0; i < num_packets; i++) {
+               if (likely(positions[i] >= 0)) {
+                       filter_stats->passed++;
+                       transmit_packet(bufs[i]);
+               } else {
+                       filter_stats->drop++;
+                       /* Drop packet, as flow is not handled by this node */
+                       rte_pktmbuf_free(bufs[i]);
+               }
+       }
+}
+
+/*
+ * Application main function - loops through
+ * receiving and processing packets. Never returns
+ */
+int
+main(int argc, char *argv[])
+{
+       const struct rte_memzone *mz;
+       struct rte_ring *rx_ring;
+       struct rte_hash *h;
+       struct rte_mempool *mp;
+       struct shared_info *info;
+       int need_flush = 0; /* indicates whether we have unsent packets */
+       int retval;
+       void *pkts[PKT_READ_SIZE];
+       uint16_t sent;
+
+       retval = rte_eal_init(argc, argv);
+       if (retval  < 0)
+               return -1;
+       argc -= retval;
+       argv += retval;
+
+       if (parse_app_args(argc, argv) < 0)
+               rte_exit(EXIT_FAILURE, "Invalid command-line arguments\n");
+
+       if (rte_eth_dev_count() == 0)
+               rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
+
+       rx_ring = rte_ring_lookup(get_rx_queue_name(node_id));
+       if (rx_ring == NULL)
+               rte_exit(EXIT_FAILURE, "Cannot get RX ring - "
+                               "is server process running?\n");
+
+       mp = rte_mempool_lookup(PKTMBUF_POOL_NAME);
+       if (mp == NULL)
+               rte_exit(EXIT_FAILURE, "Cannot get mempool for mbufs\n");
+
+       mz = rte_memzone_lookup(MZ_SHARED_INFO);
+       if (mz == NULL)
+               rte_exit(EXIT_FAILURE, "Cannot get port info structure\n");
+       info = mz->addr;
+       tx_stats = &(info->tx_stats[node_id]);
+       filter_stats = &(info->filter_stats[node_id]);
+
+       configure_output_ports(info);
+
+       h = create_hash_table(info);
+
+       populate_hash_table(h, info);
+
+       RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+       printf("\nNode process %d handling packets\n", node_id);
+       printf("[Press Ctrl-C to quit ...]\n");
+
+       for (;;) {
+               uint16_t  rx_pkts = PKT_READ_SIZE;
+               uint8_t port;
+
+               /*
+                * Try dequeuing max possible packets first, if that fails,
+                * get the most we can. Loop body should only execute once,
+                * maximum
+                */
+               while (rx_pkts > 0 &&
+                               unlikely(rte_ring_dequeue_bulk(rx_ring, pkts,
+                                       rx_pkts) != 0))
+                       rx_pkts = (uint16_t)RTE_MIN(rte_ring_count(rx_ring),
+                                       PKT_READ_SIZE);
+
+               if (unlikely(rx_pkts == 0)) {
+                       if (need_flush)
+                               for (port = 0; port < info->num_ports; port++) {
+                                       sent = rte_eth_tx_buffer_flush(
+                                                       info->id[port],
+                                                       node_id,
+                                                       tx_buffer[port]);
+                                       if (unlikely(sent))
+                                               tx_stats->tx[port] += sent;
+                               }
+                       need_flush = 0;
+                       continue;
+               }
+
+               handle_packets(h, (struct rte_mbuf **)pkts, rx_pkts);
+
+               need_flush = 1;
+       }
+}
diff --git a/examples/server_node_efd/server/Makefile b/examples/server_node_efd/server/Makefile
new file mode 100644 (file)
index 0000000..a2f2f36
--- /dev/null
@@ -0,0 +1,57 @@
+#   BSD LICENSE
+#
+#   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+#   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.
+#     * Neither the name of Intel Corporation nor the names of its
+#       contributors may 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 overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+ifneq ($(CONFIG_RTE_EXEC_ENV), "linuxapp")
+$(error This application can only operate in a linuxapp environment, \
+please change the definition of the RTE_TARGET environment variable)
+endif
+
+# binary name
+APP = server
+
+# all source are stored in SRCS-y
+SRCS-y := main.c init.c args.c
+
+INC := $(wildcard *.h)
+
+CFLAGS += $(WERROR_FLAGS) -O3
+CFLAGS += -I$(SRCDIR)/../shared
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/server_node_efd/server/args.c b/examples/server_node_efd/server/args.c
new file mode 100644 (file)
index 0000000..ee29203
--- /dev/null
@@ -0,0 +1,200 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#include <rte_memory.h>
+#include <rte_string_fns.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+/* 1M flows by default */
+#define DEFAULT_NUM_FLOWS    0x100000
+
+/* global var for number of nodes - extern in header */
+uint8_t num_nodes;
+/* global var for number of flows - extern in header */
+uint32_t num_flows = DEFAULT_NUM_FLOWS;
+
+static const char *progname;
+
+/**
+ * Prints out usage information to stdout
+ */
+static void
+usage(void)
+{
+       printf("%s [EAL options] -- -p PORTMASK -n NUM_NODES -f NUM_FLOWS\n"
+               " -p PORTMASK: hexadecimal bitmask of ports to use\n"
+               " -n NUM_NODES: number of node processes to use\n"
+               " -f NUM_FLOWS: number of flows to be added in the EFD table\n",
+               progname);
+}
+
+/**
+ * The ports to be used by the application are passed in
+ * the form of a bitmask. This function parses the bitmask
+ * and places the port numbers to be used into the port[]
+ * array variable
+ */
+static int
+parse_portmask(uint8_t max_ports, const char *portmask)
+{
+       char *end = NULL;
+       unsigned long pm;
+       uint8_t count = 0;
+
+       if (portmask == NULL || *portmask == '\0')
+               return -1;
+
+       /* convert parameter to a number and verify */
+       pm = strtoul(portmask, &end, 16);
+       if (end == NULL || *end != '\0' || pm == 0)
+               return -1;
+
+       /* loop through bits of the mask and mark ports */
+       while (pm != 0) {
+               if (pm & 0x01) { /* bit is set in mask, use port */
+                       if (count >= max_ports)
+                               printf("WARNING: requested port %u not present"
+                               " - ignoring\n", (unsigned int)count);
+                       else
+                           info->id[info->num_ports++] = count;
+               }
+               pm = (pm >> 1);
+               count++;
+       }
+
+       return 0;
+}
+
+/**
+ * Take the number of nodes parameter passed to the app
+ * and convert to a number to store in the num_nodes variable
+ */
+static int
+parse_num_nodes(const char *nodes)
+{
+       char *end = NULL;
+       unsigned long temp;
+
+       if (nodes == NULL || *nodes == '\0')
+               return -1;
+
+       temp = strtoul(nodes, &end, 10);
+       if (end == NULL || *end != '\0' || temp == 0)
+               return -1;
+
+       num_nodes = (uint8_t)temp;
+       return 0;
+}
+
+static int
+parse_num_flows(const char *flows)
+{
+       char *end = NULL;
+
+       /* parse hexadecimal string */
+       num_flows = strtoul(flows, &end, 16);
+       if ((flows[0] == '\0') || (end == NULL) || (*end != '\0'))
+               return -1;
+
+       if (num_flows == 0)
+               return -1;
+
+       return 0;
+}
+
+/**
+ * The application specific arguments follow the DPDK-specific
+ * arguments which are stripped by the DPDK init. This function
+ * processes these application arguments, printing usage info
+ * on error.
+ */
+int
+parse_app_args(uint8_t max_ports, int argc, char *argv[])
+{
+       int option_index, opt;
+       char **argvopt = argv;
+       static struct option lgopts[] = { /* no long options */
+               {NULL, 0, 0, 0 }
+       };
+       progname = argv[0];
+
+       while ((opt = getopt_long(argc, argvopt, "n:f:p:", lgopts,
+                       &option_index)) != EOF) {
+               switch (opt) {
+               case 'p':
+                       if (parse_portmask(max_ports, optarg) != 0) {
+                               usage();
+                               return -1;
+                       }
+                       break;
+               case 'n':
+                       if (parse_num_nodes(optarg) != 0) {
+                               usage();
+                               return -1;
+                       }
+                       break;
+               case 'f':
+                       if (parse_num_flows(optarg) != 0) {
+                               usage();
+                               return -1;
+                       }
+                       break;
+               default:
+                       printf("ERROR: Unknown option '%c'\n", opt);
+                       usage();
+                       return -1;
+               }
+       }
+
+       if (info->num_ports == 0 || num_nodes == 0) {
+               usage();
+               return -1;
+       }
+
+       if (info->num_ports % 2 != 0) {
+               printf("ERROR: application requires an even "
+                               "number of ports to use\n");
+               return -1;
+       }
+       return 0;
+}
diff --git a/examples/server_node_efd/server/args.h b/examples/server_node_efd/server/args.h
new file mode 100644 (file)
index 0000000..cacf395
--- /dev/null
@@ -0,0 +1,39 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#ifndef _ARGS_H_
+#define _ARGS_H_
+
+int parse_app_args(uint8_t max_ports, int argc, char *argv[]);
+
+#endif /* ifndef _ARGS_H_ */
diff --git a/examples/server_node_efd/server/init.c b/examples/server_node_efd/server/init.c
new file mode 100644 (file)
index 0000000..82457b4
--- /dev/null
@@ -0,0 +1,371 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_atomic.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_debug.h>
+#include <rte_ring.h>
+#include <rte_log.h>
+#include <rte_mempool.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ether.h>
+#include <rte_ethdev.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+#include <rte_cycles.h>
+#include <rte_efd.h>
+#include <rte_hash.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+#define MBUFS_PER_NODE 1536
+#define MBUFS_PER_PORT 1536
+#define MBUF_CACHE_SIZE 512
+
+#define RTE_MP_RX_DESC_DEFAULT 512
+#define RTE_MP_TX_DESC_DEFAULT 512
+#define NODE_QUEUE_RINGSIZE 128
+
+#define NO_FLAGS 0
+
+/* The mbuf pool for packet rx */
+struct rte_mempool *pktmbuf_pool;
+
+/* array of info/queues for nodes */
+struct node *nodes;
+
+/* EFD table */
+struct rte_efd_table *efd_table;
+
+/* Shared info between server and nodes */
+struct shared_info *info;
+
+/**
+ * Initialise the mbuf pool for packet reception for the NIC, and any other
+ * buffer pools needed by the app - currently none.
+ */
+static int
+init_mbuf_pools(void)
+{
+       const unsigned int num_mbufs = (num_nodes * MBUFS_PER_NODE) +
+                       (info->num_ports * MBUFS_PER_PORT);
+
+       /*
+        * Don't pass single-producer/single-consumer flags to mbuf create as it
+        * seems faster to use a cache instead
+        */
+       printf("Creating mbuf pool '%s' [%u mbufs] ...\n",
+                       PKTMBUF_POOL_NAME, num_mbufs);
+       pktmbuf_pool = rte_pktmbuf_pool_create(PKTMBUF_POOL_NAME, num_mbufs,
+               MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
+
+       return pktmbuf_pool == NULL; /* 0  on success */
+}
+
+/**
+ * Initialise an individual port:
+ * - configure number of rx and tx rings
+ * - set up each rx ring, to pull from the main mbuf pool
+ * - set up each tx ring
+ * - start the port and report its status to stdout
+ */
+static int
+init_port(uint8_t port_num)
+{
+       /* for port configuration all features are off by default */
+       const struct rte_eth_conf port_conf = {
+               .rxmode = {
+                       .mq_mode = ETH_MQ_RX_RSS
+               }
+       };
+       const uint16_t rx_rings = 1, tx_rings = num_nodes;
+       const uint16_t rx_ring_size = RTE_MP_RX_DESC_DEFAULT;
+       const uint16_t tx_ring_size = RTE_MP_TX_DESC_DEFAULT;
+
+       uint16_t q;
+       int retval;
+
+       printf("Port %u init ... ", (unsigned int)port_num);
+       fflush(stdout);
+
+       /*
+        * Standard DPDK port initialisation - config port, then set up
+        * rx and tx rings.
+        */
+       retval = rte_eth_dev_configure(port_num, rx_rings, tx_rings, &port_conf);
+       if (retval != 0)
+               return retval;
+
+       for (q = 0; q < rx_rings; q++) {
+               retval = rte_eth_rx_queue_setup(port_num, q, rx_ring_size,
+                               rte_eth_dev_socket_id(port_num),
+                               NULL, pktmbuf_pool);
+               if (retval < 0)
+                       return retval;
+       }
+
+       for (q = 0; q < tx_rings; q++) {
+               retval = rte_eth_tx_queue_setup(port_num, q, tx_ring_size,
+                               rte_eth_dev_socket_id(port_num),
+                               NULL);
+               if (retval < 0)
+                       return retval;
+       }
+
+       rte_eth_promiscuous_enable(port_num);
+
+       retval = rte_eth_dev_start(port_num);
+       if (retval < 0)
+               return retval;
+
+       printf("done:\n");
+
+       return 0;
+}
+
+/**
+ * Set up the DPDK rings which will be used to pass packets, via
+ * pointers, between the multi-process server and node processes.
+ * Each node needs one RX queue.
+ */
+static int
+init_shm_rings(void)
+{
+       unsigned int i;
+       unsigned int socket_id;
+       const char *q_name;
+       const unsigned int ringsize = NODE_QUEUE_RINGSIZE;
+
+       nodes = rte_malloc("node details",
+               sizeof(*nodes) * num_nodes, 0);
+       if (nodes == NULL)
+               rte_exit(EXIT_FAILURE, "Cannot allocate memory for "
+                               "node program details\n");
+
+       for (i = 0; i < num_nodes; i++) {
+               /* Create an RX queue for each node */
+               socket_id = rte_socket_id();
+               q_name = get_rx_queue_name(i);
+               nodes[i].rx_q = rte_ring_create(q_name,
+                               ringsize, socket_id,
+                               RING_F_SP_ENQ | RING_F_SC_DEQ);
+               if (nodes[i].rx_q == NULL)
+                       rte_exit(EXIT_FAILURE, "Cannot create rx ring queue "
+                                       "for node %u\n", i);
+       }
+       return 0;
+}
+
+/*
+ * Create EFD table which will contain all the flows
+ * that will be distributed among the nodes
+ */
+static void
+create_efd_table(void)
+{
+       uint8_t socket_id = rte_socket_id();
+
+       /* create table */
+       efd_table = rte_efd_create("flow table", num_flows * 2, sizeof(uint32_t),
+                       1 << socket_id, socket_id);
+
+       if (efd_table == NULL)
+               rte_exit(EXIT_FAILURE, "Problem creating the flow table\n");
+}
+
+static void
+populate_efd_table(void)
+{
+       unsigned int i;
+       int32_t ret;
+       uint32_t ip_dst;
+       uint8_t socket_id = rte_socket_id();
+       uint64_t node_id;
+
+       /* Add flows in table */
+       for (i = 0; i < num_flows; i++) {
+               node_id = i % num_nodes;
+
+               ip_dst = rte_cpu_to_be_32(i);
+               ret = rte_efd_update(efd_table, socket_id,
+                               (void *)&ip_dst, (efd_value_t)node_id);
+               if (ret < 0)
+                       rte_exit(EXIT_FAILURE, "Unable to add entry %u in "
+                                       "EFD table\n", i);
+       }
+
+       printf("EFD table: Adding 0x%x keys\n", num_flows);
+}
+
+/* Check the link status of all ports in up to 9s, and print them finally */
+static void
+check_all_ports_link_status(uint8_t port_num, uint32_t port_mask)
+{
+#define CHECK_INTERVAL 100 /* 100ms */
+#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
+       uint8_t portid, count, all_ports_up, print_flag = 0;
+       struct rte_eth_link link;
+
+       printf("\nChecking link status");
+       fflush(stdout);
+       for (count = 0; count <= MAX_CHECK_TIME; count++) {
+               all_ports_up = 1;
+               for (portid = 0; portid < port_num; portid++) {
+                       if ((port_mask & (1 << info->id[portid])) == 0)
+                               continue;
+                       memset(&link, 0, sizeof(link));
+                       rte_eth_link_get_nowait(info->id[portid], &link);
+                       /* print link status if flag set */
+                       if (print_flag == 1) {
+                               if (link.link_status)
+                                       printf("Port %d Link Up - speed %u "
+                                               "Mbps - %s\n", info->id[portid],
+                                               (unsigned int)link.link_speed,
+                               (link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
+                                       ("full-duplex") : ("half-duplex\n"));
+                               else
+                                       printf("Port %d Link Down\n",
+                                               (uint8_t)info->id[portid]);
+                               continue;
+                       }
+                       /* clear all_ports_up flag if any link down */
+                       if (link.link_status == ETH_LINK_DOWN) {
+                               all_ports_up = 0;
+                               break;
+                       }
+               }
+               /* after finally printing all link status, get out */
+               if (print_flag == 1)
+                       break;
+
+               if (all_ports_up == 0) {
+                       printf(".");
+                       fflush(stdout);
+                       rte_delay_ms(CHECK_INTERVAL);
+               }
+
+               /* set the print_flag if all ports up or timeout */
+               if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
+                       print_flag = 1;
+                       printf("done\n");
+               }
+       }
+}
+
+/**
+ * Main init function for the multi-process server app,
+ * calls subfunctions to do each stage of the initialisation.
+ */
+int
+init(int argc, char *argv[])
+{
+       int retval;
+       const struct rte_memzone *mz;
+       uint8_t i, total_ports;
+
+       /* init EAL, parsing EAL args */
+       retval = rte_eal_init(argc, argv);
+       if (retval < 0)
+               return -1;
+       argc -= retval;
+       argv += retval;
+
+       /* get total number of ports */
+       total_ports = rte_eth_dev_count();
+
+       /* set up array for port data */
+       mz = rte_memzone_reserve(MZ_SHARED_INFO, sizeof(*info),
+                               rte_socket_id(), NO_FLAGS);
+       if (mz == NULL)
+               rte_exit(EXIT_FAILURE, "Cannot reserve memory zone "
+                               "for port information\n");
+       memset(mz->addr, 0, sizeof(*info));
+       info = mz->addr;
+
+       /* parse additional, application arguments */
+       retval = parse_app_args(total_ports, argc, argv);
+       if (retval != 0)
+               return -1;
+
+       /* initialise mbuf pools */
+       retval = init_mbuf_pools();
+       if (retval != 0)
+               rte_exit(EXIT_FAILURE, "Cannot create needed mbuf pools\n");
+
+       /* now initialise the ports we will use */
+       for (i = 0; i < info->num_ports; i++) {
+               retval = init_port(info->id[i]);
+               if (retval != 0)
+                       rte_exit(EXIT_FAILURE, "Cannot initialise port %u\n",
+                                       (unsigned int) i);
+       }
+
+       check_all_ports_link_status(info->num_ports, (~0x0));
+
+       /* initialise the node queues/rings for inter-eu comms */
+       init_shm_rings();
+
+       /* Create the EFD table */
+       create_efd_table();
+
+       /* Populate the EFD table */
+       populate_efd_table();
+
+       /* Share the total number of nodes */
+       info->num_nodes = num_nodes;
+
+       /* Share the total number of flows */
+       info->num_flows = num_flows;
+       return 0;
+}
diff --git a/examples/server_node_efd/server/init.h b/examples/server_node_efd/server/init.h
new file mode 100644 (file)
index 0000000..8dc5885
--- /dev/null
@@ -0,0 +1,76 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#ifndef _INIT_H_
+#define _INIT_H_
+
+/*
+ * #include <rte_ring.h>
+ * #include "args.h"
+ */
+
+/*
+ * Define a node structure with all needed info, including
+ * stats from the nodes.
+ */
+struct node {
+       struct rte_ring *rx_q;
+       unsigned int node_id;
+       /* these stats hold how many packets the node will actually receive,
+        * and how many packets were dropped because the node's queue was full.
+        * The port-info stats, in contrast, record how many packets were received
+        * or transmitted on an actual NIC port.
+        */
+       struct {
+               uint64_t rx;
+               uint64_t rx_drop;
+       } stats;
+};
+
+extern struct rte_efd_table *efd_table;
+extern struct node *nodes;
+
+/*
+ * shared information between server and nodes: number of nodes,
+ * port numbers, rx and tx stats etc.
+ */
+extern struct shared_info *info;
+
+extern struct rte_mempool *pktmbuf_pool;
+extern uint8_t num_nodes;
+extern unsigned int num_sockets;
+extern uint32_t num_flows;
+
+int init(int argc, char *argv[]);
+
+#endif /* ifndef _INIT_H_ */
diff --git a/examples/server_node_efd/server/main.c b/examples/server_node_efd/server/main.c
new file mode 100644 (file)
index 0000000..1a54d1b
--- /dev/null
@@ -0,0 +1,362 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <inttypes.h>
+#include <sys/queue.h>
+#include <errno.h>
+#include <netinet/ip.h>
+
+#include <rte_common.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_eal.h>
+#include <rte_byteorder.h>
+#include <rte_launch.h>
+#include <rte_per_lcore.h>
+#include <rte_lcore.h>
+#include <rte_branch_prediction.h>
+#include <rte_atomic.h>
+#include <rte_ring.h>
+#include <rte_log.h>
+#include <rte_debug.h>
+#include <rte_mempool.h>
+#include <rte_memcpy.h>
+#include <rte_mbuf.h>
+#include <rte_ether.h>
+#include <rte_interrupts.h>
+#include <rte_pci.h>
+#include <rte_ethdev.h>
+#include <rte_byteorder.h>
+#include <rte_malloc.h>
+#include <rte_string_fns.h>
+#include <rte_efd.h>
+#include <rte_ip.h>
+
+#include "common.h"
+#include "args.h"
+#include "init.h"
+
+/*
+ * When doing reads from the NIC or the node queues,
+ * use this batch size
+ */
+#define PACKET_READ_SIZE 32
+
+/*
+ * Local buffers to put packets in, used to send packets in bursts to the
+ * nodes
+ */
+struct node_rx_buf {
+       struct rte_mbuf *buffer[PACKET_READ_SIZE];
+       uint16_t count;
+};
+
+struct efd_stats {
+       uint64_t distributed;
+       uint64_t drop;
+} flow_dist_stats;
+
+/* One buffer per node rx queue - dynamically allocate array */
+static struct node_rx_buf *cl_rx_buf;
+
+static const char *
+get_printable_mac_addr(uint8_t port)
+{
+       static const char err_address[] = "00:00:00:00:00:00";
+       static char addresses[RTE_MAX_ETHPORTS][sizeof(err_address)];
+       struct ether_addr mac;
+
+       if (unlikely(port >= RTE_MAX_ETHPORTS))
+               return err_address;
+       if (unlikely(addresses[port][0] == '\0')) {
+               rte_eth_macaddr_get(port, &mac);
+               snprintf(addresses[port], sizeof(addresses[port]),
+                               "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                               mac.addr_bytes[0], mac.addr_bytes[1],
+                               mac.addr_bytes[2], mac.addr_bytes[3],
+                               mac.addr_bytes[4], mac.addr_bytes[5]);
+       }
+       return addresses[port];
+}
+
+/*
+ * This function displays the recorded statistics for each port
+ * and for each node. It uses ANSI terminal codes to clear
+ * screen when called. It is called from a single non-master
+ * thread in the server process, when the process is run with more
+ * than one lcore enabled.
+ */
+static void
+do_stats_display(void)
+{
+       unsigned int i, j;
+       const char clr[] = {27, '[', '2', 'J', '\0'};
+       const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'};
+       uint64_t port_tx[RTE_MAX_ETHPORTS], port_tx_drop[RTE_MAX_ETHPORTS];
+       uint64_t node_tx[MAX_NODES], node_tx_drop[MAX_NODES];
+
+       /* to get TX stats, we need to do some summing calculations */
+       memset(port_tx, 0, sizeof(port_tx));
+       memset(port_tx_drop, 0, sizeof(port_tx_drop));
+       memset(node_tx, 0, sizeof(node_tx));
+       memset(node_tx_drop, 0, sizeof(node_tx_drop));
+
+       for (i = 0; i < num_nodes; i++) {
+               const struct tx_stats *tx = &info->tx_stats[i];
+
+               for (j = 0; j < info->num_ports; j++) {
+                       const uint64_t tx_val = tx->tx[info->id[j]];
+                       const uint64_t drop_val = tx->tx_drop[info->id[j]];
+
+                       port_tx[j] += tx_val;
+                       port_tx_drop[j] += drop_val;
+                       node_tx[i] += tx_val;
+                       node_tx_drop[i] += drop_val;
+               }
+       }
+
+       /* Clear screen and move to top left */
+       printf("%s%s", clr, topLeft);
+
+       printf("PORTS\n");
+       printf("-----\n");
+       for (i = 0; i < info->num_ports; i++)
+               printf("Port %u: '%s'\t", (unsigned int)info->id[i],
+                               get_printable_mac_addr(info->id[i]));
+       printf("\n\n");
+       for (i = 0; i < info->num_ports; i++) {
+               printf("Port %u - rx: %9"PRIu64"\t"
+                               "tx: %9"PRIu64"\n",
+                               (unsigned int)info->id[i], info->rx_stats.rx[i],
+                               port_tx[i]);
+       }
+
+       printf("\nSERVER\n");
+       printf("-----\n");
+       printf("distributed: %9"PRIu64", drop: %9"PRIu64"\n",
+                       flow_dist_stats.distributed, flow_dist_stats.drop);
+
+       printf("\nNODES\n");
+       printf("-------\n");
+       for (i = 0; i < num_nodes; i++) {
+               const unsigned long long rx = nodes[i].stats.rx;
+               const unsigned long long rx_drop = nodes[i].stats.rx_drop;
+               const struct filter_stats *filter = &info->filter_stats[i];
+
+               printf("Node %2u - rx: %9llu, rx_drop: %9llu\n"
+                               "            tx: %9"PRIu64", tx_drop: %9"PRIu64"\n"
+                               "            filter_passed: %9"PRIu64", "
+                               "filter_drop: %9"PRIu64"\n",
+                               i, rx, rx_drop, node_tx[i], node_tx_drop[i],
+                               filter->passed, filter->drop);
+       }
+
+       printf("\n");
+}
+
+/*
+ * The function called from each non-master lcore used by the process.
+ * The test_and_set function is used to randomly pick a single lcore on which
+ * the code to display the statistics will run. Otherwise, the code just
+ * repeatedly sleeps.
+ */
+static int
+sleep_lcore(__attribute__((unused)) void *dummy)
+{
+       /* Used to pick a display thread - static, so zero-initialised */
+       static rte_atomic32_t display_stats;
+
+       /* Only one core should display stats */
+       if (rte_atomic32_test_and_set(&display_stats)) {
+               const unsigned int sleeptime = 1;
+
+               printf("Core %u displaying statistics\n", rte_lcore_id());
+
+               /* Longer initial pause so above printf is seen */
+               sleep(sleeptime * 3);
+
+               /* Loop forever: sleep always returns 0 or <= param */
+               while (sleep(sleeptime) <= sleeptime)
+                       do_stats_display();
+       }
+       return 0;
+}
+
+/*
+ * Function to set all the node statistic values to zero.
+ * Called at program startup.
+ */
+static void
+clear_stats(void)
+{
+       unsigned int i;
+
+       for (i = 0; i < num_nodes; i++)
+               nodes[i].stats.rx = nodes[i].stats.rx_drop = 0;
+}
+
+/*
+ * send a burst of traffic to a node, assuming there are packets
+ * available to be sent to this node
+ */
+static void
+flush_rx_queue(uint16_t node)
+{
+       uint16_t j;
+       struct node *cl;
+
+       if (cl_rx_buf[node].count == 0)
+               return;
+
+       cl = &nodes[node];
+       if (rte_ring_enqueue_bulk(cl->rx_q, (void **)cl_rx_buf[node].buffer,
+                       cl_rx_buf[node].count) != 0){
+               for (j = 0; j < cl_rx_buf[node].count; j++)
+                       rte_pktmbuf_free(cl_rx_buf[node].buffer[j]);
+               cl->stats.rx_drop += cl_rx_buf[node].count;
+       } else
+               cl->stats.rx += cl_rx_buf[node].count;
+
+       cl_rx_buf[node].count = 0;
+}
+
+/*
+ * marks a packet down to be sent to a particular node process
+ */
+static inline void
+enqueue_rx_packet(uint8_t node, struct rte_mbuf *buf)
+{
+       cl_rx_buf[node].buffer[cl_rx_buf[node].count++] = buf;
+}
+
+/*
+ * This function takes a group of packets and routes them
+ * individually to the node process. Very simply round-robins the packets
+ * without checking any of the packet contents.
+ */
+static void
+process_packets(uint32_t port_num __rte_unused, struct rte_mbuf *pkts[],
+               uint16_t rx_count, unsigned int socket_id)
+{
+       uint16_t i;
+       uint8_t node;
+       efd_value_t data[RTE_EFD_BURST_MAX];
+       const void *key_ptrs[RTE_EFD_BURST_MAX];
+
+       struct ipv4_hdr *ipv4_hdr;
+       uint32_t ipv4_dst_ip[RTE_EFD_BURST_MAX];
+
+       for (i = 0; i < rx_count; i++) {
+               /* Handle IPv4 header.*/
+               ipv4_hdr = rte_pktmbuf_mtod_offset(pkts[i], struct ipv4_hdr *,
+                               sizeof(struct ether_hdr));
+               ipv4_dst_ip[i] = ipv4_hdr->dst_addr;
+               key_ptrs[i] = (void *)&ipv4_dst_ip[i];
+       }
+
+       rte_efd_lookup_bulk(efd_table, socket_id, rx_count,
+                               (const void **) key_ptrs, data);
+       for (i = 0; i < rx_count; i++) {
+               node = (uint8_t) ((uintptr_t)data[i]);
+
+               if (node >= num_nodes) {
+                       /*
+                        * Node is out of range, which means that
+                        * flow has not been inserted
+                        */
+                       flow_dist_stats.drop++;
+                       rte_pktmbuf_free(pkts[i]);
+               } else {
+                       flow_dist_stats.distributed++;
+                       enqueue_rx_packet(node, pkts[i]);
+               }
+       }
+
+       for (i = 0; i < num_nodes; i++)
+               flush_rx_queue(i);
+}
+
+/*
+ * Function called by the master lcore of the DPDK process.
+ */
+static void
+do_packet_forwarding(void)
+{
+       unsigned int port_num = 0; /* indexes the port[] array */
+       unsigned int socket_id = rte_socket_id();
+
+       for (;;) {
+               struct rte_mbuf *buf[PACKET_READ_SIZE];
+               uint16_t rx_count;
+
+               /* read a port */
+               rx_count = rte_eth_rx_burst(info->id[port_num], 0,
+                               buf, PACKET_READ_SIZE);
+               info->rx_stats.rx[port_num] += rx_count;
+
+               /* Now process the NIC packets read */
+               if (likely(rx_count > 0))
+                       process_packets(port_num, buf, rx_count, socket_id);
+
+               /* move to next port */
+               if (++port_num == info->num_ports)
+                       port_num = 0;
+       }
+}
+
+int
+main(int argc, char *argv[])
+{
+       /* initialise the system */
+       if (init(argc, argv) < 0)
+               return -1;
+       RTE_LOG(INFO, APP, "Finished Process Init.\n");
+
+       cl_rx_buf = calloc(num_nodes, sizeof(cl_rx_buf[0]));
+
+       /* clear statistics */
+       clear_stats();
+
+       /* put all other cores to sleep bar master */
+       rte_eal_mp_remote_launch(sleep_lcore, NULL, SKIP_MASTER);
+
+       do_packet_forwarding();
+       return 0;
+}
diff --git a/examples/server_node_efd/shared/common.h b/examples/server_node_efd/shared/common.h
new file mode 100644 (file)
index 0000000..8a13479
--- /dev/null
@@ -0,0 +1,99 @@
+/*-
+ *   BSD LICENSE
+ *
+ *   Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *   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.
+ *     * Neither the name of Intel Corporation nor the names of its
+ *       contributors may 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.
+ */
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <rte_hash_crc.h>
+#include <rte_hash.h>
+
+#define MAX_NODES             16
+/*
+ * Shared port info, including statistics information for display by server.
+ * Structure will be put in a memzone.
+ * - All port id values share one cache line as this data will be read-only
+ * during operation.
+ * - All rx statistic values share cache lines, as this data is written only
+ * by the server process. (rare reads by stats display)
+ * - The tx statistics have values for all ports per cache line, but the stats
+ * themselves are written by the nodes, so we have a distinct set, on different
+ * cache lines for each node to use.
+ */
+struct rx_stats {
+       uint64_t rx[RTE_MAX_ETHPORTS];
+} __rte_cache_aligned;
+
+struct tx_stats {
+       uint64_t tx[RTE_MAX_ETHPORTS];
+       uint64_t tx_drop[RTE_MAX_ETHPORTS];
+} __rte_cache_aligned;
+
+struct filter_stats {
+       uint64_t drop;
+       uint64_t passed;
+} __rte_cache_aligned;
+
+struct shared_info {
+       uint8_t num_nodes;
+       uint8_t num_ports;
+       uint32_t num_flows;
+       uint8_t id[RTE_MAX_ETHPORTS];
+       struct rx_stats rx_stats;
+       struct tx_stats tx_stats[MAX_NODES];
+       struct filter_stats filter_stats[MAX_NODES];
+};
+
+/* define common names for structures shared between server and node */
+#define MP_NODE_RXQ_NAME "MProc_Node_%u_RX"
+#define PKTMBUF_POOL_NAME "MProc_pktmbuf_pool"
+#define MZ_SHARED_INFO "MProc_shared_info"
+
+/*
+ * Given the rx queue name template above, get the queue name
+ */
+static inline const char *
+get_rx_queue_name(unsigned int id)
+{
+       /*
+        * Buffer for return value. Size calculated by %u being replaced
+        * by maximum 3 digits (plus an extra byte for safety)
+        */
+       static char buffer[sizeof(MP_NODE_RXQ_NAME) + 2];
+
+       snprintf(buffer, sizeof(buffer) - 1, MP_NODE_RXQ_NAME, id);
+       return buffer;
+}
+
+#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
+
+#endif