components/isc-dhcp/patches/sockets.patch
author Rishi Srivatsavai <Rishi.Srivatsavai@oracle.com>
Thu, 02 Jun 2011 13:26:03 -0700
changeset 280 c0dae1e3ca2f
permissions -rw-r--r--
PSARC 2011/043 Import ISC DHCP 7022308 Integrate ISC DHCP server and relay agent

--- 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 */
 
 /*