mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 01:43:38 +00:00 
			
		
		
		
	Done with a combination of regex'ing and banging my head against a wall. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
		
			
				
	
	
		
			274 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			274 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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 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);
 | 
						|
}
 |