]> git.proxmox.com Git - mirror_frr.git/blobdiff - ripngd/ripngd.c
ripngd: limit max of "timers basic" to 65535 as documented
[mirror_frr.git] / ripngd / ripngd.c
index d8c34e80617bdee893219a2bb28db187c7b292ee..8e97c2f8ba0991538cfa9dc5bc3a04c7aaca6f95 100644 (file)
@@ -21,9 +21,6 @@
 
 #include <zebra.h>
 
-/* For struct udphdr. */
-#include <netinet/udp.h>
-
 #include "prefix.h"
 #include "filter.h"
 #include "log.h"
 #include "distribute.h"
 #include "plist.h"
 #include "routemap.h"
+#include "if_rmap.h"
+#include "privs.h"
 
 #include "ripngd/ripngd.h"
 #include "ripngd/ripng_route.h"
 #include "ripngd/ripng_debug.h"
-#include "ripngd/ripng_ifrmap.h"
-
-#define min(a, b) ((a) < (b) ? (a) : (b))
+#include "ripngd/ripng_nexthop.h"
 
 /* RIPng structure which includes many parameters related to RIPng
    protocol. If ripng couldn't active or ripng doesn't configured,
@@ -54,13 +51,13 @@ enum
 {
   ripng_all_route,
   ripng_changed_route,
-  ripng_split_horizon,
-  ripng_no_split_horizon
 };
 
+extern struct zebra_privs_t ripngd_privs;
+
 /* Prototypes. */
 void
-ripng_output_process (struct interface *, struct sockaddr_in6 *, int, int);
+ripng_output_process (struct interface *, struct sockaddr_in6 *, int);
 
 int
 ripng_triggered_update (struct thread *);
@@ -75,16 +72,11 @@ struct ripng_nexthop
   } flag;
   struct in6_addr address;
 };
-\f
-/* Utility function for making IPv6 address string. */
-const char *
-inet6_ntop (struct in6_addr *p)
-{
-  static char buf[INET6_ADDRSTRLEN];
-
-  inet_ntop (AF_INET6, p, buf, INET6_ADDRSTRLEN);
 
-  return buf;
+static int
+ripng_route_rte (struct ripng_info *rinfo)
+{
+  return (rinfo->type == ZEBRA_ROUTE_RIPNG && rinfo->sub_type == RIPNG_ROUTE_RTE);
 }
 
 /* Allocate new ripng information. */
@@ -104,19 +96,8 @@ ripng_info_free (struct ripng_info *rinfo)
   XFREE (MTYPE_RIPNG_ROUTE, rinfo);
 }
 \f
-static int
-setsockopt_so_recvbuf (int sock, int size)
-{
-  int ret;
-
-  ret = setsockopt (sock, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof (int));
-  if (ret < 0)
-    zlog (NULL, LOG_ERR, "can't setsockopt SO_RCVBUF");
-  return ret;
-}
-
 /* Create ripng socket. */
-int 
+static int 
 ripng_make_socket (void)
 {
   int ret;
@@ -136,6 +117,11 @@ ripng_make_socket (void)
   ret = setsockopt_ipv6_pktinfo (sock, 1);
   if (ret < 0)
     return ret;
+#ifdef IPTOS_PREC_INTERNETCONTROL
+  ret = setsockopt_ipv6_tclass (sock, IPTOS_PREC_INTERNETCONTROL);
+  if (ret < 0)
+    return ret;
+#endif
   ret = setsockopt_ipv6_multicast_hops (sock, 255);
   if (ret < 0)
     return ret;
@@ -153,12 +139,19 @@ ripng_make_socket (void)
 #endif /* SIN6_LEN */
   ripaddr.sin6_port = htons (RIPNG_PORT_DEFAULT);
 
+  if (ripngd_privs.change (ZPRIVS_RAISE))
+    zlog_err ("ripng_make_socket: could not raise privs");
+  
   ret = bind (sock, (struct sockaddr *) &ripaddr, sizeof (ripaddr));
   if (ret < 0)
-    {
-      zlog (NULL, LOG_ERR, "Can't bind ripng socket: %s.", strerror (errno));
-      return ret;
-    }
+  {
+    zlog (NULL, LOG_ERR, "Can't bind ripng socket: %s.", safe_strerror (errno));
+    if (ripngd_privs.change (ZPRIVS_LOWER))
+      zlog_err ("ripng_make_socket: could not lower privs");
+    return ret;
+  }
+  if (ripngd_privs.change (ZPRIVS_LOWER))
+    zlog_err ("ripng_make_socket: could not lower privs");
   return sock;
 }
 
@@ -175,12 +168,12 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
   struct in6_pktinfo *pkt;
   struct sockaddr_in6 addr;
 
-#ifdef DEBUG
-  if (to)
-    zlog_info ("DEBUG RIPng: send to %s", inet6_ntop (&to->sin6_addr));
-  zlog_info ("DEBUG RIPng: send if %s", ifp->name);
-  zlog_info ("DEBUG RIPng: send packet size %d", bufsize);
-#endif /* DEBUG */
+  if (IS_RIPNG_DEBUG_SEND) {
+    if (to)
+      zlog_debug ("send to %s", inet6_ntoa (to->sin6_addr));
+    zlog_debug ("  send interface %s", ifp->name);
+    zlog_debug ("  send packet size %d", bufsize);
+  }
 
   memset (&addr, 0, sizeof (struct sockaddr_in6));
   addr.sin6_family = AF_INET6;
@@ -222,14 +215,19 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to,
 
   ret = sendmsg (ripng->sock, &msg, 0);
 
-  if (ret < 0)
-    zlog_warn ("RIPng send fail on %s: %s", ifp->name, strerror (errno));
+  if (ret < 0) {
+    if (to)
+      zlog_err ("RIPng send fail on %s to %s: %s", ifp->name, 
+                inet6_ntoa (to->sin6_addr), safe_strerror (errno));
+    else
+      zlog_err ("RIPng send fail on %s: %s", ifp->name, safe_strerror (errno));
+  }
 
   return ret;
 }
 
 /* Receive UDP RIPng packet from socket. */
-int
+static int
 ripng_recv_packet (int sock, u_char *buf, int bufsize,
                   struct sockaddr_in6 *from, unsigned int *ifindex, 
                   int *hoplimit)
@@ -259,7 +257,7 @@ ripng_recv_packet (int sock, u_char *buf, int bufsize,
   if (ret < 0)
     return ret;
 
-  for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL;
+  for (cmsgptr = ZCMSG_FIRSTHDR(&msg); cmsgptr != NULL;
        cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) 
     {
       /* I want interface index which this packet comes from. */
@@ -279,7 +277,10 @@ ripng_recv_packet (int sock, u_char *buf, int bufsize,
       /* Incoming packet's multicast hop limit. */
       if (cmsgptr->cmsg_level == IPPROTO_IPV6 &&
          cmsgptr->cmsg_type == IPV6_HOPLIMIT)
-       *hoplimit = *((int *) CMSG_DATA (cmsgptr));
+       {
+         int *phoplimit = (int *) CMSG_DATA (cmsgptr);
+         *hoplimit = *phoplimit;
+       }
     }
 
   /* Hoplimit check shold be done when destination address is
@@ -292,11 +293,11 @@ ripng_recv_packet (int sock, u_char *buf, int bufsize,
 
 /* Dump rip packet */
 void
-ripng_packet_dump (struct ripng_packet *packet, int size, char *sndrcv)
+ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv)
 {
   caddr_t lim;
   struct rte *rte;
-  char *command_str;
+  const char *command_str;
 
   /* Set command string. */
   if (packet->command == RIPNG_REQUEST)
@@ -307,7 +308,7 @@ ripng_packet_dump (struct ripng_packet *packet, int size, char *sndrcv)
     command_str = "unknown";
 
   /* Dump packet header. */
-  zlog_info ("%s %s version %d packet size %d", 
+  zlog_debug ("%s %s version %d packet size %d", 
             sndrcv, command_str, packet->version, size);
 
   /* Dump each routing table entry. */
@@ -316,16 +317,16 @@ ripng_packet_dump (struct ripng_packet *packet, int size, char *sndrcv)
   for (lim = (caddr_t) packet + size; (caddr_t) rte < lim; rte++)
     {
       if (rte->metric == RIPNG_METRIC_NEXTHOP)
-       zlog_info ("  nexthop %s/%d", inet6_ntop (&rte->addr), rte->prefixlen);
+       zlog_debug ("  nexthop %s/%d", inet6_ntoa (rte->addr), rte->prefixlen);
       else
-       zlog_info ("  %s/%d metric %d tag %d", 
-                  inet6_ntop (&rte->addr), rte->prefixlen, 
+       zlog_debug ("  %s/%d metric %d tag %d", 
+                  inet6_ntoa (rte->addr), rte->prefixlen, 
                   rte->metric, ntohs (rte->tag));
     }
 }
 
 /* RIPng next hop address RTE (Route Table Entry). */
-void
+static void
 ripng_nexthop_rte (struct rte *rte,
                   struct sockaddr_in6 *from,
                   struct ripng_nexthop *nexthop)
@@ -334,19 +335,19 @@ ripng_nexthop_rte (struct rte *rte,
 
   /* Logging before checking RTE. */
   if (IS_RIPNG_DEBUG_RECV)
-    zlog_info ("RIPng nexthop RTE address %s tag %d prefixlen %d",
-              inet6_ntop (&rte->addr), ntohs (rte->tag), rte->prefixlen);
+    zlog_debug ("RIPng nexthop RTE address %s tag %d prefixlen %d",
+              inet6_ntoa (rte->addr), ntohs (rte->tag), rte->prefixlen);
 
   /* RFC2080 2.1.1 Next Hop: 
    The route tag and prefix length in the next hop RTE must be
    set to zero on sending and ignored on receiption.  */
   if (ntohs (rte->tag) != 0)
     zlog_warn ("RIPng nexthop RTE with non zero tag value %d from %s",
-              ntohs (rte->tag), inet6_ntop (&from->sin6_addr));
+              ntohs (rte->tag), inet6_ntoa (from->sin6_addr));
 
   if (rte->prefixlen != 0)
     zlog_warn ("RIPng nexthop RTE with non zero prefixlen value %d from %s",
-              rte->prefixlen, inet6_ntop (&from->sin6_addr));
+              rte->prefixlen, inet6_ntoa (from->sin6_addr));
 
   /* Specifying a value of 0:0:0:0:0:0:0:0 in the prefix field of a
    next hop RTE indicates that the next hop address should be the
@@ -374,7 +375,7 @@ ripng_nexthop_rte (struct rte *rte,
    valid, route may be taken.  If the received next hop address is not
    a link-local address, it should be treated as 0:0:0:0:0:0:0:0.  */
   zlog_warn ("RIPng nexthop RTE with non link-local address %s from %s",
-            inet6_ntop (&rte->addr),
+            inet6_ntoa (rte->addr),
             inet_ntop (AF_INET6, &from->sin6_addr, buf, INET6_BUFSIZ));
 
   nexthop->flag = RIPNG_NEXTHOP_UNSPEC;
@@ -384,28 +385,27 @@ ripng_nexthop_rte (struct rte *rte,
 }
 
 /* If ifp has same link-local address then return 1. */
-int
+static int
 ripng_lladdr_check (struct interface *ifp, struct in6_addr *addr)
 {
-  listnode listnode;
+  struct listnode *node;
   struct connected *connected;
   struct prefix *p;
 
-  for (listnode = listhead (ifp->connected); listnode; nextnode (listnode))
-    if ((connected = getdata (listnode)) != NULL)
-      {
-       p = connected->address;
+  for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected))
+    {
+      p = connected->address;
 
-       if (p->family == AF_INET6 &&
-           IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6) &&
-           IN6_ARE_ADDR_EQUAL (&p->u.prefix6, addr))
-         return 1;
-      }
+      if (p->family == AF_INET6 &&
+          IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6) &&
+          IN6_ARE_ADDR_EQUAL (&p->u.prefix6, addr))
+        return 1;
+    }
   return 0;
 }
 
 /* RIPng route garbage collect timer. */
-int
+static int
 ripng_garbage_collect (struct thread *t)
 {
   struct ripng_info *rinfo;
@@ -420,14 +420,6 @@ ripng_garbage_collect (struct thread *t)
   /* Get route_node pointer. */
   rp = rinfo->rp;
 
-  /* Delete this route from the kernel. */
-  ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, 
-                          &rinfo->nexthop, rinfo->ifindex);
-  rinfo->flags &= ~RIPNG_RTF_FIB;
-
-  /* Aggregate count decrement. */
-  ripng_aggregate_decrement (rp, rinfo);
-
   /* Unlock route_node. */
   rp->info = NULL;
   route_unlock_node (rp);
@@ -439,7 +431,7 @@ ripng_garbage_collect (struct thread *t)
 }
 
 /* Timeout RIPng routes. */
-int
+static int
 ripng_timeout (struct thread *t)
 {
   struct ripng_info *rinfo;
@@ -455,9 +447,16 @@ ripng_timeout (struct thread *t)
   RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect, 
                  ripng->garbage_time);
 
+  /* 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;
+
+  /* Aggregate count decrement. */
+  ripng_aggregate_decrement (rp, rinfo);
 
   /* - The route change flag is to indicate that this entry has been
      changed. */
@@ -469,7 +468,7 @@ ripng_timeout (struct thread *t)
   return 0;
 }
 
-void
+static void
 ripng_timeout_update (struct ripng_info *rinfo)
 {
   if (rinfo->metric != RIPNG_METRIC_INFINITY)
@@ -479,12 +478,154 @@ ripng_timeout_update (struct ripng_info *rinfo)
     }
 }
 
+static int
+ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
+{
+  struct distribute *dist;
+  struct access_list *alist;
+  struct prefix_list *plist;
+
+  /* Input distribute-list filtering. */
+  if (ri->list[RIPNG_FILTER_IN])
+    {
+      if (access_list_apply (ri->list[RIPNG_FILTER_IN], 
+                            (struct prefix *) p) == FILTER_DENY)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("%s/%d filtered by distribute in",
+                      inet6_ntoa (p->prefix), p->prefixlen);
+         return -1;
+       }
+    }
+  if (ri->prefix[RIPNG_FILTER_IN])
+    {
+      if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN], 
+                            (struct prefix *) p) == PREFIX_DENY)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("%s/%d filtered by prefix-list in",
+                      inet6_ntoa (p->prefix), p->prefixlen);
+         return -1;
+       }
+    }
+
+  /* All interface filter check. */
+  dist = distribute_lookup (NULL);
+  if (dist)
+    {
+      if (dist->list[DISTRIBUTE_IN])
+       {
+         alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]);
+           
+         if (alist)
+           {
+             if (access_list_apply (alist,
+                                    (struct prefix *) p) == FILTER_DENY)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by distribute in",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 return -1;
+               }
+           }
+       }
+      if (dist->prefix[DISTRIBUTE_IN])
+       {
+         plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]);
+         
+         if (plist)
+           {
+             if (prefix_list_apply (plist,
+                                    (struct prefix *) p) == PREFIX_DENY)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by prefix-list in",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 return -1;
+               }
+           }
+       }
+    }
+  return 0;
+}
+
+static int
+ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri)
+{
+  struct distribute *dist;
+  struct access_list *alist;
+  struct prefix_list *plist;
+
+  if (ri->list[RIPNG_FILTER_OUT])
+    {
+      if (access_list_apply (ri->list[RIPNG_FILTER_OUT],
+                            (struct prefix *) p) == FILTER_DENY)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("%s/%d is filtered by distribute out",
+                      inet6_ntoa (p->prefix), p->prefixlen);
+         return -1;
+       }
+    }
+  if (ri->prefix[RIPNG_FILTER_OUT])
+    {
+      if (prefix_list_apply (ri->prefix[RIPNG_FILTER_OUT],
+                            (struct prefix *) p) == PREFIX_DENY)
+       {
+         if (IS_RIPNG_DEBUG_PACKET)
+           zlog_debug ("%s/%d is filtered by prefix-list out",
+                      inet6_ntoa (p->prefix), p->prefixlen);
+         return -1;
+       }
+    }
+
+  /* All interface filter check. */
+  dist = distribute_lookup (NULL);
+  if (dist)
+    {
+      if (dist->list[DISTRIBUTE_OUT])
+       {
+         alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]);
+           
+         if (alist)
+           {
+             if (access_list_apply (alist,
+                                    (struct prefix *) p) == FILTER_DENY)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by distribute out",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 return -1;
+               }
+           }
+       }
+      if (dist->prefix[DISTRIBUTE_OUT])
+       {
+         plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]);
+         
+         if (plist)
+           {
+             if (prefix_list_apply (plist,
+                                    (struct prefix *) p) == PREFIX_DENY)
+               {
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("%s/%d filtered by prefix-list out",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 return -1;
+               }
+           }
+       }
+    }
+  return 0;
+}
+
 /* Process RIPng route according to RFC2080. */
-void
+static void
 ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
                     struct ripng_nexthop *ripng_nexthop,
                     struct interface *ifp)
 {
+  int ret;
   struct prefix_ipv6 p;
   struct route_node *rp;
   struct ripng_info *rinfo;
@@ -508,26 +649,9 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
   /* Apply input filters. */
   ri = ifp->info;
 
-  if (ri->list[RIPNG_FILTER_IN])
-    {
-      if (access_list_apply (ri->list[RIPNG_FILTER_IN], &p) == FILTER_DENY)
-       {
-         if (IS_RIPNG_DEBUG_PACKET)
-           zlog_info ("RIPng %s/%d is filtered by distribute in",
-                      inet6_ntop (&p.prefix), p.prefixlen);
-         return;
-       }
-    }
-  if (ri->prefix[RIPNG_FILTER_IN])
-    {
-      if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN], &p) == PREFIX_DENY)
-       {
-         if (IS_RIPNG_DEBUG_PACKET)
-           zlog_info ("RIPng %s/%d is filtered by prefix-list in",
-                      inet6_ntop (&p.prefix), p.prefixlen);
-         return;
-       }
-    }
+  ret = ripng_incoming_filter (&p, ri);
+  if (ret < 0)
+    return;
 
   /* Modify entry. */
   if (ri->routemap[RIPNG_FILTER_IN])
@@ -536,7 +660,17 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
       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);
@@ -544,14 +678,50 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
       if (ret == RMAP_DENYMATCH)
        {
          if (IS_RIPNG_DEBUG_PACKET)
-           zlog_info ("RIPng %s/%d is filtered by route-map in",
-                      inet6_ntop (&p.prefix), p.prefixlen);
+           zlog_debug ("RIPng %s/%d is filtered by route-map in",
+                      inet6_ntoa (p.prefix), p.prefixlen);
          return;
        }
 
-      rte->metric = newinfo.metric;
+      /* Get back the object */
+      if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) {
+       if (! IPV6_ADDR_SAME(&newinfo.nexthop, &ripng_nexthop->address) ) {
+         /* the nexthop get changed by the routemap */
+         if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop))
+           ripng_nexthop->address = newinfo.nexthop;
+         else
+           ripng_nexthop->address = in6addr_any;
+       }
+      } else {
+       if (! IPV6_ADDR_SAME(&newinfo.nexthop, &from->sin6_addr) ) {
+         /* the nexthop get changed by the routemap */
+         if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop)) {
+           ripng_nexthop->flag = RIPNG_NEXTHOP_ADDRESS;
+           ripng_nexthop->address = newinfo.nexthop;
+         }
+       }
+      }
+      rte->tag     = htons(newinfo.tag_out); /* XXX */
+      rte->metric  = newinfo.metric_out; /* XXX: the routemap uses the metric_out field */
     }
 
+  /* Once the entry has been validated, update the metric by
+   * adding the cost of the network on wich the message
+   * arrived. If the result is greater than infinity, use infinity
+   * (RFC2453 Sec. 3.9.2)
+   **/
+  /* Zebra ripngd can handle offset-list in. */
+  ret = ripng_offset_list_apply_in (&p, ifp, &rte->metric);
+
+  /* If offset-list does not modify the metric use interface's
+   * one. */
+  if (! ret)
+    rte->metric += ifp->metric;
+
+  if (rte->metric > RIPNG_METRIC_INFINITY)
+    rte->metric = RIPNG_METRIC_INFINITY;
+
   /* Set nexthop pointer. */
   if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS)
     nexthop = &ripng_nexthop->address;
@@ -561,6 +731,23 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
   /* Lookup RIPng routing table. */
   rp = route_node_get (ripng->table, (struct prefix *) &p);
 
+  /* Sanity check */
+  rinfo = rp->info;
+  if (rinfo)
+    {
+      /* Redistributed route check. */
+      if (rinfo->type != ZEBRA_ROUTE_RIPNG
+         && rinfo->metric != RIPNG_METRIC_INFINITY)
+       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;
+    }
+
   if (rp->info == NULL)
     {
       /* Now, check to see whether there is already an explicit route
@@ -604,7 +791,8 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
          rinfo->type = ZEBRA_ROUTE_RIPNG;
          rinfo->sub_type = RIPNG_ROUTE_RTE;
 
-         ripng_zebra_ipv6_add (&p, &rinfo->nexthop, rinfo->ifindex);
+         ripng_zebra_ipv6_add (&p, &rinfo->nexthop, rinfo->ifindex,
+                               rinfo->metric);
          rinfo->flags |= RIPNG_RTF_FIB;
 
          /* Aggregate check. */
@@ -638,17 +826,45 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
          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);
+           }
 
-         if (! IN6_ARE_ADDR_EQUAL (&rinfo->nexthop, nexthop))
+         /* 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);
+             ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric);
              rinfo->flags |= RIPNG_RTF_FIB;
 
-             IPV6_ADDR_COPY (&rinfo->nexthop, nexthop);
+             if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop))
+               IPV6_ADDR_COPY (&rinfo->nexthop, nexthop);
            }
-         IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr);
-         rinfo->ifindex = ifp->ifindex;
 
          /* - Set the route change flag and signal the output process
             to trigger an update. */
@@ -675,6 +891,12 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
                  /* - 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
@@ -686,13 +908,6 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
            {
              /* otherwise, re-initialize the timeout. */
              ripng_timeout_update (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. */
-             RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
            }
        }
       /* Unlock tempolary lock of the route. */
@@ -703,7 +918,7 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from,
 /* Add redistributed route to RIPng table. */
 void
 ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, 
-                       unsigned int ifindex)
+                       unsigned int ifindex, struct in6_addr *nexthop)
 {
   struct route_node *rp;
   struct ripng_info *rinfo;
@@ -713,30 +928,89 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p,
     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)
     {
+      if (rinfo->type == ZEBRA_ROUTE_CONNECT
+          && rinfo->sub_type == RIPNG_ROUTE_INTERFACE
+         && rinfo->metric != RIPNG_METRIC_INFINITY) {
+        route_unlock_node (rp);
+          return;
+      }
+
+      /* Manually configured RIPng route check.
+       * They have the precedence on all the other entries.
+       **/
+      if (rinfo->type == ZEBRA_ROUTE_RIPNG
+          && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) ||
+              (rinfo->sub_type == RIPNG_ROUTE_DEFAULT)) ) {
+        if (type != ZEBRA_ROUTE_RIPNG || ((sub_type != RIPNG_ROUTE_STATIC) &&
+                                          (sub_type != RIPNG_ROUTE_DEFAULT))) {
+         route_unlock_node (rp);
+         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);
+
       route_unlock_node (rp);
+
     }
-  else
-    {
-      rinfo = ripng_info_new ();
-      ripng_aggregate_increment (rp, rinfo);
-    }
+
+  rinfo = ripng_info_new ();
 
   rinfo->type = type;
   rinfo->sub_type = sub_type;
   rinfo->ifindex = ifindex;
   rinfo->metric = 1;
-  rinfo->flags |= RIPNG_RTF_FIB;
-
   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",
+                 inet6_ntoa(p->prefix), p->prefixlen,
+                 ifindex2ifname(ifindex));
+    else
+      zlog_debug ("Redistribute new prefix %s/%d with nexthop %s on the interface %s",
+                 inet6_ntoa(p->prefix), p->prefixlen, inet6_ntoa(*nexthop),
+                 ifindex2ifname(ifindex));
+  }
+
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
 }
 
 /* Delete redistributed route to RIPng table. */
@@ -751,6 +1025,15 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
     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);
 
@@ -763,18 +1046,24 @@ ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p,
          && rinfo->sub_type == sub_type 
          && rinfo->ifindex == ifindex)
        {
-         rp->info = NULL;
-
+         /* 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);
-         RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+
+         /* Aggregate count decrement. */
+         ripng_aggregate_decrement (rp, rinfo);
+
+         rinfo->flags |= RIPNG_RTF_CHANGED;
          
-         ripng_info_free (rinfo);
+          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));
 
-         route_unlock_node (rp);
+         ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
        }
-
-      /* For unlock route_node_lookup (). */
-      route_unlock_node (rp);
     }
 }
 
@@ -785,25 +1074,41 @@ ripng_redistribute_withdraw (int type)
   struct route_node *rp;
   struct ripng_info *rinfo;
 
+  if (!ripng)
+    return;
+  
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
     if ((rinfo = rp->info) != NULL)
       {
-       if (rinfo->type == type)
+       if ((rinfo->type == type)
+           && (rinfo->sub_type != RIPNG_ROUTE_INTERFACE))
          {
-           rp->info = NULL;
-
+           /* 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);
-           RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
 
-           ripng_info_free (rinfo);
+           /* Aggregate count decrement. */
+           ripng_aggregate_decrement (rp, rinfo);
+
+           rinfo->flags |= RIPNG_RTF_CHANGED;
+
+           if (IS_RIPNG_DEBUG_EVENT) {
+             struct prefix_ipv6 *p = (struct prefix_ipv6 *) &rp->p;
+
+             zlog_debug ("Poisone %s/%d on the interface %s [withdraw]",
+                        inet6_ntoa(p->prefix), p->prefixlen,
+                        ifindex2ifname(rinfo->ifindex));
+           }
 
-           route_unlock_node (rp);
+           ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
          }
       }
 }
 
 /* RIP routing information. */
-void
+static void
 ripng_response_process (struct ripng_packet *packet, int size, 
                        struct sockaddr_in6 *from, struct interface *ifp,
                        int hoplimit)
@@ -817,7 +1122,8 @@ ripng_response_process (struct ripng_packet *packet, int size,
   if (ntohs (from->sin6_port) != RIPNG_PORT_DEFAULT)
     {
       zlog_warn ("RIPng packet comes from non RIPng port %d from %s",
-                ntohs (from->sin6_port), inet6_ntop (&from->sin6_addr));
+                ntohs (from->sin6_port), inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
@@ -827,7 +1133,8 @@ ripng_response_process (struct ripng_packet *packet, int size,
   if (! IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr))
    {
       zlog_warn ("RIPng packet comes from non link local address %s",
-                inet6_ntop (&from->sin6_addr));
+                inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
@@ -839,7 +1146,8 @@ ripng_response_process (struct ripng_packet *packet, int size,
   if (ripng_lladdr_check (ifp, &from->sin6_addr))
     {
       zlog_warn ("RIPng packet comes from my own link local address %s",
-                inet6_ntop (&from->sin6_addr));
+                inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
@@ -850,10 +1158,14 @@ ripng_response_process (struct ripng_packet *packet, int size,
   if (hoplimit >= 0 && hoplimit != 255)
     {
       zlog_warn ("RIPng packet comes with non 255 hop count %d from %s",
-                hoplimit, inet6_ntop (&from->sin6_addr));
+                hoplimit, inet6_ntoa (from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
+  /* Update RIPng peer. */
+  ripng_peer_update (from, packet->version);
+  
   /* Reset nexthop. */
   memset (&nexthop, 0, sizeof (struct ripng_nexthop));
   nexthop.flag = RIPNG_NEXTHOP_UNSPEC;
@@ -880,19 +1192,22 @@ ripng_response_process (struct ripng_packet *packet, int size,
       if (IN6_IS_ADDR_MULTICAST (&rte->addr))
        {
          zlog_warn ("Destination prefix is a multicast address %s/%d [%d]",
-                    inet6_ntop (&rte->addr), rte->prefixlen, rte->metric);
+                    inet6_ntoa (rte->addr), rte->prefixlen, rte->metric);
+         ripng_peer_bad_route (from);
          continue;
        }
       if (IN6_IS_ADDR_LINKLOCAL (&rte->addr))
        {
          zlog_warn ("Destination prefix is a link-local address %s/%d [%d]",
-                    inet6_ntop (&rte->addr), rte->prefixlen, rte->metric);
+                    inet6_ntoa (rte->addr), rte->prefixlen, rte->metric);
+         ripng_peer_bad_route (from);
          continue;
        }
       if (IN6_IS_ADDR_LOOPBACK (&rte->addr))
        {
          zlog_warn ("Destination prefix is a loopback address %s/%d [%d]",
-                    inet6_ntop (&rte->addr), rte->prefixlen, rte->metric);
+                    inet6_ntoa (rte->addr), rte->prefixlen, rte->metric);
+         ripng_peer_bad_route (from);
          continue;
        }
 
@@ -901,8 +1216,9 @@ ripng_response_process (struct ripng_packet *packet, int size,
       if (rte->prefixlen > 128)
        {
          zlog_warn ("Invalid prefix length %s/%d from %s%%%s",
-                    inet6_ntop (&rte->addr), rte->prefixlen,
-                    inet6_ntop (&from->sin6_addr), ifp->name);
+                    inet6_ntoa (rte->addr), rte->prefixlen,
+                    inet6_ntoa (from->sin6_addr), ifp->name);
+         ripng_peer_bad_route (from);
          continue;
        }
 
@@ -910,14 +1226,14 @@ ripng_response_process (struct ripng_packet *packet, int size,
       if (! (rte->metric >= 1 && rte->metric <= 16))
        {
          zlog_warn ("Invalid metric %d from %s%%%s", rte->metric,
-                    inet6_ntop (&from->sin6_addr), ifp->name);
+                    inet6_ntoa (from->sin6_addr), ifp->name);
+         ripng_peer_bad_route (from);
          continue;
        }
 
-      /* Metric calculation. */
-      rte->metric += ifp->metric;
-      if (rte->metric > RIPNG_METRIC_INFINITY)
-       rte->metric = RIPNG_METRIC_INFINITY;
+      /* Vincent: XXX Should we compute the direclty reachable nexthop
+       * for our RIPng network ?
+       **/
 
       /* Routing table updates. */
       ripng_route_process (rte, from, &nexthop, ifp);
@@ -925,7 +1241,7 @@ ripng_response_process (struct ripng_packet *packet, int size,
 }
 
 /* Response to request message. */
-void
+static void
 ripng_request_process (struct ripng_packet *packet,int size, 
                       struct sockaddr_in6 *from, struct interface *ifp)
 {
@@ -936,6 +1252,10 @@ ripng_request_process (struct ripng_packet *packet,int size,
   struct ripng_info *rinfo;
   struct ripng_interface *ri;
 
+  /* Does not reponse to the requests on the loopback interfaces */
+  if (if_is_loopback (ifp))
+    return;
+
   /* Check RIPng process is enabled on this interface. */
   ri = ifp->info;
   if (! ri->running)
@@ -945,6 +1265,9 @@ ripng_request_process (struct ripng_packet *packet,int size,
   if (ri->passive)
     return;
 
+  /* RIPng peer update. */
+  ripng_peer_update (from, packet->version);
+
   lim = ((caddr_t) packet) + size;
   rte = packet->rte;
 
@@ -965,7 +1288,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
       rte->metric == RIPNG_METRIC_INFINITY)
     {  
       /* All route with split horizon */
-      ripng_output_process (ifp, from, ripng_all_route, ripng_split_horizon);
+      ripng_output_process (ifp, from, ripng_all_route);
     }
   else
     {
@@ -1005,7 +1328,7 @@ ripng_request_process (struct ripng_packet *packet,int size,
 }
 
 /* First entry point of reading RIPng packet. */
-int
+static int
 ripng_read (struct thread *thread)
 {
   int len;
@@ -1034,7 +1357,7 @@ ripng_read (struct thread *thread)
                           &hoplimit);
   if (len < 0) 
     {
-      zlog_warn ("RIPng recvfrom failed: %s.", strerror (errno));
+      zlog_warn ("RIPng recvfrom failed: %s.", safe_strerror (errno));
       return len;
     }
 
@@ -1043,7 +1366,8 @@ ripng_read (struct thread *thread)
   if (((len - 4) % 20) != 0)
     {
       zlog_warn ("RIPng invalid packet size %d from %s", len,
-                inet6_ntop (&from.sin6_addr));
+                inet6_ntoa (from.sin6_addr));
+      ripng_peer_bad_packet (&from);
       return 0;
     }
 
@@ -1052,8 +1376,8 @@ ripng_read (struct thread *thread)
 
   /* RIPng packet received. */
   if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng packet received from %s port %d on %s",
-              inet6_ntop (&from.sin6_addr), ntohs (from.sin6_port), 
+    zlog_debug ("RIPng packet received from %s port %d on %s",
+              inet6_ntoa (from.sin6_addr), ntohs (from.sin6_port), 
               ifp ? ifp->name : "unknown");
 
   /* Logging before packet checking. */
@@ -1072,6 +1396,7 @@ ripng_read (struct thread *thread)
     {
       zlog_warn ("RIPng packet version %d doesn't fit to my version %d", 
                 packet->version, ripng->version);
+      ripng_peer_bad_packet (&from);
       return 0;
     }
 
@@ -1086,14 +1411,15 @@ ripng_read (struct thread *thread)
       break;
     default:
       zlog_warn ("Invalid RIPng command %d", packet->command);
+      ripng_peer_bad_packet (&from);
       break;
     }
   return 0;
 }
 
 /* Walk down the RIPng routing table then clear changed flag. */
-void
-ripng_clear_changed_flag ()
+static void
+ripng_clear_changed_flag (void)
 {
   struct route_node *rp;
   struct ripng_info *rinfo;
@@ -1106,10 +1432,10 @@ ripng_clear_changed_flag ()
 
 /* Regular update of RIPng route.  Send all routing formation to RIPng
    enabled interface. */
-int
+static int
 ripng_update (struct thread *t)
 {
-  listnode node;
+  struct listnode *node;
   struct interface *ifp;
   struct ripng_interface *ri;
 
@@ -1118,12 +1444,11 @@ ripng_update (struct thread *t)
 
   /* Logging update event. */
   if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng update timer expired!");
+    zlog_debug ("RIPng update timer expired!");
 
   /* Supply routes to each interface. */
-  for (node = listhead (iflist); node; nextnode (node))
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
     {
-      ifp = getdata (node);
       ri = ifp->info;
 
       if (if_is_loopback (ifp) || ! if_is_up (ifp))
@@ -1141,14 +1466,14 @@ ripng_update (struct thread *t)
       if (ri->ri_send == RIPNG_SEND_OFF)
        {
          if (IS_RIPNG_DEBUG_EVENT)
-           zlog (NULL, LOG_INFO
+           zlog (NULL, LOG_DEBUG
                  "[Event] RIPng send to if %d is suppressed by config",
                 ifp->ifindex);
          continue;
        }
 #endif /* RIPNG_ADVANCED */
 
-      ripng_output_process (ifp, NULL, ripng_all_route, ripng_split_horizon);
+      ripng_output_process (ifp, NULL, ripng_all_route);
     }
 
   /* Triggered updates may be suppressed if a regular update is due by
@@ -1167,7 +1492,7 @@ ripng_update (struct thread *t)
 }
 
 /* Triggered update interval timer. */
-int
+static int
 ripng_triggered_interval (struct thread *t)
 {
   ripng->t_triggered_interval = NULL;
@@ -1184,7 +1509,7 @@ ripng_triggered_interval (struct thread *t)
 int
 ripng_triggered_update (struct thread *t)
 {
-  listnode node;
+  struct listnode *node;
   struct interface *ifp;
   struct ripng_interface *ri;
   int interval;
@@ -1201,13 +1526,12 @@ ripng_triggered_update (struct thread *t)
 
   /* Logging triggered update. */
   if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng triggered update!");
+    zlog_debug ("RIPng triggered update!");
 
   /* Split Horizon processing is done when generating triggered
      updates as well as normal updates (see section 2.6). */
-  for (node = listhead (iflist); node; nextnode (node))
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
     {
-      ifp = getdata (node);
       ri = ifp->info;
 
       if (if_is_loopback (ifp) || ! if_is_up (ifp))
@@ -1221,8 +1545,7 @@ ripng_triggered_update (struct thread *t)
       if (ri->passive)
        continue;
 
-      ripng_output_process (ifp, NULL, ripng_changed_route,
-                           ripng_split_horizon);
+      ripng_output_process (ifp, NULL, ripng_changed_route);
     }
 
   /* Once all of the triggered updates have been generated, the route
@@ -1245,7 +1568,7 @@ ripng_triggered_update (struct thread *t)
    the routing table entry in the stream. */
 int
 ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p,
-                u_int16_t tag, u_char metric)
+                struct in6_addr *nexthop, u_int16_t tag, u_char metric)
 {
   /* RIPng packet header. */
   if (num == 0)
@@ -1256,9 +1579,15 @@ ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p,
     }
 
   /* Write routing table entry. */
-  stream_write (s, (caddr_t) &p->prefix, sizeof (struct in6_addr));
+  if (!nexthop)
+    stream_write (s, (u_char *) &p->prefix, sizeof (struct in6_addr));
+  else
+    stream_write (s, (u_char *) nexthop, sizeof (struct in6_addr));
   stream_putw (s, tag);
-  stream_putc (s, p->prefixlen);
+  if (p)
+    stream_putc (s, p->prefixlen);
+  else
+    stream_putc (s, 0);
   stream_putc (s, metric);
 
   return ++num;
@@ -1267,56 +1596,53 @@ ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p,
 /* Send RESPONSE message to specified destination. */
 void
 ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
-                     int route_type, int split_horizon)
+                     int route_type)
 {
   int ret;
-  struct stream *s;
   struct route_node *rp;
   struct ripng_info *rinfo;
   struct ripng_interface *ri;
   struct ripng_aggregate *aggregate;
   struct prefix_ipv6 *p;
-  int num;
-  int mtu;
-  int rtemax;
-  u_char metric;
-  u_char metric_set;
-
-  if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng update routes on interface %s", ifp->name);
-
-  /* Output stream get from ripng structre.  XXX this should be
-     interface structure. */
-  s = ripng->obuf;
-
-  /* Reset stream and RTE counter. */
-  stream_reset (s);
-  num = 0;
-
-  mtu = ifp->mtu;
-  if (mtu < 0)
-    mtu = IFMINMTU;
+  struct list * ripng_rte_list;
 
-  rtemax = (min (mtu, RIPNG_MAX_PACKET_SIZE) -
-           IPV6_HDRLEN - 
-           sizeof (struct udphdr) -
-           sizeof (struct ripng_packet) +
-           sizeof (struct rte)) / sizeof (struct rte);
+  if (IS_RIPNG_DEBUG_EVENT) {
+    if (to)
+      zlog_debug ("RIPng update routes to neighbor %s",
+                 inet6_ntoa(to->sin6_addr));
+    else
+      zlog_debug ("RIPng update routes on interface %s", ifp->name);
+  }
 
-#ifdef DEBUG
-  zlog_info ("DEBUG RIPng: ifmtu is %d", ifp->mtu);
-  zlog_info ("DEBUG RIPng: rtemax is %d", rtemax);
-#endif /* DEBUG */
-  
   /* Get RIPng interface. */
   ri = ifp->info;
-  
+  ripng_rte_list = ripng_rte_new();
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
     {
       if ((rinfo = rp->info) != NULL && rinfo->suppress == 0)
        {
+         /* If no route-map are applied, the RTE will be these following
+          * informations.
+          */
          p = (struct prefix_ipv6 *) &rp->p;
-         metric = rinfo->metric;
+         rinfo->metric_out = rinfo->metric;
+         rinfo->tag_out    = rinfo->tag;
+         memset(&rinfo->nexthop_out, 0, sizeof(rinfo->nexthop_out));
+         /* In order to avoid some local loops,
+          * if the RIPng route has a nexthop via this interface, keep the nexthop,
+          * otherwise set it to 0. The nexthop should not be propagated
+          * beyond the local broadcast/multicast area in order
+          * to avoid an IGP multi-level recursive look-up.
+          */
+         if (rinfo->ifindex == ifp->ifindex)
+           rinfo->nexthop_out = rinfo->nexthop;
+
+         /* Apply output filters. */
+         ret = ripng_outgoing_filter (p, ri);
+         if (ret < 0)
+           continue;
 
          /* Changed route only output. */
          if (route_type == ripng_changed_route &&
@@ -1324,164 +1650,130 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
            continue;
 
          /* Split horizon. */
-         if (split_horizon == ripng_split_horizon &&
-             rinfo->ifindex == ifp->ifindex)
-           continue;
-       
-         /* Apply output filters.*/
-         if (ri->list[RIPNG_FILTER_OUT])
-           {
-             if (access_list_apply (ri->list[RIPNG_FILTER_OUT], 
-                                    (struct prefix *) p) == FILTER_DENY)
-               {
-                 if (IS_RIPNG_DEBUG_PACKET)
-                   zlog_info ("RIPng %s/%d is filtered by distribute out",
-                              inet6_ntop (&p->prefix), p->prefixlen);
-                 continue;
-               }
-           }
-         if (ri->prefix[RIPNG_FILTER_OUT])
-           {
-             if (prefix_list_apply (ri->prefix[RIPNG_FILTER_OUT], 
-                                    (struct prefix *) p) == PREFIX_DENY)
-               {
-                 if (IS_RIPNG_DEBUG_PACKET)
-                   zlog_info ("RIPng %s/%d is filtered by prefix-list out",
-                              inet6_ntop (&p->prefix), p->prefixlen);
-                 continue;
-               }
-           }
+         if (ri->split_horizon == RIPNG_SPLIT_HORIZON)
+         {
+           /* We perform split horizon for RIPng routes. */
+           if ((rinfo->type == ZEBRA_ROUTE_RIPNG) &&
+               rinfo->ifindex == ifp->ifindex)
+             continue;
+         }
 
          /* Preparation for route-map. */
-         metric_set = 0;
+         rinfo->metric_set = 0;
+         /* nexthop_out,
+          * metric_out
+          * and tag_out are already initialized.
+          */
 
-         /* Route-map */
+         /* Interface route-map */
          if (ri->routemap[RIPNG_FILTER_OUT])
            {
              int ret;
-             struct ripng_info newinfo;
-
-             memset (&newinfo, 0, sizeof (struct ripng_info));
-             newinfo.metric = metric;
 
              ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT], 
                                     (struct prefix *) p, RMAP_RIPNG, 
-                                    &newinfo);
+                                    rinfo);
 
              if (ret == RMAP_DENYMATCH)
                {
                  if (IS_RIPNG_DEBUG_PACKET)
-                   zlog_info ("RIPng %s/%d is filtered by route-map out",
-                              inet6_ntop (&p->prefix), p->prefixlen);
-                 return;
+                   zlog_debug ("RIPng %s/%d is filtered by route-map out",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 continue;
                }
 
-             metric = newinfo.metric;
-             metric_set = newinfo.metric_set;
            }
 
-         /* When the interface route-map does not set metric */
-         if (! metric_set)
+         /* Redistribute route-map. */
+         if (ripng->route_map[rinfo->type].name)
            {
-             /* and the redistribute route-map is set. */
-             if (ripng->route_map[rinfo->type].name)
+             int ret;
+
+             ret = route_map_apply (ripng->route_map[rinfo->type].map,
+                                    (struct prefix *) p, RMAP_RIPNG,
+                                    rinfo);
+
+             if (ret == RMAP_DENYMATCH)
                {
-                 int ret;
-                 struct ripng_info newinfo;
-
-                 memset (&newinfo, 0, sizeof (struct ripng_info));
-                 newinfo.metric = metric;
-             
-                 ret = route_map_apply (ripng->route_map[rinfo->type].map,
-                                        (struct prefix *) p, RMAP_RIPNG,
-                                        &newinfo);
-
-                 if (ret == RMAP_DENYMATCH)
-                   {
-                     if (IS_RIPNG_DEBUG_PACKET)
-                       zlog_info ("RIPng %s/%d is filtered by route-map",
-                                  inet6_ntop (&p->prefix), p->prefixlen);
-                     continue;
-                   }
-
-                 metric = newinfo.metric;
-                 metric_set = newinfo.metric_set;
+                 if (IS_RIPNG_DEBUG_PACKET)
+                   zlog_debug ("RIPng %s/%d is filtered by route-map",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 continue;
                }
+           }
 
-             /* When the redistribute route-map does not set metric. */
-             if (! metric_set)
+         /* When the route-map does not set metric. */
+         if (! rinfo->metric_set)
+           {
+             /* If the redistribute metric is set. */
+             if (ripng->route_map[rinfo->type].metric_config
+                 && rinfo->metric != RIPNG_METRIC_INFINITY)
                {
-                 /* If the redistribute metric is set. */
-                 if (ripng->route_map[rinfo->type].metric_config
+                 rinfo->metric_out = ripng->route_map[rinfo->type].metric;
+               }
+             else
+               {
+                 /* If the route is not connected or localy generated
+                    one, use default-metric value */
+                 if (rinfo->type != ZEBRA_ROUTE_RIPNG
+                     && rinfo->type != ZEBRA_ROUTE_CONNECT
                      && rinfo->metric != RIPNG_METRIC_INFINITY)
-                   {
-                     metric = ripng->route_map[rinfo->type].metric;
-                   }
-                 else
-                   {
-                     /* If the route is not connected or localy generated
-                        one, use default-metric value */
-                     if (rinfo->type != ZEBRA_ROUTE_RIPNG
-                         && rinfo->type != ZEBRA_ROUTE_CONNECT
-                         && rinfo->metric != RIPNG_METRIC_INFINITY)
-                       metric = ripng->default_metric;
-                   }
+                   rinfo->metric_out = ripng->default_metric;
                }
            }
 
-         /* Write RTE to the stream. */
-         num = ripng_write_rte (num, s, p, rinfo->tag, metric);
-         if (num == rtemax)
-           {
-             ret = ripng_send_packet (STREAM_DATA (s), stream_get_endp (s),
-                                      to, ifp);
-
-             if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
-               ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s),
-                                  stream_get_endp(s), "SEND");
-             num = 0;
-             stream_reset (s);
-           }
+          /* Apply offset-list */
+         if (rinfo->metric_out != RIPNG_METRIC_INFINITY)
+            ripng_offset_list_apply_out (p, ifp, &rinfo->metric_out);
+
+          if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
+            rinfo->metric_out = RIPNG_METRIC_INFINITY;
+
+         /* Perform split-horizon with poisoned reverse 
+          * 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;
+         }
+
+         /* Add RTE to the list */
+         ripng_rte_add(ripng_rte_list, p, rinfo, NULL);
        }
+
+      /* Process the aggregated RTE entry */
       if ((aggregate = rp->aggregate) != NULL && 
          aggregate->count > 0 && 
          aggregate->suppress == 0)
        {
+         /* If no route-map are applied, the RTE will be these following
+          * informations.
+          */
          p = (struct prefix_ipv6 *) &rp->p;
-         metric = aggregate->metric;
+         aggregate->metric_set = 0;
+         aggregate->metric_out = aggregate->metric;
+         aggregate->tag_out    = aggregate->tag;
+         memset(&aggregate->nexthop_out, 0, sizeof(aggregate->nexthop_out));
 
          /* Apply output filters.*/
-         if (ri->list[RIPNG_FILTER_OUT])
-           {
-             if (access_list_apply (ri->list[RIPNG_FILTER_OUT], 
-                                    (struct prefix *) p) == FILTER_DENY)
-               {
-                 if (IS_RIPNG_DEBUG_PACKET)
-                   zlog_info ("RIPng %s/%d is filtered by distribute out",
-                              inet6_ntop (&p->prefix), p->prefixlen);
-                 continue;
-               }
-           }
-         if (ri->prefix[RIPNG_FILTER_OUT])
-           {
-             if (prefix_list_apply (ri->prefix[RIPNG_FILTER_OUT], 
-                                    (struct prefix *) p) == PREFIX_DENY)
-               {
-                 if (IS_RIPNG_DEBUG_PACKET)
-                   zlog_info ("RIPng %s/%d is filtered by prefix-list out",
-                              inet6_ntop (&p->prefix), p->prefixlen);
-                 continue;
-               }
-           }
+         ret = ripng_outgoing_filter (p, ri);
+         if (ret < 0)
+           continue;
 
-         /* Route-map */
+         /* Interface route-map */
          if (ri->routemap[RIPNG_FILTER_OUT])
            {
              int ret;
              struct ripng_info newinfo;
 
+             /* let's cast the aggregate structure to ripng_info */
              memset (&newinfo, 0, sizeof (struct ripng_info));
-             newinfo.metric = metric;
+             /* the nexthop is :: */
+             newinfo.metric = aggregate->metric;
+             newinfo.metric_out = aggregate->metric_out;
+             newinfo.tag = aggregate->tag;
+             newinfo.tag_out = aggregate->tag_out;
 
              ret = route_map_apply (ri->routemap[RIPNG_FILTER_OUT], 
                                     (struct prefix *) p, RMAP_RIPNG, 
@@ -1490,59 +1782,53 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to,
              if (ret == RMAP_DENYMATCH)
                {
                  if (IS_RIPNG_DEBUG_PACKET)
-                   zlog_info ("RIPng %s/%d is filtered by route-map out",
-                              inet6_ntop (&p->prefix), p->prefixlen);
-                 return;
+                   zlog_debug ("RIPng %s/%d is filtered by route-map out",
+                              inet6_ntoa (p->prefix), p->prefixlen);
+                 continue;
                }
 
-             metric = newinfo.metric;
+             aggregate->metric_out = newinfo.metric_out;
+             aggregate->tag_out = newinfo.tag_out;
+             if (IN6_IS_ADDR_LINKLOCAL(&newinfo.nexthop_out))
+               aggregate->nexthop_out = newinfo.nexthop_out;
            }
 
+         /* There is no redistribute routemap for the aggregated RTE */
+
          /* Changed route only output. */
+         /* XXX, vincent, in order to increase time convergence,
+          * it should be announced if a child has changed.
+          */
          if (route_type == ripng_changed_route)
            continue;
 
-         /* Write RTE to the stream. */
-         num = ripng_write_rte (num, s, p, aggregate->tag, metric);
-         if (num == rtemax)
-           {
-             ret = ripng_send_packet (STREAM_DATA (s), stream_get_endp (s),
-                                      to, ifp);
-
-             if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
-               ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s),
-                                  stream_get_endp(s), "SEND");
-             num = 0;
-             stream_reset (s);
-           }
+         /* Apply offset-list */
+         if (aggregate->metric_out != RIPNG_METRIC_INFINITY)
+           ripng_offset_list_apply_out (p, ifp, &aggregate->metric_out);
+
+         if (aggregate->metric_out > RIPNG_METRIC_INFINITY)
+           aggregate->metric_out = RIPNG_METRIC_INFINITY;
+
+         /* Add RTE to the list */
+         ripng_rte_add(ripng_rte_list, p, NULL, aggregate);
        }
 
     }
-  
-  /* If unwritten RTE exist, flush it. */
-  if (num != 0)
-    {
-      ret = ripng_send_packet (STREAM_DATA (s), stream_get_endp (s),
-                              to, ifp);
 
-      if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
-       ripng_packet_dump ((struct ripng_packet *)STREAM_DATA (s),
-                          stream_get_endp (s), "SEND");
-      num = 0;
-      stream_reset (s);
-    }
+  /* Flush the list */
+  ripng_rte_send(ripng_rte_list, ifp, to);
+  ripng_rte_free(ripng_rte_list);
 }
 
 /* Create new RIPng instance and set it to global variable. */
-int
-ripng_create ()
+static int
+ripng_create (void)
 {
   /* ripng should be NULL. */
   assert (ripng == NULL);
 
   /* Allocaste RIPng instance. */
-  ripng = XMALLOC (0, sizeof (struct ripng));
-  memset (ripng, 0, sizeof (struct ripng));
+  ripng = XCALLOC (MTYPE_RIPNG, sizeof (struct ripng));
 
   /* Default version and timer values. */
   ripng->version = RIPNG_V1;
@@ -1572,15 +1858,23 @@ ripng_create ()
   return 0;
 }
 
-/* Sned RIPng request to the interface. */
+/* Send RIPng request to the interface. */
 int
 ripng_request (struct interface *ifp)
 {
   struct rte *rte;
   struct ripng_packet ripng_packet;
 
+  /* In default ripd doesn't send RIP_REQUEST to the loopback interface. */
+  if (if_is_loopback(ifp))
+    return 0;
+
+  /* If interface is down, don't send RIP packet. */
+  if (! if_is_up (ifp))
+    return 0;
+
   if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng send request to %s", ifp->name);
+    zlog_debug ("RIPng send request to %s", ifp->name);
 
   memset (&ripng_packet, 0, sizeof (ripng_packet));
   ripng_packet.command = RIPNG_REQUEST;
@@ -1592,24 +1886,8 @@ ripng_request (struct interface *ifp)
                            NULL, ifp);
 }
 
-/* Clean up installed RIPng routes. */
-void
-ripng_terminate ()
-{
-  struct route_node *rp;
-  struct ripng_info *rinfo;
-
-  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->ifindex);
-      }
-}
 \f
-int
+static int
 ripng_update_jitter (int time)
 {
   return ((rand () % (time + 1)) - (time / 2));
@@ -1618,7 +1896,6 @@ ripng_update_jitter (int time)
 void
 ripng_event (enum ripng_event event, int sock)
 {
-  int ripng_request_all (struct thread *);
   int jitter = 0;
 
   switch (event)
@@ -1652,68 +1929,72 @@ ripng_event (enum ripng_event event, int sock)
     }
 }
 \f
-/* Each route type's strings and default preference. */
-struct
-{  
-  int key;
-  char *str;
-  char *str_long;
-  int distance;
-} route_info[] =
-{
-  { ZEBRA_ROUTE_SYSTEM,  "X", "system",    10},
-  { ZEBRA_ROUTE_KERNEL,  "K", "kernel",    20},
-  { ZEBRA_ROUTE_CONNECT, "C", "connected", 30},
-  { ZEBRA_ROUTE_STATIC,  "S", "static",    40},
-  { ZEBRA_ROUTE_RIP,     "R", "rip",       50},
-  { ZEBRA_ROUTE_RIPNG,   "R", "ripng",     50},
-  { ZEBRA_ROUTE_OSPF,    "O", "ospf",      60},
-  { ZEBRA_ROUTE_OSPF6,   "O", "ospf6",     60},
-  { ZEBRA_ROUTE_BGP,     "B", "bgp",       70},
-};
-
-/* For messages. */
-struct message ripng_route_info[] =
-{
-  { RIPNG_ROUTE_RTE,       " "},
-  { RIPNG_ROUTE_STATIC,    "S"},
-  { RIPNG_ROUTE_AGGREGATE, "a"}
-};
 
 /* Print out routes update time. */
 static void
 ripng_vty_out_uptime (struct vty *vty, struct ripng_info *rinfo)
 {
-  struct timeval timer_now;
   time_t clock;
   struct tm *tm;
 #define TIME_BUF 25
   char timebuf [TIME_BUF];
   struct thread *thread;
   
-  gettimeofday (&timer_now, NULL);
-
   if ((thread = rinfo->t_timeout) != NULL)
     {
-      clock = thread->u.sands.tv_sec - timer_now.tv_sec;
+      clock = thread_timer_remain_second (thread);
       tm = gmtime (&clock);
       strftime (timebuf, TIME_BUF, "%M:%S", tm);
       vty_out (vty, "%5s", timebuf);
     }
   else if ((thread = rinfo->t_garbage_collect) != NULL)
     {
-      clock = thread->u.sands.tv_sec - timer_now.tv_sec;
+      clock = thread_timer_remain_second (thread);
       tm = gmtime (&clock);
       strftime (timebuf, TIME_BUF, "%M:%S", tm);
       vty_out (vty, "%5s", timebuf);
     }
 }
 
+static char *
+ripng_route_subtype_print (struct ripng_info *rinfo)
+{
+  static char str[3];
+  memset(str, 0, 3);
+
+  if (rinfo->suppress)
+    strcat(str, "S");
+
+  switch (rinfo->sub_type)
+    {
+       case RIPNG_ROUTE_RTE:
+         strcat(str, "n");
+         break;
+       case RIPNG_ROUTE_STATIC:
+         strcat(str, "s");
+         break;
+       case RIPNG_ROUTE_DEFAULT:
+         strcat(str, "d");
+         break;
+       case RIPNG_ROUTE_REDISTRIBUTE:
+         strcat(str, "r");
+         break;
+       case RIPNG_ROUTE_INTERFACE:
+         strcat(str, "i");
+         break;
+       default:
+         strcat(str, "?");
+         break;
+    }
+  return str;
+}
+
 DEFUN (show_ipv6_ripng,
        show_ipv6_ripng_cmd,
        "show ipv6 ripng",
        SHOW_STR
-       IP_STR
+       IPV6_STR
        "Show RIPng routes\n")
 {
   struct route_node *rp;
@@ -1722,10 +2003,16 @@ DEFUN (show_ipv6_ripng,
   struct prefix_ipv6 *p;
   int len;
 
+  if (! ripng)
+    return CMD_SUCCESS;
+
   /* Header of display. */ 
-  vty_out (vty, "%sCodes: R - RIPng%s%s"
-          "   Network                           "
-          "Next Hop                  If Met Tag Time%s", VTY_NEWLINE,
+  vty_out (vty, "Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP%s"
+          "Sub-codes:%s"
+          "      (n) - normal, (s) - static, (d) - default, (r) - redistribute,%s"
+          "      (i) - interface, (a/S) - aggregated/Suppressed%s%s"
+          "   Network      Next Hop                      Via     Metric Tag Time%s",
+          VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
           VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
   
   for (rp = route_top (ripng->table); rp; rp = route_next (rp))
@@ -1735,20 +2022,18 @@ DEFUN (show_ipv6_ripng,
          p = (struct prefix_ipv6 *) &rp->p;
 
 #ifdef DEBUG
-         len = vty_out (vty, "Ra %d/%d %s/%d ",
+         len = vty_out (vty, "R(a) %d/%d %s/%d ",
                         aggregate->count, aggregate->suppress,
-                        inet6_ntop (&p->prefix), p->prefixlen);
+                        inet6_ntoa (p->prefix), p->prefixlen);
 #else
-         len = vty_out (vty, "Ra %s/%d ", 
-                        inet6_ntop (&p->prefix), p->prefixlen);
+         len = vty_out (vty, "R(a) %s/%d ", 
+                        inet6_ntoa (p->prefix), p->prefixlen);
 #endif /* DEBUG */
+         vty_out (vty, "%s", VTY_NEWLINE);
+         vty_out (vty, "%*s", 18, " ");
 
-         len = 37 - len;
-         if (len > 0)
-           vty_out (vty, "%*s", len, " ");
-
-         vty_out (vty, "%*s", 26, " ");
-         vty_out (vty, "%4d %3d%s", aggregate->metric,
+         vty_out (vty, "%*s", 28, " ");
+         vty_out (vty, "self      %2d  %3d%s", aggregate->metric,
                   aggregate->tag,
                   VTY_NEWLINE);
        }
@@ -1758,32 +2043,54 @@ DEFUN (show_ipv6_ripng,
          p = (struct prefix_ipv6 *) &rp->p;
 
 #ifdef DEBUG
-         len = vty_out (vty, "%s%s 0/%d %s/%d ",
-                        route_info[rinfo->type].str,
-                        rinfo->suppress ? "s" : " ",
+         len = vty_out (vty, "%c(%s) 0/%d %s/%d ",
+                        zebra_route_char(rinfo->type),
+                        ripng_route_subtype_print(rinfo),
                         rinfo->suppress,
-                        inet6_ntop (&p->prefix), p->prefixlen);
+                        inet6_ntoa (p->prefix), p->prefixlen);
 #else
-         len = vty_out (vty, "%s%s %s/%d ",
-                        route_info[rinfo->type].str,
-                        rinfo->suppress ? "s" : " ",
-                        inet6_ntop (&p->prefix), p->prefixlen);
+         len = vty_out (vty, "%c(%s) %s/%d ",
+                        zebra_route_char(rinfo->type),
+                        ripng_route_subtype_print(rinfo),
+                        inet6_ntoa (p->prefix), p->prefixlen);
 #endif /* DEBUG */
-         len = 37 - len;
+         vty_out (vty, "%s", VTY_NEWLINE);
+         vty_out (vty, "%*s", 18, " ");
+         len = vty_out (vty, "%s", inet6_ntoa (rinfo->nexthop));
+
+         len = 28 - len;
          if (len > 0)
-           vty_out (vty, "%*s", len, " ");
+           len = vty_out (vty, "%*s", len, " ");
 
-         len = vty_out (vty, "%s", inet6_ntop (&rinfo->nexthop));
+         /* from */
+         if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && 
+           (rinfo->sub_type == RIPNG_ROUTE_RTE))
+         {
+           len = vty_out (vty, "%s", ifindex2ifname(rinfo->ifindex));
+         } else if (rinfo->metric == RIPNG_METRIC_INFINITY)
+         {
+           len = vty_out (vty, "kill");
+         } else
+           len = vty_out (vty, "self");
 
-         len = 26 - len;
+         len = 9 - len;
          if (len > 0)
            vty_out (vty, "%*s", len, " ");
 
-         vty_out (vty, "%2d %2d %3d ",
-                  rinfo->ifindex, rinfo->metric, rinfo->tag);
+         vty_out (vty, " %2d  %3d  ",
+                  rinfo->metric, rinfo->tag);
 
-         if (rinfo->sub_type == RIPNG_ROUTE_RTE)
+         /* time */
+         if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && 
+           (rinfo->sub_type == RIPNG_ROUTE_RTE))
+         {
+           /* RTE from remote RIP routers */
            ripng_vty_out_uptime (vty, rinfo);
+         } else if (rinfo->metric == RIPNG_METRIC_INFINITY)
+         {
+           /* poisonous reversed routes (gc) */
+           ripng_vty_out_uptime (vty, rinfo);
+         }
 
          vty_out (vty, "%s", VTY_NEWLINE);
        }
@@ -1792,6 +2099,74 @@ DEFUN (show_ipv6_ripng,
   return CMD_SUCCESS;
 }
 
+DEFUN (show_ipv6_ripng_status,
+       show_ipv6_ripng_status_cmd,
+       "show ipv6 ripng status",
+       SHOW_STR
+       IPV6_STR
+       "Show RIPng routes\n"
+       "IPv6 routing protocol process parameters and statistics\n")
+{
+  struct listnode *node;
+  struct interface *ifp;
+
+  if (! ripng)
+    return CMD_SUCCESS;
+
+  vty_out (vty, "Routing Protocol is \"RIPng\"%s", VTY_NEWLINE);
+  vty_out (vty, "  Sending updates every %ld seconds with +/-50%%,",
+           ripng->update_time);
+  vty_out (vty, " next due in %lu seconds%s",
+           thread_timer_remain_second (ripng->t_update),
+           VTY_NEWLINE);
+  vty_out (vty, "  Timeout after %ld seconds,", ripng->timeout_time);
+  vty_out (vty, " garbage collect after %ld seconds%s", ripng->garbage_time,
+           VTY_NEWLINE);
+
+  /* Filtering status show. */
+  config_show_distribute (vty);
+
+  /* Default metric information. */
+  vty_out (vty, "  Default redistribution metric is %d%s",
+           ripng->default_metric, VTY_NEWLINE);
+
+  /* Redistribute information. */
+  vty_out (vty, "  Redistributing:");
+  ripng_redistribute_write (vty, 0);
+  vty_out (vty, "%s", VTY_NEWLINE);
+
+  vty_out (vty, "  Default version control: send version %d,", ripng->version);
+  vty_out (vty, " receive version %d %s", ripng->version,
+           VTY_NEWLINE);
+
+  vty_out (vty, "    Interface        Send  Recv%s", VTY_NEWLINE);
+
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    {
+      struct ripng_interface *ri;
+      
+      ri = ifp->info;
+
+      if (ri->enable_network || ri->enable_interface)
+       {
+
+         vty_out (vty, "    %-17s%-3d   %-3d%s", ifp->name,
+                  ripng->version,
+                  ripng->version,
+                  VTY_NEWLINE);
+       }
+    }
+
+  vty_out (vty, "  Routing for Networks:%s", VTY_NEWLINE);
+  ripng_network_write (vty, 0);
+
+  vty_out (vty, "  Routing Information Sources:%s", VTY_NEWLINE);
+  vty_out (vty, "    Gateway          BadPackets BadRoutes  Distance Last Update%s", VTY_NEWLINE);
+  ripng_peer_display (vty);
+
+  return CMD_SUCCESS;  
+}
+
 DEFUN (router_ripng,
        router_ripng_cmd,
        "router ripng",
@@ -1817,6 +2192,18 @@ DEFUN (router_ripng,
   return CMD_SUCCESS;
 }
 
+DEFUN (no_router_ripng,
+       no_router_ripng_cmd,
+       "no router ripng",
+       NO_STR
+       "Enable a routing process\n"
+       "Make RIPng instance command\n")
+{
+  if(ripng)
+    ripng_clean();
+  return CMD_SUCCESS;
+}
+
 DEFUN (ripng_route,
        ripng_route_cmd,
        "route IPV6ADDR",
@@ -1844,7 +2231,7 @@ DEFUN (ripng_route,
     }
   rp->info = (void *)1;
 
-  ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0);
+  ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL);
 
   return CMD_SUCCESS;
 }
@@ -2098,28 +2485,10 @@ DEFUN (ripng_timers,
   unsigned long update;
   unsigned long timeout;
   unsigned long garbage;
-  char *endptr = NULL;
 
-  update = strtoul (argv[0], &endptr, 10);
-  if (update == ULONG_MAX || *endptr != '\0')
-    {
-      vty_out (vty, "update timer value error%s", VTY_NEWLINE);
-      return CMD_WARNING;
-    }
-  
-  timeout = strtoul (argv[1], &endptr, 10);
-  if (timeout == ULONG_MAX || *endptr != '\0')
-    {
-      vty_out (vty, "timeout timer value error%s", VTY_NEWLINE);
-      return CMD_WARNING;
-    }
-  
-  garbage = strtoul (argv[2], &endptr, 10);
-  if (garbage == ULONG_MAX || *endptr != '\0')
-    {
-      vty_out (vty, "garbage timer value error%s", VTY_NEWLINE);
-      return CMD_WARNING;
-    }
+  VTY_GET_INTEGER_RANGE("update timer", update, argv[0], 0, 65535);
+  VTY_GET_INTEGER_RANGE("timeout timer", timeout, argv[1], 0, 65535);
+  VTY_GET_INTEGER_RANGE("garbage timer", garbage, argv[2], 0, 65535);
 
   /* Set each timer value. */
   ripng->update_time = update;
@@ -2150,11 +2519,20 @@ DEFUN (no_ripng_timers,
   return CMD_SUCCESS;
 }
 
+ALIAS (no_ripng_timers,
+       no_ripng_timers_val_cmd,
+       "no timers basic <0-65535> <0-65535> <0-65535>",
+       NO_STR
+       "RIPng timers setup\n"
+       "Basic timer\n"
+       "Routing table update timer value in second. Default is 30.\n"
+       "Routing information timeout timer. Default is 180.\n"
+       "Garbage collection timer. Default is 120.\n")
 
 DEFUN (show_ipv6_protocols, show_ipv6_protocols_cmd,
        "show ipv6 protocols",
        SHOW_STR
-       IP_STR
+       IPV6_STR
        "Routing protocol information")
 {
   if (! ripng)
@@ -2186,10 +2564,12 @@ DEFUN (ripng_default_information_originate,
 {
   struct prefix_ipv6 p;
 
-  ripng->default_information = 1;
+  if (! ripng ->default_information) {
+    ripng->default_information = 1;
 
-  str2prefix_ipv6 ("::/0", &p);
-  ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0);
+    str2prefix_ipv6 ("::/0", &p);
+    ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL);
+  }
 
   return CMD_SUCCESS;
 }
@@ -2203,20 +2583,22 @@ DEFUN (no_ripng_default_information_originate,
 {
   struct prefix_ipv6 p;
 
-  ripng->default_information = 0;
+  if (ripng->default_information) {
+    ripng->default_information = 0;
 
-  str2prefix_ipv6 ("::/0", &p);
-  ripng_redistribute_delete (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0);
+    str2prefix_ipv6 ("::/0", &p);
+    ripng_redistribute_delete (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0);
+  }
 
   return CMD_SUCCESS;
 }
 
 /* RIPng configuration write function. */
-int
+static int
 ripng_config_write (struct vty *vty)
 {
-  int ripng_network_write (struct vty *);
-  void ripng_redistribute_write (struct vty *);
+  int ripng_network_write (struct vty *, int);
+  void ripng_redistribute_write (struct vty *, int);
   int write = 0;
   struct route_node *rp;
 
@@ -2229,20 +2611,23 @@ ripng_config_write (struct vty *vty)
       if (ripng->default_information)
        vty_out (vty, " default-information originate%s", VTY_NEWLINE);
 
-      ripng_network_write (vty);
+      ripng_network_write (vty, 1);
 
       /* RIPng default metric configuration */
       if (ripng->default_metric != RIPNG_DEFAULT_METRIC_DEFAULT)
         vty_out (vty, " default-metric %d%s",
                 ripng->default_metric, VTY_NEWLINE);
 
-      ripng_redistribute_write (vty);
+      ripng_redistribute_write (vty, 1);
+
+      /* RIP offset-list configuration. */
+      config_write_ripng_offset_list (vty);
       
       /* RIPng aggregate routes. */
       for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp))
        if (rp->info != NULL)
          vty_out (vty, " aggregate-address %s/%d%s", 
-                  inet6_ntop (&rp->p.u.prefix6),
+                  inet6_ntoa (rp->p.u.prefix6),
                   rp->p.prefixlen, 
 
                   VTY_NEWLINE);
@@ -2250,7 +2635,7 @@ ripng_config_write (struct vty *vty)
       /* RIPng static routes. */
       for (rp = route_top (ripng->route); rp; rp = route_next (rp))
        if (rp->info != NULL)
-         vty_out (vty, " route %s/%d%s", inet6_ntop (&rp->p.u.prefix6),
+         vty_out (vty, " route %s/%d%s", inet6_ntoa (rp->p.u.prefix6),
                   rp->p.prefixlen,
                   VTY_NEWLINE);
 
@@ -2287,14 +2672,14 @@ ripng_config_write (struct vty *vty)
 }
 
 /* RIPng node structure. */
-struct cmd_node cmd_ripng_node =
+static struct cmd_node cmd_ripng_node =
 {
   RIPNG_NODE,
   "%s(config-router)# ",
   1,
 };
 
-void
+static void
 ripng_distribute_update (struct distribute *dist)
 {
   struct interface *ifp;
@@ -2355,6 +2740,7 @@ ripng_distribute_update (struct distribute *dist)
   else
     ri->prefix[RIPNG_FILTER_OUT] = NULL;
 }
+
 void
 ripng_distribute_update_interface (struct interface *ifp)
 {
@@ -2366,20 +2752,120 @@ ripng_distribute_update_interface (struct interface *ifp)
 }
 
 /* Update all interface's distribute list. */
-void
-ripng_distribute_update_all ()
+static void
+ripng_distribute_update_all (struct prefix_list *notused)
 {
   struct interface *ifp;
-  listnode node;
+  struct listnode *node;
 
-  for (node = listhead (iflist); node; nextnode (node))
-    {
-      ifp = getdata (node);
-      ripng_distribute_update_interface (ifp);
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ripng_distribute_update_interface (ifp);
+}
+
+static void
+ripng_distribute_update_all_wrapper (struct access_list *notused)
+{
+  ripng_distribute_update_all(NULL);
+}
+\f
+/* delete all the added ripng routes. */
+void
+ripng_clean()
+{
+  int i;
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+
+  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);
+
+        RIPNG_TIMER_OFF (rinfo->t_timeout);
+        RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
+
+        rp->info = NULL;
+        route_unlock_node (rp);
+
+        ripng_info_free(rinfo);
+      }
+    }
+
+    /* Cancel the RIPng timers */
+    RIPNG_TIMER_OFF (ripng->t_update);
+    RIPNG_TIMER_OFF (ripng->t_triggered_update);
+    RIPNG_TIMER_OFF (ripng->t_triggered_interval);
+
+    /* Cancel the read thread */
+    if (ripng->t_read) {
+      thread_cancel (ripng->t_read);
+      ripng->t_read = NULL;
+    }
+
+    /* Close the RIPng socket */
+    if (ripng->sock >= 0) {
+      close(ripng->sock);
+      ripng->sock = -1;
     }
+
+    /* Static RIPng route configuration. */
+    for (rp = route_top (ripng->route); rp; rp = route_next (rp))
+      if (rp->info) {
+        rp->info = NULL;
+        route_unlock_node (rp);
+    }
+
+    /* RIPng aggregated prefixes */
+    for (rp = route_top (ripng->aggregate); rp; rp = route_next (rp))
+      if (rp->info) {
+          rp->info = NULL;
+          route_unlock_node (rp);
+    }
+
+    for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+      if (ripng->route_map[i].name)
+        free (ripng->route_map[i].name);
+
+    XFREE (MTYPE_ROUTE_TABLE, ripng->table);
+    XFREE (MTYPE_ROUTE_TABLE, ripng->route);
+    XFREE (MTYPE_ROUTE_TABLE, ripng->aggregate);
+
+    XFREE (MTYPE_RIPNG, ripng);
+    ripng = NULL;
+  } /* if (ripng) */
+
+  ripng_clean_network();
+  ripng_passive_interface_clean ();
+  ripng_offset_clean ();
+  ripng_interface_clean ();
+  ripng_redistribute_clean ();
 }
 
+/* Reset all values to the default settings. */
 void
+ripng_reset ()
+{
+  /* Call ripd related reset functions. */
+  ripng_debug_reset ();
+  ripng_route_map_reset ();
+
+  /* Call library reset functions. */
+  vty_reset ();
+  access_list_reset ();
+  prefix_list_reset ();
+
+  distribute_list_reset ();
+
+  ripng_interface_reset ();
+
+  ripng_zclient_reset ();
+}
+
+static void
 ripng_if_rmap_update (struct if_rmap *if_rmap)
 {
   struct interface *ifp;
@@ -2425,7 +2911,7 @@ ripng_if_rmap_update_interface (struct interface *ifp)
     ripng_if_rmap_update (if_rmap);
 }
 
-void
+static void
 ripng_routemap_update_redistribute (void)
 {
   int i;
@@ -2441,17 +2927,14 @@ ripng_routemap_update_redistribute (void)
     }
 }
 
-void
-ripng_routemap_update ()
+static void
+ripng_routemap_update (const char *unused)
 {
   struct interface *ifp;
-  listnode node;
+  struct listnode *node;
 
-  for (node = listhead (iflist); node; nextnode (node))
-    {
-      ifp = getdata (node);
-      ripng_if_rmap_update_interface (ifp);
-    }
+  for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp))
+    ripng_if_rmap_update_interface (ifp);
 
   ripng_routemap_update_redistribute ();
 }
@@ -2468,10 +2951,13 @@ ripng_init ()
 
   /* Install ripng commands. */
   install_element (VIEW_NODE, &show_ipv6_ripng_cmd);
+  install_element (VIEW_NODE, &show_ipv6_ripng_status_cmd);
 
   install_element (ENABLE_NODE, &show_ipv6_ripng_cmd);
+  install_element (ENABLE_NODE, &show_ipv6_ripng_status_cmd);
 
   install_element (CONFIG_NODE, &router_ripng_cmd);
+  install_element (CONFIG_NODE, &no_router_ripng_cmd);
 
   install_default (RIPNG_NODE);
   install_element (RIPNG_NODE, &ripng_route_cmd);
@@ -2485,6 +2971,7 @@ ripng_init ()
 
   install_element (RIPNG_NODE, &ripng_timers_cmd);
   install_element (RIPNG_NODE, &no_ripng_timers_cmd);
+  install_element (RIPNG_NODE, &no_ripng_timers_val_cmd);
 #if 0
   install_element (RIPNG_NODE, &ripng_update_timer_cmd);
   install_element (RIPNG_NODE, &no_ripng_update_timer_cmd);
@@ -2502,8 +2989,8 @@ ripng_init ()
 
   /* Access list install. */
   access_list_init ();
-  access_list_add_hook (ripng_distribute_update_all);
-  access_list_delete_hook (ripng_distribute_update_all);
+  access_list_add_hook (ripng_distribute_update_all_wrapper);
+  access_list_delete_hook (ripng_distribute_update_all_wrapper);
 
   /* Prefix list initialize.*/
   prefix_list_init ();
@@ -2517,10 +3004,12 @@ ripng_init ()
 
   /* Route-map for interface. */
   ripng_route_map_init ();
+  ripng_offset_init ();
+
   route_map_add_hook (ripng_routemap_update);
   route_map_delete_hook (ripng_routemap_update);
 
-  if_rmap_init ();
+  if_rmap_init (RIPNG_NODE);
   if_rmap_hook_add (ripng_if_rmap_update);
   if_rmap_hook_delete (ripng_if_rmap_update);
 }