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:
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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_
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user