get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/62318/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 62318,
    "url": "http://patches.dpdk.org/api/patches/62318/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/3ee0dfc1c1b2cade4d07cc9302cac0d6d17298a5.1572621163.git.vladimir.medvedkin@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<3ee0dfc1c1b2cade4d07cc9302cac0d6d17298a5.1572621163.git.vladimir.medvedkin@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/3ee0dfc1c1b2cade4d07cc9302cac0d6d17298a5.1572621163.git.vladimir.medvedkin@intel.com",
    "date": "2019-11-01T15:21:36",
    "name": "[v6,03/12] rib: add ipv6 support for RIB",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "51f7aabc0868db836192af984ac2f8fb9a6da06c",
    "submitter": {
        "id": 1216,
        "url": "http://patches.dpdk.org/api/people/1216/?format=api",
        "name": "Vladimir Medvedkin",
        "email": "vladimir.medvedkin@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/3ee0dfc1c1b2cade4d07cc9302cac0d6d17298a5.1572621163.git.vladimir.medvedkin@intel.com/mbox/",
    "series": [
        {
            "id": 7198,
            "url": "http://patches.dpdk.org/api/series/7198/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=7198",
            "date": "2019-11-01T15:21:33",
            "name": "lib: add RIB and FIB liraries",
            "version": 6,
            "mbox": "http://patches.dpdk.org/series/7198/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/62318/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/62318/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 06FC8A00BE;\n\tFri,  1 Nov 2019 16:22:29 +0100 (CET)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 911901E535;\n\tFri,  1 Nov 2019 16:21:59 +0100 (CET)",
            "from mga09.intel.com (mga09.intel.com [134.134.136.24])\n by dpdk.org (Postfix) with ESMTP id B8CBF1BEB6\n for <dev@dpdk.org>; Fri,  1 Nov 2019 16:21:56 +0100 (CET)",
            "from orsmga004.jf.intel.com ([10.7.209.38])\n by orsmga102.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384;\n 01 Nov 2019 08:21:56 -0700",
            "from silpixa00400072.ir.intel.com ([10.237.222.213])\n by orsmga004.jf.intel.com with ESMTP; 01 Nov 2019 08:21:54 -0700"
        ],
        "X-Amp-Result": "SKIPPED(no attachment in message)",
        "X-Amp-File-Uploaded": "False",
        "X-ExtLoop1": "1",
        "X-IronPort-AV": "E=Sophos;i=\"5.68,256,1569308400\"; d=\"scan'208\";a=\"351979084\"",
        "From": "Vladimir Medvedkin <vladimir.medvedkin@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "bruce.richardson@intel.com, konstantin.ananyev@intel.com,\n thomas@monjalon.net, aconole@redhat.com",
        "Date": "Fri,  1 Nov 2019 15:21:36 +0000",
        "Message-Id": "\n <3ee0dfc1c1b2cade4d07cc9302cac0d6d17298a5.1572621163.git.vladimir.medvedkin@intel.com>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": [
            "<cover.1572621162.git.vladimir.medvedkin@intel.com>",
            "<cover.1572621162.git.vladimir.medvedkin@intel.com>"
        ],
        "References": [
            "<cover.1572621162.git.vladimir.medvedkin@intel.com>",
            "<cover.1568221361.git.vladimir.medvedkin@intel.com>\n <cover.1572621162.git.vladimir.medvedkin@intel.com>"
        ],
        "Subject": "[dpdk-dev] [PATCH v6 03/12] rib: add ipv6 support for RIB",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "Extend RIB library with ipv6 support.\n\nSigned-off-by: Vladimir Medvedkin <vladimir.medvedkin@intel.com>\n---\n lib/librte_rib/Makefile            |   4 +-\n lib/librte_rib/meson.build         |   4 +-\n lib/librte_rib/rte_rib6.c          | 598 +++++++++++++++++++++++++++++++++++++\n lib/librte_rib/rte_rib6.h          | 334 +++++++++++++++++++++\n lib/librte_rib/rte_rib_version.map |  15 +\n 5 files changed, 951 insertions(+), 4 deletions(-)\n create mode 100644 lib/librte_rib/rte_rib6.c\n create mode 100644 lib/librte_rib/rte_rib6.h",
    "diff": "diff --git a/lib/librte_rib/Makefile b/lib/librte_rib/Makefile\nindex 79f259a..6d861ad 100644\n--- a/lib/librte_rib/Makefile\n+++ b/lib/librte_rib/Makefile\n@@ -17,9 +17,9 @@ EXPORT_MAP := rte_rib_version.map\n LIBABIVER := 1\n \n # all source are stored in SRCS-y\n-SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c\n+SRCS-$(CONFIG_RTE_LIBRTE_RIB) := rte_rib.c rte_rib6.c\n \n # install this header file\n-SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h\n+SYMLINK-$(CONFIG_RTE_LIBRTE_RIB)-include := rte_rib.h rte_rib6.h\n \n include $(RTE_SDK)/mk/rte.lib.mk\ndiff --git a/lib/librte_rib/meson.build b/lib/librte_rib/meson.build\nindex e7b8920..46a1c0c 100644\n--- a/lib/librte_rib/meson.build\n+++ b/lib/librte_rib/meson.build\n@@ -3,6 +3,6 @@\n # Copyright(c) 2019 Intel Corporation\n \n allow_experimental_apis = true\n-sources = files('rte_rib.c')\n-headers = files('rte_rib.h')\n+sources = files('rte_rib.c', 'rte_rib6.c')\n+headers = files('rte_rib.h', 'rte_rib6.h')\n deps += ['mempool']\ndiff --git a/lib/librte_rib/rte_rib6.c b/lib/librte_rib/rte_rib6.c\nnew file mode 100644\nindex 0000000..78b8dcf\n--- /dev/null\n+++ b/lib/librte_rib/rte_rib6.c\n@@ -0,0 +1,598 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>\n+ * Copyright(c) 2019 Intel Corporation\n+ */\n+\n+#include <stdbool.h>\n+#include <stdint.h>\n+\n+#include <rte_eal.h>\n+#include <rte_eal_memconfig.h>\n+#include <rte_errno.h>\n+#include <rte_malloc.h>\n+#include <rte_mempool.h>\n+#include <rte_rwlock.h>\n+#include <rte_string_fns.h>\n+#include <rte_tailq.h>\n+\n+#include <rte_rib6.h>\n+\n+#define RTE_RIB_VALID_NODE\t1\n+#define RIB6_MAXDEPTH\t\t128\n+/* Maximum length of a RIB6 name. */\n+#define RTE_RIB6_NAMESIZE\t64\n+\n+TAILQ_HEAD(rte_rib6_list, rte_tailq_entry);\n+static struct rte_tailq_elem rte_rib6_tailq = {\n+\t.name = \"RTE_RIB6\",\n+};\n+EAL_REGISTER_TAILQ(rte_rib6_tailq)\n+\n+struct rte_rib6_node {\n+\tstruct rte_rib6_node\t*left;\n+\tstruct rte_rib6_node\t*right;\n+\tstruct rte_rib6_node\t*parent;\n+\tuint64_t\t\tnh;\n+\tuint8_t\t\t\tip[RTE_RIB6_IPV6_ADDR_SIZE];\n+\tuint8_t\t\t\tdepth;\n+\tuint8_t\t\t\tflag;\n+\t__extension__ uint64_t\t\text[0];\n+};\n+\n+struct rte_rib6 {\n+\tchar\t\tname[RTE_RIB6_NAMESIZE];\n+\tstruct rte_rib6_node\t*tree;\n+\tstruct rte_mempool\t*node_pool;\n+\tuint32_t\t\tcur_nodes;\n+\tuint32_t\t\tcur_routes;\n+\tint\t\t\tmax_nodes;\n+};\n+\n+static inline bool\n+is_valid_node(struct rte_rib6_node *node)\n+{\n+\treturn (node->flag & RTE_RIB_VALID_NODE) == RTE_RIB_VALID_NODE;\n+}\n+\n+static inline bool\n+is_right_node(struct rte_rib6_node *node)\n+{\n+\treturn node->parent->right == node;\n+}\n+\n+/*\n+ * Check if ip1 is covered by ip2/depth prefix\n+ */\n+static inline bool\n+is_covered(const uint8_t ip1[RTE_RIB6_IPV6_ADDR_SIZE],\n+\t\tconst uint8_t ip2[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)\n+\t\tif ((ip1[i] ^ ip2[i]) & get_msk_part(depth, i))\n+\t\t\treturn false;\n+\n+\treturn true;\n+}\n+\n+static inline int\n+get_dir(const uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)\n+{\n+\tint i = 0;\n+\tuint8_t p_depth, msk;\n+\n+\tfor (p_depth = depth; p_depth >= 8; p_depth -= 8)\n+\t\ti++;\n+\n+\tmsk = 1 << (7 - p_depth);\n+\treturn (ip[i] & msk) != 0;\n+}\n+\n+static inline struct rte_rib6_node *\n+get_nxt_node(struct rte_rib6_node *node,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])\n+{\n+\treturn (get_dir(ip, node->depth)) ? node->right : node->left;\n+}\n+\n+static struct rte_rib6_node *\n+node_alloc(struct rte_rib6 *rib)\n+{\n+\tstruct rte_rib6_node *ent;\n+\tint ret;\n+\n+\tret = rte_mempool_get(rib->node_pool, (void *)&ent);\n+\tif (unlikely(ret != 0))\n+\t\treturn NULL;\n+\t++rib->cur_nodes;\n+\treturn ent;\n+}\n+\n+static void\n+node_free(struct rte_rib6 *rib, struct rte_rib6_node *ent)\n+{\n+\t--rib->cur_nodes;\n+\trte_mempool_put(rib->node_pool, ent);\n+}\n+\n+struct rte_rib6_node *\n+rte_rib6_lookup(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])\n+{\n+\tstruct rte_rib6_node *cur;\n+\tstruct rte_rib6_node *prev = NULL;\n+\n+\tif (unlikely(rib == NULL)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\tcur = rib->tree;\n+\n+\twhile ((cur != NULL) && is_covered(ip, cur->ip, cur->depth)) {\n+\t\tif (is_valid_node(cur))\n+\t\t\tprev = cur;\n+\t\tcur = get_nxt_node(cur, ip);\n+\t}\n+\treturn prev;\n+}\n+\n+struct rte_rib6_node *\n+rte_rib6_lookup_parent(struct rte_rib6_node *ent)\n+{\n+\tstruct rte_rib6_node *tmp;\n+\n+\tif (ent == NULL)\n+\t\treturn NULL;\n+\n+\ttmp = ent->parent;\n+\twhile ((tmp != NULL) && (!is_valid_node(tmp)))\n+\t\ttmp = tmp->parent;\n+\n+\treturn tmp;\n+}\n+\n+struct rte_rib6_node *\n+rte_rib6_lookup_exact(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)\n+{\n+\tstruct rte_rib6_node *cur;\n+\tuint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];\n+\tint i;\n+\n+\tif ((rib == NULL) || (ip == NULL) || (depth > RIB6_MAXDEPTH)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\tcur = rib->tree;\n+\n+\tfor (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)\n+\t\ttmp_ip[i] = ip[i] & get_msk_part(depth, i);\n+\n+\twhile (cur != NULL) {\n+\t\tif (rte_rib6_is_equal(cur->ip, tmp_ip) &&\n+\t\t\t\t(cur->depth == depth) &&\n+\t\t\t\tis_valid_node(cur))\n+\t\t\treturn cur;\n+\n+\t\tif (!(is_covered(tmp_ip, cur->ip, cur->depth)) ||\n+\t\t\t\t(cur->depth >= depth))\n+\t\t\tbreak;\n+\n+\t\tcur = get_nxt_node(cur, tmp_ip);\n+\t}\n+\n+\treturn NULL;\n+}\n+\n+/*\n+ *  Traverses on subtree and retreeves more specific routes\n+ *  for a given in args ip/depth prefix\n+ *  last = NULL means the first invocation\n+ */\n+struct rte_rib6_node *\n+rte_rib6_get_nxt(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE],\n+\tuint8_t depth, struct rte_rib6_node *last, int flag)\n+{\n+\tstruct rte_rib6_node *tmp, *prev = NULL;\n+\tuint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];\n+\tint i;\n+\n+\tif ((rib == NULL) || (ip == NULL) || (depth > RIB6_MAXDEPTH)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\n+\tfor (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)\n+\t\ttmp_ip[i] = ip[i] & get_msk_part(depth, i);\n+\n+\tif (last == NULL) {\n+\t\ttmp = rib->tree;\n+\t\twhile ((tmp) && (tmp->depth < depth))\n+\t\t\ttmp = get_nxt_node(tmp, tmp_ip);\n+\t} else {\n+\t\ttmp = last;\n+\t\twhile ((tmp->parent != NULL) && (is_right_node(tmp) ||\n+\t\t\t\t(tmp->parent->right == NULL))) {\n+\t\t\ttmp = tmp->parent;\n+\t\t\tif (is_valid_node(tmp) &&\n+\t\t\t\t\t(is_covered(tmp->ip, tmp_ip, depth) &&\n+\t\t\t\t\t(tmp->depth > depth)))\n+\t\t\t\treturn tmp;\n+\t\t}\n+\t\ttmp = (tmp->parent != NULL) ? tmp->parent->right : NULL;\n+\t}\n+\twhile (tmp) {\n+\t\tif (is_valid_node(tmp) &&\n+\t\t\t\t(is_covered(tmp->ip, tmp_ip, depth) &&\n+\t\t\t\t(tmp->depth > depth))) {\n+\t\t\tprev = tmp;\n+\t\t\tif (flag == RTE_RIB6_GET_NXT_COVER)\n+\t\t\t\treturn prev;\n+\t\t}\n+\t\ttmp = (tmp->left != NULL) ? tmp->left : tmp->right;\n+\t}\n+\treturn prev;\n+}\n+\n+void\n+rte_rib6_remove(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)\n+{\n+\tstruct rte_rib6_node *cur, *prev, *child;\n+\n+\tcur = rte_rib6_lookup_exact(rib, ip, depth);\n+\tif (cur == NULL)\n+\t\treturn;\n+\n+\t--rib->cur_routes;\n+\tcur->flag &= ~RTE_RIB_VALID_NODE;\n+\twhile (!is_valid_node(cur)) {\n+\t\tif ((cur->left != NULL) && (cur->right != NULL))\n+\t\t\treturn;\n+\t\tchild = (cur->left == NULL) ? cur->right : cur->left;\n+\t\tif (child != NULL)\n+\t\t\tchild->parent = cur->parent;\n+\t\tif (cur->parent == NULL) {\n+\t\t\trib->tree = child;\n+\t\t\tnode_free(rib, cur);\n+\t\t\treturn;\n+\t\t}\n+\t\tif (cur->parent->left == cur)\n+\t\t\tcur->parent->left = child;\n+\t\telse\n+\t\t\tcur->parent->right = child;\n+\t\tprev = cur;\n+\t\tcur = cur->parent;\n+\t\tnode_free(rib, prev);\n+\t}\n+}\n+\n+struct rte_rib6_node *\n+rte_rib6_insert(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth)\n+{\n+\tstruct rte_rib6_node **tmp;\n+\tstruct rte_rib6_node *prev = NULL;\n+\tstruct rte_rib6_node *new_node = NULL;\n+\tstruct rte_rib6_node *common_node = NULL;\n+\tuint8_t common_prefix[RTE_RIB6_IPV6_ADDR_SIZE];\n+\tuint8_t tmp_ip[RTE_RIB6_IPV6_ADDR_SIZE];\n+\tint i, d;\n+\tuint8_t common_depth, ip_xor;\n+\n+\tif (unlikely((rib == NULL) || (ip == NULL) ||\n+\t\t\t(depth > RIB6_MAXDEPTH))) {\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\n+\ttmp = &rib->tree;\n+\n+\tfor (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)\n+\t\ttmp_ip[i] = ip[i] & get_msk_part(depth, i);\n+\n+\tnew_node = rte_rib6_lookup_exact(rib, tmp_ip, depth);\n+\tif (new_node != NULL) {\n+\t\trte_errno = EEXIST;\n+\t\treturn NULL;\n+\t}\n+\n+\tnew_node = node_alloc(rib);\n+\tif (new_node == NULL) {\n+\t\trte_errno = ENOMEM;\n+\t\treturn NULL;\n+\t}\n+\tnew_node->left = NULL;\n+\tnew_node->right = NULL;\n+\tnew_node->parent = NULL;\n+\trte_rib6_copy_addr(new_node->ip, tmp_ip);\n+\tnew_node->depth = depth;\n+\tnew_node->flag = RTE_RIB_VALID_NODE;\n+\n+\t/* traverse down the tree to find matching node or closest matching */\n+\twhile (1) {\n+\t\t/* insert as the last node in the branch */\n+\t\tif (*tmp == NULL) {\n+\t\t\t*tmp = new_node;\n+\t\t\tnew_node->parent = prev;\n+\t\t\t++rib->cur_routes;\n+\t\t\treturn *tmp;\n+\t\t}\n+\t\t/*\n+\t\t * Intermediate node found.\n+\t\t * Previous rte_rib6_lookup_exact() returned NULL\n+\t\t * but node with proper search criteria is found.\n+\t\t * Validate intermediate node and return.\n+\t\t */\n+\t\tif (rte_rib6_is_equal(tmp_ip, (*tmp)->ip) &&\n+\t\t\t\t(depth == (*tmp)->depth)) {\n+\t\t\tnode_free(rib, new_node);\n+\t\t\t(*tmp)->flag |= RTE_RIB_VALID_NODE;\n+\t\t\t++rib->cur_routes;\n+\t\t\treturn *tmp;\n+\t\t}\n+\n+\t\tif (!is_covered(tmp_ip, (*tmp)->ip, (*tmp)->depth) ||\n+\t\t\t\t((*tmp)->depth >= depth)) {\n+\t\t\tbreak;\n+\t\t}\n+\t\tprev = *tmp;\n+\n+\t\ttmp = (get_dir(tmp_ip, (*tmp)->depth)) ? &(*tmp)->right :\n+\t\t\t\t&(*tmp)->left;\n+\t}\n+\n+\t/* closest node found, new_node should be inserted in the middle */\n+\tcommon_depth = RTE_MIN(depth, (*tmp)->depth);\n+\tfor (i = 0, d = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++) {\n+\t\tip_xor = tmp_ip[i] ^ (*tmp)->ip[i];\n+\t\tif (ip_xor == 0)\n+\t\t\td += 8;\n+\t\telse {\n+\t\t\td += __builtin_clz(ip_xor << 24);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tcommon_depth = RTE_MIN(d, common_depth);\n+\n+\tfor (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++)\n+\t\tcommon_prefix[i] = tmp_ip[i] & get_msk_part(common_depth, i);\n+\n+\tif (rte_rib6_is_equal(common_prefix, tmp_ip) &&\n+\t\t\t(common_depth == depth)) {\n+\t\t/* insert as a parent */\n+\t\tif (get_dir((*tmp)->ip, depth))\n+\t\t\tnew_node->right = *tmp;\n+\t\telse\n+\t\t\tnew_node->left = *tmp;\n+\t\tnew_node->parent = (*tmp)->parent;\n+\t\t(*tmp)->parent = new_node;\n+\t\t*tmp = new_node;\n+\t} else {\n+\t\t/* create intermediate node */\n+\t\tcommon_node = node_alloc(rib);\n+\t\tif (common_node == NULL) {\n+\t\t\tnode_free(rib, new_node);\n+\t\t\trte_errno = ENOMEM;\n+\t\t\treturn NULL;\n+\t\t}\n+\t\trte_rib6_copy_addr(common_node->ip, common_prefix);\n+\t\tcommon_node->depth = common_depth;\n+\t\tcommon_node->flag = 0;\n+\t\tcommon_node->parent = (*tmp)->parent;\n+\t\tnew_node->parent = common_node;\n+\t\t(*tmp)->parent = common_node;\n+\t\tif (get_dir((*tmp)->ip, common_depth) == 1) {\n+\t\t\tcommon_node->left = new_node;\n+\t\t\tcommon_node->right = *tmp;\n+\t\t} else {\n+\t\t\tcommon_node->left = *tmp;\n+\t\t\tcommon_node->right = new_node;\n+\t\t}\n+\t\t*tmp = common_node;\n+\t}\n+\t++rib->cur_routes;\n+\treturn new_node;\n+}\n+\n+int\n+rte_rib6_get_ip(struct rte_rib6_node *node, uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE])\n+{\n+\tif ((node == NULL) || (ip == NULL)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\trte_rib6_copy_addr(ip, node->ip);\n+\treturn 0;\n+}\n+\n+int\n+rte_rib6_get_depth(struct rte_rib6_node *node, uint8_t *depth)\n+{\n+\tif ((node == NULL) || (depth == NULL)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\t*depth = node->depth;\n+\treturn 0;\n+}\n+\n+void *\n+rte_rib6_get_ext(struct rte_rib6_node *node)\n+{\n+\treturn (node == NULL) ? NULL : &node->ext[0];\n+}\n+\n+int\n+rte_rib6_get_nh(struct rte_rib6_node *node, uint64_t *nh)\n+{\n+\tif ((node == NULL) || (nh == NULL)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\t*nh = node->nh;\n+\treturn 0;\n+}\n+\n+int\n+rte_rib6_set_nh(struct rte_rib6_node *node, uint64_t nh)\n+{\n+\tif (node == NULL) {\n+\t\trte_errno = EINVAL;\n+\t\treturn -1;\n+\t}\n+\tnode->nh = nh;\n+\treturn 0;\n+}\n+\n+struct rte_rib6 *\n+rte_rib6_create(const char *name, int socket_id, struct rte_rib6_conf *conf)\n+{\n+\tchar mem_name[RTE_RIB6_NAMESIZE];\n+\tstruct rte_rib6 *rib = NULL;\n+\tstruct rte_tailq_entry *te;\n+\tstruct rte_rib6_list *rib6_list;\n+\tstruct rte_mempool *node_pool;\n+\n+\t/* Check user arguments. */\n+\tif ((name == NULL) || (conf == NULL) ||\n+\t\t\t(conf->max_nodes == 0)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\n+\tsnprintf(mem_name, sizeof(mem_name), \"MP_%s\", name);\n+\tnode_pool = rte_mempool_create(mem_name, conf->max_nodes,\n+\t\tsizeof(struct rte_rib6_node) + conf->ext_sz, 0, 0,\n+\t\tNULL, NULL, NULL, NULL, socket_id, 0);\n+\n+\tif (node_pool == NULL) {\n+\t\tRTE_LOG(ERR, LPM,\n+\t\t\t\"Can not allocate mempool for RIB6 %s\\n\", name);\n+\t\treturn NULL;\n+\t}\n+\n+\tsnprintf(mem_name, sizeof(mem_name), \"RIB6_%s\", name);\n+\trib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);\n+\n+\trte_mcfg_tailq_write_lock();\n+\n+\t/* guarantee there's no existing */\n+\tTAILQ_FOREACH(te, rib6_list, next) {\n+\t\trib = (struct rte_rib6 *)te->data;\n+\t\tif (strncmp(name, rib->name, RTE_RIB6_NAMESIZE) == 0)\n+\t\t\tbreak;\n+\t}\n+\trib = NULL;\n+\tif (te != NULL) {\n+\t\trte_errno = EEXIST;\n+\t\tgoto exit;\n+\t}\n+\n+\t/* allocate tailq entry */\n+\tte = rte_zmalloc(\"RIB6_TAILQ_ENTRY\", sizeof(*te), 0);\n+\tif (te == NULL) {\n+\t\tRTE_LOG(ERR, LPM,\n+\t\t\t\"Can not allocate tailq entry for RIB6 %s\\n\", name);\n+\t\trte_errno = ENOMEM;\n+\t\tgoto exit;\n+\t}\n+\n+\t/* Allocate memory to store the RIB6 data structures. */\n+\trib = rte_zmalloc_socket(mem_name,\n+\t\tsizeof(struct rte_rib6), RTE_CACHE_LINE_SIZE, socket_id);\n+\tif (rib == NULL) {\n+\t\tRTE_LOG(ERR, LPM, \"RIB6 %s memory allocation failed\\n\", name);\n+\t\trte_errno = ENOMEM;\n+\t\tgoto free_te;\n+\t}\n+\n+\trte_strlcpy(rib->name, name, sizeof(rib->name));\n+\trib->tree = NULL;\n+\trib->max_nodes = conf->max_nodes;\n+\trib->node_pool = node_pool;\n+\n+\tte->data = (void *)rib;\n+\tTAILQ_INSERT_TAIL(rib6_list, te, next);\n+\n+\trte_mcfg_tailq_write_unlock();\n+\n+\treturn rib;\n+\n+free_te:\n+\trte_free(te);\n+exit:\n+\trte_mcfg_tailq_write_unlock();\n+\trte_mempool_free(node_pool);\n+\n+\treturn NULL;\n+}\n+\n+struct rte_rib6 *\n+rte_rib6_find_existing(const char *name)\n+{\n+\tstruct rte_rib6 *rib = NULL;\n+\tstruct rte_tailq_entry *te;\n+\tstruct rte_rib6_list *rib6_list;\n+\n+\tif (unlikely(name == NULL)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\n+\trib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);\n+\n+\trte_mcfg_tailq_read_lock();\n+\tTAILQ_FOREACH(te, rib6_list, next) {\n+\t\trib = (struct rte_rib6 *) te->data;\n+\t\tif (strncmp(name, rib->name, RTE_RIB6_NAMESIZE) == 0)\n+\t\t\tbreak;\n+\t}\n+\trte_mcfg_tailq_read_unlock();\n+\n+\tif (te == NULL) {\n+\t\trte_errno = ENOENT;\n+\t\treturn NULL;\n+\t}\n+\n+\treturn rib;\n+}\n+\n+void\n+rte_rib6_free(struct rte_rib6 *rib)\n+{\n+\tstruct rte_tailq_entry *te;\n+\tstruct rte_rib6_list *rib6_list;\n+\tstruct rte_rib6_node *tmp = NULL;\n+\n+\tif (unlikely(rib == NULL)) {\n+\t\trte_errno = EINVAL;\n+\t\treturn;\n+\t}\n+\n+\trib6_list = RTE_TAILQ_CAST(rte_rib6_tailq.head, rte_rib6_list);\n+\n+\trte_mcfg_tailq_write_lock();\n+\n+\t/* find our tailq entry */\n+\tTAILQ_FOREACH(te, rib6_list, next) {\n+\t\tif (te->data == (void *)rib)\n+\t\t\tbreak;\n+\t}\n+\tif (te != NULL)\n+\t\tTAILQ_REMOVE(rib6_list, te, next);\n+\n+\trte_mcfg_tailq_write_unlock();\n+\n+\twhile ((tmp = rte_rib6_get_nxt(rib, 0, 0, tmp,\n+\t\t\tRTE_RIB6_GET_NXT_ALL)) != NULL)\n+\t\trte_rib6_remove(rib, tmp->ip, tmp->depth);\n+\n+\trte_mempool_free(rib->node_pool);\n+\n+\trte_free(rib);\n+\trte_free(te);\n+}\ndiff --git a/lib/librte_rib/rte_rib6.h b/lib/librte_rib/rte_rib6.h\nnew file mode 100644\nindex 0000000..8714571\n--- /dev/null\n+++ b/lib/librte_rib/rte_rib6.h\n@@ -0,0 +1,334 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2018 Vladimir Medvedkin <medvedkinv@gmail.com>\n+ * Copyright(c) 2019 Intel Corporation\n+ */\n+\n+#ifndef _RTE_RIB6_H_\n+#define _RTE_RIB6_H_\n+\n+/**\n+ * @file\n+ * Level compressed tree implementation for IPv6 Longest Prefix Match\n+ */\n+\n+#include <rte_memcpy.h>\n+#include <rte_compat.h>\n+\n+#define RTE_RIB6_IPV6_ADDR_SIZE\t16\n+\n+/**\n+ * rte_rib6_get_nxt() flags\n+ */\n+enum {\n+\t/** flag to get all subroutes in a RIB tree */\n+\tRTE_RIB6_GET_NXT_ALL,\n+\t/** flag to get first matched subroutes in a RIB tree */\n+\tRTE_RIB6_GET_NXT_COVER\n+};\n+\n+struct rte_rib6;\n+struct rte_rib6_node;\n+\n+/** RIB configuration structure */\n+struct rte_rib6_conf {\n+\t/**\n+\t * Size of extension block inside rte_rib_node.\n+\t * This space could be used to store additional user\n+\t * defined data.\n+\t */\n+\tsize_t\text_sz;\n+\t/* size of rte_rib_node's pool */\n+\tint\tmax_nodes;\n+};\n+\n+/**\n+ * Copy IPv6 address from one location to another\n+ *\n+ * @param dst\n+ *  pointer to the place to copy\n+ * @param src\n+ *  pointer from where to copy\n+ */\n+static inline void\n+rte_rib6_copy_addr(uint8_t *dst, const uint8_t *src)\n+{\n+\tif ((dst == NULL) || (src == NULL))\n+\t\treturn;\n+\trte_memcpy(dst, src, RTE_RIB6_IPV6_ADDR_SIZE);\n+}\n+\n+/**\n+ * Compare two IPv6 addresses\n+ *\n+ * @param ip1\n+ *  pointer to the first ipv6 address\n+ * @param ip2\n+ *  pointer to the second ipv6 address\n+ *\n+ * @return\n+ *  1 if equal\n+ *  0 otherwise\n+ */\n+static inline int\n+rte_rib6_is_equal(uint8_t *ip1, uint8_t *ip2) {\n+\tint i;\n+\n+\tif ((ip1 == NULL) || (ip2 == NULL))\n+\t\treturn 0;\n+\tfor (i = 0; i < RTE_RIB6_IPV6_ADDR_SIZE; i++) {\n+\t\tif (ip1[i] != ip2[i])\n+\t\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+/**\n+ * Get 8-bit part of 128-bit IPv6 mask\n+ *\n+ * @param depth\n+ *  ipv6 prefix length\n+ * @param byte\n+ *  position of a 8-bit chunk in the 128-bit mask\n+ *\n+ * @return\n+ *  8-bit chunk of the 128-bit IPv6 mask\n+ */\n+static inline uint8_t\n+get_msk_part(uint8_t depth, int byte) {\n+\tuint8_t part;\n+\n+\tbyte &= 0xf;\n+\tdepth = RTE_MIN(depth, 128);\n+\tpart = RTE_MAX((int16_t)depth - (byte * 8), 0);\n+\tpart = (part > 8) ? 8 : part;\n+\treturn (uint16_t)(~UINT8_MAX) >> part;\n+}\n+\n+/**\n+ * Lookup an IP into the RIB structure\n+ *\n+ * @param rib\n+ *  RIB object handle\n+ * @param ip\n+ *  IP to be looked up in the RIB\n+ * @return\n+ *  pointer to struct rte_rib6_node on success\n+ *  NULL otherwise\n+ */\n+__rte_experimental\n+struct rte_rib6_node *\n+rte_rib6_lookup(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE]);\n+\n+/**\n+ * Lookup less specific route into the RIB structure\n+ *\n+ * @param ent\n+ *  Pointer to struct rte_rib6_node that represents target route\n+ * @return\n+ *  pointer to struct rte_rib6_node that represents\n+ *   less specific route on success\n+ *  NULL otherwise\n+ */\n+__rte_experimental\n+struct rte_rib6_node *\n+rte_rib6_lookup_parent(struct rte_rib6_node *ent);\n+\n+/**\n+ * Provides exact mach lookup of the prefix into the RIB structure\n+ *\n+ * @param rib\n+ *  RIB object handle\n+ * @param ip\n+ *  net to be looked up in the RIB\n+ * @param depth\n+ *  prefix length\n+ * @return\n+ *  pointer to struct rte_rib6_node on success\n+ *  NULL otherwise\n+ */\n+__rte_experimental\n+struct rte_rib6_node *\n+rte_rib6_lookup_exact(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);\n+\n+/**\n+ * Retrieve next more specific prefix from the RIB\n+ * that is covered by ip/depth supernet in an ascending order\n+ *\n+ * @param rib\n+ *  RIB object handle\n+ * @param ip\n+ *  net address of supernet prefix that covers returned more specific prefixes\n+ * @param depth\n+ *  supernet prefix length\n+ * @param last\n+ *   pointer to the last returned prefix to get next prefix\n+ *   or\n+ *   NULL to get first more specific prefix\n+ * @param flag\n+ *  -RTE_RIB6_GET_NXT_ALL\n+ *   get all prefixes from subtrie\n+ *  -RTE_RIB6_GET_NXT_COVER\n+ *   get only first more specific prefix even if it have more specifics\n+ * @return\n+ *  pointer to the next more specific prefix\n+ *  NULL if there is no prefixes left\n+ */\n+__rte_experimental\n+struct rte_rib6_node *\n+rte_rib6_get_nxt(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE],\n+\tuint8_t depth, struct rte_rib6_node *last, int flag);\n+\n+/**\n+ * Remove prefix from the RIB\n+ *\n+ * @param rib\n+ *  RIB object handle\n+ * @param ip\n+ *  net to be removed from the RIB\n+ * @param depth\n+ *  prefix length\n+ */\n+__rte_experimental\n+void\n+rte_rib6_remove(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);\n+\n+/**\n+ * Insert prefix into the RIB\n+ *\n+ * @param rib\n+ *  RIB object handle\n+ * @param ip\n+ *  net to be inserted to the RIB\n+ * @param depth\n+ *  prefix length\n+ * @return\n+ *  pointer to new rte_rib6_node on success\n+ *  NULL otherwise\n+ */\n+__rte_experimental\n+struct rte_rib6_node *\n+rte_rib6_insert(struct rte_rib6 *rib,\n+\tconst uint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE], uint8_t depth);\n+\n+/**\n+ * Get an ip from rte_rib6_node\n+ *\n+ * @param node\n+ *  pointer to the rib6 node\n+ * @param ip\n+ *  pointer to the ipv6 to save\n+ * @return\n+ *  0 on success\n+ *  -1 on failure with rte_errno indicating reason for failure.\n+ */\n+__rte_experimental\n+int\n+rte_rib6_get_ip(struct rte_rib6_node *node,\n+\tuint8_t ip[RTE_RIB6_IPV6_ADDR_SIZE]);\n+\n+/**\n+ * Get a depth from rte_rib6_node\n+ *\n+ * @param node\n+ *  pointer to the rib6 node\n+ * @param depth\n+ *  pointer to the depth to save\n+ * @return\n+ *  0 on success\n+ *  -1 on failure with rte_errno indicating reason for failure.\n+ */\n+__rte_experimental\n+int\n+rte_rib6_get_depth(struct rte_rib6_node *node, uint8_t *depth);\n+\n+/**\n+ * Get ext field from the rte_rib6_node\n+ * It is caller responsibility to make sure there are necessary space\n+ * for the ext field inside rib6 node.\n+ *\n+ * @param node\n+ *  pointer to the rte_rib6_node\n+ * @return\n+ *  pointer to the ext\n+ */\n+__rte_experimental\n+void *\n+rte_rib6_get_ext(struct rte_rib6_node *node);\n+\n+/**\n+ * Get nexthop from the rte_rib6_node\n+ *\n+ * @param node\n+ *  pointer to the rib6 node\n+ * @param nh\n+ *  pointer to the nexthop to save\n+ * @return\n+ *  0 on success\n+ *  -1 on failure, with rte_errno indicating reason for failure.\n+ */\n+__rte_experimental\n+int\n+rte_rib6_get_nh(struct rte_rib6_node *node, uint64_t *nh);\n+\n+/**\n+ * Set nexthop into the rte_rib6_node\n+ *\n+ * @param node\n+ *  pointer to the rib6 node\n+ * @param nh\n+ *  nexthop value to set to the rib6 node\n+ * @return\n+ *  0 on success\n+ *  -1 on failure, with rte_errno indicating reason for failure.\n+ */\n+__rte_experimental\n+int\n+rte_rib6_set_nh(struct rte_rib6_node *node, uint64_t nh);\n+\n+/**\n+ * Create RIB\n+ *\n+ * @param name\n+ *  RIB name\n+ * @param socket_id\n+ *  NUMA socket ID for RIB table memory allocation\n+ * @param conf\n+ *  Structure containing the configuration\n+ * @return\n+ *  Pointer to RIB object on success\n+ *  NULL otherwise with rte_errno indicating reason for failure.\n+ */\n+__rte_experimental\n+struct rte_rib6 *\n+rte_rib6_create(const char *name, int socket_id, struct rte_rib6_conf *conf);\n+\n+/**\n+ * Find an existing RIB object and return a pointer to it.\n+ *\n+ * @param name\n+ *  Name of the rib object as passed to rte_rib_create()\n+ * @return\n+ *  Pointer to RIB object on success\n+ *  NULL otherwise with rte_errno indicating reason for failure.\n+ */\n+__rte_experimental\n+struct rte_rib6 *\n+rte_rib6_find_existing(const char *name);\n+\n+/**\n+ * Free an RIB object.\n+ *\n+ * @param rib\n+ *   RIB object handle\n+ * @return\n+ *   None\n+ */\n+__rte_experimental\n+void\n+rte_rib6_free(struct rte_rib6 *rib);\n+\n+#endif /* _RTE_RIB_H_ */\ndiff --git a/lib/librte_rib/rte_rib_version.map b/lib/librte_rib/rte_rib_version.map\nindex 1432a22..9b6161a 100644\n--- a/lib/librte_rib/rte_rib_version.map\n+++ b/lib/librte_rib/rte_rib_version.map\n@@ -16,5 +16,20 @@ EXPERIMENTAL {\n \trte_rib_set_nh;\n \trte_rib_remove;\n \n+\trte_rib6_create;\n+\trte_rib6_find_existing;\n+\trte_rib6_free;\n+\trte_rib6_get_depth;\n+\trte_rib6_get_ext;\n+\trte_rib6_get_ip;\n+\trte_rib6_get_nh;\n+\trte_rib6_get_nxt;\n+\trte_rib6_insert;\n+\trte_rib6_lookup;\n+\trte_rib6_lookup_parent;\n+\trte_rib6_lookup_exact;\n+\trte_rib6_set_nh;\n+\trte_rib6_remove;\n+\n \tlocal: *;\n };\n",
    "prefixes": [
        "v6",
        "03/12"
    ]
}