mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 10:37:29 +00:00
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:
parent
fb72308b99
commit
db2fede990
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user