mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-11-04 06:38:04 +00:00 
			
		
		
		
	There is a possibility that the same line can be matched as a command in some node and its parent node. In this case, when reading the config, this line is always executed as a command of the child node. For example, with the following config: ``` router ospf network 193.168.0.0/16 area 0 ! mpls ldp discovery hello interval 111 ! ``` Line `mpls ldp` is processed as command `mpls ldp-sync` inside the `router ospf` node. This leads to a complete loss of `mpls ldp` node configuration. To eliminate this issue and all possible similar issues, let's print an explicit "exit" at the end of every node config. This commit also changes indentation for a couple of existing exit commands so that all existing commands are on the same level as their corresponding node-entering commands. Fixes #9206. Signed-off-by: Igor Ryzhov <iryzhov@nfware.com>
		
			
				
	
	
		
			1351 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1351 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Nexthop Group structure definition.
 | 
						|
 * Copyright (C) 2018 Cumulus Networks, Inc.
 | 
						|
 *                    Donald Sharp
 | 
						|
 *
 | 
						|
 * This program 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 of the License, or (at your option)
 | 
						|
 * any later version.
 | 
						|
 *
 | 
						|
 * This program 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.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License along
 | 
						|
 * with this program; see the file COPYING; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | 
						|
 */
 | 
						|
#include <zebra.h>
 | 
						|
 | 
						|
#include <vrf.h>
 | 
						|
#include <sockunion.h>
 | 
						|
#include <nexthop.h>
 | 
						|
#include <nexthop_group.h>
 | 
						|
#include <nexthop_group_private.h>
 | 
						|
#include <vty.h>
 | 
						|
#include <command.h>
 | 
						|
#include <jhash.h>
 | 
						|
 | 
						|
#ifndef VTYSH_EXTRACT_PL
 | 
						|
#include "lib/nexthop_group_clippy.c"
 | 
						|
#endif
 | 
						|
 | 
						|
DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group");
 | 
						|
 | 
						|
/*
 | 
						|
 * Internal struct used to hold nhg config strings
 | 
						|
 */
 | 
						|
struct nexthop_hold {
 | 
						|
	char *nhvrf_name;
 | 
						|
	union sockunion *addr;
 | 
						|
	char *intf;
 | 
						|
	bool onlink;
 | 
						|
	char *labels;
 | 
						|
	uint32_t weight;
 | 
						|
	char *backup_str;
 | 
						|
};
 | 
						|
 | 
						|
struct nexthop_group_hooks {
 | 
						|
	void (*new)(const char *name);
 | 
						|
	void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
 | 
						|
			    const struct nexthop *nhop);
 | 
						|
	void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
 | 
						|
			    const struct nexthop *nhop);
 | 
						|
	void (*delete)(const char *name);
 | 
						|
};
 | 
						|
 | 
						|
static struct nexthop_group_hooks nhg_hooks;
 | 
						|
 | 
						|
static inline int
 | 
						|
nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
 | 
						|
			  const struct nexthop_group_cmd *nhgc2);
 | 
						|
RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry,
 | 
						|
	    nexthop_group_cmd_compare)
 | 
						|
 | 
						|
static struct nhgc_entry_head nhgc_entries;
 | 
						|
 | 
						|
static inline int
 | 
						|
nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,
 | 
						|
			  const struct nexthop_group_cmd *nhgc2)
 | 
						|
{
 | 
						|
	return strcmp(nhgc1->name, nhgc2->name);
 | 
						|
}
 | 
						|
 | 
						|
static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop = nhg->nexthop;
 | 
						|
 | 
						|
	while (nexthop && nexthop->next)
 | 
						|
		nexthop = nexthop->next;
 | 
						|
 | 
						|
	return nexthop;
 | 
						|
}
 | 
						|
 | 
						|
uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nhop;
 | 
						|
	uint8_t num = 0;
 | 
						|
 | 
						|
	for (ALL_NEXTHOPS_PTR(nhg, nhop))
 | 
						|
		num++;
 | 
						|
 | 
						|
	return num;
 | 
						|
}
 | 
						|
 | 
						|
uint8_t nexthop_group_nexthop_num_no_recurse(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nhop;
 | 
						|
	uint8_t num = 0;
 | 
						|
 | 
						|
	for (nhop = nhg->nexthop; nhop; nhop = nhop->next)
 | 
						|
		num++;
 | 
						|
 | 
						|
	return num;
 | 
						|
}
 | 
						|
 | 
						|
uint8_t nexthop_group_active_nexthop_num(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nhop;
 | 
						|
	uint8_t num = 0;
 | 
						|
 | 
						|
	for (ALL_NEXTHOPS_PTR(nhg, nhop)) {
 | 
						|
		if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
 | 
						|
			num++;
 | 
						|
	}
 | 
						|
 | 
						|
	return num;
 | 
						|
}
 | 
						|
 | 
						|
uint8_t
 | 
						|
nexthop_group_active_nexthop_num_no_recurse(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nhop;
 | 
						|
	uint8_t num = 0;
 | 
						|
 | 
						|
	for (nhop = nhg->nexthop; nhop; nhop = nhop->next) {
 | 
						|
		if (CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_ACTIVE))
 | 
						|
			num++;
 | 
						|
	}
 | 
						|
 | 
						|
	return num;
 | 
						|
}
 | 
						|
 | 
						|
struct nexthop *nexthop_exists(const struct nexthop_group *nhg,
 | 
						|
			       const struct nexthop *nh)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop;
 | 
						|
 | 
						|
	for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
 | 
						|
		if (nexthop_same(nh, nexthop))
 | 
						|
			return nexthop;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Helper that locates a nexthop in an nhg config list. Note that
 | 
						|
 * this uses a specific matching / equality rule that's different from
 | 
						|
 * the complete match performed by 'nexthop_same()'.
 | 
						|
 */
 | 
						|
static struct nexthop *nhg_nh_find(const struct nexthop_group *nhg,
 | 
						|
				   const struct nexthop *nh)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	/* We compare: vrf, gateway, and interface */
 | 
						|
 | 
						|
	for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
 | 
						|
 | 
						|
		/* Compare vrf and type */
 | 
						|
		if (nexthop->vrf_id != nh->vrf_id)
 | 
						|
			continue;
 | 
						|
		if (nexthop->type != nh->type)
 | 
						|
			continue;
 | 
						|
 | 
						|
		/* Compare gateway */
 | 
						|
		switch (nexthop->type) {
 | 
						|
		case NEXTHOP_TYPE_IPV4:
 | 
						|
		case NEXTHOP_TYPE_IPV6:
 | 
						|
			ret = nexthop_g_addr_cmp(nexthop->type,
 | 
						|
						 &nexthop->gate, &nh->gate);
 | 
						|
			if (ret != 0)
 | 
						|
				continue;
 | 
						|
			break;
 | 
						|
		case NEXTHOP_TYPE_IPV4_IFINDEX:
 | 
						|
		case NEXTHOP_TYPE_IPV6_IFINDEX:
 | 
						|
			ret = nexthop_g_addr_cmp(nexthop->type,
 | 
						|
						 &nexthop->gate, &nh->gate);
 | 
						|
			if (ret != 0)
 | 
						|
				continue;
 | 
						|
			/* Intentional Fall-Through */
 | 
						|
		case NEXTHOP_TYPE_IFINDEX:
 | 
						|
			if (nexthop->ifindex != nh->ifindex)
 | 
						|
				continue;
 | 
						|
			break;
 | 
						|
		case NEXTHOP_TYPE_BLACKHOLE:
 | 
						|
			if (nexthop->bh_type != nh->bh_type)
 | 
						|
				continue;
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		return nexthop;
 | 
						|
	}
 | 
						|
 | 
						|
	return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static bool
 | 
						|
nexthop_group_equal_common(const struct nexthop_group *nhg1,
 | 
						|
			   const struct nexthop_group *nhg2,
 | 
						|
			   uint8_t (*nexthop_group_nexthop_num_func)(
 | 
						|
				   const struct nexthop_group *nhg))
 | 
						|
{
 | 
						|
	if (nhg1 && !nhg2)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (!nhg1 && nhg2)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (nhg1 == nhg2)
 | 
						|
		return true;
 | 
						|
 | 
						|
	if (nexthop_group_nexthop_num_func(nhg1)
 | 
						|
	    != nexthop_group_nexthop_num_func(nhg2))
 | 
						|
		return false;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/* This assumes ordered */
 | 
						|
bool nexthop_group_equal_no_recurse(const struct nexthop_group *nhg1,
 | 
						|
				    const struct nexthop_group *nhg2)
 | 
						|
{
 | 
						|
	struct nexthop *nh1 = NULL;
 | 
						|
	struct nexthop *nh2 = NULL;
 | 
						|
 | 
						|
	if (!nexthop_group_equal_common(nhg1, nhg2,
 | 
						|
					&nexthop_group_nexthop_num_no_recurse))
 | 
						|
		return false;
 | 
						|
 | 
						|
	for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
 | 
						|
	     nh1 = nh1->next, nh2 = nh2->next) {
 | 
						|
		if (nh1 && !nh2)
 | 
						|
			return false;
 | 
						|
		if (!nh1 && nh2)
 | 
						|
			return false;
 | 
						|
		if (!nexthop_same(nh1, nh2))
 | 
						|
			return false;
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/* This assumes ordered */
 | 
						|
bool nexthop_group_equal(const struct nexthop_group *nhg1,
 | 
						|
			 const struct nexthop_group *nhg2)
 | 
						|
{
 | 
						|
	struct nexthop *nh1 = NULL;
 | 
						|
	struct nexthop *nh2 = NULL;
 | 
						|
 | 
						|
	if (!nexthop_group_equal_common(nhg1, nhg2, &nexthop_group_nexthop_num))
 | 
						|
		return false;
 | 
						|
 | 
						|
	for (nh1 = nhg1->nexthop, nh2 = nhg2->nexthop; nh1 || nh2;
 | 
						|
	     nh1 = nexthop_next(nh1), nh2 = nexthop_next(nh2)) {
 | 
						|
		if (nh1 && !nh2)
 | 
						|
			return false;
 | 
						|
		if (!nh1 && nh2)
 | 
						|
			return false;
 | 
						|
		if (!nexthop_same(nh1, nh2))
 | 
						|
			return false;
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
struct nexthop_group *nexthop_group_new(void)
 | 
						|
{
 | 
						|
	return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group));
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_copy(struct nexthop_group *to,
 | 
						|
			const struct nexthop_group *from)
 | 
						|
{
 | 
						|
	/* Copy everything, including recursive info */
 | 
						|
	copy_nexthops(&to->nexthop, from->nexthop, NULL);
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_delete(struct nexthop_group **nhg)
 | 
						|
{
 | 
						|
	/* OK to call with NULL group */
 | 
						|
	if ((*nhg) == NULL)
 | 
						|
		return;
 | 
						|
 | 
						|
	if ((*nhg)->nexthop)
 | 
						|
		nexthops_free((*nhg)->nexthop);
 | 
						|
 | 
						|
	XFREE(MTYPE_NEXTHOP_GROUP, *nhg);
 | 
						|
}
 | 
						|
 | 
						|
/* Add nexthop to the end of a nexthop list.  */
 | 
						|
void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)
 | 
						|
{
 | 
						|
	struct nexthop *last;
 | 
						|
 | 
						|
	for (last = *target; last && last->next; last = last->next)
 | 
						|
		;
 | 
						|
	if (last)
 | 
						|
		last->next = nexthop;
 | 
						|
	else
 | 
						|
		*target = nexthop;
 | 
						|
	nexthop->prev = last;
 | 
						|
}
 | 
						|
 | 
						|
/* Add nexthop to sorted list of nexthops */
 | 
						|
static void _nexthop_add_sorted(struct nexthop **head,
 | 
						|
				struct nexthop *nexthop)
 | 
						|
{
 | 
						|
	struct nexthop *position, *prev;
 | 
						|
 | 
						|
	assert(!nexthop->next);
 | 
						|
 | 
						|
	for (position = *head, prev = NULL; position;
 | 
						|
	     prev = position, position = position->next) {
 | 
						|
		if (nexthop_cmp(position, nexthop) > 0) {
 | 
						|
			nexthop->next = position;
 | 
						|
			nexthop->prev = prev;
 | 
						|
 | 
						|
			if (nexthop->prev)
 | 
						|
				nexthop->prev->next = nexthop;
 | 
						|
			else
 | 
						|
				*head = nexthop;
 | 
						|
 | 
						|
			position->prev = nexthop;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	nexthop->prev = prev;
 | 
						|
	if (prev)
 | 
						|
		prev->next = nexthop;
 | 
						|
	else
 | 
						|
		*head = nexthop;
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_add_sorted(struct nexthop_group *nhg,
 | 
						|
			      struct nexthop *nexthop)
 | 
						|
{
 | 
						|
	struct nexthop *tail;
 | 
						|
 | 
						|
	assert(!nexthop->next);
 | 
						|
 | 
						|
	/* Try to just append to the end first;
 | 
						|
	 * trust the list is already sorted
 | 
						|
	 */
 | 
						|
	tail = nexthop_group_tail(nhg);
 | 
						|
 | 
						|
	if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
 | 
						|
		tail->next = nexthop;
 | 
						|
		nexthop->prev = tail;
 | 
						|
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	_nexthop_add_sorted(&nhg->nexthop, nexthop);
 | 
						|
}
 | 
						|
 | 
						|
/* Delete nexthop from a nexthop list.  */
 | 
						|
void _nexthop_del(struct nexthop_group *nhg, struct nexthop *nh)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop;
 | 
						|
 | 
						|
	for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) {
 | 
						|
		if (nexthop_same(nh, nexthop))
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	assert(nexthop);
 | 
						|
 | 
						|
	if (nexthop->prev)
 | 
						|
		nexthop->prev->next = nexthop->next;
 | 
						|
	else
 | 
						|
		nhg->nexthop = nexthop->next;
 | 
						|
 | 
						|
	if (nexthop->next)
 | 
						|
		nexthop->next->prev = nexthop->prev;
 | 
						|
 | 
						|
	nh->prev = NULL;
 | 
						|
	nh->next = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Unlink a nexthop from the list it's on, unconditionally */
 | 
						|
static void nexthop_unlink(struct nexthop_group *nhg, struct nexthop *nexthop)
 | 
						|
{
 | 
						|
 | 
						|
	if (nexthop->prev)
 | 
						|
		nexthop->prev->next = nexthop->next;
 | 
						|
	else {
 | 
						|
		assert(nhg->nexthop == nexthop);
 | 
						|
		assert(nexthop->prev == NULL);
 | 
						|
		nhg->nexthop = nexthop->next;
 | 
						|
	}
 | 
						|
 | 
						|
	if (nexthop->next)
 | 
						|
		nexthop->next->prev = nexthop->prev;
 | 
						|
 | 
						|
	nexthop->prev = NULL;
 | 
						|
	nexthop->next = NULL;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Copy a list of nexthops in 'nh' to an nhg, enforcing canonical sort order
 | 
						|
 */
 | 
						|
void nexthop_group_copy_nh_sorted(struct nexthop_group *nhg,
 | 
						|
				  const struct nexthop *nh)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop, *tail;
 | 
						|
	const struct nexthop *nh1;
 | 
						|
 | 
						|
	/* We'll try to append to the end of the new list;
 | 
						|
	 * if the original list in nh is already sorted, this eliminates
 | 
						|
	 * lots of comparison operations.
 | 
						|
	 */
 | 
						|
	tail = nexthop_group_tail(nhg);
 | 
						|
 | 
						|
	for (nh1 = nh; nh1; nh1 = nh1->next) {
 | 
						|
		nexthop = nexthop_dup(nh1, NULL);
 | 
						|
 | 
						|
		if (tail && (nexthop_cmp(tail, nexthop) < 0)) {
 | 
						|
			tail->next = nexthop;
 | 
						|
			nexthop->prev = tail;
 | 
						|
 | 
						|
			tail = nexthop;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		_nexthop_add_sorted(&nhg->nexthop, nexthop);
 | 
						|
 | 
						|
		if (tail == NULL)
 | 
						|
			tail = nexthop;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Copy a list of nexthops, no effort made to sort or order them. */
 | 
						|
void copy_nexthops(struct nexthop **tnh, const struct nexthop *nh,
 | 
						|
		   struct nexthop *rparent)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop;
 | 
						|
	const struct nexthop *nh1;
 | 
						|
 | 
						|
	for (nh1 = nh; nh1; nh1 = nh1->next) {
 | 
						|
		nexthop = nexthop_dup(nh1, rparent);
 | 
						|
		_nexthop_add(tnh, nexthop);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
uint32_t nexthop_group_hash_no_recurse(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nh;
 | 
						|
	uint32_t key = 0;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * We are not interested in hashing over any recursively
 | 
						|
	 * resolved nexthops
 | 
						|
	 */
 | 
						|
	for (nh = nhg->nexthop; nh; nh = nh->next)
 | 
						|
		key = jhash_1word(nexthop_hash(nh), key);
 | 
						|
 | 
						|
	return key;
 | 
						|
}
 | 
						|
 | 
						|
uint32_t nexthop_group_hash(const struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nh;
 | 
						|
	uint32_t key = 0;
 | 
						|
 | 
						|
	for (ALL_NEXTHOPS_PTR(nhg, nh))
 | 
						|
		key = jhash_1word(nexthop_hash(nh), key);
 | 
						|
 | 
						|
	return key;
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_mark_duplicates(struct nexthop_group *nhg)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop, *prev;
 | 
						|
 | 
						|
	for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
 | 
						|
		UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE);
 | 
						|
		for (ALL_NEXTHOPS_PTR(nhg, prev)) {
 | 
						|
			if (prev == nexthop)
 | 
						|
				break;
 | 
						|
			if (nexthop_same(nexthop, prev)) {
 | 
						|
				SET_FLAG(nexthop->flags,
 | 
						|
					 NEXTHOP_FLAG_DUPLICATE);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc)
 | 
						|
{
 | 
						|
	struct nexthop *nexthop;
 | 
						|
 | 
						|
	nexthop = nhgc->nhg.nexthop;
 | 
						|
	while (nexthop) {
 | 
						|
		struct nexthop *next = nexthop_next(nexthop);
 | 
						|
 | 
						|
		_nexthop_del(&nhgc->nhg, nexthop);
 | 
						|
		if (nhg_hooks.del_nexthop)
 | 
						|
			nhg_hooks.del_nexthop(nhgc, nexthop);
 | 
						|
 | 
						|
		nexthop_free(nexthop);
 | 
						|
 | 
						|
		nexthop = next;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct nexthop_group_cmd *nhgc_find(const char *name)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd find;
 | 
						|
 | 
						|
	strlcpy(find.name, name, sizeof(find.name));
 | 
						|
 | 
						|
	return RB_FIND(nhgc_entry_head, &nhgc_entries, &find);
 | 
						|
}
 | 
						|
 | 
						|
static int nhgc_cmp_helper(const char *a, const char *b)
 | 
						|
{
 | 
						|
	if (!a && !b)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (a && !b)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (!a && b)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	return strcmp(a, b);
 | 
						|
}
 | 
						|
 | 
						|
static int nhgc_addr_cmp_helper(const union sockunion *a, const union sockunion *b)
 | 
						|
{
 | 
						|
	if (!a && !b)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	if (a && !b)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (!a && b)
 | 
						|
		return 1;
 | 
						|
 | 
						|
	return sockunion_cmp(a, b);
 | 
						|
}
 | 
						|
 | 
						|
static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
 | 
						|
	ret = nhgc_addr_cmp_helper(nh1->addr, nh2->addr);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	ret = nhgc_cmp_helper(nh1->intf, nh2->intf);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	ret = ((int)nh2->onlink) - ((int)nh1->onlink);
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
 | 
						|
	return nhgc_cmp_helper(nh1->labels, nh2->labels);
 | 
						|
}
 | 
						|
 | 
						|
static void nhgl_delete(struct nexthop_hold *nh)
 | 
						|
{
 | 
						|
	XFREE(MTYPE_TMP, nh->intf);
 | 
						|
 | 
						|
	XFREE(MTYPE_TMP, nh->nhvrf_name);
 | 
						|
 | 
						|
	if (nh->addr)
 | 
						|
		sockunion_free(nh->addr);
 | 
						|
 | 
						|
	XFREE(MTYPE_TMP, nh->labels);
 | 
						|
 | 
						|
	XFREE(MTYPE_TMP, nh);
 | 
						|
}
 | 
						|
 | 
						|
static struct nexthop_group_cmd *nhgc_get(const char *name)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd *nhgc;
 | 
						|
 | 
						|
	nhgc = nhgc_find(name);
 | 
						|
	if (!nhgc) {
 | 
						|
		nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc));
 | 
						|
		strlcpy(nhgc->name, name, sizeof(nhgc->name));
 | 
						|
 | 
						|
		QOBJ_REG(nhgc, nexthop_group_cmd);
 | 
						|
		RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc);
 | 
						|
 | 
						|
		nhgc->nhg_list = list_new();
 | 
						|
		nhgc->nhg_list->cmp = (int (*)(void *, void *))nhgl_cmp;
 | 
						|
		nhgc->nhg_list->del = (void (*)(void *))nhgl_delete;
 | 
						|
 | 
						|
		if (nhg_hooks.new)
 | 
						|
			nhg_hooks.new(name);
 | 
						|
	}
 | 
						|
 | 
						|
	return nhgc;
 | 
						|
}
 | 
						|
 | 
						|
static void nhgc_delete(struct nexthop_group_cmd *nhgc)
 | 
						|
{
 | 
						|
	nhgc_delete_nexthops(nhgc);
 | 
						|
 | 
						|
	if (nhg_hooks.delete)
 | 
						|
		nhg_hooks.delete(nhgc->name);
 | 
						|
 | 
						|
	RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc);
 | 
						|
 | 
						|
	list_delete(&nhgc->nhg_list);
 | 
						|
 | 
						|
	QOBJ_UNREG(nhgc);
 | 
						|
	XFREE(MTYPE_TMP, nhgc);
 | 
						|
}
 | 
						|
 | 
						|
DEFINE_QOBJ_TYPE(nexthop_group_cmd);
 | 
						|
 | 
						|
DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NHGNAME",
 | 
						|
	   "Enter into the nexthop-group submode\n"
 | 
						|
	   "Specify the NAME of the nexthop-group\n")
 | 
						|
{
 | 
						|
	const char *nhg_name = argv[1]->arg;
 | 
						|
	struct nexthop_group_cmd *nhgc = NULL;
 | 
						|
 | 
						|
	nhgc = nhgc_get(nhg_name);
 | 
						|
	VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc);
 | 
						|
 | 
						|
	return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
 | 
						|
	   NO_STR
 | 
						|
	   "Delete the nexthop-group\n"
 | 
						|
	   "Specify the NAME of the nexthop-group\n")
 | 
						|
{
 | 
						|
	const char *nhg_name = argv[2]->arg;
 | 
						|
	struct nexthop_group_cmd *nhgc = NULL;
 | 
						|
 | 
						|
	nhgc = nhgc_find(nhg_name);
 | 
						|
	if (nhgc)
 | 
						|
		nhgc_delete(nhgc);
 | 
						|
 | 
						|
	return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
DEFPY(nexthop_group_backup, nexthop_group_backup_cmd,
 | 
						|
      "backup-group WORD$name",
 | 
						|
      "Specify a group name containing backup nexthops\n"
 | 
						|
      "The name of the backup group\n")
 | 
						|
{
 | 
						|
	VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
 | 
						|
 | 
						|
	strlcpy(nhgc->backup_list_name, name, sizeof(nhgc->backup_list_name));
 | 
						|
 | 
						|
	return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
DEFPY(no_nexthop_group_backup, no_nexthop_group_backup_cmd,
 | 
						|
      "no backup-group [WORD$name]",
 | 
						|
      NO_STR
 | 
						|
      "Clear group name containing backup nexthops\n"
 | 
						|
      "The name of the backup group\n")
 | 
						|
{
 | 
						|
	VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
 | 
						|
 | 
						|
	nhgc->backup_list_name[0] = 0;
 | 
						|
 | 
						|
	return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
 | 
						|
				    const char *nhvrf_name,
 | 
						|
				    const union sockunion *addr,
 | 
						|
				    const char *intf, bool onlink,
 | 
						|
				    const char *labels, const uint32_t weight,
 | 
						|
				    const char *backup_str)
 | 
						|
{
 | 
						|
	struct nexthop_hold *nh;
 | 
						|
 | 
						|
	nh = XCALLOC(MTYPE_TMP, sizeof(*nh));
 | 
						|
 | 
						|
	if (nhvrf_name)
 | 
						|
		nh->nhvrf_name = XSTRDUP(MTYPE_TMP, nhvrf_name);
 | 
						|
	if (intf)
 | 
						|
		nh->intf = XSTRDUP(MTYPE_TMP, intf);
 | 
						|
	if (addr)
 | 
						|
		nh->addr = sockunion_dup(addr);
 | 
						|
	if (labels)
 | 
						|
		nh->labels = XSTRDUP(MTYPE_TMP, labels);
 | 
						|
 | 
						|
	nh->onlink = onlink;
 | 
						|
 | 
						|
	nh->weight = weight;
 | 
						|
 | 
						|
	if (backup_str)
 | 
						|
		nh->backup_str = XSTRDUP(MTYPE_TMP, backup_str);
 | 
						|
 | 
						|
	listnode_add_sort(nhgc->nhg_list, nh);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Remove config info about a nexthop from group 'nhgc'. Note that we
 | 
						|
 * use only a subset of the available attributes here to determine
 | 
						|
 * a 'match'.
 | 
						|
 * Note that this doesn't change the list of nexthops, only the config
 | 
						|
 * information.
 | 
						|
 */
 | 
						|
static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
 | 
						|
				      const char *nhvrf_name,
 | 
						|
				      const union sockunion *addr,
 | 
						|
				      const char *intf)
 | 
						|
{
 | 
						|
	struct nexthop_hold *nh;
 | 
						|
	struct listnode *node;
 | 
						|
 | 
						|
	for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
 | 
						|
		if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0
 | 
						|
		    && nhgc_addr_cmp_helper(addr, nh->addr) == 0
 | 
						|
		    && nhgc_cmp_helper(intf, nh->intf) == 0)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Something has gone seriously wrong, fail gracefully
 | 
						|
	 */
 | 
						|
	if (!nh)
 | 
						|
		return;
 | 
						|
 | 
						|
	list_delete_node(nhgc->nhg_list, node);
 | 
						|
	nhgl_delete(nh);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Parse the config strings we support for a single nexthop. This gets used
 | 
						|
 * in a couple of different ways, and we distinguish between transient
 | 
						|
 * failures - such as a still-unprocessed interface - and fatal errors
 | 
						|
 * from label-string parsing.
 | 
						|
 */
 | 
						|
static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
 | 
						|
					const union sockunion *addr,
 | 
						|
					const char *intf, bool onlink,
 | 
						|
					const char *name, const char *labels,
 | 
						|
					int *lbl_ret, uint32_t weight,
 | 
						|
					const char *backup_str)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
	struct vrf *vrf;
 | 
						|
	int num;
 | 
						|
 | 
						|
	memset(nhop, 0, sizeof(*nhop));
 | 
						|
 | 
						|
	if (name)
 | 
						|
		vrf = vrf_lookup_by_name(name);
 | 
						|
	else
 | 
						|
		vrf = vrf_lookup_by_id(VRF_DEFAULT);
 | 
						|
 | 
						|
	if (!vrf)
 | 
						|
		return false;
 | 
						|
 | 
						|
	nhop->vrf_id = vrf->vrf_id;
 | 
						|
 | 
						|
	if (intf) {
 | 
						|
		nhop->ifindex = ifname2ifindex(intf, vrf->vrf_id);
 | 
						|
		if (nhop->ifindex == IFINDEX_INTERNAL)
 | 
						|
			return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (onlink)
 | 
						|
		SET_FLAG(nhop->flags, NEXTHOP_FLAG_ONLINK);
 | 
						|
 | 
						|
	if (addr) {
 | 
						|
		if (addr->sa.sa_family == AF_INET) {
 | 
						|
			nhop->gate.ipv4.s_addr = addr->sin.sin_addr.s_addr;
 | 
						|
			if (intf)
 | 
						|
				nhop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
 | 
						|
			else
 | 
						|
				nhop->type = NEXTHOP_TYPE_IPV4;
 | 
						|
		} else {
 | 
						|
			nhop->gate.ipv6 = addr->sin6.sin6_addr;
 | 
						|
			if (intf)
 | 
						|
				nhop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
 | 
						|
			else
 | 
						|
				nhop->type = NEXTHOP_TYPE_IPV6;
 | 
						|
		}
 | 
						|
	} else
 | 
						|
		nhop->type = NEXTHOP_TYPE_IFINDEX;
 | 
						|
 | 
						|
	if (labels) {
 | 
						|
		uint8_t num = 0;
 | 
						|
		mpls_label_t larray[MPLS_MAX_LABELS];
 | 
						|
 | 
						|
		ret = mpls_str2label(labels, &num, larray);
 | 
						|
 | 
						|
		/* Return label parse result */
 | 
						|
		if (lbl_ret)
 | 
						|
			*lbl_ret = ret;
 | 
						|
 | 
						|
		if (ret < 0)
 | 
						|
			return false;
 | 
						|
		else if (num > 0)
 | 
						|
			nexthop_add_labels(nhop, ZEBRA_LSP_NONE,
 | 
						|
					   num, larray);
 | 
						|
	}
 | 
						|
 | 
						|
	nhop->weight = weight;
 | 
						|
 | 
						|
	if (backup_str) {
 | 
						|
		/* Parse backup indexes */
 | 
						|
		ret = nexthop_str2backups(backup_str,
 | 
						|
					  &num, nhop->backup_idx);
 | 
						|
		if (ret == 0) {
 | 
						|
			SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
 | 
						|
			nhop->backup_num = num;
 | 
						|
		} else
 | 
						|
			return false;
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Wrapper to parse the strings in a 'nexthop_hold'
 | 
						|
 */
 | 
						|
static bool nexthop_group_parse_nhh(struct nexthop *nhop,
 | 
						|
				    const struct nexthop_hold *nhh)
 | 
						|
{
 | 
						|
	return (nexthop_group_parse_nexthop(
 | 
						|
		nhop, nhh->addr, nhh->intf, nhh->onlink, nhh->nhvrf_name,
 | 
						|
		nhh->labels, NULL, nhh->weight, nhh->backup_str));
 | 
						|
}
 | 
						|
 | 
						|
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
 | 
						|
      "[no] nexthop\
 | 
						|
        <\
 | 
						|
	  <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf [onlink$onlink]]\
 | 
						|
	  |INTERFACE$intf\
 | 
						|
	>\
 | 
						|
	[{ \
 | 
						|
	   nexthop-vrf NAME$vrf_name \
 | 
						|
	   |label WORD \
 | 
						|
           |weight (1-255) \
 | 
						|
           |backup-idx WORD \
 | 
						|
	}]",
 | 
						|
      NO_STR
 | 
						|
      "Specify one of the nexthops in this ECMP group\n"
 | 
						|
      "v4 Address\n"
 | 
						|
      "v6 Address\n"
 | 
						|
      "Interface to use\n"
 | 
						|
      "Treat nexthop as directly attached to the interface\n"
 | 
						|
      "Interface to use\n"
 | 
						|
      "If the nexthop is in a different vrf tell us\n"
 | 
						|
      "The nexthop-vrf Name\n"
 | 
						|
      "Specify label(s) for this nexthop\n"
 | 
						|
      "One or more labels in the range (16-1048575) separated by '/'\n"
 | 
						|
      "Weight to be used by the nexthop for purposes of ECMP\n"
 | 
						|
      "Weight value to be used\n"
 | 
						|
      "Specify backup nexthop indexes in another group\n"
 | 
						|
      "One or more indexes in the range (0-254) separated by ','\n")
 | 
						|
{
 | 
						|
	VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
 | 
						|
	struct nexthop nhop;
 | 
						|
	struct nexthop *nh;
 | 
						|
	int lbl_ret = 0;
 | 
						|
	bool legal;
 | 
						|
	int num;
 | 
						|
	uint8_t backups[NEXTHOP_MAX_BACKUPS];
 | 
						|
	bool yes = !no;
 | 
						|
 | 
						|
	/* Pre-parse backup string to validate */
 | 
						|
	if (backup_idx) {
 | 
						|
		lbl_ret = nexthop_str2backups(backup_idx, &num, backups);
 | 
						|
		if (lbl_ret < 0) {
 | 
						|
			vty_out(vty, "%% Invalid backups\n");
 | 
						|
			return CMD_WARNING_CONFIG_FAILED;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	legal = nexthop_group_parse_nexthop(&nhop, addr, intf, !!onlink,
 | 
						|
					    vrf_name, label, &lbl_ret, weight,
 | 
						|
					    backup_idx);
 | 
						|
 | 
						|
	if (nhop.type == NEXTHOP_TYPE_IPV6
 | 
						|
	    && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
 | 
						|
		vty_out(vty,
 | 
						|
			"Specified a v6 LL with no interface, rejecting\n");
 | 
						|
		return CMD_WARNING_CONFIG_FAILED;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Handle label-string errors */
 | 
						|
	if (!legal && lbl_ret < 0) {
 | 
						|
		switch (lbl_ret) {
 | 
						|
		case -1:
 | 
						|
			vty_out(vty, "%% Malformed label(s)\n");
 | 
						|
			break;
 | 
						|
		case -2:
 | 
						|
			vty_out(vty,
 | 
						|
				"%% Cannot use reserved label(s) (%d-%d)\n",
 | 
						|
				MPLS_LABEL_RESERVED_MIN,
 | 
						|
				MPLS_LABEL_RESERVED_MAX);
 | 
						|
			break;
 | 
						|
		case -3:
 | 
						|
			vty_out(vty,
 | 
						|
				"%% Too many labels. Enter %d or fewer\n",
 | 
						|
				MPLS_MAX_LABELS);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		return CMD_WARNING_CONFIG_FAILED;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Look for an existing nexthop in the config. Note that the test
 | 
						|
	 * here tests only some attributes - it's not a complete comparison.
 | 
						|
	 * Note that we've got two kinds of objects to manage: 'nexthop_hold'
 | 
						|
	 * that represent config that may or may not be valid (yet), and
 | 
						|
	 * actual nexthops that have been validated and parsed.
 | 
						|
	 */
 | 
						|
	nh = nhg_nh_find(&nhgc->nhg, &nhop);
 | 
						|
 | 
						|
	/* Always attempt to remove old config info. */
 | 
						|
	nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf);
 | 
						|
 | 
						|
	/* Remove any existing nexthop, for delete and replace cases. */
 | 
						|
	if (nh) {
 | 
						|
		nexthop_unlink(&nhgc->nhg, nh);
 | 
						|
 | 
						|
		if (nhg_hooks.del_nexthop)
 | 
						|
			nhg_hooks.del_nexthop(nhgc, nh);
 | 
						|
 | 
						|
		nexthop_free(nh);
 | 
						|
	}
 | 
						|
	if (yes) {
 | 
						|
		/* Add/replace case: capture nexthop if valid, and capture
 | 
						|
		 * config info always.
 | 
						|
		 */
 | 
						|
		if (legal) {
 | 
						|
			nh = nexthop_new();
 | 
						|
 | 
						|
			memcpy(nh, &nhop, sizeof(nhop));
 | 
						|
			_nexthop_add(&nhgc->nhg.nexthop, nh);
 | 
						|
		}
 | 
						|
 | 
						|
		/* Save config always */
 | 
						|
		nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, !!onlink,
 | 
						|
					label, weight, backup_idx);
 | 
						|
 | 
						|
		if (legal && nhg_hooks.add_nexthop)
 | 
						|
			nhg_hooks.add_nexthop(nhgc, nh);
 | 
						|
	}
 | 
						|
 | 
						|
	if (intf) {
 | 
						|
		struct interface *ifp = if_lookup_by_name_all_vrf(intf);
 | 
						|
 | 
						|
		if (ifp)
 | 
						|
			ifp->configured = true;
 | 
						|
	}
 | 
						|
	return CMD_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static int nexthop_group_write(struct vty *vty);
 | 
						|
static struct cmd_node nexthop_group_node = {
 | 
						|
	.name = "nexthop-group",
 | 
						|
	.node = NH_GROUP_NODE,
 | 
						|
	.parent_node = CONFIG_NODE,
 | 
						|
	.prompt = "%s(config-nh-group)# ",
 | 
						|
	.config_write = nexthop_group_write,
 | 
						|
};
 | 
						|
 | 
						|
void nexthop_group_write_nexthop_simple(struct vty *vty,
 | 
						|
					const struct nexthop *nh,
 | 
						|
					char *altifname)
 | 
						|
{
 | 
						|
	char *ifname;
 | 
						|
 | 
						|
	vty_out(vty, "nexthop ");
 | 
						|
 | 
						|
	if (altifname)
 | 
						|
		ifname = altifname;
 | 
						|
	else
 | 
						|
		ifname = (char *)ifindex2ifname(nh->ifindex, nh->vrf_id);
 | 
						|
 | 
						|
	switch (nh->type) {
 | 
						|
	case NEXTHOP_TYPE_IFINDEX:
 | 
						|
		vty_out(vty, "%s", ifname);
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV4:
 | 
						|
		vty_out(vty, "%pI4", &nh->gate.ipv4);
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV4_IFINDEX:
 | 
						|
		vty_out(vty, "%pI4 %s", &nh->gate.ipv4, ifname);
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV6:
 | 
						|
		vty_out(vty, "%pI6", &nh->gate.ipv6);
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV6_IFINDEX:
 | 
						|
		vty_out(vty, "%pI6 %s", &nh->gate.ipv6, ifname);
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_BLACKHOLE:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_write_nexthop(struct vty *vty, const struct nexthop *nh)
 | 
						|
{
 | 
						|
	struct vrf *vrf;
 | 
						|
	int i;
 | 
						|
 | 
						|
	nexthop_group_write_nexthop_simple(vty, nh, NULL);
 | 
						|
 | 
						|
	if (nh->vrf_id != VRF_DEFAULT) {
 | 
						|
		vrf = vrf_lookup_by_id(nh->vrf_id);
 | 
						|
		vty_out(vty, " nexthop-vrf %s", VRF_LOGNAME(vrf));
 | 
						|
	}
 | 
						|
 | 
						|
	if (nh->nh_label && nh->nh_label->num_labels > 0) {
 | 
						|
		char buf[200];
 | 
						|
 | 
						|
		mpls_label2str(nh->nh_label->num_labels,
 | 
						|
			       nh->nh_label->label,
 | 
						|
			       buf, sizeof(buf), 0);
 | 
						|
		vty_out(vty, " label %s", buf);
 | 
						|
	}
 | 
						|
 | 
						|
	if (nh->weight)
 | 
						|
		vty_out(vty, " weight %u", nh->weight);
 | 
						|
 | 
						|
	if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
 | 
						|
		vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
 | 
						|
 | 
						|
		for (i = 1; i < nh->backup_num; i++)
 | 
						|
			vty_out(vty, ",%d", nh->backup_idx[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	vty_out(vty, "\n");
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_json_nexthop(json_object *j, const struct nexthop *nh)
 | 
						|
{
 | 
						|
	char buf[100];
 | 
						|
	struct vrf *vrf;
 | 
						|
	json_object *json_backups = NULL;
 | 
						|
	int i;
 | 
						|
 | 
						|
	switch (nh->type) {
 | 
						|
	case NEXTHOP_TYPE_IFINDEX:
 | 
						|
		json_object_string_add(j, "nexthop",
 | 
						|
				       ifindex2ifname(nh->ifindex, nh->vrf_id));
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV4:
 | 
						|
		json_object_string_add(
 | 
						|
			j, "nexthop",
 | 
						|
			inet_ntop(AF_INET, &nh->gate.ipv4, buf, sizeof(buf)));
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV4_IFINDEX:
 | 
						|
		json_object_string_add(
 | 
						|
			j, "nexthop",
 | 
						|
			inet_ntop(AF_INET, &nh->gate.ipv4, buf, sizeof(buf)));
 | 
						|
		json_object_string_add(j, "vrfId",
 | 
						|
				       ifindex2ifname(nh->ifindex, nh->vrf_id));
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV6:
 | 
						|
		json_object_string_add(
 | 
						|
			j, "nexthop",
 | 
						|
			inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_IPV6_IFINDEX:
 | 
						|
		json_object_string_add(
 | 
						|
			j, "nexthop",
 | 
						|
			inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)));
 | 
						|
		json_object_string_add(j, "vrfId",
 | 
						|
				       ifindex2ifname(nh->ifindex, nh->vrf_id));
 | 
						|
		break;
 | 
						|
	case NEXTHOP_TYPE_BLACKHOLE:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (nh->vrf_id != VRF_DEFAULT) {
 | 
						|
		vrf = vrf_lookup_by_id(nh->vrf_id);
 | 
						|
		json_object_string_add(j, "targetVrf", vrf->name);
 | 
						|
	}
 | 
						|
 | 
						|
	if (nh->nh_label && nh->nh_label->num_labels > 0) {
 | 
						|
		char buf[200];
 | 
						|
 | 
						|
		mpls_label2str(nh->nh_label->num_labels, nh->nh_label->label,
 | 
						|
			       buf, sizeof(buf), 0);
 | 
						|
		json_object_string_add(j, "label", buf);
 | 
						|
	}
 | 
						|
 | 
						|
	if (nh->weight)
 | 
						|
		json_object_int_add(j, "weight", nh->weight);
 | 
						|
 | 
						|
	if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
 | 
						|
		json_backups = json_object_new_array();
 | 
						|
		for (i = 0; i < nh->backup_num; i++)
 | 
						|
			json_object_array_add(
 | 
						|
				json_backups,
 | 
						|
				json_object_new_int(nh->backup_idx[i]));
 | 
						|
 | 
						|
		json_object_object_add(j, "backupIdx", json_backups);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void nexthop_group_write_nexthop_internal(struct vty *vty,
 | 
						|
						 const struct nexthop_hold *nh)
 | 
						|
{
 | 
						|
	char buf[100];
 | 
						|
 | 
						|
	vty_out(vty, "nexthop");
 | 
						|
 | 
						|
	if (nh->addr)
 | 
						|
		vty_out(vty, " %s", sockunion2str(nh->addr, buf, sizeof(buf)));
 | 
						|
 | 
						|
	if (nh->intf)
 | 
						|
		vty_out(vty, " %s", nh->intf);
 | 
						|
 | 
						|
	if (nh->onlink)
 | 
						|
		vty_out(vty, " onlink");
 | 
						|
 | 
						|
	if (nh->nhvrf_name)
 | 
						|
		vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
 | 
						|
 | 
						|
	if (nh->labels)
 | 
						|
		vty_out(vty, " label %s", nh->labels);
 | 
						|
 | 
						|
	if (nh->weight)
 | 
						|
		vty_out(vty, " weight %u", nh->weight);
 | 
						|
 | 
						|
	if (nh->backup_str)
 | 
						|
		vty_out(vty, " backup-idx %s", nh->backup_str);
 | 
						|
 | 
						|
	vty_out(vty, "\n");
 | 
						|
}
 | 
						|
 | 
						|
static int nexthop_group_write(struct vty *vty)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd *nhgc;
 | 
						|
	struct nexthop_hold *nh;
 | 
						|
 | 
						|
	RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
 | 
						|
		struct listnode *node;
 | 
						|
 | 
						|
		vty_out(vty, "nexthop-group %s\n", nhgc->name);
 | 
						|
 | 
						|
		if (nhgc->backup_list_name[0])
 | 
						|
			vty_out(vty, " backup-group %s\n",
 | 
						|
				nhgc->backup_list_name);
 | 
						|
 | 
						|
		for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
 | 
						|
			vty_out(vty, " ");
 | 
						|
			nexthop_group_write_nexthop_internal(vty, nh);
 | 
						|
		}
 | 
						|
 | 
						|
		vty_out(vty, "exit\n");
 | 
						|
		vty_out(vty, "!\n");
 | 
						|
	}
 | 
						|
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_enable_vrf(struct vrf *vrf)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd *nhgc;
 | 
						|
	struct nexthop_hold *nhh;
 | 
						|
 | 
						|
	RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
 | 
						|
		struct listnode *node;
 | 
						|
 | 
						|
		for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
 | 
						|
			struct nexthop nhop;
 | 
						|
			struct nexthop *nh;
 | 
						|
 | 
						|
			if (!nexthop_group_parse_nhh(&nhop, nhh))
 | 
						|
				continue;
 | 
						|
 | 
						|
			nh = nexthop_exists(&nhgc->nhg, &nhop);
 | 
						|
 | 
						|
			if (nh)
 | 
						|
				continue;
 | 
						|
 | 
						|
			if (nhop.vrf_id != vrf->vrf_id)
 | 
						|
				continue;
 | 
						|
 | 
						|
			nh = nexthop_new();
 | 
						|
 | 
						|
			memcpy(nh, &nhop, sizeof(nhop));
 | 
						|
			_nexthop_add(&nhgc->nhg.nexthop, nh);
 | 
						|
 | 
						|
			if (nhg_hooks.add_nexthop)
 | 
						|
				nhg_hooks.add_nexthop(nhgc, nh);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_disable_vrf(struct vrf *vrf)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd *nhgc;
 | 
						|
	struct nexthop_hold *nhh;
 | 
						|
 | 
						|
	RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
 | 
						|
		struct listnode *node;
 | 
						|
 | 
						|
		for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
 | 
						|
			struct nexthop nhop;
 | 
						|
			struct nexthop *nh;
 | 
						|
 | 
						|
			if (!nexthop_group_parse_nhh(&nhop, nhh))
 | 
						|
				continue;
 | 
						|
 | 
						|
			nh = nexthop_exists(&nhgc->nhg, &nhop);
 | 
						|
 | 
						|
			if (!nh)
 | 
						|
				continue;
 | 
						|
 | 
						|
			if (nh->vrf_id != vrf->vrf_id)
 | 
						|
				continue;
 | 
						|
 | 
						|
			_nexthop_del(&nhgc->nhg, nh);
 | 
						|
 | 
						|
			if (nhg_hooks.del_nexthop)
 | 
						|
				nhg_hooks.del_nexthop(nhgc, nh);
 | 
						|
 | 
						|
			nexthop_free(nh);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void nexthop_group_interface_state_change(struct interface *ifp,
 | 
						|
					  ifindex_t oldifindex)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd *nhgc;
 | 
						|
	struct nexthop_hold *nhh;
 | 
						|
 | 
						|
	RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
 | 
						|
		struct listnode *node;
 | 
						|
		struct nexthop *nh;
 | 
						|
 | 
						|
		if (if_is_up(ifp)) {
 | 
						|
			for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
 | 
						|
				struct nexthop nhop;
 | 
						|
 | 
						|
				if (!nexthop_group_parse_nhh(&nhop, nhh))
 | 
						|
					continue;
 | 
						|
 | 
						|
				switch (nhop.type) {
 | 
						|
				case NEXTHOP_TYPE_IPV4:
 | 
						|
				case NEXTHOP_TYPE_IPV6:
 | 
						|
				case NEXTHOP_TYPE_BLACKHOLE:
 | 
						|
					continue;
 | 
						|
				case NEXTHOP_TYPE_IFINDEX:
 | 
						|
				case NEXTHOP_TYPE_IPV4_IFINDEX:
 | 
						|
				case NEXTHOP_TYPE_IPV6_IFINDEX:
 | 
						|
					break;
 | 
						|
				}
 | 
						|
				nh = nexthop_exists(&nhgc->nhg, &nhop);
 | 
						|
 | 
						|
				if (nh)
 | 
						|
					continue;
 | 
						|
 | 
						|
				if (ifp->ifindex != nhop.ifindex)
 | 
						|
					continue;
 | 
						|
 | 
						|
				ifp->configured = true;
 | 
						|
				nh = nexthop_new();
 | 
						|
 | 
						|
				memcpy(nh, &nhop, sizeof(nhop));
 | 
						|
				_nexthop_add(&nhgc->nhg.nexthop, nh);
 | 
						|
 | 
						|
				if (nhg_hooks.add_nexthop)
 | 
						|
					nhg_hooks.add_nexthop(nhgc, nh);
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			struct nexthop *next_nh;
 | 
						|
 | 
						|
			for (nh = nhgc->nhg.nexthop; nh; nh = next_nh) {
 | 
						|
				next_nh = nh->next;
 | 
						|
				switch (nh->type) {
 | 
						|
				case NEXTHOP_TYPE_IPV4:
 | 
						|
				case NEXTHOP_TYPE_IPV6:
 | 
						|
				case NEXTHOP_TYPE_BLACKHOLE:
 | 
						|
					continue;
 | 
						|
				case NEXTHOP_TYPE_IFINDEX:
 | 
						|
				case NEXTHOP_TYPE_IPV4_IFINDEX:
 | 
						|
				case NEXTHOP_TYPE_IPV6_IFINDEX:
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				if (oldifindex != nh->ifindex)
 | 
						|
					continue;
 | 
						|
 | 
						|
				_nexthop_del(&nhgc->nhg, nh);
 | 
						|
 | 
						|
				if (nhg_hooks.del_nexthop)
 | 
						|
					nhg_hooks.del_nexthop(nhgc, nh);
 | 
						|
 | 
						|
				nexthop_free(nh);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void nhg_name_autocomplete(vector comps, struct cmd_token *token)
 | 
						|
{
 | 
						|
	struct nexthop_group_cmd *nhgc;
 | 
						|
 | 
						|
	RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) {
 | 
						|
		vector_set(comps, XSTRDUP(MTYPE_COMPLETION, nhgc->name));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static const struct cmd_variable_handler nhg_name_handlers[] = {
 | 
						|
	{.tokenname = "NHGNAME", .completions = nhg_name_autocomplete},
 | 
						|
	{.completions = NULL}};
 | 
						|
 | 
						|
void nexthop_group_init(void (*new)(const char *name),
 | 
						|
			void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
 | 
						|
					    const struct nexthop *nhop),
 | 
						|
			void (*del_nexthop)(const struct nexthop_group_cmd *nhg,
 | 
						|
					    const struct nexthop *nhop),
 | 
						|
			void (*delete)(const char *name))
 | 
						|
{
 | 
						|
	RB_INIT(nhgc_entry_head, &nhgc_entries);
 | 
						|
 | 
						|
	cmd_variable_handler_register(nhg_name_handlers);
 | 
						|
 | 
						|
	install_node(&nexthop_group_node);
 | 
						|
	install_element(CONFIG_NODE, &nexthop_group_cmd);
 | 
						|
	install_element(CONFIG_NODE, &no_nexthop_group_cmd);
 | 
						|
 | 
						|
	install_default(NH_GROUP_NODE);
 | 
						|
	install_element(NH_GROUP_NODE, &nexthop_group_backup_cmd);
 | 
						|
	install_element(NH_GROUP_NODE, &no_nexthop_group_backup_cmd);
 | 
						|
	install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
 | 
						|
 | 
						|
	memset(&nhg_hooks, 0, sizeof(nhg_hooks));
 | 
						|
 | 
						|
	if (new)
 | 
						|
		nhg_hooks.new = new;
 | 
						|
	if (add_nexthop)
 | 
						|
		nhg_hooks.add_nexthop = add_nexthop;
 | 
						|
	if (del_nexthop)
 | 
						|
		nhg_hooks.del_nexthop = del_nexthop;
 | 
						|
	if (delete)
 | 
						|
		nhg_hooks.delete = delete;
 | 
						|
}
 |