Initial revision
diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c
new file mode 100644
index 0000000..46f5301
--- /dev/null
+++ b/zebra/if_ioctl.c
@@ -0,0 +1,438 @@
+/*
+ * Interface looking up by ioctl ().
+ * Copyright (C) 1997, 98 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 "if.h"
+#include "sockunion.h"
+#include "prefix.h"
+#include "ioctl.h"
+#include "connected.h"
+#include "memory.h"
+#include "log.h"
+
+#include "zebra/interface.h"
+
+/* Interface looking up using infamous SIOCGIFCONF. */
+int
+interface_list_ioctl ()
+{
+  int ret;
+  int sock;
+#define IFNUM_BASE 32
+  int ifnum;
+  struct ifreq *ifreq;
+  struct ifconf ifconf;
+  struct interface *ifp;
+  int n;
+  int lastlen;
+
+  /* Normally SIOCGIFCONF works with AF_INET socket. */
+  sock = socket (AF_INET, SOCK_DGRAM, 0);
+  if (sock < 0) 
+    {
+      zlog_warn ("Can't make AF_INET socket stream: %s", strerror (errno));
+      return -1;
+    }
+
+  /* Set initial ifreq count.  This will be double when SIOCGIFCONF
+     fail.  Solaris has SIOCGIFNUM. */
+#ifdef SIOCGIFNUM
+  ret = ioctl (sock, SIOCGIFNUM, &ifnum);
+  if (ret < 0)
+    ifnum = IFNUM_BASE;
+  else
+    ifnum++;
+#else
+  ifnum = IFNUM_BASE;
+#endif /* SIOCGIFNUM */
+
+  ifconf.ifc_buf = NULL;
+
+  lastlen = 0;
+  /* Loop until SIOCGIFCONF success. */
+  for (;;) 
+    {
+      ifconf.ifc_len = sizeof (struct ifreq) * ifnum;
+      ifconf.ifc_buf = XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len);
+
+      ret = ioctl(sock, SIOCGIFCONF, &ifconf);
+
+      if (ret < 0) 
+	{
+	  zlog_warn ("SIOCGIFCONF: %s", strerror(errno));
+	  goto end;
+	}
+      /* Repeatedly get info til buffer fails to grow. */
+      if (ifconf.ifc_len > lastlen)
+	{
+          lastlen = ifconf.ifc_len;
+	  ifnum += 10;
+	  continue;
+	}
+      /* Success. */
+      break;
+    }
+
+  /* Allocate interface. */
+  ifreq = ifconf.ifc_req;
+
+#ifdef OPEN_BSD
+  for (n = 0; n < ifconf.ifc_len; )
+    {
+      int size;
+
+      ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n);
+      ifp = if_get_by_name (ifreq->ifr_name);
+      if_add_update (ifp);
+      size = ifreq->ifr_addr.sa_len;
+      if (size < sizeof (ifreq->ifr_addr))
+	size = sizeof (ifreq->ifr_addr);
+      size += sizeof (ifreq->ifr_name);
+      n += size;
+    }
+#else
+  for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq))
+    {
+      ifp = if_get_by_name (ifreq->ifr_name);
+      if_add_update (ifp);
+      ifreq++;
+    }
+#endif /* OPEN_BSD */
+
+ end:
+  close (sock);
+  XFREE (MTYPE_TMP, ifconf.ifc_buf);
+
+  return ret;
+}
+
+/* Get interface's index by ioctl. */
+int
+if_get_index (struct interface *ifp)
+{
+  static int if_fake_index = 1;
+
+#ifdef HAVE_BROKEN_ALIASES
+  /* Linux 2.2.X does not provide individual interface index for aliases. */
+  ifp->ifindex = if_fake_index++;
+  return ifp->ifindex;
+#else
+#ifdef SIOCGIFINDEX
+  int ret;
+  struct ifreq ifreq;
+
+  ifreq_set_name (&ifreq, ifp);
+
+  ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq);
+  if (ret < 0)
+    {
+      /* Linux 2.0.X does not have interface index. */
+      ifp->ifindex = if_fake_index++;
+      return ifp->ifindex;
+    }
+
+  /* OK we got interface index. */
+#ifdef ifr_ifindex
+  ifp->ifindex = ifreq.ifr_ifindex;
+#else
+  ifp->ifindex = ifreq.ifr_index;
+#endif
+  return ifp->ifindex;
+
+#else
+  ifp->ifindex = if_fake_index++;
+  return ifp->ifindex;
+#endif /* SIOCGIFINDEX */
+#endif /* HAVE_BROKEN_ALIASES */
+}
+
+#ifdef SIOCGIFHWADDR
+int
+if_get_hwaddr (struct interface *ifp)
+{
+  int ret;
+  struct ifreq ifreq;
+  int i;
+
+  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
+  ifreq.ifr_addr.sa_family = AF_INET;
+
+  /* Fetch Hardware address if available. */
+  ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq);
+  if (ret < 0)
+    ifp->hw_addr_len = 0;
+  else
+    {
+      memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
+
+      for (i = 0; i < 6; i++)
+	if (ifp->hw_addr[i] != 0)
+	  break;
+
+      if (i == 6)
+	ifp->hw_addr_len = 0;
+      else
+	ifp->hw_addr_len = 6;
+    }
+  return 0;
+}
+#endif /* SIOCGIFHWADDR */
+
+#ifdef HAVE_GETIFADDRS
+#include <ifaddrs.h>
+
+int
+if_getaddrs ()
+{
+  int ret;
+  struct ifaddrs *ifap;
+  struct ifaddrs *ifapfree;
+  struct interface *ifp;
+  int prefixlen;
+
+  ret = getifaddrs (&ifap); 
+  if (ret != 0)
+    {
+      zlog_err ("getifaddrs(): %s", strerror (errno));
+      return -1;
+    }
+
+  for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
+    {
+      ifp = if_lookup_by_name (ifap->ifa_name);
+      if (ifp == NULL)
+	{
+	  zlog_err ("if_getaddrs(): Can't lookup interface %s\n",
+		    ifap->ifa_name);
+	  continue;
+	}
+
+      if (ifap->ifa_addr->sa_family == AF_INET)
+	{
+	  struct sockaddr_in *addr;
+	  struct sockaddr_in *mask;
+	  struct sockaddr_in *dest;
+	  struct in_addr *dest_pnt;
+
+	  addr = (struct sockaddr_in *) ifap->ifa_addr;
+	  mask = (struct sockaddr_in *) ifap->ifa_netmask;
+	  prefixlen = ip_masklen (mask->sin_addr);
+
+	  dest_pnt = NULL;
+
+	  if (ifap->ifa_flags & IFF_POINTOPOINT) 
+	    {
+	      dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
+	      dest_pnt = &dest->sin_addr;
+	    }
+
+	  if (ifap->ifa_flags & IFF_BROADCAST)
+	    {
+	      dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
+	      dest_pnt = &dest->sin_addr;
+	    }
+
+	  connected_add_ipv4 (ifp, 0, &addr->sin_addr,
+			      prefixlen, dest_pnt, NULL);
+	}
+#ifdef HAVE_IPV6
+      if (ifap->ifa_addr->sa_family == AF_INET6)
+	{
+	  struct sockaddr_in6 *addr;
+	  struct sockaddr_in6 *mask;
+	  struct sockaddr_in6 *dest;
+	  struct in6_addr *dest_pnt;
+
+	  addr = (struct sockaddr_in6 *) ifap->ifa_addr;
+	  mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
+	  prefixlen = ip6_masklen (mask->sin6_addr);
+
+	  dest_pnt = NULL;
+
+	  if (ifap->ifa_flags & IFF_POINTOPOINT) 
+	    {
+	      if (ifap->ifa_dstaddr)
+		{
+		  dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
+		  dest_pnt = &dest->sin6_addr;
+		}
+	    }
+
+	  if (ifap->ifa_flags & IFF_BROADCAST)
+	    {
+	      if (ifap->ifa_broadaddr)
+		{
+		  dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
+		  dest_pnt = &dest->sin6_addr;
+		}
+	    }
+
+	  connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen, dest_pnt);
+	}
+#endif /* HAVE_IPV6 */
+    }
+
+  freeifaddrs (ifapfree);
+
+  return 0; 
+}
+#else /* HAVE_GETIFADDRS */
+/* Interface address lookup by ioctl.  This function only looks up
+   IPv4 address. */
+int
+if_get_addr (struct interface *ifp)
+{
+  int ret;
+  struct ifreq ifreq;
+  struct sockaddr_in addr;
+  struct sockaddr_in mask;
+  struct sockaddr_in dest;
+  struct in_addr *dest_pnt;
+  u_char prefixlen;
+
+  /* Interface's name and address family. */
+  strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
+  ifreq.ifr_addr.sa_family = AF_INET;
+
+  /* Interface's address. */
+  ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
+  if (ret < 0) 
+    {
+      if (errno != EADDRNOTAVAIL)
+	{
+	  zlog_warn ("SIOCGIFADDR fail: %s", strerror (errno));
+	  return ret;
+	}
+      return 0;
+    }
+  memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
+
+  /* Interface's network mask. */
+  ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
+  if (ret < 0) 
+    {
+      if (errno != EADDRNOTAVAIL) 
+	{
+	  zlog_warn ("SIOCGIFNETMASK fail: %s", strerror (errno));
+	  return ret;
+	}
+      return 0;
+    }
+#ifdef ifr_netmask
+  memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
+#else
+  memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
+#endif /* ifr_netmask */
+  prefixlen = ip_masklen (mask.sin_addr);
+
+  /* Point to point or borad cast address pointer init. */
+  dest_pnt = NULL;
+
+  if (ifp->flags & IFF_POINTOPOINT) 
+    {
+      ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
+      if (ret < 0) 
+	{
+	  if (errno != EADDRNOTAVAIL) 
+	    {
+	      zlog_warn ("SIOCGIFDSTADDR fail: %s", strerror (errno));
+	      return ret;
+	    }
+	  return 0;
+	}
+      memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
+      dest_pnt = &dest.sin_addr;
+    }
+  if (ifp->flags & IFF_BROADCAST)
+    {
+      ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
+      if (ret < 0) 
+	{
+	  if (errno != EADDRNOTAVAIL) 
+	    {
+	      zlog_warn ("SIOCGIFBRDADDR fail: %s", strerror (errno));
+	      return ret;
+	    }
+	  return 0;
+	}
+      memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
+      dest_pnt = &dest.sin_addr;
+    }
+
+
+  /* Set address to the interface. */
+  connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL);
+
+  return 0;
+}
+#endif /* HAVE_GETIFADDRS */
+
+/* Fetch interface information via ioctl(). */
+static void
+interface_info_ioctl ()
+{
+  listnode node;
+  struct interface *ifp;
+  
+  for (node = listhead (iflist); node; node = nextnode (node))
+    {
+      ifp = getdata (node);
+
+      if_get_index (ifp);
+#ifdef SIOCGIFHWADDR
+      if_get_hwaddr (ifp);
+#endif /* SIOCGIFHWADDR */
+      if_get_flags (ifp);
+#ifndef HAVE_GETIFADDRS
+      if_get_addr (ifp);
+#endif /* ! HAVE_GETIFADDRS */
+      if_get_mtu (ifp);
+      if_get_metric (ifp);
+    }
+}
+
+/* Lookup all interface information. */
+void
+interface_list ()
+{
+  /* Linux can do both proc & ioctl, ioctl is the only way to get
+     interface aliases in 2.2 series kernels. */
+#ifdef HAVE_PROC_NET_DEV
+  interface_list_proc ();
+#endif /* HAVE_PROC_NET_DEV */
+  interface_list_ioctl ();
+
+  /* After listing is done, get index, address, flags and other
+     interface's information. */
+  interface_info_ioctl ();
+
+#ifdef HAVE_GETIFADDRS
+  if_getaddrs ();
+#endif /* HAVE_GETIFADDRS */
+
+#if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
+  /* Linux provides interface's IPv6 address via
+     /proc/net/if_inet6. */
+  ifaddr_proc_ipv6 ();
+#endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */
+}