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>
This commit is contained in:

committed by
Neale Ranns

parent
79c04d622a
commit
6c92f5babd
File diff suppressed because it is too large
Load Diff
@ -59,6 +59,67 @@
|
||||
/* Flag used by IOAM code. Classifier sets it pop-hop-by-hop checks it */
|
||||
#define OI_DECAP 0x80000000
|
||||
|
||||
static void
|
||||
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;
|
||||
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 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)
|
||||
{
|
||||
/* 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),
|
||||
DPO_PROTO_IP6,
|
||||
/* No next-hop address */
|
||||
NULL, sw_if_index,
|
||||
/* invalid FIB index */
|
||||
~0, 1,
|
||||
/* 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,
|
||||
@ -72,21 +133,9 @@ ip6_add_interface_routes (vnet_main_t * vnm, u32 sw_if_index,
|
||||
.fp_addr.ip6 = *address,
|
||||
};
|
||||
|
||||
if (a->address_length < 128)
|
||||
{
|
||||
fib_table_entry_update_one_path (fib_index,
|
||||
&pfx,
|
||||
FIB_SOURCE_INTERFACE,
|
||||
(FIB_ENTRY_FLAG_CONNECTED |
|
||||
FIB_ENTRY_FLAG_ATTACHED),
|
||||
DPO_PROTO_IP6,
|
||||
/* No next-hop address */
|
||||
NULL, sw_if_index,
|
||||
/* invalid FIB index */
|
||||
~0, 1,
|
||||
/* no label stack */
|
||||
NULL, FIB_ROUTE_PATH_FLAG_NONE);
|
||||
}
|
||||
/* 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* */
|
||||
|
@ -207,6 +207,8 @@ ip_lookup_init (ip_lookup_main_t * lm, u32 is_ip6)
|
||||
lm->fib_result_n_bytes = sizeof (uword);
|
||||
|
||||
lm->is_ip6 = is_ip6;
|
||||
mhash_init (&lm->prefix_to_if_prefix_index, sizeof (uword),
|
||||
sizeof (ip_interface_prefix_key_t));
|
||||
if (is_ip6)
|
||||
{
|
||||
lm->format_address_and_length = format_ip6_address_and_length;
|
||||
|
@ -86,6 +86,24 @@ typedef u32 flow_hash_config_t;
|
||||
/* An all zeros address */
|
||||
extern const ip46_address_t zero_addr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
fib_prefix_t prefix;
|
||||
|
||||
u32 sw_if_index;
|
||||
} ip_interface_prefix_key_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* key - prefix and sw_if_index */
|
||||
ip_interface_prefix_key_t key;
|
||||
|
||||
/* number of addresses in this prefix on the interface */
|
||||
u16 ref_count;
|
||||
|
||||
/* index of the interface address used as a default source address */
|
||||
u32 src_ia_index;
|
||||
} ip_interface_prefix_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -131,6 +149,12 @@ typedef struct ip_lookup_main_t
|
||||
~0 means this interface has no address. */
|
||||
u32 *if_address_pool_index_by_sw_if_index;
|
||||
|
||||
/** Pool of prefixes containing addresses assigned to interfaces */
|
||||
ip_interface_prefix_t *if_prefix_pool;
|
||||
|
||||
/** Hash table mapping prefix to index in interface prefix pool */
|
||||
mhash_t prefix_to_if_prefix_index;
|
||||
|
||||
/** First table index to use for this interface, ~0 => none */
|
||||
u32 *classify_table_index_by_sw_if_index;
|
||||
|
||||
@ -178,6 +202,13 @@ ip_interface_address_get_address (ip_lookup_main_t * lm,
|
||||
return mhash_key_to_mem (&lm->address_to_if_address_index, a->address_key);
|
||||
}
|
||||
|
||||
always_inline ip_interface_prefix_t *
|
||||
ip_get_interface_prefix (ip_lookup_main_t * lm, ip_interface_prefix_key_t * k)
|
||||
{
|
||||
uword *p = mhash_get (&lm->prefix_to_if_prefix_index, k);
|
||||
return p ? pool_elt_at_index (lm->if_prefix_pool, p[0]) : 0;
|
||||
}
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
#define foreach_ip_interface_address(lm,a,sw_if_index,loop,body) \
|
||||
do { \
|
||||
|
@ -15,7 +15,9 @@ from framework import VppTestCase, VppTestRunner
|
||||
from util import ppp
|
||||
from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \
|
||||
VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
|
||||
VppMplsTable, VppIpTable, FibPathType, find_route
|
||||
VppMplsTable, VppIpTable, FibPathType, find_route, \
|
||||
VppIpInterfaceAddress
|
||||
from vpp_ip import VppIpAddress
|
||||
from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
|
||||
from vpp_papi import VppEnum
|
||||
|
||||
@ -210,19 +212,19 @@ class TestIPv4(VppTestCase):
|
||||
self.verify_capture(i, pkts)
|
||||
|
||||
|
||||
class TestIPV4IfAddrRoute(VppTestCase):
|
||||
class TestIPv4IfAddrRoute(VppTestCase):
|
||||
""" IPv4 Interface Addr Route Test Case """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestIPV4IfAddrRoute, cls).setUpClass()
|
||||
super(TestIPv4IfAddrRoute, cls).setUpClass()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(TestIPV4IfAddrRoute, cls).tearDownClass()
|
||||
super(TestIPv4IfAddrRoute, cls).tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
super(TestIPV4IfAddrRoute, self).setUp()
|
||||
super(TestIPv4IfAddrRoute, self).setUp()
|
||||
|
||||
# create 1 pg interface
|
||||
self.create_pg_interfaces(range(1))
|
||||
@ -233,11 +235,62 @@ class TestIPV4IfAddrRoute(VppTestCase):
|
||||
i.resolve_arp()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestIPV4IfAddrRoute, self).tearDown()
|
||||
super(TestIPv4IfAddrRoute, self).tearDown()
|
||||
for i in self.pg_interfaces:
|
||||
i.unconfig_ip4()
|
||||
i.admin_down()
|
||||
|
||||
def test_ipv4_ifaddrs_same_prefix(self):
|
||||
""" IPv4 Interface Addresses Same Prefix test
|
||||
|
||||
Test scenario:
|
||||
|
||||
- Verify no route in FIB for prefix 10.10.10.0/24
|
||||
- Configure IPv4 address 10.10.10.10/24 on an interface
|
||||
- Verify route in FIB for prefix 10.10.10.0/24
|
||||
- Configure IPv4 address 10.10.10.20/24 on an interface
|
||||
- Delete 10.10.10.10/24 from interface
|
||||
- Verify route in FIB for prefix 10.10.10.0/24
|
||||
- Delete 10.10.10.20/24 from interface
|
||||
- Verify no route in FIB for prefix 10.10.10.0/24
|
||||
"""
|
||||
|
||||
# create two addresses, verify route not present
|
||||
if_addr1 = VppIpInterfaceAddress(self, self.pg0,
|
||||
VppIpAddress("10.10.10.10"), 24)
|
||||
if_addr2 = VppIpInterfaceAddress(self, self.pg0,
|
||||
VppIpAddress("10.10.10.20"), 24)
|
||||
self.assertFalse(if_addr1.query_vpp_config()) # 10.10.10.0/24
|
||||
self.assertFalse(find_route(self, "10.10.10.10", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.20", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.255", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.0", 32))
|
||||
|
||||
# configure first address, verify route present
|
||||
if_addr1.add_vpp_config()
|
||||
self.assertTrue(if_addr1.query_vpp_config()) # 10.10.10.0/24
|
||||
self.assertTrue(find_route(self, "10.10.10.10", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.20", 32))
|
||||
self.assertTrue(find_route(self, "10.10.10.255", 32))
|
||||
self.assertTrue(find_route(self, "10.10.10.0", 32))
|
||||
|
||||
# configure second address, delete first, verify route not removed
|
||||
if_addr2.add_vpp_config()
|
||||
if_addr1.remove_vpp_config()
|
||||
self.assertTrue(if_addr1.query_vpp_config()) # 10.10.10.0/24
|
||||
self.assertFalse(find_route(self, "10.10.10.10", 32))
|
||||
self.assertTrue(find_route(self, "10.10.10.20", 32))
|
||||
self.assertTrue(find_route(self, "10.10.10.255", 32))
|
||||
self.assertTrue(find_route(self, "10.10.10.0", 32))
|
||||
|
||||
# delete second address, verify route removed
|
||||
if_addr2.remove_vpp_config()
|
||||
self.assertFalse(if_addr1.query_vpp_config()) # 10.10.10.0/24
|
||||
self.assertFalse(find_route(self, "10.10.10.10", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.20", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.255", 32))
|
||||
self.assertFalse(find_route(self, "10.10.10.0", 32))
|
||||
|
||||
def test_ipv4_ifaddr_route(self):
|
||||
""" IPv4 Interface Address Route test
|
||||
|
||||
|
@ -20,10 +20,11 @@ from six import moves
|
||||
|
||||
from framework import VppTestCase, VppTestRunner
|
||||
from util import ppp, ip6_normalize, mk_ll_addr
|
||||
from vpp_ip import DpoProto
|
||||
from vpp_ip import DpoProto, VppIpAddress
|
||||
from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \
|
||||
VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
|
||||
VppMplsRoute, VppMplsTable, VppIpTable, FibPathType
|
||||
VppMplsRoute, VppMplsTable, VppIpTable, FibPathType, \
|
||||
VppIpInterfaceAddress
|
||||
from vpp_neighbor import find_nbr, VppNeighbor
|
||||
from vpp_pg_interface import is_ipv6_misc
|
||||
from vpp_sub_interface import VppSubInterface, VppDot1QSubint
|
||||
@ -935,6 +936,80 @@ class TestIPv6(TestIPv6ND):
|
||||
self.pg0.ip6_ra_config(no=1, suppress=1, send_unicast=0)
|
||||
|
||||
|
||||
class TestIPv6IfAddrRoute(VppTestCase):
|
||||
""" IPv6 Interface Addr Route Test Case """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestIPv6IfAddrRoute, cls).setUpClass()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(TestIPv6IfAddrRoute, cls).tearDownClass()
|
||||
|
||||
def setUp(self):
|
||||
super(TestIPv6IfAddrRoute, self).setUp()
|
||||
|
||||
# create 1 pg interface
|
||||
self.create_pg_interfaces(range(1))
|
||||
|
||||
for i in self.pg_interfaces:
|
||||
i.admin_up()
|
||||
i.config_ip6()
|
||||
i.resolve_ndp()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestIPv6IfAddrRoute, self).tearDown()
|
||||
for i in self.pg_interfaces:
|
||||
i.unconfig_ip6()
|
||||
i.admin_down()
|
||||
|
||||
def test_ipv6_ifaddrs_same_prefix(self):
|
||||
""" IPv6 Interface Addresses Same Prefix test
|
||||
|
||||
Test scenario:
|
||||
|
||||
- Verify no route in FIB for prefix 2001:10::/64
|
||||
- Configure IPv4 address 2001:10::10/64 on an interface
|
||||
- Verify route in FIB for prefix 2001:10::/64
|
||||
- Configure IPv4 address 2001:10::20/64 on an interface
|
||||
- Delete 2001:10::10/64 from interface
|
||||
- Verify route in FIB for prefix 2001:10::/64
|
||||
- Delete 2001:10::20/64 from interface
|
||||
- Verify no route in FIB for prefix 2001:10::/64
|
||||
"""
|
||||
|
||||
addr1 = "2001:10::10"
|
||||
addr2 = "2001:10::20"
|
||||
|
||||
if_addr1 = VppIpInterfaceAddress(self, self.pg0,
|
||||
VppIpAddress(addr1), 64)
|
||||
if_addr2 = VppIpInterfaceAddress(self, self.pg0,
|
||||
VppIpAddress(addr2), 64)
|
||||
self.assertFalse(if_addr1.query_vpp_config()) # 2001:10::/64
|
||||
self.assertFalse(find_route(self, addr1, 128))
|
||||
self.assertFalse(find_route(self, addr2, 128))
|
||||
|
||||
# configure first address, verify route present
|
||||
if_addr1.add_vpp_config()
|
||||
self.assertTrue(if_addr1.query_vpp_config()) # 2001:10::/64
|
||||
self.assertTrue(find_route(self, addr1, 128))
|
||||
self.assertFalse(find_route(self, addr2, 128))
|
||||
|
||||
# configure second address, delete first, verify route not removed
|
||||
if_addr2.add_vpp_config()
|
||||
if_addr1.remove_vpp_config()
|
||||
self.assertTrue(if_addr1.query_vpp_config()) # 2001:10::/64
|
||||
self.assertFalse(find_route(self, addr1, 128))
|
||||
self.assertTrue(find_route(self, addr2, 128))
|
||||
|
||||
# delete second address, verify route removed
|
||||
if_addr2.remove_vpp_config()
|
||||
self.assertFalse(if_addr1.query_vpp_config()) # 2001:10::/64
|
||||
self.assertFalse(find_route(self, addr1, 128))
|
||||
self.assertFalse(find_route(self, addr2, 128))
|
||||
|
||||
|
||||
class TestICMPv6Echo(VppTestCase):
|
||||
""" ICMPv6 Echo Test Case """
|
||||
|
||||
|
Reference in New Issue
Block a user