-static void compute_prefix_lengths_in_search_order (ip6_main_t * im)
-{
- int i;
- vec_reset_length (im->prefix_lengths_in_search_order);
- /* Note: bitmap reversed so this is in fact a longest prefix match */
- clib_bitmap_foreach (i, im->non_empty_dst_address_length_bitmap,
- ({
- int dst_address_length = 128 - i;
- vec_add1 (im->prefix_lengths_in_search_order, dst_address_length);
- }));
-}
-
-u32
-ip6_fib_lookup_with_table (ip6_main_t * im, u32 fib_index, ip6_address_t * dst)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- int i, len;
- int rv;
- BVT(clib_bihash_kv) kv, value;
- u64 fib;
-
- len = vec_len (im->prefix_lengths_in_search_order);
-
- kv.key[0] = dst->as_u64[0];
- kv.key[1] = dst->as_u64[1];
- fib = ((u64)((fib_index))<<32);
-
- for (i = 0; i < len; i++)
- {
- int dst_address_length = im->prefix_lengths_in_search_order[i];
- ip6_address_t * mask = &im->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 = BV(clib_bihash_search_inline_2)(&im->ip6_lookup_table, &kv, &value);
- if (rv == 0)
- return value.value;
- }
-
- return lm->miss_adj_index;
-}
-
-u32 ip6_fib_lookup (ip6_main_t * im, u32 sw_if_index, ip6_address_t * dst)
-{
- u32 fib_index = vec_elt (im->fib_index_by_sw_if_index, sw_if_index);
- return ip6_fib_lookup_with_table (im, fib_index, dst);
-}
-
-void
-vnet_ip6_fib_init (ip6_main_t * im, u32 fib_index)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- ip6_add_del_route_args_t a;
- ip_adjacency_t * adj;
-
- memset(&a, 0x0, sizeof(ip6_add_del_route_args_t));
-
- a.table_index_or_table_id = fib_index;
- a.flags = (IP6_ROUTE_FLAG_ADD
- | IP6_ROUTE_FLAG_FIB_INDEX
- | IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY
- | IP6_ROUTE_FLAG_NO_REDISTRIBUTE);
-
- /* Add ff02::1:ff00:0/104 via local route for all tables.
- This is required for neighbor discovery to work. */
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a.adj_index);
- adj->lookup_next_index = IP_LOOKUP_NEXT_LOCAL;
- adj->if_address_index = ~0;
- adj->rewrite_header.data_bytes = 0;
-
- ip6_set_solicited_node_multicast_address (&a.dst_address, 0);
-
- a.dst_address_length = 104;
- ip6_add_del_route (im, &a);
-
- /* Add all-routers multicast address via local route for all tables */
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a.adj_index);
- adj->lookup_next_index = IP_LOOKUP_NEXT_LOCAL;
- adj->if_address_index = ~0;
- adj->rewrite_header.data_bytes = 0;
-
- ip6_set_reserved_multicast_address (&a.dst_address,
- IP6_MULTICAST_SCOPE_link_local,
- IP6_MULTICAST_GROUP_ID_all_routers);
-
- a.dst_address_length = 128;
- ip6_add_del_route (im, &a);
-
- /* Add all-nodes multicast address via local route for all tables */
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a.adj_index);
- adj->lookup_next_index = IP_LOOKUP_NEXT_LOCAL;
- adj->if_address_index = ~0;
- adj->rewrite_header.data_bytes = 0;
-
- ip6_set_reserved_multicast_address (&a.dst_address,
- IP6_MULTICAST_SCOPE_link_local,
- IP6_MULTICAST_GROUP_ID_all_hosts);
-
- a.dst_address_length = 128;
- ip6_add_del_route (im, &a);
-
- /* Add all-mldv2 multicast address via local route for all tables */
- adj = ip_add_adjacency (lm, /* template */ 0, /* block size */ 1,
- &a.adj_index);
- adj->lookup_next_index = IP_LOOKUP_NEXT_LOCAL;
- adj->if_address_index = ~0;
- adj->rewrite_header.data_bytes = 0;
-
- ip6_set_reserved_multicast_address (&a.dst_address,
- IP6_MULTICAST_SCOPE_link_local,
- IP6_MULTICAST_GROUP_ID_mldv2_routers);
-
- a.dst_address_length = 128;
- ip6_add_del_route (im, &a);
-}
-
-static ip6_fib_t *
-create_fib_with_table_id (ip6_main_t * im, u32 table_id)
-{
- ip6_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;
- fib->flow_hash_config = IP_FLOW_HASH_DEFAULT;
- vnet_ip6_fib_init (im, fib->index);
- return fib;
-}
-
-ip6_fib_t *
-find_ip6_fib_by_table_index_or_id (ip6_main_t * im, u32 table_index_or_id, u32 flags)
-{
- uword * p, fib_index;
-
- fib_index = table_index_or_id;
- if (! (flags & IP6_ROUTE_FLAG_FIB_INDEX))
- {
- if (table_index_or_id == ~0) {
- table_index_or_id = 0;
- while (hash_get (im->fib_index_by_table_id, table_index_or_id)) {
- table_index_or_id++;
- }
- return create_fib_with_table_id (im, table_index_or_id);
- }
-
- p = hash_get (im->fib_index_by_table_id, table_index_or_id);
- if (! p)
- return create_fib_with_table_id (im, table_index_or_id);
- fib_index = p[0];
- }
- return vec_elt_at_index (im->fibs, fib_index);
-}
-
-void ip6_add_del_route (ip6_main_t * im, ip6_add_del_route_args_t * a)
-{
- ip_lookup_main_t * lm = &im->lookup_main;
- ip6_fib_t * fib;
- ip6_address_t dst_address;
- u32 dst_address_length, adj_index;
- uword is_del;
- u32 old_adj_index = ~0;
- BVT(clib_bihash_kv) kv, value;
-
- vlib_smp_unsafe_warning();
-
- is_del = (a->flags & IP6_ROUTE_FLAG_DEL) != 0;
-
- /* 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;
- dst_address_length = a->dst_address_length;
- fib = find_ip6_fib_by_table_index_or_id (im, a->table_index_or_table_id,
- a->flags);
-
- ASSERT (dst_address_length < ARRAY_LEN (im->fib_masks));
- ip6_address_mask (&dst_address, &im->fib_masks[dst_address_length]);
-
- /* refcount accounting */
- if (is_del)
- {
- ASSERT (im->dst_address_length_refcounts[dst_address_length] > 0);
- if (--im->dst_address_length_refcounts[dst_address_length] == 0)
- {
- im->non_empty_dst_address_length_bitmap =
- clib_bitmap_set (im->non_empty_dst_address_length_bitmap,
- 128 - dst_address_length, 0);
- compute_prefix_lengths_in_search_order (im);
- }
- }
- else
- {
- im->dst_address_length_refcounts[dst_address_length]++;
-
- im->non_empty_dst_address_length_bitmap =
- clib_bitmap_set (im->non_empty_dst_address_length_bitmap,
- 128 - dst_address_length, 1);
- compute_prefix_lengths_in_search_order (im);
- }
-
- kv.key[0] = dst_address.as_u64[0];
- kv.key[1] = dst_address.as_u64[1];
- kv.key[2] = ((u64)((fib - im->fibs))<<32) | dst_address_length;
-
- if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) == 0)
- old_adj_index = value.value;
-
- if (is_del)
- BV(clib_bihash_add_del) (&im->ip6_lookup_table, &kv, 0 /* is_add */);
- else
- {
- /* Make sure adj index is valid. */
- if (CLIB_DEBUG > 0)
- (void) ip_get_adjacency (lm, adj_index);
-
- kv.value = adj_index;
-
- BV(clib_bihash_add_del) (&im->ip6_lookup_table, &kv, 1 /* is_add */);
- }
-
- /* Avoid spurious reference count increments */
- if (old_adj_index == adj_index
- && adj_index != ~0
- && !(a->flags & IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY))
- {
- ip_adjacency_t * adj = ip_get_adjacency (lm, adj_index);
- if (adj->share_count > 0)
- adj->share_count --;
- }
-
- /* Delete old adjacency index if present and changed. */
- {
- if (! (a->flags & IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY)
- && old_adj_index != ~0
- && old_adj_index != adj_index)
- ip_del_adjacency (lm, old_adj_index);
- }
-}
-
-void
-ip6_add_del_route_next_hop (ip6_main_t * im,
- u32 flags,
- ip6_address_t * dst_address,
- u32 dst_address_length,
- ip6_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;
- ip6_fib_t * fib;
- ip6_address_t masked_dst_address;
- u32 old_mp_adj_index, new_mp_adj_index;
- u32 dst_adj_index, nh_adj_index;
- int rv;
- ip_adjacency_t * dst_adj;
- ip_multipath_adjacency_t * old_mp, * new_mp;
- int is_del = (flags & IP6_ROUTE_FLAG_DEL) != 0;
- int is_interface_next_hop;
- clib_error_t * error = 0;
- uword * nh_result;
- BVT(clib_bihash_kv) kv, value;
-
- vlib_smp_unsafe_warning();
-
- 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. */
- is_interface_next_hop = ip6_address_is_zero (next_hop);
- if (adj_index == (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);
- ip6_adjacency_set_interface_route (vnm, adj,
- next_hop_sw_if_index, ~0);
- ip_call_add_del_adjacency_callbacks
- (lm, next_hop_sw_if_index, /* is_del */ 0);
- hash_set (im->interface_route_adj_index_by_sw_if_index,
- next_hop_sw_if_index, nh_adj_index);
- }
- }
- else
- {
- /* Look for the interface /128 route */
- kv.key[0] = next_hop->as_u64[0];
- kv.key[1] = next_hop->as_u64[1];
- kv.key[2] = ((u64)((fib - im->fibs))<<32) | 128;
-
- if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) < 0)
- {
- ip_adjacency_t * adj;
- nh_adj_index = ip6_fib_lookup_with_table (im, fib_index, next_hop);
- adj = ip_get_adjacency (lm, nh_adj_index);
- /* if ND interface adjacencty is present, we need to
- install ND adjaceny for specific next hop */
- if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP &&
- adj->arp.next_hop.ip6.as_u64[0] == 0 &&
- adj->arp.next_hop.ip6.as_u64[1] == 0)
- {
- nh_adj_index = vnet_ip6_neighbor_glean_add(fib_index, next_hop);
- }
- else
- {
- ip_adjacency_t add_adj;
- add_adj.lookup_next_index = IP_LOOKUP_NEXT_INDIRECT;
- add_adj.indirect.next_hop.ip6.as_u64[0] = next_hop->as_u64[0];
- add_adj.indirect.next_hop.ip6.as_u64[1] = next_hop->as_u64[1];
- add_adj.explicit_fib_index = explicit_fib_index;
- ip_add_adjacency (lm, &add_adj, 1, &nh_adj_index);
- }
- }
- else
- nh_adj_index = value.value;
-
- }
- }
- else
- {
- /* Look for the interface /128 route */
- kv.key[0] = next_hop->as_u64[0];
- kv.key[1] = next_hop->as_u64[1];
- kv.key[2] = ((u64)((fib - im->fibs))<<32) | 128;
-
- if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) < 0)
- {
- vnm->api_errno = VNET_API_ERROR_UNKNOWN_DESTINATION;
- error = clib_error_return (0, "next-hop %U/128 not in FIB",
- format_ip6_address, next_hop);
- goto done;
- }
-
- nh_adj_index = value.value;
- }
-
- ASSERT (dst_address_length < ARRAY_LEN (im->fib_masks));
- masked_dst_address = dst_address[0];
- ip6_address_mask (&masked_dst_address, &im->fib_masks[dst_address_length]);
-
- kv.key[0] = masked_dst_address.as_u64[0];
- kv.key[1] = masked_dst_address.as_u64[1];
- kv.key[2] = ((u64)((fib - im->fibs))<<32) | dst_address_length;
-
- rv = BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value);
-
- if (rv == 0)
- {
- dst_adj_index = value.value;
- 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_ip6_address, dst_address,
- dst_address_length);
- goto done;
- }
-
- dst_adj_index = ~0;
- dst_adj = 0;
- }
-
- /* Ignore adds of X/128 with next hop of X. */
- if (! is_del
- && dst_address_length == 128
- && ip6_address_is_equal (dst_address, next_hop))
- {
- vnm->api_errno = VNET_API_ERROR_PREFIX_MATCHES_NEXT_HOP;
- error = clib_error_return (0, "prefix matches next hop %U/%d",
- format_ip6_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 new adjacency */
- ip6_add_del_route_args_t a;
- a.table_index_or_table_id = fib_index;
- a.flags = ((is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD)
- | IP6_ROUTE_FLAG_FIB_INDEX
- | IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY
- | (flags & (IP6_ROUTE_FLAG_NO_REDISTRIBUTE
- | IP6_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;
-
- ip6_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,
- dst_adj ? dst_adj->heap_handle : ~0,
- 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_ip6_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)
- {
- ip6_add_del_route_args_t a;
- a.table_index_or_table_id = fib_index;
- a.flags = ((is_del ? IP6_ROUTE_FLAG_DEL : IP6_ROUTE_FLAG_ADD)
- | IP6_ROUTE_FLAG_FIB_INDEX
- | IP6_ROUTE_FLAG_KEEP_OLD_ADJACENCY
- | (flags & IP6_ROUTE_FLAG_NO_REDISTRIBUTE));
- 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;
-
- ip6_add_del_route (im, &a);
- }
-
- done:
- if (error)
- clib_error_report (error);
-}
-
-u32
-ip6_get_route (ip6_main_t * im,
- u32 table_index_or_table_id,
- u32 flags,
- ip6_address_t * address,
- u32 address_length)
-{
- ip6_fib_t * fib = find_ip6_fib_by_table_index_or_id (im, table_index_or_table_id, flags);
- ip6_address_t masked_address;
- BVT(clib_bihash_kv) kv, value;
-
- ASSERT (address_length < ARRAY_LEN (im->fib_masks));
- clib_memcpy (&masked_address, address, sizeof (masked_address));
- ip6_address_mask (&masked_address, &im->fib_masks[address_length]);
-
- kv.key[0] = masked_address.as_u64[0];
- kv.key[1] = masked_address.as_u64[1];
- kv.key[2] = ((u64)((fib - im->fibs))<<32) | address_length;
-
- if (BV(clib_bihash_search)(&im->ip6_lookup_table, &kv, &value) == 0)
- return (value.value);
- return 0;
-}
-
-void
-ip6_foreach_matching_route (ip6_main_t * im,
- u32 table_index_or_table_id,
- u32 flags,
- ip6_address_t * dst_address,
- u32 address_length,
- ip6_address_t ** results,
- u8 ** result_lengths)
-{
- ip6_fib_t * fib =
- find_ip6_fib_by_table_index_or_id (im, table_index_or_table_id, flags);
- BVT(clib_bihash) * h = &im->ip6_lookup_table;
- BVT(clib_bihash_value) * v;
- clib_bihash_bucket_t * b;
- int i, j, k;
-
- if (*results)
- _vec_len (*results) = 0;
- if (*result_lengths)
- _vec_len (*result_lengths) = 0;
-
- /* Walk the table looking for routes which match the supplied address */
- for (i = 0; i < h->nbuckets; i++)
- {
- b = &h->buckets [i];
- if (b->offset == 0)
- continue;
-
- v = BV(clib_bihash_get_value) (h, b->offset);
- for (j = 0; j < (1<<b->log2_pages); j++)
- {
- for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
- {
- if (BV(clib_bihash_is_free)(&v->kvp[k]))
- continue;
-
- if ((v->kvp[k].key[2]
- == (((u64)((fib - im->fibs))<<32) | address_length))
- && ip6_destination_matches_route
- (im, dst_address, (ip6_address_t *) &v->kvp[k],
- address_length))
- {
- ip6_address_t * a;
-
- a = (ip6_address_t *)(&v->kvp[k]);
-
- vec_add1 (*results, a[0]);
- vec_add1 (*result_lengths, address_length);
- }
- }
- v++;
- }
- }
-}
-
-void ip6_maybe_remap_adjacencies (ip6_main_t * im,
- u32 table_index_or_table_id,
- u32 flags)
-{
-#if SOONE
- ip6_fib_t * fib
- = find_ip6_fib_by_table_index_or_id (im, table_index_or_table_id, flags);
-#endif
- ip_lookup_main_t * lm = &im->lookup_main;
-
- if (lm->n_adjacency_remaps == 0)
- return;
-
- clib_warning ("unimplemented, please report to vpp-dev@cisco.com");
-
- /* All remaps have been performed. */
- lm->n_adjacency_remaps = 0;
-}
-
-void ip6_delete_matching_routes (ip6_main_t * im,
- u32 table_index_or_table_id,
- u32 flags,
- ip6_address_t * address,
- u32 address_length)
-{
- /* $$$$ static may be OK - this should happen only on thread 0 */
- static ip6_address_t * matching_addresses;
- static u8 * matching_address_lengths;
- u32 l, i;
- ip6_add_del_route_args_t a;
-
- vlib_smp_unsafe_warning();
-
- a.flags = IP6_ROUTE_FLAG_DEL | IP6_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 <= 128; l++)
- {
- ip6_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];
- ip6_add_del_route (im, &a);
- }
- }
-
- ip6_maybe_remap_adjacencies (im, table_index_or_table_id, flags);
-}
-