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