lib: support backup nexthops in nexthop-groups and zapi

Add vty support for backup nexthops in nexthop groups. Capture
backup nexthop info in zapi route messages.

Signed-off-by: Mark Stapp <mjs@voltanet.io>
This commit is contained in:
Mark Stapp 2020-01-16 16:25:22 -05:00
parent 62e46303f4
commit 0a8881b4b5
4 changed files with 118 additions and 12 deletions

View File

@ -43,8 +43,12 @@ struct nexthop_hold {
char *intf; char *intf;
char *labels; char *labels;
uint32_t weight; uint32_t weight;
int backup_idx; /* Index of backup nexthop, if >= 0 */
}; };
/* Invalid/unset value for nexthop_hold's backup_idx */
#define NHH_BACKUP_IDX_INVALID -1
struct nexthop_group_hooks { struct nexthop_group_hooks {
void (*new)(const char *name); void (*new)(const char *name);
void (*add_nexthop)(const struct nexthop_group_cmd *nhg, void (*add_nexthop)(const struct nexthop_group_cmd *nhg,
@ -571,11 +575,36 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
return CMD_SUCCESS; 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, static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
const char *nhvrf_name, const char *nhvrf_name,
const union sockunion *addr, const union sockunion *addr,
const char *intf, const char *labels, const char *intf, const char *labels,
const uint32_t weight) const uint32_t weight, int backup_idx)
{ {
struct nexthop_hold *nh; struct nexthop_hold *nh;
@ -592,6 +621,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
nh->weight = weight; nh->weight = weight;
nh->backup_idx = backup_idx;
listnode_add_sort(nhgc->nhg_list, nh); listnode_add_sort(nhgc->nhg_list, nh);
} }
@ -633,7 +664,7 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
const union sockunion *addr, const union sockunion *addr,
const char *intf, const char *name, const char *intf, const char *name,
const char *labels, int *lbl_ret, const char *labels, int *lbl_ret,
uint32_t weight) uint32_t weight, int backup_idx)
{ {
int ret = 0; int ret = 0;
struct vrf *vrf; struct vrf *vrf;
@ -692,6 +723,15 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
nhop->weight = weight; nhop->weight = weight;
if (backup_idx != NHH_BACKUP_IDX_INVALID) {
/* Validate index value */
if (backup_idx > NEXTHOP_BACKUP_IDX_MAX)
return false;
SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
nhop->backup_idx = backup_idx;
}
return true; return true;
} }
@ -703,7 +743,7 @@ static bool nexthop_group_parse_nhh(struct nexthop *nhop,
{ {
return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf, return (nexthop_group_parse_nexthop(nhop, nhh->addr, nhh->intf,
nhh->nhvrf_name, nhh->labels, NULL, nhh->nhvrf_name, nhh->labels, NULL,
nhh->weight)); nhh->weight, nhh->backup_idx));
} }
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
@ -716,6 +756,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
nexthop-vrf NAME$vrf_name \ nexthop-vrf NAME$vrf_name \
|label WORD \ |label WORD \
|weight (1-255) \ |weight (1-255) \
|backup-idx$bi_str (0-254)$idx \
}]", }]",
NO_STR NO_STR
"Specify one of the nexthops in this ECMP group\n" "Specify one of the nexthops in this ECMP group\n"
@ -728,16 +769,23 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"Specify label(s) for this nexthop\n" "Specify label(s) for this nexthop\n"
"One or more labels in the range (16-1048575) separated by '/'\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 to be used by the nexthop for purposes of ECMP\n"
"Weight value to be used\n") "Weight value to be used\n"
"Backup nexthop index in another group\n"
"Nexthop index value\n")
{ {
VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc);
struct nexthop nhop; struct nexthop nhop;
struct nexthop *nh; struct nexthop *nh;
int lbl_ret = 0; int lbl_ret = 0;
bool legal; bool legal;
int backup_idx = idx;
bool add_update = false;
if (bi_str == NULL)
backup_idx = NHH_BACKUP_IDX_INVALID;
legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label, legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name, label,
&lbl_ret, weight); &lbl_ret, weight, backup_idx);
if (nhop.type == NEXTHOP_TYPE_IPV6 if (nhop.type == NEXTHOP_TYPE_IPV6
&& IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) { && IN6_IS_ADDR_LINKLOCAL(&nhop.gate.ipv6)) {
@ -769,19 +817,30 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
nh = nexthop_exists(&nhgc->nhg, &nhop); nh = nexthop_exists(&nhgc->nhg, &nhop);
if (no) { if (no || nh) {
/* Remove or replace cases */
/* Remove existing config */
nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label, nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label,
weight); weight);
if (nh) { if (nh) {
/* Remove nexthop object */
_nexthop_del(&nhgc->nhg, nh); _nexthop_del(&nhgc->nhg, nh);
if (nhg_hooks.del_nexthop) if (nhg_hooks.del_nexthop)
nhg_hooks.del_nexthop(nhgc, nh); nhg_hooks.del_nexthop(nhgc, nh);
nexthop_free(nh); nexthop_free(nh);
nh = NULL;
} }
} else if (!nh) { }
/* must be adding new nexthop since !no and !nexthop_exists */
add_update = !no;
if (add_update) {
/* Add or replace cases */
/* If valid config, add nexthop object */
if (legal) { if (legal) {
nh = nexthop_new(); nh = nexthop_new();
@ -789,8 +848,9 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
_nexthop_add(&nhgc->nhg.nexthop, nh); _nexthop_add(&nhgc->nhg.nexthop, nh);
} }
/* Save config always */
nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label, nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label,
weight); weight, backup_idx);
if (legal && nhg_hooks.add_nexthop) if (legal && nhg_hooks.add_nexthop)
nhg_hooks.add_nexthop(nhgc, nh); nhg_hooks.add_nexthop(nhgc, nh);
@ -853,6 +913,9 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
if (nh->weight) if (nh->weight)
vty_out(vty, " weight %u", 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);
vty_out(vty, "\n"); vty_out(vty, "\n");
} }
@ -878,6 +941,9 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty,
if (nh->weight) if (nh->weight)
vty_out(vty, " weight %u", nh->weight); vty_out(vty, " weight %u", nh->weight);
if (nh->backup_idx != NHH_BACKUP_IDX_INVALID)
vty_out(vty, " backup-idx %d", nh->backup_idx);
vty_out(vty, "\n"); vty_out(vty, "\n");
} }
@ -891,6 +957,10 @@ static int nexthop_group_write(struct vty *vty)
vty_out(vty, "nexthop-group %s\n", nhgc->name); 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)) { for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
vty_out(vty, " "); vty_out(vty, " ");
nexthop_group_write_nexthop_internal(vty, nh); nexthop_group_write_nexthop_internal(vty, nh);
@ -1071,6 +1141,8 @@ void nexthop_group_init(void (*new)(const char *name),
install_element(CONFIG_NODE, &no_nexthop_group_cmd); install_element(CONFIG_NODE, &no_nexthop_group_cmd);
install_default(NH_GROUP_NODE); 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); install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd);
memset(&nhg_hooks, 0, sizeof(nhg_hooks)); memset(&nhg_hooks, 0, sizeof(nhg_hooks));

View File

@ -81,11 +81,16 @@ void nexthop_group_add_sorted(struct nexthop_group *nhg,
(nhop) = nexthop_next(nhop) (nhop) = nexthop_next(nhop)
#define NHGC_NAME_SIZE 80
struct nexthop_group_cmd { struct nexthop_group_cmd {
RB_ENTRY(nexthop_group_cmd) nhgc_entry; RB_ENTRY(nexthop_group_cmd) nhgc_entry;
char name[80]; char name[NHGC_NAME_SIZE];
/* Name of group containing backup nexthops (if set) */
char backup_list_name[NHGC_NAME_SIZE];
struct nexthop_group nhg; struct nexthop_group nhg;

View File

@ -904,6 +904,7 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
} }
} }
/* If present, set 'weight' flag before encoding flags */
if (api_nh->weight) if (api_nh->weight)
SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT); SET_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_WEIGHT);
@ -1479,6 +1480,11 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh)
znh->labels); znh->labels);
} }
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP);
n->backup_idx = znh->backup_idx;
}
return n; return n;
} }
@ -1510,9 +1516,30 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL); SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
} }
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
znh->backup_idx = nh->backup_idx;
}
return 0; return 0;
} }
/*
* Wrapper that converts backup nexthop
*/
int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh,
const struct nexthop *nh)
{
int ret;
/* Ensure that zapi flags are correct: backups don't have backups */
ret = zapi_nexthop_from_nexthop(znh, nh);
if (ret == 0)
UNSET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
return ret;
}
/* /*
* Decode the nexthop-tracking update message * Decode the nexthop-tracking update message
*/ */

View File

@ -783,10 +783,12 @@ bool zapi_iptable_notify_decode(struct stream *s,
uint32_t *unique, uint32_t *unique,
enum zapi_iptable_notify_owner *note); enum zapi_iptable_notify_owner *note);
extern struct nexthop *nexthop_from_zapi_nexthop( extern struct nexthop *
const struct zapi_nexthop *znh); nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh);
int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh,
const struct nexthop *nh); const struct nexthop *nh);
int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh,
const struct nexthop *nh);
extern bool zapi_nexthop_update_decode(struct stream *s, extern bool zapi_nexthop_update_decode(struct stream *s,
struct zapi_route *nhr); struct zapi_route *nhr);