diff options
Diffstat (limited to 'src/vnet/ip/ip_interface.c')
-rw-r--r-- | src/vnet/ip/ip_interface.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/src/vnet/ip/ip_interface.c b/src/vnet/ip/ip_interface.c new file mode 100644 index 00000000000..23c3df81638 --- /dev/null +++ b/src/vnet/ip/ip_interface.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2020 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <vnet/ip/ip.h> + +/** + * @file + * @brief IP prefix management on interfaces + */ + +u32 +ip_interface_address_find (ip_lookup_main_t * lm, + void *addr_fib, u32 address_length) +{ + uword *p = mhash_get (&lm->address_to_if_address_index, addr_fib); + + if (p) + return (p[0]); + + return (~0); +} + +clib_error_t * +ip_interface_address_add (ip_lookup_main_t * lm, + u32 sw_if_index, + void *addr_fib, + u32 address_length, u32 * result_if_address_index) +{ + vnet_main_t *vnm = vnet_get_main (); + ip_interface_address_t *a, *prev; + u32 pi; /* previous index */ + u32 ai; + u32 hi; /* head index */ + + /* Verify given length. */ + if ((address_length == 0) || + (lm->is_ip6 && address_length > 128) || + (!lm->is_ip6 && address_length > 32)) + { + vnm->api_errno = VNET_API_ERROR_ADDRESS_LENGTH_MISMATCH; + return clib_error_create + ("%U wrong length for interface %U", + lm->format_address_and_length, addr_fib, + address_length, format_vnet_sw_if_index_name, vnm, sw_if_index); + } + + vec_validate_init_empty (lm->if_address_pool_index_by_sw_if_index, + sw_if_index, ~0); + + pool_get_zero (lm->if_address_pool, a); + + 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; + + *result_if_address_index = ai; + + return (NULL); +} + +void +ip_interface_address_del (ip_lookup_main_t * lm, + u32 address_index, void *addr_fib) +{ + ip_interface_address_t *a, *prev, *next; + + a = pool_elt_at_index (lm->if_address_pool, address_index); + + 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[a->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[a->sw_if_index] = ~0; + + mhash_unset (&lm->address_to_if_address_index, addr_fib, + /* old_value */ 0); + pool_put (lm->if_address_pool, a); +} + +u8 +ip_interface_has_address (u32 sw_if_index, ip46_address_t * ip, u8 is_ip4) +{ + ip_interface_address_t *ia = 0; + + if (is_ip4) + { + ip_lookup_main_t *lm4 = &ip4_main.lookup_main; + ip4_address_t *ip4; + /* *INDENT-OFF* */ + foreach_ip_interface_address (lm4, ia, sw_if_index, 1 /* unnumbered */ , + ({ + ip4 = ip_interface_address_get_address (lm4, ia); + if (ip4_address_compare (ip4, &ip->ip4) == 0) + return 1; + })); + /* *INDENT-ON* */ + } + else + { + ip_lookup_main_t *lm6 = &ip6_main.lookup_main; + ip6_address_t *ip6; + /* *INDENT-OFF* */ + foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ , + ({ + ip6 = ip_interface_address_get_address (lm6, ia); + if (ip6_address_compare (ip6, &ip->ip6) == 0) + return 1; + })); + /* *INDENT-ON* */ + } + return 0; +} + +void * +ip_interface_get_first_ip (u32 sw_if_index, u8 is_ip4) +{ + ip_lookup_main_t *lm4 = &ip4_main.lookup_main; + ip_lookup_main_t *lm6 = &ip6_main.lookup_main; + ip_interface_address_t *ia = 0; + + if (is_ip4) + { + /* *INDENT-OFF* */ + foreach_ip_interface_address (lm4, ia, sw_if_index, 1 /* unnumbered */ , + ({ + return ip_interface_address_get_address (lm4, ia); + })); + /* *INDENT-ON* */ + } + else + { + /* *INDENT-OFF* */ + foreach_ip_interface_address (lm6, ia, sw_if_index, 1 /* unnumbered */ , + ({ + ip6_address_t *rv; + rv = ip_interface_address_get_address (lm6, ia); + /* Trying to use a link-local ip6 src address is a fool's errand */ + if (!ip6_address_is_link_local_unicast (rv)) + return rv; + })); + /* *INDENT-ON* */ + } + + return 0; +} + +static walk_rc_t +ip_interface_address_mark_one_interface (vnet_main_t * vnm, + vnet_sw_interface_t * si, void *ctx) +{ + ip_lookup_main_t *lm4 = &ip4_main.lookup_main; + ip_lookup_main_t *lm6 = &ip6_main.lookup_main; + ip_interface_address_t *ia = 0; + + /* *INDENT-OFF* */ + foreach_ip_interface_address (lm4, ia, si->sw_if_index, 1 /* unnumbered */ , + ({ + ia->flags |= IP_INTERFACE_ADDRESS_FLAG_STALE; + })); + foreach_ip_interface_address (lm6, ia, si->sw_if_index, 1 /* unnumbered */ , + ({ + ia->flags |= IP_INTERFACE_ADDRESS_FLAG_STALE; + })); + /* *INDENT-ON* */ + + return (WALK_CONTINUE); +} + +void +ip_interface_address_mark (void) +{ + vnet_sw_interface_walk (vnet_get_main (), + ip_interface_address_mark_one_interface, NULL); +} + +static walk_rc_t +ip_interface_address_sweep_one_interface (vnet_main_t * vnm, + vnet_sw_interface_t * si, void *ctx) +{ + vlib_main_t *vm = vlib_get_main (); + ip4_address_t *ip4_addrs = 0; + ip6_address_t *ip6_addrs = 0; + ip4_main_t *im4 = &ip4_main; + ip6_main_t *im6 = &ip6_main; + ip_interface_address_t *ia; + u32 *ip6_masks = 0; + u32 *ip4_masks = 0; + int i; + + /* *INDENT-OFF* */ + foreach_ip_interface_address (&im4->lookup_main, ia, si->sw_if_index, 1, + ({ + if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE) + { + ip4_address_t * x = (ip4_address_t *) + ip_interface_address_get_address (&im4->lookup_main, ia); + vec_add1 (ip4_addrs, x[0]); + vec_add1 (ip4_masks, ia->address_length); + } + })); + + foreach_ip_interface_address (&im6->lookup_main, ia, si->sw_if_index, 1, + ({ + if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE) + { + ip6_address_t * x = (ip6_address_t *) + ip_interface_address_get_address (&im6->lookup_main, ia); + vec_add1 (ip6_addrs, x[0]); + vec_add1 (ip6_masks, ia->address_length); + } + })); + /* *INDENT-ON* */ + + for (i = 0; i < vec_len (ip4_addrs); i++) + ip4_add_del_interface_address (vm, si->sw_if_index, &ip4_addrs[i], + ip4_masks[i], 1 /* is_del */ ); + for (i = 0; i < vec_len (ip6_addrs); i++) + ip6_add_del_interface_address (vm, si->sw_if_index, &ip6_addrs[i], + ip6_masks[i], 1 /* is_del */ ); + + vec_free (ip4_addrs); + vec_free (ip4_masks); + vec_free (ip6_addrs); + vec_free (ip6_masks); + + return (WALK_CONTINUE); +} + +void +ip_interface_address_sweep (void) +{ + vnet_sw_interface_walk (vnet_get_main (), + ip_interface_address_sweep_one_interface, NULL); +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ |