r25476: Finally fix up new interface detection code :-).
[ira/wip.git] / source3 / lib / interfaces.c
1 /*
2    Unix SMB/CIFS implementation.
3    return a list of network interfaces
4    Copyright (C) Andrew Tridgell 1998
5    Copyright (C) Jeremy Allison 2007
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 /* working out the interfaces for a OS is an incredibly non-portable
23    thing. We have several possible implementations below, and autoconf
24    tries each of them to see what works
25
26    Note that this file does _not_ include includes.h. That is so this code
27    can be called directly from the autoconf tests. That also means
28    this code cannot use any of the normal Samba debug stuff or defines.
29    This is standalone code.
30
31 */
32
33 #ifndef AUTOCONF_TEST
34 #include "config.h"
35 #endif
36
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <netdb.h>
41 #include <sys/ioctl.h>
42 #include <netdb.h>
43 #include <sys/ioctl.h>
44 #include <sys/time.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48
49 #ifdef HAVE_IFADDRS_H
50 #include <ifaddrs.h>
51 #endif
52
53 #ifdef HAVE_SYS_TIME_H
54 #include <sys/time.h>
55 #endif
56
57 #ifndef SIOCGIFCONF
58 #ifdef HAVE_SYS_SOCKIO_H
59 #include <sys/sockio.h>
60 #endif
61 #endif
62
63 #ifdef HAVE_STDLIB_H
64 #include <stdlib.h>
65 #endif
66
67 #ifdef HAVE_STRING_H
68 #include <string.h>
69 #endif
70
71 #ifdef HAVE_STRINGS_H
72 #include <strings.h>
73 #endif
74
75 #ifdef __COMPAR_FN_T
76 #define QSORT_CAST (__compar_fn_t)
77 #endif
78
79 #ifndef QSORT_CAST
80 #define QSORT_CAST (int (*)(const void *, const void *))
81 #endif
82
83 #ifdef HAVE_NET_IF_H
84 #include <net/if.h>
85 #endif
86
87 #include "interfaces.h"
88
89 /****************************************************************************
90  Try the "standard" getifaddrs/freeifaddrs interfaces.
91  Also gets IPv6 interfaces.
92 ****************************************************************************/
93
94 #if HAVE_IFACE_GETIFADDRS
95 /****************************************************************************
96  Get the netmask address for a local interface.
97 ****************************************************************************/
98
99 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
100 {
101         struct ifaddrs *iflist = NULL;
102         struct ifaddrs *ifptr = NULL;
103         int total = 0;
104
105         if (getifaddrs(&iflist) < 0) {
106                 return -1;
107         }
108
109         /* Loop through interfaces, looking for given IP address */
110         for (ifptr = iflist, total = 0;
111                         ifptr != NULL && total < max_interfaces;
112                         ifptr = ifptr->ifa_next) {
113
114                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
115                         continue;
116                 }
117
118                 /* Skip ipv6 for now. */
119                 if (ifptr->ifa_addr->sa_family != AF_INET) {
120                         continue;
121                 }
122                 if (!(ifptr->ifa_flags & IFF_UP)) {
123                         continue;
124                 }
125
126                 ifaces[total].sa_family = ifptr->ifa_addr->sa_family;
127
128                 ifaces[total].iface_addr.ip =
129                         ((struct sockaddr_in *)ifptr->ifa_addr)->sin_addr;
130
131                 ifaces[total].iface_netmask.netmask =
132                         ((struct sockaddr_in *)ifptr->ifa_netmask)->sin_addr;
133
134                 strncpy(ifaces[total].name, ifptr->ifa_name,
135                                 sizeof(ifaces[total].name)-1);
136                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
137                 total++;
138         }
139
140         freeifaddrs(iflist);
141
142         return total;
143 }
144
145 #define _FOUND_IFACE_ANY
146 #endif /* HAVE_IFACE_GETIFADDRS */
147 #if HAVE_IFACE_IFCONF
148
149 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
150    V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
151
152    It probably also works on any BSD style system.  */
153
154 /****************************************************************************
155  Get the netmask address for a local interface.
156 ****************************************************************************/
157
158 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
159 {
160         struct ifconf ifc;
161         char buff[8192];
162         int fd, i, n;
163         struct ifreq *ifr=NULL;
164         int total = 0;
165         struct in_addr ipaddr;
166         struct in_addr nmask;
167         char *iname;
168
169         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
170                 return -1;
171         }
172
173         ifc.ifc_len = sizeof(buff);
174         ifc.ifc_buf = buff;
175
176         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
177                 close(fd);
178                 return -1;
179         }
180
181         ifr = ifc.ifc_req;
182
183         n = ifc.ifc_len / sizeof(struct ifreq);
184
185         /* Loop through interfaces, looking for given IP address */
186         for (i=n-1;i>=0 && total < max_interfaces;i--) {
187                 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
188                         continue;
189                 }
190
191                 iname = ifr[i].ifr_name;
192                 ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr;
193
194                 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
195                         continue;
196                 }
197
198                 if (!(ifr[i].ifr_flags & IFF_UP)) {
199                         continue;
200                 }
201
202                 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
203                         continue;
204                 }
205
206                 nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr;
207
208                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
209                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
210                 ifaces[total].sa_family = AF_INET;
211                 ifaces[total].iface_addr.ip = ipaddr;
212                 ifaces[total].iface_netmask.netmask = nmask;
213                 total++;
214         }
215
216         close(fd);
217
218         return total;
219 }
220
221 #define _FOUND_IFACE_ANY
222 #endif /* HAVE_IFACE_IFCONF */
223 #ifdef HAVE_IFACE_IFREQ
224
225 #ifndef I_STR
226 #include <sys/stropts.h>
227 #endif
228
229 /****************************************************************************
230  This should cover most of the streams based systems.
231  Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code.
232 ****************************************************************************/
233
234 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
235 {
236         struct ifreq ifreq;
237         struct strioctl strioctl;
238         char buff[8192];
239         int fd, i, n;
240         struct ifreq *ifr=NULL;
241         int total = 0;
242         struct in_addr ipaddr;
243         struct in_addr nmask;
244         char *iname;
245
246         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
247                 return -1;
248         }
249
250         strioctl.ic_cmd = SIOCGIFCONF;
251         strioctl.ic_dp  = buff;
252         strioctl.ic_len = sizeof(buff);
253         if (ioctl(fd, I_STR, &strioctl) < 0) {
254                 close(fd);
255                 return -1;
256         }
257
258         /* we can ignore the possible sizeof(int) here as the resulting
259            number of interface structures won't change */
260         n = strioctl.ic_len / sizeof(struct ifreq);
261
262         /* we will assume that the kernel returns the length as an int
263            at the start of the buffer if the offered size is a
264            multiple of the structure size plus an int */
265         if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
266                 ifr = (struct ifreq *)(buff + sizeof(int));
267         } else {
268                 ifr = (struct ifreq *)buff;
269         }
270
271         /* Loop through interfaces */
272
273         for (i = 0; i<n && total < max_interfaces; i++) {
274                 ifreq = ifr[i];
275
276                 strioctl.ic_cmd = SIOCGIFFLAGS;
277                 strioctl.ic_dp  = (char *)&ifreq;
278                 strioctl.ic_len = sizeof(struct ifreq);
279                 if (ioctl(fd, I_STR, &strioctl) != 0) {
280                         continue;
281                 }
282
283                 if (!(ifreq.ifr_flags & IFF_UP)) {
284                         continue;
285                 }
286
287                 strioctl.ic_cmd = SIOCGIFADDR;
288                 strioctl.ic_dp  = (char *)&ifreq;
289                 strioctl.ic_len = sizeof(struct ifreq);
290                 if (ioctl(fd, I_STR, &strioctl) != 0) {
291                         continue;
292                 }
293
294                 ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr;
295                 iname = ifreq.ifr_name;
296
297                 strioctl.ic_cmd = SIOCGIFNETMASK;
298                 strioctl.ic_dp  = (char *)&ifreq;
299                 strioctl.ic_len = sizeof(struct ifreq);
300                 if (ioctl(fd, I_STR, &strioctl) != 0) {
301                         continue;
302                 }
303
304                 nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr;
305
306                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
307                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
308                 ifaces[total].sa_family = AF_INET;
309                 ifaces[total].iface_addr.ip = ipaddr;
310                 ifaces[total].iface_netmask.netmask = nmask;
311
312                 total++;
313         }
314
315         close(fd);
316
317         return total;
318 }
319
320 #define _FOUND_IFACE_ANY
321 #endif /* HAVE_IFACE_IFREQ */
322 #ifdef HAVE_IFACE_AIX
323
324 /****************************************************************************
325  This one is for AIX (tested on 4.2).
326 ****************************************************************************/
327
328 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
329 {
330         char buff[8192];
331         int fd, i;
332         struct ifconf ifc;
333         struct ifreq *ifr=NULL;
334         struct in_addr ipaddr;
335         struct in_addr nmask;
336         char *iname;
337         int total = 0;
338
339         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
340                 return -1;
341         }
342
343
344         ifc.ifc_len = sizeof(buff);
345         ifc.ifc_buf = buff;
346
347         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
348                 close(fd);
349                 return -1;
350         }
351
352         ifr = ifc.ifc_req;
353
354         /* Loop through interfaces */
355         i = ifc.ifc_len;
356
357         while (i > 0 && total < max_interfaces) {
358                 uint_t inc;
359
360                 inc = ifr->ifr_addr.sa_len;
361
362                 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
363                         goto next;
364                 }
365
366                 ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr;
367                 iname = ifr->ifr_name;
368
369                 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
370                         goto next;
371                 }
372
373                 if (!(ifr->ifr_flags & IFF_UP)) {
374                         goto next;
375                 }
376
377                 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
378                         goto next;
379                 }
380
381                 nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
382
383                 strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1);
384                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
385                 ifaces[total].sa_family = AF_INET;
386                 ifaces[total].iface_addr.ip = ipaddr;
387                 ifaces[total].iface_netmask.netmask = nmask;
388
389                 total++;
390
391         next:
392                 /*
393                  * Patch from Archie Cobbs (archie@whistle.com).  The
394                  * addresses in the SIOCGIFCONF interface list have a
395                  * minimum size. Usually this doesn't matter, but if
396                  * your machine has tunnel interfaces, etc. that have
397                  * a zero length "link address", this does matter.  */
398
399                 if (inc < sizeof(ifr->ifr_addr))
400                         inc = sizeof(ifr->ifr_addr);
401                 inc += IFNAMSIZ;
402
403                 ifr = (struct ifreq*) (((char*) ifr) + inc);
404                 i -= inc;
405         }
406
407         close(fd);
408         return total;
409 }
410
411 #define _FOUND_IFACE_ANY
412 #endif /* HAVE_IFACE_AIX */
413 #ifndef _FOUND_IFACE_ANY
414 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
415 {
416         return -1;
417 }
418 #endif
419
420
421 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
422 {
423         int r;
424         r = strcmp(i1->name, i2->name);
425         if (r) {
426                 return r;
427         }
428         r = i1->sa_family - i2->sa_family;
429         if (r) {
430                 return r;
431         }
432
433 #ifdef AF_INET6
434         if (i1->sa_family == AF_INET6) {
435                 r = memcmp(&i1->iface_addr.ip6,
436                                 &i2->iface_addr.ip6,
437                                 sizeof(struct in6_addr));
438                 if (r) {
439                         return r;
440                 }
441                 r = memcmp(&i1->iface_netmask.netmask6,
442                                 &i1->iface_netmask.netmask6,
443                                 sizeof(struct in6_addr));
444                 if (r) {
445                         return r;
446                 }
447         }
448 #endif
449
450         if (i1->sa_family == AF_INET) {
451                 r = ntohl(i1->iface_addr.ip.s_addr) -
452                         ntohl(i2->iface_addr.ip.s_addr);
453                 if (r) {
454                         return r;
455                 }
456                 r = ntohl(i1->iface_netmask.netmask.s_addr) -
457                         ntohl(i2->iface_netmask.netmask.s_addr);
458         }
459         return r;
460 }
461
462 int get_interfaces(struct iface_struct *ifaces, int max_interfaces);
463 /* this wrapper is used to remove duplicates from the interface list generated
464    above */
465 int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
466 {
467         int total, i, j;
468
469         total = _get_interfaces(ifaces, max_interfaces);
470         if (total <= 0) return total;
471
472         /* now we need to remove duplicates */
473         qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
474
475         for (i=1;i<total;) {
476                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
477                         for (j=i-1;j<total-1;j++) {
478                                 ifaces[j] = ifaces[j+1];
479                         }
480                         total--;
481                 } else {
482                         i++;
483                 }
484         }
485
486         return total;
487 }
488
489
490 #ifdef AUTOCONF_TEST
491 /* this is the autoconf driver to test get_interfaces() */
492
493  int main()
494 {
495         struct iface_struct ifaces[MAX_INTERFACES];
496         int total = get_interfaces(ifaces, MAX_INTERFACES);
497         int i;
498
499         printf("got %d interfaces:\n", total);
500         if (total <= 0) {
501                 exit(1);
502         }
503
504         for (i=0;i<total;i++) {
505                 char addr[INET6_ADDRSTRLEN];
506                 printf("%-10s ", ifaces[i].name);
507                 printf("IP=%s ", inet_ntop(ifaces[i].sa_family,
508                                         (const void *)&ifaces[i].iface_addr.ip,
509                                         addr,
510                                         sizeof(addr)));
511                 printf("NETMASK=%s\n", inet_ntop(ifaces[i].sa_family,
512                                 (const void *)&ifaces[i].iface_netmask.netmask,
513                                 addr,
514                                 sizeof(addr)));
515         }
516         return 0;
517 }
518 #endif