Use only non-cancelable interfaces.
[kopensolaris-gnu/glibc.git] / sysdeps / unix / sysv / linux / if_index.c
1 /* Copyright (C) 1997,98,99,2000,2002,2003 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02111-1307 USA.  */
18
19 #include <errno.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <net/if.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <bits/libc-lock.h>
28 #include <not-cancel.h>
29
30 #include "kernel-features.h"
31
32 /* Variable to signal whether SIOCGIFCONF is not available.  */
33 #if __ASSUME_SIOCGIFNAME == 0
34 static int old_siocgifconf;
35 #else
36 # define old_siocgifconf 0
37 #endif
38
39
40 unsigned int
41 if_nametoindex (const char *ifname)
42 {
43 #ifndef SIOCGIFINDEX
44   __set_errno (ENOSYS);
45   return 0;
46 #else
47   struct ifreq ifr;
48   int fd = __opensock ();
49
50   if (fd < 0)
51     return 0;
52
53   strncpy (ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
54   if (__ioctl (fd, SIOCGIFINDEX, &ifr) < 0)
55     {
56       int saved_errno = errno;
57       close_not_cancel_no_status (fd);
58       if (saved_errno == EINVAL)
59         __set_errno (ENOSYS);
60       return 0;
61     }
62   close_not_cancel_no_status (fd);
63   return ifr.ifr_ifindex;
64 #endif
65 }
66 libc_hidden_def (if_nametoindex)
67
68
69 void
70 if_freenameindex (struct if_nameindex *ifn)
71 {
72   struct if_nameindex *ptr = ifn;
73   while (ptr->if_name || ptr->if_index)
74     {
75       if (ptr->if_name)
76         free (ptr->if_name);
77       ++ptr;
78     }
79   free (ifn);
80 }
81
82 struct if_nameindex *
83 if_nameindex (void)
84 {
85 #ifndef SIOCGIFINDEX
86   __set_errno (ENOSYS);
87   return NULL;
88 #else
89   int fd = __opensock ();
90   struct ifconf ifc;
91   unsigned int nifs, i;
92   int rq_len;
93   struct if_nameindex *idx = NULL;
94 # define RQ_IFS 4
95
96   if (fd < 0)
97     return NULL;
98
99   ifc.ifc_buf = NULL;
100
101   /* We may be able to get the needed buffer size directly, rather than
102      guessing.  */
103   if (! old_siocgifconf)
104     {
105       ifc.ifc_buf = NULL;
106       ifc.ifc_len = 0;
107       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0)
108         {
109 # if __ASSUME_SIOCGIFNAME == 0
110           old_siocgifconf = 1;
111 # endif
112           rq_len = RQ_IFS * sizeof (struct ifreq);
113         }
114       else
115         rq_len = ifc.ifc_len;
116     }
117   else
118     rq_len = RQ_IFS * sizeof (struct ifreq);
119
120   /* Read all the interfaces out of the kernel.  */
121   do
122     {
123       ifc.ifc_buf = alloca (ifc.ifc_len = rq_len);
124       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0)
125         {
126           close_not_cancel_no_status (fd);
127           return NULL;
128         }
129       rq_len *= 2;
130     }
131   while (ifc.ifc_len == rq_len && old_siocgifconf);
132
133   nifs = ifc.ifc_len / sizeof (struct ifreq);
134
135   idx = malloc ((nifs + 1) * sizeof (struct if_nameindex));
136   if (idx == NULL)
137     {
138       close_not_cancel_no_status (fd);
139       __set_errno (ENOBUFS);
140       return NULL;
141     }
142
143   for (i = 0; i < nifs; ++i)
144     {
145       struct ifreq *ifr = &ifc.ifc_req[i];
146       idx[i].if_name = __strdup (ifr->ifr_name);
147       if (idx[i].if_name == NULL
148           || __ioctl (fd, SIOCGIFINDEX, ifr) < 0)
149         {
150           int saved_errno = errno;
151           unsigned int j;
152
153           for (j =  0; j < i; ++j)
154             free (idx[j].if_name);
155           free (idx);
156           close_not_cancel_no_status (fd);
157           if (saved_errno == EINVAL)
158             saved_errno = ENOSYS;
159           else if (saved_errno == ENOMEM)
160             saved_errno = ENOBUFS;
161           __set_errno (saved_errno);
162           return NULL;
163         }
164       idx[i].if_index = ifr->ifr_ifindex;
165     }
166
167   idx[i].if_index = 0;
168   idx[i].if_name = NULL;
169
170   close_not_cancel_no_status (fd);
171   return idx;
172 #endif
173 }
174
175 char *
176 if_indextoname (unsigned int ifindex, char *ifname)
177 {
178 #if !defined SIOCGIFINDEX && __ASSUME_SIOCGIFNAME == 0
179   __set_errno (ENOSYS);
180   return NULL;
181 #else
182 # if __ASSUME_SIOCGIFNAME == 0
183   struct if_nameindex *idx;
184   struct if_nameindex *p;
185   char *result = NULL;
186 # endif
187
188 # if defined SIOCGIFNAME || __ASSUME_SIOCGIFNAME > 0
189   /* We may be able to do the conversion directly, rather than searching a
190      list.  This ioctl is not present in kernels before version 2.1.50.  */
191   struct ifreq ifr;
192   int fd;
193 #  if __ASSUME_SIOCGIFNAME == 0
194   static int siocgifname_works_not;
195
196   if (!siocgifname_works_not)
197 #  endif
198     {
199 #  if __ASSUME_SIOCGIFNAME == 0
200       int serrno = errno;
201 #  endif
202       int status;
203
204       fd = __opensock ();
205
206       if (fd < 0)
207         return NULL;
208
209       ifr.ifr_ifindex = ifindex;
210       status = __ioctl (fd, SIOCGIFNAME, &ifr);
211
212       close_not_cancel_no_status (fd);
213
214 #  if __ASSUME_SIOCGIFNAME == 0
215       if (status  < 0)
216         {
217           if (errno == EINVAL)
218             siocgifname_works_not = 1; /* Don't make the same mistake twice. */
219         }
220       else
221         return strncpy (ifname, ifr.ifr_name, IFNAMSIZ);
222
223       __set_errno (serrno);
224 #  else
225       return status < 0 ? NULL : strncpy (ifname, ifr.ifr_name, IFNAMSIZ);
226 #  endif
227     }
228 # endif
229
230 # if __ASSUME_SIOCGIFNAME == 0
231   idx = if_nameindex ();
232
233   if (idx != NULL)
234     {
235       for (p = idx; p->if_index || p->if_name; ++p)
236         if (p->if_index == ifindex)
237           {
238             result = strncpy (ifname, p->if_name, IFNAMSIZ);
239             break;
240           }
241
242       if_freenameindex (idx);
243     }
244   return result;
245 # endif
246 #endif
247 }
248 libc_hidden_def (if_indextoname)
249
250 #if 0
251 void
252 internal_function
253 __protocol_available (int *have_inet, int *have_inet6)
254 {
255   int fd = __opensock ();
256   unsigned int nifs;
257   int rq_len;
258   struct ifconf ifc;
259 # define RQ_IFS 4
260
261   /* Wirst case assumption.  */
262   *have_inet = 0;
263   *have_inet6 = 0;
264
265   if (fd < 0)
266     /* We cannot open the socket.  No networking at all?  */
267     return;
268
269   /* We may be able to get the needed buffer size directly, rather than
270      guessing.  */
271   if (! old_siocgifconf)
272     {
273       ifc.ifc_buf = NULL;
274       ifc.ifc_len = 0;
275       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0)
276         {
277 # if __ASSUME_SIOCGIFNAME == 0
278           old_siocgifconf = 1;
279 # endif
280           rq_len = RQ_IFS * sizeof (struct ifreq);
281         }
282       else
283         rq_len = ifc.ifc_len;
284     }
285   else
286     rq_len = RQ_IFS * sizeof (struct ifreq);
287
288   /* Read all the interfaces out of the kernel.  */
289   do
290     {
291       ifc.ifc_buf = alloca (ifc.ifc_len = rq_len);
292       if (__ioctl (fd, SIOCGIFCONF, &ifc) < 0)
293         {
294           close_not_cancel_no_status (fd);
295           return;
296         }
297       rq_len *= 2;
298     }
299   while (ifc.ifc_len == rq_len && old_siocgifconf);
300
301   nifs = ifc.ifc_len / sizeof (struct ifreq);
302
303   /* Go through all the interfaces and get the address.  */
304   while (nifs-- > 0)
305     if (__ioctl (fd, SIOCGIFADDR, &ifc.ifc_req[nifs]) >= 0)
306       {
307         /* We successfully got information about this interface.  Now
308            test whether it is an IPv4 or IPv6 address.  */
309         if (ifc.ifc_req[nifs].ifr_addr.sa_family == AF_INET)
310           *have_inet = 1;
311         else if (ifc.ifc_req[nifs].ifr_addr.sa_family == AF_INET6)
312           *have_inet6 = 1;
313
314         /* Note, this is & not &&.  It works since the values are always
315            0 or 1.  */
316         if (*have_inet & *have_inet6)
317           /* We can stop early.  */
318           break;
319       }
320
321   close_not_cancel_no_status (fd);
322 }
323 #endif