You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
443 lines
10 KiB
443 lines
10 KiB
From c09dd24a7d63988e0acef7d033bd3e088fc005c0 Mon Sep 17 00:00:00 2001 |
|
From: Jiri Popelka <jpopelka@redhat.com> |
|
Date: Thu, 24 Jan 2013 12:39:50 +0100 |
|
Subject: [PATCH] Linux interface discovery |
|
|
|
Use the same discovery code as for *BSD and OS X, |
|
i.e. the getifaddrs() function. |
|
--- |
|
common/discover.c | 398 +++--------------------------------------------------- |
|
1 file changed, 17 insertions(+), 381 deletions(-) |
|
|
|
diff --git a/common/discover.c b/common/discover.c |
|
index 1d84219..f2a8f6d 100644 |
|
--- a/common/discover.c |
|
+++ b/common/discover.c |
|
@@ -379,391 +379,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[256]; |
|
- 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[256]; |
|
- 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; |
|
- } |
|
- strcpy(info->name, name); |
|
- |
|
-#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)); |
|
- strcpy(tmp.ifr_name, name); |
|
- 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)); |
|
- strcpy(tmp.ifr_name, name); |
|
- 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[256]; |
|
- 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; |
|
- } |
|
- strcpy(info->name, name); |
|
- |
|
-#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)); |
|
- strcpy(tmp.ifr_name, name); |
|
- 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) { |
|
- 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 |
|
* ----------- |
|
* |
|
- * FreeBSD, NetBSD, OpenBSD, and OS X all have the getifaddrs() |
|
+ * FreeBSD, NetBSD, OpenBSD, OS X and Linux all have the getifaddrs() |
|
* function. |
|
* |
|
* The getifaddrs() man page describes the use. |
|
@@ -811,6 +433,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; |
|
@@ -822,8 +446,20 @@ next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { |
|
return 0; |
|
} |
|
strcpy(info->name, ifaces->next->ifa_name); |
|
- memcpy(&info->addr, ifaces->next->ifa_addr, |
|
- ifaces->next->ifa_addr->sa_len); |
|
+ |
|
+ memset(&info->addr, 0 , sizeof(info->addr)); |
|
+ |
|
+ if (ifaces->next->ifa_addr != NULL) { |
|
+#ifdef HAVE_SA_LEN |
|
+ sa_len = ifaces->next->ifa_addr->sa_len; |
|
+#else |
|
+ 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); |
|
+#endif |
|
+ memcpy(&info->addr, ifaces->next->ifa_addr, sa_len); |
|
+ } |
|
info->flags = ifaces->next->ifa_flags; |
|
ifaces->next = ifaces->next->ifa_next; |
|
*err = 0; |
|
-- |
|
1.8.1 |
|
|
|
|