r26467: Use getifaddrs() for interface enumeration and provide replacements for platf...
authorJelmer Vernooij <jelmer@samba.org>
Sun, 16 Dec 2007 01:39:01 +0000 (02:39 +0100)
committerMichael Adam <obnox@samba.org>
Tue, 19 Feb 2008 20:55:09 +0000 (21:55 +0100)
(lib/replace part of 9b4924fbd8619033c55b4c6e2589da247332e7db - Michael)

source/lib/replace/getifaddrs.c [new file with mode: 0644]
source/lib/replace/getifaddrs.m4 [new file with mode: 0644]
source/lib/replace/libreplace.m4
source/lib/replace/system/network.h

diff --git a/source/lib/replace/getifaddrs.c b/source/lib/replace/getifaddrs.c
new file mode 100644 (file)
index 0000000..3969535
--- /dev/null
@@ -0,0 +1,391 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+   Copyright (C) Andrew Tridgell 1998
+   Copyright (C) Jeremy Allison 2007
+   Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/network.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifndef SIOCGIFCONF
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#endif
+
+#ifdef HAVE_IFACE_GETIFADDRS
+#define _FOUND_IFACE_ANY
+#else
+
+void freeifaddrs(struct ifaddrs *ifp)
+{
+       free(ifp->ifa_name);
+       free(ifp->ifa_addr);
+       free(ifp->ifa_netmask);
+       free(ifp->ifa_dstaddr);
+       if (ifp->ifa_next != NULL)
+               freeifaddrs(ifp->ifa_next);
+       free(ifp);
+}
+
+struct sockaddr *sockaddr_dup(struct sockaddr *sa)
+{
+       struct sockaddr *ret = calloc(1, sa->sa_len);
+       if (ret == NULL)
+               return NULL;
+       memcpy(ret, sa, sa->sa_len);
+       return ret;
+}
+#endif
+
+#if HAVE_IFACE_IFCONF
+
+/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1
+   V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2.
+
+   It probably also works on any BSD style system.  */
+
+int getifaddrs(struct ifaddrs **ifap)
+{
+       struct ifconf ifc;
+       char buff[8192];
+       int fd, i, n;
+       struct ifreq *ifr=NULL;
+       int total = 0;
+       struct in_addr ipaddr;
+       struct in_addr nmask;
+       char *iname;
+       struct ifaddrs *curif, *lastif;
+
+       *ifap = NULL;
+
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               return -1;
+       }
+  
+       ifc.ifc_len = sizeof(buff);
+       ifc.ifc_buf = buff;
+
+       if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+               close(fd);
+               return -1;
+       } 
+
+       ifr = ifc.ifc_req;
+  
+       n = ifc.ifc_len / sizeof(struct ifreq);
+
+       /* Loop through interfaces, looking for given IP address */
+       for (i=n-1;i>=0 && total < max_interfaces;i--) {
+               if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) {
+                       freeifaddrs(*ifap);
+               }
+
+               curif = calloc(1, sizeof(struct ifaddrs));
+               if (lastif == NULL) {
+                       *ifap = curif;
+               } else {
+                       lastif->ifa_next = (*ifap);
+               }
+
+               curif->ifa_name = strdup(ifr[i].ifr_name);
+               curif->ifa_flags = ifreq.ifr_flags;
+               curif->ifa_addr = sockaddr_dup(&ifr[i].ifr_addr);
+               curif->ifa_dstaddr = NULL;
+               curif->ifa_data = NULL;
+               curif->ifa_next = NULL;
+               curif->ifa_netmask = NULL;
+
+               if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) {
+                       freeifaddrs(*ifap);
+                       return -1;
+               }  
+
+               if (!(ifr[i].ifr_flags & IFF_UP)) {
+                       freeifaddrs(curif);
+                       continue;
+               }
+
+               if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) {
+                       freeifaddrs(*ifap);
+                       return -1;
+               }  
+
+               curif->ifa_netmask = sockaddr_dup(&ifr[i].ifr_addr);
+
+               lastif = curif;
+       }
+
+       close(fd);
+
+       return 0;
+}  
+
+#define _FOUND_IFACE_ANY
+#endif /* HAVE_IFACE_IFCONF */
+#ifdef HAVE_IFACE_IFREQ
+
+#ifndef I_STR
+#include <sys/stropts.h>
+#endif
+
+/****************************************************************************
+this should cover most of the streams based systems
+Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code
+****************************************************************************/
+int getifaddrs(struct ifaddrs **ifap)
+{
+       struct ifreq ifreq;
+       struct strioctl strioctl;
+       char buff[8192];
+       int fd, i, n;
+       struct ifreq *ifr=NULL;
+       int total = 0;
+       struct in_addr ipaddr;
+       struct in_addr nmask;
+       char *iname;
+       struct ifaddrs *curif;
+
+       *ifap = NULL;
+
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               return -1;
+       }
+  
+       strioctl.ic_cmd = SIOCGIFCONF;
+       strioctl.ic_dp  = buff;
+       strioctl.ic_len = sizeof(buff);
+       if (ioctl(fd, I_STR, &strioctl) < 0) {
+               close(fd);
+               return -1;
+       } 
+
+       /* we can ignore the possible sizeof(int) here as the resulting
+          number of interface structures won't change */
+       n = strioctl.ic_len / sizeof(struct ifreq);
+
+       /* we will assume that the kernel returns the length as an int
+           at the start of the buffer if the offered size is a
+           multiple of the structure size plus an int */
+       if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) {
+               ifr = (struct ifreq *)(buff + sizeof(int));  
+       } else {
+               ifr = (struct ifreq *)buff;  
+       }
+
+       /* Loop through interfaces */
+
+       for (i = 0; i<n && total < max_interfaces; i++) {
+               ifreq = ifr[i];
+  
+               strioctl.ic_cmd = SIOCGIFFLAGS;
+               strioctl.ic_dp  = (char *)&ifreq;
+               strioctl.ic_len = sizeof(struct ifreq);
+               if (ioctl(fd, I_STR, &strioctl) != 0) {
+                       freeifaddrs(*ifap);
+                       return -1;
+               }
+               
+               if (!(ifreq.ifr_flags & IFF_UP)) {
+                       continue;
+               }
+
+               strioctl.ic_cmd = SIOCGIFADDR;
+               strioctl.ic_dp  = (char *)&ifreq;
+               strioctl.ic_len = sizeof(struct ifreq);
+               if (ioctl(fd, I_STR, &strioctl) != 0) {
+                       freeifaddrs(*ifap);
+                       return -1;
+               }
+
+               curif = calloc(1, sizeof(struct ifaddrs));
+               if (lastif == NULL) {
+                       *ifap = curif;
+               } else {
+                       lastif->ifa_next = (*ifap);
+               }
+
+               curif->ifa_name = strdup(ifreq.ifr_name);
+               curif->ifa_flags = ifreq.ifr_flags;
+               curif->ifa_addr = sockaddr_dup(&ifreq.ifr_addr);
+               curif->ifa_dstaddr = NULL;
+               curif->ifa_data = NULL;
+               curif->ifa_next = NULL;
+               curif->ifa_netmask = NULL;
+
+               strioctl.ic_cmd = SIOCGIFNETMASK;
+               strioctl.ic_dp  = (char *)&ifreq;
+               strioctl.ic_len = sizeof(struct ifreq);
+               if (ioctl(fd, I_STR, &strioctl) != 0) {
+                       freeifaddrs(*ifap);
+                       return -1;
+               }
+
+               curif->ifa_netmask = sockaddr_dup(&ifreq.ifr_addr);
+
+               lastif = curif;
+       }
+
+       close(fd);
+
+       return 0;
+}
+
+#define _FOUND_IFACE_ANY
+#endif /* HAVE_IFACE_IFREQ */
+#ifdef HAVE_IFACE_AIX
+
+/****************************************************************************
+this one is for AIX (tested on 4.2)
+****************************************************************************/
+int getifaddrs(struct ifaddrs **ifap)
+{
+       char buff[8192];
+       int fd, i;
+       struct ifconf ifc;
+       struct ifreq *ifr=NULL;
+       struct in_addr ipaddr;
+       struct in_addr nmask;
+       char *iname;
+       int total = 0;
+       struct ifaddrs *curif, *lastif;
+
+       *ifap = NULL;
+
+       if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+               return -1;
+       }
+
+       ifc.ifc_len = sizeof(buff);
+       ifc.ifc_buf = buff;
+
+       if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) {
+               close(fd);
+               return -1;
+       }
+
+       ifr = ifc.ifc_req;
+
+       /* Loop through interfaces */
+       i = ifc.ifc_len;
+
+       while (i > 0) {
+               uint_t inc;
+
+               inc = ifr->ifr_addr.sa_len;
+
+               if (ioctl(fd, SIOCGIFADDR, ifr) != 0) {
+                       freeaddrinfo(*ifap);
+                       return -1;
+               }
+
+               curif = calloc(1, sizeof(struct ifaddrs));
+               if (lastif == NULL) {
+                       *ifap = curif;
+               } else {
+                       lastif->ifa_next = (*ifap);
+               }
+
+               curif->ifa_name = strdup(ifr->ifr_name);
+               curif->ifa_flags = ifr->ifr_flags;
+               curif->ifa_addr = sockaddr_dup(&ifr->ifr_addr);
+               curif->ifa_dstaddr = NULL;
+               curif->ifa_data = NULL;
+               curif->ifa_netmask = NULL;
+               curif->ifa_next = NULL;
+
+               if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) {
+                       freeaddrinfo(*ifap);
+                       return -1;
+               }
+
+               if (!(ifr->ifr_flags & IFF_UP)) {
+                       freeaddrinfo(curif);
+                       continue;
+               }
+
+               if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) {
+                       freeaddrinfo(*ifap);
+                       return -1;
+               }
+
+               curif->ifa_netmask = sockaddr_dup(&ifr->ifr_addr);
+
+               lastif = curif;
+
+       next:
+               /*
+                * Patch from Archie Cobbs (archie@whistle.com).  The
+                * addresses in the SIOCGIFCONF interface list have a
+                * minimum size. Usually this doesn't matter, but if
+                * your machine has tunnel interfaces, etc. that have
+                * a zero length "link address", this does matter.  */
+
+               if (inc < sizeof(ifr->ifr_addr))
+                       inc = sizeof(ifr->ifr_addr);
+               inc += IFNAMSIZ;
+
+               ifr = (struct ifreq*) (((char*) ifr) + inc);
+               i -= inc;
+       }
+
+       close(fd);
+       return 0;
+}
+
+#define _FOUND_IFACE_ANY
+#endif /* HAVE_IFACE_AIX */
+#ifndef _FOUND_IFACE_ANY
+int getifaddrs(struct ifaddrs **ifap)
+{
+       errno = ENOSYS;
+       return -1;
+}
+#endif
+
+#ifdef AUTOCONF_TEST
+/* this is the autoconf driver to test get_interfaces() */
+
+ int main()
+{
+       struct ifaddrs *ifs;
+       int total = get_interfaces(ifaces, MAX_INTERFACES);
+       int i;
+
+       int ret = getifaddrs(&ifs);
+       if (ret != 0) {
+               perror("getifaddrs() failed");
+               return 1;
+       }
+
+       while (ifs) {
+               printf("%-10s ", ifs->ifr_name);
+               printf("IP=%s ", inet_ntoa(((struct sockaddr_in *)ifs->ifr_addr)->sin_addr));
+               printf("NETMASK=%s\n", inet_ntoa(((struct sockaddr_in *)ifs->ifr_netmask)->sin_addr));
+       }
+       return 0;
+}
+#endif
diff --git a/source/lib/replace/getifaddrs.m4 b/source/lib/replace/getifaddrs.m4
new file mode 100644 (file)
index 0000000..f388274
--- /dev/null
@@ -0,0 +1,94 @@
+AC_CHECK_HEADERS([ifaddrs.h])
+
+dnl test for getifaddrs and freeifaddrs
+AC_CACHE_CHECK([for getifaddrs and freeifaddrs],samba_cv_HAVE_GETIFADDRS,[
+AC_TRY_COMPILE([
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <netdb.h>],
+[
+struct ifaddrs *ifp = NULL;
+int ret = getifaddrs (&ifp);
+freeifaddrs(ifp);
+],
+samba_cv_HAVE_GETIFADDRS=yes,samba_cv_HAVE_GETIFADDRS=no)])
+if test x"$samba_cv_HAVE_GETIFADDRS" = x"yes"; then
+    AC_DEFINE(HAVE_GETIFADDRS,1,[Whether the system has getifaddrs])
+    AC_DEFINE(HAVE_FREEIFADDRS,1,[Whether the system has freeifaddrs])
+       AC_DEFINE(HAVE_STRUCT_IFADDRS,1,[Whether struct ifaddrs is available])
+fi
+
+##################
+# look for a method of finding the list of network interfaces
+#
+# This tests need LIBS="$NSL_LIBS $SOCKET_LIBS"
+#
+old_CFLAGS=$CFLAGS
+old_LIBS=$LIBS
+LIBS="$NSL_LIBS $SOCKET_LIBS"
+CFLAGS="$CFLAGS -Ilib/replace"
+iface=no;
+##################
+# look for a method of finding the list of network interfaces
+iface=no;
+AC_CACHE_CHECK([for iface getifaddrs],samba_cv_HAVE_IFACE_GETIFADDRS,[
+SAVE_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS ${SAMBA_CONFIGURE_CPPFLAGS}"
+AC_TRY_RUN([
+#define NO_CONFIG_H 1
+#define HAVE_IFACE_GETIFADDRS 1
+#define AUTOCONF_TEST 1
+#include "${srcdir-.}/lib/replace/replace.c"
+#include "${srcdir-.}/lib/replace/getifaddrs.c"],
+           samba_cv_HAVE_IFACE_GETIFADDRS=yes,samba_cv_HAVE_IFACE_GETIFADDRS=no,samba_cv_HAVE_IFACE_GETIFADDRS=cross)])
+CPPFLAGS="$SAVE_CPPFLAGS"
+if test x"$samba_cv_HAVE_IFACE_GETIFADDRS" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_GETIFADDRS,1,[Whether iface getifaddrs is available])
+else
+       LIBREPLACEOBJ="${LIBREPLACEOBJ} getifaddrs.o"
+fi
+
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface AIX],samba_cv_HAVE_IFACE_AIX,[
+AC_TRY_RUN([
+#define HAVE_IFACE_AIX 1
+#define AUTOCONF_TEST 1
+#undef _XOPEN_SOURCE_EXTENDED
+#include "${srcdir-.}/lib/replace/getifaddrs.c"],
+           samba_cv_HAVE_IFACE_AIX=yes,samba_cv_HAVE_IFACE_AIX=no,samba_cv_HAVE_IFACE_AIX=cross)])
+if test x"$samba_cv_HAVE_IFACE_AIX" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_AIX,1,[Whether iface AIX is available])
+fi
+fi
+
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifconf],samba_cv_HAVE_IFACE_IFCONF,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFCONF 1
+#define AUTOCONF_TEST 1
+#include "${srcdir-.}/lib/replace/getifaddrs.c"],
+           samba_cv_HAVE_IFACE_IFCONF=yes,samba_cv_HAVE_IFACE_IFCONF=no,samba_cv_HAVE_IFACE_IFCONF=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFCONF" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_IFCONF,1,[Whether iface ifconf is available])
+fi
+fi
+
+if test $iface = no; then
+AC_CACHE_CHECK([for iface ifreq],samba_cv_HAVE_IFACE_IFREQ,[
+AC_TRY_RUN([
+#define HAVE_IFACE_IFREQ 1
+#define AUTOCONF_TEST 1
+#include "${srcdir-.}/lib/replace/getifaddrs.c"],
+           samba_cv_HAVE_IFACE_IFREQ=yes,samba_cv_HAVE_IFACE_IFREQ=no,samba_cv_HAVE_IFACE_IFREQ=cross)])
+if test x"$samba_cv_HAVE_IFACE_IFREQ" = x"yes"; then
+    iface=yes;AC_DEFINE(HAVE_IFACE_IFREQ,1,[Whether iface ifreq is available])
+fi
+fi
+
+CFLAGS=$old_CFLAGS
+LIBS=$old_LIBS
index f866b3648fcf977b87e94e7681e051210a773aad..6d1d6b8afc23e15194e71468b723d3393714ad77 100644 (file)
@@ -348,6 +348,7 @@ m4_include(inet_ntop.m4)
 m4_include(inet_pton.m4)
 m4_include(getaddrinfo.m4)
 m4_include(repdir.m4)
+m4_include(getifaddrs.m4)
 
 AC_CHECK_FUNCS([syslog printf memset memcpy],,[AC_MSG_ERROR([Required function not found])])
 
index aff8a841daef6e39a5e709eba0d4fee24d3dcd22..61be51744c386693f9db373eedd77df0c01f8231 100644 (file)
@@ -6,6 +6,7 @@
    networking system include wrappers
 
    Copyright (C) Andrew Tridgell 2004
+   Copyright (C) Jelmer Vernooij 2007
    
      ** NOTE! The following LGPL license applies to the replace
      ** library. This does NOT imply that all of Samba is released
@@ -97,6 +98,30 @@ int rep_inet_pton(int af, const char *src, void *dst);
 const char *rep_inet_ntop(int af, const void *src, char *dst, socklen_t size);
 #endif
 
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+
+#ifndef HAVE_STRUCT_IFADDRS
+struct ifaddrs {
+       struct ifaddrs   *ifa_next;         /* Pointer to next struct */
+       char             *ifa_name;         /* Interface name */
+       u_int             ifa_flags;        /* Interface flags */
+       struct sockaddr  *ifa_addr;         /* Interface address */
+       struct sockaddr  *ifa_netmask;      /* Interface netmask */
+       struct sockaddr  *ifa_dstaddr;      /* P2P interface destination */
+       void             *ifa_data;         /* Address specific data */
+};
+#endif
+
+#ifndef HAVE_GETIFADDRS
+int rep_getifaddrs(struct ifaddrs **);
+#endif
+
+#ifndef HAVE_FREEIFADDRS
+int rep_freeifaddrs(struct ifaddrs **);
+#endif
+
 /*
  * Some systems have getaddrinfo but not the
  * defines needed to use it.