aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/ip/ip6_forward.c
diff options
context:
space:
mode:
authorMatthew Smith <mgsmith@netgate.com>2019-08-07 11:46:30 -0500
committerNeale Ranns <nranns@cisco.com>2019-08-12 16:50:54 +0000
commit6c92f5babdc3c52cf343509fc9cf9d8a9a3df390 (patch)
tree969249e9836e232929949492f3f45ac5c1deba35 /src/vnet/ip/ip6_forward.c
parent79c04d622a55b75de969bf010cdcb820ccfbe816 (diff)
ip: allow addrs from the same prefix on intf
Type: feature Adding a prefix to an interface was not permitted if it overlapped with another prefix on an interface which used the same FIB. Loosen the restriction. Allow 2 or more addresses from the same prefix on a single interface. Reference count the prefix to figure out when a glean/connected route for the prefix needs to be added or removed. Added unit tests to check that the route is only removed when all addresses in the prefix are removed from the interface. Change-Id: I1a962ecb5e1ee65fc6d41f98a4cc097a51a55321 Signed-off-by: Matthew Smith <mgsmith@netgate.com>
Diffstat (limited to 'src/vnet/ip/ip6_forward.c')
-rw-r--r--src/vnet/ip/ip6_forward.c151
1 files changed, 129 insertions, 22 deletions
diff --git a/src/vnet/ip/ip6_forward.c b/src/vnet/ip/ip6_forward.c
index b990d7c45d2..fbdf0b9b5a0 100644
--- a/src/vnet/ip/ip6_forward.c
+++ b/src/vnet/ip/ip6_forward.c
@@ -60,22 +60,53 @@
#define OI_DECAP 0x80000000
static void
-ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index,
- ip6_main_t * im, u32 fib_index,
- ip_interface_address_t * a)
+ip6_add_interface_prefix_routes (ip6_main_t * im,
+ u32 sw_if_index,
+ u32 fib_index,
+ ip6_address_t * address, u32 address_length)
{
ip_lookup_main_t *lm = &im->lookup_main;
- ip6_address_t *address = ip_interface_address_get_address (lm, a);
- fib_prefix_t pfx = {
- .fp_len = a->address_length,
- .fp_proto = FIB_PROTOCOL_IP6,
- .fp_addr.ip6 = *address,
+ ip_interface_prefix_t *if_prefix;
+
+ ip_interface_prefix_key_t key = {
+ .prefix = {
+ .fp_len = address_length,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_addr.ip6 = {
+ .as_u64 = {
+ address->as_u64[0] &
+ im->fib_masks[address_length].
+ as_u64[0],
+ address->
+ as_u64[1] &
+ im->fib_masks[address_length].
+ as_u64[1],
+ },
+ },
+ },
+ .sw_if_index = sw_if_index,
};
- if (a->address_length < 128)
+ /* If prefix already set on interface, just increment ref count & return */
+ if_prefix = ip_get_interface_prefix (lm, &key);
+ if (if_prefix)
+ {
+ if_prefix->ref_count += 1;
+ return;
+ }
+
+ /* New prefix - allocate a pool entry, initialize it, add to the hash */
+ pool_get (lm->if_prefix_pool, if_prefix);
+ if_prefix->ref_count = 1;
+ clib_memcpy (&if_prefix->key, &key, sizeof (key));
+ mhash_set (&lm->prefix_to_if_prefix_index, &key,
+ if_prefix - lm->if_prefix_pool, 0 /* old value */ );
+
+ /* length < 128 - add glean */
+ if (address_length < 128)
{
- fib_table_entry_update_one_path (fib_index,
- &pfx,
+ /* set the glean route for the prefix */
+ fib_table_entry_update_one_path (fib_index, &key.prefix,
FIB_SOURCE_INTERFACE,
(FIB_ENTRY_FLAG_CONNECTED |
FIB_ENTRY_FLAG_ATTACHED),
@@ -84,9 +115,27 @@ ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index,
NULL, sw_if_index,
/* invalid FIB index */
~0, 1,
- /* no label stack */
+ /* no out-label stack */
NULL, FIB_ROUTE_PATH_FLAG_NONE);
}
+}
+
+static void
+ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index,
+ ip6_main_t * im, u32 fib_index,
+ ip_interface_address_t * a)
+{
+ ip_lookup_main_t *lm = &im->lookup_main;
+ ip6_address_t *address = ip_interface_address_get_address (lm, a);
+ fib_prefix_t pfx = {
+ .fp_len = a->address_length,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_addr.ip6 = *address,
+ };
+
+ /* set special routes for the prefix if needed */
+ ip6_add_interface_prefix_routes (im, sw_if_index, fib_index,
+ address, a->address_length);
pfx.fp_len = 128;
if (sw_if_index < vec_len (lm->classify_table_index_by_sw_if_index))
@@ -121,23 +170,73 @@ ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index,
}
static void
-ip6_del_interface_routes (ip6_main_t * im,
+ip6_del_interface_prefix_routes (ip6_main_t * im,
+ u32 sw_if_index,
+ u32 fib_index,
+ ip6_address_t * address, u32 address_length)
+{
+ ip_lookup_main_t *lm = &im->lookup_main;
+ ip_interface_prefix_t *if_prefix;
+
+ ip_interface_prefix_key_t key = {
+ .prefix = {
+ .fp_len = address_length,
+ .fp_proto = FIB_PROTOCOL_IP6,
+ .fp_addr.ip6 = {
+ .as_u64 = {
+ address->as_u64[0] &
+ im->fib_masks[address_length].
+ as_u64[0],
+ address->
+ as_u64[1] &
+ im->fib_masks[address_length].
+ as_u64[1],
+ },
+ },
+ },
+ .sw_if_index = sw_if_index,
+ };
+
+ if_prefix = ip_get_interface_prefix (lm, &key);
+ if (!if_prefix)
+ {
+ clib_warning ("Prefix not found while deleting %U",
+ format_ip4_address_and_length, address, address_length);
+ return;
+ }
+
+ /* If not deleting last intf addr in prefix, decrement ref count & return */
+ if_prefix->ref_count -= 1;
+ if (if_prefix->ref_count > 0)
+ return;
+
+ /* length <= 30, delete glean route */
+ if (address_length <= 128)
+ {
+ /* remove glean route for prefix */
+ fib_table_entry_delete (fib_index, &key.prefix, FIB_SOURCE_INTERFACE);
+
+ }
+
+ mhash_unset (&lm->prefix_to_if_prefix_index, &key, 0 /* old_value */ );
+ pool_put (lm->if_prefix_pool, if_prefix);
+}
+
+static void
+ip6_del_interface_routes (u32 sw_if_index, ip6_main_t * im,
u32 fib_index,
ip6_address_t * address, u32 address_length)
{
fib_prefix_t pfx = {
- .fp_len = address_length,
+ .fp_len = 128,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_addr.ip6 = *address,
};
- if (pfx.fp_len < 128)
- {
- fib_table_entry_delete (fib_index, &pfx, FIB_SOURCE_INTERFACE);
-
- }
+ /* delete special routes for the prefix if needed */
+ ip6_del_interface_prefix_routes (im, sw_if_index, fib_index,
+ address, address_length);
- pfx.fp_len = 128;
fib_table_entry_delete (fib_index, &pfx, FIB_SOURCE_INTERFACE);
}
@@ -278,6 +377,13 @@ ip6_add_del_interface_address (vlib_main_t * vm,
address,
address_length))
{
+ /* an intf may have >1 addr from the same prefix */
+ if ((sw_if_index == sif->sw_if_index) &&
+ (ia->address_length == address_length) &&
+ !ip6_address_is_equal (x, address))
+ continue;
+
+ /* error if the length or intf was different */
vnm->api_errno = VNET_API_ERROR_DUPLICATE_IF_ADDRESS;
return
clib_error_create
@@ -311,7 +417,8 @@ ip6_add_del_interface_address (vlib_main_t * vm,
ip6_sw_interface_enable_disable (sw_if_index, !is_del);
if (is_del)
- ip6_del_interface_routes (im, ip6_af.fib_index, address, address_length);
+ ip6_del_interface_routes (sw_if_index,
+ im, ip6_af.fib_index, address, address_length);
else
ip6_add_interface_routes (vnm, sw_if_index,
im, ip6_af.fib_index,
@@ -361,7 +468,7 @@ ip6_sw_interface_admin_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
im, fib_index,
ia);
else
- ip6_del_interface_routes (im, fib_index,
+ ip6_del_interface_routes (sw_if_index, im, fib_index,
a, ia->address_length);
}));
/* *INDENT-ON* */