Back to index

glibc  2.9
if_index.c
Go to the documentation of this file.
00001 /* Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2007
00002    Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <alloca.h>
00021 #include <errno.h>
00022 #include <string.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <unistd.h>
00026 #include <net/if.h>
00027 #include <sys/socket.h>
00028 #include <sys/ioctl.h>
00029 #include <bits/libc-lock.h>
00030 #include <not-cancel.h>
00031 #include <kernel-features.h>
00032 
00033 #include "netlinkaccess.h"
00034 
00035 
00036 /* Variable to signal whether SIOCGIFCONF is not available.  */
00037 # if __ASSUME_SIOCGIFNAME == 0
00038 static int old_siocgifconf;
00039 #else
00040 # define old_siocgifconf 0
00041 #endif
00042 
00043 
00044 unsigned int
00045 if_nametoindex (const char *ifname)
00046 {
00047 #ifndef SIOCGIFINDEX
00048   __set_errno (ENOSYS);
00049   return 0;
00050 #else
00051   struct ifreq ifr;
00052   int fd = __opensock ();
00053 
00054   if (fd < 0)
00055     return 0;
00056 
00057   strncpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
00058   if (__ioctl (fd, SIOCGIFINDEX, &ifr) < 0)
00059     {
00060       int saved_errno = errno;
00061       close_not_cancel_no_status (fd);
00062       if (saved_errno == EINVAL)
00063        __set_errno (ENOSYS);
00064       return 0;
00065     }
00066   close_not_cancel_no_status (fd);
00067   return ifr.ifr_ifindex;
00068 #endif
00069 }
00070 libc_hidden_def (if_nametoindex)
00071 
00072 
00073 void
00074 if_freenameindex (struct if_nameindex *ifn)
00075 {
00076   struct if_nameindex *ptr = ifn;
00077   while (ptr->if_name || ptr->if_index)
00078     {
00079       free (ptr->if_name);
00080       ++ptr;
00081     }
00082   free (ifn);
00083 }
00084 libc_hidden_def (if_freenameindex)
00085 
00086 
00087 #if __ASSUME_NETLINK_SUPPORT == 0
00088 static struct if_nameindex *
00089 if_nameindex_ioctl (void)
00090 {
00091   int fd = __opensock ();
00092   struct ifconf ifc;
00093   unsigned int nifs, i;
00094   int rq_len;
00095   struct if_nameindex *idx = NULL;
00096 # define RQ_IFS      4
00097 
00098   if (fd < 0)
00099     return NULL;
00100 
00101   ifc.ifc_buf = NULL;
00102 
00103   /* We may be able to get the needed buffer size directly, rather than
00104      guessing.  */
00105   if (! old_siocgifconf)
00106     {
00107       ifc.ifc_buf = NULL;
00108       ifc.ifc_len = 0;
00109       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0)
00110        {
00111 # if __ASSUME_SIOCGIFNAME == 0
00112          old_siocgifconf = 1;
00113 # endif
00114          rq_len = RQ_IFS * sizeof (struct ifreq);
00115        }
00116       else
00117        rq_len = ifc.ifc_len;
00118     }
00119   else
00120     rq_len = RQ_IFS * sizeof (struct ifreq);
00121 
00122   /* Read all the interfaces out of the kernel.  */
00123   ifc.ifc_buf = alloca (rq_len);
00124   ifc.ifc_len = rq_len;
00125   while (1)
00126     {
00127         if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0)
00128        {
00129          close_not_cancel_no_status (fd);
00130          return NULL;
00131        }
00132       if (ifc.ifc_len < rq_len || ! old_siocgifconf)
00133        break;
00134 
00135       ifc.ifc_buf = extend_alloca (ifc.ifc_buf, rq_len, 2 * rq_len);
00136       ifc.ifc_len = rq_len;
00137     }
00138 
00139   nifs = ifc.ifc_len / sizeof (struct ifreq);
00140 
00141   idx = malloc ((nifs + 1) * sizeof (struct if_nameindex));
00142   if (idx == NULL)
00143     {
00144       close_not_cancel_no_status (fd);
00145       __set_errno (ENOBUFS);
00146       return NULL;
00147     }
00148 
00149   for (i = 0; i < nifs; ++i)
00150     {
00151       struct ifreq *ifr = &ifc.ifc_req[i];
00152       idx[i].if_name = __strdup (ifr->ifr_name);
00153       if (idx[i].if_name == NULL
00154          || __ioctl (fd, SIOCGIFINDEX, ifr) < 0)
00155        {
00156          int saved_errno = errno;
00157          unsigned int j;
00158 
00159          for (j =  0; j < i; ++j)
00160            free (idx[j].if_name);
00161          free (idx);
00162          close_not_cancel_no_status (fd);
00163          if (saved_errno == EINVAL)
00164            saved_errno = ENOSYS;
00165          else if (saved_errno == ENOMEM)
00166            saved_errno = ENOBUFS;
00167          __set_errno (saved_errno);
00168          return NULL;
00169        }
00170       idx[i].if_index = ifr->ifr_ifindex;
00171     }
00172 
00173   idx[i].if_index = 0;
00174   idx[i].if_name = NULL;
00175 
00176   close_not_cancel_no_status (fd);
00177   return idx;
00178 }
00179 #endif
00180 
00181 
00182 static struct if_nameindex *
00183 if_nameindex_netlink (void)
00184 {
00185   struct netlink_handle nh = { 0, 0, 0, NULL, NULL };
00186   struct if_nameindex *idx = NULL;
00187 
00188   if (__no_netlink_support || __netlink_open (&nh) < 0)
00189     return NULL;
00190 
00191 
00192   /* Tell the kernel that we wish to get a list of all
00193      active interfaces.  Collect all data for every interface.  */
00194   if (__netlink_request (&nh, RTM_GETLINK) < 0)
00195     goto exit_free;
00196 
00197   /* Count the interfaces.  */
00198   unsigned int nifs = 0;
00199   for (struct netlink_res *nlp = nh.nlm_list; nlp; nlp = nlp->next)
00200     {
00201       struct nlmsghdr *nlh;
00202       size_t size = nlp->size;
00203 
00204       if (nlp->nlh == NULL)
00205        continue;
00206 
00207       /* Walk through all entries we got from the kernel and look, which
00208          message type they contain.  */
00209       for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size))
00210        {
00211          /* Check if the message is what we want.  */
00212          if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq)
00213            continue;
00214 
00215          if (nlh->nlmsg_type == NLMSG_DONE)
00216            break;           /* ok */
00217 
00218          if (nlh->nlmsg_type == RTM_NEWLINK)
00219            ++nifs;
00220        }
00221     }
00222 
00223   idx = malloc ((nifs + 1) * sizeof (struct if_nameindex));
00224   if (idx == NULL)
00225     {
00226     nomem:
00227       __set_errno (ENOBUFS);
00228       goto exit_free;
00229     }
00230 
00231   /* Add the interfaces.  */
00232   nifs = 0;
00233   for (struct netlink_res *nlp = nh.nlm_list; nlp; nlp = nlp->next)
00234     {
00235       struct nlmsghdr *nlh;
00236       size_t size = nlp->size;
00237 
00238       if (nlp->nlh == NULL)
00239        continue;
00240 
00241       /* Walk through all entries we got from the kernel and look, which
00242          message type they contain.  */
00243       for (nlh = nlp->nlh; NLMSG_OK (nlh, size); nlh = NLMSG_NEXT (nlh, size))
00244        {
00245          /* Check if the message is what we want.  */
00246          if ((pid_t) nlh->nlmsg_pid != nh.pid || nlh->nlmsg_seq != nlp->seq)
00247            continue;
00248 
00249          if (nlh->nlmsg_type == NLMSG_DONE)
00250            break;           /* ok */
00251 
00252          if (nlh->nlmsg_type == RTM_NEWLINK)
00253            {
00254              struct ifinfomsg *ifim = (struct ifinfomsg *) NLMSG_DATA (nlh);
00255              struct rtattr *rta = IFLA_RTA (ifim);
00256              size_t rtasize = IFLA_PAYLOAD (nlh);
00257 
00258              idx[nifs].if_index = ifim->ifi_index;
00259 
00260              while (RTA_OK (rta, rtasize))
00261               {
00262                 char *rta_data = RTA_DATA (rta);
00263                 size_t rta_payload = RTA_PAYLOAD (rta);
00264 
00265                 if (rta->rta_type == IFLA_IFNAME)
00266                   {
00267                     idx[nifs].if_name = __strndup (rta_data, rta_payload);
00268                     if (idx[nifs].if_name == NULL)
00269                      {
00270                        idx[nifs].if_index = 0;
00271                        if_freenameindex (idx);
00272                        idx = NULL;
00273                        goto nomem;
00274                      }
00275                     break;
00276                   }
00277 
00278                 rta = RTA_NEXT (rta, rtasize);
00279               }
00280 
00281              ++nifs;
00282            }
00283        }
00284     }
00285 
00286   idx[nifs].if_index = 0;
00287   idx[nifs].if_name = NULL;
00288 
00289  exit_free:
00290   __netlink_free_handle (&nh);
00291   __netlink_close (&nh);
00292 
00293   return idx;
00294 }
00295 
00296 
00297 struct if_nameindex *
00298 if_nameindex (void)
00299 {
00300 #ifndef SIOCGIFINDEX
00301   __set_errno (ENOSYS);
00302   return NULL;
00303 #else
00304   struct if_nameindex *result = if_nameindex_netlink ();
00305 # if __ASSUME_NETLINK_SUPPORT == 0
00306   if (__no_netlink_support)
00307     result = if_nameindex_ioctl ();
00308 # endif
00309   return result;
00310 #endif
00311 }
00312 libc_hidden_def (if_nameindex)
00313 
00314 
00315 char *
00316 if_indextoname (unsigned int ifindex, char *ifname)
00317 {
00318 #if !defined SIOCGIFINDEX && __ASSUME_SIOCGIFNAME == 0
00319   __set_errno (ENOSYS);
00320   return NULL;
00321 #else
00322 # if __ASSUME_SIOCGIFNAME == 0
00323   struct if_nameindex *idx;
00324   struct if_nameindex *p;
00325   char *result = NULL;
00326 # endif
00327 
00328 # if defined SIOCGIFNAME || __ASSUME_SIOCGIFNAME > 0
00329   /* We may be able to do the conversion directly, rather than searching a
00330      list.  This ioctl is not present in kernels before version 2.1.50.  */
00331   struct ifreq ifr;
00332   int fd;
00333 #  if __ASSUME_SIOCGIFNAME == 0
00334   static int siocgifname_works_not;
00335 
00336   if (!siocgifname_works_not)
00337 #  endif
00338     {
00339 #  if __ASSUME_SIOCGIFNAME == 0
00340       int serrno = errno;
00341 #  endif
00342       int status;
00343 
00344       fd = __opensock ();
00345 
00346       if (fd < 0)
00347        return NULL;
00348 
00349       ifr.ifr_ifindex = ifindex;
00350       status = __ioctl (fd, SIOCGIFNAME, &ifr);
00351 
00352       close_not_cancel_no_status (fd);
00353 
00354       if (status  < 0)
00355        {
00356 #  if __ASSUME_SIOCGIFNAME == 0
00357          if (errno == EINVAL)
00358            siocgifname_works_not = 1; /* Don't make the same mistake twice. */
00359          else
00360 #  endif
00361            {
00362              if (errno == ENODEV)
00363               /* POSIX requires ENXIO.  */
00364               __set_errno (ENXIO);
00365 
00366              return NULL;
00367            }
00368        }
00369       else
00370        return strncpy (ifname, ifr.ifr_name, IFNAMSIZ);
00371 
00372 #  if __ASSUME_SIOCGIFNAME == 0
00373       __set_errno (serrno);
00374 #  endif
00375     }
00376 # endif
00377 
00378 # if __ASSUME_SIOCGIFNAME == 0
00379   idx = if_nameindex ();
00380 
00381   if (idx != NULL)
00382     {
00383       for (p = idx; p->if_index || p->if_name; ++p)
00384        if (p->if_index == ifindex)
00385          {
00386            result = strncpy (ifname, p->if_name, IFNAMSIZ);
00387            break;
00388          }
00389 
00390       if_freenameindex (idx);
00391 
00392       if (result == NULL)
00393        __set_errno (ENXIO);
00394     }
00395   return result;
00396 # endif
00397 #endif
00398 }
00399 libc_hidden_def (if_indextoname)
00400 
00401 
00402 #if 0
00403 void
00404 internal_function
00405 __protocol_available (int *have_inet, int *have_inet6)
00406 {
00407   int fd = __opensock ();
00408   unsigned int nifs;
00409   int rq_len;
00410   struct ifconf ifc;
00411 # define RQ_IFS      4
00412 
00413   /* Wirst case assumption.  */
00414   *have_inet = 0;
00415   *have_inet6 = 0;
00416 
00417   if (fd < 0)
00418     /* We cannot open the socket.  No networking at all?  */
00419     return;
00420 
00421   /* We may be able to get the needed buffer size directly, rather than
00422      guessing.  */
00423   if (! old_siocgifconf)
00424     {
00425       ifc.ifc_buf = NULL;
00426       ifc.ifc_len = 0;
00427       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0)
00428        {
00429 # if __ASSUME_SIOCGIFNAME == 0
00430          old_siocgifconf = 1;
00431 # endif
00432          rq_len = RQ_IFS * sizeof (struct ifreq);
00433        }
00434       else
00435        rq_len = ifc.ifc_len;
00436     }
00437   else
00438     rq_len = RQ_IFS * sizeof (struct ifreq);
00439 
00440   /* Read all the interfaces out of the kernel.  */
00441   do
00442     {
00443       ifc.ifc_buf = alloca (ifc.ifc_len = rq_len);
00444       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0)
00445        {
00446          close_not_cancel_no_status (fd);
00447          return;
00448        }
00449       rq_len *= 2;
00450     }
00451   while (ifc.ifc_len == rq_len && old_siocgifconf);
00452 
00453   nifs = ifc.ifc_len / sizeof (struct ifreq);
00454 
00455   /* Go through all the interfaces and get the address.  */
00456   while (nifs-- > 0)
00457     if (__ioctl (fd, SIOCGIFADDR, &ifc.ifc_req[nifs]) >= 0)
00458       {
00459        /* We successfully got information about this interface.  Now
00460           test whether it is an IPv4 or IPv6 address.  */
00461        if (ifc.ifc_req[nifs].ifr_addr.sa_family == AF_INET)
00462          *have_inet = 1;
00463        else if (ifc.ifc_req[nifs].ifr_addr.sa_family == AF_INET6)
00464          *have_inet6 = 1;
00465 
00466        /* Note, this is & not &&.  It works since the values are always
00467           0 or 1.  */
00468        if (*have_inet & *have_inet6)
00469          /* We can stop early.  */
00470          break;
00471       }
00472 
00473   close_not_cancel_no_status (fd);
00474 }
00475 #endif