]> git.puffer.fish Git - mirror/frr.git/commitdiff
zebra: free neighbor state before exit to avoid memleaks 11608/head
authorChristian Hopps <chopps@labn.net>
Thu, 14 Jul 2022 16:20:01 +0000 (12:20 -0400)
committerMergify <37929162+mergify[bot]@users.noreply.github.com>
Thu, 14 Jul 2022 20:05:07 +0000 (20:05 +0000)
Signed-off-by: Christian Hopps <chopps@labn.net>
(cherry picked from commit 11c9ab3202b2cefca0daca2ca1883e6772524a73)

# Conflicts:
# zebra/zebra_neigh.c

zebra/zebra_neigh.c [new file with mode: 0644]

diff --git a/zebra/zebra_neigh.c b/zebra/zebra_neigh.c
new file mode 100644 (file)
index 0000000..09e9a62
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Zebra neighbor table management
+ *
+ * Copyright (C) 2021 Nvidia
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#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 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);
+}