summaryrefslogtreecommitdiffstats
path: root/src/vnet/ip-neighbor
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2019-09-30 10:53:31 +0000
committerOle Trøan <otroan@employees.org>2019-12-17 10:56:20 +0000
commitcbe25aab3be72154f2c706c39eeba6a77f34450f (patch)
tree131fb53b5ec973be045ffb9e2eb797af01d112a0 /src/vnet/ip-neighbor
parent96453fd2417ebd1d69354a7fb692976129cea80e (diff)
ip: Protocol Independent IP Neighbors
Type: feature - ip-neighbour: generic neighbour handling; APIs, DBs, event handling, aging - arp: ARP protocol implementation - ip6-nd; IPv6 neighbor discovery implementation; separate ND, MLD, RA - ip6-link; manage link-local addresses - l2-arp-term; events separated from IP neighbours, since they are not the same. vnet retains just enough education to perform ND/ARP packet construction. arp and ip6-nd to be moved to plugins soon. Change-Id: I88dedd0006b299344f4c7024a0aa5baa6b9a8bbe Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet/ip-neighbor')
-rw-r--r--src/vnet/ip-neighbor/ip4_neighbor.c319
-rw-r--r--src/vnet/ip-neighbor/ip4_neighbor.h85
-rw-r--r--src/vnet/ip-neighbor/ip6_neighbor.c338
-rw-r--r--src/vnet/ip-neighbor/ip6_neighbor.h112
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.api168
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.c1664
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor.h124
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_api.c298
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_dp.c39
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_dp.h38
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_types.c83
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_types.h127
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_watch.c283
-rw-r--r--src/vnet/ip-neighbor/ip_neighbor_watch.h42
14 files changed, 3720 insertions, 0 deletions
diff --git a/src/vnet/ip-neighbor/ip4_neighbor.c b/src/vnet/ip-neighbor/ip4_neighbor.c
new file mode 100644
index 00000000000..2a9e2675a78
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip4_neighbor.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * ip/ip4_forward.c: IP v4 forwarding
+ *
+ * Copyright (c) 2008 Eliot Dresselhaus
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <vnet/ip-neighbor/ip4_neighbor.h>
+#include <vnet/ethernet/ethernet.h>
+
+void
+ip4_neighbor_probe_dst (const ip_adjacency_t * adj, const ip4_address_t * dst)
+{
+ ip_interface_address_t *ia;
+ ip4_address_t *src;
+
+ src = ip4_interface_address_matching_destination
+ (&ip4_main,
+ &adj->sub_type.nbr.next_hop.ip4, adj->rewrite_header.sw_if_index, &ia);
+ if (!src)
+ return;
+
+ ip4_neighbor_probe (vlib_get_main (), vnet_get_main (), adj, src, dst);
+}
+
+void
+ip4_neighbor_advertise (vlib_main_t * vm,
+ vnet_main_t * vnm,
+ u32 sw_if_index, const ip4_address_t * addr)
+{
+ vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
+ ip4_main_t *i4m = &ip4_main;
+ u8 *rewrite, rewrite_len;
+
+ if (NULL == addr)
+ {
+ ip4_main_t *i4m = &ip4_main;
+ addr = ip4_interface_first_address (i4m, sw_if_index, 0);
+ }
+
+ if (addr)
+ {
+ clib_warning ("Sending GARP for IP4 address %U on sw_if_idex %d",
+ format_ip4_address, addr, sw_if_index);
+
+ /* Form GARP packet for output - Gratuitous ARP is an ARP request packet
+ where the interface IP/MAC pair is used for both source and request
+ MAC/IP pairs in the request */
+ u32 bi = 0;
+ ethernet_arp_header_t *h = vlib_packet_template_get_packet
+ (vm, &i4m->ip4_arp_request_packet_template, &bi);
+
+ if (!h)
+ return;
+
+ mac_address_from_bytes (&h->ip4_over_ethernet[0].mac, hi->hw_address);
+ mac_address_from_bytes (&h->ip4_over_ethernet[1].mac, hi->hw_address);
+ h->ip4_over_ethernet[0].ip4 = addr[0];
+ h->ip4_over_ethernet[1].ip4 = addr[0];
+
+ /* Setup MAC header with ARP Etype and broadcast DMAC */
+ vlib_buffer_t *b = vlib_get_buffer (vm, bi);
+ rewrite =
+ ethernet_build_rewrite (vnm, sw_if_index, VNET_LINK_ARP,
+ VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST);
+ rewrite_len = vec_len (rewrite);
+ vlib_buffer_advance (b, -rewrite_len);
+ ethernet_header_t *e = vlib_buffer_get_current (b);
+ clib_memcpy_fast (e->dst_address, rewrite, rewrite_len);
+ vec_free (rewrite);
+
+ /* Send GARP packet out the specified interface */
+ vnet_buffer (b)->sw_if_index[VLIB_RX] =
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
+ vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
+ u32 *to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, hi->output_node_index, f);
+ }
+}
+
+always_inline uword
+ip4_arp_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, int is_glean)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ ip4_main_t *im = &ip4_main;
+ ip_lookup_main_t *lm = &im->lookup_main;
+ u32 *from, *to_next_drop;
+ uword n_left_from, n_left_to_next_drop, next_index;
+ u32 thread_index = vm->thread_index;
+ u64 seed;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ ip4_forward_next_trace (vm, node, frame, VLIB_TX);
+
+ seed = throttle_seed (&im->arp_throttle, thread_index, vlib_time_now (vm));
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ next_index = node->cached_next_index;
+ if (next_index == IP4_ARP_NEXT_DROP)
+ next_index = IP4_ARP_N_NEXT; /* point to first interface */
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, IP4_ARP_NEXT_DROP,
+ to_next_drop, n_left_to_next_drop);
+
+ while (n_left_from > 0 && n_left_to_next_drop > 0)
+ {
+ u32 pi0, adj_index0, sw_if_index0;
+ ip4_address_t resolve0, src0;
+ vlib_buffer_t *p0, *b0;
+ ip_adjacency_t *adj0;
+ u64 r0;
+
+ pi0 = from[0];
+ p0 = vlib_get_buffer (vm, pi0);
+
+ from += 1;
+ n_left_from -= 1;
+ to_next_drop[0] = pi0;
+ to_next_drop += 1;
+ n_left_to_next_drop -= 1;
+
+ adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+ adj0 = adj_get (adj_index0);
+ sw_if_index0 = adj0->rewrite_header.sw_if_index;
+
+ if (is_glean)
+ {
+ /* resolve the packet's destination */
+ ip4_header_t *ip0 = vlib_buffer_get_current (p0);
+ resolve0 = ip0->dst_address;
+ src0 = adj0->sub_type.glean.receive_addr.ip4;
+ }
+ else
+ {
+ /* resolve the incomplete adj */
+ resolve0 = adj0->sub_type.nbr.next_hop.ip4;
+ /* Src IP address in ARP header. */
+ if (ip4_src_address_for_packet (lm, sw_if_index0, &src0))
+ {
+ /* No source address available */
+ p0->error = node->errors[IP4_ARP_ERROR_NO_SOURCE_ADDRESS];
+ continue;
+ }
+ }
+
+ /* combine the address and interface for the hash key */
+ r0 = (u64) resolve0.data_u32 << 32;
+ r0 |= sw_if_index0;
+
+ if (throttle_check (&im->arp_throttle, thread_index, r0, seed))
+ {
+ p0->error = node->errors[IP4_ARP_ERROR_THROTTLED];
+ continue;
+ }
+
+ /*
+ * the adj has been updated to a rewrite but the node the DPO that got
+ * us here hasn't - yet. no big deal. we'll drop while we wait.
+ */
+ if (IP_LOOKUP_NEXT_REWRITE == adj0->lookup_next_index)
+ {
+ p0->error = node->errors[IP4_ARP_ERROR_RESOLVED];
+ continue;
+ }
+
+ /*
+ * Can happen if the control-plane is programming tables
+ * with traffic flowing; at least that's today's lame excuse.
+ */
+ if ((is_glean && adj0->lookup_next_index != IP_LOOKUP_NEXT_GLEAN)
+ || (!is_glean && adj0->lookup_next_index != IP_LOOKUP_NEXT_ARP))
+ {
+ p0->error = node->errors[IP4_ARP_ERROR_NON_ARP_ADJ];
+ continue;
+ }
+
+ /* Send ARP request. */
+ b0 = ip4_neighbor_probe (vm, vnm, adj0, &src0, &resolve0);
+
+ if (PREDICT_TRUE (NULL != b0))
+ {
+ /* copy the persistent fields from the original */
+ clib_memcpy_fast (b0->opaque2, p0->opaque2,
+ sizeof (p0->opaque2));
+ p0->error = node->errors[IP4_ARP_ERROR_REQUEST_SENT];
+ }
+ else
+ {
+ p0->error = node->errors[IP4_ARP_ERROR_NO_BUFFERS];
+ continue;
+ }
+ }
+
+ vlib_put_next_frame (vm, node, IP4_ARP_NEXT_DROP, n_left_to_next_drop);
+ }
+
+ return frame->n_vectors;
+}
+
+VLIB_NODE_FN (ip4_arp_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (ip4_arp_inline (vm, node, frame, 0));
+}
+
+VLIB_NODE_FN (ip4_glean_node) (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (ip4_arp_inline (vm, node, frame, 1));
+}
+
+static char *ip4_arp_error_strings[] = {
+ [IP4_ARP_ERROR_THROTTLED] = "ARP requests throttled",
+ [IP4_ARP_ERROR_RESOLVED] = "ARP requests resolved",
+ [IP4_ARP_ERROR_NO_BUFFERS] = "ARP requests out of buffer",
+ [IP4_ARP_ERROR_REQUEST_SENT] = "ARP requests sent",
+ [IP4_ARP_ERROR_NON_ARP_ADJ] = "ARPs to non-ARP adjacencies",
+ [IP4_ARP_ERROR_NO_SOURCE_ADDRESS] = "no source address for ARP request",
+};
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_arp_node) =
+{
+ .name = "ip4-arp",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ip4_forward_next_trace,
+ .n_errors = ARRAY_LEN (ip4_arp_error_strings),
+ .error_strings = ip4_arp_error_strings,
+ .n_next_nodes = IP4_ARP_N_NEXT,
+ .next_nodes = {
+ [IP4_ARP_NEXT_DROP] = "ip4-drop",
+ },
+};
+
+VLIB_REGISTER_NODE (ip4_glean_node) =
+{
+ .name = "ip4-glean",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ip4_forward_next_trace,
+ .n_errors = ARRAY_LEN (ip4_arp_error_strings),
+ .error_strings = ip4_arp_error_strings,
+ .n_next_nodes = IP4_ARP_N_NEXT,
+ .next_nodes = {
+ [IP4_ARP_NEXT_DROP] = "ip4-drop",
+ },
+};
+/* *INDENT-ON* */
+
+#define foreach_notrace_ip4_arp_error \
+_(THROTTLED) \
+_(RESOLVED) \
+_(NO_BUFFERS) \
+_(REQUEST_SENT) \
+_(NON_ARP_ADJ) \
+_(NO_SOURCE_ADDRESS)
+
+static clib_error_t *
+arp_notrace_init (vlib_main_t * vm)
+{
+ vlib_node_runtime_t *rt = vlib_node_get_runtime (vm, ip4_arp_node.index);
+
+ /* don't trace ARP request packets */
+#define _(a) \
+ vnet_pcap_drop_trace_filter_add_del \
+ (rt->errors[IP4_ARP_ERROR_##a], \
+ 1 /* is_add */);
+ foreach_notrace_ip4_arp_error;
+#undef _
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (arp_notrace_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip4_neighbor.h b/src/vnet/ip-neighbor/ip4_neighbor.h
new file mode 100644
index 00000000000..c52e2d446af
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip4_neighbor.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __IP4_NEIGHBOR_H__
+#define __IP4_NEIGHBOR_H__
+
+#include <vnet/ip/ip.h>
+#include <vnet/ethernet/arp_packet.h>
+
+extern void ip4_neighbor_probe_dst (const ip_adjacency_t * adj,
+ const ip4_address_t * dst);
+extern void ip4_neighbor_advertise (vlib_main_t * vm,
+ vnet_main_t * vnm,
+ u32 sw_if_index,
+ const ip4_address_t * addr);
+
+always_inline vlib_buffer_t *
+ip4_neighbor_probe (vlib_main_t * vm,
+ vnet_main_t * vnm,
+ const ip_adjacency_t * adj0,
+ const ip4_address_t * src, const ip4_address_t * dst)
+{
+ vnet_hw_interface_t *hw_if0;
+ ethernet_arp_header_t *h0;
+ vlib_buffer_t *b0;
+ u32 bi0;
+
+ /* Send ARP request. */
+ h0 = vlib_packet_template_get_packet (vm,
+ &ip4_main.ip4_arp_request_packet_template,
+ &bi0);
+ /* Seems we're out of buffers */
+ if (PREDICT_FALSE (!h0))
+ return (NULL);
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ /* Add rewrite/encap string for ARP packet. */
+ vnet_rewrite_one_header (adj0[0], h0, sizeof (ethernet_header_t));
+
+ hw_if0 = vnet_get_sup_hw_interface (vnm, adj0->rewrite_header.sw_if_index);
+
+ /* Src ethernet address in ARP header. */
+ mac_address_from_bytes (&h0->ip4_over_ethernet[0].mac, hw_if0->hw_address);
+
+ h0->ip4_over_ethernet[0].ip4 = *src;
+ h0->ip4_over_ethernet[1].ip4 = *dst;
+
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = adj0->rewrite_header.sw_if_index;
+
+ vlib_buffer_advance (b0, -adj0->rewrite_header.data_bytes);
+
+ {
+ vlib_frame_t *f = vlib_get_frame_to_node (vm, hw_if0->output_node_index);
+ u32 *to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, hw_if0->output_node_index, f);
+ }
+
+ return b0;
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip6_neighbor.c b/src/vnet/ip-neighbor/ip6_neighbor.c
new file mode 100644
index 00000000000..325db8c6277
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip6_neighbor.c
@@ -0,0 +1,338 @@
+/*
+ * ip/ip6_neighbor.c: IP6 neighbor handling
+ *
+ * Copyright (c) 2010 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ip-neighbor/ip6_neighbor.h>
+
+void
+ip6_neighbor_probe_dst (const ip_adjacency_t * adj, const ip6_address_t * dst)
+{
+ ip_interface_address_t *ia;
+ ip6_address_t *src;
+
+ src = ip6_interface_address_matching_destination
+ (&ip6_main, dst, adj->rewrite_header.sw_if_index, &ia);
+
+ if (!src)
+ return;
+
+ ip6_neighbor_probe (vlib_get_main (), vnet_get_main (), adj, src, dst);
+}
+
+void
+ip6_neighbor_advertise (vlib_main_t * vm,
+ vnet_main_t * vnm,
+ u32 sw_if_index, const ip6_address_t * addr)
+{
+ vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, sw_if_index);
+ ip6_main_t *i6m = &ip6_main;
+ u8 *rewrite, rewrite_len;
+ u8 dst_address[6];
+
+ if (NULL == addr)
+ addr = ip6_interface_first_address (i6m, sw_if_index);
+
+ if (addr)
+ {
+ clib_warning
+ ("Sending unsolicitated NA IP6 address %U on sw_if_idex %d",
+ format_ip6_address, addr, sw_if_index);
+
+ /* Form unsolicited neighbor advertisement packet from NS pkt template */
+ int bogus_length;
+ u32 bi = 0;
+ icmp6_neighbor_solicitation_header_t *h =
+ vlib_packet_template_get_packet (vm,
+ &ip6_neighbor_packet_template,
+ &bi);
+ if (!h)
+ return;
+
+ ip6_set_reserved_multicast_address (&h->ip.dst_address,
+ IP6_MULTICAST_SCOPE_link_local,
+ IP6_MULTICAST_GROUP_ID_all_hosts);
+ h->ip.src_address = addr[0];
+ h->neighbor.icmp.type = ICMP6_neighbor_advertisement;
+ h->neighbor.target_address = addr[0];
+ h->neighbor.advertisement_flags = clib_host_to_net_u32
+ (ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE);
+ h->link_layer_option.header.type =
+ ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
+ clib_memcpy (h->link_layer_option.ethernet_address,
+ hi->hw_address, vec_len (hi->hw_address));
+ h->neighbor.icmp.checksum =
+ ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h->ip, &bogus_length);
+ ASSERT (bogus_length == 0);
+
+ /* Setup MAC header with IP6 Etype and mcast DMAC */
+ vlib_buffer_t *b = vlib_get_buffer (vm, bi);
+ ip6_multicast_ethernet_address (dst_address,
+ IP6_MULTICAST_GROUP_ID_all_hosts);
+ rewrite =
+ ethernet_build_rewrite (vnm, sw_if_index, VNET_LINK_IP6, dst_address);
+ rewrite_len = vec_len (rewrite);
+ vlib_buffer_advance (b, -rewrite_len);
+ ethernet_header_t *e = vlib_buffer_get_current (b);
+ clib_memcpy (e->dst_address, rewrite, rewrite_len);
+ vec_free (rewrite);
+
+ /* Send unsolicited ND advertisement packet out the specified interface */
+ vnet_buffer (b)->sw_if_index[VLIB_RX] =
+ vnet_buffer (b)->sw_if_index[VLIB_TX] = sw_if_index;
+ vlib_frame_t *f = vlib_get_frame_to_node (vm, hi->output_node_index);
+ u32 *to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, hi->output_node_index, f);
+ }
+}
+
+typedef enum
+{
+ IP6_NBR_NEXT_DROP,
+ IP6_NBR_NEXT_REPLY_TX,
+ IP6_NBR_N_NEXT,
+} ip6_discover_neighbor_next_t;
+
+typedef enum
+{
+ IP6_NBR_ERROR_DROP,
+ IP6_NBR_ERROR_REQUEST_SENT,
+ IP6_NBR_ERROR_NO_SOURCE_ADDRESS,
+ IP6_NBR_ERROR_NO_BUFFERS,
+} ip6_discover_neighbor_error_t;
+
+static uword
+ip6_discover_neighbor_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, int is_glean)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ ip6_main_t *im = &ip6_main;
+ u32 *from, *to_next_drop;
+ uword n_left_from, n_left_to_next_drop;
+ u64 seed;
+ u32 thread_index = vm->thread_index;
+
+ if (node->flags & VLIB_NODE_FLAG_TRACE)
+ ip6_forward_next_trace (vm, node, frame, VLIB_TX);
+
+ seed = throttle_seed (&im->nd_throttle, thread_index, vlib_time_now (vm));
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+
+ while (n_left_from > 0)
+ {
+ vlib_get_next_frame (vm, node, IP6_NBR_NEXT_DROP,
+ to_next_drop, n_left_to_next_drop);
+
+ while (n_left_from > 0 && n_left_to_next_drop > 0)
+ {
+ u32 pi0, adj_index0, sw_if_index0, drop0, r0;
+ vnet_hw_interface_t *hw_if0;
+ vlib_buffer_t *p0, *b0;
+ ip_adjacency_t *adj0;
+ ip6_address_t src;
+ ip6_header_t *ip0;
+
+ pi0 = from[0];
+
+ p0 = vlib_get_buffer (vm, pi0);
+
+ adj_index0 = vnet_buffer (p0)->ip.adj_index[VLIB_TX];
+
+ ip0 = vlib_buffer_get_current (p0);
+
+ adj0 = adj_get (adj_index0);
+
+ if (!is_glean)
+ {
+ ip0->dst_address.as_u64[0] =
+ adj0->sub_type.nbr.next_hop.ip6.as_u64[0];
+ ip0->dst_address.as_u64[1] =
+ adj0->sub_type.nbr.next_hop.ip6.as_u64[1];
+ }
+
+ sw_if_index0 = adj0->rewrite_header.sw_if_index;
+ vnet_buffer (p0)->sw_if_index[VLIB_TX] = sw_if_index0;
+
+ /* combine the address and interface for a hash */
+ r0 = ip6_address_hash_to_u64 (&ip0->dst_address) ^ sw_if_index0;
+
+ drop0 = throttle_check (&im->nd_throttle, thread_index, r0, seed);
+
+ from += 1;
+ n_left_from -= 1;
+ to_next_drop[0] = pi0;
+ to_next_drop += 1;
+ n_left_to_next_drop -= 1;
+
+ hw_if0 = vnet_get_sup_hw_interface (vnm, sw_if_index0);
+
+ /* If the interface is link-down, drop the pkt */
+ if (!(hw_if0->flags & VNET_HW_INTERFACE_FLAG_LINK_UP))
+ drop0 = 1;
+
+ if (!ip6_link_is_enabled (sw_if_index0))
+ drop0 = 1;
+
+ /*
+ * the adj has been updated to a rewrite but the node the DPO that got
+ * us here hasn't - yet. no big deal. we'll drop while we wait.
+ */
+ if (IP_LOOKUP_NEXT_REWRITE == adj0->lookup_next_index)
+ drop0 = 1;
+
+ if (drop0)
+ {
+ p0->error = node->errors[IP6_NBR_ERROR_DROP];
+ continue;
+ }
+
+ /*
+ * Choose source address based on destination lookup
+ * adjacency.
+ */
+ if (!ip6_src_address_for_packet (sw_if_index0,
+ &ip0->dst_address, &src))
+ {
+ /* There is no address on the interface */
+ p0->error = node->errors[IP6_NBR_ERROR_NO_SOURCE_ADDRESS];
+ continue;
+ }
+
+ b0 = ip6_neighbor_probe (vm, vnm, adj0, &src, &ip0->dst_address);
+
+ if (PREDICT_TRUE (NULL != b0))
+ {
+ clib_memcpy_fast (b0->opaque2, p0->opaque2,
+ sizeof (p0->opaque2));
+ b0->flags |= p0->flags & VLIB_BUFFER_IS_TRACED;
+ b0->trace_handle = p0->trace_handle;
+ p0->error = node->errors[IP6_NBR_ERROR_REQUEST_SENT];
+ }
+ else
+ {
+ /* There is no address on the interface */
+ p0->error = node->errors[IP6_NBR_ERROR_NO_BUFFERS];
+ continue;
+ }
+ }
+
+ vlib_put_next_frame (vm, node, IP6_NBR_NEXT_DROP, n_left_to_next_drop);
+ }
+
+ return frame->n_vectors;
+}
+
+static uword
+ip6_discover_neighbor (vlib_main_t * vm,
+ vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ return (ip6_discover_neighbor_inline (vm, node, frame, 0));
+}
+
+static uword
+ip6_glean (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+ return (ip6_discover_neighbor_inline (vm, node, frame, 1));
+}
+
+static char *ip6_discover_neighbor_error_strings[] = {
+ [IP6_NBR_ERROR_DROP] = "address overflow drops",
+ [IP6_NBR_ERROR_REQUEST_SENT] = "neighbor solicitations sent",
+ [IP6_NBR_ERROR_NO_SOURCE_ADDRESS] = "no source address for ND solicitation",
+ [IP6_NBR_ERROR_NO_BUFFERS] = "no buffers",
+};
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip6_glean_node) =
+{
+ .function = ip6_glean,
+ .name = "ip6-glean",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ip6_forward_next_trace,
+ .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings),
+ .error_strings = ip6_discover_neighbor_error_strings,
+ .n_next_nodes = IP6_NBR_N_NEXT,
+ .next_nodes =
+ {
+ [IP6_NBR_NEXT_DROP] = "ip6-drop",
+ [IP6_NBR_NEXT_REPLY_TX] = "ip6-rewrite-mcast",
+ },
+};
+VLIB_REGISTER_NODE (ip6_discover_neighbor_node) =
+{
+ .function = ip6_discover_neighbor,
+ .name = "ip6-discover-neighbor",
+ .vector_size = sizeof (u32),
+ .format_trace = format_ip6_forward_next_trace,
+ .n_errors = ARRAY_LEN (ip6_discover_neighbor_error_strings),
+ .error_strings = ip6_discover_neighbor_error_strings,
+ .n_next_nodes = IP6_NBR_N_NEXT,
+ .next_nodes =
+ {
+ [IP6_NBR_NEXT_DROP] = "ip6-drop",
+ [IP6_NBR_NEXT_REPLY_TX] = "ip6-rewrite-mcast",
+ },
+};
+/* *INDENT-ON* */
+
+/* Template used to generate IP6 neighbor solicitation packets. */
+vlib_packet_template_t ip6_neighbor_packet_template;
+
+static clib_error_t *
+ip6_neighbor_init (vlib_main_t * vm)
+{
+ icmp6_neighbor_solicitation_header_t p;
+
+ clib_memset (&p, 0, sizeof (p));
+
+ p.ip.ip_version_traffic_class_and_flow_label =
+ clib_host_to_net_u32 (0x6 << 28);
+ p.ip.payload_length =
+ clib_host_to_net_u16 (sizeof (p) -
+ STRUCT_OFFSET_OF
+ (icmp6_neighbor_solicitation_header_t, neighbor));
+ p.ip.protocol = IP_PROTOCOL_ICMP6;
+ p.ip.hop_limit = 255;
+ ip6_set_solicited_node_multicast_address (&p.ip.dst_address, 0);
+
+ p.neighbor.icmp.type = ICMP6_neighbor_solicitation;
+
+ p.link_layer_option.header.type =
+ ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address;
+ p.link_layer_option.header.n_data_u64s =
+ sizeof (p.link_layer_option) / sizeof (u64);
+
+ vlib_packet_template_init (vm,
+ &ip6_neighbor_packet_template, &p, sizeof (p),
+ /* alloc chunk size */ 8,
+ "ip6 neighbor discovery");
+
+ return NULL;
+}
+
+VLIB_INIT_FUNCTION (ip6_neighbor_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip6_neighbor.h b/src/vnet/ip-neighbor/ip6_neighbor.h
new file mode 100644
index 00000000000..934a2885ee1
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip6_neighbor.h
@@ -0,0 +1,112 @@
+/*
+ * ip/ip6_neighbor.h: IP6 NS transmit
+ *
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __IP6_NEIGHBOR_H__
+#define __IP6_NEIGHBOR_H__
+
+#include <vlib/vlib.h>
+#include <vnet/ip/format.h>
+#include <vnet/ip/ip6.h>
+#include <vnet/ip/ip6_link.h>
+#include <vnet/ip/icmp46_packet.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/adj/adj_internal.h>
+
+/* Template used to generate IP6 neighbor solicitation packets. */
+extern vlib_packet_template_t ip6_neighbor_packet_template;
+
+extern void ip6_neighbor_advertise (vlib_main_t * vm,
+ vnet_main_t * vnm,
+ u32 sw_if_index,
+ const ip6_address_t * addr);
+
+extern void ip6_neighbor_probe_dst (const ip_adjacency_t * adj,
+ const ip6_address_t * dst);
+
+always_inline vlib_buffer_t *
+ip6_neighbor_probe (vlib_main_t * vm,
+ vnet_main_t * vnm,
+ const ip_adjacency_t * adj,
+ const ip6_address_t * src, const ip6_address_t * dst)
+{
+ icmp6_neighbor_solicitation_header_t *h0;
+ vnet_hw_interface_t *hw_if0;
+ vlib_buffer_t *b0;
+ int bogus_length;
+ u32 bi0 = 0;
+
+ h0 = vlib_packet_template_get_packet
+ (vm, &ip6_neighbor_packet_template, &bi0);
+ if (!h0)
+ return NULL;;
+
+ b0 = vlib_get_buffer (vm, bi0);
+
+ hw_if0 = vnet_get_sup_hw_interface (vnm, adj->rewrite_header.sw_if_index);
+
+ /*
+ * Destination address is a solicited node multicast address.
+ * We need to fill in
+ * the low 24 bits with low 24 bits of target's address.
+ */
+ h0->ip.src_address = *src;
+ h0->ip.dst_address.as_u8[13] = dst->as_u8[13];
+ h0->ip.dst_address.as_u8[14] = dst->as_u8[14];
+ h0->ip.dst_address.as_u8[15] = dst->as_u8[15];
+
+ h0->neighbor.target_address = *dst;
+
+ clib_memcpy (h0->link_layer_option.ethernet_address,
+ hw_if0->hw_address, vec_len (hw_if0->hw_address));
+
+ /* $$$$ appears we need this; why is the checksum non-zero? */
+ h0->neighbor.icmp.checksum = 0;
+ h0->neighbor.icmp.checksum =
+ ip6_tcp_udp_icmp_compute_checksum (vm, 0, &h0->ip, &bogus_length);
+
+ ASSERT (bogus_length == 0);
+ VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
+
+ vnet_buffer (b0)->sw_if_index[VLIB_TX] = adj->rewrite_header.sw_if_index;
+
+ /* Use the link's mcast adj to ship the packet */
+ vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
+ ip6_link_get_mcast_adj (adj->rewrite_header.sw_if_index);
+ adj = adj_get (vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+
+ b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
+
+ {
+ vlib_frame_t *f = vlib_get_frame_to_node (vm, adj->ia_node_index);
+ u32 *to_next = vlib_frame_vector_args (f);
+ to_next[0] = bi0;
+ f->n_vectors = 1;
+ vlib_put_frame_to_node (vm, adj->ia_node_index, f);
+ }
+
+ return b0;
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor.api b/src/vnet/ip-neighbor/ip_neighbor.api
new file mode 100644
index 00000000000..a8fb24a712c
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor.api
@@ -0,0 +1,168 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** \file
+
+ This file defines vpp IP neighbor control-plane API messages which are generally
+ called through a shared memory interface.
+*/
+
+option version = "1.0.0";
+
+import "vnet/ip/ip_types.api";
+import "vnet/ethernet/ethernet_types.api";
+import "vnet/interface_types.api";
+
+/** \brief IP neighbor flags
+ @param is_static - A static neighbor Entry - there are not flushed
+ If the interface goes down.
+ @param is_no_fib_entry - Do not create a corresponding entry in the FIB
+ table for the neighbor.
+*/
+enum ip_neighbor_flags: u8
+{
+ IP_API_NEIGHBOR_FLAG_NONE = 0,
+ IP_API_NEIGHBOR_FLAG_STATIC = 0x1,
+ IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY = 0x2,
+};
+
+/** \brief IP neighbor
+ @param sw_if_index - interface used to reach neighbor
+ @param flags - flags for the nieghbor
+ @param mac_address - l2 address of the neighbor
+ @param ip_address - ip4 or ip6 address of the neighbor
+*/
+typedef ip_neighbor {
+ vl_api_interface_index_t sw_if_index;
+ vl_api_ip_neighbor_flags_t flags;
+ vl_api_mac_address_t mac_address;
+ vl_api_address_t ip_address;
+};
+
+/** \brief IP neighbor add / del request
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param is_add - 1 to add neighbor, 0 to delete
+ @param neighbor - the neighor to add/remove
+*/
+define ip_neighbor_add_del
+{
+ u32 client_index;
+ u32 context;
+ /* 1 = add, 0 = delete */
+ bool is_add;
+ vl_api_ip_neighbor_t neighbor;
+};
+/** \brief IP neighbor add / del reply
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param retval - return value
+ @param stats_index - the index to use for this neighbor in the stats segement
+*/
+define ip_neighbor_add_del_reply
+{
+ u32 context;
+ i32 retval;
+ u32 stats_index;
+};
+
+/** \brief Dump IP neighboors
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param sw_if_index - the interface to dump neighboors, ~0 == all
+ @param af - address family is ipv[6|4]
+*/
+define ip_neighbor_dump
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index [default=0xffffffff];
+ vl_api_address_family_t af;
+};
+
+/** \brief IP neighboors dump response
+ @param context - sender context which was passed in the request
+ @param neighbour - the neighbor
+*/
+define ip_neighbor_details {
+ u32 context;
+ vl_api_ip_neighbor_t neighbor;
+};
+
+/** \brief Enable/disable periodic IP neighbor scan
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param af - Address family v4/v6
+ @param max_number - The maximum number of neighbours that will be created.
+ default 50k
+ @param max_age - The maximum age (in seconds) before an inactive neighbour
+ is flushed
+ default 0 => never
+ @param recycle - If max_number of neighbours is reached and new ones need
+ to be created should the oldest neighbour be 'recycled'.
+*/
+autoreply define ip_neighbor_config
+{
+ u32 client_index;
+ u32 context;
+ vl_api_address_family_t af;
+ u32 max_number;
+ u32 max_age;
+ bool recycle;
+};
+
+/** \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
+ @param context - sender context, to match reply w/ request
+ @param enable - 1 => register for events, 0 => cancel registration
+ @param pid - sender's pid
+ @param ip - exact IP address of interested neighbor resolution event
+ @param sw_if_index - interface on which the IP address is present.
+*/
+autoreply define want_ip_neighbor_events
+{
+ u32 client_index;
+ u32 context;
+ bool enable;
+ u32 pid;
+ vl_api_address_t ip;
+ vl_api_interface_index_t sw_if_index [default=0xffffffff];
+};
+
+/** \brief Tell client about an IP4 ARP resolution event or
+ MAC/IP info from ARP requests in L2 BDs
+ @param client_index - opaque cookie to identify the sender
+ @param pid - client pid registered to receive notification
+ @param neighbor - new neighbor created
+*/
+define ip_neighbor_event
+{
+ u32 client_index;
+ u32 pid;
+ vl_api_ip_neighbor_t neighbor;
+};
+
+service {
+ rpc want_ip_neighbor_events returns want_ip_neighbor_events_reply
+ events ip_neighbor_event;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor.c b/src/vnet/ip-neighbor/ip_neighbor.c
new file mode 100644
index 00000000000..11684eb9de5
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor.c
@@ -0,0 +1,1664 @@
+/*
+ * src/vnet/ip/ip_neighboor.c: ip neighbor generic handling
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vppinfra/llist.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.h>
+#include <vnet/ip-neighbor/ip4_neighbor.h>
+#include <vnet/ip-neighbor/ip6_neighbor.h>
+#include <vnet/ip-neighbor/ip_neighbor_watch.h>
+
+#include <vnet/ip/ip6_ll_table.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/adj/adj_mcast.h>
+
+/** Pool for All IP neighbors */
+static ip_neighbor_t *ip_neighbor_pool;
+
+/** protocol specific lists of time sorted neighbors */
+index_t ip_neighbor_list_head[IP46_N_TYPES];
+
+typedef struct ip_neighbor_elt_t_
+{
+ clib_llist_anchor_t ipne_anchor;
+ index_t ipne_index;
+} ip_neighbor_elt_t;
+
+/** Pool of linked list elemeents */
+ip_neighbor_elt_t *ip_neighbor_elt_pool;
+
+typedef struct ip_neighbor_db_t_
+{
+ /** per interface hash */
+ uword **ipndb_hash;
+ /** per-protocol limit - max number of neighbors*/
+ u32 ipndb_limit;
+ /** max age of a neighbor before it's forcibly evicted */
+ u32 ipndb_age;
+ /** when the limit is reached and new neighbors are created, should
+ * we recycle an old one */
+ bool ipndb_recycle;
+ /** per-protocol number of elements */
+ u32 ipndb_n_elts;
+ /** per-protocol number of elements per-fib-index*/
+ u32 *ipndb_n_elts_per_fib;
+} ip_neighbor_db_t;
+
+static vlib_log_class_t ipn_logger;
+
+/* DBs of neighbours one per AF */
+/* *INDENT-OFF* */
+static ip_neighbor_db_t ip_neighbor_db[IP46_N_TYPES] = {
+ [IP46_TYPE_IP4] = {
+ .ipndb_limit = 50000,
+ /* Default to not aging and not recycling */
+ .ipndb_age = 0,
+ .ipndb_recycle = false,
+ },
+ [IP46_TYPE_IP6] = {
+ .ipndb_limit = 50000,
+ /* Default to not aging and not recycling */
+ .ipndb_age = 0,
+ .ipndb_recycle = false,
+ }
+};
+/* *INDENT-ON* */
+
+#define IP_NEIGHBOR_DBG(...) \
+ vlib_log_debug (ipn_logger, __VA_ARGS__);
+
+#define IP_NEIGHBOR_INFO(...) \
+ vlib_log_notice (ipn_logger, __VA_ARGS__);
+
+ip_neighbor_t *
+ip_neighbor_get (index_t ipni)
+{
+ if (pool_is_free_index (ip_neighbor_pool, ipni))
+ return (NULL);
+
+ return (pool_elt_at_index (ip_neighbor_pool, ipni));
+}
+
+static index_t
+ip_neighbor_get_index (const ip_neighbor_t * ipn)
+{
+ return (ipn - ip_neighbor_pool);
+}
+
+static bool
+ip_neighbor_is_dynamic (const ip_neighbor_t * ipn)
+{
+ return (ipn->ipn_flags & IP_NEIGHBOR_FLAG_DYNAMIC);
+}
+
+const ip46_address_t *
+ip_neighbor_get_ip (const ip_neighbor_t * ipn)
+{
+ return (&ipn->ipn_key->ipnk_ip);
+}
+
+const mac_address_t *
+ip_neighbor_get_mac (const ip_neighbor_t * ipn)
+{
+ return (&ipn->ipn_mac);
+}
+
+const u32
+ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn)
+{
+ return (ipn->ipn_key->ipnk_sw_if_index);
+}
+
+static void
+ip_neighbor_list_remove (ip_neighbor_t * ipn)
+{
+ /* new neighbours, are added to the head of the list, since the
+ * list is time sorted, newest first */
+ ip_neighbor_elt_t *elt;
+
+ if (~0 != ipn->ipn_elt)
+ {
+ elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
+
+ clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
+ }
+}
+
+static void
+ip_neighbor_refresh (ip_neighbor_t * ipn)
+{
+ /* new neighbours, are added to the head of the list, since the
+ * list is time sorted, newest first */
+ ip_neighbor_elt_t *elt, *head;
+
+ ipn->ipn_time_last_updated = vlib_time_now (vlib_get_main ());
+ ipn->ipn_n_probes = 0;
+
+ if (ip_neighbor_is_dynamic (ipn))
+ {
+ if (~0 == ipn->ipn_elt)
+ /* first time insertion */
+ pool_get_zero (ip_neighbor_elt_pool, elt);
+ else
+ {
+ /* already inserted - extract first */
+ elt = pool_elt_at_index (ip_neighbor_elt_pool, ipn->ipn_elt);
+
+ clib_llist_remove (ip_neighbor_elt_pool, ipne_anchor, elt);
+ }
+ head = pool_elt_at_index (ip_neighbor_elt_pool,
+ ip_neighbor_list_head[ipn->
+ ipn_key->ipnk_type]);
+
+ elt->ipne_index = ip_neighbor_get_index (ipn);
+ clib_llist_add (ip_neighbor_elt_pool, ipne_anchor, elt, head);
+ ipn->ipn_elt = elt - ip_neighbor_elt_pool;
+ }
+}
+
+static void
+ip_neighbor_db_add (const ip_neighbor_t * ipn)
+{
+ vec_validate (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash,
+ ipn->ipn_key->ipnk_sw_if_index);
+
+ if (!ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
+ [ipn->ipn_key->ipnk_sw_if_index])
+ ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash[ipn->
+ ipn_key->ipnk_sw_if_index]
+ = hash_create_mem (0, sizeof (ip_neighbor_key_t), sizeof (index_t));
+
+ hash_set_mem (ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_hash
+ [ipn->ipn_key->ipnk_sw_if_index], ipn->ipn_key,
+ ip_neighbor_get_index (ipn));
+
+ ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_n_elts++;
+}
+
+static void
+ip_neighbor_db_remove (const ip_neighbor_key_t * key)
+{
+ vec_validate (ip_neighbor_db[key->ipnk_type].ipndb_hash,
+ key->ipnk_sw_if_index);
+
+ hash_unset_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
+ [key->ipnk_sw_if_index], key);
+
+ ip_neighbor_db[key->ipnk_type].ipndb_n_elts--;
+}
+
+static ip_neighbor_t *
+ip_neighbor_db_find (const ip_neighbor_key_t * key)
+{
+ uword *p;
+
+ if (key->ipnk_sw_if_index >=
+ vec_len (ip_neighbor_db[key->ipnk_type].ipndb_hash))
+ return NULL;
+
+ p =
+ hash_get_mem (ip_neighbor_db[key->ipnk_type].ipndb_hash
+ [key->ipnk_sw_if_index], key);
+
+ if (p)
+ return ip_neighbor_get (p[0]);
+
+ return (NULL);
+}
+
+static u8
+ip46_type_pfx_len (ip46_type_t type)
+{
+ return (type == IP46_TYPE_IP4 ? 32 : 128);
+}
+
+static void
+ip_neighbor_adj_fib_add (ip_neighbor_t * ipn, u32 fib_index)
+{
+ if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
+ ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
+ {
+ ip6_ll_prefix_t pfx = {
+ .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
+ .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
+ };
+ ipn->ipn_fib_entry_index =
+ ip6_ll_table_entry_update (&pfx, FIB_ROUTE_PATH_FLAG_NONE);
+ }
+ else
+ {
+ fib_protocol_t fproto;
+
+ fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
+
+ fib_prefix_t pfx = {
+ .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
+ .fp_proto = fproto,
+ .fp_addr = ipn->ipn_key->ipnk_ip,
+ };
+
+ ipn->ipn_fib_entry_index =
+ fib_table_entry_path_add (fib_index, &pfx, FIB_SOURCE_ADJ,
+ FIB_ENTRY_FLAG_ATTACHED,
+ fib_proto_to_dpo (fproto),
+ &pfx.fp_addr,
+ ipn->ipn_key->ipnk_sw_if_index,
+ ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
+
+ vec_validate (ip_neighbor_db
+ [ipn->ipn_key->ipnk_type].ipndb_n_elts_per_fib,
+ fib_index);
+
+ ip_neighbor_db[ipn->ipn_key->
+ ipnk_type].ipndb_n_elts_per_fib[fib_index]++;
+
+ if (1 ==
+ ip_neighbor_db[ipn->ipn_key->
+ ipnk_type].ipndb_n_elts_per_fib[fib_index])
+ fib_table_lock (fib_index, fproto, FIB_SOURCE_ADJ);
+ }
+}
+
+static void
+ip_neighbor_adj_fib_remove (ip_neighbor_t * ipn, u32 fib_index)
+{
+ if (FIB_NODE_INDEX_INVALID != ipn->ipn_fib_entry_index)
+ {
+ if (ipn->ipn_key->ipnk_type == IP46_TYPE_IP6 &&
+ ip6_address_is_link_local_unicast (&ipn->ipn_key->ipnk_ip.ip6))
+ {
+ ip6_ll_prefix_t pfx = {
+ .ilp_addr = ipn->ipn_key->ipnk_ip.ip6,
+ .ilp_sw_if_index = ipn->ipn_key->ipnk_sw_if_index,
+ };
+ ip6_ll_table_entry_delete (&pfx);
+ }
+ else
+ {
+ fib_protocol_t fproto;
+
+ fproto = fib_proto_from_ip46 (ipn->ipn_key->ipnk_type);
+
+ fib_prefix_t pfx = {
+ .fp_len = ip46_type_pfx_len (ipn->ipn_key->ipnk_type),
+ .fp_proto = fproto,
+ .fp_addr = ipn->ipn_key->ipnk_ip,
+ };
+
+ fib_table_entry_path_remove (fib_index,
+ &pfx,
+ FIB_SOURCE_ADJ,
+ fib_proto_to_dpo (fproto),
+ &pfx.fp_addr,
+ ipn->ipn_key->ipnk_sw_if_index,
+ ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
+
+ ip_neighbor_db[ipn->ipn_key->
+ ipnk_type].ipndb_n_elts_per_fib[fib_index]--;
+
+ if (0 ==
+ ip_neighbor_db[ipn->ipn_key->
+ ipnk_type].ipndb_n_elts_per_fib[fib_index])
+ fib_table_unlock (fib_index, fproto, FIB_SOURCE_ADJ);
+ }
+ }
+}
+
+static void
+ip_neighbor_mk_complete (adj_index_t ai, ip_neighbor_t * ipn)
+{
+ adj_nbr_update_rewrite (ai, ADJ_NBR_REWRITE_FLAG_COMPLETE,
+ ethernet_build_rewrite (vnet_get_main (),
+ ipn->
+ ipn_key->ipnk_sw_if_index,
+ adj_get_link_type (ai),
+ ipn->ipn_mac.bytes));
+}
+
+static void
+ip_neighbor_mk_incomplete (adj_index_t ai)
+{
+ ip_adjacency_t *adj = adj_get (ai);
+
+ adj_nbr_update_rewrite (ai,
+ ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
+ ethernet_build_rewrite (vnet_get_main (),
+ adj->
+ rewrite_header.sw_if_index,
+ adj_get_link_type (ai),
+ VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
+}
+
+static adj_walk_rc_t
+ip_neighbor_mk_complete_walk (adj_index_t ai, void *ctx)
+{
+ ip_neighbor_t *ipn = ctx;
+
+ ip_neighbor_mk_complete (ai, ipn);
+
+ return (ADJ_WALK_RC_CONTINUE);
+}
+
+static adj_walk_rc_t
+ip_neighbor_mk_incomplete_walk (adj_index_t ai, void *ctx)
+{
+ ip_neighbor_mk_incomplete (ai);
+
+ return (ADJ_WALK_RC_CONTINUE);
+}
+
+static void
+ip_neighbor_free (ip_neighbor_t * ipn)
+{
+ IP_NEIGHBOR_DBG ("free: %U", format_ip_neighbor,
+ ip_neighbor_get_index (ipn));
+
+ adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
+ fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
+ &ipn->ipn_key->ipnk_ip,
+ ip_neighbor_mk_incomplete_walk, ipn);
+ ip_neighbor_adj_fib_remove
+ (ipn,
+ fib_table_get_index_for_sw_if_index
+ (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
+ ipn->ipn_key->ipnk_sw_if_index));
+
+ ip_neighbor_list_remove (ipn);
+ ip_neighbor_db_remove (ipn->ipn_key);
+ clib_mem_free (ipn->ipn_key);
+
+ pool_put (ip_neighbor_pool, ipn);
+}
+
+static bool
+ip_neighbor_force_reuse (ip46_type_t type)
+{
+ if (!ip_neighbor_db[type].ipndb_recycle)
+ return false;
+
+ /* pluck the oldest entry, which is the one from the end of the list */
+ ip_neighbor_elt_t *elt, *head;
+
+ head =
+ pool_elt_at_index (ip_neighbor_elt_pool, ip_neighbor_list_head[type]);
+
+ if (clib_llist_is_empty (ip_neighbor_elt_pool, ipne_anchor, head))
+ return (false);
+
+ elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
+ ip_neighbor_free (ip_neighbor_get (elt->ipne_index));
+
+ return (true);
+}
+
+static ip_neighbor_t *
+ip_neighbor_alloc (const ip_neighbor_key_t * key,
+ const mac_address_t * mac, ip_neighbor_flags_t flags)
+{
+ ip_neighbor_t *ipn;
+
+ if (ip_neighbor_db[key->ipnk_type].ipndb_limit &&
+ (ip_neighbor_db[key->ipnk_type].ipndb_n_elts >=
+ ip_neighbor_db[key->ipnk_type].ipndb_limit))
+ {
+ if (!ip_neighbor_force_reuse (key->ipnk_type))
+ return (NULL);
+ }
+
+ pool_get_zero (ip_neighbor_pool, ipn);
+
+ ipn->ipn_key = clib_mem_alloc (sizeof (*ipn->ipn_key));
+ clib_memcpy (ipn->ipn_key, key, sizeof (*ipn->ipn_key));
+
+ ipn->ipn_fib_entry_index = FIB_NODE_INDEX_INVALID;
+ ipn->ipn_flags = flags;
+ ipn->ipn_elt = ~0;
+
+ mac_address_copy (&ipn->ipn_mac, mac);
+
+ ip_neighbor_db_add (ipn);
+
+ /* create the adj-fib. the entry in the FIB table for the peer's interface */
+ if (!(ipn->ipn_flags & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY))
+ ip_neighbor_adj_fib_add
+ (ipn, fib_table_get_index_for_sw_if_index
+ (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
+ ipn->ipn_key->ipnk_sw_if_index));
+
+ return (ipn);
+}
+
+int
+ip_neighbor_add (const ip46_address_t * ip,
+ ip46_type_t type,
+ const mac_address_t * mac,
+ u32 sw_if_index,
+ ip_neighbor_flags_t flags, u32 * stats_index)
+{
+ fib_protocol_t fproto;
+ ip_neighbor_t *ipn;
+
+ /* main thread only */
+ ASSERT (0 == vlib_get_thread_index ());
+
+ fproto = fib_proto_from_ip46 (type);
+
+ const ip_neighbor_key_t key = {
+ .ipnk_ip = *ip,
+ .ipnk_sw_if_index = sw_if_index,
+ .ipnk_type = type,
+ };
+
+ ipn = ip_neighbor_db_find (&key);
+
+ if (ipn)
+ {
+ IP_NEIGHBOR_DBG ("update: %U, %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip46_address, ip, type,
+ format_ip_neighbor_flags, flags, format_mac_address_t,
+ mac);
+
+ /* Refuse to over-write static neighbor entry. */
+ if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
+ (ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
+ {
+ /* if MAC address match, still check to send event */
+ if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
+ goto check_customers;
+ return -2;
+ }
+
+ /*
+ * prevent a DoS attack from the data-plane that
+ * spams us with no-op updates to the MAC address
+ */
+ if (0 == mac_address_cmp (&ipn->ipn_mac, mac))
+ {
+ ip_neighbor_refresh (ipn);
+ goto check_customers;
+ }
+
+ mac_address_copy (&ipn->ipn_mac, mac);
+
+ /* A dynamic entry can become static, but not vice-versa.
+ * i.e. since if it was programmed by the CP then it must
+ * be removed by the CP */
+ if ((flags & IP_NEIGHBOR_FLAG_STATIC) &&
+ !(ipn->ipn_flags & IP_NEIGHBOR_FLAG_STATIC))
+ {
+ ip_neighbor_list_remove (ipn);
+ ipn->ipn_flags |= IP_NEIGHBOR_FLAG_STATIC;
+ ipn->ipn_flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
+ }
+ }
+ else
+ {
+ IP_NEIGHBOR_INFO ("add: %U, %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip46_address, ip, type,
+ format_ip_neighbor_flags, flags, format_mac_address_t,
+ mac);
+
+ ipn = ip_neighbor_alloc (&key, mac, flags);
+
+ if (NULL == ipn)
+ return VNET_API_ERROR_LIMIT_EXCEEDED;
+ }
+
+ /* Update time stamp and flags. */
+ ip_neighbor_refresh (ipn);
+
+ adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
+ fproto, &ipn->ipn_key->ipnk_ip,
+ ip_neighbor_mk_complete_walk, ipn);
+
+check_customers:
+ /* Customer(s) requesting event for this address? */
+ ip_neighbor_publish (ip_neighbor_get_index (ipn));
+
+ if (stats_index)
+ *stats_index = adj_nbr_find (fproto,
+ fib_proto_to_link (fproto),
+ &ipn->ipn_key->ipnk_ip,
+ ipn->ipn_key->ipnk_sw_if_index);
+ return 0;
+}
+
+int
+ip_neighbor_del (const ip46_address_t * ip, ip46_type_t type, u32 sw_if_index)
+{
+ ip_neighbor_t *ipn;
+
+ /* main thread only */
+ ASSERT (0 == vlib_get_thread_index ());
+
+ IP_NEIGHBOR_INFO ("delete: %U, %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip46_address, ip, type);
+
+ const ip_neighbor_key_t key = {
+ .ipnk_ip = *ip,
+ .ipnk_sw_if_index = sw_if_index,
+ .ipnk_type = type,
+ };
+
+ ipn = ip_neighbor_db_find (&key);
+
+ if (NULL == ipn)
+ return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+ ip_neighbor_free (ipn);
+
+ return (0);
+}
+
+void
+ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai)
+{
+ ip_neighbor_t *ipn;
+ ip_adjacency_t *adj;
+
+ adj = adj_get (ai);
+
+ ip_neighbor_key_t key = {
+ .ipnk_ip = adj->sub_type.nbr.next_hop,
+ .ipnk_type = fib_proto_to_ip46 (adj->ia_nh_proto),
+ .ipnk_sw_if_index = adj->rewrite_header.sw_if_index,
+ };
+ ipn = ip_neighbor_db_find (&key);
+
+ switch (adj->lookup_next_index)
+ {
+ case IP_LOOKUP_NEXT_ARP:
+ if (NULL != ipn)
+ {
+ adj_nbr_walk_nh (adj->rewrite_header.sw_if_index,
+ adj->ia_nh_proto,
+ &ipn->ipn_key->ipnk_ip,
+ ip_neighbor_mk_complete_walk, ipn);
+ }
+ else
+ {
+ /*
+ * no matching ARP entry.
+ * construct the rewrite required to for an ARP packet, and stick
+ * that in the adj's pipe to smoke.
+ */
+ adj_nbr_update_rewrite
+ (ai,
+ ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
+ ethernet_build_rewrite
+ (vnm,
+ adj->rewrite_header.sw_if_index,
+ VNET_LINK_ARP,
+ VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
+
+ /*
+ * since the FIB has added this adj for a route, it makes sense it
+ * may want to forward traffic sometime soon. Let's send a
+ * speculative ARP. just one. If we were to do periodically that
+ * wouldn't be bad either, but that's more code than i'm prepared to
+ * write at this time for relatively little reward.
+ */
+ ip_neighbor_probe (adj);
+ }
+ break;
+ case IP_LOOKUP_NEXT_GLEAN:
+ case IP_LOOKUP_NEXT_BCAST:
+ case IP_LOOKUP_NEXT_MCAST:
+ case IP_LOOKUP_NEXT_DROP:
+ case IP_LOOKUP_NEXT_PUNT:
+ case IP_LOOKUP_NEXT_LOCAL:
+ case IP_LOOKUP_NEXT_REWRITE:
+ case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
+ case IP_LOOKUP_NEXT_MIDCHAIN:
+ case IP_LOOKUP_NEXT_ICMP_ERROR:
+ case IP_LOOKUP_N_NEXT:
+ ASSERT (0);
+ break;
+ }
+}
+
+void
+ip_neighbor_learn (const ip_neighbor_learn_t * l)
+{
+ ip_neighbor_add (&l->ip, l->type, &l->mac, l->sw_if_index,
+ IP_NEIGHBOR_FLAG_DYNAMIC, NULL);
+}
+
+static clib_error_t *
+ip_neighbor_cmd (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ ip46_address_t ip = ip46_address_initializer;
+ mac_address_t mac = ZERO_MAC_ADDRESS;
+ vnet_main_t *vnm = vnet_get_main ();
+ ip_neighbor_flags_t flags;
+ u32 sw_if_index = ~0;
+ int is_add = 1;
+ int count = 1;
+
+ flags = IP_NEIGHBOR_FLAG_DYNAMIC;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ /* set ip arp TenGigE1/1/0/1 1.2.3.4 aa:bb:... or aabb.ccdd... */
+ if (unformat (input, "%U %U %U",
+ unformat_vnet_sw_interface, vnm, &sw_if_index,
+ unformat_ip46_address, &ip, IP46_TYPE_ANY,
+ unformat_mac_address_t, &mac))
+ ;
+ else if (unformat (input, "delete") || unformat (input, "del"))
+ is_add = 0;
+ else if (unformat (input, "static"))
+ {
+ flags |= IP_NEIGHBOR_FLAG_STATIC;
+ flags &= ~IP_NEIGHBOR_FLAG_DYNAMIC;
+ }
+ else if (unformat (input, "no-fib-entry"))
+ flags |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
+ else if (unformat (input, "count %d", &count))
+ ;
+ else
+ break;
+ }
+
+ if (sw_if_index == ~0 ||
+ ip46_address_is_zero (&ip) || mac_address_is_zero (&mac))
+ return clib_error_return (0,
+ "specify interface, IP address and MAC: `%U'",
+ format_unformat_error, input);
+
+ while (count)
+ {
+ if (is_add)
+ ip_neighbor_add (&ip, ip46_address_get_type (&ip), &mac, sw_if_index,
+ flags, NULL);
+ else
+ ip_neighbor_del (&ip, ip46_address_get_type (&ip), sw_if_index);
+
+ ip46_address_increment (ip46_address_get_type (&ip), &ip);
+ mac_address_increment (&mac);
+
+ --count;
+ }
+
+ return NULL;
+}
+
+/* *INDENT-OFF* */
+/*?
+ * Add or delete IPv4 ARP cache entries.
+ *
+ * @note 'set ip neighbor' options (e.g. delete, static, 'fib-id <id>',
+ * 'count <number>', 'interface ip4_addr mac_addr') can be added in
+ * any order and combination.
+ *
+ * @cliexpar
+ * @parblock
+ * Add or delete IPv4 ARP cache entries as follows. MAC Address can be in
+ * either aa:bb:cc:dd:ee:ff format or aabb.ccdd.eeff format.
+ * @cliexcmd{set ip neighbor GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
+ * @cliexcmd{set ip neighbor delete GigabitEthernet2/0/0 6.0.0.3 de:ad:be:ef:ba:be}
+ *
+ * To add or delete an IPv4 ARP cache entry to or from a specific fib
+ * table:
+ * @cliexcmd{set ip neighbor fib-id 1 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
+ * @cliexcmd{set ip neighbor fib-id 1 delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
+ *
+ * Add or delete IPv4 static ARP cache entries as follows:
+ * @cliexcmd{set ip neighbor static GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
+ * @cliexcmd{set ip neighbor static delete GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
+ *
+ * For testing / debugging purposes, the 'set ip neighbor' command can add or
+ * delete multiple entries. Supply the 'count N' parameter:
+ * @cliexcmd{set ip neighbor count 10 GigabitEthernet2/0/0 6.0.0.3 dead.beef.babe}
+ * @endparblock
+ ?*/
+VLIB_CLI_COMMAND (ip_neighbor_command, static) = {
+ .path = "set ip neighbor",
+ .short_help =
+ "set ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
+ .function = ip_neighbor_cmd,
+};
+VLIB_CLI_COMMAND (ip_neighbor_command2, static) = {
+ .path = "ip neighbor",
+ .short_help =
+ "ip neighbor [del] <intfc> <ip-address> <mac-address> [static] [no-fib-entry] [count <count>] [fib-id <fib-id>] [proxy <lo-addr> - <hi-addr>]",
+ .function = ip_neighbor_cmd,
+};
+/* *INDENT-ON* */
+
+static int
+ip_neighbor_sort (void *a1, void *a2)
+{
+ index_t *ipni1 = a1, *ipni2 = a2;
+ ip_neighbor_t *ipn1, *ipn2;
+ int cmp;
+
+ ipn1 = ip_neighbor_get (*ipni1);
+ ipn2 = ip_neighbor_get (*ipni2);
+
+ cmp = vnet_sw_interface_compare (vnet_get_main (),
+ ipn1->ipn_key->ipnk_sw_if_index,
+ ipn2->ipn_key->ipnk_sw_if_index);
+ if (!cmp)
+ cmp = ip46_address_cmp (&ipn1->ipn_key->ipnk_ip, &ipn2->ipn_key->ipnk_ip);
+ return cmp;
+}
+
+static index_t *
+ip_neighbor_entries (u32 sw_if_index, ip46_type_t type)
+{
+ index_t *ipnis = NULL;
+ ip_neighbor_t *ipn;
+
+ /* *INDENT-OFF* */
+ pool_foreach (ipn, ip_neighbor_pool,
+ ({
+ if (sw_if_index != ~0 &&
+ ipn->ipn_key->ipnk_sw_if_index != sw_if_index &&
+ (IP46_TYPE_ANY == type ||
+ (ipn->ipn_key->ipnk_type == type)))
+ continue;
+ vec_add1 (ipnis, ip_neighbor_get_index(ipn));
+ }));
+
+ /* *INDENT-ON* */
+
+ if (ipnis)
+ vec_sort_with_function (ipnis, ip_neighbor_sort);
+ return ipnis;
+}
+
+static clib_error_t *
+ip_neighbor_show_sorted_i (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd, ip46_type_t type)
+{
+ ip_neighbor_elt_t *elt, *head;
+
+ head = pool_elt_at_index (ip_neighbor_elt_pool,
+ ip_neighbor_list_head[type]);
+
+
+ vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
+ "Flags", "Ethernet", "Interface");
+
+ /* *INDENT-OFF*/
+ /* the list is time sorted, newest first, so start from the back
+ * and work forwards. Stop when we get to one that is alive */
+ clib_llist_foreach_reverse(ip_neighbor_elt_pool,
+ ipne_anchor, head, elt,
+ ({
+ vlib_cli_output (vm, "%U", format_ip_neighbor, elt->ipne_index);
+ }));
+ /* *INDENT-ON*/
+
+ return (NULL);
+}
+
+static clib_error_t *
+ip_neighbor_show_i (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd, ip46_type_t type)
+{
+ index_t *ipni, *ipnis = NULL;
+ u32 sw_if_index;
+
+ /* Filter entries by interface if given. */
+ sw_if_index = ~0;
+ (void) unformat_user (input, unformat_vnet_sw_interface, vnet_get_main (),
+ &sw_if_index);
+
+ ipnis = ip_neighbor_entries (sw_if_index, type);
+
+ if (ipnis)
+ vlib_cli_output (vm, "%=12s%=40s%=6s%=20s%=24s", "Time", "IP",
+ "Flags", "Ethernet", "Interface");
+
+ vec_foreach (ipni, ipnis)
+ {
+ vlib_cli_output (vm, "%U", format_ip_neighbor, *ipni);
+ }
+ vec_free (ipnis);
+
+ return (NULL);
+}
+
+static clib_error_t *
+ip_neighbor_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_ANY));
+}
+
+static clib_error_t *
+ip6_neighbor_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP6));
+}
+
+static clib_error_t *
+ip4_neighbor_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return (ip_neighbor_show_i (vm, input, cmd, IP46_TYPE_IP4));
+}
+
+static clib_error_t *
+ip6_neighbor_show_sorted (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP6));
+}
+
+static clib_error_t *
+ip4_neighbor_show_sorted (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ return (ip_neighbor_show_sorted_i (vm, input, cmd, IP46_TYPE_IP4));
+}
+
+/*?
+ * Display all the IP neighbor entries.
+ *
+ * @cliexpar
+ * Example of how to display the IPv4 ARP table:
+ * @cliexstart{show ip neighbor}
+ * Time FIB IP4 Flags Ethernet Interface
+ * 346.3028 0 6.1.1.3 de:ad:be:ef:ba:be GigabitEthernet2/0/0
+ * 3077.4271 0 6.1.1.4 S de:ad:be:ef:ff:ff GigabitEthernet2/0/0
+ * 2998.6409 1 6.2.2.3 de:ad:be:ef:00:01 GigabitEthernet2/0/0
+ * Proxy arps enabled for:
+ * Fib_index 0 6.0.0.1 - 6.0.0.11
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_ip_neighbors_cmd_node, static) = {
+ .path = "show ip neighbors",
+ .function = ip_neighbor_show,
+ .short_help = "show ip neighbors [interface]",
+};
+VLIB_CLI_COMMAND (show_ip4_neighbors_cmd_node, static) = {
+ .path = "show ip4 neighbors",
+ .function = ip4_neighbor_show,
+ .short_help = "show ip4 neighbors [interface]",
+};
+VLIB_CLI_COMMAND (show_ip6_neighbors_cmd_node, static) = {
+ .path = "show ip6 neighbors",
+ .function = ip6_neighbor_show,
+ .short_help = "show ip6 neighbors [interface]",
+};
+VLIB_CLI_COMMAND (show_ip_neighbor_cmd_node, static) = {
+ .path = "show ip neighbor",
+ .function = ip_neighbor_show,
+ .short_help = "show ip neighbor [interface]",
+};
+VLIB_CLI_COMMAND (show_ip4_neighbor_cmd_node, static) = {
+ .path = "show ip4 neighbor",
+ .function = ip4_neighbor_show,
+ .short_help = "show ip4 neighbor [interface]",
+};
+VLIB_CLI_COMMAND (show_ip6_neighbor_cmd_node, static) = {
+ .path = "show ip6 neighbor",
+ .function = ip6_neighbor_show,
+ .short_help = "show ip6 neighbor [interface]",
+};
+VLIB_CLI_COMMAND (show_ip4_neighbor_sorted_cmd_node, static) = {
+ .path = "show ip4 neighbor-sorted",
+ .function = ip4_neighbor_show_sorted,
+ .short_help = "show ip4 neighbor-sorted",
+};
+VLIB_CLI_COMMAND (show_ip6_neighbor_sorted_cmd_node, static) = {
+ .path = "show ip6 neighbor-sorted",
+ .function = ip6_neighbor_show_sorted,
+ .short_help = "show ip6 neighbor-sorted",
+};
+/* *INDENT-ON* */
+
+static ip_neighbor_vft_t ip_nbr_vfts[IP46_N_TYPES];
+
+void
+ip_neighbor_register (ip46_type_t type, const ip_neighbor_vft_t * vft)
+{
+ ip_nbr_vfts[type] = *vft;
+}
+
+void
+ip_neighbor_probe_dst (const ip_adjacency_t * adj, const ip46_address_t * dst)
+{
+ if (!vnet_sw_interface_is_admin_up (vnet_get_main (),
+ adj->rewrite_header.sw_if_index))
+ return;
+
+ switch (adj->ia_nh_proto)
+ {
+ case FIB_PROTOCOL_IP6:
+ ip6_neighbor_probe_dst (adj, &dst->ip6);
+ break;
+ case FIB_PROTOCOL_IP4:
+ ip4_neighbor_probe_dst (adj, &dst->ip4);
+ break;
+ case FIB_PROTOCOL_MPLS:
+ ASSERT (0);
+ break;
+ }
+}
+
+void
+ip_neighbor_probe (const ip_adjacency_t * adj)
+{
+ ip_neighbor_probe_dst (adj, &adj->sub_type.nbr.next_hop);
+}
+
+void
+ip_neighbor_advertise (vlib_main_t * vm,
+ ip46_type_t type,
+ const ip46_address_t * addr, u32 sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+
+ if (type == IP46_TYPE_IP4 || type == IP46_TYPE_BOTH)
+ ip4_neighbor_advertise (vm, vnm, sw_if_index, &addr->ip4);
+ if (type == IP46_TYPE_IP6 || type == IP46_TYPE_BOTH)
+ ip6_neighbor_advertise (vm, vnm, sw_if_index, &addr->ip6);
+}
+
+void
+ip_neighbor_walk (ip46_type_t type,
+ u32 sw_if_index, ip_neighbor_walk_cb_t cb, void *ctx)
+{
+ ip_neighbor_key_t *key;
+ index_t ipni;
+
+ if (~0 == sw_if_index)
+ {
+ uword **hash;
+
+ vec_foreach (hash, ip_neighbor_db[type].ipndb_hash)
+ {
+ /* *INDENT-OFF* */
+ hash_foreach (key, ipni, *hash,
+ ({
+ cb (ipni, ctx);
+ }));
+ /* *INDENT-ON* */
+ }
+ }
+ else
+ {
+ uword *hash;
+
+ if (vec_len (ip_neighbor_db[type].ipndb_hash) <= sw_if_index)
+ return;
+ hash = ip_neighbor_db[type].ipndb_hash[sw_if_index];
+
+ /* *INDENT-OFF* */
+ hash_foreach (key, ipni, hash,
+ ({
+ cb (ipni, ctx);
+ }));
+ /* *INDENT-ON* */
+ }
+}
+
+int
+ip4_neighbor_proxy_add (u32 fib_index,
+ const ip4_address_t * start,
+ const ip4_address_t * end)
+{
+ if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add)
+ {
+ return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_add
+ (fib_index, start, end));
+ }
+
+ return (-1);
+}
+
+int
+ip4_neighbor_proxy_delete (u32 fib_index,
+ const ip4_address_t * start,
+ const ip4_address_t * end)
+{
+ if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del)
+ {
+ return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_del
+ (fib_index, start, end));
+ }
+ return -1;
+}
+
+int
+ip4_neighbor_proxy_enable (u32 sw_if_index)
+{
+ if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable)
+ {
+ return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_enable (sw_if_index));
+ }
+ return -1;
+}
+
+int
+ip4_neighbor_proxy_disable (u32 sw_if_index)
+{
+ if (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable)
+ {
+ return (ip_nbr_vfts[IP46_TYPE_IP4].inv_proxy4_disable (sw_if_index));
+ }
+ return -1;
+}
+
+int
+ip6_neighbor_proxy_add (u32 sw_if_index, const ip6_address_t * addr)
+{
+ if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add)
+ {
+ return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_add (sw_if_index, addr));
+ }
+ return -1;
+}
+
+int
+ip6_neighbor_proxy_del (u32 sw_if_index, const ip6_address_t * addr)
+{
+ if (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del)
+ {
+ return (ip_nbr_vfts[IP46_TYPE_IP6].inv_proxy6_del (sw_if_index, addr));
+ }
+ return -1;
+}
+
+static void
+ip_neighbor_ethernet_change_mac (ethernet_main_t * em,
+ u32 sw_if_index, uword opaque)
+{
+ ip_neighbor_t *ipn;
+ adj_index_t ai;
+
+ IP_NEIGHBOR_DBG ("mac-change: %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index);
+
+ /* *INDENT-OFF* */
+ pool_foreach (ipn, ip_neighbor_pool,
+ ({
+ if (ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
+ adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
+ fib_proto_from_ip46(ipn->ipn_key->ipnk_type),
+ &ipn->ipn_key->ipnk_ip,
+ ip_neighbor_mk_complete_walk,
+ ipn);
+ }));
+ /* *INDENT-ON* */
+
+ ai = adj_glean_get (FIB_PROTOCOL_IP4, sw_if_index);
+
+ if (ADJ_INDEX_INVALID != ai)
+ adj_glean_update_rewrite (ai);
+}
+
+void
+ip_neighbor_populate (ip46_type_t type, u32 sw_if_index)
+{
+ index_t *ipnis = NULL, *ipni;
+ ip_neighbor_t *ipn;
+
+ IP_NEIGHBOR_DBG ("populate: %U %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip46_type, type);
+
+ /* *INDENT-OFF* */
+ pool_foreach (ipn, ip_neighbor_pool,
+ ({
+ if (ipn->ipn_key->ipnk_type == type &&
+ ipn->ipn_key->ipnk_sw_if_index == sw_if_index)
+ vec_add1 (ipnis, ipn - ip_neighbor_pool);
+ }));
+ /* *INDENT-ON* */
+
+ vec_foreach (ipni, ipnis)
+ {
+ ipn = ip_neighbor_get (*ipni);
+
+ adj_nbr_walk_nh (ipn->ipn_key->ipnk_sw_if_index,
+ fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
+ &ipn->ipn_key->ipnk_ip,
+ ip_neighbor_mk_complete_walk, ipn);
+ }
+ vec_free (ipnis);
+}
+
+void
+ip_neighbor_flush (ip46_type_t type, u32 sw_if_index)
+{
+ index_t *ipnis = NULL, *ipni;
+ ip_neighbor_t *ipn;
+
+ IP_NEIGHBOR_DBG ("flush: %U %U",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip46_type, type);
+
+ /* *INDENT-OFF* */
+ pool_foreach (ipn, ip_neighbor_pool,
+ ({
+ if (ipn->ipn_key->ipnk_type == type &&
+ ipn->ipn_key->ipnk_sw_if_index == sw_if_index &&
+ ip_neighbor_is_dynamic (ipn))
+ vec_add1 (ipnis, ipn - ip_neighbor_pool);
+ }));
+ /* *INDENT-ON* */
+
+ vec_foreach (ipni, ipnis) ip_neighbor_free (ip_neighbor_get (*ipni));
+ vec_free (ipnis);
+}
+
+/*
+ * Remove any arp entries associated with the specified interface
+ */
+static clib_error_t *
+ip_neighbor_interface_admin_change (vnet_main_t * vnm,
+ u32 sw_if_index, u32 flags)
+{
+ ip46_type_t type;
+
+ IP_NEIGHBOR_DBG ("interface-admin: %U %s",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index,
+ (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ? "up" : "down"));
+
+ if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+ {
+ FOREACH_IP46_TYPE (type) ip_neighbor_populate (type, sw_if_index);
+ }
+ else
+ {
+ /* admin down, flush all neighbours */
+ FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
+ }
+
+ return (NULL);
+}
+
+VNET_SW_INTERFACE_ADMIN_UP_DOWN_FUNCTION (ip_neighbor_interface_admin_change);
+
+/*
+ * Remove any arp entries associated with the specified interface
+ */
+static clib_error_t *
+ip_neighbor_delete_sw_interface (vnet_main_t * vnm,
+ u32 sw_if_index, u32 is_add)
+{
+ IP_NEIGHBOR_DBG ("interface-change: %U %s",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, (is_add ? "add" : "del"));
+
+ if (!is_add && sw_if_index != ~0)
+ {
+ ip46_type_t type;
+
+ FOREACH_IP46_TYPE (type) ip_neighbor_flush (type, sw_if_index);
+ }
+
+ return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (ip_neighbor_delete_sw_interface);
+
+typedef struct ip_neighbor_walk_covered_ctx_t_
+{
+ ip46_type_t type;
+ ip46_address_t addr;
+ u32 length;
+ index_t *ipnis;
+} ip_neighbor_walk_covered_ctx_t;
+
+static walk_rc_t
+ip_neighbor_walk_covered (index_t ipni, void *arg)
+{
+ ip_neighbor_walk_covered_ctx_t *ctx = arg;
+ ip_neighbor_t *ipn;
+
+ ipn = ip_neighbor_get (ipni);
+
+ ASSERT (ipn->ipn_key->ipnk_type == ctx->type);
+
+ if (IP46_TYPE_IP4 == ctx->type)
+ {
+ if (ip4_destination_matches_route (&ip4_main,
+ &ipn->ipn_key->ipnk_ip.ip4,
+ &ctx->addr.ip4,
+ ctx->length) &&
+ ip_neighbor_is_dynamic (ipn))
+ {
+ vec_add1 (ctx->ipnis, ip_neighbor_get_index (ipn));
+ }
+ }
+ return (WALK_CONTINUE);
+}
+
+
+/*
+ * callback when an interface address is added or deleted
+ */
+static void
+ip_neighbor_add_del_interface_address_v4 (ip4_main_t * im,
+ uword opaque,
+ u32 sw_if_index,
+ ip4_address_t * address,
+ u32 address_length,
+ u32 if_address_index, u32 is_del)
+{
+ /*
+ * Flush the ARP cache of all entries covered by the address
+ * that is being removed.
+ */
+ IP_NEIGHBOR_DBG ("addr-%d: %U, %U/%d",
+ (is_del ? "del" : "add"),
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip4_address, address, address_length);
+
+ if (is_del)
+ {
+ ip_neighbor_walk_covered_ctx_t ctx = {
+ .addr.ip4 = *address,
+ .type = IP46_TYPE_IP4,
+ .length = address_length,
+ };
+ index_t *ipni;
+
+ ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
+ ip_neighbor_walk_covered, &ctx);
+
+ vec_foreach (ipni, ctx.ipnis)
+ ip_neighbor_free (ip_neighbor_get (*ipni));
+
+ vec_free (ctx.ipnis);
+ }
+}
+
+/*
+ * callback when an interface address is added or deleted
+ */
+static void
+ip_neighbor_add_del_interface_address_v6 (ip6_main_t * im,
+ uword opaque,
+ u32 sw_if_index,
+ ip6_address_t * address,
+ u32 address_length,
+ u32 if_address_index, u32 is_del)
+{
+ /*
+ * Flush the ARP cache of all entries covered by the address
+ * that is being removed.
+ */
+ IP_NEIGHBOR_DBG ("addr-change: %U, %U/%d %s",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index, format_ip6_address, address, address_length,
+ (is_del ? "del" : "add"));
+
+ if (is_del)
+ {
+ ip_neighbor_walk_covered_ctx_t ctx = {
+ .addr.ip6 = *address,
+ .type = IP46_TYPE_IP6,
+ .length = address_length,
+ };
+ index_t *ipni;
+
+ ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
+ ip_neighbor_walk_covered, &ctx);
+
+ vec_foreach (ipni, ctx.ipnis)
+ ip_neighbor_free (ip_neighbor_get (*ipni));
+
+ vec_free (ctx.ipnis);
+ }
+}
+
+typedef struct ip_neighbor_table_bind_ctx_t_
+{
+ u32 new_fib_index;
+ u32 old_fib_index;
+} ip_neighbor_table_bind_ctx_t;
+
+static walk_rc_t
+ip_neighbor_walk_table_bind (index_t ipni, void *arg)
+{
+ ip_neighbor_table_bind_ctx_t *ctx = arg;
+ ip_neighbor_t *ipn;
+
+ ipn = ip_neighbor_get (ipni);
+ ip_neighbor_adj_fib_remove (ipn, ctx->old_fib_index);
+ ip_neighbor_adj_fib_add (ipn, ctx->new_fib_index);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+ip_neighbor_table_bind_v4 (ip4_main_t * im,
+ uword opaque,
+ u32 sw_if_index,
+ u32 new_fib_index, u32 old_fib_index)
+{
+ ip_neighbor_table_bind_ctx_t ctx = {
+ .old_fib_index = old_fib_index,
+ .new_fib_index = new_fib_index,
+ };
+
+ ip_neighbor_walk (IP46_TYPE_IP4, sw_if_index,
+ ip_neighbor_walk_table_bind, &ctx);
+}
+
+static void
+ip_neighbor_table_bind_v6 (ip6_main_t * im,
+ uword opaque,
+ u32 sw_if_index,
+ u32 new_fib_index, u32 old_fib_index)
+{
+ ip_neighbor_table_bind_ctx_t ctx = {
+ .old_fib_index = old_fib_index,
+ .new_fib_index = new_fib_index,
+ };
+
+ ip_neighbor_walk (IP46_TYPE_IP6, sw_if_index,
+ ip_neighbor_walk_table_bind, &ctx);
+}
+
+typedef enum ip_neighbor_age_state_t_
+{
+ IP_NEIGHBOR_AGE_ALIVE,
+ IP_NEIGHBOR_AGE_PROBE,
+ IP_NEIGHBOR_AGE_DEAD,
+} ip_neighbor_age_state_t;
+
+#define IP_NEIGHBOR_PROCESS_SLEEP_LONG (0)
+
+static ip_neighbor_age_state_t
+ip_neighbour_age_out (index_t ipni, f64 now, f64 * wait)
+{
+ ip_neighbor_t *ipn;
+ f64 ttl;
+
+ ipn = ip_neighbor_get (ipni);
+ ttl = now - ipn->ipn_time_last_updated;
+ *wait = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
+
+ if (ttl > ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age)
+ {
+ IP_NEIGHBOR_DBG ("aged: %U @%f - %f > %d",
+ format_ip_neighbor, ipni, now,
+ ipn->ipn_time_last_updated,
+ ip_neighbor_db[ipn->ipn_key->ipnk_type].ipndb_age);
+ if (ipn->ipn_n_probes > 2)
+ {
+ /* 3 strikes and yea-re out */
+ IP_NEIGHBOR_DBG ("dead: %U", format_ip_neighbor, ipni);
+ return (IP_NEIGHBOR_AGE_DEAD);
+ }
+ else
+ {
+ adj_index_t ai;
+
+ ai = adj_glean_get (fib_proto_from_ip46 (ipn->ipn_key->ipnk_type),
+ ip_neighbor_get_sw_if_index (ipn));
+
+ if (ADJ_INDEX_INVALID != ai)
+ ip_neighbor_probe_dst (adj_get (ai), ip_neighbor_get_ip (ipn));
+
+ ipn->ipn_n_probes++;
+ *wait = 1;
+ }
+ }
+ else
+ {
+ *wait = ttl;
+ return (IP_NEIGHBOR_AGE_ALIVE);
+ }
+
+ return (IP_NEIGHBOR_AGE_PROBE);
+}
+
+typedef enum ip_neighbor_process_event_t_
+{
+ IP_NEIGHBOR_AGE_PROCESS_WAKEUP,
+} ip_neighbor_process_event_t;
+
+static uword
+ip_neighbor_age_loop (vlib_main_t * vm,
+ vlib_node_runtime_t * rt,
+ vlib_frame_t * f, ip46_type_t type)
+{
+ uword event_type, *event_data = NULL;
+ f64 timeout;
+
+ /* Set the timeout to an effectively infinite value when the process starts */
+ timeout = IP_NEIGHBOR_PROCESS_SLEEP_LONG;
+
+ while (1)
+ {
+ f64 now;
+
+ if (!timeout)
+ vlib_process_wait_for_event (vm);
+ else
+ vlib_process_wait_for_event_or_clock (vm, timeout);
+
+ event_type = vlib_process_get_events (vm, &event_data);
+ vec_reset_length (event_data);
+
+ now = vlib_time_now (vm);
+
+ switch (event_type)
+ {
+ case ~0:
+ {
+ /* timer expired */
+ ip_neighbor_elt_t *elt, *head;
+ f64 wait;
+
+ timeout = 1e5;
+ head = pool_elt_at_index (ip_neighbor_elt_pool,
+ ip_neighbor_list_head[type]);
+
+ /* *INDENT-OFF*/
+ /* the list is time sorted, newest first, so start from the back
+ * and work forwards. Stop when we get to one that is alive */
+ restart:
+ clib_llist_foreach_reverse(ip_neighbor_elt_pool,
+ ipne_anchor, head, elt,
+ ({
+ ip_neighbor_age_state_t res;
+
+ res = ip_neighbour_age_out(elt->ipne_index, now, &wait);
+
+ if (IP_NEIGHBOR_AGE_ALIVE == res) {
+ /* the oldest neighbor has not yet expired, go back to sleep */
+ break;
+ }
+ else if (IP_NEIGHBOR_AGE_DEAD == res) {
+ /* the oldest neighbor is dead, pop it, then restart the walk
+ * again from the back */
+ ip_neighbor_free (ip_neighbor_get(elt->ipne_index));
+ goto restart;
+ }
+
+ timeout = clib_min (wait, timeout);
+ }));
+ /* *INDENT-ON* */
+ break;
+ }
+ case IP_NEIGHBOR_AGE_PROCESS_WAKEUP:
+ {
+
+ if (!ip_neighbor_db[type].ipndb_age)
+ {
+ /* aging has been disabled */
+ timeout = 0;
+ break;
+ }
+ ip_neighbor_elt_t *elt, *head;
+
+ head = pool_elt_at_index (ip_neighbor_elt_pool,
+ ip_neighbor_list_head[type]);
+ elt = clib_llist_prev (ip_neighbor_elt_pool, ipne_anchor, head);
+
+ /* poke the oldset neighbour for aging, which returns how long we sleep for */
+ if (IP_NEIGHBOR_AGE_PROBE ==
+ ip_neighbour_age_out (elt->ipne_index, now, &timeout))
+ /* we probed for the oldest entry, sleep for a short time to get to the next */
+ timeout = 0.01;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+static uword
+ip4_neighbor_age_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP4));
+}
+
+static uword
+ip6_neighbor_age_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ return (ip_neighbor_age_loop (vm, rt, f, IP46_TYPE_IP6));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_neighbor_age_process_node,static) = {
+ .function = ip4_neighbor_age_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "ip4-neighbor-age-process",
+};
+VLIB_REGISTER_NODE (ip6_neighbor_age_process_node,static) = {
+ .function = ip6_neighbor_age_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "ip6-neighbor-age-process",
+};
+/* *INDENT-ON* */
+
+int
+ip_neighbor_config (ip46_type_t type, u32 limit, u32 age, bool recycle)
+{
+ ip_neighbor_db[type].ipndb_limit = limit;
+ ip_neighbor_db[type].ipndb_recycle = recycle;
+ ip_neighbor_db[type].ipndb_age = age;
+
+ vlib_process_signal_event (vlib_get_main (),
+ (IP46_TYPE_IP4 == type ?
+ ip4_neighbor_age_process_node.index :
+ ip6_neighbor_age_process_node.index),
+ IP_NEIGHBOR_AGE_PROCESS_WAKEUP, 0);
+
+ return (0);
+}
+
+static clib_error_t *
+ip_neighbor_config_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ ip46_type_t type;
+
+ /* *INDENT-OFF* */
+ FOREACH_IP46_TYPE(type) {
+ vlib_cli_output (vm, "%U:", format_ip46_type, type);
+ vlib_cli_output (vm, " limit:%d, age:%d, recycle:%d",
+ ip_neighbor_db[type].ipndb_limit,
+ ip_neighbor_db[type].ipndb_age,
+ ip_neighbor_db[type].ipndb_recycle);
+ }
+
+ /* *INDENT-ON* */
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_ip_neighbor_cfg_cmd_node, static) = {
+ .path = "show ip neighbor-config",
+ .function = ip_neighbor_config_show,
+ .short_help = "show ip neighbor-config",
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+ip_neighbor_init (vlib_main_t * vm)
+{
+ {
+ ip4_add_del_interface_address_callback_t cb = {
+ .function = ip_neighbor_add_del_interface_address_v4,
+ };
+ vec_add1 (ip4_main.add_del_interface_address_callbacks, cb);
+ }
+ {
+ ip6_add_del_interface_address_callback_t cb = {
+ .function = ip_neighbor_add_del_interface_address_v6,
+ };
+ vec_add1 (ip6_main.add_del_interface_address_callbacks, cb);
+ }
+ {
+ ip4_table_bind_callback_t cb = {
+ .function = ip_neighbor_table_bind_v4,
+ };
+ vec_add1 (ip4_main.table_bind_callbacks, cb);
+ }
+ {
+ ip6_table_bind_callback_t cb = {
+ .function = ip_neighbor_table_bind_v6,
+ };
+ vec_add1 (ip6_main.table_bind_callbacks, cb);
+ }
+ {
+ ethernet_address_change_ctx_t ctx = {
+ .function = ip_neighbor_ethernet_change_mac,
+ .function_opaque = 0,
+ };
+ vec_add1 (ethernet_main.address_change_callbacks, ctx);
+ }
+
+ ipn_logger = vlib_log_register_class ("ip", "neighbor");
+
+ ip46_type_t type;
+
+ FOREACH_IP46_TYPE (type)
+ ip_neighbor_list_head[type] =
+ clib_llist_make_head (ip_neighbor_elt_pool, ipne_anchor);
+
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (ip_neighbor_init) =
+{
+ .runs_after = VLIB_INITS("ip_main_init"),
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor.h b/src/vnet/ip-neighbor/ip_neighbor.h
new file mode 100644
index 00000000000..cb384c5e240
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor.h
@@ -0,0 +1,124 @@
+/*
+ * ip_neighboor.h: ip neighbor generic services
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INCLUDE_IP_NEIGHBOR_H__
+#define __INCLUDE_IP_NEIGHBOR_H__
+
+#include <vnet/ip-neighbor/ip_neighbor_types.h>
+
+#include <vnet/adj/adj.h>
+
+void ip_neighbor_scan_enable_disable (ip_neighbor_scan_arg_t * arg);
+
+
+/*****
+ * APIs external modules can invoke on the neighbor subsystem
+ */
+
+extern ip_neighbor_t *ip_neighbor_get (index_t ipni);
+extern int ip_neighbor_add (const ip46_address_t * ip,
+ ip46_type_t type,
+ const mac_address_t * mac,
+ u32 sw_if_index,
+ ip_neighbor_flags_t flags, u32 * stats_index);
+extern int ip_neighbor_del (const ip46_address_t * ip,
+ ip46_type_t type, u32 sw_if_index);
+
+extern int ip_neighbor_config (ip46_type_t type, u32 limit, u32 age,
+ bool recycle);
+
+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,
+ ip_neighbor_walk_cb_t fn, void *ctx);
+
+extern const ip46_address_t *ip_neighbor_get_ip (const ip_neighbor_t * ipn);
+extern const mac_address_t *ip_neighbor_get_mac (const ip_neighbor_t * ipn);
+extern const u32 ip_neighbor_get_sw_if_index (const ip_neighbor_t * ipn);
+
+extern void ip_neighbor_learn (const ip_neighbor_learn_t * l);
+
+extern void ip_neighbor_update (vnet_main_t * vnm, adj_index_t ai);
+
+extern void ip_neighbor_advertise (vlib_main_t * vm,
+ ip46_type_t tyoe,
+ const ip46_address_t * addr,
+ u32 sw_if_index);
+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);
+
+/**
+ * From the watcher to the API to publish a new neighbor
+ */
+extern void ip_neighbor_handle_event (const ip_neighbor_event_t * ipne);
+
+/**
+ * The set of function that vnet requires from the IP neighbour module.
+ * Note that an implementation of these functions will not exist
+ * if the ip-neighbour plugin is not loaded. so check the error codes!
+ */
+extern int ip4_neighbor_proxy_add (u32 fib_index,
+ const ip4_address_t * start,
+ const ip4_address_t * end);
+extern int ip4_neighbor_proxy_delete (u32 fib_index,
+ const ip4_address_t * start,
+ const ip4_address_t * end);
+extern int ip4_neighbor_proxy_enable (u32 sw_if_index);
+extern int ip4_neighbor_proxy_disable (u32 sw_if_index);
+extern int ip6_neighbor_proxy_add (u32 sw_if_index,
+ const ip6_address_t * addr);
+extern int ip6_neighbor_proxy_del (u32 sw_if_index,
+ const ip6_address_t * addr);
+
+/**
+ * neighbor protocol implementation registration functions
+ * this are provided by ARP and IP-ND
+ */
+typedef int (*ip4_neighbor_proxy_addr_t) (u32 fib_index,
+ const ip4_address_t * start,
+ const ip4_address_t * end);
+typedef int (*ip4_neighbor_proxy_cfg_t) (u32 sw_if_index);
+typedef int (*ip6_neighbor_proxy_cfg_t) (u32 sw_if_index,
+ const ip6_address_t * addr);
+
+/**
+ * Virtual function Table for neighbor protocol implementations to register
+ */
+typedef struct ip_neighbor_vft_t_
+{
+ ip4_neighbor_proxy_cfg_t inv_proxy4_enable;
+ ip4_neighbor_proxy_cfg_t inv_proxy4_disable;
+ ip4_neighbor_proxy_addr_t inv_proxy4_add;
+ ip4_neighbor_proxy_addr_t inv_proxy4_del;
+ ip6_neighbor_proxy_cfg_t inv_proxy6_add;
+ ip6_neighbor_proxy_cfg_t inv_proxy6_del;
+} ip_neighbor_vft_t;
+
+extern void ip_neighbor_register (ip46_type_t type,
+ const ip_neighbor_vft_t * vft);
+
+
+#endif /* __INCLUDE_IP_NEIGHBOR_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_api.c b/src/vnet/ip-neighbor/ip_neighbor_api.c
new file mode 100644
index 00000000000..b066423afe5
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_api.c
@@ -0,0 +1,298 @@
+/*
+ *------------------------------------------------------------------
+ * ip_api.c - vnet ip api
+ *
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <stddef.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.h>
+#include <vnet/ip-neighbor/ip_neighbor_watch.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/ethernet/ethernet_types_api.h>
+
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
+#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
+
+static u16 msg_id_base;
+#define REPLY_MSG_ID_BASE msg_id_base
+
+#include <vlibapi/api_helper_macros.h>
+
+#include <vnet/format_fns.h>
+
+
+static ip46_type_t
+ip46_type_from_af (ip_address_family_t af)
+{
+ return (AF_IP4 == af ? IP46_TYPE_IP4 : IP46_TYPE_IP6);
+}
+
+static vl_api_ip_neighbor_flags_t
+ip_neighbor_flags_encode (ip_neighbor_flags_t f)
+{
+ vl_api_ip_neighbor_flags_t v = IP_API_NEIGHBOR_FLAG_NONE;
+
+ if (f & IP_NEIGHBOR_FLAG_STATIC)
+ v |= IP_API_NEIGHBOR_FLAG_STATIC;
+ if (f & IP_NEIGHBOR_FLAG_NO_FIB_ENTRY)
+ v |= IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY;
+
+ return (v);
+}
+
+static void
+ip_neighbor_encode (vl_api_ip_neighbor_t * api, const ip_neighbor_t * ipn)
+{
+ api->sw_if_index = htonl (ipn->ipn_key->ipnk_sw_if_index);
+ api->flags = ip_neighbor_flags_encode (ipn->ipn_flags);
+
+ ip_address_encode (&ipn->ipn_key->ipnk_ip,
+ ipn->ipn_key->ipnk_type, &api->ip_address);
+ mac_address_encode (&ipn->ipn_mac, api->mac_address);
+}
+
+void
+ip_neighbor_handle_event (const ip_neighbor_event_t * ipne)
+{
+ vl_api_ip_neighbor_event_t *mp;
+ vl_api_registration_t *reg;
+ const ip_neighbor_t *ipn;
+
+ ipn = ip_neighbor_get (ipne->ipne_index);
+
+ if (NULL == ipn)
+ /* Client can cancel, die, etc. */
+ return;
+
+ /* Customer(s) requesting event for this neighbor */
+ reg = vl_api_client_index_to_registration (ipne->ipne_watch.ipw_client);
+ if (!reg)
+ return;
+
+ if (vl_api_can_send_msg (reg))
+ {
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_IP_NEIGHBOR_EVENT + REPLY_MSG_ID_BASE);
+ mp->client_index = ipne->ipne_watch.ipw_client;
+ mp->pid = ipne->ipne_watch.ipw_pid;
+
+ ip_neighbor_encode (&mp->neighbor, ipn);
+
+ vl_api_send_msg (reg, (u8 *) mp);
+ }
+ else
+ {
+ static f64 last_time;
+ /*
+ * Throttle syslog msgs.
+ * It's pretty tempting to just revoke the registration...
+ */
+ if (vlib_time_now (vlib_get_main ()) > last_time + 10.0)
+ {
+ clib_warning ("ip6 nd event for %U to pid %d: queue stuffed!",
+ format_ip46_address, &ipn->ipn_key->ipnk_ip,
+ IP46_TYPE_ANY, ipne->ipne_watch.ipw_pid);
+ last_time = vlib_time_now (vlib_get_main ());
+ }
+ }
+}
+
+typedef struct ip_neighbor_dump_ctx_t_
+{
+ vl_api_registration_t *reg;
+ u32 context;
+} ip_neighbor_dump_ctx_t;
+
+static walk_rc_t
+send_ip_neighbor_details (index_t ipni, void *arg)
+{
+ ip_neighbor_dump_ctx_t *ctx = arg;
+ vl_api_ip_neighbor_details_t *mp;
+ ip_neighbor_t *ipn;
+
+ ipn = ip_neighbor_get (ipni);
+ mp = vl_msg_api_alloc (sizeof (*mp));
+ clib_memset (mp, 0, sizeof (*mp));
+ mp->_vl_msg_id = ntohs (VL_API_IP_NEIGHBOR_DETAILS + REPLY_MSG_ID_BASE);
+ mp->context = ctx->context;
+ ip_neighbor_encode (&mp->neighbor, ipn);
+
+ vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_ip_neighbor_dump_t_handler (vl_api_ip_neighbor_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+ ip_address_family_t af;
+ int rv;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (!reg)
+ return;
+
+ u32 sw_if_index = ntohl (mp->sw_if_index);
+
+ rv = ip_address_family_decode (mp->af, &af);
+
+ if (rv)
+ return;
+
+ ip_neighbor_dump_ctx_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ // walk all neighbours on all interfaces
+ ip_neighbor_walk ((af == AF_IP4 ?
+ IP46_TYPE_IP4 :
+ IP46_TYPE_IP6),
+ sw_if_index, send_ip_neighbor_details, &ctx);
+}
+
+static ip_neighbor_flags_t
+ip_neighbor_flags_decode (vl_api_ip_neighbor_flags_t v)
+{
+ ip_neighbor_flags_t f = IP_NEIGHBOR_FLAG_NONE;
+
+ if (v & IP_API_NEIGHBOR_FLAG_STATIC)
+ f |= IP_NEIGHBOR_FLAG_STATIC;
+ if (v & IP_API_NEIGHBOR_FLAG_NO_FIB_ENTRY)
+ f |= IP_NEIGHBOR_FLAG_NO_FIB_ENTRY;
+
+ return (f);
+}
+
+static void
+vl_api_ip_neighbor_add_del_t_handler (vl_api_ip_neighbor_add_del_t * mp,
+ vlib_main_t * vm)
+{
+ vl_api_ip_neighbor_add_del_reply_t *rmp;
+ ip_neighbor_flags_t flags;
+ u32 stats_index = ~0;
+ ip46_address_t ip = ip46_address_initializer;
+ mac_address_t mac;
+ ip46_type_t type;
+ int rv;
+
+ VALIDATE_SW_IF_INDEX ((&mp->neighbor));
+
+ flags = ip_neighbor_flags_decode (mp->neighbor.flags);
+ type = ip_address_decode (&mp->neighbor.ip_address, &ip);
+ mac_address_decode (mp->neighbor.mac_address, &mac);
+
+ /* must be static or dynamic, default to dynamic */
+ if (!(flags & IP_NEIGHBOR_FLAG_STATIC) &&
+ !(flags & IP_NEIGHBOR_FLAG_DYNAMIC))
+ flags |= IP_NEIGHBOR_FLAG_DYNAMIC;
+
+ /*
+ * there's no validation here of the ND/ARP entry being added.
+ * The expectation is that the FIB will ensure that nothing bad
+ * will come of adding bogus entries.
+ */
+ if (mp->is_add)
+ rv = ip_neighbor_add (&ip, type, &mac,
+ ntohl (mp->neighbor.sw_if_index),
+ flags, &stats_index);
+ else
+ rv = ip_neighbor_del (&ip, type, ntohl (mp->neighbor.sw_if_index));
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2 (VL_API_IP_NEIGHBOR_ADD_DEL_REPLY,
+ ({
+ rmp->stats_index = htonl (stats_index);
+ }));
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_want_ip_neighbor_events_t_handler (vl_api_want_ip_neighbor_events_t *
+ mp)
+{
+ vl_api_want_ip_neighbor_events_reply_t *rmp;
+ ip46_address_t ip;
+ ip46_type_t itype;
+ int rv = 0;
+
+ if (mp->sw_if_index != ~0)
+ VALIDATE_SW_IF_INDEX (mp);
+ itype = ip_address_decode (&mp->ip, &ip);
+
+ ip_neighbor_watcher_t watch = {
+ .ipw_client = mp->client_index,
+ .ipw_pid = mp->pid,
+ };
+
+ if (mp->enable)
+ ip_neighbor_watch (&ip, itype, ntohl (mp->sw_if_index), &watch);
+ else
+ ip_neighbor_unwatch (&ip, itype, ntohl (mp->sw_if_index), &watch);
+
+ BAD_SW_IF_INDEX_LABEL;
+ REPLY_MACRO (VL_API_WANT_IP_NEIGHBOR_EVENTS_REPLY);
+}
+
+static void
+vl_api_ip_neighbor_config_t_handler (vl_api_ip_neighbor_config_t * mp)
+{
+ vl_api_ip_neighbor_config_reply_t *rmp;
+ ip_address_family_t af;
+ int rv;
+
+ rv = ip_address_family_decode (mp->af, &af);
+
+ if (!rv)
+ rv = ip_neighbor_config (ip46_type_from_af (af),
+ ntohl (mp->max_number),
+ ntohl (mp->max_age), mp->recycle);
+
+ REPLY_MACRO (VL_API_IP_NEIGHBOR_CONFIG_REPLY);
+}
+
+#define vl_msg_name_crc_list
+#include <vnet/ip-neighbor/ip_neighbor.api.h>
+#undef vl_msg_name_crc_list
+
+#include <vnet/ip-neighbor/ip_neighbor.api.c>
+
+static clib_error_t *
+ip_neighbor_api_init (vlib_main_t * vm)
+{
+ /* Ask for a correctly-sized block of API message decode slots */
+ msg_id_base = setup_message_id_table ();
+
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (ip_neighbor_api_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_dp.c b/src/vnet/ip-neighbor/ip_neighbor_dp.c
new file mode 100644
index 00000000000..6219a730af4
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_dp.c
@@ -0,0 +1,39 @@
+/*
+ * ip_neighboor.h: ip neighbor generic services
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlibmemory/api.h>
+
+#include <vnet/ip-neighbor/ip_neighbor_dp.h>
+#include <vnet/ip-neighbor/ip_neighbor.h>
+
+/**
+ * APIs invoked by neighbor implementation (i.s. ARP and ND) that can be
+ * called from the DP when the protocol has resolved a neighbor
+ */
+void
+ip_neighbor_learn_dp (const ip_neighbor_learn_t * l)
+{
+ vl_api_rpc_call_main_thread (ip_neighbor_learn, (u8 *) l, sizeof (*l));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_dp.h b/src/vnet/ip-neighbor/ip_neighbor_dp.h
new file mode 100644
index 00000000000..b5664c2d195
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_dp.h
@@ -0,0 +1,38 @@
+/*
+ * ip_neighboor.h: ip neighbor generic services
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INCLUDE_IP_NEIGHBOR_DP_H__
+#define __INCLUDE_IP_NEIGHBOR_DP_H__
+
+#include <vnet/ip-neighbor/ip_neighbor_types.h>
+
+/**
+ * APIs invoked by neighbor implementation (i.s. ARP and ND) that can be
+ * called from the DP when the protocol has resolved a neighbor
+ */
+
+extern void ip_neighbor_learn_dp (const ip_neighbor_learn_t * l);
+
+#endif /* __INCLUDE_IP_NEIGHBOR_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.c b/src/vnet/ip-neighbor/ip_neighbor_types.c
new file mode 100644
index 00000000000..27262a5d62c
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_types.c
@@ -0,0 +1,83 @@
+/*
+ * ip_neighboor.h: ip neighbor generic services
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ip-neighbor/ip_neighbor_types.h>
+
+u8 *
+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;
+}
+
+
+u8 *
+format_ip_neighbor_key (u8 * s, va_list * va)
+{
+ ip_neighbor_key_t *key = va_arg (*va, ip_neighbor_key_t *);
+
+ return (format (s, "[%U, %U]",
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ key->ipnk_sw_if_index,
+ format_ip46_address, &key->ipnk_ip, key->ipnk_type));
+}
+
+u8 *
+format_ip_neighbor_watcher (u8 * s, va_list * va)
+{
+ ip_neighbor_watcher_t *watcher = va_arg (*va, ip_neighbor_watcher_t *);
+
+ return (format (s, "[pid:%d, client:%d]",
+ clib_host_to_net_u32 (watcher->ipw_pid),
+ clib_host_to_net_u32 (watcher->ipw_client)));
+}
+
+u8 *
+format_ip_neighbor (u8 * s, va_list * va)
+{
+ index_t ipni = va_arg (*va, index_t);
+ ip_neighbor_t *ipn;
+
+ ipn = ip_neighbor_get (ipni);
+
+ return (format (s, "%=12U%=40U%=6U%=20U%U",
+ format_vlib_time, vlib_get_main (),
+ ipn->ipn_time_last_updated,
+ format_ip46_address, &ipn->ipn_key->ipnk_ip, IP46_TYPE_ANY,
+ format_ip_neighbor_flags, ipn->ipn_flags,
+ format_mac_address_t, &ipn->ipn_mac,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ ipn->ipn_key->ipnk_sw_if_index));
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_types.h b/src/vnet/ip-neighbor/ip_neighbor_types.h
new file mode 100644
index 00000000000..c6d4e104e8b
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_types.h
@@ -0,0 +1,127 @@
+/*
+ * ip_neighbor.h: ip neighbor generic services
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __INCLUDE_IP_NEIGHBOR_TYPES_H__
+#define __INCLUDE_IP_NEIGHBOR_TYPES_H__
+
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ethernet/mac_address.h>
+#include <vnet/fib/fib_types.h>
+
+#define IP_SCAN_DISABLED 0
+#define IP_SCAN_V4_NEIGHBORS (1 << 0)
+#define IP_SCAN_V6_NEIGHBORS (1 << 1)
+#define IP_SCAN_V46_NEIGHBORS (IP_SCAN_V4_NEIGHBORS | IP_SCAN_V6_NEIGHBORS)
+
+typedef struct
+{
+ u8 mode; /* 0: disable, 1: ip4, 2: ip6, 3: both */
+ u8 scan_interval; /* neighbor scan interval in minutes */
+ u8 max_proc_time; /* max processing time per run, in usecs */
+ u8 max_update; /* max probe/delete operations per run */
+ u8 scan_int_delay; /* delay in msecs, to resume scan on max */
+ u8 stale_threshold; /* Threshold in minutes to delete nei entry */
+} ip_neighbor_scan_arg_t;
+
+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),
+} __attribute__ ((packed)) ip_neighbor_flags_t;
+
+typedef struct ip_neighbor_watcher_t_
+{
+ u32 ipw_pid;
+ u32 ipw_client;
+} ip_neighbor_watcher_t;
+
+extern u8 *format_ip_neighbor_watcher (u8 * s, va_list * args);
+
+typedef struct ip_neighbor_key_t_
+{
+ ip46_address_t ipnk_ip;
+ ip46_type_t ipnk_type;
+ u32 ipnk_sw_if_index;
+} ip_neighbor_key_t;
+
+/**
+ * A representation of an IP neighbour/peer
+ */
+typedef struct ip_neighbor_t_
+{
+ /**
+ * The idempotent key
+ */
+ ip_neighbor_key_t *ipn_key;
+
+ /**
+ * The learned MAC address of the neighbour
+ */
+ mac_address_t ipn_mac;
+
+ /**
+ * Falgs for this object
+ */
+ ip_neighbor_flags_t ipn_flags;
+
+ /**
+ * Aging related data
+ * - last time the neighbour was probed
+ * - number of probes - 3 and it's dead
+ */
+ f64 ipn_time_last_updated;
+ u8 ipn_n_probes;
+ index_t ipn_elt;
+
+ /**
+ * The index of the adj fib created for this neighbour
+ */
+ fib_node_index_t ipn_fib_entry_index;
+} ip_neighbor_t;
+
+extern u8 *format_ip_neighbor_flags (u8 * s, va_list * args);
+extern u8 *format_ip_neighbor_key (u8 * s, va_list * args);
+extern u8 *format_ip_neighbor (u8 * s, va_list * args);
+
+extern ip_neighbor_t *ip_neighbor_get (index_t ipni);
+
+typedef struct ip_neighbor_learn_t_
+{
+ ip46_address_t ip;
+ ip46_type_t type;
+ mac_address_t mac;
+ u32 sw_if_index;
+} ip_neighbor_learn_t;
+
+typedef struct ip_neighbor_event_t_
+{
+ ip_neighbor_watcher_t ipne_watch;
+ index_t ipne_index;
+} ip_neighbor_event_t;
+
+#endif /* __INCLUDE_IP_NEIGHBOR_H__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_watch.c b/src/vnet/ip-neighbor/ip_neighbor_watch.c
new file mode 100644
index 00000000000..7464ee62189
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_watch.c
@@ -0,0 +1,283 @@
+/*
+ * ip_neighboor_watch.c; IP neighbor watching
+ *
+ * Copyright (c) 2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ip-neighbor/ip_neighbor.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vnet/ethernet/ethernet_types_api.h>
+
+#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
+#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
+
+#include <vlibmemory/api.h>
+
+/**
+ * Database of registered watchers
+ * The key for a watcher is {type, sw_if_index, addreess}
+ * interface=~0 / address=all-zeros imples any.
+ */
+typedef struct ip_neighbor_watch_db_t_
+{
+ mhash_t ipnwdb_hash;
+} ip_neighbor_watch_db_t;
+
+static ip_neighbor_watch_db_t ipnw_db;
+
+static uword
+ip_neighbor_event_process (vlib_main_t * vm,
+ vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+ ip_neighbor_event_t *ipne, *ipnes = NULL;
+ uword event_type = ~0;
+
+ while (1)
+ {
+ vlib_process_wait_for_event (vm);
+
+ ipnes = vlib_process_get_event_data (vm, &event_type);
+
+ switch (event_type)
+ {
+ default:
+ vec_foreach (ipne, ipnes) ip_neighbor_handle_event (ipne);
+ break;
+
+ case ~0:
+ /* timeout - */
+ break;
+ }
+
+ vec_reset_length (ipnes);
+ }
+ return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip_neighbor_event_process_node) = {
+ .function = ip_neighbor_event_process,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name = "ip-neighbor-event",
+};
+/* *INDENT-ON* */
+
+
+static clib_error_t *
+want_ip_neighbor_events_reaper (u32 client_index)
+{
+ ip_neighbor_key_t *key, *empty_keys = NULL;
+ ip_neighbor_watcher_t *watchers;
+ uword *v;
+ i32 pos;
+
+ /* walk the entire IP neighbour DB and removes the client's registrations */
+ /* *INDENT-OFF* */
+ mhash_foreach(key, v, &ipnw_db.ipnwdb_hash,
+ ({
+ watchers = (ip_neighbor_watcher_t*) *v;
+
+ vec_foreach_index_backwards (pos, watchers) {
+ if (watchers[pos].ipw_client == client_index)
+ vec_del1(watchers, pos);
+ }
+
+ if (vec_len(watchers) == 0)
+ vec_add1 (empty_keys, *key);
+ }));
+ /* *INDENT-OFF* */
+
+ vec_foreach (key, empty_keys)
+ mhash_unset (&ipnw_db.ipnwdb_hash, key, NULL);
+ vec_free (empty_keys);
+ return (NULL);
+}
+
+VL_MSG_API_REAPER_FUNCTION (want_ip_neighbor_events_reaper);
+
+static int
+ip_neighbor_watch_cmp (const ip_neighbor_watcher_t * w1,
+ const ip_neighbor_watcher_t * w2)
+{
+ return (0 == clib_memcmp (w1, w2, sizeof(*w1)));
+}
+
+void
+ip_neighbor_watch (const ip46_address_t * ip,
+ ip46_type_t type,
+ u32 sw_if_index,
+ const ip_neighbor_watcher_t * watch)
+{
+ ip_neighbor_key_t key = {
+ .ipnk_ip = *ip,
+ .ipnk_sw_if_index = (sw_if_index == 0 ? ~0 : sw_if_index),
+ .ipnk_type = type,
+ };
+ ip_neighbor_watcher_t *ipws = NULL;
+ uword *p;
+
+ p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
+
+ if (p)
+ {
+ ipws = (ip_neighbor_watcher_t*) p[0];
+
+ if (~0 != vec_search_with_function (ipws, watch,
+ ip_neighbor_watch_cmp))
+ /* duplicate */
+ return;
+ }
+
+ vec_add1 (ipws, *watch);
+
+ mhash_set (&ipnw_db.ipnwdb_hash, &key, (uword) ipws, NULL);
+}
+
+void
+ip_neighbor_unwatch (const ip46_address_t * ip,
+ ip46_type_t type,
+ u32 sw_if_index,
+ const ip_neighbor_watcher_t * watch)
+{
+ ip_neighbor_key_t key = {
+ .ipnk_ip = *ip,
+ .ipnk_sw_if_index = (sw_if_index == 0 ? ~0 : sw_if_index),
+ .ipnk_type = type,
+ };
+ ip_neighbor_watcher_t *ipws = NULL;
+ uword *p;
+ u32 pos;
+
+ p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
+
+ if (!p)
+ return;
+
+ ipws = (ip_neighbor_watcher_t*) p[0];
+
+ pos = vec_search_with_function (ipws, watch, ip_neighbor_watch_cmp);
+
+ if (~0 == pos)
+ return;
+
+ vec_del1 (ipws, pos);
+
+ if (vec_len(ipws) == 0)
+ mhash_unset (&ipnw_db.ipnwdb_hash, &key, NULL);
+}
+
+static void
+ip_neighbor_signal (ip_neighbor_watcher_t *watchers, index_t ipni)
+{
+ ip_neighbor_watcher_t *watcher;
+
+ vec_foreach (watcher, watchers) {
+ ip_neighbor_event_t *ipne;
+
+ ipne = vlib_process_signal_event_data (vlib_get_main(),
+ ip_neighbor_event_process_node.index,
+ 0, 1, sizeof(*ipne));
+ ipne->ipne_watch = *watcher;
+ ipne->ipne_index = ipni;
+ }
+}
+
+void
+ip_neighbor_publish (index_t ipni)
+{
+ const ip_neighbor_t *ipn;
+ ip_neighbor_key_t key;
+ uword *p;
+
+ ipn = ip_neighbor_get (ipni);
+
+ clib_memcpy (&key, ipn->ipn_key, sizeof (key));
+
+ /* Search the DB from longest to shortest key */
+ p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
+
+ if (p) {
+ ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni);
+ }
+
+ ip46_address_reset (&key.ipnk_ip);
+ p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
+
+ if (p) {
+ ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni);
+ }
+
+ key.ipnk_sw_if_index = ~0;
+ p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
+
+ if (p) {
+ ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni);
+ }
+}
+
+static clib_error_t *
+ip_neighbor_watchers_show (vlib_main_t * vm,
+ unformat_input_t * input,
+ vlib_cli_command_t * cmd)
+{
+ ip_neighbor_watcher_t *watchers, *watcher;
+ ip_neighbor_key_t *key;
+ uword *v;
+
+ /* *INDENT-OFF* */
+ mhash_foreach(key, v, &ipnw_db.ipnwdb_hash,
+ ({
+ watchers = (ip_neighbor_watcher_t*) *v;
+
+ ASSERT(vec_len(watchers));
+ vlib_cli_output (vm, "Key: %U", format_ip_neighbor_key, key);
+
+ vec_foreach (watcher, watchers)
+ vlib_cli_output (vm, " %U", format_ip_neighbor_watcher, watcher);
+ }));
+ /* *INDENT-ON* */
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (show_ip_neighbor_watchers_cmd_node, static) = {
+ .path = "show ip neighbor-watcher",
+ .function = ip_neighbor_watchers_show,
+ .short_help = "show ip neighbors-watcher",
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+ip_neighbor_watch_init (vlib_main_t * vm)
+{
+ mhash_init (&ipnw_db.ipnwdb_hash,
+ sizeof (ip_neighbor_watcher_t *), sizeof (ip_neighbor_key_t));
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (ip_neighbor_watch_init) =
+{
+ .runs_after = VLIB_INITS("ip_neighbor_init"),
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/ip-neighbor/ip_neighbor_watch.h b/src/vnet/ip-neighbor/ip_neighbor_watch.h
new file mode 100644
index 00000000000..91d9f6fe12f
--- /dev/null
+++ b/src/vnet/ip-neighbor/ip_neighbor_watch.h
@@ -0,0 +1,42 @@
+/*
+ * ip_neighboor_watch.h: ip neighbor event handling
+ *
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __IP_NEIGHBOR_WATCH_H__
+#define __IP_NEIGHBOR_WATCH_H__
+
+#include <vnet/ip-neighbor/ip_neighbor_types.h>
+
+extern void ip_neighbor_watch (const ip46_address_t * ip,
+ ip46_type_t type,
+ u32 sw_if_index,
+ const ip_neighbor_watcher_t * watch);
+extern void ip_neighbor_unwatch (const ip46_address_t * ip,
+ ip46_type_t type,
+ u32 sw_if_index,
+ const ip_neighbor_watcher_t * watch);
+
+extern void ip_neighbor_publish (index_t ipni);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */