From c87fbb417a580bf8e93d0176dba6a90b3cd6a787 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Thu, 2 Apr 2020 17:08:28 +0000 Subject: [PATCH] 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 Change-Id: I54ab06e11552219e2a18e1b4a87d531321cf3829 --- src/vnet/ip-neighbor/ip_neighbor.api | 37 ++++++++++ src/vnet/ip-neighbor/ip_neighbor.c | 63 +++++++++++++++++ src/vnet/ip-neighbor/ip_neighbor.h | 3 + src/vnet/ip-neighbor/ip_neighbor_api.c | 26 +++++++ src/vnet/ip-neighbor/ip_neighbor_types.c | 17 ++--- src/vnet/ip-neighbor/ip_neighbor_types.h | 14 ++-- test/test_neighbor.py | 117 +++++++++++++++++++++++++++++++ 7 files changed, 262 insertions(+), 15 deletions(-) diff --git a/src/vnet/ip-neighbor/ip_neighbor.api b/src/vnet/ip-neighbor/ip_neighbor.api index b82036052f7..fc8f8222189 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.api +++ b/src/vnet/ip-neighbor/ip_neighbor.api @@ -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 diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c index 960da1252a8..5b18473e462 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.c +++ b/src/vnet/ip-neighbor/ip_neighbor.c @@ -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 */ diff --git a/src/vnet/ip-neighbor/ip_neighbor.h b/src/vnet/ip-neighbor/ip_neighbor.h index cb384c5e240..8769fd5efd7 100644 --- a/src/vnet/ip-neighbor/ip_neighbor.h +++ b/src/vnet/ip-neighbor/ip_neighbor.h @@ -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 */ diff --git a/src/vnet/ip-neighbor/ip_neighbor_api.c b/src/vnet/ip-neighbor/ip_neighbor_api.c index ec1e493f368..86587fab31b 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_api.c +++ b/src/vnet/ip-neighbor/ip_neighbor_api.c @@ -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 #undef vl_msg_name_crc_list diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.c b/src/vnet/ip-neighbor/ip_neighbor_types.c index 27262a5d62c..32c674d2392 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_types.c +++ b/src/vnet/ip-neighbor/ip_neighbor_types.c @@ -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) { diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.h b/src/vnet/ip-neighbor/ip_neighbor_types.h index c6d4e104e8b..82c54177e80 100644 --- a/src/vnet/ip-neighbor/ip_neighbor_types.h +++ b/src/vnet/ip-neighbor/ip_neighbor_types.h @@ -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_ diff --git a/test/test_neighbor.py b/test/test_neighbor.py index 7157839127b..cc1357ce0d8 100644 --- a/test/test_neighbor.py +++ b/test/test_neighbor.py @@ -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) -- 2.16.6