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.
39 #include <sys/types.h>
41 #include <sys/ioctl.h>
43 #include <sys/ioctl.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
53 #ifdef HAVE_SYS_TIME_H
58 #ifdef HAVE_SYS_SOCKIO_H
59 #include <sys/sockio.h>
76 #define QSORT_CAST (__compar_fn_t)
80 #define QSORT_CAST (int (*)(const void *, const void *))
87 #define SOCKET_WRAPPER_NOT_REPLACE
88 #include "interfaces.h"
89 #include "lib/replace/replace.h"
91 /****************************************************************************
93 ****************************************************************************/
95 /****************************************************************************
96 Create a struct sockaddr_storage with the netmask bits set to 1.
97 ****************************************************************************/
99 bool make_netmask(struct sockaddr_storage
*pss_out
,
100 const struct sockaddr_storage
*pss_in
,
101 unsigned long masklen
)
104 /* Now apply masklen bits of mask. */
105 #if defined(HAVE_IPV6)
106 if (pss_in
->ss_family
== AF_INET6
) {
107 char *p
= (char *)&((struct sockaddr_in6
*)pss_out
)->sin6_addr
;
113 for (i
= 0; masklen
>= 8; masklen
-= 8, i
++) {
116 /* Deal with the partial byte. */
117 *p
++ &= (0xff & ~(0xff>>masklen
));
119 for (;i
< sizeof(struct in6_addr
); i
++) {
125 if (pss_in
->ss_family
== AF_INET
) {
129 ((struct sockaddr_in
*)pss_out
)->sin_addr
.s_addr
=
130 htonl(((0xFFFFFFFFL
>> masklen
) ^ 0xFFFFFFFFL
));
136 /****************************************************************************
137 Create a struct sockaddr_storage set to the broadcast or network adress from
138 an incoming sockaddr_storage.
139 ****************************************************************************/
141 static void make_bcast_or_net(struct sockaddr_storage
*pss_out
,
142 const struct sockaddr_storage
*pss_in
,
143 const struct sockaddr_storage
*nmask
,
146 unsigned int i
= 0, len
= 0;
151 /* Set all zero netmask bits to 1. */
152 #if defined(HAVE_IPV6)
153 if (pss_in
->ss_family
== AF_INET6
) {
154 p
= (char *)&((struct sockaddr_in6
*)pss_out
)->sin6_addr
;
155 pmask
= (char *)&((struct sockaddr_in6
*)nmask
)->sin6_addr
;
159 if (pss_in
->ss_family
== AF_INET
) {
160 p
= (char *)&((struct sockaddr_in
*)pss_out
)->sin_addr
;
161 pmask
= (char *)&((struct sockaddr_in
*)nmask
)->sin_addr
;
165 for (i
= 0; i
< len
; i
++, p
++, pmask
++) {
167 *p
= (*p
& *pmask
) | (*pmask
^ 0xff);
175 void make_bcast(struct sockaddr_storage
*pss_out
,
176 const struct sockaddr_storage
*pss_in
,
177 const struct sockaddr_storage
*nmask
)
179 make_bcast_or_net(pss_out
, pss_in
, nmask
, true);
182 void make_net(struct sockaddr_storage
*pss_out
,
183 const struct sockaddr_storage
*pss_in
,
184 const struct sockaddr_storage
*nmask
)
186 make_bcast_or_net(pss_out
, pss_in
, nmask
, false);
189 /****************************************************************************
190 Try the "standard" getifaddrs/freeifaddrs interfaces.
191 Also gets IPv6 interfaces.
192 ****************************************************************************/
194 #if HAVE_IFACE_GETIFADDRS
195 /****************************************************************************
196 Get the netmask address for a local interface.
197 ****************************************************************************/
199 static int _get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
)
201 struct ifaddrs
*iflist
= NULL
;
202 struct ifaddrs
*ifptr
= NULL
;
206 if (getifaddrs(&iflist
) < 0) {
210 /* Loop through interfaces, looking for given IP address */
211 for (ifptr
= iflist
, total
= 0;
212 ifptr
!= NULL
&& total
< max_interfaces
;
213 ifptr
= ifptr
->ifa_next
) {
215 memset(&ifaces
[total
], '\0', sizeof(ifaces
[total
]));
217 copy_size
= sizeof(struct sockaddr_in
);
219 if (!ifptr
->ifa_addr
|| !ifptr
->ifa_netmask
) {
223 ifaces
[total
].flags
= ifptr
->ifa_flags
;
225 /* Check the interface is up. */
226 if (!(ifaces
[total
].flags
& IFF_UP
)) {
230 #if defined(HAVE_IPV6)
231 if (ifptr
->ifa_addr
->sa_family
== AF_INET6
) {
232 copy_size
= sizeof(struct sockaddr_in6
);
236 memcpy(&ifaces
[total
].ip
, ifptr
->ifa_addr
, copy_size
);
237 memcpy(&ifaces
[total
].netmask
, ifptr
->ifa_netmask
, copy_size
);
239 if (ifaces
[total
].flags
& (IFF_BROADCAST
|IFF_LOOPBACK
)) {
240 if (ifptr
->ifa_broadaddr
) {
241 memcpy(&ifaces
[total
].bcast
,
242 ifptr
->ifa_broadaddr
,
245 /* For some reason ifptr->ifa_broadaddr
246 * is null. Make one from ifa_addr and
249 make_bcast(&ifaces
[total
].bcast
,
251 &ifaces
[total
].netmask
);
253 } else if ((ifaces
[total
].flags
& IFF_POINTOPOINT
) &&
254 ifptr
->ifa_dstaddr
) {
255 memcpy(&ifaces
[total
].bcast
,
262 strlcpy(ifaces
[total
].name
, ifptr
->ifa_name
,
263 sizeof(ifaces
[total
].name
));
272 #define _FOUND_IFACE_ANY
273 #endif /* HAVE_IFACE_GETIFADDRS */
274 #if HAVE_IFACE_IFCONF
276 /* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
277 V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
279 It probably also works on any BSD style system. */
281 /****************************************************************************
282 Get the netmask address for a local interface.
283 ****************************************************************************/
285 static int _get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
)
290 struct ifreq
*ifr
=NULL
;
293 if ((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
297 ifc
.ifc_len
= sizeof(buff
);
300 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) != 0) {
307 n
= ifc
.ifc_len
/ sizeof(struct ifreq
);
309 /* Loop through interfaces, looking for given IP address */
310 for (i
=n
-1;i
>=0 && total
< max_interfaces
;i
--) {
312 memset(&ifaces
[total
], '\0', sizeof(ifaces
[total
]));
314 /* Check the interface is up. */
315 if (ioctl(fd
, SIOCGIFFLAGS
, &ifr
[i
]) != 0) {
319 ifaces
[total
].flags
= ifr
[i
].ifr_flags
;
321 if (!(ifaces
[total
].flags
& IFF_UP
)) {
325 if (ioctl(fd
, SIOCGIFADDR
, &ifr
[i
]) != 0) {
329 strlcpy(ifaces
[total
].name
, ifr
[i
].ifr_name
,
330 sizeof(ifaces
[total
].name
));
332 memcpy(&ifaces
[total
].ip
, &ifr
[i
].ifr_addr
,
333 sizeof(struct sockaddr_in
));
335 if (ioctl(fd
, SIOCGIFNETMASK
, &ifr
[i
]) != 0) {
339 memcpy(&ifaces
[total
].netmask
, &ifr
[i
].ifr_netmask
,
340 sizeof(struct sockaddr_in
));
342 if (ifaces
[total
].flags
& IFF_BROADCAST
) {
343 if (ioctl(fd
, SIOCGIFBRDADDR
, &ifr
[i
]) != 0) {
346 memcpy(&ifaces
[total
].bcast
, &ifr
[i
].ifr_broadaddr
,
347 sizeof(struct sockaddr_in
));
348 } else if (ifaces
[total
].flags
& IFF_POINTOPOINT
) {
349 if (ioctl(fd
, SIOCGIFDSTADDR
, &ifr
[i
]) != 0) {
352 memcpy(&ifaces
[total
].bcast
, &ifr
[i
].ifr_dstaddr
,
353 sizeof(struct sockaddr_in
));
366 #define _FOUND_IFACE_ANY
367 #endif /* HAVE_IFACE_IFCONF */
368 #ifdef HAVE_IFACE_IFREQ
371 #include <sys/stropts.h>
374 /****************************************************************************
375 This should cover most of the streams based systems.
376 Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code.
377 ****************************************************************************/
379 static int _get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
)
382 struct strioctl strioctl
;
385 struct ifreq
*ifr
=NULL
;
388 if ((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
392 strioctl
.ic_cmd
= SIOCGIFCONF
;
393 strioctl
.ic_dp
= buff
;
394 strioctl
.ic_len
= sizeof(buff
);
395 if (ioctl(fd
, I_STR
, &strioctl
) < 0) {
400 /* we can ignore the possible sizeof(int) here as the resulting
401 number of interface structures won't change */
402 n
= strioctl
.ic_len
/ sizeof(struct ifreq
);
404 /* we will assume that the kernel returns the length as an int
405 at the start of the buffer if the offered size is a
406 multiple of the structure size plus an int */
407 if (n
*sizeof(struct ifreq
) + sizeof(int) == strioctl
.ic_len
) {
408 ifr
= (struct ifreq
*)(buff
+ sizeof(int));
410 ifr
= (struct ifreq
*)buff
;
413 /* Loop through interfaces */
415 for (i
= 0; i
<n
&& total
< max_interfaces
; i
++) {
417 memset(&ifaces
[total
], '\0', sizeof(ifaces
[total
]));
421 strioctl
.ic_cmd
= SIOCGIFFLAGS
;
422 strioctl
.ic_dp
= (char *)&ifreq
;
423 strioctl
.ic_len
= sizeof(struct ifreq
);
424 if (ioctl(fd
, I_STR
, &strioctl
) != 0) {
428 ifaces
[total
].flags
= ifreq
.ifr_flags
;
430 if (!(ifaces
[total
].flags
& IFF_UP
)) {
434 strioctl
.ic_cmd
= SIOCGIFADDR
;
435 strioctl
.ic_dp
= (char *)&ifreq
;
436 strioctl
.ic_len
= sizeof(struct ifreq
);
437 if (ioctl(fd
, I_STR
, &strioctl
) != 0) {
441 strlcpy(ifaces
[total
].name
,
443 sizeof(ifaces
[total
].name
));
445 memcpy(&ifaces
[total
].ip
, &ifreq
.ifr_addr
,
446 sizeof(struct sockaddr_in
));
448 strioctl
.ic_cmd
= SIOCGIFNETMASK
;
449 strioctl
.ic_dp
= (char *)&ifreq
;
450 strioctl
.ic_len
= sizeof(struct ifreq
);
451 if (ioctl(fd
, I_STR
, &strioctl
) != 0) {
455 memcpy(&ifaces
[total
].netmask
, &ifreq
.ifr_addr
,
456 sizeof(struct sockaddr_in
));
458 if (ifaces
[total
].flags
& IFF_BROADCAST
) {
459 strioctl
.ic_cmd
= SIOCGIFBRDADDR
;
460 strioctl
.ic_dp
= (char *)&ifreq
;
461 strioctl
.ic_len
= sizeof(struct ifreq
);
462 if (ioctl(fd
, I_STR
, &strioctl
) != 0) {
465 memcpy(&ifaces
[total
].bcast
, &ifreq
.ifr_broadaddr
,
466 sizeof(struct sockaddr_in
));
467 } else if (ifaces
[total
].flags
& IFF_POINTOPOINT
) {
468 strioctl
.ic_cmd
= SIOCGIFDSTADDR
;
469 strioctl
.ic_dp
= (char *)&ifreq
;
470 strioctl
.ic_len
= sizeof(struct ifreq
);
471 if (ioctl(fd
, I_STR
, &strioctl
) != 0) {
474 memcpy(&ifaces
[total
].bcast
, &ifreq
.ifr_dstaddr
,
475 sizeof(struct sockaddr_in
));
488 #define _FOUND_IFACE_ANY
489 #endif /* HAVE_IFACE_IFREQ */
490 #ifdef HAVE_IFACE_AIX
492 /****************************************************************************
493 This one is for AIX (tested on 4.2).
494 ****************************************************************************/
496 static int _get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
)
501 struct ifreq
*ifr
=NULL
;
504 if ((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
509 ifc
.ifc_len
= sizeof(buff
);
512 if (ioctl(fd
, SIOCGIFCONF
, &ifc
) != 0) {
519 /* Loop through interfaces */
522 while (i
> 0 && total
< max_interfaces
) {
525 memset(&ifaces
[total
], '\0', sizeof(ifaces
[total
]));
527 inc
= ifr
->ifr_addr
.sa_len
;
529 if (ioctl(fd
, SIOCGIFFLAGS
, ifr
) != 0) {
533 ifaces
[total
].flags
= ifr
->ifr_flags
;
535 if (!(ifaces
[total
].flags
& IFF_UP
)) {
539 if (ioctl(fd
, SIOCGIFADDR
, ifr
) != 0) {
543 memcpy(&ifaces
[total
].ip
, &ifr
->ifr_addr
,
544 sizeof(struct sockaddr_in
));
546 strlcpy(ifaces
[total
].name
, ifr
->ifr_name
,
547 sizeof(ifaces
[total
].name
));
549 if (ioctl(fd
, SIOCGIFNETMASK
, ifr
) != 0) {
553 memcpy(&ifaces
[total
].netmask
, &ifr
->ifr_addr
,
554 sizeof(struct sockaddr_in
));
556 if (ifaces
[total
].flags
& IFF_BROADCAST
) {
557 if (ioctl(fd
, SIOCGIFBRDADDR
, ifr
) != 0) {
560 memcpy(&ifaces
[total
].bcast
, &ifr
->ifr_broadaddr
,
561 sizeof(struct sockaddr_in
));
562 } else if (ifaces
[total
].flags
& IFF_POINTOPOINT
) {
563 if (ioctl(fd
, SIOCGIFDSTADDR
, ifr
) != 0) {
566 memcpy(&ifaces
[total
].bcast
, &ifr
->ifr_dstaddr
,
567 sizeof(struct sockaddr_in
));
577 * Patch from Archie Cobbs (archie@whistle.com). The
578 * addresses in the SIOCGIFCONF interface list have a
579 * minimum size. Usually this doesn't matter, but if
580 * your machine has tunnel interfaces, etc. that have
581 * a zero length "link address", this does matter. */
583 if (inc
< sizeof(ifr
->ifr_addr
))
584 inc
= sizeof(ifr
->ifr_addr
);
587 ifr
= (struct ifreq
*) (((char*) ifr
) + inc
);
595 #define _FOUND_IFACE_ANY
596 #endif /* HAVE_IFACE_AIX */
597 #ifndef _FOUND_IFACE_ANY
598 static int _get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
)
605 static int iface_comp(struct iface_struct
*i1
, struct iface_struct
*i2
)
609 #if defined(HAVE_IPV6)
611 * If we have IPv6 - sort these interfaces lower
612 * than any IPv4 ones.
614 if (i1
->ip
.ss_family
== AF_INET6
&&
615 i2
->ip
.ss_family
== AF_INET
) {
617 } else if (i1
->ip
.ss_family
== AF_INET
&&
618 i2
->ip
.ss_family
== AF_INET6
) {
622 if (i1
->ip
.ss_family
== AF_INET6
) {
623 struct sockaddr_in6
*s1
= (struct sockaddr_in6
*)&i1
->ip
;
624 struct sockaddr_in6
*s2
= (struct sockaddr_in6
*)&i2
->ip
;
626 r
= memcmp(&s1
->sin6_addr
,
628 sizeof(struct in6_addr
));
633 s1
= (struct sockaddr_in6
*)&i1
->netmask
;
634 s2
= (struct sockaddr_in6
*)&i2
->netmask
;
636 r
= memcmp(&s1
->sin6_addr
,
638 sizeof(struct in6_addr
));
645 /* AIX uses __ss_family instead of ss_family inside of
646 sockaddr_storage. Instead of trying to figure out which field to
647 use, we can just cast it to a sockaddr.
650 if (((struct sockaddr
*)&i1
->ip
)->sa_family
== AF_INET
) {
651 struct sockaddr_in
*s1
= (struct sockaddr_in
*)&i1
->ip
;
652 struct sockaddr_in
*s2
= (struct sockaddr_in
*)&i2
->ip
;
654 r
= ntohl(s1
->sin_addr
.s_addr
) -
655 ntohl(s2
->sin_addr
.s_addr
);
660 s1
= (struct sockaddr_in
*)&i1
->netmask
;
661 s2
= (struct sockaddr_in
*)&i2
->netmask
;
663 return ntohl(s1
->sin_addr
.s_addr
) -
664 ntohl(s2
->sin_addr
.s_addr
);
669 int get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
);
670 /* this wrapper is used to remove duplicates from the interface list generated
672 int get_interfaces(struct iface_struct
*ifaces
, int max_interfaces
)
676 total
= _get_interfaces(ifaces
, max_interfaces
);
677 if (total
<= 0) return total
;
679 /* now we need to remove duplicates */
680 qsort(ifaces
, total
, sizeof(ifaces
[0]), QSORT_CAST iface_comp
);
683 if (iface_comp(&ifaces
[i
-1], &ifaces
[i
]) == 0) {
684 for (j
=i
-1;j
<total
-1;j
++) {
685 ifaces
[j
] = ifaces
[j
+1];
698 /* this is the autoconf driver to test get_interfaces() */
700 static socklen_t
calc_sa_size(struct sockaddr
*psa
)
702 socklen_t sl
= sizeof(struct sockaddr_in
);
703 #if defined(HAVE_IPV6)
704 if (psa
->sa_family
== AF_INET6
) {
705 sl
= sizeof(struct sockaddr_in6
);
713 struct iface_struct ifaces
[MAX_INTERFACES
];
714 int total
= get_interfaces(ifaces
, MAX_INTERFACES
);
717 printf("got %d interfaces:\n", total
);
722 for (i
=0;i
<total
;i
++) {
723 char addr
[INET6_ADDRSTRLEN
];
725 printf("%-10s ", ifaces
[i
].name
);
727 ret
= getnameinfo((struct sockaddr
*)&ifaces
[i
].ip
,
728 calc_sa_size(&ifaces
[i
].ip
),
730 NULL
, 0, NI_NUMERICHOST
);
731 printf("IP=%s ", addr
);
733 ret
= getnameinfo((struct sockaddr
*)&ifaces
[i
].netmask
,
734 calc_sa_size(&ifaces
[i
].netmask
),
736 NULL
, 0, NI_NUMERICHOST
);
737 printf("NETMASK=%s ", addr
);
739 ret
= getnameinfo((struct sockaddr
*)&ifaces
[i
].bcast
,
740 calc_sa_size(&ifaces
[i
].bcast
),
742 NULL
, 0, NI_NUMERICHOST
);
743 printf("BCAST=%s\n", addr
);