ip-neighbor: Replace feature for the ip-neighbor data-base

Type: feature

DB replace is implemented with a mark and sweep algorithm (just the the
FIB)

Signed-off-by: Neale Ranns <nranns@cisco.com>
Change-Id: I54ab06e11552219e2a18e1b4a87d531321cf3829
This commit is contained in:
Neale Ranns
2020-04-02 17:08:28 +00:00
parent c17ff6ec3b
commit c87fbb417a
7 changed files with 262 additions and 15 deletions

View File

@ -126,6 +126,43 @@ autoreply define ip_neighbor_config
bool recycle;
};
/** \brief IP neighbour replace begin
The use-case is that, for some unspecified reason, the control plane
has a different set of neighbours it than VPP
currently has. The CP would thus like to 'replace' VPP's set
only by specifying what the new set shall be, i.e. it is not
going to delete anything that already eixts, rather, is wants any
unspecified neighbors deleted implicitly.
The CP declares the start of this procedure with this replace_begin
API Call, and when it has populated all neighbours it wants, it calls
the below replace_end API. From this point on it is of course free
to add and delete neighbours as usual.
The underlying mechanism by which VPP implements this replace is
intentionally left unspecified.
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
*/
autoreply define ip_neighbor_replace_begin
{
u32 client_index;
u32 context;
};
/** \brief IP neighbour replace end
see ip_neighbor_replace_begin description.
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
*/
autoreply define ip_neighbor_replace_end
{
u32 client_index;
u32 context;
};
/** \brief Register for IP4 ARP resolution event on receing ARP reply or
MAC/IP info from ARP requests in L2 BDs
@param client_index - opaque cookie to identify the sender

View File

@ -99,6 +99,12 @@ ip_neighbor_get_index (const ip_neighbor_t * ipn)
return (ipn - ip_neighbor_pool);
}
static void
ip_neighbor_touch (ip_neighbor_t * ipn)
{
ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_STALE;
}
static bool
ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
{
@ -145,6 +151,7 @@ ip_neighbor_refresh (ip_neighbor_t * ipn)
* list is time sorted, newest first */
ip_neighbor_elt_t *elt, *head;
ip_neighbor_touch (ipn);
ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
ipn->ipn_n_probes = 0;
@ -473,6 +480,8 @@ ip_neighbor_add (const ip46_address_t * ip,
format_ip_neighbor_flags, flags, format_mac_address_t,
mac);
ip_neighbor_touch (ipn);
/* Refuse to over-write static neighbor entry. */
if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
@ -1177,6 +1186,60 @@ ip_neighbor_flush (ip46_type_t type, u32 sw_if_index)
vec_free (ipnis);
}
static walk_rc_t
ip_neighbor_mark_one (index_t ipni, void *ctx)
{
ip_neighbor_t *ipn;
ipn = ip_neighbor_get (ipni);
ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STALE;
return (WALK_CONTINUE);
}
void
ip_neighbor_mark (ip46_type_t type)
{
ip_neighbor_walk (type, ~0, ip_neighbor_mark_one, NULL);
}
typedef struct ip_neighbor_sweep_ctx_t_
{
index_t *ipnsc_stale;
} ip_neighbor_sweep_ctx_t;
static walk_rc_t
ip_neighbor_sweep_one (index_t ipni, void *arg)
{
ip_neighbor_sweep_ctx_t *ctx = arg;
ip_neighbor_t *ipn;
ipn = ip_neighbor_get (ipni);
if (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STALE)
{
vec_add1 (ctx->ipnsc_stale, ipni);
}
return (WALK_CONTINUE);
}
void
ip_neighbor_sweep (ip46_type_t type)
{
ip_neighbor_sweep_ctx_t ctx = { };
index_t *ipni;
ip_neighbor_walk (type, ~0, ip_neighbor_sweep_one, &ctx);
vec_foreach (ipni, ctx.ipnsc_stale)
{
ip_neighbor_free (ip_neighbor_get (*ipni));
}
vec_free (ctx.ipnsc_stale);
}
/*
* Remove any arp entries associated with the specified interface
*/

View File

@ -62,6 +62,9 @@ extern void ip_neighbor_probe (const ip_adjacency_t * adj);
extern void ip_neighbor_probe_dst (const ip_adjacency_t * adj,
const ip46_address_t * ip);
extern void ip_neighbor_mark (ip46_type_t type);
extern void ip_neighbor_sweep (ip46_type_t type);
/**
* From the watcher to the API to publish a new neighbor
*/

View File

@ -275,6 +275,32 @@ vl_api_ip_neighbor_config_t_handler (vl_api_ip_neighbor_config_t * mp)
REPLY_MACRO (VL_API_IP_NEIGHBOR_CONFIG_REPLY);
}
static void
vl_api_ip_neighbor_replace_begin_t_handler (vl_api_ip_neighbor_replace_begin_t
* mp)
{
vl_api_ip_neighbor_replace_begin_reply_t *rmp;
int rv = 0;
ip_neighbor_mark (IP46_TYPE_IP4);
ip_neighbor_mark (IP46_TYPE_IP6);
REPLY_MACRO (VL_API_IP_NEIGHBOR_REPLACE_BEGIN_REPLY);
}
static void
vl_api_ip_neighbor_replace_end_t_handler (vl_api_ip_neighbor_replace_end_t *
mp)
{
vl_api_ip_neighbor_replace_end_reply_t *rmp;
int rv = 0;
ip_neighbor_sweep (IP46_TYPE_IP4);
ip_neighbor_sweep (IP46_TYPE_IP6);
REPLY_MACRO (VL_API_IP_NEIGHBOR_REPLACE_END_REPLY);
}
#define vl_msg_name_crc_list
#include <vnet/ip-neighbor/ip_neighbor.api.h>
#undef vl_msg_name_crc_list

View File

@ -22,19 +22,14 @@ format_ip_neighbor_flags (u8 * s, va_list * args)
{
ip_neighbor_flags_t flags = va_arg (*args, int);
if (flags & IP_NEIGHBOR_FLAG_STATIC)
s = format (s, "S");
if (flags & IP_NEIGHBOR_FLAG_DYNAMIC)
s = format (s, "D");
if (flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY)
s = format (s, "N");
return s;
#define _(a,b,c,d) \
if (flags & IP_NEIGHBOR_FLAG_##a) \
s = format (s, "%s", d);
foreach_ip_neighbor_flag
#undef _
return s;
}
u8 *
format_ip_neighbor_key (u8 * s, va_list * va)
{

View File

@ -37,13 +37,19 @@ typedef struct
u8 stale_threshold; /* Threshold in minutes to delete nei entry */
} ip_neighbor_scan_arg_t;
#define foreach_ip_neighbor_flag \
_(STATIC, 1 << 0, "static", "S") \
_(DYNAMIC, 1 << 1, "dynamic", "D") \
_(NO_FIB_ENTRY, 1 << 2, "no-fib-entry", "N") \
_(PENDING, 1 << 3, "pending", "P") \
_(STALE, 1 << 4, "stale", "A") \
typedef enum ip_neighbor_flags_t_
{
IP_NEIGHBOR_FLAG_NONE = 0,
IP_NEIGHBOR_FLAG_STATIC = (1 << 0),
IP_NEIGHBOR_FLAG_DYNAMIC = (1 << 1),
IP_NEIGHBOR_FLAG_NO_FIB_ENTRY = (1 << 2),
IP_NEIGHBOR_FLAG_PENDING = (1 << 3),
#define _(a,b,c,d) IP_NEIGHBOR_FLAG_##a = b,
foreach_ip_neighbor_flag
#undef _
} __attribute__ ((packed)) ip_neighbor_flags_t;
typedef struct ip_neighbor_watcher_t_

View File

@ -1849,5 +1849,122 @@ class NeighborAgeTestCase(VppTestCase):
self.pg0.remote_hosts[0].ip4))
class NeighborReplaceTestCase(VppTestCase):
""" ARP/ND Replacement """
@classmethod
def setUpClass(cls):
super(NeighborReplaceTestCase, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(NeighborReplaceTestCase, cls).tearDownClass()
def setUp(self):
super(NeighborReplaceTestCase, self).setUp()
self.create_pg_interfaces(range(4))
# pg0 configured with ip4 and 6 addresses used for input
# pg1 configured with ip4 and 6 addresses used for output
# pg2 is unnumbered to pg0
for i in self.pg_interfaces:
i.admin_up()
i.config_ip4()
i.config_ip6()
i.resolve_arp()
i.resolve_ndp()
def tearDown(self):
super(NeighborReplaceTestCase, self).tearDown()
for i in self.pg_interfaces:
i.unconfig_ip4()
i.unconfig_ip6()
i.admin_down()
def test_replace(self):
""" replace """
N_HOSTS = 16
for i in self.pg_interfaces:
i.generate_remote_hosts(N_HOSTS)
i.configure_ipv4_neighbors()
i.configure_ipv6_neighbors()
# replace them all
self.vapi.ip_neighbor_replace_begin()
self.vapi.ip_neighbor_replace_end()
for i in self.pg_interfaces:
for h in range(N_HOSTS):
self.assertFalse(find_nbr(self,
self.pg0.sw_if_index,
self.pg0.remote_hosts[h].ip4))
self.assertFalse(find_nbr(self,
self.pg0.sw_if_index,
self.pg0.remote_hosts[h].ip6))
#
# and them all back via the API
#
for i in self.pg_interfaces:
for h in range(N_HOSTS):
VppNeighbor(self,
i.sw_if_index,
i.remote_hosts[h].mac,
i.remote_hosts[h].ip4).add_vpp_config()
VppNeighbor(self,
i.sw_if_index,
i.remote_hosts[h].mac,
i.remote_hosts[h].ip6).add_vpp_config()
#
# begin the replacement again, this time touch some
# the neighbours on pg1 so they are not deleted
#
self.vapi.ip_neighbor_replace_begin()
# update from the API all neighbours on pg1
for h in range(N_HOSTS):
VppNeighbor(self,
self.pg1.sw_if_index,
self.pg1.remote_hosts[h].mac,
self.pg1.remote_hosts[h].ip4).add_vpp_config()
VppNeighbor(self,
self.pg1.sw_if_index,
self.pg1.remote_hosts[h].mac,
self.pg1.remote_hosts[h].ip6).add_vpp_config()
# update from the data-plane all neighbours on pg3
self.pg3.configure_ipv4_neighbors()
self.pg3.configure_ipv6_neighbors()
# complete the replacement
self.logger.info(self.vapi.cli("sh ip neighbors"))
self.vapi.ip_neighbor_replace_end()
for i in self.pg_interfaces:
if i == self.pg1 or i == self.pg3:
# neighbours on pg1 and pg3 are still present
for h in range(N_HOSTS):
self.assertTrue(find_nbr(self,
i.sw_if_index,
i.remote_hosts[h].ip4))
self.assertTrue(find_nbr(self,
i.sw_if_index,
i.remote_hosts[h].ip6))
else:
# all other neighbours are toast
for h in range(N_HOSTS):
self.assertFalse(find_nbr(self,
i.sw_if_index,
i.remote_hosts[h].ip4))
self.assertFalse(find_nbr(self,
i.sw_if_index,
i.remote_hosts[h].ip6))
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)