c8bb8f
From c09dd24a7d63988e0acef7d033bd3e088fc005c0 Mon Sep 17 00:00:00 2001
c8bb8f
From: Jiri Popelka <jpopelka@redhat.com>
c8bb8f
Date: Thu, 24 Jan 2013 12:39:50 +0100
c8bb8f
Subject: [PATCH] Linux interface discovery
c8bb8f
c8bb8f
Use the same discovery code as for *BSD and OS X,
c8bb8f
i.e. the getifaddrs() function.
c8bb8f
---
c8bb8f
 common/discover.c | 398 +++---------------------------------------------------
c8bb8f
 1 file changed, 17 insertions(+), 381 deletions(-)
c8bb8f
c8bb8f
diff --git a/common/discover.c b/common/discover.c
c8bb8f
index 1d84219..f2a8f6d 100644
c8bb8f
--- a/common/discover.c
c8bb8f
+++ b/common/discover.c
c8bb8f
@@ -379,391 +379,13 @@ end_iface_scan(struct iface_conf_list *ifaces) {
c8bb8f
 	ifaces->sock = -1;
c8bb8f
 }
c8bb8f
 
c8bb8f
-#elif __linux /* !HAVE_SIOCGLIFCONF */
c8bb8f
-/* 
c8bb8f
- * Linux support
c8bb8f
- * -------------
c8bb8f
- *
c8bb8f
- * In Linux, we use the /proc pseudo-filesystem to get information
c8bb8f
- * about interfaces, along with selected ioctl() calls.
c8bb8f
- *
c8bb8f
- * Linux low level access is documented in the netdevice man page.
c8bb8f
- */
c8bb8f
-
c8bb8f
-/* 
c8bb8f
- * Structure holding state about the scan.
c8bb8f
- */
c8bb8f
-struct iface_conf_list {
c8bb8f
-	int sock;	/* file descriptor used to get information */
c8bb8f
-	FILE *fp;	/* input from /proc/net/dev */
c8bb8f
-#ifdef DHCPv6
c8bb8f
-	FILE *fp6;	/* input from /proc/net/if_inet6 */
c8bb8f
-#endif
c8bb8f
-};
c8bb8f
-
c8bb8f
-/* 
c8bb8f
- * Structure used to return information about a specific interface.
c8bb8f
- */
c8bb8f
-struct iface_info {
c8bb8f
-	char name[IFNAMSIZ];		/* name of the interface, e.g. "eth0" */
c8bb8f
-	struct sockaddr_storage addr;	/* address information */
c8bb8f
-	isc_uint64_t flags;		/* interface flags, e.g. IFF_LOOPBACK */
c8bb8f
-};
c8bb8f
-
c8bb8f
-/* 
c8bb8f
- * Start a scan of interfaces.
c8bb8f
- *
c8bb8f
- * The iface_conf_list structure maintains state for this process.
c8bb8f
- */
c8bb8f
-int 
c8bb8f
-begin_iface_scan(struct iface_conf_list *ifaces) {
c8bb8f
-	char buf[256];
c8bb8f
-	int len;
c8bb8f
-	int i;
c8bb8f
-
c8bb8f
-	ifaces->fp = fopen("/proc/net/dev", "r");
c8bb8f
-	if (ifaces->fp == NULL) {
c8bb8f
-		log_error("Error opening '/proc/net/dev' to list interfaces");
c8bb8f
-		return 0;
c8bb8f
-	}
c8bb8f
-
c8bb8f
-	/*
c8bb8f
-	 * The first 2 lines are header information, so read and ignore them.
c8bb8f
-	 */
c8bb8f
-	for (i=0; i<2; i++) {
c8bb8f
-		if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
c8bb8f
-			log_error("Error reading headers from '/proc/net/dev'");
c8bb8f
-			fclose(ifaces->fp);
c8bb8f
-			ifaces->fp = NULL;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-		len = strlen(buf);
c8bb8f
-		if ((len <= 0) || (buf[len-1] != '\n')) { 
c8bb8f
-			log_error("Bad header line in '/proc/net/dev'");
c8bb8f
-			fclose(ifaces->fp);
c8bb8f
-			ifaces->fp = NULL;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-	}
c8bb8f
-
c8bb8f
-	ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
c8bb8f
-	if (ifaces->sock < 0) {
c8bb8f
-		log_error("Error creating socket to list interfaces; %m");
c8bb8f
-		fclose(ifaces->fp);
c8bb8f
-		ifaces->fp = NULL;
c8bb8f
-		return 0;
c8bb8f
-	}
c8bb8f
-
c8bb8f
-#ifdef DHCPv6
c8bb8f
-	if (local_family == AF_INET6) {
c8bb8f
-		ifaces->fp6 = fopen("/proc/net/if_inet6", "r");
c8bb8f
-		if (ifaces->fp6 == NULL) {
c8bb8f
-			log_error("Error opening '/proc/net/if_inet6' to "
c8bb8f
-				  "list IPv6 interfaces; %m");
c8bb8f
-			close(ifaces->sock);
c8bb8f
-			ifaces->sock = -1;
c8bb8f
-			fclose(ifaces->fp);
c8bb8f
-			ifaces->fp = NULL;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-	}
c8bb8f
-#endif
c8bb8f
-
c8bb8f
-	return 1;
c8bb8f
-}
c8bb8f
-
c8bb8f
-/*
c8bb8f
- * Read our IPv4 interfaces from /proc/net/dev.
c8bb8f
- *
c8bb8f
- * The file looks something like this:
c8bb8f
- *
c8bb8f
- * Inter-|   Receive ...
c8bb8f
- *  face |bytes    packets errs drop fifo frame ...
c8bb8f
- *     lo: 1580562    4207    0    0    0     0 ...
c8bb8f
- *   eth0:       0       0    0    0    0     0 ...
c8bb8f
- *   eth1:1801552440   37895    0   14    0     ...
c8bb8f
- *
c8bb8f
- * We only care about the interface name, which is at the start of 
c8bb8f
- * each line.
c8bb8f
- *
c8bb8f
- * We use an ioctl() to get the address and flags for each interface.
c8bb8f
- */
c8bb8f
-static int
c8bb8f
-next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
c8bb8f
-	char buf[256];
c8bb8f
-	int len;
c8bb8f
-	char *p;
c8bb8f
-	char *name;
c8bb8f
-	struct ifreq tmp;
c8bb8f
-
c8bb8f
-	/*
c8bb8f
-	 * Loop exits when we find an interface that has an address, or 
c8bb8f
-	 * when we run out of interfaces.
c8bb8f
-	 */
c8bb8f
-	for (;;) {
c8bb8f
-		do {
c8bb8f
-			/*
c8bb8f
-	 		 *  Read the next line in the file.
c8bb8f
-	 		 */
c8bb8f
-			if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
c8bb8f
-				if (ferror(ifaces->fp)) {
c8bb8f
-					*err = 1;
c8bb8f
-					log_error("Error reading interface "
c8bb8f
-					  	"information");
c8bb8f
-				} else {
c8bb8f
-					*err = 0;
c8bb8f
-				}
c8bb8f
-				return 0;
c8bb8f
-			}
c8bb8f
-
c8bb8f
-			/*
c8bb8f
-	 		 * Make sure the line is a nice, 
c8bb8f
-			 * newline-terminated line.
c8bb8f
-	 		 */
c8bb8f
-			len = strlen(buf);
c8bb8f
-			if ((len <= 0) || (buf[len-1] != '\n')) { 
c8bb8f
-				log_error("Bad line reading interface "
c8bb8f
-					  "information");
c8bb8f
-				*err = 1;
c8bb8f
-				return 0;
c8bb8f
-			}
c8bb8f
-
c8bb8f
-			/*
c8bb8f
-	 		 * Figure out our name.
c8bb8f
-	 		 */
c8bb8f
-			p = strrchr(buf, ':');
c8bb8f
-			if (p == NULL) {
c8bb8f
-				log_error("Bad line reading interface "
c8bb8f
-					  "information (no colon)");
c8bb8f
-				*err = 1;
c8bb8f
-				return 0;
c8bb8f
-			}
c8bb8f
-			*p = '\0';
c8bb8f
-			name = buf;
c8bb8f
-			while (isspace(*name)) {
c8bb8f
-				name++;
c8bb8f
-			}
c8bb8f
-
c8bb8f
-			/* 
c8bb8f
-		 	 * Copy our name into our interface structure.
c8bb8f
-		 	 */
c8bb8f
-			len = p - name;
c8bb8f
-			if (len >= sizeof(info->name)) {
c8bb8f
-				*err = 1;
c8bb8f
-				log_error("Interface name '%s' too long", name);
c8bb8f
-				return 0;
c8bb8f
-			}
c8bb8f
-			strcpy(info->name, name);
c8bb8f
-
c8bb8f
-#ifdef ALIAS_NAMED_PERMUTED
c8bb8f
-			/* interface aliases look like "eth0:1" or "wlan1:3" */
c8bb8f
-			s = strchr(info->name, ':');
c8bb8f
-			if (s != NULL) {
c8bb8f
-				*s = '\0';
c8bb8f
-			}
c8bb8f
-#endif
c8bb8f
-
c8bb8f
-#ifdef SKIP_DUMMY_INTERFACES
c8bb8f
-		} while (strncmp(info->name, "dummy", 5) == 0);
c8bb8f
-#else
c8bb8f
-		} while (0);
c8bb8f
-#endif
c8bb8f
-
c8bb8f
-		memset(&tmp, 0, sizeof(tmp));
c8bb8f
-		strcpy(tmp.ifr_name, name);
c8bb8f
-		if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) {
c8bb8f
-			if (errno == EADDRNOTAVAIL) {
c8bb8f
-				continue;
c8bb8f
-			}
c8bb8f
-			log_error("Error getting interface address "
c8bb8f
-				  "for '%s'; %m", name);
c8bb8f
-			*err = 1;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-		memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr));
c8bb8f
-
c8bb8f
-		memset(&tmp, 0, sizeof(tmp));
c8bb8f
-		strcpy(tmp.ifr_name, name);
c8bb8f
-		if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
c8bb8f
-			log_error("Error getting interface flags for '%s'; %m", 
c8bb8f
-			  	name);
c8bb8f
-			*err = 1;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-		info->flags = tmp.ifr_flags;
c8bb8f
-
c8bb8f
-		*err = 0;
c8bb8f
-		return 1;
c8bb8f
-	}
c8bb8f
-}
c8bb8f
-
c8bb8f
-#ifdef DHCPv6
c8bb8f
-/*
c8bb8f
- * Read our IPv6 interfaces from /proc/net/if_inet6.
c8bb8f
- *
c8bb8f
- * The file looks something like this:
c8bb8f
- *
c8bb8f
- * fe80000000000000025056fffec00008 05 40 20 80   vmnet8
c8bb8f
- * 00000000000000000000000000000001 01 80 10 80       lo
c8bb8f
- * fe80000000000000025056fffec00001 06 40 20 80   vmnet1
c8bb8f
- * 200108881936000202166ffffe497d9b 03 40 00 00     eth1
c8bb8f
- * fe8000000000000002166ffffe497d9b 03 40 20 80     eth1
c8bb8f
- *
c8bb8f
- * We get IPv6 address from the start, the interface name from the end, 
c8bb8f
- * and ioctl() to get flags.
c8bb8f
- */
c8bb8f
-static int
c8bb8f
-next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
c8bb8f
-	char buf[256];
c8bb8f
-	int len;
c8bb8f
-	char *p;
c8bb8f
-	char *name;
c8bb8f
-	int i;
c8bb8f
-	struct sockaddr_in6 addr;
c8bb8f
-	struct ifreq tmp;
c8bb8f
-
c8bb8f
-	do {
c8bb8f
-		/*
c8bb8f
-		 *  Read the next line in the file.
c8bb8f
-		 */
c8bb8f
-		if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) {
c8bb8f
-			if (ferror(ifaces->fp6)) {
c8bb8f
-				*err = 1;
c8bb8f
-				log_error("Error reading IPv6 "
c8bb8f
-					  "interface information");
c8bb8f
-			} else {
c8bb8f
-				*err = 0;
c8bb8f
-			}
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-
c8bb8f
-		/*
c8bb8f
-		 * Make sure the line is a nice, newline-terminated line.
c8bb8f
-		 */
c8bb8f
-		len = strlen(buf);
c8bb8f
-		if ((len <= 0) || (buf[len-1] != '\n')) { 
c8bb8f
-			log_error("Bad line reading IPv6 "
c8bb8f
-				  "interface information");
c8bb8f
-			*err = 1;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-
c8bb8f
-		/*
c8bb8f
- 		 * Figure out our name.
c8bb8f
- 		 */
c8bb8f
-		buf[--len] = '\0';
c8bb8f
-		p = strrchr(buf, ' ');
c8bb8f
-		if (p == NULL) {
c8bb8f
-			log_error("Bad line reading IPv6 interface "
c8bb8f
-			          "information (no space)");
c8bb8f
-			*err = 1;
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-		name = p+1;
c8bb8f
-
c8bb8f
-		/* 
c8bb8f
- 		 * Copy our name into our interface structure.
c8bb8f
- 		 */
c8bb8f
-		len = strlen(name);
c8bb8f
-		if (len >= sizeof(info->name)) {
c8bb8f
-			*err = 1;
c8bb8f
-			log_error("IPv6 interface name '%s' too long", name);
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-		strcpy(info->name, name);
c8bb8f
-
c8bb8f
-#ifdef SKIP_DUMMY_INTERFACES
c8bb8f
-	} while (strncmp(info->name, "dummy", 5) == 0);
c8bb8f
-#else
c8bb8f
-	} while (0);
c8bb8f
-#endif
c8bb8f
-
c8bb8f
-	/*
c8bb8f
-	 * Double-check we start with the IPv6 address.
c8bb8f
-	 */
c8bb8f
-	for (i=0; i<32; i++) {
c8bb8f
-		if (!isxdigit(buf[i]) || isupper(buf[i])) {
c8bb8f
-			*err = 1;
c8bb8f
-			log_error("Bad line reading IPv6 interface address "
c8bb8f
-				  "for '%s'", name);
c8bb8f
-			return 0;
c8bb8f
-		}
c8bb8f
-	}
c8bb8f
-
c8bb8f
-	/* 
c8bb8f
-	 * Load our socket structure.
c8bb8f
-	 */
c8bb8f
-	memset(&addr, 0, sizeof(addr));
c8bb8f
-	addr.sin6_family = AF_INET6;
c8bb8f
-	for (i=0; i<16; i++) {
c8bb8f
-		unsigned char byte;
c8bb8f
-                static const char hex[] = "0123456789abcdef";
c8bb8f
-                byte = ((index(hex, buf[i * 2]) - hex) << 4) |
c8bb8f
-			(index(hex, buf[i * 2 + 1]) - hex);
c8bb8f
-		addr.sin6_addr.s6_addr[i] = byte;
c8bb8f
-	}
c8bb8f
-	memcpy(&info->addr, &addr, sizeof(addr));
c8bb8f
-
c8bb8f
-	/*
c8bb8f
-	 * Get our flags.
c8bb8f
-	 */
c8bb8f
-	memset(&tmp, 0, sizeof(tmp));
c8bb8f
-	strcpy(tmp.ifr_name, name);
c8bb8f
-	if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
c8bb8f
-		log_error("Error getting interface flags for '%s'; %m", name);
c8bb8f
-		*err = 1;
c8bb8f
-		return 0;
c8bb8f
-	}
c8bb8f
-	info->flags = tmp.ifr_flags;
c8bb8f
-
c8bb8f
-	*err = 0;
c8bb8f
-	return 1;
c8bb8f
-}
c8bb8f
-#endif /* DHCPv6 */
c8bb8f
-
c8bb8f
-/*
c8bb8f
- * Retrieve the next interface.
c8bb8f
- *
c8bb8f
- * Returns information in the info structure. 
c8bb8f
- * Sets err to 1 if there is an error, otherwise 0.
c8bb8f
- */
c8bb8f
-int
c8bb8f
-next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
c8bb8f
-	if (next_iface4(info, err, ifaces)) {
c8bb8f
-		return 1;
c8bb8f
-	}
c8bb8f
-#ifdef DHCPv6
c8bb8f
-	if (!(*err)) {
c8bb8f
-		if (local_family == AF_INET6)
c8bb8f
-			return next_iface6(info, err, ifaces);
c8bb8f
-	}
c8bb8f
-#endif
c8bb8f
-	return 0;
c8bb8f
-}
c8bb8f
-
c8bb8f
-/*
c8bb8f
- * End scan of interfaces.
c8bb8f
- */
c8bb8f
-void
c8bb8f
-end_iface_scan(struct iface_conf_list *ifaces) {
c8bb8f
-	fclose(ifaces->fp);
c8bb8f
-	ifaces->fp = NULL;
c8bb8f
-	close(ifaces->sock);
c8bb8f
-	ifaces->sock = -1;
c8bb8f
-#ifdef DHCPv6
c8bb8f
-	if (local_family == AF_INET6) {
c8bb8f
-		fclose(ifaces->fp6);
c8bb8f
-		ifaces->fp6 = NULL;
c8bb8f
-	}
c8bb8f
-#endif
c8bb8f
-}
c8bb8f
 #else
c8bb8f
 
c8bb8f
 /* 
c8bb8f
  * BSD support
c8bb8f
  * -----------
c8bb8f
  *
c8bb8f
- * FreeBSD, NetBSD, OpenBSD, and OS X all have the getifaddrs() 
c8bb8f
+ * FreeBSD, NetBSD, OpenBSD, OS X and Linux all have the getifaddrs()
c8bb8f
  * function.
c8bb8f
  *
c8bb8f
  * The getifaddrs() man page describes the use.
c8bb8f
@@ -811,6 +433,8 @@ begin_iface_scan(struct iface_conf_list *ifaces) {
c8bb8f
  */
c8bb8f
 int
c8bb8f
 next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
c8bb8f
+	size_t sa_len = 0;
c8bb8f
+
c8bb8f
 	if (ifaces->next == NULL) {
c8bb8f
 		*err = 0;
c8bb8f
 		return 0;
c8bb8f
@@ -822,8 +446,20 @@ next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
c8bb8f
 		return 0;
c8bb8f
 	}
c8bb8f
 	strcpy(info->name, ifaces->next->ifa_name);
c8bb8f
-	memcpy(&info->addr, ifaces->next->ifa_addr, 
c8bb8f
-	       ifaces->next->ifa_addr->sa_len);
c8bb8f
+
c8bb8f
+	memset(&info->addr, 0 , sizeof(info->addr));
c8bb8f
+
c8bb8f
+	if (ifaces->next->ifa_addr != NULL) {
c8bb8f
+#ifdef HAVE_SA_LEN
c8bb8f
+		sa_len = ifaces->next->ifa_addr->sa_len;
c8bb8f
+#else
c8bb8f
+		if (ifaces->next->ifa_addr->sa_family == AF_INET)
c8bb8f
+			sa_len = sizeof(struct sockaddr_in);
c8bb8f
+		else if (ifaces->next->ifa_addr->sa_family == AF_INET6)
c8bb8f
+			sa_len = sizeof(struct sockaddr_in6);
c8bb8f
+#endif
c8bb8f
+		memcpy(&info->addr, ifaces->next->ifa_addr, sa_len);
c8bb8f
+	}
c8bb8f
 	info->flags = ifaces->next->ifa_flags;
c8bb8f
 	ifaces->next = ifaces->next->ifa_next;
c8bb8f
 	*err = 0;
c8bb8f
-- 
c8bb8f
1.8.1
c8bb8f