Browse Source

Ripngd part of 6Wind patch.

hasso 17 years ago
parent
commit
a94434b691

+ 27 - 0
lib/memory.c

@@ -255,6 +255,7 @@ struct memory_list memory_list_lib[] =
   { 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 @@ struct memory_list memory_list_rip[] =
   { -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 @@ DEFUN (show_memory_all,
   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 @@ DEFUN (show_memory_rip,
   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 @@ memory_init ()
   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 @@ memory_init ()
   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);

+ 7 - 2
lib/memory.h

@@ -58,8 +58,6 @@ enum
   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 @@ enum
   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,
 

+ 2 - 2
ripngd/Makefile.am

@@ -9,7 +9,7 @@ sbin_PROGRAMS = ripngd
 
 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 @@ noinst_HEADERS = \
 ripngd_SOURCES = \
 	ripng_main.c $(libripng_a_SOURCES)
 
-ripngd_LDADD = ../lib/libzebra.a
+ripngd_LDADD = -L../lib -lzebra
 
 sysconf_DATA = ripngd.conf.sample
 

+ 2 - 1
ripngd/ripng_debug.c

@@ -207,7 +207,8 @@ DEFUN (no_debug_ripng_zebra,
 struct cmd_node debug_node =
 {
   DEBUG_NODE,
-  ""				/* Debug node has no interface. */
+  "",				/* Debug node has no interface. */
+  1 /* VTYSH */
 };
 
 int

+ 1 - 0
ripngd/ripng_debug.h

@@ -48,5 +48,6 @@ extern unsigned long ripng_debug_packet;
 extern unsigned long ripng_debug_zebra;
 
 void ripng_debug_init ();
+void ripng_debug_reset ();
 
 #endif /* _ZEBRA_RIPNG_DEBUG_H */

+ 471 - 77
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 @@ ripng_multicast_join (struct interface *ifp)
   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 @@ ripng_multicast_leave (struct interface *ifp)
   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 (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 (IS_RIPNG_DEBUG_EVENT)
-    zlog_info ("RIPng %s leave from all-rip-routers multicast group",
-	       ifp->name);
+      if ((p->family == AF_INET6) &&
+          IN6_IS_ADDR_LINKLOCAL (&p->u.prefix6))
+        count++;
+    }
 
-  return ret;
+  return count;
 }
 
 /* Check max mtu size. */
@@ -120,7 +153,7 @@ ripng_if_down (struct interface *ifp)
   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 @@ ripng_if_down (struct interface *ifp)
 					 &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 @@ ripng_if_down (struct interface *ifp)
 
   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 @@ int
 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 @@ ripng_interface_address_add (int command, struct zclient *zclient,
     {
       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 @@ ripng_interface_address_delete (int command, struct zclient *zclient,
 				  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 @@ vector ripng_enable_if;
 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 @@ ripng_enable_network_lookup (struct interface *ifp)
 
 	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 @@ ripng_enable_network_lookup (struct interface *ifp)
   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 @@ ripng_enable_network_add (struct prefix *p)
   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 @@ ripng_enable_if_add (char *ifname)
 
   vector_set (ripng_enable_if, strdup (ifname));
 
+  ripng_enable_apply_all();
+
   return 1;
 }
 
@@ -433,6 +652,8 @@ ripng_enable_if_delete (char *ifname)
   free (str);
   vector_unset (ripng_enable_if, index);
 
+  ripng_enable_apply_all();
+
   return 1;
 }
 
@@ -450,7 +671,13 @@ ripng_interface_wakeup (struct thread *t)
   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 @@ ripng_interface_wakeup (struct thread *t)
   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 @@ ripng_enable_apply (struct interface *ifp)
   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 @@ ripng_enable_apply (struct interface *ifp)
   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 @@ ripng_enable_apply (struct interface *ifp)
 	  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);
-
-	  /* Leave from multicast group. */
-	  ripng_multicast_leave (ifp);
+	  /* 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);
 
-	  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 @@ ripng_enable_apply_all ()
     }
 }
 
+/* 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 @@ ripng_passive_interface_clean (void)
 
 /* 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 @@ ripng_network_write (struct vty *vty)
     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 @@ ripng_network_write (struct vty *vty)
   
   /* 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 @@ DEFUN (ripng_network,
       return CMD_WARNING;
     }
 
-  ripng_enable_apply_all ();
-
   return CMD_SUCCESS;
 }
 
@@ -728,11 +1014,71 @@ DEFUN (no_ripng_network,
       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 @@ ri_new ()
 {
   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 @@ ripng_if_new_hook (struct interface *ifp)
   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 @@ interface_config_write (struct vty *vty)
       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 @@ struct cmd_node interface_node =
 {
   INTERFACE_NODE,
   "%s(config-if)# ",
+  1 /* VTYSH */
 };
 
 /* Initialization of interface. */
@@ -808,6 +1197,7 @@ ripng_if_init ()
   /* 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 @@ ripng_if_init ()
 
   /* 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 @@ ripng_if_init ()
   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);
 }

+ 25 - 5
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 @@ struct option longopts[] =
 /* 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 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
 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 @@ int
 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 @@ main (int argc, char **argv)
   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. */

+ 216 - 0
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);
+  }
+}

+ 62 - 0
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 */

+ 417 - 0
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;
+}

+ 220 - 0
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;
+}

+ 1 - 0
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"

+ 6 - 0
ripngd/ripng_route.h

@@ -36,6 +36,12 @@ struct ripng_aggregate
 
   /* 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

+ 510 - 109
ripngd/ripng_routemap.c

@@ -26,11 +26,175 @@
 #include "prefix.h"
 #include "routemap.h"
 #include "command.h"
+#include "sockunion.h"
 
 #include "ripngd/ripngd.h"
 
-#if 0
+struct rip_metric_modifier
+{
+  enum 
+  {
+    metric_increment,
+    metric_decrement,
+    metric_absolute
+  } type;
+
+  u_char metric;
+};
+
+
+int
+ripng_route_match_add (struct vty *vty, struct route_map_index *index,
+		       char *command, char *arg)
+{
+  int ret;
+
+  ret = route_map_add_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+	{
+	case RMAP_RULE_MISSING:
+	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	case RMAP_COMPILE_ERROR:
+	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	}
+    }
+  return CMD_SUCCESS;
+}
+
+int
+ripng_route_match_delete (struct vty *vty, struct route_map_index *index,
+			  char *command, char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_match (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+	{
+	case RMAP_RULE_MISSING:
+	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	case RMAP_COMPILE_ERROR:
+	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	}
+    }
+  return CMD_SUCCESS;
+}
+
+int
+ripng_route_set_add (struct vty *vty, struct route_map_index *index,
+		     char *command, char *arg)
+{
+  int ret;
+
+  ret = route_map_add_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+	{
+	case RMAP_RULE_MISSING:
+	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	case RMAP_COMPILE_ERROR:
+	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	}
+    }
+  return CMD_SUCCESS;
+}
+
+int
+ripng_route_set_delete (struct vty *vty, struct route_map_index *index,
+			char *command, char *arg)
+{
+  int ret;
+
+  ret = route_map_delete_set (index, command, arg);
+  if (ret)
+    {
+      switch (ret)
+	{
+	case RMAP_RULE_MISSING:
+	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	case RMAP_COMPILE_ERROR:
+	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
+	  return CMD_WARNING;
+	  break;
+	}
+    }
+  return CMD_SUCCESS;
+}
+
+/* `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)
@@ -39,24 +203,25 @@ route_match_interface (void *rule, struct prefix *prefix,
   struct interface *ifp;
   char *ifname;
 
-  if (type == ROUTE_MAP_RIPNG)
+  if (type == RMAP_RIPNG)
     {
       ifname = rule;
       ifp = if_lookup_by_name(ifname);
 
       if (!ifp)
-	return RM_NOMATCH;
+	return RMAP_NOMATCH;
 
       rinfo = object;
 
       if (rinfo->ifindex == ifp->ifindex)
-	return RM_MATCH;
+	return RMAP_MATCH;
       else
-	return RM_NOMATCH;
+	return RMAP_NOMATCH;
     }
-  return RM_NOMATCH;
+  return RMAP_NOMATCH;
 }
 
+/* Route map `match interface' match statement. `arg' is IFNAME value */
 void *
 route_match_interface_compile (char *arg)
 {
@@ -76,20 +241,61 @@ struct route_map_rule_cmd route_match_interface_cmd =
   route_match_interface_compile,
   route_match_interface_free
 };
-#endif /* 0 */
-
-struct rip_metric_modifier
+
+/* `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)
 {
-  enum 
-  {
-    metric_increment,
-    metric_decrement,
-    metric_absolute
-  } type;
+  u_short *tag;
+  struct ripng_info *rinfo;
 
-  u_char metric;
+  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)
@@ -103,22 +309,23 @@ route_set_metric (void *rule, struct prefix *prefix,
       rinfo = object;
 
       if (mod->type == metric_increment)
-	rinfo->metric += mod->metric;
+	rinfo->metric_out += mod->metric;
       else if (mod->type == metric_decrement)
-	rinfo->metric -= mod->metric;
+	rinfo->metric_out-= mod->metric;
       else if (mod->type == metric_absolute)
-	rinfo->metric = mod->metric;
+	rinfo->metric_out = mod->metric;
 
-      if (rinfo->metric < 1)
-	rinfo->metric = 1;
-      if (rinfo->metric > RIPNG_METRIC_INFINITY)
-	rinfo->metric = RIPNG_METRIC_INFINITY;
+      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)
 {
@@ -171,6 +378,7 @@ route_set_metric_compile (char *arg)
   return mod;
 }
 
+/* Free route map's compiled `set metric' value. */
 void
 route_set_metric_free (void *rule)
 {
@@ -184,109 +392,158 @@ struct route_map_rule_cmd route_set_metric_cmd =
   route_set_metric_compile,
   route_set_metric_free,
 };
-
-int
-ripng_route_match_add (struct vty *vty, struct route_map_index *index,
-		       char *command, char *arg)
+
+/* `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)
 {
-  int ret;
+  struct in6_addr *address;
+  struct ripng_info *rinfo;
 
-  ret = route_map_add_match (index, command, arg);
-  if (ret)
+  if(type == RMAP_RIPNG)
     {
-      switch (ret)
-	{
-	case RMAP_RULE_MISSING:
-	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	case RMAP_COMPILE_ERROR:
-	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	}
+      /* Fetch routemap's rule information. */
+      address = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->nexthop_out = *address;
     }
-  return CMD_SUCCESS;
+
+  return RMAP_OKAY;
 }
 
-int
-ripng_route_match_delete (struct vty *vty, struct route_map_index *index,
-			  char *command, char *arg)
+/* 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;
 
-  ret = route_map_delete_match (index, command, arg);
-  if (ret)
+  address = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct in_addr));
+
+  ret = inet_pton (AF_INET6, arg, address);
+
+  if (ret == 0)
     {
-      switch (ret)
-	{
-	case RMAP_RULE_MISSING:
-	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	case RMAP_COMPILE_ERROR:
-	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	}
+      XFREE (MTYPE_ROUTE_MAP_COMPILED, address);
+      return NULL;
     }
-  return CMD_SUCCESS;
+
+  return address;
 }
 
-int
-ripng_route_set_add (struct vty *vty, struct route_map_index *index,
-		     char *command, char *arg)
+/* Free route map's compiled `ipv6 nexthop local' value. */
+void
+route_set_ipv6_nexthop_local_free (void *rule)
 {
-  int ret;
+  XFREE (MTYPE_ROUTE_MAP_COMPILED, rule);
+}
 
-  ret = route_map_add_set (index, command, arg);
-  if (ret)
+/* 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)
     {
-      switch (ret)
-	{
-	case RMAP_RULE_MISSING:
-	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	case RMAP_COMPILE_ERROR:
-	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	}
+      /* Fetch routemap's rule information. */
+      tag = rule;
+      rinfo = object;
+    
+      /* Set next hop value. */ 
+      rinfo->tag_out = *tag;
     }
-  return CMD_SUCCESS;
+
+  return RMAP_OKAY;
 }
 
-int
-ripng_route_set_delete (struct vty *vty, struct route_map_index *index,
-			char *command, char *arg)
+/* Route map `tag' compile function.  Given string is converted
+   to u_short. */
+void *
+route_set_tag_compile (char *arg)
 {
-  int ret;
+  u_short *tag;
 
-  ret = route_map_delete_set (index, command, arg);
-  if (ret)
-    {
-      switch (ret)
-	{
-	case RMAP_RULE_MISSING:
-	  vty_out (vty, "Can't find rule.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	case RMAP_COMPILE_ERROR:
-	  vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE);
-	  return CMD_WARNING;
-	  break;
-	}
-    }
-  return CMD_SUCCESS;
+  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
+};
 
-#if 0
+#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 (match_interface,
 
 DEFUN (no_match_interface,
        no_match_interface_cmd,
+       "no match interface",
+       NO_STR
+       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]);
+}
+
+ALIAS (no_match_interface,
+       no_match_interface_val_cmd,
        "no match interface WORD",
        NO_STR
-       "Match value\n"
-       "Interface\n"
+       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_delete (vty, vty->index, "interface", argv[0]);
+  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]);
 }
-#endif /* 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 @@ ALIAS (no_set_metric,
        "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_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);
 }

+ 207 - 504
ripngd/ripng_zebra.c

@@ -133,13 +133,19 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient,
     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 @@ ripng_redistribute_unset (int type)
   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_redistribute_metric_set (int type, int metric)
   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_redistribute_routemap_unset (int type)
   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 @@ DEFUN (no_ripng_redistribute_ripng,
   return CMD_SUCCESS;
 }
 
-DEFUN (ripng_redistribute_static,
-       ripng_redistribute_static_cmd,
-       "redistribute static",
-       "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
+DEFUN (ripng_redistribute_type,
+       ripng_redistribute_type_cmd,
+       "redistribute (kernel|connected|static|ospf6|bgp)",
        "Redistribute information from another routing protocol\n"
-       "Kernel routes\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_KERNEL);
-  ripng_redistribute_routemap_unset (ZEBRA_ROUTE_KERNEL);
-  return ripng_redistribute_unset (ZEBRA_ROUTE_KERNEL);
-}
+  int i;
 
-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;
-}
+  for(i = 0; redist_type[i].str; i++) 
+    {