Fix an uninitialized variable warning
[tprouty/samba.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         size_t copy_size;
105
106         if (getifaddrs(&iflist) < 0) {
107                 return -1;
108         }
109
110         /* Loop through interfaces, looking for given IP address */
111         for (ifptr = iflist, total = 0;
112                         ifptr != NULL && total < max_interfaces;
113                         ifptr = ifptr->ifa_next) {
114
115                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
116
117                 copy_size = sizeof(struct sockaddr_in);
118
119                 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
120                         continue;
121                 }
122
123                 ifaces[total].flags = ifptr->ifa_flags;
124
125                 /* Check the interface is up. */
126                 if (!(ifaces[total].flags & IFF_UP)) {
127                         continue;
128                 }
129
130 #ifdef AF_INET6
131                 if (ifptr->ifa_addr->sa_family == AF_INET6) {
132                         copy_size = sizeof(struct sockaddr_in6);
133                 }
134 #endif
135
136                 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
137                 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
138
139                 if ((ifaces[total].flags & (IFF_BROADCAST|IFF_LOOPBACK)) &&
140                                 ifptr->ifa_broadaddr) {
141                         memcpy(&ifaces[total].bcast,
142                                 ifptr->ifa_broadaddr,
143                                 copy_size);
144                 } else if ((ifaces[total].flags & IFF_POINTOPOINT) &&
145                                ifptr->ifa_dstaddr ) {
146                         memcpy(&ifaces[total].bcast,
147                                 ifptr->ifa_dstaddr,
148                                 copy_size);
149                 } else {
150                         continue;
151                 }
152
153                 strncpy(ifaces[total].name, ifptr->ifa_name,
154                                 sizeof(ifaces[total].name)-1);
155                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
156                 total++;
157         }
158
159         freeifaddrs(iflist);
160
161         return total;
162 }
163
164 #define _FOUND_IFACE_ANY
165 #endif /* HAVE_IFACE_GETIFADDRS */
166 #if HAVE_IFACE_IFCONF
167
168 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
169    V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
170
171    It probably also works on any BSD style system.  */
172
173 /****************************************************************************
174  Get the netmask address for a local interface.
175 ****************************************************************************/
176
177 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
178 {
179         struct ifconf ifc;
180         char buff[8192];
181         int fd, i, n;
182         struct ifreq *ifr=NULL;
183         int total = 0;
184
185         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
186                 return -1;
187         }
188
189         ifc.ifc_len = sizeof(buff);
190         ifc.ifc_buf = buff;
191
192         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
193                 close(fd);
194                 return -1;
195         }
196
197         ifr = ifc.ifc_req;
198
199         n = ifc.ifc_len / sizeof(struct ifreq);
200
201         /* Loop through interfaces, looking for given IP address */
202         for (i=n-1;i>=0 && total < max_interfaces;i--) {
203
204                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
205
206                 /* Check the interface is up. */
207                 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
208                         continue;
209                 }
210
211                 ifaces[total].flags = ifr[i].ifr_flags;
212
213                 if (!(flags & IFF_UP)) {
214                         continue;
215                 }
216
217                 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
218                         continue;
219                 }
220
221                 strncpy(ifaces[total].name, ifr[i].ifr_name,
222                                 sizeof(ifaces[total].name)-1);
223                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
224
225                 memcpy(&ifaces[total].ip, &ifr[i].ifr_addr,
226                                 sizeof(struct sockaddr_in));
227
228                 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
229                         continue;
230                 }
231
232                 memcpy(&ifaces[total].netmask, &ifr[i].ifr_netmask,
233                                 sizeof(struct sockaddr_in));
234
235                 if (ifaces[total].flags & IFF_BROADCAST) {
236                         if (ioctl(fd, SIOCGIFBRDADDR, &ifr[i]) != 0) {
237                                 continue;
238                         }
239                         memcpy(&ifaces[total].bcast, &ifr[i].ifr_broadaddr,
240                                 sizeof(struct sockaddr_in));
241                 } else if (ifaces[total].flags & IFF_POINTOPOINT) {
242                         if (ioctl(fd, SIOCGIFDSTADDR, &ifr[i]) != 0) {
243                                 continue;
244                         }
245                         memcpy(&ifaces[total].bcast, &ifr[i].ifr_dstaddr,
246                                 sizeof(struct sockaddr_in));
247                 } else {
248                         continue;
249                 }
250
251                 total++;
252         }
253
254         close(fd);
255
256         return total;
257 }
258
259 #define _FOUND_IFACE_ANY
260 #endif /* HAVE_IFACE_IFCONF */
261 #ifdef HAVE_IFACE_IFREQ
262
263 #ifndef I_STR
264 #include <sys/stropts.h>
265 #endif
266
267 /****************************************************************************
268  This should cover most of the streams based systems.
269  Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code.
270 ****************************************************************************/
271
272 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
273 {
274         struct ifreq ifreq;
275         struct strioctl strioctl;
276         char buff[8192];
277         int fd, i, n;
278         struct ifreq *ifr=NULL;
279         int total = 0;
280
281         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
282                 return -1;
283         }
284
285         strioctl.ic_cmd = SIOCGIFCONF;
286         strioctl.ic_dp  = buff;
287         strioctl.ic_len = sizeof(buff);
288         if (ioctl(fd, I_STR, &strioctl) < 0) {
289                 close(fd);
290                 return -1;
291         }
292
293         /* we can ignore the possible sizeof(int) here as the resulting
294            number of interface structures won't change */
295         n = strioctl.ic_len / sizeof(struct ifreq);
296
297         /* we will assume that the kernel returns the length as an int
298            at the start of the buffer if the offered size is a
299            multiple of the structure size plus an int */
300         if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
301                 ifr = (struct ifreq *)(buff + sizeof(int));
302         } else {
303                 ifr = (struct ifreq *)buff;
304         }
305
306         /* Loop through interfaces */
307
308         for (i = 0; i<n && total < max_interfaces; i++) {
309
310                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
311
312                 ifreq = ifr[i];
313
314                 strioctl.ic_cmd = SIOCGIFFLAGS;
315                 strioctl.ic_dp  = (char *)&ifreq;
316                 strioctl.ic_len = sizeof(struct ifreq);
317                 if (ioctl(fd, I_STR, &strioctl) != 0) {
318                         continue;
319                 }
320
321                 ifaces[total].flags = ifreq.ifr_flags;
322
323                 if (!(ifaces[total].flags & IFF_UP)) {
324                         continue;
325                 }
326
327                 strioctl.ic_cmd = SIOCGIFADDR;
328                 strioctl.ic_dp  = (char *)&ifreq;
329                 strioctl.ic_len = sizeof(struct ifreq);
330                 if (ioctl(fd, I_STR, &strioctl) != 0) {
331                         continue;
332                 }
333
334                 strncpy(ifaces[total].name, iname,
335                                 sizeof(ifaces[total].name)-1);
336                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
337
338                 memcpy(&ifaces[total].ip, &ifreq.ifr_addr,
339                                 sizeof(struct sockaddr_in));
340
341                 strioctl.ic_cmd = SIOCGIFNETMASK;
342                 strioctl.ic_dp  = (char *)&ifreq;
343                 strioctl.ic_len = sizeof(struct ifreq);
344                 if (ioctl(fd, I_STR, &strioctl) != 0) {
345                         continue;
346                 }
347
348                 memcpy(&ifaces[total].netmask, &ifreq.ifr_addr,
349                                 sizeof(struct sockaddr_in));
350
351                 if (ifaces[total].flags & IFF_BROADCAST) {
352                         strioctl.ic_cmd = SIOCGIFBRDADDR;
353                         strioctl.ic_dp  = (char *)&ifreq;
354                         strioctl.ic_len = sizeof(struct ifreq);
355                         if (ioctl(fd, I_STR, &strioctl) != 0) {
356                                 continue;
357                         }
358                         memcpy(&ifaces[total].bcast, &ifreq.ifr_broadaddr,
359                                 sizeof(struct sockaddr_in));
360                 } else if (ifaces[total].flags & IFF_POINTOPOINT) {
361                         strioctl.ic_cmd = SIOCGIFDSTADDR;
362                         strioctl.ic_dp  = (char *)&ifreq;
363                         strioctl.ic_len = sizeof(struct ifreq);
364                         if (ioctl(fd, I_STR, &strioctl) != 0) {
365                                 continue;
366                         }
367                         memcpy(&ifaces[total].bcast, &ifreq.ifr_dstaddr,
368                                 sizeof(struct sockaddr_in));
369                 } else {
370                         continue;
371                 }
372
373                 total++;
374         }
375
376         close(fd);
377
378         return total;
379 }
380
381 #define _FOUND_IFACE_ANY
382 #endif /* HAVE_IFACE_IFREQ */
383 #ifdef HAVE_IFACE_AIX
384
385 /****************************************************************************
386  This one is for AIX (tested on 4.2).
387 ****************************************************************************/
388
389 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
390 {
391         char buff[8192];
392         int fd, i;
393         struct ifconf ifc;
394         struct ifreq *ifr=NULL;
395         int total = 0;
396
397         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
398                 return -1;
399         }
400
401
402         ifc.ifc_len = sizeof(buff);
403         ifc.ifc_buf = buff;
404
405         if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
406                 close(fd);
407                 return -1;
408         }
409
410         ifr = ifc.ifc_req;
411
412         /* Loop through interfaces */
413         i = ifc.ifc_len;
414
415         while (i > 0 && total < max_interfaces) {
416                 uint_t inc;
417
418                 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
419
420                 inc = ifr->ifr_addr.sa_len;
421
422                 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
423                         goto next;
424                 }
425
426                 ifaces[total].flags = ifr->ifr_flags;
427
428                 if (!(ifaces[total].flags & IFF_UP)) {
429                         goto next;
430                 }
431
432                 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
433                         goto next;
434                 }
435
436                 memcpy(&ifaces[total].ip, &ifr->ifr_addr,
437                                 sizeof(struct sockaddr_in));
438
439                 strncpy(ifaces[total].name, ifr->ifr_name,
440                                 sizeof(ifaces[total].name)-1);
441                 ifaces[total].name[sizeof(ifaces[total].name)-1] = 0;
442
443                 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
444                         goto next;
445                 }
446
447                 memcpy(&ifaces[total].netmask, &ifr->ifr_addr,
448                                 sizeof(struct sockaddr_in));
449
450                 if (ifaces[total].flags & IFF_BROADCAST) {
451                         if (ioctl(fd, SIOCGIFBRDADDR, &ifr[i]) != 0) {
452                                 continue;
453                         }
454                         memcpy(&ifaces[total].bcast, &ifr[i].ifr_broadaddr,
455                                 sizeof(struct sockaddr_in));
456                 } else if (ifaces[total].flags & IFF_POINTOPOINT) {
457                         if (ioctl(fd, SIOCGIFDSTADDR, &ifr[i]) != 0) {
458                                 continue;
459                         }
460                         memcpy(&ifaces[total].bcast, &ifr[i].ifr_dstaddr,
461                                 sizeof(struct sockaddr_in));
462                 } else {
463                         continue;
464                 }
465
466
467                 total++;
468
469         next:
470                 /*
471                  * Patch from Archie Cobbs (archie@whistle.com).  The
472                  * addresses in the SIOCGIFCONF interface list have a
473                  * minimum size. Usually this doesn't matter, but if
474                  * your machine has tunnel interfaces, etc. that have
475                  * a zero length "link address", this does matter.  */
476
477                 if (inc < sizeof(ifr->ifr_addr))
478                         inc = sizeof(ifr->ifr_addr);
479                 inc += IFNAMSIZ;
480
481                 ifr = (struct ifreq*) (((char*) ifr) + inc);
482                 i -= inc;
483         }
484
485         close(fd);
486         return total;
487 }
488
489 #define _FOUND_IFACE_ANY
490 #endif /* HAVE_IFACE_AIX */
491 #ifndef _FOUND_IFACE_ANY
492 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
493 {
494         return -1;
495 }
496 #endif
497
498
499 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
500 {
501         int r;
502
503 #ifdef AF_INET6
504         /*
505          * If we have IPv6 - sort these interfaces lower
506          * than any IPv4 ones.
507          */
508         if (i1->ip.ss_family == AF_INET6 &&
509                         i2->ip.ss_family == AF_INET) {
510                 return -1;
511         } else if (i1->ip.ss_family == AF_INET &&
512                         i2->ip.ss_family == AF_INET6) {
513                 return 1;
514         }
515
516         if (i1->ip.ss_family == AF_INET6) {
517                 struct sockaddr_in6 *s1 = (struct sockaddr_in6 *)&i1->ip;
518                 struct sockaddr_in6 *s2 = (struct sockaddr_in6 *)&i2->ip;
519
520                 r = memcmp(&s1->sin6_addr,
521                                 &s2->sin6_addr,
522                                 sizeof(struct in6_addr));
523                 if (r) {
524                         return r;
525                 }
526
527                 s1 = (struct sockaddr_in6 *)&i1->netmask;
528                 s2 = (struct sockaddr_in6 *)&i2->netmask;
529
530                 r = memcmp(&s1->sin6_addr,
531                                 &s2->sin6_addr,
532                                 sizeof(struct in6_addr));
533                 if (r) {
534                         return r;
535                 }
536         }
537 #endif
538
539         if (i1->ip.ss_family == AF_INET) {
540                 struct sockaddr_in *s1 = (struct sockaddr_in *)&i1->ip;
541                 struct sockaddr_in *s2 = (struct sockaddr_in *)&i2->ip;
542
543                 r = ntohl(s1->sin_addr.s_addr) -
544                         ntohl(s2->sin_addr.s_addr);
545                 if (r) {
546                         return r;
547                 }
548
549                 s1 = (struct sockaddr_in *)&i1->netmask;
550                 s2 = (struct sockaddr_in *)&i2->netmask;
551
552                 return ntohl(s1->sin_addr.s_addr) -
553                         ntohl(s2->sin_addr.s_addr);
554         }
555         return 0;
556 }
557
558 int get_interfaces(struct iface_struct *ifaces, int max_interfaces);
559 /* this wrapper is used to remove duplicates from the interface list generated
560    above */
561 int get_interfaces(struct iface_struct *ifaces, int max_interfaces)
562 {
563         int total, i, j;
564
565         total = _get_interfaces(ifaces, max_interfaces);
566         if (total <= 0) return total;
567
568         /* now we need to remove duplicates */
569         qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
570
571         for (i=1;i<total;) {
572                 if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) {
573                         for (j=i-1;j<total-1;j++) {
574                                 ifaces[j] = ifaces[j+1];
575                         }
576                         total--;
577                 } else {
578                         i++;
579                 }
580         }
581
582         return total;
583 }
584
585
586 #ifdef AUTOCONF_TEST
587 /* this is the autoconf driver to test get_interfaces() */
588
589  int main()
590 {
591         struct iface_struct ifaces[MAX_INTERFACES];
592         int total = get_interfaces(ifaces, MAX_INTERFACES);
593         int i;
594
595         printf("got %d interfaces:\n", total);
596         if (total <= 0) {
597                 exit(1);
598         }
599
600         for (i=0;i<total;i++) {
601                 char addr[INET6_ADDRSTRLEN];
602                 int ret;
603                 printf("%-10s ", ifaces[i].name);
604                 addr[0] = '\0';
605                 ret = getnameinfo((struct sockaddr *)&ifaces[i].ip,
606                                 sizeof(ifaces[i].ip),
607                                 addr, sizeof(addr),
608                                 NULL, 0, NI_NUMERICHOST);
609                 printf("IP=%s ", addr);
610                 addr[0] = '\0';
611                 ret = getnameinfo((struct sockaddr *)&ifaces[i].netmask,
612                                 sizeof(ifaces[i].netmask),
613                                 addr, sizeof(addr),
614                                 NULL, 0, NI_NUMERICHOST);
615                 printf("NETMASK=%s ", addr);
616                 addr[0] = '\0';
617                 ret = getnameinfo((struct sockaddr *)&ifaces[i].bcast,
618                                 sizeof(ifaces[i].bcast),
619                                 addr, sizeof(addr),
620                                 NULL, 0, NI_NUMERICHOST);
621                 printf("BCAST=%s\n", addr);
622         }
623         return 0;
624 }
625 #endif