lib: support labelled nexthops in nexthop_groups

Add support for labelled nexthops in nexthop-group
vtysh configuration. Also update the PBR doc where
the cli is described.

Signed-off-by: Mark Stapp <mjs@voltanet.io>
This commit is contained in:
Mark Stapp 2019-11-27 13:02:23 -05:00
parent fb72308b99
commit db2fede990
3 changed files with 115 additions and 26 deletions

View File

@ -39,7 +39,7 @@ listing of ECMP nexthops used to forward packets for when a pbr-map is matched.
sub-mode where you can specify individual nexthops. To exit this mode type sub-mode where you can specify individual nexthops. To exit this mode type
exit or end as per normal conventions for leaving a sub-mode. exit or end as per normal conventions for leaving a sub-mode.
.. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] .. clicmd:: nexthop [A.B.C.D|X:X::X:XX] [interface] [nexthop-vrf NAME] [label LABELS]
Create a v4 or v6 nexthop. All normal rules for creating nexthops that you Create a v4 or v6 nexthop. All normal rules for creating nexthops that you
are used to are allowed here. The syntax was intentionally kept the same as are used to are allowed here. The syntax was intentionally kept the same as

View File

@ -34,6 +34,16 @@
DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") 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;
char *labels;
};
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,
@ -425,7 +435,11 @@ static int nhgl_cmp(struct nexthop_hold *nh1, struct nexthop_hold *nh2)
if (ret) if (ret)
return ret; return ret;
return nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name); ret = nhgc_cmp_helper(nh1->nhvrf_name, nh2->nhvrf_name);
if (ret)
return ret;
return nhgc_cmp_helper(nh1->labels, nh2->labels);
} }
static void nhgl_delete(struct nexthop_hold *nh) static void nhgl_delete(struct nexthop_hold *nh)
@ -437,6 +451,8 @@ static void nhgl_delete(struct nexthop_hold *nh)
if (nh->addr) if (nh->addr)
sockunion_free(nh->addr); sockunion_free(nh->addr);
XFREE(MTYPE_TMP, nh->labels);
XFREE(MTYPE_TMP, nh); XFREE(MTYPE_TMP, nh);
} }
@ -510,7 +526,8 @@ DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NHGNAME",
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 *intf,
const char *labels)
{ {
struct nexthop_hold *nh; struct nexthop_hold *nh;
@ -522,6 +539,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
nh->intf = XSTRDUP(MTYPE_TMP, intf); nh->intf = XSTRDUP(MTYPE_TMP, intf);
if (addr) if (addr)
nh->addr = sockunion_dup(addr); nh->addr = sockunion_dup(addr);
if (labels)
nh->labels = XSTRDUP(MTYPE_TMP, labels);
listnode_add_sort(nhgc->nhg_list, nh); listnode_add_sort(nhgc->nhg_list, nh);
} }
@ -529,7 +548,8 @@ static void nexthop_group_save_nhop(struct nexthop_group_cmd *nhgc,
static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc, static void nexthop_group_unsave_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 *intf,
const char *labels)
{ {
struct nexthop_hold *nh; struct nexthop_hold *nh;
struct listnode *node; struct listnode *node;
@ -537,7 +557,8 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) { for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nh)) {
if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 && if (nhgc_cmp_helper(nhvrf_name, nh->nhvrf_name) == 0 &&
nhgc_addr_cmp_helper(addr, nh->addr) == 0 && nhgc_addr_cmp_helper(addr, nh->addr) == 0 &&
nhgc_cmp_helper(intf, nh->intf) == 0) nhgc_cmp_helper(intf, nh->intf) == 0 &&
nhgc_cmp_helper(labels, nh->labels) == 0)
break; break;
} }
@ -551,10 +572,19 @@ static void nexthop_group_unsave_nhop(struct nexthop_group_cmd *nhgc,
nhgl_delete(nh); 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, 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)
{ {
int ret = 0;
struct vrf *vrf; struct vrf *vrf;
memset(nhop, 0, sizeof(*nhop)); memset(nhop, 0, sizeof(*nhop));
@ -592,16 +622,49 @@ static bool nexthop_group_parse_nexthop(struct nexthop *nhop,
} else } else
nhop->type = NEXTHOP_TYPE_IFINDEX; 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);
}
return true; 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->nhvrf_name,
nhh->labels,
NULL));
}
DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"[no] nexthop\ "[no] nexthop\
<\ <\
<A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\ <A.B.C.D|X:X::X:X>$addr [INTERFACE$intf]\
|INTERFACE$intf\ |INTERFACE$intf\
>\ >\
[nexthop-vrf NAME$vrf_name]", [{ \
nexthop-vrf NAME$vrf_name \
|label WORD \
}]",
NO_STR NO_STR
"Specify one of the nexthops in this ECMP group\n" "Specify one of the nexthops in this ECMP group\n"
"v4 Address\n" "v4 Address\n"
@ -609,14 +672,18 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
"Interface to use\n" "Interface to use\n"
"Interface to use\n" "Interface to use\n"
"If the nexthop is in a different vrf tell us\n" "If the nexthop is in a different vrf tell us\n"
"The nexthop-vrf Name\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")
{ {
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;
bool legal; bool legal;
legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name); legal = nexthop_group_parse_nexthop(&nhop, addr, intf, vrf_name,
label, &lbl_ret);
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)) {
@ -625,10 +692,31 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
return CMD_WARNING_CONFIG_FAILED; 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;
}
nh = nexthop_exists(&nhgc->nhg, &nhop); nh = nexthop_exists(&nhgc->nhg, &nhop);
if (no) { if (no) {
nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf); nexthop_group_unsave_nhop(nhgc, vrf_name, addr, intf, label);
if (nh) { if (nh) {
_nexthop_del(&nhgc->nhg, nh); _nexthop_del(&nhgc->nhg, nh);
@ -646,7 +734,7 @@ DEFPY(ecmp_nexthops, ecmp_nexthops_cmd,
_nexthop_add(&nhgc->nhg.nexthop, nh); _nexthop_add(&nhgc->nhg.nexthop, nh);
} }
nexthop_group_save_nhop(nhgc, vrf_name, addr, intf); nexthop_group_save_nhop(nhgc, vrf_name, addr, intf, label);
if (legal && nhg_hooks.add_nexthop) if (legal && nhg_hooks.add_nexthop)
nhg_hooks.add_nexthop(nhgc, nh); nhg_hooks.add_nexthop(nhgc, nh);
@ -696,6 +784,16 @@ void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh)
vrf = vrf_lookup_by_id(nh->vrf_id); vrf = vrf_lookup_by_id(nh->vrf_id);
vty_out(vty, " nexthop-vrf %s", vrf->name); vty_out(vty, " nexthop-vrf %s", 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);
vty_out(vty, " label %s", buf);
}
vty_out(vty, "\n"); vty_out(vty, "\n");
} }
@ -715,6 +813,9 @@ static void nexthop_group_write_nexthop_internal(struct vty *vty,
if (nh->nhvrf_name) if (nh->nhvrf_name)
vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name); vty_out(vty, " nexthop-vrf %s", nh->nhvrf_name);
if (nh->labels)
vty_out(vty, " label %s", nh->labels);
vty_out(vty, "\n"); vty_out(vty, "\n");
} }
@ -751,9 +852,7 @@ void nexthop_group_enable_vrf(struct vrf *vrf)
struct nexthop nhop; struct nexthop nhop;
struct nexthop *nh; struct nexthop *nh;
if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, if (!nexthop_group_parse_nhh(&nhop, nhh))
nhh->intf,
nhh->nhvrf_name))
continue; continue;
nh = nexthop_exists(&nhgc->nhg, &nhop); nh = nexthop_exists(&nhgc->nhg, &nhop);
@ -787,9 +886,7 @@ void nexthop_group_disable_vrf(struct vrf *vrf)
struct nexthop nhop; struct nexthop nhop;
struct nexthop *nh; struct nexthop *nh;
if (!nexthop_group_parse_nexthop(&nhop, nhh->addr, if (!nexthop_group_parse_nhh(&nhop, nhh))
nhh->intf,
nhh->nhvrf_name))
continue; continue;
nh = nexthop_exists(&nhgc->nhg, &nhop); nh = nexthop_exists(&nhgc->nhg, &nhop);
@ -824,9 +921,7 @@ void nexthop_group_interface_state_change(struct interface *ifp,
for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) { for (ALL_LIST_ELEMENTS_RO(nhgc->nhg_list, node, nhh)) {
struct nexthop nhop; struct nexthop nhop;
if (!nexthop_group_parse_nexthop( if (!nexthop_group_parse_nhh(&nhop, nhh))
&nhop, nhh->addr, nhh->intf,
nhh->nhvrf_name))
continue; continue;
switch (nhop.type) { switch (nhop.type) {

View File

@ -70,12 +70,6 @@ void nexthop_group_mark_duplicates(struct nexthop_group *nhg);
(nhop) = nexthop_next(nhop) (nhop) = nexthop_next(nhop)
struct nexthop_hold {
char *nhvrf_name;
union sockunion *addr;
char *intf;
};
struct nexthop_group_cmd { struct nexthop_group_cmd {
RB_ENTRY(nexthop_group_cmd) nhgc_entry; RB_ENTRY(nexthop_group_cmd) nhgc_entry;