-/** for srp_hw_interface_class */
-#include <vnet/srp/srp.h>
-/** for API error numbers */
-#include <vnet/api_errno.h>
-
-/** @file
- vnet ip4 forwarding
-*/
-
-/* This is really, really simple but stupid fib. */
-u32
-ip4_fib_lookup_with_table (ip4_main_t * im, u32 fib_index,
- ip4_address_t * dst,
- u32 disable_default_route)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- ip4_fib_t * fib = vec_elt_at_index (im->fibs, fib_index);
- uword * p, * hash, key;
- i32 i, i_min, dst_address, ai;
-
- i_min = disable_default_route ? 1 : 0;
- dst_address = clib_mem_unaligned (&dst->data_u32, u32);
- for (i = ARRAY_LEN (fib->adj_index_by_dst_address) - 1; i >= i_min; i--)
- {
- hash = fib->adj_index_by_dst_address[i];
- if (! hash)
- continue;
-
- key = dst_address & im->fib_masks[i];
- if ((p = hash_get (hash, key)) != 0)
- {
- ai = p[0];
- goto done;
- }
- }
-
- /* Nothing matches in table. */
- ai = lm->miss_adj_index;
-
- done:
- return ai;
-}
-
-/** @brief Create FIB from table ID and init all hashing.
- @param im - @ref ip4_main_t
- @param table_id - table ID
- @return fib - @ref ip4_fib_t
-*/
-static ip4_fib_t *
-create_fib_with_table_id (ip4_main_t * im, u32 table_id)
-{
- ip4_fib_t * fib;
- hash_set (im->fib_index_by_table_id, table_id, vec_len (im->fibs));
- vec_add2 (im->fibs, fib, 1);
- fib->table_id = table_id;
- fib->index = fib - im->fibs;
- /* IP_FLOW_HASH_DEFAULT is net value of 5 tuple flags without "reverse" bit */
- fib->flow_hash_config = IP_FLOW_HASH_DEFAULT;
- fib->fwd_classify_table_index = ~0;
- fib->rev_classify_table_index = ~0;
- ip4_mtrie_init (&fib->mtrie);
- return fib;
-}
-
-/** @brief Find existing or Create new FIB based on index
- @param im @ref ip4_main_t
- @param table_index_or_id - overloaded parameter referring
- to the table or a table's index in the FIB vector
- @param flags - used to check if table_index_or_id was a table or
- an index (detected by @ref IP4_ROUTE_FLAG_FIB_INDEX)
- @return either the existing or a new ip4_fib_t entry
-*/
-ip4_fib_t *
-find_ip4_fib_by_table_index_or_id (ip4_main_t * im,
- u32 table_index_or_id, u32 flags)
-{
- uword * p, fib_index;
-
- fib_index = table_index_or_id;
- /* If this isn't a FIB_INDEX ... */
- if (! (flags & IP4_ROUTE_FLAG_FIB_INDEX))
- {
- /* If passed ~0 then request the next table available */
- if (table_index_or_id == ~0) {
- table_index_or_id = 0;
- while ((p = hash_get (im->fib_index_by_table_id, table_index_or_id))) {
- table_index_or_id++;
- }
- /* Create the next table and return the ip4_fib_t associated with it */
- return create_fib_with_table_id (im, table_index_or_id);
- }
- /* A specific table_id was requested.. */
- p = hash_get (im->fib_index_by_table_id, table_index_or_id);
- /* ... and if it doesn't exist create it else grab its index */
- if (! p)
- return create_fib_with_table_id (im, table_index_or_id);
- fib_index = p[0];
- }
- /* Return the ip4_fib_t associated with this index */
- return vec_elt_at_index (im->fibs, fib_index);
-}
-
-static void
-ip4_fib_init_adj_index_by_dst_address (ip_lookup_main_t * lm,
- ip4_fib_t * fib,
- u32 address_length)
-{
- hash_t * h;
- uword max_index;
-
- ASSERT (lm->fib_result_n_bytes >= sizeof (uword));
- lm->fib_result_n_words = round_pow2 (lm->fib_result_n_bytes, sizeof (uword)) / sizeof (uword);
-
- fib->adj_index_by_dst_address[address_length] =
- hash_create (32 /* elts */, lm->fib_result_n_words * sizeof (uword));
-
- hash_set_flags (fib->adj_index_by_dst_address[address_length],
- HASH_FLAG_NO_AUTO_SHRINK);
-
- h = hash_header (fib->adj_index_by_dst_address[address_length]);
- max_index = (hash_value_bytes (h) / sizeof (fib->new_hash_values[0])) - 1;
-
- /* Initialize new/old hash value vectors. */
- vec_validate_init_empty (fib->new_hash_values, max_index, ~0);
- vec_validate_init_empty (fib->old_hash_values, max_index, ~0);
-}
-
-static void
-ip4_fib_set_adj_index (ip4_main_t * im,
- ip4_fib_t * fib,
- u32 flags,
- u32 dst_address_u32,
- u32 dst_address_length,
- u32 adj_index)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- uword * hash;
-
- if (vec_bytes(fib->old_hash_values))
- memset (fib->old_hash_values, ~0, vec_bytes (fib->old_hash_values));
- if (vec_bytes(fib->new_hash_values))
- memset (fib->new_hash_values, ~0, vec_bytes (fib->new_hash_values));
- fib->new_hash_values[0] = adj_index;
-
- /* Make sure adj index is valid. */
- if (CLIB_DEBUG > 0)
- (void) ip_get_adjacency (lm, adj_index);
-
- hash = fib->adj_index_by_dst_address[dst_address_length];
-
- hash = _hash_set3 (hash, dst_address_u32,
- fib->new_hash_values,
- fib->old_hash_values);
-
- fib->adj_index_by_dst_address[dst_address_length] = hash;
-
- if (vec_len (im->add_del_route_callbacks) > 0)
- {
- ip4_add_del_route_callback_t * cb;
- ip4_address_t d;
- uword * p;
-
- d.data_u32 = dst_address_u32;
- vec_foreach (cb, im->add_del_route_callbacks)
- if ((flags & cb->required_flags) == cb->required_flags)
- cb->function (im, cb->function_opaque,
- fib, flags,
- &d, dst_address_length,
- fib->old_hash_values,
- fib->new_hash_values);
-
- p = hash_get (hash, dst_address_u32);
- /* hash_get should never return NULL here */
- if (p)
- clib_memcpy (p, fib->new_hash_values,
- vec_bytes (fib->new_hash_values));
- else
- ASSERT(0);
- }
-}
-
-void ip4_add_del_route (ip4_main_t * im, ip4_add_del_route_args_t * a)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- ip4_fib_t * fib;
- u32 dst_address, dst_address_length, adj_index, old_adj_index;
- uword * hash, is_del;
- ip4_add_del_route_callback_t * cb;
-
- /* Either create new adjacency or use given one depending on arguments. */
- if (a->n_add_adj > 0)
- {
- ip_add_adjacency (lm, a->add_adj, a->n_add_adj, &adj_index);
- ip_call_add_del_adjacency_callbacks (lm, adj_index, /* is_del */ 0);
- }
- else
- adj_index = a->adj_index;
-
- dst_address = a->dst_address.data_u32;
- dst_address_length = a->dst_address_length;
- fib = find_ip4_fib_by_table_index_or_id (im, a->table_index_or_table_id, a->flags);
-
- ASSERT (dst_address_length < ARRAY_LEN (im->fib_masks));
- dst_address &= im->fib_masks[dst_address_length];
-
- if (! fib->adj_index_by_dst_address[dst_address_length])
- ip4_fib_init_adj_index_by_dst_address (lm, fib, dst_address_length);
-
- hash = fib->adj_index_by_dst_address[dst_address_length];
-
- is_del = (a->flags & IP4_ROUTE_FLAG_DEL) != 0;
-
- if (is_del)
- {
- fib->old_hash_values[0] = ~0;
- hash = _hash_unset (hash, dst_address, fib->old_hash_values);
- fib->adj_index_by_dst_address[dst_address_length] = hash;
-
- if (vec_len (im->add_del_route_callbacks) > 0
- && fib->old_hash_values[0] != ~0) /* make sure destination was found in hash */
- {
- fib->new_hash_values[0] = ~0;
- vec_foreach (cb, im->add_del_route_callbacks)
- if ((a->flags & cb->required_flags) == cb->required_flags)
- cb->function (im, cb->function_opaque,
- fib, a->flags,
- &a->dst_address, dst_address_length,
- fib->old_hash_values,
- fib->new_hash_values);
- }
- }
- else
- ip4_fib_set_adj_index (im, fib, a->flags, dst_address, dst_address_length,
- adj_index);
-
- old_adj_index = fib->old_hash_values[0];
-
- /* Avoid spurious reference count increments */
- if (old_adj_index == adj_index
- && adj_index != ~0
- && !(a->flags & IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY))
- {
- ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index);
- if (adj->share_count > 0)
- adj->share_count --;
- }
-
- ip4_fib_mtrie_add_del_route (fib, a->dst_address, dst_address_length,
- is_del ? old_adj_index : adj_index,
- is_del);
-
- /* Delete old adjacency index if present and changed. */
- if (! (a->flags & IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY)
- && old_adj_index != ~0
- && old_adj_index != adj_index)
- ip_del_adjacency (lm, old_adj_index);
-}
-
-
-u32
-ip4_route_get_next_hop_adj (ip4_main_t * im,
- u32 fib_index,
- ip4_address_t *next_hop,
- u32 next_hop_sw_if_index,
- u32 explicit_fib_index)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- vnet_main_t * vnm = vnet_get_main();
- uword * nh_hash, * nh_result;
- int is_interface_next_hop;
- u32 nh_adj_index;
- ip4_fib_t * fib;
-
- fib = vec_elt_at_index (im->fibs, fib_index);
-
- is_interface_next_hop = next_hop->data_u32 == 0;
- if (is_interface_next_hop)
- {
- nh_result = hash_get (im->interface_route_adj_index_by_sw_if_index, next_hop_sw_if_index);
- if (nh_result)
- nh_adj_index = *nh_result;
- else
- {
- ip_adjacency_t * adj;
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &nh_adj_index);
- ip4_adjacency_set_interface_route (vnm, adj, next_hop_sw_if_index, /* if_address_index */ ~0);
- ip_call_add_del_adjacency_callbacks (lm, nh_adj_index, /* is_del */ 0);
- hash_set (im->interface_route_adj_index_by_sw_if_index, next_hop_sw_if_index, nh_adj_index);
- }
- }
- else if (next_hop_sw_if_index == ~0)
- {
- /* next-hop is recursive. we always need a indirect adj
- * for recursive paths. Any LPM we perform now will give
- * us a valid adj, but without tracking the next-hop we
- * have no way to keep it valid.
- */
- ip_adjacency_t add_adj;
- memset (&add_adj, 0, sizeof(add_adj));
- add_adj.n_adj = 1;
- add_adj.lookup_next_index = IP_LOOKUP_NEXT_INDIRECT;
- add_adj.indirect.next_hop.ip4.as_u32 = next_hop->as_u32;
- add_adj.explicit_fib_index = explicit_fib_index;
- ip_add_adjacency (lm, &add_adj, 1, &nh_adj_index);
- }
- else
- {
- nh_hash = fib->adj_index_by_dst_address[32];
- nh_result = hash_get (nh_hash, next_hop->data_u32);
-
- /* Next hop must be known. */
- if (! nh_result)
- {
- ip_adjacency_t * adj;
-
- /* no /32 exists, get the longest prefix match */
- nh_adj_index = ip4_fib_lookup_with_table (im, fib_index,
- next_hop, 0);
- adj = ip_get_adjacency (lm, nh_adj_index);
- /* if ARP interface adjacency is present, we need to
- install ARP adjaceny for specific next hop */
- if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
- adj->arp.next_hop.ip4.as_u32 == 0)
- {
- nh_adj_index = vnet_arp_glean_add(fib_index, next_hop);
- }
- }
- else
- {
- nh_adj_index = *nh_result;
- }
- }
-
- return (nh_adj_index);
-}
-
-void
-ip4_add_del_route_next_hop (ip4_main_t * im,
- u32 flags,
- ip4_address_t * dst_address,
- u32 dst_address_length,
- ip4_address_t * next_hop,
- u32 next_hop_sw_if_index,
- u32 next_hop_weight, u32 adj_index,
- u32 explicit_fib_index)
-{
- vnet_main_t * vnm = vnet_get_main();
- ip_lookup_main_t * lm = &im->lookup_main;
- u32 fib_index;
- ip4_fib_t * fib;
- u32 dst_address_u32, old_mp_adj_index, new_mp_adj_index;
- u32 dst_adj_index, nh_adj_index;
- uword * dst_hash, * dst_result;
- ip_adjacency_t * dst_adj;
- ip_multipath_adjacency_t * old_mp, * new_mp;
- int is_del = (flags & IP4_ROUTE_FLAG_DEL) != 0;
- clib_error_t * error = 0;
-
- if (explicit_fib_index == (u32)~0)
- fib_index = vec_elt (im->fib_index_by_sw_if_index, next_hop_sw_if_index);
- else
- fib_index = explicit_fib_index;
-
- fib = vec_elt_at_index (im->fibs, fib_index);
-
- /* Lookup next hop to be added or deleted. */
- if (adj_index == (u32)~0)
- {
- nh_adj_index = ip4_route_get_next_hop_adj(im, fib_index,
- next_hop,
- next_hop_sw_if_index,
- explicit_fib_index);
- }
- else
- {
- nh_adj_index = adj_index;
- }
- ASSERT (dst_address_length < ARRAY_LEN (im->fib_masks));
- dst_address_u32 = dst_address->data_u32 & im->fib_masks[dst_address_length];
-
- dst_hash = fib->adj_index_by_dst_address[dst_address_length];
- dst_result = hash_get (dst_hash, dst_address_u32);
- if (dst_result)
- {
- dst_adj_index = dst_result[0];
- dst_adj = ip_get_adjacency (lm, dst_adj_index);
- }
- else
- {
- /* For deletes destination must be known. */
- if (is_del)
- {
- vnm->api_errno = VNET_API_ERROR_UNKNOWN_DESTINATION;
- error = clib_error_return (0, "unknown destination %U/%d",
- format_ip4_address, dst_address,
- dst_address_length);
- goto done;
- }
-
- dst_adj_index = ~0;
- dst_adj = 0;
- }
-
- /* Ignore adds of X/32 with next hop of X. */
- if (! is_del
- && dst_address_length == 32
- && dst_address->data_u32 == next_hop->data_u32
- && adj_index != (u32)~0)
- {
- vnm->api_errno = VNET_API_ERROR_PREFIX_MATCHES_NEXT_HOP;
- error = clib_error_return (0, "prefix matches next hop %U/%d",
- format_ip4_address, dst_address,
- dst_address_length);
- goto done;
- }
-
- /* Destination is not known and default weight is set so add route
- to existing non-multipath adjacency */
- if (dst_adj_index == ~0 && next_hop_weight == 1 && next_hop_sw_if_index == ~0)
- {
- /* create / delete additional mapping of existing adjacency */
- ip4_add_del_route_args_t a;
-
- a.table_index_or_table_id = fib_index;
- a.flags = ((is_del ? IP4_ROUTE_FLAG_DEL : IP4_ROUTE_FLAG_ADD)
- | IP4_ROUTE_FLAG_FIB_INDEX
- | IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY
- | (flags & (IP4_ROUTE_FLAG_NO_REDISTRIBUTE
- | IP4_ROUTE_FLAG_NOT_LAST_IN_GROUP)));
- a.dst_address = dst_address[0];
- a.dst_address_length = dst_address_length;
- a.adj_index = nh_adj_index;
- a.add_adj = 0;
- a.n_add_adj = 0;
-
- ip4_add_del_route (im, &a);
- goto done;
- }
-
- old_mp_adj_index = dst_adj ? dst_adj->heap_handle : ~0;
-
- if (! ip_multipath_adjacency_add_del_next_hop
- (lm, is_del,
- old_mp_adj_index,
- nh_adj_index,
- next_hop_weight,
- &new_mp_adj_index))
- {
- vnm->api_errno = VNET_API_ERROR_NEXT_HOP_NOT_FOUND_MP;
- error = clib_error_return (0, "requested deleting next-hop %U not found in multi-path",
- format_ip4_address, next_hop);
- goto done;
- }
-
- old_mp = new_mp = 0;
- if (old_mp_adj_index != ~0)
- old_mp = vec_elt_at_index (lm->multipath_adjacencies, old_mp_adj_index);
- if (new_mp_adj_index != ~0)
- new_mp = vec_elt_at_index (lm->multipath_adjacencies, new_mp_adj_index);
-
- if (old_mp != new_mp)
- {
- ip4_add_del_route_args_t a;
- ip_adjacency_t * adj;
-
- a.table_index_or_table_id = fib_index;
- a.flags = ((is_del && ! new_mp ? IP4_ROUTE_FLAG_DEL : IP4_ROUTE_FLAG_ADD)
- | IP4_ROUTE_FLAG_FIB_INDEX
- | IP4_ROUTE_FLAG_KEEP_OLD_ADJACENCY
- | (flags & (IP4_ROUTE_FLAG_NO_REDISTRIBUTE | IP4_ROUTE_FLAG_NOT_LAST_IN_GROUP)));
- a.dst_address = dst_address[0];
- a.dst_address_length = dst_address_length;
- a.adj_index = new_mp ? new_mp->adj_index : dst_adj_index;
- a.add_adj = 0;
- a.n_add_adj = 0;
-
- ip4_add_del_route (im, &a);
-
- adj = ip_get_adjacency (lm, new_mp ? new_mp->adj_index : dst_adj_index);
- if (adj->n_adj == 1)
- adj->share_count += is_del ? -1 : 1;
- }
-
- done:
- if (error)
- clib_error_report (error);
-}
-
-void *
-ip4_get_route (ip4_main_t * im,
- u32 table_index_or_table_id,
- u32 flags,
- u8 * address,
- u32 address_length)
-{
- ip4_fib_t * fib = find_ip4_fib_by_table_index_or_id (im, table_index_or_table_id, flags);
- u32 dst_address = * (u32 *) address;
- uword * hash, * p;
-
- ASSERT (address_length < ARRAY_LEN (im->fib_masks));
- dst_address &= im->fib_masks[address_length];
-
- hash = fib->adj_index_by_dst_address[address_length];
- p = hash_get (hash, dst_address);
- return (void *) p;
-}
-
-void
-ip4_foreach_matching_route (ip4_main_t * im,
- u32 table_index_or_table_id,
- u32 flags,
- ip4_address_t * address,
- u32 address_length,
- ip4_address_t ** results,
- u8 ** result_lengths)
-{
- ip4_fib_t * fib = find_ip4_fib_by_table_index_or_id (im, table_index_or_table_id, flags);
- u32 dst_address = address->data_u32;
- u32 this_length = address_length;
-
- if (*results)
- _vec_len (*results) = 0;
- if (*result_lengths)
- _vec_len (*result_lengths) = 0;
-
- while (this_length <= 32 && vec_len (results) == 0)
- {
- uword k, v;
- hash_foreach (k, v, fib->adj_index_by_dst_address[this_length], ({
- if (0 == ((k ^ dst_address) & im->fib_masks[address_length]))
- {
- ip4_address_t a;
- a.data_u32 = k;
- vec_add1 (*results, a);
- vec_add1 (*result_lengths, this_length);
- }
- }));
-
- this_length++;
- }
-}
-
-void ip4_maybe_remap_adjacencies (ip4_main_t * im,
- u32 table_index_or_table_id,
- u32 flags)
-{
- ip4_fib_t * fib = find_ip4_fib_by_table_index_or_id (im, table_index_or_table_id, flags);
- ip_lookup_main_t * lm = &im->lookup_main;
- u32 i, l;
- ip4_address_t a;
- ip4_add_del_route_callback_t * cb;
- static ip4_address_t * to_delete;
-
- if (lm->n_adjacency_remaps == 0)
- return;
-
- for (l = 0; l <= 32; l++)
- {
- hash_pair_t * p;
- uword * hash = fib->adj_index_by_dst_address[l];
-
- if (hash_elts (hash) == 0)
- continue;
-
- if (to_delete)
- _vec_len (to_delete) = 0;
-
- hash_foreach_pair (p, hash, ({
- u32 adj_index = p->value[0];
- u32 m = vec_elt (lm->adjacency_remap_table, adj_index);
-
- if (m)
- {
- /* Record destination address from hash key. */
- a.data_u32 = p->key;
-
- /* New adjacency points to nothing: so delete prefix. */
- if (m == ~0)
- vec_add1 (to_delete, a);
- else
- {
- /* Remap to new adjacency. */
- clib_memcpy (fib->old_hash_values, p->value, vec_bytes (fib->old_hash_values));
-
- /* Set new adjacency value. */
- fib->new_hash_values[0] = p->value[0] = m - 1;
-
- vec_foreach (cb, im->add_del_route_callbacks)
- if ((flags & cb->required_flags) == cb->required_flags)
- cb->function (im, cb->function_opaque,
- fib, flags | IP4_ROUTE_FLAG_ADD,
- &a, l,
- fib->old_hash_values,
- fib->new_hash_values);
- }
- }
- }));
-
- fib->new_hash_values[0] = ~0;
- for (i = 0; i < vec_len (to_delete); i++)
- {
- hash = _hash_unset (hash, to_delete[i].data_u32, fib->old_hash_values);
- vec_foreach (cb, im->add_del_route_callbacks)
- if ((flags & cb->required_flags) == cb->required_flags)
- cb->function (im, cb->function_opaque,
- fib, flags | IP4_ROUTE_FLAG_DEL,
- &a, l,
- fib->old_hash_values,
- fib->new_hash_values);
- }
- }
-
- /* Also remap adjacencies in mtrie. */
- ip4_mtrie_maybe_remap_adjacencies (lm, &fib->mtrie);
-
- /* Reset mapping table. */
- vec_zero (lm->adjacency_remap_table);
-
- /* All remaps have been performed. */
- lm->n_adjacency_remaps = 0;
-}
-
-void ip4_delete_matching_routes (ip4_main_t * im,
- u32 table_index_or_table_id,
- u32 flags,
- ip4_address_t * address,
- u32 address_length)
-{
- static ip4_address_t * matching_addresses;
- static u8 * matching_address_lengths;
- u32 l, i;
- ip4_add_del_route_args_t a;
-
- a.flags = IP4_ROUTE_FLAG_DEL | IP4_ROUTE_FLAG_NO_REDISTRIBUTE | flags;
- a.table_index_or_table_id = table_index_or_table_id;
- a.adj_index = ~0;
- a.add_adj = 0;
- a.n_add_adj = 0;
-
- for (l = address_length + 1; l <= 32; l++)
- {
- ip4_foreach_matching_route (im, table_index_or_table_id, flags,
- address,
- l,
- &matching_addresses,
- &matching_address_lengths);
- for (i = 0; i < vec_len (matching_addresses); i++)
- {
- a.dst_address = matching_addresses[i];
- a.dst_address_length = matching_address_lengths[i];
- ip4_add_del_route (im, &a);
- }
- }
-
- ip4_maybe_remap_adjacencies (im, table_index_or_table_id, flags);
-}