summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2020-04-23 09:04:59 +0000
committerOle Trøan <otroan@employees.org>2020-04-30 20:24:45 +0000
commit240dcb24a00004017ec2d1b23b93d9ae8d3c9f65 (patch)
tree5c0397ecbf4c0936ce703871888ff666239f775f
parentf3783e15014c4c93a1b43697bfcc6422b0a7e369 (diff)
ip-neighbor: Add flush API
Type: feature Signed-off-by: Neale Ranns <nranns@cisco.com> Change-Id: I96509c274895b917c3e220204d7959d9270de30d
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.api16
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.c34
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.h2
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_api.c19
-rw-r--r--test/test_neighbor.py162
5 files changed, 227 insertions, 6 deletions
diff --git a/src/vnet/ip-neighbor/ip_neighbor.api b/src/vnet/ip-neighbor/ip_neighbor.api
index 473024a49ea..fe344af5c82 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.api
+++ b/src/vnet/ip-neighbor/ip_neighbor.api
@@ -163,6 +163,22 @@ autoreply define ip_neighbor_replace_end
u32 context;
};
+/** \brief IP neighbor flush request - removes *all* neighbours.
+ dynamic and static from API/CLI and dynamic from data-plane.
+
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param af - Flush neighbours of this address family
+ @param sw_if_index - Flush on this interface (~0 => all interfaces)
+*/
+autoreply define ip_neighbor_flush
+{
+ u32 client_index;
+ u32 context;
+ vl_api_address_family_t af;
+ vl_api_interface_index_t sw_if_index [default=0xffffffff];
+};
+
/** \brief Register for IP4 ARP resolution event on receiving 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 5b18473e462..afb97acb39c 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.c
+++ b/src/vnet/ip-neighbor/ip_neighbor.c
@@ -576,6 +576,40 @@ ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index)
return (0);
}
+typedef struct ip_neighbor_del_all_ctx_t_
+{
+ index_t *ipn_del;
+} ip_neighbor_del_all_ctx_t;
+
+static walk_rc_t
+ip_neighbor_del_all_walk_cb (index_t ipni, void *arg)
+{
+ ip_neighbor_del_all_ctx_t *ctx = arg;
+
+ vec_add1 (ctx->ipn_del, ipni);
+
+ return (WALK_CONTINUE);
+}
+
+void
+ip_neighbor_del_all (ip46_type_t type, u32 sw_if_index)
+{
+ IP_NEIGHBOR_INFO ("delete-all: %U, %U",
+ format_ip46_type, type,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index);
+
+ ip_neighbor_del_all_ctx_t ctx = {
+ .ipn_del = NULL,
+ };
+ index_t *ipni;
+
+ ip_neighbor_walk (type, sw_if_index, ip_neighbor_del_all_walk_cb, &ctx);
+
+ vec_foreach (ipni, ctx.ipn_del) ip_neighbor_free (ip_neighbor_get (*ipni));
+ vec_free (ctx.ipn_del);
+}
+
void
ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
{
diff --git a/src/vnet/ip-neighbor/ip_neighbor.h b/src/vnet/ip-neighbor/ip_neighbor.h
index 8769fd5efd7..cdceadb0859 100644
--- a/src/vnet/ip-neighbor/ip_neighbor.h
+++ b/src/vnet/ip-neighbor/ip_neighbor.h
@@ -41,6 +41,8 @@ extern int ip_neighbor_del (const ip46_address_t * ip,
extern int ip_neighbor_config (ip46_type_t type, u32 limit, u32 age,
bool recycle);
+extern void ip_neighbor_del_all (ip46_type_t type, u32 sw_if_index);
+
typedef walk_rc_t (*ip_neighbor_walk_cb_t) (index_t ipni, void *ctx);
extern void ip_neighbor_walk (ip46_type_t type,
u32 sw_if_index,
diff --git a/src/vnet/ip-neighbor/ip_neighbor_api.c b/src/vnet/ip-neighbor/ip_neighbor_api.c
index 86587fab31b..02952c7ba24 100644
--- a/src/vnet/ip-neighbor/ip_neighbor_api.c
+++ b/src/vnet/ip-neighbor/ip_neighbor_api.c
@@ -301,6 +301,25 @@ vl_api_ip_neighbor_replace_end_t_handler (vl_api_ip_neighbor_replace_end_t *
REPLY_MACRO (VL_API_IP_NEIGHBOR_REPLACE_END_REPLY);
}
+static void
+vl_api_ip_neighbor_flush_t_handler (vl_api_ip_neighbor_flush_t * mp)
+{
+ vl_api_ip_neighbor_flush_reply_t *rmp;
+ ip_address_family_t af;
+ int rv;
+
+ if (mp->sw_if_index != ~0)
+ VALIDATE_SW_IF_INDEX (mp);
+
+ rv = ip_address_family_decode (mp->af, &af);
+
+ if (!rv)
+ ip_neighbor_del_all (ip46_type_from_af (af), ntohl (mp->sw_if_index));
+
+ BAD_SW_IF_INDEX_LABEL;
+ REPLY_MACRO (VL_API_IP_NEIGHBOR_FLUSH_REPLY);
+}
+
#define vl_msg_name_crc_list
#include <vnet/ip-neighbor/ip_neighbor.api.h>
#undef vl_msg_name_crc_list
diff --git a/test/test_neighbor.py b/test/test_neighbor.py
index cc1357ce0d8..416241e8a64 100644
--- a/test/test_neighbor.py
+++ b/test/test_neighbor.py
@@ -13,7 +13,7 @@ from vpp_papi import VppEnum
import scapy.compat
from scapy.packet import Raw
from scapy.layers.l2 import Ether, ARP, Dot1Q
-from scapy.layers.inet import IP, UDP
+from scapy.layers.inet import IP, UDP, TCP
from scapy.layers.inet6 import IPv6
from scapy.contrib.mpls import MPLS
from scapy.layers.inet6 import IPv6
@@ -777,11 +777,7 @@ class ARPTestCase(VppTestCase):
# Send the ARP request with an originating address that
# is VPP's own address
#
- self.pg2.add_stream(arp_req_from_me)
- self.pg_enable_capture(self.pg_interfaces)
- self.pg_start()
-
- rx = self.pg2.get_capture(1)
+ rx = self.send_and_expect(self.pg2, [arp_req_from_me], self.pg2)
self.verify_arp_resp(rx[0],
self.pg2.local_mac,
self.pg2.remote_mac,
@@ -796,6 +792,48 @@ class ARPTestCase(VppTestCase):
self.pg0.local_ip4))
#
+ # setup a punt redirect so packets from the uplink go to the tap
+ #
+ self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
+ self.pg2.sw_if_index,
+ self.pg0.local_ip4)
+
+ p_tcp = (Ether(src=self.pg0.remote_mac,
+ dst=self.pg0.local_mac,) /
+ IP(src=self.pg0.remote_ip4,
+ dst=self.pg0.local_ip4) /
+ TCP(sport=80, dport=80) /
+ Raw())
+ rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
+
+ # there's no ARP entry so this is an ARP req
+ self.assertTrue(rx[0].haslayer(ARP))
+
+ # and ARP entry for VPP's pg0 address on the host interface
+ n1 = VppNeighbor(self,
+ self.pg2.sw_if_index,
+ self.pg2.remote_mac,
+ self.pg0.local_ip4,
+ is_no_fib_entry=True).add_vpp_config()
+ # now the packets shold forward
+ rx = self.send_and_expect(self.pg0, [p_tcp], self.pg2)
+ self.assertFalse(rx[0].haslayer(ARP))
+ self.assertEqual(rx[0][Ether].dst, self.pg2.remote_mac)
+
+ #
+ # flush the neighbor cache on the uplink
+ #
+ af = VppEnum.vl_api_address_family_t
+ self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
+
+ # ensure we can still resolve the ARPs on the uplink
+ self.pg0.resolve_arp()
+
+ self.assertTrue(find_nbr(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_ip4))
+
+ #
# cleanup
#
self.pg2.set_proxy_arp(0)
@@ -1966,5 +2004,117 @@ class NeighborReplaceTestCase(VppTestCase):
i.remote_hosts[h].ip6))
+class NeighborFlush(VppTestCase):
+ """ Neighbor Flush """
+
+ @classmethod
+ def setUpClass(cls):
+ super(NeighborFlush, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(NeighborFlush, cls).tearDownClass()
+
+ def setUp(self):
+ super(NeighborFlush, self).setUp()
+
+ self.create_pg_interfaces(range(2))
+
+ 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(NeighborFlush, self).tearDown()
+
+ for i in self.pg_interfaces:
+ i.unconfig_ip4()
+ i.unconfig_ip6()
+ i.admin_down()
+
+ def test_flush(self):
+ """ Neighbour Flush """
+
+ e = VppEnum
+ nf = e.vl_api_ip_neighbor_flags_t
+ af = e.vl_api_address_family_t
+ N_HOSTS = 16
+ static = [False, True]
+ self.pg0.generate_remote_hosts(N_HOSTS)
+ self.pg1.generate_remote_hosts(N_HOSTS)
+
+ for s in static:
+ # a few v4 and v6 dynamic neoghbors
+ for n in range(N_HOSTS):
+ VppNeighbor(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_hosts[n].mac,
+ self.pg0.remote_hosts[n].ip4,
+ is_static=s).add_vpp_config()
+ VppNeighbor(self,
+ self.pg1.sw_if_index,
+ self.pg1.remote_hosts[n].mac,
+ self.pg1.remote_hosts[n].ip6,
+ is_static=s).add_vpp_config()
+
+ # flush the interfaces individually
+ self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, self.pg0.sw_if_index)
+
+ # check we haven't flushed that which we shouldn't
+ for n in range(N_HOSTS):
+ self.assertTrue(find_nbr(self,
+ self.pg1.sw_if_index,
+ self.pg1.remote_hosts[n].ip6,
+ is_static=s))
+
+ self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, self.pg1.sw_if_index)
+
+ for n in range(N_HOSTS):
+ self.assertFalse(find_nbr(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_hosts[n].ip4))
+ self.assertFalse(find_nbr(self,
+ self.pg1.sw_if_index,
+ self.pg1.remote_hosts[n].ip6))
+
+ # add the nieghbours back
+ for n in range(N_HOSTS):
+ VppNeighbor(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_hosts[n].mac,
+ self.pg0.remote_hosts[n].ip4,
+ is_static=s).add_vpp_config()
+ VppNeighbor(self,
+ self.pg1.sw_if_index,
+ self.pg1.remote_hosts[n].mac,
+ self.pg1.remote_hosts[n].ip6,
+ is_static=s).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh ip neighbor"))
+
+ # flush both interfaces at the same time
+ self.vapi.ip_neighbor_flush(af.ADDRESS_IP6, 0xffffffff)
+
+ # check we haven't flushed that which we shouldn't
+ for n in range(N_HOSTS):
+ self.assertTrue(find_nbr(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_hosts[n].ip4,
+ is_static=s))
+
+ self.vapi.ip_neighbor_flush(af.ADDRESS_IP4, 0xffffffff)
+
+ for n in range(N_HOSTS):
+ self.assertFalse(find_nbr(self,
+ self.pg0.sw_if_index,
+ self.pg0.remote_hosts[n].ip4))
+ self.assertFalse(find_nbr(self,
+ self.pg1.sw_if_index,
+ self.pg1.remote_hosts[n].ip6))
+
+
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)