--- embedaddon/quagga/ripd/ripd.c 2012/02/21 17:26:12 1.1 +++ embedaddon/quagga/ripd/ripd.c 2016/11/02 10:09:10 1.1.1.3 @@ -61,20 +61,20 @@ long rip_global_route_changes = 0; /* RIP queries. */ long rip_global_queries = 0; - + /* Prototypes. */ static void rip_event (enum rip_event, int); static void rip_output_process (struct connected *, struct sockaddr_in *, int, u_char); static int rip_triggered_update (struct thread *); static int rip_update_jitter (unsigned long); - + /* RIP output routes type. */ enum { rip_all_route, rip_changed_route }; - + /* RIP command strings. */ static const struct message rip_msg[] = { @@ -86,7 +86,7 @@ static const struct message rip_msg[] = {RIP_POLL_ENTRY, "POLL ENTRY"}, {0, NULL}, }; - + /* Utility function to set boradcast option to the socket. */ static int sockopt_broadcast (int sock) @@ -138,8 +138,13 @@ rip_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 RIP routing information. */ rip_info_free (rinfo); @@ -147,36 +152,155 @@ rip_garbage_collect (struct thread *t) return 0; } -/* Timeout RIP routes. */ -static int -rip_timeout (struct thread *t) +static void rip_timeout_update (struct rip_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 rip_info * +rip_ecmp_add (struct rip_info *rinfo_new) { - struct rip_info *rinfo; - struct route_node *rn; + struct route_node *rp = rinfo_new->rp; + struct rip_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; - rn = rinfo->rp; + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !rip->ecmp) + return NULL; - /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, rip_garbage_collect, - rip->garbage_time); + rinfo = rip_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + listnode_add (list, rinfo); - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rn->p, &rinfo->nexthop, - rinfo->metric); - /* - The metric for the route is set to 16 (infinity). This causes - the route to be removed from service. */ - rinfo->metric = RIP_METRIC_INFINITY; - rinfo->flags &= ~RIP_RTF_FIB; + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + rip_zebra_ipv4_add (rp); + } - /* - The route change flag is to indicate that this entry has been - changed. */ - rinfo->flags |= RIP_RTF_CHANGED; + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); - /* - The output process is signalled to trigger a response. */ + /* Signal the output process to trigger an update (see section 2.5). */ rip_event (RIP_TRIGGERED_UPDATE, 0); + return rinfo; +} + +/* Replace the ECMP list with the new route. + * RETURN: the new entry added in the list + */ +struct rip_info * +rip_ecmp_replace (struct rip_info *rinfo_new) +{ + struct route_node *rp = rinfo_new->rp; + struct list *list = (struct list *)rp->info; + struct rip_info *rinfo = NULL, *tmp_rinfo = NULL; + struct listnode *node = NULL, *nextnode = NULL; + + if (list == NULL || listcount (list) == 0) + return rip_ecmp_add (rinfo_new); + + /* Get the first entry */ + rinfo = listgetdata (listhead (list)); + + /* Learnt route replaced by a local one. Delete it from zebra. */ + if (rip_route_rte (rinfo) && !rip_route_rte (rinfo_new)) + if (CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + + /* Re-use the first entry, and delete the others. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } + + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, rinfo_new, sizeof (struct rip_info)); + + if (rip_route_rte (rinfo)) + { + rip_timeout_update (rinfo); + /* The ADD message implies an update. */ + rip_zebra_ipv4_add (rp); + } + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_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 rip_info * +rip_ecmp_delete (struct rip_info *rinfo) +{ + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; + + RIP_TIMER_OFF (rinfo->t_timeout); + + if (listcount (list) > 1) + { + /* Some other ECMP entries still exist. Just delete this entry. */ + RIP_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + /* The ADD message implies the update. */ + rip_zebra_ipv4_add (rp); + rip_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 = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + + if (rip_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIP_RTF_FIB)) + rip_zebra_ipv4_delete (rp); + } + + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update (see section 2.5). */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Timeout RIP routes. */ +static int +rip_timeout (struct thread *t) +{ + rip_ecmp_delete ((struct rip_info *)THREAD_ARG (t)); return 0; } @@ -365,13 +489,13 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * int ret; struct prefix_ipv4 p; struct route_node *rp; - struct rip_info *rinfo, rinfotmp; + struct rip_info *rinfo = NULL, newinfo; struct rip_interface *ri; struct in_addr *nexthop; - u_char oldmetric; int same = 0; - int route_reuse = 0; unsigned char old_dist, new_dist; + struct list *list = NULL; + struct listnode *node = NULL; /* Make prefix structure. */ memset (&p, 0, sizeof (struct prefix_ipv4)); @@ -389,22 +513,21 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * if (ret < 0) return; + memset (&newinfo, 0, sizeof (newinfo)); + newinfo.type = ZEBRA_ROUTE_RIP; + newinfo.sub_type = RIP_ROUTE_RTE; + newinfo.nexthop = rte->nexthop; + newinfo.from = from->sin_addr; + newinfo.ifindex = ifp->ifindex; + newinfo.metric = rte->metric; + newinfo.metric_out = rte->metric; /* XXX */ + newinfo.tag = ntohs (rte->tag); /* XXX */ + /* Modify entry according to the interface routemap. */ if (ri->routemap[RIP_FILTER_IN]) { int ret; - struct rip_info newinfo; - memset (&newinfo, 0, sizeof (newinfo)); - newinfo.type = ZEBRA_ROUTE_RIP; - newinfo.sub_type = RIP_ROUTE_RTE; - newinfo.nexthop = rte->nexthop; - newinfo.from = from->sin_addr; - newinfo.ifindex = ifp->ifindex; - newinfo.metric = rte->metric; - newinfo.metric_out = rte->metric; /* XXX */ - newinfo.tag = ntohs (rte->tag); /* XXX */ - /* The object should be of the type of rip_info */ ret = route_map_apply (ri->routemap[RIP_FILTER_IN], (struct prefix *) &p, RMAP_RIP, &newinfo); @@ -433,7 +556,7 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * /* If offset-list does not modify the metric use interface's metric. */ if (!ret) - rte->metric += ifp->metric; + rte->metric += ifp->metric ? ifp->metric : 1; if (rte->metric > RIP_METRIC_INFINITY) rte->metric = RIP_METRIC_INFINITY; @@ -455,9 +578,63 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * /* Get index for the prefix. */ rp = route_node_get (rip->table, (struct prefix *) &p); + newinfo.rp = rp; + newinfo.nexthop = *nexthop; + newinfo.metric = rte->metric; + newinfo.tag = ntohs (rte->tag); + newinfo.distance = rip_distance_apply (&newinfo); + + new_dist = newinfo.distance ? newinfo.distance : ZEBRA_RIP_DISTANCE_DEFAULT; + /* Check to see whether there is already RIP route on the table. */ - rinfo = rp->info; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + /* Need to compare with redistributed entry or local entry */ + if (!rip_route_rte (rinfo)) + break; + if (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) && + IPV4_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. We compare the distances. */ + old_dist = rinfo->distance ? \ + rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT; + + if (new_dist > old_dist) + { + /* New route has a greater distance. Discard it. */ + route_unlock_node (rp); + return; + } + + if (new_dist < old_dist) + /* New route has a smaller distance. Replace the ECMP list + * with the new one in below. */ + break; + + /* Metrics and distances are both same. Keep "rinfo" null and + * the new route is added in the ECMP list in below. */ + } + } + if (rinfo) { /* Local static route. */ @@ -474,101 +651,37 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->metric != RIP_METRIC_INFINITY) { - /* Fill in a minimaly temporary rip_info structure, for a future - rip_distance_apply() use) */ - memset (&rinfotmp, 0, sizeof (rinfotmp)); - IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr); - rinfotmp.rp = rinfo->rp; - new_dist = rip_distance_apply (&rinfotmp); - new_dist = new_dist ? new_dist : ZEBRA_RIP_DISTANCE_DEFAULT; old_dist = rinfo->distance; - /* Only connected routes may have a valid NULL distance */ - if (rinfo->type != ZEBRA_ROUTE_CONNECT) + /* Only routes directly connected to an interface (nexthop == 0) + * may have a valid NULL distance */ + if (rinfo->nexthop.s_addr != 0) old_dist = old_dist ? old_dist : ZEBRA_RIP_DISTANCE_DEFAULT; /* If imported route does not have STRICT precedence, mark it as a ghost */ - if (new_dist > old_dist - || rte->metric == RIP_METRIC_INFINITY) - { - route_unlock_node (rp); - return; - } - else - { - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - rp->info = NULL; - if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - rip_info_free (rinfo); - rinfo = NULL; - route_reuse = 1; - } + if (new_dist <= old_dist && rte->metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + + route_unlock_node (rp); + return; } } if (!rinfo) { + if (rp->info) + route_unlock_node (rp); + /* Now, check to see whether there is already an explicit route for the destination prefix. If there is no such route, add this route to the routing table, unless the metric is infinity (there is no point in adding a route which unusable). */ if (rte->metric != RIP_METRIC_INFINITY) - { - rinfo = rip_info_new (); - - /* - Setting the destination prefix and length to those in - the RTE. */ - 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. */ - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr); - rinfo->ifindex = ifp->ifindex; - - /* - Initialize the timeout for the route. If the - garbage-collection timer is running for this route, stop it - (see section 2.3 for a discussion of the timers). */ - rip_timeout_update (rinfo); - - /* - Set the route change flag. */ - rinfo->flags |= RIP_RTF_CHANGED; - - /* - Signal the output process to trigger an update (see section - 2.5). */ - rip_event (RIP_TRIGGERED_UPDATE, 0); - - /* Finally, route goes into the kernel. */ - rinfo->type = ZEBRA_ROUTE_RIP; - rinfo->sub_type = RIP_ROUTE_RTE; - - /* Set distance value. */ - rinfo->distance = rip_distance_apply (rinfo); - - rp->info = rinfo; - rip_zebra_ipv4_add (&p, &rinfo->nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; - } - - /* Unlock temporary lock, i.e. same behaviour */ - if (route_reuse) - route_unlock_node (rp); + rip_ecmp_add (&newinfo); } else { /* Route is there but we are not sure the route is RIP or not. */ - 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. @@ -577,17 +690,9 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * same = (IPV4_ADDR_SAME (&rinfo->from, &from->sin_addr) && (rinfo->ifindex == ifp->ifindex)); - if (same) - rip_timeout_update (rinfo); + old_dist = rinfo->distance ? \ + rinfo->distance : ZEBRA_RIP_DISTANCE_DEFAULT; - - /* Fill in a minimaly temporary rip_info structure, for a future - rip_distance_apply() use) */ - memset (&rinfotmp, 0, sizeof (rinfotmp)); - IPV4_ADDR_COPY (&rinfotmp.from, &from->sin_addr); - rinfotmp.rp = rinfo->rp; - - /* 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 @@ -598,95 +703,51 @@ rip_rte_process (struct rte *rte, struct sockaddr_in * || (rte->metric < rinfo->metric) || ((same) && (rinfo->metric == rte->metric) - && ntohs (rte->tag) != rinfo->tag) - || (rinfo->distance > rip_distance_apply (&rinfotmp)) - || ((rinfo->distance != rip_distance_apply (rinfo)) && same)) + && (newinfo.tag != rinfo->tag)) + || (old_dist > new_dist) + || ((old_dist != new_dist) && same)) { - /* - 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); - IPV4_ADDR_COPY (&rinfo->from, &from->sin_addr); - rinfo->ifindex = ifp->ifindex; - rinfo->distance = rip_distance_apply (rinfo); - - /* 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 == RIP_METRIC_INFINITY && - rinfo->metric < RIP_METRIC_INFINITY) + if (listcount (list) == 1) { - rinfo->type = ZEBRA_ROUTE_RIP; - rinfo->sub_type = RIP_ROUTE_RTE; - - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - - rip_zebra_ipv4_add (&p, nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; + if (newinfo.metric != RIP_METRIC_INFINITY) + rip_ecmp_replace (&newinfo); + else + rip_ecmp_delete (rinfo); } - - /* Update nexthop and/or metric value. */ - if (oldmetric != RIP_METRIC_INFINITY) + else { - rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric); - rip_zebra_ipv4_add (&p, nexthop, rinfo->metric, - rinfo->distance); - rinfo->flags |= RIP_RTF_FIB; + if (newinfo.metric < rinfo->metric) + rip_ecmp_replace (&newinfo); + else if (newinfo.metric > rinfo->metric) + rip_ecmp_delete (rinfo); + else if (new_dist < old_dist) + rip_ecmp_replace (&newinfo); + else if (new_dist > old_dist) + rip_ecmp_delete (rinfo); + else + { + int update = CHECK_FLAG (rinfo->flags, RIP_RTF_FIB) ? 1 : 0; - if (!IPV4_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV4_ADDR_COPY (&rinfo->nexthop, nexthop); - } + assert (newinfo.metric != RIP_METRIC_INFINITY); - /* - Set the route change flag and signal the output process - to trigger an update. */ - rinfo->flags |= RIP_RTF_CHANGED; - rip_event (RIP_TRIGGERED_UPDATE, 0); - - /* - If the new metric is infinity, start the deletion - process (described above); */ - if (rinfo->metric == RIP_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 != RIP_METRIC_INFINITY) - { - /* - The garbage-collection timer is set for 120 seconds. */ - RIP_TIMER_ON (rinfo->t_garbage_collect, - rip_garbage_collect, rip->garbage_time); RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, &newinfo, sizeof (struct rip_info)); + rip_timeout_update (rinfo); - /* - The metric for the route is set to 16 - (infinity). This causes the route to be removed - from service. */ - rip_zebra_ipv4_delete (&p, &rinfo->nexthop, oldmetric); - rinfo->flags &= ~RIP_RTF_FIB; + if (update) + rip_zebra_ipv4_add (rp); - /* - 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. */ + /* - Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + rip_event (RIP_TRIGGERED_UPDATE, 0); } } - else - { - /* otherwise, re-initialize the timeout. */ - rip_timeout_update (rinfo); - } } + else /* same & no change */ + rip_timeout_update (rinfo); + /* Unlock tempolary lock of the route. */ route_unlock_node (rp); } @@ -1107,7 +1168,8 @@ rip_response_process (struct rip_packet *packet, int s struct prefix_ipv4 ifaddr; struct prefix_ipv4 ifaddrclass; int subnetted; - + + memset(&ifaddr, 0, sizeof(ifaddr)); /* We don't know yet. */ subnetted = -1; @@ -1475,6 +1537,7 @@ rip_send_packet (u_char * buf, int size, struct sockad sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP); /* multicast send should bind to local interface address */ + memset (&from, 0, sizeof (from)); from.sin_family = AF_INET; from.sin_port = htons (RIP_PORT_DEFAULT); from.sin_addr = ifc->address->u.prefix4; @@ -1514,12 +1577,13 @@ rip_send_packet (u_char * buf, int size, struct sockad /* Add redistributed route to RIP table. */ void rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, - unsigned int ifindex, struct in_addr *nexthop, + ifindex_t ifindex, struct in_addr *nexthop, unsigned int metric, unsigned char distance) { int ret; - struct route_node *rp; - struct rip_info *rinfo; + struct route_node *rp = NULL; + struct rip_info *rinfo = NULL, newinfo; + struct list *list = NULL; /* Redistribute route */ ret = rip_destination_check (p->prefix); @@ -1528,10 +1592,21 @@ rip_redistribute_add (int type, int sub_type, struct p rp = route_node_get (rip->table, (struct prefix *) p); - rinfo = rp->info; + memset (&newinfo, 0, sizeof (struct rip_info)); + newinfo.type = type; + newinfo.sub_type = sub_type; + newinfo.ifindex = ifindex; + newinfo.metric = 1; + newinfo.external_metric = metric; + newinfo.distance = distance; + newinfo.rp = rp; + if (nexthop) + newinfo.nexthop = *nexthop; - if (rinfo) + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); + if (rinfo->type == ZEBRA_ROUTE_CONNECT && rinfo->sub_type == RIP_ROUTE_INTERFACE && rinfo->metric != RIP_METRIC_INFINITY) @@ -1553,36 +1628,12 @@ rip_redistribute_add (int type, int sub_type, struct p } } - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); - - if (rip_route_rte (rinfo)) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, &rinfo->nexthop, - rinfo->metric); - rp->info = NULL; - rip_info_free (rinfo); - - route_unlock_node (rp); + rinfo = rip_ecmp_replace (&newinfo); + route_unlock_node (rp); } + else + rinfo = rip_ecmp_add (&newinfo); - rinfo = rip_info_new (); - - rinfo->type = type; - rinfo->sub_type = sub_type; - rinfo->ifindex = ifindex; - rinfo->metric = 1; - rinfo->external_metric = metric; - rinfo->distance = distance; - rinfo->rp = rp; - - if (nexthop) - rinfo->nexthop = *nexthop; - - rinfo->flags |= RIP_RTF_FIB; - rp->info = rinfo; - - rinfo->flags |= RIP_RTF_CHANGED; - if (IS_RIP_DEBUG_EVENT) { if (!nexthop) zlog_debug ("Redistribute new prefix %s/%d on the interface %s", @@ -1594,14 +1645,13 @@ rip_redistribute_add (int type, int sub_type, struct p ifindex2ifname(ifindex)); } - rip_event (RIP_TRIGGERED_UPDATE, 0); } /* Delete redistributed route from RIP table. */ void rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, - unsigned int ifindex) + ifindex_t ifindex) { int ret; struct route_node *rp; @@ -1614,27 +1664,33 @@ rip_redistribute_delete (int type, int sub_type, struc rp = route_node_lookup (rip->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 = RIP_METRIC_INFINITY; - RIP_TIMER_ON (rinfo->t_garbage_collect, - rip_garbage_collect, rip->garbage_time); - RIP_TIMER_OFF (rinfo->t_timeout); - rinfo->flags |= RIP_RTF_CHANGED; + 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 = RIP_METRIC_INFINITY; + RIP_TIMER_ON (rinfo->t_garbage_collect, + rip_garbage_collect, rip->garbage_time); + RIP_TIMER_OFF (rinfo->t_timeout); + rinfo->flags |= RIP_RTF_CHANGED; - if (IS_RIP_DEBUG_EVENT) - zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]", - inet_ntoa(p->prefix), p->prefixlen, - ifindex2ifname(ifindex)); + if (IS_RIP_DEBUG_EVENT) + zlog_debug ("Poisone %s/%d on the interface %s with an " + "infinity metric [delete]", + inet_ntoa(p->prefix), p->prefixlen, + ifindex2ifname(ifindex)); - rip_event (RIP_TRIGGERED_UPDATE, 0); - } + rip_event (RIP_TRIGGERED_UPDATE, 0); + } + } + route_unlock_node (rp); } } @@ -1682,16 +1738,6 @@ rip_request_process (struct rip_packet *packet, int si ntohs (rte->family) == 0 && ntohl (rte->metric) == RIP_METRIC_INFINITY) { - struct prefix_ipv4 saddr; - - /* saddr will be used for determining which routes to split-horizon. - Since the source address we'll pick will be on the same subnet as the - destination, for the purpose of split-horizoning, we'll - pretend that "from" is our source address. */ - saddr.family = AF_INET; - saddr.prefixlen = IPV4_MAX_BITLEN; - saddr.prefix = from->sin_addr; - /* All route with split horizon */ rip_output_process (ifc, from, rip_all_route, packet->version); } @@ -1716,7 +1762,7 @@ rip_request_process (struct rip_packet *packet, int si rp = route_node_lookup (rip->table, (struct prefix *) &p); if (rp) { - rinfo = rp->info; + rinfo = listgetdata (listhead ((struct list *)rp->info)); rte->metric = htonl (rinfo->metric); route_unlock_node (rp); } @@ -1747,7 +1793,7 @@ setsockopt_pktinfo (int sock) /* Read RIP packet by recvmsg function. */ int rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from, - int *ifindex) + ifindex_t *ifindex) { int ret; struct msghdr msg; @@ -1788,7 +1834,7 @@ rip_read_new (struct thread *t) int sock; char buf[RIP_PACKET_MAXSIZ]; struct sockaddr_in from; - unsigned int ifindex; + ifindex_t ifindex; /* Fetch socket then register myself. */ sock = THREAD_FD (t); @@ -2151,6 +2197,8 @@ rip_output_process (struct connected *ifc, struct sock int num = 0; int rtemax; int subnetted = 0; + struct list *list = NULL; + struct listnode *listnode = NULL; /* Logging output event. */ if (IS_RIP_DEBUG_EVENT) @@ -2167,7 +2215,7 @@ rip_output_process (struct connected *ifc, struct sock /* Reset stream and RTE counter. */ stream_reset (s); - rtemax = (RIP_PACKET_MAXSIZ - 4) / 20; + rtemax = RIP_MAX_RTE; /* Get RIP interface. */ ri = ifc->ifp->info; @@ -2208,8 +2256,9 @@ rip_output_process (struct connected *ifc, struct sock } for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); /* For RIPv1, if we are subnetted, output subnets in our network */ /* that have the same mask as the output "interface". For other */ /* networks, only the classfull version is output. */ @@ -2268,11 +2317,22 @@ rip_output_process (struct connected *ifc, struct sock * (in order to handle the case when multiple subnets are * configured on the same interface). */ - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) - continue; - if (rinfo->type == ZEBRA_ROUTE_CONNECT && + int suppress = 0; + struct rip_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIP && + tmp_rinfo->ifindex == ifc->ifp->ifindex) + { + suppress = 1; + break; + } + + if (!suppress && rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) + suppress = 1; + + if (suppress) continue; } @@ -2366,12 +2426,15 @@ rip_output_process (struct connected *ifc, struct sock * (in order to handle the case when multiple subnets are * configured on the same interface). */ - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->ifindex == ifc->ifp->ifindex) - rinfo->metric_out = RIP_METRIC_INFINITY; - if (rinfo->type == ZEBRA_ROUTE_CONNECT && + struct rip_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIP && + tmp_rinfo->ifindex == ifc->ifp->ifindex) + rinfo->metric_out = RIP_METRIC_INFINITY; + if (tmp_rinfo->type == ZEBRA_ROUTE_CONNECT && prefix_match((struct prefix *)p, ifc->address)) - rinfo->metric_out = RIP_METRIC_INFINITY; + rinfo->metric_out = RIP_METRIC_INFINITY; } /* Prepare preamble, auth headers, if needs be */ @@ -2511,7 +2574,7 @@ rip_update_process (int route_type) if (IS_RIP_DEBUG_EVENT) zlog_debug("SEND UPDATE to %s ifindex %d", - (ifp->name ? ifp->name : "_unknown_"), ifp->ifindex); + ifp->name, ifp->ifindex); /* send update on each connected network */ for (ALL_LIST_ELEMENTS (ifp->connected, ifnode, ifnnode, connected)) @@ -2591,12 +2654,18 @@ static void rip_clear_changed_flag (void) { struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - if (rinfo->flags & RIP_RTF_CHANGED) - rinfo->flags &= ~RIP_RTF_CHANGED; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + UNSET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + /* This flag can be set only on the first entry. */ + break; + } } /* Triggered update interval timer. */ @@ -2661,14 +2730,16 @@ void rip_redistribute_withdraw (int type) { struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; if (!rip) return; for (rp = route_top (rip->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 != RIP_ROUTE_INTERFACE) { @@ -2772,7 +2843,7 @@ rip_request_send (struct sockaddr_in *to, struct inter } return sizeof (rip_packet); } - + static int rip_update_jitter (unsigned long time) { @@ -2789,7 +2860,7 @@ rip_update_jitter (unsigned long time) if (jitter_input < JITTER_BOUND) jitter_input = JITTER_BOUND; - jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input)); + jitter = (((random () % ((jitter_input * 2) + 1)) - jitter_input)); return jitter/JITTER_BOUND; } @@ -2826,7 +2897,7 @@ rip_event (enum rip_event event, int sock) break; } } - + DEFUN (router_rip, router_rip_cmd, "router rip", @@ -2981,12 +3052,15 @@ static void rip_update_default_metric (void) { struct route_node *np; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (np = route_top (rip->table); np; np = route_next (np)) - if ((rinfo = np->info) != NULL) - if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT) - rinfo->metric = rip->default_metric; + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + if (rinfo->type != ZEBRA_ROUTE_RIP && rinfo->type != ZEBRA_ROUTE_CONNECT) + rinfo->metric = rip->default_metric; } #endif @@ -3102,7 +3176,7 @@ ALIAS (no_rip_timers, "Routing information timeout timer. Default is 180.\n" "Garbage collection timer. Default is 120.\n") - + struct route_table *rip_distance_table; struct rip_distance @@ -3179,7 +3253,6 @@ rip_distance_unset (struct vty *vty, const char *dista { int ret; struct prefix_ipv4 p; - u_char distance; struct route_node *rn; struct rip_distance *rdistance; @@ -3190,8 +3263,6 @@ rip_distance_unset (struct vty *vty, const char *dista return CMD_WARNING; } - distance = atoi (distance_str); - rn = route_node_lookup (rip_distance_table, (struct prefix *)&p); if (! rn) { @@ -3370,7 +3441,81 @@ DEFUN (no_rip_distance_source_access_list, rip_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + +/* Update ECMP routes to zebra when ECMP is disabled. */ +static void +rip_ecmp_disable (void) +{ + struct route_node *rp; + struct rip_info *rinfo, *tmp_rinfo; + struct list *list; + struct listnode *node, *nextnode; + + if (!rip) + return; + + for (rp = route_top (rip->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL && listcount (list) > 1) + { + rinfo = listgetdata (listhead (list)); + if (!rip_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) + { + RIP_TIMER_OFF (tmp_rinfo->t_timeout); + RIP_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + rip_info_free (tmp_rinfo); + } + + /* Update zebra. */ + rip_zebra_ipv4_add (rp); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIP_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + rip_event (RIP_TRIGGERED_UPDATE, 0); + } +} + +DEFUN (rip_allow_ecmp, + rip_allow_ecmp_cmd, + "allow-ecmp", + "Allow Equal Cost MultiPath\n") +{ + if (rip->ecmp) + { + vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip->ecmp = 1; + zlog_info ("ECMP is enabled."); + return CMD_SUCCESS; +} + +DEFUN (no_rip_allow_ecmp, + no_rip_allow_ecmp_cmd, + "no allow-ecmp", + NO_STR + "Allow Equal Cost MultiPath\n") +{ + if (!rip->ecmp) + { + vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rip->ecmp = 0; + zlog_info ("ECMP is disabled."); + rip_ecmp_disable (); + return CMD_SUCCESS; +} + /* Print out routes update time. */ static void rip_vty_out_uptime (struct vty *vty, struct rip_info *rinfo) @@ -3425,7 +3570,9 @@ DEFUN (show_ip_rip, "Show RIP routes\n") { struct route_node *np; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; if (! rip) return CMD_SUCCESS; @@ -3438,7 +3585,8 @@ DEFUN (show_ip_rip, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); for (np = route_top (rip->table); np; np = route_next (np)) - if ((rinfo = np->info) != NULL) + if ((list = np->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) { int len; @@ -3675,6 +3823,10 @@ config_write_rip (struct vty *vty) rdistance->access_list ? rdistance->access_list : "", VTY_NEWLINE); + /* ECMP configuration. */ + if (rip->ecmp) + vty_out (vty, " allow-ecmp%s", VTY_NEWLINE); + /* RIP static route configuration. */ for (rn = route_top (rip->route); rn; rn = route_next (rn)) if (rn->info) @@ -3694,7 +3846,7 @@ static struct cmd_node rip_node = "%s(config-router)# ", 1 }; - + /* Distribute-list update functions. */ static void rip_distribute_update (struct distribute *dist) @@ -3785,35 +3937,38 @@ rip_distribute_update_all_wrapper(struct access_list * { rip_distribute_update_all(NULL); } - + /* Delete all added rip route. */ void rip_clean (void) { int i; struct route_node *rp; - struct rip_info *rinfo; + struct rip_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; if (rip) { /* Clear RIP routes */ for (rp = route_top (rip->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - { - if (rinfo->type == ZEBRA_ROUTE_RIP && - rinfo->sub_type == RIP_ROUTE_RTE) - rip_zebra_ipv4_delete ((struct prefix_ipv4 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - - RIP_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_OFF (rinfo->t_garbage_collect); + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if (rip_route_rte (rinfo)) + rip_zebra_ipv4_delete (rp); - rp->info = NULL; - route_unlock_node (rp); + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_OFF (rinfo->t_garbage_collect); + rip_info_free (rinfo); + } + list_delete (list); + rp->info = NULL; + route_unlock_node (rp); + } - rip_info_free (rinfo); - } - /* Cancel RIP related timers. */ RIP_TIMER_OFF (rip->t_update); RIP_TIMER_OFF (rip->t_triggered_update); @@ -3978,7 +4133,7 @@ void rip_init (void) { /* Randomize for triggered update random(). */ - srand (time (NULL)); + srandom (time (NULL)); /* Install top nodes. */ install_node (&rip_node, config_write_rip); @@ -4009,6 +4164,8 @@ rip_init (void) install_element (RIP_NODE, &no_rip_distance_source_cmd); install_element (RIP_NODE, &rip_distance_source_access_list_cmd); install_element (RIP_NODE, &no_rip_distance_source_access_list_cmd); + install_element (RIP_NODE, &rip_allow_ecmp_cmd); + install_element (RIP_NODE, &no_rip_allow_ecmp_cmd); /* Debug related init. */ rip_debug_init ();