summaryrefslogtreecommitdiffstats
path: root/zebra/zebra_neigh.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra_neigh.c')
-rw-r--r--zebra/zebra_neigh.c286
1 files changed, 286 insertions, 0 deletions
diff --git a/zebra/zebra_neigh.c b/zebra/zebra_neigh.c
new file mode 100644
index 0000000..941088a
--- /dev/null
+++ b/zebra/zebra_neigh.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Zebra neighbor table management
+ *
+ * Copyright (C) 2021 Nvidia
+ * Anuradha Karuppiah
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "hash.h"
+#include "if.h"
+#include "jhash.h"
+#include "linklist.h"
+#include "log.h"
+#include "memory.h"
+#include "prefix.h"
+#include "stream.h"
+#include "table.h"
+
+#include "zebra/zebra_router.h"
+#include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/zebra_errors.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_neigh.h"
+#include "zebra/zebra_pbr.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZNEIGH_INFO, "Zebra neigh table");
+DEFINE_MTYPE_STATIC(ZEBRA, ZNEIGH_ENT, "Zebra neigh entry");
+
+static int zebra_neigh_rb_cmp(const struct zebra_neigh_ent *n1,
+ const struct zebra_neigh_ent *n2)
+{
+ if (n1->ifindex < n2->ifindex)
+ return -1;
+
+ if (n1->ifindex > n2->ifindex)
+ return 1;
+
+ if (n1->ip.ipa_type < n2->ip.ipa_type)
+ return -1;
+
+ if (n1->ip.ipa_type > n2->ip.ipa_type)
+ return 1;
+
+ if (n1->ip.ipa_type == AF_INET) {
+ if (n1->ip.ipaddr_v4.s_addr < n2->ip.ipaddr_v4.s_addr)
+ return -1;
+
+ if (n1->ip.ipaddr_v4.s_addr > n2->ip.ipaddr_v4.s_addr)
+ return 1;
+
+ return 0;
+ }
+
+ return memcmp(&n1->ip.ipaddr_v6, &n2->ip.ipaddr_v6, IPV6_MAX_BYTELEN);
+}
+RB_GENERATE(zebra_neigh_rb_head, zebra_neigh_ent, rb_node, zebra_neigh_rb_cmp);
+
+static struct zebra_neigh_ent *zebra_neigh_find(ifindex_t ifindex,
+ struct ipaddr *ip)
+{
+ struct zebra_neigh_ent tmp;
+
+ tmp.ifindex = ifindex;
+ memcpy(&tmp.ip, ip, sizeof(*ip));
+ return RB_FIND(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, &tmp);
+}
+
+static struct zebra_neigh_ent *
+zebra_neigh_new(ifindex_t ifindex, struct ipaddr *ip, struct ethaddr *mac)
+{
+ struct zebra_neigh_ent *n;
+
+ n = XCALLOC(MTYPE_ZNEIGH_ENT, sizeof(struct zebra_neigh_ent));
+
+ memcpy(&n->ip, ip, sizeof(*ip));
+ n->ifindex = ifindex;
+ if (mac) {
+ memcpy(&n->mac, mac, sizeof(*mac));
+ n->flags |= ZEBRA_NEIGH_ENT_ACTIVE;
+ }
+
+ /* Add to rb_tree */
+ if (RB_INSERT(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, n)) {
+ XFREE(MTYPE_ZNEIGH_ENT, n);
+ return NULL;
+ }
+
+ /* Initialise the pbr rule list */
+ n->pbr_rule_list = list_new();
+ listset_app_node_mem(n->pbr_rule_list);
+
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh new if %d %pIA %pEA", n->ifindex,
+ &n->ip, &n->mac);
+
+ return n;
+}
+
+static void zebra_neigh_pbr_rules_update(struct zebra_neigh_ent *n)
+{
+ struct zebra_pbr_rule *rule;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(n->pbr_rule_list, node, rule))
+ dplane_pbr_rule_update(rule, rule);
+}
+
+static void zebra_neigh_free(struct zebra_neigh_ent *n)
+{
+ if (listcount(n->pbr_rule_list)) {
+ /* if rules are still using the neigh mark it as inactive and
+ * update the dataplane
+ */
+ if (n->flags & ZEBRA_NEIGH_ENT_ACTIVE) {
+ n->flags &= ~ZEBRA_NEIGH_ENT_ACTIVE;
+ memset(&n->mac, 0, sizeof(n->mac));
+ }
+ zebra_neigh_pbr_rules_update(n);
+ return;
+ }
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh free if %d %pIA %pEA", n->ifindex,
+ &n->ip, &n->mac);
+
+ /* cleanup resources maintained against the neigh */
+ list_delete(&n->pbr_rule_list);
+
+ RB_REMOVE(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, n);
+
+ XFREE(MTYPE_ZNEIGH_ENT, n);
+}
+
+/* kernel neigh del */
+void zebra_neigh_del(struct interface *ifp, struct ipaddr *ip)
+{
+ struct zebra_neigh_ent *n;
+
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh del if %s/%d %pIA", ifp->name,
+ ifp->ifindex, ip);
+
+ n = zebra_neigh_find(ifp->ifindex, ip);
+ if (!n)
+ return;
+ zebra_neigh_free(n);
+}
+
+/* kernel neigh delete all for a given interface */
+void zebra_neigh_del_all(struct interface *ifp)
+{
+ struct zebra_neigh_ent *n, *nn;
+
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh delete all for interface %s/%d",
+ ifp->name, ifp->ifindex);
+
+ RB_FOREACH_SAFE (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree, nn)
+ zebra_neigh_del(ifp, &n->ip);
+}
+
+/* kernel neigh add */
+void zebra_neigh_add(struct interface *ifp, struct ipaddr *ip,
+ struct ethaddr *mac)
+{
+ struct zebra_neigh_ent *n;
+
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh add if %s/%d %pIA %pEA", ifp->name,
+ ifp->ifindex, ip, mac);
+
+ n = zebra_neigh_find(ifp->ifindex, ip);
+ if (n) {
+ if (!memcmp(&n->mac, mac, sizeof(*mac)))
+ return;
+
+ memcpy(&n->mac, mac, sizeof(*mac));
+ n->flags |= ZEBRA_NEIGH_ENT_ACTIVE;
+
+ /* update rules linked to the neigh */
+ zebra_neigh_pbr_rules_update(n);
+ } else {
+ zebra_neigh_new(ifp->ifindex, ip, mac);
+ }
+}
+
+void zebra_neigh_deref(struct zebra_pbr_rule *rule)
+{
+ struct zebra_neigh_ent *n = rule->action.neigh;
+
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh deref if %d %pIA by pbr rule %u",
+ n->ifindex, &n->ip, rule->rule.seq);
+
+ rule->action.neigh = NULL;
+ /* remove rule from the list and free if it is inactive */
+ list_delete_node(n->pbr_rule_list, &rule->action.neigh_listnode);
+ if (!(n->flags & ZEBRA_NEIGH_ENT_ACTIVE))
+ zebra_neigh_free(n);
+}
+
+/* XXX - this needs to work with evpn's neigh read */
+static void zebra_neigh_read_on_first_ref(void)
+{
+ static bool neigh_read_done;
+
+ if (!neigh_read_done) {
+ neigh_read(zebra_ns_lookup(NS_DEFAULT));
+ neigh_read_done = true;
+ }
+}
+
+void zebra_neigh_ref(int ifindex, struct ipaddr *ip,
+ struct zebra_pbr_rule *rule)
+{
+ struct zebra_neigh_ent *n;
+
+ if (IS_ZEBRA_DEBUG_NEIGH)
+ zlog_debug("zebra neigh ref if %d %pIA by pbr rule %u", ifindex,
+ ip, rule->rule.seq);
+
+ zebra_neigh_read_on_first_ref();
+ n = zebra_neigh_find(ifindex, ip);
+ if (!n)
+ n = zebra_neigh_new(ifindex, ip, NULL);
+
+ /* link the pbr entry to the neigh */
+ if (rule->action.neigh == n)
+ return;
+
+ if (rule->action.neigh)
+ zebra_neigh_deref(rule);
+
+ rule->action.neigh = n;
+ listnode_init(&rule->action.neigh_listnode, rule);
+ listnode_add(n->pbr_rule_list, &rule->action.neigh_listnode);
+}
+
+static void zebra_neigh_show_one(struct vty *vty, struct zebra_neigh_ent *n)
+{
+ char mac_buf[ETHER_ADDR_STRLEN];
+ char ip_buf[INET6_ADDRSTRLEN];
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+ n->ifindex);
+ ipaddr2str(&n->ip, ip_buf, sizeof(ip_buf));
+ prefix_mac2str(&n->mac, mac_buf, sizeof(mac_buf));
+ vty_out(vty, "%-20s %-30s %-18s %u\n", ifp ? ifp->name : "-", ip_buf,
+ mac_buf, listcount(n->pbr_rule_list));
+}
+
+void zebra_neigh_show(struct vty *vty)
+{
+ struct zebra_neigh_ent *n;
+
+ vty_out(vty, "%-20s %-30s %-18s %s\n", "Interface", "Neighbor", "MAC",
+ "#Rules");
+ RB_FOREACH (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree)
+ zebra_neigh_show_one(vty, n);
+}
+
+void zebra_neigh_init(void)
+{
+ zneigh_info = XCALLOC(MTYPE_ZNEIGH_INFO, sizeof(*zrouter.neigh_info));
+ RB_INIT(zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree);
+}
+
+void zebra_neigh_terminate(void)
+{
+ struct zebra_neigh_ent *n, *next;
+
+ if (!zrouter.neigh_info)
+ return;
+
+ RB_FOREACH_SAFE (n, zebra_neigh_rb_head, &zneigh_info->neigh_rb_tree,
+ next)
+ zebra_neigh_free(n);
+ XFREE(MTYPE_ZNEIGH_INFO, zneigh_info);
+}