Commit 2a4556df authored by Shawn Routhier's avatar Shawn Routhier
Browse files

In Solaris 11 switch to using sockets instead of DLPI, thanks

to a patch form Oracle.  [ISC-Bugs #24634].
parent bc3b86ba
......@@ -33,6 +33,8 @@ the ISC DHCP Distribution.
5.6 FreeBSD
5.7 NeXTSTEP
5.8 SOLARIS
5.8.1 Solaris 11
5.8.2 Other Solaris Items
5.9 AIX
5.10 MacOS X
6 SUPPORT
......@@ -412,6 +414,21 @@ must install this extension in order to get dhcpd or dhclient to work.
SOLARIS
Solaris 11
We have integrated a patch from Oracle to use sockets instead of
DLPI on Solaris 11. This functionality was written for use with
Solaris Studio 12.2 and requires the system/header package.
By default this code is disabled in order to minimize disruptions
for current users. In order to enable this code you will need to
enable both USE_SOCKETS and USE_V4_PKTINFO as part of the
configuration step. The command line would be something like:
./configure --enable-use-sockets --enable-ipv4-pktinfo
Other Solaris Items
One problem which has been observed and is not fixed in this
patchlevel has to do with using DLPI on Solaris machines. The symptom
of this problem is that the DHCP server never receives any requests.
......
......@@ -177,6 +177,9 @@ work on other platforms. Please report any problems and suggested fixes to
print routines to allow for greater than 60 characters or, when
printing as hex strings, 20 characters. [ISC-Bugs #22743]
- In Solaris 11 switch to using sockets instead of DLPI, thanks
to a patch form Oracle. [ISC-Bugs #24634].
Changes since 4.1-ESV-R1
! In dhclient check the data for some string options for
......
......@@ -309,12 +309,15 @@ int
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;
#endif
do {
foundif = ISC_FALSE;
if (ifaces->next >= ifaces->num) {
*err = 0;
return 0;
......@@ -328,6 +331,13 @@ next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
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 +350,9 @@ next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
}
#endif /* defined(sun) || defined(__linux) */
} while (strncmp(info->name, "dummy", 5) == 0);
foundif = ISC_TRUE;
} while ((foundif == ISC_FALSE) ||
(strncmp(info->name, "dummy", 5) == 0));
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.lifr_name, info->name);
......@@ -958,7 +970,12 @@ discover_interfaces(int state) {
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 +1403,25 @@ isc_result_t got_one (h)
if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN)
return ISC_R_UNEXPECTED;
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
{
/* 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);
......@@ -1443,6 +1479,7 @@ got_one_v6(omapi_object_t *h) {
memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
/* Seek forward to find the matching source interface. */
ip = interfaces;
while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx))
ip = ip->next;
......
......@@ -3,7 +3,7 @@
BSD socket interface code... */
/*
* Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004-2011 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
......@@ -47,6 +47,13 @@
#include <sys/uio.h>
#include <signal.h>
#if defined(sun) && defined(USE_V4_PKTINFO)
#include <sys/sysmacros.h>
#include <net/if.h>
#include <sys/sockio.h>
#include <net/if_dl.h>
#endif
#ifdef USE_SOCKET_FALLBACK
# if !defined (USE_SOCKET_SEND)
# define if_register_send if_register_fallback
......@@ -66,6 +73,16 @@ static int global_v6_socket = -1;
static void if_register_multicast(struct interface_info *info);
#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 (for example Solaris 11).
*/
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
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
......@@ -242,6 +259,20 @@ if_register_socket(struct interface_info *info, int family,
log_fatal("Can't set IP_BROADCAST_IF on dhcp socket: %m");
#endif
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
/*
* 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 +306,6 @@ if_register_socket(struct interface_info *info, int family,
}
#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 */
......@@ -288,21 +315,24 @@ void if_register_send (info)
struct interface_info *info;
{
#ifndef USE_SOCKET_RECEIVE
info -> wfdesc = if_register_socket (info, AF_INET, 0);
info->wfdesc = if_register_socket(info, AF_INET, 0);
/* 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);
#if defined (USE_SOCKET_FALLBACK)
/* Fallback only registers for send, but may need to receive as
well. */
info -> rfdesc = info -> wfdesc;
info->rfdesc = info->wfdesc;
#endif
#else
info -> wfdesc = info -> rfdesc;
info->wfdesc = info->rfdesc;
#endif
if (!quiet_interface_discovery)
log_info ("Sending on Socket/%s%s%s",
info -> name,
(info -> shared_network ? "/" : ""),
(info -> shared_network ?
info -> shared_network -> name : ""));
info->name,
(info->shared_network ? "/" : ""),
(info->shared_network ?
info->shared_network->name : ""));
}
#if defined (USE_SOCKET_SEND)
......@@ -328,23 +358,61 @@ void if_deregister_send (info)
void if_register_receive (info)
struct interface_info *info;
{
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
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);
info->rfdesc = if_register_socket(info, AF_INET, 0);
#endif /* IP_PKTINFO... */
/* 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);
if (!quiet_interface_discovery)
log_info ("Listening on Socket/%s%s%s",
info -> name,
(info -> shared_network ? "/" : ""),
(info -> shared_network ?
info -> shared_network -> name : ""));
info->name,
(info->shared_network ? "/" : ""),
(info->shared_network ?
info->shared_network->name : ""));
}
void if_deregister_receive (info)
struct interface_info *info;
{
close (info -> rfdesc);
info -> rfdesc = -1;
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
/* 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 /* IP_PKTINFO... */
if (!quiet_interface_discovery)
log_info ("Disabling input on Socket/%s%s%s",
info -> name,
......@@ -488,6 +556,18 @@ ssize_t send_packet (interface, packet, raw, len, from, to, hto)
#ifdef IGNORE_HOSTUNREACH
int retry = 0;
do {
#endif
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
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);
......@@ -559,11 +639,15 @@ static size_t CMSG_SPACE(size_t len) {
#endif /* DHCPv6 */
#ifdef DHCPv6
#if defined(DHCPv6) || \
(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
defined(USE_V4_PKTINFO))
/*
* 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() when we use a single socket and IP_PKTINFO to
* send the packet out the correct interface.
*/
static void *control_buf = NULL;
static size_t control_buf_len = 0;
......@@ -574,7 +658,9 @@ allocate_cmsg_cbuf(void) {
control_buf = dmalloc(control_buf_len, MDL);
return;
}
#endif /* DHCPv6, IP_PKTINFO ... */
#ifdef DHCPv6
/*
* For both send_packet6() and receive_packet6() we need to use the
* sendmsg()/recvmsg() functions rather than the simpler send()/recv()
......@@ -673,7 +759,9 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
struct sockaddr_in *from;
struct hardware *hfrom;
{
#if !defined(USE_V4_PKTINFO)
SOCKLEN_T flen = sizeof *from;
#endif
int result;
/*
......@@ -687,8 +775,99 @@ ssize_t receive_packet (interface, buf, len, from, hfrom)
int retry = 0;
do {
#endif
#if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
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 /* IP_PKTINFO ... */
#ifdef IGNORE_HOSTUNREACH
} while (result < 0 &&
(errno == EHOSTUNREACH ||
......@@ -842,10 +1021,12 @@ int can_receive_unicast_unconfigured (ip)
int supports_multiple_interfaces (ip)
struct interface_info *ip;
{
#if defined (SO_BINDTODEVICE)
return 1;
#if defined(SO_BINDTODEVICE) || \
(defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && \
defined(USE_V4_PKTINFO))
return(1);
#else
return 0;
return(0);
#endif
}
......@@ -876,6 +1057,68 @@ void maybe_setup_fallback ()
}
#endif
}
#if defined(sun) && defined(USE_V4_PKTINFO)
/* This code assumes the existence of SIOCGLIFHWADDR */
void
get_hw_addr(const char *name, struct hardware *hw) {
struct sockaddr_dl *dladdrp;
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 (sock != -1)
(void) close(sock);
#ifdef DHCPv6
/*
* If approrpriate try this with an IPv6 socket
*/
if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) >= 0 &&
ioctl(sock, SIOCGLIFFLAGS, &lifr) >= 0) {
goto flag_check;
}
if (sock != -1)
(void) close(sock);
#endif
log_fatal("Couldn't get interface flags for %s: %m", name);
}
flag_check:
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 (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);
if (sock != -1)
(void) close(sock);
}
#endif /* defined(sun) */
#endif /* USE_SOCKET_SEND */
/*
......
......@@ -143,6 +143,24 @@ if test "$enable_early_chroot" = "yes" ; then
[Define to any value to chroot() prior to loading config.])
fi
AC_ARG_ENABLE(IPv4_PKTINFO,
AC_HELP_STRING([--enable-ipv4-pktinfo],
[enable use of pktinfo on IPv4 sockets (default is no)]))
if test "$enable_ipv4_pktinfo" = "yes"; then
AC_DEFINE([USE_V4_PKTINFO], [1],
[Define to 1 to enable IPv4 packet info support.])
fi
AC_ARG_ENABLE(USE_SOCKETS,
AC_HELP_STRING([--enable-use-sockets],
[use the standard BSD socket API (default is no)]))
if test "$enable_use_sockets" = "yes"; then
AC_DEFINE([USE_SOCKETS], [1],
[Define to 1 to use the standard BSD socket API.])
fi
###
### Path fun. Older versions of DHCP were installed in /usr/sbin, so we
### need to look there and potentially overwrite by default (but not if
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment