]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
Merged #28761 (Linux interface discovery)
authorFrancis Dupont <fdupont@isc.org>
Wed, 8 Mar 2017 15:01:38 +0000 (16:01 +0100)
committerFrancis Dupont <fdupont@isc.org>
Wed, 8 Mar 2017 15:01:38 +0000 (16:01 +0100)
RELNOTES
common/discover.c

index 29de8f11f078695a86e590baf4d5a410cfab7893..a5613f1d450bb68e51e83381758df4d331e900bb 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -985,6 +985,16 @@ by Eric Young (eay@cryptsoft.com).
   "Run out of memory." on the standard error and exists with status 1.
   [ISC-Bugs #32744]
 
+- The linux interface discovery code has been modified to use getifaddrs() 
+  as is done for BSD and OS-X.  Prior to this the code would only recognize
+  the first address on an interface and thereby omit vlans.
+  Thanks to Jiri Popelka at Redhat, Marius Tomaschewski at Suse, and Wei
+  Kong at Novell, who all submitted patches.
+  [ISC-Bugs #28761]
+  [ISC-Bugs #31992]
+  [ISC-Bugs #25428]
+  [ISC-Bugs #31940]
+
                        Changes since 4.2.0 (new features)
 
 - If a client renews before 'dhcp-cache-threshold' percent of its lease
index 8e7f6328a9ec707b6137aa1e752464dcb229727e..6824ec4b1e1942a3dfe9d517ce23434573367288 100644 (file)
@@ -373,392 +373,13 @@ end_iface_scan(struct iface_conf_list *ifaces) {
        ifaces->sock = -1;
 }
 
-#elif __linux /* !HAVE_SIOCGLIFCONF */
-/* 
- * Linux support
- * -------------
- *
- * In Linux, we use the /proc pseudo-filesystem to get information
- * about interfaces, along with selected ioctl() calls.
- *
- * Linux low level access is documented in the netdevice man page.
- */
-
-/* 
- * Structure holding state about the scan.
- */
-struct iface_conf_list {
-       int sock;       /* file descriptor used to get information */
-       FILE *fp;       /* input from /proc/net/dev */
-#ifdef DHCPv6
-       FILE *fp6;      /* input from /proc/net/if_inet6 */
-#endif
-};
-
-/* 
- * Structure used to return information about a specific interface.
- */
-struct iface_info {
-       char name[IFNAMSIZ];            /* name of the interface, e.g. "eth0" */
-       struct sockaddr_storage addr;   /* address information */
-       isc_uint64_t flags;             /* interface flags, e.g. IFF_LOOPBACK */
-};
-
-/* 
- * Start a scan of interfaces.
- *
- * The iface_conf_list structure maintains state for this process.
- */
-int 
-begin_iface_scan(struct iface_conf_list *ifaces) {
-       char buf[IF_LINE_LENGTH];
-       int len;
-       int i;
-
-       ifaces->fp = fopen("/proc/net/dev", "r");
-       if (ifaces->fp == NULL) {
-               log_error("Error opening '/proc/net/dev' to list interfaces");
-               return 0;
-       }
-
-       /*
-        * The first 2 lines are header information, so read and ignore them.
-        */
-       for (i=0; i<2; i++) {
-               if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
-                       log_error("Error reading headers from '/proc/net/dev'");
-                       fclose(ifaces->fp);
-                       ifaces->fp = NULL;
-                       return 0;
-               }
-               len = strlen(buf);
-               if ((len <= 0) || (buf[len-1] != '\n')) { 
-                       log_error("Bad header line in '/proc/net/dev'");
-                       fclose(ifaces->fp);
-                       ifaces->fp = NULL;
-                       return 0;
-               }
-       }
-
-       ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       if (ifaces->sock < 0) {
-               log_error("Error creating socket to list interfaces; %m");
-               fclose(ifaces->fp);
-               ifaces->fp = NULL;
-               return 0;
-       }
-
-#ifdef DHCPv6
-       if (local_family == AF_INET6) {
-               ifaces->fp6 = fopen("/proc/net/if_inet6", "r");
-               if (ifaces->fp6 == NULL) {
-                       log_error("Error opening '/proc/net/if_inet6' to "
-                                 "list IPv6 interfaces; %m");
-                       close(ifaces->sock);
-                       ifaces->sock = -1;
-                       fclose(ifaces->fp);
-                       ifaces->fp = NULL;
-                       return 0;
-               }
-       }
-#endif
-
-       return 1;
-}
-
-/*
- * Read our IPv4 interfaces from /proc/net/dev.
- *
- * The file looks something like this:
- *
- * Inter-|   Receive ...
- *  face |bytes    packets errs drop fifo frame ...
- *     lo: 1580562    4207    0    0    0     0 ...
- *   eth0:       0       0    0    0    0     0 ...
- *   eth1:1801552440   37895    0   14    0     ...
- *
- * We only care about the interface name, which is at the start of 
- * each line.
- *
- * We use an ioctl() to get the address and flags for each interface.
- */
-static int
-next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
-       char buf[IF_LINE_LENGTH];
-       int len;
-       char *p;
-       char *name;
-       struct ifreq tmp;
-
-       /*
-        * Loop exits when we find an interface that has an address, or 
-        * when we run out of interfaces.
-        */
-       for (;;) {
-               do {
-                       /*
-                        *  Read the next line in the file.
-                        */
-                       if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
-                               if (ferror(ifaces->fp)) {
-                                       *err = 1;
-                                       log_error("Error reading interface "
-                                               "information");
-                               } else {
-                                       *err = 0;
-                               }
-                               return 0;
-                       }
-
-                       /*
-                        * Make sure the line is a nice, 
-                        * newline-terminated line.
-                        */
-                       len = strlen(buf);
-                       if ((len <= 0) || (buf[len-1] != '\n')) { 
-                               log_error("Bad line reading interface "
-                                         "information");
-                               *err = 1;
-                               return 0;
-                       }
-
-                       /*
-                        * Figure out our name.
-                        */
-                       p = strrchr(buf, ':');
-                       if (p == NULL) {
-                               log_error("Bad line reading interface "
-                                         "information (no colon)");
-                               *err = 1;
-                               return 0;
-                       }
-                       *p = '\0';
-                       name = buf;
-                       while (isspace(*name)) {
-                               name++;
-                       }
-
-                       /* 
-                        * Copy our name into our interface structure.
-                        */
-                       len = p - name;
-                       if (len >= sizeof(info->name)) {
-                               *err = 1;
-                               log_error("Interface name '%s' too long", name);
-                               return 0;
-                       }
-                       strncpy(info->name, name, sizeof(info->name) - 1);
-
-#ifdef ALIAS_NAMED_PERMUTED
-                       /* interface aliases look like "eth0:1" or "wlan1:3" */
-                       s = strchr(info->name, ':');
-                       if (s != NULL) {
-                               *s = '\0';
-                       }
-#endif
-
-#ifdef SKIP_DUMMY_INTERFACES
-               } while (strncmp(info->name, "dummy", 5) == 0);
-#else
-               } while (0);
-#endif
-
-               memset(&tmp, 0, sizeof(tmp));
-               strncpy(tmp.ifr_name, name, sizeof(tmp.ifr_name) - 1);
-               if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) {
-                       if (errno == EADDRNOTAVAIL) {
-                               continue;
-                       }
-                       log_error("Error getting interface address "
-                                 "for '%s'; %m", name);
-                       *err = 1;
-                       return 0;
-               }
-               memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr));
-
-               memset(&tmp, 0, sizeof(tmp));
-               strncpy(tmp.ifr_name, name, sizeof(tmp.ifr_name) - 1);
-               if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
-                       log_error("Error getting interface flags for '%s'; %m", 
-                               name);
-                       *err = 1;
-                       return 0;
-               }
-               info->flags = tmp.ifr_flags;
-
-               *err = 0;
-               return 1;
-       }
-}
-
-#ifdef DHCPv6
-/*
- * Read our IPv6 interfaces from /proc/net/if_inet6.
- *
- * The file looks something like this:
- *
- * fe80000000000000025056fffec00008 05 40 20 80   vmnet8
- * 00000000000000000000000000000001 01 80 10 80       lo
- * fe80000000000000025056fffec00001 06 40 20 80   vmnet1
- * 200108881936000202166ffffe497d9b 03 40 00 00     eth1
- * fe8000000000000002166ffffe497d9b 03 40 20 80     eth1
- *
- * We get IPv6 address from the start, the interface name from the end, 
- * and ioctl() to get flags.
- */
-static int
-next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
-       char buf[IF_LINE_LENGTH];
-       int len;
-       char *p;
-       char *name;
-       int i;
-       struct sockaddr_in6 addr;
-       struct ifreq tmp;
-
-       do {
-               /*
-                *  Read the next line in the file.
-                */
-               if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) {
-                       if (ferror(ifaces->fp6)) {
-                               *err = 1;
-                               log_error("Error reading IPv6 "
-                                         "interface information");
-                       } else {
-                               *err = 0;
-                       }
-                       return 0;
-               }
-
-               /*
-                * Make sure the line is a nice, newline-terminated line.
-                */
-               len = strlen(buf);
-               if ((len <= 0) || (buf[len-1] != '\n')) { 
-                       log_error("Bad line reading IPv6 "
-                                 "interface information");
-                       *err = 1;
-                       return 0;
-               }
-
-               /*
-                * Figure out our name.
-                */
-               buf[--len] = '\0';
-               p = strrchr(buf, ' ');
-               if (p == NULL) {
-                       log_error("Bad line reading IPv6 interface "
-                                 "information (no space)");
-                       *err = 1;
-                       return 0;
-               }
-               name = p+1;
-
-               /* 
-                * Copy our name into our interface structure.
-                */
-               len = strlen(name);
-               if (len >= sizeof(info->name)) {
-                       *err = 1;
-                       log_error("IPv6 interface name '%s' too long", name);
-                       return 0;
-               }
-               strncpy(info->name, name, sizeof(info->name) - 1);
-
-#ifdef SKIP_DUMMY_INTERFACES
-       } while (strncmp(info->name, "dummy", 5) == 0);
-#else
-       } while (0);
-#endif
-
-       /*
-        * Double-check we start with the IPv6 address.
-        */
-       for (i=0; i<32; i++) {
-               if (!isxdigit(buf[i]) || isupper(buf[i])) {
-                       *err = 1;
-                       log_error("Bad line reading IPv6 interface address "
-                                 "for '%s'", name);
-                       return 0;
-               }
-       }
-
-       /* 
-        * Load our socket structure.
-        */
-       memset(&addr, 0, sizeof(addr));
-       addr.sin6_family = AF_INET6;
-       for (i=0; i<16; i++) {
-               unsigned char byte;
-                static const char hex[] = "0123456789abcdef";
-                byte = ((index(hex, buf[i * 2]) - hex) << 4) |
-                       (index(hex, buf[i * 2 + 1]) - hex);
-               addr.sin6_addr.s6_addr[i] = byte;
-       }
-       memcpy(&info->addr, &addr, sizeof(addr));
-
-       /*
-        * Get our flags.
-        */
-       memset(&tmp, 0, sizeof(tmp));
-       strncpy(tmp.ifr_name, name, sizeof(tmp.ifr_name) - 1);
-       if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
-               log_error("Error getting interface flags for '%s'; %m", name);
-               *err = 1;
-               return 0;
-       }
-       info->flags = tmp.ifr_flags;
-
-       *err = 0;
-       return 1;
-}
-#endif /* DHCPv6 */
-
-/*
- * Retrieve the next interface.
- *
- * Returns information in the info structure. 
- * Sets err to 1 if there is an error, otherwise 0.
- */
-int
-next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
-       memset(info, 0, sizeof(struct iface_info));
-       if (next_iface4(info, err, ifaces)) {
-               return 1;
-       }
-#ifdef DHCPv6
-       if (!(*err)) {
-               if (local_family == AF_INET6)
-                       return next_iface6(info, err, ifaces);
-       }
-#endif
-       return 0;
-}
-
-/*
- * End scan of interfaces.
- */
-void
-end_iface_scan(struct iface_conf_list *ifaces) {
-       fclose(ifaces->fp);
-       ifaces->fp = NULL;
-       close(ifaces->sock);
-       ifaces->sock = -1;
-#ifdef DHCPv6
-       if (local_family == AF_INET6) {
-               fclose(ifaces->fp6);
-               ifaces->fp6 = NULL;
-       }
-#endif
-}
 #else
 
 /* 
- * BSD support
+ * BSD/Linux support
  * -----------
  *
- * FreeBSD, NetBSD, OpenBSD, and OS X all have the getifaddrs() 
+ * FreeBSD, NetBSD, OpenBSD, OS X/macOS and Linux all have the getifaddrs() 
  * function.
  *
  * The getifaddrs() man page describes the use.
@@ -806,6 +427,8 @@ begin_iface_scan(struct iface_conf_list *ifaces) {
  */
 int
 next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+       size_t sa_len = 0;
+
        if (ifaces->next == NULL) {
                *err = 0;
                return 0;
@@ -818,8 +441,23 @@ next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
        }
        memset(info, 0, sizeof(struct iface_info));
        strncpy(info->name, ifaces->next->ifa_name, sizeof(info->name) - 1);
-       memcpy(&info->addr, ifaces->next->ifa_addr, 
-              ifaces->next->ifa_addr->sa_len);
+       memset(&info->addr, 0 , sizeof(info->addr));
+       /*
+        * getifaddrs() can on Linux with some interfaces like PPP or TEQL
+        * result in a record with no address (ifa_addr).
+        */
+       if (ifaces->next->ifa_addr != NULL) {
+/* Linux lacks the sa_len member in struct sockaddr. */
+#if defined(__linux)
+               if (ifaces->next->ifa_addr->sa_family == AF_INET)
+                       sa_len = sizeof(struct sockaddr_in);
+               else if (ifaces->next->ifa_addr->sa_family == AF_INET6)
+                       sa_len = sizeof(struct sockaddr_in6);
+#else
+               sa_len = ifaces->next->ifa_addr->sa_len;
+#endif
+               memcpy(&info->addr, ifaces->next->ifa_addr, sa_len);
+       }
        info->flags = ifaces->next->ifa_flags;
        ifaces->next = ifaces->next->ifa_next;
        *err = 0;