Add test for "struct in6_addr" to the HAVE_IPV6 configure test.
[Samba/gebeck_regimport.git] / source3 / lib / interfaces.c
blob3b15e3e87b96f41601dccbe8c8133e0d389326dc
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
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.
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.
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/>.
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
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.
33 #ifndef AUTOCONF_TEST
34 #include "config.h"
35 #endif
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>
49 #ifdef HAVE_IFADDRS_H
50 #include <ifaddrs.h>
51 #endif
53 #ifdef HAVE_SYS_TIME_H
54 #include <sys/time.h>
55 #endif
57 #ifndef SIOCGIFCONF
58 #ifdef HAVE_SYS_SOCKIO_H
59 #include <sys/sockio.h>
60 #endif
61 #endif
63 #ifdef HAVE_STDLIB_H
64 #include <stdlib.h>
65 #endif
67 #ifdef HAVE_STRING_H
68 #include <string.h>
69 #endif
71 #ifdef HAVE_STRINGS_H
72 #include <strings.h>
73 #endif
75 #ifdef __COMPAR_FN_T
76 #define QSORT_CAST (__compar_fn_t)
77 #endif
79 #ifndef QSORT_CAST
80 #define QSORT_CAST (int (*)(const void *, const void *))
81 #endif
83 #ifdef HAVE_NET_IF_H
84 #include <net/if.h>
85 #endif
87 #include "interfaces.h"
88 #include "lib/replace/replace.h"
90 /****************************************************************************
91 Try the "standard" getifaddrs/freeifaddrs interfaces.
92 Also gets IPv6 interfaces.
93 ****************************************************************************/
95 #if HAVE_IFACE_GETIFADDRS
96 /****************************************************************************
97 Get the netmask address for a local interface.
98 ****************************************************************************/
100 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
102 struct ifaddrs *iflist = NULL;
103 struct ifaddrs *ifptr = NULL;
104 int total = 0;
105 size_t copy_size;
107 if (getifaddrs(&iflist) < 0) {
108 return -1;
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) {
116 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
118 copy_size = sizeof(struct sockaddr_in);
120 if (!ifptr->ifa_addr || !ifptr->ifa_netmask) {
121 continue;
124 ifaces[total].flags = ifptr->ifa_flags;
126 /* Check the interface is up. */
127 if (!(ifaces[total].flags & IFF_UP)) {
128 continue;
131 #if defined(HAVE_IPV6)
132 if (ifptr->ifa_addr->sa_family == AF_INET6) {
133 copy_size = sizeof(struct sockaddr_in6);
135 #endif
137 memcpy(&ifaces[total].ip, ifptr->ifa_addr, copy_size);
138 memcpy(&ifaces[total].netmask, ifptr->ifa_netmask, copy_size);
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;
154 strlcpy(ifaces[total].name, ifptr->ifa_name,
155 sizeof(ifaces[total].name));
156 total++;
159 freeifaddrs(iflist);
161 return total;
164 #define _FOUND_IFACE_ANY
165 #endif /* HAVE_IFACE_GETIFADDRS */
166 #if HAVE_IFACE_IFCONF
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.
171 It probably also works on any BSD style system. */
173 /****************************************************************************
174 Get the netmask address for a local interface.
175 ****************************************************************************/
177 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
179 struct ifconf ifc;
180 char buff[8192];
181 int fd, i, n;
182 struct ifreq *ifr=NULL;
183 int total = 0;
185 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
186 return -1;
189 ifc.ifc_len = sizeof(buff);
190 ifc.ifc_buf = buff;
192 if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
193 close(fd);
194 return -1;
197 ifr = ifc.ifc_req;
199 n = ifc.ifc_len / sizeof(struct ifreq);
201 /* Loop through interfaces, looking for given IP address */
202 for (i=n-1;i>=0 && total < max_interfaces;i--) {
204 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
206 /* Check the interface is up. */
207 if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
208 continue;
211 ifaces[total].flags = ifr[i].ifr_flags;
213 if (!(flags & IFF_UP)) {
214 continue;
217 if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
218 continue;
221 strlcpy(ifaces[total].name, ifr[i].ifr_name,
222 sizeof(ifaces[total].name));
224 memcpy(&ifaces[total].ip, &ifr[i].ifr_addr,
225 sizeof(struct sockaddr_in));
227 if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
228 continue;
231 memcpy(&ifaces[total].netmask, &ifr[i].ifr_netmask,
232 sizeof(struct sockaddr_in));
234 if (ifaces[total].flags & IFF_BROADCAST) {
235 if (ioctl(fd, SIOCGIFBRDADDR, &ifr[i]) != 0) {
236 continue;
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;
244 memcpy(&ifaces[total].bcast, &ifr[i].ifr_dstaddr,
245 sizeof(struct sockaddr_in));
246 } else {
247 continue;
250 total++;
253 close(fd);
255 return total;
258 #define _FOUND_IFACE_ANY
259 #endif /* HAVE_IFACE_IFCONF */
260 #ifdef HAVE_IFACE_IFREQ
262 #ifndef I_STR
263 #include <sys/stropts.h>
264 #endif
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 ****************************************************************************/
271 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
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;
280 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
281 return -1;
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;
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);
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;
305 /* Loop through interfaces */
307 for (i = 0; i<n && total < max_interfaces; i++) {
309 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
311 ifreq = ifr[i];
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;
320 ifaces[total].flags = ifreq.ifr_flags;
322 if (!(ifaces[total].flags & IFF_UP)) {
323 continue;
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;
333 strlcpy(ifaces[total].name, iname, sizeof(ifaces[total].name));
335 memcpy(&ifaces[total].ip, &ifreq.ifr_addr,
336 sizeof(struct sockaddr_in));
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;
345 memcpy(&ifaces[total].netmask, &ifreq.ifr_addr,
346 sizeof(struct sockaddr_in));
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;
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;
364 memcpy(&ifaces[total].bcast, &ifreq.ifr_dstaddr,
365 sizeof(struct sockaddr_in));
366 } else {
367 continue;
370 total++;
373 close(fd);
375 return total;
378 #define _FOUND_IFACE_ANY
379 #endif /* HAVE_IFACE_IFREQ */
380 #ifdef HAVE_IFACE_AIX
382 /****************************************************************************
383 This one is for AIX (tested on 4.2).
384 ****************************************************************************/
386 static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces)
388 char buff[8192];
389 int fd, i;
390 struct ifconf ifc;
391 struct ifreq *ifr=NULL;
392 int total = 0;
394 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
395 return -1;
399 ifc.ifc_len = sizeof(buff);
400 ifc.ifc_buf = buff;
402 if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
403 close(fd);
404 return -1;
407 ifr = ifc.ifc_req;
409 /* Loop through interfaces */
410 i = ifc.ifc_len;
412 while (i > 0 && total < max_interfaces) {
413 uint_t inc;
415 memset(&ifaces[total], '\0', sizeof(ifaces[total]));
417 inc = ifr->ifr_addr.sa_len;
419 if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
420 goto next;
423 ifaces[total].flags = ifr->ifr_flags;
425 if (!(ifaces[total].flags & IFF_UP)) {
426 goto next;
429 if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
430 goto next;
433 memcpy(&ifaces[total].ip, &ifr->ifr_addr,
434 sizeof(struct sockaddr_in));
436 strlcpy(ifaces[total].name, ifr->ifr_name,
437 sizeof(ifaces[total].name));
439 if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
440 goto next;
443 memcpy(&ifaces[total].netmask, &ifr->ifr_addr,
444 sizeof(struct sockaddr_in));
446 if (ifaces[total].flags & IFF_BROADCAST) {
447 if (ioctl(fd, SIOCGIFBRDADDR, &ifr[i]) != 0) {
448 continue;
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;
456 memcpy(&ifaces[total].bcast, &ifr[i].ifr_dstaddr,
457 sizeof(struct sockaddr_in));
458 } else {
459 continue;
463 total++;
465 next:
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. */
473 if (inc < sizeof(ifr->ifr_addr))
474 inc = sizeof(ifr->ifr_addr);
475 inc += IFNAMSIZ;
477 ifr = (struct ifreq*) (((char*) ifr) + inc);
478 i -= inc;
481 close(fd);
482 return total;
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)
490 return -1;
492 #endif
495 static int iface_comp(struct iface_struct *i1, struct iface_struct *i2)
497 int r;
499 #if defined(HAVE_IPV6)
501 * If we have IPv6 - sort these interfaces lower
502 * than any IPv4 ones.
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;
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;
516 r = memcmp(&s1->sin6_addr,
517 &s2->sin6_addr,
518 sizeof(struct in6_addr));
519 if (r) {
520 return r;
523 s1 = (struct sockaddr_in6 *)&i1->netmask;
524 s2 = (struct sockaddr_in6 *)&i2->netmask;
526 r = memcmp(&s1->sin6_addr,
527 &s2->sin6_addr,
528 sizeof(struct in6_addr));
529 if (r) {
530 return r;
533 #endif
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;
539 r = ntohl(s1->sin_addr.s_addr) -
540 ntohl(s2->sin_addr.s_addr);
541 if (r) {
542 return r;
545 s1 = (struct sockaddr_in *)&i1->netmask;
546 s2 = (struct sockaddr_in *)&i2->netmask;
548 return ntohl(s1->sin_addr.s_addr) -
549 ntohl(s2->sin_addr.s_addr);
551 return 0;
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)
559 int total, i, j;
561 total = _get_interfaces(ifaces, max_interfaces);
562 if (total <= 0) return total;
564 /* now we need to remove duplicates */
565 qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp);
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];
572 total--;
573 } else {
574 i++;
578 return total;
582 #ifdef AUTOCONF_TEST
583 /* this is the autoconf driver to test get_interfaces() */
585 int main()
587 struct iface_struct ifaces[MAX_INTERFACES];
588 int total = get_interfaces(ifaces, MAX_INTERFACES);
589 int i;
591 printf("got %d interfaces:\n", total);
592 if (total <= 0) {
593 exit(1);
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);
619 return 0;
621 #endif