mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-28 11:50:21 +00:00
ripng: Implement allow-ecmp X
command
A port of ripd implementation for ripngd implemented by 75fce4645a
.
Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
parent
697e7e5174
commit
57aedde6ef
@ -85,14 +85,32 @@ void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode,
|
|||||||
/*
|
/*
|
||||||
* XPath: /frr-ripngd:ripngd/instance/allow-ecmp
|
* XPath: /frr-ripngd:ripngd/instance/allow-ecmp
|
||||||
*/
|
*/
|
||||||
DEFPY_YANG (ripng_allow_ecmp,
|
DEFUN_YANG (ripng_allow_ecmp,
|
||||||
ripng_allow_ecmp_cmd,
|
ripng_allow_ecmp_cmd,
|
||||||
"[no] allow-ecmp",
|
"allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
|
||||||
NO_STR
|
"Allow Equal Cost MultiPath\n"
|
||||||
"Allow Equal Cost MultiPath\n")
|
"Number of paths\n")
|
||||||
{
|
{
|
||||||
nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY,
|
int idx_number = 0;
|
||||||
no ? "false" : "true");
|
char mpaths[3] = {};
|
||||||
|
uint32_t paths = MULTIPATH_NUM;
|
||||||
|
|
||||||
|
if (argv_find(argv, argc, CMD_RANGE_STR(1, MULTIPATH_NUM), &idx_number))
|
||||||
|
paths = strtol(argv[idx_number]->arg, NULL, 10);
|
||||||
|
snprintf(mpaths, sizeof(mpaths), "%u", paths);
|
||||||
|
|
||||||
|
nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths);
|
||||||
|
|
||||||
|
return nb_cli_apply_changes(vty, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFUN_YANG (no_ripng_allow_ecmp,
|
||||||
|
no_ripng_allow_ecmp_cmd,
|
||||||
|
"no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", NO_STR
|
||||||
|
"Allow Equal Cost MultiPath\n"
|
||||||
|
"Number of paths\n")
|
||||||
|
{
|
||||||
|
nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0);
|
||||||
|
|
||||||
return nb_cli_apply_changes(vty, NULL);
|
return nb_cli_apply_changes(vty, NULL);
|
||||||
}
|
}
|
||||||
@ -100,10 +118,14 @@ DEFPY_YANG (ripng_allow_ecmp,
|
|||||||
void cli_show_ripng_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
|
void cli_show_ripng_allow_ecmp(struct vty *vty, const struct lyd_node *dnode,
|
||||||
bool show_defaults)
|
bool show_defaults)
|
||||||
{
|
{
|
||||||
if (!yang_dnode_get_bool(dnode, NULL))
|
uint8_t paths;
|
||||||
vty_out(vty, " no");
|
|
||||||
|
|
||||||
vty_out(vty, " allow-ecmp\n");
|
paths = yang_dnode_get_uint8(dnode, NULL);
|
||||||
|
|
||||||
|
if (!paths)
|
||||||
|
vty_out(vty, " no allow-ecmp\n");
|
||||||
|
else
|
||||||
|
vty_out(vty, " allow-ecmp %d\n", paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -547,6 +569,7 @@ void ripng_cli_init(void)
|
|||||||
install_element(RIPNG_NODE, &ripng_no_ipv6_distribute_list_cmd);
|
install_element(RIPNG_NODE, &ripng_no_ipv6_distribute_list_cmd);
|
||||||
|
|
||||||
install_element(RIPNG_NODE, &ripng_allow_ecmp_cmd);
|
install_element(RIPNG_NODE, &ripng_allow_ecmp_cmd);
|
||||||
|
install_element(RIPNG_NODE, &no_ripng_allow_ecmp_cmd);
|
||||||
install_element(RIPNG_NODE, &ripng_default_information_originate_cmd);
|
install_element(RIPNG_NODE, &ripng_default_information_originate_cmd);
|
||||||
install_element(RIPNG_NODE, &ripng_default_metric_cmd);
|
install_element(RIPNG_NODE, &ripng_default_metric_cmd);
|
||||||
install_element(RIPNG_NODE, &no_ripng_default_metric_cmd);
|
install_element(RIPNG_NODE, &no_ripng_default_metric_cmd);
|
||||||
|
@ -129,9 +129,13 @@ int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args)
|
|||||||
return NB_OK;
|
return NB_OK;
|
||||||
|
|
||||||
ripng = nb_running_get_entry(args->dnode, NULL, true);
|
ripng = nb_running_get_entry(args->dnode, NULL, true);
|
||||||
ripng->ecmp = yang_dnode_get_bool(args->dnode, NULL);
|
ripng->ecmp = yang_dnode_get_uint8(args->dnode, NULL);
|
||||||
if (!ripng->ecmp)
|
if (!ripng->ecmp) {
|
||||||
ripng_ecmp_disable(ripng);
|
ripng_ecmp_disable(ripng);
|
||||||
|
return NB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ripng_ecmp_change(ripng);
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
/* All information about zebra. */
|
/* All information about zebra. */
|
||||||
struct zclient *zclient = NULL;
|
struct zclient *zclient = NULL;
|
||||||
|
uint32_t zebra_ecmp_count = MULTIPATH_NUM;
|
||||||
|
|
||||||
/* Send ECMP routes to zebra. */
|
/* Send ECMP routes to zebra. */
|
||||||
static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp,
|
static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp,
|
||||||
@ -30,7 +31,7 @@ static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp,
|
|||||||
struct zapi_nexthop *api_nh;
|
struct zapi_nexthop *api_nh;
|
||||||
struct listnode *listnode = NULL;
|
struct listnode *listnode = NULL;
|
||||||
struct ripng_info *rinfo = NULL;
|
struct ripng_info *rinfo = NULL;
|
||||||
int count = 0;
|
uint32_t count = 0;
|
||||||
const struct prefix *p = agg_node_get_prefix(rp);
|
const struct prefix *p = agg_node_get_prefix(rp);
|
||||||
|
|
||||||
memset(&api, 0, sizeof(api));
|
memset(&api, 0, sizeof(api));
|
||||||
@ -41,7 +42,7 @@ static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp,
|
|||||||
|
|
||||||
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
|
||||||
for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
|
for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) {
|
||||||
if (count >= MULTIPATH_NUM)
|
if (count >= zebra_ecmp_count)
|
||||||
break;
|
break;
|
||||||
api_nh = &api.nexthops[count];
|
api_nh = &api.nexthops[count];
|
||||||
api_nh->vrf_id = ripng->vrf->vrf_id;
|
api_nh->vrf_id = ripng->vrf->vrf_id;
|
||||||
@ -227,6 +228,11 @@ static zclient_handler *const ripng_handlers[] = {
|
|||||||
[ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ripng_zebra_read_route,
|
[ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ripng_zebra_read_route,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void ripng_zebra_capabilities(struct zclient_capabilities *cap)
|
||||||
|
{
|
||||||
|
zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count);
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize zebra structure and it's commands. */
|
/* Initialize zebra structure and it's commands. */
|
||||||
void zebra_init(struct event_loop *master)
|
void zebra_init(struct event_loop *master)
|
||||||
{
|
{
|
||||||
@ -236,6 +242,7 @@ void zebra_init(struct event_loop *master)
|
|||||||
zclient_init(zclient, ZEBRA_ROUTE_RIPNG, 0, &ripngd_privs);
|
zclient_init(zclient, ZEBRA_ROUTE_RIPNG, 0, &ripngd_privs);
|
||||||
|
|
||||||
zclient->zebra_connected = ripng_zebra_connected;
|
zclient->zebra_connected = ripng_zebra_connected;
|
||||||
|
zclient->zebra_capabilities = ripng_zebra_capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ripng_zebra_stop(void)
|
void ripng_zebra_stop(void)
|
||||||
|
@ -443,7 +443,10 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng,
|
|||||||
{
|
{
|
||||||
struct agg_node *rp = rinfo_new->rp;
|
struct agg_node *rp = rinfo_new->rp;
|
||||||
struct ripng_info *rinfo = NULL;
|
struct ripng_info *rinfo = NULL;
|
||||||
|
struct ripng_info *rinfo_exist = NULL;
|
||||||
struct list *list = NULL;
|
struct list *list = NULL;
|
||||||
|
struct listnode *node = NULL;
|
||||||
|
struct listnode *nnode = NULL;
|
||||||
|
|
||||||
if (rp->info == NULL)
|
if (rp->info == NULL)
|
||||||
rp->info = list_new();
|
rp->info = list_new();
|
||||||
@ -454,6 +457,33 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng,
|
|||||||
if (listcount(list) && !ripng->ecmp)
|
if (listcount(list) && !ripng->ecmp)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
/* Add or replace an existing ECMP path with lower neighbor IP */
|
||||||
|
if (listcount(list) && listcount(list) >= ripng->ecmp) {
|
||||||
|
struct ripng_info *from_highest = NULL;
|
||||||
|
|
||||||
|
/* Find the rip_info struct that has the highest nexthop IP */
|
||||||
|
for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist))
|
||||||
|
if (!from_highest ||
|
||||||
|
(from_highest &&
|
||||||
|
IPV6_ADDR_CMP(&rinfo_exist->from,
|
||||||
|
&from_highest->from) > 0)) {
|
||||||
|
from_highest = rinfo_exist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we have a route in ECMP group, delete the old
|
||||||
|
* one that has a higher next-hop address. Lower IP is
|
||||||
|
* preferred.
|
||||||
|
*/
|
||||||
|
if (ripng->ecmp > 1 && from_highest &&
|
||||||
|
IPV6_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) {
|
||||||
|
ripng_ecmp_delete(ripng, from_highest);
|
||||||
|
goto add_or_replace;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_or_replace:
|
||||||
rinfo = ripng_info_new();
|
rinfo = ripng_info_new();
|
||||||
memcpy(rinfo, rinfo_new, sizeof(struct ripng_info));
|
memcpy(rinfo, rinfo_new, sizeof(struct ripng_info));
|
||||||
listnode_add(list, rinfo);
|
listnode_add(list, rinfo);
|
||||||
@ -475,6 +505,36 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng,
|
|||||||
return rinfo;
|
return rinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update ECMP routes to zebra when `allow-ecmp` changed. */
|
||||||
|
void ripng_ecmp_change(struct ripng *ripng)
|
||||||
|
{
|
||||||
|
struct agg_node *rp;
|
||||||
|
struct ripng_info *rinfo;
|
||||||
|
struct list *list;
|
||||||
|
struct listnode *node, *nextnode;
|
||||||
|
|
||||||
|
for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) {
|
||||||
|
list = rp->info;
|
||||||
|
if (list && listcount(list) > 1) {
|
||||||
|
while (listcount(list) > ripng->ecmp) {
|
||||||
|
struct ripng_info *from_highest = NULL;
|
||||||
|
|
||||||
|
for (ALL_LIST_ELEMENTS(list, node, nextnode,
|
||||||
|
rinfo)) {
|
||||||
|
if (!from_highest ||
|
||||||
|
(from_highest &&
|
||||||
|
IPV6_ADDR_CMP(
|
||||||
|
&rinfo->from,
|
||||||
|
&from_highest->from) > 0))
|
||||||
|
from_highest = rinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
ripng_ecmp_delete(ripng, from_highest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Replace the ECMP list with the new route.
|
/* Replace the ECMP list with the new route.
|
||||||
* RETURN: the new entry added in the list
|
* RETURN: the new entry added in the list
|
||||||
*/
|
*/
|
||||||
@ -1814,7 +1874,7 @@ struct ripng *ripng_create(const char *vrf_name, struct vrf *vrf, int socket)
|
|||||||
"%s/timers/flush-interval", RIPNG_INSTANCE);
|
"%s/timers/flush-interval", RIPNG_INSTANCE);
|
||||||
ripng->default_metric =
|
ripng->default_metric =
|
||||||
yang_get_default_uint8("%s/default-metric", RIPNG_INSTANCE);
|
yang_get_default_uint8("%s/default-metric", RIPNG_INSTANCE);
|
||||||
ripng->ecmp = yang_get_default_bool("%s/allow-ecmp", RIPNG_INSTANCE);
|
ripng->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIPNG_INSTANCE);
|
||||||
|
|
||||||
/* Make buffer. */
|
/* Make buffer. */
|
||||||
ripng->ibuf = stream_new(RIPNG_MAX_PACKET_SIZE * 5);
|
ripng->ibuf = stream_new(RIPNG_MAX_PACKET_SIZE * 5);
|
||||||
|
@ -130,7 +130,7 @@ struct ripng {
|
|||||||
struct event *t_triggered_interval;
|
struct event *t_triggered_interval;
|
||||||
|
|
||||||
/* RIPng ECMP flag */
|
/* RIPng ECMP flag */
|
||||||
bool ecmp;
|
uint8_t ecmp;
|
||||||
|
|
||||||
/* RIPng redistribute configuration. */
|
/* RIPng redistribute configuration. */
|
||||||
struct {
|
struct {
|
||||||
@ -429,6 +429,7 @@ extern struct ripng_info *ripng_ecmp_replace(struct ripng *ripng,
|
|||||||
struct ripng_info *rinfo);
|
struct ripng_info *rinfo);
|
||||||
extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng,
|
extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng,
|
||||||
struct ripng_info *rinfo);
|
struct ripng_info *rinfo);
|
||||||
|
extern void ripng_ecmp_change(struct ripng *ripng);
|
||||||
|
|
||||||
extern void ripng_vrf_init(void);
|
extern void ripng_vrf_init(void);
|
||||||
extern void ripng_vrf_terminate(void);
|
extern void ripng_vrf_terminate(void);
|
||||||
|
@ -86,8 +86,8 @@ module frr-ripngd {
|
|||||||
"VRF name.";
|
"VRF name.";
|
||||||
}
|
}
|
||||||
leaf allow-ecmp {
|
leaf allow-ecmp {
|
||||||
type boolean;
|
type uint8;
|
||||||
default "false";
|
default 0;
|
||||||
description
|
description
|
||||||
"Allow equal-cost multi-path.";
|
"Allow equal-cost multi-path.";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user