Initial revision
diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c
new file mode 100644
index 0000000..c177381
--- /dev/null
+++ b/ripngd/ripng_interface.c
@@ -0,0 +1,835 @@
+/*
+ * Interface related function for RIPng.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * 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.  
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "prefix.h"
+#include "memory.h"
+#include "network.h"
+#include "filter.h"
+#include "log.h"
+#include "stream.h"
+#include "zclient.h"
+#include "command.h"
+#include "table.h"
+#include "thread.h"
+
+#include "ripngd/ripngd.h"
+#include "ripngd/ripng_debug.h"
+
+/* If RFC2133 definition is used. */
+#ifndef IPV6_JOIN_GROUP
+#define IPV6_JOIN_GROUP  IPV6_ADD_MEMBERSHIP 
+#endif
+#ifndef IPV6_LEAVE_GROUP
+#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP 
+#endif
+
+/* Static utility function. */
+static void ripng_enable_apply (struct interface *);
+static void ripng_passive_interface_apply (struct interface *);
+
+/* Join to the all rip routers multicast group. */
+int
+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;
+
+  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);
+
+  return ret;
+}
+
+/* Leave from the all rip routers multicast group. */
+int
+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;
+
+  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);
+
+  return ret;
+}
+
+/* Check max mtu size. */
+int
+ripng_check_max_mtu ()
+{
+  listnode node;
+  struct interface *ifp;
+  int mtu;
+
+  mtu = 0;
+  for (node = listhead (iflist); node; nextnode (node))
+    {
+      ifp = getdata (node);
+      if (mtu < ifp->mtu)
+	mtu = ifp->mtu;
+    }
+  return mtu;
+}
+
+int
+ripng_if_down (struct interface *ifp)
+{
+  struct route_node *rp;
+  struct ripng_info *rinfo;
+  struct ripng_interface *ri;
+
+  if (ripng->table)
+    {
+      for (rp = route_top (ripng->table); rp; rp = route_next (rp))
+	if ((rinfo = rp->info) != NULL)
+	  {
+	    /* Routes got through this interface. */
+	    if (rinfo->ifindex == ifp->ifindex
+		&& rinfo->type == ZEBRA_ROUTE_RIPNG
+		&& rinfo->sub_type == RIPNG_ROUTE_RTE)
+	      {
+		ripng_zebra_ipv6_delete ((struct prefix_ipv6 *) &rp->p,
+					 &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);
+	      }
+	    else
+	      {
+		/* All redistributed routes got through this interface. */
+		if (rinfo->ifindex == ifp->ifindex)
+		  ripng_redistribute_delete (rinfo->type, rinfo->sub_type,
+					     (struct prefix_ipv6 *) &rp->p,
+					     rinfo->ifindex);
+	      }
+	  }
+    }
+
+  ri = ifp->info;
+  
+  if (ripng && ri->running)
+   {
+     if (IS_RIPNG_DEBUG_EVENT)
+       zlog_info ("turn off %s", ifp->name);
+
+     /* Leave from multicast group. */
+     ripng_multicast_leave (ifp);
+
+     ri->running = 0;
+   }
+
+  return 0;
+}
+
+/* Inteface link up message processing. */
+int
+ripng_interface_up (int command, struct zclient *zclient, zebra_size_t length)
+{
+  struct stream *s;
+  struct interface *ifp;
+
+  /* zebra_interface_state_read() updates interface structure in iflist. */
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s);
+
+  if (ifp == NULL)
+    return 0;
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    zlog_info ("interface up %s index %d flags %ld metric %d mtu %d",
+	       ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
+
+  /* Check if this interface is RIPng enabled or not. */
+  ripng_enable_apply (ifp);
+
+  /* Check for a passive interface. */
+  ripng_passive_interface_apply (ifp);
+
+  /* Apply distribute list to the all interface. */
+  ripng_distribute_update_interface (ifp);
+
+  return 0;
+}
+
+/* Inteface link down message processing. */
+int
+ripng_interface_down (int command, struct zclient *zclient,
+		      zebra_size_t length)
+{
+  struct stream *s;
+  struct interface *ifp;
+
+  /* zebra_interface_state_read() updates interface structure in iflist. */
+  s = zclient->ibuf;
+  ifp = zebra_interface_state_read (s);
+
+  if (ifp == NULL)
+    return 0;
+
+  ripng_if_down (ifp);
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    zlog_info ("interface down %s index %d flags %ld metric %d mtu %d",
+	       ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
+
+  return 0;
+}
+
+/* Inteface addition message from zebra. */
+int
+ripng_interface_add (int command, struct zclient *zclient, zebra_size_t length)
+{
+  struct interface *ifp;
+
+  ifp = zebra_interface_add_read (zclient->ibuf);
+
+  if (IS_RIPNG_DEBUG_ZEBRA)
+    zlog_info ("RIPng interface add %s index %d flags %ld metric %d mtu %d",
+	       ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu);
+
+  /* Check is this interface is RIP enabled or not.*/
+  ripng_enable_apply (ifp);
+
+  /* Apply distribute list to the interface. */
+  ripng_distribute_update_interface (ifp);
+
+  /* Check interface routemap. */
+  ripng_if_rmap_update_interface (ifp);
+
+  return 0;
+}
+
+int
+ripng_interface_delete (int command, struct zclient *zclient,
+			zebra_size_t length)
+{
+  return 0;
+}
+
+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);
+
+  if (c == NULL)
+    return 0;
+
+  p = c->address;
+
+  if (p->family == AF_INET6)
+    {
+      if (IS_RIPNG_DEBUG_ZEBRA)
+	zlog_info ("RIPng connected address %s/%d add",
+		   inet_ntop (AF_INET6, &p->u.prefix6, buf, INET6_ADDRSTRLEN),
+		   p->prefixlen);
+      
+      /* Check is this interface is RIP enabled or not.*/
+      ripng_enable_apply (c->ifp);
+    }
+
+  return 0;
+}
+
+int
+ripng_interface_address_delete (int command, struct zclient *zclient,
+				zebra_size_t length)
+{
+  struct connected *ifc;
+  struct prefix *p;
+  char buf[INET6_ADDRSTRLEN];
+
+  ifc = zebra_interface_address_delete_read (zclient->ibuf);
+  
+  if (ifc)
+    {
+      p = ifc->address;
+
+      if (p->family == AF_INET6)
+	{
+	  if (IS_RIPNG_DEBUG_ZEBRA)
+	    zlog_info ("RIPng connected address %s/%d delete",
+		       inet_ntop (AF_INET6, &p->u.prefix6, buf,
+				  INET6_ADDRSTRLEN),
+		       p->prefixlen);
+
+	  /* Check is this interface is RIP enabled or not.*/
+	  ripng_enable_apply (ifc->ifp);
+	}
+      connected_free (ifc);
+    }
+
+  return 0;
+}
+
+/* RIPng enable interface vector. */
+vector ripng_enable_if;
+
+/* RIPng enable network table. */
+struct route_table *ripng_enable_network;
+
+/* Lookup RIPng enable network. */
+int
+ripng_enable_network_lookup (struct interface *ifp)
+{
+  listnode listnode;
+  struct connected *connected;
+
+  for (listnode = listhead (ifp->connected); listnode; nextnode (listnode))
+    if ((connected = getdata (listnode)) != NULL)
+      {
+	struct prefix *p; 
+	struct route_node *node;
+
+	p = connected->address;
+
+	if (p->family == AF_INET6)
+	  {
+	    node = route_node_match (ripng_enable_network, p);
+	    if (node)
+	      {
+		route_unlock_node (node);
+		return 1;
+	      }
+	  }
+      }
+  return -1;
+}
+
+/* Add RIPng enable network. */
+int
+ripng_enable_network_add (struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_node_get (ripng_enable_network, p);
+
+  if (node->info)
+    {
+      route_unlock_node (node);
+      return -1;
+    }
+  else
+    node->info = "enabled";
+
+  return 1;
+}
+
+/* Delete RIPng enable network. */
+int
+ripng_enable_network_delete (struct prefix *p)
+{
+  struct route_node *node;
+
+  node = route_node_lookup (ripng_enable_network, p);
+  if (node)
+    {
+      node->info = NULL;
+
+      /* Unlock info lock. */
+      route_unlock_node (node);
+
+      /* Unlock lookup lock. */
+      route_unlock_node (node);
+
+      return 1;
+    }
+  return -1;
+}
+
+/* Lookup function. */
+int
+ripng_enable_if_lookup (char *ifname)
+{
+  int i;
+  char *str;
+
+  for (i = 0; i < vector_max (ripng_enable_if); i++)
+    if ((str = vector_slot (ripng_enable_if, i)) != NULL)
+      if (strcmp (str, ifname) == 0)
+	return i;
+  return -1;
+}
+
+/* Add interface to ripng_enable_if. */
+int
+ripng_enable_if_add (char *ifname)
+{
+  int ret;
+
+  ret = ripng_enable_if_lookup (ifname);
+  if (ret >= 0)
+    return -1;
+
+  vector_set (ripng_enable_if, strdup (ifname));
+
+  return 1;
+}
+
+/* Delete interface from ripng_enable_if. */
+int
+ripng_enable_if_delete (char *ifname)
+{
+  int index;
+  char *str;
+
+  index = ripng_enable_if_lookup (ifname);
+  if (index < 0)
+    return -1;
+
+  str = vector_slot (ripng_enable_if, index);
+  free (str);
+  vector_unset (ripng_enable_if, index);
+
+  return 1;
+}
+
+/* Wake up interface. */
+int
+ripng_interface_wakeup (struct thread *t)
+{
+  struct interface *ifp;
+  struct ripng_interface *ri;
+
+  /* Get interface. */
+  ifp = THREAD_ARG (t);
+
+  ri = ifp->info;
+  ri->t_wakeup = NULL;
+
+  /* Join to multicast group. */
+  ripng_multicast_join (ifp);
+
+  /* Send RIP request to the interface. */
+  ripng_request (ifp);
+
+  return 0;
+}
+
+/* Check RIPng is enabed on this interface. */
+void
+ripng_enable_apply (struct interface *ifp)
+{
+  int ret;
+  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);
+
+  /* If the interface is matched. */
+  if (ret > 0)
+    ri->enable_network = 1;
+  else
+    ri->enable_network = 0;
+
+  /* Check interface name configuration. */
+  ret = ripng_enable_if_lookup (ifp->name);
+  if (ret >= 0)
+    ri->enable_interface = 1;
+  else
+    ri->enable_interface = 0;
+
+  /* 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);
+
+	  /* Add interface wake up thread. */
+	  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;
+	}
+    }
+  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);
+
+	  ri->running = 0;
+	}
+    }
+}
+
+/* Set distribute list to all interfaces. */
+static void
+ripng_enable_apply_all ()
+{
+  struct interface *ifp;
+  listnode node;
+
+  for (node = listhead (iflist); node; nextnode (node))
+    {
+      ifp = getdata (node);
+      ripng_enable_apply (ifp);
+    }
+}
+
+/* Vector to store passive-interface name. */
+vector Vripng_passive_interface;
+
+/* Utility function for looking up passive interface settings. */
+int
+ripng_passive_interface_lookup (char *ifname)
+{
+  int i;
+  char *str;
+
+  for (i = 0; i < vector_max (Vripng_passive_interface); i++)
+    if ((str = vector_slot (Vripng_passive_interface, i)) != NULL)
+      if (strcmp (str, ifname) == 0)
+	return i;
+  return -1;
+}
+
+void
+ripng_passive_interface_apply (struct interface *ifp)
+{
+  int ret;
+  struct ripng_interface *ri;
+
+  ri = ifp->info;
+
+  ret = ripng_passive_interface_lookup (ifp->name);
+  if (ret < 0)
+    ri->passive = 0;
+  else
+    ri->passive = 1;
+}
+
+void
+ripng_passive_interface_apply_all (void)
+{
+  struct interface *ifp;
+  listnode node;
+
+  for (node = listhead (iflist); node; nextnode (node))
+    {
+      ifp = getdata (node);
+      ripng_passive_interface_apply (ifp);
+    }
+}
+
+/* Passive interface. */
+int
+ripng_passive_interface_set (struct vty *vty, char *ifname)
+{
+  if (ripng_passive_interface_lookup (ifname) >= 0)
+    return CMD_WARNING;
+
+  vector_set (Vripng_passive_interface, strdup (ifname));
+
+  ripng_passive_interface_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+int
+ripng_passive_interface_unset (struct vty *vty, char *ifname)
+{
+  int i;
+  char *str;
+
+  i = ripng_passive_interface_lookup (ifname);
+  if (i < 0)
+    return CMD_WARNING;
+
+  str = vector_slot (Vripng_passive_interface, i);
+  free (str);
+  vector_unset (Vripng_passive_interface, i);
+
+  ripng_passive_interface_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+/* Free all configured RIP passive-interface settings. */
+void
+ripng_passive_interface_clean (void)
+{
+  int i;
+  char *str;
+
+  for (i = 0; i < vector_max (Vripng_passive_interface); i++)
+    if ((str = vector_slot (Vripng_passive_interface, i)) != NULL)
+      {
+	free (str);
+	vector_slot (Vripng_passive_interface, i) = NULL;
+      }
+  ripng_passive_interface_apply_all ();
+}
+
+/* Write RIPng enable network and interface to the vty. */
+int
+ripng_network_write (struct vty *vty)
+{
+  int i;
+  char *str;
+  char *ifname;
+  struct route_node *node;
+  char buf[BUFSIZ];
+
+  /* Write enable network. */
+  for (node = route_top (ripng_enable_network); node; node = route_next (node))
+    if (node->info)
+      {
+	struct prefix *p = &node->p;
+	vty_out (vty, " network %s/%d%s", 
+		 inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
+		 p->prefixlen,
+		 VTY_NEWLINE);
+
+      }
+  
+  /* 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,
+	       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);
+
+  return 0;
+}
+
+/* RIPng enable on specified interface or matched network. */
+DEFUN (ripng_network,
+       ripng_network_cmd,
+       "network IF_OR_ADDR",
+       "RIPng enable on specified interface or network.\n"
+       "Interface or address")
+{
+  int ret;
+  struct prefix p;
+
+  ret = str2prefix (argv[0], &p);
+
+  /* Given string is IPv6 network or interface name. */
+  if (ret)
+    ret = ripng_enable_network_add (&p);
+  else
+    ret = ripng_enable_if_add (argv[0]);
+
+  if (ret < 0)
+    {
+      vty_out (vty, "There is same network configuration %s%s", argv[0],
+	       VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  ripng_enable_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+/* RIPng enable on specified interface or matched network. */
+DEFUN (no_ripng_network,
+       no_ripng_network_cmd,
+       "no network IF_OR_ADDR",
+       NO_STR
+       "RIPng enable on specified interface or network.\n"
+       "Interface or address")
+{
+  int ret;
+  struct prefix p;
+
+  ret = str2prefix (argv[0], &p);
+
+  /* Given string is interface name. */
+  if (ret)
+    ret = ripng_enable_network_delete (&p);
+  else
+    ret = ripng_enable_if_delete (argv[0]);
+
+  if (ret < 0)
+    {
+      vty_out (vty, "can't find network %s%s", argv[0],
+	       VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+  
+  ripng_enable_apply_all ();
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (ripng_passive_interface,
+       ripng_passive_interface_cmd,
+       "passive-interface IFNAME",
+       "Suppress routing updates on an interface\n"
+       "Interface name\n")
+{
+  return ripng_passive_interface_set (vty, argv[0]);
+}
+
+DEFUN (no_ripng_passive_interface,
+       no_ripng_passive_interface_cmd,
+       "no passive-interface IFNAME",
+       NO_STR
+       "Suppress routing updates on an interface\n"
+       "Interface name\n")
+{
+  return ripng_passive_interface_unset (vty, argv[0]);
+}
+
+struct ripng_interface *
+ri_new ()
+{
+  struct ripng_interface *ri;
+  ri = XCALLOC (MTYPE_IF, sizeof (struct ripng_interface));
+  return ri;
+}
+
+int
+ripng_if_new_hook (struct interface *ifp)
+{
+  ifp->info = ri_new ();
+  return 0;
+}
+
+/* Configuration write function for ripngd. */
+int
+interface_config_write (struct vty *vty)
+{
+  listnode node;
+  struct interface *ifp;
+  struct ripng_interface *ri;
+  int write = 0;
+
+  for (node = listhead (iflist); node; nextnode (node))
+    {
+      ifp = getdata (node);
+      ri = ifp->info;
+
+      vty_out (vty, "interface %s%s", ifp->name,
+	       VTY_NEWLINE);
+      if (ifp->desc)
+	vty_out (vty, " description %s%s", ifp->desc,
+		 VTY_NEWLINE);
+
+      vty_out (vty, "!%s", VTY_NEWLINE);
+
+      write++;
+    }
+  return write;
+}
+
+/* ripngd's interface node. */
+struct cmd_node interface_node =
+{
+  INTERFACE_NODE,
+  "%s(config-if)# ",
+};
+
+/* Initialization of interface. */
+void
+ripng_if_init ()
+{
+  /* Interface initialize. */
+  iflist = list_new ();
+  if_add_hook (IF_NEW_HOOK, ripng_if_new_hook);
+
+  /* RIPng enable network init. */
+  ripng_enable_network = route_table_init ();
+
+  /* RIPng enable interface init. */
+  ripng_enable_if = vector_init (1);
+
+  /* RIPng passive interface. */
+  Vripng_passive_interface = vector_init (1);
+
+  /* Install interface node. */
+  install_node (&interface_node, interface_config_write);
+
+  install_element (CONFIG_NODE, &interface_cmd);
+  install_element (INTERFACE_NODE, &config_end_cmd);
+  install_element (INTERFACE_NODE, &config_exit_cmd);
+  install_element (INTERFACE_NODE, &config_help_cmd);
+  install_element (INTERFACE_NODE, &interface_desc_cmd);
+  install_element (INTERFACE_NODE, &no_interface_desc_cmd);
+
+  install_element (RIPNG_NODE, &ripng_network_cmd);
+  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);
+}