mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-02 01:15:33 +00:00

Create a nhg_depenents tree that will function as a way to get back pointers for NHE's depending on it. Abstract the RB nodes into nhg_connected for both depends and dependents. This same struct is used for both. Signed-off-by: Stephen Worley <sworley@cumulusnetworks.com>
1299 lines
34 KiB
C
1299 lines
34 KiB
C
/* Zebra Nexthop Group Code.
|
|
* Copyright (C) 2019 Cumulus Networks, Inc.
|
|
* Donald Sharp
|
|
* Stephen Worley
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with FRR; see the file COPYING. If not, write to the Free
|
|
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
#include <zebra.h>
|
|
|
|
#include "lib/nexthop.h"
|
|
#include "lib/nexthop_group_private.h"
|
|
#include "lib/routemap.h"
|
|
#include "lib/mpls.h"
|
|
#include "lib/jhash.h"
|
|
#include "lib/debug.h"
|
|
|
|
#include "zebra/connected.h"
|
|
#include "zebra/debug.h"
|
|
#include "zebra/zebra_router.h"
|
|
#include "zebra/zebra_nhg.h"
|
|
#include "zebra/zebra_rnh.h"
|
|
#include "zebra/zebra_routemap.h"
|
|
#include "zebra/zebra_memory.h"
|
|
#include "zebra/zserv.h"
|
|
#include "zebra/rt.h"
|
|
#include "zebra_errors.h"
|
|
#include "zebra_dplane.h"
|
|
|
|
DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
|
|
DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
|
|
|
|
static int zebra_nhg_connected_cmp(const struct nhg_connected *dep1,
|
|
const struct nhg_connected *dep2);
|
|
|
|
RB_GENERATE(nhg_connected_head, nhg_connected, nhg_entry,
|
|
zebra_nhg_connected_cmp);
|
|
|
|
static void nhg_connected_free(struct nhg_connected *dep)
|
|
{
|
|
XFREE(MTYPE_NHG_CONNECTED, dep);
|
|
}
|
|
|
|
static struct nhg_connected *nhg_connected_new(struct nhg_hash_entry *nhe)
|
|
{
|
|
struct nhg_connected *new = NULL;
|
|
|
|
new = XCALLOC(MTYPE_NHG_CONNECTED, sizeof(struct nhg_connected));
|
|
new->nhe = nhe;
|
|
|
|
return new;
|
|
}
|
|
|
|
static uint8_t zebra_nhg_connected_count(const struct nhg_connected_head *head)
|
|
{
|
|
struct nhg_connected *rb_node_dep = NULL;
|
|
uint8_t i = 0;
|
|
|
|
RB_FOREACH (rb_node_dep, nhg_connected_head, head) {
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
uint8_t zebra_nhg_depends_count(const struct nhg_hash_entry *nhe)
|
|
{
|
|
return zebra_nhg_connected_count(&nhe->nhg_depends);
|
|
}
|
|
|
|
static bool
|
|
zebra_nhg_connected_head_is_empty(const struct nhg_connected_head *head)
|
|
{
|
|
return RB_EMPTY(nhg_connected_head, head);
|
|
}
|
|
|
|
bool zebra_nhg_depends_is_empty(const struct nhg_hash_entry *nhe)
|
|
{
|
|
return zebra_nhg_connected_head_is_empty(&nhe->nhg_depends);
|
|
}
|
|
|
|
void zebra_nhg_connected_head_del(struct nhg_connected_head *head,
|
|
struct nhg_hash_entry *depend)
|
|
{
|
|
struct nhg_connected lookup = {};
|
|
struct nhg_connected *removed = NULL;
|
|
|
|
lookup.nhe = depend;
|
|
|
|
removed = RB_REMOVE(nhg_connected_head, head, &lookup);
|
|
|
|
nhg_connected_free(removed);
|
|
}
|
|
|
|
void zebra_nhg_connected_head_add(struct nhg_connected_head *head,
|
|
struct nhg_hash_entry *depend)
|
|
{
|
|
struct nhg_connected *new = NULL;
|
|
|
|
new = nhg_connected_new(depend);
|
|
|
|
RB_INSERT(nhg_connected_head, head, new);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_dependents_del() - Delete a dependent from the nhg_hash_entry
|
|
*
|
|
* @from: Nexthop group hash entry we are deleting from
|
|
* @dependent: Dependent we are deleting
|
|
*/
|
|
void zebra_nhg_dependents_del(struct nhg_hash_entry *from,
|
|
struct nhg_hash_entry *dependent)
|
|
{
|
|
zebra_nhg_connected_head_del(&from->nhg_dependents, dependent);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_dependents_add() - Add a new dependent to the nhg_hash_entry
|
|
*
|
|
* @to: Nexthop group hash entry we are adding to
|
|
* @dependent: Dependent we are adding
|
|
*/
|
|
void zebra_nhg_dependents_add(struct nhg_hash_entry *to,
|
|
struct nhg_hash_entry *dependent)
|
|
{
|
|
zebra_nhg_connected_head_add(&to->nhg_dependents, dependent);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_depends_del() - Delete a dependency from the nhg_hash_entry
|
|
*
|
|
* @from: Nexthop group hash entry we are deleting from
|
|
* @depend: Dependency we are deleting
|
|
*/
|
|
void zebra_nhg_depends_del(struct nhg_hash_entry *from,
|
|
struct nhg_hash_entry *depend)
|
|
{
|
|
zebra_nhg_connected_head_del(&from->nhg_depends, depend);
|
|
|
|
/* Delete from the dependent tree */
|
|
zebra_nhg_dependents_del(depend, from);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_depends_add() - Add a new dependency to the nhg_hash_entry
|
|
*
|
|
* @to: Nexthop group hash entry we are adding to
|
|
* @depend: Dependency we are adding
|
|
*/
|
|
void zebra_nhg_depends_add(struct nhg_hash_entry *to,
|
|
struct nhg_hash_entry *depend)
|
|
{
|
|
zebra_nhg_connected_head_add(&to->nhg_depends, depend);
|
|
}
|
|
|
|
void zebra_nhg_connected_head_init(struct nhg_connected_head *head)
|
|
{
|
|
RB_INIT(nhg_connected_head, head);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_depends_init() - Initialize tree for nhg dependencies
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*/
|
|
void zebra_nhg_depends_init(struct nhg_hash_entry *nhe)
|
|
{
|
|
zebra_nhg_connected_head_init(&nhe->nhg_depends);
|
|
}
|
|
|
|
|
|
/**
|
|
* zebra_nhg_depends_equal() - Are the dependencies of these nhe's equal
|
|
*
|
|
* @nhe1: Nexthop group hash entry
|
|
* @nhe2: Nexthop group hash entry
|
|
*
|
|
* Return: True if equal
|
|
*
|
|
* We don't care about ordering of the dependencies. If they contain
|
|
* the same nhe ID's, they are equivalent.
|
|
*/
|
|
static bool zebra_nhg_depends_equal(const struct nhg_hash_entry *nhe1,
|
|
const struct nhg_hash_entry *nhe2)
|
|
{
|
|
struct nhg_connected *rb_node_dep = NULL;
|
|
|
|
if (zebra_nhg_depends_is_empty(nhe1)
|
|
&& zebra_nhg_depends_is_empty(nhe2))
|
|
return true;
|
|
|
|
if ((zebra_nhg_depends_is_empty(nhe1)
|
|
&& !zebra_nhg_depends_is_empty(nhe2))
|
|
|| (zebra_nhg_depends_is_empty(nhe2)
|
|
&& !zebra_nhg_depends_is_empty(nhe1)))
|
|
return false;
|
|
|
|
RB_FOREACH (rb_node_dep, nhg_connected_head, &nhe1->nhg_depends) {
|
|
if (!RB_FIND(nhg_connected_head, &nhe2->nhg_depends,
|
|
rb_node_dep))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_lookup_id() - Lookup the nexthop group id in the id table
|
|
*
|
|
* @id: ID to look for
|
|
*
|
|
* Return: Nexthop hash entry if found/NULL if not found
|
|
*/
|
|
struct nhg_hash_entry *zebra_nhg_lookup_id(uint32_t id)
|
|
{
|
|
struct nhg_hash_entry lookup = {};
|
|
|
|
lookup.id = id;
|
|
return hash_lookup(zrouter.nhgs_id, &lookup);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_insert_id() - Insert a nhe into the id hashed table
|
|
*
|
|
* @nhe: The entry directly from the other table
|
|
*
|
|
* Return: Result status
|
|
*/
|
|
int zebra_nhg_insert_id(struct nhg_hash_entry *nhe)
|
|
{
|
|
if (hash_lookup(zrouter.nhgs_id, nhe)) {
|
|
flog_err(
|
|
EC_ZEBRA_NHG_TABLE_INSERT_FAILED,
|
|
"Failed inserting NHG id=%u into the ID hash table, entry already exists",
|
|
nhe->id);
|
|
return -1;
|
|
}
|
|
|
|
hash_get(zrouter.nhgs_id, nhe, hash_alloc_intern);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void *zebra_nhg_alloc(void *arg)
|
|
{
|
|
struct nhg_hash_entry *nhe;
|
|
struct nhg_hash_entry *copy = arg;
|
|
struct nhg_connected *rb_node_dep = NULL;
|
|
|
|
|
|
nhe = XCALLOC(MTYPE_NHG, sizeof(struct nhg_hash_entry));
|
|
|
|
|
|
nhe->id = copy->id;
|
|
|
|
nhe->nhg_depends = copy->nhg_depends;
|
|
|
|
nhe->nhg = nexthop_group_new();
|
|
nexthop_group_copy(nhe->nhg, copy->nhg);
|
|
|
|
nhe->vrf_id = copy->vrf_id;
|
|
nhe->afi = copy->afi;
|
|
nhe->refcnt = 0;
|
|
nhe->is_kernel_nh = copy->is_kernel_nh;
|
|
nhe->dplane_ref = zebra_router_get_next_sequence();
|
|
nhe->ifp = NULL;
|
|
|
|
/* Attach backpointer to anything that it depends on */
|
|
if (!zebra_nhg_depends_is_empty(nhe)) {
|
|
RB_FOREACH (rb_node_dep, nhg_connected_head,
|
|
&nhe->nhg_depends) {
|
|
zebra_nhg_dependents_add(rb_node_dep->nhe, nhe);
|
|
}
|
|
}
|
|
|
|
/* Add to id table as well */
|
|
zebra_nhg_insert_id(nhe);
|
|
|
|
|
|
/* Send it to the kernel */
|
|
if (!nhe->is_kernel_nh)
|
|
zebra_nhg_install_kernel(nhe);
|
|
|
|
return nhe;
|
|
}
|
|
|
|
uint32_t zebra_nhg_hash_key(const void *arg)
|
|
{
|
|
const struct nhg_hash_entry *nhe = arg;
|
|
|
|
uint32_t key = 0x5a351234;
|
|
|
|
key = jhash_2words(nhe->vrf_id, nhe->afi, key);
|
|
|
|
key = jhash_1word(nexthop_group_hash(nhe->nhg), key);
|
|
|
|
return key;
|
|
}
|
|
|
|
uint32_t zebra_nhg_id_key(const void *arg)
|
|
{
|
|
const struct nhg_hash_entry *nhe = arg;
|
|
|
|
return nhe->id;
|
|
}
|
|
|
|
bool zebra_nhg_hash_equal(const void *arg1, const void *arg2)
|
|
{
|
|
const struct nhg_hash_entry *nhe1 = arg1;
|
|
const struct nhg_hash_entry *nhe2 = arg2;
|
|
|
|
if (nhe1->vrf_id != nhe2->vrf_id)
|
|
return false;
|
|
|
|
if (nhe1->afi != nhe2->afi)
|
|
return false;
|
|
|
|
if (!zebra_nhg_depends_equal(nhe1, nhe2))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool zebra_nhg_hash_id_equal(const void *arg1, const void *arg2)
|
|
{
|
|
const struct nhg_hash_entry *nhe1 = arg1;
|
|
const struct nhg_hash_entry *nhe2 = arg2;
|
|
|
|
return nhe1->id == nhe2->id;
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_cmp() - Compare the ID's of two nhe's
|
|
*
|
|
* @nhe1: Nexthop group hash entry #1
|
|
* @nhe2: Nexthop group hash entry #2
|
|
*
|
|
* Return:
|
|
* - Negative: #1 < #2
|
|
* - Positive: #1 > #2
|
|
* - Zero: #1 = #2
|
|
*
|
|
* This is used in the nhg RB trees.
|
|
*/
|
|
static int zebra_nhg_cmp(const struct nhg_hash_entry *nhe1,
|
|
const struct nhg_hash_entry *nhe2)
|
|
{
|
|
return nhe1->id - nhe2->id;
|
|
}
|
|
|
|
static int zebra_nhg_connected_cmp(const struct nhg_connected *dep1,
|
|
const struct nhg_connected *dep2)
|
|
{
|
|
return zebra_nhg_cmp(dep1->nhe, dep2->nhe);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_find() - Find the zebra nhg in our table, or create it
|
|
*
|
|
* @nhg: Nexthop group we lookup with
|
|
* @vrf_id: VRF id
|
|
* @afi: Address Family type
|
|
* @id: ID we lookup with, 0 means its from us and we
|
|
* need to give it an ID, otherwise its from the
|
|
* kernel as we use the ID it gave us.
|
|
* @nhg_depends: Nexthop dependency tree head
|
|
* @is_kernel_nh: Was the nexthop created by the kernel
|
|
*
|
|
* Return: Hash entry found or created
|
|
*
|
|
* The nhg and n_grp are fundementally the same thing (a group of nexthops).
|
|
* We are just using the nhg representation with routes and the n_grp
|
|
* is what the kernel gives us (a list of IDs). Our nhg_hash_entry
|
|
* will contain both.
|
|
*
|
|
* nhg_hash_entry example:
|
|
*
|
|
* nhe:
|
|
* ->nhg:
|
|
* .nexthop->nexthop->nexthop
|
|
* ->nhg_depends:
|
|
* .nhe->nhe->nhe
|
|
*
|
|
* Routes will use the nhg directly, and any updating of nexthops
|
|
* we have to do or flag setting, we use the nhg_depends.
|
|
*
|
|
*/
|
|
struct nhg_hash_entry *zebra_nhg_find(struct nexthop_group *nhg,
|
|
vrf_id_t vrf_id, afi_t afi, uint32_t id,
|
|
struct nhg_connected_head *nhg_depends,
|
|
bool is_kernel_nh)
|
|
{
|
|
/* lock for getiing and setting the id */
|
|
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
|
|
/* id counter to keep in sync with kernel */
|
|
static uint32_t id_counter = 0;
|
|
|
|
struct nhg_hash_entry lookup = {};
|
|
struct nhg_hash_entry *nhe = NULL;
|
|
uint32_t old_id_counter = 0;
|
|
|
|
pthread_mutex_lock(&lock); /* Lock, set the id counter */
|
|
|
|
old_id_counter = id_counter;
|
|
|
|
if (id) {
|
|
if (id > id_counter) {
|
|
/* Increase our counter so we don't try to create
|
|
* an ID that already exists
|
|
*/
|
|
id_counter = id;
|
|
}
|
|
lookup.id = id;
|
|
} else {
|
|
lookup.id = ++id_counter;
|
|
}
|
|
|
|
lookup.vrf_id = vrf_id;
|
|
lookup.afi = afi;
|
|
lookup.nhg = nhg;
|
|
lookup.nhg_depends = *nhg_depends;
|
|
lookup.is_kernel_nh = is_kernel_nh;
|
|
|
|
if (id)
|
|
nhe = zebra_nhg_lookup_id(id);
|
|
else
|
|
nhe = hash_lookup(zrouter.nhgs, &lookup);
|
|
|
|
/* If it found an nhe in our tables, this new ID is unused */
|
|
if (nhe)
|
|
id_counter = old_id_counter;
|
|
|
|
pthread_mutex_unlock(&lock);
|
|
|
|
if (!nhe)
|
|
nhe = hash_get(zrouter.nhgs, &lookup, zebra_nhg_alloc);
|
|
|
|
return nhe;
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_find_nexthop() - Create a group with a single nexthop, find it in
|
|
* our table, or create it
|
|
*
|
|
* @nh: Nexthop to lookup
|
|
* @afi: Address Family type
|
|
*
|
|
* Return: Hash entry found or created
|
|
*/
|
|
struct nhg_hash_entry *zebra_nhg_find_nexthop(struct nexthop *nh, afi_t afi)
|
|
{
|
|
struct nhg_hash_entry *nhe = NULL;
|
|
|
|
struct nexthop_group *nhg = nexthop_group_new();
|
|
|
|
nexthop_group_add_sorted(nhg, nh);
|
|
nhe = zebra_nhg_find(nhg, nh->vrf_id, afi, 0, NULL, false);
|
|
|
|
nexthop_group_delete(&nhg);
|
|
|
|
return nhe;
|
|
}
|
|
|
|
void zebra_nhg_connected_head_free(struct nhg_connected_head *head)
|
|
{
|
|
struct nhg_connected *rb_node_dep = NULL;
|
|
struct nhg_connected *tmp = NULL;
|
|
|
|
if (!zebra_nhg_connected_head_is_empty(head)) {
|
|
RB_FOREACH_SAFE (rb_node_dep, nhg_connected_head, head, tmp) {
|
|
RB_REMOVE(nhg_connected_head, head, rb_node_dep);
|
|
nhg_connected_free(rb_node_dep);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_free_group_depends() - Helper function for freeing nexthop_group
|
|
* struct and depends
|
|
*
|
|
* @nhg: Nexthop_group
|
|
* @nhg_depends: Nexthop group dependency tree head
|
|
*/
|
|
void zebra_nhg_free_group_depends(struct nexthop_group **nhg,
|
|
struct nhg_connected_head *head)
|
|
{
|
|
// TODO
|
|
//
|
|
//
|
|
// FIX THIS NAMING
|
|
//
|
|
//
|
|
//
|
|
//
|
|
//
|
|
if (head)
|
|
zebra_nhg_connected_head_free(head);
|
|
|
|
if (nhg)
|
|
nexthop_group_free_delete(nhg);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_free_members() - Free all members in the hash entry struct
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*
|
|
* Just use this to free everything but the entry itself.
|
|
*/
|
|
void zebra_nhg_free_members(struct nhg_hash_entry *nhe)
|
|
{
|
|
zebra_nhg_free_group_depends(&nhe->nhg, &nhe->nhg_depends);
|
|
|
|
// TODO: Fixup this function
|
|
zebra_nhg_connected_head_free(&nhe->nhg_dependents);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_free() - Free the nexthop group hash entry
|
|
*
|
|
* arg: Nexthop group entry to free
|
|
*/
|
|
void zebra_nhg_free(void *arg)
|
|
{
|
|
struct nhg_hash_entry *nhe = NULL;
|
|
|
|
nhe = (struct nhg_hash_entry *)arg;
|
|
|
|
zebra_nhg_free_members(nhe);
|
|
|
|
XFREE(MTYPE_NHG, nhe);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_release() - Release a nhe from the tables
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*/
|
|
void zebra_nhg_release(struct nhg_hash_entry *nhe)
|
|
{
|
|
if (!nhe->refcnt) {
|
|
zlog_debug("Releasing nexthop group with ID (%u)", nhe->id);
|
|
hash_release(zrouter.nhgs, nhe);
|
|
hash_release(zrouter.nhgs_id, nhe);
|
|
zebra_nhg_free(nhe);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_uninstall_release() - Unistall and release a nhe
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*/
|
|
static void zebra_nhg_uninstall_release(struct nhg_hash_entry *nhe)
|
|
{
|
|
zebra_nhg_uninstall_kernel(nhe);
|
|
// zebra_nhg_release(nhe);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_decrement_ref() - Decrement the reference count, release if unused
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*
|
|
* If the counter hits 0 and is not a nexthop group that was created by the
|
|
* kernel, we don't need to have it in our table anymore.
|
|
*/
|
|
void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)
|
|
{
|
|
if (!zebra_nhg_depends_is_empty(nhe)) {
|
|
struct nhg_connected *rb_node_dep = NULL;
|
|
|
|
RB_FOREACH (rb_node_dep, nhg_connected_head,
|
|
&nhe->nhg_depends) {
|
|
zebra_nhg_decrement_ref(rb_node_dep->nhe);
|
|
}
|
|
}
|
|
|
|
nhe->refcnt--;
|
|
|
|
if (!nhe->is_kernel_nh && nhe->refcnt <= 0) {
|
|
zebra_nhg_uninstall_release(nhe);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_increment_ref() - Increment the reference count
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*/
|
|
void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
|
|
{
|
|
if (!zebra_nhg_depends_is_empty(nhe)) {
|
|
struct nhg_connected *rb_node_dep = NULL;
|
|
|
|
RB_FOREACH (rb_node_dep, nhg_connected_head,
|
|
&nhe->nhg_depends) {
|
|
zebra_nhg_increment_ref(rb_node_dep->nhe);
|
|
}
|
|
}
|
|
|
|
nhe->refcnt++;
|
|
}
|
|
|
|
static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
|
|
struct nexthop *nexthop)
|
|
{
|
|
struct nexthop *resolved_hop;
|
|
uint8_t num_labels = 0;
|
|
mpls_label_t labels[MPLS_MAX_LABELS];
|
|
enum lsp_types_t label_type = ZEBRA_LSP_NONE;
|
|
int i = 0;
|
|
|
|
resolved_hop = nexthop_new();
|
|
SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
|
|
resolved_hop->vrf_id = nexthop->vrf_id;
|
|
switch (newhop->type) {
|
|
case NEXTHOP_TYPE_IPV4:
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
/* If the resolving route specifies a gateway, use it */
|
|
resolved_hop->type = newhop->type;
|
|
resolved_hop->gate.ipv4 = newhop->gate.ipv4;
|
|
|
|
if (newhop->ifindex) {
|
|
resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
|
resolved_hop->ifindex = newhop->ifindex;
|
|
}
|
|
break;
|
|
case NEXTHOP_TYPE_IPV6:
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
resolved_hop->type = newhop->type;
|
|
resolved_hop->gate.ipv6 = newhop->gate.ipv6;
|
|
|
|
if (newhop->ifindex) {
|
|
resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
|
|
resolved_hop->ifindex = newhop->ifindex;
|
|
}
|
|
break;
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
/* If the resolving route is an interface route,
|
|
* it means the gateway we are looking up is connected
|
|
* to that interface. (The actual network is _not_ onlink).
|
|
* Therefore, the resolved route should have the original
|
|
* gateway as nexthop as it is directly connected.
|
|
*
|
|
* On Linux, we have to set the onlink netlink flag because
|
|
* otherwise, the kernel won't accept the route.
|
|
*/
|
|
resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
|
|
if (afi == AFI_IP) {
|
|
resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
|
|
resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
|
|
} else if (afi == AFI_IP6) {
|
|
resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
|
|
resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
|
|
}
|
|
resolved_hop->ifindex = newhop->ifindex;
|
|
break;
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
|
resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
|
|
resolved_hop->bh_type = newhop->bh_type;
|
|
break;
|
|
}
|
|
|
|
if (newhop->flags & NEXTHOP_FLAG_ONLINK)
|
|
resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
|
|
|
|
/* Copy labels of the resolved route and the parent resolving to it */
|
|
if (newhop->nh_label) {
|
|
for (i = 0; i < newhop->nh_label->num_labels; i++)
|
|
labels[num_labels++] = newhop->nh_label->label[i];
|
|
label_type = newhop->nh_label_type;
|
|
}
|
|
|
|
if (nexthop->nh_label) {
|
|
for (i = 0; i < nexthop->nh_label->num_labels; i++)
|
|
labels[num_labels++] = nexthop->nh_label->label[i];
|
|
|
|
/* If the parent has labels, use its type */
|
|
label_type = nexthop->nh_label_type;
|
|
}
|
|
|
|
if (num_labels)
|
|
nexthop_add_labels(resolved_hop, label_type, num_labels,
|
|
labels);
|
|
|
|
resolved_hop->rparent = nexthop;
|
|
_nexthop_add(&nexthop->resolved, resolved_hop);
|
|
}
|
|
|
|
/* Checks if nexthop we are trying to resolve to is valid */
|
|
static bool nexthop_valid_resolve(const struct nexthop *nexthop,
|
|
const struct nexthop *resolved)
|
|
{
|
|
/* Can't resolve to a recursive nexthop */
|
|
if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE))
|
|
return false;
|
|
|
|
switch (nexthop->type) {
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
/* If the nexthop we are resolving to does not match the
|
|
* ifindex for the nexthop the route wanted, its not valid.
|
|
*/
|
|
if (nexthop->ifindex != resolved->ifindex)
|
|
return false;
|
|
break;
|
|
case NEXTHOP_TYPE_IPV4:
|
|
case NEXTHOP_TYPE_IPV6:
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Given a nexthop we need to properly recursively resolve
|
|
* the route. As such, do a table lookup to find and match
|
|
* if at all possible. Set the nexthop->ifindex as appropriate
|
|
*/
|
|
static int nexthop_active(afi_t afi, struct route_entry *re,
|
|
struct nexthop *nexthop, struct route_node *top)
|
|
{
|
|
struct prefix p;
|
|
struct route_table *table;
|
|
struct route_node *rn;
|
|
struct route_entry *match = NULL;
|
|
int resolved;
|
|
struct nexthop *newhop;
|
|
struct interface *ifp;
|
|
rib_dest_t *dest;
|
|
struct zebra_vrf *zvrf;
|
|
|
|
if ((nexthop->type == NEXTHOP_TYPE_IPV4)
|
|
|| nexthop->type == NEXTHOP_TYPE_IPV6)
|
|
nexthop->ifindex = 0;
|
|
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
|
|
nexthops_free(nexthop->resolved);
|
|
nexthop->resolved = NULL;
|
|
re->nexthop_mtu = 0;
|
|
|
|
/*
|
|
* If the kernel has sent us a NEW route, then
|
|
* by golly gee whiz it's a good route.
|
|
*
|
|
* If its an already INSTALLED route we have already handled, then the
|
|
* kernel route's nexthop might have became unreachable
|
|
* and we have to handle that.
|
|
*/
|
|
if (!CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)
|
|
&& (re->type == ZEBRA_ROUTE_KERNEL
|
|
|| re->type == ZEBRA_ROUTE_SYSTEM))
|
|
return 1;
|
|
|
|
/*
|
|
* Check to see if we should trust the passed in information
|
|
* for UNNUMBERED interfaces as that we won't find the GW
|
|
* address in the routing table.
|
|
* This check should suffice to handle IPv4 or IPv6 routes
|
|
* sourced from EVPN routes which are installed with the
|
|
* next hop as the remote VTEP IP.
|
|
*/
|
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
|
|
ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
|
|
if (!ifp) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug(
|
|
"\t%s: Onlink and interface: %u[%u] does not exist",
|
|
__PRETTY_FUNCTION__, nexthop->ifindex,
|
|
nexthop->vrf_id);
|
|
return 0;
|
|
}
|
|
if (connected_is_unnumbered(ifp)) {
|
|
if (if_is_operative(ifp))
|
|
return 1;
|
|
else {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug(
|
|
"\t%s: Onlink and interface %s is not operative",
|
|
__PRETTY_FUNCTION__, ifp->name);
|
|
return 0;
|
|
}
|
|
}
|
|
if (!if_is_operative(ifp)) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug(
|
|
"\t%s: Interface %s is not unnumbered",
|
|
__PRETTY_FUNCTION__, ifp->name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Make lookup prefix. */
|
|
memset(&p, 0, sizeof(struct prefix));
|
|
switch (afi) {
|
|
case AFI_IP:
|
|
p.family = AF_INET;
|
|
p.prefixlen = IPV4_MAX_PREFIXLEN;
|
|
p.u.prefix4 = nexthop->gate.ipv4;
|
|
break;
|
|
case AFI_IP6:
|
|
p.family = AF_INET6;
|
|
p.prefixlen = IPV6_MAX_PREFIXLEN;
|
|
p.u.prefix6 = nexthop->gate.ipv6;
|
|
break;
|
|
default:
|
|
assert(afi != AFI_IP && afi != AFI_IP6);
|
|
break;
|
|
}
|
|
/* Lookup table. */
|
|
table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id);
|
|
/* get zvrf */
|
|
zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
|
|
if (!table || !zvrf) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug("\t%s: Table not found",
|
|
__PRETTY_FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
rn = route_node_match(table, (struct prefix *)&p);
|
|
while (rn) {
|
|
route_unlock_node(rn);
|
|
|
|
/* Lookup should halt if we've matched against ourselves ('top',
|
|
* if specified) - i.e., we cannot have a nexthop NH1 is
|
|
* resolved by a route NH1. The exception is if the route is a
|
|
* host route.
|
|
*/
|
|
if (top && rn == top)
|
|
if (((afi == AFI_IP) && (rn->p.prefixlen != 32))
|
|
|| ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug(
|
|
"\t%s: Matched against ourself and prefix length is not max bit length",
|
|
__PRETTY_FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/* Pick up selected route. */
|
|
/* However, do not resolve over default route unless explicitly
|
|
* allowed. */
|
|
if (is_default_prefix(&rn->p)
|
|
&& !rnh_resolve_via_default(zvrf, p.family)) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug(
|
|
"\t:%s: Resolved against default route",
|
|
__PRETTY_FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
dest = rib_dest_from_rnode(rn);
|
|
if (dest && dest->selected_fib
|
|
&& !CHECK_FLAG(dest->selected_fib->status,
|
|
ROUTE_ENTRY_REMOVED)
|
|
&& dest->selected_fib->type != ZEBRA_ROUTE_TABLE)
|
|
match = dest->selected_fib;
|
|
|
|
/* If there is no selected route or matched route is EGP, go up
|
|
tree. */
|
|
if (!match) {
|
|
do {
|
|
rn = rn->parent;
|
|
} while (rn && rn->info == NULL);
|
|
if (rn)
|
|
route_lock_node(rn);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (match->type == ZEBRA_ROUTE_CONNECT) {
|
|
/* Directly point connected route. */
|
|
newhop = match->ng->nexthop;
|
|
if (newhop) {
|
|
if (nexthop->type == NEXTHOP_TYPE_IPV4
|
|
|| nexthop->type == NEXTHOP_TYPE_IPV6)
|
|
nexthop->ifindex = newhop->ifindex;
|
|
}
|
|
return 1;
|
|
} else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
|
|
resolved = 0;
|
|
for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
|
|
if (!CHECK_FLAG(match->status,
|
|
ROUTE_ENTRY_INSTALLED))
|
|
continue;
|
|
if (!nexthop_valid_resolve(nexthop, newhop))
|
|
continue;
|
|
|
|
SET_FLAG(nexthop->flags,
|
|
NEXTHOP_FLAG_RECURSIVE);
|
|
nexthop_set_resolved(afi, newhop, nexthop);
|
|
resolved = 1;
|
|
}
|
|
if (resolved)
|
|
re->nexthop_mtu = match->mtu;
|
|
if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug("\t%s: Recursion failed to find",
|
|
__PRETTY_FUNCTION__);
|
|
return resolved;
|
|
} else if (re->type == ZEBRA_ROUTE_STATIC) {
|
|
resolved = 0;
|
|
for (ALL_NEXTHOPS_PTR(match->ng, newhop)) {
|
|
if (!CHECK_FLAG(match->status,
|
|
ROUTE_ENTRY_INSTALLED))
|
|
continue;
|
|
if (!nexthop_valid_resolve(nexthop, newhop))
|
|
continue;
|
|
|
|
SET_FLAG(nexthop->flags,
|
|
NEXTHOP_FLAG_RECURSIVE);
|
|
nexthop_set_resolved(afi, newhop, nexthop);
|
|
resolved = 1;
|
|
}
|
|
if (resolved)
|
|
re->nexthop_mtu = match->mtu;
|
|
|
|
if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug(
|
|
"\t%s: Static route unable to resolve",
|
|
__PRETTY_FUNCTION__);
|
|
return resolved;
|
|
} else {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
|
|
zlog_debug(
|
|
"\t%s: Route Type %s has not turned on recursion",
|
|
__PRETTY_FUNCTION__,
|
|
zebra_route_string(re->type));
|
|
if (re->type == ZEBRA_ROUTE_BGP
|
|
&& !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP))
|
|
zlog_debug(
|
|
"\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\"");
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug("\t%s: Nexthop did not lookup in table",
|
|
__PRETTY_FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/* This function verifies reachability of one given nexthop, which can be
|
|
* numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
|
|
* in nexthop->flags field. The nexthop->ifindex will be updated
|
|
* appropriately as well. An existing route map can turn
|
|
* (otherwise active) nexthop into inactive, but not vice versa.
|
|
*
|
|
* The return value is the final value of 'ACTIVE' flag.
|
|
*/
|
|
static unsigned nexthop_active_check(struct route_node *rn,
|
|
struct route_entry *re,
|
|
struct nexthop *nexthop)
|
|
{
|
|
struct interface *ifp;
|
|
route_map_result_t ret = RMAP_PERMITMATCH;
|
|
int family;
|
|
char buf[SRCDEST2STR_BUFFER];
|
|
const struct prefix *p, *src_p;
|
|
struct zebra_vrf *zvrf;
|
|
|
|
srcdest_rnode_prefixes(rn, &p, &src_p);
|
|
|
|
if (rn->p.family == AF_INET)
|
|
family = AFI_IP;
|
|
else if (rn->p.family == AF_INET6)
|
|
family = AFI_IP6;
|
|
else
|
|
family = 0;
|
|
switch (nexthop->type) {
|
|
case NEXTHOP_TYPE_IFINDEX:
|
|
ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
|
|
if (ifp && if_is_operative(ifp))
|
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
else
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
break;
|
|
case NEXTHOP_TYPE_IPV4:
|
|
case NEXTHOP_TYPE_IPV4_IFINDEX:
|
|
family = AFI_IP;
|
|
if (nexthop_active(AFI_IP, re, nexthop, rn))
|
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
else
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
break;
|
|
case NEXTHOP_TYPE_IPV6:
|
|
family = AFI_IP6;
|
|
if (nexthop_active(AFI_IP6, re, nexthop, rn))
|
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
else
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
break;
|
|
case NEXTHOP_TYPE_IPV6_IFINDEX:
|
|
/* RFC 5549, v4 prefix with v6 NH */
|
|
if (rn->p.family != AF_INET)
|
|
family = AFI_IP6;
|
|
if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) {
|
|
ifp = if_lookup_by_index(nexthop->ifindex,
|
|
nexthop->vrf_id);
|
|
if (ifp && if_is_operative(ifp))
|
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
else
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
} else {
|
|
if (nexthop_active(AFI_IP6, re, nexthop, rn))
|
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
else
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
}
|
|
break;
|
|
case NEXTHOP_TYPE_BLACKHOLE:
|
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug("\t%s: Unable to find a active nexthop",
|
|
__PRETTY_FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
/* XXX: What exactly do those checks do? Do we support
|
|
* e.g. IPv4 routes with IPv6 nexthops or vice versa?
|
|
*/
|
|
if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET)
|
|
|| (family == AFI_IP6 && p->family != AF_INET6))
|
|
return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
|
|
/* The original code didn't determine the family correctly
|
|
* e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
|
|
* from the rib_table_info in those cases.
|
|
* Possibly it may be better to use only the rib_table_info
|
|
* in every case.
|
|
*/
|
|
if (!family) {
|
|
rib_table_info_t *info;
|
|
|
|
info = srcdest_rnode_table_info(rn);
|
|
family = info->afi;
|
|
}
|
|
|
|
memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr));
|
|
|
|
zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
|
|
if (!zvrf) {
|
|
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
|
|
zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__);
|
|
return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
}
|
|
|
|
/* It'll get set if required inside */
|
|
ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop,
|
|
zvrf, re->tag);
|
|
if (ret == RMAP_DENYMATCH) {
|
|
if (IS_ZEBRA_DEBUG_RIB) {
|
|
srcdest_rnode2str(rn, buf, sizeof(buf));
|
|
zlog_debug(
|
|
"%u:%s: Filtering out with NH out %s due to route map",
|
|
re->vrf_id, buf,
|
|
ifindex2ifname(nexthop->ifindex,
|
|
nexthop->vrf_id));
|
|
}
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
}
|
|
return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
}
|
|
|
|
/*
|
|
* Iterate over all nexthops of the given RIB entry and refresh their
|
|
* ACTIVE flag. If any nexthop is found to toggle the ACTIVE flag,
|
|
* the whole re structure is flagged with ROUTE_ENTRY_CHANGED.
|
|
*
|
|
* Return value is the new number of active nexthops.
|
|
*/
|
|
int nexthop_active_update(struct route_node *rn, struct route_entry *re)
|
|
{
|
|
struct nexthop *nexthop;
|
|
union g_addr prev_src;
|
|
unsigned int prev_active, new_active;
|
|
ifindex_t prev_index;
|
|
uint8_t curr_active = 0;
|
|
|
|
UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
|
|
|
|
for (nexthop = re->ng->nexthop; nexthop; nexthop = nexthop->next) {
|
|
/* No protocol daemon provides src and so we're skipping
|
|
* tracking it */
|
|
prev_src = nexthop->rmap_src;
|
|
prev_active = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
prev_index = nexthop->ifindex;
|
|
/*
|
|
* We need to respect the multipath_num here
|
|
* as that what we should be able to install from
|
|
* a multipath perpsective should not be a data plane
|
|
* decision point.
|
|
*/
|
|
new_active = nexthop_active_check(rn, re, nexthop);
|
|
if (new_active
|
|
&& nexthop_group_active_nexthop_num(re->ng)
|
|
>= zrouter.multipath_num) {
|
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);
|
|
new_active = 0;
|
|
}
|
|
|
|
if (new_active)
|
|
curr_active++;
|
|
|
|
/* Don't allow src setting on IPv6 addr for now */
|
|
if (prev_active != new_active || prev_index != nexthop->ifindex
|
|
|| ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
|
|
&& nexthop->type < NEXTHOP_TYPE_IPV6)
|
|
&& prev_src.ipv4.s_addr
|
|
!= nexthop->rmap_src.ipv4.s_addr)
|
|
|| ((nexthop->type >= NEXTHOP_TYPE_IPV6
|
|
&& nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
|
|
&& !(IPV6_ADDR_SAME(&prev_src.ipv6,
|
|
&nexthop->rmap_src.ipv6)))
|
|
|| CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED))
|
|
SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
|
|
}
|
|
|
|
return curr_active;
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_install_kernel() - Install Nexthop Group hash entry into kernel
|
|
*
|
|
* @nhe: Nexthop Group hash entry to install
|
|
*/
|
|
void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe)
|
|
{
|
|
if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) {
|
|
nhe->is_kernel_nh = false;
|
|
int ret = dplane_nexthop_add(nhe);
|
|
switch (ret) {
|
|
case ZEBRA_DPLANE_REQUEST_QUEUED:
|
|
SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
|
|
break;
|
|
case ZEBRA_DPLANE_REQUEST_FAILURE:
|
|
flog_err(
|
|
EC_ZEBRA_DP_INSTALL_FAIL,
|
|
"Failed to install Nexthop ID (%u) into the kernel",
|
|
nhe->id);
|
|
break;
|
|
case ZEBRA_DPLANE_REQUEST_SUCCESS:
|
|
SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_uninstall_kernel() - Uninstall Nexthop Group hash entry into kernel
|
|
*
|
|
* @nhe: Nexthop Group hash entry to uninstall
|
|
*/
|
|
void zebra_nhg_uninstall_kernel(struct nhg_hash_entry *nhe)
|
|
{
|
|
if (CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED)) {
|
|
int ret = dplane_nexthop_delete(nhe);
|
|
switch (ret) {
|
|
case ZEBRA_DPLANE_REQUEST_QUEUED:
|
|
SET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
|
|
break;
|
|
case ZEBRA_DPLANE_REQUEST_FAILURE:
|
|
flog_err(
|
|
EC_ZEBRA_DP_DELETE_FAIL,
|
|
"Failed to uninstall Nexthop ID (%u) from the kernel",
|
|
nhe->id);
|
|
break;
|
|
case ZEBRA_DPLANE_REQUEST_SUCCESS:
|
|
UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_uninstall_created() - Uninstall nexthops we created in the kernel
|
|
*
|
|
* @nhe: Nexthop group hash entry
|
|
*/
|
|
static void zebra_nhg_uninstall_created(struct hash_bucket *bucket, void *arg)
|
|
{
|
|
struct nhg_hash_entry *nhe = NULL;
|
|
|
|
nhe = (struct nhg_hash_entry *)bucket->data;
|
|
|
|
if (nhe && !nhe->is_kernel_nh)
|
|
zebra_nhg_uninstall_kernel(nhe);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_cleanup_tables() - Iterate over our tables to uninstall nh's
|
|
* we created
|
|
*/
|
|
void zebra_nhg_cleanup_tables(void)
|
|
{
|
|
hash_iterate(zrouter.nhgs, zebra_nhg_uninstall_created, NULL);
|
|
}
|
|
|
|
/**
|
|
* zebra_nhg_dplane_result() - Process dplane result
|
|
*
|
|
* @ctx: Dataplane context
|
|
*/
|
|
void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
|
|
{
|
|
enum dplane_op_e op;
|
|
enum zebra_dplane_result status;
|
|
uint32_t id = 0;
|
|
struct nhg_hash_entry *nhe = NULL;
|
|
|
|
op = dplane_ctx_get_op(ctx);
|
|
status = dplane_ctx_get_status(ctx);
|
|
|
|
id = dplane_ctx_get_nhe_id(ctx);
|
|
nhe = zebra_nhg_lookup_id(id);
|
|
|
|
if (nhe) {
|
|
UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_QUEUED);
|
|
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
|
|
zlog_debug(
|
|
"Nexthop dplane ctx %p, op %s, nexthop ID (%u), result %s",
|
|
ctx, dplane_op2str(op), nhe->id,
|
|
dplane_res2str(status));
|
|
|
|
switch (op) {
|
|
case DPLANE_OP_NH_DELETE:
|
|
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
|
|
UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
|
|
} else {
|
|
flog_err(
|
|
EC_ZEBRA_DP_DELETE_FAIL,
|
|
"Failed to uninstall Nexthop ID (%u) from the kernel",
|
|
nhe->id);
|
|
}
|
|
break;
|
|
case DPLANE_OP_NH_INSTALL:
|
|
case DPLANE_OP_NH_UPDATE:
|
|
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
|
|
SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
|
|
} else {
|
|
flog_err(
|
|
EC_ZEBRA_DP_INSTALL_FAIL,
|
|
"Failed to install Nexthop ID (%u) into the kernel",
|
|
nhe->id);
|
|
UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED);
|
|
}
|
|
break;
|
|
case DPLANE_OP_ROUTE_INSTALL:
|
|
case DPLANE_OP_ROUTE_UPDATE:
|
|
case DPLANE_OP_ROUTE_DELETE:
|
|
case DPLANE_OP_ROUTE_NOTIFY:
|
|
case DPLANE_OP_LSP_INSTALL:
|
|
case DPLANE_OP_LSP_UPDATE:
|
|
case DPLANE_OP_LSP_DELETE:
|
|
case DPLANE_OP_LSP_NOTIFY:
|
|
case DPLANE_OP_PW_INSTALL:
|
|
case DPLANE_OP_PW_UNINSTALL:
|
|
case DPLANE_OP_SYS_ROUTE_ADD:
|
|
case DPLANE_OP_SYS_ROUTE_DELETE:
|
|
case DPLANE_OP_ADDR_INSTALL:
|
|
case DPLANE_OP_ADDR_UNINSTALL:
|
|
case DPLANE_OP_MAC_INSTALL:
|
|
case DPLANE_OP_MAC_DELETE:
|
|
case DPLANE_OP_NONE:
|
|
break;
|
|
}
|
|
dplane_ctx_fini(&ctx);
|
|
|
|
} else {
|
|
flog_err(
|
|
EC_ZEBRA_NHG_SYNC,
|
|
"%s operation preformed on Nexthop ID (%u) in the kernel, that we no longer have in our table",
|
|
dplane_op2str(op), id);
|
|
}
|
|
}
|
|
|