diff options
Diffstat (limited to 'zebra/zebra_neigh.c')
-rw-r--r-- | zebra/zebra_neigh.c | 286 |
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); +} |