--- dhcp-4.1-ESV-R1/common/discover.c Tue Sep 29 12:44:49 2009
+++ dhcp-4.1-ESV-R1-patched/common/discover.c Thu May 26 11:49:33 2011
@@ -309,6 +309,7 @@
next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
struct LIFREQ *p;
struct LIFREQ tmp;
+ isc_boolean_t foundif;
#if defined(sun) || defined(__linux)
/* Pointer used to remove interface aliases. */
char *s;
@@ -315,6 +316,7 @@
#endif
do {
+ foundif = ISC_FALSE;
if (ifaces->next >= ifaces->num) {
*err = 0;
return 0;
@@ -328,6 +330,13 @@
log_error("Interface name '%s' too long", p->lifr_name);
return 0;
}
+
+ /* Reject if interface address family does not match */
+ if (p->lifr_addr.ss_family != local_family) {
+ ifaces->next++;
+ continue;
+ }
+
strcpy(info->name, p->lifr_name);
memset(&info->addr, 0, sizeof(info->addr));
memcpy(&info->addr, &p->lifr_addr, sizeof(p->lifr_addr));
@@ -340,7 +349,9 @@
}
#endif /* defined(sun) || defined(__linux) */
- } while (strncmp(info->name, "dummy", 5) == 0);
+ foundif = ISC_TRUE;
+ } while ((foundif == ISC_FALSE) ||
+ (strncmp(p->lifr_name, "dummy", 5) == 0));
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.lifr_name, info->name);
@@ -958,7 +969,12 @@
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 (((!(info.flags & IFF_BROADCAST) ||
+ if ((((local_family == AF_INET &&
+ !(info.flags & IFF_BROADCAST)) ||
+#ifdef DHCPv6
+ (local_family == AF_INET6 &&
+ !(info.flags & IFF_MULTICAST)) ||
+#endif
info.flags & IFF_LOOPBACK ||
info.flags & IFF_POINTOPOINT) && !tmp) ||
(!(info.flags & IFF_UP) &&
@@ -1386,6 +1402,25 @@
if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN)
return ISC_R_UNEXPECTED;
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+ {
+ /* We retrieve the ifindex from the unused hfrom variable */
+ unsigned int ifindex;
+
+ memcpy(&ifindex, hfrom.hbuf, sizeof (ifindex));
+
+ /*
+ * Seek forward from the first interface to find the matching
+ * source interface by interface index.
+ */
+ ip = interfaces;
+ while ((ip != NULL) && (if_nametoindex(ip->name) != ifindex))
+ ip = ip->next;
+ if (ip == NULL)
+ return ISC_R_NOTFOUND;
+ }
+#endif
+
if (bootp_packet_handler) {
ifrom.len = 4;
memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
@@ -1442,7 +1477,11 @@
ifrom.len = 16;
memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
- /* Seek forward to find the matching source interface. */
+ /*
+ * Seek forward from the first interface to find the matching
+ * source interface by interface index.
+ */
+ ip = interfaces;
while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx))
ip = ip->next;
--- dhcp-4.1-ESV-R1/common/socket.c Tue Oct 5 17:32:52 2010
+++ dhcp-4.1-ESV-R1-patched/common/socket.c Thu May 12 16:11:13 2011
@@ -45,6 +45,16 @@
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/uio.h>
+#if defined(sun)
+#include <sys/sysmacros.h>
+#include <net/if.h>
+#include <sys/sockio.h>
+#if defined(SIOCGLIFHWADDR)
+#include <net/if_dl.h>
+#else
+#include <libdlpi.h>
+#endif
+#endif
#include <signal.h>
#ifdef USE_SOCKET_FALLBACK
@@ -67,6 +77,16 @@
#endif
/*
+ * We can use a single socket for AF_INET (similar to AF_INET6) on all
+ * interfaces configured for DHCP if the system has support for IP_PKTINFO
+ * and IP_RECVPKTINFO (f.e. Solaris 11).
+ */
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+static unsigned int global_v4_socket_references = 0;
+static int global_v4_socket = -1;
+#endif
+
+/*
* If we can't bind() to a specific interface, then we can only have
* a single socket. This variable insures that we don't try to listen
* on two sockets.
@@ -242,6 +262,20 @@
log_fatal("Can't set IP_BROADCAST_IF on dhcp socket: %m");
#endif
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+ /*
+ * If we turn on IP_RECVPKTINFO we will be able to receive
+ * the interface index information of the received packet.
+ */
+ if (family == AF_INET) {
+ int on = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_RECVPKTINFO,
+ &on, sizeof(on)) != 0) {
+ log_fatal("setsockopt: IPV_RECVPKTINFO: %m");
+ }
+ }
+#endif
+
#ifdef DHCPv6
/*
* If we turn on IPV6_PKTINFO, we will be able to receive
@@ -275,10 +309,6 @@
}
#endif /* DHCPv6 */
- /* If this is a normal IPv4 address, get the hardware address. */
- if ((local_family == AF_INET) && (strcmp(info->name, "fallback") != 0))
- get_hw_addr(info->name, &info->hw_address);
-
return sock;
}
#endif /* USE_SOCKET_SEND || USE_SOCKET_RECEIVE || USE_SOCKET_FALLBACK */
@@ -328,9 +358,25 @@
void if_register_receive (info)
struct interface_info *info;
{
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+ if (global_v4_socket_references == 0) {
+ global_v4_socket = if_register_socket(info, AF_INET, 0);
+ if (global_v4_socket < 0) {
+ /*
+ * if_register_socket() fatally logs if it fails to
+ * create a socket, this is just a sanity check.
+ */
+ log_fatal("Failed to create AF_INET socket %s:%d", MDL);
+ }
+ }
+
+ info->rfdesc = global_v4_socket;
+ global_v4_socket_references++;
+#else
/* If we're using the socket API for sending and receiving,
we don't need to register this interface twice. */
info -> rfdesc = if_register_socket (info, AF_INET, 0);
+#endif
if (!quiet_interface_discovery)
log_info ("Listening on Socket/%s%s%s",
info -> name,
@@ -337,13 +383,34 @@
(info -> shared_network ? "/" : ""),
(info -> shared_network ?
info -> shared_network -> name : ""));
+
+ /* If this is a normal IPv4 address, get the hardware address. */
+ if (strcmp(info->name, "fallback") != 0)
+ get_hw_addr(info->name, &info->hw_address);
}
void if_deregister_receive (info)
struct interface_info *info;
{
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+ /* Dereference the global v4 socket. */
+ if ((info->rfdesc == global_v4_socket) &&
+ (info->wfdesc == global_v4_socket) &&
+ (global_v4_socket_references > 0)) {
+ global_v4_socket_references--;
+ info->rfdesc = -1;
+ } else {
+ log_fatal("Impossible condition at %s:%d", MDL);
+ }
+
+ if (global_v4_socket_references == 0) {
+ close(global_v4_socket);
+ global_v4_socket = -1;
+ }
+#else
close (info -> rfdesc);
info -> rfdesc = -1;
+#endif
if (!quiet_interface_discovery)
log_info ("Disabling input on Socket/%s%s%s",
@@ -489,6 +556,17 @@
int retry = 0;
do {
#endif
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+ struct in_pktinfo pktinfo;
+
+ if (interface->ifp != NULL) {
+ memset(&pktinfo, 0, sizeof (pktinfo));
+ pktinfo.ipi_ifindex = interface->ifp->ifr_index;
+ if (setsockopt(interface -> wfdesc, IPPROTO_IP,
+ IP_PKTINFO, (char *)&pktinfo, sizeof (pktinfo)) < 0)
+ log_fatal("setsockopt: IP_PKTINFO: %m");
+ }
+#endif
result = sendto (interface -> wfdesc, (char *)raw, len, 0,
(struct sockaddr *)to, sizeof *to);
#ifdef IGNORE_HOSTUNREACH
@@ -559,11 +637,13 @@
#endif /* DHCPv6 */
-#ifdef DHCPv6
+#ifdef DHCPv6 || (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO))
/*
* For both send_packet6() and receive_packet6() we need to allocate
* space for the cmsg header information. We do this once and reuse
- * the buffer.
+ * the buffer. We also need the control buf for send_packet and
+ * receive_packet for AF_INET when we use a single socket and IP_PKTINFO
+ * to send the packet out the right interface.
*/
static void *control_buf = NULL;
static size_t control_buf_len = 0;
@@ -574,7 +654,9 @@
control_buf = dmalloc(control_buf_len, MDL);
return;
}
+#endif
+#ifdef DHCPv6
/*
* For both send_packet6() and receive_packet6() we need to use the
* sendmsg()/recvmsg() functions rather than the simpler send()/recv()
@@ -687,8 +769,97 @@
int retry = 0;
do {
#endif
+#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO)
+ struct msghdr m;
+ struct iovec v;
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pktinfo;
+ unsigned int ifindex;
+ int found_pktinfo;
+
+ /*
+ * If necessary allocate space for the control message header.
+ * The space is common between send and receive.
+ */
+ if (control_buf == NULL) {
+ allocate_cmsg_cbuf();
+ if (control_buf == NULL) {
+ log_error("receive_packet: unable to allocate cmsg "
+ "header");
+ return(ENOMEM);
+ }
+ }
+ memset(control_buf, 0, control_buf_len);
+
+ /*
+ * Initialize our message header structure.
+ */
+ memset(&m, 0, sizeof(m));
+
+ /*
+ * Point so we can get the from address.
+ */
+ m.msg_name = from;
+ m.msg_namelen = sizeof(*from);
+
+ /*
+ * Set the data buffer we're receiving. (Using this wacky
+ * "scatter-gather" stuff... but we that doesn't really make
+ * sense for us, so we use a single vector entry.)
+ */
+ v.iov_base = buf;
+ v.iov_len = len;
+ m.msg_iov = &v;
+ m.msg_iovlen = 1;
+
+ /*
+ * Getting the interface is a bit more involved.
+ *
+ * We set up some space for a "control message". We have
+ * previously asked the kernel to give us packet
+ * information (when we initialized the interface), so we
+ * should get the destination address from that.
+ */
+ m.msg_control = control_buf;
+ m.msg_controllen = control_buf_len;
+
+ result = recvmsg(interface->rfdesc, &m, 0);
+
+ if (result >= 0) {
+ /*
+ * If we did read successfully, then we need to loop
+ * through the control messages we received and
+ * find the one with our destination address.
+ *
+ * We also keep a flag to see if we found it. If we
+ * didn't, then we consider this to be an error.
+ */
+ found_pktinfo = 0;
+ cmsg = CMSG_FIRSTHDR(&m);
+ while (cmsg != NULL) {
+ if ((cmsg->cmsg_level == IPPROTO_IP) &&
+ (cmsg->cmsg_type == IP_PKTINFO)) {
+ pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ ifindex = pktinfo->ipi_ifindex;
+ /*
+ * We pass the ifindex back to the caller using
+ * the unused hfrom parameter avoiding interface
+ * changes between sockets and the discover code.
+ */
+ memcpy(hfrom->hbuf, &ifindex, sizeof (ifindex));
+ found_pktinfo = 1;
+ }
+ cmsg = CMSG_NXTHDR(&m, cmsg);
+ }
+ if (!found_pktinfo) {
+ result = -1;
+ errno = EIO;
+ }
+ }
+#else
result = recvfrom (interface -> rfdesc, (char *)buf, len, 0,
(struct sockaddr *)from, &flen);
+#endif
#ifdef IGNORE_HOSTUNREACH
} while (result < 0 &&
(errno == EHOSTUNREACH ||
@@ -842,7 +1013,7 @@
int supports_multiple_interfaces (ip)
struct interface_info *ip;
{
-#if defined (SO_BINDTODEVICE)
+#if defined (SO_BINDTODEVICE) || (defined(IP_PKTINFO) && defined(IP_RECVPKTINFO))
return 1;
#else
return 0;
@@ -876,6 +1047,80 @@
}
#endif
}
+
+#if defined(sun)
+void
+get_hw_addr(const char *name, struct hardware *hw) {
+#if defined(SIOCGLIFHWADDR)
+ struct sockaddr_dl *dladdrp;
+#else
+ dlpi_handle_t dh;
+ uint8_t pa_buf[DLPI_PHYSADDR_MAX];
+ size_t len = sizeof (pa_buf);
+#endif
+ int rv, sock, i;
+ struct lifreq lifr;
+
+ memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name));
+ /*
+ * Check if the interface is a virtual or IPMP interface - in those
+ * cases it has no hw address, so generate a random one.
+ */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
+ ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) {
+ /*
+ * If the interface only has IPv6, try this with an IPv6 socket.
+ */
+ if (sock != -1)
+ (void) close(sock);
+
+ if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ||
+ ioctl(sock, SIOCGLIFFLAGS, &lifr) < 0) {
+ log_fatal("Couldn't get interface flags for %s: %m", name);
+ }
+ }
+
+ if (lifr.lifr_flags & (IFF_VIRTUAL|IFF_IPMP)) {
+ hw->hlen = sizeof (hw->hbuf);
+ srandom((long)gethrtime());
+
+ for (i = 0; i < hw->hlen; ++i) {
+ hw->hbuf[i] = random() % 256;
+ }
+
+ if (sock != -1)
+ (void) close(sock);
+ return;
+ }
+
+#if defined(SIOCGLIFHWADDR)
+ if (ioctl(sock, SIOCGLIFHWADDR, &lifr) < 0)
+ log_fatal("Couldn't get interface hardware address for %s: %m", name);
+ dladdrp = (struct sockaddr_dl *)&lifr.lifr_addr;
+ hw->hlen = dladdrp->sdl_alen;
+ memcpy(hw->hbuf, LLADDR(dladdrp), hw->hlen);
+#else
+ if ((rv = dlpi_open(name, &dh, 0)) != DLPI_SUCCESS) {
+ log_fatal("Couldn't open DLPI device for %s: %s", name,
+ dlpi_strerror(rv));
+ }
+
+ if ((rv = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, pa_buf, &len))
+ != DLPI_SUCCESS) {
+ log_fatal("Couldn't get physical address for device %s: %s",
+ name, dlpi_strerror(rv));
+ }
+
+ hw->hlen = MIN(sizeof (hw->hbuf), len);
+ memcpy(hw->hbuf, pa_buf, hw->hlen);
+
+ dlpi_close(dh);
+#endif
+ if (sock != -1)
+ (void) close(sock);
+}
+#endif /* defined(sun) */
#endif /* USE_SOCKET_SEND */
/*