hash: add predictable RSS
authorVladimir Medvedkin <vladimir.medvedkin@intel.com>
Mon, 19 Apr 2021 15:59:51 +0000 (16:59 +0100)
committerThomas Monjalon <thomas@monjalon.net>
Tue, 20 Apr 2021 21:13:23 +0000 (23:13 +0200)
This patch adds predictable RSS API.
It is based on the idea of searching partial Toeplitz hash collisions.

Signed-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
Acked-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
Acked-by: Yipeng Wang <yipeng1.wang@intel.com>
Acked-by: John McNamara <john.mcnamara@intel.com>
MAINTAINERS
app/test/test_thash.c
doc/guides/prog_guide/img/predictable_snat_1.svg [new file with mode: 0644]
doc/guides/prog_guide/img/predictable_snat_2.svg [new file with mode: 0644]
doc/guides/prog_guide/toeplitz_hash_lib.rst
doc/guides/rel_notes/release_21_05.rst
lib/librte_hash/meson.build
lib/librte_hash/rte_thash.c [new file with mode: 0644]
lib/librte_hash/rte_thash.h
lib/librte_hash/version.map

index aa4dbbc..35a87e8 100644 (file)
@@ -1430,6 +1430,7 @@ Hashes
 M: Yipeng Wang <yipeng1.wang@intel.com>
 M: Sameh Gobriel <sameh.gobriel@intel.com>
 M: Bruce Richardson <bruce.richardson@intel.com>
+M: Vladimir Medvedkin <vladimir.medvedkin@intel.com>
 F: lib/librte_hash/
 F: doc/guides/prog_guide/hash_lib.rst
 F: doc/guides/prog_guide/toeplitz_hash_lib.rst
index a6aadd1..d8981fb 100644 (file)
@@ -5,11 +5,15 @@
 #include <rte_common.h>
 #include <rte_eal.h>
 #include <rte_ip.h>
+#include <rte_random.h>
 
 #include "test.h"
 
 #include <rte_thash.h>
 
+#define HASH_MSK(reta_sz)      ((1 << reta_sz) - 1)
+#define TUPLE_SZ       (RTE_THASH_V4_L4_LEN * 4)
+
 struct test_thash_v4 {
        uint32_t        dst_ip;
        uint32_t        src_ip;
@@ -75,7 +79,7 @@ uint8_t default_rss_key[] = {
 };
 
 static int
-test_thash(void)
+test_toeplitz_hash_calc(void)
 {
        uint32_t i, j;
        union rte_thash_tuple tuple;
@@ -100,7 +104,7 @@ test_thash(void)
                                RTE_THASH_V4_L4_LEN, default_rss_key);
                if ((rss_l3 != v4_tbl[i].hash_l3) ||
                                (rss_l3l4 != v4_tbl[i].hash_l3l4))
-                       return -1;
+                       return -TEST_FAILED;
                /*Calculate hash with converted key*/
                rss_l3 = rte_softrss_be((uint32_t *)&tuple,
                                RTE_THASH_V4_L3_LEN, rss_key_be);
@@ -108,7 +112,7 @@ test_thash(void)
                                RTE_THASH_V4_L4_LEN, rss_key_be);
                if ((rss_l3 != v4_tbl[i].hash_l3) ||
                                (rss_l3l4 != v4_tbl[i].hash_l3l4))
-                       return -1;
+                       return -TEST_FAILED;
        }
        for (i = 0; i < RTE_DIM(v6_tbl); i++) {
                /*Fill ipv6 hdr*/
@@ -127,7 +131,7 @@ test_thash(void)
                                RTE_THASH_V6_L4_LEN, default_rss_key);
                if ((rss_l3 != v6_tbl[i].hash_l3) ||
                                (rss_l3l4 != v6_tbl[i].hash_l3l4))
-                       return -1;
+                       return -TEST_FAILED;
                /*Calculate hash with converted key*/
                rss_l3 = rte_softrss_be((uint32_t *)&tuple,
                                RTE_THASH_V6_L3_LEN, rss_key_be);
@@ -135,9 +139,462 @@ test_thash(void)
                                RTE_THASH_V6_L4_LEN, rss_key_be);
                if ((rss_l3 != v6_tbl[i].hash_l3) ||
                                (rss_l3l4 != v6_tbl[i].hash_l3l4))
-                       return -1;
+                       return -TEST_FAILED;
+       }
+       return TEST_SUCCESS;
+}
+
+static int
+test_create_invalid(void)
+{
+       struct rte_thash_ctx *ctx;
+       int key_len = 40;
+       int reta_sz = 7;
+
+       ctx = rte_thash_init_ctx(NULL, key_len, reta_sz, NULL, 0);
+       RTE_TEST_ASSERT(ctx == NULL,
+               "Call succeeded with invalid parameters\n");
+
+       ctx = rte_thash_init_ctx("test", 0, reta_sz, NULL, 0);
+       RTE_TEST_ASSERT(ctx == NULL,
+               "Call succeeded with invalid parameters\n");
+
+       ctx = rte_thash_init_ctx(NULL, key_len, 1, NULL, 0);
+       RTE_TEST_ASSERT(ctx == NULL,
+               "Call succeeded with invalid parameters\n");
+
+       ctx = rte_thash_init_ctx(NULL, key_len, 17, NULL, 0);
+       RTE_TEST_ASSERT(ctx == NULL,
+               "Call succeeded with invalid parameters\n");
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_multiple_create(void)
+{
+       struct rte_thash_ctx *ctx;
+       int key_len = 40;
+       int reta_sz = 7;
+       int i;
+
+       for (i = 0; i < 100; i++) {
+               ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
+               RTE_TEST_ASSERT(ctx != NULL, "Can not create CTX\n");
+
+               rte_thash_free_ctx(ctx);
+       }
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_free_null(void)
+{
+       struct rte_thash_ctx *ctx;
+
+       ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "Can not create CTX\n");
+
+       rte_thash_free_ctx(ctx);
+       rte_thash_free_ctx(NULL);
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_add_invalid_helper(void)
+{
+       struct rte_thash_ctx *ctx;
+       const int key_len = 40;
+       int reta_sz = 7;
+       int ret;
+
+       ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
+
+       ret = rte_thash_add_helper(NULL, "test", reta_sz, 0);
+       RTE_TEST_ASSERT(ret == -EINVAL,
+               "Call succeeded with invalid parameters\n");
+
+       ret = rte_thash_add_helper(ctx, NULL, reta_sz, 0);
+       RTE_TEST_ASSERT(ret == -EINVAL,
+               "Call succeeded with invalid parameters\n");
+
+       ret = rte_thash_add_helper(ctx, "test", reta_sz - 1, 0);
+       RTE_TEST_ASSERT(ret == -EINVAL,
+               "Call succeeded with invalid parameters\n");
+
+       ret = rte_thash_add_helper(ctx, "test", reta_sz, key_len * 8);
+       RTE_TEST_ASSERT(ret == -EINVAL,
+               "Call succeeded with invalid parameters\n");
+
+       ret = rte_thash_add_helper(ctx, "first_range", reta_sz, 0);
+       RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
+
+       ret = rte_thash_add_helper(ctx, "first_range", reta_sz, 0);
+       RTE_TEST_ASSERT(ret == -EEXIST,
+               "Call succeeded with duplicated name\n");
+
+       /*
+        * Create second helper with offset 3 * reta_sz.
+        * Note first_range helper created range in key:
+        * [0, 32 + length{= reta_sz} - 1), i.e [0, 37).
+        * second range is [44, 81)
+        */
+       ret = rte_thash_add_helper(ctx, "second_range", reta_sz,
+               32 +  2 * reta_sz);
+       RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
+
+       /*
+        * Try to create overlapping with first_ and second_ ranges,
+        * i.e. [6, 49)
+        */
+       ret = rte_thash_add_helper(ctx, "third_range", 2 * reta_sz, reta_sz);
+       RTE_TEST_ASSERT(ret == -EEXIST,
+               "Call succeeded with overlapping ranges\n");
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_find_existing(void)
+{
+       struct rte_thash_ctx *ctx, *ret_ctx;
+
+       ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
+
+       ret_ctx = rte_thash_find_existing("test");
+       RTE_TEST_ASSERT(ret_ctx != NULL, "can not find existing ctx\n");
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_get_helper(void)
+{
+       struct rte_thash_ctx *ctx;
+       struct rte_thash_subtuple_helper *h;
+       int ret;
+
+       ctx = rte_thash_init_ctx("test", 40, 7, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n");
+
+       h = rte_thash_get_helper(NULL, "first_range");
+       RTE_TEST_ASSERT(h == NULL, "Call succeeded with invalid parameters\n");
+
+       h = rte_thash_get_helper(ctx, NULL);
+       RTE_TEST_ASSERT(h == NULL, "Call succeeded with invalid parameters\n");
+
+       ret = rte_thash_add_helper(ctx, "first_range", 8, 0);
+       RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
+
+       h = rte_thash_get_helper(ctx, "first_range");
+       RTE_TEST_ASSERT(h != NULL, "Can not find helper\n");
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_period_overflow(void)
+{
+       struct rte_thash_ctx *ctx;
+       int reta_sz = 7; /* reflects polynomial degree */
+       int ret;
+
+       /* first create without RTE_THASH_IGNORE_PERIOD_OVERFLOW flag */
+       ctx = rte_thash_init_ctx("test", 40, reta_sz, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n");
+
+       /* requested range > (2^reta_sz) - 1 */
+       ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz), 0);
+       RTE_TEST_ASSERT(ret == -ENOSPC,
+               "Call succeeded with invalid parameters\n");
+
+       /* requested range == len + 32 - 1, smaller than (2^reta_sz) - 1 */
+       ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz) - 32, 0);
+       RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
+
+       rte_thash_free_ctx(ctx);
+
+       /* create with RTE_THASH_IGNORE_PERIOD_OVERFLOW flag */
+       ctx = rte_thash_init_ctx("test", 40, reta_sz, NULL,
+               RTE_THASH_IGNORE_PERIOD_OVERFLOW);
+       RTE_TEST_ASSERT(ctx != NULL, "Can not create thash ctx\n");
+
+       /* requested range > (2^reta_sz - 1) */
+       ret = rte_thash_add_helper(ctx, "test", (1 << reta_sz) + 10, 0);
+       RTE_TEST_ASSERT(ret == 0, "Can not create helper\n");
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+static int
+test_predictable_rss_min_seq(void)
+{
+       struct rte_thash_ctx *ctx;
+       struct rte_thash_subtuple_helper *h;
+       const int key_len = 40;
+       int reta_sz = 6;
+       uint8_t initial_key[key_len];
+       const uint8_t *new_key;
+       int ret;
+       union rte_thash_tuple tuple;
+       uint32_t orig_hash, adj_hash, adj;
+       unsigned int desired_value = 27 & HASH_MSK(reta_sz);
+       uint16_t port_value = 22;
+
+       memset(initial_key, 0, key_len);
+
+       ctx = rte_thash_init_ctx("test", key_len, reta_sz, initial_key,
+               RTE_THASH_MINIMAL_SEQ);
+       RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
+
+       ret = rte_thash_add_helper(ctx, "snat", sizeof(uint16_t) * 8,
+               offsetof(union rte_thash_tuple, v4.sport) * 8);
+       RTE_TEST_ASSERT(ret == 0, "can not add helper, ret %d\n", ret);
+
+       h = rte_thash_get_helper(ctx, "snat");
+       RTE_TEST_ASSERT(h != NULL, "can not find helper\n");
+
+       new_key = rte_thash_get_key(ctx);
+       tuple.v4.src_addr = RTE_IPV4(0, 0, 0, 0);
+       tuple.v4.dst_addr = RTE_IPV4(0, 0, 0, 0);
+       tuple.v4.sport = 0;
+       tuple.v4.sport = rte_cpu_to_be_16(port_value);
+       tuple.v4.dport = 0;
+       tuple.v4.sctp_tag = rte_be_to_cpu_32(tuple.v4.sctp_tag);
+
+       orig_hash = rte_softrss((uint32_t *)&tuple,
+               RTE_THASH_V4_L4_LEN, new_key);
+       adj = rte_thash_get_complement(h, orig_hash, desired_value);
+
+       tuple.v4.sctp_tag = rte_cpu_to_be_32(tuple.v4.sctp_tag);
+       tuple.v4.sport ^= rte_cpu_to_be_16(adj);
+       tuple.v4.sctp_tag = rte_be_to_cpu_32(tuple.v4.sctp_tag);
+
+       adj_hash = rte_softrss((uint32_t *)&tuple,
+               RTE_THASH_V4_L4_LEN, new_key);
+       RTE_TEST_ASSERT((adj_hash & HASH_MSK(reta_sz)) ==
+               desired_value, "bad desired value\n");
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+/*
+ * This test creates 7 subranges in the following order:
+ * range_one   = [56, 95),     len = 8, offset = 56
+ * range_two   = [64, 103),    len = 8, offset = 64
+ * range_three = [120, 159),   len = 8, offset = 120
+ * range_four  = [48, 87),     len = 8, offset = 48
+ * range_five  = [57, 95),     len = 7, offset = 57
+ * range_six   = [40, 111),    len = 40, offset = 40
+ * range_seven = [0, 39),      len = 8, offset = 0
+ */
+struct range {
+       const char *name;
+       int len;
+       int offset;
+       int byte_idx;
+};
+
+struct range rng_arr[] = {
+       {"one",   8,  56,  7},
+       {"two",   8,  64,  8},
+       {"three", 8,  120, 15},
+       {"four",  8,  48,  6},
+       {"six",   40, 40,  9},
+       {"five",  7,  57,  7},
+       {"seven", 8,  0,   0}
+};
+
+static int
+test_predictable_rss_multirange(void)
+{
+       struct rte_thash_ctx *ctx;
+       struct rte_thash_subtuple_helper *h[RTE_DIM(rng_arr)];
+       const uint8_t *new_key;
+       const int key_len = 40;
+       int reta_sz = 7;
+       unsigned int i, j, k;
+       int ret;
+       uint32_t desired_value = rte_rand() & HASH_MSK(reta_sz);
+       uint8_t tuples[RTE_DIM(rng_arr)][16] = { {0} };
+       uint32_t *ptr;
+       uint32_t hashes[RTE_DIM(rng_arr)];
+       uint32_t adj_hashes[RTE_DIM(rng_arr)];
+       uint32_t adj;
+
+       ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
+
+       for (i = 0; i < RTE_DIM(rng_arr); i++) {
+               ret = rte_thash_add_helper(ctx, rng_arr[i].name,
+                       rng_arr[i].len, rng_arr[i].offset);
+               RTE_TEST_ASSERT(ret == 0, "can not add helper\n");
+
+               h[i] = rte_thash_get_helper(ctx, rng_arr[i].name);
+               RTE_TEST_ASSERT(h[i] != NULL, "can not find helper\n");
+       }
+       new_key = rte_thash_get_key(ctx);
+
+       /*
+        * calculate hashes, complements, then adjust keys with
+        * complements and recalsulate hashes
+        */
+       for (i = 0; i < RTE_DIM(rng_arr); i++) {
+               for (k = 0; k < 100; k++) {
+                       /* init with random keys */
+                       ptr = (uint32_t *)&tuples[i][0];
+                       for (j = 0; j < 4; j++)
+                               ptr[j] = rte_rand();
+                       /* convert keys from BE to CPU byte order */
+                       for (j = 0; j < 4; j++)
+                               ptr[j] = rte_be_to_cpu_32(ptr[j]);
+
+                       hashes[i] = rte_softrss(ptr, 4, new_key);
+                       adj = rte_thash_get_complement(h[i], hashes[i],
+                               desired_value);
+                       /* convert back to BE to adjust the value */
+                       for (j = 0; j < 4; j++)
+                               ptr[j] = rte_cpu_to_be_32(ptr[j]);
+
+                       tuples[i][rng_arr[i].byte_idx] ^= adj;
+
+                       for (j = 0; j < 4; j++)
+                               ptr[j] = rte_be_to_cpu_32(ptr[j]);
+
+                       adj_hashes[i] = rte_softrss(ptr, 4, new_key);
+                       RTE_TEST_ASSERT((adj_hashes[i] & HASH_MSK(reta_sz)) ==
+                               desired_value,
+                               "bad desired value for %d tuple\n", i);
+               }
        }
-       return 0;
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+static int
+cmp_tuple_eq(void *userdata, uint8_t *tuple)
+{
+       return memcmp(userdata, tuple, TUPLE_SZ);
+}
+
+static int
+test_adjust_tuple(void)
+{
+       struct rte_thash_ctx *ctx;
+       struct rte_thash_subtuple_helper *h;
+       const int key_len = 40;
+       const uint8_t *new_key;
+       uint8_t tuple[TUPLE_SZ];
+       uint32_t tmp_tuple[TUPLE_SZ / sizeof(uint32_t)];
+       uint32_t tuple_copy[TUPLE_SZ / sizeof(uint32_t)];
+       uint32_t hash;
+       int reta_sz = CHAR_BIT;
+       int ret;
+       unsigned int i, desired_value = rte_rand() & HASH_MSK(reta_sz);
+
+       memset(tuple, 0xab, TUPLE_SZ);
+
+       ctx = rte_thash_init_ctx("test", key_len, reta_sz, NULL, 0);
+       RTE_TEST_ASSERT(ctx != NULL, "can not create thash ctx\n");
+
+       /*
+        * set offset to be in the middle of a byte
+        * set size of the subtuple to be 2 * rets_sz
+        * to have the room for random bits
+        */
+       ret = rte_thash_add_helper(ctx, "test", reta_sz * 2,
+               (5 * CHAR_BIT) + 4);
+       RTE_TEST_ASSERT(ret == 0, "can not add helper, ret %d\n", ret);
+
+       new_key = rte_thash_get_key(ctx);
+
+       h = rte_thash_get_helper(ctx, "test");
+       RTE_TEST_ASSERT(h != NULL, "can not find helper\n");
+
+       ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value,
+               1, NULL, NULL);
+       RTE_TEST_ASSERT(ret == 0, "can not adjust tuple, ret %d\n", ret);
+
+       for (i = 0; i < (TUPLE_SZ / 4); i++)
+               tmp_tuple[i] =
+                       rte_be_to_cpu_32(*(uint32_t *)&tuple[i * 4]);
+
+       hash = rte_softrss(tmp_tuple, TUPLE_SZ / 4, new_key);
+       RTE_TEST_ASSERT((hash & HASH_MSK(reta_sz)) ==
+               desired_value, "bad desired value\n");
+
+
+       /* Pass previously calculated tuple to callback function */
+       memcpy(tuple_copy, tuple, TUPLE_SZ);
+
+       memset(tuple, 0xab, TUPLE_SZ);
+       ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value,
+               1, cmp_tuple_eq, tuple_copy);
+       RTE_TEST_ASSERT(ret == -EEXIST,
+               "adjust tuple didn't indicate collision\n");
+
+       /*
+        * Make the function to generate random bits into subtuple
+        * after first adjustment attempt.
+        */
+       memset(tuple, 0xab, TUPLE_SZ);
+       ret = rte_thash_adjust_tuple(ctx, h, tuple, TUPLE_SZ, desired_value,
+               2, cmp_tuple_eq, tuple_copy);
+       RTE_TEST_ASSERT(ret == 0, "can not adjust tuple, ret %d\n", ret);
+
+       for (i = 0; i < (TUPLE_SZ / 4); i++)
+               tmp_tuple[i] =
+                       rte_be_to_cpu_32(*(uint32_t *)&tuple[i * 4]);
+
+       hash = rte_softrss(tmp_tuple, TUPLE_SZ / 4, new_key);
+       RTE_TEST_ASSERT((hash & HASH_MSK(reta_sz)) ==
+               desired_value, "bad desired value\n");
+
+       rte_thash_free_ctx(ctx);
+
+       return TEST_SUCCESS;
+}
+
+static struct unit_test_suite thash_tests = {
+       .suite_name = "thash autotest",
+       .setup = NULL,
+       .teardown = NULL,
+       .unit_test_cases = {
+       TEST_CASE(test_toeplitz_hash_calc),
+       TEST_CASE(test_create_invalid),
+       TEST_CASE(test_multiple_create),
+       TEST_CASE(test_free_null),
+       TEST_CASE(test_add_invalid_helper),
+       TEST_CASE(test_find_existing),
+       TEST_CASE(test_get_helper),
+       TEST_CASE(test_period_overflow),
+       TEST_CASE(test_predictable_rss_min_seq),
+       TEST_CASE(test_predictable_rss_multirange),
+       TEST_CASE(test_adjust_tuple),
+       TEST_CASES_END()
+       }
+};
+
+static int
+test_thash(void)
+{
+       return unit_test_suite_runner(&thash_tests);
 }
 
 REGISTER_TEST_COMMAND(thash_autotest, test_thash);
diff --git a/doc/guides/prog_guide/img/predictable_snat_1.svg b/doc/guides/prog_guide/img/predictable_snat_1.svg
new file mode 100644 (file)
index 0000000..5f97ccb
--- /dev/null
@@ -0,0 +1,1444 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generated by Microsoft Visio, SVG Export predictable_snat_1.svg Page-4 -->
+
+<svg
+   xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="7.8211055in"
+   height="1.8973334in"
+   viewBox="0 0 563.12017 136.6082"
+   xml:space="preserve"
+   class="st14"
+   version="1.1"
+   id="svg1495"
+   sodipodi:docname="predictable_snat_1.svg"
+   style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
+   id="metadata1499"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1640"
+   inkscape:window-height="878"
+   id="namedview1497"
+   showgrid="false"
+   inkscape:zoom="0.75544421"
+   inkscape:cx="638.25085"
+   inkscape:cy="70.081592"
+   inkscape:window-x="52"
+   inkscape:window-y="52"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="svg1495" />
+       <v:documentProperties
+   v:langID="6153"
+   v:metric="true"
+   v:viewMarkup="false">
+               <v:userDefs>
+                       <v:ud
+   v:nameU="msvNoAutoConnect"
+   v:val="VT0(1):26" />
+               </v:userDefs>
+       </v:documentProperties>
+
+       <style
+   type="text/css"
+   id="style1158">
+       <![CDATA[
+               .st1 {fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0;stroke-width:0.72}
+               .st2 {fill:url(#grad0-7);stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72}
+               .st3 {fill:#ff0000;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
+               .st4 {fill:url(#grad0-19);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72}
+               .st5 {stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+               .st6 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24;visibility:hidden}
+               .st7 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
+               .st8 {fill:#000000;font-family:Calibri;font-size:0.666664em}
+               .st9 {marker-end:url(#mrkr4-70);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.239976}
+               .st10 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.086948582161584}
+               .st11 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+               .st12 {fill:#000000;font-family:Calibri;font-size:1.00001em}
+               .st13 {font-size:1em}
+               .st14 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+       ]]>
+       </style>
+
+       <defs
+   id="Patterns_And_Gradients">
+               <linearGradient
+   id="grad0-7"
+   x1="-0.2804561"
+   y1="1048.661"
+   x2="32.190596"
+   y2="1048.661"
+   gradientTransform="scale(1.2836234,0.7790447)"
+   gradientUnits="userSpaceOnUse">
+                       <stop
+   offset="0"
+   stop-color="#ffffff"
+   stop-opacity="1"
+   id="stop1160" />
+                       <stop
+   offset="1"
+   stop-color="#dfdfdf"
+   stop-opacity="1"
+   id="stop1162" />
+               </linearGradient>
+               <linearGradient
+   id="grad0-19"
+   x1="-0.30286968"
+   y1="967.01808"
+   x2="34.733349"
+   y2="967.01808"
+   gradientTransform="scale(1.1896436,0.84058789)"
+   gradientUnits="userSpaceOnUse">
+                       <stop
+   offset="0.01"
+   stop-color="#c0c0c0"
+   stop-opacity="1"
+   id="stop1165" />
+                       <stop
+   offset="0.5"
+   stop-color="#ffffff"
+   stop-opacity="1"
+   id="stop1167" />
+                       <stop
+   offset="1"
+   stop-color="#c0c0c0"
+   stop-opacity="1"
+   id="stop1169" />
+               </linearGradient>
+       </defs>
+       <defs
+   id="Markers">
+               <g
+   id="lend4">
+                       <path
+   d="M 2,1 0,0 2,-1 v 2"
+   style="stroke:none"
+   id="path1173"
+   inkscape:connector-curvature="0" />
+               </g>
+               <marker
+   id="mrkr4-70"
+   class="st10"
+   v:arrowType="4"
+   v:arrowSize="2"
+   v:setback="23.0021"
+   refX="-23.0021"
+   orient="auto"
+   markerUnits="strokeWidth"
+   overflow="visible"
+   style="overflow:visible;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.08694858;stroke-opacity:1">
+                       <use
+   xlink:href="#lend4"
+   transform="scale(-11.50105)"
+   id="use1176"
+   x="0"
+   y="0"
+   width="100%"
+   height="100%" />
+               </marker>
+       </defs>
+       <g
+   v:mID="8"
+   v:index="4"
+   v:groupContext="foregroundPage"
+   id="g1493"
+   transform="translate(-98.5864,-336.89801)">
+               <title
+   id="title1180">Page-4</title>
+               <v:pageProperties
+   v:drawingScale="0.0393701"
+   v:pageScale="0.0393701"
+   v:drawingUnits="24"
+   v:shadowOffsetX="8.50394"
+   v:shadowOffsetY="-8.50394" />
+               <v:layer
+   v:name="Connector"
+   v:index="0" />
+               <g
+   id="group1001-1"
+   transform="translate(275.811,-396.85)"
+   v:mID="1001"
+   v:groupContext="group">
+                       <v:custProps>
+                               <v:cp
+   v:nameU="ShapeClass"
+   v:lbl="ShapeClass"
+   v:type="0"
+   v:invis="true"
+   v:ask="false"
+   v:langID="1033"
+   v:val="VT4(Equipment)" />
+                               <v:cp
+   v:nameU="ShapeType"
+   v:lbl="ShapeType"
+   v:type="0"
+   v:invis="true"
+   v:ask="false"
+   v:langID="1033"
+   v:val="VT4(Device)" />
+                               <v:cp
+   v:nameU="SubShapeType"
+   v:lbl="SubShapeType"
+   v:type="0"
+   v:invis="true"
+   v:ask="false"
+   v:langID="1033"
+   v:val="VT4(Switch)" />
+                               <v:cp
+   v:nameU="Manufacturer"
+   v:lbl="Manufacturer"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="ProductNumber"
+   v:lbl="Product Number"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="PartNumber"
+   v:lbl="Part Number"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="ProductDescription"
+   v:lbl="Product Description"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="AssetNumber"
+   v:lbl="Asset Number"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="SerialNumber"
+   v:lbl="Serial Number"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="Location"
+   v:lbl="Location"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="Building"
+   v:lbl="Building"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="Room"
+   v:lbl="Room"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="NetworkName"
+   v:lbl="Network Name"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="IPAddress"
+   v:lbl="IP Address"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="SubnetMask"
+   v:lbl="Subnet Mask"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="AdminInterface"
+   v:lbl="Administrative Interface"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="NumberOfPorts"
+   v:lbl="Number of Ports"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="MACAddress"
+   v:lbl="MAC Address"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="CommunityString"
+   v:lbl="Community String"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="NetworkDescription"
+   v:lbl="Network Description"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                       </v:custProps>
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="HasText"
+   v:val="VT0(0):5" />
+                               <v:ud
+   v:nameU="ShapeClass"
+   v:val="VT0(5):26" />
+                               <v:ud
+   v:nameU="ShapeType"
+   v:val="VT0(8):26" />
+                               <v:ud
+   v:nameU="SubShapeType"
+   v:val="VT0(66):26" />
+                               <v:ud
+   v:nameU="visLegendShape"
+   v:val="VT0(2):26" />
+                               <v:ud
+   v:nameU="SolSH"
+   v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41" />
+                               <v:ud
+   v:nameU="visVersion"
+   v:prompt=""
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title1182">Router.1001</title>
+                       <g
+   id="shape1002-2"
+   v:mID="1002"
+   v:groupContext="shape">
+                               <title
+   id="title1184">Sheet.1002</title>
+                               <path
+   d="m 40.96,813.22 a 20.4803,12.2882 -180 1 0 -40.96,0 v 16.38 a 20.4803,12.2882 -180 1 0 40.96,0 z"
+   class="st1"
+   id="path1186"
+   inkscape:connector-curvature="0"
+   style="fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0" />
+                       </g>
+                       <g
+   id="shape1003-4"
+   v:mID="1003"
+   v:groupContext="shape"
+   transform="translate(0,-16.3843)">
+                               <title
+   id="title1189">Sheet.1003</title>
+                               <ellipse
+   cx="20.480301"
+   cy="829.60199"
+   rx="20.480301"
+   ry="12.2882"
+   class="st2"
+   id="ellipse1191"
+   style="fill:url(#grad0-7);stroke:#ffffff;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1004-8"
+   v:mID="1004"
+   v:groupContext="shape"
+   transform="matrix(-1,0,0,1,34.4921,-28.6724)">
+                               <title
+   id="title1194">Sheet.1004</title>
+                               <path
+   d="m 0,839.53 v -5.73 h 9.93 l -4.08,2.36 8.16,4.71 -1.77,1.02 -8.16,-4.71 z"
+   class="st3"
+   id="path1196"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1005-10"
+   v:mID="1005"
+   v:groupContext="shape"
+   transform="translate(5.58168,-28.6724)">
+                               <title
+   id="title1199">Sheet.1005</title>
+                               <path
+   d="m 14.9,836.16 -0.89,5.22 -9.04,0.51 4.08,-2.36 -9.05,-5.22 1.77,-1.02 9.05,5.22 z"
+   class="st3"
+   id="path1201"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1006-12"
+   v:mID="1006"
+   v:groupContext="shape"
+   transform="translate(20.4803,-20.0707)">
+                               <title
+   id="title1204">Sheet.1006</title>
+                               <path
+   d="m 0,839.02 0.89,-5.22 9.04,-0.51 -4.08,2.35 9.05,5.23 -1.78,1.02 -9.04,-5.22 z"
+   class="st3"
+   id="path1206"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1007-14"
+   v:mID="1007"
+   v:groupContext="shape"
+   transform="matrix(1,0,0,-1,6.4685,1655.11)">
+                               <title
+   id="title1209">Sheet.1007</title>
+                               <path
+   d="m 0,839.53 v -5.73 h 9.93 l -4.08,2.36 8.16,4.71 -1.77,1.02 -8.16,-4.71 z"
+   class="st3"
+   id="path1211"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1008-16"
+   v:mID="1008"
+   v:groupContext="shape">
+                               <title
+   id="title1214">Sheet.1008</title>
+                               <path
+   d="m 0,813.22 v 16.38 a 20.4803,12.2882 -180 1 0 40.96,0 v -16.38 a 20.4803,12.2882 0 1 1 -40.96,0 z"
+   class="st4"
+   id="path1216"
+   inkscape:connector-curvature="0"
+   style="fill:url(#grad0-19);stroke:#000000;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1009-20"
+   v:mID="1009"
+   v:groupContext="shape">
+                               <title
+   id="title1219">Sheet.1009</title>
+                               <path
+   d="m 40.96,813.22 a 20.4803,12.2882 -180 1 0 -40.96,0 v 16.38 a 20.4803,12.2882 -180 1 0 40.96,0 v -16.38"
+   class="st5"
+   id="path1221"
+   inkscape:connector-curvature="0"
+   style="stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+               </g>
+               <g
+   id="group1011-23"
+   transform="translate(101.657,-458.787)"
+   v:mID="1011"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title1225">Array.1011</title>
+                       <g
+   id="shape1012-24"
+   v:mID="1012"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title1227">Sheet.1012</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1229"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1013-26"
+   v:mID="1013"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title1232">Sheet.1013</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1234"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1014-28"
+   v:mID="1014"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title1237">Sheet.1014</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1239"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1015-30"
+   v:mID="1015"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title1242">Sheet.1015</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1244"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1016-32"
+   v:mID="1016"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title1247">Sheet.1016</title>
+                               <desc
+   id="desc1249">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1251"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1253"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1017-35"
+   v:mID="1017"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title1256">Sheet.1017</title>
+                               <desc
+   id="desc1258">10000</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1260"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1262"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10000</text>
+                       </g>
+                       <g
+   id="shape1018-38"
+   v:mID="1018"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title1265">Sheet.1018</title>
+                               <desc
+   id="desc1267">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1269"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1271"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+                       <g
+   id="shape1019-41"
+   v:mID="1019"
+   v:groupContext="shape">
+                               <title
+   id="title1274">Sheet.1019</title>
+                               <desc
+   id="desc1276">10.10.10.10</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1278"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1280"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10.10.10.10</text>
+                       </g>
+               </g>
+               <g
+   id="group1020-44"
+   transform="translate(321.429,-458.787)"
+   v:mID="1020"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title1284">Array.1020</title>
+                       <g
+   id="shape1021-45"
+   v:mID="1021"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title1286">Sheet.1021</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1288"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1022-47"
+   v:mID="1022"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title1291">Sheet.1022</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1293"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1023-49"
+   v:mID="1023"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title1296">Sheet.1023</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1298"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1024-51"
+   v:mID="1024"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title1301">Sheet.1024</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1303"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1025-53"
+   v:mID="1025"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title1306">Sheet.1025</title>
+                               <desc
+   id="desc1308">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1310"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1312"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1026-56"
+   v:mID="1026"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title1315">Sheet.1026</title>
+                               <desc
+   id="desc1317">12345</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1319"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1321"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />12345</text>
+                       </g>
+                       <g
+   id="shape1027-59"
+   v:mID="1027"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title1324">Sheet.1027</title>
+                               <desc
+   id="desc1326">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1328"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1330"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+                       <g
+   id="shape1028-62"
+   v:mID="1028"
+   v:groupContext="shape">
+                               <title
+   id="title1333">Sheet.1028</title>
+                               <desc
+   id="desc1335">172.16.0.20</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1337"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1339"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />172.16.0.20</text>
+                       </g>
+               </g>
+               <g
+   id="shape1029-65"
+   v:mID="1029"
+   v:groupContext="shape"
+   v:layerMember="0"
+   transform="translate(271.736,-458.787)">
+                       <title
+   id="title1343">Dynamic connector.1029</title>
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <path
+   d="M 0,834.8 H 44.17"
+   class="st9"
+   id="path1345"
+   inkscape:connector-curvature="0"
+   style="stroke:#000000;stroke-width:0.239976;stroke-linecap:round;stroke-linejoin:round;marker-end:url(#mrkr4-70)" />
+               </g>
+               <g
+   id="group1030-71"
+   transform="translate(321.403,-368.504)"
+   v:mID="1030"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title1348">Array.1030</title>
+                       <g
+   id="shape1031-72"
+   v:mID="1031"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title1350">Sheet.1031</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1352"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1032-74"
+   v:mID="1032"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title1355">Sheet.1032</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1357"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1033-76"
+   v:mID="1033"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title1360">Sheet.1033</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1362"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1034-78"
+   v:mID="1034"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title1365">Sheet.1034</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1367"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1035-80"
+   v:mID="1035"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title1370">Sheet.1035</title>
+                               <desc
+   id="desc1372">12345</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1374"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1376"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />12345</text>
+                       </g>
+                       <g
+   id="shape1036-83"
+   v:mID="1036"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title1379">Sheet.1036</title>
+                               <desc
+   id="desc1381">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1383"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1385"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1037-86"
+   v:mID="1037"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title1388">Sheet.1037</title>
+                               <desc
+   id="desc1390">172.16.0.20</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1392"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1394"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />172.16.0.20</text>
+                       </g>
+                       <g
+   id="shape1038-89"
+   v:mID="1038"
+   v:groupContext="shape">
+                               <title
+   id="title1397">Sheet.1038</title>
+                               <desc
+   id="desc1399">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1401"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1403"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+               </g>
+               <g
+   id="group1039-92"
+   transform="translate(98.7064,-368.504)"
+   v:mID="1039"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title1407">Array.1039</title>
+                       <g
+   id="shape1040-93"
+   v:mID="1040"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title1409">Sheet.1040</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1411"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1041-95"
+   v:mID="1041"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title1414">Sheet.1041</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1416"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1042-97"
+   v:mID="1042"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title1419">Sheet.1042</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1421"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1043-99"
+   v:mID="1043"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title1424">Sheet.1043</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect1426"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1044-101"
+   v:mID="1044"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title1429">Sheet.1044</title>
+                               <desc
+   id="desc1431">10000</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1433"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1435"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10000</text>
+                       </g>
+                       <g
+   id="shape1045-104"
+   v:mID="1045"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title1438">Sheet.1045</title>
+                               <desc
+   id="desc1440">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1442"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1444"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1046-107"
+   v:mID="1046"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title1447">Sheet.1046</title>
+                               <desc
+   id="desc1449">10.10.10.10</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1451"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1453"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10.10.10.10</text>
+                       </g>
+                       <g
+   id="shape1047-110"
+   v:mID="1047"
+   v:groupContext="shape">
+                               <title
+   id="title1456">Sheet.1047</title>
+                               <desc
+   id="desc1458">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect1460"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text1462"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+               </g>
+               <g
+   id="shape1048-113"
+   v:mID="1048"
+   v:groupContext="shape"
+   v:layerMember="0"
+   transform="translate(321.403,-368.504)">
+                       <title
+   id="title1466">Dynamic connector.1048</title>
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <path
+   d="M 0,834.8 H -47.1"
+   class="st9"
+   id="path1468"
+   inkscape:connector-curvature="0"
+   style="stroke:#000000;stroke-width:0.239976;stroke-linecap:round;stroke-linejoin:round;marker-end:url(#mrkr4-70)" />
+               </g>
+               <g
+   id="shape1049-118"
+   v:mID="1049"
+   v:groupContext="shape"
+   transform="translate(101.657,-480.047)">
+                       <title
+   id="title1471">Sheet.1049</title>
+                       <desc
+   id="desc1473">RSS hash value 0xdeadbeef Packet assigned to queue 15</desc>
+                       <v:textBlock
+   v:margins="rect(4,4,4,4)"
+   v:tabSpace="42.5197" />
+                       <v:textRect
+   cx="89.1142"
+   cy="829.417"
+   width="178.23"
+   height="24.9449" />
+                       <rect
+   x="0"
+   y="816.94501"
+   width="178.228"
+   height="24.944901"
+   class="st11"
+   id="rect1475"
+   style="fill:none;stroke:none;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round" />
+                       <text
+   x="22.889999"
+   y="825.82001"
+   class="st12"
+   v:langID="6153"
+   id="text1479"
+   style="font-size:12.00012016px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />RSS hash value 0xdeadbeef<v:newlineChar /><tspan
+   x="19.709999"
+   dy="1.2em"
+   class="st13"
+   id="tspan1477"
+   style="font-size:12.00012016px">Packet assigned to queue 15</tspan></text>
+               </g>
+               <g
+   id="shape1051-122"
+   v:mID="1051"
+   v:groupContext="shape"
+   transform="translate(318.331,-386.079)">
+                       <title
+   id="title1482">Sheet.1051</title>
+                       <desc
+   id="desc1484">RSS hash value 0xbadcab1e Packet assigned to queue 14</desc>
+                       <v:textBlock
+   v:margins="rect(4,4,4,4)"
+   v:tabSpace="42.5197" />
+                       <v:textRect
+   cx="89.1142"
+   cy="829.417"
+   width="178.23"
+   height="24.9449" />
+                       <rect
+   x="0"
+   y="816.94501"
+   width="178.228"
+   height="24.944901"
+   class="st11"
+   id="rect1486"
+   style="fill:none;stroke:none;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round" />
+                       <text
+   x="22.24"
+   y="825.82001"
+   class="st12"
+   v:langID="6153"
+   id="text1490"
+   style="font-size:12.00012016px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />RSS hash value 0xbadcab1e<v:newlineChar /><tspan
+   x="19.709999"
+   dy="1.2em"
+   class="st13"
+   id="tspan1488"
+   style="font-size:12.00012016px">Packet assigned to queue 14</tspan></text>
+               </g>
+       </g>
+</svg>
\ No newline at end of file
diff --git a/doc/guides/prog_guide/img/predictable_snat_2.svg b/doc/guides/prog_guide/img/predictable_snat_2.svg
new file mode 100644 (file)
index 0000000..8525459
--- /dev/null
@@ -0,0 +1,1444 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generated by Microsoft Visio, SVG Export predictable_snat_2.svg Page-5 -->
+
+<svg
+   xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="7.8211055in"
+   height="1.8973334in"
+   viewBox="0 0 563.12017 136.6082"
+   xml:space="preserve"
+   class="st14"
+   version="1.1"
+   id="svg7179"
+   sodipodi:docname="predictable_snat_2.svg"
+   style="font-size:12px;overflow:visible;color-interpolation-filters:sRGB;fill:none;fill-rule:evenodd;stroke-linecap:square;stroke-miterlimit:3"
+   inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
+   id="metadata7183"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1296"
+   inkscape:window-height="757"
+   id="namedview7181"
+   showgrid="false"
+   inkscape:zoom="0.3689753"
+   inkscape:cx="598.10007"
+   inkscape:cy="151.8361"
+   inkscape:window-x="356"
+   inkscape:window-y="113"
+   inkscape:window-maximized="0"
+   inkscape:current-layer="svg7179" />
+       <v:documentProperties
+   v:langID="6153"
+   v:metric="true"
+   v:viewMarkup="false">
+               <v:userDefs>
+                       <v:ud
+   v:nameU="msvNoAutoConnect"
+   v:val="VT0(1):26" />
+               </v:userDefs>
+       </v:documentProperties>
+
+       <style
+   type="text/css"
+   id="style6842">
+       <![CDATA[
+               .st1 {fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0;stroke-width:0.72}
+               .st2 {fill:url(#grad0-7);stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72}
+               .st3 {fill:#ff0000;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
+               .st4 {fill:url(#grad0-19);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72}
+               .st5 {stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
+               .st6 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24;visibility:hidden}
+               .st7 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
+               .st8 {fill:#000000;font-family:Calibri;font-size:0.666664em}
+               .st9 {marker-end:url(#mrkr4-70);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.239976}
+               .st10 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.086948582161584}
+               .st11 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
+               .st12 {fill:#000000;font-family:Calibri;font-size:1.00001em}
+               .st13 {font-size:1em}
+               .st14 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
+       ]]>
+       </style>
+
+       <defs
+   id="Patterns_And_Gradients">
+               <linearGradient
+   id="grad0-7"
+   x1="-0.2804561"
+   y1="1048.661"
+   x2="32.190596"
+   y2="1048.661"
+   gradientTransform="scale(1.2836234,0.7790447)"
+   gradientUnits="userSpaceOnUse">
+                       <stop
+   offset="0"
+   stop-color="#ffffff"
+   stop-opacity="1"
+   id="stop6844" />
+                       <stop
+   offset="1"
+   stop-color="#dfdfdf"
+   stop-opacity="1"
+   id="stop6846" />
+               </linearGradient>
+               <linearGradient
+   id="grad0-19"
+   x1="-0.30286968"
+   y1="967.01808"
+   x2="34.733349"
+   y2="967.01808"
+   gradientTransform="scale(1.1896436,0.84058789)"
+   gradientUnits="userSpaceOnUse">
+                       <stop
+   offset="0.01"
+   stop-color="#c0c0c0"
+   stop-opacity="1"
+   id="stop6849" />
+                       <stop
+   offset="0.5"
+   stop-color="#ffffff"
+   stop-opacity="1"
+   id="stop6851" />
+                       <stop
+   offset="1"
+   stop-color="#c0c0c0"
+   stop-opacity="1"
+   id="stop6853" />
+               </linearGradient>
+       </defs>
+       <defs
+   id="Markers">
+               <g
+   id="lend4">
+                       <path
+   d="M 2,1 0,0 2,-1 v 2"
+   style="stroke:none"
+   id="path6857"
+   inkscape:connector-curvature="0" />
+               </g>
+               <marker
+   id="mrkr4-70"
+   class="st10"
+   v:arrowType="4"
+   v:arrowSize="2"
+   v:setback="23.0021"
+   refX="-23.0021"
+   orient="auto"
+   markerUnits="strokeWidth"
+   overflow="visible"
+   style="overflow:visible;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.08694858;stroke-opacity:1">
+                       <use
+   xlink:href="#lend4"
+   transform="scale(-11.50105)"
+   id="use6860"
+   x="0"
+   y="0"
+   width="100%"
+   height="100%" />
+               </marker>
+       </defs>
+       <g
+   v:mID="9"
+   v:index="5"
+   v:groupContext="foregroundPage"
+   id="g7177"
+   transform="translate(-98.5864,-336.89801)">
+               <title
+   id="title6864">Page-5</title>
+               <v:pageProperties
+   v:drawingScale="0.0393701"
+   v:pageScale="0.0393701"
+   v:drawingUnits="24"
+   v:shadowOffsetX="8.50394"
+   v:shadowOffsetY="-8.50394" />
+               <v:layer
+   v:name="Connector"
+   v:index="0" />
+               <g
+   id="group1001-1"
+   transform="translate(275.811,-396.85)"
+   v:mID="1001"
+   v:groupContext="group">
+                       <v:custProps>
+                               <v:cp
+   v:nameU="ShapeClass"
+   v:lbl="ShapeClass"
+   v:type="0"
+   v:invis="true"
+   v:ask="false"
+   v:langID="1033"
+   v:val="VT4(Equipment)" />
+                               <v:cp
+   v:nameU="ShapeType"
+   v:lbl="ShapeType"
+   v:type="0"
+   v:invis="true"
+   v:ask="false"
+   v:langID="1033"
+   v:val="VT4(Device)" />
+                               <v:cp
+   v:nameU="SubShapeType"
+   v:lbl="SubShapeType"
+   v:type="0"
+   v:invis="true"
+   v:ask="false"
+   v:langID="1033"
+   v:val="VT4(Switch)" />
+                               <v:cp
+   v:nameU="Manufacturer"
+   v:lbl="Manufacturer"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="ProductNumber"
+   v:lbl="Product Number"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="PartNumber"
+   v:lbl="Part Number"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="ProductDescription"
+   v:lbl="Product Description"
+   v:type="0"
+   v:sortKey="Equipment"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="AssetNumber"
+   v:lbl="Asset Number"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="SerialNumber"
+   v:lbl="Serial Number"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="Location"
+   v:lbl="Location"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="Building"
+   v:lbl="Building"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="Room"
+   v:lbl="Room"
+   v:type="0"
+   v:sortKey="Asset"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="NetworkName"
+   v:lbl="Network Name"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="IPAddress"
+   v:lbl="IP Address"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="SubnetMask"
+   v:lbl="Subnet Mask"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="AdminInterface"
+   v:lbl="Administrative Interface"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="NumberOfPorts"
+   v:lbl="Number of Ports"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="MACAddress"
+   v:lbl="MAC Address"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="CommunityString"
+   v:lbl="Community String"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                               <v:cp
+   v:nameU="NetworkDescription"
+   v:lbl="Network Description"
+   v:type="0"
+   v:sortKey="Network"
+   v:invis="false"
+   v:ask="false"
+   v:langID="1033" />
+                       </v:custProps>
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="HasText"
+   v:val="VT0(0):5" />
+                               <v:ud
+   v:nameU="ShapeClass"
+   v:val="VT0(5):26" />
+                               <v:ud
+   v:nameU="ShapeType"
+   v:val="VT0(8):26" />
+                               <v:ud
+   v:nameU="SubShapeType"
+   v:val="VT0(66):26" />
+                               <v:ud
+   v:nameU="visLegendShape"
+   v:val="VT0(2):26" />
+                               <v:ud
+   v:nameU="SolSH"
+   v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41" />
+                               <v:ud
+   v:nameU="visVersion"
+   v:prompt=""
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title6866">Router.1001</title>
+                       <g
+   id="shape1002-2"
+   v:mID="1002"
+   v:groupContext="shape">
+                               <title
+   id="title6868">Sheet.1002</title>
+                               <path
+   d="m 40.96,813.22 a 20.4803,12.2882 -180 1 0 -40.96,0 v 16.38 a 20.4803,12.2882 -180 1 0 40.96,0 z"
+   class="st1"
+   id="path6870"
+   inkscape:connector-curvature="0"
+   style="fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:0" />
+                       </g>
+                       <g
+   id="shape1003-4"
+   v:mID="1003"
+   v:groupContext="shape"
+   transform="translate(0,-16.3843)">
+                               <title
+   id="title6873">Sheet.1003</title>
+                               <ellipse
+   cx="20.480301"
+   cy="829.60199"
+   rx="20.480301"
+   ry="12.2882"
+   class="st2"
+   id="ellipse6875"
+   style="fill:url(#grad0-7);stroke:#ffffff;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1004-8"
+   v:mID="1004"
+   v:groupContext="shape"
+   transform="matrix(-1,0,0,1,34.4921,-28.6724)">
+                               <title
+   id="title6878">Sheet.1004</title>
+                               <path
+   d="m 0,839.53 v -5.73 h 9.93 l -4.08,2.36 8.16,4.71 -1.77,1.02 -8.16,-4.71 z"
+   class="st3"
+   id="path6880"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1005-10"
+   v:mID="1005"
+   v:groupContext="shape"
+   transform="translate(5.58168,-28.6724)">
+                               <title
+   id="title6883">Sheet.1005</title>
+                               <path
+   d="m 14.9,836.16 -0.89,5.22 -9.04,0.51 4.08,-2.36 -9.05,-5.22 1.77,-1.02 9.05,5.22 z"
+   class="st3"
+   id="path6885"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1006-12"
+   v:mID="1006"
+   v:groupContext="shape"
+   transform="translate(20.4803,-20.0707)">
+                               <title
+   id="title6888">Sheet.1006</title>
+                               <path
+   d="m 0,839.02 0.89,-5.22 9.04,-0.51 -4.08,2.35 9.05,5.23 -1.78,1.02 -9.04,-5.22 z"
+   class="st3"
+   id="path6890"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1007-14"
+   v:mID="1007"
+   v:groupContext="shape"
+   transform="matrix(1,0,0,-1,6.4685,1655.11)">
+                               <title
+   id="title6893">Sheet.1007</title>
+                               <path
+   d="m 0,839.53 v -5.73 h 9.93 l -4.08,2.36 8.16,4.71 -1.77,1.02 -8.16,-4.71 z"
+   class="st3"
+   id="path6895"
+   inkscape:connector-curvature="0"
+   style="fill:#ff0000;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1008-16"
+   v:mID="1008"
+   v:groupContext="shape">
+                               <title
+   id="title6898">Sheet.1008</title>
+                               <path
+   d="m 0,813.22 v 16.38 a 20.4803,12.2882 -180 1 0 40.96,0 v -16.38 a 20.4803,12.2882 0 1 1 -40.96,0 z"
+   class="st4"
+   id="path6900"
+   inkscape:connector-curvature="0"
+   style="fill:url(#grad0-19);stroke:#000000;stroke-width:0.72000003;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1009-20"
+   v:mID="1009"
+   v:groupContext="shape">
+                               <title
+   id="title6903">Sheet.1009</title>
+                               <path
+   d="m 40.96,813.22 a 20.4803,12.2882 -180 1 0 -40.96,0 v 16.38 a 20.4803,12.2882 -180 1 0 40.96,0 v -16.38"
+   class="st5"
+   id="path6905"
+   inkscape:connector-curvature="0"
+   style="stroke:#000000;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+               </g>
+               <g
+   id="group1011-23"
+   transform="translate(101.657,-458.787)"
+   v:mID="1011"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title6909">Array.1011</title>
+                       <g
+   id="shape1012-24"
+   v:mID="1012"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title6911">Sheet.1012</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6913"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1013-26"
+   v:mID="1013"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title6916">Sheet.1013</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6918"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1014-28"
+   v:mID="1014"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title6921">Sheet.1014</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6923"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1015-30"
+   v:mID="1015"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title6926">Sheet.1015</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6928"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1016-32"
+   v:mID="1016"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title6931">Sheet.1016</title>
+                               <desc
+   id="desc6933">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect6935"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text6937"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1017-35"
+   v:mID="1017"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title6940">Sheet.1017</title>
+                               <desc
+   id="desc6942">10000</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect6944"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text6946"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10000</text>
+                       </g>
+                       <g
+   id="shape1018-38"
+   v:mID="1018"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title6949">Sheet.1018</title>
+                               <desc
+   id="desc6951">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect6953"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text6955"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+                       <g
+   id="shape1019-41"
+   v:mID="1019"
+   v:groupContext="shape">
+                               <title
+   id="title6958">Sheet.1019</title>
+                               <desc
+   id="desc6960">10.10.10.10</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect6962"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text6964"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10.10.10.10</text>
+                       </g>
+               </g>
+               <g
+   id="group1020-44"
+   transform="translate(321.429,-458.787)"
+   v:mID="1020"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title6968">Array.1020</title>
+                       <g
+   id="shape1021-45"
+   v:mID="1021"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title6970">Sheet.1021</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6972"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1022-47"
+   v:mID="1022"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title6975">Sheet.1022</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6977"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1023-49"
+   v:mID="1023"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title6980">Sheet.1023</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6982"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1024-51"
+   v:mID="1024"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title6985">Sheet.1024</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect6987"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1025-53"
+   v:mID="1025"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title6990">Sheet.1025</title>
+                               <desc
+   id="desc6992">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect6994"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text6996"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1026-56"
+   v:mID="1026"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title6999">Sheet.1026</title>
+                               <desc
+   id="desc7001">23456</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7003"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7005"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />23456</text>
+                       </g>
+                       <g
+   id="shape1027-59"
+   v:mID="1027"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title7008">Sheet.1027</title>
+                               <desc
+   id="desc7010">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7012"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7014"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+                       <g
+   id="shape1028-62"
+   v:mID="1028"
+   v:groupContext="shape">
+                               <title
+   id="title7017">Sheet.1028</title>
+                               <desc
+   id="desc7019">172.16.0.20</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7021"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7023"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />172.16.0.20</text>
+                       </g>
+               </g>
+               <g
+   id="shape1029-65"
+   v:mID="1029"
+   v:groupContext="shape"
+   v:layerMember="0"
+   transform="translate(271.736,-458.787)">
+                       <title
+   id="title7027">Dynamic connector.1029</title>
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <path
+   d="M 0,834.8 H 44.17"
+   class="st9"
+   id="path7029"
+   inkscape:connector-curvature="0"
+   style="stroke:#000000;stroke-width:0.239976;stroke-linecap:round;stroke-linejoin:round;marker-end:url(#mrkr4-70)" />
+               </g>
+               <g
+   id="group1030-71"
+   transform="translate(321.403,-368.504)"
+   v:mID="1030"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title7032">Array.1030</title>
+                       <g
+   id="shape1031-72"
+   v:mID="1031"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title7034">Sheet.1031</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7036"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1032-74"
+   v:mID="1032"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title7039">Sheet.1032</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7041"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1033-76"
+   v:mID="1033"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title7044">Sheet.1033</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7046"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1034-78"
+   v:mID="1034"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title7049">Sheet.1034</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7051"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1035-80"
+   v:mID="1035"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title7054">Sheet.1035</title>
+                               <desc
+   id="desc7056">23456</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7058"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7060"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />23456</text>
+                       </g>
+                       <g
+   id="shape1036-83"
+   v:mID="1036"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title7063">Sheet.1036</title>
+                               <desc
+   id="desc7065">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7067"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7069"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1037-86"
+   v:mID="1037"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title7072">Sheet.1037</title>
+                               <desc
+   id="desc7074">172.16.0.20</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7076"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7078"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />172.16.0.20</text>
+                       </g>
+                       <g
+   id="shape1038-89"
+   v:mID="1038"
+   v:groupContext="shape">
+                               <title
+   id="title7081">Sheet.1038</title>
+                               <desc
+   id="desc7083">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7085"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7087"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+               </g>
+               <g
+   id="group1039-92"
+   transform="translate(98.7064,-368.504)"
+   v:mID="1039"
+   v:groupContext="group">
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <title
+   id="title7091">Array.1039</title>
+                       <g
+   id="shape1040-93"
+   v:mID="1040"
+   v:groupContext="shape"
+   transform="translate(297.638)">
+                               <title
+   id="title7093">Sheet.1040</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7095"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1041-95"
+   v:mID="1041"
+   v:groupContext="shape"
+   transform="translate(255.118)">
+                               <title
+   id="title7098">Sheet.1041</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7100"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1042-97"
+   v:mID="1042"
+   v:groupContext="shape"
+   transform="translate(212.598)">
+                               <title
+   id="title7103">Sheet.1042</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7105"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1043-99"
+   v:mID="1043"
+   v:groupContext="shape"
+   transform="translate(170.079)">
+                               <title
+   id="title7108">Sheet.1043</title>
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st6"
+   id="rect7110"
+   style="visibility:hidden;fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                       </g>
+                       <g
+   id="shape1044-101"
+   v:mID="1044"
+   v:groupContext="shape"
+   transform="translate(127.559)">
+                               <title
+   id="title7113">Sheet.1044</title>
+                               <desc
+   id="desc7115">10000</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7117"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="11.12"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7119"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10000</text>
+                       </g>
+                       <g
+   id="shape1045-104"
+   v:mID="1045"
+   v:groupContext="shape"
+   transform="translate(85.0394)">
+                               <title
+   id="title7122">Sheet.1045</title>
+                               <desc
+   id="desc7124">443</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7126"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="15.18"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7128"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />443</text>
+                       </g>
+                       <g
+   id="shape1046-107"
+   v:mID="1046"
+   v:groupContext="shape"
+   transform="translate(42.5197)">
+                               <title
+   id="title7131">Sheet.1046</title>
+                               <desc
+   id="desc7133">10.10.10.10</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7135"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7137"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />10.10.10.10</text>
+                       </g>
+                       <g
+   id="shape1047-110"
+   v:mID="1047"
+   v:groupContext="shape">
+                               <title
+   id="title7140">Sheet.1047</title>
+                               <desc
+   id="desc7142">192.0.2.100</desc>
+                               <v:textBlock
+   v:margins="rect(1,1,1,1)"
+   v:tabSpace="42.5197" />
+                               <v:textRect
+   cx="21.2598"
+   cy="834.803"
+   width="42.52"
+   height="14.1732" />
+                               <rect
+   x="0"
+   y="827.71698"
+   width="42.519699"
+   height="14.1732"
+   class="st7"
+   id="rect7144"
+   style="fill:#ffffff;stroke:#000000;stroke-width:0.23999999;stroke-linecap:round;stroke-linejoin:round" />
+                               <text
+   x="2.01"
+   y="837.20001"
+   class="st8"
+   v:langID="6153"
+   id="text7146"
+   style="font-size:7.99996805px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />192.0.2.100</text>
+                       </g>
+               </g>
+               <g
+   id="shape1048-113"
+   v:mID="1048"
+   v:groupContext="shape"
+   v:layerMember="0"
+   transform="translate(321.403,-368.504)">
+                       <title
+   id="title7150">Dynamic connector.1048</title>
+                       <v:userDefs>
+                               <v:ud
+   v:nameU="visVersion"
+   v:val="VT0(15):26" />
+                       </v:userDefs>
+                       <path
+   d="M 0,834.8 H -47.1"
+   class="st9"
+   id="path7152"
+   inkscape:connector-curvature="0"
+   style="stroke:#000000;stroke-width:0.239976;stroke-linecap:round;stroke-linejoin:round;marker-end:url(#mrkr4-70)" />
+               </g>
+               <g
+   id="shape1049-118"
+   v:mID="1049"
+   v:groupContext="shape"
+   transform="translate(101.657,-480.047)">
+                       <title
+   id="title7155">Sheet.1049</title>
+                       <desc
+   id="desc7157">RSS hash value 0xdeadbeef Packet assigned to queue 15</desc>
+                       <v:textBlock
+   v:margins="rect(4,4,4,4)"
+   v:tabSpace="42.5197" />
+                       <v:textRect
+   cx="89.1142"
+   cy="829.417"
+   width="178.23"
+   height="24.9449" />
+                       <rect
+   x="0"
+   y="816.94501"
+   width="178.228"
+   height="24.944901"
+   class="st11"
+   id="rect7159"
+   style="fill:none;stroke:none;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round" />
+                       <text
+   x="22.889999"
+   y="825.82001"
+   class="st12"
+   v:langID="6153"
+   id="text7163"
+   style="font-size:12.00012016px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />RSS hash value 0xdeadbeef<v:newlineChar /><tspan
+   x="19.709999"
+   dy="1.2em"
+   class="st13"
+   id="tspan7161"
+   style="font-size:12.00012016px">Packet assigned to queue 15</tspan></text>
+               </g>
+               <g
+   id="shape1051-122"
+   v:mID="1051"
+   v:groupContext="shape"
+   transform="translate(318.331,-386.079)">
+                       <title
+   id="title7166">Sheet.1051</title>
+                       <desc
+   id="desc7168">RSS hash value 0xf00d1eaf Packet assigned to queue 15</desc>
+                       <v:textBlock
+   v:margins="rect(4,4,4,4)"
+   v:tabSpace="42.5197" />
+                       <v:textRect
+   cx="89.1142"
+   cy="829.417"
+   width="178.23"
+   height="24.9449" />
+                       <rect
+   x="0"
+   y="816.94501"
+   width="178.228"
+   height="24.944901"
+   class="st11"
+   id="rect7170"
+   style="fill:none;stroke:none;stroke-width:0.75;stroke-linecap:round;stroke-linejoin:round" />
+                       <text
+   x="24.209999"
+   y="825.82001"
+   class="st12"
+   v:langID="6153"
+   id="text7174"
+   style="font-size:12.00012016px;font-family:Calibri;fill:#000000"><v:paragraph
+   v:horizAlign="1" /><v:tabList />RSS hash value 0xf00d1eaf<v:newlineChar /><tspan
+   x="19.709999"
+   dy="1.2em"
+   class="st13"
+   id="tspan7172"
+   style="font-size:12.00012016px">Packet assigned to queue 15</tspan></text>
+               </g>
+       </g>
+</svg>
\ No newline at end of file
index 2480c55..83d5b01 100644 (file)
@@ -36,3 +36,250 @@ The ``rte_softrss()`` function expects the ``rss_key``
 to be exactly the same as the one installed on the NIC.
 The ``rte_softrss_be`` function is a faster implementation,
 but it expects ``rss_key`` to be converted to the host byte order.
+
+
+Predictable RSS
+---------------
+
+In some usecases it is useful to have a way to find partial collisions of the
+Toeplitz hash function. In figure :numref:`figure_rss_queue_assign` only a few
+of the least significant bits (LSB) of the hash value are used to indicate an
+entry in the RSS Redirection Table (ReTa) and thus the index of the queue. So,
+in this case it would be useful to find another tuple whose hash has the same
+LSB's as the hash from the original tuple.
+
+For example:
+
+- In the case of SNAT (Source Network Address Translation) it is possible to
+  find a special source port number on translation so that the hash of
+  returning packets, of the given connection, will have desired LSB's.
+- In the case of MPLS (Multiprotocol Label Switching), if the MPLS tag is used
+  in the hash calculation, the Label Switching router can allocate a special
+  MPLS tag to bind an LSP (Label Switching Path) to a given queue. This method
+  can be used with the allocation of IPSec SPI, VXLan VNI, etc., to bind the
+  tunnel to the desired queue.
+- In the case of a TCP stack, a special source port could be chosen for
+  outgoing connections, such that the response packets will be assigned to the
+  desired queue.
+
+This functionality is provided by the API shown below.
+The API consists of 3 parts:
+
+* Create the thash context.
+
+* Create the thash helper, associated with a context.
+
+* Use the helper run time to calculate the adjustable bits of the tuple to
+  ensure a collision.
+
+
+Thash context
+~~~~~~~~~~~~~
+
+The function ``rte_thash_init_ctx()`` initializes the context struct
+associated with a particular NIC or a set of NICs. It expects:
+
+* The log2 value of the size of the RSS redirection table for the
+  corresponding NIC. It reflects the number of least significant bits of the
+  hash value to produce a collision for.
+
+* A predefined RSS hash key. This is optional, if ``NULL`` then a random key
+  will be initialized.
+
+* The length of the RSS hash key. This value is usually hardware/driver
+  specific and can be found in the NIC datasheet.
+
+* Optional flags, as shown below.
+
+Supported flags:
+
+* ``RTE_THASH_IGNORE_PERIOD_OVERFLOW`` - By default, and for security reasons,
+  the library prohibits generating a repeatable sequence in the hash key. This
+  flag disables such checking. The flag is mainly used for testing in the lab
+  to generate an RSS hash key with a uniform hash distribution, if the input
+  traffic also has a uniform distribution.
+
+* ``RTE_THASH_MINIMAL_SEQ`` - By default, the library generates a special bit
+  sequence in the hash key for all the bits of the subtuple. However, the
+  collision generation task requires only the ``log2(RETA_SZ)`` bits in the
+  subtuple. This flag forces the minimum bit sequence in the hash key to be
+  generated for the required ``log2(RETA_SZ)`` least significant bits of the
+  subtuple. The flag can be used in the case of a relatively large number of
+  helpers that may overlap with their corresponding bit sequences of RSS hash
+  keys.
+
+
+Thash helper
+~~~~~~~~~~~~
+
+The function ``rte_thash_add_helper()`` initializes the helper struct
+associated with a given context and a part of a target tuple of interest which
+could be altered to produce a hash collision. On success it writes a specially
+calculated bit sequence into the RSS hash key which is stored in the context
+and calculates a table with values to be XORed with a subtuple.
+
+It expects:
+
+* A pointer to the Thash context to be associated with.
+
+* A length of the subtuple to be modified. The length is counted in bits.
+
+* An offset of the subtuple to be modified from the beginning of the tuple. It
+  is also counted in bits.
+
+.. note::
+
+   Adding a helper changes the key stored in the corresponding context. So the
+   updated RSS hash key must be uploaded into the NIC after creating all the
+   required helpers.
+
+
+Calculation of the complementary bits to adjust the subtuple
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``rte_thash_get_complement()`` function returns a special bit sequence
+with length ``N = log2(rss_reta_sz)`` (for the ``rss_reta_sz`` provided at
+context initialization) to be xored with N least significant bits of the
+subtuple.
+
+It expects:
+
+* A corresponding helper created for a given subtuple of the tuple.
+
+* A hash value of the tuple we want to alter.
+
+* The desired LSB's of the hash value the user expects to have.
+
+After the returned bit sequence has been XORed with the subtuple, the resulted
+LSB's of the new hash value, calculated from the altered tuple, will be the
+same as in ``desired_hash``.
+
+
+Adjust tuple API
+~~~~~~~~~~~~~~~~~
+
+The ``rte_thash_get_complement()`` function is a user-friendly wrapper around
+a number of other functions. It alters a passed tuple to meet the above
+mentioned requirements around the desired hash LSB's.
+
+It expects:
+
+* A Thash context and helper.
+
+* A pointer to the tuple to be changed.
+
+* The length of the tuple.
+
+* A callback function and its userdata to check the tuple after it has been
+  changed.
+
+* The number of attempts to change the tuple. Basically, it makes sense if
+  there is a callback and a limit on the number of attempts to change the
+  tuple, if the callback function returns an error.
+
+
+Usecase example
+---------------
+
+There could be a number of different usecases, such as NAT, TCP stack, MPLS
+tag allocation, etc. In the following we will consider a SNAT application.
+
+Packets of a single bidirectional flow belonging to different directions can
+end up being assigned to different queues and thus processed by different
+lcores, as shown in :numref:`figure_predictable_snat_1`:
+
+.. _figure_predictable_snat_1:
+
+.. figure:: img/predictable_snat_1.*
+
+   Bidirectional flow packets distribution in general
+
+That leads to a situation where the same packet flow can be shared between two
+cores. Such a situation is not ideal from a performance perspective and
+requires extra synchronization efforts that might lead to various performance
+penalties, for example:
+
+* The connections table is global so locking/RCU on the flow insertion/removal
+  is required.
+
+* Connection metadata must be protected to avoid race conditions.
+
+* More cache pressure if a single connection metadata is kept in different
+  L1/L2 caches of a different CPU core.
+
+* Cache pressure/less cache locality on packet handover to the different cores.
+
+We can avoid all these penalties if it can be guaranteed that packets
+belonging to one bidirectional flow will be assigned to the same queue, as
+shown in :numref:`figure_predictable_snat_2`:
+
+.. _figure_predictable_snat_2:
+
+.. figure:: img/predictable_snat_2.*
+
+   Bidirectional flow packets distribution with predictable RSS
+
+
+To achieve this in a SNAT scenario it is possible to choose a source port not
+randomly, but using the predictable RSS library to produce a partial hash
+collision. This is shown in the code below.
+
+.. code-block:: c
+
+   int key_len = 40; /* The default Niantic RSS key length. */
+
+   /** The default Niantic RSS reta size = 2^7 entries, LSBs of hash value are
+    *  used as an indexes in RSS ReTa. */
+   int reta_sz = 7;
+   int ret;
+   struct rte_thash_ctx *ctx;
+
+   uint8_t initial_key[key_len] = {0}; /* Default empty key. */
+
+   /* Create and initialize a new thash context. */
+   ctx = rte_thash_init_ctx("SNAT", key_len, reta_sz, initial_key, 0);
+
+   /** Add a helper and specify the variable tuple part and its length. In the
+    *  SNAT case we want to choose a new source port on SNAT translation in a
+    *  way that the reverse tuple will have the same LSBs as the original
+    *  direction tuple so that the selected source port will be the
+    *  destination port on reply.
+    */
+   ret = rte_thash_add_helper(ctx, "snat", sizeof(uint16_t) * 8,
+                              offsetof(union rte_thash_tuple, v4.dport) * 8);
+
+   if (ret != 0)
+       return ret;
+
+   /* Get handler of the required helper. */
+   struct rte_thash_subtuple_helper *h = rte_thash_get_helper(ctx, "snat");
+
+   /** After calling rte_thash_add_helper() the initial_key passed on ctx
+    *  creation has been changed so we get the new one.
+    */
+   uint8_t *new_key = rte_thash_get_key(ctx);
+
+   union rte_thash_tuple tuple, rev_tuple;
+
+   /* A complete tuple from the packet. */
+   complete_tuple(mbuf, &tuple);
+
+   /* Calculate the RSS hash or get it from mbuf->hash.rss. */
+   uint32_t orig_hash = rte_softrss((uint32_t *)&tuple, RTE_THASH_V4_L4_LEN, new_key);
+
+   /** Complete the reverse tuple by translating the SRC address and swapping
+    *  src and dst addresses and ports.
+    */
+   get_rev_tuple(&rev_tuple, &tuple, new_ip);
+
+   /* Calculate the expected rss hash for the reverse tuple. */
+   uint32_t rev_hash = rte_softrss((uint32_t *)&rev_tuple, RTE_THASH_V4_L4_LEN, new_key);
+
+   /* Get the adjustment bits for the src port to get a new port. */
+   uint32_t adj = rte_thash_get_compliment(h, rev_hash, orig_hash);
+
+   /* Adjust the source port bits. */
+   uint16_t new_sport = tuple.v4.sport ^ adj;
+
+   /* Make an actual packet translation. */
+   do_snat(mbuf, new_ip, new_sport);
index 0ae862b..565e764 100644 (file)
@@ -203,6 +203,12 @@ New Features
     the events across multiple stages.
   * This also reduced the scheduling overhead on a event device.
 
+* **Added Predictable RSS functionality to the Toeplitz hash library.**
+
+  Added feature for finding collisions of the Toeplitz hash function -
+  the hash function used in NICs to spread the traffic among the queues.
+  It can be used to get predictable mapping of the flows.
+
 * **Updated testpmd.**
 
   * Added a command line option to configure forced speed for Ethernet port.
index 242859f..3546014 100644 (file)
@@ -8,6 +8,7 @@ headers = files('rte_fbk_hash.h',
        'rte_thash.h')
 indirect_headers += files('rte_crc_arm64.h')
 
-sources = files('rte_cuckoo_hash.c', 'rte_fbk_hash.c')
+sources = files('rte_cuckoo_hash.c', 'rte_fbk_hash.c', 'rte_thash.c')
+deps += ['net']
 deps += ['ring']
 deps += ['rcu']
diff --git a/lib/librte_hash/rte_thash.c b/lib/librte_hash/rte_thash.c
new file mode 100644 (file)
index 0000000..135a26d
--- /dev/null
@@ -0,0 +1,682 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2021 Intel Corporation
+ */
+
+#include <rte_thash.h>
+#include <rte_tailq.h>
+#include <rte_random.h>
+#include <rte_memcpy.h>
+#include <rte_errno.h>
+#include <rte_eal.h>
+#include <rte_eal_memconfig.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+
+#define THASH_NAME_LEN         64
+#define TOEPLITZ_HASH_LEN      32
+
+#define RETA_SZ_IN_RANGE(reta_sz)      ((reta_sz >= RTE_THASH_RETA_SZ_MIN) &&\
+                                       (reta_sz <= RTE_THASH_RETA_SZ_MAX))
+
+TAILQ_HEAD(rte_thash_list, rte_tailq_entry);
+static struct rte_tailq_elem rte_thash_tailq = {
+       .name = "RTE_THASH",
+};
+EAL_REGISTER_TAILQ(rte_thash_tailq)
+
+/**
+ * Table of some irreducible polinomials over GF(2).
+ * For lfsr they are reperesented in BE bit order, and
+ * x^0 is masked out.
+ * For example, poly x^5 + x^2 + 1 will be represented
+ * as (101001b & 11111b) = 01001b = 0x9
+ */
+static const uint32_t irreducible_poly_table[][4] = {
+       {0, 0, 0, 0},   /** < degree 0 */
+       {1, 1, 1, 1},   /** < degree 1 */
+       {0x3, 0x3, 0x3, 0x3},   /** < degree 2 and so on... */
+       {0x5, 0x3, 0x5, 0x3},
+       {0x9, 0x3, 0x9, 0x3},
+       {0x9, 0x1b, 0xf, 0x5},
+       {0x21, 0x33, 0x1b, 0x2d},
+       {0x41, 0x11, 0x71, 0x9},
+       {0x71, 0xa9, 0xf5, 0x8d},
+       {0x21, 0xd1, 0x69, 0x1d9},
+       {0x81, 0x2c1, 0x3b1, 0x185},
+       {0x201, 0x541, 0x341, 0x461},
+       {0x941, 0x609, 0xe19, 0x45d},
+       {0x1601, 0x1f51, 0x1171, 0x359},
+       {0x2141, 0x2111, 0x2db1, 0x2109},
+       {0x4001, 0x801, 0x101, 0x7301},
+       {0x7781, 0xa011, 0x4211, 0x86d9},
+};
+
+struct thash_lfsr {
+       uint32_t        ref_cnt;
+       uint32_t        poly;
+       /**< polynomial associated with the lfsr */
+       uint32_t        rev_poly;
+       /**< polynomial to generate the sequence in reverse direction */
+       uint32_t        state;
+       /**< current state of the lfsr */
+       uint32_t        rev_state;
+       /**< current state of the lfsr for reverse direction */
+       uint32_t        deg;    /**< polynomial degree*/
+       uint32_t        bits_cnt;  /**< number of bits generated by lfsr*/
+};
+
+struct rte_thash_subtuple_helper {
+       char    name[THASH_NAME_LEN];   /** < Name of subtuple configuration */
+       LIST_ENTRY(rte_thash_subtuple_helper)   next;
+       struct thash_lfsr       *lfsr;
+       uint32_t        offset;         /** < Offset of the m-sequence */
+       uint32_t        len;            /** < Length of the m-sequence */
+       uint32_t        tuple_offset;   /** < Offset in bits of the subtuple */
+       uint32_t        tuple_len;      /** < Length in bits of the subtuple */
+       uint32_t        lsb_msk;        /** < (1 << reta_sz_log) - 1 */
+       __extension__ uint32_t  compl_table[0] __rte_cache_aligned;
+       /** < Complementary table */
+};
+
+struct rte_thash_ctx {
+       char            name[THASH_NAME_LEN];
+       LIST_HEAD(, rte_thash_subtuple_helper) head;
+       uint32_t        key_len;        /** < Length of the NIC RSS hash key */
+       uint32_t        reta_sz_log;    /** < size of the RSS ReTa in bits */
+       uint32_t        subtuples_nb;   /** < number of subtuples */
+       uint32_t        flags;
+       uint8_t         hash_key[0];
+};
+
+static inline uint32_t
+get_bit_lfsr(struct thash_lfsr *lfsr)
+{
+       uint32_t bit, ret;
+
+       /*
+        * masking the TAP bits defined by the polynomial and
+        * calculating parity
+        */
+       bit = __builtin_popcount(lfsr->state & lfsr->poly) & 0x1;
+       ret = lfsr->state & 0x1;
+       lfsr->state = ((lfsr->state >> 1) | (bit << (lfsr->deg - 1))) &
+               ((1 << lfsr->deg) - 1);
+
+       lfsr->bits_cnt++;
+       return ret;
+}
+
+static inline uint32_t
+get_rev_bit_lfsr(struct thash_lfsr *lfsr)
+{
+       uint32_t bit, ret;
+
+       bit = __builtin_popcount(lfsr->rev_state & lfsr->rev_poly) & 0x1;
+       ret = lfsr->rev_state & (1 << (lfsr->deg - 1));
+       lfsr->rev_state = ((lfsr->rev_state << 1) | bit) &
+               ((1 << lfsr->deg) - 1);
+
+       lfsr->bits_cnt++;
+       return ret;
+}
+
+static inline uint32_t
+thash_get_rand_poly(uint32_t poly_degree)
+{
+       return irreducible_poly_table[poly_degree][rte_rand() %
+               RTE_DIM(irreducible_poly_table[poly_degree])];
+}
+
+static struct thash_lfsr *
+alloc_lfsr(struct rte_thash_ctx *ctx)
+{
+       struct thash_lfsr *lfsr;
+       uint32_t i;
+
+       if (ctx == NULL)
+               return NULL;
+
+       lfsr = rte_zmalloc(NULL, sizeof(struct thash_lfsr), 0);
+       if (lfsr == NULL)
+               return NULL;
+
+       lfsr->deg = ctx->reta_sz_log;
+       lfsr->poly = thash_get_rand_poly(lfsr->deg);
+       do {
+               lfsr->state = rte_rand() & ((1 << lfsr->deg) - 1);
+       } while (lfsr->state == 0);
+       /* init reverse order polynomial */
+       lfsr->rev_poly = (lfsr->poly >> 1) | (1 << (lfsr->deg - 1));
+       /* init proper rev_state*/
+       lfsr->rev_state = lfsr->state;
+       for (i = 0; i <= lfsr->deg; i++)
+               get_rev_bit_lfsr(lfsr);
+
+       /* clear bits_cnt after rev_state was inited */
+       lfsr->bits_cnt = 0;
+       lfsr->ref_cnt = 1;
+
+       return lfsr;
+}
+
+static void
+attach_lfsr(struct rte_thash_subtuple_helper *h, struct thash_lfsr *lfsr)
+{
+       lfsr->ref_cnt++;
+       h->lfsr = lfsr;
+}
+
+static void
+free_lfsr(struct thash_lfsr *lfsr)
+{
+       lfsr->ref_cnt--;
+       if (lfsr->ref_cnt == 0)
+               rte_free(lfsr);
+}
+
+struct rte_thash_ctx *
+rte_thash_init_ctx(const char *name, uint32_t key_len, uint32_t reta_sz,
+       uint8_t *key, uint32_t flags)
+{
+       struct rte_thash_ctx *ctx;
+       struct rte_tailq_entry *te;
+       struct rte_thash_list *thash_list;
+       uint32_t i;
+
+       if ((name == NULL) || (key_len == 0) || !RETA_SZ_IN_RANGE(reta_sz)) {
+               rte_errno = EINVAL;
+               return NULL;
+       }
+
+       thash_list = RTE_TAILQ_CAST(rte_thash_tailq.head, rte_thash_list);
+
+       rte_mcfg_tailq_write_lock();
+
+       /* guarantee there's no existing */
+       TAILQ_FOREACH(te, thash_list, next) {
+               ctx = (struct rte_thash_ctx *)te->data;
+               if (strncmp(name, ctx->name, sizeof(ctx->name)) == 0)
+                       break;
+       }
+       ctx = NULL;
+       if (te != NULL) {
+               rte_errno = EEXIST;
+               goto exit;
+       }
+
+       /* allocate tailq entry */
+       te = rte_zmalloc("THASH_TAILQ_ENTRY", sizeof(*te), 0);
+       if (te == NULL) {
+               RTE_LOG(ERR, HASH,
+                       "Can not allocate tailq entry for thash context %s\n",
+                       name);
+               rte_errno = ENOMEM;
+               goto exit;
+       }
+
+       ctx = rte_zmalloc(NULL, sizeof(struct rte_thash_ctx) + key_len, 0);
+       if (ctx == NULL) {
+               RTE_LOG(ERR, HASH, "thash ctx %s memory allocation failed\n",
+                       name);
+               rte_errno = ENOMEM;
+               goto free_te;
+       }
+
+       rte_strlcpy(ctx->name, name, sizeof(ctx->name));
+       ctx->key_len = key_len;
+       ctx->reta_sz_log = reta_sz;
+       LIST_INIT(&ctx->head);
+       ctx->flags = flags;
+
+       if (key)
+               rte_memcpy(ctx->hash_key, key, key_len);
+       else {
+               for (i = 0; i < key_len; i++)
+                       ctx->hash_key[i] = rte_rand();
+       }
+
+       te->data = (void *)ctx;
+       TAILQ_INSERT_TAIL(thash_list, te, next);
+
+       rte_mcfg_tailq_write_unlock();
+
+       return ctx;
+free_te:
+       rte_free(te);
+exit:
+       rte_mcfg_tailq_write_unlock();
+       return NULL;
+}
+
+struct rte_thash_ctx *
+rte_thash_find_existing(const char *name)
+{
+       struct rte_thash_ctx *ctx;
+       struct rte_tailq_entry *te;
+       struct rte_thash_list *thash_list;
+
+       thash_list = RTE_TAILQ_CAST(rte_thash_tailq.head, rte_thash_list);
+
+       rte_mcfg_tailq_read_lock();
+       TAILQ_FOREACH(te, thash_list, next) {
+               ctx = (struct rte_thash_ctx *)te->data;
+               if (strncmp(name, ctx->name, sizeof(ctx->name)) == 0)
+                       break;
+       }
+
+       rte_mcfg_tailq_read_unlock();
+
+       if (te == NULL) {
+               rte_errno = ENOENT;
+               return NULL;
+       }
+
+       return ctx;
+}
+
+void
+rte_thash_free_ctx(struct rte_thash_ctx *ctx)
+{
+       struct rte_tailq_entry *te;
+       struct rte_thash_list *thash_list;
+       struct rte_thash_subtuple_helper *ent, *tmp;
+
+       if (ctx == NULL)
+               return;
+
+       thash_list = RTE_TAILQ_CAST(rte_thash_tailq.head, rte_thash_list);
+       rte_mcfg_tailq_write_lock();
+       TAILQ_FOREACH(te, thash_list, next) {
+               if (te->data == (void *)ctx)
+                       break;
+       }
+
+       if (te != NULL)
+               TAILQ_REMOVE(thash_list, te, next);
+
+       rte_mcfg_tailq_write_unlock();
+       ent = LIST_FIRST(&(ctx->head));
+       while (ent) {
+               free_lfsr(ent->lfsr);
+               tmp = ent;
+               ent = LIST_NEXT(ent, next);
+               LIST_REMOVE(tmp, next);
+               rte_free(tmp);
+       }
+
+       rte_free(ctx);
+       rte_free(te);
+}
+
+static inline void
+set_bit(uint8_t *ptr, uint32_t bit, uint32_t pos)
+{
+       uint32_t byte_idx = pos / CHAR_BIT;
+       /* index of the bit int byte, indexing starts from MSB */
+       uint32_t bit_idx = (CHAR_BIT - 1) - (pos & (CHAR_BIT - 1));
+       uint8_t tmp;
+
+       tmp = ptr[byte_idx];
+       tmp &= ~(1 << bit_idx);
+       tmp |= bit << bit_idx;
+       ptr[byte_idx] = tmp;
+}
+
+/**
+ * writes m-sequence to the hash_key for range [start, end]
+ * (i.e. including start and end positions)
+ */
+static int
+generate_subkey(struct rte_thash_ctx *ctx, struct thash_lfsr *lfsr,
+       uint32_t start, uint32_t end)
+{
+       uint32_t i;
+       uint32_t req_bits = (start < end) ? (end - start) : (start - end);
+       req_bits++; /* due to including end */
+
+       /* check if lfsr overflow period of the m-sequence */
+       if (((lfsr->bits_cnt + req_bits) > (1ULL << lfsr->deg) - 1) &&
+                       ((ctx->flags & RTE_THASH_IGNORE_PERIOD_OVERFLOW) !=
+                       RTE_THASH_IGNORE_PERIOD_OVERFLOW)) {
+               RTE_LOG(ERR, HASH,
+                       "Can't generate m-sequence due to period overflow\n");
+               return -ENOSPC;
+       }
+
+       if (start < end) {
+               /* original direction (from left to right)*/
+               for (i = start; i <= end; i++)
+                       set_bit(ctx->hash_key, get_bit_lfsr(lfsr), i);
+
+       } else {
+               /* reverse direction (from right to left) */
+               for (i = end; i >= start; i--)
+                       set_bit(ctx->hash_key, get_rev_bit_lfsr(lfsr), i);
+       }
+
+       return 0;
+}
+
+static inline uint32_t
+get_subvalue(struct rte_thash_ctx *ctx, uint32_t offset)
+{
+       uint32_t *tmp, val;
+
+       tmp = (uint32_t *)(&ctx->hash_key[offset >> 3]);
+       val = rte_be_to_cpu_32(*tmp);
+       val >>= (TOEPLITZ_HASH_LEN - ((offset & (CHAR_BIT - 1)) +
+               ctx->reta_sz_log));
+
+       return val & ((1 << ctx->reta_sz_log) - 1);
+}
+
+static inline void
+generate_complement_table(struct rte_thash_ctx *ctx,
+       struct rte_thash_subtuple_helper *h)
+{
+       int i, j, k;
+       uint32_t val;
+       uint32_t start;
+
+       start = h->offset + h->len - (2 * ctx->reta_sz_log - 1);
+
+       for (i = 1; i < (1 << ctx->reta_sz_log); i++) {
+               val = 0;
+               for (j = i; j; j &= (j - 1)) {
+                       k = rte_bsf32(j);
+                       val ^= get_subvalue(ctx, start - k +
+                               ctx->reta_sz_log - 1);
+               }
+               h->compl_table[val] = i;
+       }
+}
+
+static inline int
+insert_before(struct rte_thash_ctx *ctx,
+       struct rte_thash_subtuple_helper *ent,
+       struct rte_thash_subtuple_helper *cur_ent,
+       struct rte_thash_subtuple_helper *next_ent,
+       uint32_t start, uint32_t end, uint32_t range_end)
+{
+       int ret;
+
+       if (end < cur_ent->offset) {
+               ent->lfsr = alloc_lfsr(ctx);
+               if (ent->lfsr == NULL) {
+                       rte_free(ent);
+                       return -ENOMEM;
+               }
+               /* generate nonoverlapping range [start, end) */
+               ret = generate_subkey(ctx, ent->lfsr, start, end - 1);
+               if (ret != 0) {
+                       free_lfsr(ent->lfsr);
+                       rte_free(ent);
+                       return ret;
+               }
+       } else if ((next_ent != NULL) && (end > next_ent->offset)) {
+               rte_free(ent);
+               RTE_LOG(ERR, HASH,
+                       "Can't add helper %s due to conflict with existing"
+                       " helper %s\n", ent->name, next_ent->name);
+               return -ENOSPC;
+       }
+       attach_lfsr(ent, cur_ent->lfsr);
+
+       /**
+        * generate partially overlapping range
+        * [start, cur_ent->start) in reverse order
+        */
+       ret = generate_subkey(ctx, ent->lfsr, cur_ent->offset - 1, start);
+       if (ret != 0) {
+               free_lfsr(ent->lfsr);
+               rte_free(ent);
+               return ret;
+       }
+
+       if (end > range_end) {
+               /**
+                * generate partially overlapping range
+                * (range_end, end)
+                */
+               ret = generate_subkey(ctx, ent->lfsr, range_end, end - 1);
+               if (ret != 0) {
+                       free_lfsr(ent->lfsr);
+                       rte_free(ent);
+                       return ret;
+               }
+       }
+
+       LIST_INSERT_BEFORE(cur_ent, ent, next);
+       generate_complement_table(ctx, ent);
+       ctx->subtuples_nb++;
+       return 0;
+}
+
+static inline int
+insert_after(struct rte_thash_ctx *ctx,
+       struct rte_thash_subtuple_helper *ent,
+       struct rte_thash_subtuple_helper *cur_ent,
+       struct rte_thash_subtuple_helper *next_ent,
+       struct rte_thash_subtuple_helper *prev_ent,
+       uint32_t end, uint32_t range_end)
+{
+       int ret;
+
+       if ((next_ent != NULL) && (end > next_ent->offset)) {
+               rte_free(ent);
+               RTE_LOG(ERR, HASH,
+                       "Can't add helper %s due to conflict with existing"
+                       " helper %s\n", ent->name, next_ent->name);
+               return -EEXIST;
+       }
+
+       attach_lfsr(ent, cur_ent->lfsr);
+       if (end > range_end) {
+               /**
+                * generate partially overlapping range
+                * (range_end, end)
+                */
+               ret = generate_subkey(ctx, ent->lfsr, range_end, end - 1);
+               if (ret != 0) {
+                       free_lfsr(ent->lfsr);
+                       rte_free(ent);
+                       return ret;
+               }
+       }
+
+       LIST_INSERT_AFTER(prev_ent, ent, next);
+       generate_complement_table(ctx, ent);
+       ctx->subtuples_nb++;
+
+       return 0;
+}
+
+int
+rte_thash_add_helper(struct rte_thash_ctx *ctx, const char *name, uint32_t len,
+       uint32_t offset)
+{
+       struct rte_thash_subtuple_helper *ent, *cur_ent, *prev_ent, *next_ent;
+       uint32_t start, end;
+       int ret;
+
+       if ((ctx == NULL) || (name == NULL) || (len < ctx->reta_sz_log) ||
+                       ((offset + len + TOEPLITZ_HASH_LEN - 1) >
+                       ctx->key_len * CHAR_BIT))
+               return -EINVAL;
+
+       /* Check for existing name*/
+       LIST_FOREACH(cur_ent, &ctx->head, next) {
+               if (strncmp(name, cur_ent->name, sizeof(cur_ent->name)) == 0)
+                       return -EEXIST;
+       }
+
+       end = offset + len + TOEPLITZ_HASH_LEN - 1;
+       start = ((ctx->flags & RTE_THASH_MINIMAL_SEQ) ==
+               RTE_THASH_MINIMAL_SEQ) ? (end - (2 * ctx->reta_sz_log - 1)) :
+               offset;
+
+       ent = rte_zmalloc(NULL, sizeof(struct rte_thash_subtuple_helper) +
+               sizeof(uint32_t) * (1 << ctx->reta_sz_log),
+               RTE_CACHE_LINE_SIZE);
+       if (ent == NULL)
+               return -ENOMEM;
+
+       rte_strlcpy(ent->name, name, sizeof(ent->name));
+       ent->offset = start;
+       ent->len = end - start;
+       ent->tuple_offset = offset;
+       ent->tuple_len = len;
+       ent->lsb_msk = (1 << ctx->reta_sz_log) - 1;
+
+       cur_ent = LIST_FIRST(&ctx->head);
+       while (cur_ent) {
+               uint32_t range_end = cur_ent->offset + cur_ent->len;
+               next_ent = LIST_NEXT(cur_ent, next);
+               prev_ent = cur_ent;
+               /* Iterate through overlapping ranges */
+               while ((next_ent != NULL) && (next_ent->offset < range_end)) {
+                       range_end = RTE_MAX(next_ent->offset + next_ent->len,
+                               range_end);
+                       if (start > next_ent->offset)
+                               prev_ent = next_ent;
+
+                       next_ent = LIST_NEXT(next_ent, next);
+               }
+
+               if (start < cur_ent->offset)
+                       return insert_before(ctx, ent, cur_ent, next_ent,
+                               start, end, range_end);
+               else if (start < range_end)
+                       return insert_after(ctx, ent, cur_ent, next_ent,
+                               prev_ent, end, range_end);
+
+               cur_ent = next_ent;
+               continue;
+       }
+
+       ent->lfsr = alloc_lfsr(ctx);
+       if (ent->lfsr == NULL) {
+               rte_free(ent);
+               return -ENOMEM;
+       }
+
+       /* generate nonoverlapping range [start, end) */
+       ret = generate_subkey(ctx, ent->lfsr, start, end - 1);
+       if (ret != 0) {
+               free_lfsr(ent->lfsr);
+               rte_free(ent);
+               return ret;
+       }
+       if (LIST_EMPTY(&ctx->head)) {
+               LIST_INSERT_HEAD(&ctx->head, ent, next);
+       } else {
+               LIST_FOREACH(next_ent, &ctx->head, next)
+                       prev_ent = next_ent;
+
+               LIST_INSERT_AFTER(prev_ent, ent, next);
+       }
+       generate_complement_table(ctx, ent);
+       ctx->subtuples_nb++;
+
+       return 0;
+}
+
+struct rte_thash_subtuple_helper *
+rte_thash_get_helper(struct rte_thash_ctx *ctx, const char *name)
+{
+       struct rte_thash_subtuple_helper *ent;
+
+       if ((ctx == NULL) || (name == NULL))
+               return NULL;
+
+       LIST_FOREACH(ent, &ctx->head, next) {
+               if (strncmp(name, ent->name, sizeof(ent->name)) == 0)
+                       return ent;
+       }
+
+       return NULL;
+}
+
+uint32_t
+rte_thash_get_complement(struct rte_thash_subtuple_helper *h,
+       uint32_t hash, uint32_t desired_hash)
+{
+       return h->compl_table[(hash ^ desired_hash) & h->lsb_msk];
+}
+
+const uint8_t *
+rte_thash_get_key(struct rte_thash_ctx *ctx)
+{
+       return ctx->hash_key;
+}
+
+static inline void
+xor_bit(uint8_t *ptr, uint32_t bit, uint32_t pos)
+{
+       uint32_t byte_idx = pos >> 3;
+       uint32_t bit_idx = (CHAR_BIT - 1) - (pos & (CHAR_BIT - 1));
+       uint8_t tmp;
+
+       tmp = ptr[byte_idx];
+       tmp ^= bit << bit_idx;
+       ptr[byte_idx] = tmp;
+}
+
+int
+rte_thash_adjust_tuple(struct rte_thash_ctx *ctx,
+       struct rte_thash_subtuple_helper *h,
+       uint8_t *tuple, unsigned int tuple_len,
+       uint32_t desired_value, unsigned int attempts,
+       rte_thash_check_tuple_t fn, void *userdata)
+{
+       uint32_t tmp_tuple[tuple_len / sizeof(uint32_t)];
+       unsigned int i, j, ret = 0;
+       uint32_t hash, adj_bits;
+       uint8_t bit;
+       const uint8_t *hash_key;
+
+       if ((ctx == NULL) || (h == NULL) || (tuple == NULL) ||
+                       (tuple_len % sizeof(uint32_t) != 0) || (attempts <= 0))
+               return -EINVAL;
+
+       hash_key = rte_thash_get_key(ctx);
+
+       for (i = 0; i < attempts; i++) {
+               for (j = 0; j < (tuple_len / 4); j++)
+                       tmp_tuple[j] =
+                               rte_be_to_cpu_32(*(uint32_t *)&tuple[j * 4]);
+
+               hash = rte_softrss(tmp_tuple, tuple_len / 4, hash_key);
+               adj_bits = rte_thash_get_complement(h, hash, desired_value);
+
+               /*
+                * Hint: LSB of adj_bits corresponds to
+                * offset + len bit of tuple
+                */
+               for (j = 0; j < sizeof(uint32_t) * CHAR_BIT; j++) {
+                       bit = (adj_bits >> j) & 0x1;
+                       if (bit)
+                               xor_bit(tuple, bit, h->tuple_offset +
+                                       h->tuple_len - 1 - j);
+               }
+
+               if (fn != NULL) {
+                       ret = (fn(userdata, tuple)) ? 0 : -EEXIST;
+                       if (ret == 0)
+                               return 0;
+                       else if (i < (attempts - 1)) {
+                               /* Update tuple with random bits */
+                               for (j = 0; j < h->tuple_len; j++) {
+                                       bit = rte_rand() & 0x1;
+                                       if (bit)
+                                               xor_bit(tuple, bit,
+                                                       h->tuple_offset +
+                                                       h->tuple_len - 1 - j);
+                               }
+                       }
+               } else
+                       return 0;
+       }
+
+       return ret;
+}
index 061efa2..76109fc 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: BSD-3-Clause
  * Copyright(c) 2015-2019 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2021 Intel Corporation
  */
 
 #ifndef _RTE_THASH_H
@@ -222,6 +223,228 @@ rte_softrss_be(uint32_t *input_tuple, uint32_t input_len,
        return ret;
 }
 
+/** @internal Logarithm of minimum size of the RSS ReTa */
+#define        RTE_THASH_RETA_SZ_MIN   2U
+/** @internal Logarithm of maximum size of the RSS ReTa */
+#define        RTE_THASH_RETA_SZ_MAX   16U
+
+/**
+ * LFSR will ignore if generated m-sequence has more than 2^n -1 bits,
+ * where n is the logarithm of the RSS ReTa size.
+ */
+#define RTE_THASH_IGNORE_PERIOD_OVERFLOW       0x1
+/**
+ * Generate minimal required bit (equal to ReTa LSB) sequence into
+ * the hash_key
+ */
+#define RTE_THASH_MINIMAL_SEQ                  0x2
+
+/** @internal thash context structure. */
+struct rte_thash_ctx;
+/** @internal thash helper structure. */
+struct rte_thash_subtuple_helper;
+
+/**
+ * Create a new thash context.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param name
+ *  Context name
+ * @param key_len
+ *  Length of the toeplitz hash key
+ * @param reta_sz
+ *  Logarithm of the NIC's Redirection Table (ReTa) size,
+ *  i.e. number of the LSBs if the hash used to determine
+ *  the reta entry.
+ * @param key
+ *  Pointer to the key used to init an internal key state.
+ *  Could be NULL, in this case internal key will be inited with random.
+ * @param flags
+ *  Supported flags are:
+ *   RTE_THASH_IGNORE_PERIOD_OVERFLOW
+ *   RTE_THASH_MINIMAL_SEQ
+ * @return
+ *  A pointer to the created context on success
+ *  NULL otherwise
+ */
+__rte_experimental
+struct rte_thash_ctx *
+rte_thash_init_ctx(const char *name, uint32_t key_len, uint32_t reta_sz,
+       uint8_t *key, uint32_t flags);
+
+/**
+ * Find an existing thash context and return a pointer to it.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param name
+ *  Name of the thash context
+ * @return
+ *  Pointer to the thash context or NULL if it was not found with rte_errno
+ *  set appropriately. Possible rte_errno values include:
+ *   - ENOENT - required entry not available to return.
+ */
+__rte_experimental
+struct rte_thash_ctx *
+rte_thash_find_existing(const char *name);
+
+/**
+ * Free a thash context object
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param ctx
+ *  Thash context
+ * @return
+ *  None
+ */
+__rte_experimental
+void
+rte_thash_free_ctx(struct rte_thash_ctx *ctx);
+
+/**
+ * Add a special properties to the toeplitz hash key inside a thash context.
+ * Creates an internal helper struct which has a complementary table
+ * to calculate toeplitz hash collisions.
+ * This function is not multi-thread safe.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param ctx
+ *  Thash context
+ * @param name
+ *  Name of the helper
+ * @param len
+ *  Length in bits of the target subtuple
+ *  Must be no shorter than reta_sz passed on rte_thash_init_ctx().
+ * @param offset
+ *  Offset in bits of the subtuple
+ * @return
+ *  0 on success
+ *  negative on error
+ */
+__rte_experimental
+int
+rte_thash_add_helper(struct rte_thash_ctx *ctx, const char *name, uint32_t len,
+       uint32_t offset);
+
+/**
+ * Find a helper in the context by the given name
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param ctx
+ *  Thash context
+ * @param name
+ *  Name of the helper
+ * @return
+ *  Pointer to the thash helper or NULL if it was not found.
+ */
+__rte_experimental
+struct rte_thash_subtuple_helper *
+rte_thash_get_helper(struct rte_thash_ctx *ctx, const char *name);
+
+/**
+ * Get a complementary value for the subtuple to produce a
+ * partial toeplitz hash collision. It must be XOR'ed with the
+ * subtuple to produce the hash value with the desired hash LSB's
+ * This function is multi-thread safe.
+ *
+ * @param h
+ *  Pointer to the helper struct
+ * @param hash
+ *  Toeplitz hash value calculated for the given tuple
+ * @param desired_hash
+ *  Desired hash value to find a collision for
+ * @return
+ *  A complementary value which must be xored with the corresponding subtuple
+ */
+__rte_experimental
+uint32_t
+rte_thash_get_complement(struct rte_thash_subtuple_helper *h,
+       uint32_t hash, uint32_t desired_hash);
+
+/**
+ * Get a pointer to the toeplitz hash contained in the context.
+ * It changes after each addition of a helper. It should be installed to
+ * the NIC.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param ctx
+ *  Thash context
+ * @return
+ *  A pointer to the toeplitz hash key
+ */
+__rte_experimental
+const uint8_t *
+rte_thash_get_key(struct rte_thash_ctx *ctx);
+
+/**
+ * Function prototype for the rte_thash_adjust_tuple
+ * to check if adjusted tuple could be used.
+ * Generally it is some kind of lookup function to check
+ * if adjusted tuple is already in use.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param userdata
+ *  Pointer to the userdata. It could be a pointer to the
+ *  table with used tuples to search.
+ * @param tuple
+ *  Pointer to the tuple to check
+ *
+ * @return
+ *  1 on success
+ *  0 otherwise
+ */
+typedef int (*rte_thash_check_tuple_t)(void *userdata, uint8_t *tuple);
+
+/**
+ * Adjusts tuple in the way to make Toeplitz hash has
+ * desired least significant bits.
+ * This function is multi-thread safe.
+ *
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *
+ * @param ctx
+ *  Thash context
+ * @param h
+ *  Pointer to the helper struct
+ * @param tuple
+ *  Pointer to the tuple to be adjusted
+ * @param tuple_len
+ *  Length of the tuple. Must be multiple of 4.
+ * @param desired_value
+ *  Desired value of least significant bits of the hash
+ * @param attempts
+ *  Number of attempts to adjust tuple with fn() calling
+ * @param fn
+ *  Callback function to check adjusted tuple. Could be NULL
+ * @param userdata
+ *  Pointer to the userdata to be passed to fn(). Could be NULL
+ *
+ * @return
+ *  0 on success
+ *  negative otherwise
+ */
+__rte_experimental
+int
+rte_thash_adjust_tuple(struct rte_thash_ctx *ctx,
+       struct rte_thash_subtuple_helper *h,
+       uint8_t *tuple, unsigned int tuple_len,
+       uint32_t desired_value, unsigned int attempts,
+       rte_thash_check_tuple_t fn, void *userdata);
+
 #ifdef __cplusplus
 }
 #endif
index c6d7308..9b95197 100644 (file)
@@ -37,4 +37,12 @@ EXPERIMENTAL {
        rte_hash_lookup_with_hash_bulk_data;
        rte_hash_max_key_id;
        rte_hash_rcu_qsbr_add;
+       rte_thash_add_helper;
+       rte_thash_adjust_tuple;
+       rte_thash_find_existing;
+       rte_thash_free_ctx;
+       rte_thash_get_complement;
+       rte_thash_get_helper;
+       rte_thash_get_key;
+       rte_thash_init_ctx;
 };