PSARC/2007/565 IP_XMIT_IF removal and IP_BOUND_IF rationalization
authormeem
Tue, 30 Oct 2007 11:15:43 -0700
changeset 5381 6bff17151099
parent 5380 bed8a5ead37d
child 5382 e03ab59ed18b
PSARC/2007/565 IP_XMIT_IF removal and IP_BOUND_IF rationalization PSARC/2007/571 IP_DHCPINIT_IF socket option 4354207 dhcpagent should use sockets rather than DLPI 6533610 IP_XMIT_IF should fade from in.routed's view 6607674 DHCP client's "checkaddr" warnings have output reversed 6607676 DHCP client misreports dhcp_bound_complete as configure_bound 6609845 torch IP_XMIT_IF and complete IP_BOUND_IF 6609852 need a way to receive IP unicast DHCP traffic before lease acquisition 6609868 IP should create broadcast IREs when bringing up 0.0.0.0 6616106 IP_UNSPEC_SRC sockets should skip calls to ipif_select_source()
deleted_files/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c
deleted_files/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h
deleted_files/usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg
usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile
usr/src/cmd/cmd-inet/sbin/dhcpagent/README
usr/src/cmd/cmd-inet/sbin/dhcpagent/README.v6
usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h
usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg
usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h
usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h
usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c
usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h
usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h
usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c
usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c
usr/src/uts/common/inet/ip.h
usr/src/uts/common/inet/ip/icmp.c
usr/src/uts/common/inet/ip/icmp_opt_data.c
usr/src/uts/common/inet/ip/ip.c
usr/src/uts/common/inet/ip/ip_if.c
usr/src/uts/common/inet/ip/ip_opt_data.c
usr/src/uts/common/inet/ip_impl.h
usr/src/uts/common/inet/ipclassifier.h
usr/src/uts/common/inet/rawip_impl.h
usr/src/uts/common/inet/tcp/tcp.c
usr/src/uts/common/inet/udp/udp.c
usr/src/uts/common/inet/udp/udp_opt_data.c
usr/src/uts/common/inet/udp_impl.h
usr/src/uts/common/netinet/in.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deleted_files/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c	Tue Oct 30 11:15:43 2007 -0700
@@ -0,0 +1,343 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/pfmod.h>
+#include <sys/socket.h>
+#include <netinet/in.h>			/* in_addr (ip.h) */
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <stropts.h>
+#include <string.h>			/* strpbrk */
+#include <sys/uio.h>
+#include <stdlib.h>
+#include <dhcpmsg.h>
+
+#include "dlpi_io.h"
+#include "v4_sum_impl.h"
+#include "common.h"
+
+/*
+ * timeout to wait for acknowledgement of packet filter, in seconds.
+ */
+#define	FILTER_TIMEOUT	5
+
+/*
+ * dlpi_recvfrom(): receives data on a DLPI stream
+ *
+ *  input: dlpi_handle_t: dlpi handle to receive the data on
+ *	    void *: a buffer to store the data in
+ *	    size_t: the size of the buffer
+ *	    struct sockaddr_in *: if non-NULL, sender's IP address is filled in
+ *	    struct sockaddr_in *: if non-NULL, recipient's IP address
+ *  output: ssize_t: the number of bytes read on success, -1 on failure
+ */
+ssize_t
+dlpi_recvfrom(dlpi_handle_t dh, void *buf, size_t buflen,
+    struct sockaddr_in *from, struct sockaddr_in *to)
+{
+	struct ip		*ip;
+	struct udphdr		*udphdr;
+	void			*msgbuf;
+	size_t			msglen;
+	dlpi_recvinfo_t		dlrecv;
+	int			rc;
+
+	msglen = buflen + sizeof (struct ip) + sizeof (struct udphdr);
+	msgbuf = malloc(msglen);
+
+	if (msgbuf == NULL) {
+		dhcpmsg(MSG_ERR, "dlpi_recvfrom: cannot allocate packet");
+		return (-1);
+	}
+
+	rc = dlpi_recv(dh, NULL, NULL, msgbuf, &msglen, -1, &dlrecv);
+	if (rc != DLPI_SUCCESS) {
+		dhcpmsg(MSG_ERR, "dlpi_recvfrom: dlpi_recv failed: %s",
+		    dlpi_strerror(rc));
+		free(msgbuf);
+		return (-1);
+	}
+
+	/*
+	 * since we're just pulling data off the wire, what we have
+	 * may look nothing like a DHCP packet.  note that this
+	 * shouldn't happen (pfmod should have tossed it already).
+	 */
+	if (msglen < sizeof (struct ip) + sizeof (struct udphdr)) {
+		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped short packet");
+		free(msgbuf);
+		return (-1);
+	}
+
+	if (msglen < dlrecv.dri_totmsglen) {
+		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: discarding stray "
+		    "data on streamhead");
+	}
+
+	/*
+	 * verify checksums
+	 */
+	ip = msgbuf;
+	if (ipv4cksum((uint16_t *)ip, ip->ip_hl << 2) != 0) {
+		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped packet with bad "
+		    "ipv4 checksum");
+		free(msgbuf);
+		return (-1);
+	}
+
+	udphdr = (struct udphdr *)&ip[1];
+	if ((udphdr->uh_sum != 0) &&
+	    (udp_chksum(udphdr, &ip->ip_src, &ip->ip_dst, ip->ip_p) != 0)) {
+		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped packet with bad "
+		    "UDP checksum");
+		free(msgbuf);
+		return (-1);
+	}
+
+	msglen -= (sizeof (struct ip) + sizeof (struct udphdr));
+	(void) memcpy(buf, &udphdr[1], msglen);
+
+	if (from != NULL) {
+		from->sin_family = AF_INET;
+		from->sin_addr = ip->ip_src;
+		from->sin_port = udphdr->uh_sport;
+	}
+
+	if (to != NULL) {
+		to->sin_family = AF_INET;
+		to->sin_addr = ip->ip_dst;
+		to->sin_port = udphdr->uh_dport;
+	}
+
+	free(msgbuf);
+	return (msglen);
+}
+
+/*
+ * dlpi_sendto(): sends UDP packets on a DLPI stream
+ *
+ *  input: dlpi_handle_t: dlpi handle to send the packet on
+ *	    void *: a buffer to send
+ *	    size_t: the size of the buffer
+ *	    struct sockaddr_in *: the IP address to send the data to
+ *	    uchar_t *: the link-layer destination address
+ *	    size_t: the size of the link-layer destination address
+ *  output: ssize_t: the number of bytes sent on success, -1 on failure
+ */
+ssize_t
+dlpi_sendto(dlpi_handle_t dh, void *buf, size_t buflen,
+    struct sockaddr_in *to, uchar_t *dl_to, size_t dl_to_len)
+{
+	struct ip		*ip;
+	struct udphdr		*udphdr;
+	void			*msgbuf;
+	size_t			msglen;
+	static uint16_t		ip_id = 0;
+	int			rc;
+
+	/*
+	 * TODO: someday we might want to support `to' not being
+	 * the same as INADDR_BROADCAST.  we don't need the support
+	 * right now, but it's annoying to have a general interface
+	 * that only supports a specific function.
+	 */
+	if (to->sin_addr.s_addr != htonl(INADDR_BROADCAST)) {
+		dhcpmsg(MSG_ERROR, "dlpi_sendto: send to unicast address");
+		return (-1);
+	}
+
+	/*
+	 * we allocate one extra byte here in case the UDP checksum
+	 * routine needs it to get the packet length to be even.
+	 */
+	msglen = sizeof (struct ip) + sizeof (struct udphdr) + buflen;
+	msgbuf = calloc(1, msglen + 1);
+	if (msgbuf == NULL) {
+		dhcpmsg(MSG_ERR, "dlpi_sendto: cannot allocate packet");
+		return (-1);
+	}
+
+	ip	= (struct ip *)msgbuf;
+	udphdr	= (struct udphdr *)&ip[1];
+
+	(void) memcpy(&udphdr[1], buf, buflen);
+
+	/*
+	 * build the ipv4 header.  assume that our source address is 0
+	 * (since we wouldn't be using DLPI if we could actually send
+	 * packets an easier way).  note that we only need to set nonzero
+	 * fields since we got calloc()'d memory above.
+	 */
+
+	/*
+	 * From a purist's perspective, we should set the TTL to 1 for
+	 * limited broadcasts. But operational experience (cisco routers)
+	 * has shown that doing so results in the relay agent dropping our
+	 * packets. These same devices (ciscos) also don't set the TTL
+	 * to MAXTTL on the unicast side of the relay agent. Thus, the only
+	 * safe thing to do is to always set the ttl to MAXTTL. Sigh.
+	 */
+
+	ip->ip_ttl	  = MAXTTL;
+
+	ip->ip_v	  = 4;
+	ip->ip_hl	  = sizeof (struct ip) / 4;
+	ip->ip_id	  = htons(ip_id++);
+	ip->ip_off	  = htons(IP_DF);
+	ip->ip_p	  = IPPROTO_UDP;
+	ip->ip_len	  = htons(msglen);
+	ip->ip_dst	  = to->sin_addr;
+	ip->ip_src.s_addr = htonl(INADDR_ANY);
+	ip->ip_sum	  = ipv4cksum((uint16_t *)ip, sizeof (struct ip));
+
+	udphdr->uh_ulen	  = htons(sizeof (struct udphdr) + buflen);
+	udphdr->uh_sport  = htons(IPPORT_BOOTPC);
+	udphdr->uh_dport  = htons(IPPORT_BOOTPS);
+	udphdr->uh_sum = udp_chksum(udphdr, &ip->ip_src, &ip->ip_dst, ip->ip_p);
+
+	rc = dlpi_send(dh, dl_to, dl_to_len, msgbuf, msglen, NULL);
+	if (rc != DLPI_SUCCESS) {
+		free(msgbuf);
+		dhcpmsg(MSG_ERR, "dlpi_sendto: dlpi_send: %s",
+		    dlpi_strerror(rc));
+		return (-1);
+	}
+
+	free(msgbuf);
+	return (buflen);
+}
+
+/*
+ * set_packet_filter(): sets the current packet filter on a DLPI stream
+ *
+ *   input: dlpi_handle_t: the DLPI handle to set the packet filter on
+ *	    filter_func_t *: the filter to use
+ *	    void *: an argument to pass to the filter function
+ *	    const char *: a text description of the filter's purpose
+ *  output: boolean_t: B_TRUE on success, B_FALSE on failure.
+ */
+boolean_t
+set_packet_filter(dlpi_handle_t dh, filter_func_t *filter, void *arg,
+    const char *filter_name)
+{
+	struct strioctl		sioc;
+	struct packetfilt	pf;
+	ushort_t		*pfp = pf.Pf_Filter;
+	int			fd = dlpi_fd(dh);
+
+	if (ioctl(fd, I_PUSH, "pfmod") == -1) {
+		dhcpmsg(MSG_ERR,
+		    "open_dlpi_pif: cannot push pfmod on stream");
+		return (B_FALSE);
+	}
+
+	pf.Pf_FilterLen = filter(pfp, arg) - pf.Pf_Filter;
+
+	sioc.ic_cmd	= PFIOCSETF;
+	sioc.ic_timout	= FILTER_TIMEOUT;
+	sioc.ic_len	= sizeof (struct packetfilt);
+	sioc.ic_dp	= (caddr_t)&pf;
+
+	/*
+	 * if this ioctl() fails, we're really hosed.  the best we can
+	 * really do is play on.
+	 */
+
+	if (ioctl(fd, I_STR, &sioc) == -1)
+		dhcpmsg(MSG_ERR, "set_packet_filter: PFIOCSETF");
+	else
+		dhcpmsg(MSG_DEBUG, "set_packet_filter: set filter %p "
+		    "(%s filter)", (void *)filter, filter_name);
+
+	/*
+	 * clean out any potential cruft on the descriptor that
+	 * appeared before we were able to set the filter
+	 */
+
+	(void) ioctl(fd, I_FLUSH, FLUSHR);
+
+	return (B_TRUE);
+}
+
+/*
+ * dhcp_filter(): builds a packet filter that permits only DHCP/BOOTP messages
+ *
+ *   input: ushort_t *: a place to store the packet filter code
+ *	    void *: not used
+ *  output: ushort_t *: two bytes past the last byte in the packet filter
+ */
+
+/* ARGSUSED */
+ushort_t *
+dhcp_filter(ushort_t *pfp, void *arg)
+{
+	/*
+	 * only pass up UDP packets -- 8th byte is the ttl/proto field
+	 */
+
+	*pfp++ = ENF_PUSHWORD + 4;
+	*pfp++ = ENF_PUSHLIT | ENF_AND;
+	*pfp++ = htons(0xff);
+	*pfp++ = ENF_PUSHLIT | ENF_CAND;
+	*pfp++ = htons(IPPROTO_UDP);
+
+	/*
+	 * make sure the IP packet doesn't have any options.  2nd
+	 * nibble is the header length field.
+	 * TODO: if we decide to handle options, this code goes away.
+	 */
+
+	*pfp++ = ENF_PUSHWORD + 0;
+	*pfp++ = ENF_PUSHLIT | ENF_AND;
+	*pfp++ = htons(0x0f00);			/* only care about 2nd nibble */
+	*pfp++ = ENF_PUSHLIT | ENF_CAND;
+	*pfp++ = htons(0x0500);			/* which should be 5 * 4 = 20 */
+
+	/*
+	 * if there's a fragment offset, or if the IP_MF bit is lit,
+	 * pitch the packet.  this  pitches all fragments.
+	 * TODO: if we decide to handle fragments, this code goes away.
+	 */
+
+	*pfp++ = ENF_PUSHWORD + 3;
+	*pfp++ = ENF_PUSHLIT | ENF_AND;
+	*pfp++ = htons(0x1fff | IP_MF);
+	*pfp++ = ENF_PUSHZERO | ENF_CAND;
+
+	/*
+	 * make sure the packet is for the DHCP client port -- 22nd
+	 * byte is the UDP port number.
+	 */
+
+	*pfp++ = ENF_PUSHWORD + 11;
+	*pfp++ = ENF_PUSHLIT | ENF_CAND;
+	*pfp++ = htons(IPPORT_BOOTPC);
+
+	return (pfp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deleted_files/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h	Tue Oct 30 11:15:43 2007 -0700
@@ -0,0 +1,59 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	DLPI_IO_H
+#define	DLPI_IO_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <libdlpi.h>
+
+/*
+ * dlpi_io.[ch] contain the interface the agent uses to interact with
+ * DLPI for data transfer primitives. see dlpi_io.c for documentation
+ * on how to use the exported functions.
+ */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef ushort_t *filter_func_t(ushort_t *, void *);
+
+filter_func_t	dhcp_filter;
+boolean_t	set_packet_filter(dlpi_handle_t, filter_func_t *, void *,
+		    const char *);
+ssize_t		dlpi_recvfrom(dlpi_handle_t, void *, size_t,
+		    struct sockaddr_in *, struct sockaddr_in *);
+ssize_t		dlpi_sendto(dlpi_handle_t, void *, size_t, struct sockaddr_in *,
+		    uchar_t *, size_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* DLPI_IO_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deleted_files/usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg	Tue Oct 30 11:15:43 2007 -0700
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License").  You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+find_files "s.*" usr/src/common/net/dhcp
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/Makefile	Tue Oct 30 11:15:43 2007 -0700
@@ -29,26 +29,21 @@
 ROOTFS_PROG = $(PROG)
 DEFAULTFILES = dhcpagent.dfl
 
-LOCOBJS = adopt.o agent.o async.o bound.o class_id.o defaults.o   \
-	  dlpi_io.o inform.o init_reboot.o interface.o ipc_action.o \
-	  packet.o release.o renew.o request.o script_handler.o select.o \
-	  states.o util.o
-COMDIR  = $(SRC)/common/net/dhcp
-COMOBJS = ipv4_sum.o udp_sum.o
+OBJS =	adopt.o agent.o async.o bound.o class_id.o defaults.o inform.o \
+	init_reboot.o interface.o ipc_action.o packet.o release.o renew.o \
+	request.o script_handler.o select.o states.o util.o
 
 include ../../../Makefile.cmd
 
-OBJS    = $(COMOBJS) $(LOCOBJS)
-SRCS    = $(COMOBJS:%.o=$(COMDIR)/%.c) $(LOCOBJS:%.o=%.c)
-
-POFILES = $(LOCOBJS:%.o=%.po)
+SRCS    = $(OBJS:%.o=%.c)
+POFILES = $(OBJS:%.o=%.po)
 XGETFLAGS += -a -x dhcpagent.xcl
 
 #
 # to compile a debug version, do a `make COPTFLAG="-g -XO0"'
 #
 
-CPPFLAGS  += -I$(COMDIR) -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
+CPPFLAGS  += -D_XOPEN_SOURCE=500 -D__EXTENSIONS__
 LDLIBS    += -lxnet -lnvpair -ldhcpagent -ldhcputil -linetutil -ldevinfo -ldlpi
 
 # Disable warnings that affect all XPG applications.
@@ -64,10 +59,6 @@
 		$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
 		$(POST_PROCESS)
 
-%.o:		$(COMDIR)/%.c
-		$(COMPILE.c) $(OUTPUT_OPTION) $<
-		$(POST_PROCESS_O)
-
 $(POFILE):	$(POFILES)
 		$(RM) $@; $(CAT) $(POFILES) > $@; $(RM) $(POFILES)
 
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/README	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/README	Tue Oct 30 11:15:43 2007 -0700
@@ -81,8 +81,8 @@
 	  difference between this and the first group is that the
 	  interfaces exported from these files do not operate on
 	  an "object", but rather perform a specific task.  Examples
-	  include "dlpi_io.c", which provides a useful interface
-	  to DLPI-related i/o operations.
+	  include "defaults.c", which provides a useful interface
+	  to /etc/default/dhcpagent file operations.
 
 OVERVIEW
 ========
@@ -306,9 +306,7 @@
 the DHCP packet.  Finally, send_pkt() and send_pkt_v6() can be used to
 transmit the packet to a given IP address.
 
-The send_pkt() function is actually quite complicated; for one, it
-must internally use either DLPI or sockets depending on the machine
-state; for another, it handles the details of packet timeout and
+The send_pkt() function handles the details of packet timeout and
 retransmission.  The last argument to send_pkt() is a pointer to a
 "stop function."  If this argument is passed as NULL, then the packet
 will only be sent once (it won't be retransmitted).  Otherwise, before
@@ -325,24 +323,20 @@
 
 The recv_pkt() function is simpler but still complicated by the fact
 that one may want to receive several different types of packets at
-once and in different ways (DLPI or sockets).  The caller registers an
-event handler on the file descriptor, and then calls recv_pkt() to
-read in the packet along with meta information about the message (the
-sender and interface identifier).
-
+once.  The caller registers an event handler on the file descriptor,
+and then calls recv_pkt() to read in the packet along with meta
+information about the message (the sender and interface identifier).
+				
 For IPv6, packet reception is done with a single socket, using
 IPV6_PKTINFO to determine the actual destination address and receiving
 interface.  Packets are then matched against the state machines on the
 given interface through the transaction ID.
 
-The same facility exists for inbound IPv4 packets, but because there's
-no IP_PKTINFO processing on output yet in Solaris, and because IPv4
-still relies on DLPI, DHCP packets are handled on a per-LIF (when
-bound) and per-PIF (when unbound) basis.  Eventually, when IP_PKTINFO
-is available for IPv4, the per-LIF sockets can go away.  If it ever
-becomes possible to send and receive IP packets without having an IP
-address configured on an interface, then the DLPI streams can go as
-well.
+For IPv4, due to oddities in the DHCP specification (discussed in
+PSARC/2007/571), a special IP_DHCPINIT_IF socket option must be used
+to allow unicast DHCP traffic to be received on an interface during
+lease acquisition.  Since the IP_DHCPINIT_IF socket option can only
+enable one interface at a time, one socket must be used per interface.
 
 Time
 ----
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/README.v6	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/README.v6	Tue Oct 30 11:15:43 2007 -0700
@@ -22,6 +22,15 @@
 
 ident	"%Z%%M%	%I%	%E% SMI"
 
+
+**  PLEASE NOTE:
+**
+**  This document discusses aspects of the DHCPv4 client design that have
+**  since changed (e.g., DLPI is no longer used).  However, since those
+**  aspects affected the DHCPv6 design, the discussion has been left for
+**  historical record.
+
+
 DHCPv6 Client Low-Level Design
 
 Introduction
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/agent.c	Tue Oct 30 11:15:43 2007 -0700
@@ -1163,6 +1163,7 @@
 		    lif->lif_name);
 		lif_mark_decline(lif, "duplicate address");
 		close_ip_lif(lif);
+		(void) open_ip_lif(lif, INADDR_ANY);
 	}
 
 	dad_wait = lif->lif_dad_wait;
@@ -1404,20 +1405,8 @@
 		 * remove them from the list.  Any leases that become empty are
 		 * also removed as part of the decline-generation process.
 		 */
-		if (dsmp->dsm_lif_down != 0) {
-			/*
-			 * We need to switch back to PRE_BOUND state so that
-			 * send_pkt_internal() uses DLPI instead of sockets.
-			 * Our logical interface has already been torn down by
-			 * the kernel, and thus we can't send DHCPDECLINE by
-			 * way of regular IP.  (Unless we're adopting -- allow
-			 * the grandparent to be handled as expected.)
-			 */
-			if (oldstate != ADOPTING) {
-				(void) set_smach_state(dsmp, PRE_BOUND);
-			}
+		if (dsmp->dsm_lif_down != 0)
 			send_declines(dsmp);
-		}
 
 		if (dsmp->dsm_leases == NULL) {
 			dsmp->dsm_bad_offers++;
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/bound.c	Tue Oct 30 11:15:43 2007 -0700
@@ -337,7 +337,7 @@
 	 */
 	if (dsmp->dsm_isv6) {
 		(void) set_smach_state(dsmp, BOUND);
-		dhcpmsg(MSG_DEBUG, "configure_bound: bound %s",
+		dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s",
 		    dsmp->dsm_name);
 		(void) script_start(dsmp, EVENT_BOUND6, bound_event_cb, NULL,
 		    NULL);
@@ -363,7 +363,7 @@
 		dsmp->dsm_nrouters = router_list->len / sizeof (ipaddr_t);
 		dsmp->dsm_routers  = malloc(router_list->len);
 		if (dsmp->dsm_routers == NULL) {
-			dhcpmsg(MSG_ERR, "configure_bound: cannot allocate "
+			dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot allocate "
 			    "default router list, ignoring default routers");
 			dsmp->dsm_nrouters = 0;
 		}
@@ -376,8 +376,8 @@
 
 			if (!add_default_route(lif->lif_pif->pif_index,
 			    &dsmp->dsm_routers[i])) {
-				dhcpmsg(MSG_ERR, "configure_bound: cannot add "
-				    "default router %s on %s", inet_ntoa(
+				dhcpmsg(MSG_ERR, "dhcp_bound_complete: cannot "
+				    "add default router %s on %s", inet_ntoa(
 				    dsmp->dsm_routers[i]), dsmp->dsm_name);
 				dsmp->dsm_routers[i].s_addr = htonl(INADDR_ANY);
 				continue;
@@ -391,12 +391,12 @@
 	oldstate = dsmp->dsm_state;
 	if (!set_smach_state(dsmp, BOUND)) {
 		dhcpmsg(MSG_ERR,
-		    "configure_bound: cannot set bound state on %s",
+		    "dhcp_bound_complete: cannot set bound state on %s",
 		    dsmp->dsm_name);
 		return;
 	}
 
-	dhcpmsg(MSG_DEBUG, "configure_bound: bound %s", dsmp->dsm_name);
+	dhcpmsg(MSG_DEBUG, "dhcp_bound_complete: bound %s", dsmp->dsm_name);
 
 	/*
 	 * We're now committed to this binding, so if it came from BOOTP, set
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.c	Tue Oct 30 09:32:44 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,343 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/pfmod.h>
-#include <sys/socket.h>
-#include <netinet/in.h>			/* in_addr (ip.h) */
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-#include <stropts.h>
-#include <string.h>			/* strpbrk */
-#include <sys/uio.h>
-#include <stdlib.h>
-#include <dhcpmsg.h>
-
-#include "dlpi_io.h"
-#include "v4_sum_impl.h"
-#include "common.h"
-
-/*
- * timeout to wait for acknowledgement of packet filter, in seconds.
- */
-#define	FILTER_TIMEOUT	5
-
-/*
- * dlpi_recvfrom(): receives data on a DLPI stream
- *
- *  input: dlpi_handle_t: dlpi handle to receive the data on
- *	    void *: a buffer to store the data in
- *	    size_t: the size of the buffer
- *	    struct sockaddr_in *: if non-NULL, sender's IP address is filled in
- *	    struct sockaddr_in *: if non-NULL, recipient's IP address
- *  output: ssize_t: the number of bytes read on success, -1 on failure
- */
-ssize_t
-dlpi_recvfrom(dlpi_handle_t dh, void *buf, size_t buflen,
-    struct sockaddr_in *from, struct sockaddr_in *to)
-{
-	struct ip		*ip;
-	struct udphdr		*udphdr;
-	void			*msgbuf;
-	size_t			msglen;
-	dlpi_recvinfo_t		dlrecv;
-	int			rc;
-
-	msglen = buflen + sizeof (struct ip) + sizeof (struct udphdr);
-	msgbuf = malloc(msglen);
-
-	if (msgbuf == NULL) {
-		dhcpmsg(MSG_ERR, "dlpi_recvfrom: cannot allocate packet");
-		return (-1);
-	}
-
-	rc = dlpi_recv(dh, NULL, NULL, msgbuf, &msglen, -1, &dlrecv);
-	if (rc != DLPI_SUCCESS) {
-		dhcpmsg(MSG_ERR, "dlpi_recvfrom: dlpi_recv failed: %s",
-		    dlpi_strerror(rc));
-		free(msgbuf);
-		return (-1);
-	}
-
-	/*
-	 * since we're just pulling data off the wire, what we have
-	 * may look nothing like a DHCP packet.  note that this
-	 * shouldn't happen (pfmod should have tossed it already).
-	 */
-	if (msglen < sizeof (struct ip) + sizeof (struct udphdr)) {
-		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped short packet");
-		free(msgbuf);
-		return (-1);
-	}
-
-	if (msglen < dlrecv.dri_totmsglen) {
-		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: discarding stray "
-		    "data on streamhead");
-	}
-
-	/*
-	 * verify checksums
-	 */
-	ip = msgbuf;
-	if (ipv4cksum((uint16_t *)ip, ip->ip_hl << 2) != 0) {
-		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped packet with bad "
-		    "ipv4 checksum");
-		free(msgbuf);
-		return (-1);
-	}
-
-	udphdr = (struct udphdr *)&ip[1];
-	if ((udphdr->uh_sum != 0) &&
-	    (udp_chksum(udphdr, &ip->ip_src, &ip->ip_dst, ip->ip_p) != 0)) {
-		dhcpmsg(MSG_WARNING, "dlpi_recvfrom: dropped packet with bad "
-		    "UDP checksum");
-		free(msgbuf);
-		return (-1);
-	}
-
-	msglen -= (sizeof (struct ip) + sizeof (struct udphdr));
-	(void) memcpy(buf, &udphdr[1], msglen);
-
-	if (from != NULL) {
-		from->sin_family = AF_INET;
-		from->sin_addr = ip->ip_src;
-		from->sin_port = udphdr->uh_sport;
-	}
-
-	if (to != NULL) {
-		to->sin_family = AF_INET;
-		to->sin_addr = ip->ip_dst;
-		to->sin_port = udphdr->uh_dport;
-	}
-
-	free(msgbuf);
-	return (msglen);
-}
-
-/*
- * dlpi_sendto(): sends UDP packets on a DLPI stream
- *
- *  input: dlpi_handle_t: dlpi handle to send the packet on
- *	    void *: a buffer to send
- *	    size_t: the size of the buffer
- *	    struct sockaddr_in *: the IP address to send the data to
- *	    uchar_t *: the link-layer destination address
- *	    size_t: the size of the link-layer destination address
- *  output: ssize_t: the number of bytes sent on success, -1 on failure
- */
-ssize_t
-dlpi_sendto(dlpi_handle_t dh, void *buf, size_t buflen,
-    struct sockaddr_in *to, uchar_t *dl_to, size_t dl_to_len)
-{
-	struct ip		*ip;
-	struct udphdr		*udphdr;
-	void			*msgbuf;
-	size_t			msglen;
-	static uint16_t		ip_id = 0;
-	int			rc;
-
-	/*
-	 * TODO: someday we might want to support `to' not being
-	 * the same as INADDR_BROADCAST.  we don't need the support
-	 * right now, but it's annoying to have a general interface
-	 * that only supports a specific function.
-	 */
-	if (to->sin_addr.s_addr != htonl(INADDR_BROADCAST)) {
-		dhcpmsg(MSG_ERROR, "dlpi_sendto: send to unicast address");
-		return (-1);
-	}
-
-	/*
-	 * we allocate one extra byte here in case the UDP checksum
-	 * routine needs it to get the packet length to be even.
-	 */
-	msglen = sizeof (struct ip) + sizeof (struct udphdr) + buflen;
-	msgbuf = calloc(1, msglen + 1);
-	if (msgbuf == NULL) {
-		dhcpmsg(MSG_ERR, "dlpi_sendto: cannot allocate packet");
-		return (-1);
-	}
-
-	ip	= (struct ip *)msgbuf;
-	udphdr	= (struct udphdr *)&ip[1];
-
-	(void) memcpy(&udphdr[1], buf, buflen);
-
-	/*
-	 * build the ipv4 header.  assume that our source address is 0
-	 * (since we wouldn't be using DLPI if we could actually send
-	 * packets an easier way).  note that we only need to set nonzero
-	 * fields since we got calloc()'d memory above.
-	 */
-
-	/*
-	 * From a purist's perspective, we should set the TTL to 1 for
-	 * limited broadcasts. But operational experience (cisco routers)
-	 * has shown that doing so results in the relay agent dropping our
-	 * packets. These same devices (ciscos) also don't set the TTL
-	 * to MAXTTL on the unicast side of the relay agent. Thus, the only
-	 * safe thing to do is to always set the ttl to MAXTTL. Sigh.
-	 */
-
-	ip->ip_ttl	  = MAXTTL;
-
-	ip->ip_v	  = 4;
-	ip->ip_hl	  = sizeof (struct ip) / 4;
-	ip->ip_id	  = htons(ip_id++);
-	ip->ip_off	  = htons(IP_DF);
-	ip->ip_p	  = IPPROTO_UDP;
-	ip->ip_len	  = htons(msglen);
-	ip->ip_dst	  = to->sin_addr;
-	ip->ip_src.s_addr = htonl(INADDR_ANY);
-	ip->ip_sum	  = ipv4cksum((uint16_t *)ip, sizeof (struct ip));
-
-	udphdr->uh_ulen	  = htons(sizeof (struct udphdr) + buflen);
-	udphdr->uh_sport  = htons(IPPORT_BOOTPC);
-	udphdr->uh_dport  = htons(IPPORT_BOOTPS);
-	udphdr->uh_sum = udp_chksum(udphdr, &ip->ip_src, &ip->ip_dst, ip->ip_p);
-
-	rc = dlpi_send(dh, dl_to, dl_to_len, msgbuf, msglen, NULL);
-	if (rc != DLPI_SUCCESS) {
-		free(msgbuf);
-		dhcpmsg(MSG_ERR, "dlpi_sendto: dlpi_send: %s",
-		    dlpi_strerror(rc));
-		return (-1);
-	}
-
-	free(msgbuf);
-	return (buflen);
-}
-
-/*
- * set_packet_filter(): sets the current packet filter on a DLPI stream
- *
- *   input: dlpi_handle_t: the DLPI handle to set the packet filter on
- *	    filter_func_t *: the filter to use
- *	    void *: an argument to pass to the filter function
- *	    const char *: a text description of the filter's purpose
- *  output: boolean_t: B_TRUE on success, B_FALSE on failure.
- */
-boolean_t
-set_packet_filter(dlpi_handle_t dh, filter_func_t *filter, void *arg,
-    const char *filter_name)
-{
-	struct strioctl		sioc;
-	struct packetfilt	pf;
-	ushort_t		*pfp = pf.Pf_Filter;
-	int			fd = dlpi_fd(dh);
-
-	if (ioctl(fd, I_PUSH, "pfmod") == -1) {
-		dhcpmsg(MSG_ERR,
-		    "open_dlpi_pif: cannot push pfmod on stream");
-		return (B_FALSE);
-	}
-
-	pf.Pf_FilterLen = filter(pfp, arg) - pf.Pf_Filter;
-
-	sioc.ic_cmd	= PFIOCSETF;
-	sioc.ic_timout	= FILTER_TIMEOUT;
-	sioc.ic_len	= sizeof (struct packetfilt);
-	sioc.ic_dp	= (caddr_t)&pf;
-
-	/*
-	 * if this ioctl() fails, we're really hosed.  the best we can
-	 * really do is play on.
-	 */
-
-	if (ioctl(fd, I_STR, &sioc) == -1)
-		dhcpmsg(MSG_ERR, "set_packet_filter: PFIOCSETF");
-	else
-		dhcpmsg(MSG_DEBUG, "set_packet_filter: set filter %p "
-		    "(%s filter)", (void *)filter, filter_name);
-
-	/*
-	 * clean out any potential cruft on the descriptor that
-	 * appeared before we were able to set the filter
-	 */
-
-	(void) ioctl(fd, I_FLUSH, FLUSHR);
-
-	return (B_TRUE);
-}
-
-/*
- * dhcp_filter(): builds a packet filter that permits only DHCP/BOOTP messages
- *
- *   input: ushort_t *: a place to store the packet filter code
- *	    void *: not used
- *  output: ushort_t *: two bytes past the last byte in the packet filter
- */
-
-/* ARGSUSED */
-ushort_t *
-dhcp_filter(ushort_t *pfp, void *arg)
-{
-	/*
-	 * only pass up UDP packets -- 8th byte is the ttl/proto field
-	 */
-
-	*pfp++ = ENF_PUSHWORD + 4;
-	*pfp++ = ENF_PUSHLIT | ENF_AND;
-	*pfp++ = htons(0xff);
-	*pfp++ = ENF_PUSHLIT | ENF_CAND;
-	*pfp++ = htons(IPPROTO_UDP);
-
-	/*
-	 * make sure the IP packet doesn't have any options.  2nd
-	 * nibble is the header length field.
-	 * TODO: if we decide to handle options, this code goes away.
-	 */
-
-	*pfp++ = ENF_PUSHWORD + 0;
-	*pfp++ = ENF_PUSHLIT | ENF_AND;
-	*pfp++ = htons(0x0f00);			/* only care about 2nd nibble */
-	*pfp++ = ENF_PUSHLIT | ENF_CAND;
-	*pfp++ = htons(0x0500);			/* which should be 5 * 4 = 20 */
-
-	/*
-	 * if there's a fragment offset, or if the IP_MF bit is lit,
-	 * pitch the packet.  this  pitches all fragments.
-	 * TODO: if we decide to handle fragments, this code goes away.
-	 */
-
-	*pfp++ = ENF_PUSHWORD + 3;
-	*pfp++ = ENF_PUSHLIT | ENF_AND;
-	*pfp++ = htons(0x1fff | IP_MF);
-	*pfp++ = ENF_PUSHZERO | ENF_CAND;
-
-	/*
-	 * make sure the packet is for the DHCP client port -- 22nd
-	 * byte is the UDP port number.
-	 */
-
-	*pfp++ = ENF_PUSHWORD + 11;
-	*pfp++ = ENF_PUSHLIT | ENF_CAND;
-	*pfp++ = htons(IPPORT_BOOTPC);
-
-	return (pfp);
-}
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/dlpi_io.h	Tue Oct 30 09:32:44 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef	DLPI_IO_H
-#define	DLPI_IO_H
-
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-#include <netinet/in.h>
-#include <sys/types.h>
-#include <libdlpi.h>
-
-/*
- * dlpi_io.[ch] contain the interface the agent uses to interact with
- * DLPI for data transfer primitives. see dlpi_io.c for documentation
- * on how to use the exported functions.
- */
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-typedef ushort_t *filter_func_t(ushort_t *, void *);
-
-filter_func_t	dhcp_filter;
-boolean_t	set_packet_filter(dlpi_handle_t, filter_func_t *, void *,
-		    const char *);
-ssize_t		dlpi_recvfrom(dlpi_handle_t, void *, size_t,
-		    struct sockaddr_in *, struct sockaddr_in *);
-ssize_t		dlpi_sendto(dlpi_handle_t, void *, size_t, struct sockaddr_in *,
-		    uchar_t *, size_t);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* DLPI_IO_H */
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/inc.flg	Tue Oct 30 09:32:44 2007 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-#!/bin/sh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License").  You may not use this file except in compliance
-# with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-#
-#ident	"%Z%%M%	%I%	%E% SMI"
-#
-# Copyright (c) 1999 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-
-find_files "s.*" usr/src/common/net/dhcp
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c	Tue Oct 30 11:15:43 2007 -0700
@@ -41,12 +41,10 @@
 #include <arpa/inet.h>
 #include <dhcpmsg.h>
 #include <dhcp_inittab.h>
-#include <stropts.h>
 
 #include "agent.h"
 #include "interface.h"
 #include "util.h"
-#include "dlpi_io.h"
 #include "packet.h"
 #include "states.h"
 
@@ -80,6 +78,7 @@
 {
 	dhcp_pif_t *pif;
 	struct lifreq lifr;
+	dlpi_handle_t dh = NULL;
 
 	if ((pif = calloc(1, sizeof (*pif))) == NULL) {
 		dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for "
@@ -89,8 +88,6 @@
 	}
 
 	pif->pif_isv6 = isv6;
-	pif->pif_dlpi_hd = NULL;
-	pif->pif_dlpi_id = -1;
 	pif->pif_hold_count = 1;
 	pif->pif_running = B_TRUE;
 
@@ -104,17 +101,14 @@
 	/* We do not use DLPI with DHCPv6 */
 	if (!isv6) {
 		int			rc;
-		dlpi_handle_t		dh;
 		dlpi_info_t		dlinfo;
 
 		/*
 		 * Do the allocations necessary for IPv4 DHCP.
 		 *
 		 *  1. open the interface using DLPI
-		 *  2. get the interface max SDU
-		 *  3. get the interface hardware type and hardware length
-		 *  4. get the interface hardware address
-		 *  5. get the interface hardware broadcast address
+		 *  2. get the interface hardware type and hardware length
+		 *  3. get the interface hardware address
 		 */
 
 		/* step 1 */
@@ -124,7 +118,6 @@
 			*error = DHCP_IPC_E_INVIF;
 			goto failure;
 		}
-		pif->pif_dlpi_hd = dh;
 
 		if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s",
@@ -134,7 +127,7 @@
 		}
 
 		/* step 2 */
-		rc = dlpi_info(pif->pif_dlpi_hd, &dlinfo, 0);
+		rc = dlpi_info(dh, &dlinfo, 0);
 		if (rc != DLPI_SUCCESS) {
 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s",
 			    dlpi_strerror(rc));
@@ -142,25 +135,13 @@
 			goto failure;
 		}
 
-		pif->pif_max = dlinfo.di_max_sdu;
-		if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
-			dhcpmsg(MSG_ERROR, "insert_pif: %s does not have a "
-			    "large enough maximum SDU to support DHCP "
-			    "(%u < %u)", pname, pif->pif_max,
-			    DHCP_DEF_MAX_SIZE);
-			*error = DHCP_IPC_E_INVIF;
-			goto failure;
-		}
-
-		/* step 3 */
 		pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype);
 		pif->pif_hwlen  = dlinfo.di_physaddrlen;
 
-		dhcpmsg(MSG_DEBUG, "insert_pif: %s: sdumax %u, hwtype %d, "
-		    "hwlen %d", pname, pif->pif_max, pif->pif_hwtype,
-		    pif->pif_hwlen);
+		dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d",
+		    pname, pif->pif_hwtype, pif->pif_hwlen);
 
-		/* step 4 */
+		/* step 3 */
 		if (pif->pif_hwlen > 0) {
 			pif->pif_hwaddr = malloc(pif->pif_hwlen);
 			if (pif->pif_hwaddr == NULL) {
@@ -169,29 +150,12 @@
 				*error = DHCP_IPC_E_MEMORY;
 				goto failure;
 			}
+			(void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
+			    pif->pif_hwlen);
 		}
 
-		(void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
-		    pif->pif_hwlen);
-
-		/*
-		 * step 5
-		 * Some media types has no broadcast address.
-		 */
-		if ((pif->pif_dlen = dlinfo.di_bcastaddrlen) != 0) {
-			pif->pif_daddr = malloc(pif->pif_dlen);
-			if (pif->pif_daddr == NULL) {
-				dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
-				    "pif_daddr for %s", pname);
-				*error = DHCP_IPC_E_MEMORY;
-				goto failure;
-			}
-		}
-		(void) memcpy(pif->pif_daddr, dlinfo.di_bcastaddr,
-		    pif->pif_dlen);
-
-		/* Close the DLPI stream until actually needed */
-		close_dlpi_pif(pif);
+		dlpi_close(dh);
+		dh = NULL;
 	}
 
 	/*
@@ -202,20 +166,34 @@
 	(void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ);
 
 	if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) {
-		if (errno == ENXIO)
-			*error = DHCP_IPC_E_INVIF;
-		else
-			*error = DHCP_IPC_E_INT;
+		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname);
 		goto failure;
 	}
 	pif->pif_index = lifr.lifr_index;
 
+	if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFMTU, &lifr) == -1) {
+		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
+		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname);
+		goto failure;
+	}
+	pif->pif_max = lifr.lifr_mtu;
+
+	if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
+		dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to "
+		    "support DHCP (%u < %u)", pname, pif->pif_max,
+		    DHCP_DEF_MAX_SIZE);
+		*error = DHCP_IPC_E_INVIF;
+		goto failure;
+	}
+
 	insque(pif, isv6 ? &v6root : &v4root);
 
 	return (pif);
 
 failure:
+	if (dh != NULL)
+		dlpi_close(dh);
 	release_pif(pif);
 	return (NULL);
 }
@@ -256,10 +234,7 @@
 		    pif->pif_name);
 
 		remque(pif);
-		pif->pif_dlpi_count = 1;
-		close_dlpi_pif(pif);
 		free(pif->pif_hwaddr);
-		free(pif->pif_daddr);
 		free(pif);
 	} else {
 		dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
@@ -343,80 +318,9 @@
 }
 
 /*
- * open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening
- *		    the connection if necessary.
- *
- *   input: dhcp_pif_t *: the physical interface on which to use DLPI
- *  output: boolean_t: B_TRUE on success, B_FALSE on failure.
- */
-
-boolean_t
-open_dlpi_pif(dhcp_pif_t *pif)
-{
-	int		rc;
-	dlpi_handle_t	dh;
-
-	if (pif->pif_dlpi_hd == NULL) {
-		if ((rc = dlpi_open(pif->pif_name, &dh, 0)) != DLPI_SUCCESS) {
-			dhcpmsg(MSG_ERROR, "open_dlpi_pif: dlpi_open: %s",
-			    dlpi_strerror(rc));
-			return (B_FALSE);
-		}
-
-		if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
-			dhcpmsg(MSG_ERROR, "open_dlpi_pif: dlpi_bind: %s",
-			    dlpi_strerror(rc));
-			dlpi_close(dh);
-			return (B_FALSE);
-		}
-
-		if (!(set_packet_filter(dh, dhcp_filter, NULL, "DHCP"))) {
-			dlpi_close(dh);
-			return (B_FALSE);
-		}
-		pif->pif_dlpi_id = iu_register_event(eh, dlpi_fd(dh), POLLIN,
-		    dhcp_collect_dlpi, pif);
-		if (pif->pif_dlpi_id == -1) {
-			dlpi_close(dh);
-			return (B_FALSE);
-		}
-
-		pif->pif_dlpi_hd = dh;
-	}
-	pif->pif_dlpi_count++;
-	return (B_TRUE);
-}
-
-/*
- * close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing
- *		     the connection if this was the last user.
- *
- *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
- *  output: none
- */
-
-void
-close_dlpi_pif(dhcp_pif_t *pif)
-{
-	if (pif->pif_dlpi_count > 1) {
-		pif->pif_dlpi_count--;
-		return;
-	}
-	pif->pif_dlpi_count = 0;
-	if (pif->pif_dlpi_id != -1) {
-		(void) iu_unregister_event(eh, pif->pif_dlpi_id, NULL);
-		pif->pif_dlpi_id = -1;
-	}
-	if (pif->pif_dlpi_hd != NULL) {
-		dlpi_close(pif->pif_dlpi_hd);
-		pif->pif_dlpi_hd = NULL;
-	}
-}
-
-/*
  * pif_status(): update the physical interface up/down status.
  *
- *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
+ *   input: dhcp_pif_t *: the physical interface to be updated
  *	    boolean_t: B_TRUE if the interface is going up
  *  output: none
  */
@@ -478,7 +382,7 @@
 	}
 
 	lif->lif_sock_ip_fd = -1;
-	lif->lif_acknak_id = -1;
+	lif->lif_packet_id = -1;
 	lif->lif_iaid_id = -1;
 	lif->lif_hold_count = 1;
 	lif->lif_pif = pif;
@@ -729,6 +633,8 @@
 	boolean_t isv6;
 	int fd;
 	struct lifreq lifr;
+	char abuf1[INET6_ADDRSTRLEN];
+	char abuf2[INET6_ADDRSTRLEN];
 
 	(void) memset(&lifr, 0, sizeof (struct lifreq));
 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
@@ -748,32 +654,27 @@
 	} else if (isv6) {
 		struct sockaddr_in6 *sin6 =
 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
-		char abuf1[INET6_ADDRSTRLEN];
-		char abuf2[INET6_ADDRSTRLEN];
 
 		if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
 			dhcpmsg(MSG_WARNING,
-			    "checkaddr: expected %s %s on %s, have %s",
-			    aname, inet_ntop(AF_INET6, &sin6->sin6_addr, abuf1,
-			    sizeof (abuf1)),  lif->lif_name,
-			    inet_ntop(AF_INET6, addr, abuf2, sizeof (abuf2)));
+			    "checkaddr: expected %s %s on %s, have %s", aname,
+			    inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)),
+			    lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr,
+			    abuf2, sizeof (abuf2)));
 			return (B_FALSE);
 		}
 	} else {
 		struct sockaddr_in *sinp =
 		    (struct sockaddr_in *)&lifr.lifr_addr;
 		ipaddr_t v4addr;
-		char abuf1[INET_ADDRSTRLEN];
-		char abuf2[INET_ADDRSTRLEN];
 
 		IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
 		if (sinp->sin_addr.s_addr != v4addr) {
 			dhcpmsg(MSG_WARNING,
-			    "checkaddr: expected %s %s on %s, have %s",
-			    aname, inet_ntop(AF_INET, &sinp->sin_addr, abuf1,
-			    sizeof (abuf1)),  lif->lif_name,
-			    inet_ntop(AF_INET, &v4addr, abuf2,
-			    sizeof (abuf2)));
+			    "checkaddr: expected %s %s on %s, have %s", aname,
+			    inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)),
+			    lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr,
+			    abuf2, sizeof (abuf2)));
 			return (B_FALSE);
 		}
 	}
@@ -896,11 +797,12 @@
  *		   unplumb_lif().
  *
  *   input: dhcp_lif_t *: the interface to canonize
+ *	    boolean_t: only canonize lif if it's under DHCP control
  *  output: none
  */
 
 static void
-canonize_lif(dhcp_lif_t *lif)
+canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly)
 {
 	boolean_t isv6;
 	int fd;
@@ -934,8 +836,7 @@
 		return;
 	}
 
-	/* Should not happen */
-	if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) {
+	if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) {
 		dhcpmsg(MSG_INFO,
 		    "canonize_lif: cannot clear %s; flags are %llx",
 		    lif->lif_name, lifr.lifr_flags);
@@ -1117,7 +1018,7 @@
 	 * just canonize it and remove it from the lease.
 	 */
 	if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
-		canonize_lif(lif);
+		canonize_lif(lif, B_TRUE);
 		cancel_lif_timers(lif);
 		if (lif->lif_declined != NULL) {
 			dlp->dl_smach->dsm_lif_down--;
@@ -1349,12 +1250,17 @@
  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
  *
  *   input: dhcp_lif_t *: the logical interface to operate on
+ *	    in_addr_t: the address the socket will be bound to (in hbo)
  *  output: boolean_t: B_TRUE if the socket was opened successfully.
  */
 
 boolean_t
-open_ip_lif(dhcp_lif_t *lif)
+open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo)
 {
+	const char *errmsg;
+	struct lifreq lifr;
+	int on = 1;
+
 	if (lif->lif_sock_ip_fd != -1) {
 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
 		    lif->lif_name);
@@ -1363,27 +1269,90 @@
 
 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
 	if (lif->lif_sock_ip_fd == -1) {
-		dhcpmsg(MSG_ERR, "open_ip_lif: cannot create v4 socket on %s",
-		    lif->lif_name);
-		return (B_FALSE);
+		errmsg = "cannot create v4 socket";
+		goto failure;
+	}
+
+	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) {
+		errmsg = "cannot bind v4 socket";
+		goto failure;
+	}
+
+	/*
+	 * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
+	 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
+	 * unspecified (0.0.0.0) address.  Also, enable IP_DHCPINIT_IF so that
+	 * the IP module will accept unicast DHCP traffic regardless of the IP
+	 * address it's sent to.  (We'll then figure out which packets are
+	 * ours based on the xid.)
+	 */
+	if (addr_hbo == INADDR_ANY) {
+		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC,
+		    &on, sizeof (int)) == -1) {
+			errmsg = "cannot set IP_UNSPEC_SRC";
+			goto failure;
+		}
+
+		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
+		    &lif->lif_pif->pif_index, sizeof (int)) == -1) {
+			errmsg = "cannot set IP_DHCPINIT_IF";
+			goto failure;
+		}
+	}
+
+	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF,
+	    &lif->lif_pif->pif_index, sizeof (int)) == -1) {
+		errmsg = "cannot set IP_BOUND_IF";
+		goto failure;
 	}
 
-	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC,
-	    ntohl(lif->lif_addr))) {
-		dhcpmsg(MSG_ERR, "open_ip_lif: cannot bind v4 socket on %s",
-		    lif->lif_name);
-		return (B_FALSE);
+	/*
+	 * Make sure at least one lif on the interface we used in IP_BOUND_IF
+	 * is IFF_UP so that we can send and receive IP packets.
+	 */
+	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
+	if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
+		errmsg = "cannot get interface flags";
+		goto failure;
 	}
 
-	lif->lif_acknak_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
-	    dhcp_acknak_lif, lif);
-	if (lif->lif_acknak_id == -1) {
-		dhcpmsg(MSG_WARNING, "open_ip_lif: cannot register to "
-		    "receive IP unicast");
-		close_ip_lif(lif);
-		return (B_FALSE);
+	if (!(lifr.lifr_flags & IFF_UP)) {
+		/*
+		 * Start from a clean slate.
+		 */
+		canonize_lif(lif, B_FALSE);
+
+		lifr.lifr_flags |= IFF_UP;
+		if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
+			errmsg = "cannot bring up";
+			goto failure;
+		}
+
+		/*
+		 * When bringing 0.0.0.0 IFF_UP, the kernel changes the
+		 * netmask to 255.0.0.0, so re-fetch our expected netmask.
+		 */
+		if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) {
+			errmsg = "cannot get netmask";
+			goto failure;
+		}
+
+		lif->lif_netmask =
+		    ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
 	}
+
+	lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
+	    dhcp_packet_lif, lif);
+	if (lif->lif_packet_id == -1) {
+		errmsg = "cannot register to receive DHCP packets";
+		goto failure;
+	}
+
 	return (B_TRUE);
+failure:
+	dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg);
+	close_ip_lif(lif);
+	return (B_FALSE);
 }
 
 /*
@@ -1396,9 +1365,9 @@
 void
 close_ip_lif(dhcp_lif_t *lif)
 {
-	if (lif->lif_acknak_id != -1) {
-		(void) iu_unregister_event(eh, lif->lif_acknak_id, NULL);
-		lif->lif_acknak_id = -1;
+	if (lif->lif_packet_id != -1) {
+		(void) iu_unregister_event(eh, lif->lif_packet_id, NULL);
+		lif->lif_packet_id = -1;
 	}
 	if (lif->lif_sock_ip_fd != -1) {
 		(void) close(lif->lif_sock_ip_fd);
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.h	Tue Oct 30 11:15:43 2007 -0700
@@ -47,7 +47,6 @@
 #include <netinet/dhcp.h>
 #include <dhcpagent_ipc.h>
 #include <libinetutil.h>
-#include <libdlpi.h>
 
 #include "common.h"
 #include "util.h"
@@ -65,14 +64,7 @@
 	uchar_t		pif_hwtype;	/* type of link-layer */
 	boolean_t	pif_isv6;
 	boolean_t	pif_running;	/* interface is running */
-	dlpi_handle_t 	pif_dlpi_hd;	/* dlpi handle */
-	int		pif_dlpi_count;
-	iu_event_id_t	pif_dlpi_id;	/* event id for ack/nak/offer */
 	uint_t		pif_hold_count;	/* reference count */
-
-	uchar_t		*pif_daddr;	/* our L2 destination address */
-	uchar_t		pif_dlen;	/* our L2 destination address len */
-
 	char		pif_name[LIFNAMSIZ];
 };
 
@@ -84,7 +76,7 @@
 	dhcp_lease_t	*lif_lease;	/* backpointer to lease holding LIF */
 	uint64_t	lif_flags;	/* Interface flags (IFF_*) */
 	int		lif_sock_ip_fd;	/* Bound to addr.BOOTPC for src addr */
-	iu_event_id_t	lif_acknak_id;	/* event acknak id */
+	iu_event_id_t	lif_packet_id;	/* event packet id */
 	uint_t		lif_max;	/* maximum IP message size */
 	uint_t		lif_hold_count;	/* reference count */
 	boolean_t	lif_dad_wait;	/* waiting for DAD resolution */
@@ -177,8 +169,6 @@
 dhcp_pif_t	*lookup_pif_by_index(uint_t, boolean_t);
 dhcp_pif_t	*lookup_pif_by_uindex(uint16_t, dhcp_pif_t *, boolean_t);
 dhcp_pif_t	*lookup_pif_by_name(const char *, boolean_t);
-boolean_t	open_dlpi_pif(dhcp_pif_t *);
-void		close_dlpi_pif(dhcp_pif_t *);
 void		pif_status(dhcp_pif_t *, boolean_t);
 
 dhcp_lif_t	*insert_lif(dhcp_pif_t *, const char *, int *);
@@ -193,7 +183,7 @@
 int		set_lif_dhcp(dhcp_lif_t *, boolean_t);
 void		set_lif_deprecated(dhcp_lif_t *);
 boolean_t	clear_lif_deprecated(dhcp_lif_t *);
-boolean_t	open_ip_lif(dhcp_lif_t *);
+boolean_t	open_ip_lif(dhcp_lif_t *, in_addr_t);
 void		close_ip_lif(dhcp_lif_t *);
 void		lif_mark_decline(dhcp_lif_t *, const char *);
 boolean_t	schedule_lif_timer(dhcp_lif_t *, dhcp_timer_t *,
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.c	Tue Oct 30 11:15:43 2007 -0700
@@ -46,7 +46,6 @@
 #include "agent.h"
 #include "packet.h"
 #include "util.h"
-#include "dlpi_io.h"
 
 int v6_sock_fd = -1;
 int v4_sock_fd = -1;
@@ -157,19 +156,18 @@
 dhcp_pkt_t *
 init_pkt(dhcp_smach_t *dsmp, uchar_t type)
 {
-	uint_t		mtu;
 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
 	dhcp_lif_t	*lif = dsmp->dsm_lif;
 	dhcp_pif_t	*pif = lif->lif_pif;
+	uint_t		mtu = lif->lif_max;
 	uint32_t	xid;
 	boolean_t	isv6;
 
-	mtu = dsmp->dsm_using_dlpi ? pif->pif_max : lif->lif_max;
 	dpkt->pkt_isv6 = isv6 = pif->pif_isv6;
 
 	/*
-	 * since multiple dhcp leases may be maintained over the same dlpi
-	 * device (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
+	 * Since multiple dhcp leases may be maintained over the same pif
+	 * (e.g. "hme0" and "hme0:1"), make sure the xid is unique.
 	 *
 	 * Note that transaction ID zero is intentionally never assigned.
 	 * That's used to represent "no ID."  Also note that transaction IDs
@@ -250,14 +248,12 @@
 			 * thus server can not unicast the reply. Per
 			 * RFC 2131 4.4.1, client can set this bit in
 			 * DISCOVER/REQUEST. If the client is already
-			 * in BOUND/REBINDING/RENEWING state, do not set
-			 * this bit, as it can respond to unicast responses
-			 * from server using the 'ciaddr' address.
+			 * in a bound state, do not set this bit, as it
+			 * can respond to unicast responses from server
+			 * using the 'ciaddr' address.
 			 */
-			if (type == DISCOVER ||
-			    (type == REQUEST && dsmp->dsm_state != RENEWING &&
-			    dsmp->dsm_state != REBINDING &&
-			    dsmp->dsm_state != BOUND))
+			if (type == DISCOVER || (type == REQUEST &&
+			    !is_bound_state(dsmp->dsm_state)))
 				v4->flags = htons(BCAST_MASK);
 		}
 
@@ -804,7 +800,6 @@
 {
 	ssize_t		n_bytes;
 	dhcp_lif_t	*lif = dsmp->dsm_lif;
-	dhcp_pif_t	*pif = lif->lif_pif;
 	dhcp_pkt_t	*dpkt = &dsmp->dsm_send_pkt;
 	uchar_t		ptype = pkt_send_type(dpkt);
 	const char	*pkt_name;
@@ -813,6 +808,7 @@
 	struct cmsghdr	*cmsg;
 	struct in6_pktinfo *ipi6;
 	boolean_t	ismcast;
+	int		msgtype;
 
 	/*
 	 * Timer should not be running at the point we go to send a packet.
@@ -985,27 +981,19 @@
 
 		n_bytes = sendmsg(v6_sock_fd, &msg, 0);
 	} else {
-		if (dsmp->dsm_using_dlpi) {
-			n_bytes = dlpi_sendto(pif->pif_dlpi_hd, dpkt->pkt,
-			    dpkt->pkt_cur_len, &dsmp->dsm_send_dest.v4,
-			    pif->pif_daddr, pif->pif_dlen);
-			/* dlpi_sendto calls putmsg */
-			if (n_bytes == 0)
-				n_bytes = dpkt->pkt_cur_len;
-		} else {
-			n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
-			    dpkt->pkt_cur_len, 0,
-			    (struct sockaddr *)&dsmp->dsm_send_dest.v4,
-			    sizeof (struct sockaddr_in));
-		}
+		n_bytes = sendto(lif->lif_sock_ip_fd, dpkt->pkt,
+		    dpkt->pkt_cur_len, 0,
+		    (struct sockaddr *)&dsmp->dsm_send_dest.v4,
+		    sizeof (struct sockaddr_in));
 	}
 
 	if (n_bytes != dpkt->pkt_cur_len) {
+		msgtype = (n_bytes == -1) ? MSG_ERR : MSG_WARNING;
 		if (dsmp->dsm_retrans_timer == -1)
-			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
+			dhcpmsg(msgtype, "send_pkt_internal: cannot send "
 			    "%s packet to server", pkt_name);
 		else
-			dhcpmsg(MSG_WARNING, "send_pkt_internal: cannot send "
+			dhcpmsg(msgtype, "send_pkt_internal: cannot send "
 			    "%s packet to server (will retry in %u seconds)",
 			    pkt_name, dsmp->dsm_send_timeout / MILLISEC);
 		return (B_FALSE);
@@ -1319,16 +1307,14 @@
 /*
  * recv_pkt(): receives a single DHCP packet on a given file descriptor.
  *
- *   input: int: if not using dlpi, the file descriptor to receive the packet
+ *   input: int: the file descriptor to receive the packet from
  *	    int: the maximum packet size to allow
  *	    boolean_t: B_TRUE for IPv6
- *	    boolean_t: B_TRUE if using DLPI
- *	    void *: if using DLPI, structure that has DLPI handle
  *  output: PKT_LIST *: the received packet
  */
 
 PKT_LIST *
-recv_pkt(int fd, int mtu, boolean_t isv6, boolean_t isdlpi, dhcp_pif_t *arg)
+recv_pkt(int fd, int mtu, boolean_t isv6)
 {
 	PKT_LIST	*plp;
 	ssize_t		retval;
@@ -1339,41 +1325,21 @@
 		return (NULL);
 	}
 
-	if (isv6) {
-		retval = sock_recvpkt(fd, plp);
+	retval = sock_recvpkt(fd, plp);
+	if (retval == -1) {
+		dhcpmsg(MSG_ERR, "recv_pkt: recvfrom v%d failed, dropped",
+		    isv6 ? 6 : 4);
+		goto failure;
+	}
 
-		if (retval == -1) {
-			dhcpmsg(MSG_ERR,
-			    "recv_pkt: recvfrom v6 failed, dropped");
-			goto failure;
-		}
+	plp->len = retval;
 
-		plp->len = retval;
-
+	if (isv6) {
 		if (retval < sizeof (dhcpv6_message_t)) {
 			dhcpmsg(MSG_WARNING, "recv_pkt: runt message");
 			goto failure;
 		}
 	} else {
-		if (isdlpi) {
-			dhcp_pif_t	*pif = arg;
-
-			retval = dlpi_recvfrom(pif->pif_dlpi_hd, plp->pkt, mtu,
-			    (struct sockaddr_in *)&plp->pktfrom,
-			    (struct sockaddr_in *)&plp->pktto);
-		} else {
-			retval = sock_recvpkt(fd, plp);
-		}
-
-		if (retval == -1) {
-			dhcpmsg(MSG_ERR,
-			    "recv_pkt: %srecvfrom v4 failed, dropped",
-			    isdlpi ? "dlpi_" : "");
-			goto failure;
-		}
-
-		plp->len = retval;
-
 		switch (dhcp_options_scan(plp, B_TRUE)) {
 
 		case DHCP_WRONG_MSG_TYPE:
@@ -1576,7 +1542,7 @@
 		return (B_FALSE);
 	}
 
-	if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_common,
+	if (iu_register_event(eh, v4_sock_fd, POLLIN, dhcp_acknak_global,
 	    NULL) == -1) {
 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
 		    "receive IPv4 broadcasts");
@@ -1603,7 +1569,7 @@
 		return (B_FALSE);
 	}
 
-	if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_common,
+	if (iu_register_event(eh, v6_sock_fd, POLLIN, dhcp_acknak_global,
 	    NULL) == -1) {
 		dhcpmsg(MSG_WARNING, "dhcp_ip_default: cannot register to "
 		    "receive IPv6 packets");
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/packet.h	Tue Oct 30 11:15:43 2007 -0700
@@ -130,7 +130,7 @@
 boolean_t	add_pkt_lif(dhcp_pkt_t *, dhcp_lif_t *, int, const char *);
 void		stop_pkt_retransmission(dhcp_smach_t *);
 void		retransmit_now(dhcp_smach_t *);
-PKT_LIST	*recv_pkt(int, int, boolean_t, boolean_t, dhcp_pif_t *);
+PKT_LIST	*recv_pkt(int, int, boolean_t);
 boolean_t	pkt_v4_match(uchar_t, dhcp_message_type_t);
 void		pkt_smach_enqueue(dhcp_smach_t *, PKT_LIST *);
 boolean_t	send_pkt(dhcp_smach_t *, dhcp_pkt_t *, in_addr_t,
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c	Tue Oct 30 11:15:43 2007 -0700
@@ -372,7 +372,7 @@
 	/*
 	 * Look through the packet contents.  Valid packets must have our
 	 * client ID and a server ID, which has already been checked by
-	 * dhcp_acknak_lif.  Bonus points for each option.
+	 * dhcp_packet_lif.  Bonus points for each option.
 	 */
 
 	/* One point for having a valid message. */
@@ -959,7 +959,7 @@
 }
 
 /*
- * dhcp_acknak_common(): Processes reception of an ACK or NAK packet on the
+ * dhcp_acknak_global(): Processes reception of an ACK or NAK packet on the
  *			 global socket -- broadcast packets for IPv4, all
  *			 packets for DHCPv6.
  *
@@ -973,7 +973,7 @@
 
 /* ARGSUSED */
 void
-dhcp_acknak_common(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
+dhcp_acknak_global(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
     void *arg)
 {
 	PKT_LIST	*plp;
@@ -983,14 +983,18 @@
 	uint_t		xid;
 	dhcp_smach_t	*dsmp;
 	boolean_t	isv6 = (fd == v6_sock_fd);
+	struct sockaddr_in sin;
+	const char	*reason;
+	size_t		sinlen = sizeof (sin);
+	int		sock;
 
-	plp = recv_pkt(fd, get_max_mtu(isv6), isv6, B_FALSE, NULL);
+	plp = recv_pkt(fd, get_max_mtu(isv6), isv6);
 	if (plp == NULL)
 		return;
 
 	pif = lookup_pif_by_index(plp->ifindex, isv6);
 	if (pif == NULL) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored packet "
+		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored packet "
 		    "received on v%d ifIndex %d", isv6 ? 6 : 4, plp->ifindex);
 		free_pkt_entry(plp);
 		return;
@@ -998,33 +1002,45 @@
 
 	recv_type = pkt_recv_type(plp);
 	pname = pkt_type_to_string(recv_type, isv6);
-	if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored %s packet "
-		    "received via broadcast on %s", pname, pif->pif_name);
-		free_pkt_entry(plp);
-		return;
-	}
 
 	/*
-	 * Find the corresponding state machine not using DLPI.
+	 * Find the corresponding state machine.
 	 *
 	 * Note that DHCPv6 Reconfigure would be special: it's not the reply to
 	 * any transaction, and thus we would need to search on transaction ID
-	 * zero (all state machines) to find the match.  However, Reconfigure
+	 * zero (all state machines) to find the match.	 However, Reconfigure
 	 * is not yet supported.
 	 */
 	xid = pkt_get_xid(plp->pkt, isv6);
+	if (!isv6 && !pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
+		reason = "not ACK or NAK";
+		goto drop;
+	}
+
 	for (dsmp = lookup_smach_by_xid(xid, NULL, isv6); dsmp != NULL;
 	    dsmp = lookup_smach_by_xid(xid, dsmp, isv6)) {
 		if (dsmp->dsm_lif->lif_pif == pif)
 			break;
 	}
-	if (dsmp == NULL || dsmp->dsm_using_dlpi) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_common: ignored %s packet "
-		    "received via broadcast %s; %s", pname, pif->pif_name,
-		    dsmp == NULL ? "unknown state machine" : "not using DLPI");
-		free_pkt_entry(plp);
-		return;
+
+	if (dsmp == NULL) {
+		reason = "unknown state machine";
+		goto drop;
+	}
+
+	/*
+	 * For IPv4, most packets will be handled by dhcp_packet_lif().  The
+	 * only exceptions are broadcast packets sent when lif_sock_ip_fd has
+	 * bound to something other than INADDR_ANY.
+	 */
+	if (!isv6) {
+		sock = dsmp->dsm_lif->lif_sock_ip_fd;
+
+		if (getsockname(sock, (struct sockaddr *)&sin, &sinlen) != -1 &&
+		    sin.sin_addr.s_addr == INADDR_ANY) {
+			reason = "handled by lif_sock_ip_fd";
+			goto drop;
+		}
 	}
 
 	/*
@@ -1035,6 +1051,12 @@
 		accept_v6_message(dsmp, plp, pname, recv_type);
 	else
 		accept_v4_acknak(dsmp, plp);
+	return;
+drop:
+	dhcpmsg(MSG_VERBOSE, "dhcp_acknak_global: ignored v%d %s packet for %s "
+	    "received on global socket: %s", isv6 ? 6 : 4, pname, pif->pif_name,
+	    reason);
+	free_pkt_entry(plp);
 }
 
 /*
@@ -1062,11 +1084,11 @@
 }
 
 /*
- * dhcp_acknak_lif(): Processes reception of an ACK or NAK packet on a given
- *		      logical interface for IPv4 (only).
+ * dhcp_packet_lif(): Processes reception of an ACK, NAK, or OFFER packet on
+ *		      a given logical interface for IPv4 (only).
  *
  *   input: iu_eh_t *: unused
- *	    int: the global file descriptor the ACK/NAK arrived on
+ *	    int: the file descriptor the packet arrived on
  *	    short: unused
  *	    iu_event_id_t: the id of this event callback with the handler
  *	    void *: pointer to logical interface receiving message
@@ -1075,7 +1097,7 @@
 
 /* ARGSUSED */
 void
-dhcp_acknak_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
+dhcp_packet_lif(iu_eh_t *ehp, int fd, short events, iu_event_id_t id,
     void *arg)
 {
 	dhcp_lif_t	*lif = arg;
@@ -1085,21 +1107,22 @@
 	uint_t		xid;
 	dhcp_smach_t	*dsmp;
 
-	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE, B_FALSE, NULL)) == NULL)
+	if ((plp = recv_pkt(fd, lif->lif_max, B_FALSE)) == NULL)
 		return;
 
 	recv_type = pkt_recv_type(plp);
 	pname = pkt_type_to_string(recv_type, B_FALSE);
 
-	if (!pkt_v4_match(recv_type, DHCP_PACK | DHCP_PNAK)) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_lif: ignored v4 %s packet "
+	if (!pkt_v4_match(recv_type,
+	    DHCP_PACK | DHCP_PNAK | DHCP_PUNTYPED | DHCP_POFFER)) {
+		dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored v4 %s packet "
 		    "received via LIF %s", pname, lif->lif_name);
 		free_pkt_entry(plp);
 		return;
 	}
 
 	/*
-	 * Find the corresponding state machine not using DLPI.
+	 * Find the corresponding state machine.
 	 */
 	xid = pkt_get_xid(plp->pkt, B_FALSE);
 	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
@@ -1107,19 +1130,31 @@
 		if (dsmp->dsm_lif == lif)
 			break;
 	}
-	if (dsmp == NULL || dsmp->dsm_using_dlpi) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_acknak_lif: ignored %s packet xid "
-		    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
-		    dsmp == NULL ? "unknown state machine" : "not using DLPI");
-		free_pkt_entry(plp);
-		return;
+
+	if (dsmp == NULL)
+		goto drop;
+
+	if (pkt_v4_match(recv_type, DHCP_PACK|DHCP_PNAK)) {
+		/*
+		 * We've got an ACK/NAK; make sure it's acceptable and cancel
+		 * the REQUEST retransmissions.
+		 */
+		accept_v4_acknak(dsmp, plp);
+	} else {
+		if (is_bound_state(dsmp->dsm_state))
+			goto drop;
+		/*
+		 * Must be an OFFER or a BOOTP message: enqueue it for later
+		 * processing by select_best().
+		 */
+		pkt_smach_enqueue(dsmp, plp);
 	}
-
-	/*
-	 * We've got a packet; make sure it's acceptable and cancel the REQUEST
-	 * retransmissions.
-	 */
-	accept_v4_acknak(dsmp, plp);
+	return;
+drop:
+	dhcpmsg(MSG_VERBOSE, "dhcp_packet_lif: ignored %s packet xid "
+	    "%x received via LIF %s; %s", pname, xid, lif->lif_name,
+	    dsmp == NULL ? "unknown state machine" : "bound");
+	free_pkt_entry(plp);
 }
 
 /*
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/select.c	Tue Oct 30 11:15:43 2007 -0700
@@ -235,102 +235,6 @@
 }
 
 /*
- * dhcp_collect_dlpi(): collects incoming OFFERs, ACKs, and NAKs via DLPI.
- *
- *   input: iu_eh_t *: unused
- *	    int: unused
- *	    short: unused
- *	    iu_event_id_t: the id of this event callback with the handler
- *	    void *: the physical interface that received the message
- *  output: void
- */
-
-/* ARGSUSED */
-void
-dhcp_collect_dlpi(iu_eh_t *eh, int fd, short events, iu_event_id_t id,
-    void *arg)
-{
-	dhcp_pif_t	*pif = arg;
-	PKT_LIST	*plp;
-	uchar_t		recv_type;
-	const char	*pname;
-	dhcp_smach_t	*dsmp;
-	uint_t		xid;
-
-	if ((plp = recv_pkt(fd, pif->pif_max, B_FALSE, B_TRUE, pif)) == NULL)
-		return;
-
-	recv_type = pkt_recv_type(plp);
-	pname = pkt_type_to_string(recv_type, B_FALSE);
-
-	/*
-	 * DHCP_PUNTYPED messages are BOOTP server responses.
-	 */
-	if (!pkt_v4_match(recv_type,
-	    DHCP_PACK | DHCP_PNAK | DHCP_POFFER | DHCP_PUNTYPED)) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: ignored %s packet "
-		    "received via DLPI on %s", pname, pif->pif_name);
-		free_pkt_entry(plp);
-		return;
-	}
-
-	/*
-	 * Loop through the state machines that match on XID to find one that's
-	 * interested in this offer.  If there are none, then discard.
-	 */
-	xid = pkt_get_xid(plp->pkt, B_FALSE);
-	for (dsmp = lookup_smach_by_xid(xid, NULL, B_FALSE); dsmp != NULL;
-	    dsmp = lookup_smach_by_xid(xid, dsmp, B_FALSE)) {
-
-		/*
-		 * Find state machine on correct interface.
-		 */
-		if (dsmp->dsm_lif->lif_pif == pif)
-			break;
-	}
-
-	if (dsmp == NULL) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: no matching state "
-		    "machine for %s packet XID %#x received via DLPI on %s",
-		    pname, xid, pif->pif_name);
-		free_pkt_entry(plp);
-		return;
-	}
-
-	/*
-	 * Ignore state machines that aren't looking for DLPI messages.
-	 */
-	if (!dsmp->dsm_using_dlpi) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: ignore state "
-		    "machine for %s packet XID %#x received via DLPI on %s",
-		    pname, xid, pif->pif_name);
-		free_pkt_entry(plp);
-		return;
-	}
-
-	/* See also accept_v[46]_message; account for processed packets. */
-	dsmp->dsm_received++;
-
-	if (pkt_v4_match(recv_type, DHCP_PACK)) {
-		if (!dhcp_bound(dsmp, plp)) {
-			dhcpmsg(MSG_WARNING, "dhcp_collect_dlpi: dhcp_bound "
-			    "failed for %s", dsmp->dsm_name);
-			dhcp_restart(dsmp);
-			return;
-		}
-		dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: %s on %s",
-		    pname, dsmp->dsm_name);
-	} else if (pkt_v4_match(recv_type, DHCP_PNAK)) {
-		dhcpmsg(MSG_VERBOSE, "dhcp_collect_dlpi: %s on %s",
-		    pname, dsmp->dsm_name);
-		free_pkt_entry(plp);
-		dhcp_restart(dsmp);
-	} else {
-		pkt_smach_enqueue(dsmp, plp);
-	}
-}
-
-/*
  * stop_selecting(): decides when to stop retransmitting DISCOVERs -- only when
  *		     abandoning the state machine.  For DHCPv6, this timer may
  *		     go off before the offer wait timer.  If so, then this is a
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.c	Tue Oct 30 11:15:43 2007 -0700
@@ -149,18 +149,16 @@
 		    &dsmp->dsm_server);
 
 		/*
-		 * With IPv4 DHCP, we start off doing our I/O via DLPI, so open
-		 * that up now.
+		 * With IPv4 DHCP, we use a socket per lif.
 		 */
-		if (!open_dlpi_pif(lif->lif_pif)) {
-			dhcpmsg(MSG_ERR, "unable to open DLPI for %s",
+		if (!open_ip_lif(lif, INADDR_ANY)) {
+			dhcpmsg(MSG_ERR, "unable to open socket for %s",
 			    lif->lif_name);
 			/* This will also dispose of the LIF */
 			release_smach(dsmp);
 			*error = DHCP_IPC_E_SOCKET;
 			return (NULL);
 		}
-		dsmp->dsm_using_dlpi = B_TRUE;
 	}
 	dsmp->dsm_retrans_timer		= -1;
 	dsmp->dsm_offer_timer		= -1;
@@ -657,6 +655,20 @@
 }
 
 /*
+ * is_bound_state(): checks if a state indicates the client is bound
+ *
+ *   input: DHCPSTATE: the state to check
+ *  output: boolean_t: B_TRUE if the state is bound, B_FALSE if not
+ */
+
+boolean_t
+is_bound_state(DHCPSTATE state)
+{
+	return (state == BOUND || state == REBINDING || state == INFORMATION ||
+	    state == RELEASING || state == INFORM_SENT || state == RENEWING);
+}
+
+/*
  * set_smach_state(): changes state and updates I/O
  *
  *   input: dhcp_smach_t *: the state machine to change
@@ -667,35 +679,33 @@
 boolean_t
 set_smach_state(dhcp_smach_t *dsmp, DHCPSTATE state)
 {
+	dhcp_lif_t *lif = dsmp->dsm_lif;
+
 	if (dsmp->dsm_state != state) {
-		boolean_t is_bound;
-
 		dhcpmsg(MSG_DEBUG,
 		    "set_smach_state: changing from %s to %s on %s",
 		    dhcp_state_to_string(dsmp->dsm_state),
 		    dhcp_state_to_string(state), dsmp->dsm_name);
 
+		/*
+		 * For IPv4, when we're in a bound state our socket must be
+		 * bound to our address.  Otherwise, our socket must be bound
+		 * to INADDR_ANY.  For IPv6, no such change is necessary.
+		 */
 		if (!dsmp->dsm_isv6) {
-			/*
-			 * When we're in a bound state for IPv4, we receive our
-			 * packets through our LIF.  Otherwise, we receive them
-			 * through DLPI.  Make sure the right one is connected.
-			 * For IPv6, no such change is necessary.
-			 */
-			is_bound = (state == BOUND || state == REBINDING ||
-			    state == RENEWING || state == RELEASING ||
-			    state == INFORM_SENT || state == INFORMATION);
-			if (dsmp->dsm_using_dlpi && is_bound) {
-				if (!open_ip_lif(dsmp->dsm_lif))
-					return (B_FALSE);
-				dsmp->dsm_using_dlpi = B_FALSE;
-				close_dlpi_pif(dsmp->dsm_lif->lif_pif);
-			}
-			if (!dsmp->dsm_using_dlpi && !is_bound) {
-				if (!open_dlpi_pif(dsmp->dsm_lif->lif_pif))
-					return (B_FALSE);
-				dsmp->dsm_using_dlpi = B_TRUE;
-				close_ip_lif(dsmp->dsm_lif);
+			if (is_bound_state(dsmp->dsm_state)) {
+				if (!is_bound_state(state)) {
+					close_ip_lif(lif);
+					if (!open_ip_lif(lif, INADDR_ANY))
+						return (B_FALSE);
+				}
+			} else {
+				if (is_bound_state(state)) {
+					close_ip_lif(lif);
+					if (!open_ip_lif(lif,
+					    ntohl(lif->lif_addr)))
+						return (B_FALSE);
+				}
 			}
 		}
 
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/states.h	Tue Oct 30 11:15:43 2007 -0700
@@ -72,8 +72,6 @@
 	uint_t		dsm_lif_wait;	/* LIFs waiting on DAD */
 	uint_t		dsm_lif_down;	/* LIFs failed */
 
-	boolean_t	dsm_using_dlpi;
-
 	/*
 	 * each state machine can have at most one pending asynchronous
 	 * action, which is represented in a `struct async_action'.
@@ -258,9 +256,8 @@
 };
 
 /* The IU event callback functions */
-iu_eh_callback_t	dhcp_acknak_common;
-iu_eh_callback_t	dhcp_acknak_lif;
-iu_eh_callback_t	dhcp_collect_dlpi;
+iu_eh_callback_t	dhcp_acknak_global;
+iu_eh_callback_t	dhcp_packet_lif;
 
 /* Common state-machine related routines throughout dhcpagent */
 boolean_t	dhcp_adopt(void);
@@ -311,6 +308,7 @@
 void		cancel_offer_timer(dhcp_smach_t *);
 void		discard_default_routes(dhcp_smach_t *);
 void		remove_default_routes(dhcp_smach_t *);
+boolean_t	is_bound_state(DHCPSTATE);
 
 /* Lease-related support functions in states.c */
 dhcp_lease_t	*insert_lease(dhcp_smach_t *);
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/defs.h	Tue Oct 30 11:15:43 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Copyright (c) 1983, 1988, 1993
@@ -801,6 +801,8 @@
 extern void rip_mcast_on(struct interface *);
 extern void rip_mcast_off(struct interface *);
 extern void trace_dump();
+extern int sendtoif(int, const void *, uint_t, uint_t, struct sockaddr_in *,
+    uint_t);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/output.c	Tue Oct 30 11:15:43 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Copyright (c) 1983, 1988, 1993
@@ -40,6 +40,7 @@
 
 #include "defs.h"
 #include <md5.h>
+#include <alloca.h>
 
 uint_t update_seqno;
 
@@ -109,7 +110,6 @@
 	int res;
 	int ifindex;
 	struct in_addr addr;
-	static int rip_sock_ifindex;
 
 	sin = *dst;
 	if (sin.sin_port == 0)
@@ -148,22 +148,12 @@
 	}
 
 	/*
-	 * Note that we intentionally reset IP_XMIT_IF to zero if
-	 * we're doing multicast.  The kernel ignores IP_MULTICAST_IF
-	 * if IP_XMIT_IF is set, and we can't deal with alias source
-	 * addresses without it.
+	 * IP_PKTINFO overrides IP_MULTICAST_IF, so we don't set ifindex
+	 * for multicast traffic.
 	 */
 	ifindex = (type != OUT_MULTICAST && type != OUT_QUERY &&
 	    ifp != NULL && ifp->int_phys != NULL) ?
 	    ifp->int_phys->phyi_index : 0;
-	if (rip_sock_ifindex != ifindex) {
-		if (setsockopt(rip_sock, IPPROTO_IP, IP_XMIT_IF, &ifindex,
-		    sizeof (ifindex)) == -1) {
-			LOGERR("setsockopt(rip_sock, IP_XMIT_IF)");
-			return (-1);
-		}
-		rip_sock_ifindex = ifindex;
-	}
 
 	if (rip_sock_interface != ifp) {
 		/*
@@ -186,10 +176,8 @@
 
 	trace_rip(msg, "to", &sin, ifp, buf, size);
 
-	res = sendto(rip_sock, buf, size, flags,
-	    (struct sockaddr *)&sin, sizeof (sin));
-	if (res < 0 &&
-	    (ifp == NULL || !(ifp->int_state & IS_BROKE))) {
+	res = sendtoif(rip_sock, buf, size, flags, &sin, ifindex);
+	if (res < 0 && (ifp == NULL || !(ifp->int_state & IS_BROKE))) {
 		writelog(LOG_WARNING, "%s sendto(%s%s%s.%d): %s", msg,
 		    ifp != NULL ? ifp->int_name : "",
 		    ifp != NULL ? ", " : "",
@@ -201,6 +189,55 @@
 	return (res);
 }
 
+/*
+ * Semantically identical to sendto(), but sends the message through a
+ * specific interface (if ifindex is non-zero) using IP_PKTINFO.
+ */
+int
+sendtoif(int fd, const void *buf, uint_t bufsize, uint_t flags,
+    struct sockaddr_in *sinp, uint_t ifindex)
+{
+	struct iovec iov;
+	struct msghdr msg;
+	struct cmsghdr *cmsgp;
+	struct in_pktinfo *ipip;
+
+	iov.iov_base = (void *)buf;
+	iov.iov_len = bufsize;
+
+	(void) memset(&msg, 0, sizeof (struct msghdr));
+	msg.msg_name = (struct sockaddr *)sinp;
+	msg.msg_namelen = sizeof (struct sockaddr_in);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	if (ifindex != 0) {
+		/*
+		 * We can't precisely predict the alignment padding we'll
+		 * need, so we allocate the maximum alignment and then
+		 * use CMSG_NXTHDR() to fix it up at the end.
+		 */
+		msg.msg_controllen = sizeof (*cmsgp) + _MAX_ALIGNMENT +
+		    sizeof (*ipip) + _MAX_ALIGNMENT + sizeof (*cmsgp);
+		msg.msg_control = alloca(msg.msg_controllen);
+
+		cmsgp = CMSG_FIRSTHDR(&msg);
+		ipip = (void *)CMSG_DATA(cmsgp);
+		(void) memset(ipip, 0, sizeof (struct in_pktinfo));
+		ipip->ipi_ifindex = ifindex;
+		cmsgp->cmsg_len = (caddr_t)(ipip + 1) - (caddr_t)cmsgp;
+		cmsgp->cmsg_type = IP_PKTINFO;
+		cmsgp->cmsg_level = IPPROTO_IP;
+
+		/*
+		 * Correct the control message length.
+		 */
+		cmsgp = CMSG_NXTHDR(&msg, cmsgp);
+		msg.msg_controllen = (caddr_t)cmsgp - (caddr_t)msg.msg_control;
+	}
+
+	return (sendmsg(fd, &msg, flags));
+}
 
 /*
  * Find the first key for a packet to send.
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c	Tue Oct 30 11:15:43 2007 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Copyright (c) 1995
@@ -663,9 +663,9 @@
 		    new_drp->dr_life > drp->dr_life))))) ||
 		    ((new_st & IS_SICK) &&
 		    !(drp->dr_ifp->int_state & IS_SICK))) {
-			    new_drp = drp;
-			    new_st = drp->dr_ifp->int_state;
-			    new_pref = drp->dr_pref;
+			new_drp = drp;
+			new_st = drp->dr_ifp->int_state;
+			new_pref = drp->dr_pref;
 		}
 	}
 
@@ -1010,7 +1010,7 @@
 	struct sockaddr_in sin;
 	int flags = 0;
 	const char *msg;
-	int ifindex;
+	int ifindex = 0;
 	struct in_addr addr;
 
 	/*
@@ -1052,15 +1052,11 @@
 	if (rdisc_sock < 0)
 		get_rdisc_sock();
 
+	/* select the right interface. */
+	ifindex = (type != mcast && ifp->int_phys != NULL) ?
+	    ifp->int_phys->phyi_index : 0;
+
 	if (rdisc_sock_interface != ifp) {
-		/* select the right interface. */
-		ifindex = (type != mcast && ifp->int_phys != NULL) ?
-		    ifp->int_phys->phyi_index : 0;
-		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_XMIT_IF, &ifindex,
-		    sizeof (ifindex)) == -1) {
-			LOGERR("setsockopt(rdisc_sock, IP_XMIT_IF)");
-			return;
-		}
 		/*
 		 * For multicast, we have to choose the source
 		 * address.  This is either the local address
@@ -1070,7 +1066,7 @@
 		    ifp->int_dstaddr : ifp->int_addr;
 		if (type == mcast &&
 		    setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr,
-			sizeof (addr)) == -1) {
+		    sizeof (addr)) == -1) {
 			LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)");
 			return;
 		}
@@ -1079,8 +1075,7 @@
 
 	trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, p, p_size);
 
-	if (0 > sendto(rdisc_sock, p, p_size, flags,
-	    (struct sockaddr *)&sin, sizeof (sin))) {
+	if (0 > sendtoif(rdisc_sock, p, p_size, flags, &sin, ifindex)) {
 		if (!(ifp->int_state & IS_BROKE))
 			writelog(LOG_WARNING, "sendto(%s%s%s): %s",
 			    ifp->int_name, ", ",
@@ -1294,7 +1289,7 @@
 		cc = recvmsg(rdisc_sock, &msg, 0);
 		if (cc <= 0) {
 			if (cc < 0 && errno != EWOULDBLOCK)
-			    LOGERR("recvmsg(rdisc_sock)");
+				LOGERR("recvmsg(rdisc_sock)");
 			break;
 		}
 
--- a/usr/src/uts/common/inet/ip.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip.h	Tue Oct 30 11:15:43 2007 -0700
@@ -622,9 +622,9 @@
 #define	IRE_MARK_HIDDEN		0x0004	/* Typically Used by in.mpathd */
 
 /*
- * ire with IRE_MARK_NOADD is created in ip_newroute_ipif, when outgoing
- * interface is specified by IP_XMIT_IF socket option. This ire is not
- * added in IRE_CACHE.
+ * An IRE with IRE_MARK_NOADD is created in ip_newroute_ipif when the outgoing
+ * interface is specified by e.g. IP_PKTINFO.  The IRE is not added to the IRE
+ * cache table.
  */
 #define	IRE_MARK_NOADD		0x0008	/* Mark not to add ire in cache */
 
@@ -1031,7 +1031,6 @@
  * ipc_acking_unbind 		conn_acking_unbind
  * ipc_pad_to_bit_31 		conn_pad_to_bit_31
  *
- * ipc_xmit_if_ill		conn_xmit_if_ill
  * ipc_nofailover_ill		conn_nofailover_ill
  *
  * ipc_proto			conn_proto
@@ -1048,7 +1047,6 @@
  * ipc_multicast_ill		conn_multicast_ill
  * ipc_orig_bound_ifindex	conn_orig_bound_ifindex
  * ipc_orig_multicast_ifindex	conn_orig_multicast_ifindex
- * ipc_orig_xmit_ifindex	conn_orig_xmit_ifindex
  * ipc_drain_next		conn_drain_next
  * ipc_drain_prev		conn_drain_prev
  * ipc_idl			conn_idl
@@ -1842,7 +1840,7 @@
 		ill_arp_closing : 1,
 
 		ill_arp_bringup_pending : 1,
-		ill_mtu_userspecified : 1, /* SIOCSLNKINFO has set the mtu */
+		ill_mtu_userspecified : 1, /* SIOCSLIFLNKINFO has set the mtu */
 		ill_arp_extend : 1,	/* ARP has DAD extensions */
 		ill_pad_bit_31 : 25;
 
@@ -1962,6 +1960,7 @@
 	boolean_t	ill_trace_disable;	/* True when alloc fails */
 	zoneid_t	ill_zoneid;
 	ip_stack_t	*ill_ipst;	/* Corresponds to a netstack_hold */
+	uint32_t	ill_dhcpinit;	/* IP_DHCPINIT_IFs for ill */
 } ill_t;
 
 /*
@@ -2063,6 +2062,7 @@
  * ill_nce_cnt			ill_lock		ill_lock
  * ill_trace			ill_lock		ill_lock
  * ill_usesrc_grp_next		ill_g_usesrc_lock	ill_g_usesrc_lock
+ * ill_dhcpinit			atomics			atomics
  */
 
 /*
--- a/usr/src/uts/common/inet/ip/icmp.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip/icmp.c	Tue Oct 30 11:15:43 2007 -0700
@@ -1766,9 +1766,6 @@
 		case IP_UNSPEC_SRC:
 			*ptr = icmp->icmp_unspec_source;
 			break;	/* goto sizeof (int) option return */
-		case IP_XMIT_IF:
-			*i1 = icmp->icmp_xmit_if;
-			break;	/* goto sizeof (int) option return */
 		case IP_RECVIF:
 			*ptr = icmp->icmp_recvif;
 			break;	/* goto sizeof (int) option return */
@@ -1956,8 +1953,7 @@
 				return (0);
 
 			return (ip_fill_mtuinfo(&icmp->icmp_v6dst, 0,
-			    (struct ip6_mtuinfo *)ptr,
-			    is->is_netstack));
+			    (struct ip6_mtuinfo *)ptr, is->is_netstack));
 		case IPV6_TCLASS:
 			if (ipp->ipp_fields & IPPF_TCLASS)
 				*i1 = ipp->ipp_tclass;
@@ -2317,10 +2313,6 @@
 			if (!checkonly)
 				icmp->icmp_unspec_source = onoff;
 			break;
-		case IP_XMIT_IF:
-			if (!checkonly)
-				icmp->icmp_xmit_if = *i1;
-			break;
 		case IP_RECVIF:
 			if (!checkonly)
 				icmp->icmp_recvif = onoff;
@@ -5539,8 +5531,7 @@
 void
 icmp_ddi_init(void)
 {
-	icmp_max_optsize =
-	    optcom_max_optsize(icmp_opt_obj.odb_opt_des_arr,
+	icmp_max_optsize = optcom_max_optsize(icmp_opt_obj.odb_opt_des_arr,
 	    icmp_opt_obj.odb_opt_arr_cnt);
 
 	/*
--- a/usr/src/uts/common/inet/ip/icmp_opt_data.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip/icmp_opt_data.c	Tue Oct 30 11:15:43 2007 -0700
@@ -140,9 +140,6 @@
 { IP_BOUND_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
 	sizeof (int),	0 /* no ifindex */ },
 
-{ IP_XMIT_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
-	sizeof (int),	0 /* no ifindex */ },
-
 { IP_DONTFAILOVER_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
 	sizeof (struct in_addr), 0 /* not initialized */ },
 
--- a/usr/src/uts/common/inet/ip/ip.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip/ip.c	Tue Oct 30 11:15:43 2007 -0700
@@ -1294,7 +1294,9 @@
 	/* 181 */ { SIOCSIPMSFILTER, sizeof (struct ip_msfilter), IPI_WR,
 			MSFILT_CMD, ip_sioctl_msfilter, NULL },
 	/* 182 */ { SIOCSIPMPFAILBACK, sizeof (int), IPI_PRIV, MISC_CMD,
-			ip_sioctl_set_ipmpfailback, NULL }
+			ip_sioctl_set_ipmpfailback, NULL },
+	/* SIOCSENABLESDP is handled by SDP */
+	/* 183 */ { IPI_DONTCARE /* SIOCSENABLESDP */, 0, 0, 0, NULL, NULL },
 };
 
 int ip_ndx_ioctl_count = sizeof (ip_ndx_ioctl_table) / sizeof (ip_ioctl_cmd_t);
@@ -5526,6 +5528,11 @@
 		drain_cleanup_reqd = B_TRUE;
 	if (connp->conn_oper_pending_ill != NULL)
 		conn_ioctl_cleanup_reqd = B_TRUE;
+	if (connp->conn_dhcpinit_ill != NULL) {
+		ASSERT(connp->conn_dhcpinit_ill->ill_dhcpinit != 0);
+		atomic_dec_32(&connp->conn_dhcpinit_ill->ill_dhcpinit);
+		connp->conn_dhcpinit_ill = NULL;
+	}
 	if (connp->conn_ilg_inuse != 0)
 		ilg_cleanup_reqd = B_TRUE;
 	mutex_exit(&connp->conn_lock);
@@ -7792,6 +7799,7 @@
 	    MULTIRT_CACHEGW | MULTIRT_USESTAMP | MULTIRT_SETSTAMP;
 	boolean_t multirt_is_resolvable;
 	boolean_t multirt_resolve_next;
+	boolean_t unspec_src;
 	boolean_t do_attach_ill = B_FALSE;
 	boolean_t ip_nexthop = B_FALSE;
 	tsol_ire_gw_secattr_t *attrp = NULL;
@@ -8200,7 +8208,11 @@
 			src_ipif = ipif_lookup_addr(sire->ire_src_addr, NULL,
 			    zoneid, NULL, NULL, NULL, NULL, ipst);
 		}
-		if (src_ipif == NULL) {
+
+		unspec_src = (connp != NULL && connp->conn_unspec_src);
+
+		if (src_ipif == NULL &&
+		    (!unspec_src || ipha->ipha_src != INADDR_ANY)) {
 			ire_marks |= IRE_MARK_USESRC_CHECK;
 			if ((dst_ill->ill_group != NULL) ||
 			    (ire->ire_ipif->ipif_flags & IPIF_DEPRECATED) ||
@@ -8257,10 +8269,9 @@
 		 * NOTE : ip_newroute_v6 does not have this piece of code as
 		 *	  it uses ip6i to store this information.
 		 */
-		if (ipha->ipha_src == INADDR_ANY &&
-		    (connp == NULL || !connp->conn_unspec_src)) {
+		if (ipha->ipha_src == INADDR_ANY && !unspec_src)
 			ipha->ipha_src = src_ipif->ipif_src_addr;
-		}
+
 		if (ip_debug > 3) {
 			/* ip2dbg */
 			pr_addr_dbg("ip_newroute: first hop %s\n",
@@ -8963,7 +8974,7 @@
  * ip_rput_forward_multicast whenever we need to send
  * out a packet to a destination address for which we do not have specific
  * routing information. It is used when the packet will be sent out
- * on a specific interface. It is also called by ip_wput() when IP_XMIT_IF
+ * on a specific interface. It is also called by ip_wput() when IP_BOUND_IF
  * socket option is set or icmp error message wants to go out on a particular
  * interface for a unicast packet.
  *
@@ -9005,6 +9016,7 @@
 	ire_t   *fire = NULL;
 	mblk_t  *copy_mp = NULL;
 	boolean_t multirt_resolve_next;
+	boolean_t unspec_src;
 	ipaddr_t ipha_dst;
 	ip_stack_t *ipst = ipif->ipif_ill->ill_ipst;
 
@@ -9105,14 +9117,6 @@
 			ASSERT(dst_ill == attach_ill);
 		} else {
 			/*
-			 * If this is set by IP_XMIT_IF, then make sure that
-			 * ipif is pointing to the same ill as the IP_XMIT_IF
-			 * specified ill.
-			 */
-			ASSERT((connp == NULL) ||
-			    (connp->conn_xmit_if_ill == NULL) ||
-			    (connp->conn_xmit_if_ill == ipif->ipif_ill));
-			/*
 			 * If the interface belongs to an interface group,
 			 * make sure the next possible interface in the group
 			 * is used.  This encourages load spreading among
@@ -9163,10 +9167,15 @@
 			src_ipif = ipif_lookup_addr(fire->ire_src_addr, NULL,
 			    zoneid, NULL, NULL, NULL, NULL, ipst);
 		}
-		if (((ipif->ipif_flags & IPIF_DEPRECATED) ||
+
+		unspec_src = (connp != NULL && connp->conn_unspec_src);
+
+		if (((!ipif->ipif_isv6 && ipif->ipif_lcl_addr == INADDR_ANY) ||
+		    (ipif->ipif_flags & (IPIF_DEPRECATED|IPIF_UP)) != IPIF_UP ||
 		    (connp != NULL && ipif->ipif_zoneid != zoneid &&
 		    ipif->ipif_zoneid != ALL_ZONES)) &&
-		    (src_ipif == NULL)) {
+		    (src_ipif == NULL) &&
+		    (!unspec_src || ipha->ipha_src != INADDR_ANY)) {
 			src_ipif = ipif_select_source(dst_ill, dst, zoneid);
 			if (src_ipif == NULL) {
 				if (ip_debug > 2) {
@@ -9192,19 +9201,17 @@
 		 * Assign a source address while we have the conn.
 		 * We can't have ip_wput_ire pick a source address when the
 		 * packet returns from arp since conn_unspec_src might be set
-		 * and we loose the conn when going through arp.
-		 */
-		if (ipha->ipha_src == INADDR_ANY &&
-		    (connp == NULL || !connp->conn_unspec_src)) {
+		 * and we lose the conn when going through arp.
+		 */
+		if (ipha->ipha_src == INADDR_ANY && !unspec_src)
 			ipha->ipha_src = src_ipif->ipif_src_addr;
-		}
-
-		/*
-		 * In the case of IP_XMIT_IF, it is possible that the
-		 * outgoing interface does not have an interface ire.
+
+		/*
+		 * In the case of IP_BOUND_IF and IP_PKTINFO, it is possible
+		 * that the outgoing interface does not have an interface ire.
 		 */
 		if (CLASSD(ipha_dst) && (connp == NULL ||
-		    connp->conn_xmit_if_ill == NULL) &&
+		    connp->conn_outgoing_ill == NULL) &&
 		    infop->ip_opt_ill_index == 0) {
 			/* ipif_to_ire returns an held ire */
 			ire = ipif_to_ire(ipif);
@@ -9250,12 +9257,12 @@
 			}
 		} else {
 			ASSERT((connp == NULL) ||
-			    (connp->conn_xmit_if_ill != NULL) ||
+			    (connp->conn_outgoing_ill != NULL) ||
 			    (connp->conn_dontroute) ||
 			    infop->ip_opt_ill_index != 0);
 			/*
 			 * The only ways we can come here are:
-			 * 1) IP_XMIT_IF socket option is set
+			 * 1) IP_BOUND_IF socket option is set
 			 * 2) SO_DONTROUTE socket option is set
 			 * 3) IP_PKTINFO option is passed in as ancillary data.
 			 * In all cases, the new ire will not be added
@@ -10493,19 +10500,6 @@
 			    0 : ifindex;
 			break;
 
-		case IP_XMIT_IF:
-			/*
-			 * Similar to IP_BOUND_IF, but this only
-			 * determines the outgoing interface for
-			 * unicast packets. Also no IRE_CACHE entry
-			 * is added for the destination of the
-			 * outgoing packets.
-			 */
-			connp->conn_xmit_if_ill = ill;
-			connp->conn_orig_xmit_ifindex = (ill == NULL) ?
-			    0 : ifindex;
-			break;
-
 		case IP_MULTICAST_IF:
 			/*
 			 * This option is an internal special. The socket
@@ -10530,6 +10524,26 @@
 				}
 			}
 			break;
+
+		case IP_DHCPINIT_IF:
+			if (connp->conn_dhcpinit_ill != NULL) {
+				/*
+				 * We've locked the conn so conn_cleanup_ill()
+				 * cannot clear conn_dhcpinit_ill -- so it's
+				 * safe to access the ill.
+				 */
+				ill_t *oill = connp->conn_dhcpinit_ill;
+
+				ASSERT(oill->ill_dhcpinit != 0);
+				atomic_dec_32(&oill->ill_dhcpinit);
+				connp->conn_dhcpinit_ill = NULL;
+			}
+
+			if (ill != NULL) {
+				connp->conn_dhcpinit_ill = ill;
+				atomic_inc_32(&ill->ill_dhcpinit);
+			}
+			break;
 		}
 	} else {
 		switch (option) {
@@ -11048,7 +11062,7 @@
 			*outlenp = inlen;
 			return (0);
 		case IP_BOUND_IF:
-		case IP_XMIT_IF:
+		case IP_DHCPINIT_IF:
 			error = ip_opt_set_ill(connp, *i1, B_FALSE, checkonly,
 			    level, name, first_mp);
 			if (error != 0)
@@ -15003,11 +15017,12 @@
 		 *	o no options in the packet
 		 *	o not a RSVP packet
 		 * 	o not a multicast packet
+		 *	o ill not in IP_DHCPINIT_IF mode
 		 */
 		if (!is_system_labeled() &&
 		    !ipst->ips_ip_cgtp_filter && ipp_action_count == 0 &&
 		    opt_len == 0 && ipha->ipha_protocol != IPPROTO_RSVP &&
-		    !ll_multicast && !CLASSD(dst)) {
+		    !ll_multicast && !CLASSD(dst) && ill->ill_dhcpinit == 0) {
 			if (ire == NULL)
 				ire = ire_cache_lookup(dst, ALL_ZONES, NULL,
 				    ipst);
@@ -15031,6 +15046,31 @@
 			ire = NULL;
 		}
 
+		/*
+		 * Brutal hack for DHCPv4 unicast: RFC2131 allows a DHCP
+		 * server to unicast DHCP packets to a DHCP client using the
+		 * IP address it is offering to the client.  This can be
+		 * disabled through the "broadcast bit", but not all DHCP
+		 * servers honor that bit.  Therefore, to interoperate with as
+		 * many DHCP servers as possible, the DHCP client allows the
+		 * server to unicast, but we treat those packets as broadcast
+		 * here.  Note that we don't rewrite the packet itself since
+		 * (a) that would mess up the checksums and (b) the DHCP
+		 * client conn is bound to INADDR_ANY so ip_fanout_udp() will
+		 * hand it the packet regardless.
+		 */
+		if (ill->ill_dhcpinit != 0 &&
+		    IS_SIMPLE_IPH(ipha) && ipha->ipha_protocol == IPPROTO_UDP &&
+		    MBLKL(mp) > sizeof (ipha_t) + sizeof (udpha_t)) {
+			udpha_t *udpha = (udpha_t *)&ipha[1];
+
+			if (ntohs(udpha->uha_dst_port) == IPPORT_BOOTPC) {
+				DTRACE_PROBE2(ip4__dhcpinit__pkt, ill_t *, ill,
+				    mblk_t *, mp);
+				dst = INADDR_BROADCAST;
+			}
+		}
+
 		/* Full-blown slow path */
 		if (opt_len != 0) {
 			if (len != 0)
@@ -19988,7 +20028,7 @@
 	int		match_flags;
 	ill_t		*attach_ill = NULL;
 					/* Bind to IPIF_NOFAILOVER ill etc. */
-	ill_t		*xmit_ill = NULL;	/* IP_XMIT_IF etc. */
+	ill_t		*xmit_ill = NULL;	/* IP_PKTINFO etc. */
 	ipif_t		*dst_ipif;
 	boolean_t	multirt_need_resolve = B_FALSE;
 	mblk_t		*copy_mp = NULL;
@@ -20109,11 +20149,11 @@
 	}
 
 	/*
-	 * IP_DONTFAILOVER_IF and IP_XMIT_IF have precedence over
-	 * ill index passed in IP_PKTINFO.
+	 * IP_DONTFAILOVER_IF and IP_BOUND_IF have precedence over ill index
+	 * passed in IP_PKTINFO.
 	 */
 	if (infop->ip_opt_ill_index != 0 &&
-	    connp->conn_xmit_if_ill == NULL &&
+	    connp->conn_outgoing_ill == NULL &&
 	    connp->conn_nofailover_ill == NULL) {
 
 		xmit_ill = ill_lookup_on_ifindex(
@@ -20178,6 +20218,15 @@
 		}
 	}
 
+	/* If IP_BOUND_IF has been set, use that ill. */
+	if (connp->conn_outgoing_ill != NULL) {
+		xmit_ill = conn_get_held_ill(connp,
+		    &connp->conn_outgoing_ill, &err);
+		if (err == ILL_LOOKUP_FAILED)
+			goto drop_pkt;
+
+		goto send_from_ill;
+	}
 
 	/* is packet multicast? */
 	if (CLASSD(dst))
@@ -20187,74 +20236,39 @@
 	 * If xmit_ill is set above due to index passed in ip_pkt_info. It
 	 * takes precedence over conn_dontroute and conn_nexthop_set
 	 */
-	if (xmit_ill != NULL) {
+	if (xmit_ill != NULL)
 		goto send_from_ill;
-	}
-
-	if ((connp->conn_dontroute) || (connp->conn_xmit_if_ill != NULL) ||
-	    (connp->conn_nexthop_set)) {
-		/*
-		 * If the destination is a broadcast or a loopback
-		 * address, SO_DONTROUTE, IP_XMIT_IF and IP_NEXTHOP go
-		 * through the standard path. But in the case of local
-		 * destination only SO_DONTROUTE and IP_NEXTHOP go through
-		 * the standard path not IP_XMIT_IF.
+
+	if (connp->conn_dontroute || connp->conn_nexthop_set) {
+		/*
+		 * If the destination is a broadcast, local, or loopback
+		 * address, SO_DONTROUTE and IP_NEXTHOP go through the
+		 * standard path.
 		 */
 		ire = ire_cache_lookup(dst, zoneid, MBLK_GETLABEL(mp), ipst);
-		if ((ire == NULL) || ((ire->ire_type != IRE_BROADCAST) &&
-		    (ire->ire_type != IRE_LOOPBACK))) {
-			if ((connp->conn_dontroute ||
-			    connp->conn_nexthop_set) && (ire != NULL) &&
-			    (ire->ire_type == IRE_LOCAL))
-				goto standard_path;
-
+		if ((ire == NULL) || (ire->ire_type &
+		    (IRE_BROADCAST | IRE_LOCAL | IRE_LOOPBACK)) == 0) {
 			if (ire != NULL) {
 				ire_refrele(ire);
 				/* No more access to ire */
 				ire = NULL;
 			}
 			/*
-			 * bypass routing checks and go directly to
-			 * interface.
-			 */
-			if (connp->conn_dontroute) {
+			 * bypass routing checks and go directly to interface.
+			 */
+			if (connp->conn_dontroute)
 				goto dontroute;
-			} else if (connp->conn_nexthop_set) {
-				ip_nexthop = B_TRUE;
-				nexthop_addr = connp->conn_nexthop_v4;
-				goto send_from_ill;
-			}
-
-			/*
-			 * If IP_XMIT_IF socket option is set,
-			 * then we allow unicast and multicast
-			 * packets to go through the ill. It is
-			 * quite possible that the destination
-			 * is not in the ire cache table and we
-			 * do not want to go to ip_newroute()
-			 * instead we call ip_newroute_ipif.
-			 */
-			xmit_ill = conn_get_held_ill(connp,
-			    &connp->conn_xmit_if_ill, &err);
-			if (err == ILL_LOOKUP_FAILED) {
-				BUMP_MIB(&ipst->ips_ip_mib,
-				    ipIfStatsOutDiscards);
-				if (attach_ill != NULL)
-					ill_refrele(attach_ill);
-				if (need_decref)
-					CONN_DEC_REF(connp);
-				freemsg(first_mp);
-				return;
-			}
+
+			ASSERT(connp->conn_nexthop_set);
+			ip_nexthop = B_TRUE;
+			nexthop_addr = connp->conn_nexthop_v4;
 			goto send_from_ill;
 		}
-standard_path:
+
 		/* Must be a broadcast, a loopback or a local ire */
-		if (ire != NULL) {
-			ire_refrele(ire);
-			/* No more access to ire */
-			ire = NULL;
-		}
+		ire_refrele(ire);
+		/* No more access to ire */
+		ire = NULL;
 	}
 
 	if (attach_ill != NULL)
@@ -20819,17 +20833,16 @@
 			    ntohl(dst), ill->ill_name));
 		} else {
 			/*
-			 * The order of precedence is IP_XMIT_IF, IP_PKTINFO
-			 * and IP_MULTICAST_IF.
-			 * Block comment above this function explains the
-			 * locking mechanism used here
+			 * The order of precedence is IP_BOUND_IF, IP_PKTINFO
+			 * and IP_MULTICAST_IF.  The block comment above this
+			 * function explains the locking mechanism used here.
 			 */
 			if (xmit_ill == NULL) {
 				xmit_ill = conn_get_held_ill(connp,
-				    &connp->conn_xmit_if_ill, &err);
+				    &connp->conn_outgoing_ill, &err);
 				if (err == ILL_LOOKUP_FAILED) {
 					ip1dbg(("ip_wput: No ill for "
-					    "IP_XMIT_IF\n"));
+					    "IP_BOUND_IF\n"));
 					BUMP_MIB(&ipst->ips_ip_mib,
 					    ipIfStatsOutNoRoutes);
 					goto drop_pkt;
@@ -20851,7 +20864,7 @@
 				ipif = ipif_get_next_ipif(NULL, xmit_ill);
 				if (ipif == NULL) {
 					ip1dbg(("ip_wput: No ipif for "
-					    "IP_XMIT_IF\n"));
+					    "xmit_ill\n"));
 					BUMP_MIB(&ipst->ips_ip_mib,
 					    ipIfStatsOutNoRoutes);
 					goto drop_pkt;
@@ -20994,7 +21007,7 @@
 			dst = ipif->ipif_lcl_addr;
 
 		/*
-		 * If IP_XMIT_IF is set, we branch out to ip_newroute_ipif.
+		 * If xmit_ill is set, we branch out to ip_newroute_ipif.
 		 * We don't need to lookup ire in ctable as the packet
 		 * needs to be sent to the destination through the specified
 		 * ill irrespective of ires in the cache table.
@@ -21095,54 +21108,39 @@
 			 * connectivity.
 			 */
 			ipha->ipha_ttl = 1;
-			/*
-			 * If IP_XMIT_IF is also set (conn_xmit_if_ill != NULL)
-			 * along with SO_DONTROUTE, higher precedence is
-			 * given to IP_XMIT_IF and the IP_XMIT_IF ipif is used.
-			 */
-			if (connp->conn_xmit_if_ill == NULL) {
-				/* If suitable ipif not found, drop packet */
-				dst_ipif = ipif_lookup_onlink_addr(dst, zoneid,
-				    ipst);
-				if (dst_ipif == NULL) {
-					ip1dbg(("ip_wput: no route for "
-					    "dst using SO_DONTROUTE\n"));
-					BUMP_MIB(&ipst->ips_ip_mib,
-					    ipIfStatsOutNoRoutes);
-					mp->b_prev = mp->b_next = NULL;
-					if (first_mp == NULL)
-						first_mp = mp;
-					goto drop_pkt;
-				} else {
-					/*
-					 * If suitable ipif has been found, set
-					 * xmit_ill to the corresponding
-					 * ipif_ill because we'll be following
-					 * the IP_XMIT_IF logic.
-					 */
-					ASSERT(xmit_ill == NULL);
-					xmit_ill = dst_ipif->ipif_ill;
-					mutex_enter(&xmit_ill->ill_lock);
-					if (!ILL_CAN_LOOKUP(xmit_ill)) {
-						mutex_exit(&xmit_ill->ill_lock);
-						xmit_ill = NULL;
-						ipif_refrele(dst_ipif);
-						ip1dbg(("ip_wput: no route for"
-						    " dst using"
-						    " SO_DONTROUTE\n"));
-						BUMP_MIB(&ipst->ips_ip_mib,
-						    ipIfStatsOutNoRoutes);
-						mp->b_prev = mp->b_next = NULL;
-						if (first_mp == NULL)
-							first_mp = mp;
-						goto drop_pkt;
-					}
-					ill_refhold_locked(xmit_ill);
+
+			/* If suitable ipif not found, drop packet */
+			dst_ipif = ipif_lookup_onlink_addr(dst, zoneid, ipst);
+			if (dst_ipif == NULL) {
+noroute:
+				ip1dbg(("ip_wput: no route for dst using"
+				    " SO_DONTROUTE\n"));
+				BUMP_MIB(&ipst->ips_ip_mib,
+				    ipIfStatsOutNoRoutes);
+				mp->b_prev = mp->b_next = NULL;
+				if (first_mp == NULL)
+					first_mp = mp;
+				goto drop_pkt;
+			} else {
+				/*
+				 * If suitable ipif has been found, set
+				 * xmit_ill to the corresponding
+				 * ipif_ill because we'll be using the
+				 * send_from_ill logic below.
+				 */
+				ASSERT(xmit_ill == NULL);
+				xmit_ill = dst_ipif->ipif_ill;
+				mutex_enter(&xmit_ill->ill_lock);
+				if (!ILL_CAN_LOOKUP(xmit_ill)) {
 					mutex_exit(&xmit_ill->ill_lock);
+					xmit_ill = NULL;
 					ipif_refrele(dst_ipif);
-				}
-			}
-
+					goto noroute;
+				}
+				ill_refhold_locked(xmit_ill);
+				mutex_exit(&xmit_ill->ill_lock);
+				ipif_refrele(dst_ipif);
+			}
 		}
 		/*
 		 * If we are bound to IPIF_NOFAILOVER address, look for
@@ -21170,84 +21168,64 @@
 			ire = ire_ctable_lookup(dst, 0, 0, attach_ipif,
 			    zoneid, MBLK_GETLABEL(mp), match_flags, ipst);
 			ipif_refrele(attach_ipif);
-		} else if (xmit_ill != NULL || (connp != NULL &&
-		    connp->conn_xmit_if_ill != NULL)) {
+		} else if (xmit_ill != NULL) {
+			ipif_t *ipif;
+
 			/*
 			 * Mark this packet as originated locally
 			 */
 			mp->b_prev = mp->b_next = NULL;
-			/*
-			 * xmit_ill could be NULL if SO_DONTROUTE
-			 * is also set.
-			 */
-			if (xmit_ill == NULL) {
-				xmit_ill = conn_get_held_ill(connp,
-				    &connp->conn_xmit_if_ill, &err);
-				if (err == ILL_LOOKUP_FAILED) {
-					BUMP_MIB(&ipst->ips_ip_mib,
-					    ipIfStatsOutDiscards);
-					if (need_decref)
-						CONN_DEC_REF(connp);
-					freemsg(first_mp);
-					return;
-				}
-				if (xmit_ill == NULL) {
-					if (connp->conn_dontroute)
-						goto dontroute;
-					goto send_from_ill;
-				}
-			}
+
 			/*
 			 * Could be SO_DONTROUTE case also.
-			 * check at least one interface is UP as
-			 * specified by this ILL
-			 */
-			if (xmit_ill->ill_ipif_up_count > 0) {
-				ipif_t *ipif;
-
-				ipif = ipif_get_next_ipif(NULL, xmit_ill);
-				if (ipif == NULL) {
-					ip1dbg(("ip_output: "
-					    "xmit_ill NULL ipif\n"));
-					goto drop_pkt;
-				}
-				/*
-				 * Look for a ire that is part of the group,
-				 * if found use it else call ip_newroute_ipif.
-				 * IPCL_ZONEID is not used for matching because
-				 * IP_ALLZONES option is valid only when the
-				 * ill is accessible from all zones i.e has a
-				 * valid ipif in all zones.
-				 */
-				match_flags = MATCH_IRE_ILL_GROUP |
-				    MATCH_IRE_SECATTR;
-				ire = ire_ctable_lookup(dst, 0, 0, ipif, zoneid,
-				    MBLK_GETLABEL(mp), match_flags, ipst);
-				/*
-				 * If an ire exists use it or else create
-				 * an ire but don't add it to the cache.
-				 * Adding an ire may cause issues with
-				 * asymmetric routing.
-				 * In case of multiroute always act as if
-				 * ire does not exist.
-				 */
-				if (ire == NULL ||
-				    ire->ire_flags & RTF_MULTIRT) {
-					if (ire != NULL)
-						ire_refrele(ire);
-					ip_newroute_ipif(q, first_mp, ipif,
-					    dst, connp, 0, zoneid, infop);
-					ipif_refrele(ipif);
-					ip1dbg(("ip_wput: ip_unicast_if\n"));
-					ill_refrele(xmit_ill);
-					if (need_decref)
-						CONN_DEC_REF(connp);
-					return;
-				}
+			 * Verify that at least one ipif is up on the ill.
+			 */
+			if (xmit_ill->ill_ipif_up_count == 0) {
+				ip1dbg(("ip_output: xmit_ill %s is down\n",
+				    xmit_ill->ill_name));
+				goto drop_pkt;
+			}
+
+			ipif = ipif_get_next_ipif(NULL, xmit_ill);
+			if (ipif == NULL) {
+				ip1dbg(("ip_output: xmit_ill %s NULL ipif\n",
+				    xmit_ill->ill_name));
+				goto drop_pkt;
+			}
+
+			/*
+			 * Look for a ire that is part of the group,
+			 * if found use it else call ip_newroute_ipif.
+			 * IPCL_ZONEID is not used for matching because
+			 * IP_ALLZONES option is valid only when the
+			 * ill is accessible from all zones i.e has a
+			 * valid ipif in all zones.
+			 */
+			match_flags = MATCH_IRE_ILL_GROUP | MATCH_IRE_SECATTR;
+			ire = ire_ctable_lookup(dst, 0, 0, ipif, zoneid,
+			    MBLK_GETLABEL(mp), match_flags, ipst);
+			/*
+			 * If an ire exists use it or else create
+			 * an ire but don't add it to the cache.
+			 * Adding an ire may cause issues with
+			 * asymmetric routing.
+			 * In case of multiroute always act as if
+			 * ire does not exist.
+			 */
+			if (ire == NULL || ire->ire_flags & RTF_MULTIRT) {
+				if (ire != NULL)
+					ire_refrele(ire);
+				ip_newroute_ipif(q, first_mp, ipif,
+				    dst, connp, 0, zoneid, infop);
 				ipif_refrele(ipif);
-			} else {
-				goto drop_pkt;
-			}
+				ip1dbg(("ip_output: xmit_ill via %s\n",
+				    xmit_ill->ill_name));
+				ill_refrele(xmit_ill);
+				if (need_decref)
+					CONN_DEC_REF(connp);
+				return;
+			}
+			ipif_refrele(ipif);
 		} else if (ip_nexthop || (connp != NULL &&
 		    (connp->conn_nexthop_set)) && !ignore_nexthop) {
 			if (!ip_nexthop) {
@@ -21458,7 +21436,7 @@
  *
  * The following rules must be observed when accessing any ipif or ill
  * that has been cached in the conn. Typically conn_nofailover_ill,
- * conn_xmit_if_ill, conn_multicast_ipif and conn_multicast_ill.
+ * conn_outgoing_ill, conn_multicast_ipif and conn_multicast_ill.
  *
  * Access: The ipif or ill pointed to from the conn can be accessed under
  * the protection of the conn_lock or after it has been refheld under the
@@ -22020,11 +21998,10 @@
 	}
 
 	/*
-	 * conn_outgoing_ill is used only in the broadcast loop.
+	 * conn_outgoing_ill variable is used only in the broadcast loop.
 	 * for performance we don't grab the mutexs in the fastpath
 	 */
 	if ((connp != NULL) &&
-	    (connp->conn_xmit_if_ill == NULL) &&
 	    (ire->ire_type == IRE_BROADCAST) &&
 	    ((connp->conn_nofailover_ill != NULL) ||
 	    (connp->conn_outgoing_ill != NULL))) {
@@ -22611,8 +22588,8 @@
 				rw_exit(&ire->ire_bucket->irb_lock);
 				/* Did not find a matching ill */
 				ip1dbg(("ip_wput_ire: broadcast with no "
-				    "matching IP_BOUND_IF ill %s\n",
-				    conn_outgoing_ill->ill_name));
+				    "matching IP_BOUND_IF ill %s dst %x\n",
+				    conn_outgoing_ill->ill_name, dst));
 				freemsg(first_mp);
 				if (ire != NULL)
 					ire_refrele(ire);
--- a/usr/src/uts/common/inet/ip/ip_if.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip/ip_if.c	Tue Oct 30 11:15:43 2007 -0700
@@ -1526,8 +1526,11 @@
 		connp->conn_outgoing_pill = NULL;
 	if (connp->conn_nofailover_ill == ill)
 		connp->conn_nofailover_ill = NULL;
-	if (connp->conn_xmit_if_ill == ill)
-		connp->conn_xmit_if_ill = NULL;
+	if (connp->conn_dhcpinit_ill == ill) {
+		connp->conn_dhcpinit_ill = NULL;
+		ASSERT(ill->ill_dhcpinit != 0);
+		atomic_dec_32(&ill->ill_dhcpinit);
+	}
 	if (connp->conn_ire_cache != NULL) {
 		ire = connp->conn_ire_cache;
 		/*
@@ -16610,11 +16613,6 @@
 		connp->conn_multicast_ill = connm->cm_to_ill;
 	}
 
-	/* Change IP_XMIT_IF associations */
-	if ((connp->conn_xmit_if_ill == from_ill) &&
-	    (ifindex == 0 || connp->conn_orig_xmit_ifindex == ifindex)) {
-		connp->conn_xmit_if_ill = to_ill;
-	}
 	/*
 	 * Change the ilg_ill to point to the new one. This assumes
 	 * ilm_move_v6 has moved the ilms to new_ill and the driver
@@ -20216,8 +20214,7 @@
 	/*
 	 * Create any necessary broadcast IREs.
 	 */
-	if ((ipif->ipif_subnet != INADDR_ANY) &&
-	    (ipif->ipif_flags & IPIF_BROADCAST))
+	if (ipif->ipif_flags & IPIF_BROADCAST)
 		irep = ipif_create_bcast_ires(ipif, irep);
 
 	ASSERT(!MUTEX_HELD(&ipif->ipif_ill->ill_lock));
@@ -21712,9 +21709,6 @@
 	if (connp->conn_orig_multicast_ifindex == old_ifindex)
 		connp->conn_orig_multicast_ifindex = new_ifindex;
 
-	if (connp->conn_orig_xmit_ifindex == old_ifindex)
-		connp->conn_orig_xmit_ifindex = new_ifindex;
-
 	for (i = connp->conn_ilg_inuse - 1; i >= 0; i--) {
 		ilg = &connp->conn_ilg[i];
 		if (ilg->ilg_orig_ifindex == old_ifindex)
--- a/usr/src/uts/common/inet/ip/ip_opt_data.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip/ip_opt_data.c	Tue Oct 30 11:15:43 2007 -0700
@@ -119,12 +119,12 @@
 { IP_BOUND_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, 0,
 	sizeof (int),	0 /* no ifindex */ },
 
-{ IP_XMIT_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, 0,
-	sizeof (int), 0 /* no ifindex */ },
-
 { IP_DONTFAILOVER_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, 0,
 	sizeof (struct in_addr),	0 /* not initialized */ },
 
+{ IP_DHCPINIT_IF, IPPROTO_IP, OA_R, OA_RW, OP_CONFIG, 0,
+	sizeof (int), 0 },
+
 { IP_UNSPEC_SRC, IPPROTO_IP, OA_R, OA_RW, OP_RAW, 0,
 	sizeof (int), 0 },
 
--- a/usr/src/uts/common/inet/ip_impl.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ip_impl.h	Tue Oct 30 11:15:43 2007 -0700
@@ -395,7 +395,6 @@
 	((connp)->conn_dontroute == 0 &&	/* SO_DONTROUTE */	\
 	!((connp)->conn_nexthop_set) &&		/* IP_NEXTHOP */	\
 	(connp)->conn_nofailover_ill == NULL &&	/* IPIF_NOFAILOVER */	\
-	(connp)->conn_xmit_if_ill == NULL &&	/* IP_XMIT_IF */	\
 	(connp)->conn_outgoing_pill == NULL &&	/* IP{V6}_BOUND_PIF */	\
 	(connp)->conn_outgoing_ill == NULL)	/* IP{V6}_BOUND_IF */
 
--- a/usr/src/uts/common/inet/ipclassifier.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/ipclassifier.h	Tue Oct 30 11:15:43 2007 -0700
@@ -226,8 +226,8 @@
 
 		conn_lso_ok : 1;		/* LSO is usable */
 
-	ill_t		*conn_xmit_if_ill;	/* Outbound ill */
 	ill_t		*conn_nofailover_ill;	/* Failover ill */
+	ill_t		*conn_dhcpinit_ill;	/* IP_DHCPINIT_IF */
 	ipsec_latch_t	*conn_latch;		/* latched state */
 	ill_t		*conn_outgoing_ill;	/* IP{,V6}_BOUND_IF */
 	edesc_spf	conn_send;		/* Pointer to send routine */
@@ -285,7 +285,6 @@
 	int		conn_orig_bound_ifindex; /* BOUND_IF before MOVE */
 	int		conn_orig_multicast_ifindex;
 						/* IPv6 MC IF before MOVE */
-	int		conn_orig_xmit_ifindex; /* IP_XMIT_IF before move */
 	struct conn_s 	*conn_drain_next;	/* Next conn in drain list */
 	struct conn_s	*conn_drain_prev;	/* Prev conn in drain list */
 	idl_t		*conn_idl;		/* Ptr to the drain list head */
--- a/usr/src/uts/common/inet/rawip_impl.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/rawip_impl.h	Tue Oct 30 11:15:43 2007 -0700
@@ -95,7 +95,6 @@
 	uint8_t		icmp_multicast_ttl;	/* IP*_MULTICAST_TTL/HOPS */
 	ipaddr_t	icmp_multicast_if_addr; /* IP_MULTICAST_IF option */
 	uint_t		icmp_multicast_if_index; /* IPV6_MULTICAST_IF option */
-	int		icmp_xmit_if;		/* IP_XMIT_IF option */
 	int		icmp_bound_if;		/* IP*_BOUND_IF option */
 
 	/* Written to only once at the time of opening the endpoint */
--- a/usr/src/uts/common/inet/tcp/tcp.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/tcp/tcp.c	Tue Oct 30 11:15:43 2007 -0700
@@ -18622,7 +18622,7 @@
 	    (connp->conn_flags & IPCL_CHECK_POLICY) == 0 &&
 	    connp->conn_dontroute == 0 &&
 	    !connp->conn_nexthop_set &&
-	    connp->conn_xmit_if_ill == NULL &&
+	    connp->conn_outgoing_ill == NULL &&
 	    connp->conn_nofailover_ill == NULL &&
 	    do_tcpzcopy == 1) {
 		/*
--- a/usr/src/uts/common/inet/udp/udp.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/udp/udp.c	Tue Oct 30 11:15:43 2007 -0700
@@ -2666,6 +2666,8 @@
 		case IP_TTL:
 			*i1 = (int)udp->udp_ttl;
 			break;	/* goto sizeof (int) option return */
+		case IP_DHCPINIT_IF:
+			return (-EINVAL);
 		case IP_NEXTHOP:
 		case IP_RECVPKTINFO:
 			/*
@@ -2725,9 +2727,6 @@
 		case IP_UNSPEC_SRC:
 			*i1 = udp->udp_unspec_source;
 			break;	/* goto sizeof (int) option return */
-		case IP_XMIT_IF:
-			*i1 = udp->udp_xmit_if;
-			break; /* goto sizeof (int) option return */
 		default:
 			return (-1);
 		}
@@ -3259,6 +3258,7 @@
 		case MCAST_LEAVE_SOURCE_GROUP:
 		case IP_SEC_OPT:
 		case IP_NEXTHOP:
+		case IP_DHCPINIT_IF:
 			/*
 			 * "soft" error (negative)
 			 * option not handled at this level
@@ -3273,10 +3273,6 @@
 			if (!checkonly)
 				udp->udp_unspec_source = onoff;
 			break;
-		case IP_XMIT_IF:
-			if (!checkonly)
-				udp->udp_xmit_if = *i1;
-			break;
 		default:
 			*outlenp = 0;
 			return (EINVAL);
@@ -5994,7 +5990,7 @@
 
 	if ((connp->conn_flags & IPCL_CHECK_POLICY) != 0 ||
 	    CONN_OUTBOUND_POLICY_PRESENT(connp, ipss) ||
-	    connp->conn_dontroute || connp->conn_xmit_if_ill != NULL ||
+	    connp->conn_dontroute ||
 	    connp->conn_nofailover_ill != NULL ||
 	    connp->conn_outgoing_ill != NULL || optinfo.ip_opt_flags != 0 ||
 	    optinfo.ip_opt_ill_index != 0 ||
--- a/usr/src/uts/common/inet/udp/udp_opt_data.c	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/udp/udp_opt_data.c	Tue Oct 30 11:15:43 2007 -0700
@@ -131,12 +131,13 @@
 
 { IP_BOUND_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
 	sizeof (int),	0 /* no ifindex */ },
-{ IP_XMIT_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
-	sizeof (int), 0 /* no ifindex */ },
 
 { IP_DONTFAILOVER_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
 	sizeof (struct in_addr),	0 /* not initialized */ },
 
+{ IP_DHCPINIT_IF, IPPROTO_IP, OA_R, OA_RW, OP_CONFIG, OP_PASSNEXT,
+	sizeof (int), 0 },
+
 { IP_UNSPEC_SRC, IPPROTO_IP, OA_R, OA_RW, OP_RAW, OP_PASSNEXT,
 	sizeof (int), 0 },
 
--- a/usr/src/uts/common/inet/udp_impl.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/inet/udp_impl.h	Tue Oct 30 11:15:43 2007 -0700
@@ -296,7 +296,6 @@
 	ipaddr_t	udp_multicast_if_addr;  /* IP_MULTICAST_IF option */
 	uint_t		udp_multicast_if_index;	/* IPV6_MULTICAST_IF option */
 	int		udp_bound_if;		/* IP*_BOUND_IF option */
-	int		udp_xmit_if;		/* IP_XMIT_IF option */
 
 	/* Written to only once at the time of opening the endpoint */
 	conn_t		*udp_connp;
--- a/usr/src/uts/common/netinet/in.h	Tue Oct 30 09:32:44 2007 -0700
+++ b/usr/src/uts/common/netinet/in.h	Tue Oct 30 11:15:43 2007 -0700
@@ -910,13 +910,9 @@
  * SunOS private (potentially not portable) IP_ option names
  */
 #define	IP_BOUND_IF		0x41	/* bind socket to an ifindex	   */
-#define	IP_UNSPEC_SRC		0x42	/* use unspecified source address   */
-/*
- * IP_XMIT_IF is used to send unicast/multicast packets through the specified
- * interface without looking at the routing table entries.
- * This is a Sun private interface.
- */
-#define	IP_XMIT_IF		0x43	/* use specified outgoing interface */
+#define	IP_UNSPEC_SRC		0x42	/* use unspecified source address  */
+/*	UNUSED			0x43	   was IP_XMIT_IF -- can be reused */
+
 /*
  * IP_DONTFAILOVER_IF option is used to indicate that outbound unicast and
  * multicast packets go through the specified interface, no load spreading,
@@ -925,6 +921,8 @@
  */
 #define	IP_DONTFAILOVER_IF	0x44
 
+#define	IP_DHCPINIT_IF		0x45	/* accept all unicast DHCP traffic */
+
 /*
  * Option values and names (when !_XPG5) shared with <xti_inet.h>
  */