diff options
author | David Hankins <dhankins@isc.org> | 2007-05-08 23:05:22 +0000 |
---|---|---|
committer | David Hankins <dhankins@isc.org> | 2007-05-08 23:05:22 +0000 |
commit | 98bd7ca0990e6d88e3345d3bc966ebe8216691a7 (patch) | |
tree | c4437ca467e8f733d530170a5c445747b2defd68 /common/discover.c | |
parent | 74dc3e0b2786c46956e7517398ae6f7c6dad52d7 (diff) | |
download | isc-dhcp-98bd7ca0990e6d88e3345d3bc966ebe8216691a7.tar.gz |
DHCPv6 branch merged to HEAD.
Diffstat (limited to 'common/discover.c')
-rw-r--r-- | common/discover.c | 1012 |
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; } |