diff options
author | Ed Warnicke <eaw@cisco.com> | 2015-12-08 15:45:58 -0700 |
---|---|---|
committer | Ed Warnicke <eaw@cisco.com> | 2015-12-08 15:47:27 -0700 |
commit | cb9cadad578297ffd78fa8a33670bdf1ab669e7e (patch) | |
tree | 6ac2be912482cc7849a26f0ab845561c3d7f4e26 /vnet/vnet/ip/lookup.c | |
parent | fb0815d4ae4bb0fe27bd9313f34b45c8593b907e (diff) |
Initial commit of vpp code.v1.0.0
Change-Id: Ib246f1fbfce93274020ee93ce461e3d8bd8b9f17
Signed-off-by: Ed Warnicke <eaw@cisco.com>
Diffstat (limited to 'vnet/vnet/ip/lookup.c')
-rw-r--r-- | vnet/vnet/ip/lookup.c | 2271 |
1 files changed, 2271 insertions, 0 deletions
diff --git a/vnet/vnet/ip/lookup.c b/vnet/vnet/ip/lookup.c new file mode 100644 index 00000000000..80f0a33e731 --- /dev/null +++ b/vnet/vnet/ip/lookup.c @@ -0,0 +1,2271 @@ +/* + * 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. + */ +/* + * ip/ip_lookup.c: ip4/6 adjacency and lookup table managment + * + * Copyright (c) 2008 Eliot Dresselhaus + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <vppinfra/math.h> /* for fabs */ +#include <vnet/ip/ip.h> + +static void +ip_multipath_del_adjacency (ip_lookup_main_t * lm, u32 del_adj_index); + +always_inline void +ip_poison_adjacencies (ip_adjacency_t * adj, uword n_adj) +{ + if (CLIB_DEBUG > 0) + memset (adj, 0xfe, n_adj * sizeof (adj[0])); +} + +/* Create new block of given number of contiguous adjacencies. */ +ip_adjacency_t * +ip_add_adjacency (ip_lookup_main_t * lm, + ip_adjacency_t * copy_adj, + u32 n_adj, + u32 * adj_index_return) +{ + ip_adjacency_t * adj; + u32 ai, i, handle; + + ai = heap_alloc (lm->adjacency_heap, n_adj, handle); + adj = heap_elt_at_index (lm->adjacency_heap, ai); + + ip_poison_adjacencies (adj, n_adj); + + /* Validate adjacency counters. */ + vlib_validate_combined_counter (&lm->adjacency_counters, ai + n_adj - 1); + + for (i = 0; i < n_adj; i++) + { + /* Make sure certain fields are always initialized. */ + adj[i].rewrite_header.sw_if_index = ~0; + adj[i].explicit_fib_index = ~0; + adj[i].mcast_group_index = ~0; + adj[i].classify_table_index = ~0; + adj[i].saved_lookup_next_index = 0; + + if (copy_adj) + adj[i] = copy_adj[i]; + + adj[i].heap_handle = handle; + adj[i].n_adj = n_adj; + + /* Zero possibly stale counters for re-used adjacencies. */ + vlib_zero_combined_counter (&lm->adjacency_counters, ai + i); + } + + *adj_index_return = ai; + return adj; +} + +static void ip_del_adjacency2 (ip_lookup_main_t * lm, u32 adj_index, u32 delete_multipath_adjacency) +{ + ip_adjacency_t * adj; + uword handle; + + ip_call_add_del_adjacency_callbacks (lm, adj_index, /* is_del */ 1); + + adj = ip_get_adjacency (lm, adj_index); + handle = adj->heap_handle; + + if (delete_multipath_adjacency) + ip_multipath_del_adjacency (lm, adj_index); + + ip_poison_adjacencies (adj, adj->n_adj); + + heap_dealloc (lm->adjacency_heap, handle); +} + +void ip_del_adjacency (ip_lookup_main_t * lm, u32 adj_index) +{ ip_del_adjacency2 (lm, adj_index, /* delete_multipath_adjacency */ 1); } + +static int +next_hop_sort_by_weight (ip_multipath_next_hop_t * n1, + ip_multipath_next_hop_t * n2) +{ + int cmp = (int) n1->weight - (int) n2->weight; + return (cmp == 0 + ? (int) n1->next_hop_adj_index - (int) n2->next_hop_adj_index + : (cmp > 0 ? +1 : -1)); +} + +/* Given next hop vector is over-written with normalized one with sorted weights and + with weights corresponding to the number of adjacencies for each next hop. + Returns number of adjacencies in block. */ +static u32 ip_multipath_normalize_next_hops (ip_lookup_main_t * lm, + ip_multipath_next_hop_t * raw_next_hops, + ip_multipath_next_hop_t ** normalized_next_hops) +{ + ip_multipath_next_hop_t * nhs; + uword n_nhs, n_adj, n_adj_left, i; + f64 sum_weight, norm, error; + + n_nhs = vec_len (raw_next_hops); + ASSERT (n_nhs > 0); + if (n_nhs == 0) + return 0; + + /* Allocate enough space for 2 copies; we'll use second copy to save original weights. */ + nhs = *normalized_next_hops; + vec_validate (nhs, 2*n_nhs - 1); + + /* Fast path: 1 next hop in block. */ + n_adj = n_nhs; + if (n_nhs == 1) + { + nhs[0] = raw_next_hops[0]; + nhs[0].weight = 1; + _vec_len (nhs) = 1; + goto done; + } + + else if (n_nhs == 2) + { + int cmp = next_hop_sort_by_weight (&raw_next_hops[0], &raw_next_hops[1]) < 0; + + /* Fast sort. */ + nhs[0] = raw_next_hops[cmp]; + nhs[1] = raw_next_hops[cmp ^ 1]; + + /* Fast path: equal cost multipath with 2 next hops. */ + if (nhs[0].weight == nhs[1].weight) + { + nhs[0].weight = nhs[1].weight = 1; + _vec_len (nhs) = 2; + goto done; + } + } + else + { + memcpy (nhs, raw_next_hops, n_nhs * sizeof (raw_next_hops[0])); + qsort (nhs, n_nhs, sizeof (nhs[0]), (void *) next_hop_sort_by_weight); + } + + /* Find total weight to normalize weights. */ + sum_weight = 0; + for (i = 0; i < n_nhs; i++) + sum_weight += nhs[i].weight; + + /* In the unlikely case that all weights are given as 0, set them all to 1. */ + if (sum_weight == 0) + { + for (i = 0; i < n_nhs; i++) + nhs[i].weight = 1; + sum_weight = n_nhs; + } + + /* Save copies of all next hop weights to avoid being overwritten in loop below. */ + for (i = 0; i < n_nhs; i++) + nhs[n_nhs + i].weight = nhs[i].weight; + + /* Try larger and larger power of 2 sized adjacency blocks until we + find one where traffic flows to within 1% of specified weights. */ + for (n_adj = max_pow2 (n_nhs); ; n_adj *= 2) + { + error = 0; + + norm = n_adj / sum_weight; + n_adj_left = n_adj; + for (i = 0; i < n_nhs; i++) + { + f64 nf = nhs[n_nhs + i].weight * norm; /* use saved weights */ + word n = flt_round_nearest (nf); + + n = n > n_adj_left ? n_adj_left : n; + n_adj_left -= n; + error += fabs (nf - n); + nhs[i].weight = n; + } + + nhs[0].weight += n_adj_left; + + /* Less than 5% average error per adjacency with this size adjacency block? */ + if (error <= lm->multipath_next_hop_error_tolerance*n_adj) + { + /* Truncate any next hops with zero weight. */ + _vec_len (nhs) = i; + break; + } + } + + done: + /* Save vector for next call. */ + *normalized_next_hops = nhs; + return n_adj; +} + +always_inline uword +ip_next_hop_hash_key_from_handle (uword handle) +{ return 1 + 2*handle; } + +always_inline uword +ip_next_hop_hash_key_is_heap_handle (uword k) +{ return k & 1; } + +always_inline uword +ip_next_hop_hash_key_get_heap_handle (uword k) +{ + ASSERT (ip_next_hop_hash_key_is_heap_handle (k)); + return k / 2; +} + +static u32 +ip_multipath_adjacency_get (ip_lookup_main_t * lm, + ip_multipath_next_hop_t * raw_next_hops, + uword create_if_non_existent) +{ + uword * p; + u32 i, j, n_adj, adj_index, adj_heap_handle; + ip_adjacency_t * adj, * copy_adj; + ip_multipath_next_hop_t * nh, * nhs; + ip_multipath_adjacency_t * madj; + + n_adj = ip_multipath_normalize_next_hops (lm, raw_next_hops, &lm->next_hop_hash_lookup_key_normalized); + nhs = lm->next_hop_hash_lookup_key_normalized; + + /* Basic sanity. */ + ASSERT (n_adj >= vec_len (raw_next_hops)); + + /* Use normalized next hops to see if we've seen a block equivalent to this one before. */ + p = hash_get_mem (lm->multipath_adjacency_by_next_hops, nhs); + if (p) + return p[0]; + + if (! create_if_non_existent) + return 0; + + adj = ip_add_adjacency (lm, /* copy_adj */ 0, n_adj, &adj_index); + adj_heap_handle = adj[0].heap_handle; + + /* Fill in adjacencies in block based on corresponding next hop adjacencies. */ + i = 0; + vec_foreach (nh, nhs) + { + copy_adj = ip_get_adjacency (lm, nh->next_hop_adj_index); + for (j = 0; j < nh->weight; j++) + { + adj[i] = copy_adj[0]; + adj[i].heap_handle = adj_heap_handle; + adj[i].n_adj = n_adj; + i++; + } + } + + /* All adjacencies should have been initialized. */ + ASSERT (i == n_adj); + + vec_validate (lm->multipath_adjacencies, adj_heap_handle); + madj = vec_elt_at_index (lm->multipath_adjacencies, adj_heap_handle); + + madj->adj_index = adj_index; + madj->n_adj_in_block = n_adj; + madj->reference_count = 0; /* caller will set to one. */ + + madj->normalized_next_hops.count = vec_len (nhs); + madj->normalized_next_hops.heap_offset + = heap_alloc (lm->next_hop_heap, vec_len (nhs), + madj->normalized_next_hops.heap_handle); + memcpy (lm->next_hop_heap + madj->normalized_next_hops.heap_offset, + nhs, vec_bytes (nhs)); + + hash_set (lm->multipath_adjacency_by_next_hops, + ip_next_hop_hash_key_from_handle (madj->normalized_next_hops.heap_handle), + madj - lm->multipath_adjacencies); + + madj->unnormalized_next_hops.count = vec_len (raw_next_hops); + madj->unnormalized_next_hops.heap_offset + = heap_alloc (lm->next_hop_heap, vec_len (raw_next_hops), + madj->unnormalized_next_hops.heap_handle); + memcpy (lm->next_hop_heap + madj->unnormalized_next_hops.heap_offset, + raw_next_hops, vec_bytes (raw_next_hops)); + + ip_call_add_del_adjacency_callbacks (lm, adj_index, /* is_del */ 0); + + return adj_heap_handle; +} + +/* Returns 0 for next hop not found. */ +u32 +ip_multipath_adjacency_add_del_next_hop (ip_lookup_main_t * lm, + u32 is_del, + u32 old_mp_adj_index, + u32 next_hop_adj_index, + u32 next_hop_weight, + u32 * new_mp_adj_index) +{ + ip_multipath_adjacency_t * mp_old, * mp_new; + ip_multipath_next_hop_t * nh, * nhs, * hash_nhs; + u32 n_nhs, i_nh; + + mp_new = mp_old = 0; + n_nhs = 0; + i_nh = 0; + nhs = 0; + + /* If old multipath adjacency is valid, find requested next hop. */ + if (old_mp_adj_index < vec_len (lm->multipath_adjacencies) + && lm->multipath_adjacencies[old_mp_adj_index].normalized_next_hops.count > 0) + { + mp_old = vec_elt_at_index (lm->multipath_adjacencies, old_mp_adj_index); + + nhs = vec_elt_at_index (lm->next_hop_heap, mp_old->unnormalized_next_hops.heap_offset); + n_nhs = mp_old->unnormalized_next_hops.count; + + /* Linear search: ok since n_next_hops is small. */ + for (i_nh = 0; i_nh < n_nhs; i_nh++) + if (nhs[i_nh].next_hop_adj_index == next_hop_adj_index) + break; + + /* Given next hop not found. */ + if (i_nh >= n_nhs && is_del) + return 0; + } + + hash_nhs = lm->next_hop_hash_lookup_key; + if (hash_nhs) + _vec_len (hash_nhs) = 0; + + if (is_del) + { + if (n_nhs > 1) + { + /* Prepare lookup key for multipath with target next hop deleted. */ + if (i_nh > 0) + vec_add (hash_nhs, nhs + 0, i_nh); + if (i_nh + 1 < n_nhs) + vec_add (hash_nhs, nhs + i_nh + 1, n_nhs - (i_nh + 1)); + } + } + else /* it's an add. */ + { + /* If next hop is already there with the same weight, we have nothing to do. */ + if (i_nh < n_nhs && nhs[i_nh].weight == next_hop_weight) + { + new_mp_adj_index[0] = ~0; + goto done; + } + + /* Copy old next hops to lookup key vector. */ + if (n_nhs > 0) + vec_add (hash_nhs, nhs, n_nhs); + + if (i_nh < n_nhs) + { + /* Change weight of existing next hop. */ + nh = vec_elt_at_index (hash_nhs, i_nh); + } + else + { + /* Add a new next hop. */ + vec_add2 (hash_nhs, nh, 1); + nh->next_hop_adj_index = next_hop_adj_index; + } + + /* Set weight for added or old next hop. */ + nh->weight = next_hop_weight; + } + + if (vec_len (hash_nhs) > 0) + { + u32 tmp = ip_multipath_adjacency_get (lm, hash_nhs, + /* create_if_non_existent */ 1); + if (tmp != ~0) + mp_new = vec_elt_at_index (lm->multipath_adjacencies, tmp); + + /* Fetch again since pool may have moved. */ + if (mp_old) + mp_old = vec_elt_at_index (lm->multipath_adjacencies, old_mp_adj_index); + } + + new_mp_adj_index[0] = mp_new ? mp_new - lm->multipath_adjacencies : ~0; + + if (mp_new != mp_old) + { + if (mp_old) + { + ASSERT (mp_old->reference_count > 0); + mp_old->reference_count -= 1; + } + if (mp_new) + mp_new->reference_count += 1; + } + + if (mp_old && mp_old->reference_count == 0) + ip_multipath_adjacency_free (lm, mp_old); + + done: + /* Save key vector next call. */ + lm->next_hop_hash_lookup_key = hash_nhs; + + return 1; +} + +static void +ip_multipath_del_adjacency (ip_lookup_main_t * lm, u32 del_adj_index) +{ + ip_adjacency_t * adj = ip_get_adjacency (lm, del_adj_index); + ip_multipath_adjacency_t * madj, * new_madj; + ip_multipath_next_hop_t * nhs, * hash_nhs; + u32 i, n_nhs, madj_index, new_madj_index; + + if (adj->heap_handle >= vec_len (lm->multipath_adjacencies)) + return; + + vec_validate (lm->adjacency_remap_table, vec_len (lm->adjacency_heap) - 1); + + for (madj_index = 0; madj_index < vec_len (lm->multipath_adjacencies); madj_index++) + { + madj = vec_elt_at_index (lm->multipath_adjacencies, madj_index); + if (madj->n_adj_in_block == 0) + continue; + + nhs = heap_elt_at_index (lm->next_hop_heap, madj->unnormalized_next_hops.heap_offset); + n_nhs = madj->unnormalized_next_hops.count; + for (i = 0; i < n_nhs; i++) + if (nhs[i].next_hop_adj_index == del_adj_index) + break; + + /* del_adj_index not found in unnormalized_next_hops? We're done. */ + if (i >= n_nhs) + continue; + + new_madj = 0; + if (n_nhs > 1) + { + hash_nhs = lm->next_hop_hash_lookup_key; + if (hash_nhs) + _vec_len (hash_nhs) = 0; + if (i > 0) + vec_add (hash_nhs, nhs + 0, i); + if (i + 1 < n_nhs) + vec_add (hash_nhs, nhs + i + 1, n_nhs - (i + 1)); + + new_madj_index = ip_multipath_adjacency_get (lm, hash_nhs, /* create_if_non_existent */ 1); + + lm->next_hop_hash_lookup_key = hash_nhs; + + if (new_madj_index == madj_index) + continue; + + new_madj = vec_elt_at_index (lm->multipath_adjacencies, new_madj_index); + } + + lm->adjacency_remap_table[madj->adj_index] = new_madj ? 1 + new_madj->adj_index : ~0; + lm->n_adjacency_remaps += 1; + ip_multipath_adjacency_free (lm, madj); + } +} + +void +ip_multipath_adjacency_free (ip_lookup_main_t * lm, + ip_multipath_adjacency_t * a) +{ + hash_unset (lm->multipath_adjacency_by_next_hops, + ip_next_hop_hash_key_from_handle (a->normalized_next_hops.heap_handle)); + heap_dealloc (lm->next_hop_heap, a->normalized_next_hops.heap_handle); + heap_dealloc (lm->next_hop_heap, a->unnormalized_next_hops.heap_handle); + + ip_del_adjacency2 (lm, a->adj_index, a->reference_count == 0); + memset (a, 0, sizeof (a[0])); +} + +always_inline ip_multipath_next_hop_t * +ip_next_hop_hash_key_get_next_hops (ip_lookup_main_t * lm, uword k, + uword * n_next_hops) +{ + ip_multipath_next_hop_t * nhs; + uword n_nhs; + if (ip_next_hop_hash_key_is_heap_handle (k)) + { + uword handle = ip_next_hop_hash_key_get_heap_handle (k); + nhs = heap_elt_with_handle (lm->next_hop_heap, handle); + n_nhs = heap_len (lm->next_hop_heap, handle); + } + else + { + nhs = uword_to_pointer (k, ip_multipath_next_hop_t *); + n_nhs = vec_len (nhs); + } + *n_next_hops = n_nhs; + return nhs; +} + +static uword +ip_next_hop_hash_key_sum (hash_t * h, uword key0) +{ + ip_lookup_main_t * lm = uword_to_pointer (h->user, ip_lookup_main_t *); + ip_multipath_next_hop_t * k0; + uword n0; + + k0 = ip_next_hop_hash_key_get_next_hops (lm, key0, &n0); + return hash_memory (k0, n0 * sizeof (k0[0]), /* seed */ n0); +} + +static uword +ip_next_hop_hash_key_equal (hash_t * h, uword key0, uword key1) +{ + ip_lookup_main_t * lm = uword_to_pointer (h->user, ip_lookup_main_t *); + ip_multipath_next_hop_t * k0, * k1; + uword n0, n1; + + k0 = ip_next_hop_hash_key_get_next_hops (lm, key0, &n0); + k1 = ip_next_hop_hash_key_get_next_hops (lm, key1, &n1); + + return n0 == n1 && ! memcmp (k0, k1, n0 * sizeof (k0[0])); +} + +clib_error_t * +ip_interface_address_add_del (ip_lookup_main_t * lm, + u32 sw_if_index, + void * addr_fib, + u32 address_length, + u32 is_del, + u32 * result_if_address_index) +{ + vnet_main_t * vnm = vnet_get_main(); + ip_interface_address_t * a, * prev, * next; + uword * p = mhash_get (&lm->address_to_if_address_index, addr_fib); + + vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index, sw_if_index, ~0); + a = p ? pool_elt_at_index (lm->if_address_pool, p[0]) : 0; + + /* Verify given length. */ + if ((a && (address_length != a->address_length)) || (address_length == 0)) + { + vnm->api_errno = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH; + return clib_error_create + ( "%U wrong length (expected %d) for interface %U", + lm->format_address_and_length, addr_fib, + address_length, a? a->address_length : -1, + format_vnet_sw_if_index_name, vnm, sw_if_index); + } + + if (is_del) + { + if (!a) + { + vnet_sw_interface_t * si = vnet_get_sw_interface (vnm, sw_if_index); + vnm->api_errno = VNET_API_ERROR_ADDRESS_NOT_FOUND_FOR_INTERFACE; + return clib_error_create ("%U not found for interface %U", + lm->format_address_and_length, + addr_fib, address_length, + format_vnet_sw_interface_name, vnm, si); + } + + if (a->prev_this_sw_interface != ~0) + { + prev = pool_elt_at_index (lm->if_address_pool, a->prev_this_sw_interface); + prev->next_this_sw_interface = a->next_this_sw_interface; + } + if (a->next_this_sw_interface != ~0) + { + next = pool_elt_at_index (lm->if_address_pool, a->next_this_sw_interface); + next->prev_this_sw_interface = a->prev_this_sw_interface; + + if(a->prev_this_sw_interface == ~0) + lm->if_address_pool_index_by_sw_if_index[sw_if_index] = a->next_this_sw_interface; + } + + if ((a->next_this_sw_interface == ~0) && (a->prev_this_sw_interface == ~0)) + lm->if_address_pool_index_by_sw_if_index[sw_if_index] = ~0; + + mhash_unset (&lm->address_to_if_address_index, addr_fib, + /* old_value */ 0); + pool_put (lm->if_address_pool, a); + + if (result_if_address_index) + *result_if_address_index = ~0; + } + + else if (! a) + { + u32 pi; /* previous index */ + u32 ai; + u32 hi; /* head index */ + + pool_get (lm->if_address_pool, a); + memset (a, ~0, sizeof (a[0])); + ai = a - lm->if_address_pool; + + hi = pi = lm->if_address_pool_index_by_sw_if_index[sw_if_index]; + prev = 0; + while (pi != (u32)~0) + { + prev = pool_elt_at_index(lm->if_address_pool, pi); + pi = prev->next_this_sw_interface; + } + pi = prev ? prev - lm->if_address_pool : (u32)~0; + + a->address_key = mhash_set (&lm->address_to_if_address_index, + addr_fib, ai, /* old_value */ 0); + a->address_length = address_length; + a->sw_if_index = sw_if_index; + a->flags = 0; + a->prev_this_sw_interface = pi; + a->next_this_sw_interface = ~0; + if (prev) + prev->next_this_sw_interface = ai; + + lm->if_address_pool_index_by_sw_if_index[sw_if_index] = + (hi != ~0) ? hi : ai; + if (result_if_address_index) + *result_if_address_index = ai; + } + else + { + if (result_if_address_index) + *result_if_address_index = a - lm->if_address_pool; + } + + + return /* no error */ 0; +} + +void serialize_vec_ip_adjacency (serialize_main_t * m, va_list * va) +{ + ip_adjacency_t * a = va_arg (*va, ip_adjacency_t *); + u32 n = va_arg (*va, u32); + u32 i; + for (i = 0; i < n; i++) + { + serialize_integer (m, a[i].heap_handle, sizeof (a[i].heap_handle)); + serialize_integer (m, a[i].n_adj, sizeof (a[i].n_adj)); + serialize_integer (m, a[i].lookup_next_index, sizeof (a[i].lookup_next_index_as_int)); + switch (a[i].lookup_next_index) + { + case IP_LOOKUP_NEXT_LOCAL: + serialize_integer (m, a[i].if_address_index, sizeof (a[i].if_address_index)); + break; + + case IP_LOOKUP_NEXT_ARP: + serialize_integer (m, a[i].if_address_index, sizeof (a[i].if_address_index)); + serialize_integer (m, a[i].rewrite_header.sw_if_index, sizeof (a[i].rewrite_header.sw_if_index)); + break; + + case IP_LOOKUP_NEXT_REWRITE: + serialize (m, serialize_vnet_rewrite, &a[i].rewrite_header, sizeof (a[i].rewrite_data)); + break; + + default: + /* nothing else to serialize. */ + break; + } + } +} + +void unserialize_vec_ip_adjacency (serialize_main_t * m, va_list * va) +{ + ip_adjacency_t * a = va_arg (*va, ip_adjacency_t *); + u32 n = va_arg (*va, u32); + u32 i; + ip_poison_adjacencies (a, n); + for (i = 0; i < n; i++) + { + unserialize_integer (m, &a[i].heap_handle, sizeof (a[i].heap_handle)); + unserialize_integer (m, &a[i].n_adj, sizeof (a[i].n_adj)); + unserialize_integer (m, &a[i].lookup_next_index_as_int, sizeof (a[i].lookup_next_index_as_int)); + switch (a[i].lookup_next_index) + { + case IP_LOOKUP_NEXT_LOCAL: + unserialize_integer (m, &a[i].if_address_index, sizeof (a[i].if_address_index)); + break; + + case IP_LOOKUP_NEXT_ARP: + unserialize_integer (m, &a[i].if_address_index, sizeof (a[i].if_address_index)); + unserialize_integer (m, &a[i].rewrite_header.sw_if_index, sizeof (a[i].rewrite_header.sw_if_index)); + break; + + case IP_LOOKUP_NEXT_REWRITE: + unserialize (m, unserialize_vnet_rewrite, &a[i].rewrite_header, sizeof (a[i].rewrite_data)); + break; + + default: + /* nothing else to unserialize. */ + break; + } + } +} + +static void serialize_vec_ip_multipath_next_hop (serialize_main_t * m, va_list * va) +{ + ip_multipath_next_hop_t * nh = va_arg (*va, ip_multipath_next_hop_t *); + u32 n = va_arg (*va, u32); + u32 i; + for (i = 0; i < n; i++) + { + serialize_integer (m, nh[i].next_hop_adj_index, sizeof (nh[i].next_hop_adj_index)); + serialize_integer (m, nh[i].weight, sizeof (nh[i].weight)); + } +} + +static void unserialize_vec_ip_multipath_next_hop (serialize_main_t * m, va_list * va) +{ + ip_multipath_next_hop_t * nh = va_arg (*va, ip_multipath_next_hop_t *); + u32 n = va_arg (*va, u32); + u32 i; + for (i = 0; i < n; i++) + { + unserialize_integer (m, &nh[i].next_hop_adj_index, sizeof (nh[i].next_hop_adj_index)); + unserialize_integer (m, &nh[i].weight, sizeof (nh[i].weight)); + } +} + +static void serialize_vec_ip_multipath_adjacency (serialize_main_t * m, va_list * va) +{ + ip_multipath_adjacency_t * a = va_arg (*va, ip_multipath_adjacency_t *); + u32 n = va_arg (*va, u32); + u32 i; + for (i = 0; i < n; i++) + { +#define foreach_ip_multipath_adjacency_field \ + _ (adj_index) _ (n_adj_in_block) _ (reference_count) \ + _ (normalized_next_hops.count) \ + _ (normalized_next_hops.heap_offset) \ + _ (normalized_next_hops.heap_handle) \ + _ (unnormalized_next_hops.count) \ + _ (unnormalized_next_hops.heap_offset) \ + _ (unnormalized_next_hops.heap_handle) + +#define _(f) serialize_integer (m, a[i].f, sizeof (a[i].f)); + foreach_ip_multipath_adjacency_field; +#undef _ + } +} + +static void unserialize_vec_ip_multipath_adjacency (serialize_main_t * m, va_list * va) +{ + ip_multipath_adjacency_t * a = va_arg (*va, ip_multipath_adjacency_t *); + u32 n = va_arg (*va, u32); + u32 i; + for (i = 0; i < n; i++) + { +#define _(f) unserialize_integer (m, &a[i].f, sizeof (a[i].f)); + foreach_ip_multipath_adjacency_field; +#undef _ + } +} + +void serialize_ip_lookup_main (serialize_main_t * m, va_list * va) +{ + ip_lookup_main_t * lm = va_arg (*va, ip_lookup_main_t *); + + /* If this isn't true you need to call e.g. ip4_maybe_remap_adjacencies + to make it true. */ + ASSERT (lm->n_adjacency_remaps == 0); + + serialize (m, serialize_heap, lm->adjacency_heap, serialize_vec_ip_adjacency); + + serialize (m, serialize_heap, lm->next_hop_heap, serialize_vec_ip_multipath_next_hop); + vec_serialize (m, lm->multipath_adjacencies, serialize_vec_ip_multipath_adjacency); + + /* Adjacency counters (FIXME disabled for now). */ + if (0) + serialize (m, serialize_vlib_combined_counter_main, &lm->adjacency_counters, /* incremental */ 0); +} + +void unserialize_ip_lookup_main (serialize_main_t * m, va_list * va) +{ + ip_lookup_main_t * lm = va_arg (*va, ip_lookup_main_t *); + + unserialize (m, unserialize_heap, &lm->adjacency_heap, unserialize_vec_ip_adjacency); + unserialize (m, unserialize_heap, &lm->next_hop_heap, unserialize_vec_ip_multipath_next_hop); + vec_unserialize (m, &lm->multipath_adjacencies, unserialize_vec_ip_multipath_adjacency); + + /* Build hash table from unserialized data. */ + { + ip_multipath_adjacency_t * a; + + vec_foreach (a, lm->multipath_adjacencies) + { + if (a->n_adj_in_block > 0 && a->reference_count > 0) + hash_set (lm->multipath_adjacency_by_next_hops, + ip_next_hop_hash_key_from_handle (a->normalized_next_hops.heap_handle), + a - lm->multipath_adjacencies); + } + } + + /* Validate adjacency counters. */ + vlib_validate_combined_counter (&lm->adjacency_counters, + vec_len (lm->adjacency_heap) - 1); + + /* Adjacency counters (FIXME disabled for now). */ + if (0) + unserialize (m, unserialize_vlib_combined_counter_main, &lm->adjacency_counters, /* incremental */ 0); +} + +void ip_lookup_init (ip_lookup_main_t * lm, u32 is_ip6) +{ + ip_adjacency_t * adj; + + /* Hand-craft special miss adjacency to use when nothing matches in the + routing table. Same for drop adjacency. */ + adj = ip_add_adjacency (lm, /* template */ 0, /* n-adj */ 1, &lm->miss_adj_index); + adj->lookup_next_index = IP_LOOKUP_NEXT_MISS; + ASSERT (lm->miss_adj_index == IP_LOOKUP_MISS_ADJ_INDEX); + + adj = ip_add_adjacency (lm, /* template */ 0, /* n-adj */ 1, &lm->drop_adj_index); + adj->lookup_next_index = IP_LOOKUP_NEXT_DROP; + + adj = ip_add_adjacency (lm, /* template */ 0, /* n-adj */ 1, &lm->local_adj_index); + adj->lookup_next_index = IP_LOOKUP_NEXT_LOCAL; + adj->if_address_index = ~0; + + if (! lm->fib_result_n_bytes) + lm->fib_result_n_bytes = sizeof (uword); + + lm->multipath_adjacency_by_next_hops + = hash_create2 (/* elts */ 0, + /* user */ pointer_to_uword (lm), + /* value_bytes */ sizeof (uword), + ip_next_hop_hash_key_sum, + ip_next_hop_hash_key_equal, + /* format pair/arg */ + 0, 0); + + /* 1% max error tolerance for multipath. */ + lm->multipath_next_hop_error_tolerance = .01; + + lm->is_ip6 = is_ip6; + if (is_ip6) + { + lm->format_address_and_length = format_ip6_address_and_length; + mhash_init (&lm->address_to_if_address_index, sizeof (uword), + sizeof (ip6_address_fib_t)); + } + else + { + lm->format_address_and_length = format_ip4_address_and_length; + mhash_init (&lm->address_to_if_address_index, sizeof (uword), + sizeof (ip4_address_fib_t)); + } + + { + int i; + + /* Setup all IP protocols to be punted and builtin-unknown. */ + for (i = 0; i < 256; i++) + { + lm->local_next_by_ip_protocol[i] = IP_LOCAL_NEXT_PUNT; + lm->builtin_protocol_by_ip_protocol[i] = IP_BUILTIN_PROTOCOL_UNKNOWN; + } +#if 0 + /* Eliot's TCP doesn't actually work */ + lm->local_next_by_ip_protocol[IP_PROTOCOL_TCP] = IP_LOCAL_NEXT_TCP_LOOKUP; + lm->builtin_protocol_by_ip_protocol[IP_PROTOCOL_TCP] = + IP_BUILTIN_PROTOCOL_TCP; +#endif + + lm->local_next_by_ip_protocol[IP_PROTOCOL_UDP] = IP_LOCAL_NEXT_UDP_LOOKUP; + lm->local_next_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 : IP_PROTOCOL_ICMP] = IP_LOCAL_NEXT_ICMP; + lm->builtin_protocol_by_ip_protocol[IP_PROTOCOL_UDP] = IP_BUILTIN_PROTOCOL_UDP; + lm->builtin_protocol_by_ip_protocol[is_ip6 ? IP_PROTOCOL_ICMP6 : IP_PROTOCOL_ICMP] = IP_BUILTIN_PROTOCOL_ICMP; + } +} + +u8 * format_ip_flow_hash_config (u8 * s, va_list * args) +{ + u32 flow_hash_config = va_arg (*args, u32); + +#define _(n,v) if (flow_hash_config & v) s = format (s, "%s ", #n); + foreach_flow_hash_bit; +#undef _ + + return s; +} + +u8 * format_ip_lookup_next (u8 * s, va_list * args) +{ + ip_lookup_next_t n = va_arg (*args, ip_lookup_next_t); + char * t = 0; + + switch (n) + { + default: + s = format (s, "unknown %d", n); + return s; + + case IP_LOOKUP_NEXT_MISS: t = "miss"; break; + case IP_LOOKUP_NEXT_DROP: t = "drop"; break; + case IP_LOOKUP_NEXT_PUNT: t = "punt"; break; + case IP_LOOKUP_NEXT_LOCAL: t = "local"; break; + case IP_LOOKUP_NEXT_ARP: t = "arp"; break; + case IP_LOOKUP_NEXT_CLASSIFY: t = "classify"; break; + case IP_LOOKUP_NEXT_MAP: t = "map"; break; + case IP_LOOKUP_NEXT_MAP_T: t = "map-t"; break; + case IP_LOOKUP_NEXT_SIXRD: t = "sixrd"; break; + case IP_LOOKUP_NEXT_REWRITE: + break; + } + + if (t) + vec_add (s, t, strlen (t)); + + return s; +} + +static u8 * format_ip_interface_address (u8 * s, va_list * args) +{ + ip_lookup_main_t * lm = va_arg (*args, ip_lookup_main_t *); + u32 if_address_index = va_arg (*args, u32); + ip_interface_address_t * ia = pool_elt_at_index (lm->if_address_pool, if_address_index); + void * a = ip_interface_address_get_address (lm, ia); + + if (lm->is_ip6) + return format (s, "%U", format_ip6_address_and_length, a, ia->address_length); + else + return format (s, "%U", format_ip4_address_and_length, a, ia->address_length); +} + +u8 * format_ip_adjacency (u8 * s, va_list * args) +{ + vnet_main_t * vnm = va_arg (*args, vnet_main_t *); + ip_lookup_main_t * lm = va_arg (*args, ip_lookup_main_t *); + u32 adj_index = va_arg (*args, u32); + ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index); + + switch (adj->lookup_next_index) + { + case IP_LOOKUP_NEXT_REWRITE: + s = format (s, "%U", + format_vnet_rewrite, + vnm->vlib_main, &adj->rewrite_header, sizeof (adj->rewrite_data)); + break; + + default: + s = format (s, "%U", format_ip_lookup_next, adj->lookup_next_index); + if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP) + s = format (s, " %U", + format_vnet_sw_interface_name, + vnm, + vnet_get_sw_interface (vnm, adj->rewrite_header.sw_if_index)); + switch (adj->lookup_next_index) + { + case IP_LOOKUP_NEXT_ARP: + case IP_LOOKUP_NEXT_LOCAL: + if (adj->if_address_index != ~0) + s = format (s, " %U", format_ip_interface_address, lm, adj->if_address_index); + break; + + case IP_LOOKUP_NEXT_CLASSIFY: + s = format (s, " table %d", adj->classify_table_index); + + default: + break; + } + break; + } + if (adj->explicit_fib_index != ~0 && adj->explicit_fib_index != 0) + s = format (s, " lookup fib index %d", adj->explicit_fib_index); + + return s; +} + +u8 * format_ip_adjacency_packet_data (u8 * s, va_list * args) +{ + vnet_main_t * vnm = va_arg (*args, vnet_main_t *); + ip_lookup_main_t * lm = va_arg (*args, ip_lookup_main_t *); + u32 adj_index = va_arg (*args, u32); + u8 * packet_data = va_arg (*args, u8 *); + u32 n_packet_data_bytes = va_arg (*args, u32); + ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index); + + switch (adj->lookup_next_index) + { + case IP_LOOKUP_NEXT_REWRITE: + s = format (s, "%U", + format_vnet_rewrite_header, + vnm->vlib_main, &adj->rewrite_header, packet_data, n_packet_data_bytes); + break; + + default: + break; + } + + return s; +} + +static uword unformat_ip_lookup_next (unformat_input_t * input, va_list * args) +{ + ip_lookup_next_t * result = va_arg (*args, ip_lookup_next_t *); + ip_lookup_next_t n; + + if (unformat (input, "drop")) + n = IP_LOOKUP_NEXT_DROP; + + else if (unformat (input, "punt")) + n = IP_LOOKUP_NEXT_PUNT; + + else if (unformat (input, "local")) + n = IP_LOOKUP_NEXT_LOCAL; + + else if (unformat (input, "arp")) + n = IP_LOOKUP_NEXT_ARP; + + else if (unformat (input, "classify")) + n = IP_LOOKUP_NEXT_CLASSIFY; + + else + return 0; + + *result = n; + return 1; +} + +static uword unformat_ip_adjacency (unformat_input_t * input, va_list * args) +{ + vlib_main_t * vm = va_arg (*args, vlib_main_t *); + ip_adjacency_t * adj = va_arg (*args, ip_adjacency_t *); + u32 node_index = va_arg (*args, u32); + vnet_main_t * vnm = vnet_get_main(); + u32 sw_if_index, is_ip6; + ip46_address_t a46; + ip_lookup_next_t next; + + is_ip6 = node_index == ip6_rewrite_node.index; + adj->rewrite_header.node_index = node_index; + adj->explicit_fib_index = ~0; + + if (unformat (input, "arp %U %U", + unformat_vnet_sw_interface, vnm, &sw_if_index, + unformat_ip46_address, &a46, is_ip6)) + { + ip_lookup_main_t * lm = is_ip6 ? &ip6_main.lookup_main : &ip4_main.lookup_main; + ip_adjacency_t * a_adj; + u32 adj_index; + + if (is_ip6) + adj_index = ip6_fib_lookup (&ip6_main, sw_if_index, &a46.ip6); + else + adj_index = ip4_fib_lookup (&ip4_main, sw_if_index, &a46.ip4); + + a_adj = ip_get_adjacency (lm, adj_index); + + if (a_adj->rewrite_header.sw_if_index != sw_if_index) + return 0; + + if (is_ip6) + ip6_adjacency_set_interface_route (vnm, adj, sw_if_index, a_adj->if_address_index); + else + ip4_adjacency_set_interface_route (vnm, adj, sw_if_index, a_adj->if_address_index); + } + + else if (unformat_user (input, unformat_ip_lookup_next, &next)) + { + adj->lookup_next_index = next; + adj->if_address_index = ~0; + if (next == IP_LOOKUP_NEXT_LOCAL) + (void) unformat (input, "%d", &adj->if_address_index); + else if (next == IP_LOOKUP_NEXT_CLASSIFY) + if (!unformat (input, "%d", &adj->classify_table_index)) + { + clib_warning ("classify adj must specify table index"); + return 0; + } + } + + else if (unformat_user (input, + unformat_vnet_rewrite, + vm, &adj->rewrite_header, sizeof (adj->rewrite_data))) + adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE; + + else + return 0; + + return 1; +} + +clib_error_t * +vnet_ip_route_cmd (vlib_main_t * vm, unformat_input_t * main_input, vlib_cli_command_t * cmd) +{ + vnet_main_t * vnm = vnet_get_main(); + clib_error_t * error = 0; + u32 table_id, is_del; + u32 weight, * weights = 0; + u32 * table_ids = 0; + u32 sw_if_index, * sw_if_indices = 0; + ip4_address_t ip4_addr, * ip4_dst_addresses = 0, * ip4_via_next_hops = 0; + ip6_address_t ip6_addr, * ip6_dst_addresses = 0, * ip6_via_next_hops = 0; + u32 dst_address_length, * dst_address_lengths = 0; + ip_adjacency_t parse_adj, * add_adj = 0; + unformat_input_t _line_input, * line_input = &_line_input; + f64 count; + u32 outer_table_id; + + is_del = 0; + table_id = 0; + count = 1; + + /* Get a line of input. */ + if (! unformat_user (main_input, unformat_line_input, line_input)) + return 0; + + memset(&parse_adj, 0, sizeof (parse_adj)); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "table %d", &table_id)) + ; + else if (unformat (line_input, "del")) + is_del = 1; + else if (unformat (line_input, "add")) + is_del = 0; + else if (unformat (line_input, "count %f", &count)) + ; + + else if (unformat (line_input, "%U/%d", + unformat_ip4_address, &ip4_addr, + &dst_address_length)) + { + vec_add1 (ip4_dst_addresses, ip4_addr); + vec_add1 (dst_address_lengths, dst_address_length); + } + + else if (unformat (line_input, "%U/%d", + unformat_ip6_address, &ip6_addr, + &dst_address_length)) + { + vec_add1 (ip6_dst_addresses, ip6_addr); + vec_add1 (dst_address_lengths, dst_address_length); + } + + else if (unformat (line_input, "via %U %U weight %u", + unformat_ip4_address, &ip4_addr, + unformat_vnet_sw_interface, vnm, &sw_if_index, + &weight)) + { + vec_add1 (ip4_via_next_hops, ip4_addr); + vec_add1 (sw_if_indices, sw_if_index); + vec_add1 (weights, weight); + vec_add1 (table_ids, (u32)~0); + } + + else if (unformat (line_input, "via %U %U weight %u", + unformat_ip6_address, &ip6_addr, + unformat_vnet_sw_interface, vnm, &sw_if_index, + &weight)) + { + vec_add1 (ip6_via_next_hops, ip6_addr); + vec_add1 (sw_if_indices, sw_if_index); + vec_add1 (weights, weight); + vec_add1 (table_ids, (u32)~0); + } + + else if (unformat (line_input, "via %U %U", + unformat_ip4_address, &ip4_addr, + unformat_vnet_sw_interface, vnm, &sw_if_index)) + { + vec_add1 (ip4_via_next_hops, ip4_addr); + vec_add1 (sw_if_indices, sw_if_index); + vec_add1 (weights, 1); + vec_add1 (table_ids, (u32)~0); + } + + else if (unformat (line_input, "via %U %U", + unformat_ip6_address, &ip6_addr, + unformat_vnet_sw_interface, vnm, &sw_if_index)) + { + vec_add1 (ip6_via_next_hops, ip6_addr); + vec_add1 (sw_if_indices, sw_if_index); + vec_add1 (weights, 1); + vec_add1 (table_ids, (u32)~0); + } + else if (unformat (line_input, "via %U", + unformat_ip4_address, &ip4_addr)) + { + vec_add1 (ip4_via_next_hops, ip4_addr); + vec_add1 (sw_if_indices, (u32)~0); + vec_add1 (weights, 1); + vec_add1 (table_ids, table_id); + } + else if (unformat (line_input, "via %U", + unformat_ip6_address, &ip6_addr)) + { + vec_add1 (ip6_via_next_hops, ip6_addr); + vec_add1 (sw_if_indices, (u32)~0); + vec_add1 (weights, 1); + vec_add1 (table_ids, (u32)table_id); + } + + else if (vec_len (ip4_dst_addresses) > 0 + && unformat (line_input, "via %U", + unformat_ip_adjacency, vm, &parse_adj, ip4_rewrite_node.index)) + vec_add1 (add_adj, parse_adj); + + else if (vec_len (ip6_dst_addresses) > 0 + && unformat (line_input, "via %U", + unformat_ip_adjacency, vm, &parse_adj, ip6_rewrite_node.index)) + vec_add1 (add_adj, parse_adj); + else if (unformat (line_input, "lookup in table %d", &outer_table_id)) + { + uword * p; + + if (vec_len (ip4_dst_addresses) > 0) + p = hash_get (ip4_main.fib_index_by_table_id, outer_table_id); + else + p = hash_get (ip6_main.fib_index_by_table_id, outer_table_id); + + if (p == 0) + { + error = clib_error_return (0, "Nonexistent outer table id %d", + outer_table_id); + goto done; + } + + parse_adj.lookup_next_index = IP_LOOKUP_NEXT_LOCAL; + parse_adj.explicit_fib_index = p[0]; + vec_add1 (add_adj, parse_adj); + } + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + unformat_free (line_input); + + if (vec_len (ip4_dst_addresses) + vec_len (ip6_dst_addresses) == 0) + { + error = clib_error_return (0, "expected ip4/ip6 destination address/length."); + goto done; + } + + if (vec_len (ip4_dst_addresses) > 0 && vec_len (ip6_dst_addresses) > 0) + { + error = clib_error_return (0, "mixed ip4/ip6 address/length."); + goto done; + } + + if (vec_len (ip4_dst_addresses) > 0 && vec_len (ip6_via_next_hops) > 0) + { + error = clib_error_return (0, "ip4 destinations with ip6 next hops."); + goto done; + } + + if (vec_len (ip6_dst_addresses) > 0 && vec_len (ip4_via_next_hops) > 0) + { + error = clib_error_return (0, "ip6 destinations with ip4 next hops."); + goto done; + } + + if (! is_del && vec_len (add_adj) + vec_len (weights) == 0) + { + error = clib_error_return (0, "no next hops or adjacencies to add."); + goto done; + } + + if (vec_len(ip4_via_next_hops)) + { + if (sw_if_indices[0] == (u32)~0) + { + u32 ai; + uword * p; + u32 fib_index; + ip_adjacency_t *nh_adj; + + p = hash_get (ip4_main.fib_index_by_table_id, table_ids[0]); + if (p == 0) + { + error = clib_error_return (0, "Nonexistent FIB id %d", + table_ids[0]); + goto done; + } + + fib_index = p[0]; + + ai = ip4_fib_lookup_with_table (&ip4_main, + fib_index, + ip4_via_next_hops, + 1 /* disable default route */); + if (ai == 0) + { + error = clib_error_return (0, "next hop %U not in FIB", + format_ip4_address, + ip4_via_next_hops); + goto done; + } + nh_adj = ip_get_adjacency (&ip4_main.lookup_main, ai); + vec_add1 (add_adj, nh_adj[0]); + } + } + if (vec_len(ip6_via_next_hops)) + { + if (sw_if_indices[0] == (u32)~0) + { + u32 ai; + uword * p; + u32 fib_index; + ip_adjacency_t *nh_adj; + + p = hash_get (ip6_main.fib_index_by_table_id, table_ids[0]); + if (p == 0) + { + error = clib_error_return (0, "Nonexistent FIB id %d", + table_ids[0]); + goto done; + } + + fib_index = p[0]; + ai = ip6_fib_lookup_with_table (&ip6_main, + fib_index, + ip6_via_next_hops); + if (ai == 0) + { + error = clib_error_return (0, "next hop %U not in FIB", + format_ip6_address, + ip6_via_next_hops); + goto done; + } + nh_adj = ip_get_adjacency (&ip6_main.lookup_main, ai); + vec_add1 (add_adj, nh_adj[0]); + } + } + + { + int i; + ip4_main_t * im4 = &ip4_main; + ip6_main_t * im6 = &ip6_main; + + for (i = 0; i < vec_len (ip4_dst_addresses); i++) + { + ip4_add_del_route_args_t a; + + memset (&a, 0, sizeof (a)); + a.flags = IP4_ROUTE_FLAG_TABLE_ID; + a.table_index_or_table_id = table_id; + a.dst_address = ip4_dst_addresses[i]; + a.dst_address_length = dst_address_lengths[i]; + a.adj_index = ~0; + + if (is_del) + { + if (vec_len (ip4_via_next_hops) == 0) + { + uword * dst_hash, * dst_result; + u32 dst_address_u32; + ip4_fib_t * fib; + + fib = find_ip4_fib_by_table_index_or_id (im4, table_id, + 0 /* by table id */); + + a.flags |= IP4_ROUTE_FLAG_DEL; + dst_address_u32 = a.dst_address.as_u32 + & im4->fib_masks[a.dst_address_length]; + + dst_hash = + fib->adj_index_by_dst_address[a.dst_address_length]; + dst_result = hash_get (dst_hash, dst_address_u32); + if (dst_result) + a.adj_index = dst_result[0]; + else + { + clib_warning ("%U/%d not in FIB", + format_ip4_address, &a.dst_address, + a.dst_address_length); + continue; + } + + ip4_add_del_route (im4, &a); + ip4_maybe_remap_adjacencies (im4, table_id, + IP4_ROUTE_FLAG_TABLE_ID); + } + else + { + u32 i, j, n, f, incr; + ip4_address_t dst = a.dst_address; + f64 t[2]; + n = count; + t[0] = vlib_time_now (vm); + incr = 1<<(32 - a.dst_address_length); + for (i = 0; i < n; i++) + { + f = i + 1 < n ? IP4_ROUTE_FLAG_NOT_LAST_IN_GROUP : 0; + a.dst_address = dst; + for (j = 0; j < vec_len (ip4_via_next_hops); j++) + { + if (table_ids[j] != (u32)~0) + { + uword * p = hash_get (im4->fib_index_by_table_id, + table_ids[j]); + if (p == 0) + { + clib_warning ("no such FIB table %d", + table_ids[j]); + continue; + } + table_ids[j] = p[0]; + } + + ip4_add_del_route_next_hop (im4, + IP4_ROUTE_FLAG_DEL | f, + &a.dst_address, + a.dst_address_length, + &ip4_via_next_hops[j], + sw_if_indices[j], + weights[j], (u32)~0, + table_ids[j] /* fib index */); + } + dst.as_u32 = clib_host_to_net_u32 (incr + clib_net_to_host_u32 (dst.as_u32)); + } + t[1] = vlib_time_now (vm); + if (count > 1) + vlib_cli_output (vm, "%.6e routes/sec", count / (t[1] - t[0])); + } + } + else + { + if (vec_len (add_adj) > 0) + { + a.flags |= IP4_ROUTE_FLAG_ADD; + a.add_adj = add_adj; + a.n_add_adj = vec_len (add_adj); + + ip4_add_del_route (im4, &a); + } + else if (vec_len (ip4_via_next_hops) > 0) + { + u32 i, j, n, f, incr; + ip4_address_t dst = a.dst_address; + f64 t[2]; + n = count; + t[0] = vlib_time_now (vm); + incr = 1<<(32 - a.dst_address_length); + for (i = 0; i < n; i++) + { + f = i + 1 < n ? IP4_ROUTE_FLAG_NOT_LAST_IN_GROUP : 0; + a.dst_address = dst; + for (j = 0; j < vec_len (ip4_via_next_hops); j++) + { + if (table_ids[j] != (u32)~0) + { + uword * p = hash_get (im4->fib_index_by_table_id, + table_ids[j]); + if (p == 0) + { + clib_warning ("no such FIB table %d", + table_ids[j]); + continue; + } + table_ids[j] = p[0]; + } + ip4_add_del_route_next_hop (im4, + IP4_ROUTE_FLAG_ADD | f, + &a.dst_address, + a.dst_address_length, + &ip4_via_next_hops[j], + sw_if_indices[j], + weights[j], (u32)~0, + table_ids[j] /* fib index */); + } + dst.as_u32 = clib_host_to_net_u32 (incr + clib_net_to_host_u32 (dst.as_u32)); + } + t[1] = vlib_time_now (vm); + if (count > 1) + vlib_cli_output (vm, "%.6e routes/sec", count / (t[1] - t[0])); + } + } + } + + for (i = 0; i < vec_len (ip6_dst_addresses); i++) + { + ip6_add_del_route_args_t a; + + + memset (&a, 0, sizeof (a)); + a.flags = IP6_ROUTE_FLAG_TABLE_ID; + a.table_index_or_table_id = table_id; + a.dst_address = ip6_dst_addresses[i]; + a.dst_address_length = dst_address_lengths[i]; + a.adj_index = ~0; + + if (is_del) + { + if (vec_len (ip6_via_next_hops) == 0) + { + BVT(clib_bihash_kv) kv, value; + ip6_address_t dst_address; + ip6_fib_t * fib; + + fib = find_ip6_fib_by_table_index_or_id (im6, table_id, + 0 /* by table id */); + + a.flags |= IP4_ROUTE_FLAG_DEL; + + dst_address = ip6_dst_addresses[i]; + + ip6_address_mask (&dst_address, + &im6->fib_masks[dst_address_length]); + + kv.key[0] = dst_address.as_u64[0]; + kv.key[1] = dst_address.as_u64[1]; + kv.key[2] = ((u64)(fib - im6->fibs)<<32) + | a.dst_address_length; + + if (BV(clib_bihash_search)(&im6->ip6_lookup_table, + &kv, &value) == 0) + a.adj_index = value.value; + else + { + clib_warning ("%U/%d not in FIB", + format_ip6_address, &a.dst_address, + a.dst_address_length); + continue; + } + + a.flags |= IP6_ROUTE_FLAG_DEL; + ip6_add_del_route (im6, &a); + ip6_maybe_remap_adjacencies (im6, table_id, + IP6_ROUTE_FLAG_TABLE_ID); + } + else + { + u32 i; + for (i = 0; i < vec_len (ip6_via_next_hops); i++) + { + ip6_add_del_route_next_hop (im6, + IP6_ROUTE_FLAG_DEL, + &a.dst_address, + a.dst_address_length, + &ip6_via_next_hops[i], + sw_if_indices[i], + weights[i], (u32)~0, + table_ids[i] /* fib index */); + } + } + } + else + { + if (vec_len (add_adj) > 0) + { + a.flags |= IP6_ROUTE_FLAG_ADD; + a.add_adj = add_adj; + a.n_add_adj = vec_len (add_adj); + + ip6_add_del_route (im6, &a); + } + else if (vec_len (ip6_via_next_hops) > 0) + { + u32 i; + for (i = 0; i < vec_len (ip6_via_next_hops); i++) + { + ip6_add_del_route_next_hop (im6, + IP6_ROUTE_FLAG_ADD, + &a.dst_address, + a.dst_address_length, + &ip6_via_next_hops[i], + sw_if_indices[i], + weights[i], (u32)~0, + table_ids[i]); + } + } + } + } + } + + done: + vec_free (add_adj); + vec_free (weights); + vec_free (dst_address_lengths); + vec_free (ip4_dst_addresses); + vec_free (ip6_dst_addresses); + vec_free (ip4_via_next_hops); + vec_free (ip6_via_next_hops); + return error; +} + +VLIB_CLI_COMMAND (vlib_cli_ip_command, static) = { + .path = "ip", + .short_help = "Internet protocol (IP) commands", +}; + +VLIB_CLI_COMMAND (vlib_cli_show_ip_command, static) = { + .path = "show ip", + .short_help = "Internet protocol (IP) show commands", +}; + +VLIB_CLI_COMMAND (vlib_cli_show_ip4_command, static) = { + .path = "show ip4", + .short_help = "Internet protocol version 4 (IP4) show commands", +}; + +VLIB_CLI_COMMAND (vlib_cli_show_ip6_command, static) = { + .path = "show ip6", + .short_help = "Internet protocol version 6 (IP6) show commands", +}; + +VLIB_CLI_COMMAND (ip_route_command, static) = { + .path = "ip route", + .short_help = "Add/delete IP routes", + .function = vnet_ip_route_cmd, +}; + +/* + * The next two routines address a longstanding script hemorrhoid. + * Probing a v4 or v6 neighbor needs to appear to be synchronous, + * or dependent route-adds will simply fail. + */ +static clib_error_t * +ip6_probe_neighbor_wait (vlib_main_t *vm, ip6_address_t * a, u32 sw_if_index, + int retry_count) +{ + vnet_main_t * vnm = vnet_get_main(); + clib_error_t * e; + int i; + int resolved = 0; + uword event_type; + uword *event_data = 0; + + ASSERT (vlib_in_process_context(vm)); + + if (retry_count > 0) + vnet_register_ip6_neighbor_resolution_event + (vnm, a, vlib_get_current_process (vm)->node_runtime.node_index, + 1 /* event */, 0 /* data */); + + for (i = 0; i < retry_count; i++) + { + /* The interface may be down, etc. */ + e = ip6_probe_neighbor (vm, a, sw_if_index); + + if (e) + return e; + + vlib_process_wait_for_event_or_clock (vm, 1.0); + event_type = vlib_process_get_events (vm, &event_data); + switch (event_type) + { + case 1: /* resolved... */ + vlib_cli_output (vm, "Resolved %U", + format_ip6_address, a); + resolved = 1; + goto done; + + case ~0: /* timeout */ + break; + + default: + clib_warning ("unknown event_type %d", event_type); + } + } + + done: + vec_reset_length (event_data); + + if (!resolved) + return clib_error_return (0, "Resolution failed for %U", + format_ip6_address, a); + return 0; +} + +static clib_error_t * +ip4_probe_neighbor_wait (vlib_main_t *vm, ip4_address_t * a, u32 sw_if_index, + int retry_count) +{ + vnet_main_t * vnm = vnet_get_main(); + clib_error_t * e; + int i; + int resolved = 0; + uword event_type; + uword *event_data = 0; + + ASSERT (vlib_in_process_context(vm)); + + if (retry_count > 0) + vnet_register_ip4_arp_resolution_event + (vnm, a, vlib_get_current_process (vm)->node_runtime.node_index, + 1 /* event */, 0 /* data */); + + for (i = 0; i < retry_count; i++) + { + /* The interface may be down, etc. */ + e = ip4_probe_neighbor (vm, a, sw_if_index); + + if (e) + return e; + + vlib_process_wait_for_event_or_clock (vm, 1.0); + event_type = vlib_process_get_events (vm, &event_data); + switch (event_type) + { + case 1: /* resolved... */ + vlib_cli_output (vm, "Resolved %U", + format_ip4_address, a); + resolved = 1; + goto done; + + case ~0: /* timeout */ + break; + + default: + clib_warning ("unknown event_type %d", event_type); + } + } + + done: + + vec_reset_length (event_data); + + if (!resolved) + return clib_error_return (0, "Resolution failed for %U", + format_ip4_address, a); + return 0; +} + +static clib_error_t * +probe_neighbor_address (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vnet_main_t * vnm = vnet_get_main(); + unformat_input_t _line_input, * line_input = &_line_input; + ip4_address_t a4; + ip6_address_t a6; + clib_error_t * error = 0; + u32 sw_if_index = ~0; + int retry_count = 3; + int is_ip4 = 1; + int address_set = 0; + + /* Get a line of input. */ + if (! unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user (line_input, unformat_vnet_sw_interface, vnm, + &sw_if_index)) + ; + else if (unformat (line_input, "retry %d", &retry_count)) + ; + + else if (unformat (line_input, "%U", unformat_ip4_address, &a4)) + address_set++; + else if (unformat (line_input, "%U", unformat_ip6_address, &a6)) + { + address_set++; + is_ip4 = 0; + } + else + return clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + } + + unformat_free (line_input); + + if (sw_if_index == ~0) + return clib_error_return (0, "Interface required, not set."); + if (address_set == 0) + return clib_error_return (0, "ip address required, not set."); + if (address_set > 1) + return clib_error_return (0, "Multiple ip addresses not supported."); + + if (is_ip4) + error = ip4_probe_neighbor_wait (vm, &a4, sw_if_index, retry_count); + else + error = ip6_probe_neighbor_wait (vm, &a6, sw_if_index, retry_count); + + return error; +} + +VLIB_CLI_COMMAND (ip_probe_neighbor_command, static) = { + .path = "ip probe-neighbor", + .function = probe_neighbor_address, + .short_help = "ip probe-neighbor <intfc> <ip4-addr> | <ip6-addr> [retry nn]", +}; + +typedef CLIB_PACKED (struct { + ip4_address_t address; + + u32 address_length : 6; + + u32 index : 26; +}) ip4_route_t; + +static clib_error_t * +ip4_show_fib (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t * vnm = vnet_get_main(); + ip4_main_t * im4 = &ip4_main; + ip4_route_t * routes, * r; + ip4_fib_t * fib; + ip_lookup_main_t * lm = &im4->lookup_main; + uword * results, i; + int verbose, matching, mtrie, include_empty_fibs; + ip4_address_t matching_address; + u8 clear = 0; + int table_id = -1; + + routes = 0; + results = 0; + verbose = 1; + include_empty_fibs = 0; + matching = 0; + mtrie = 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, "mtrie")) + mtrie = 1; + + else if (unformat (input, "include-empty")) + include_empty_fibs = 1; + + else if (unformat (input, "%U", unformat_ip4_address, &matching_address)) + matching = 1; + + else if (unformat (input, "clear")) + clear = 1; + + else if (unformat (input, "table %d", &table_id)) + ; + else + break; + } + + vec_foreach (fib, im4->fibs) + { + int fib_not_empty; + + fib_not_empty = 0; + for (i = 0; i < ARRAY_LEN (fib->adj_index_by_dst_address); i++) + { + uword * hash = fib->adj_index_by_dst_address[i]; + uword n_elts = hash_elts (hash); + if (n_elts) + { + fib_not_empty = 1; + break; + } + } + + if (fib_not_empty == 0 && include_empty_fibs == 0) + continue; + + if (table_id >= 0 && table_id != (int)fib->table_id) + continue; + + if (include_empty_fibs) + vlib_cli_output (vm, "Table %d, fib_index %d, flow hash: %U", + fib->table_id, fib - im4->fibs, + format_ip_flow_hash_config, fib->flow_hash_config); + + /* Show summary? */ + if (! verbose) + { + if (include_empty_fibs == 0) + vlib_cli_output (vm, "Table %d, fib_index %d, flow hash: %U", + fib->table_id, fib - im4->fibs, + format_ip_flow_hash_config, fib->flow_hash_config); + vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count"); + for (i = 0; i < ARRAY_LEN (fib->adj_index_by_dst_address); i++) + { + uword * hash = fib->adj_index_by_dst_address[i]; + uword n_elts = hash_elts (hash); + if (n_elts > 0) + vlib_cli_output (vm, "%20d%16d", i, n_elts); + } + continue; + } + + if (routes) + _vec_len (routes) = 0; + if (results) + _vec_len (results) = 0; + + for (i = 0; i < ARRAY_LEN (fib->adj_index_by_dst_address); i++) + { + uword * hash = fib->adj_index_by_dst_address[i]; + hash_pair_t * p; + ip4_route_t x; + + x.address_length = i; + + if (matching) + { + x.address.as_u32 = matching_address.as_u32 & im4->fib_masks[i]; + p = hash_get_pair (hash, x.address.as_u32); + if (p) + { + if (lm->fib_result_n_words > 1) + { + x.index = vec_len (results); + vec_add (results, p->value, lm->fib_result_n_words); + } + else + x.index = p->value[0]; + vec_add1 (routes, x); + } + } + else + { + hash_foreach_pair (p, hash, ({ + x.address.data_u32 = p->key; + if (lm->fib_result_n_words > 1) + { + x.index = vec_len (results); + vec_add (results, p->value, lm->fib_result_n_words); + } + else + x.index = p->value[0]; + + vec_add1 (routes, x); + })); + } + } + + vec_sort (routes, r1, r2, + ({ int cmp = ip4_address_compare (&r1->address, &r2->address); + cmp ? cmp : ((int) r1->address_length - (int) r2->address_length); })); + if (vec_len(routes)) { + if (include_empty_fibs == 0) + vlib_cli_output (vm, "Table %d, fib_index %d, flow hash: %U", + fib->table_id, fib - im4->fibs, + format_ip_flow_hash_config, fib->flow_hash_config); + if (mtrie) + vlib_cli_output (vm, "%U", format_ip4_fib_mtrie, &fib->mtrie); + vlib_cli_output (vm, "%=20s%=16s%=16s%=16s", + "Destination", "Packets", "Bytes", "Adjacency"); + } + vec_foreach (r, routes) + { + vlib_counter_t c, sum; + uword i, j, n_left, n_nhs, adj_index, * result = 0; + ip_adjacency_t * adj; + ip_multipath_next_hop_t * nhs, tmp_nhs[1]; + + adj_index = r->index; + if (lm->fib_result_n_words > 1) + { + result = vec_elt_at_index (results, adj_index); + adj_index = result[0]; + } + + adj = ip_get_adjacency (lm, adj_index); + if (adj->n_adj == 1) + { + nhs = &tmp_nhs[0]; + nhs[0].next_hop_adj_index = ~0; /* not used */ + nhs[0].weight = 1; + n_nhs = 1; + } + else + { + ip_multipath_adjacency_t * madj; + madj = vec_elt_at_index (lm->multipath_adjacencies, adj->heap_handle); + nhs = heap_elt_at_index (lm->next_hop_heap, madj->normalized_next_hops.heap_offset); + n_nhs = madj->normalized_next_hops.count; + } + + n_left = nhs[0].weight; + vlib_counter_zero (&sum); + for (i = j = 0; i < adj->n_adj; i++) + { + n_left -= 1; + vlib_get_combined_counter (&lm->adjacency_counters, + adj_index + i, &c); + if (clear) + vlib_zero_combined_counter (&lm->adjacency_counters, + adj_index + i); + vlib_counter_add (&sum, &c); + if (n_left == 0) + { + u8 * msg = 0; + uword indent; + + if (j == 0) + msg = format (msg, "%-20U", + format_ip4_address_and_length, + r->address.data, r->address_length); + else + msg = format (msg, "%U", format_white_space, 20); + + msg = format (msg, "%16Ld%16Ld ", sum.packets, sum.bytes); + + indent = vec_len (msg); + msg = format (msg, "weight %d, index %d\n%U%U", + nhs[j].weight, adj_index + i, + format_white_space, indent, + format_ip_adjacency, + vnm, lm, adj_index + i); + + vlib_cli_output (vm, "%v", msg); + vec_free (msg); + + if (result && lm->format_fib_result) + vlib_cli_output (vm, "%20s%U", "", + lm->format_fib_result, vm, lm, result, + i + 1 - nhs[j].weight, + nhs[j].weight); + + j++; + if (j < n_nhs) + { + n_left = nhs[j].weight; + vlib_counter_zero (&sum); + } + } + } + } + } + + vec_free (routes); + vec_free (results); + + return 0; +} + +VLIB_CLI_COMMAND (ip4_show_fib_command, static) = { + .path = "show ip fib", + .short_help = "show ip fib [mtrie] [summary] [table <n>] [<ip4-addr>] [clear] [include-empty]", + .function = ip4_show_fib, +}; + +typedef struct { + ip6_address_t address; + + u32 address_length; + + u32 index; +} ip6_route_t; + +typedef struct { + u32 fib_index; + ip6_route_t ** routep; +} add_routes_in_fib_arg_t; + +static void add_routes_in_fib (BVT(clib_bihash_kv) * kvp, void *arg) +{ + add_routes_in_fib_arg_t * ap = arg; + + if (kvp->key[2]>>32 == ap->fib_index) + { + ip6_address_t *addr; + ip6_route_t * r; + addr = (ip6_address_t *) kvp; + vec_add2 (*ap->routep, r, 1); + r->address = addr[0]; + r->address_length = kvp->key[2] & 0xFF; + r->index = kvp->value; + } +} + +typedef struct { + u32 fib_index; + u64 count_by_prefix_length[129]; +} count_routes_in_fib_at_prefix_length_arg_t; + +static void count_routes_in_fib_at_prefix_length +(BVT(clib_bihash_kv) * 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; + + mask_width = kvp->key[2] & 0xFF; + + ap->count_by_prefix_length[mask_width]++; +} + + +static clib_error_t * +ip6_show_fib (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) +{ + vnet_main_t * vnm = vnet_get_main(); + ip6_main_t * im6 = &ip6_main; + ip6_route_t * routes, * r; + ip6_fib_t * fib; + ip_lookup_main_t * lm = &im6->lookup_main; + uword * results; + int verbose; + BVT(clib_bihash) * h = &im6->ip6_lookup_table; + __attribute__((unused)) u8 clear = 0; + add_routes_in_fib_arg_t _a, *a=&_a; + count_routes_in_fib_at_prefix_length_arg_t _ca, *ca = &_ca; + + routes = 0; + results = 0; + verbose = 1; + if (unformat (input, "brief") || unformat (input, "summary") + || unformat (input, "sum")) + verbose = 0; + + if (unformat (input, "clear")) + clear = 1; + + vlib_cli_output (vm, "FIB lookup table: %d buckets, %lld MB heap", + im6->lookup_table_nbuckets, im6->lookup_table_size>>20); + vlib_cli_output (vm, "%U", format_mheap, h->mheap, 0 /*verbose*/); + vlib_cli_output (vm, " "); + + vec_foreach (fib, im6->fibs) + { + vlib_cli_output (vm, "VRF %d, fib_index %d, flow hash: %U", + fib->table_id, fib - im6->fibs, + format_ip_flow_hash_config, fib->flow_hash_config); + + /* Show summary? */ + if (! verbose) + { + int len; + vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count"); + + memset (ca, 0, sizeof(*ca)); + ca->fib_index = fib - im6->fibs; + + BV(clib_bihash_foreach_key_value_pair) + (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 (routes) + _vec_len (routes) = 0; + if (results) + _vec_len (results) = 0; + + a->fib_index = fib - im6->fibs; + a->routep = &routes; + + BV(clib_bihash_foreach_key_value_pair)(h, add_routes_in_fib, a); + + vec_sort (routes, r1, r2, + ({ int cmp = ip6_address_compare (&r1->address, &r2->address); + cmp ? cmp : ((int) r1->address_length - (int) r2->address_length); })); + + vlib_cli_output (vm, "%=45s%=16s%=16s%=16s", + "Destination", "Packets", "Bytes", "Adjacency"); + vec_foreach (r, routes) + { + vlib_counter_t c, sum; + uword i, j, n_left, n_nhs, adj_index, * result = 0; + ip_adjacency_t * adj; + ip_multipath_next_hop_t * nhs, tmp_nhs[1]; + + adj_index = r->index; + if (lm->fib_result_n_words > 1) + { + result = vec_elt_at_index (results, adj_index); + adj_index = result[0]; + } + + adj = ip_get_adjacency (lm, adj_index); + if (adj->n_adj == 1) + { + nhs = &tmp_nhs[0]; + nhs[0].next_hop_adj_index = ~0; /* not used */ + nhs[0].weight = 1; + n_nhs = 1; + } + else + { + ip_multipath_adjacency_t * madj; + madj = vec_elt_at_index (lm->multipath_adjacencies, adj->heap_handle); + nhs = heap_elt_at_index (lm->next_hop_heap, madj->normalized_next_hops.heap_offset); + n_nhs = madj->normalized_next_hops.count; + } + + n_left = nhs[0].weight; + vlib_counter_zero (&sum); + for (i = j = 0; i < adj->n_adj; i++) + { + n_left -= 1; + vlib_get_combined_counter (&lm->adjacency_counters, + adj_index + i, &c); + if (clear) + vlib_zero_combined_counter (&lm->adjacency_counters, + adj_index + i); + vlib_counter_add (&sum, &c); + if (n_left == 0) + { + u8 * msg = 0; + uword indent; + + if (j == 0) + msg = format (msg, "%-45U", + format_ip6_address_and_length, + r->address.as_u8, r->address_length); + else + msg = format (msg, "%U", format_white_space, 20); + + msg = format (msg, "%16Ld%16Ld ", sum.packets, sum.bytes); + + indent = vec_len (msg); + msg = format (msg, "weight %d, index %d\n%U%U", + nhs[j].weight, adj_index + i, + format_white_space, indent, + format_ip_adjacency, + vnm, lm, adj_index + i); + + vlib_cli_output (vm, "%v", msg); + vec_free (msg); + + j++; + if (j < n_nhs) + { + n_left = nhs[j].weight; + vlib_counter_zero (&sum); + } + } + } + + if (result && lm->format_fib_result) + vlib_cli_output (vm, "%20s%U", "", lm->format_fib_result, vm, lm, result, 0); + } + vlib_cli_output (vm, " "); + } + + vec_free (routes); + vec_free (results); + + return 0; +} + +VLIB_CLI_COMMAND (ip6_show_fib_command, static) = { + .path = "show ip6 fib", + .short_help = "show ip6 fib [summary] [clear]", + .function = ip6_show_fib, +}; |