summaryrefslogtreecommitdiffstats
path: root/src/vnet/fib/ip6_fib.c
blob: 708fddce66aaf5e7e6f207c74f03dc4a9721d878 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (c) 2015 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef included_vnet_api_errno_h
#define included_vnet_api_errno_h

#define foreach_vnet_api_error						\
_(UNSPECIFIED, -1, "Unspecified Error")                                 \
_(INVALID_SW_IF_INDEX, -2, "Invalid sw_if_index")                       \
_(NO_SUCH_FIB, -3, "No such FIB / VRF")                                 \
_(NO_SUCH_INNER_FIB, -4, "No such inner FIB / VRF")                     \
_(NO_SUCH_LABEL, -5, "No such label")                                   \
_(NO_SUCH_ENTRY, -6, "No such entry")                                   \
_(INVALID_VALUE, -7, "Invalid value")                                   \
_(INVALID_VALUE_2, -8, "Invalid value #2")                              \
_(UNIMPLEMENTED, -9, "Unimplemented")                                   \
_(INVALID_SW_IF_INDEX_2, -10, "Invalid sw_if_index #2")                 \
_(SYSCALL_ERROR_1, -11, "System call error #1")                         \
_(SYSCALL_ERROR_2, -12, "System call error #2")                         \
_(SYSCALL_ERROR_3, -13, "System call error #3")                         \
_(SYSCALL_ERROR_4, -14, "System call error #4")                         \
_(SYSCALL_ERROR_5, -15, "System call error #5")                         \
_(SYSCALL_ERROR_6, -16, "System call error #6")                         \
_(SYSCALL_ERROR_7, -17, "System call error #7")                         \
_(SYSCALL_ERROR_8, -18, "System call error #8")                         \
_(SYSCALL_ERROR_9, -19, "System call error #9")                         \
_(SYSCALL_ERROR_10, -20, "System call error #10")                       \
_(FEATURE_DISABLED, -30, "Feature disabled by configuration")           \
_(INVALID_REGISTRATION, -31, "Invalid registration")                    \
_(NEXT_HOP_NOT_IN_FIB, -50, "Next hop not in FIB")                      \
_(UNKNOWN_DESTINATION, -51, "Unknown destination")                      \
_(PREFIX_MATCHES_NEXT_HOP, -52, "Prefix matches next hop")              \
_(NEXT_HOP_NOT_FOUND_MP, -53, "Next hop not found (multipath)")         \
_(NO_MATCHING_INTERFACE, -54, "No matching interface for probe")        \
_(INVALID_VLAN, -55, "Invalid VLAN")                                    \
_(VLAN_ALREADY_EXISTS, -56, "VLAN subif already exists")                \
_(INVALID_SRC_ADDRESS, -57, "Invalid src address")                      \
_(INVALID_DST_ADDRESS, -58, "Invalid dst address")                      \
_(ADDRESS_LENGTH_MISMATCH, -59, "Address length mismatch")              \
_(ADDRESS_NOT_FOUND_FOR_INTERFACE, -60, "Address not found for interface") \
_(ADDRESS_NOT_LINK_LOCAL, -61, "Address not link-local")                \
_(IP6_NOT_ENABLED, -62, "ip6 not enabled")				\
_(IN_PROGRESS, 10, "Operation in progress")				\
_(NO_SUCH_NODE, -63, "No such graph node")				\
_(NO_SUCH_NODE2, -64, "No such graph node #2")				\
_(NO_SUCH_TABLE, -65, "No such table")                                  \
_(NO_SUCH_TABLE2, -66, "No such table #2")                              \
_(NO_SUCH_TABLE3, -67, "No such table #3")                              \
_(SUBIF_ALREADY_EXISTS, -68, "Subinterface already exists")             \
_(SUBIF_CREATE_FAILED, -69, "Subinterface creation failed")		\
_(INVALID_MEMORY_SIZE, -70, "Invalid memory size requested")            \
_(INVALID_INTERFACE, -71, "Invalid interface")                          \
_(INVALID_VLAN_TAG_COUNT, -72, "Invalid number of tags for requested operation") \
_(INVALID_ARGUMENT, -73, "Invalid argument")                            \
_(UNEXPECTED_INTF_STATE, -74, "Unexpected interface state")             \
_(TUNNEL_EXIST, -75, "Tunnel already exists")                           \
_(INVALID_DECAP_NEXT, -76, "Invalid decap-next")			\
_(RESPONSE_NOT_READY, -77, "Response not ready")			\
_(NOT_CONNECTED, -78, "Not connected to the data plane")                \
_(IF_ALREADY_EXISTS, -79, "Interface already exists")                   \
_(BOND_SLAVE_NOT_ALLOWED, -80, "Operation not allowed on slave of BondEthernet") \
_(VALUE_EXIST, -81, "Value already exists")                             \
_(SAME_SRC_DST, -82, "Source and destination are the same")             \
_(IP6_MULTICAST_ADDRESS_NOT_PRESENT, -83, "IP6 multicast address required") \
_(SR_POLICY_NAME_NOT_PRESENT, -84, "Segement routing policy name required") \
_(NOT_RUNNING_AS_ROOT, -85, "Not running as root") \
_(ALREADY_CONNECTED, -86, "Connection to the data plane already exists") \
_(UNSUPPORTED_JNI_VERSION, -87, "Unsupported JNI version") \
_(FAILED_TO_ATTACH_TO_JAVA_THREAD, -88, "Failed to attach to Java thread") \
_(INVALID_WORKER, -89, "Invalid worker thread")                         \
_(LISP_DISABLED, -90, "LISP is disabled")                               \
_(CLASSIFY_TABLE_NOT_FOUND, -91, "Classify table not found")            \
_(INVALID_EID_TYPE, -92, "Unsupported LSIP EID type")                   \
_(CANNOT_CREATE_PCAP_FILE, -93, "Cannot create pcap file")              \
_(INCORRECT_ADJACENCY_TYPE, -94, "Invalid adjacency type for this operation") \
_(EXCEEDED_NUMBER_OF_RANGES_CAPACITY, -95, "Operation would exceed configured capacity of ranges") \
_(EXCEEDED_NUMBER_OF_PORTS_CAPACITY, -96, "Operation would exceed capacity of number of ports") \
_(INVALID_ADDRESS_FAMILY, -97, "Invalid address family")                \
_(INVALID_SUB_SW_IF_INDEX, -98, "Invalid sub-interface sw_if_index")    \
_(TABLE_TOO_BIG, -99, "Table too big")                                  \
_(CANNOT_ENABLE_DISABLE_FEATURE, -100, "Cannot enable/disable feature") \
_(BFD_EEXIST, -101, "Duplicate BFD object")                             \
_(BFD_ENOENT, -102, "No such BFD object")                               \
_(BFD_EINUSE, -103, "BFD object in use")                                \
_(BFD_NOTSUPP, -104, "BFD feature not supported")                       \
_(ADDRESS_IN_USE, -105, "Address in use")				\
_(ADDRESS_NOT_IN_USE, -106, "Address not in use")			\
_(QUEUE_FULL, -107, "Queue full")                                       \
_(APP_UNSUPPORTED_CFG, -108, "Unsupported application config")		\
_(URI_FIFO_CREATE_FAILED, -109, "URI FIFO segment create failed")       \
_(LISP_RLOC_LOCAL, -110, "RLOC address is local")                       \
_(BFD_EAGAIN, -111, "BFD object cannot be manipulated at this time")	\
_(INVALID_GPE_MODE, -112, "Invalid GPE mode")                           \
_(LISP_GPE_ENTRIES_PRESENT, -113, "LISP GPE entries are present")       \
_(ADDRESS_FOUND_FOR_INTERFACE, -114, "Address found for interface")	\
_(SESSION_CONNECT, -115, "Session failed to connect")              	\
_(ENTRY_ALREADY_EXISTS, -116, "Entry already exists")			\
_(SVM_SEGMENT_CREATE_FAIL, -117, "svm segment create fail")		\
_(APPLICATION_NOT_ATTACHED, -118, "application not attached")           \
_(BD_ALREADY_EXISTS, -119, "Bridge domain already exists")              \
_(BD_IN_USE, -120, "Bridge domain has member interfaces")		\
_(BD_NOT_MODIFIABLE, -121, "Bridge domain 0 can't be deleted/modified") \
_(BD_ID_EXCEED_MAX, -122, "Bridge domain ID exceed 16M limit")		\
_(SUBIF_DOESNT_EXIST, -123, "Subinterface doesn't exist")               \
_(L2_MACS_EVENT_CLINET_PRESENT, -124, "Client already exist for L2 MACs events") \
_(INVALID_QUEUE, -125, "Invalid queue")                 		\
_(UNSUPPORTED, -126, "Unsupported")					\
_(DUPLICATE_IF_ADDRESS, -127, "Address already present on another interface")	\
_(APP_INVALID_NS, -128, "Invalid application namespace")			\
_(APP_WRONG_NS_SECRET, -129, "Wrong app namespace secret")		\
_(APP_CONNECT_SCOPE, -130, "Connect scope")				\
_(APP_ALREADY_ATTACHED, -131, "App already attached")			\
_(SESSION_REDIRECT, -132, "Redirect failed")				\
_(ILLEGAL_NAME, -133, "Illegal name")					\
_(NO_NAME_SERVERS, -134, "No name servers configured")			\
_(NAME_SERVER_NOT_FOUND, -135, "Name server not found")			\
_(NAME_RESOLUTION_NOT_ENABLED, -136, "Name resolution not enabled")	\
_(NAME_SERVER_FORMAT_ERROR, -137, "Server format error (bug!)")		\
_(NAME_SERVER_NO_SUCH_NAME, -138, "No such name")                       \
_(NAME_SERVER_NO_ADDRESSES, -139, "No addresses available")		\
_(NAME_SERVER_NEXT_SERVER, -140, "Retry with new server")		\
_(APP_CONNECT_FILTERED, -141, "Connect was filtered")			\
_(ACL_IN_USE_INBOUND, -142, "Inbound ACL in use")			\
_(ACL_IN_USE_OUTBOUND, -143, "Outbound ACL in use")			\
_(INIT_FAILED, -144, "Initialization Failed")				\
_(NETLINK_ERROR, -145, "netlink error")                                 \
_(BIER_BSL_UNSUP, -146, "BIER bit-string-length unsupported")		\
_(INSTANCE_IN_USE, -147, "Instance in use")				\
_(INVALID_SESSION_ID, -148, "session ID out of range")

typedef enum
{
#define _(a,b,c) VNET_API_ERROR_##a = (b),
  foreach_vnet_api_error
#undef _
    VNET_API_N_ERROR,
} vnet_api_error_t;

#endif /* included_vnet_api_errno_h */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
'>872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
/*
 * Copyright (c) 2016 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/fib/ip6_fib.h>
#include <vnet/fib/fib_table.h>
#include <vnet/dpo/ip6_ll_dpo.h>

#include <vppinfra/bihash_24_8.h>
#include <vppinfra/bihash_template.c>

ip6_fib_table_instance_t ip6_fib_table[IP6_FIB_NUM_TABLES];

/* ip6 lookup table config parameters */
u32 ip6_fib_table_nbuckets;
uword ip6_fib_table_size;

static void
vnet_ip6_fib_init (u32 fib_index)
{
    fib_prefix_t pfx = {
	.fp_proto = FIB_PROTOCOL_IP6,
	.fp_len = 0,
	.fp_addr = {
	    .ip6 = {
		{ 0, 0, },
	    },
	}
    };

    /*
     * Add the default route.
     */
    fib_table_entry_special_add(fib_index,
				&pfx,
				FIB_SOURCE_DEFAULT_ROUTE,
				FIB_ENTRY_FLAG_DROP);

    /*
     * all link local via the link local lookup DPO
     */
    pfx.fp_addr.ip6.as_u64[0] = clib_host_to_net_u64 (0xFE80000000000000ULL);
    pfx.fp_addr.ip6.as_u64[1] = 0;
    pfx.fp_len = 10;
    fib_table_entry_special_dpo_add(fib_index,
                                    &pfx,
                                    FIB_SOURCE_SPECIAL,
                                    FIB_ENTRY_FLAG_NONE,
                                    ip6_ll_dpo_get());
}

static u32
create_fib_with_table_id (u32 table_id,
                          fib_source_t src,
                          fib_table_flags_t flags,
                          u8 *desc)
{
    fib_table_t *fib_table;
    ip6_fib_t *v6_fib;

    pool_get(ip6_main.fibs, fib_table);
    pool_get_aligned(ip6_main.v6_fibs, v6_fib, CLIB_CACHE_LINE_BYTES);

    clib_memset(fib_table, 0, sizeof(*fib_table));
    clib_memset(v6_fib, 0, sizeof(*v6_fib));

    ASSERT((fib_table - ip6_main.fibs) ==
           (v6_fib - ip6_main.v6_fibs));
    
    fib_table->ft_proto = FIB_PROTOCOL_IP6;
    fib_table->ft_index =
	    v6_fib->index =
                (fib_table - ip6_main.fibs);

    hash_set(ip6_main.fib_index_by_table_id, table_id, fib_table->ft_index);

    fib_table->ft_table_id =
	v6_fib->table_id =
	    table_id;
    fib_table->ft_flow_hash_config = IP_FLOW_HASH_DEFAULT;
    fib_table->ft_flags = flags;
    fib_table->ft_desc = desc;

    vnet_ip6_fib_init(fib_table->ft_index);
    fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_IP6, src);

    return (fib_table->ft_index);
}

u32
ip6_fib_table_find_or_create_and_lock (u32 table_id,
                                       fib_source_t src)
{
    uword * p;

    p = hash_get (ip6_main.fib_index_by_table_id, table_id);
    if (NULL == p)
	return create_fib_with_table_id(table_id, src,
                                        FIB_TABLE_FLAG_NONE,
                                        NULL);

    fib_table_lock(p[0], FIB_PROTOCOL_IP6, src);

    return (p[0]);
}

u32
ip6_fib_table_create_and_lock (fib_source_t src,
                               fib_table_flags_t flags,
                               u8 *desc)
{
    return (create_fib_with_table_id(~0, src, flags, desc));
}

void
ip6_fib_table_destroy (u32 fib_index)
{
    /*
     * all link local first ...
     */
    fib_prefix_t pfx = {
	.fp_proto = FIB_PROTOCOL_IP6,
	.fp_len = 10,
	.fp_addr = {
	    .ip6 = {
                .as_u8 = {
                    [0] = 0xFE,
                    [1] = 0x80,
                },
	    },
	}
    };
    fib_table_entry_delete(fib_index,
                           &pfx,
                           FIB_SOURCE_SPECIAL);

    /*
     * ... then the default route.
     */
    pfx.fp_addr.ip6.as_u64[0] = 0;
    pfx.fp_len = 00;
    fib_table_entry_special_remove(fib_index,
				   &pfx,
				   FIB_SOURCE_DEFAULT_ROUTE);

    fib_table_t *fib_table = fib_table_get(fib_index, FIB_PROTOCOL_IP6);
    fib_source_t source;

    /*
     * validate no more routes.
     */
#if CLIB_DEBUG > 0
    if (0 != fib_table->ft_total_route_counts)
        fib_table_assert_empty(fib_table);
#endif

    vec_foreach_index(source, fib_table->ft_src_route_counts)
    {
	ASSERT(0 == fib_table->ft_src_route_counts[source]);
    }

    if (~0 != fib_table->ft_table_id)
    {
	hash_unset (ip6_main.fib_index_by_table_id, fib_table->ft_table_id);
    }
    vec_free(fib_table->ft_src_route_counts);
    pool_put_index(ip6_main.v6_fibs, fib_table->ft_index);
    pool_put(ip6_main.fibs, fib_table);
}

fib_node_index_t
ip6_fib_table_lookup (u32 fib_index,
		      const ip6_address_t *addr,
		      u32 len)
{
    ip6_fib_table_instance_t *table;
    clib_bihash_kv_24_8_t kv, value;
    int i, n_p, rv;
    u64 fib;

    table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
    n_p = vec_len (table->prefix_lengths_in_search_order);

    kv.key[0] = addr->as_u64[0];
    kv.key[1] = addr->as_u64[1];
    fib = ((u64)((fib_index))<<32);

    /*
     * start search from a mask length same length or shorter.
     * we don't want matches longer than the mask passed
     */
    i = 0;
    while (i < n_p && table->prefix_lengths_in_search_order[i] > len)
    {
        i++;
    }

    for (; i < n_p; i++)
    {
	int dst_address_length = table->prefix_lengths_in_search_order[i];
	ip6_address_t * mask = &ip6_main.fib_masks[dst_address_length];
      
	ASSERT(dst_address_length >= 0 && dst_address_length <= 128);
	//As lengths are decreasing, masks are increasingly specific.
	kv.key[0] &= mask->as_u64[0];
	kv.key[1] &= mask->as_u64[1];
	kv.key[2] = fib | dst_address_length;
      
	rv = clib_bihash_search_inline_2_24_8(&table->ip6_hash, &kv, &value);
	if (rv == 0)
	    return value.value;
    }

    return (FIB_NODE_INDEX_INVALID);
}

fib_node_index_t
ip6_fib_table_lookup_exact_match (u32 fib_index,
				  const ip6_address_t *addr,
				  u32 len)
{
    ip6_fib_table_instance_t *table;
    clib_bihash_kv_24_8_t kv, value;
    ip6_address_t *mask;
    u64 fib;
    int rv;

    table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
    mask = &ip6_main.fib_masks[len];
    fib = ((u64)((fib_index))<<32);

    kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
    kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
    kv.key[2] = fib | len;
      
    rv = clib_bihash_search_inline_2_24_8(&table->ip6_hash, &kv, &value);
    if (rv == 0)
	return value.value;

    return (FIB_NODE_INDEX_INVALID);
}

static void
compute_prefix_lengths_in_search_order (ip6_fib_table_instance_t *table)
{
    u8 *old, *prefix_lengths_in_search_order = NULL;
    int i;

    /*
     * build the list in a scratch space then cutover so the workers
     * can continue uninterrupted.
     */
    old = table->prefix_lengths_in_search_order;

    /* Note: bitmap reversed so this is in fact a longest prefix match */
    clib_bitmap_foreach (i, table->non_empty_dst_address_length_bitmap)
     {
	int dst_address_length = 128 - i;
	vec_add1(prefix_lengths_in_search_order, dst_address_length);
    }

    table->prefix_lengths_in_search_order = prefix_lengths_in_search_order;

    /*
     * let the workers go once round the track before we free the old set
     */
    vlib_worker_wait_one_loop();
    vec_free(old);
}

void
ip6_fib_table_entry_remove (u32 fib_index,
			    const ip6_address_t *addr,
			    u32 len)
{
    ip6_fib_table_instance_t *table;
    clib_bihash_kv_24_8_t kv;
    ip6_address_t *mask;
    u64 fib;

    table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
    mask = &ip6_main.fib_masks[len];
    fib = ((u64)((fib_index))<<32);

    kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
    kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
    kv.key[2] = fib | len;

    clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);

    /* refcount accounting */
    ASSERT (table->dst_address_length_refcounts[len] > 0);
    if (--table->dst_address_length_refcounts[len] == 0)
    {
	table->non_empty_dst_address_length_bitmap =
            clib_bitmap_set (table->non_empty_dst_address_length_bitmap, 
                             128 - len, 0);
	compute_prefix_lengths_in_search_order (table);
    }
}

void
ip6_fib_table_entry_insert (u32 fib_index,
			    const ip6_address_t *addr,
			    u32 len,
			    fib_node_index_t fib_entry_index)
{
    ip6_fib_table_instance_t *table;
    clib_bihash_kv_24_8_t kv;
    ip6_address_t *mask;
    u64 fib;

    table = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING];
    mask = &ip6_main.fib_masks[len];
    fib = ((u64)((fib_index))<<32);

    kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
    kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
    kv.key[2] = fib | len;
    kv.value = fib_entry_index;

    clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);

    if (0 == table->dst_address_length_refcounts[len]++)
    {
        table->non_empty_dst_address_length_bitmap =
            clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
                             128 - len, 1);
        compute_prefix_lengths_in_search_order (table);
    }
}

u32 ip6_fib_table_fwding_lookup_with_if_index (ip6_main_t * im,
					       u32 sw_if_index,
					       const ip6_address_t * dst)
{
    u32 fib_index = vec_elt (im->fib_index_by_sw_if_index, sw_if_index);
    return ip6_fib_table_fwding_lookup(fib_index, dst);
}

u32
ip6_fib_table_get_index_for_sw_if_index (u32 sw_if_index)
{
    if (sw_if_index >= vec_len(ip6_main.fib_index_by_sw_if_index))
    {
	/*
	 * This is the case for interfaces that are not yet mapped to
	 * a IP table
	 */
	return (~0);
    }
    return (ip6_main.fib_index_by_sw_if_index[sw_if_index]);
}

void
ip6_fib_table_fwding_dpo_update (u32 fib_index,
				 const ip6_address_t *addr,
				 u32 len,
				 const dpo_id_t *dpo)
{
    ip6_fib_table_instance_t *table;
    clib_bihash_kv_24_8_t kv;
    ip6_address_t *mask;
    u64 fib;

    table = &ip6_fib_table[IP6_FIB_TABLE_FWDING];
    mask = &ip6_main.fib_masks[len];
    fib = ((u64)((fib_index))<<32);

    kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
    kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
    kv.key[2] = fib | len;
    kv.value = dpo->dpoi_index;

    clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 1);

    if (0 == table->dst_address_length_refcounts[len]++)
    {
        table->non_empty_dst_address_length_bitmap =
            clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
                             128 - len, 1);
        compute_prefix_lengths_in_search_order (table);
    }
}

void
ip6_fib_table_fwding_dpo_remove (u32 fib_index,
				 const ip6_address_t *addr,
				 u32 len,
				 const dpo_id_t *dpo)
{
    ip6_fib_table_instance_t *table;
    clib_bihash_kv_24_8_t kv;
    ip6_address_t *mask;
    u64 fib;

    table = &ip6_fib_table[IP6_FIB_TABLE_FWDING];
    mask = &ip6_main.fib_masks[len];
    fib = ((u64)((fib_index))<<32);

    kv.key[0] = addr->as_u64[0] & mask->as_u64[0];
    kv.key[1] = addr->as_u64[1] & mask->as_u64[1];
    kv.key[2] = fib | len;
    kv.value = dpo->dpoi_index;

    clib_bihash_add_del_24_8(&table->ip6_hash, &kv, 0);

    /* refcount accounting */
    ASSERT (table->dst_address_length_refcounts[len] > 0);
    if (--table->dst_address_length_refcounts[len] == 0)
    {
	table->non_empty_dst_address_length_bitmap =
            clib_bitmap_set (table->non_empty_dst_address_length_bitmap,
                             128 - len, 0);
	compute_prefix_lengths_in_search_order (table);
    }
}

/**
 * @brief Context when walking the IPv6 table. Since all VRFs are in the
 * same hash table, we need to filter only those we need as we walk
 */
typedef struct ip6_fib_walk_ctx_t_
{
    u32 i6w_fib_index;
    fib_table_walk_fn_t i6w_fn;
    void *i6w_ctx;
    fib_prefix_t i6w_root;
    fib_prefix_t *i6w_sub_trees;
} ip6_fib_walk_ctx_t;

static int
ip6_fib_walk_cb (clib_bihash_kv_24_8_t * kvp,
                 void *arg)
{
    ip6_fib_walk_ctx_t *ctx = arg;
    ip6_address_t key;

    if ((kvp->key[2] >> 32) == ctx->i6w_fib_index)
    {
        key.as_u64[0] = kvp->key[0];
        key.as_u64[1] = kvp->key[1];

        if (ip6_destination_matches_route(&ip6_main,
                                          &key,
                                          &ctx->i6w_root.fp_addr.ip6,
                                          ctx->i6w_root.fp_len))
        {
            const fib_prefix_t *sub_tree;
            int skip = 0;

            /*
             * exclude sub-trees the walk does not want to explore
             */
            vec_foreach(sub_tree, ctx->i6w_sub_trees)
            {
                if (ip6_destination_matches_route(&ip6_main,
                                                  &key,
                                                  &sub_tree->fp_addr.ip6,
                                                  sub_tree->fp_len))
                {
                    skip = 1;
                    break;
                }
            }

            if (!skip)
            {
                switch (ctx->i6w_fn(kvp->value, ctx->i6w_ctx))
                {
                case FIB_TABLE_WALK_CONTINUE:
                    break;
                case FIB_TABLE_WALK_SUB_TREE_STOP: {
                    fib_prefix_t pfx = {
                        .fp_proto = FIB_PROTOCOL_IP6,
                        .fp_len = kvp->key[2] & 0xffffffff,
                        .fp_addr.ip6 = key,
                    };
                    vec_add1(ctx->i6w_sub_trees, pfx);
                    break;
                }
                case FIB_TABLE_WALK_STOP:
                    goto done;
                }
            }
        }
    }
done:

    return (1);
}

void
ip6_fib_table_walk (u32 fib_index,
                    fib_table_walk_fn_t fn,
                    void *arg)
{
    ip6_fib_walk_ctx_t ctx = {
        .i6w_fib_index = fib_index,
        .i6w_fn = fn,
        .i6w_ctx = arg,
        .i6w_root = {
            .fp_proto = FIB_PROTOCOL_IP6,
        },
        .i6w_sub_trees = NULL,
    };

    clib_bihash_foreach_key_value_pair_24_8(
        &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
        ip6_fib_walk_cb,
        &ctx);

    vec_free(ctx.i6w_sub_trees);
}

void
ip6_fib_table_sub_tree_walk (u32 fib_index,
                             const fib_prefix_t *root,
                             fib_table_walk_fn_t fn,
                             void *arg)
{
    ip6_fib_walk_ctx_t ctx = {
        .i6w_fib_index = fib_index,
        .i6w_fn = fn,
        .i6w_ctx = arg,
        .i6w_root = *root,
    };

    clib_bihash_foreach_key_value_pair_24_8(
        &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
        ip6_fib_walk_cb,
        &ctx);
}

typedef struct ip6_fib_show_ctx_t_ {
    fib_node_index_t *entries;
} ip6_fib_show_ctx_t;

static fib_table_walk_rc_t
ip6_fib_table_show_walk (fib_node_index_t fib_entry_index,
                         void *arg)
{
    ip6_fib_show_ctx_t *ctx = arg;

    vec_add1(ctx->entries, fib_entry_index);

    return (FIB_TABLE_WALK_CONTINUE);
}

static void
ip6_fib_table_show_all (ip6_fib_t *fib,
			vlib_main_t * vm)
{
    fib_node_index_t *fib_entry_index;
    ip6_fib_show_ctx_t ctx = {
	.entries = NULL,
    };

    ip6_fib_table_walk(fib->index, ip6_fib_table_show_walk, &ctx);
    vec_sort_with_function(ctx.entries, fib_entry_cmp_for_sort);

    vec_foreach(fib_entry_index, ctx.entries)
    {
	vlib_cli_output(vm, "%U",
                        format_fib_entry,
                        *fib_entry_index,
                        FIB_ENTRY_FORMAT_BRIEF);
    }

    vec_free(ctx.entries);
}

static void
ip6_fib_table_show_one (ip6_fib_t *fib,
			vlib_main_t * vm,
			ip6_address_t *address,
			u32 mask_len,
                        int detail)
{
    vlib_cli_output(vm, "%U",
                    format_fib_entry,
                    ip6_fib_table_lookup(fib->index, address, mask_len),
                    (detail ?
                     FIB_ENTRY_FORMAT_DETAIL2:
                     FIB_ENTRY_FORMAT_DETAIL));
}

u8 *
format_ip6_fib_table_memory (u8 * s, va_list * args)
{
    uword bytes_inuse;

    bytes_inuse = (alloc_arena_next(&(ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash)) +
                   alloc_arena_next(&(ip6_fib_table[IP6_FIB_TABLE_FWDING].ip6_hash)));

    s = format(s, "%=30s %=6d %=12ld\n",
               "IPv6 unicast",
               pool_elts(ip6_main.fibs),
               bytes_inuse);
    return (s);
}

typedef struct {
  u32 fib_index;
  u64 count_by_prefix_length[129];
} count_routes_in_fib_at_prefix_length_arg_t;

static int
count_routes_in_fib_at_prefix_length (clib_bihash_kv_24_8_t * kvp,
                                      void *arg)
{
  count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
  int mask_width;

  if ((kvp->key[2]>>32) != ap->fib_index)
      return (BIHASH_WALK_CONTINUE);

  mask_width = kvp->key[2] & 0xFF;

  ap->count_by_prefix_length[mask_width]++;

  return (BIHASH_WALK_CONTINUE);
}

static clib_error_t *
ip6_show_fib (vlib_main_t * vm,
	      unformat_input_t * input,
	      vlib_cli_command_t * cmd)
{
    count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca;
    ip6_main_t * im6 = &ip6_main;
    fib_table_t *fib_table;
    ip6_fib_t * fib;
    int verbose, matching;
    ip6_address_t matching_address;
    u32 mask_len  = 128;
    int table_id = -1, fib_index = ~0;
    int detail = 0;
    int hash = 0;

    verbose = 1;
    matching = 0;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
	if (unformat (input, "brief")   ||
	    unformat (input, "summary") ||
	    unformat (input, "sum"))
	    verbose = 0;
 
	else if (unformat (input, "detail")   ||
                 unformat (input, "det"))
	    detail = 1;

	else if (unformat (input, "hash") ||
                 unformat (input, "mem") ||
                 unformat (input, "memory"))
	    hash = 1;

	else if (unformat (input, "%U/%d",
			   unformat_ip6_address, &matching_address, &mask_len))
	    matching = 1;

	else if (unformat (input, "%U", unformat_ip6_address, &matching_address))
	    matching = 1;

	else if (unformat (input, "table %d", &table_id))
	    ;
	else if (unformat (input, "index %d", &fib_index))
	    ;
	else
	    break;
    }

    if (hash)
    {
        vlib_cli_output (vm, "IPv6 Non-Forwarding Hash Table:\n%U\n",
                         BV (format_bihash),
                         &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
                         detail);
        vlib_cli_output (vm, "IPv6 Forwarding Hash Table:\n%U\n",
                         BV (format_bihash),
                         &ip6_fib_table[IP6_FIB_TABLE_FWDING].ip6_hash,
                         detail);
        return (NULL);
    }

    pool_foreach (fib_table, im6->fibs)
     {
        fib_source_t source;
        u8 *s = NULL;

	fib = pool_elt_at_index(im6->v6_fibs, fib_table->ft_index);
	if (table_id >= 0 && table_id != (int)fib->table_id)
	    continue;
	if (fib_index != ~0 && fib_index != (int)fib->index)
	    continue;
        if (fib_table->ft_flags & FIB_TABLE_FLAG_IP6_LL)
            continue;

	s = format(s, "%U, fib_index:%d, flow hash:[%U] epoch:%d flags:%U locks:[",
                   format_fib_table_name, fib->index,
                   FIB_PROTOCOL_IP6,
                   fib->index,
                   format_ip_flow_hash_config,
                   fib_table->ft_flow_hash_config,
                   fib_table->ft_epoch,
                   format_fib_table_flags, fib_table->ft_flags);

        vec_foreach_index(source, fib_table->ft_locks)
        {
            if (0 != fib_table->ft_locks[source])
            {
                s = format(s, "%U:%d, ",
                           format_fib_source, source,
                           fib_table->ft_locks[source]);
            }
        }
        s = format (s, "]");
        vlib_cli_output (vm, "%v", s);
        vec_free(s);

	/* Show summary? */
	if (! verbose)
	{
	    clib_bihash_24_8_t * h = &ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash;
	    int len;

	    vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count");

	    clib_memset (ca, 0, sizeof(*ca));
	    ca->fib_index = fib->index;

	    clib_bihash_foreach_key_value_pair_24_8
		(h, count_routes_in_fib_at_prefix_length, ca);

	    for (len = 128; len >= 0; len--)
            {
		if (ca->count_by_prefix_length[len])
		    vlib_cli_output (vm, "%=20d%=16lld", 
				     len, ca->count_by_prefix_length[len]);
            }
	    continue;
	}

	if (!matching)
	{
	    ip6_fib_table_show_all(fib, vm);
	}
	else
	{
	    ip6_fib_table_show_one(fib, vm, &matching_address, mask_len, detail);
	}
    }

    return 0;
}

/*?
 * This command displays the IPv6 FIB Tables (VRF Tables) and the route
 * entries for each table.
 *
 * @note This command will run for a long time when the FIB tables are
 * comprised of millions of entries. For those scenarios, consider displaying
 * in summary mode.
 *
 * @cliexpar
 * @parblock
 * Example of how to display all the IPv6 FIB tables:
 * @cliexstart{show ip6 fib}
 * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
 * @::/0
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:5 buckets:1 uRPF:5 to:[0:0]]
 *     [0] [@0]: dpo-drop ip6
 * fe80::/10
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:10 buckets:1 uRPF:10 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::1/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:8 buckets:1 uRPF:8 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::2/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:7 buckets:1 uRPF:7 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::16/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:9 buckets:1 uRPF:9 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::1:ff00:0/104
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:6 buckets:1 uRPF:6 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
 * @::/0
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:21 buckets:1 uRPF:20 to:[0:0]]
 *     [0] [@0]: dpo-drop ip6
 * @::a:1:1:0:4/126
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:27 buckets:1 uRPF:26 to:[0:0]]
 *     [0] [@4]: ipv6-glean: af_packet0
 * @::a:1:1:0:7/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:28 buckets:1 uRPF:27 to:[0:0]]
 *     [0] [@2]: dpo-receive: @::a:1:1:0:7 on af_packet0
 * fe80::/10
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:26 buckets:1 uRPF:25 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * fe80::fe:3eff:fe3e:9222/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:29 buckets:1 uRPF:28 to:[0:0]]
 *     [0] [@2]: dpo-receive: fe80::fe:3eff:fe3e:9222 on af_packet0
 * ff02::1/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:24 buckets:1 uRPF:23 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::2/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:23 buckets:1 uRPF:22 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::16/128
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:25 buckets:1 uRPF:24 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * ff02::1:ff00:0/104
 *   unicast-ip6-chain
 *   [@0]: dpo-load-balance: [index:22 buckets:1 uRPF:21 to:[0:0]]
 *     [0] [@2]: dpo-receive
 * @cliexend
 *
 * Example of how to display a summary of all IPv6 FIB tables:
 * @cliexstart{show ip6 fib summary}
 * ipv6-VRF:0, fib_index 0, flow hash: src dst sport dport proto
 *     Prefix length         Count
 *          128                3
 *          104                1
 *          10                 1
 *           0                 1
 * ipv6-VRF:8, fib_index 1, flow hash: src dst sport dport proto
 *     Prefix length         Count
 *          128                5
 *          126                1
 *          104                1
 *          10                 1
 *           0                 1
 * @cliexend
 * @endparblock
 ?*/
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip6_show_fib_command, static) = {
    .path = "show ip6 fib",
    .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]] [detail]",
    .function = ip6_show_fib,
};
/* *INDENT-ON* */

static clib_error_t *
ip6_config (vlib_main_t * vm, unformat_input_t * input)
{
  uword heapsize = 0;
  u32 nbuckets = 0;

  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (input, "hash-buckets %d", &nbuckets))
          ;
      else if (unformat (input, "heap-size %U",
			 unformat_memory_size, &heapsize))
	;
      else
	return clib_error_return (0, "unknown input '%U'",
				  format_unformat_error, input);
    }

  ip6_fib_table_nbuckets = nbuckets;
  ip6_fib_table_size = heapsize;

  return 0;
}

VLIB_EARLY_CONFIG_FUNCTION (ip6_config, "ip6");

static clib_error_t *
ip6_fib_init (vlib_main_t * vm)
{
    if (ip6_fib_table_nbuckets == 0)
        ip6_fib_table_nbuckets = IP6_FIB_DEFAULT_HASH_NUM_BUCKETS;

    ip6_fib_table_nbuckets = 1 << max_log2 (ip6_fib_table_nbuckets);

    if (ip6_fib_table_size == 0)
        ip6_fib_table_size = IP6_FIB_DEFAULT_HASH_MEMORY_SIZE;

    clib_bihash_init_24_8 (&(ip6_fib_table[IP6_FIB_TABLE_FWDING].ip6_hash),
                           "ip6 FIB fwding table",
                           ip6_fib_table_nbuckets, ip6_fib_table_size);
    clib_bihash_init_24_8 (&ip6_fib_table[IP6_FIB_TABLE_NON_FWDING].ip6_hash,
                           "ip6 FIB non-fwding table",
                           ip6_fib_table_nbuckets, ip6_fib_table_size);

    return (NULL);
}

VLIB_INIT_FUNCTION (ip6_fib_init) =
{
  .runs_before = VLIB_INITS("ip6_lookup_init"),
};