frr/lib/nexthop.c
Carmine Scarpitta 312f7b3f8c lib: Add sidStructure in SRv6 SIDs JSON output
The `show ipv6 route json` command displays the IPv6 routing table in
JSON format, including SRv6 SIDs. For each SRv6 SID, it provides
behavior and SID attributes. However, it does not include the SID
structure.

This commit adds the SID structure to the SRv6 SID JSON output.

Signed-off-by: Carmine Scarpitta <cscarpit@cisco.com>
2025-02-08 22:31:19 +01:00

1500 lines
37 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/* A generic nexthop structure
* Copyright (C) 2013 Cumulus Networks, Inc.
*/
#include <zebra.h>
#include "prefix.h"
#include "table.h"
#include "memory.h"
#include "command.h"
#include "log.h"
#include "sockunion.h"
#include "linklist.h"
#include "prefix.h"
#include "nexthop.h"
#include "mpls.h"
#include "jhash.h"
#include "printfrr.h"
#include "vrf.h"
#include "nexthop_group.h"
DEFINE_MTYPE_STATIC(LIB, NEXTHOP, "Nexthop");
DEFINE_MTYPE_STATIC(LIB, NH_LABEL, "Nexthop label");
DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6");
static int _nexthop_labels_cmp(const struct nexthop *nh1,
const struct nexthop *nh2)
{
const struct mpls_label_stack *nhl1 = NULL;
const struct mpls_label_stack *nhl2 = NULL;
nhl1 = nh1->nh_label;
nhl2 = nh2->nh_label;
/* No labels is a match */
if (!nhl1 && !nhl2)
return 0;
if (nhl1 && !nhl2)
return 1;
if (nhl2 && !nhl1)
return -1;
if (nhl1->num_labels > nhl2->num_labels)
return 1;
if (nhl1->num_labels < nhl2->num_labels)
return -1;
return memcmp(nhl1->label, nhl2->label,
(nhl1->num_labels * sizeof(mpls_label_t)));
}
static int _nexthop_srv6_cmp(const struct nexthop *nh1,
const struct nexthop *nh2)
{
int ret = 0;
int i = 0;
if (!nh1->nh_srv6 && !nh2->nh_srv6)
return 0;
if (nh1->nh_srv6 && !nh2->nh_srv6)
return 1;
if (!nh1->nh_srv6 && nh2->nh_srv6)
return -1;
if (nh1->nh_srv6->seg6local_action > nh2->nh_srv6->seg6local_action)
return 1;
if (nh2->nh_srv6->seg6local_action < nh1->nh_srv6->seg6local_action)
return -1;
ret = memcmp(&nh1->nh_srv6->seg6local_ctx,
&nh2->nh_srv6->seg6local_ctx,
sizeof(struct seg6local_context));
if (ret != 0)
return ret;
if (!nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs)
return 0;
if (!nh1->nh_srv6->seg6_segs && nh2->nh_srv6->seg6_segs)
return -1;
if (nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs)
return 1;
if (nh1->nh_srv6->seg6_segs->num_segs !=
nh2->nh_srv6->seg6_segs->num_segs)
return -1;
for (i = 0; i < nh1->nh_srv6->seg6_segs->num_segs; i++) {
ret = memcmp(&nh1->nh_srv6->seg6_segs->seg[i],
&nh2->nh_srv6->seg6_segs->seg[i],
sizeof(struct in6_addr));
if (ret != 0)
break;
}
return ret;
}
int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1,
const union g_addr *addr2)
{
int ret = 0;
switch (type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
ret = IPV4_ADDR_CMP(&addr1->ipv4, &addr2->ipv4);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
ret = IPV6_ADDR_CMP(&addr1->ipv6, &addr2->ipv6);
break;
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_BLACKHOLE:
/* No addr here */
break;
}
return ret;
}
static int _nexthop_gateway_cmp(const struct nexthop *nh1,
const struct nexthop *nh2)
{
return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
}
static int _nexthop_source_cmp(const struct nexthop *nh1,
const struct nexthop *nh2)
{
return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
}
static int _nexthop_cmp_no_labels(const struct nexthop *next1,
const struct nexthop *next2, bool use_weight)
{
int ret = 0;
if (next1->vrf_id < next2->vrf_id)
return -1;
if (next1->vrf_id > next2->vrf_id)
return 1;
if (next1->type < next2->type)
return -1;
if (next1->type > next2->type)
return 1;
if (use_weight) {
if (next1->weight < next2->weight)
return -1;
if (next1->weight > next2->weight)
return 1;
}
switch (next1->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV6:
ret = _nexthop_gateway_cmp(next1, next2);
if (ret != 0)
return ret;
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV6_IFINDEX:
ret = _nexthop_gateway_cmp(next1, next2);
if (ret != 0)
return ret;
fallthrough;
case NEXTHOP_TYPE_IFINDEX:
if (next1->ifindex < next2->ifindex)
return -1;
if (next1->ifindex > next2->ifindex)
return 1;
break;
case NEXTHOP_TYPE_BLACKHOLE:
if (next1->bh_type < next2->bh_type)
return -1;
if (next1->bh_type > next2->bh_type)
return 1;
break;
}
if (next1->srte_color < next2->srte_color)
return -1;
if (next1->srte_color > next2->srte_color)
return 1;
ret = _nexthop_source_cmp(next1, next2);
if (ret != 0)
goto done;
if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
!CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return 0;
if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return -1;
if (CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
!CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return 1;
if (next1->backup_num == 0 && next2->backup_num == 0)
goto done;
if (next1->backup_num < next2->backup_num)
return -1;
if (next1->backup_num > next2->backup_num)
return 1;
ret = memcmp(next1->backup_idx,
next2->backup_idx, next1->backup_num);
done:
return ret;
}
static int nexthop_cmp_internal(const struct nexthop *next1,
const struct nexthop *next2, bool use_weight)
{
int ret = 0;
ret = _nexthop_cmp_no_labels(next1, next2, use_weight);
if (ret != 0)
return ret;
ret = _nexthop_labels_cmp(next1, next2);
if (ret != 0)
return ret;
ret = _nexthop_srv6_cmp(next1, next2);
return ret;
}
int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)
{
return nexthop_cmp_internal(next1, next2, true);
}
int nexthop_cmp_no_weight(const struct nexthop *next1,
const struct nexthop *next2)
{
return nexthop_cmp_internal(next1, next2, false);
}
/*
* More-limited comparison function used to detect duplicate
* nexthops. This is used in places where we don't need the full
* comparison of 'nexthop_cmp()'.
*/
int nexthop_cmp_basic(const struct nexthop *nh1,
const struct nexthop *nh2)
{
int ret = 0;
const struct mpls_label_stack *nhl1 = NULL;
const struct mpls_label_stack *nhl2 = NULL;
if (nh1 == NULL && nh2 == NULL)
return 0;
if (nh1 && !nh2)
return 1;
if (!nh1 && nh2)
return -1;
if (nh1->vrf_id < nh2->vrf_id)
return -1;
if (nh1->vrf_id > nh2->vrf_id)
return 1;
if (nh1->type < nh2->type)
return -1;
if (nh1->type > nh2->type)
return 1;
if (nh1->weight < nh2->weight)
return -1;
if (nh1->weight > nh2->weight)
return 1;
switch (nh1->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV6:
ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
if (ret != 0)
return ret;
break;
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV6_IFINDEX:
ret = nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);
if (ret != 0)
return ret;
fallthrough;
case NEXTHOP_TYPE_IFINDEX:
if (nh1->ifindex < nh2->ifindex)
return -1;
if (nh1->ifindex > nh2->ifindex)
return 1;
break;
case NEXTHOP_TYPE_BLACKHOLE:
if (nh1->bh_type < nh2->bh_type)
return -1;
if (nh1->bh_type > nh2->bh_type)
return 1;
break;
}
/* Compare source addr */
ret = nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);
if (ret != 0)
goto done;
nhl1 = nh1->nh_label;
nhl2 = nh2->nh_label;
/* No labels is a match */
if (!nhl1 && !nhl2)
return 0;
if (nhl1 && !nhl2)
return 1;
if (nhl2 && !nhl1)
return -1;
if (nhl1->num_labels > nhl2->num_labels)
return 1;
if (nhl1->num_labels < nhl2->num_labels)
return -1;
ret = memcmp(nhl1->label, nhl2->label,
(nhl1->num_labels * sizeof(mpls_label_t)));
done:
return ret;
}
/*
* nexthop_type_to_str
*/
const char *nexthop_type_to_str(enum nexthop_types_t nh_type)
{
static const char *const desc[] = {
"none", "Directly connected",
"IPv4 nexthop", "IPv4 nexthop with ifindex",
"IPv6 nexthop", "IPv6 nexthop with ifindex",
"Null0 nexthop",
};
return desc[nh_type];
}
/*
* Check if the labels match for the 2 nexthops specified.
*/
bool nexthop_labels_match(const struct nexthop *nh1, const struct nexthop *nh2)
{
if (_nexthop_labels_cmp(nh1, nh2) != 0)
return false;
return true;
}
struct nexthop *nexthop_new(void)
{
struct nexthop *nh;
nh = XCALLOC(MTYPE_NEXTHOP, sizeof(struct nexthop));
/*
* Default the weight to 1 here for all nexthops.
* The linux kernel does some weird stuff with adding +1 to
* all nexthop weights it gets over netlink.
* To handle this, just default everything to 1 right from
* the beginning so we don't have to special case
* default weights in the linux netlink code.
*
* 1 should be a valid on all platforms anyway.
*/
nh->weight = 1;
return nh;
}
/* Free nexthop. */
void nexthop_free(struct nexthop *nexthop)
{
nexthop_del_labels(nexthop);
nexthop_del_srv6_seg6local(nexthop);
nexthop_del_srv6_seg6(nexthop);
if (nexthop->resolved)
nexthops_free(nexthop->resolved);
XFREE(MTYPE_NEXTHOP, nexthop);
}
/* Frees a list of nexthops */
void nexthops_free(struct nexthop *nexthop)
{
struct nexthop *nh, *next;
for (nh = nexthop; nh; nh = next) {
next = nh->next;
nexthop_free(nh);
}
}
bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2)
{
if (nh1 && !nh2)
return false;
if (!nh1 && nh2)
return false;
if (nh1 == nh2)
return true;
if (nexthop_cmp(nh1, nh2) != 0)
return false;
return true;
}
bool nexthop_same_no_labels(const struct nexthop *nh1,
const struct nexthop *nh2)
{
if (nh1 && !nh2)
return false;
if (!nh1 && nh2)
return false;
if (nh1 == nh2)
return true;
if (_nexthop_cmp_no_labels(nh1, nh2, true) != 0)
return false;
return true;
}
/*
* Allocate a new nexthop object and initialize it from various args.
*/
struct nexthop *nexthop_from_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->type = NEXTHOP_TYPE_IFINDEX;
nexthop->ifindex = ifindex;
nexthop->vrf_id = vrf_id;
return nexthop;
}
struct nexthop *nexthop_from_ipv4(const struct in_addr *ipv4,
const struct in_addr *src,
vrf_id_t vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->type = NEXTHOP_TYPE_IPV4;
nexthop->vrf_id = vrf_id;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
return nexthop;
}
struct nexthop *nexthop_from_ipv4_ifindex(const struct in_addr *ipv4,
const struct in_addr *src,
ifindex_t ifindex, vrf_id_t vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
nexthop->vrf_id = vrf_id;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
nexthop->ifindex = ifindex;
return nexthop;
}
struct nexthop *nexthop_from_ipv6(const struct in6_addr *ipv6,
vrf_id_t vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->vrf_id = vrf_id;
nexthop->type = NEXTHOP_TYPE_IPV6;
nexthop->gate.ipv6 = *ipv6;
return nexthop;
}
struct nexthop *nexthop_from_ipv6_ifindex(const struct in6_addr *ipv6,
ifindex_t ifindex, vrf_id_t vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->vrf_id = vrf_id;
nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifindex = ifindex;
return nexthop;
}
struct nexthop *nexthop_from_blackhole(enum blackhole_type bh_type,
vrf_id_t nh_vrf_id)
{
struct nexthop *nexthop;
nexthop = nexthop_new();
nexthop->vrf_id = nh_vrf_id;
nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
nexthop->bh_type = bh_type;
return nexthop;
}
/* Update nexthop with label information. */
void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t ltype,
uint8_t num_labels, const mpls_label_t *labels)
{
struct mpls_label_stack *nh_label;
int i;
if (num_labels == 0)
return;
/* Enforce limit on label stack size */
if (num_labels > MPLS_MAX_LABELS)
num_labels = MPLS_MAX_LABELS;
nexthop->nh_label_type = ltype;
nh_label = XCALLOC(MTYPE_NH_LABEL,
sizeof(struct mpls_label_stack)
+ num_labels * sizeof(mpls_label_t));
nh_label->num_labels = num_labels;
for (i = 0; i < num_labels; i++)
nh_label->label[i] = *(labels + i);
nexthop->nh_label = nh_label;
}
/* Free label information of nexthop, if present. */
void nexthop_del_labels(struct nexthop *nexthop)
{
XFREE(MTYPE_NH_LABEL, nexthop->nh_label);
nexthop->nh_label_type = ZEBRA_LSP_NONE;
}
void nexthop_change_labels(struct nexthop *nexthop, struct mpls_label_stack *new_stack)
{
struct mpls_label_stack *nh_label_tmp;
uint32_t i;
/* Enforce limit on label stack size */
if (new_stack->num_labels > MPLS_MAX_LABELS)
new_stack->num_labels = MPLS_MAX_LABELS;
/* Resize the array to accommodate the new label stack */
if (new_stack->num_labels > nexthop->nh_label->num_labels) {
nh_label_tmp = XREALLOC(MTYPE_NH_LABEL, nexthop->nh_label,
sizeof(struct mpls_label_stack) +
new_stack->num_labels * sizeof(mpls_label_t));
if (nh_label_tmp) {
nexthop->nh_label = nh_label_tmp;
nexthop->nh_label->num_labels = new_stack->num_labels;
} else
new_stack->num_labels = nexthop->nh_label->num_labels;
}
/* Copy the label stack into the array */
for (i = 0; i < new_stack->num_labels; i++)
nexthop->nh_label->label[i] = new_stack->label[i];
}
void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action,
const struct seg6local_context *ctx)
{
if (action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
return;
if (!nexthop->nh_srv6)
nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
sizeof(struct nexthop_srv6));
nexthop->nh_srv6->seg6local_action = action;
nexthop->nh_srv6->seg6local_ctx = *ctx;
}
void nexthop_del_srv6_seg6local(struct nexthop *nexthop)
{
if (!nexthop->nh_srv6)
return;
if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
return;
nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
if (nexthop->nh_srv6->seg6_segs &&
(nexthop->nh_srv6->seg6_segs->num_segs == 0 ||
sid_zero(nexthop->nh_srv6->seg6_segs)))
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs);
if (nexthop->nh_srv6->seg6_segs == NULL)
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
}
void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs,
int num_segs)
{
int i;
if (!segs)
return;
if (!nexthop->nh_srv6)
nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6,
sizeof(struct nexthop_srv6));
/* Enforce limit on srv6 seg stack size */
if (num_segs > SRV6_MAX_SIDS)
num_segs = SRV6_MAX_SIDS;
if (!nexthop->nh_srv6->seg6_segs) {
nexthop->nh_srv6->seg6_segs =
XCALLOC(MTYPE_NH_SRV6,
sizeof(struct seg6_seg_stack) +
num_segs * sizeof(struct in6_addr));
}
nexthop->nh_srv6->seg6_segs->num_segs = num_segs;
for (i = 0; i < num_segs; i++)
memcpy(&nexthop->nh_srv6->seg6_segs->seg[i], &segs[i],
sizeof(struct in6_addr));
}
void nexthop_del_srv6_seg6(struct nexthop *nexthop)
{
if (!nexthop->nh_srv6)
return;
if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC &&
nexthop->nh_srv6->seg6_segs) {
memset(nexthop->nh_srv6->seg6_segs->seg, 0,
sizeof(struct in6_addr) *
nexthop->nh_srv6->seg6_segs->num_segs);
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs);
}
XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6);
}
const char *nexthop2str(const struct nexthop *nexthop, char *str, int size)
{
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
snprintf(str, size, "if %u", nexthop->ifindex);
break;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
snprintfrr(str, size, "%pI4 if %u", &nexthop->gate.ipv4,
nexthop->ifindex);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
snprintfrr(str, size, "%pI6 if %u", &nexthop->gate.ipv6,
nexthop->ifindex);
break;
case NEXTHOP_TYPE_BLACKHOLE:
snprintf(str, size, "blackhole");
break;
}
return str;
}
/*
* Iteration step for ALL_NEXTHOPS macro:
* This is the tricky part. Check if `nexthop' has
* NEXTHOP_FLAG_RECURSIVE set. If yes, this implies that `nexthop' has
* at least one nexthop attached to `nexthop->resolved', which will be
* the next one.
*
* If NEXTHOP_FLAG_RECURSIVE is not set, `nexthop' will progress in its
* current chain. In case its current chain end is reached, it will move
* upwards in the recursion levels and progress there. Whenever a step
* forward in a chain is done, recursion will be checked again.
* In a nustshell, it's equivalent to a pre-traversal order assuming that
* left branch is 'resolved' and right branch is 'next':
* https://en.wikipedia.org/wiki/Tree_traversal#/media/File:Sorted_binary_tree_preorder.svg
*/
struct nexthop *nexthop_next(const struct nexthop *nexthop)
{
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
return nexthop->resolved;
if (nexthop->next)
return nexthop->next;
for (struct nexthop *par = nexthop->rparent; par; par = par->rparent)
if (par->next)
return par->next;
return NULL;
}
struct nexthop *nexthop_next_resolution(const struct nexthop *nexthop,
bool nexthop_resolution)
{
if (nexthop_resolution)
return nexthop_next(nexthop);
/* no resolution attempt */
return nexthop->next;
}
/* Return the next nexthop in the tree that is resolved and active */
struct nexthop *nexthop_next_active_resolved(const struct nexthop *nexthop)
{
struct nexthop *next = nexthop_next(nexthop);
while (next
&& (CHECK_FLAG(next->flags, NEXTHOP_FLAG_RECURSIVE)
|| !CHECK_FLAG(next->flags, NEXTHOP_FLAG_ACTIVE)))
next = nexthop_next(next);
return next;
}
unsigned int nexthop_level(const struct nexthop *nexthop)
{
unsigned int rv = 0;
for (const struct nexthop *par = nexthop->rparent;
par; par = par->rparent)
rv++;
return rv;
}
uint32_t nexthop_hash(const struct nexthop *nexthop)
{
uint32_t key = 0x45afe398;
/* type, vrf, ifindex, ip addresses - see nexthop.h */
key = _nexthop_hash_bytes(nexthop, key);
key = jhash_1word(nexthop->flags & NEXTHOP_FLAGS_HASHED, key);
if (nexthop->nh_label) {
const struct mpls_label_stack *ls = nexthop->nh_label;
/* num_labels itself isn't useful to hash, if the number of
* labels is different, the hash value will change just due to
* that already.
*/
key = jhash(ls->label, sizeof(ls->label[0]) * ls->num_labels, key);
}
/* Include backup nexthops, if present */
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
int backups = nexthop->backup_num;
key = jhash(nexthop->backup_idx, sizeof(nexthop->backup_idx[0]) * backups, key);
}
if (nexthop->nh_srv6) {
int segs_num = 0;
int i = 0;
if (nexthop->nh_srv6->seg6local_action !=
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) {
key = jhash_1word(nexthop->nh_srv6->seg6local_action,
key);
key = jhash(&nexthop->nh_srv6->seg6local_ctx,
sizeof(nexthop->nh_srv6->seg6local_ctx),
key);
if (nexthop->nh_srv6->seg6_segs)
key = jhash(&nexthop->nh_srv6->seg6_segs->seg[0],
sizeof(struct in6_addr), key);
} else {
if (nexthop->nh_srv6->seg6_segs) {
segs_num = nexthop->nh_srv6->seg6_segs->num_segs;
while (segs_num >= 1) {
key = jhash(&nexthop->nh_srv6->seg6_segs
->seg[i],
sizeof(struct in6_addr),
key);
segs_num -= 1;
i += 1;
}
}
}
}
return key;
}
void nexthop_copy_no_recurse(struct nexthop *copy,
const struct nexthop *nexthop,
struct nexthop *rparent)
{
copy->vrf_id = nexthop->vrf_id;
copy->ifindex = nexthop->ifindex;
copy->type = nexthop->type;
copy->flags = nexthop->flags;
copy->weight = nexthop->weight;
assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS);
copy->backup_num = nexthop->backup_num;
if (copy->backup_num > 0)
memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
copy->srte_color = nexthop->srte_color;
memcpy(&copy->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(&copy->src, &nexthop->src, sizeof(nexthop->src));
memcpy(&copy->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
memcpy(&copy->rmac, &nexthop->rmac, sizeof(nexthop->rmac));
copy->rparent = rparent;
if (nexthop->nh_label)
nexthop_add_labels(copy, nexthop->nh_label_type,
nexthop->nh_label->num_labels,
&nexthop->nh_label->label[0]);
if (nexthop->nh_srv6) {
if (nexthop->nh_srv6->seg6local_action !=
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
nexthop_add_srv6_seg6local(copy,
nexthop->nh_srv6->seg6local_action,
&nexthop->nh_srv6->seg6local_ctx);
if (nexthop->nh_srv6->seg6_segs &&
nexthop->nh_srv6->seg6_segs->num_segs &&
!sid_zero(nexthop->nh_srv6->seg6_segs))
nexthop_add_srv6_seg6(copy,
&nexthop->nh_srv6->seg6_segs->seg[0],
nexthop->nh_srv6->seg6_segs
->num_segs);
}
}
void nexthop_copy(struct nexthop *copy, const struct nexthop *nexthop,
struct nexthop *rparent)
{
nexthop_copy_no_recurse(copy, nexthop, rparent);
/* Bit of a special case here, we need to handle the case
* of a nexthop resolving to a group. Hence, we need to
* use a nexthop_group API.
*/
if (CHECK_FLAG(copy->flags, NEXTHOP_FLAG_RECURSIVE))
copy_nexthops(&copy->resolved, nexthop->resolved, copy);
}
struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop,
struct nexthop *rparent)
{
struct nexthop *new = nexthop_new();
nexthop_copy_no_recurse(new, nexthop, rparent);
return new;
}
struct nexthop *nexthop_dup(const struct nexthop *nexthop,
struct nexthop *rparent)
{
struct nexthop *new = nexthop_new();
nexthop_copy(new, nexthop, rparent);
return new;
}
/*
* Parse one or more backup index values, as comma-separated numbers,
* into caller's array of uint8_ts. The array must be NEXTHOP_MAX_BACKUPS
* in size. Mails back the number of values converted, and returns 0 on
* success, <0 if an error in parsing.
*/
int nexthop_str2backups(const char *str, int *num_backups,
uint8_t *backups)
{
char *ostr; /* copy of string (start) */
char *lstr; /* working copy of string */
char *nump; /* pointer to next segment */
char *endp; /* end pointer */
int i, ret;
uint8_t tmp[NEXTHOP_MAX_BACKUPS];
uint32_t lval;
/* Copy incoming string; the parse is destructive */
lstr = ostr = XSTRDUP(MTYPE_TMP, str);
*num_backups = 0;
ret = 0;
for (i = 0; i < NEXTHOP_MAX_BACKUPS && lstr; i++) {
nump = strsep(&lstr, ",");
lval = strtoul(nump, &endp, 10);
/* Format check */
if (*endp != '\0') {
ret = -1;
break;
}
/* Empty value */
if (endp == nump) {
ret = -1;
break;
}
/* Limit to one octet */
if (lval > 255) {
ret = -1;
break;
}
tmp[i] = lval;
}
/* Excess values */
if (ret == 0 && i == NEXTHOP_MAX_BACKUPS && lstr)
ret = -1;
if (ret == 0) {
*num_backups = i;
memcpy(backups, tmp, i);
}
XFREE(MTYPE_TMP, ostr);
return ret;
}
ssize_t printfrr_nhs(struct fbuf *buf, const struct nexthop *nexthop)
{
ssize_t ret = 0;
if (!nexthop)
return bputs(buf, "(null)");
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
ret += bprintfrr(buf, "if %u", nexthop->ifindex);
break;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
ret += bprintfrr(buf, "%pI4 if %u", &nexthop->gate.ipv4,
nexthop->ifindex);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
ret += bprintfrr(buf, "%pI6 if %u", &nexthop->gate.ipv6,
nexthop->ifindex);
break;
case NEXTHOP_TYPE_BLACKHOLE:
ret += bputs(buf, "blackhole");
break;
}
ret += bprintfrr(buf, " vrfid %u", nexthop->vrf_id);
return ret;
}
/*
* nexthop printing variants:
* %pNHvv
* via 1.2.3.4
* via 1.2.3.4, eth0
* is directly connected, eth0
* unreachable (blackhole)
* %pNHv
* 1.2.3.4
* 1.2.3.4, via eth0
* directly connected, eth0
* unreachable (blackhole)
* %pNHs
* nexthop2str()
* %pNHcg
* 1.2.3.4
* (0-length if no IP address present)
* %pNHci
* eth0
* (0-length if no interface present)
*/
printfrr_ext_autoreg_p("NH", printfrr_nh);
static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
const struct nexthop *nexthop = ptr;
bool do_ifi = false;
const char *v_is = "", *v_via = "", *v_viaif = "via ";
ssize_t ret = 0;
switch (*ea->fmt) {
case 'v':
ea->fmt++;
if (*ea->fmt == 'v') {
v_is = "is ";
v_via = "via ";
v_viaif = "";
ea->fmt++;
}
if (!nexthop)
return bputs(buf, "(null)");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
ret += bprintfrr(buf, "%s%pI4", v_via,
&nexthop->gate.ipv4);
do_ifi = true;
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
ret += bprintfrr(buf, "%s%pI6", v_via,
&nexthop->gate.ipv6);
do_ifi = true;
break;
case NEXTHOP_TYPE_IFINDEX:
ret += bprintfrr(buf, "%sdirectly connected, %s", v_is,
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
ret += bputs(buf, "unreachable");
switch (nexthop->bh_type) {
case BLACKHOLE_REJECT:
ret += bputs(buf, " (ICMP unreachable)");
break;
case BLACKHOLE_ADMINPROHIB:
ret += bputs(buf, " (ICMP admin-prohibited)");
break;
case BLACKHOLE_NULL:
ret += bputs(buf, " (blackhole)");
break;
case BLACKHOLE_UNSPEC:
break;
}
break;
}
if (do_ifi && nexthop->ifindex)
ret += bprintfrr(buf, ", %s%s", v_viaif,
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
return ret;
case 's':
ea->fmt++;
ret += printfrr_nhs(buf, nexthop);
return ret;
case 'c':
ea->fmt++;
if (*ea->fmt == 'g') {
ea->fmt++;
if (!nexthop)
return bputs(buf, "(null)");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
ret += bprintfrr(buf, "%pI4",
&nexthop->gate.ipv4);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
ret += bprintfrr(buf, "%pI6",
&nexthop->gate.ipv6);
break;
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_BLACKHOLE:
break;
}
} else if (*ea->fmt == 'i') {
ea->fmt++;
if (!nexthop)
return bputs(buf, "(null)");
switch (nexthop->type) {
case NEXTHOP_TYPE_IFINDEX:
ret += bprintfrr(
buf, "%s",
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
if (nexthop->ifindex)
ret += bprintfrr(
buf, "%s",
ifindex2ifname(
nexthop->ifindex,
nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
break;
}
}
return ret;
}
return -1;
}
bool nexthop_is_blackhole(const struct nexthop *nh)
{
return nh->type == NEXTHOP_TYPE_BLACKHOLE;
}
/*
* Render a nexthop into a json object; the caller allocates and owns
* the json object memory.
*/
void nexthop_json_helper(json_object *json_nexthop,
const struct nexthop *nexthop, bool display_vrfid,
uint8_t rn_family)
{
json_object *json_labels = NULL;
json_object *json_backups = NULL;
json_object *json_seg6local = NULL;
json_object *json_seg6local_context = NULL;
json_object *json_srv6_sid_structure = NULL;
json_object *json_seg6 = NULL;
json_object *json_segs = NULL;
int i;
json_object_int_add(json_nexthop, "flags", nexthop->flags);
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
json_object_boolean_true_add(json_nexthop, "duplicate");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
json_object_boolean_true_add(json_nexthop, "fib");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
json_object_string_addf(json_nexthop, "ip", "%pI4",
&nexthop->gate.ipv4);
json_object_string_add(json_nexthop, "afi", "ipv4");
if (nexthop->ifindex) {
json_object_int_add(json_nexthop, "interfaceIndex",
nexthop->ifindex);
json_object_string_add(json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
}
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
json_object_string_addf(json_nexthop, "ip", "%pI6",
&nexthop->gate.ipv6);
json_object_string_add(json_nexthop, "afi", "ipv6");
if (nexthop->ifindex) {
json_object_int_add(json_nexthop, "interfaceIndex",
nexthop->ifindex);
json_object_string_add(json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
}
break;
case NEXTHOP_TYPE_IFINDEX:
json_object_boolean_true_add(json_nexthop, "directlyConnected");
json_object_int_add(json_nexthop, "interfaceIndex",
nexthop->ifindex);
json_object_string_add(json_nexthop, "interfaceName",
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
json_object_boolean_true_add(json_nexthop, "unreachable");
switch (nexthop->bh_type) {
case BLACKHOLE_REJECT:
json_object_boolean_true_add(json_nexthop, "reject");
break;
case BLACKHOLE_ADMINPROHIB:
json_object_boolean_true_add(json_nexthop,
"adminProhibited");
break;
case BLACKHOLE_NULL:
json_object_boolean_true_add(json_nexthop, "blackhole");
break;
case BLACKHOLE_UNSPEC:
break;
}
break;
}
/* This nexthop is a resolver for the parent nexthop.
* Set resolver flag for better clarity and delimiter
* in flat list of nexthops in json.
*/
if (nexthop->rparent)
json_object_boolean_true_add(json_nexthop, "resolver");
if (display_vrfid)
json_object_string_add(json_nexthop, "vrf",
vrf_id_to_name(nexthop->vrf_id));
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE))
json_object_boolean_true_add(json_nexthop, "duplicate");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
json_object_boolean_true_add(json_nexthop, "active");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
json_object_boolean_true_add(json_nexthop, "onLink");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
json_object_boolean_true_add(json_nexthop, "linkDown");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
json_object_boolean_true_add(json_nexthop, "recursive");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
json_backups = json_object_new_array();
for (i = 0; i < nexthop->backup_num; i++) {
json_object_array_add(json_backups,
json_object_new_int(
nexthop->backup_idx[i]));
}
json_object_object_add(json_nexthop, "backupIndex",
json_backups);
}
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
if (nexthop->rmap_src.ipv4.s_addr)
json_object_string_addf(json_nexthop, "rmapSource",
"%pI4", &nexthop->rmap_src.ipv4);
else if (nexthop->src.ipv4.s_addr)
json_object_string_addf(json_nexthop, "source", "%pI4",
&nexthop->src.ipv4);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
/* Allow for 5549 ipv4 prefix with ipv6 nexthop */
if (rn_family == AF_INET && nexthop->rmap_src.ipv4.s_addr)
json_object_string_addf(json_nexthop, "rmapSource",
"%pI4", &nexthop->rmap_src.ipv4);
else if (!IPV6_ADDR_SAME(&nexthop->rmap_src.ipv6, &in6addr_any))
json_object_string_addf(json_nexthop, "rmapSource",
"%pI6", &nexthop->rmap_src.ipv6);
else if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
json_object_string_addf(json_nexthop, "source", "%pI6",
&nexthop->src.ipv6);
break;
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_BLACKHOLE:
break;
}
if (nexthop->nh_label && nexthop->nh_label->num_labels) {
json_labels = json_object_new_array();
for (int label_index = 0;
label_index < nexthop->nh_label->num_labels; label_index++)
json_object_array_add(
json_labels,
json_object_new_int((
(nexthop->nh_label_type == ZEBRA_LSP_EVPN)
? label2vni(
&nexthop->nh_label->label
[label_index])
: nexthop->nh_label
->label[label_index])));
json_object_object_add(json_nexthop, "labels", json_labels);
}
if (nexthop->weight)
json_object_int_add(json_nexthop, "weight", nexthop->weight);
if (nexthop->srte_color)
json_object_int_add(json_nexthop, "srteColor",
nexthop->srte_color);
if (nexthop->nh_srv6) {
json_seg6local = json_object_new_object();
json_object_string_add(json_seg6local, "action",
seg6local_action2str(
nexthop->nh_srv6
->seg6local_action));
json_seg6local_context = json_object_new_object();
json_object_object_add(json_nexthop, "seg6local",
json_seg6local);
seg6local_context2json(&nexthop->nh_srv6->seg6local_ctx,
nexthop->nh_srv6->seg6local_action,
json_seg6local_context);
json_object_object_add(json_nexthop, "seg6localContext",
json_seg6local_context);
json_srv6_sid_structure = json_object_new_object();
srv6_sid_structure2json(&nexthop->nh_srv6->seg6local_ctx, json_srv6_sid_structure);
json_object_object_add(json_seg6local, "sidStructure", json_srv6_sid_structure);
if (nexthop->nh_srv6->seg6_segs &&
nexthop->nh_srv6->seg6_segs->num_segs == 1) {
json_seg6 = json_object_new_object();
json_object_string_addf(json_seg6, "segs", "%pI6",
&nexthop->nh_srv6->seg6_segs
->seg[0]);
json_object_object_add(json_nexthop, "seg6", json_seg6);
} else {
if (nexthop->nh_srv6->seg6_segs) {
json_segs = json_object_new_array();
for (int seg_idx = 0;
seg_idx <
nexthop->nh_srv6->seg6_segs->num_segs;
seg_idx++)
json_object_array_add(
json_segs,
json_object_new_stringf(
"%pI6",
&nexthop->nh_srv6
->seg6_segs
->seg[seg_idx]));
json_object_object_add(json_nexthop, "seg6",
json_segs);
}
}
}
}
/*
* Helper for nexthop output
*/
void nexthop_vty_helper(struct vty *vty, const struct nexthop *nexthop,
bool display_vrfid, uint8_t rn_family)
{
char buf[MPLS_LABEL_STRLEN];
char seg_buf[SRV6_SEG_STRLEN];
struct seg6_segs segs;
uint8_t i;
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
vty_out(vty, " via %pI4", &nexthop->gate.ipv4);
if (nexthop->ifindex)
vty_out(vty, ", %s",
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
vty_out(vty, " via %s",
inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
sizeof(buf)));
if (nexthop->ifindex)
vty_out(vty, ", %s",
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
break;
case NEXTHOP_TYPE_IFINDEX:
vty_out(vty, " is directly connected, %s",
ifindex2ifname(nexthop->ifindex, nexthop->vrf_id));
break;
case NEXTHOP_TYPE_BLACKHOLE:
vty_out(vty, " unreachable");
switch (nexthop->bh_type) {
case BLACKHOLE_REJECT:
vty_out(vty, " (ICMP unreachable)");
break;
case BLACKHOLE_ADMINPROHIB:
vty_out(vty, " (ICMP admin-prohibited)");
break;
case BLACKHOLE_NULL:
vty_out(vty, " (blackhole)");
break;
case BLACKHOLE_UNSPEC:
break;
}
break;
}
if (display_vrfid)
vty_out(vty, " (vrf %s)", vrf_id_to_name(nexthop->vrf_id));
if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
vty_out(vty, " inactive");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
vty_out(vty, " onlink");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_LINKDOWN))
vty_out(vty, " linkdown");
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
vty_out(vty, " (recursive)");
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
if (nexthop->rmap_src.ipv4.s_addr)
vty_out(vty, ", rmapsrc %pI4", &nexthop->rmap_src.ipv4);
else if (nexthop->src.ipv4.s_addr)
vty_out(vty, ", src %pI4", &nexthop->src.ipv4);
break;
case NEXTHOP_TYPE_IPV6:
case NEXTHOP_TYPE_IPV6_IFINDEX:
/* Allow for 5549 ipv4 prefix with ipv6 nexthop */
if (rn_family == AF_INET && nexthop->rmap_src.ipv4.s_addr)
vty_out(vty, ", rmapsrc %pI4", &nexthop->rmap_src.ipv4);
else if (!IPV6_ADDR_SAME(&nexthop->rmap_src.ipv6, &in6addr_any))
vty_out(vty, ", rmapsrc %pI6", &nexthop->rmap_src.ipv6);
else if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any))
vty_out(vty, ", src %pI6", &nexthop->src.ipv6);
break;
case NEXTHOP_TYPE_IFINDEX:
case NEXTHOP_TYPE_BLACKHOLE:
break;
}
/* SR-TE information */
if (nexthop->srte_color)
vty_out(vty, ", SR-TE color %u", nexthop->srte_color);
/* Label information */
if (nexthop->nh_label && nexthop->nh_label->num_labels) {
vty_out(vty, ", label %s",
mpls_label2str(nexthop->nh_label->num_labels,
nexthop->nh_label->label, buf,
sizeof(buf), nexthop->nh_label_type, 1));
}
if (nexthop->nh_srv6) {
seg6local_context2str(buf, sizeof(buf),
&nexthop->nh_srv6->seg6local_ctx,
nexthop->nh_srv6->seg6local_action);
if (nexthop->nh_srv6->seg6local_action !=
ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
vty_out(vty, ", seg6local %s %s",
seg6local_action2str(
nexthop->nh_srv6->seg6local_action),
buf);
if (nexthop->nh_srv6->seg6_segs &&
IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0],
&in6addr_any)) {
segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs;
for (i = 0; i < segs.num_segs; i++)
memcpy(&segs.segs[i],
&nexthop->nh_srv6->seg6_segs->seg[i],
sizeof(struct in6_addr));
snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs);
vty_out(vty, ", seg6 %s", seg_buf);
}
}
if (nexthop->weight)
vty_out(vty, ", weight %u", nexthop->weight);
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
for (i = 1; i < nexthop->backup_num; i++)
vty_out(vty, ",%d", nexthop->backup_idx[i]);
}
}