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>
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
#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;
};
static int
-test_thash(void)
+test_toeplitz_hash_calc(void)
{
uint32_t i, j;
union rte_thash_tuple tuple;
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);
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*/
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);
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);
--- /dev/null
+<?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
--- /dev/null
+<?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
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);
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.
'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']
--- /dev/null
+/* 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;
+}
/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(c) 2015-2019 Vladimir Medvedkin <medvedkinv@gmail.com>
+ * Copyright(c) 2021 Intel Corporation
*/
#ifndef _RTE_THASH_H
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
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;
};