--- embedaddon/quagga/ripngd/ripngd.c 2012/10/09 09:22:29 1.1.1.2 +++ embedaddon/quagga/ripngd/ripngd.c 2016/11/02 10:09:12 1.1.1.3 @@ -95,7 +95,7 @@ ripng_info_free (struct ripng_info *rinfo) { XFREE (MTYPE_RIPNG_ROUTE, rinfo); } - + /* Create ripng socket. */ static int ripng_make_socket (void) @@ -229,14 +229,14 @@ ripng_send_packet (caddr_t buf, int bufsize, struct so /* Receive UDP RIPng packet from socket. */ static int ripng_recv_packet (int sock, u_char *buf, int bufsize, - struct sockaddr_in6 *from, unsigned int *ifindex, + struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { int ret; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsgptr; - struct in6_addr dst; + struct in6_addr dst = { .s6_addr = { 0 } }; /* Ancillary data. This store cmsghdr and in6_pktinfo. But at this point I can't determine size of cmsghdr */ @@ -421,8 +421,13 @@ ripng_garbage_collect (struct thread *t) rp = rinfo->rp; /* Unlock route_node. */ - rp->info = NULL; - route_unlock_node (rp); + listnode_delete (rp->info, rinfo); + if (list_isempty ((struct list *)rp->info)) + { + list_free (rp->info); + rp->info = NULL; + route_unlock_node (rp); + } /* Free RIPng routing information. */ ripng_info_free (rinfo); @@ -430,41 +435,165 @@ ripng_garbage_collect (struct thread *t) return 0; } -/* Timeout RIPng routes. */ -static int -ripng_timeout (struct thread *t) +static void ripng_timeout_update (struct ripng_info *rinfo); + +/* Add new route to the ECMP list. + * RETURN: the new entry added in the list, or NULL if it is not the first + * entry and ECMP is not allowed. + */ +struct ripng_info * +ripng_ecmp_add (struct ripng_info *rinfo_new) { - struct ripng_info *rinfo; - struct route_node *rp; + struct route_node *rp = rinfo_new->rp; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; - rinfo = THREAD_ARG (t); - rinfo->t_timeout = NULL; + if (rp->info == NULL) + rp->info = list_new (); + list = (struct list *)rp->info; - /* Get route_node pointer. */ - rp = rinfo->rp; + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !ripng->ecmp) + return NULL; - /* - The garbage-collection timer is set for 120 seconds. */ - RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect, - ripng->garbage_time); + rinfo = ripng_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct ripng_info)); + listnode_add (list, rinfo); - /* Delete this route from the kernel. */ - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, &rinfo->nexthop, - rinfo->ifindex); - /* - The metric for the route is set to 16 (infinity). This causes - the route to be removed from service. */ - rinfo->metric = RIPNG_METRIC_INFINITY; - rinfo->flags &= ~RIPNG_RTF_FIB; + if (ripng_route_rte (rinfo)) + { + ripng_timeout_update (rinfo); + ripng_zebra_ipv6_add (rp); + } - /* Aggregate count decrement. */ - ripng_aggregate_decrement (rp, rinfo); + ripng_aggregate_increment (rp, rinfo); - /* - The route change flag is to indicate that this entry has been - changed. */ - rinfo->flags |= RIPNG_RTF_CHANGED; + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); - /* - The output process is signalled to trigger a response. */ + /* Signal the output process to trigger an update. */ ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + return rinfo; +} + +/* Replace the ECMP list with the new route. + * RETURN: the new entry added in the list + */ +struct ripng_info * +ripng_ecmp_replace (struct ripng_info *rinfo_new) +{ + struct route_node *rp = rinfo_new->rp; + struct list *list = (struct list *)rp->info; + struct ripng_info *rinfo = NULL, *tmp_rinfo = NULL; + struct listnode *node = NULL, *nextnode = NULL; + + if (list == NULL || listcount (list) == 0) + return ripng_ecmp_add (rinfo_new); + + /* Get the first entry */ + rinfo = listgetdata (listhead (list)); + + /* Learnt route replaced by a local one. Delete it from zebra. */ + if (ripng_route_rte (rinfo) && !ripng_route_rte (rinfo_new)) + if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + ripng_zebra_ipv6_delete (rp); + + if (rinfo->metric != RIPNG_METRIC_INFINITY) + ripng_aggregate_decrement_list (rp, list); + + /* Re-use the first entry, and delete the others. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIPNG_TIMER_OFF (tmp_rinfo->t_timeout); + RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + ripng_info_free (tmp_rinfo); + } + + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, rinfo_new, sizeof (struct ripng_info)); + + if (ripng_route_rte (rinfo)) + { + ripng_timeout_update (rinfo); + /* The ADD message implies an update. */ + ripng_zebra_ipv6_add (rp); + } + + ripng_aggregate_increment (rp, rinfo); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Delete one route from the ECMP list. + * RETURN: + * null - the entry is freed, and other entries exist in the list + * the entry - the entry is the last one in the list; its metric is set + * to INFINITY, and the garbage collector is started for it + */ +struct ripng_info * +ripng_ecmp_delete (struct ripng_info *rinfo) +{ + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; + + RIPNG_TIMER_OFF (rinfo->t_timeout); + + if (rinfo->metric != RIPNG_METRIC_INFINITY) + ripng_aggregate_decrement (rp, rinfo); + + if (listcount (list) > 1) + { + /* Some other ECMP entries still exist. Just delete this entry. */ + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + /* The ADD message implies the update. */ + ripng_zebra_ipv6_add (rp); + ripng_info_free (rinfo); + rinfo = NULL; + } + else + { + assert (rinfo == listgetdata (listhead (list))); + + /* This is the only entry left in the list. We must keep it in + * the list for garbage collection time, with INFINITY metric. */ + + rinfo->metric = RIPNG_METRIC_INFINITY; + RIPNG_TIMER_ON (rinfo->t_garbage_collect, + ripng_garbage_collect, ripng->garbage_time); + + if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + ripng_zebra_ipv6_delete (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Timeout RIPng routes. */ +static int +ripng_timeout (struct thread *t) +{ + ripng_ecmp_delete ((struct ripng_info *)THREAD_ARG (t)); return 0; } @@ -628,11 +757,12 @@ ripng_route_process (struct rte *rte, struct sockaddr_ int ret; struct prefix_ipv6 p; struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL, newinfo; struct ripng_interface *ri; struct in6_addr *nexthop; - u_char oldmetric; int same = 0; + struct list *list = NULL; + struct listnode *node = NULL; /* Make prefix structure. */ memset (&p, 0, sizeof (struct prefix_ipv6)); @@ -653,25 +783,24 @@ ripng_route_process (struct rte *rte, struct sockaddr_ if (ret < 0) return; + memset (&newinfo, 0, sizeof (newinfo)); + newinfo.type = ZEBRA_ROUTE_RIPNG; + newinfo.sub_type = RIPNG_ROUTE_RTE; + if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) + newinfo.nexthop = ripng_nexthop->address; + else + newinfo.nexthop = from->sin6_addr; + newinfo.from = from->sin6_addr; + newinfo.ifindex = ifp->ifindex; + newinfo.metric = rte->metric; + newinfo.metric_out = rte->metric; /* XXX */ + newinfo.tag = ntohs (rte->tag); /* XXX */ + /* Modify entry. */ if (ri->routemap[RIPNG_FILTER_IN]) { int ret; - struct ripng_info newinfo; - memset (&newinfo, 0, sizeof (struct ripng_info)); - newinfo.type = ZEBRA_ROUTE_RIPNG; - newinfo.sub_type = RIPNG_ROUTE_RTE; - if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) - newinfo.nexthop = ripng_nexthop->address; - else - newinfo.nexthop = from->sin6_addr; - newinfo.from = from->sin6_addr; - newinfo.ifindex = ifp->ifindex; - newinfo.metric = rte->metric; - newinfo.metric_out = rte->metric; /* XXX */ - newinfo.tag = ntohs(rte->tag); /* XXX */ - ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN], (struct prefix *)&p, RMAP_RIPNG, &newinfo); @@ -717,7 +846,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_ /* If offset-list does not modify the metric use interface's * one. */ if (! ret) - rte->metric += ifp->metric; + rte->metric += ifp->metric ? ifp->metric : 1; if (rte->metric > RIPNG_METRIC_INFINITY) rte->metric = RIPNG_METRIC_INFINITY; @@ -731,24 +860,66 @@ ripng_route_process (struct rte *rte, struct sockaddr_ /* Lookup RIPng routing table. */ rp = route_node_get (ripng->table, (struct prefix *) &p); - /* Sanity check */ - rinfo = rp->info; + newinfo.rp = rp; + newinfo.nexthop = *nexthop; + newinfo.metric = rte->metric; + newinfo.tag = ntohs (rte->tag); + + /* Check to see whether there is already RIPng route on the table. */ + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + /* Need to compare with redistributed entry or local entry */ + if (!ripng_route_rte (rinfo)) + break; + + if (IPV6_ADDR_SAME (&rinfo->from, &from->sin6_addr) && + IPV6_ADDR_SAME (&rinfo->nexthop, nexthop)) + break; + + if (!listnextnode (node)) + { + /* Not found in the list */ + + if (rte->metric > rinfo->metric) + { + /* New route has a greater metric. Discard it. */ + route_unlock_node (rp); + return; + } + + if (rte->metric < rinfo->metric) + /* New route has a smaller metric. Replace the ECMP list + * with the new one in below. */ + break; + + /* Metrics are same. Keep "rinfo" null and the new route + * is added in the ECMP list in below. */ + } + } + if (rinfo) { /* Redistributed route check. */ if (rinfo->type != ZEBRA_ROUTE_RIPNG && rinfo->metric != RIPNG_METRIC_INFINITY) - return; + { + route_unlock_node (rp); + return; + } /* Local static route. */ if (rinfo->type == ZEBRA_ROUTE_RIPNG && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) || (rinfo->sub_type == RIPNG_ROUTE_DEFAULT)) && rinfo->metric != RIPNG_METRIC_INFINITY) - return; + { + route_unlock_node (rp); + return; + } } - if (rp->info == NULL) + if (!rinfo) { /* Now, check to see whether there is already an explicit route for the destination prefix. If there is no such route, add @@ -756,53 +927,10 @@ ripng_route_process (struct rte *rte, struct sockaddr_ infinity (there is no point in adding a route which unusable). */ if (rte->metric != RIPNG_METRIC_INFINITY) - { - rinfo = ripng_info_new (); - - /* - Setting the destination prefix and length to those in - the RTE. */ - rp->info = rinfo; - rinfo->rp = rp; - - /* - Setting the metric to the newly calculated metric (as - described above). */ - rinfo->metric = rte->metric; - rinfo->tag = ntohs (rte->tag); - - /* - Set the next hop address to be the address of the router - from which the datagram came or the next hop address - specified by a next hop RTE. */ - IPV6_ADDR_COPY (&rinfo->nexthop, nexthop); - IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr); - rinfo->ifindex = ifp->ifindex; - - /* - Initialize the timeout for the route. If the - garbage-collection timer is running for this route, stop it. */ - ripng_timeout_update (rinfo); - - /* - Set the route change flag. */ - rinfo->flags |= RIPNG_RTF_CHANGED; - - /* - Signal the output process to trigger an update (see section - 2.5). */ - ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - - /* Finally, route goes into the kernel. */ - rinfo->type = ZEBRA_ROUTE_RIPNG; - rinfo->sub_type = RIPNG_ROUTE_RTE; - - ripng_zebra_ipv6_add (&p, &rinfo->nexthop, rinfo->ifindex, - rinfo->metric); - rinfo->flags |= RIPNG_RTF_FIB; - - /* Aggregate check. */ - ripng_aggregate_increment (rp, rinfo); - } + ripng_ecmp_add (&newinfo); } else { - rinfo = rp->info; - /* If there is an existing route, compare the next hop address to the address of the router from which the datagram came. If this datagram is from the same router as the existing @@ -810,9 +938,6 @@ ripng_route_process (struct rte *rte, struct sockaddr_ same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr) && (rinfo->ifindex == ifp->ifindex)); - if (same) - ripng_timeout_update (rinfo); - /* Next, compare the metrics. If the datagram is from the same router as the existing route, and the new metric is different than the old one; or, if the new metric is lower than the old @@ -820,96 +945,24 @@ ripng_route_process (struct rte *rte, struct sockaddr_ if ((same && rinfo->metric != rte->metric) || rte->metric < rinfo->metric) { - /* - Adopt the route from the datagram. That is, put the - new metric in, and adjust the next hop address (if - necessary). */ - oldmetric = rinfo->metric; - rinfo->metric = rte->metric; - rinfo->tag = ntohs (rte->tag); - IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr); - rinfo->ifindex = ifp->ifindex; - - /* Should a new route to this network be established - while the garbage-collection timer is running, the - new route will replace the one that is about to be - deleted. In this case the garbage-collection timer - must be cleared. */ - - if (oldmetric == RIPNG_METRIC_INFINITY && - rinfo->metric < RIPNG_METRIC_INFINITY) - { - rinfo->type = ZEBRA_ROUTE_RIPNG; - rinfo->sub_type = RIPNG_ROUTE_RTE; - - RIPNG_TIMER_OFF (rinfo->t_garbage_collect); - - if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV6_ADDR_COPY (&rinfo->nexthop, nexthop); - - ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric); - rinfo->flags |= RIPNG_RTF_FIB; - - /* The aggregation counter needs to be updated because - the prefixes, which are into the gc, have been - removed from the aggregator (see ripng_timout). */ - ripng_aggregate_increment (rp, rinfo); - } - - /* Update nexthop and/or metric value. */ - if (oldmetric != RIPNG_METRIC_INFINITY) - { - ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex); - ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric); - rinfo->flags |= RIPNG_RTF_FIB; - - if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV6_ADDR_COPY (&rinfo->nexthop, nexthop); - } - - /* - Set the route change flag and signal the output process - to trigger an update. */ - rinfo->flags |= RIPNG_RTF_CHANGED; - ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - - /* - If the new metric is infinity, start the deletion - process (described above); */ - if (rinfo->metric == RIPNG_METRIC_INFINITY) - { - /* If the new metric is infinity, the deletion process - begins for the route, which is no longer used for - routing packets. Note that the deletion process is - started only when the metric is first set to - infinity. If the metric was already infinity, then a - new deletion process is not started. */ - if (oldmetric != RIPNG_METRIC_INFINITY) - { - /* - The garbage-collection timer is set for 120 seconds. */ - RIPNG_TIMER_ON (rinfo->t_garbage_collect, - ripng_garbage_collect, ripng->garbage_time); - RIPNG_TIMER_OFF (rinfo->t_timeout); - - /* - The metric for the route is set to 16 - (infinity). This causes the route to be removed - from service.*/ - ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex); - rinfo->flags &= ~RIPNG_RTF_FIB; - - /* Aggregate count decrement. */ - ripng_aggregate_decrement (rp, rinfo); - - /* - The route change flag is to indicate that this - entry has been changed. */ - /* - The output process is signalled to trigger a - response. */ - ; /* Above processes are already done previously. */ - } - } - else - { - /* otherwise, re-initialize the timeout. */ - ripng_timeout_update (rinfo); - } + if (listcount (list) == 1) + { + if (newinfo.metric != RIPNG_METRIC_INFINITY) + ripng_ecmp_replace (&newinfo); + else + ripng_ecmp_delete (rinfo); + } + else + { + if (newinfo.metric < rinfo->metric) + ripng_ecmp_replace (&newinfo); + else /* newinfo.metric > rinfo->metric */ + ripng_ecmp_delete (rinfo); + } } + else /* same & no change */ + ripng_timeout_update (rinfo); + /* Unlock tempolary lock of the route. */ route_unlock_node (rp); } @@ -918,31 +971,33 @@ ripng_route_process (struct rte *rte, struct sockaddr_ /* Add redistributed route to RIPng table. */ void ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, - unsigned int ifindex, struct in6_addr *nexthop) + ifindex_t ifindex, struct in6_addr *nexthop) { struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL, newinfo; + struct list *list = NULL; /* Redistribute route */ if (IN6_IS_ADDR_LINKLOCAL (&p->prefix)) return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#if defined (MUSICA) || defined (LINUX) - /* XXX As long as the RIPng redistribution is applied to all the connected - * routes, one needs to filter the ::/96 prefixes. - * However it could be a wanted case, it will be removed soon. - */ - if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || - (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) - return; -#endif /* MUSICA or LINUX */ rp = route_node_get (ripng->table, (struct prefix *) p); - rinfo = rp->info; - if (rinfo) + memset (&newinfo, 0, sizeof (struct ripng_info)); + newinfo.type = type; + newinfo.sub_type = sub_type; + newinfo.ifindex = ifindex; + newinfo.metric = 1; + newinfo.rp = rp; + if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop)) + newinfo.nexthop = *nexthop; + + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); + if (rinfo->type == ZEBRA_ROUTE_CONNECT && rinfo->sub_type == RIPNG_ROUTE_INTERFACE && rinfo->metric != RIPNG_METRIC_INFINITY) { @@ -962,43 +1017,13 @@ ripng_redistribute_add (int type, int sub_type, struct return; } } - - RIPNG_TIMER_OFF (rinfo->t_timeout); - RIPNG_TIMER_OFF (rinfo->t_garbage_collect); - /* Tells the other daemons about the deletion of - * this RIPng route - **/ - if (ripng_route_rte (rinfo)) - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, &rinfo->nexthop, - rinfo->metric); - - rp->info = NULL; - ripng_info_free (rinfo); - + rinfo = ripng_ecmp_replace (&newinfo); route_unlock_node (rp); - } + else + rinfo = ripng_ecmp_add (&newinfo); - rinfo = ripng_info_new (); - - rinfo->type = type; - rinfo->sub_type = sub_type; - rinfo->ifindex = ifindex; - rinfo->metric = 1; - rinfo->rp = rp; - - if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop)) - rinfo->nexthop = *nexthop; - - rinfo->flags |= RIPNG_RTF_FIB; - rp->info = rinfo; - - /* Aggregate check. */ - ripng_aggregate_increment (rp, rinfo); - - rinfo->flags |= RIPNG_RTF_CHANGED; - if (IS_RIPNG_DEBUG_EVENT) { if (!nexthop) zlog_debug ("Redistribute new prefix %s/%d on the interface %s", @@ -1016,7 +1041,7 @@ ripng_redistribute_add (int type, int sub_type, struct /* Delete redistributed route to RIPng table. */ void ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, - unsigned int ifindex) + ifindex_t ifindex) { struct route_node *rp; struct ripng_info *rinfo; @@ -1025,45 +1050,42 @@ ripng_redistribute_delete (int type, int sub_type, str return; if (IN6_IS_ADDR_LOOPBACK (&p->prefix)) return; -#if defined (MUSICA) || defined (LINUX) - /* XXX As long as the RIPng redistribution is applied to all the connected - * routes, one needs to filter the ::/96 prefixes. - * However it could be a wanted case, it will be removed soon. - */ - if ((IN6_IS_ADDR_V4COMPAT(&p->prefix)) || - (IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && (p->prefixlen == 96))) - return; -#endif /* MUSICA or LINUX */ rp = route_node_lookup (ripng->table, (struct prefix *) p); if (rp) { - rinfo = rp->info; + struct list *list = rp->info; - if (rinfo != NULL - && rinfo->type == type - && rinfo->sub_type == sub_type - && rinfo->ifindex == ifindex) - { - /* Perform poisoned reverse. */ - rinfo->metric = RIPNG_METRIC_INFINITY; - RIPNG_TIMER_ON (rinfo->t_garbage_collect, - ripng_garbage_collect, ripng->garbage_time); - RIPNG_TIMER_OFF (rinfo->t_timeout); + if (list != NULL && listcount (list) != 0) + { + rinfo = listgetdata (listhead (list)); + if (rinfo != NULL + && rinfo->type == type + && rinfo->sub_type == sub_type + && rinfo->ifindex == ifindex) + { + /* Perform poisoned reverse. */ + rinfo->metric = RIPNG_METRIC_INFINITY; + RIPNG_TIMER_ON (rinfo->t_garbage_collect, + ripng_garbage_collect, ripng->garbage_time); + RIPNG_TIMER_OFF (rinfo->t_timeout); - /* Aggregate count decrement. */ - ripng_aggregate_decrement (rp, rinfo); + /* Aggregate count decrement. */ + ripng_aggregate_decrement (rp, rinfo); - rinfo->flags |= RIPNG_RTF_CHANGED; - - if (IS_RIPNG_DEBUG_EVENT) - zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]", - inet6_ntoa(p->prefix), p->prefixlen, - ifindex2ifname(ifindex)); + rinfo->flags |= RIPNG_RTF_CHANGED; - ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - } + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("Poisone %s/%d on the interface %s with an " + "infinity metric [delete]", + inet6_ntoa (p->prefix), p->prefixlen, + ifindex2ifname (ifindex)); + + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + } + } + route_unlock_node (rp); } } @@ -1072,14 +1094,16 @@ void ripng_redistribute_withdraw (int type) { struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; if (!ripng) return; for (rp = route_top (ripng->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL) { + rinfo = listgetdata (listhead (list)); if ((rinfo->type == type) && (rinfo->sub_type != RIPNG_ROUTE_INTERFACE)) { @@ -1314,7 +1338,7 @@ ripng_request_process (struct ripng_packet *packet,int if (rp) { - rinfo = rp->info; + rinfo = listgetdata (listhead ((struct list *)rp->info)); rte->metric = rinfo->metric; route_unlock_node (rp); } @@ -1335,7 +1359,7 @@ ripng_read (struct thread *thread) int sock; struct sockaddr_in6 from; struct ripng_packet *packet; - unsigned int ifindex; + ifindex_t ifindex = 0; struct interface *ifp; int hoplimit = -1; @@ -1422,12 +1446,18 @@ static void ripng_clear_changed_flag (void) { struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (rp = route_top (ripng->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - if (rinfo->flags & RIPNG_RTF_CHANGED) - rinfo->flags &= ~RIPNG_RTF_CHANGED; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + UNSET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + /* This flag can be set only on the first entry. */ + break; + } } /* Regular update of RIPng route. Send all routing formation to RIPng @@ -1605,6 +1635,8 @@ ripng_output_process (struct interface *ifp, struct so struct ripng_aggregate *aggregate; struct prefix_ipv6 *p; struct list * ripng_rte_list; + struct list *list = NULL; + struct listnode *listnode = NULL; if (IS_RIPNG_DEBUG_EVENT) { if (to) @@ -1621,7 +1653,9 @@ ripng_output_process (struct interface *ifp, struct so for (rp = route_top (ripng->table); rp; rp = route_next (rp)) { - if ((rinfo = rp->info) != NULL && rinfo->suppress == 0) + if ((list = rp->info) != NULL && + (rinfo = listgetdata (listhead (list))) != NULL && + rinfo->suppress == 0) { /* If no route-map are applied, the RTE will be these following * informations. @@ -1653,8 +1687,17 @@ ripng_output_process (struct interface *ifp, struct so if (ri->split_horizon == RIPNG_SPLIT_HORIZON) { /* We perform split horizon for RIPng routes. */ - if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && - rinfo->ifindex == ifp->ifindex) + int suppress = 0; + struct ripng_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIPNG && + tmp_rinfo->ifindex == ifp->ifindex) + { + suppress = 1; + break; + } + if (suppress) continue; } @@ -1733,9 +1776,12 @@ ripng_output_process (struct interface *ifp, struct so * for RIPng routes. **/ if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) { - if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && - rinfo->ifindex == ifp->ifindex) - rinfo->metric_out = RIPNG_METRIC_INFINITY; + struct ripng_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if ((tmp_rinfo->type == ZEBRA_ROUTE_RIPNG) && + tmp_rinfo->ifindex == ifp->ifindex) + rinfo->metric_out = RIPNG_METRIC_INFINITY; } /* Add RTE to the list */ @@ -1886,11 +1932,11 @@ ripng_request (struct interface *ifp) NULL, ifp); } - + static int ripng_update_jitter (int time) { - return ((rand () % (time + 1)) - (time / 2)); + return ((random () % (time + 1)) - (time / 2)); } void @@ -1928,8 +1974,8 @@ ripng_event (enum ripng_event event, int sock) break; } } - + /* Print out routes update time. */ static void ripng_vty_out_uptime (struct vty *vty, struct ripng_info *rinfo) @@ -2001,6 +2047,8 @@ DEFUN (show_ipv6_ripng, struct ripng_info *rinfo; struct ripng_aggregate *aggregate; struct prefix_ipv6 *p; + struct list *list = NULL; + struct listnode *listnode = NULL; int len; if (! ripng) @@ -2038,7 +2086,8 @@ DEFUN (show_ipv6_ripng, VTY_NEWLINE); } - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) { p = (struct prefix_ipv6 *) &rp->p; @@ -2593,6 +2642,80 @@ DEFUN (no_ripng_default_information_originate, return CMD_SUCCESS; } +/* Update ECMP routes to zebra when ECMP is disabled. */ +static void +ripng_ecmp_disable (void) +{ + struct route_node *rp; + struct ripng_info *rinfo, *tmp_rinfo; + struct list *list; + struct listnode *node, *nextnode; + + if (!ripng) + return; + + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL && listcount (list) > 1) + { + rinfo = listgetdata (listhead (list)); + if (!ripng_route_rte (rinfo)) + continue; + + /* Drop all other entries, except the first one. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIPNG_TIMER_OFF (tmp_rinfo->t_timeout); + RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + ripng_info_free (tmp_rinfo); + } + + /* Update zebra. */ + ripng_zebra_ipv6_add (rp); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + } +} + +DEFUN (ripng_allow_ecmp, + ripng_allow_ecmp_cmd, + "allow-ecmp", + "Allow Equal Cost MultiPath\n") +{ + if (ripng->ecmp) + { + vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->ecmp = 1; + zlog_info ("ECMP is enabled."); + return CMD_SUCCESS; +} + +DEFUN (no_ripng_allow_ecmp, + no_ripng_allow_ecmp_cmd, + "no allow-ecmp", + NO_STR + "Allow Equal Cost MultiPath\n") +{ + if (!ripng->ecmp) + { + vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->ecmp = 0; + zlog_info ("ECMP is disabled."); + ripng_ecmp_disable (); + return CMD_SUCCESS; +} + /* RIPng configuration write function. */ static int ripng_config_write (struct vty *vty) @@ -2632,6 +2755,10 @@ ripng_config_write (struct vty *vty) VTY_NEWLINE); + /* ECMP configuration. */ + if (ripng->ecmp) + vty_out (vty, " allow-ecmp%s", VTY_NEWLINE); + /* RIPng static routes. */ for (rp = route_top (ripng->route); rp; rp = route_next (rp)) if (rp->info != NULL) @@ -2767,7 +2894,7 @@ ripng_distribute_update_all_wrapper (struct access_lis { ripng_distribute_update_all(NULL); } - + /* delete all the added ripng routes. */ void ripng_clean() @@ -2775,24 +2902,37 @@ ripng_clean() int i; struct route_node *rp; struct ripng_info *rinfo; + struct ripng_aggregate *aggregate; + struct list *list = NULL; + struct listnode *listnode = NULL; if (ripng) { /* Clear RIPng routes */ - for (rp = route_top (ripng->table); rp; rp = route_next (rp)) { - if ((rinfo = rp->info) != NULL) { - if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && - (rinfo->sub_type == RIPNG_ROUTE_RTE)) - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, - &rinfo->nexthop, rinfo->metric); + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + { + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if (ripng_route_rte (rinfo)) + ripng_zebra_ipv6_delete (rp); - RIPNG_TIMER_OFF (rinfo->t_timeout); - RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + ripng_info_free (rinfo); + } + list_delete (list); + rp->info = NULL; + route_unlock_node (rp); + } - rp->info = NULL; - route_unlock_node (rp); - - ripng_info_free(rinfo); - } + if ((aggregate = rp->aggregate) != NULL) + { + ripng_aggregate_free (aggregate); + rp->aggregate = NULL; + route_unlock_node (rp); + } } /* Cancel the RIPng timers */ @@ -2944,7 +3084,7 @@ void ripng_init () { /* Randomize. */ - srand (time (NULL)); + srandom (time (NULL)); /* Install RIPNG_NODE. */ install_node (&cmd_ripng_node, ripng_config_write); @@ -2983,6 +3123,9 @@ ripng_init () install_element (RIPNG_NODE, &ripng_default_information_originate_cmd); install_element (RIPNG_NODE, &no_ripng_default_information_originate_cmd); + + install_element (RIPNG_NODE, &ripng_allow_ecmp_cmd); + install_element (RIPNG_NODE, &no_ripng_allow_ecmp_cmd); ripng_if_init (); ripng_debug_init ();