diff options
| -rw-r--r-- | ripngd/ripng_cli.c | 43 | ||||
| -rw-r--r-- | ripngd/ripng_nb_config.c | 8 | ||||
| -rw-r--r-- | ripngd/ripng_zebra.c | 11 | ||||
| -rw-r--r-- | ripngd/ripngd.c | 62 | ||||
| -rw-r--r-- | ripngd/ripngd.h | 3 | ||||
| -rw-r--r-- | yang/frr-ripngd.yang | 4 | 
6 files changed, 113 insertions, 18 deletions
diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c index 5e59dfd2c4..9a96e29313 100644 --- a/ripngd/ripng_cli.c +++ b/ripngd/ripng_cli.c @@ -85,14 +85,32 @@ void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode,  /*   * XPath: /frr-ripngd:ripngd/instance/allow-ecmp   */ -DEFPY_YANG (ripng_allow_ecmp, -       ripng_allow_ecmp_cmd, -       "[no] allow-ecmp", -       NO_STR -       "Allow Equal Cost MultiPath\n") +DEFUN_YANG (ripng_allow_ecmp, +            ripng_allow_ecmp_cmd, +            "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", +            "Allow Equal Cost MultiPath\n" +            "Number of paths\n") +{ +	int idx_number = 0; +	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, -			      no ? "false" : "true"); +	nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0);  	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,  			       bool show_defaults)  { -	if (!yang_dnode_get_bool(dnode, NULL)) -		vty_out(vty, " no"); +	uint8_t paths; + +	paths = yang_dnode_get_uint8(dnode, NULL); -	vty_out(vty, " allow-ecmp\n"); +	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_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_metric_cmd);  	install_element(RIPNG_NODE, &no_ripng_default_metric_cmd); diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c index 30f707e061..de72319354 100644 --- a/ripngd/ripng_nb_config.c +++ b/ripngd/ripng_nb_config.c @@ -129,9 +129,13 @@ int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args)  		return NB_OK;  	ripng = nb_running_get_entry(args->dnode, NULL, true); -	ripng->ecmp = yang_dnode_get_bool(args->dnode, NULL); -	if (!ripng->ecmp) +	ripng->ecmp = yang_dnode_get_uint8(args->dnode, NULL); +	if (!ripng->ecmp) {  		ripng_ecmp_disable(ripng); +		return NB_OK; +	} + +	ripng_ecmp_change(ripng);  	return NB_OK;  } diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index d974d65df5..6122c4255c 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -20,6 +20,7 @@  /* All information about zebra. */  struct zclient *zclient = NULL; +uint32_t zebra_ecmp_count = MULTIPATH_NUM;  /* Send ECMP routes to zebra. */  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 listnode *listnode = NULL;  	struct ripng_info *rinfo = NULL; -	int count = 0; +	uint32_t count = 0;  	const struct prefix *p = agg_node_get_prefix(rp);  	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);  	for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { -		if (count >= MULTIPATH_NUM) +		if (count >= zebra_ecmp_count)  			break;  		api_nh = &api.nexthops[count];  		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,  }; +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. */  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->zebra_connected = ripng_zebra_connected; +	zclient->zebra_capabilities = ripng_zebra_capabilities;  }  void ripng_zebra_stop(void) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 2f6409a70d..7269e76656 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -443,7 +443,10 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng,  {  	struct agg_node *rp = rinfo_new->rp;  	struct ripng_info *rinfo = NULL; +	struct ripng_info *rinfo_exist = NULL;  	struct list *list = NULL; +	struct listnode *node = NULL; +	struct listnode *nnode = NULL;  	if (rp->info == NULL)  		rp->info = list_new(); @@ -454,6 +457,33 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng,  	if (listcount(list) && !ripng->ecmp)  		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();  	memcpy(rinfo, rinfo_new, sizeof(struct ripng_info));  	listnode_add(list, rinfo); @@ -475,6 +505,36 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng,  	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.   * 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);  	ripng->default_metric =  		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.  */  	ripng->ibuf = stream_new(RIPNG_MAX_PACKET_SIZE * 5); diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index eefcb0ee69..3d13097dd6 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -130,7 +130,7 @@ struct ripng {  	struct event *t_triggered_interval;  	/* RIPng ECMP flag */ -	bool ecmp; +	uint8_t ecmp;  	/* RIPng redistribute configuration. */  	struct { @@ -429,6 +429,7 @@ extern struct ripng_info *ripng_ecmp_replace(struct ripng *ripng,  					     struct ripng_info *rinfo);  extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng,  					    struct ripng_info *rinfo); +extern void ripng_ecmp_change(struct ripng *ripng);  extern void ripng_vrf_init(void);  extern void ripng_vrf_terminate(void); diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index 7b2b135fb5..4aeaf36400 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -86,8 +86,8 @@ module frr-ripngd {            "VRF name.";        }        leaf allow-ecmp { -        type boolean; -        default "false"; +        type uint8; +        default 0;          description            "Allow equal-cost multi-path.";        }  | 
