Ripngd part of 6Wind patch.
diff --git a/lib/memory.c b/lib/memory.c
index bf142dc..9383311 100644
--- a/lib/memory.c
+++ b/lib/memory.c
@@ -255,6 +255,7 @@
   { MTYPE_ROUTE_MAP_INDEX,    "Route map index " },
   { MTYPE_ROUTE_MAP_RULE,     "Route map rule  " },
   { MTYPE_ROUTE_MAP_RULE_STR, "Route map rule str" },
+  { MTYPE_ROUTE_MAP_COMPILED, "Route map compiled" },
   { MTYPE_DESC,               "Command desc    " },
   { MTYPE_BUFFER,             "Buffer          " },
   { MTYPE_BUFFER_DATA,        "Buffer data     " },
@@ -323,6 +324,17 @@
   { -1, NULL }
 };
 
+struct memory_list memory_list_ripng[] =
+{
+  { MTYPE_RIPNG,              "RIPng structure " },
+  { MTYPE_RIPNG_ROUTE,        "RIPng route info" },
+  { MTYPE_RIPNG_AGGREGATE,    "RIPng aggregate " },
+  { MTYPE_RIPNG_PEER,         "RIPng peer      " },
+  { MTYPE_RIPNG_OFFSET_LIST,  "RIPng offset lst" },
+  { MTYPE_RIPNG_RTE_DATA,     "RIPng rte data  " },
+  { -1, NULL }
+};
+
 struct memory_list memory_list_ospf[] =
 {
   { MTYPE_OSPF_TOP,           "OSPF top        " },
@@ -402,6 +414,8 @@
   show_memory_vty (vty, memory_list_separator);
   show_memory_vty (vty, memory_list_rip);
   show_memory_vty (vty, memory_list_separator);
+  show_memory_vty (vty, memory_list_ripng);
+  show_memory_vty (vty, memory_list_separator);
   show_memory_vty (vty, memory_list_ospf);
   show_memory_vty (vty, memory_list_separator);
   show_memory_vty (vty, memory_list_ospf6);
@@ -439,6 +453,17 @@
   return CMD_SUCCESS;
 }
 
+DEFUN (show_memory_ripng,
+       show_memory_ripng_cmd,
+       "show memory ripng",
+       SHOW_STR
+       "Memory statistics\n"
+       "RIPng memory\n")
+{
+  show_memory_vty (vty, memory_list_ripng);
+  return CMD_SUCCESS;
+}
+
 DEFUN (show_memory_bgp,
        show_memory_bgp_cmd,
        "show memory bgp",
@@ -479,6 +504,7 @@
   install_element (VIEW_NODE, &show_memory_all_cmd);
   install_element (VIEW_NODE, &show_memory_lib_cmd);
   install_element (VIEW_NODE, &show_memory_rip_cmd);
+  install_element (VIEW_NODE, &show_memory_ripng_cmd);
   install_element (VIEW_NODE, &show_memory_bgp_cmd);
   install_element (VIEW_NODE, &show_memory_ospf_cmd);
   install_element (VIEW_NODE, &show_memory_ospf6_cmd);
@@ -487,6 +513,7 @@
   install_element (ENABLE_NODE, &show_memory_all_cmd);
   install_element (ENABLE_NODE, &show_memory_lib_cmd);
   install_element (ENABLE_NODE, &show_memory_rip_cmd);
+  install_element (ENABLE_NODE, &show_memory_ripng_cmd);
   install_element (ENABLE_NODE, &show_memory_bgp_cmd);
   install_element (ENABLE_NODE, &show_memory_ospf_cmd);
   install_element (ENABLE_NODE, &show_memory_ospf6_cmd);
diff --git a/lib/memory.h b/lib/memory.h
index 52e3bc1..a38cda3 100644
--- a/lib/memory.h
+++ b/lib/memory.h
@@ -58,8 +58,6 @@
   MTYPE_HASH,
   MTYPE_HASH_INDEX,
   MTYPE_HASH_BACKET,
-  MTYPE_RIPNG_ROUTE,
-  MTYPE_RIPNG_AGGREGATE,
   MTYPE_ROUTE_TABLE,
   MTYPE_ROUTE_NODE,
   MTYPE_ACCESS_LIST,
@@ -179,6 +177,13 @@
   MTYPE_KEYCHAIN,
   MTYPE_KEY,
 
+  MTYPE_RIPNG,
+  MTYPE_RIPNG_ROUTE,
+  MTYPE_RIPNG_AGGREGATE,
+  MTYPE_RIPNG_PEER,
+  MTYPE_RIPNG_OFFSET_LIST,
+  MTYPE_RIPNG_RTE_DATA,
+
   MTYPE_VTYSH_CONFIG,
   MTYPE_VTYSH_CONFIG_LINE,
 
diff --git a/ripngd/Makefile.am b/ripngd/Makefile.am
index 2835aa2..66f6483 100644
--- a/ripngd/Makefile.am
+++ b/ripngd/Makefile.am
@@ -9,7 +9,7 @@
 
 libripng_a_SOURCES = \
 	ripng_interface.c ripngd.c ripng_zebra.c ripng_route.c ripng_debug.c \
-	ripng_routemap.c
+	ripng_routemap.c ripng_offset.c ripng_peer.c ripng_nexthop.c
 
 noinst_HEADERS = \
 	ripng_debug.h ripng_route.h ripngd.h
@@ -17,7 +17,7 @@
 ripngd_SOURCES = \
 	ripng_main.c $(libripng_a_SOURCES)
 
-ripngd_LDADD = ../lib/libzebra.a
+ripngd_LDADD = -L../lib -lzebra
 
 sysconf_DATA = ripngd.conf.sample
 
diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c
index 51314bc..2cff3d0 100644
--- a/ripngd/ripng_debug.c
+++ b/ripngd/ripng_debug.c
@@ -207,7 +207,8 @@
 struct cmd_node debug_node =
 {
   DEBUG_NODE,
-  ""				/* Debug node has no interface. */
+  "",				/* Debug node has no interface. */
+  1 /* VTYSH */
 };
 
 int
diff --git a/ripngd/ripng_debug.h b/ripngd/ripng_debug.h
index 6713a15..617a07d 100644
--- a/ripngd/ripng_debug.h
+++ b/ripngd/ripng_debug.h
@@ -48,5 +48,6 @@
 extern unsigned long ripng_debug_zebra;
 
 void ripng_debug_init ();
+void ripng_debug_reset ();
 
 #endif /* _ZEBRA_RIPNG_DEBUG_H */
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
index 369c3d6..b4299eb 100644
--- a/ripngd/ripng_interface.c
+++ b/ripngd/ripng_interface.c
@@ -49,6 +49,9 @@
 /* Static utility function. */
 static void ripng_enable_apply (struct interface *);
 static void ripng_passive_interface_apply (struct interface *);
+int ripng_enable_if_lookup (char *ifname);
+int ripng_enable_network_lookup2 (struct connected *connected);
+void ripng_enable_apply_all ();
 
 /* Join to the all rip routers multicast group. */
 int
@@ -57,19 +60,23 @@
   int ret;
   struct ipv6_mreq mreq;
 
-  memset (&mreq, 0, sizeof (mreq));
-  inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
-  mreq.ipv6mr_interface = ifp->ifindex;
+  if (if_is_up (ifp) && if_is_multicast (ifp)) {
+    memset (&mreq, 0, sizeof (mreq));
+    inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
+    mreq.ipv6mr_interface = ifp->ifindex;
 
-  ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
-		    (char *) &mreq, sizeof (mreq));
-  if (ret < 0)
-    zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s", strerror (errno));
+    ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+		      (char *) &mreq, sizeof (mreq));
+    if (ret < 0)
+      zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s", strerror (errno));
 
-  if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng %s join to all-rip-routers multicast group", ifp->name);
+    if (IS_RIPNG_DEBUG_EVENT)
+      zlog_info ("RIPng %s join to all-rip-routers multicast group", ifp->name);
 
-  return ret;
+    if (ret < 0)
+      return -1;
+  }
+  return 0;
 }
 
 /* Leave from the all rip routers multicast group. */
@@ -79,20 +86,46 @@
   int ret;
   struct ipv6_mreq mreq;
 
-  memset (&mreq, 0, sizeof (mreq));
-  inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
-  mreq.ipv6mr_interface = ifp->ifindex;
+  if (if_is_up (ifp) && if_is_multicast (ifp)) {
+    memset (&mreq, 0, sizeof (mreq));
+    inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr);
+    mreq.ipv6mr_interface = ifp->ifindex;
 
-  ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
-		    (char *) &mreq, sizeof (mreq));
-  if (ret < 0)
-    zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s\n", strerror (errno));
+    ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
+		      (char *) &mreq, sizeof (mreq));
+    if (ret < 0)
+      zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s\n", strerror (errno));
 
-  if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng %s leave from all-rip-routers multicast group",
-	       ifp->name);
+    if (IS_RIPNG_DEBUG_EVENT)
+      zlog_info ("RIPng %s leave from all-rip-routers multicast group",
+	         ifp->name);
 
-  return ret;
+    if (ret < 0)
+      return -1;
+  }
+
+  return 0;
+}
+
+/* How many link local IPv6 address could be used on the interface ? */
+int
+ripng_if_ipv6_lladdress_check (struct interface *ifp)
+{
+  struct listnode *nn;
+  struct connected *connected;
+  int count = 0;
+
+  for (nn = listhead (ifp->connected); nn; nextnode (nn))
+    if ((connected = getdata (nn)) != NULL) {
+      struct prefix *p;
+      p = connected->address;
+
+      if ((p->family == AF_INET6) &&
+          IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6))
+        count++;
+    }
+
+  return count;
 }
 
 /* Check max mtu size. */
@@ -120,7 +153,7 @@
   struct ripng_info *rinfo;
   struct ripng_interface *ri;
 
-  if (ripng->table)
+  if (ripng)
     {
       for (rp = route_top (ripng->table); rp; rp = route_next (rp))
 	if ((rinfo = rp->info) != NULL)
@@ -134,18 +167,17 @@
 					 &rinfo->nexthop,
 					 rinfo->ifindex);
 
-		RIPNG_TIMER_OFF (rinfo->t_timeout);
-		RIPNG_TIMER_OFF (rinfo->t_garbage_collect);
-	      
-		rp->info = NULL;
-		route_unlock_node (rp);
-	      
-		ripng_info_free (rinfo);
+		ripng_redistribute_delete (rinfo->type, rinfo->sub_type,
+					   (struct prefix_ipv6 *)&rp->p,
+					   rinfo->ifindex);
 	      }
 	    else
 	      {
-		/* All redistributed routes got through this interface. */
-		if (rinfo->ifindex == ifp->ifindex)
+		/* All redistributed routes got through this interface,
+		 * but the static and system ones are kept. */
+		if ((rinfo->ifindex == ifp->ifindex) &&
+		    (rinfo->type != ZEBRA_ROUTE_STATIC) &&
+		    (rinfo->type != ZEBRA_ROUTE_SYSTEM))
 		  ripng_redistribute_delete (rinfo->type, rinfo->sub_type,
 					     (struct prefix_ipv6 *) &rp->p,
 					     rinfo->ifindex);
@@ -155,7 +187,7 @@
 
   ri = ifp->info;
   
-  if (ripng && ri->running)
+  if (ri->running)
    {
      if (IS_RIPNG_DEBUG_EVENT)
        zlog_info ("turn off %s", ifp->name);
@@ -251,16 +283,121 @@
 ripng_interface_delete (int command, struct zclient *zclient,
 			zebra_size_t length)
 {
+  struct interface *ifp;
+  struct stream *s;
+
+  s = zclient->ibuf;
+  /*  zebra_interface_state_read() updates interface structure in iflist */
+  ifp = zebra_interface_state_read(s);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (if_is_up (ifp)) {
+    ripng_if_down(ifp);
+  }
+
+  zlog_info("interface delete %s index %d flags %ld metric %d mtu %d",
+            ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
+
+  /* To support pseudo interface do not free interface structure.  */
+  /* if_delete(ifp); */
+
   return 0;
 }
 
+void
+ripng_interface_clean ()
+{
+  listnode node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  for (node = listhead (iflist); node; nextnode (node))
+    {
+      ifp = getdata (node);
+      ri = ifp->info;
+
+      ri->enable_network = 0;
+      ri->enable_interface = 0;
+      ri->running = 0;
+
+      if (ri->t_wakeup)
+        {
+          thread_cancel (ri->t_wakeup);
+          ri->t_wakeup = NULL;
+        }
+    }
+}
+
+void
+ripng_interface_reset () {
+  listnode node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  for (node = listhead (iflist); node; nextnode (node))
+    {
+      ifp = getdata (node);
+      ri = ifp->info;
+
+      ri->enable_network = 0;
+      ri->enable_interface = 0;
+      ri->running = 0;
+
+      ri->split_horizon = RIPNG_NO_SPLIT_HORIZON;
+      ri->split_horizon_default = RIPNG_NO_SPLIT_HORIZON;
+
+      ri->list[RIPNG_FILTER_IN] = NULL;
+      ri->list[RIPNG_FILTER_OUT] = NULL;
+
+      ri->prefix[RIPNG_FILTER_IN] = NULL;
+      ri->prefix[RIPNG_FILTER_OUT] = NULL;
+
+      if (ri->t_wakeup)
+        {
+          thread_cancel (ri->t_wakeup);
+          ri->t_wakeup = NULL;
+        }
+
+      ri->passive = 0;
+    }
+}
+
+static void
+ripng_apply_address_add (struct connected *ifc) {
+  struct prefix_ipv6 address;
+  struct prefix *p;
+
+  if (!ripng)
+    return;
+
+  if (! if_is_up(ifc->ifp))
+    return;
+
+  p = ifc->address;
+
+  memset (&address, 0, sizeof (address));
+  address.family = p->family;
+  address.prefix = p->u.prefix6;
+  address.prefixlen = p->prefixlen;
+  apply_mask_ipv6(&address);
+
+  /* Check if this interface is RIP enabled or not
+     or  Check if this address's prefix is RIP enabled */
+  if ((ripng_enable_if_lookup(ifc->ifp->name) >= 0) ||
+      (ripng_enable_network_lookup2(ifc) >= 0))
+    ripng_redistribute_add(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                           &address, ifc->ifp->ifindex, NULL);
+
+}
+
 int
 ripng_interface_address_add (int command, struct zclient *zclient,
 			     zebra_size_t length)
 {
   struct connected *c;
   struct prefix *p;
-  char buf[INET6_ADDRSTRLEN];
 
   c = zebra_interface_address_add_read (zclient->ibuf);
 
@@ -273,16 +410,56 @@
     {
       if (IS_RIPNG_DEBUG_ZEBRA)
 	zlog_info ("RIPng connected address %s/%d add",
-		   inet_ntop (AF_INET6, &p->u.prefix6, buf, INET6_ADDRSTRLEN),
+		   inet6_ntop(&p->u.prefix6),
 		   p->prefixlen);
       
-      /* Check is this interface is RIP enabled or not.*/
-      ripng_enable_apply (c->ifp);
+      /* Check is this prefix needs to be redistributed. */
+      ripng_apply_address_add(c);
+
+      /* Let's try once again whether the interface could be activated */
+      if (c->ifp) {
+        struct ripng_interface *ri = c->ifp->info;
+
+        if (!ri->running) {
+          /* Check if this interface is RIP enabled or not.*/
+          ripng_enable_apply (c->ifp);
+
+          /* Apply distribute list to the interface. */
+          ripng_distribute_update_interface (c->ifp);
+
+          /* Check interface routemap. */
+          ripng_if_rmap_update_interface (c->ifp);
+        }
+      }
+
     }
 
   return 0;
 }
 
+static void
+ripng_apply_address_del (struct connected *ifc) {
+  struct prefix_ipv6 address;
+  struct prefix *p;
+
+  if (!ripng)
+    return;
+
+  if (! if_is_up(ifc->ifp))
+    return;
+
+  p = ifc->address;
+
+  memset (&address, 0, sizeof (address));
+  address.family = p->family;
+  address.prefix = p->u.prefix6;
+  address.prefixlen = p->prefixlen;
+  apply_mask_ipv6(&address);
+
+  ripng_redistribute_delete(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                            &address, ifc->ifp->ifindex);
+}
+
 int
 ripng_interface_address_delete (int command, struct zclient *zclient,
 				zebra_size_t length)
@@ -305,8 +482,8 @@
 				  INET6_ADDRSTRLEN),
 		       p->prefixlen);
 
-	  /* Check is this interface is RIP enabled or not.*/
-	  ripng_enable_apply (ifc->ifp);
+	  /* Check wether this prefix needs to be removed. */
+	  ripng_apply_address_del(ifc);
 	}
       connected_free (ifc);
     }
@@ -321,11 +498,14 @@
 struct route_table *ripng_enable_network;
 
 /* Lookup RIPng enable network. */
+/* Check wether the interface has at least a connected prefix that
+ * is within the ripng_enable_network table. */
 int
-ripng_enable_network_lookup (struct interface *ifp)
+ripng_enable_network_lookup_if (struct interface *ifp)
 {
   listnode listnode;
   struct connected *connected;
+  struct prefix_ipv6 address;
 
   for (listnode = listhead (ifp->connected); listnode; nextnode (listnode))
     if ((connected = getdata (listnode)) != NULL)
@@ -337,7 +517,12 @@
 
 	if (p->family == AF_INET6)
 	  {
-	    node = route_node_match (ripng_enable_network, p);
+	    address.family = AF_INET6;
+	    address.prefix = p->u.prefix6;
+	    address.prefixlen = IPV6_MAX_BITLEN;
+
+	    node = route_node_match (ripng_enable_network,
+			             (struct prefix *)&address);
 	    if (node)
 	      {
 		route_unlock_node (node);
@@ -348,6 +533,35 @@
   return -1;
 }
 
+/* Check wether connected is within the ripng_enable_network table. */
+int
+ripng_enable_network_lookup2 (struct connected *connected)
+{
+  struct prefix_ipv6 address;
+  struct prefix *p;
+
+  p = connected->address;
+
+  if (p->family == AF_INET6) {
+    struct route_node *node;
+
+    address.family = p->family;
+    address.prefix = p->u.prefix6;
+    address.prefixlen = IPV6_MAX_BITLEN;
+
+    /* LPM on p->family, p->u.prefix6/IPV6_MAX_BITLEN within ripng_enable_network */
+    node = route_node_match (ripng_enable_network,
+                             (struct prefix *)&address);
+
+    if (node) {
+      route_unlock_node (node);
+      return 1;
+    }
+  }
+
+  return -1;
+}
+
 /* Add RIPng enable network. */
 int
 ripng_enable_network_add (struct prefix *p)
@@ -364,6 +578,9 @@
   else
     node->info = "enabled";
 
+  /* XXX: One should find a better solution than a generic one */
+  ripng_enable_apply_all();
+
   return 1;
 }
 
@@ -415,6 +632,8 @@
 
   vector_set (ripng_enable_if, strdup (ifname));
 
+  ripng_enable_apply_all();
+
   return 1;
 }
 
@@ -433,6 +652,8 @@
   free (str);
   vector_unset (ripng_enable_if, index);
 
+  ripng_enable_apply_all();
+
   return 1;
 }
 
@@ -450,7 +671,13 @@
   ri->t_wakeup = NULL;
 
   /* Join to multicast group. */
-  ripng_multicast_join (ifp);
+  if (ripng_multicast_join (ifp) < 0) {
+    zlog_err ("multicast join failed, interface %s not running", ifp->name);
+    return 0;
+  }
+    
+  /* Set running flag. */
+  ri->running = 1;
 
   /* Send RIP request to the interface. */
   ripng_request (ifp);
@@ -458,6 +685,44 @@
   return 0;
 }
 
+int ripng_redistribute_check (int);
+
+void
+ripng_connect_set (struct interface *ifp, int set)
+{
+  struct listnode *nn;
+  struct connected *connected;
+  struct prefix_ipv6 address;
+
+  for (nn = listhead (ifp->connected); nn; nextnode (nn))
+    if ((connected = getdata (nn)) != NULL) {
+      struct prefix *p;
+      p = connected->address;
+
+      if (p->family != AF_INET6)
+        continue;
+
+      address.family = AF_INET6;
+      address.prefix = p->u.prefix6;
+      address.prefixlen = p->prefixlen;
+      apply_mask_ipv6 (&address);
+
+      if (set) {
+        /* Check once more wether this prefix is within a "network IF_OR_PREF" one */
+        if ((ripng_enable_if_lookup(connected->ifp->name) >= 0) ||
+            (ripng_enable_network_lookup2(connected) >= 0))
+          ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                                  &address, connected->ifp->ifindex, NULL);
+      } else {
+        ripng_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE,
+                                   &address, connected->ifp->ifindex);
+        if (ripng_redistribute_check (ZEBRA_ROUTE_CONNECT))
+          ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_REDISTRIBUTE,
+                                  &address, connected->ifp->ifindex, NULL);
+      }
+    }
+}
+
 /* Check RIPng is enabed on this interface. */
 void
 ripng_enable_apply (struct interface *ifp)
@@ -466,16 +731,13 @@
   struct ripng_interface *ri = NULL;
 
   /* Check interface. */
-  if (if_is_loopback (ifp))
-    return;
-
   if (! if_is_up (ifp))
     return;
   
   ri = ifp->info;
 
-  /* Check network configuration. */
-  ret = ripng_enable_network_lookup (ifp);
+  /* Is this interface a candidate for RIPng ? */
+  ret = ripng_enable_network_lookup_if (ifp);
 
   /* If the interface is matched. */
   if (ret > 0)
@@ -490,10 +752,18 @@
   else
     ri->enable_interface = 0;
 
+  /* any candidate interface MUST have a link-local IPv6 address */
+  if ((! ripng_if_ipv6_lladdress_check (ifp)) &&
+      (ri->enable_network || ri->enable_interface)) {
+    ri->enable_network = 0;
+    ri->enable_interface = 0;
+    zlog_warn("Interface %s does not have any link-local address",
+              ifp->name);
+  }
+
   /* Update running status of the interface. */
   if (ri->enable_network || ri->enable_interface)
     {
-      if (! ri->running)
 	{
 	  if (IS_RIPNG_DEBUG_EVENT)
 	    zlog_info ("RIPng turn on %s", ifp->name);
@@ -502,34 +772,26 @@
 	  if (! ri->t_wakeup)
 	    ri->t_wakeup = thread_add_timer (master, ripng_interface_wakeup,
 					     ifp, 1);
-#if 0
-	  /* Join to multicast group. */
-	  ripng_multicast_join (ifp);
 
-	  /* Send RIP request to the interface. */
-	  ripng_request (ifp);
-#endif /* 0 */
-
-	  ri->running = 1;
+	  ripng_connect_set (ifp, 1);
 	}
     }
   else
     {
       if (ri->running)
 	{
-	  if (IS_RIPNG_DEBUG_EVENT)
-	    zlog_info ("RIPng turn off %s", ifp->name);
+	  /* Might as well clean up the route table as well
+	   * ripng_if_down sets to 0 ri->running, and displays "turn off %s"
+	   **/
+	  ripng_if_down(ifp);
 
-	  /* Leave from multicast group. */
-	  ripng_multicast_leave (ifp);
-
-	  ri->running = 0;
+	  ripng_connect_set (ifp, 0);
 	}
     }
 }
 
 /* Set distribute list to all interfaces. */
-static void
+void
 ripng_enable_apply_all ()
 {
   struct interface *ifp;
@@ -542,6 +804,29 @@
     }
 }
 
+/* Clear all network and neighbor configuration */
+void
+ripng_clean_network ()
+{
+  int i;
+  char *str;
+  struct route_node *rn;
+
+  /* ripng_enable_network */
+  for (rn = route_top (ripng_enable_network); rn; rn = route_next (rn))
+    if (rn->info) {
+      rn->info = NULL;
+      route_unlock_node(rn);
+    }
+
+  /* ripng_enable_if */
+  for (i = 0; i < vector_max (ripng_enable_if); i++)
+    if ((str = vector_slot (ripng_enable_if, i)) != NULL) {
+      free (str);
+      vector_slot (ripng_enable_if, i) = NULL;
+    }
+}
+
 /* Vector to store passive-interface name. */
 vector Vripng_passive_interface;
 
@@ -638,10 +923,9 @@
 
 /* Write RIPng enable network and interface to the vty. */
 int
-ripng_network_write (struct vty *vty)
+ripng_network_write (struct vty *vty, int config_mode)
 {
   int i;
-  char *str;
   char *ifname;
   struct route_node *node;
   char buf[BUFSIZ];
@@ -651,7 +935,8 @@
     if (node->info)
       {
 	struct prefix *p = &node->p;
-	vty_out (vty, " network %s/%d%s", 
+	vty_out (vty, "%s%s/%d%s", 
+		 config_mode ? " network " : "    ",
 		 inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
 		 p->prefixlen,
 		 VTY_NEWLINE);
@@ -660,14 +945,17 @@
   
   /* Write enable interface. */
   for (i = 0; i < vector_max (ripng_enable_if); i++)
-    if ((str = vector_slot (ripng_enable_if, i)) != NULL)
-      vty_out (vty, " network %s%s", str,
+    if ((ifname = vector_slot (ripng_enable_if, i)) != NULL)
+      vty_out (vty, "%s%s%s",
+	       config_mode ? " network " : "    ",
+	       ifname,
 	       VTY_NEWLINE);
 
   /* Write passive interface. */
-  for (i = 0; i < vector_max (Vripng_passive_interface); i++)
-    if ((ifname = vector_slot (Vripng_passive_interface, i)) != NULL)
-      vty_out (vty, " passive-interface %s%s", ifname, VTY_NEWLINE);
+  if (config_mode)
+    for (i = 0; i < vector_max (Vripng_passive_interface); i++)
+      if ((ifname = vector_slot (Vripng_passive_interface, i)) != NULL)
+        vty_out (vty, " passive-interface %s%s", ifname, VTY_NEWLINE);
 
   return 0;
 }
@@ -697,8 +985,6 @@
       return CMD_WARNING;
     }
 
-  ripng_enable_apply_all ();
-
   return CMD_SUCCESS;
 }
 
@@ -728,11 +1014,71 @@
       return CMD_WARNING;
     }
   
-  ripng_enable_apply_all ();
-
   return CMD_SUCCESS;
 }
 
+DEFUN (ipv6_ripng_split_horizon,
+       ipv6_ripng_split_horizon_cmd,
+       "ipv6 ripng split-horizon",
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n")
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIPNG_SPLIT_HORIZON;
+  return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_ripng_split_horizon_poisoned_reverse,
+       ipv6_ripng_split_horizon_poisoned_reverse_cmd,
+       "ipv6 ripng split-horizon poisoned-reverse",
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n"
+       "With poisoned-reverse\n")
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIPNG_SPLIT_HORIZON_POISONED_REVERSE;
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ripng_split_horizon,
+       no_ipv6_ripng_split_horizon_cmd,
+       "no ipv6 ripng split-horizon",
+       NO_STR
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n")
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  ifp = vty->index;
+  ri = ifp->info;
+
+  ri->split_horizon = RIPNG_NO_SPLIT_HORIZON;
+  return CMD_SUCCESS;
+}
+
+ALIAS (no_ipv6_ripng_split_horizon,
+       no_ipv6_ripng_split_horizon_poisoned_reverse_cmd,
+       "no ipv6 ripng split-horizon poisoned-reverse",
+       NO_STR
+       IPV6_STR
+       "Routing Information Protocol\n"
+       "Perform split horizon\n"
+       "With poisoned-reverse\n")
+
 DEFUN (ripng_passive_interface,
        ripng_passive_interface_cmd,
        "passive-interface IFNAME",
@@ -757,6 +1103,14 @@
 {
   struct ripng_interface *ri;
   ri = XCALLOC (MTYPE_IF, sizeof (struct ripng_interface));
+
+  /* Set default split-horizon behavior.  If the interface is Frame
+     Relay or SMDS is enabled, the default value for split-horizon is
+     off.  But currently Zebra does detect Frame Relay or SMDS
+     interface.  So all interface is set to split horizon.  */
+  ri->split_horizon_default = RIPNG_SPLIT_HORIZON;
+  ri->split_horizon = ri->split_horizon_default;
+
   return ri;
 }
 
@@ -767,6 +1121,15 @@
   return 0;
 }
 
+/* Called when interface structure deleted. */
+int
+ripng_if_delete_hook (struct interface *ifp)
+{
+  XFREE (MTYPE_IF, ifp->info);
+  ifp->info = NULL;
+  return 0;
+}
+
 /* Configuration write function for ripngd. */
 int
 interface_config_write (struct vty *vty)
@@ -781,12 +1144,37 @@
       ifp = getdata (node);
       ri = ifp->info;
 
+      /* Do not display the interface if there is no
+       * configuration about it.
+       **/
+      if ((!ifp->desc) &&
+          (ri->split_horizon == ri->split_horizon_default))
+        continue;
+
       vty_out (vty, "interface %s%s", ifp->name,
 	       VTY_NEWLINE);
       if (ifp->desc)
 	vty_out (vty, " description %s%s", ifp->desc,
 		 VTY_NEWLINE);
 
+      /* Split horizon. */
+      if (ri->split_horizon != ri->split_horizon_default)
+	{
+          switch (ri->split_horizon) {
+          case RIPNG_SPLIT_HORIZON:
+            vty_out (vty, " ipv6 ripng split-horizon%s", VTY_NEWLINE);
+            break;
+          case RIPNG_SPLIT_HORIZON_POISONED_REVERSE:
+            vty_out (vty, " ipv6 ripng split-horizon poisoned-reverse%s",
+                          VTY_NEWLINE);
+            break;
+          case RIPNG_NO_SPLIT_HORIZON:
+          default:
+            vty_out (vty, " no ipv6 ripng split-horizon%s", VTY_NEWLINE);
+            break;
+          }
+	}
+
       vty_out (vty, "!%s", VTY_NEWLINE);
 
       write++;
@@ -799,6 +1187,7 @@
 {
   INTERFACE_NODE,
   "%s(config-if)# ",
+  1 /* VTYSH */
 };
 
 /* Initialization of interface. */
@@ -808,6 +1197,7 @@
   /* Interface initialize. */
   iflist = list_new ();
   if_add_hook (IF_NEW_HOOK, ripng_if_new_hook);
+  if_add_hook (IF_DELETE_HOOK, ripng_if_delete_hook);
 
   /* RIPng enable network init. */
   ripng_enable_network = route_table_init ();
@@ -820,12 +1210,11 @@
 
   /* Install interface node. */
   install_node (&interface_node, interface_config_write);
-
+  
+  /* Install commands. */
   install_element (CONFIG_NODE, &interface_cmd);
   install_element (CONFIG_NODE, &no_interface_cmd);
-  install_element (INTERFACE_NODE, &config_end_cmd);
-  install_element (INTERFACE_NODE, &config_exit_cmd);
-  install_element (INTERFACE_NODE, &config_help_cmd);
+  install_default (INTERFACE_NODE);
   install_element (INTERFACE_NODE, &interface_desc_cmd);
   install_element (INTERFACE_NODE, &no_interface_desc_cmd);
 
@@ -833,4 +1222,9 @@
   install_element (RIPNG_NODE, &no_ripng_network_cmd);
   install_element (RIPNG_NODE, &ripng_passive_interface_cmd);
   install_element (RIPNG_NODE, &no_ripng_passive_interface_cmd);
+
+  install_element (INTERFACE_NODE, &ipv6_ripng_split_horizon_cmd);
+  install_element (INTERFACE_NODE, &ipv6_ripng_split_horizon_poisoned_reverse_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ripng_split_horizon_cmd);
+  install_element (INTERFACE_NODE, &no_ipv6_ripng_split_horizon_poisoned_reverse_cmd);
 }
diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c
index 3a7ed4a..44c3876 100644
--- a/ripngd/ripng_main.c
+++ b/ripngd/ripng_main.c
@@ -27,6 +27,7 @@
 #include "vector.h"
 #include "vty.h"
 #include "command.h"
+#include "memory.h"
 #include "thread.h"
 #include "log.h"
 #include "prefix.h"
@@ -37,6 +38,7 @@
 /* Configuration filename and directory. */
 char config_current[] = RIPNG_DEFAULT_CONFIG;
 char config_default[] = SYSCONFDIR RIPNG_DEFAULT_CONFIG;
+char *config_file = NULL;
 
 /* RIPngd options. */
 struct option longopts[] = 
@@ -58,6 +60,12 @@
 /* Route retain mode flag. */
 int retain_mode = 0;
 
+/* RIPng VTY bind address. */
+char *vty_addr = NULL;
+
+/* RIPng VTY connection port. */
+int vty_port = RIPNG_VTY_PORT;
+
 /* Master of threads. */
 struct thread_master *master;
 
@@ -93,17 +101,27 @@
 void 
 sighup (int sig)
 {
-  zlog (NULL, LOG_INFO, "SIGHUP received");
+  zlog_info ("SIGHUP received");
+  ripng_clean ();
+  ripng_reset ();
+  zlog_info ("Terminating on signal");
+
+  /* Reload config file. */
+  vty_read_config (config_file, config_current, config_default);
+  /* Create VTY's socket */
+  vty_serv_sock (vty_addr, vty_port, RIPNG_VTYSH_PATH);
+
+  /* Try to return to normal operation. */
 }
 
 /* SIGINT handler. */
 void
 sigint (int sig)
 {
-  zlog (NULL, LOG_INFO, "Terminating on signal");
+  zlog_info ("Terminating on signal");
 
   if (! retain_mode)
-    ripng_terminate ();
+    ripng_clean ();
 
   exit (0);
 }
@@ -154,10 +172,8 @@
 main (int argc, char **argv)
 {
   char *p;
-  char *vty_addr = NULL;
   int vty_port = RIPNG_VTY_PORT;
   int daemon_mode = 0;
-  char *config_file = NULL;
   char *progname;
   struct thread thread;
 
@@ -231,10 +247,14 @@
   signal_init ();
   cmd_init (1);
   vty_init ();
+  memory_init ();
 
   /* RIPngd inits. */
   ripng_init ();
   zebra_init ();
+  ripng_peer_init ();
+
+  /* Sort all installed commands. */
   sort_node ();
 
   /* Get configuration file. */
diff --git a/ripngd/ripng_nexthop.c b/ripngd/ripng_nexthop.c
new file mode 100644
index 0000000..2c5d45c
--- /dev/null
+++ b/ripngd/ripng_nexthop.c
@@ -0,0 +1,216 @@
+/* RIPngd Zebra
+ * Copyright (C) 2002 6WIND <vincent.jardin@6wind.com>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* This file is required in order to support properly the RIPng nexthop
+ * feature.
+ */
+
+#include <zebra.h>
+
+/* For struct udphdr. */
+#include <netinet/udp.h>
+
+#include "linklist.h"
+#include "stream.h"
+#include "log.h"
+#include "memory.h"
+#include "vty.h"
+#include "if.h"
+#include "prefix.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+#include "ripngd/ripng_nexthop.h"
+
+#define DEBUG 1
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+struct ripng_rte_data {
+  struct prefix_ipv6 *p;
+  struct ripng_info *rinfo;
+  struct ripng_aggregate *aggregate;
+};
+
+void _ripng_rte_del(struct ripng_rte_data *A);
+int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B);
+
+#define METRIC_OUT(a) \
+    (a->rinfo ?  a->rinfo->metric_out : a->aggregate->metric_out)
+#define NEXTHOP_OUT(a) \
+    (a->rinfo ?  a->rinfo->nexthop_out : a->aggregate->nexthop_out)
+#define TAG_OUT(a) \
+    (a->rinfo ?  a->rinfo->tag_out : a->aggregate->tag_out)
+
+struct list *
+ripng_rte_new(void) {
+  struct list *rte;
+
+  rte = list_new();
+  rte->cmp = (int (*)(void *, void *)) _ripng_rte_cmp;
+  rte->del = (void (*)(void *)) _ripng_rte_del;
+
+  return rte;
+}
+
+void
+ripng_rte_free(struct list *ripng_rte_list) {
+  list_delete(ripng_rte_list);
+}
+
+/* Delete RTE */
+void
+_ripng_rte_del(struct ripng_rte_data *A) {
+  XFREE(MTYPE_RIPNG_RTE_DATA, A);
+}
+
+/* Compare RTE:
+ *  return +  if A > B
+ *         0  if A = B
+ *         -  if A < B
+ */
+int
+_ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B) {
+  return addr6_cmp(&NEXTHOP_OUT(A), &NEXTHOP_OUT(B));
+}
+
+/* Add routing table entry */
+void
+ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
+              struct ripng_info *rinfo, struct ripng_aggregate *aggregate) {
+
+  struct ripng_rte_data *data;
+
+  /* At least one should not be null */
+  assert(!rinfo || !aggregate);
+
+  data = XMALLOC(MTYPE_RIPNG_RTE_DATA, sizeof(*data));
+  data->p     = p;
+  data->rinfo = rinfo;
+  data->aggregate = aggregate;
+
+  listnode_add_sort(ripng_rte_list, data);
+} 
+
+/* Send the RTE with the nexthop support
+ */
+void
+ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
+               struct sockaddr_in6 *to) {
+
+  struct ripng_rte_data *data;
+  struct listnode * nn;
+
+  struct in6_addr last_nexthop;
+  struct in6_addr myself_nexthop;
+
+  struct stream *s;
+  int num;
+  int mtu;
+  int rtemax;
+  int ret;
+
+  /* Most of the time, there is no nexthop */
+  memset(&last_nexthop, 0, sizeof(last_nexthop));
+
+  /* Use myself_nexthop if the nexthop is not a link-local address, because
+   * we remain a right path without beeing the optimal one.
+   */
+  memset(&myself_nexthop, 0, sizeof(myself_nexthop));
+
+  /* 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;
+
+  rtemax = (min (mtu, RIPNG_MAX_PACKET_SIZE) -
+	    IPV6_HDRLEN - 
+	    sizeof (struct udphdr) -
+	    sizeof (struct ripng_packet) +
+	    sizeof (struct rte)) / sizeof (struct rte);
+
+  LIST_LOOP(ripng_rte_list, data, nn) {
+
+    /* (2.1) Next hop support */
+    if (!IPV6_ADDR_SAME(&last_nexthop, &NEXTHOP_OUT(data))) {
+
+      /* A nexthop entry should be at least followed by 1 RTE */
+      if (num == (rtemax-1)) {
+	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);
+      }
+
+      /* Add the nexthop (2.1) */
+
+      /* 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.
+       */
+      if (!IN6_IS_ADDR_LINKLOCAL(&NEXTHOP_OUT(data)))
+        last_nexthop = myself_nexthop;
+      else
+	last_nexthop = NEXTHOP_OUT(data);
+
+      num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP);
+    } else {
+      /* Rewrite the nexthop for each new packet */
+      if ((num == 0) && !IPV6_ADDR_SAME(&last_nexthop, &myself_nexthop))
+        num = ripng_write_rte(num, s, NULL, &last_nexthop, 0, RIPNG_METRIC_NEXTHOP);
+    }
+    num = ripng_write_rte(num, s, data->p, NULL,
+			  TAG_OUT(data), METRIC_OUT(data));
+
+    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);
+    }
+  }
+
+  /* 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);
+  }
+}
diff --git a/ripngd/ripng_nexthop.h b/ripngd/ripng_nexthop.h
new file mode 100644
index 0000000..5c778f5
--- /dev/null
+++ b/ripngd/ripng_nexthop.h
@@ -0,0 +1,62 @@
+/* RIPng nexthop support
+ * Copyright (C) 6WIND Vincent Jardin <vincent.jardin@6wind.com>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+#ifndef _ZEBRA_RIPNG_RIPNG_NEXTHOP_H
+#define _ZEBRA_RIPNG_RIPNG_NEXTHOP_H
+
+#include <zebra.h>
+#include "linklist.h"
+#include "ripngd/ripng_route.h"
+#include "ripngd/ripngd.h"
+
+struct list * ripng_rte_new(void);
+void ripng_rte_free(struct list *ripng_rte_list);
+void ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
+                   struct ripng_info *rinfo, struct ripng_aggregate *aggregate);
+void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
+                    struct sockaddr_in6 *to);
+
+/***
+ * 1 if A > B
+ * 0 if A = B
+ * -1 if A < B
+ **/
+static inline int
+addr6_cmp(struct in6_addr *A, struct in6_addr *B) {
+#define a(i) A->s6_addr32[i]
+#define b(i) B->s6_addr32[i]
+
+  if (a(3) > b(3))
+    return 1;
+  else if ((a(3) == b(3)) && (a(2) > b(2)))
+    return 1;
+  else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) > b(1)))
+    return 1;
+  else if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1)) && (a(0) > b(0)))
+    return 1;
+
+  if ((a(3) == b(3)) && (a(2) == b(2)) && (a(1) == b(1)) && (a(0) == b(0)))
+    return 0;
+
+  return -1;
+}
+
+#endif /* _ZEBRA_RIPNG_RIPNG_NEXTHOP_H */
diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c
new file mode 100644
index 0000000..ab3c460
--- /dev/null
+++ b/ripngd/ripng_offset.c
@@ -0,0 +1,417 @@
+/* RIPng offset-list
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+ /* RIPng support by Vincent Jardin <vincent.jardin@6wind.com>
+  * Copyright (C) 2002 6WIND
+  */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "filter.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+
+#define RIPNG_OFFSET_LIST_IN  0
+#define RIPNG_OFFSET_LIST_OUT 1
+#define RIPNG_OFFSET_LIST_MAX 2
+
+struct ripng_offset_list
+{
+  char *ifname;
+
+  struct 
+  {
+    char *alist_name;
+    /* struct access_list *alist; */
+    int metric;
+  } direct[RIPNG_OFFSET_LIST_MAX];
+};
+
+static struct list *ripng_offset_list_master;
+
+int
+strcmp_safe (char *s1, char *s2)
+{
+  if (s1 == NULL && s2 == NULL)
+    return 0;
+  if (s1 == NULL)
+    return -1;
+  if (s2 == NULL)
+    return 1;
+  return strcmp (s1, s2);
+}
+
+struct ripng_offset_list *
+ripng_offset_list_new ()
+{
+  struct ripng_offset_list *new;
+
+  new = XCALLOC (MTYPE_RIPNG_OFFSET_LIST, sizeof (struct ripng_offset_list));
+  return new;
+}
+
+void
+ripng_offset_list_free (struct ripng_offset_list *offset)
+{
+  XFREE (MTYPE_RIPNG_OFFSET_LIST, offset);
+}
+
+struct ripng_offset_list *
+ripng_offset_list_lookup (char *ifname)
+{
+  struct ripng_offset_list *offset;
+  struct listnode *nn;
+
+  LIST_LOOP (ripng_offset_list_master, offset, nn)
+    {
+      if (strcmp_safe (offset->ifname, ifname) == 0)
+	return offset;
+    }
+  return NULL;
+}
+
+struct ripng_offset_list *
+ripng_offset_list_get (char *ifname)
+{
+  struct ripng_offset_list *offset;
+  
+  offset = ripng_offset_list_lookup (ifname);
+  if (offset)
+    return offset;
+
+  offset = ripng_offset_list_new ();
+  if (ifname)
+    offset->ifname = strdup (ifname);
+  listnode_add_sort (ripng_offset_list_master, offset);
+
+  return offset;
+}
+
+int
+ripng_offset_list_set (struct vty *vty, char *alist, char *direct_str,
+		       char *metric_str, char *ifname)
+{
+  int direct;
+  int metric;
+  struct ripng_offset_list *offset;
+
+  /* Check direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_OUT;
+  else
+    {
+      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check metric. */
+  metric = atoi (metric_str);
+  if (metric < 0 || metric > 16)
+    {
+      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get offset-list structure with interface name. */
+  offset = ripng_offset_list_get (ifname);
+
+  if (offset->direct[direct].alist_name)
+    free (offset->direct[direct].alist_name);
+  offset->direct[direct].alist_name = strdup (alist);
+  offset->direct[direct].metric = metric;
+
+  return CMD_SUCCESS;
+}
+
+int
+ripng_offset_list_unset (struct vty *vty, char *alist, char *direct_str,
+		         char *metric_str, char *ifname)
+{
+  int direct;
+  int metric;
+  struct ripng_offset_list *offset;
+
+  /* Check direction. */
+  if (strncmp (direct_str, "i", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_IN;
+  else if (strncmp (direct_str, "o", 1) == 0)
+    direct = RIPNG_OFFSET_LIST_OUT;
+  else
+    {
+      vty_out (vty, "Invalid direction: %s%s", direct_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Check metric. */
+  metric = atoi (metric_str);
+  if (metric < 0 || metric > 16)
+    {
+      vty_out (vty, "Invalid metric: %s%s", metric_str, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  /* Get offset-list structure with interface name. */
+  offset = ripng_offset_list_lookup (ifname);
+
+  if (offset)
+    {
+      if (offset->direct[direct].alist_name)
+	free (offset->direct[direct].alist_name);
+      offset->direct[direct].alist_name = NULL;
+
+      if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name == NULL &&
+	  offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name == NULL)
+	{
+	  listnode_delete (ripng_offset_list_master, offset);
+	  if (offset->ifname)
+	    free (offset->ifname);
+	  ripng_offset_list_free (offset);
+	}
+    }
+  else
+    {
+      vty_out (vty, "Can't find offset-list%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  return CMD_SUCCESS;
+}
+
+#define OFFSET_LIST_IN_NAME(O)  ((O)->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+#define OFFSET_LIST_IN_METRIC(O)  ((O)->direct[RIPNG_OFFSET_LIST_IN].metric)
+
+#define OFFSET_LIST_OUT_NAME(O)  ((O)->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+#define OFFSET_LIST_OUT_METRIC(O)  ((O)->direct[RIPNG_OFFSET_LIST_OUT].metric)
+
+/* If metric is modifed return 1. */
+int
+ripng_offset_list_apply_in (struct prefix_ipv6 *p, struct interface *ifp,
+			    u_char *metric)
+{
+  struct ripng_offset_list *offset;
+  struct access_list *alist;
+
+  /* Look up offset-list with interface name. */
+  offset = ripng_offset_list_lookup (ifp->name);
+  if (offset && OFFSET_LIST_IN_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset));
+
+      if (alist 
+	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+	{
+	  *metric += OFFSET_LIST_IN_METRIC (offset);
+	  return 1;
+	}
+      return 0;
+    }
+  /* Look up offset-list without interface name. */
+  offset = ripng_offset_list_lookup (NULL);
+  if (offset && OFFSET_LIST_IN_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_IN_NAME (offset));
+
+      if (alist 
+	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+	{
+	  *metric += OFFSET_LIST_IN_METRIC (offset);
+	  return 1;
+	}
+      return 0;
+    }
+  return 0;
+}
+
+/* If metric is modifed return 1. */
+int
+ripng_offset_list_apply_out (struct prefix_ipv6 *p, struct interface *ifp,
+			     u_char *metric)
+{
+  struct ripng_offset_list *offset;
+  struct access_list *alist;
+
+  /* Look up offset-list with interface name. */
+  offset = ripng_offset_list_lookup (ifp->name);
+  if (offset && OFFSET_LIST_OUT_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset));
+
+      if (alist 
+	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+	{
+	  *metric += OFFSET_LIST_OUT_METRIC (offset);
+	  return 1;
+	}
+      return 0;
+    }
+
+  /* Look up offset-list without interface name. */
+  offset = ripng_offset_list_lookup (NULL);
+  if (offset && OFFSET_LIST_OUT_NAME (offset))
+    {
+      alist = access_list_lookup (AFI_IP6, OFFSET_LIST_OUT_NAME (offset));
+
+      if (alist 
+	  && access_list_apply (alist, (struct prefix *)p) == FILTER_PERMIT)
+	{
+	  *metric += OFFSET_LIST_OUT_METRIC (offset);
+	  return 1;
+	}
+      return 0;
+    }
+  return 0;
+}
+
+DEFUN (ripng_offset_list,
+       ripng_offset_list_cmd,
+       "offset-list WORD (in|out) <0-16>",
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n")
+{
+  return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (ripng_offset_list_ifname,
+       ripng_offset_list_ifname_cmd,
+       "offset-list WORD (in|out) <0-16> IFNAME",
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n"
+       "Interface to match\n")
+{
+  return ripng_offset_list_set (vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+DEFUN (no_ripng_offset_list,
+       no_ripng_offset_list_cmd,
+       "no offset-list WORD (in|out) <0-16>",
+       NO_STR
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n")
+{
+  return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], NULL);
+}
+
+DEFUN (no_ripng_offset_list_ifname,
+       no_ripng_offset_list_ifname_cmd,
+       "no offset-list WORD (in|out) <0-16> IFNAME",
+       NO_STR
+       "Modify RIPng metric\n"
+       "Access-list name\n"
+       "For incoming updates\n"
+       "For outgoing updates\n"
+       "Metric value\n"
+       "Interface to match\n")
+{
+  return ripng_offset_list_unset (vty, argv[0], argv[1], argv[2], argv[3]);
+}
+
+int
+offset_list_cmp (struct ripng_offset_list *o1, struct ripng_offset_list *o2)
+{
+  return strcmp_safe (o1->ifname, o2->ifname);
+}
+
+void
+offset_list_del (struct ripng_offset_list *offset)
+{
+  if (OFFSET_LIST_IN_NAME (offset))
+    free (OFFSET_LIST_IN_NAME (offset));
+  if (OFFSET_LIST_OUT_NAME (offset))
+    free (OFFSET_LIST_OUT_NAME (offset));
+  if (offset->ifname)
+    free (offset->ifname);
+  ripng_offset_list_free (offset);
+}
+
+void
+ripng_offset_init ()
+{
+  ripng_offset_list_master = list_new ();
+  ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
+  ripng_offset_list_master->del = (void (*)(void *)) offset_list_del;
+
+  install_element (RIPNG_NODE, &ripng_offset_list_cmd);
+  install_element (RIPNG_NODE, &ripng_offset_list_ifname_cmd);
+  install_element (RIPNG_NODE, &no_ripng_offset_list_cmd);
+  install_element (RIPNG_NODE, &no_ripng_offset_list_ifname_cmd);
+}
+
+void
+ripng_offset_clean ()
+{
+  list_delete (ripng_offset_list_master);
+
+  ripng_offset_list_master = list_new ();
+  ripng_offset_list_master->cmp = (int (*)(void *, void *)) offset_list_cmp;
+  ripng_offset_list_master->del = (void (*)(void *)) offset_list_del;
+}
+
+int
+config_write_ripng_offset_list (struct vty *vty)
+{
+  struct listnode *nn;
+  struct ripng_offset_list *offset;
+
+  LIST_LOOP (ripng_offset_list_master, offset, nn)
+    {
+      if (! offset->ifname)
+	{
+	  if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+	    vty_out (vty, " offset-list %s in %d%s",
+		     offset->direct[RIPNG_OFFSET_LIST_IN].alist_name,
+		     offset->direct[RIPNG_OFFSET_LIST_IN].metric,
+		     VTY_NEWLINE);
+	  if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+	    vty_out (vty, " offset-list %s out %d%s",
+		     offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name,
+		     offset->direct[RIPNG_OFFSET_LIST_OUT].metric,
+		     VTY_NEWLINE);
+	}
+      else
+	{
+	  if (offset->direct[RIPNG_OFFSET_LIST_IN].alist_name)
+	    vty_out (vty, " offset-list %s in %d %s%s",
+		     offset->direct[RIPNG_OFFSET_LIST_IN].alist_name,
+		     offset->direct[RIPNG_OFFSET_LIST_IN].metric,
+		     offset->ifname, VTY_NEWLINE);
+	  if (offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name)
+	    vty_out (vty, " offset-list %s out %d %s%s",
+		     offset->direct[RIPNG_OFFSET_LIST_OUT].alist_name,
+		     offset->direct[RIPNG_OFFSET_LIST_OUT].metric,
+		     offset->ifname, VTY_NEWLINE);
+	}
+    }
+
+  return 0;
+}
diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c
new file mode 100644
index 0000000..b9af930
--- /dev/null
+++ b/ripngd/ripng_peer.c
@@ -0,0 +1,220 @@
+/* RIPng peer support
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING.  If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  
+ */
+
+/* RIPng support added by Vincent Jardin <vincent.jardin@6wind.com>
+ * Copyright (C) 2002 6WIND
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "linklist.h"
+#include "thread.h"
+#include "memory.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_nexthop.h"
+
+
+/* Linked list of RIPng peer. */
+struct list *peer_list;
+
+struct ripng_peer *
+ripng_peer_new ()
+{
+  struct ripng_peer *new;
+
+  new = XMALLOC (MTYPE_RIPNG_PEER, sizeof (struct ripng_peer));
+  memset (new, 0, sizeof (struct ripng_peer));
+  return new;
+}
+
+void
+ripng_peer_free (struct ripng_peer *peer)
+{
+  XFREE (MTYPE_RIPNG_PEER, peer);
+}
+
+struct ripng_peer *
+ripng_peer_lookup (struct in6_addr *addr)
+{
+  struct ripng_peer *peer;
+  struct listnode *nn;
+
+  LIST_LOOP (peer_list, peer, nn)
+    {
+      if (IPV6_ADDR_SAME (&peer->addr, addr))
+	return peer;
+    }
+  return NULL;
+}
+
+struct ripng_peer *
+ripng_peer_lookup_next (struct in6_addr *addr)
+{
+  struct ripng_peer *peer;
+  struct listnode *nn;
+
+  LIST_LOOP (peer_list, peer, nn)
+    {
+      if (addr6_cmp(&peer->addr, addr) > 0) 
+	return peer;
+    }
+  return NULL;
+}
+
+/* RIPng peer is timeout.
+ * Garbage collector.
+ **/
+int
+ripng_peer_timeout (struct thread *t)
+{
+  struct ripng_peer *peer;
+
+  peer = THREAD_ARG (t);
+  listnode_delete (peer_list, peer);
+  ripng_peer_free (peer);
+
+  return 0;
+}
+
+/* Get RIPng peer.  At the same time update timeout thread. */
+struct ripng_peer *
+ripng_peer_get (struct in6_addr *addr)
+{
+  struct ripng_peer *peer;
+
+  peer = ripng_peer_lookup (addr);
+
+  if (peer)
+    {
+      if (peer->t_timeout)
+	thread_cancel (peer->t_timeout);
+    }
+  else
+    {
+      peer = ripng_peer_new ();
+      peer->addr = *addr; /* XXX */
+      listnode_add_sort (peer_list, peer);
+    }
+
+  /* Update timeout thread. */
+  peer->t_timeout = thread_add_timer (master, ripng_peer_timeout, peer,
+				      RIPNG_PEER_TIMER_DEFAULT);
+
+  /* Last update time set. */
+  time (&peer->uptime);
+  
+  return peer;
+}
+
+void
+ripng_peer_update (struct sockaddr_in6 *from, u_char version)
+{
+  struct ripng_peer *peer;
+  peer = ripng_peer_get (&from->sin6_addr);
+  peer->version = version;
+}
+
+void
+ripng_peer_bad_route (struct sockaddr_in6 *from)
+{
+  struct ripng_peer *peer;
+  peer = ripng_peer_get (&from->sin6_addr);
+  peer->recv_badroutes++;
+}
+
+void
+ripng_peer_bad_packet (struct sockaddr_in6 *from)
+{
+  struct ripng_peer *peer;
+  peer = ripng_peer_get (&from->sin6_addr);
+  peer->recv_badpackets++;
+}
+
+/* Display peer uptime. */
+char *
+ripng_peer_uptime (struct ripng_peer *peer, char *buf, size_t len)
+{
+  time_t uptime;
+  struct tm *tm;
+
+  /* If there is no connection has been done before print `never'. */
+  if (peer->uptime == 0)
+    {
+      snprintf (buf, len, "never   ");
+      return buf;
+    }
+
+  /* Get current time. */
+  uptime = time (NULL);
+  uptime -= peer->uptime;
+  tm = gmtime (&uptime);
+
+  /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+
+  if (uptime < ONE_DAY_SECOND)
+    snprintf (buf, len, "%02d:%02d:%02d", 
+	      tm->tm_hour, tm->tm_min, tm->tm_sec);
+  else if (uptime < ONE_WEEK_SECOND)
+    snprintf (buf, len, "%dd%02dh%02dm", 
+	      tm->tm_yday, tm->tm_hour, tm->tm_min);
+  else
+    snprintf (buf, len, "%02dw%dd%02dh", 
+	      tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
+  return buf;
+}
+
+void
+ripng_peer_display (struct vty *vty)
+{
+  struct ripng_peer *peer;
+  struct listnode *nn;
+#define RIPNG_UPTIME_LEN 25
+  char timebuf[RIPNG_UPTIME_LEN];
+
+  LIST_LOOP (peer_list, peer, nn)
+    {
+      vty_out (vty, "    %s %s%14s %10d %10d %10d      %s%s", inet6_ntop (&peer->addr),
+               VTY_NEWLINE, " ",
+	       peer->recv_badpackets, peer->recv_badroutes,
+	       ZEBRA_RIPNG_DISTANCE_DEFAULT,
+	       ripng_peer_uptime (peer, timebuf, RIPNG_UPTIME_LEN),
+	       VTY_NEWLINE);
+    }
+}
+
+int
+ripng_peer_list_cmp (struct ripng_peer *p1, struct ripng_peer *p2)
+{
+  return addr6_cmp(&p1->addr, &p2->addr) > 0;
+}
+
+void
+ripng_peer_init ()
+{
+  peer_list = list_new ();
+  peer_list->cmp = (int (*)(void *, void *)) ripng_peer_list_cmp;
+}
diff --git a/ripngd/ripng_route.c b/ripngd/ripng_route.c
index 27475f0..9c3c4f9 100644
--- a/ripngd/ripng_route.c
+++ b/ripngd/ripng_route.c
@@ -26,6 +26,7 @@
 #include "table.h"
 #include "memory.h"
 #include "if.h"
+#include "vty.h"
 
 #include "ripngd/ripngd.h"
 #include "ripngd/ripng_route.h"
diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h
index 283d826..db53f14 100644
--- a/ripngd/ripng_route.h
+++ b/ripngd/ripng_route.h
@@ -36,6 +36,12 @@
 
   /* Tag field of RIPng packet.*/
   u_short tag;		
+
+  /* Route-map futures - this variables can be changed. */
+  struct in6_addr nexthop_out;
+  u_char metric_set;
+  u_char metric_out;
+  u_short tag_out;
 };
 
 void
diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c
index 832f17c..0bd7e44 100644
--- a/ripngd/ripng_routemap.c
+++ b/ripngd/ripng_routemap.c
@@ -26,58 +26,10 @@
 #include "prefix.h"
 #include "routemap.h"
 #include "command.h"
+#include "sockunion.h"
 
 #include "ripngd/ripngd.h"
 
-#if 0
-/* `match interface IFNAME' */
-route_map_result_t
-route_match_interface (void *rule, struct prefix *prefix,
-		       route_map_object_t type, void *object)
-{
-  struct ripng_info *rinfo;
-  struct interface *ifp;
-  char *ifname;
-
-  if (type == ROUTE_MAP_RIPNG)
-    {
-      ifname = rule;
-      ifp = if_lookup_by_name(ifname);
-
-      if (!ifp)
-	return RM_NOMATCH;
-
-      rinfo = object;
-
-      if (rinfo->ifindex == ifp->ifindex)
-	return RM_MATCH;
-      else
-	return RM_NOMATCH;
-    }
-  return RM_NOMATCH;
-}
-
-void *
-route_match_interface_compile (char *arg)
-{
-  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
-}
-
-void
-route_match_interface_free (void *rule)
-{
-  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
-}
-
-struct route_map_rule_cmd route_match_interface_cmd =
-{
-  "interface",
-  route_match_interface,
-  route_match_interface_compile,
-  route_match_interface_free
-};
-#endif /* 0 */
-
 struct rip_metric_modifier
 {
   enum 
@@ -90,100 +42,6 @@
   u_char metric;
 };
 
-route_map_result_t
-route_set_metric (void *rule, struct prefix *prefix, 
-		  route_map_object_t type, void *object)
-{
-  if (type == RMAP_RIPNG)
-    {
-      struct rip_metric_modifier *mod;
-      struct ripng_info *rinfo;
-
-      mod = rule;
-      rinfo = object;
-
-      if (mod->type == metric_increment)
-	rinfo->metric += mod->metric;
-      else if (mod->type == metric_decrement)
-	rinfo->metric -= mod->metric;
-      else if (mod->type == metric_absolute)
-	rinfo->metric = mod->metric;
-
-      if (rinfo->metric < 1)
-	rinfo->metric = 1;
-      if (rinfo->metric > RIPNG_METRIC_INFINITY)
-	rinfo->metric = RIPNG_METRIC_INFINITY;
-
-      rinfo->metric_set = 1;
-    }
-  return RMAP_OKAY;
-}
-
-void *
-route_set_metric_compile (char *arg)
-{
-  int len;
-  char *pnt;
-  int type;
-  long metric;
-  char *endptr = NULL;
-  struct rip_metric_modifier *mod;
-
-  len = strlen (arg);
-  pnt = arg;
-
-  if (len == 0)
-    return NULL;
-
-  /* Examine first character. */
-  if (arg[0] == '+')
-    {
-      type = metric_increment;
-      pnt++;
-    }
-  else if (arg[0] == '-')
-    {
-      type = metric_decrement;
-      pnt++;
-    }
-  else
-    type = metric_absolute;
-
-  /* Check beginning with digit string. */
-  if (*pnt < '0' || *pnt > '9')
-    return NULL;
-
-  /* Convert string to integer. */
-  metric = strtol (pnt, &endptr, 10);
-
-  if (metric == LONG_MAX || *endptr != '\0')
-    return NULL;
-  /* Commented out by Hasso Tepper, to avoid problems in vtysh. */
-  /* if (metric < 0 || metric > RIPNG_METRIC_INFINITY) */
-  if (metric < 0)
-    return NULL;
-
-  mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, 
-		 sizeof (struct rip_metric_modifier));
-  mod->type = type;
-  mod->metric = metric;
-
-  return mod;
-}
-
-void
-route_set_metric_free (void *rule)
-{
-  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
-}
-
-struct route_map_rule_cmd route_set_metric_cmd = 
-{
-  "metric",
-  route_set_metric,
-  route_set_metric_compile,
-  route_set_metric_free,
-};
 
 int
 ripng_route_match_add (struct vty *vty, struct route_map_index *index,
@@ -281,12 +139,411 @@
   return CMD_SUCCESS;
 }
 
-#if 0
+/* `match metric METRIC' */
+/* Match function return 1 if match is success else return zero. */
+route_map_result_t
+route_match_metric (void *rule, struct prefix *prefix, 
+		    route_map_object_t type, void *object)
+{
+  u_int32_t *metric;
+  struct ripng_info *rinfo;
+
+  if (type == RMAP_RIPNG)
+    {
+      metric = rule;
+      rinfo = object;
+    
+      if (rinfo->metric == *metric)
+	return RMAP_MATCH;
+      else
+	return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match metric' match statement. `arg' is METRIC value */
+void *
+route_match_metric_compile (char *arg)
+{
+  u_int32_t *metric;
+
+  metric = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t));
+  *metric = atoi (arg);
+
+  if(*metric > 0)
+    return metric;
+
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, metric);
+  return NULL;
+}
+
+/* Free route map's compiled `match metric' value. */
+void
+route_match_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+struct route_map_rule_cmd route_match_metric_cmd =
+{
+  "metric",
+  route_match_metric,
+  route_match_metric_compile,
+  route_match_metric_free
+};
+
+/* `match interface IFNAME' */
+/* Match function return 1 if match is success else return zero. */
+route_map_result_t
+route_match_interface (void *rule, struct prefix *prefix,
+		       route_map_object_t type, void *object)
+{
+  struct ripng_info *rinfo;
+  struct interface *ifp;
+  char *ifname;
+
+  if (type == RMAP_RIPNG)
+    {
+      ifname = rule;
+      ifp = if_lookup_by_name(ifname);
+
+      if (!ifp)
+	return RMAP_NOMATCH;
+
+      rinfo = object;
+
+      if (rinfo->ifindex == ifp->ifindex)
+	return RMAP_MATCH;
+      else
+	return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match interface' match statement. `arg' is IFNAME value */
+void *
+route_match_interface_compile (char *arg)
+{
+  return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+void
+route_match_interface_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_match_interface_cmd =
+{
+  "interface",
+  route_match_interface,
+  route_match_interface_compile,
+  route_match_interface_free
+};
+
+/* `match tag TAG' */
+/* Match function return 1 if match is success else return zero. */
+route_map_result_t
+route_match_tag (void *rule, struct prefix *prefix, 
+		    route_map_object_t type, void *object)
+{
+  u_short *tag;
+  struct ripng_info *rinfo;
+
+  if (type == RMAP_RIPNG)
+    {
+      tag = rule;
+      rinfo = object;
+
+      /* The information stored by rinfo is host ordered. */
+      if (rinfo->tag == *tag)
+	return RMAP_MATCH;
+      else
+	return RMAP_NOMATCH;
+    }
+  return RMAP_NOMATCH;
+}
+
+/* Route map `match tag' match statement. `arg' is TAG value */
+void *
+route_match_tag_compile (char *arg)
+{
+  u_short *tag;
+
+  tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short));
+  *tag = atoi (arg);
+
+  return tag;
+}
+
+/* Free route map's compiled `match tag' value. */
+void
+route_match_tag_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for tag matching. */
+struct route_map_rule_cmd route_match_tag_cmd =
+{
+  "tag",
+  route_match_tag,
+  route_match_tag_compile,
+  route_match_tag_free
+};
+
+/* `set metric METRIC' */
+
+/* Set metric to attribute. */
+route_map_result_t
+route_set_metric (void *rule, struct prefix *prefix, 
+		  route_map_object_t type, void *object)
+{
+  if (type == RMAP_RIPNG)
+    {
+      struct rip_metric_modifier *mod;
+      struct ripng_info *rinfo;
+
+      mod = rule;
+      rinfo = object;
+
+      if (mod->type == metric_increment)
+	rinfo->metric_out += mod->metric;
+      else if (mod->type == metric_decrement)
+	rinfo->metric_out-= mod->metric;
+      else if (mod->type == metric_absolute)
+	rinfo->metric_out = mod->metric;
+
+      if (rinfo->metric_out < 1)
+	rinfo->metric_out = 1;
+      if (rinfo->metric_out > RIPNG_METRIC_INFINITY)
+	rinfo->metric_out = RIPNG_METRIC_INFINITY;
+
+      rinfo->metric_set = 1;
+    }
+  return RMAP_OKAY;
+}
+
+/* set metric compilation. */
+void *
+route_set_metric_compile (char *arg)
+{
+  int len;
+  char *pnt;
+  int type;
+  long metric;
+  char *endptr = NULL;
+  struct rip_metric_modifier *mod;
+
+  len = strlen (arg);
+  pnt = arg;
+
+  if (len == 0)
+    return NULL;
+
+  /* Examine first character. */
+  if (arg[0] == '+')
+    {
+      type = metric_increment;
+      pnt++;
+    }
+  else if (arg[0] == '-')
+    {
+      type = metric_decrement;
+      pnt++;
+    }
+  else
+    type = metric_absolute;
+
+  /* Check beginning with digit string. */
+  if (*pnt < '0' || *pnt > '9')
+    return NULL;
+
+  /* Convert string to integer. */
+  metric = strtol (pnt, &endptr, 10);
+
+  if (metric == LONG_MAX || *endptr != '\0')
+    return NULL;
+  /* Commented out by Hasso Tepper, to avoid problems in vtysh. */
+  /* if (metric < 0 || metric > RIPNG_METRIC_INFINITY) */
+  if (metric < 0)
+    return NULL;
+
+  mod = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, 
+		 sizeof (struct rip_metric_modifier));
+  mod->type = type;
+  mod->metric = metric;
+
+  return mod;
+}
+
+/* Free route map's compiled `set metric' value. */
+void
+route_set_metric_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+struct route_map_rule_cmd route_set_metric_cmd = 
+{
+  "metric",
+  route_set_metric,
+  route_set_metric_compile,
+  route_set_metric_free,
+};
+
+/* `set ipv6 next-hop local IP_ADDRESS' */
+
+/* Set nexthop to object.  ojbect must be pointer to struct attr. */
+route_map_result_t
+route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix, 
+		      route_map_object_t type, void *object)
+{
+  struct in6_addr *address;
+  struct ripng_info *rinfo;
+
+  if(type == RMAP_RIPNG)
+    {
+      /* Fetch routemap's rule information. */
+      address = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->nexthop_out = *address;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `ipv6 nexthop local' compile function.  Given string is converted
+   to struct in6_addr structure. */
+void *
+route_set_ipv6_nexthop_local_compile (char *arg)
+{
+  int ret;
+  struct in6_addr *address;
+
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr));
+
+  ret = inet_pton (AF_INET6, arg, address);
+
+  if (ret == 0)
+    {
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
+    }
+
+  return address;
+}
+
+/* Free route map's compiled `ipv6 nexthop local' value. */
+void
+route_set_ipv6_nexthop_local_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ipv6 nexthop local set. */
+struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd =
+{
+  "ipv6 next-hop local",
+  route_set_ipv6_nexthop_local,
+  route_set_ipv6_nexthop_local_compile,
+  route_set_ipv6_nexthop_local_free
+};
+
+/* `set tag TAG' */
+
+/* Set tag to object.  ojbect must be pointer to struct attr. */
+route_map_result_t
+route_set_tag (void *rule, struct prefix *prefix, 
+		      route_map_object_t type, void *object)
+{
+  u_short *tag;
+  struct ripng_info *rinfo;
+
+  if(type == RMAP_RIPNG)
+    {
+      /* Fetch routemap's rule information. */
+      tag = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->tag_out = *tag;
+    }
+
+  return RMAP_OKAY;
+}
+
+/* Route map `tag' compile function.  Given string is converted
+   to u_short. */
+void *
+route_set_tag_compile (char *arg)
+{
+  u_short *tag;
+
+  tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short));
+  *tag = atoi (arg);
+
+  return tag;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+void
+route_set_tag_free (void *rule)
+{
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for tag set. */
+struct route_map_rule_cmd route_set_tag_cmd =
+{
+  "tag",
+  route_set_tag,
+  route_set_tag_compile,
+  route_set_tag_free
+};
+
+#define MATCH_STR "Match values from routing table\n"
+#define SET_STR "Set values in destination routing protocol\n"
+
+DEFUN (match_metric, 
+       match_metric_cmd,
+       "match metric <0-4294967295>",
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+{
+  return ripng_route_match_add (vty, vty->index, "metric", argv[0]);
+}
+
+DEFUN (no_match_metric,
+       no_match_metric_cmd,
+       "no match metric",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n")
+{
+  if (argc == 0)
+    return ripng_route_match_delete (vty, vty->index, "metric", NULL);
+
+  return ripng_route_match_delete (vty, vty->index, "metric", argv[0]);
+}
+
+ALIAS (no_match_metric,
+       no_match_metric_val_cmd,
+       "no match metric <0-4294967295>",
+       NO_STR
+       MATCH_STR
+       "Match metric of route\n"
+       "Metric value\n")
+
 DEFUN (match_interface,
        match_interface_cmd,
        "match interface WORD",
-       "Match value\n"
-       "Interface\n"
+       MATCH_STR
+       "Match first hop interface of route\n"
        "Interface name\n")
 {
   return ripng_route_match_add (vty, vty->index, "interface", argv[0]);
@@ -294,22 +551,64 @@
 
 DEFUN (no_match_interface,
        no_match_interface_cmd,
-       "no match interface WORD",
+       "no match interface",
        NO_STR
-       "Match value\n"
-       "Interface\n"
-       "Interface name\n")
+       MATCH_STR
+       "Match first hop interface of route\n")
 {
+  if (argc == 0)
+    return ripng_route_match_delete (vty, vty->index, "interface", NULL);
+
   return ripng_route_match_delete (vty, vty->index, "interface", argv[0]);
 }
-#endif /* 0 */
+
+ALIAS (no_match_interface,
+       no_match_interface_val_cmd,
+       "no match interface WORD",
+       NO_STR
+       MATCH_STR
+       "Match first hop interface of route\n"
+       "Interface name\n")
+
+DEFUN (match_tag,
+       match_tag_cmd,
+       "match tag <0-65535>",
+       MATCH_STR
+       "Match tag of route\n"
+       "Metric value\n")
+{
+  return ripng_route_match_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_match_tag,
+       no_match_tag_cmd,
+       "no match tag",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n")
+{
+  if (argc == 0)
+    return ripng_route_match_delete (vty, vty->index, "tag", NULL);
+
+  return ripng_route_match_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_match_tag,
+       no_match_tag_val_cmd,
+       "no match tag <0-65535>",
+       NO_STR
+       MATCH_STR
+       "Match tag of route\n"
+       "Metric value\n")
+
+/* set functions */
 
 DEFUN (set_metric,
        set_metric_cmd,
        "set metric <0-4294967295>",
        "Set value\n"
-       "Metric\n"
-       "METRIC value\n")
+       "Metric value for destination routing protocol\n"
+       "Metric value\n")
 {
   return ripng_route_set_add (vty, vty->index, "metric", argv[0]);
 }
@@ -335,20 +634,122 @@
        "Metric value for destination routing protocol\n"
        "Metric value\n")
 
+DEFUN (set_ipv6_nexthop_local,
+       set_ipv6_nexthop_local_cmd,
+       "set ipv6 next-hop local X:X::X:X",
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n"
+       "IPv6 address of next hop\n")
+{
+  union sockunion su;
+  int ret;
+
+  ret = str2sockunion (argv[0], &su);
+  if (ret < 0)
+    {
+      vty_out (vty, "%% Malformed next-hop local address%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return ripng_route_set_add (vty, vty->index, "ipv6 next-hop local", argv[0]);
+}
+
+DEFUN (no_set_ipv6_nexthop_local,
+       no_set_ipv6_nexthop_local_cmd,
+       "no set ipv6 next-hop local",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n")
+{
+  if (argc == 0)
+    return ripng_route_set_delete (vty, vty->index, "ipv6 next-hop local", NULL);
+
+  return ripng_route_set_delete (vty, vty->index, "ipv6 next-hop local", argv[0]);
+}
+
+ALIAS (no_set_ipv6_nexthop_local,
+       no_set_ipv6_nexthop_local_val_cmd,
+       "no set ipv6 next-hop local X:X::X:X",
+       NO_STR
+       SET_STR
+       IPV6_STR
+       "IPv6 next-hop address\n"
+       "IPv6 local address\n"
+       "IPv6 address of next hop\n")
+
+DEFUN (set_tag,
+       set_tag_cmd,
+       "set tag <0-65535>",
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+{
+  return ripng_route_set_add (vty, vty->index, "tag", argv[0]);
+}
+
+DEFUN (no_set_tag,
+       no_set_tag_cmd,
+       "no set tag",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n")
+{
+  if (argc == 0)
+    return ripng_route_set_delete (vty, vty->index, "tag", NULL);
+
+  return ripng_route_set_delete (vty, vty->index, "tag", argv[0]);
+}
+
+ALIAS (no_set_tag,
+       no_set_tag_val_cmd,
+       "no set tag <0-65535>",
+       NO_STR
+       SET_STR
+       "Tag value for routing protocol\n"
+       "Tag value\n")
+
+void
+ripng_route_map_reset ()
+{
+  /* XXX ??? */
+  ;
+}
+
 void
 ripng_route_map_init ()
 {
   route_map_init ();
   route_map_init_vty ();
 
-  /* route_map_install_match (&route_match_interface_cmd); */
-  route_map_install_set (&route_set_metric_cmd);
+  route_map_install_match (&route_match_metric_cmd);
+  route_map_install_match (&route_match_interface_cmd);
+  route_map_install_match (&route_match_tag_cmd);
 
-  /*
+  route_map_install_set (&route_set_metric_cmd);
+  route_map_install_set (&route_set_ipv6_nexthop_local_cmd);
+  route_map_install_set (&route_set_tag_cmd);
+
+  install_element (RMAP_NODE, &match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_cmd);
+  install_element (RMAP_NODE, &no_match_metric_val_cmd);
   install_element (RMAP_NODE, &match_interface_cmd);
   install_element (RMAP_NODE, &no_match_interface_cmd);
-  */
+  install_element (RMAP_NODE, &no_match_interface_val_cmd);
+  install_element (RMAP_NODE, &match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_cmd);
+  install_element (RMAP_NODE, &no_match_tag_val_cmd);
 
   install_element (RMAP_NODE, &set_metric_cmd);
   install_element (RMAP_NODE, &no_set_metric_cmd);
+  install_element (RMAP_NODE, &no_set_metric_val_cmd);
+  install_element (RMAP_NODE, &set_ipv6_nexthop_local_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_cmd);
+  install_element (RMAP_NODE, &no_set_ipv6_nexthop_local_val_cmd);
+  install_element (RMAP_NODE, &set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_cmd);
+  install_element (RMAP_NODE, &no_set_tag_val_cmd);
 }
diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c
index 4d8a48d..1af5e24 100644
--- a/ripngd/ripng_zebra.c
+++ b/ripngd/ripng_zebra.c
@@ -133,13 +133,19 @@
     api.metric = 0;
 
   if (command == ZEBRA_IPV6_ROUTE_ADD)
-    ripng_redistribute_add (api.type, 0, &p, ifindex);
+    ripng_redistribute_add (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex, &nexthop);
   else
-    ripng_redistribute_delete (api.type, 0, &p, ifindex);
+    ripng_redistribute_delete (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex);
 
   return 0;
 }
 
+void
+ripng_zclient_reset ()
+{
+  zclient_reset (zclient);
+}
+
 int
 ripng_redistribute_unset (int type)
 {
@@ -156,6 +162,12 @@
   return CMD_SUCCESS;
 }
 
+int
+ripng_redistribute_check (int type)
+{
+  return (zclient->redist[type]);
+}
+
 void
 ripng_redistribute_metric_set (int type, int metric)
 {
@@ -163,11 +175,12 @@
   ripng->route_map[type].metric = metric;
 }
 
-void
+int
 ripng_redistribute_metric_unset (int type)
 {
   ripng->route_map[type].metric_config = 0;
   ripng->route_map[type].metric = 0;
+  return 0;
 }
 
 void
@@ -189,8 +202,42 @@
   ripng->route_map[type].name = NULL;
   ripng->route_map[type].map = NULL;
 }
-
 
+/* Redistribution types */
+static struct {
+  int type;
+  int str_min_len;
+  char *str;
+} redist_type[] = {
+  {ZEBRA_ROUTE_KERNEL,  1, "kernel"},
+  {ZEBRA_ROUTE_CONNECT, 1, "connected"},
+  {ZEBRA_ROUTE_STATIC,  1, "static"},
+  {ZEBRA_ROUTE_OSPF6,   1, "ospf6"},
+  {ZEBRA_ROUTE_BGP,     1, "bgp"},
+  {0, 0, NULL}
+};
+
+void
+ripng_redistribute_clean ()
+{
+  int i;
+
+  for (i = 0; redist_type[i].str; i++)
+    {
+      if (zclient->redist[redist_type[i].type])
+        {
+          if (zclient->sock > 0)
+            zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE,
+                                     zclient->sock, redist_type[i].type);
+
+          zclient->redist[redist_type[i].type] = 0;
+
+          /* Remove the routes from RIPng table. */
+          ripng_redistribute_withdraw (redist_type[i].type);
+        }
+    }
+}
+
 DEFUN (router_zebra,
        router_zebra_cmd,
        "router zebra",
@@ -236,513 +283,205 @@
   return CMD_SUCCESS;
 }
 
-DEFUN (ripng_redistribute_static,
-       ripng_redistribute_static_cmd,
-       "redistribute static",
+DEFUN (ripng_redistribute_type,
+       ripng_redistribute_type_cmd,
+       "redistribute (kernel|connected|static|ospf6|bgp)",
        "Redistribute information from another routing protocol\n"
-       "Static routes\n")
-{
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_STATIC);
-  return CMD_SUCCESS;
-}
-
-DEFUN (no_ripng_redistribute_static,
-       no_ripng_redistribute_static_cmd,
-       "no redistribute static",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Static routes\n")
-{
-  ripng_redistribute_metric_unset (ZEBRA_ROUTE_STATIC);
-  ripng_redistribute_routemap_unset (ZEBRA_ROUTE_STATIC);
-  return ripng_redistribute_unset (ZEBRA_ROUTE_STATIC);
-}
-
-DEFUN (ripng_redistribute_kernel,
-       ripng_redistribute_kernel_cmd,
-       "redistribute kernel",
-       "Redistribute information from another routing protocol\n"
-       "Kernel routes\n")
-{
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_KERNEL);
-  return CMD_SUCCESS;
-}
-
-DEFUN (no_ripng_redistribute_kernel,
-       no_ripng_redistribute_kernel_cmd,
-       "no redistribute kernel",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Kernel routes\n")
-{
-  ripng_redistribute_metric_unset (ZEBRA_ROUTE_KERNEL);
-  ripng_redistribute_routemap_unset (ZEBRA_ROUTE_KERNEL);
-  return ripng_redistribute_unset (ZEBRA_ROUTE_KERNEL);
-}
-
-DEFUN (ripng_redistribute_connected,
-       ripng_redistribute_connected_cmd,
-       "redistribute connected",
-       "Redistribute information from another routing protocol\n"
-       "Connected\n")
-{
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_CONNECT);
-  return CMD_SUCCESS;
-}
-
-DEFUN (no_ripng_redistribute_connected,
-       no_ripng_redistribute_connected_cmd,
-       "no redistribute connected",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Connected\n")
-{
-  ripng_redistribute_metric_unset (ZEBRA_ROUTE_CONNECT);
-  ripng_redistribute_routemap_unset (ZEBRA_ROUTE_CONNECT);
-  return ripng_redistribute_unset (ZEBRA_ROUTE_CONNECT);
-}
-
-DEFUN (ripng_redistribute_bgp,
-       ripng_redistribute_bgp_cmd,
-       "redistribute bgp",
-       "Redistribute information from another routing protocol\n"
+       "Kernel routes\n"
+       "Connected\n"
+       "Static routes\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n")
 {
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_BGP);
-  return CMD_SUCCESS;
+  int i;
+
+  for(i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp (redist_type[i].str, argv[0], 
+		   redist_type[i].str_min_len) == 0) 
+	{
+	  zclient_redistribute_set (zclient, redist_type[i].type);
+	  return CMD_SUCCESS;
+	}
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+	  VTY_NEWLINE);
+
+  return CMD_WARNING;
 }
 
-DEFUN (no_ripng_redistribute_bgp,
-       no_ripng_redistribute_bgp_cmd,
-       "no redistribute bgp",
+DEFUN (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_cmd,
+       "no redistribute (kernel|connected|static|ospf6|bgp)",
        NO_STR
        "Redistribute information from another routing protocol\n"
+       "Kernel routes\n"
+       "Connected\n"
+       "Static routes\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n")
 {
-  ripng_redistribute_metric_unset (ZEBRA_ROUTE_BGP);
-  ripng_redistribute_routemap_unset (ZEBRA_ROUTE_BGP);
-  return ripng_redistribute_unset (ZEBRA_ROUTE_BGP);
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) 
+    {
+      if (strncmp(redist_type[i].str, argv[0], 
+		  redist_type[i].str_min_len) == 0) 
+	{
+	  ripng_redistribute_metric_unset (redist_type[i].type);
+	  ripng_redistribute_routemap_unset (redist_type[i].type);
+	  return ripng_redistribute_unset (redist_type[i].type);
+        }
+    }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+	  VTY_NEWLINE);
+
+  return CMD_WARNING;
 }
 
-DEFUN (ripng_redistribute_ospf6,
-       ripng_redistribute_ospf6_cmd,
-       "redistribute ospf6",
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n")
-{
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_OSPF6);
-  return CMD_SUCCESS;
-}
 
-DEFUN (no_ripng_redistribute_ospf6,
-       no_ripng_redistribute_ospf6_cmd,
-       "no redistribute ospf6",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n")
-{
-  ripng_redistribute_metric_unset (ZEBRA_ROUTE_OSPF6);
-  ripng_redistribute_routemap_unset (ZEBRA_ROUTE_OSPF6);
-  return ripng_redistribute_unset (ZEBRA_ROUTE_OSPF6);
-}
-
-DEFUN (ripng_redistribute_kernel_metric,
-       ripng_redistribute_kernel_metric_cmd,
-       "redistribute kernel metric <0-16>",
+DEFUN (ripng_redistribute_type_metric,
+       ripng_redistribute_type_metric_cmd,
+       "redistribute (kernel|connected|static|ospf6|bgp) metric <0-16>",
        "Redistribute information from another routing protocol\n"
        "Kernel routes\n"
-       "Metric\n"
-       "Metric value\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_KERNEL, atoi (argv[0]));
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_KERNEL);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_kernel,
-       no_ripng_redistribute_kernel_metric_cmd,
-       "no redistribute kernel metric",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Kernel routes\n"
-       "Metric\n")
-
-ALIAS (no_ripng_redistribute_kernel,
-       no_ripng_redistribute_kernel_metric_val_cmd,
-       "no redistribute kernel metric <0-16>",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Kernel routes\n"
-       "Metric\n"
-       "Metric value\n")
-
-DEFUN (ripng_redistribute_connected_metric,
-       ripng_redistribute_connected_metric_cmd,
-       "redistribute connected metric <0-16>",
-       "Redistribute information from another routing protocol\n"
        "Connected\n"
-       "Metric\n"
-       "Metric value\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_CONNECT, atoi (argv[0]));
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_CONNECT);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_connected,
-       no_ripng_redistribute_connected_metric_cmd,
-       "no redistribute connected metric",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Connected\n"
-       "Metric\n")
-
-ALIAS (no_ripng_redistribute_connected,
-       no_ripng_redistribute_connected_metric_val_cmd,
-       "no redistribute connected metric <0-16>",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Connected\n"
-       "Metric\n"
-       "Metric value\n")
-
-DEFUN (ripng_redistribute_static_metric,
-       ripng_redistribute_static_metric_cmd,
-       "redistribute static metric <0-16>",
-       "Redistribute information from another routing protocol\n"
        "Static routes\n"
-       "Metric\n"
-       "Metric value\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_STATIC, atoi (argv[0]));
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_STATIC);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_static,
-       no_ripng_redistribute_static_metric_cmd,
-       "no redistribute static metric",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Static routes\n"
-       "Metric\n")
-
-ALIAS (no_ripng_redistribute_static,
-       no_ripng_redistribute_static_metric_val_cmd,
-       "no redistribute static metric <0-16>",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Static routes\n"
-       "Metric\n"
-       "Metric value\n")
-
-DEFUN (ripng_redistribute_ospf6_metric,
-       ripng_redistribute_ospf6_metric_cmd,
-       "redistribute ospf6 metric <0-16>",
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Metric\n"
-       "Metric value\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_OSPF6, atoi (argv[0]));
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_OSPF6);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_ospf6,
-       no_ripng_redistribute_ospf6_metric_cmd,
-       "no redistribute ospf6 metric",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Metric\n")
-
-ALIAS (no_ripng_redistribute_ospf6,
-       no_ripng_redistribute_ospf6_metric_val_cmd,
-       "no redistribute ospf6 metric <0-16>",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Metric\n"
-       "Metric value\n")
-
-DEFUN (ripng_redistribute_bgp_metric,
-       ripng_redistribute_bgp_metric_cmd,
-       "redistribute bgp metric <0-16>",
-       "Redistribute information from another routing protocol\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n"
        "Metric\n"
        "Metric value\n")
 {
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_BGP, atoi (argv[0]));
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_BGP);
-  return CMD_SUCCESS;
+  int i;
+  int metric;
+
+  metric = atoi (argv[1]);
+
+  for (i = 0; redist_type[i].str; i++) {
+    if (strncmp(redist_type[i].str, argv[0],
+		redist_type[i].str_min_len) == 0) 
+      {
+	ripng_redistribute_metric_set (redist_type[i].type, metric);
+	zclient_redistribute_set (zclient, redist_type[i].type);
+	return CMD_SUCCESS;
+      }
+  }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+	  VTY_NEWLINE);
+
+  return CMD_WARNING;
 }
 
-ALIAS (no_ripng_redistribute_bgp,
-       no_ripng_redistribute_bgp_metric_cmd,
-       "no redistribute bgp metric",
+ALIAS (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_metric_cmd,
+       "no redistribute (kernel|connected|static|ospf6|bgp) metric <0-16>",
        NO_STR
        "Redistribute information from another routing protocol\n"
-       "Border Gateway Protocol (BGP)\n"
-       "Metric\n")
-
-ALIAS (no_ripng_redistribute_bgp,
-       no_ripng_redistribute_bgp_metric_val_cmd,
-       "no redistribute bgp metric <0-16>",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
+       "Kernel routes\n"
+       "Connected\n"
+       "Static routes\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n"
        "Metric\n"
        "Metric value\n")
 
-DEFUN (ripng_redistribute_kernel_routemap,
-       ripng_redistribute_kernel_routemap_cmd,
-       "redistribute kernel route-map WORD",
+DEFUN (ripng_redistribute_type_routemap,
+       ripng_redistribute_type_routemap_cmd,
+       "redistribute (kernel|connected|static|ospf6|bgp) route-map WORD",
        "Redistribute information from another routing protocol\n"
        "Kernel routes\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_KERNEL, argv[0]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_KERNEL);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_kernel,
-       no_ripng_redistribute_kernel_routemap_cmd,
-       "no redistribute kernel route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Kernel routes\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_connected_routemap,
-       ripng_redistribute_connected_routemap_cmd,
-       "redistribute connected route-map WORD",
-       "Redistribute information from another routing protocol\n"
        "Connected\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_CONNECT, argv[0]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_CONNECT);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_connected,
-       no_ripng_redistribute_connected_routemap_cmd,
-       "no redistribute connected route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Connected\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_static_routemap,
-       ripng_redistribute_static_routemap_cmd,
-       "redistribute static route-map WORD",
-       "Redistribute information from another routing protocol\n"
        "Static routes\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_STATIC, argv[0]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_STATIC);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_static,
-       no_ripng_redistribute_static_routemap_cmd,
-       "no redistribute static route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Static routes\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_ospf6_routemap,
-       ripng_redistribute_ospf6_routemap_cmd,
-       "redistribute ospf6 route-map WORD",
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_OSPF6, argv[0]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_OSPF6);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_ospf6,
-       no_ripng_redistribute_ospf6_routemap_cmd,
-       "no redistribute ospf6 route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_bgp_routemap,
-       ripng_redistribute_bgp_routemap_cmd,
-       "redistribute bgp route-map WORD",
-       "Redistribute information from another routing protocol\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n"
        "Route map reference\n"
        "Pointer to route-map entries\n")
 {
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_BGP, argv[0]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_BGP);
-  return CMD_SUCCESS;
+  int i;
+
+  for (i = 0; redist_type[i].str; i++) {
+    if (strncmp(redist_type[i].str, argv[0],
+		redist_type[i].str_min_len) == 0) 
+      {
+	ripng_redistribute_routemap_set (redist_type[i].type, argv[1]);
+	zclient_redistribute_set (zclient, redist_type[i].type);
+	return CMD_SUCCESS;
+      }
+  }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+	  VTY_NEWLINE);
+
+  return CMD_WARNING;
 }
 
-ALIAS (no_ripng_redistribute_bgp,
-       no_ripng_redistribute_bgp_routemap_cmd,
-       "no redistribute bgp route-map WORD",
+ALIAS (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_routemap_cmd,
+       "no redistribute (kernel|connected|static|ospf6|bgp) route-map WORD",
        NO_STR
        "Redistribute information from another routing protocol\n"
+       "Kernel routes\n"
+       "Connected\n"
+       "Static routes\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n"
        "Route map reference\n"
        "Pointer to route-map entries\n")
 
-DEFUN (ripng_redistribute_kernel_metric_routemap,
-       ripng_redistribute_kernel_metric_routemap_cmd,
-       "redistribute kernel metric <0-16> route-map WORD",
+DEFUN (ripng_redistribute_type_metric_routemap,
+       ripng_redistribute_type_metric_routemap_cmd,
+       "redistribute (kernel|connected|static|ospf6|bgp) metric <0-16> route-map WORD",
        "Redistribute information from another routing protocol\n"
        "Kernel routes\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_KERNEL, atoi (argv[0]));
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_KERNEL, argv[1]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_KERNEL);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_kernel,
-       no_ripng_redistribute_kernel_metric_routemap_cmd,
-       "no redistribute kernel metric <0-16> route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Kernel routes\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_connected_metric_routemap,
-       ripng_redistribute_connected_metric_routemap_cmd,
-       "redistribute connected metric <0-16> route-map WORD",
-       "Redistribute information from another routing protocol\n"
        "Connected\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_CONNECT, atoi (argv[0]));
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_CONNECT, argv[1]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_CONNECT);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_connected,
-       no_ripng_redistribute_connected_metric_routemap_cmd,
-       "no redistribute connected metric <0-16> route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Connected\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_static_metric_routemap,
-       ripng_redistribute_static_metric_routemap_cmd,
-       "redistribute static metric <0-16> route-map WORD",
-       "Redistribute information from another routing protocol\n"
        "Static routes\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_STATIC, atoi (argv[0]));
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_STATIC, argv[1]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_STATIC);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_static,
-       no_ripng_redistribute_static_metric_routemap_cmd,
-       "no redistribute static metric <0-16> route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "Static routes\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_ospf6_metric_routemap,
-       ripng_redistribute_ospf6_metric_routemap_cmd,
-       "redistribute ospf6 metric <0-16> route-map WORD",
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-{
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_OSPF6, atoi (argv[0]));
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_OSPF6, argv[1]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_OSPF6);
-  return CMD_SUCCESS;
-}
-
-ALIAS (no_ripng_redistribute_ospf6,
-       no_ripng_redistribute_ospf6_metric_routemap_cmd,
-       "no redistribute ospf6 metric <0-16> route-map WORD",
-       NO_STR
-       "Redistribute information from another routing protocol\n"
-       "IPv6 Open Shortest Path First (OSPFv3)\n"
-       "Metric\n"
-       "Metric value\n"
-       "Route map reference\n"
-       "Pointer to route-map entries\n")
-
-DEFUN (ripng_redistribute_bgp_metric_routemap,
-       ripng_redistribute_bgp_metric_routemap_cmd,
-       "redistribute bgp metric <0-16> route-map WORD",
-       "Redistribute information from another routing protocol\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n"
        "Metric\n"
        "Metric value\n"
        "Route map reference\n"
        "Pointer to route-map entries\n")
 {
-  ripng_redistribute_metric_set (ZEBRA_ROUTE_BGP, atoi (argv[0]));
-  ripng_redistribute_routemap_set (ZEBRA_ROUTE_BGP, argv[1]);
-  zclient_redistribute_set (zclient, ZEBRA_ROUTE_BGP);
-  return CMD_SUCCESS;
+  int i;
+  int metric;
+
+  metric = atoi (argv[1]);
+
+  for (i = 0; redist_type[i].str; i++) {
+    if (strncmp(redist_type[i].str, argv[0],
+		redist_type[i].str_min_len) == 0) 
+      {
+	ripng_redistribute_metric_set (redist_type[i].type, metric);
+	ripng_redistribute_routemap_set (redist_type[i].type, argv[2]);
+	zclient_redistribute_set (zclient, redist_type[i].type);
+	return CMD_SUCCESS;
+      }
+  }
+
+  vty_out(vty, "Invalid type %s%s", argv[0],
+	  VTY_NEWLINE);
+
+  return CMD_WARNING;
 }
 
-ALIAS (no_ripng_redistribute_bgp,
-       no_ripng_redistribute_bgp_metric_routemap_cmd,
-       "no redistribute bgp metric <0-16> route-map WORD",
+ALIAS (no_ripng_redistribute_type,
+       no_ripng_redistribute_type_metric_routemap_cmd,
+       "no redistribute (kernel|connected|static|ospf6|bgp) metric <0-16> route-map WORD",
        NO_STR
        "Redistribute information from another routing protocol\n"
+       "Kernel routes\n"
+       "Connected\n"
+       "Static routes\n"
+       "Open Shortest Path First (OSPFv3)\n"
        "Border Gateway Protocol (BGP)\n"
-       "Metric\n"
-       "Metric value\n"
        "Route map reference\n"
        "Pointer to route-map entries\n")
 
 void
-ripng_redistribute_write (struct vty *vty)
+ripng_redistribute_write (struct vty *vty, int config_mode)
 {
   int i;
   char *str[] = { "system", "kernel", "connected", "static", "rip",
@@ -751,24 +490,29 @@
   for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
     if (i != zclient->redist_default && zclient->redist[i])
       {
-	if (ripng->route_map[i].metric_config)
-	  {
-	    if (ripng->route_map[i].name)
-	      vty_out (vty, " redistribute %s metric %d route-map %s%s",
-		       str[i], ripng->route_map[i].metric,
-		       ripng->route_map[i].name, VTY_NEWLINE);
-	    else
-	      vty_out (vty, " redistribute %s metric %d%s",
-		       str[i], ripng->route_map[i].metric, VTY_NEWLINE);
-	  }
-	else
-	  {
-	    if (ripng->route_map[i].name)
-	      vty_out (vty, " redistribute %s route-map %s%s",
-		       str[i], ripng->route_map[i].name, VTY_NEWLINE);
-	    else
-	      vty_out (vty, " redistribute %s%s", str[i], VTY_NEWLINE);
-	  }
+      if (config_mode)
+	{
+	  if (ripng->route_map[i].metric_config)
+	    {
+	      if (ripng->route_map[i].name)
+		vty_out (vty, " redistribute %s metric %d route-map %s%s",
+			 str[i], ripng->route_map[i].metric,
+			ripng->route_map[i].name, VTY_NEWLINE);
+	      else
+		vty_out (vty, " redistribute %s metric %d%s",
+			str[i], ripng->route_map[i].metric, VTY_NEWLINE);
+	    }
+	  else
+	    {
+	      if (ripng->route_map[i].name)
+		vty_out (vty, " redistribute %s route-map %s%s",
+			 str[i], ripng->route_map[i].name, VTY_NEWLINE);
+	      else
+		vty_out (vty, " redistribute %s%s", str[i], VTY_NEWLINE);
+	    }
+	}
+      else
+	vty_out (vty, "    %s", str[i]);
       }
 }
 
@@ -823,55 +567,14 @@
   install_default (ZEBRA_NODE);
   install_element (ZEBRA_NODE, &ripng_redistribute_ripng_cmd);
   install_element (ZEBRA_NODE, &no_ripng_redistribute_ripng_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_static_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_static_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_kernel_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_kernel_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_connected_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_connected_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_bgp_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_bgp_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_ospf6_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_ospf6_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_kernel_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_kernel_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_kernel_metric_val_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_connected_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_connected_metric_cmd);
-  install_element (RIPNG_NODE,
-		   &no_ripng_redistribute_connected_metric_val_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_static_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_static_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_static_metric_val_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_ospf6_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_ospf6_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_ospf6_metric_val_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_bgp_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_bgp_metric_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_bgp_metric_val_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_kernel_routemap_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_kernel_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_connected_routemap_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_connected_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_static_routemap_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_static_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_ospf6_routemap_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_ospf6_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_bgp_routemap_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_bgp_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_kernel_metric_routemap_cmd);
-  install_element (RIPNG_NODE,
-		   &no_ripng_redistribute_kernel_metric_routemap_cmd);
-  install_element (RIPNG_NODE,
-		   &ripng_redistribute_connected_metric_routemap_cmd);
-  install_element (RIPNG_NODE,
-		   &no_ripng_redistribute_connected_metric_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_static_metric_routemap_cmd);
-  install_element (RIPNG_NODE,
-		   &no_ripng_redistribute_static_metric_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_ospf6_metric_routemap_cmd);
-  install_element (RIPNG_NODE,
-		   &no_ripng_redistribute_ospf6_metric_routemap_cmd);
-  install_element (RIPNG_NODE, &ripng_redistribute_bgp_metric_routemap_cmd);
-  install_element (RIPNG_NODE, &no_ripng_redistribute_bgp_metric_routemap_cmd);
+
+  /* Install command elements to ripng node */
+  install_element (RIPNG_NODE, &ripng_redistribute_type_cmd);
+  install_element (RIPNG_NODE, &ripng_redistribute_type_routemap_cmd);
+  install_element (RIPNG_NODE, &ripng_redistribute_type_metric_cmd);
+  install_element (RIPNG_NODE, &ripng_redistribute_type_metric_routemap_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_routemap_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_metric_cmd);
+  install_element (RIPNG_NODE, &no_ripng_redistribute_type_metric_routemap_cmd);
 }
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index e7cefaf..a4d119d 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -21,9 +21,6 @@
 
 #include <zebra.h>
 
-/* For struct udphdr. */
-#include <netinet/udp.h>
-
 #include "prefix.h"
 #include "filter.h"
 #include "log.h"
@@ -42,8 +39,7 @@
 #include "ripngd/ripngd.h"
 #include "ripngd/ripng_route.h"
 #include "ripngd/ripng_debug.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 +50,11 @@
 {
   ripng_all_route,
   ripng_changed_route,
-  ripng_split_horizon,
-  ripng_no_split_horizon
 };
 
 /* 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 *);
@@ -87,6 +81,12 @@
   return buf;
 }
 
+int
+ripng_route_rte (struct ripng_info *rinfo)
+{
+  return (rinfo->type == ZEBRA_ROUTE_RIPNG && rinfo->sub_type == RIPNG_ROUTE_RTE);
+}
+
 /* Allocate new ripng information. */
 struct ripng_info *
 ripng_info_new ()
@@ -175,12 +175,12 @@
   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_info ("send to %s", inet6_ntop (&to->sin6_addr));
+    zlog_info ("  send interface %s", ifp->name);
+    zlog_info ("  send packet size %d", bufsize);
+  }
 
   memset (&addr, 0, sizeof (struct sockaddr_in6));
   addr.sin6_family = AF_INET6;
@@ -222,8 +222,13 @@
 
   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_ntop (&to->sin6_addr), strerror (errno));
+    else
+      zlog_err ("RIPng send fail on %s: %s", ifp->name, strerror (errno));
+  }
 
   return ret;
 }
@@ -420,14 +425,6 @@
   /* 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);
@@ -455,9 +452,16 @@
   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. */
@@ -479,12 +483,154 @@
     }
 }
 
+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_info ("%s/%d filtered by distribute in",
+		       inet6_ntop (&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_info ("%s/%d filtered by prefix-list in",
+		       inet6_ntop (&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_info ("%s/%d filtered by distribute in",
+			       inet6_ntop (&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_info ("%s/%d filtered by prefix-list in",
+			       inet6_ntop (&p->prefix), p->prefixlen);
+		  return -1;
+		}
+	    }
+	}
+    }
+  return 0;
+}
+
+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_info ("%s/%d is filtered by distribute out",
+		       inet6_ntop (&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_info ("%s/%d is filtered by prefix-list out",
+		       inet6_ntop (&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_info ("%s/%d filtered by distribute out",
+			       inet6_ntop (&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_info ("%s/%d filtered by prefix-list out",
+			       inet6_ntop (&p->prefix), p->prefixlen);
+		  return -1;
+		}
+	    }
+	}
+    }
+  return 0;
+}
+
 /* Process RIPng route according to RFC2080. */
 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 +654,9 @@
   /* 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 +665,17 @@
       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);
@@ -549,9 +688,45 @@
 	  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 +736,23 @@
   /* 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
@@ -638,17 +830,45 @@
 	  oldmetric = rinfo->metric;
 	  rinfo->metric = rte->metric;
 	  rinfo->tag = ntohs (rte->tag);
+	  IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr);
+	  rinfo->ifindex = ifp->ifindex;
 
-	  if (! IN6_ARE_ADDR_EQUAL (&rinfo->nexthop, nexthop))
+	  /* 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->flags |= RIPNG_RTF_FIB;
+
+	      /* The aggregation counter needs to be updated because
+		     the prefixes, which are into the gc, have been
+			 removed from the aggregator (see ripng_timout). */
+		  ripng_aggregate_increment (rp, rinfo);
+	    }
+
+	  /* Update nexthop and/or metric value.  */
+	  if (oldmetric != RIPNG_METRIC_INFINITY)
 	    {
 	      ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex);
 	      ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex);
 	      rinfo->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 +895,12 @@
 		  /* - 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 +912,6 @@
 	    {
 	      /* 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 +922,7 @@
 /* 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 +932,89 @@
     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_info ("Redistribute new prefix %s/%d on the interface %s",
+                 inet6_ntop(&p->prefix), p->prefixlen,
+                 ifindex2ifname(ifindex));
+    else
+      zlog_info ("Redistribute new prefix %s/%d with nexthop %s on the interface %s",
+                 inet6_ntop(&p->prefix), p->prefixlen, inet6_ntop(nexthop),
+                 ifindex2ifname(ifindex));
+  }
+
+  ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
 }
 
 /* Delete redistributed route to RIPng table. */
@@ -751,6 +1029,15 @@
     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 +1050,24 @@
 	  && 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_info ("Poisone %s/%d on the interface %s with an infinity metric [delete]",
+                       inet6_ntop(&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,19 +1078,35 @@
   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);
 
-	    route_unlock_node (rp);
+	    rinfo->flags |= RIPNG_RTF_CHANGED;
+
+	    if (IS_RIPNG_DEBUG_EVENT) {
+	      struct prefix_ipv6 *p = (struct prefix_ipv6 *) &rp->p;
+
+	      zlog_info ("Poisone %s/%d on the interface %s [withdraw]",
+	                 inet6_ntop(&p->prefix), p->prefixlen,
+	                 ifindex2ifname(rinfo->ifindex));
+	    }
+
+	    ripng_event (RIPNG_TRIGGERED_UPDATE, 0);
 	  }
       }
 }
@@ -818,6 +1127,7 @@
     {
       zlog_warn ("RIPng packet comes from non RIPng port %d from %s",
 		 ntohs (from->sin6_port), inet6_ntop (&from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
@@ -828,6 +1138,7 @@
    {
       zlog_warn ("RIPng packet comes from non link local address %s",
 		 inet6_ntop (&from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
@@ -840,6 +1151,7 @@
     {
       zlog_warn ("RIPng packet comes from my own link local address %s",
 		 inet6_ntop (&from->sin6_addr));
+      ripng_peer_bad_packet (from);
       return;
     }
 
@@ -851,9 +1163,13 @@
     {
       zlog_warn ("RIPng packet comes with non 255 hop count %d from %s",
 		 hoplimit, inet6_ntop (&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;
@@ -881,18 +1197,21 @@
 	{
 	  zlog_warn ("Destination prefix is a multicast address %s/%d [%d]",
 		     inet6_ntop (&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);
+	  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);
+	  ripng_peer_bad_route (from);
 	  continue;
 	}
 
@@ -903,6 +1222,7 @@
 	  zlog_warn ("Invalid prefix length %s/%d from %s%%%s",
 		     inet6_ntop (&rte->addr), rte->prefixlen,
 		     inet6_ntop (&from->sin6_addr), ifp->name);
+	  ripng_peer_bad_route (from);
 	  continue;
 	}
 
@@ -911,13 +1231,13 @@
 	{
 	  zlog_warn ("Invalid metric %d from %s%%%s", rte->metric,
 		     inet6_ntop (&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);
@@ -936,6 +1256,10 @@
   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 +1269,9 @@
   if (ri->passive)
     return;
 
+  /* RIPng peer update. */
+  ripng_peer_update (from, packet->version);
+
   lim = ((caddr_t) packet) + size;
   rte = packet->rte;
 
@@ -965,7 +1292,7 @@
       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
     {
@@ -1044,6 +1371,7 @@
     {
       zlog_warn ("RIPng invalid packet size %d from %s", len,
 		 inet6_ntop (&from.sin6_addr));
+      ripng_peer_bad_packet (&from);
       return 0;
     }
 
@@ -1072,6 +1400,7 @@
     {
       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,6 +1415,7 @@
       break;
     default:
       zlog_warn ("Invalid RIPng command %d", packet->command);
+      ripng_peer_bad_packet (&from);
       break;
     }
   return 0;
@@ -1148,7 +1478,7 @@
 	}
 #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
@@ -1221,8 +1551,7 @@
       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 +1574,7 @@
    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 +1585,15 @@
     }
 
   /* Write routing table entry. */
-  stream_write (s, (caddr_t) &p->prefix, sizeof (struct in6_addr));
+  if (!nexthop)
+    stream_write (s, (caddr_t) &p->prefix, sizeof (struct in6_addr));
+  else
+    stream_write (s, (caddr_t) 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 +1602,53 @@
 /* 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;
+  struct list * ripng_rte_list;
 
-  if (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng update routes on interface %s", ifp->name);
+  if (IS_RIPNG_DEBUG_EVENT) {
+    if (to)
+      zlog_info ("RIPng update routes to neighbor %s",
+                 inet6_ntop(&to->sin6_addr));
+    else
+      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;
-
-  rtemax = (min (mtu, RIPNG_MAX_PACKET_SIZE) -
-	    IPV6_HDRLEN - 
-	    sizeof (struct udphdr) -
-	    sizeof (struct ripng_packet) +
-	    sizeof (struct rte)) / sizeof (struct rte);
-
-#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 +1656,130 @@
 	    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;
+		  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_info ("RIPng %s/%d is filtered by route-map",
+			       inet6_ntop (&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);
+          /* Apply offset-list */
+	  if (rinfo->metric_out != RIPNG_METRIC_INFINITY)
+            ripng_offset_list_apply_out (p, ifp, &rinfo->metric_out);
 
-	      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);
-	    }
+          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, 
@@ -1492,45 +1790,40 @@
 		  if (IS_RIPNG_DEBUG_PACKET)
 		    zlog_info ("RIPng %s/%d is filtered by route-map out",
 			       inet6_ntop (&p->prefix), p->prefixlen);
-		  return;
+		  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);
+	  /* Apply offset-list */
+	  if (aggregate->metric_out != RIPNG_METRIC_INFINITY)
+	    ripng_offset_list_apply_out (p, ifp, &aggregate->metric_out);
 
-	      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);
-	    }
+	  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. */
@@ -1541,7 +1834,7 @@
   assert (ripng == NULL);
 
   /* Allocaste RIPng instance. */
-  ripng = XMALLOC (0, sizeof (struct ripng));
+  ripng = XMALLOC (MTYPE_RIPNG, sizeof (struct ripng));
   memset (ripng, 0, sizeof (struct ripng));
 
   /* Default version and timer values. */
@@ -1572,13 +1865,21 @@
   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);
 
@@ -1592,22 +1893,6 @@
 			    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);
-      }
-}
 
 int
 ripng_update_jitter (int time)
@@ -1618,7 +1903,6 @@
 void
 ripng_event (enum ripng_event event, int sock)
 {
-  int ripng_request_all (struct thread *);
   int jitter = 0;
 
   switch (event)
@@ -1672,14 +1956,6 @@
   { 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)
@@ -1709,6 +1985,40 @@
     }
 }
 
+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",
@@ -1722,10 +2032,16 @@
   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 +2051,18 @@
 	  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);
 #else
-	  len = vty_out (vty, "Ra %s/%d ", 
+	  len = vty_out (vty, "R(a) %s/%d ", 
 			 inet6_ntop (&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 +2072,54 @@
 	  p = (struct prefix_ipv6 *) &rp->p;
 
 #ifdef DEBUG
-	  len = vty_out (vty, "%s%s 0/%d %s/%d ",
+	  len = vty_out (vty, "%s(%s) 0/%d %s/%d ",
 			 route_info[rinfo->type].str,
-			 rinfo->suppress ? "s" : " ",
+			 ripng_route_subtype_print(rinfo),
 			 rinfo->suppress,
 			 inet6_ntop (&p->prefix), p->prefixlen);
 #else
-	  len = vty_out (vty, "%s%s %s/%d ",
+	  len = vty_out (vty, "%s(%s) %s/%d ",
 			 route_info[rinfo->type].str,
-			 rinfo->suppress ? "s" : " ",
+			 ripng_route_subtype_print(rinfo),
 			 inet6_ntop (&p->prefix), p->prefixlen);
 #endif /* DEBUG */
-	  len = 37 - len;
-	  if (len > 0)
-	    vty_out (vty, "%*s", len, " ");
-
+	  vty_out (vty, "%s", VTY_NEWLINE);
+	  vty_out (vty, "%*s", 18, " ");
 	  len = vty_out (vty, "%s", inet6_ntop (&rinfo->nexthop));
 
-	  len = 26 - len;
+	  len = 28 - len;
+	  if (len > 0)
+	    len = vty_out (vty, "%*s", len, " ");
+
+	  /* 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 = 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 +2128,88 @@
   return CMD_SUCCESS;
 }
 
+/* Return next event time. */
+static int
+ripng_next_thread_timer (struct thread *thread)
+{
+  struct timeval timer_now;
+
+  gettimeofday (&timer_now, NULL);
+
+  return thread->u.sands.tv_sec - timer_now.tv_sec;
+}
+
+DEFUN (show_ipv6_ripng_status,
+       show_ipv6_ripng_status_cmd,
+       "show ipv6 ripng status",
+       SHOW_STR
+       IP_STR
+       "Show RIPng routes\n"
+       "IPv6 routing protocol process parameters and statistics\n")
+{
+  listnode node;
+  int ripng_network_write (struct vty *, int);
+  void ripng_redistribute_write (struct vty *, int);
+
+  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 %d seconds%s",
+           ripng_next_thread_timer (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 (node = listhead (iflist); node; node = nextnode (node))
+    {
+      struct ripng_interface *ri;
+      struct interface *ifp;
+
+      ifp = getdata (node);
+      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 +2235,18 @@
   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 +2274,7 @@
     }
   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;
 }
@@ -2150,6 +2580,15 @@
   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",
@@ -2186,10 +2625,12 @@
 {
   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,10 +2644,12 @@
 {
   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;
 }
@@ -2215,8 +2658,8 @@
 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,14 +2672,17 @@
       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))
@@ -2355,6 +2801,7 @@
   else
     ri->prefix[RIPNG_FILTER_OUT] = NULL;
 }
+
 void
 ripng_distribute_update_interface (struct interface *ifp)
 {
@@ -2378,6 +2825,103 @@
       ripng_distribute_update_interface (ifp);
     }
 }
+
+/* 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 ();
+}
 
 void
 ripng_if_rmap_update (struct if_rmap *if_rmap)
@@ -2468,10 +3012,13 @@
 
   /* 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 +3032,7 @@
 
   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);
@@ -2517,6 +3065,8 @@
 
   /* 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);
 
diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h
index 816deb6..609e692 100644
--- a/ripngd/ripngd.h
+++ b/ripngd/ripngd.h
@@ -44,13 +44,19 @@
 #define RIPNG_TIMEOUT_TIMER_DEFAULT    180
 #define RIPNG_GARBAGE_TIMER_DEFAULT    120
 
+/* RIPng peer timeout value. */
+#define RIPNG_PEER_TIMER_DEFAULT       180
+
 /* Default config file name. */
 #define RIPNG_DEFAULT_CONFIG "ripngd.conf"
 
 /* RIPng route types. */
 #define RIPNG_ROUTE_RTE                  0
 #define RIPNG_ROUTE_STATIC               1
-#define RIPNG_ROUTE_AGGREGATE            2
+#define RIPNG_ROUTE_DEFAULT              2
+#define RIPNG_ROUTE_REDISTRIBUTE         3
+#define RIPNG_ROUTE_INTERFACE            4
+#define RIPNG_ROUTE_AGGREGATE            5
 
 /* Interface send/receive configuration. */
 #define RIPNG_SEND_UNSPEC                0
@@ -58,12 +64,6 @@
 #define RIPNG_RECEIVE_UNSPEC             0
 #define RIPNG_RECEIVE_OFF                1
 
-/* Split horizon definitions. */
-#define RIPNG_SPLIT_HORIZON_UNSPEC       0
-#define RIPNG_SPLIT_HORIZON_NONE         1
-#define RIPNG_SPLIT_HORIZON              2
-#define RIPNG_SPLIT_HORIZON_POISONED     3
-
 /* RIP default route's accept/announce methods. */
 #define RIPNG_DEFAULT_ADVERTISE_UNSPEC   0
 #define RIPNG_DEFAULT_ADVERTISE_NONE     1
@@ -139,10 +139,12 @@
 /* Routing table entry. */
 struct rte
 {
-  struct in6_addr addr;
-  u_short tag;
-  u_char prefixlen;
-  u_char metric;
+  struct in6_addr addr;	/* RIPng destination prefix */
+  u_short tag;		/* RIPng tag */
+  u_char prefixlen;	/* Length of the RIPng prefix */
+  u_char metric;	/* Metric of the RIPng route */
+  			/* The nexthop is stored by the structure
+			 * ripng_nexthop within ripngd.c */
 };
 
 /* RIPNG send packet. */
@@ -189,11 +191,16 @@
   struct thread *t_garbage_collect;
 
   /* Route-map features - this variables can be changed. */
+  struct in6_addr nexthop_out;
   u_char metric_set;
+  u_char metric_out;
+  u_short tag_out;
 
   struct route_node *rp;
 };
 
+#ifdef notyet
+#if 0
 /* RIPng tag structure. */
 struct ripng_tag
 {
@@ -218,6 +225,14 @@
   /* Poison reverse. */
   u_char poison_reverse;
 };
+#endif /* 0 */
+#endif /* not yet */
+
+typedef enum {
+  RIPNG_NO_SPLIT_HORIZON = 0,
+  RIPNG_SPLIT_HORIZON,
+  RIPNG_SPLIT_HORIZON_POISONED_REVERSE
+} split_horizon_policy_t;
 
 /* RIPng specific interface configuration. */
 struct ripng_interface
@@ -229,6 +244,10 @@
   /* RIPng is running on this interface. */
   int running;
 
+  /* Split horizon flag. */
+  split_horizon_policy_t split_horizon;
+  split_horizon_policy_t split_horizon_default;
+  
   /* For filter type slot. */
 #define RIPNG_FILTER_IN  0
 #define RIPNG_FILTER_OUT 1
@@ -243,8 +262,12 @@
   /* Route-map. */
   struct route_map *routemap[RIPNG_FILTER_MAX];
 
+#ifdef notyet
+#if 0
   /* RIPng tag configuration. */
   struct ripng_tag *rtag;
+#endif /* 0 */
+#endif /* notyet */
 
   /* Default information originate. */
   u_char default_originate;
@@ -259,6 +282,29 @@
   int passive;
 };
 
+/* RIPng peer information. */
+struct ripng_peer
+{
+  /* Peer address. */
+  struct in6_addr addr;
+
+  /* Peer RIPng tag value. */
+  int domain;
+
+  /* Last update time. */
+  time_t uptime;
+
+  /* Peer RIP version. */
+  u_char version;
+
+  /* Statistics. */
+  int recv_badpackets;
+  int recv_badroutes;
+
+  /* Timeout thread. */
+  struct thread *t_timeout;
+};
+
 /* All RIPng events. */
 enum ripng_event
 {
@@ -295,15 +341,42 @@
 
 /* Prototypes. */
 void ripng_init ();
+void ripng_reset ();
+void ripng_clean ();
+void ripng_clean_network ();
+void ripng_interface_clean ();
+void ripng_interface_reset ();
+void ripng_passive_interface_clean ();
 void ripng_if_init ();
+void ripng_route_map_init ();
+void ripng_route_map_reset ();
 void ripng_terminate ();
-void ripng_zclient_start ();
+ /* zclient_init() is done by ripng_zebra.c:zebra_init() */
 void zebra_init ();
+void ripng_zclient_start ();
+void ripng_zclient_reset ();
+void ripng_offset_init ();
+
+int config_write_ripng_offset_list (struct vty *);
+
+void ripng_peer_init ();
+void ripng_peer_update (struct sockaddr_in6 *, u_char);
+void ripng_peer_bad_route (struct sockaddr_in6 *);
+void ripng_peer_bad_packet (struct sockaddr_in6 *);
+void ripng_peer_display (struct vty *);
+struct ripng_peer *ripng_peer_lookup (struct in6_addr *);
+struct ripng_peer *ripng_peer_lookup_next (struct in6_addr *);
+
+int ripng_offset_list_apply_in (struct prefix_ipv6 *, struct interface *, u_char *);
+int ripng_offset_list_apply_out (struct prefix_ipv6 *, struct interface *, u_char *);
+void ripng_offset_clean ();
+
 struct ripng_info * ripng_info_new ();
 void ripng_info_free (struct ripng_info *rinfo);
 void ripng_event (enum ripng_event, int);
 int ripng_request (struct interface *ifp);
-void ripng_redistribute_add (int, int, struct prefix_ipv6 *, unsigned int);
+void ripng_redistribute_add (int, int, struct prefix_ipv6 *, unsigned int,
+			     struct in6_addr *);
 void ripng_redistribute_delete (int, int, struct prefix_ipv6 *, unsigned int);
 void ripng_redistribute_withdraw (int type);
 
@@ -312,6 +385,17 @@
 
 void ripng_zebra_ipv6_add (struct prefix_ipv6 *p, struct in6_addr *nexthop, unsigned int ifindex);
 void ripng_zebra_ipv6_delete (struct prefix_ipv6 *p, struct in6_addr *nexthop, unsigned int ifindex);
-void ripng_route_map_init ();
+
+void ripng_redistribute_clean ();
+
+const char *inet6_ntop (struct in6_addr *p);
+
+int ripng_write_rte (int num, struct stream *s, struct prefix_ipv6 *p,
+		     struct in6_addr *nexthop, u_int16_t tag, u_char metric);
+int ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to, 
+		       struct interface *ifp);
+
+void ripng_packet_dump (struct ripng_packet *packet, int size, char *sndrcv);
+
 
 #endif /* _ZEBRA_RIPNG_RIPNGD_H */