if_ioctl.c
上传用户:xiaozhuqw
上传日期:2009-11-15
资源大小:1338k
文件大小:10k
源码类别:

网络

开发平台:

Unix_Linux

  1. /*
  2.  * Interface looking up by ioctl ().
  3.  * Copyright (C) 1997, 98 Kunihiro Ishiguro
  4.  *
  5.  * This file is part of GNU Zebra.
  6.  *
  7.  * GNU Zebra is free software; you can redistribute it and/or modify it
  8.  * under the terms of the GNU General Public License as published by the
  9.  * Free Software Foundation; either version 2, or (at your option) any
  10.  * later version.
  11.  *
  12.  * GNU Zebra is distributed in the hope that it will be useful, but
  13.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with GNU Zebra; see the file COPYING.  If not, write to the Free
  19.  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  20.  * 02111-1307, USA.  
  21.  */
  22. #include <zebra.h>
  23. #include "if.h"
  24. #include "sockunion.h"
  25. #include "prefix.h"
  26. #include "ioctl.h"
  27. #include "connected.h"
  28. #include "memory.h"
  29. #include "log.h"
  30. #include "zebra/interface.h"
  31. /* Interface looking up using infamous SIOCGIFCONF. */
  32. int
  33. interface_list_ioctl ()
  34. {
  35.   int ret;
  36.   int sock;
  37. #define IFNUM_BASE 32
  38.   int ifnum;
  39.   struct ifreq *ifreq;
  40.   struct ifconf ifconf;
  41.   struct interface *ifp;
  42.   int n;
  43.   int lastlen;
  44.   /* Normally SIOCGIFCONF works with AF_INET socket. */
  45.   sock = socket (AF_INET, SOCK_DGRAM, 0);
  46.   if (sock < 0) 
  47.     {
  48.       zlog_warn ("Can't make AF_INET socket stream: %s", strerror (errno));
  49.       return -1;
  50.     }
  51.   /* Set initial ifreq count.  This will be double when SIOCGIFCONF
  52.      fail.  Solaris has SIOCGIFNUM. */
  53. #ifdef SIOCGIFNUM
  54.   ret = ioctl (sock, SIOCGIFNUM, &ifnum);
  55.   if (ret < 0)
  56.     ifnum = IFNUM_BASE;
  57.   else
  58.     ifnum++;
  59. #else
  60.   ifnum = IFNUM_BASE;
  61. #endif /* SIOCGIFNUM */
  62.   ifconf.ifc_buf = NULL;
  63.   lastlen = 0;
  64.   /* Loop until SIOCGIFCONF success. */
  65.   for (;;) 
  66.     {
  67.       ifconf.ifc_len = sizeof (struct ifreq) * ifnum;
  68.       ifconf.ifc_buf = XREALLOC(MTYPE_TMP, ifconf.ifc_buf, ifconf.ifc_len);
  69.       ret = ioctl(sock, SIOCGIFCONF, &ifconf);
  70.       if (ret < 0) 
  71. {
  72.   zlog_warn ("SIOCGIFCONF: %s", strerror(errno));
  73.   goto end;
  74. }
  75.       /* Repeatedly get info til buffer fails to grow. */
  76.       if (ifconf.ifc_len > lastlen)
  77. {
  78.           lastlen = ifconf.ifc_len;
  79.   ifnum += 10;
  80.   continue;
  81. }
  82.       /* Success. */
  83.       break;
  84.     }
  85.   /* Allocate interface. */
  86.   ifreq = ifconf.ifc_req;
  87. #ifdef OPEN_BSD
  88.   for (n = 0; n < ifconf.ifc_len; )
  89.     {
  90.       int size;
  91.       ifreq = (struct ifreq *)((caddr_t) ifconf.ifc_req + n);
  92.       ifp = if_get_by_name (ifreq->ifr_name);
  93.       if_add_update (ifp);
  94.       size = ifreq->ifr_addr.sa_len;
  95.       if (size < sizeof (ifreq->ifr_addr))
  96. size = sizeof (ifreq->ifr_addr);
  97.       size += sizeof (ifreq->ifr_name);
  98.       n += size;
  99.     }
  100. #else
  101.   for (n = 0; n < ifconf.ifc_len; n += sizeof(struct ifreq))
  102.     {
  103.       ifp = if_get_by_name (ifreq->ifr_name);
  104.       if_add_update (ifp);
  105.       ifreq++;
  106.     }
  107. #endif /* OPEN_BSD */
  108.  end:
  109.   close (sock);
  110.   XFREE (MTYPE_TMP, ifconf.ifc_buf);
  111.   return ret;
  112. }
  113. /* Get interface's index by ioctl. */
  114. int
  115. if_get_index (struct interface *ifp)
  116. {
  117.   static int if_fake_index = 1;
  118. #ifdef HAVE_BROKEN_ALIASES
  119.   /* Linux 2.2.X does not provide individual interface index for aliases. */
  120.   ifp->ifindex = if_fake_index++;
  121.   return ifp->ifindex;
  122. #else
  123. #ifdef SIOCGIFINDEX
  124.   int ret;
  125.   struct ifreq ifreq;
  126.   ifreq_set_name (&ifreq, ifp);
  127.   ret = if_ioctl (SIOCGIFINDEX, (caddr_t) &ifreq);
  128.   if (ret < 0)
  129.     {
  130.       /* Linux 2.0.X does not have interface index. */
  131.       ifp->ifindex = if_fake_index++;
  132.       return ifp->ifindex;
  133.     }
  134.   /* OK we got interface index. */
  135. #ifdef ifr_ifindex
  136.   ifp->ifindex = ifreq.ifr_ifindex;
  137. #else
  138.   ifp->ifindex = ifreq.ifr_index;
  139. #endif
  140.   return ifp->ifindex;
  141. #else
  142.   ifp->ifindex = if_fake_index++;
  143.   return ifp->ifindex;
  144. #endif /* SIOCGIFINDEX */
  145. #endif /* HAVE_BROKEN_ALIASES */
  146. }
  147. #ifdef SIOCGIFHWADDR
  148. int
  149. if_get_hwaddr (struct interface *ifp)
  150. {
  151.   int ret;
  152.   struct ifreq ifreq;
  153.   int i;
  154.   strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
  155.   ifreq.ifr_addr.sa_family = AF_INET;
  156.   /* Fetch Hardware address if available. */
  157.   ret = if_ioctl (SIOCGIFHWADDR, (caddr_t) &ifreq);
  158.   if (ret < 0)
  159.     ifp->hw_addr_len = 0;
  160.   else
  161.     {
  162.       memcpy (ifp->hw_addr, ifreq.ifr_hwaddr.sa_data, 6);
  163.       for (i = 0; i < 6; i++)
  164. if (ifp->hw_addr[i] != 0)
  165.   break;
  166.       if (i == 6)
  167. ifp->hw_addr_len = 0;
  168.       else
  169. ifp->hw_addr_len = 6;
  170.     }
  171.   return 0;
  172. }
  173. #endif /* SIOCGIFHWADDR */
  174. #ifdef HAVE_GETIFADDRS
  175. #include <ifaddrs.h>
  176. int
  177. if_getaddrs ()
  178. {
  179.   int ret;
  180.   struct ifaddrs *ifap;
  181.   struct ifaddrs *ifapfree;
  182.   struct interface *ifp;
  183.   int prefixlen;
  184.   ret = getifaddrs (&ifap); 
  185.   if (ret != 0)
  186.     {
  187.       zlog_err ("getifaddrs(): %s", strerror (errno));
  188.       return -1;
  189.     }
  190.   for (ifapfree = ifap; ifap; ifap = ifap->ifa_next)
  191.     {
  192.       ifp = if_lookup_by_name (ifap->ifa_name);
  193.       if (ifp == NULL)
  194. {
  195.   zlog_err ("if_getaddrs(): Can't lookup interface %sn",
  196.     ifap->ifa_name);
  197.   continue;
  198. }
  199.       if (ifap->ifa_addr->sa_family == AF_INET)
  200. {
  201.   struct sockaddr_in *addr;
  202.   struct sockaddr_in *mask;
  203.   struct sockaddr_in *dest;
  204.   struct in_addr *dest_pnt;
  205.   addr = (struct sockaddr_in *) ifap->ifa_addr;
  206.   mask = (struct sockaddr_in *) ifap->ifa_netmask;
  207.   prefixlen = ip_masklen (mask->sin_addr);
  208.   dest_pnt = NULL;
  209.   if (ifap->ifa_flags & IFF_POINTOPOINT) 
  210.     {
  211.       dest = (struct sockaddr_in *) ifap->ifa_dstaddr;
  212.       dest_pnt = &dest->sin_addr;
  213.     }
  214.   if (ifap->ifa_flags & IFF_BROADCAST)
  215.     {
  216.       dest = (struct sockaddr_in *) ifap->ifa_broadaddr;
  217.       dest_pnt = &dest->sin_addr;
  218.     }
  219.   connected_add_ipv4 (ifp, 0, &addr->sin_addr,
  220.       prefixlen, dest_pnt, NULL);
  221. }
  222. #ifdef HAVE_IPV6
  223.       if (ifap->ifa_addr->sa_family == AF_INET6)
  224. {
  225.   struct sockaddr_in6 *addr;
  226.   struct sockaddr_in6 *mask;
  227.   struct sockaddr_in6 *dest;
  228.   struct in6_addr *dest_pnt;
  229.   addr = (struct sockaddr_in6 *) ifap->ifa_addr;
  230.   mask = (struct sockaddr_in6 *) ifap->ifa_netmask;
  231.   prefixlen = ip6_masklen (mask->sin6_addr);
  232.   dest_pnt = NULL;
  233.   if (ifap->ifa_flags & IFF_POINTOPOINT) 
  234.     {
  235.       if (ifap->ifa_dstaddr)
  236. {
  237.   dest = (struct sockaddr_in6 *) ifap->ifa_dstaddr;
  238.   dest_pnt = &dest->sin6_addr;
  239. }
  240.     }
  241.   if (ifap->ifa_flags & IFF_BROADCAST)
  242.     {
  243.       if (ifap->ifa_broadaddr)
  244. {
  245.   dest = (struct sockaddr_in6 *) ifap->ifa_broadaddr;
  246.   dest_pnt = &dest->sin6_addr;
  247. }
  248.     }
  249.   connected_add_ipv6 (ifp, &addr->sin6_addr, prefixlen, dest_pnt);
  250. }
  251. #endif /* HAVE_IPV6 */
  252.     }
  253.   freeifaddrs (ifapfree);
  254.   return 0; 
  255. }
  256. #else /* HAVE_GETIFADDRS */
  257. /* Interface address lookup by ioctl.  This function only looks up
  258.    IPv4 address. */
  259. int
  260. if_get_addr (struct interface *ifp)
  261. {
  262.   int ret;
  263.   struct ifreq ifreq;
  264.   struct sockaddr_in addr;
  265.   struct sockaddr_in mask;
  266.   struct sockaddr_in dest;
  267.   struct in_addr *dest_pnt;
  268.   u_char prefixlen;
  269.   /* Interface's name and address family. */
  270.   strncpy (ifreq.ifr_name, ifp->name, IFNAMSIZ);
  271.   ifreq.ifr_addr.sa_family = AF_INET;
  272.   /* Interface's address. */
  273.   ret = if_ioctl (SIOCGIFADDR, (caddr_t) &ifreq);
  274.   if (ret < 0) 
  275.     {
  276.       if (errno != EADDRNOTAVAIL)
  277. {
  278.   zlog_warn ("SIOCGIFADDR fail: %s", strerror (errno));
  279.   return ret;
  280. }
  281.       return 0;
  282.     }
  283.   memcpy (&addr, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
  284.   /* Interface's network mask. */
  285.   ret = if_ioctl (SIOCGIFNETMASK, (caddr_t) &ifreq);
  286.   if (ret < 0) 
  287.     {
  288.       if (errno != EADDRNOTAVAIL) 
  289. {
  290.   zlog_warn ("SIOCGIFNETMASK fail: %s", strerror (errno));
  291.   return ret;
  292. }
  293.       return 0;
  294.     }
  295. #ifdef ifr_netmask
  296.   memcpy (&mask, &ifreq.ifr_netmask, sizeof (struct sockaddr_in));
  297. #else
  298.   memcpy (&mask, &ifreq.ifr_addr, sizeof (struct sockaddr_in));
  299. #endif /* ifr_netmask */
  300.   prefixlen = ip_masklen (mask.sin_addr);
  301.   /* Point to point or borad cast address pointer init. */
  302.   dest_pnt = NULL;
  303.   if (ifp->flags & IFF_POINTOPOINT) 
  304.     {
  305.       ret = if_ioctl (SIOCGIFDSTADDR, (caddr_t) &ifreq);
  306.       if (ret < 0) 
  307. {
  308.   if (errno != EADDRNOTAVAIL) 
  309.     {
  310.       zlog_warn ("SIOCGIFDSTADDR fail: %s", strerror (errno));
  311.       return ret;
  312.     }
  313.   return 0;
  314. }
  315.       memcpy (&dest, &ifreq.ifr_dstaddr, sizeof (struct sockaddr_in));
  316.       dest_pnt = &dest.sin_addr;
  317.     }
  318.   if (ifp->flags & IFF_BROADCAST)
  319.     {
  320.       ret = if_ioctl (SIOCGIFBRDADDR, (caddr_t) &ifreq);
  321.       if (ret < 0) 
  322. {
  323.   if (errno != EADDRNOTAVAIL) 
  324.     {
  325.       zlog_warn ("SIOCGIFBRDADDR fail: %s", strerror (errno));
  326.       return ret;
  327.     }
  328.   return 0;
  329. }
  330.       memcpy (&dest, &ifreq.ifr_broadaddr, sizeof (struct sockaddr_in));
  331.       dest_pnt = &dest.sin_addr;
  332.     }
  333.   /* Set address to the interface. */
  334.   connected_add_ipv4 (ifp, 0, &addr.sin_addr, prefixlen, dest_pnt, NULL);
  335.   return 0;
  336. }
  337. #endif /* HAVE_GETIFADDRS */
  338. /* Fetch interface information via ioctl(). */
  339. static void
  340. interface_info_ioctl ()
  341. {
  342.   listnode node;
  343.   struct interface *ifp;
  344.   
  345.   for (node = listhead (iflist); node; node = nextnode (node))
  346.     {
  347.       ifp = getdata (node);
  348.       if_get_index (ifp);
  349. #ifdef SIOCGIFHWADDR
  350.       if_get_hwaddr (ifp);
  351. #endif /* SIOCGIFHWADDR */
  352.       if_get_flags (ifp);
  353. #ifndef HAVE_GETIFADDRS
  354.       if_get_addr (ifp);
  355. #endif /* ! HAVE_GETIFADDRS */
  356.       if_get_mtu (ifp);
  357.       if_get_metric (ifp);
  358.     }
  359. }
  360. /* Lookup all interface information. */
  361. void
  362. interface_list ()
  363. {
  364.   /* Linux can do both proc & ioctl, ioctl is the only way to get
  365.      interface aliases in 2.2 series kernels. */
  366. #ifdef HAVE_PROC_NET_DEV
  367.   interface_list_proc ();
  368. #endif /* HAVE_PROC_NET_DEV */
  369.   interface_list_ioctl ();
  370.   /* After listing is done, get index, address, flags and other
  371.      interface's information. */
  372.   interface_info_ioctl ();
  373. #ifdef HAVE_GETIFADDRS
  374.   if_getaddrs ();
  375. #endif /* HAVE_GETIFADDRS */
  376. #if defined(HAVE_IPV6) && defined(HAVE_PROC_NET_IF_INET6)
  377.   /* Linux provides interface's IPv6 address via
  378.      /proc/net/if_inet6. */
  379.   ifaddr_proc_ipv6 ();
  380. #endif /* HAVE_IPV6 && HAVE_PROC_NET_IF_INET6 */
  381. }