summaryrefslogtreecommitdiff
path: root/common/discover.c
diff options
context:
space:
mode:
authorDavid Hankins <dhankins@isc.org>2007-05-08 23:05:22 +0000
committerDavid Hankins <dhankins@isc.org>2007-05-08 23:05:22 +0000
commit98bd7ca0990e6d88e3345d3bc966ebe8216691a7 (patch)
treec4437ca467e8f733d530170a5c445747b2defd68 /common/discover.c
parent74dc3e0b2786c46956e7517398ae6f7c6dad52d7 (diff)
downloadisc-dhcp-98bd7ca0990e6d88e3345d3bc966ebe8216691a7.tar.gz
DHCPv6 branch merged to HEAD.
Diffstat (limited to 'common/discover.c')
-rw-r--r--common/discover.c1012
1 files changed, 844 insertions, 168 deletions
diff --git a/common/discover.c b/common/discover.c
index e6921867..f2442916 100644
--- a/common/discover.c
+++ b/common/discover.c
@@ -1,6 +1,6 @@
-/* dispatch.c
+/* discover.c
- Network input dispatcher... */
+ Find and identify the network interfaces. */
/*
* Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
@@ -34,7 +34,7 @@
#ifndef lint
static char copyright[] =
-"$Id: discover.c,v 1.52 2006/11/07 23:40:14 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: discover.c,v 1.53 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#include "dhcpd.h"
@@ -51,12 +51,20 @@ isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *);
int (*dhcp_interface_shutdown_hook) (struct interface_info *);
struct in_addr limited_broadcast;
+
+int local_family = AF_INET6;
struct in_addr local_address;
+struct in6_addr local_address6;
void (*bootp_packet_handler) PROTO ((struct interface_info *,
struct dhcp_packet *, unsigned,
unsigned int,
struct iaddr, struct hardware *));
+void (*dhcpv6_packet_handler)(struct interface_info *,
+ const char *, int,
+ int, const struct iaddr *,
+ isc_boolean_t);
+
omapi_object_type_t *dhcp_type_interface;
#if defined (TRACING)
@@ -116,94 +124,650 @@ isc_result_t interface_initialize (omapi_object_t *ipo,
return ISC_R_SUCCESS;
}
-/* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces.
- For each interface that's of type INET and not the loopback interface,
- register that interface with the network I/O software, figure out what
- subnet it's on, and add it to the list of interfaces. */
-void discover_interfaces (state)
- int state;
-{
- struct interface_info *tmp, *ip;
- struct interface_info *last, *next;
- char buf [2048];
- struct ifconf ic;
- struct ifreq ifr;
- int i;
- int sock;
- int address_count = 0;
- struct subnet *subnet;
- struct shared_network *share;
- struct sockaddr_in foo;
- int ir;
- struct ifreq *tif;
-#ifdef ALIAS_NAMES_PERMUTED
+/*
+ * Scanning for Interfaces
+ * -----------------------
+ *
+ * To find interfaces, we create an iterator that abstracts out most
+ * of the platform specifics. Use is fairly straightforward:
+ *
+ * - begin_iface_scan() starts the process.
+ * - Use next_iface() until it returns 0.
+ * - end_iface_scan() performs any necessary cleanup.
+ *
+ * We check for errors on each call to next_iface(), which returns a
+ * description of the error as a string if any occurs.
+ *
+ * We currently have code for Solaris and Linux. Other systems need
+ * to have code written.
+ *
+ * NOTE: the long-term goal is to use the interface code from BIND 9.
+ */
+
+#if defined(SIOCGLIFNUM)
+/*
+ * Solaris support
+ * ---------------
+ *
+ * The SIOCGLIFCONF ioctl() are the extension that you need to use
+ * on Solaris to get information about IPv6 addresses.
+ *
+ * Solaris' extended interface is documented in the if_tcp man page.
+ */
+
+/*
+ * Structure holding state about the scan.
+ */
+struct iface_conf_list {
+ int sock; /* file descriptor used to get information */
+ int num; /* total number of interfaces */
+ struct lifconf conf; /* structure used to get information */
+ int next; /* next interface to retrieve when iterating */
+};
+
+/*
+ * Structure used to return information about a specific interface.
+ */
+struct iface_info {
+ char name[LIFNAMSIZ]; /* name of the interface, e.g. "bge0" */
+ 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) {
+ struct lifnum lifnum;
+
+ ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (ifaces->sock < 0) {
+ log_error("Error creating socket to list interfaces; %m");
+ return 0;
+ }
+
+ memset(&lifnum, 0, sizeof(lifnum));
+ lifnum.lifn_family = AF_UNSPEC;
+ if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) {
+ log_error("Error finding total number of interfaces; %m");
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ return 0;
+ }
+
+ ifaces->num = lifnum.lifn_count;
+ memset(&ifaces->conf, 0, sizeof(ifaces->conf));
+ ifaces->conf.lifc_family = AF_UNSPEC;
+ ifaces->conf.lifc_len = ifaces->num * sizeof(struct lifreq);
+ ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL);
+ if (ifaces->conf.lifc_buf == NULL) {
+ log_fatal("Out of memory getting interface list.");
+ }
+
+ if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) {
+ log_error("Error getting interfaces configuration list; %m");
+ dfree(ifaces->conf.lifc_buf, MDL);
+ close(ifaces->sock);
+ ifaces->sock = -1;
+ return 0;
+ }
+
+ ifaces->next = 0;
+
+ return 1;
+}
+
+/*
+ * Retrieve the next interface.
+ *
+ * Returns information in the info structure.
+ * Sets err to 1 if there is an error, otherwise 1.
+ */
+int
+next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
+ struct lifreq *p;
+ struct lifreq tmp;
char *s;
-#endif
- isc_result_t status;
- static int setup_fallback = 0;
- int wifcount = 0;
- /* Create an unbound datagram socket to do the SIOCGIFADDR ioctl on. */
- if ((sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
- log_fatal ("Can't create addrlist socket");
+ do {
+ if (ifaces->next >= ifaces->num) {
+ *err = 0;
+ return 0;
+ }
- /* Get the interface configuration information... */
+ p = ifaces->conf.lifc_req;
+ p += ifaces->next;
-#ifdef SIOCGIFCONF_ZERO_PROBE
- /* linux will only tell us how long a buffer it wants if we give it
- * a null buffer first. So, do a dry run to figure out the length.
- *
- * XXX this code is duplicated from below because trying to fold
- * the logic into the if statement and goto resulted in excesssive
- * obfuscation. The intent is that unless you run Linux you shouldn't
- * have to deal with this. */
+ if (strlen(p->lifr_name) >= sizeof(info->name)) {
+ *err = 1;
+ log_error("Interface name '%s' too long", p->lifr_name);
+ return 0;
+ }
+ strcpy(info->name, p->lifr_name);
+ info->addr = p->lifr_addr;
- ic.ifc_len = 0;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)NULL;
+#ifdef ALIAS_NAMES_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
- /* otherwise, we just feed it a starting size, and it'll tell us if
- * it needs more */
+ } while (0);
+#endif
+
+ memset(&tmp, 0, sizeof(tmp));
+ strcpy(tmp.lifr_name, info->name);
+ if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) {
+ log_error("Error getting interface flags for '%s'; %m",
+ p->lifr_name);
+ *err = 1;
+ return 0;
+ }
+ info->flags = tmp.lifr_flags;
+
+ ifaces->next++;
+ *err = 0;
+ return 1;
+}
+
+/*
+ * End scan of interfaces.
+ */
+void
+end_iface_scan(struct iface_conf_list *ifaces) {
+ dfree(ifaces->conf.lifc_buf, MDL);
+ close(ifaces->sock);
+ 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.
+ */
- ic.ifc_len = sizeof buf;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
+/*
+ * 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
+};
- gifconf_again:
- i = ioctl(sock, SIOCGIFCONF, &ic);
+/*
+ * 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;
- if (i < 0)
- log_fatal ("ioctl: SIOCGIFCONF: %m");
+ ifaces->fp = fopen(PROCDEV_DEVICE, "r");
+ if (ifaces->fp == NULL) {
+ log_error("Error opening '%s' to list interfaces",
+ PROCDEV_DEVICE);
+ return 0;
+ }
-#ifdef SIOCGIFCONF_ZERO_PROBE
- /* Workaround for SIOCGIFCONF bug on some Linux versions. */
- if (ic.ifc_ifcu.ifcu_buf == 0 && ic.ifc_len == 0) {
- ic.ifc_len = sizeof buf;
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
- goto gifconf_again;
+ /*
+ * 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 '%s'",
+ PROCDEV_DEVICE);
+ fclose(ifaces->fp);
+ ifaces->fp = NULL;
+ return 0;
+ }
+ len = strlen(buf);
+ if ((len <= 0) || (buf[len-1] != '\n')) {
+ log_error("Bad header line in '%s'", PROCDEV_DEVICE);
+ 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
+ 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
- /* If the SIOCGIFCONF resulted in more data than would fit in
- a buffer, allocate a bigger buffer. */
- if ((ic.ifc_ifcu.ifcu_buf == buf
-#ifdef SIOCGIFCONF_ZERO_PROBE
- || ic.ifc_ifcu.ifcu_buf == 0
+ 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
- ) && ic.ifc_len > sizeof buf) {
- ic.ifc_ifcu.ifcu_buf = dmalloc ((size_t)ic.ifc_len, MDL);
- if (!ic.ifc_ifcu.ifcu_buf)
- log_fatal ("Can't allocate SIOCGIFCONF buffer.");
- goto gifconf_again;
-#ifdef SIOCGIFCONF_ZERO_PROBE
- } else if (ic.ifc_ifcu.ifcu_buf == 0) {
- ic.ifc_ifcu.ifcu_buf = (caddr_t)buf;
- ic.ifc_len = sizeof buf;
- goto gifconf_again;
+
+#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 1.
+ */
+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)) {
+ 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
+ fclose(ifaces->fp6);
+ ifaces->fp6 = NULL;
+#endif
+}
+#else
+/* XXX: need to define non-Solaris, non-Linux iterators */
+#endif
+
+/* XXX: perhaps create drealloc() rather than do it manually */
+void
+add_ipv4_addr_to_interface(struct interface_info *iface,
+ const struct in_addr *addr) {
+ /*
+ * We don't expect a lot of addresses per IPv4 interface, so
+ * we use 4, as our "chunk size" for collecting addresses.
+ */
+ if (iface->addresses == NULL) {
+ iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL);
+ if (iface->addresses == NULL) {
+ log_fatal("Out of memory saving IPv4 address "
+ "on interface.");
+ }
+ iface->address_count = 0;
+ iface->address_max = 4;
+ } else if (iface->address_count >= iface->address_max) {
+ struct in_addr *tmp;
+ int new_max;
+
+ new_max = iface->address_max + 4;
+ tmp = dmalloc(new_max * sizeof(struct in_addr), MDL);
+ if (tmp == NULL) {
+ log_fatal("Out of memory saving IPv4 address "
+ "on interface.");
+ }
+ memcpy(tmp,
+ iface->addresses,
+ iface->address_max * sizeof(struct in_addr));
+ dfree(iface->addresses, MDL);
+ iface->addresses = tmp;
+ iface->address_max = new_max;
+ }
+ iface->addresses[iface->address_count++] = *addr;
+}
+
+/* XXX: perhaps create drealloc() rather than do it manually */
+void
+add_ipv6_addr_to_interface(struct interface_info *iface,
+ const struct in6_addr *addr) {
+ /*
+ * Each IPv6 interface will have at least two IPv6 addresses,
+ * and likely quite a few more. So we use 8, as our "chunk size" for
+ * collecting addresses.
+ */
+ if (iface->v6addresses == NULL) {
+ iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL);
+ if (iface->v6addresses == NULL) {
+ log_fatal("Out of memory saving IPv6 address "
+ "on interface.");
+ }
+ iface->v6address_count = 0;
+ iface->v6address_max = 8;
+ } else if (iface->v6address_count >= iface->v6address_max) {
+ struct in6_addr *tmp;
+ int new_max;
+
+ new_max = iface->v6address_max + 8;
+ tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL);
+ if (tmp == NULL) {
+ log_fatal("Out of memory saving IPv6 address "
+ "on interface.");
+ }
+ memcpy(tmp,
+ iface->v6addresses,
+ iface->v6address_max * sizeof(struct in6_addr));
+ dfree(iface->v6addresses, MDL);
+ iface->v6addresses = tmp;
+ iface->v6address_max = new_max;
+ }
+ iface->v6addresses[iface->v6address_count++] = *addr;
+}
+
+/* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces.
+ For each interface that's of type INET and not the loopback interface,
+ register that interface with the network I/O software, figure out what
+ subnet it's on, and add it to the list of interfaces. */
+
+void
+discover_interfaces(int state) {
+ struct iface_conf_list ifaces;
+ struct iface_info info;
+ int err;
+
+ struct interface_info *tmp, *ip;
+ struct interface_info *last, *next;
+
+ char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+
+ struct subnet *subnet;
+ int ir;
+ isc_result_t status;
+ int wifcount = 0;
+
+ static int setup_fallback = 0;
+
+ if (!begin_iface_scan(&ifaces)) {
+ log_fatal("Can't get list of interfaces.");
}
-
/* If we already have a list of interfaces, and we're running as
a DHCP server, the interfaces were requested. */
if (interfaces && (state == DISCOVER_SERVER ||
@@ -216,100 +780,129 @@ void discover_interfaces (state)
ir = INTERFACE_REQUESTED;
/* Cycle through the list of interfaces looking for IP addresses. */
- for (i = 0; i < ic.ifc_len;) {
- struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i);
-#ifdef HAVE_SA_LEN
- if (ifp -> ifr_addr.sa_len > sizeof (struct sockaddr))
- i += (sizeof ifp -> ifr_name) + ifp -> ifr_addr.sa_len;
- else
-#endif
- i += sizeof *ifp;
-
-#ifdef ALIAS_NAMES_PERMUTED
- if ((s = strrchr (ifp -> ifr_name, ':'))) {
- *s = 0;
- }
-#endif
-
-#ifdef SKIP_DUMMY_INTERFACES
- if (!strncmp (ifp -> ifr_name, "dummy", 5))
- continue;
-#endif
-
-
- /* See if this is the sort of interface we want to
- deal with. */
- strcpy (ifr.ifr_name, ifp -> ifr_name);
- if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
- log_fatal ("Can't get interface flags for %s: %m",
- ifr.ifr_name);
+ while (next_iface(&info, &err, &ifaces)) {
/* See if we've seen an interface that matches this one. */
- for (tmp = interfaces; tmp; tmp = tmp -> next)
- if (!strcmp (tmp -> name, ifp -> ifr_name))
+ for (tmp = interfaces; tmp; tmp = tmp->next) {
+ if (!strcmp(tmp->name, info.name))
break;
+ }
/* Skip non broadcast interfaces (plus loopback and
point-to-point in case an OS incorrectly marks them
as broadcast). Also skip down interfaces unless we're
trying to get a list of configurable interfaces. */
- if (((!(ifr.ifr_flags & IFF_BROADCAST) ||
- ifr.ifr_flags & IFF_LOOPBACK ||
- ifr.ifr_flags & IFF_POINTOPOINT) && !tmp) ||
- (!(ifr.ifr_flags & IFF_UP) &&
+ if (((!(info.flags & IFF_BROADCAST) ||
+ info.flags & IFF_LOOPBACK ||
+ info.flags & IFF_POINTOPOINT) && !tmp) ||
+ (!(info.flags & IFF_UP) &&
state != DISCOVER_UNCONFIGURED))
continue;
/* If there isn't already an interface by this name,
allocate one. */
- if (!tmp) {
- tmp = (struct interface_info *)0;
- status = interface_allocate (&tmp, MDL);
- if (status != ISC_R_SUCCESS)
- log_fatal ("Error allocating interface %s: %s",
- ifp -> ifr_name,
- isc_result_totext (status));
- strcpy (tmp -> name, ifp -> ifr_name);
- interface_snorf (tmp, ir);
- interface_dereference (&tmp, MDL);
+ if (tmp == NULL) {
+ status = interface_allocate(&tmp, MDL);
+ if (status != ISC_R_SUCCESS) {
+ log_fatal("Error allocating interface %s: %s",
+ info.name, isc_result_totext(status));
+ }
+ strcpy(tmp->name, info.name);
+ interface_snorf(tmp, ir);
+ interface_dereference(&tmp, MDL);
tmp = interfaces; /* XXX */
}
- if (dhcp_interface_discovery_hook)
- (*dhcp_interface_discovery_hook) (tmp);
+ if (dhcp_interface_discovery_hook) {
+ (*dhcp_interface_discovery_hook)(tmp);
+ }
/* If we have the capability, extract link information
- and record it in a linked list. */
+ and record it in. */
#ifdef HAVE_AF_LINK
- if (ifp -> ifr_addr.sa_family == AF_LINK) {
- struct sockaddr_dl *foo = ((struct sockaddr_dl *)
- (&ifp -> ifr_addr));
-#if defined (HAVE_SIN_LEN)
- tmp -> hw_address.hlen = foo -> sdl_alen;
-#else
- tmp -> hw_address.hlen = 6; /* XXX!!! */
-#endif
- tmp -> hw_address.hbuf [0] = HTYPE_ETHER; /* XXX */
- memcpy (&tmp -> hw_address.hbuf [1],
- LLADDR (foo), tmp -> hw_address.hlen);
- tmp -> hw_address.hlen++; /* for type. */
+ if (info.addr.ss_family == AF_LINK) {
+ struct sockaddr_dl *d = (struct sockaddr_dl*)&info.addr;
+ tmp->hw_address.hlen = d->sdl_alen;
+ tmp->hw_address.hbuf[0] = HTYPE_ETHER; /* XXX */
+ memcpy(&tmp->hw_address.hbuf[1],
+ LLADDR(d),
+ tmp->hw_address.hlen);
+ tmp->hw_address.hlen++; /* for type. */
} else
#endif /* AF_LINK */
- if (ifp -> ifr_addr.sa_family == AF_INET) {
+ if ((info.addr.ss_family == AF_INET) &&
+ (local_family == AF_INET)) {
+ struct sockaddr_in *a = (struct sockaddr_in*)&info.addr;
struct iaddr addr;
- /* Get a pointer to the address... */
- memcpy (&foo, &ifp -> ifr_addr,
- sizeof ifp -> ifr_addr);
+ /* We don't want the loopback interface. */
+ if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
+ ((tmp->flags & INTERFACE_AUTOMATIC) &&
+ state == DISCOVER_SERVER))
+ continue;
+
+ /* If the only address we have is 0.0.0.0, we
+ shouldn't consider the interface configured. */
+ if (a->sin_addr.s_addr != htonl(INADDR_ANY))
+ tmp->configured = 1;
+
+ add_ipv4_addr_to_interface(tmp, &a->sin_addr);
+
+/*
+ * XXX: We don't have ifreq in Solaris-land if we want IPv6. Fortunately,
+ * we don't actually need this for anything on Solaris.
+ */
+#if 0
+ /* If this is the first real IP address we've
+ found, keep a pointer to ifreq structure in
+ which we found it. */
+ if (!tmp -> ifp) {
+#ifdef HAVE_SA_LEN
+ unsigned len = ((sizeof ifp -> ifr_name) +
+ ifp -> ifr_addr.sa_len);
+#else
+ unsigned len = sizeof *ifp;
+#endif
+ tif = (struct ifreq *)dmalloc (len, MDL);
+ if (!tif)
+ log_fatal ("no space for ifp.");
+ memcpy (tif, ifp, len);
+ tmp -> ifp = tif;
+ }
+#endif /* 0 */
+
+ /* invoke the setup hook */
+ addr.len = 4;
+ memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len);
+ if (dhcp_interface_setup_hook) {
+ (*dhcp_interface_setup_hook)(tmp, &addr);
+ }
+ }
+ else if ((info.addr.ss_family == AF_INET6) &&
+ (local_family == AF_INET6)) {
+ struct sockaddr_in6 *a =
+ (struct sockaddr_in6*)&info.addr;
+ struct iaddr addr;
/* We don't want the loopback interface. */
- if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK) &&
- ((tmp -> flags & INTERFACE_AUTOMATIC) &&
+ if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) &&
+ ((tmp->flags & INTERFACE_AUTOMATIC) &&
state == DISCOVER_SERVER))
continue;
+ /* If the only address we have is 0.0.0.0, we
+ shouldn't consider the interface configured. */
+ if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
+ tmp->configured = 1;
+ add_ipv6_addr_to_interface(tmp, &a->sin6_addr);
+
+/*
+ * XXX: We don't have ifreq in Solaris-land if we want IPv6. Fortunately,
+ * we don't actually need this for anything on Solaris.
+ */
+#if 0
/* If this is the first real IP address we've
found, keep a pointer to ifreq structure in
which we found it. */
@@ -327,20 +920,24 @@ void discover_interfaces (state)
tmp -> ifp = tif;
tmp -> primary_address = foo.sin_addr;
}
+#endif /* 0 */
- /* Grab the address... */
- addr.len = 4;
- memcpy (addr.iabuf, &foo.sin_addr.s_addr,
- addr.len);
- if (dhcp_interface_setup_hook)
- (*dhcp_interface_setup_hook) (tmp, &addr);
+ /* invoke the setup hook */
+ addr.len = 16;
+ memcpy(addr.iabuf, &a->sin6_addr, addr.len);
+ if (dhcp_interface_setup_hook) {
+ (*dhcp_interface_setup_hook)(tmp, &addr);
+ }
}
}
- /* If we allocated a buffer, free it. */
- if (ic.ifc_ifcu.ifcu_buf != buf)
- dfree (ic.ifc_ifcu.ifcu_buf, MDL);
+ if (err) {
+ log_fatal("Error getting interface information.");
+ }
+
+ end_iface_scan(&ifaces);
+#if 0
#if defined (LINUX_SLASHPROC_DISCOVERY)
/* On Linux, interfaces that don't have IP addresses don't
show up in the SIOCGIFCONF syscall. This only matters for
@@ -428,11 +1025,14 @@ void discover_interfaces (state)
fclose (proc_dev);
}
#endif
+#endif /* 0 */
/* Now cycle through all the interfaces we found, looking for
hardware addresses. */
+ /* XXX: The dlpi interface code will get this information in Solaris */
+#if 0
#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK)
- for (tmp = interfaces; tmp; tmp = tmp -> next) {
+ for (tmp = interfaces; tmp != NULL; tmp = tmp->next) {
struct ifreq ifr;
struct sockaddr sa;
int b, sk;
@@ -539,16 +1139,16 @@ void discover_interfaces (state)
}
}
#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */
+#endif /* 0 */
/* If we're just trying to get a list of interfaces that we might
be able to configure, we can quit now. */
if (state == DISCOVER_UNCONFIGURED) {
- close (sock);
return;
}
/* Weed out the interfaces that did not have IP addresses. */
- tmp = last = next = (struct interface_info *)0;
+ tmp = last = next = NULL;
if (interfaces)
interface_reference (&tmp, interfaces, MDL);
while (tmp) {
@@ -567,7 +1167,9 @@ void discover_interfaces (state)
state == DISCOVER_REQUESTED)
tmp -> flags &= ~(INTERFACE_AUTOMATIC |
INTERFACE_REQUESTED);
- if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
+/* XXX: no ifp in Solaris */
+/* if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {*/
+ if (!(tmp->flags & INTERFACE_REQUESTED)) {
if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
log_fatal ("%s: not found", tmp -> name);
if (!last) {
@@ -600,15 +1202,31 @@ void discover_interfaces (state)
}
last = tmp;
- memcpy (&foo, &tmp -> ifp -> ifr_addr,
- sizeof tmp -> ifp -> ifr_addr);
+/* XXX: no ifp in Solaris */
+/* memcpy (&foo, &tmp -> ifp -> ifr_addr,
+ sizeof tmp -> ifp -> ifr_addr);*/
/* We must have a subnet declaration for each interface. */
- if (!tmp -> shared_network && (state == DISCOVER_SERVER)) {
- log_error ("%s", "");
- log_error ("No subnet declaration for %s (%s).",
- tmp -> name, inet_ntoa (foo.sin_addr));
- if (supports_multiple_interfaces (tmp)) {
+ if (!tmp->shared_network && (state == DISCOVER_SERVER)) {
+ log_error("%s", "");
+ if (local_family == AF_INET) {
+ log_error("No subnet declaration for %s (%s).",
+ tmp->name,
+ inet_ntoa(tmp->addresses[0]));
+ } else {
+ if (tmp->v6addresses != NULL) {
+ inet_ntop(AF_INET6,
+ &tmp->v6addresses[0],
+ abuf,
+ sizeof(abuf));
+ } else {
+ strcpy(abuf, "no addresses");
+ }
+ log_error("No subnet declaration for %s (%s).",
+ tmp->name,
+ abuf);
+ }
+ if (supports_multiple_interfaces(tmp)) {
log_error ("** Ignoring requests on %s. %s",
tmp -> name, "If this is not what");
log_error (" you want, please write %s",
@@ -643,7 +1261,7 @@ void discover_interfaces (state)
to the first address we found. */
subnet -> interface_address.len = 4;
memcpy (subnet -> interface_address.iabuf,
- &foo.sin_addr.s_addr, 4);
+ &tmp->addresses[0].s_addr, 4);
}
}
@@ -652,8 +1270,16 @@ void discover_interfaces (state)
tmp -> index = -1;
/* Register the interface... */
- if_register_receive (tmp);
- if_register_send (tmp);
+ if (local_family == AF_INET) {
+ if_register_receive(tmp);
+ if_register_send(tmp);
+ } else {
+ if (state == DISCOVER_SERVER) {
+ if_register6(tmp, 1);
+ } else {
+ if_register6(tmp, 0);
+ }
+ }
interface_stash (tmp);
wifcount++;
@@ -680,24 +1306,28 @@ void discover_interfaces (state)
continue;
if (tmp -> rfdesc == -1)
continue;
- status = omapi_register_io_object ((omapi_object_t *)tmp,
- if_readsocket, 0,
- got_one, 0, 0);
+ if (local_family == AF_INET) {
+ status = omapi_register_io_object((omapi_object_t *)tmp,
+ if_readsocket,
+ 0, got_one, 0, 0);
+ } else {
+ status = omapi_register_io_object((omapi_object_t *)tmp,
+ if_readsocket,
+ 0, got_one_v6, 0, 0);
+ }
if (status != ISC_R_SUCCESS)
log_fatal ("Can't register I/O handle for %s: %s",
tmp -> name, isc_result_totext (status));
}
- close (sock);
-
if (state == DISCOVER_SERVER && wifcount == 0) {
log_info ("%s", "");
log_fatal ("Not configured to listen on any interfaces!");
}
- if (!setup_fallback) {
+ if ((local_family == AF_INET) && !setup_fallback) {
setup_fallback = 1;
- maybe_setup_fallback ();
+ maybe_setup_fallback();
}
#if defined (HAVE_SETFD)
@@ -808,6 +1438,48 @@ isc_result_t got_one (h)
return ISC_R_SUCCESS;
}
+isc_result_t
+got_one_v6(omapi_object_t *h) {
+ struct sockaddr_in6 from;
+ struct in6_addr to;
+ struct iaddr ifrom;
+ int result;
+ char buf[65536]; /* maximum size for a UDP packet is 65536 */
+ struct interface_info *ip;
+ int is_unicast;
+
+ if (h->type != dhcp_type_interface) {
+ return ISC_R_INVALIDARG;
+ }
+ ip = (struct interface_info *)h;
+
+ result = receive_packet6(ip, buf, sizeof(buf), &from, &to);
+ if (result < 0) {
+ log_error("receive_packet6() failed on %s: %m", ip->name);
+ return ISC_R_UNEXPECTED;
+ }
+
+ if (dhcpv6_packet_handler != NULL) {
+ /*
+ * If a packet is not multicast, we assume it is unicast.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&to)) {
+ is_unicast = ISC_FALSE;
+ } else {
+ is_unicast = ISC_TRUE;
+ }
+
+ ifrom.len = 16;
+ memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
+
+ (*dhcpv6_packet_handler)(ip, buf,
+ result, from.sin6_port,
+ &ifrom, is_unicast);
+ }
+
+ return ISC_R_SUCCESS;
+}
+
isc_result_t dhcp_interface_set_value (omapi_object_t *h,
omapi_object_t *id,
omapi_data_string_t *name,
@@ -1102,8 +1774,12 @@ isc_result_t dhcp_interface_remove (omapi_object_t *lp,
/* remove the io object */
omapi_unregister_io_object ((omapi_object_t *)interface);
- if_deregister_send (interface);
- if_deregister_receive (interface);
+ if (local_family == AF_INET) {
+ if_deregister_send(interface);
+ if_deregister_receive(interface);
+ } else {
+ if_deregister6(interface);
+ }
return ISC_R_SUCCESS;
}