changeset 3938:670947f6c3f6

PSARC 2007/136 Network Auto-Magic (NWAM) Phase 0 6355747 /lib/svc/method/net-svc makes a mess of hosts and ipnodes 6366093 "ifconfig <wireless-lan-device> dhcp" not enough to surf with browser 6539574 _link_aton() underallocates a buffer
author jbeck
date Fri, 30 Mar 2007 17:01:13 -0700
parents 8518e11fa028
children 8f689b97125d
files usr/src/Makefile.lint usr/src/Targetdirs usr/src/cmd/cmd-inet/Makefile usr/src/cmd/cmd-inet/Makefile.msg usr/src/cmd/cmd-inet/lib/Makefile usr/src/cmd/cmd-inet/lib/nwamd/Makefile usr/src/cmd/cmd-inet/lib/nwamd/defines.h usr/src/cmd/cmd-inet/lib/nwamd/events.c usr/src/cmd/cmd-inet/lib/nwamd/functions.h usr/src/cmd/cmd-inet/lib/nwamd/interface.c usr/src/cmd/cmd-inet/lib/nwamd/llp.c usr/src/cmd/cmd-inet/lib/nwamd/main.c usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c usr/src/cmd/cmd-inet/lib/nwamd/structures.h usr/src/cmd/cmd-inet/lib/nwamd/util.c usr/src/cmd/cmd-inet/lib/nwamd/variables.h usr/src/cmd/cmd-inet/lib/nwamd/wireless.c usr/src/cmd/svc/milestone/Makefile usr/src/cmd/svc/milestone/net-loopback usr/src/cmd/svc/milestone/net-nwam usr/src/cmd/svc/milestone/net-svc usr/src/cmd/svc/milestone/network-physical.xml usr/src/lib/libsecdb/auth_attr.txt usr/src/lib/libsecdb/help/auths/Makefile usr/src/lib/libsecdb/help/auths/SmfNWAMStates.html usr/src/lib/libsecdb/help/auths/SmfValueNWAM.html usr/src/lib/libsecdb/prof_attr.txt usr/src/lib/libsocket/inet/link_addr.c usr/src/pkgdefs/SUNW0on/prototype_com usr/src/pkgdefs/SUNWcsr/prototype_com usr/src/pkgdefs/SUNWcsu/prototype_com
diffstat 31 files changed, 5846 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/Makefile.lint	Fri Mar 30 17:01:13 2007 -0700
@@ -67,6 +67,7 @@
 	cmd/cksum \
 	cmd/clinfo \
 	cmd/cmd-crypto \
+	cmd/cmd-inet/lib \
 	cmd/cmd-inet/sbin \
 	cmd/cmd-inet/usr.bin \
 	cmd/cmd-inet/usr.lib/dsvclockd \
--- a/usr/src/Targetdirs	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/Targetdirs	Fri Mar 30 17:01:13 2007 -0700
@@ -203,6 +203,7 @@
 	/etc/sma \
 	/etc/sma/snmp \
 	/lib \
+	/lib/inet \
 	/lib/secure \
 	/lib/svc \
 	/lib/svc/bin \
--- a/usr/src/cmd/cmd-inet/Makefile	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/cmd/cmd-inet/Makefile	Fri Mar 30 17:01:13 2007 -0700
@@ -2,9 +2,8 @@
 # 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.
+# 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.
@@ -22,15 +21,16 @@
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
-include	../Makefile.cmd
+include		../Makefile.cmd
 
-SUBDIRS=	etc sbin usr.bin usr.sbin usr.lib usr.sadm
-MSGSUBDIRS=	usr.bin usr.sbin usr.lib usr.sadm
-POFILES=        usr.bin/usr.bin.po usr.sbin/usr.sbin.po usr.sadm/usr.sadm.po
+SUBDIRS=	etc lib sbin usr.bin usr.sbin usr.lib usr.sadm
+MSGSUBDIRS=	lib usr.bin usr.sbin usr.lib usr.sadm
+POFILES=	lib/lib.po usr.bin/usr.bin.po usr.sbin/usr.sbin.po \
+		usr.sadm/usr.sadm.po
 POFILE=		cmd-inet.po
 
 all:=		TARGET= all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/Makefile.msg	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,34 @@
+#
+# 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
+#
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+_msg:=          TARGET= _msg
+
+_msg: $(MSGSUBDIRS) .WAIT $(POFILE)
+
+$(POFILE): $(POFILES)
+	$(RM) $(POFILE)
+	$(CAT) $(POFILES) > $(POFILE)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/Makefile	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+SUBDIRS=	nwamd
+MSGSUBDIRS=	nwamd
+
+include 	../../Makefile.cmd
+
+POFILES=	$(MSGSUBDIRS:%=%/%.po)
+POFILE=		lib.po
+
+all:=		TARGET= all
+install:=	TARGET= install
+clean:=		TARGET= clean
+clobber:=	TARGET= clobber
+lint:=		TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include		../Makefile.msg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/Makefile	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,72 @@
+#
+# 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
+#
+
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# usr/src/cmd/cmd-inet/lib/nwamd/Makefile
+#
+
+# Needed for ROOTFS_LIBDIR definition
+include		../../../../lib/Makefile.lib
+
+PROG=		nwamd
+OBJS=		events.o interface.o llp.o main.o \
+		state_machine.o util.o wireless.o
+SRCS=		$(OBJS:%.o=%.c)
+HEADERS=	defines.h functions.h structures.h variables.h
+
+include		../../../Makefile.cmd
+
+POFILE=		$(PROG).po
+POFILES=	interface.po wireless.po
+
+ROOTCMDDIR=	$(ROOTFS_LIBDIR)/inet
+
+LDLIBS +=	-lsocket -lnsl -linetcfg -linetutil -lumem -lscf -ldladm
+
+.KEEP_STATE:
+
+.PARALLEL:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+	$(POST_PROCESS)
+
+install: $(ROOTCMD)
+
+check:  $(SRCS) $(HEADERS)
+	$(CSTYLE) -cpP $(SRCS) $(HEADERS)
+
+$(ROOTCMD): all
+
+clean:
+	$(RM) $(OBJS)
+
+lint:	lint_SRCS
+
+include		../../../Makefile.targ
+include		../../Makefile.msg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/defines.h	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,58 @@
+/*
+ * 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 _DEFINES_H
+#define	_DEFINES_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/time.h>
+
+#define	PKILL		"/usr/bin/pkill"
+#define	ZENITY		"/usr/bin/zenity"
+#define	SVCADM		"/usr/sbin/svcadm"
+#define	IFCONFIG	"/sbin/ifconfig"
+#define	NET_SVC_METHOD	"/lib/svc/method/net-svc"
+#define	NET_SVC_FMRI	"svc:/network/service:default"
+#define	PFEXEC		"/usr/bin/pfexec"
+
+#define	ULP_DIR		"/etc/nwam/ulp"
+#define	LLPDIR		"/etc/nwam"
+#define	LLPFILE		LLPDIR"/llp"
+#define	KNOWN_WIFI_NETS	LLPDIR"/known_wifi_nets"
+
+#define	BOOLEAN_TO_STRING(x) ((x) ? "TRUE" : "FALSE")
+#define	STRING(s) (((s) == NULL) ? "NULL" : (s))
+
+/* IPC listening port */
+#define	NP_LISTEN_PORT		12340
+
+#define	NWAM_DEFAULT_DHCP_WAIT_TIME	60	/* 1 minute */
+
+#define	TIMER_INFINITY		0xffffffff	/* we use uint32s for timers */
+#define	NSEC_TO_SEC(nsec)	(nsec) / NANOSEC
+
+#endif /* _DEFINES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/events.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,656 @@
+/*
+ * 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"
+
+/*
+ * This file contains routines to retrieve events from the system and package
+ * them for high level processing.
+ *
+ * struct np_event is the basic event structure.  The np_event structure and
+ * its npe_name member are allocated using malloc(3c).  free_event() frees both
+ * the npe_name member and the associated np_event structure.
+ *
+ * np_queue_add_event() and np_queue_get_event() provide functionality for
+ * adding events to a queue and blocking on that queue for an event.
+ *
+ * Functions of the form addevent_*() provide the mechanism to cook down a
+ * higher level event into an np_event and put it on the queue.
+ *
+ * routing_events() reads routing messages off of an IPv4 routing socket and
+ * by calling addevent_*() functions places appropriate events on the queue.
+ *
+ * start_event_collection() creates a thread to run routing_events() and one
+ * to run periodic_wireless_scan() in.  Finally it does an initial collection
+ * of information from each interface currently known.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <libsysevent.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <sys/sysevent/eventdefs.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+struct np_event *equeue = NULL;
+static struct np_event *equeue_end = NULL;
+
+pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
+pthread_t routing, scan;
+
+static void printaddrs(int mask, void *address);
+static char *printaddr(void **address);
+static struct sockaddr *dupsockaddr(struct sockaddr *);
+static boolean_t cmpsockaddr(struct sockaddr *, struct sockaddr *);
+static void *getaddr(int addrid, int mask, void *address);
+
+union rtm_buf
+{
+	/* Routing information. */
+	struct
+	{
+		struct rt_msghdr rtm;
+		struct sockaddr_storage addr[RTAX_MAX];
+	} r;
+
+	/* Interface information. */
+	struct
+	{
+		struct if_msghdr ifm;
+		struct sockaddr_storage addr[RTAX_MAX];
+	} im;
+
+	/* Interface address information. */
+	struct
+	{
+		struct ifa_msghdr ifa;
+		struct sockaddr_storage addr[RTAX_MAX];
+	} ia;
+};
+
+void
+free_event(struct np_event *e)
+{
+	free(e->npe_name);
+	free(e);
+}
+
+void
+np_queue_add_event(struct np_event *e)
+{
+	(void) pthread_mutex_lock(&queue_mutex);
+	if (equeue_end != NULL) {
+		equeue_end->npe_next = e;
+		equeue_end = e;
+	} else {
+		equeue = equeue_end = e;
+	}
+	equeue_end->npe_next = NULL;
+	(void) pthread_cond_signal(&queue_cond);
+	(void) pthread_mutex_unlock(&queue_mutex);
+}
+
+/*
+ * Blocking getevent.  This routine will block until there is an event for
+ * it to return.
+ */
+struct np_event *
+np_queue_get_event(void)
+{
+	struct np_event *rv = NULL;
+
+	(void) pthread_mutex_lock(&queue_mutex);
+
+	while (equeue == NULL)
+		(void) pthread_cond_wait(&queue_cond, &queue_mutex);
+
+	rv = equeue;
+	equeue = equeue->npe_next;
+	if (equeue == NULL)
+		equeue_end = NULL;
+
+	(void) pthread_mutex_unlock(&queue_mutex);
+
+	rv->npe_next = NULL;
+	return (rv);
+}
+
+const char *
+npe_type_str(enum np_event_type type)
+{
+	switch (type) {
+		case EV_ROUTING:
+			return ("ROUTING");
+		case EV_SYS:
+			return ("SYS");
+		case EV_TIMER:
+			return ("TIMER");
+		case EV_SHUTDOWN:
+			return ("SHUTDOWN");
+		case EV_NEWADDR:
+			return ("NEWADDR");
+		default:
+			return ("unknown");
+	}
+}
+
+static void
+addevent_routing_ifa(struct ifa_msghdr *ifa, const char *name)
+{
+	struct np_event *e;
+
+	dprintf("addevent_routing_ifa");
+	if (ifa->ifam_index == 0) {
+		/* what is this? */
+		dprintf("tossing index 0 routing event");
+		return;
+	}
+
+	e = calloc(1, sizeof (*e));
+	if (e == NULL) {
+		syslog(LOG_ERR, "calloc failed");
+		return;
+	}
+
+	switch (ifa->ifam_type) {
+	case RTM_NEWADDR:
+		assert(name != NULL);
+		e->npe_type = EV_NEWADDR;
+		if ((e->npe_name = strdup(name)) == NULL) {
+			syslog(LOG_ERR, "strdup failed");
+			free(e);
+			return;
+		}
+		dprintf("adding event type %s name %s to queue",
+		    npe_type_str(e->npe_type), STRING(e->npe_name));
+		np_queue_add_event(e);
+		break;
+
+	default:
+		free(e);
+		dprintf("unhandled type in addevent_routing_ifa %d",
+		    ifa->ifam_type);
+		break;
+	}
+}
+
+static void
+addevent_routing_msghdr(struct if_msghdr *ifm, const char *name)
+{
+	struct np_event *e;
+
+	dprintf("addevent_routing_msghdr");
+	if (ifm->ifm_index == 0) {
+		/* what is this? */
+		dprintf("tossing index 0 routing event");
+		return;
+	}
+
+	switch (ifm->ifm_type) {
+	case RTM_IFINFO:
+		assert(name != NULL);
+		e = calloc(1, sizeof (*e));
+		if (e == NULL) {
+			syslog(LOG_ERR, "calloc failed");
+			return;
+		}
+
+		e->npe_type = EV_ROUTING;
+		if ((e->npe_name = strdup(name)) == NULL) {
+			syslog(LOG_ERR, "strdup failed");
+			free(e);
+			return;
+		}
+		dprintf("flags = %x, IFF_RUNNING = %x", ifm->ifm_flags,
+		    IFF_RUNNING);
+		dprintf("adding event type %s name %s to queue",
+		    npe_type_str(e->npe_type), STRING(e->npe_name));
+		np_queue_add_event(e);
+		break;
+
+	default:
+		dprintf("unhandled type in addevent_routing_msghdr %d",
+		    ifm->ifm_type);
+		break;
+	}
+}
+
+static const char *
+rtmtype_str(int type)
+{
+	static char typestr[12]; /* strlen("type ") + enough for an int */
+
+	switch (type) {
+		case RTM_ADD:
+			return ("ADD");
+		case RTM_DELETE:
+			return ("DELETE");
+		case RTM_NEWADDR:
+			return ("NEWADDR");
+		case RTM_DELADDR:
+			return ("DELADDR");
+		case RTM_IFINFO:
+			return ("IFINFO");
+		default:
+			(void) snprintf(typestr, sizeof (typestr), "type %d",
+			    type);
+			return (typestr);
+	}
+}
+
+/* ARGSUSED */
+static void *
+routing_events(void *arg)
+{
+	int rtsock;
+	int n;
+	union rtm_buf buffer;
+	struct rt_msghdr *rtm;
+	struct ifa_msghdr *ifa;
+	struct if_msghdr *ifm;
+
+	/*
+	 * We use v4 interfaces as proxies for links so those are the only
+	 * routing messages we need to listen to.  Look at the comments in
+	 * structures.h for more information about the split between the
+	 * llp and interfaces.
+	 */
+	rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
+	if (rtsock == -1) {
+		syslog(LOG_ERR, "failed to open routing socket: %m");
+		exit(EXIT_FAILURE);
+	}
+
+	dprintf("routing socket %d", rtsock);
+
+	for (;;) {
+		struct interface *ifp;
+		char *addrs, *if_name;
+		struct sockaddr_dl *addr_dl;
+		struct sockaddr *addr;
+
+		rtm = &buffer.r.rtm;
+		n = read(rtsock, &buffer, sizeof (buffer));
+		if (n == -1 && errno == EAGAIN) {
+			continue;
+		} else if (n == -1) {
+			syslog(LOG_ERR, "error reading routing socket "
+			    "%d: %m", rtsock);
+			/* Low likelihood.  What's recovery path?  */
+			continue;
+		}
+
+		if (rtm->rtm_msglen < n) {
+			syslog(LOG_ERR, "only read %d bytes from "
+			    "routing socket but message claims to be "
+			    "of length %d", rtm->rtm_msglen);
+			continue;
+		}
+
+		if (rtm->rtm_version != RTM_VERSION) {
+			syslog(LOG_ERR, "tossing routing message of "
+			    "version %d type %d", rtm->rtm_version,
+			    rtm->rtm_type);
+			continue;
+		}
+
+		if (rtm->rtm_msglen != n) {
+			dprintf("routing message of %d size came from "
+			    "read of %d on socket %d", rtm->rtm_msglen,
+			    n, rtsock);
+		}
+
+		switch (rtm->rtm_type) {
+		case RTM_NEWADDR:
+			ifa = (void *)rtm;
+			addrs = (char *)ifa + sizeof (*ifa);
+
+			dprintf("routing message NEWADDR: index %d flags %x",
+			    ifa->ifam_index, ifa->ifam_flags);
+			printaddrs(ifa->ifam_addrs, addrs);
+
+			if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
+			    ifa->ifam_addrs, addrs)) == NULL)
+				break;
+
+			if ((addr_dl = (struct sockaddr_dl *)getaddr
+			    (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
+				break;
+			if_name = addr_dl->sdl_data;
+			ifp = get_interface(if_name);
+			if (ifp == NULL) {
+				dprintf("no interface struct for %s; ignoring "
+				    "message", STRING(if_name));
+				break;
+			}
+
+			/* if no cached address, cache it */
+			if (ifp->if_ipaddr == NULL) {
+				ifp->if_ipaddr = dupsockaddr(addr);
+				dprintf("cached address %s for link %s",
+				    printaddr((void **)&addr), if_name);
+				addevent_routing_ifa(ifa, if_name);
+			} else if (!cmpsockaddr(addr, ifp->if_ipaddr)) {
+				free(ifp->if_ipaddr);
+				ifp->if_ipaddr = dupsockaddr(addr);
+				addevent_routing_ifa(ifa, if_name);
+			}
+			break;
+		case RTM_IFINFO:
+		{
+			boolean_t plugged_in;
+
+			ifm = (void *)rtm;
+			addrs = (char *)ifm + sizeof (*ifm);
+			dprintf("routing message IFINFO: index %d flags %x",
+			    ifm->ifm_index, ifm->ifm_flags);
+			printaddrs(ifm->ifm_addrs, addrs);
+
+			if ((addr_dl = (struct sockaddr_dl *)getaddr(RTA_IFP,
+			    ifm->ifm_addrs, addrs)) == NULL)
+				break;
+			if_name = addr_dl->sdl_data;
+			ifp = get_interface(if_name);
+			if (ifp == NULL) {
+				dprintf("no interface struct for %s; ignoring "
+				    "message", STRING(if_name));
+				break;
+			}
+
+			/*
+			 * Check for toggling of the IFF_RUNNING
+			 * flag.
+			 *
+			 * On any change in the flag value, we
+			 * turn off the DHCPFAILED and DHCPSTARTED
+			 * flags; the change in the RUNNING state
+			 * indicates a "fresh start" for the
+			 * interface, so we should try dhcp again.
+			 *
+			 * Ignore specific IFF_RUNNING changes for
+			 * wireless interfaces; their semantics are
+			 * a bit different (either the flag is always
+			 * on, or, with newer drivers, it indicates
+			 * whether or not they are connected to an AP).
+			 *
+			 * For wired interfaces, if the interface was
+			 * not plugged in and now it is, start info
+			 * collection.
+			 *
+			 * If it was plugged in and now it is
+			 * unplugged, generate an event.
+			 *
+			 * XXX We probably need a lock to protect
+			 * if_flags setting and getting.
+			 */
+			if ((ifp->if_flags & IFF_RUNNING) !=
+			    (ifm->ifm_flags & IFF_RUNNING)) {
+				ifp->if_lflags &= ~IF_DHCPFAILED;
+				ifp->if_lflags &= ~IF_DHCPSTARTED;
+			}
+			if (ifp->if_type == IF_WIRELESS)
+				break;
+			plugged_in =
+			    ((ifp->if_flags & IFF_RUNNING) != 0);
+			ifp->if_flags = ifm->ifm_flags;
+			if (!plugged_in &&
+			    (ifm->ifm_flags & IFF_RUNNING)) {
+				start_if_info_collect(ifp, NULL);
+			} else if (plugged_in &&
+			    !(ifm->ifm_flags & IFF_RUNNING)) {
+				check_drop_dhcp(ifp);
+				addevent_routing_msghdr(ifm, if_name);
+			}
+			break;
+		}
+		default:
+			dprintf("routing message %s socket %d discarded",
+			    rtmtype_str(rtm->rtm_type), rtsock);
+			break;
+		}
+	}
+	/* NOTREACHED */
+	return (NULL);
+}
+
+/* return B_TRUE if sin_family and sin_addr are the same, B_FALSE if not */
+static boolean_t
+cmpsockaddr(struct sockaddr *addr1, struct sockaddr *addr2)
+{
+	struct sockaddr_in *sina, *sinb;
+	struct sockaddr_in6 *sin6a, *sin6b;
+
+	if (addr1->sa_family != addr2->sa_family)
+		return (B_FALSE);
+
+	switch (addr1->sa_family) {
+	case AF_INET:
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		sina = (struct sockaddr_in *)addr1;
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		sinb = (struct sockaddr_in *)addr2;
+		return (sina->sin_addr.s_addr == sinb->sin_addr.s_addr);
+	case AF_INET6:
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		sin6a = (struct sockaddr_in6 *)addr1;
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		sin6b = (struct sockaddr_in6 *)addr2;
+		return
+		    (IN6_ARE_ADDR_EQUAL(&sin6a->sin6_addr, &sin6b->sin6_addr));
+	default:
+		dprintf("cmpsockaddr: unsupported af (%d)", addr1->sa_family);
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Duplicate a sockaddr. Caller will be responsible for freeing memory when it
+ * is no longer needed. Currently only supports AF_INET and AF_INET6
+ * (returns NULL otherwise).
+ */
+static struct sockaddr *
+dupsockaddr(struct sockaddr *addr)
+{
+	struct sockaddr_in *t1, *ret1;
+	struct sockaddr_in6 *t2, *ret2;
+
+	switch (addr->sa_family) {
+	case AF_INET:
+		if ((ret1 = calloc(1, sizeof (struct sockaddr_in))) == NULL) {
+			syslog(LOG_ERR, "dupsockaddr: calloc failed");
+			return (NULL);
+		}
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		t1 = (struct sockaddr_in *)addr;
+		ret1->sin_family = t1->sin_family;
+		ret1->sin_addr.s_addr = t1->sin_addr.s_addr;
+		return ((struct sockaddr *)ret1);
+	case AF_INET6:
+		if ((ret2 = calloc(1, sizeof (struct sockaddr_in6))) == NULL) {
+			syslog(LOG_ERR, "dupsockaddr: calloc failed");
+			return (NULL);
+		}
+		/* LINTED E_BAD_PTR_CAST_ALIGN */
+		t2 = (struct sockaddr_in6 *)addr;
+		ret2->sin6_family = t2->sin6_family;
+		(void) memcpy((void *)&ret2->sin6_addr,
+		    (const void *)&t2->sin6_addr, sizeof (struct in6_addr));
+		return ((struct sockaddr *)ret2);
+	default:
+		dprintf("dupsockaddr: unsupported af (%d)", addr->sa_family);
+		return (NULL);
+	}
+}
+
+static char *
+printaddr(void **address)
+{
+	static char buffer[80];
+	sa_family_t family = *(sa_family_t *)*address;
+	struct sockaddr_in *s4 = *address;
+	struct sockaddr_in6 *s6 = *address;
+	struct sockaddr_dl *dl = *address;
+
+	switch (family) {
+		case AF_UNSPEC:
+			(void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
+			    sizeof (buffer));
+			*address = (char *)*address + sizeof (*s4);
+			break;
+		case AF_INET:
+			(void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
+			    sizeof (buffer));
+			*address = (char *)*address + sizeof (*s4);
+			break;
+		case AF_INET6:
+			(void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
+			    sizeof (buffer));
+			*address = (char *)*address + sizeof (*s6);
+			break;
+		case AF_LINK:
+			(void) snprintf(buffer, sizeof (buffer), "link %s",
+			    dl->sdl_data);
+			*address = (char *)*address + sizeof (*dl);
+			break;
+		default:
+			/*
+			 * We can't reliably update the size of this thing
+			 * because we don't know what its type is.  So bump
+			 * it by a sockaddr_in and see what happens.  The
+			 * caller should really make sure this never happens.
+			 */
+			*address = (char *)*address + sizeof (*s4);
+			(void) snprintf(buffer, sizeof (buffer),
+			    "unknown address family %d", family);
+			break;
+	}
+	return (buffer);
+}
+
+static void
+printaddrs(int mask, void *address)
+{
+	if (mask == 0)
+		return;
+	if (mask & RTA_DST)
+		dprintf("destination address: %s", printaddr(&address));
+	if (mask & RTA_GATEWAY)
+		dprintf("gateway address: %s", printaddr(&address));
+	if (mask & RTA_NETMASK)
+		dprintf("netmask: %s", printaddr(&address));
+	if (mask & RTA_GENMASK)
+		dprintf("cloning mask: %s", printaddr(&address));
+	if (mask & RTA_IFP)
+		dprintf("interface name: %s", printaddr(&address));
+	if (mask & RTA_IFA)
+		dprintf("interface address: %s", printaddr(&address));
+	if (mask & RTA_AUTHOR)
+		dprintf("author: %s", printaddr(&address));
+	if (mask & RTA_BRD)
+		dprintf("broadcast address: %s", printaddr(&address));
+}
+
+static void
+nextaddr(void **address)
+{
+	sa_family_t family = *(sa_family_t *)*address;
+
+	switch (family) {
+	case AF_UNSPEC:
+	case AF_INET:
+		*address = (char *)*address + sizeof (struct sockaddr_in);
+		break;
+	case AF_INET6:
+		*address = (char *)*address + sizeof (struct sockaddr_in6);
+		break;
+	case AF_LINK:
+		*address = (char *)*address + sizeof (struct sockaddr_dl);
+		break;
+	default:
+		syslog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
+		break;
+	}
+}
+
+static void *
+getaddr(int addrid, int mask, void *address)
+{
+	int i;
+	void *p = address;
+
+	if ((mask & addrid) == 0)
+		return (NULL);
+
+	for (i = 1; i < addrid; i <<= 1) {
+		if (i & mask)
+			nextaddr(&p);
+	}
+	return (p);
+}
+
+boolean_t
+start_event_collection(void)
+{
+	int err;
+
+	/*
+	 * if these are ever created/destroyed repetitively then we will
+	 * have to change this.
+	 */
+
+	if (err = pthread_create(&routing, NULL, routing_events, NULL)) {
+		syslog(LOG_ERR, "pthread_create routing: %s", strerror(err));
+		exit(EXIT_FAILURE);
+	} else {
+		dprintf("routing thread: %d", routing);
+	}
+
+	if (err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL)) {
+		syslog(LOG_ERR, "pthread_create wireless scan: %s",
+		    strerror(err));
+		exit(EXIT_FAILURE);
+	} else {
+		dprintf("scan thread: %d", scan);
+	}
+
+	walk_interface(start_if_info_collect, NULL);
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/functions.h	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,102 @@
+/*
+ * 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 _FUNCTIONS_H
+#define	_FUNCTIONS_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/* events.c: event queue handling */
+extern void free_event(struct np_event *);
+extern void np_queue_add_event(struct np_event *);
+extern struct np_event *np_queue_get_event(void);
+extern const char *npe_type_str(enum np_event_type);
+extern boolean_t start_event_collection(void);
+
+/* interface.c: interface and upper layer profile handling */
+extern void check_drop_dhcp(struct interface *);
+extern void gen_newif_event(struct interface *);
+extern void initialize_interfaces(void);
+extern struct interface *add_interface(sa_family_t, const char *, uint64_t);
+extern struct interface *get_interface(const char *);
+extern void walk_interface(void (*)(struct interface *, void *), void *);
+extern enum interface_type find_if_type(const char *);
+extern const char *if_type_str(enum interface_type);
+extern boolean_t interface_is_active(const struct interface *);
+extern void show_if_status(const char *);
+extern boolean_t bringupinterface(const char *, const char *, const char *,
+    boolean_t);
+extern void takedowninterface(const char *, boolean_t, boolean_t, boolean_t);
+extern void take_down_all_ifs(const char *);
+extern void check_interface_timer(struct interface *, void *);
+extern void start_if_info_collect(struct interface *, void *);
+extern boolean_t ulp_is_active(void);
+extern void activate_upper_layer_profile(boolean_t, const char *);
+extern void deactivate_upper_layer_profile(void);
+extern void display(const char *);
+extern int lookup_boolean_property(const char *, const char *, boolean_t *);
+extern int lookup_count_property(const char *, const char *, uint64_t *);
+
+/* wireless.c: wifi link handling */
+extern void init_mutexes(void);
+extern int get_user_preference(const char *, const char *, const char *,
+    struct wireless_lan **, const struct wireless_lan *);
+extern boolean_t connect_chosen_lan(struct wireless_lan *, const char *);
+struct wireless_lan *prompt_for_visited(void);
+boolean_t handle_wireless_lan(const char *);
+extern boolean_t scan_wireless_nets(struct interface *);
+extern void create_known_wifi_nets_file(void);
+extern void update_known_wifi_nets_file(const char *, const char *);
+extern void *periodic_wireless_scan(void *);
+extern boolean_t known_wifi_nets_lookup(const char *, const char *);
+
+/* llp.c: link layer profile handling */
+extern void llp_parse_config(void);
+extern llp_t *llp_lookup(const char *);
+extern llp_t *llp_high_pri(llp_t *, llp_t *);
+extern llp_t *llp_best_avail(void);
+extern boolean_t llp_activate(llp_t *);
+extern void llp_deactivate(void);
+extern void llp_swap(llp_t *);
+extern char *llp_prnm(llp_t *);
+
+/* state_machine.c: state machine handling */
+extern void state_machine(struct np_event *);
+extern void cleanup(void);
+
+/* util.c: utility & ipc functions */
+/* PRINTFLIKE1 */
+extern void dprintf(const char *fmt, ...);
+extern uint32_t getcurrenttime(void);
+extern uint64_t get_ifflags(const char *, sa_family_t);
+extern boolean_t is_plugged_in(struct interface *);
+extern int start_childv(const char *command, char const * const *argv);
+extern int start_child(const char *command, ...);
+extern void start_timer(uint32_t,  uint32_t);
+extern boolean_t valid_graphical_user(boolean_t);
+extern void lookup_zonename(char *zonename, size_t zonesize);
+
+#endif /* _FUNCTIONS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/interface.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,1171 @@
+/*
+ * 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"
+
+/*
+ * This file contains the routines that manipulate interfaces, the
+ * list of interfaces present on the system, and upper layer profiles;
+ * and various support functions.  It also contains the functions used
+ * to display various bits of informations and queries for the user
+ * using /usr/bin/zenity, and a set of functions to read property
+ * values stored in the SMF repository.  Finally, it contains the
+ * functions required for the "gather info" threads.
+ *
+ * The daemon maintains a list of structures that represent each IPv4
+ * interface found on the system (after doing 'ifconfig -a plumb').
+ * This list represents the objects manipulated by the daemon; while
+ * the list of llp_t structures represents the configuration details
+ * requested by the user (either the automatic defaults or entries in
+ * /etc/nwam/llp).  IPv6 interfaces are not tracked in the interfaces
+ * list; rather, when the decision is made to make an interface active,
+ * IPv6 is brought up in addition to IPv4 (assuming the LLP configuration
+ * includes IPv6; this is the default for automatic configuration).
+ *
+ * Interfaces are brought up and torn down by a sequence of ifconfig
+ * commands (currently posix_spawn'd() by nwamd; the longer-term direction
+ * here is to use libinetcfg).
+ *
+ * Upper Layer Profile management is controlled by user-provided scripts,
+ * which should be created in /etc/nwam/ulp.  One script,
+ * /etc/nwam/ulp/check-conditions, checks the current network setup and
+ * returns the name of the ULP which should be active under the current
+ * conditions.  A ULP is specified by two scripts, found in
+ * /etc/nwam/ulp/<ulp name>: bringup and teardown.  All scripts are
+ * optional; if they do not exist or are not executable, nwamd will
+ * simply move on.
+ *
+ * When an interface has been successfully brought up (signalled by the
+ * assignment of an IP address to the interface), the daemon will first
+ * teardown the existing ULP (if there is one) by running the teardown
+ * script for that ULP.  It will then run the check-conditions script;
+ * if the name of a ULP is returned, it runs the bringup script for that
+ * ULP.
+ *
+ * A "gather info" thread is initiated for an interface when it becomes
+ * available.  For a wired interface, "available" means the IFF_RUNNING
+ * flag is set; wireless interfaces are considered to always be available,
+ * so a wireless interface's gather info thread will run once, when it is
+ * found at startup.  This thread will do a scan on a wireless interface,
+ * and initiate DHCP on a wired interface.  It will then generate an event
+ * for the state machine that indicates the availability of a new interface.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/sockio.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <libscf.h>
+#include <utmpx.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <inetcfg.h>
+#include <locale.h>
+#include <libintl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <sys/sysmacros.h>
+#include <libdllink.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+static struct interface *ifs_head = NULL;
+static struct interface *ifs_wired = NULL;
+static struct interface *ifs_wireless = NULL;
+
+static char upper_layer_profile[MAXHOSTNAMELEN];
+
+static void print_interface_list();
+static struct interface *get_next_interface(struct interface *);
+
+#define	IPFILTER_FMRI	"svc:/network/ipfilter:default"
+#define	LOOPBACK_IF	"lo0"
+
+void
+display(const char *msg)
+{
+	char cmd[1024];
+
+	dprintf("display('%s')", STRING(msg));
+	if (valid_graphical_user(B_FALSE)) {
+		(void) snprintf(cmd, sizeof (cmd), "--text=\"%s\"", msg);
+		(void) start_child(ZENITY, "--info", cmd, NULL);
+	} else {
+		syslog(LOG_INFO, "%s", msg);
+	}
+}
+
+void
+show_if_status(const char *ifname)
+{
+	char msg[128];
+	icfg_if_t intf;
+	icfg_handle_t h;
+	struct sockaddr_in sin;
+	socklen_t addrlen = sizeof (struct sockaddr_in);
+	int prefixlen = 0;
+
+	(void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name));
+	/* We only display new addr info for v4 interfaces */
+	intf.if_protocol = AF_INET;
+	if (icfg_open(&h, &intf) != ICFG_SUCCESS) {
+		syslog(LOG_ERR, "icfg_open failed on interface %s", ifname);
+		return;
+	}
+	if (icfg_get_addr(h, (struct sockaddr *)&sin, &addrlen, &prefixlen,
+	    B_TRUE) != ICFG_SUCCESS) {
+		syslog(LOG_ERR, "icfg_get_addr failed on interface %s", ifname);
+		icfg_close(h);
+		return;
+	}
+	icfg_close(h);
+	(void) snprintf(msg, sizeof (msg),
+	    gettext("brought interface %s up, got address %s"), ifname,
+	    inet_ntoa(sin.sin_addr));
+	display(msg);
+}
+
+/*
+ * If this interface matches the currently active llp, return B_TRUE.
+ * Otherwise, return B_FALSE.
+ */
+boolean_t
+interface_is_active(const struct interface *ifp)
+{
+	if (link_layer_profile == NULL || ifp == NULL)
+		return (B_FALSE);
+
+	return (strcmp(ifp->if_name, link_layer_profile->llp_lname) == 0);
+}
+
+/*
+ * Execute 'ifconfig ifname dhcp wait 0'.
+ */
+static void
+start_dhcp(struct interface *ifp)
+{
+	int res;
+	uint32_t now_s;
+	uint64_t timer_s;
+
+	if ((ifp->if_lflags & IF_DHCPSTARTED) != 0) {
+		dprintf("start_dhcp: already started; returning");
+		return;
+	}
+	ifp->if_lflags |= IF_DHCPSTARTED;
+
+	(void) start_child(IFCONFIG, ifp->if_name, "dhcp", "wait", "0", NULL);
+
+	/* start dhcp timer */
+	res = lookup_count_property(OUR_PG, "dhcp_wait_time", &timer_s);
+	if (res == -1)
+		timer_s = NWAM_DEFAULT_DHCP_WAIT_TIME;
+
+	now_s = NSEC_TO_SEC(gethrtime());
+	ifp->if_timer_expire = now_s + timer_s;
+
+	start_timer(now_s, timer_s);
+}
+
+static boolean_t
+check_svc_up(const char *fmri, int wait_time)
+{
+	int i;
+	char *state;
+
+	for (i = 1; i <= wait_time; i++) {
+		state = smf_get_state(fmri);
+		if (strcmp(SCF_STATE_STRING_ONLINE, state) == 0) {
+			free(state);
+			return (B_TRUE);
+		}
+		free(state);
+		(void) sleep(1);
+	}
+	return (B_FALSE);
+}
+
+boolean_t
+ulp_is_active(void)
+{
+	return (upper_layer_profile[0] != '\0');
+}
+
+/*
+ * Inputs:
+ *   res is a pointer to the scf_resources_t to be released.
+ */
+static void
+release_scf_resources(scf_resources_t *res)
+{
+	scf_value_destroy(res->sr_val);
+	scf_property_destroy(res->sr_prop);
+	scf_pg_destroy(res->sr_pg);
+	scf_snapshot_destroy(res->sr_snap);
+	scf_instance_destroy(res->sr_inst);
+	(void) scf_handle_unbind(res->sr_handle);
+	scf_handle_destroy(res->sr_handle);
+}
+
+/*
+ * Inputs:
+ *   lpg is the property group to look up
+ *   lprop is the property within that group to look up
+ * Outputs:
+ *   res is a pointer to an scf_resources_t.  This is an internal
+ *   structure that holds all the handles needed to get a specific
+ *   property from the running snapshot; on a successful return it
+ *   contains the scf_value_t that should be passed to the desired
+ *   scf_value_get_foo() function, and must be freed after use by
+ *   calling release_scf_resources().  On a failure return, any
+ *   resources that may have been assigned to res are released, so
+ *   the caller does not need to do any cleanup in the failure case.
+ * Returns:
+ *    0 on success
+ *   -1 on failure
+ */
+static int
+get_property_value(const char *lpg, const char *lprop, scf_resources_t *res)
+{
+	res->sr_inst = NULL;
+	res->sr_snap = NULL;
+	res->sr_pg = NULL;
+	res->sr_prop = NULL;
+	res->sr_val = NULL;
+
+	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
+		syslog(LOG_ERR, "scf_handle_create() failed: %s",
+		    scf_strerror(scf_error()));
+		return (-1);
+	}
+
+	if (scf_handle_bind(res->sr_handle) != 0) {
+		scf_handle_destroy(res->sr_handle);
+		syslog(LOG_ERR, "scf_handle_destroy() failed: %s",
+		    scf_strerror(scf_error()));
+		return (-1);
+	}
+	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
+		syslog(LOG_ERR, "scf_instance_create() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if (scf_handle_decode_fmri(res->sr_handle, OUR_FMRI, NULL, NULL,
+	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
+		syslog(LOG_ERR, "scf_handle_decode_fmri() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
+		syslog(LOG_ERR, "scf_snapshot_create() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if (scf_instance_get_snapshot(res->sr_inst, "running",
+	    res->sr_snap) != 0) {
+		syslog(LOG_ERR, "scf_instance_get_snapshot() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
+		syslog(LOG_ERR, "scf_pg_create() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if (scf_instance_get_pg_composed(res->sr_inst, res->sr_snap, lpg,
+	    res->sr_pg) != 0) {
+		syslog(LOG_ERR, "scf_instance_get_pg_composed(%s) failed: %s",
+		    lpg, scf_strerror(scf_error()));
+		goto failure;
+	}
+	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
+		syslog(LOG_ERR, "scf_property_create() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if (scf_pg_get_property(res->sr_pg, lprop, res->sr_prop) != 0) {
+		syslog(LOG_ERR, "scf_pg_get_property(%s) failed: %s",
+		    lprop, scf_strerror(scf_error()));
+		goto failure;
+	}
+	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
+		syslog(LOG_ERR, "scf_value_create() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
+		syslog(LOG_ERR, "scf_property_get_value() failed: %s",
+		    scf_strerror(scf_error()));
+		goto failure;
+	}
+	return (0);
+
+failure:
+	release_scf_resources(res);
+	return (-1);
+}
+
+/*
+ * Inputs:
+ *   lpg is the property group to look up
+ *   lprop is the property within that group to look up
+ * Outputs:
+ *   answer is a pointer to the property value
+ * Returns:
+ *    0 on success
+ *   -1 on failure
+ * If successful, the property value is retured in *answer.
+ * Otherwise, *answer is undefined, and it is up to the caller to decide
+ * how to handle that case.
+ */
+int
+lookup_boolean_property(const char *lpg, const char *lprop, boolean_t *answer)
+{
+	int result = -1;
+	scf_resources_t res;
+	uint8_t prop_val;
+
+	if (get_property_value(lpg, lprop, &res) != 0) {
+		/*
+		 * an error was already logged by get_property_value,
+		 * and it released any resources assigned to res before
+		 * returning.
+		 */
+		return (result);
+	}
+	if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
+		syslog(LOG_ERR, "scf_value_get_boolean() failed: %s",
+		    scf_strerror(scf_error()));
+		goto cleanup;
+	}
+	*answer = (boolean_t)prop_val;
+	dprintf("lookup_boolean_property(%s, %s) returns %s", lpg, lprop,
+	    *answer ? "TRUE" : "FALSE");
+	result = 0;
+cleanup:
+	release_scf_resources(&res);
+	return (result);
+}
+
+/*
+ * Inputs:
+ *   lpg is the property group to look up
+ *   lprop is the property within that group to look up
+ * Outputs:
+ *   answer is a pointer to the property value
+ * Returns:
+ *    0 on success
+ *   -1 on failure
+ * If successful, the property value is retured in *answer.
+ * Otherwise, *answer is undefined, and it is up to the caller to decide
+ * how to handle that case.
+ */
+int
+lookup_count_property(const char *lpg, const char *lprop, uint64_t *answer)
+{
+	int result = -1;
+	scf_resources_t res;
+
+	if (get_property_value(lpg, lprop, &res) != 0) {
+		/*
+		 * an error was already logged by get_property_value,
+		 * and it released any resources assigned to res before
+		 * returning.
+		 */
+		return (result);
+	}
+	if (scf_value_get_count(res.sr_val, answer) != 0) {
+		syslog(LOG_ERR, "scf_value_get_count() failed: %s",
+		    scf_strerror(scf_error()));
+		goto cleanup;
+	}
+	dprintf("lookup_count_property(%s, %s) returns %lld", lpg, lprop,
+	    *answer);
+	result = 0;
+cleanup:
+	release_scf_resources(&res);
+	return (result);
+}
+
+void
+activate_upper_layer_profile(boolean_t do_dhcp, const char *ifname)
+{
+	FILE *f;
+	char buffer[1024];
+	size_t buflen;
+	size_t offset;
+	const char bringup[] = "/bringup";
+
+	if (do_dhcp) {
+		boolean_t should;
+		int res;
+
+		res = lookup_boolean_property(OUR_PG, "use_net_svc", &should);
+		/*
+		 * If the look-up failed, try anyway: only avoid this if we
+		 * know for sure not to.
+		 */
+		if ((res == 0 && should) || (res == -1)) {
+			if (check_svc_up(NET_SVC_FMRI, 5)) {
+				(void) start_child(NET_SVC_METHOD, "start",
+				    ifname, NULL);
+			} else {
+				syslog(LOG_WARNING, "timed out when waiting "
+				    "for %s to come up, start method %s not "
+				    "executed", NET_SVC_FMRI, NET_SVC_METHOD);
+			}
+		}
+	}
+	f = popen(ULP_DIR "/check-conditions", "r");
+	if (f == NULL)
+		return;
+	/*
+	 * We want to build a path to the user's upper layer profile script
+	 * that looks like ULP_DIR "/<string we read here>/bringup".  If we
+	 * leave some space at the beginning of this buffer for ULP_DIR "/"
+	 * that saves us some shuffling later.
+	 */
+	offset = sizeof (ULP_DIR);
+	if (fgets(buffer + offset,
+	    MIN(sizeof (upper_layer_profile), sizeof (buffer) - offset),
+	    f) == NULL) {
+		(void) pclose(f);
+		return; /* EOF before anything read */
+	}
+	(void) pclose(f);
+	(void) memcpy(buffer, ULP_DIR "/", sizeof (ULP_DIR));
+	buflen = strlen(buffer);
+	if (buffer[buflen - 1] == '\n')
+		buffer[--buflen] = '\0';
+	(void) memcpy(upper_layer_profile, buffer + offset,
+	    buflen + 1 - offset);
+	(void) strlcpy(buffer + buflen, bringup, sizeof (buffer) - buflen);
+	(void) start_child(PFEXEC, "-P", "basic", buffer, NULL);
+
+	syslog(LOG_NOTICE, "upper layer profile %s activated",
+	    upper_layer_profile);
+}
+
+void
+deactivate_upper_layer_profile(void)
+{
+	char buffer[1024];
+
+	/*
+	 * If ULP wasn't defined...
+	 */
+	if (!ulp_is_active())
+		return;
+
+	(void) snprintf(buffer, sizeof (buffer), ULP_DIR "/%s/teardown",
+	    upper_layer_profile);
+	(void) start_child(PFEXEC, "-P", "basic", buffer, NULL);
+
+	syslog(LOG_NOTICE, "upper layer profile %s deactivated",
+	    upper_layer_profile);
+
+	upper_layer_profile[0] = '\0';
+}
+
+/*
+ * Returns B_TRUE if the interface is successfully brought up;
+ * B_FALSE if bringup fails.
+ */
+boolean_t
+bringupinterface(const char *ifname, const char *host, const char *ipv6addr,
+    boolean_t ipv6onlink)
+{
+	boolean_t do_dhcp;
+	struct interface *intf;
+	uint64_t ifflags;
+
+	intf = get_interface(ifname);
+	if (intf == NULL) {
+		syslog(LOG_ERR, "could not bring up interface %s: not in list",
+		    ifname);
+		return (B_FALSE);
+	}
+
+	/* check current state; no point going on if flags are 0 */
+	if ((ifflags = get_ifflags(ifname, intf->if_family)) == 0) {
+		dprintf("bringupinterface(%s): get_ifflags() returned 0",
+		    ifname);
+		return (B_FALSE);
+	}
+
+	/*
+	 * If the link layer profile says that we want v6 then plumb it and
+	 * bring it up; if there's a static address, configure it as well.
+	 */
+	if (ipv6onlink) {
+		dprintf("bringupinterface: configuring ipv6");
+		(void) start_child(IFCONFIG, ifname, "inet6", "plumb", "up",
+		    NULL);
+		if (ipv6addr) {
+			(void) start_child(IFCONFIG, ifname, "inet6", "addif",
+			    ipv6addr, "up", NULL);
+		}
+	}
+
+	do_dhcp = (strcmp(host, "dhcp") == 0);
+
+	/*
+	 * If we need to use DHCP and DHCP is already controlling
+	 * the interface, we don't need to do anything.
+	 */
+	if (do_dhcp && (ifflags & IFF_DHCPRUNNING) != 0) {
+		dprintf("bringupinterface: nothing to do");
+		return (B_TRUE);
+	}
+
+	if (intf->if_type == IF_WIRELESS) {
+		if (!handle_wireless_lan(ifname)) {
+			syslog(LOG_INFO, "Could not connect to any WLAN, not "
+			    "bringing %s up", ifname);
+			return (B_FALSE);
+		}
+	}
+
+	if (do_dhcp) {
+		start_dhcp(intf);
+	} else {
+		(void) start_child(IFCONFIG, ifname, host, NULL);
+		(void) start_child(IFCONFIG, ifname, "up", NULL);
+	}
+
+	return (B_TRUE);
+}
+
+void
+takedowninterface(const char *ifname, boolean_t dhcp, boolean_t popup,
+    boolean_t v6onlink)
+{
+	uint64_t flags;
+	struct interface *ifp;
+
+	dprintf("takedowninterface(%s, %s, %s, %s)", ifname,
+	    BOOLEAN_TO_STRING(dhcp), BOOLEAN_TO_STRING(popup),
+	    BOOLEAN_TO_STRING(v6onlink));
+
+	if ((ifp = get_interface(ifname)) == NULL) {
+		dprintf("takedowninterface: can't find interface struct for %s",
+		    ifname);
+	} else {
+		if (ifp->if_lflags & IF_DHCPFAILED) {
+			/*
+			 * We're here because of a dhcp failure, and
+			 * we actually want dhcp to keep trying.  So
+			 * don't take the interface down.
+			 */
+			dprintf("takedowninterface: still trying for dhcp on "
+			    "%s, so will not take down interface", ifname);
+			return;
+		}
+	}
+
+	flags = get_ifflags(ifname, AF_INET);
+
+	if (dhcp) {
+		if ((flags & IFF_DHCPRUNNING) != 0) {
+			/*
+			 * We generally prefer doing a release, as that
+			 * tells the server that it can relinquish the
+			 * lease, whereas drop is just a client-side
+			 * operation.  But if we never came up, release
+			 * will fail, because dhcpagent does not allow
+			 * an interface which is selecting to release,
+			 * so we have to drop in that circumstance.  So
+			 * try release first, then fall back to drop.
+			 */
+			if (start_child(IFCONFIG, ifname, "dhcp", "release",
+			    NULL) != 0) {
+				(void) start_child(IFCONFIG, ifname, "dhcp",
+				    "drop", NULL);
+			}
+		}
+	} else {
+		if ((flags & IFF_UP) != 0)
+			(void) start_child(IFCONFIG, ifname, "down", NULL);
+		/* need to unset a statically configured addr */
+		(void) start_child(IFCONFIG, ifname, "0.0.0.0", "netmask",
+		    "0", "broadcast", "0.0.0.0", NULL);
+	}
+
+	if (v6onlink) {
+		/*
+		 * Unplumbing the link local interface causes dhcp and ndpd to
+		 * remove other addresses they have added.
+		 */
+		(void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL);
+	}
+
+	if (find_if_type(ifname) == IF_WIRELESS)
+		(void) dladm_wlan_disconnect(ifname);
+
+	dprintf("takedown interface, free cached ip address");
+	free(ifp->if_ipaddr);
+	ifp->if_ipaddr = NULL;
+	if (popup) {
+		char msg[64]; /* enough to hold this string */
+
+		(void) snprintf(msg, sizeof (msg),
+		    gettext("took interface %s down"), ifname);
+		display(msg);
+	}
+}
+
+/*
+ * Take down all known interfaces.  If ignore_if is non-null, an
+ * active (IFF_UP) interface whose name matches ignore_if will *not*
+ * be taken down.
+ */
+void
+take_down_all_ifs(const char *ignore_if)
+{
+	struct interface *ifp;
+	uint64_t flags;
+	boolean_t ignore_set = (ignore_if != NULL);
+
+	deactivate_upper_layer_profile();
+
+	for (ifp = get_next_interface(NULL); ifp != NULL;
+	    ifp = get_next_interface(ifp)) {
+		if (ignore_set && strcmp(ifp->if_name, ignore_if) == 0)
+			continue;
+		flags = get_ifflags(ifp->if_name, ifp->if_family);
+		if ((flags & IFF_UP) != 0) {
+			takedowninterface(ifp->if_name,
+			    flags & IFF_DHCPRUNNING, B_FALSE,
+			    ifp->if_family == AF_INET6);
+		}
+	}
+}
+
+static struct interface *
+get_next_interface(struct interface *ifp)
+{
+	return (ifp == NULL ? ifs_head : ifp->if_next);
+}
+
+/*
+ * Add an interface struct to the interface list.  The list is
+ * partially ordered; all the wired interfaces appear first,
+ * followed by all the wireless interfaces.  New interfaces are
+ * added at the end of the appropriate list section.
+ */
+static void
+interface_list_insert(struct interface *ifp)
+{
+	struct interface **wpp;
+	struct interface *endp;
+	boolean_t first_wireless = B_FALSE;
+	boolean_t first_wired = B_FALSE;
+
+	switch (ifp->if_type) {
+	case IF_WIRELESS:
+		first_wireless = (ifs_wireless == NULL);
+		wpp = &ifs_wireless;
+		endp = NULL;
+		break;
+
+	case IF_WIRED:
+		first_wired = (ifs_wired == NULL);
+		wpp = &ifs_wired;
+		endp = ifs_wireless;
+		break;
+
+	default:
+		/* don't add to the list */
+		return;
+	}
+
+	/* set list head if this is the first entry */
+	if (ifs_head == NULL) {
+		ifs_head = *wpp = ifp;
+		ifp->if_next = NULL;
+		return;
+	}
+
+	if (*wpp != NULL) {
+		while (*wpp != endp)
+			wpp = &(*wpp)->if_next;
+	}
+	*wpp = ifp;
+	ifp->if_next = endp;
+
+	/* update list head if we just inserted the first wired interface */
+	if (first_wired)
+		ifs_head = ifs_wired;
+
+	/* link sections if we just inserted the first wireless interface */
+	if (first_wireless) {
+		wpp = &ifs_wired;
+		while (*wpp != NULL)
+			wpp = &(*wpp)->if_next;
+		*wpp = ifs_wireless;
+	}
+}
+
+/*
+ * Returns the interface structure upon success.  Returns NULL and sets
+ * errno upon error.  If lr is null then it will look up the information
+ * needed.
+ *
+ * Note that given the MT nature of this program we are almost certainly
+ * racing for this structure.  That needs to be fixed.
+ */
+struct interface *
+add_interface(sa_family_t family, const char *name, uint64_t flags)
+{
+	struct interface *i;
+	enum interface_type iftype;
+
+	if (name == NULL)
+		return (NULL);
+
+	dprintf("add_interface: found interface %s", name);
+	if (family == AF_INET6) {
+		/*
+		 * we don't track IPv6 interfaces separately from their
+		 * v4 counterparts; a link either has v4 only, or both
+		 * v4 and v6, so we only maintain a v4 interface struct.
+		 */
+		dprintf("not adding v6 interface for %s", name);
+		return (NULL);
+	} else if (family != AF_INET) {
+		/*
+		 * the classic "shouldn't happen"...
+		 */
+		dprintf("not adding af %d interface for %s", family, name);
+		return (NULL);
+	}
+
+	if ((iftype = find_if_type(name)) == IF_TUN) {
+		/*
+		 * for now, we're ignoring tunnel interfaces (we expect
+		 * them to be entirely manipulated by higher layer profile
+		 * activation/deactivation scripts)
+		 */
+		dprintf("%s is a tunnel interface; ignoring", name);
+		return (NULL);
+	}
+
+	if ((i = malloc(sizeof (*i))) == NULL) {
+		dprintf("add_interface: malloc failed");
+		return (NULL);
+	}
+
+	i->if_name = strdup(name);
+	if (i->if_name == NULL) {
+		free(i);
+		dprintf("add_interface: malloc failed");
+		return (NULL);
+	}
+	i->if_ipaddr = NULL;
+	i->if_family = family;
+	i->if_type = iftype;
+	i->if_flags = flags == 0 ? get_ifflags(name, family) : flags;
+	i->if_lflags = 0;
+	i->if_timer_expire = 0;
+
+	dprintf("added interface %s of type %s af %d; is %savailable",
+	    i->if_name, if_type_str(i->if_type), i->if_family,
+	    ((i->if_type == IF_WIRELESS) ||
+	    ((i->if_flags & IFF_RUNNING) != 0)) ? "" : "not ");
+
+	interface_list_insert(i);
+
+	return (i);
+}
+
+/*
+ * Searches for an interface and returns the interface structure if found.
+ * Returns NULL otherwise.  errno is set upon error exit.
+ */
+struct interface *
+get_interface(const char *name)
+{
+	struct interface *i;
+
+	if (name == NULL)
+		return (NULL);
+
+	for (i = ifs_head; i != NULL; i = i->if_next) {
+		if (strcmp(name, i->if_name) == 0) {
+			return (i);
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Checks interface flags and, if IFF_DHCPRUNNING and !IFF_UP, does
+ * an 'ifconfig ifname dhcp drop'.
+ */
+void
+check_drop_dhcp(struct interface *ifp)
+{
+	uint64_t flags = get_ifflags(ifp->if_name, ifp->if_family);
+
+	if (!(flags & IFF_DHCPRUNNING) || (flags & IFF_UP)) {
+		dprintf("check_drop_dhcp: nothing to do (flags=0x%llx)", flags);
+		return;
+	}
+
+	(void) start_child(IFCONFIG, ifp->if_name, "dhcp", "drop", NULL);
+}
+
+/*
+ * For wireless interface, we will try to find out available wireless
+ * network; for wired, if dhcp should be used, start it now to try to
+ * avoid delays there.
+ *
+ * For the real code, we should pass back the network information
+ * gathered.  Note that the state engine will then use the llp to
+ * determine which interface should be set up...
+ */
+static void *
+gather_interface_info(void *arg)
+{
+	struct interface *i = arg;
+	llp_t *llp;
+
+	assert(i != NULL);
+
+	dprintf("Start gathering info for %s", i->if_name);
+
+	switch (i->if_type) {
+	case IF_WIRELESS:
+		(void) scan_wireless_nets(i);
+		break;
+	case IF_WIRED:
+		/*
+		 * It should not happen as the llp list should be done when
+		 * this function is called.  But let the state engine decide
+		 * what to do.
+		 */
+		if ((llp = llp_lookup(i->if_name)) == NULL)
+			break;
+		/*
+		 * The following is to avoid locking up the state machine
+		 * as it is currently the choke point.  We start dhcp with
+		 * a wait time of 0; later, if we see the link go down
+		 * (IFF_RUNNING is cleared), we will drop the attempt.
+		 */
+		if (llp->llp_ipv4src == IPV4SRC_DHCP && is_plugged_in(i))
+			start_dhcp(i);
+		break;
+	default:
+		/* For other types, do not do anything. */
+		return (NULL);
+	}
+
+	gen_newif_event(i);
+
+	dprintf("Done gathering info for %s", i->if_name);
+	return (NULL);
+}
+
+void
+gen_newif_event(struct interface *i)
+{
+	struct np_event *e;
+
+	e = calloc(1, sizeof (struct np_event));
+	if (e == NULL) {
+		dprintf("gen_newif_event: calloc failed");
+		return;
+	}
+	e->npe_name = strdup(i->if_name);
+	if (e->npe_name == NULL) {
+		dprintf("gen_newif_event: strdup failed");
+		free(e);
+		return;
+	}
+	e->npe_type = EV_ROUTING;
+
+	/*
+	 * This event notifies the state machine that a new interface is
+	 * (at least nominally) available to be brought up.  When the state
+	 * machine processes the event, it will look at the entire list of
+	 * interfaces and corresponding LLPs, and make a determination about
+	 * the best available LLP under current conditions.
+	 */
+	np_queue_add_event(e);
+	dprintf("gen_newif_event: generated event for if %s", i->if_name);
+}
+
+/*
+ * Caller uses this function to walk through the whole interface list.
+ * For each interface, the caller provided walker is called with
+ * the interface and arg as parameters.
+ *
+ * XXX There is no lock held right now for accessing the interface
+ * list.  We probably need that in future.
+ */
+void
+walk_interface(void (*walker)(struct interface *, void *), void *arg)
+{
+	struct interface *i;
+
+	for (i = ifs_head; i != NULL; i = i->if_next)
+		walker(i, arg);
+}
+
+static void
+print_interface_list(void)
+{
+	struct interface *wp;
+
+	dprintf("Walking interface list; starting with wired interfaces");
+	for (wp = ifs_head; wp != NULL; wp = wp->if_next) {
+		if (wp == ifs_wireless)
+			dprintf("Now wireless interfaces");
+		dprintf("==> %s", wp->if_name);
+	}
+}
+
+/*
+ * Walker function passed to icfg_iterate_if() below - the icfg_if_it *
+ * argument is guaranteed to be non-NULL by icfg_iterate_if(),
+ * since the function it uses to generate the list - icfg_get_if_list()) -
+ * guarantees this.
+ */
+/* ARGSUSED */
+static int
+do_add_interface(icfg_if_t *intf, void *arg)
+{
+	uint64_t flags = get_ifflags(intf->if_name, intf->if_protocol);
+
+	/* We don't touch loopback interface. */
+	if (flags & IFF_LOOPBACK)
+		return (ICFG_SUCCESS);
+
+	/* If adding fails, just ignore that interface... */
+	(void) add_interface(intf->if_protocol, intf->if_name, flags);
+
+	return (ICFG_SUCCESS);
+}
+
+/*
+ * Walker function passed to icfg_iterate_if() below - the icfg_if_it *
+ * argument is guaranteed to be non-NULL by icfg_iterate_if(),
+ * since the function it uses to generate the list - icfg_get_if_list()) -
+ * guarantees this.
+ */
+/* ARGSUSED */
+static int
+do_unplumb_if(icfg_if_t *intf, void *arg)
+{
+	uint64_t flags = get_ifflags(intf->if_name, intf->if_protocol);
+
+	/* We don't touch loopback interface. */
+	if (flags & IFF_LOOPBACK)
+		return (ICFG_SUCCESS);
+
+	(void) start_child(IFCONFIG, intf->if_name,
+	    intf->if_protocol == AF_INET6 ? "inet6" : "inet", "unplumb", NULL);
+
+	return (ICFG_SUCCESS);
+}
+
+void
+initialize_interfaces(void)
+{
+	int times;
+
+	dprintf("initialize_interfaces: setting link_layer_profile(%p) to NULL",
+	    (void *)link_layer_profile);
+	link_layer_profile = NULL;
+	upper_layer_profile[0] = '\0';
+
+	/*
+	 * Bring down all interfaces bar lo0.
+	 */
+	(void) icfg_iterate_if(AF_INET, ICFG_PLUMBED, NULL, do_unplumb_if);
+	(void) icfg_iterate_if(AF_INET6, ICFG_PLUMBED, NULL, do_unplumb_if);
+
+	/*
+	 * In case dhcpagent is running...  If it is running, when
+	 * we do another DHCP command on the same interface later, it may
+	 * be confused.  Just kill dhcpagent to simplify handling.
+	 */
+	dprintf("killing dhcpagent");
+	(void) start_child(PKILL, "-z", zonename, "dhcpagent", NULL);
+
+	/*
+	 * Really we should walk the device tree instead of doing
+	 * the 'ifconfig -a plumb'.
+	 */
+	for (times = 1; times <= 30; times++) {
+		if (start_child(IFCONFIG, "-a", "plumb", NULL) == 0)
+			break;
+
+		/*
+		 * Assume the di_init problem is the cause: sleep and try
+		 * again, as the DDI has probably not been initialized yet.
+		 */
+		(void) sleep(1);
+	}
+	if (times > 30)
+		syslog(LOG_ERR, IFCONFIG "-a plumb failed %d times", times);
+
+	(void) dladm_init_linkprop();
+
+	(void) icfg_iterate_if(AF_INET, ICFG_PLUMBED, NULL, do_add_interface);
+
+	print_interface_list();
+
+}
+
+/*
+ * Walker function used to start info gathering of each interface.
+ */
+/* ARGSUSED */
+void
+start_if_info_collect(struct interface *ifp, void *arg)
+{
+	pthread_t if_thr;
+	pthread_attr_t attr;
+
+	/*
+	 * Only if the cable of the wired interface is
+	 * plugged in, start gathering info from it.
+	 */
+	if (!is_plugged_in(ifp))
+		return;
+
+	/*
+	 * This is a "fresh start" for the interface; if dhcp
+	 * previously failed, the flag can now be cleared.
+	 */
+	ifp->if_lflags &= ~IF_DHCPFAILED;
+
+	(void) pthread_attr_init(&attr);
+	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	if (pthread_create(&if_thr, &attr, gather_interface_info,
+	    (void *)ifp) != 0) {
+		syslog(LOG_ERR, "create interface gathering thread: %m");
+		exit(EXIT_FAILURE);
+	} else {
+		dprintf("interface info thread: %d", if_thr);
+	}
+}
+
+/*
+ * Walker function used to check timer for each interface.
+ * If timer has expired, generate a timer event for the
+ * interface.
+ */
+/* ARGSUSED */
+void
+check_interface_timer(struct interface *ifp, void *arg)
+{
+	uint32_t now = *(uint32_t *)arg;
+	struct np_event *ev;
+
+	if (ifp->if_timer_expire == 0)
+		return;
+
+	if (ifp->if_timer_expire > now) {
+		start_timer(now, ifp->if_timer_expire - now);
+		return;
+	}
+
+	ifp->if_timer_expire = 0;
+
+	if ((ev = calloc(1, sizeof (*ev))) == NULL) {
+		dprintf("could not allocate timer event for %s; ignoring timer",
+		    ifp->if_name);
+		return;
+	}
+	ev->npe_type = EV_TIMER;
+	ev->npe_name = strdup(ifp->if_name);
+	if (ev->npe_name == NULL) {
+		dprintf("could not strdup name for timer event on %s; ignoring",
+		    ifp->if_name);
+		free(ev);
+		return;
+	}
+	np_queue_add_event(ev);
+}
+
+enum interface_type
+find_if_type(const char *name)
+{
+	enum interface_type type;
+
+	if (name == NULL) {
+		dprintf("find_if_type: no ifname; returning IF_UNKNOWN");
+		return (IF_UNKNOWN);
+	}
+
+	if (strncmp(name, "ip.tun", 6) == 0) {
+		/*
+		 * We'll need to update our tunnel detection once
+		 * clearview/uv and clearview/tun driver projects
+		 * go back; tunnel names won't necessarily be ip.tunN
+		 */
+		type = IF_TUN;
+	} else {
+		/*
+		 * We didn't recognize it.  Try the libdladm function
+		 * to decide if it is wireless or not; if not, assume
+		 * that it's wired.
+		 */
+		type = dladm_wlan_is_valid(name) ? IF_WIRELESS : IF_WIRED;
+	}
+
+	return (type);
+}
+
+const char *
+if_type_str(enum interface_type type)
+{
+	switch (type) {
+	case IF_WIRED:
+		return ("wired");
+	case IF_WIRELESS:
+		return ("wireless");
+	case IF_TUN:
+		return ("tunnel");
+	case IF_UNKNOWN:
+	default:
+		return ("unknown type");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/llp.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,666 @@
+/*
+ * 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"
+
+/*
+ * This file contains the routines that manipulate Link Layer Profiles
+ * (aka LLPs) and various support functions.  This includes parsing and
+ * updating of the /etc/nwam/llp file.
+ *
+ * The daemon maintains a list of llp_t structures that represent the
+ * provided configuration information for a link.  After the llp file
+ * is read, entries are added to the LLP list for any known links
+ * (identified by checking the interface list, which is based on the
+ * v4 interfaces present after 'ifconfig -a plumb') which were not
+ * represented in the llp file.  These entries contain the default
+ * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the
+ * v4 interface, and accept router- and DHCPv6-assigned addresses on
+ * the v6 interface.  The default entries created by the daemon are
+ * also added to the llp file.
+ *
+ * LLP priority is assigned based on two factors: the order within
+ * the llp file, with earlier entries having higher priority; and
+ * a preference for wired interfaces before wireless.  Entries that
+ * are added to the file by the daemon are added *after* any existing
+ * entries; within the added block, wired entries are added before
+ * wireless.  Thus if the llp file is never modified externally, wired
+ * will generally be ordered before wireless.  However, if the
+ * administrator creates the file with wireless entries before wired,
+ * that priority order will be respected.
+ *
+ * The llp list (pointed to by the global llp_head) is protected by
+ * the global llp_lock, which should be pthread_mutex_lock()'d before
+ * reading or writing the list.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <atomic.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+/* Lock to protect the llp list. */
+static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER;
+
+llp_t *llp_head = NULL;
+llp_t *link_layer_profile = NULL;
+
+/*
+ * Global variable to hold the highest priority.  Need to use the atomic
+ * integer arithmetic functions to update it.
+ */
+static uint32_t llp_highest_pri = 0;
+
+static void print_llp_list(void);
+
+char *
+llp_prnm(llp_t *llp)
+{
+	if (llp == NULL)
+		return ("null_llp");
+	else if (llp->llp_lname == NULL)
+		return ("null_lname");
+	else
+		return (llp->llp_lname);
+}
+
+static void
+llp_list_free(llp_t *head)
+{
+	llp_t **llpp;
+	llp_t *llpfree;
+
+	if (pthread_mutex_lock(&llp_lock) != 0) {
+		/* Something very serious is wrong... */
+		syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %m");
+		return;
+	}
+	llpp = &head;
+	while (*llpp != NULL) {
+		llpfree = *llpp;
+		*llpp = llpfree->llp_next;
+		free(llpfree->llp_ipv4addrstr);
+		free(llpfree);
+	}
+	(void) pthread_mutex_unlock(&llp_lock);
+}
+
+llp_t *
+llp_lookup(const char *link)
+{
+	llp_t *llp;
+
+	if (link == NULL)
+		return (NULL);
+
+	/* The name may change.  Better hold the lock. */
+	if (pthread_mutex_lock(&llp_lock) != 0) {
+		/* Something very serious is wrong... */
+		syslog(LOG_ERR, "llp_lookup: cannot lock mutex: %m");
+		return (NULL);
+	}
+	for (llp = llp_head; llp != NULL; llp = llp->llp_next) {
+		if (strcmp(link, llp->llp_lname) == 0)
+			break;
+	}
+	(void) pthread_mutex_unlock(&llp_lock);
+	return (llp);
+}
+
+/*
+ * Choose the higher priority llp of the two passed in.  If one is
+ * NULL, the other will be higher priority.  If both are NULL, NULL
+ * is returned.
+ *
+ * Assumes that both are available (i.e. doesn't check IFF_RUNNING
+ * or IF_DHCPFAILED flag values).
+ */
+llp_t *
+llp_high_pri(llp_t *a, llp_t *b)
+{
+	if (a == NULL)
+		return (b);
+	else if (b == NULL)
+		return (a);
+
+	/*
+	 * Higher priority is represented by a lower number.  This seems a
+	 * bit backwards, but for now it makes assigning priorities very easy.
+	 *
+	 * We shouldn't have ties right now, but just in case, tie goes to a.
+	 */
+	return ((a->llp_pri <= b->llp_pri) ? a : b);
+}
+
+/*
+ * Chooses the highest priority link that corresponds to an
+ * available interface.
+ */
+llp_t *
+llp_best_avail(void)
+{
+	llp_t *p, *rtnllp = NULL;
+	struct interface *ifp;
+
+	/* The priority may change.  Better hold the lock. */
+	if (pthread_mutex_lock(&llp_lock) != 0) {
+		/* Something very serious is wrong... */
+		syslog(LOG_ERR, "llp_best_avail: cannot lock mutex: %m");
+		return (NULL);
+	}
+	for (p = llp_head; p != NULL; p = p->llp_next) {
+		ifp = get_interface(p->llp_lname);
+		if (ifp == NULL || !is_plugged_in(ifp) ||
+		    (ifp->if_lflags & IF_DHCPFAILED) != 0)
+			continue;
+		rtnllp = llp_high_pri(p, rtnllp);
+	}
+	(void) pthread_mutex_unlock(&llp_lock);
+
+	return (rtnllp);
+}
+
+/*
+ * Returns B_TRUE if llp is successfully activated;
+ * B_FALSE if activation fails.
+ */
+boolean_t
+llp_activate(llp_t *llp)
+{
+	boolean_t rtn;
+	char *host;
+	/*
+	 * Choosing "dhcp" as a hostname is unsupported right now.
+	 * We use hostname="dhcp" as a keyword telling bringupinterface()
+	 * to use dhcp on the interface.
+	 */
+	char *dhcpstr = "dhcp";
+
+	llp_deactivate();
+
+	host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr :
+	    llp->llp_ipv4addrstr;
+
+	if (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
+	    llp->llp_ipv6onlink)) {
+		link_layer_profile = llp;
+		dprintf("llp_activate: activated llp for %s", llp_prnm(llp));
+		rtn = B_TRUE;
+	} else {
+		dprintf("llp_activate: failed to bringup %s", llp_prnm(llp));
+		link_layer_profile = NULL;
+		rtn = B_FALSE;
+	}
+
+	return (rtn);
+}
+
+/*
+ * Deactivate the current active llp (link_layer_profile)
+ */
+void
+llp_deactivate(void)
+{
+	if (link_layer_profile == NULL)
+		return;
+
+	takedowninterface(link_layer_profile->llp_lname,
+	    link_layer_profile->llp_ipv4src == IPV4SRC_DHCP, B_TRUE,
+	    link_layer_profile->llp_ipv6onlink);
+
+	dprintf("llp_deactivate: setting link_layer_profile(%p) to NULL",
+	    (void *)link_layer_profile);
+	link_layer_profile = NULL;
+}
+
+/*
+ * Replace the currently active link layer profile with the one
+ * specified.  And since we're changing the lower layer stuff,
+ * we need to first deactivate the current upper layer profile.
+ * An upper layer profile will be reactivated later, when we get
+ * confirmation that the new llp is fully up (has an address
+ * assigned).
+ *
+ * If the new llp is the same as the currently active one, don't
+ * do anything.
+ *
+ * If the new llp is NULL, just take down the currently active one.
+ */
+void
+llp_swap(llp_t *newllp)
+{
+	char *upifname;
+
+	if (newllp == link_layer_profile)
+		return;
+
+	deactivate_upper_layer_profile();
+
+	if (link_layer_profile == NULL) {
+		/*
+		 * there shouldn't be anything else running;
+		 * make sure that's the case!
+		 */
+		upifname = (newllp == NULL) ? NULL : newllp->llp_lname;
+		take_down_all_ifs(upifname);
+	} else {
+		dprintf("taking down current link layer profile (%s)",
+		    llp_prnm(link_layer_profile));
+		llp_deactivate();
+	}
+	if (newllp != NULL) {
+		dprintf("bringing up new link layer profile (%s)",
+		    llp_prnm(newllp));
+		(void) llp_activate(newllp);
+	}
+}
+
+/*
+ *
+ * ifp->if_family == AF_INET, addr_src == DHCP ==> addr == NULL
+ * ifp->if_family == AF_INET, addr_src == STATIC ==> addr non null sockaddr_in
+ * ifp->if_family == AF_INET6, ipv6onlink == FALSE ==> addr == NULL
+ * ifp->if_family == AF_INET6, ipv6onlink == TRUE,
+ *     if addr non NULL then it is the textual representation of the address
+ *     and prefix.
+ *
+ * The above set of conditions describe what the inputs to this fuction are
+ * expected to be.  Given input which meets those conditions this functions
+ * then outputs a line of configuration describing the inputs.
+ *
+ * Note that it is assumed only one thread can call this function at
+ * any time.  So there is no lock to protect the file writing.  This
+ * is true as the only caller of this function should originate from
+ * llp_parse_config(), which is done at program initialization time.
+ */
+static void
+add_if_file(FILE *fp, struct interface *ifp, ipv4src_t addr_src,
+    boolean_t ipv6onlink, void *addr)
+{
+	char addr_buf[INET6_ADDRSTRLEN];
+
+	switch (ifp->if_family) {
+	case AF_INET:
+		switch (addr_src) {
+		case IPV4SRC_STATIC:
+			/* This is not supposed to happen... */
+			if (addr == NULL) {
+				(void) fprintf(fp, "%s\tdhcp\n", ifp->if_name);
+				break;
+			}
+			(void) inet_ntop(AF_INET, addr, addr_buf,
+			    INET6_ADDRSTRLEN);
+			(void) fprintf(fp, "%s\tstatic\t%s\n", ifp->if_name,
+			    addr_buf);
+			break;
+		case IPV4SRC_DHCP:
+			/* Default is DHCP for now. */
+		default:
+			(void) fprintf(fp, "%s\tdhcp\n", ifp->if_name);
+			break;
+		}
+		break;
+
+	case AF_INET6:
+		if (ipv6onlink)
+			(void) fprintf(fp, "%s\tipv6\n", ifp->if_name);
+		break;
+
+	default:
+		syslog(LOG_ERR, "interface %s of type %d?!", ifp->if_name,
+		    ifp->if_family);
+		break;
+	}
+}
+
+/*
+ * Walker function to pass to walk_interface() to add a default
+ * interface description to the LLPFILE.
+ *
+ * Regarding IF_TUN interfaces: see comments before find_and_add_llp()
+ * for an explanation of why we skip them.
+ */
+static void
+add_if_default(struct interface *ifp, void *arg)
+{
+	FILE *fp = (FILE *)arg;
+
+	if (ifp->if_type != IF_TUN)
+		add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL);
+}
+
+/* Create the LLPFILE using info from the interface list. */
+static void
+create_llp_file(void)
+{
+	FILE *fp;
+	int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+
+	/* Create the NWAM directory in case it does not exist. */
+	if (mkdir(LLPDIR, dirmode) != 0) {
+		if (errno != EEXIST) {
+			syslog(LOG_ERR, "create NWAM directory: %m");
+			return;
+		}
+	}
+	if ((fp = fopen(LLPFILE, "w")) == NULL) {
+		syslog(LOG_ERR, "create LLP config file: %m");
+		return;
+	}
+	syslog(LOG_INFO, "Creating %s", LLPFILE);
+	walk_interface(add_if_default, fp);
+	(void) fclose(fp);
+}
+
+/*
+ * Append an llp struct to the end of the llp list.
+ */
+static void
+llp_list_append(llp_t *llp)
+{
+	llp_t **wpp = &llp_head;
+
+	/*
+	 * should be a no-op, but for now, make sure we only
+	 * create llps for wired and wireless interfaces.
+	 */
+	if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS)
+		return;
+
+	if (pthread_mutex_lock(&llp_lock) != 0) {
+		/* Something very serious is wrong... */
+		syslog(LOG_ERR, "llp_list_append: cannot lock mutex: %m");
+		return;
+	}
+
+	while (*wpp != NULL)
+		wpp = &(*wpp)->llp_next;
+	*wpp = llp;
+	llp->llp_next = NULL;
+
+	(void) pthread_mutex_unlock(&llp_lock);
+}
+
+/*
+ * Create a llp given the parameters and add it to the global list.
+ */
+static void
+create_and_add_llp(const char *name, ipv4src_t ipv4src, const char *addrstr,
+    boolean_t ipv6onlink, const char *ipv6addrstr)
+{
+	llp_t *newllp;
+	int lnamelen;
+
+	if ((newllp = llp_lookup(name)) != NULL) {
+		if (ipv6addrstr != NULL) {
+			newllp->llp_ipv6addrstr = strdup(ipv6addrstr);
+			if (newllp->llp_ipv6addrstr == NULL) {
+				syslog(LOG_ERR, "could not save ipv6 static "
+				    "address for %s", name);
+			}
+		}
+		newllp->llp_ipv6onlink = ipv6onlink;
+		return;
+	} else if ((newllp = calloc(1, sizeof (llp_t))) == NULL) {
+		syslog(LOG_ERR, "calloc llp: %m");
+		return;
+	}
+
+	lnamelen = sizeof (newllp->llp_lname);
+	if (strlcpy(newllp->llp_lname, name, lnamelen) >= lnamelen) {
+		syslog(LOG_ERR, "llp: link name too long; ignoring entry");
+		free(newllp);
+		return;
+	}
+	if (ipv4src == IPV4SRC_STATIC) {
+		if ((newllp->llp_ipv4addrstr = strdup(addrstr)) == NULL) {
+			syslog(LOG_ERR, "malloc ipaddrstr: %m");
+			free(newllp);
+			return;
+		}
+	} else {
+		newllp->llp_ipv4addrstr = NULL;
+	}
+	newllp->llp_next = NULL;
+	newllp->llp_pri = atomic_add_32_nv(&llp_highest_pri, 1);
+	newllp->llp_ipv4src = ipv4src;
+	newllp->llp_type = find_if_type(newllp->llp_lname);
+	newllp->llp_ipv6onlink = ipv6onlink;
+
+	if (ipv6onlink && ipv6addrstr != NULL) {
+		newllp->llp_ipv6addrstr = strdup(ipv6addrstr);
+		if (newllp->llp_ipv6addrstr == NULL)
+			syslog(LOG_WARNING, "could not store static address %s"
+			    "on interface %s", ipv6addrstr, newllp->llp_lname);
+	} else {
+		newllp->llp_ipv6addrstr = NULL;
+	}
+
+	llp_list_append(newllp);
+
+	dprintf("created llp for link %s, pri %d", newllp->llp_lname,
+	    newllp->llp_pri);
+}
+
+/*
+ * Walker function to pass to walk_interface() to find out if
+ * an interface description is missing from LLPFILE.  If it is,
+ * add it.
+ *
+ * Currently, IF_TUN type interfaces are special-cased: they are
+ * only handled as user-enabled, layered links (which may be created
+ * as part of a higher-layer profile, for example).  Thus, they
+ * shouldn't be considered when looking at the llp list, so don't
+ * add them here.
+ */
+static void
+find_and_add_llp(struct interface *ifp, void *arg)
+{
+	FILE *fp = (FILE *)arg;
+
+	if (ifp->if_type != IF_TUN && (llp_lookup(ifp->if_name) == NULL)) {
+		dprintf("Adding %s to %s", ifp->if_name, LLPFILE);
+		add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL);
+		/* If we run out of memory, ignore this interface for now. */
+		create_and_add_llp(ifp->if_name, IPV4SRC_DHCP, NULL,
+		    B_TRUE, NULL);
+	}
+}
+
+/*
+ * This is a very "slow" function.  It uses walk_interface() to find
+ * out if any of the interface is missing from the LLPFILE.  For the
+ * missing ones, add them to the LLPFILE.
+ */
+static void
+add_missing_if_llp(FILE *fp)
+{
+	walk_interface(find_and_add_llp, fp);
+}
+
+static void
+print_llp_list(void)
+{
+	llp_t *wp;
+
+	dprintf("Walking llp list");
+	for (wp = llp_head; wp != NULL; wp = wp->llp_next)
+		dprintf("==> %s", wp->llp_lname);
+}
+
+/*
+ * This function parses /etc/nwam/llp which describes the phase 0 link layer
+ * profile.  The file is line oriented with each line containing tab or space
+ * delimited fields.  Each address family (IPv4, IPv6) is described on a
+ * separate line.
+ * The first field is a link name.
+ * The second field can be either static, dhcp, ipv6, or noipv6.
+ * If the second field is static then the next field is an ipv4 address which
+ *    can contain a prefix.  Previous versions of this file could contain a
+ *    hostname in this field which is no longer supported.
+ * If the second field is dhcp then dhcp will be used on the interface.
+ * If the second field is ipv6 then an ipv6 interface is plumbed up.  The
+ *    outcome of this is that if offered by the network in.ndpd and dhcp
+ *    will conspire to put addresses on additional ipv6 logical interfaces.
+ *    If the next field is non-null then it is taken to be an IPv6 address
+ *    and possible prefix which are applied to the interface.
+ * If the second field is noipv6 then no ipv6 interfaces will be put on that
+ *    link.
+ */
+void
+llp_parse_config(void)
+{
+	static const char STATICSTR[] = "static";
+	static const char DHCP[] = "dhcp";
+	static const char IPV6[] = "ipv6";
+	static const char NOIPV6[] = "noipv6";
+	FILE *fp;
+	char line[LINE_MAX];
+	char *cp, *lasts, *lstr, *srcstr, *addrstr, *v6addrstr;
+	int lnum;
+	ipv4src_t ipv4src;
+	boolean_t ipv6onlink;
+
+	fp = fopen(LLPFILE, "r+");
+	if (fp == NULL) {
+		if (errno != ENOENT) {
+			/*
+			 * XXX See comment before create_llp_file() re
+			 * better error handling.
+			 */
+			syslog(LOG_ERR, "open LLP config file: %m");
+			return;
+		}
+
+		/*
+		 * If there is none, we should create one instead.
+		 * For now, we will use the order of the interface list
+		 * for the priority.  We should have a priority field
+		 * in the llp file eventually...
+		 */
+		create_llp_file();
+
+		/* Now we can try to reopen the file for processing. */
+		fp = fopen(LLPFILE, "r+");
+		if (fp == NULL) {
+			syslog(LOG_ERR, "2nd open LLP config file: %m");
+			return;
+		}
+	}
+
+	if (llp_head != NULL)
+		llp_list_free(llp_head);
+	llp_head = NULL;
+
+	for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
+		ipv4src = IPV4SRC_DHCP;
+		ipv6onlink = B_FALSE;
+		addrstr = NULL;
+		v6addrstr = NULL;
+
+		if (line[strlen(line) - 1] == '\n')
+			line[strlen(line) - 1] = '\0';
+
+		cp = line;
+		while (isspace(*cp))
+			cp++;
+
+		if (*cp == '#' || *cp == '\0')
+			continue;
+
+		dprintf("parsing llp conf file line %d...", lnum);
+
+		if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) ||
+		    ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) {
+			syslog(LOG_ERR, "llp:%d: not enough tokens; "
+			    "ignoring entry", lnum);
+			continue;
+		}
+		if (strcasecmp(srcstr, STATICSTR) == 0) {
+			if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL ||
+			    atoi(addrstr) == 0) { /* crude check for number */
+				syslog(LOG_ERR, "llp:%d: missing ipaddr "
+				    "for static config; ignoring entry",
+				    lnum);
+				continue;
+			}
+			ipv4src = IPV4SRC_STATIC;
+		} else if (strcasecmp(srcstr, DHCP) == 0) {
+			ipv4src = IPV4SRC_DHCP;
+		} else if (strcasecmp(srcstr, IPV6) == 0) {
+			ipv6onlink = B_TRUE;
+			if ((addrstr = strtok_r(NULL, " \t", &lasts)) != NULL) {
+				v6addrstr = strdup(addrstr);
+				if (v6addrstr == NULL) {
+					syslog(LOG_ERR, "could not store v6 "
+					    "static address %s for %s",
+					    v6addrstr, lstr);
+				}
+			} else {
+				v6addrstr = NULL;
+			}
+		} else if (strcasecmp(srcstr, NOIPV6) == 0) {
+			ipv6onlink = B_FALSE;
+		} else {
+			syslog(LOG_ERR, "llp:%d: unrecognized "
+			    "field; ignoring entry", lnum);
+			continue;
+		}
+
+		create_and_add_llp(lstr, ipv4src, addrstr, ipv6onlink,
+		    v6addrstr);
+	}
+
+	/*
+	 * So we have read in the llp file, is there an interface which
+	 * it does not describe?  If yes, we'd better add it to the
+	 * file for future reference.  Again, since we don't have a
+	 * priority field yet, we will add the interface in the order
+	 * in the interface list.
+	 */
+	add_missing_if_llp(fp);
+
+	(void) fclose(fp);
+
+	print_llp_list();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/main.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,392 @@
+/*
+ * 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"
+
+/*
+ * nwamd - NetWork Auto-Magic Daemon
+ */
+
+#include <fcntl.h>
+#include <priv.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <errno.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+#define	TIMESPECGT(x, y)	((x.tv_sec > y.tv_sec) || \
+	    ((x.tv_sec == y.tv_sec) && (x.tv_nsec > y.tv_nsec)))
+
+const char *OUR_FMRI = "svc:/network/physical:nwam";
+const char *OUR_PG = "nwamd";
+
+boolean_t fg = B_FALSE;
+boolean_t shutting_down;
+sigset_t original_sigmask;
+char zonename[ZONENAME_MAX];
+
+/*
+ * nwamd
+ *
+ * This is the Network Auto-Magic daemon.  For further high level information
+ * see the Network Auto-Magic project and the Approachability communities
+ * on opensolaris.org, and nwamd(1M).
+ *
+ * The general structure of the code is as a set of threads collecting
+ * system events which are fed into a state machine which alters system
+ * state based on configuration.
+ *
+ * signal management
+ * Due to being threaded, a simple set of signal handlers would not work
+ * very well for nwamd.  Instead nwamd blocks signals at startup and
+ * then starts a thread which sits in sigwait(2) waiting for signals.
+ * When a signal is received the signal handling thread dispatches it.
+ * It handles:
+ * - shutting down, done by creating an event which is passed through the
+ *   system allowing the various subsystems to do any necessary cleanup.
+ * - SIGALRM for timers.
+ * - SIGHUP for instance refresh, which tells us to look up various
+ *   properties from SMF(5).
+ *
+ * subprocess management
+ * nwamd starts several different subprocesses to manage the system.  Some
+ * of those start other processes (e.g. `ifconfig <if> dhcp` ends up starting
+ * dhcpagent if necessary).  Due to the way we manage signals if we started
+ * those up without doing anything special their signal mask would mostly
+ * block signals.  So we restore the signal mask when we start subprocesses.
+ * This is especially important with respect to DHCP as later when we exit
+ * we need to kill the dhcpagent process which we started; for details, see
+ * the block comment in state_machine.c in its cleanup() function.
+ */
+
+/*
+ * In this file there are several utility functions which might otherwise
+ * belong in util.c, but since they are only called from main(), they can
+ * live here as static functions:
+ * - syslog set-up
+ * - daemonizing
+ * - looking up SMF(5) properties
+ * - signal handling
+ * - managing privileges(5)
+ */
+
+static void
+start_logging(void)
+{
+	openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+}
+
+static void
+daemonize(void)
+{
+	pid_t pid;
+
+	/*
+	 * A little bit of magic here.  By the first fork+setsid, we
+	 * disconnect from our current controlling terminal and become
+	 * a session group leader.  By forking again without calling
+	 * setsid again, we make certain that we are not the session
+	 * group leader and can never reacquire a controlling terminal.
+	 */
+	if ((pid = fork()) == (pid_t)-1) {
+		syslog(LOG_ERR, "fork 1 failed");
+		exit(EXIT_FAILURE);
+	}
+	if (pid != 0) {
+		(void) wait(NULL);
+		dprintf("child %d exited, daemonizing", pid);
+		_exit(0);
+	}
+	if (setsid() == (pid_t)-1) {
+		syslog(LOG_ERR, "setsid");
+		exit(EXIT_FAILURE);
+	}
+	if ((pid = fork()) == (pid_t)-1) {
+		syslog(LOG_ERR, "fork 2 failed");
+		exit(EXIT_FAILURE);
+	}
+	if (pid != 0) {
+		_exit(0);
+	}
+	(void) chdir("/");
+	(void) umask(022);
+	closelog();
+	(void) closefrom(STDIN_FILENO);
+	(void) open("/dev/null", O_RDONLY);
+	(void) open("/dev/null", O_WRONLY);
+	(void) dup2(STDOUT_FILENO, STDERR_FILENO);
+	start_logging();
+}
+
+/*
+ * Look up nwamd property values and set daemon variables appropriately.
+ * This function will be called on startup and via the signal handling
+ * thread on receiving a HUP (which occurs when the nwam service is
+ * refreshed).
+ */
+static void
+lookup_daemon_properties(void)
+{
+	boolean_t debug_set;
+	uint64_t scan_interval;
+
+	if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0)
+		debug = debug_set;
+	if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0)
+		wlan_scan_interval = scan_interval;
+	dprintf("Read daemon configuration properties.");
+}
+
+/* ARGSUSED */
+static void *
+sighandler(void *arg)
+{
+	struct np_event *ev;
+	sigset_t sigset;
+	int sig;
+	uint32_t now;
+
+	(void) sigfillset(&sigset);
+
+	for (;;) {
+		sig = sigwait(&sigset);
+		dprintf("signal %d caught", sig);
+		switch (sig) {
+		case SIGALRM:
+			/*
+			 * We may have multiple interfaces with
+			 * scheduled timers; walk the list and
+			 * create a timer event for each one.
+			 */
+			timer_expire = TIMER_INFINITY;
+			now = NSEC_TO_SEC(gethrtime());
+			walk_interface(check_interface_timer, &now);
+			break;
+		case SIGHUP:
+			/*
+			 * Refresh action - reread configuration properties.
+			 */
+			lookup_daemon_properties();
+			break;
+		default:
+			syslog(LOG_NOTICE, "%s received, shutting down",
+			    strsignal(sig));
+			shutting_down = B_TRUE;
+			if ((ev = malloc(sizeof (*ev))) == NULL) {
+				dprintf("could not allocate shutdown event");
+				cleanup();
+				exit(EXIT_FAILURE);
+			}
+			ev->npe_type = EV_SHUTDOWN;
+			ev->npe_name = NULL;
+			np_queue_add_event(ev);
+			break;
+		}
+
+		/* if we're shutting down, exit this thread */
+		if (shutting_down)
+			return (NULL);
+	}
+}
+
+static void
+init_signalhandling(void)
+{
+	pthread_attr_t attr;
+	pthread_t sighand;
+	int err;
+	sigset_t new;
+
+	(void) sigfillset(&new);
+	(void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask);
+	(void) pthread_attr_init(&attr);
+	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	if (err = pthread_create(&sighand, &attr, sighandler, NULL)) {
+		syslog(LOG_ERR, "pthread_create system: %s", strerror(err));
+		exit(EXIT_FAILURE);
+	} else {
+		dprintf("signal handler thread: %d", sighand);
+	}
+	(void) pthread_attr_destroy(&attr);
+}
+
+static void
+change_user_set_privs(void)
+{
+	priv_set_t *priv_set;
+
+	priv_set = priv_allocset();
+	if (getppriv(PRIV_PERMITTED, priv_set) == -1) {
+		dprintf("getppriv %s", strerror(errno));
+	} else {
+		char *p;
+
+		p = priv_set_to_str(priv_set, ',', 0);
+		dprintf("started with privs %s", p != NULL ? p : "Unknown");
+		free(p);
+	}
+
+	priv_emptyset(priv_set);
+	(void) priv_addset(priv_set, "basic");
+	(void) priv_addset(priv_set, "file_chown_self");
+	(void) priv_addset(priv_set, "file_dac_read");
+	(void) priv_addset(priv_set, "file_dac_write");
+	(void) priv_addset(priv_set, "net_privaddr");
+	(void) priv_addset(priv_set, "net_rawaccess");
+	(void) priv_addset(priv_set, "proc_exec");
+	(void) priv_addset(priv_set, "proc_fork");
+	(void) priv_addset(priv_set, "proc_info");
+	(void) priv_addset(priv_set, "proc_owner");
+	(void) priv_addset(priv_set, "proc_session");
+	(void) priv_addset(priv_set, "proc_setid");
+	(void) priv_addset(priv_set, "sys_ip_config");
+	(void) priv_addset(priv_set, "sys_ipc_config");
+	(void) priv_addset(priv_set, "sys_net_config");
+	(void) priv_addset(priv_set, "sys_res_config");
+	(void) priv_addset(priv_set, "sys_resource");
+
+	if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
+		syslog(LOG_ERR, "setppriv inheritable: %m");
+		priv_freeset(priv_set);
+		exit(EXIT_FAILURE);
+	}
+
+	if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) {
+		syslog(LOG_ERR, "setppriv permitted: %m");
+		priv_freeset(priv_set);
+		exit(EXIT_FAILURE);
+	}
+
+	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
+		syslog(LOG_ERR, "setppriv effective: %m");
+		priv_freeset(priv_set);
+		exit(EXIT_FAILURE);
+	}
+
+	priv_freeset(priv_set);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c;
+	int scan_lev;
+	struct np_event *e;
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	shutting_down = B_FALSE;
+	start_logging();
+	syslog(LOG_INFO, "nwamd pid %d started", getpid());
+
+	while ((c = getopt(argc, argv, "fs:")) != -1) {
+		switch (c) {
+			case 'f':
+				fg = B_TRUE;
+				break;
+			case 's':
+				scan_lev = atoi(optarg);
+				if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK &&
+				    scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) {
+					wireless_scan_level = scan_lev;
+				} else {
+					syslog(LOG_ERR, "invalid signal "
+					    "strength: %s", optarg);
+				}
+				break;
+			default:
+				syslog(LOG_ERR, "unrecognized option %c",
+				    optopt);
+				break;
+		}
+	}
+
+	lookup_daemon_properties();
+
+	change_user_set_privs();
+
+	if (!fg)
+		daemonize();
+
+	init_signalhandling();
+
+	init_mutexes();
+
+	lookup_zonename(zonename, sizeof (zonename));
+
+	initialize_interfaces();
+
+	llp_parse_config();
+
+	(void) start_event_collection();
+
+	while ((e = np_queue_get_event()) != NULL) { /* forever */
+
+		syslog(LOG_INFO, "got event type %s",
+		    npe_type_str(e->npe_type));
+		switch (e->npe_type) {
+			case EV_ROUTING:
+			case EV_NEWADDR:
+			case EV_TIMER:
+				state_machine(e);
+				free_event(e);
+				break;
+			case EV_SYS:
+				free_event(e);
+				break;
+			case EV_SHUTDOWN:
+				state_machine(e);
+				(void) pthread_cancel(routing);
+				(void) pthread_cancel(scan);
+				(void) pthread_join(routing, NULL);
+				(void) pthread_join(scan, NULL);
+				syslog(LOG_INFO, "nwamd shutting down");
+				exit(EXIT_SUCCESS);
+				/* NOTREACHED */
+			default:
+				free_event(e);
+				syslog(LOG_NOTICE, "unknown event");
+				break;
+		}
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,218 @@
+/*
+ * 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"
+
+/*
+ * This file contains the core logic of nwamd.
+ *
+ * This functionality is built around state_machine() which consumes an event.
+ * The events which are input to this function are generated by either a change
+ * in interface state or a signal.  The events which correspond to signals are
+ * either external requests to shutdown or a timer event.  The interface events
+ * indicate if an interface has acquired a new address (via DHCP) or if a new
+ * interface has appeared on the system.  The latter event is used to detect new
+ * links.
+ *
+ * state_machine() calls high level routines in llp.c and interface.c to act on
+ * the state of the machine in response to events.
+ */
+
+#include <assert.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libsysevent.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/nvpair.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+void
+state_machine(struct np_event *e)
+{
+	struct interface *evif;
+	llp_t *evllp, *prefllp = NULL;
+	uint64_t flags;
+	boolean_t dhcp_restored = B_FALSE;
+
+	dprintf("state_machine(event type: %s, name: %s)",
+	    npe_type_str(e->npe_type), STRING(e->npe_name));
+	switch (e->npe_type) {
+	case EV_TIMER:
+		/* Our timer popped; check our dhcp status. */
+		if ((evif = get_interface(e->npe_name)) == NULL) {
+			dprintf("couldn't find waiting interface; "
+			    "ignoring EV_TIMER event");
+			break;
+		}
+		flags = get_ifflags(evif->if_name, evif->if_family);
+		if ((flags & IFF_DHCPRUNNING) == 0 || (flags & IFF_UP) != 0) {
+			/*
+			 * dhcp did come up successfully, or we're no
+			 * longer trying to do dhcp in this interface;
+			 * so no need to worry about the timer expiring.
+			 */
+			dprintf("timer popped for %s, but dhcp state is okay "
+			    "(ifflags 0x%llx)", evif->if_name, flags);
+			break;
+		}
+		/*
+		 * dhcp has not yet completed; give up on it for
+		 * now and, if it is the currently active llp,
+		 * switch to the next best.
+		 */
+		dprintf("giving up on dhcp on %s (ifflags 0x%llx)",
+		    evif->if_name, flags);
+		evif->if_lflags |= IF_DHCPFAILED;
+		if (interface_is_active(evif)) {
+			prefllp = llp_best_avail();
+			llp_swap(prefllp);
+		}
+
+		break;
+
+	case EV_ROUTING:
+		if ((evif = get_interface(e->npe_name)) == NULL ||
+		    (evllp = llp_lookup(e->npe_name)) == NULL) {
+			dprintf("state_machine: either no intf (%p) or no llp "
+			    "(%p) for %s; ignoring EV_ROUTING event",
+			    (void *)evif, (void *)evllp, STRING(e->npe_name));
+			break;
+		}
+		prefllp = llp_best_avail();
+		if (prefllp != link_layer_profile) {
+			dprintf("state_machine: change in state of link %s "
+			    "resulted in new preferred llp: %s (was %s)",
+			    llp_prnm(evllp), llp_prnm(prefllp),
+			    llp_prnm(link_layer_profile));
+			llp_swap(prefllp);
+		}
+
+		break;
+
+	case EV_NEWADDR:
+		if ((evif = get_interface(e->npe_name)) == NULL ||
+		    (evllp = llp_lookup(e->npe_name)) == NULL) {
+			dprintf("state_machine: either no intf (%p) or no llp "
+			    "(%p) for %s; ignoring EV_NEWADDR event",
+			    (void *)evif, (void *)evllp, STRING(e->npe_name));
+			break;
+		}
+		flags = get_ifflags(evif->if_name, evif->if_family);
+		if (!(flags & IFF_DHCPRUNNING) && !(flags & IFF_UP) &&
+		    evllp->llp_ipv4src == IPV4SRC_DHCP) {
+			evif->if_timer_expire = 0;
+			if ((evif->if_lflags & IF_DHCPFAILED) != 0) {
+				evif->if_lflags &= ~IF_DHCPFAILED;
+				dhcp_restored = B_TRUE;
+			}
+		}
+		if (evllp != link_layer_profile) {
+			if (dhcp_restored &&
+			    llp_high_pri(evllp, link_layer_profile) == evllp) {
+				dprintf("state_machine: dhcp completed on "
+				    "higher priority llp (%s); swapping",
+				    llp_prnm(evllp));
+				llp_swap(evllp);
+			} else {
+				dprintf("state_machine: newaddr event was for "
+				    "%s, not for current active link (%s); "
+				    "taking down %s", evllp->llp_lname,
+				    llp_prnm(link_layer_profile),
+				    evllp->llp_lname);
+				takedowninterface(evllp->llp_lname,
+				    evllp->llp_ipv4src == IPV4SRC_DHCP, B_FALSE,
+				    evllp->llp_ipv6onlink);
+				break;
+			}
+		}
+		/*
+		 * An address has been assigned to the current active link.
+		 * Notify the user, and activate the upper layer profile.
+		 *
+		 * Since other changes to the link (netmask change, broadcast
+		 * addr change, etc.) can cause a NEWADDR event (XXX would
+		 * be good if our event generator could do a better job
+		 * filtering!), only do this if there is not currently an
+		 * active ulp.
+		 */
+		if (!ulp_is_active()) {
+			show_if_status(evllp->llp_lname);
+			activate_upper_layer_profile(evllp->llp_ipv4src ==
+			    IPV4SRC_DHCP, evllp->llp_lname);
+		}
+		break;
+
+	case EV_SHUTDOWN:
+		/* Cleanup not expecting to see any more events after this */
+		cleanup();
+		return;
+		/* NOTREACHED */
+
+	default:
+		dprintf("unknown event");
+		break;
+	}
+}
+
+void
+cleanup(void)
+{
+	if (link_layer_profile != NULL) {
+		deactivate_upper_layer_profile();
+		takedowninterface(link_layer_profile->llp_lname,
+		    link_layer_profile->llp_ipv4src == IPV4SRC_DHCP, B_FALSE,
+		    link_layer_profile->llp_ipv6onlink);
+	}
+	/*
+	 * Since actions taken in nwamd result in dhcpagent being
+	 * launched, it's under our contract.  Thus, it needs to be
+	 * stopped when our stop method is executed.  But it needs
+	 * to stick around long enough for us to release any leases
+	 * we might have; thus, we don't want the stop method to
+	 * explicitly kill it.  We do it here, when we know we've
+	 * finished any dhcp cleanup that needed to be done.
+	 */
+	dprintf("killing dhcpagent");
+	(void) start_child(PKILL, "-z", zonename, "dhcpagent", NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,183 @@
+/*
+ * 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 _STRUCTURES_H
+#define	_STRUCTURES_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <net/if.h>
+#include <libscf.h>
+#include <libdlwlan.h>
+
+/*
+ * XXX More work on the state machine is needed.  In the future,
+ * events will be more like EV_NEWADDR, identifying the actual
+ * event that we care about, rather than the source of the event
+ * that we originally implemented.  For example, EV_ROUTING should
+ * be split into EV_LINK_UP and EV_LINK_DOWN.  It's a bit of a mix
+ * right now.
+ */
+enum np_event_type {
+	EV_ROUTING,
+	EV_SYS,
+	EV_TIMER,
+	EV_SHUTDOWN,
+	EV_NEWADDR
+};
+
+enum interface_type {
+	IF_UNKNOWN,
+	IF_WIRED,
+	IF_WIRELESS,
+	IF_TUN
+};
+
+struct np_event {
+	enum np_event_type npe_type;
+	char *npe_name;
+	struct np_event *npe_next;
+};
+
+/*
+ * This structure is used to represent the current state of the system.  We
+ * maintain these for IPv4 as proxies for links in the system.  This is
+ * differentiated from the LLP which contains the intended configuration of
+ * the system.
+ *
+ * Currently these are stored on ifs_head with the wired interfaces sorted
+ * together and the wireless ones sorted together with ifs_wired and
+ * ifs_wireless pointed at these sublists.  Access to these lists is not
+ * currently MT-safe.
+ *
+ * We explicitly do not maintain IPv6 interface structures.  In the current
+ * state machine, IPv6 interfaces are completely dependent on their IPv4
+ * counterparts.  For example, we make a decision to bring up an interface
+ * based on routing socket messages read from an AF_INET socket; we will
+ * always bring up v4, and may additionally bring up v6.  Also, when we
+ * start up, we find all interfaces in the system by doing 'ifconfig -a
+ * plumb', which will plumb v4 on all links; we always keep the v4 interface
+ * plumbed, taking it up and down depending on whether it's currently in use
+ * or not.  v6 interfaces are not plumbed initially; when we decide a link
+ * should be active (and its llp tells us to do v6), we'll mark the (already
+ * existing) v4 interface up, and 'plumb up' the v6 interface.  Conversely,
+ * when a link is no longer active, we 'down unplumb' the v6 interface, but
+ * only 'down' the v4 interface.
+ */
+struct interface {
+	char *if_name;
+	sa_family_t if_family;
+	uint64_t if_flags;
+	uint32_t if_lflags;
+	enum interface_type if_type;
+	uint32_t if_timer_expire;
+	struct sockaddr *if_ipaddr;
+	struct interface *if_next;
+};
+
+/*
+ * interface local flag values
+ */
+/*
+ * IF_DHCPFAILED: indicates that we timed out a dhcp request.  Will be
+ * cleared if the request eventually succeeds, or if the IFF_RUNNING flag
+ * is toggled off/on (i.e. the cable is unplugged/plugged)
+ */
+#define	IF_DHCPFAILED	0x01
+/*
+ * IF_DHCPSTARTED: much like IFF_DHCPRUNNING; but means specifically that
+ * we have inititated dhcp on this interface.  Used to prevent overlapping
+ * invocations of dhcp.
+ */
+#define	IF_DHCPSTARTED	0x02
+
+/*
+ * visited_wlans are stored on visited_wlan_list of type visisted_wlans_list.
+ * Access is protected by wifi_mutex.
+ */
+struct visited_wlans {
+	struct wireless_lan *wifi_net;
+	struct visited_wlans *next;
+};
+
+struct visited_wlans_list {
+	struct visited_wlans *head;
+	int total;
+};
+
+typedef enum {
+	IPV4SRC_STATIC,
+	IPV4SRC_DHCP
+} ipv4src_t;
+
+/*
+ * This structure contains the intended configuration of the system as
+ * differentiated from the actual IPv4 configuration of the system represented
+ * by the interface structures.
+ *
+ * llp structures are held on the list llp_head.  Access to this list is
+ * protected by llp_lock.
+ */
+typedef struct llp {
+	struct llp *llp_next;
+	char	llp_lname[LIFNAMSIZ];
+	int	llp_pri;		/* lower number => higher priority */
+	enum interface_type llp_type;
+	ipv4src_t llp_ipv4src;
+	char	*llp_ipv4addrstr;	/* if ipsrc is STATIC */
+	char	*llp_ipv6addrstr;	/* if the user provided a static addr */
+	boolean_t llp_ipv6onlink;	/* true if we plumb up a v6 interface */
+} llp_t;
+
+/*
+ * These entries are user allocated and should be managed by whomever
+ * originates the structure.
+ */
+struct wireless_lan {
+	char *essid;
+	char *bssid;
+	char *signal_strength;
+	char *raw_wepkey;
+	dladm_wlan_wepkey_t *cooked_wepkey;
+	boolean_t need_wepkey;
+	char *wl_if_name;
+};
+
+/*
+ * A holder for all the resources needed to get a property value
+ * using libscf.
+ */
+typedef struct scf_resources {
+	scf_handle_t *sr_handle;
+	scf_instance_t *sr_inst;
+	scf_snapshot_t *sr_snap;
+	scf_propertygroup_t *sr_pg;
+	scf_property_t *sr_prop;
+	scf_value_t *sr_val;
+} scf_resources_t;
+
+
+#endif /* _STRUCTURES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/util.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,321 @@
+/*
+ * 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"
+
+/*
+ * util.c contains a set of miscellaneous utility functions which:
+ * - syslog(LOG_DEBUG, ...) if debugging is enabled
+ * - check for an IP interface being marked running
+ * - look up all flags for an IP interface
+ * - start a child process
+ * - schedule a timer
+ * - check to see if a user is logged in to a graphical console
+ * - look up the zone name
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <spawn.h>
+#include <wait.h>
+#include <inetcfg.h>
+#include <utmpx.h>
+#include <pwd.h>
+#include <limits.h>
+#include <errno.h>
+#include <zone.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+extern char **environ;
+boolean_t debug = B_FALSE;
+
+void
+dprintf(const char *fmt, ...)
+{
+	va_list ap;
+	char vbuf[1024];
+
+	va_start(ap, fmt);
+	if (debug) {
+		(void) vsnprintf(vbuf, sizeof (vbuf), fmt, ap);
+		syslog(LOG_DEBUG, "%d: %s", pthread_self(), vbuf);
+	}
+	va_end(ap);
+}
+
+boolean_t
+is_plugged_in(struct interface *i)
+{
+	if (i->if_type == IF_WIRELESS)
+		return (B_TRUE);
+
+	return ((get_ifflags(i->if_name, i->if_family) & IFF_RUNNING) != 0);
+}
+
+uint64_t
+get_ifflags(const char *name, sa_family_t family)
+{
+	icfg_if_t intf;
+	icfg_handle_t h;
+	uint64_t flags = 0;
+
+	(void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
+	intf.if_protocol = family;
+
+	if (icfg_open(&h, &intf) != ICFG_SUCCESS)
+		return (0);
+
+	if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
+		/*
+		 * Interfaces can be ripped out from underneath us (for example
+		 * by DHCP).  We don't want to spam the console for those.
+		 */
+		if (errno == ENOENT)
+			dprintf("get_ifflags: icfg_get_flags failed for '%s'",
+			    name);
+		else
+			syslog(LOG_ERR, "get_ifflags: icfg_get_flags %s af "
+			    "%d: %m", name, family);
+		/* just to be sure... */
+		flags = 0;
+	}
+	icfg_close(h);
+
+	return (flags);
+}
+
+/*
+ *
+ * This starts a child process determined by command.  If command contains a
+ * slash then it is assumed to be a full path; otherwise the path is searched
+ * for an executable file with the name command.  Command is also used as
+ * argv[0] of the new process.  The rest of the arguments of the function
+ * up to the first NULL make up pointers to arguments of the new process.
+ *
+ * This function returns child exit status on success and -1 on failure.
+ *
+ * NOTE: original_sigmask must be set before this function is called.
+ */
+int
+start_childv(const char *command, char const * const *argv)
+{
+	posix_spawnattr_t attr;
+	sigset_t fullset;
+	int i, rc, status, n;
+	pid_t pid;
+	char vbuf[1024];
+
+	vbuf[0] = 0;
+	n = sizeof (vbuf);
+	for (i = 1; argv[i] != NULL && n > 2; i++) {
+		n -= strlcat(vbuf, " ", n);
+		n -= strlcat(vbuf, argv[i], n);
+	}
+	if (argv[i] != NULL || n < 0)
+		syslog(LOG_ERR, "start_childv can't log full arg vector");
+
+	if ((rc = posix_spawnattr_init(&attr)) != 0) {
+		dprintf("posix_spawnattr_init %d %s\n", rc, strerror(rc));
+		return (-1);
+	}
+	(void) sigfillset(&fullset);
+	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
+		dprintf("setsigdefault %d %s\n", rc, strerror(rc));
+		return (-1);
+	}
+	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
+		dprintf("setsigmask %d %s\n", rc, strerror(rc));
+		return (-1);
+	}
+	if ((rc = posix_spawnattr_setflags(&attr,
+	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
+		dprintf("setflags %d %s\n", rc, strerror(rc));
+		return (-1);
+	}
+
+	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
+	    environ)) > 0) {
+		dprintf("posix_spawnp failed errno %d", rc);
+		return (-1);
+	}
+
+	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
+		dprintf("posix_spawn_attr_destroy %d %s\n", rc, strerror(rc));
+		return (-1);
+	}
+
+	(void) waitpid(pid, &status, 0);
+	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
+		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
+		syslog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
+		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
+		    strsignal(i));
+		return (-2);
+	} else {
+		syslog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
+		    WEXITSTATUS(status));
+		return (WEXITSTATUS(status));
+	}
+}
+
+int
+start_child(const char *command, ...)
+{
+	const char **argv = NULL;
+	int argv_len = 0;
+	va_list ap;
+	int i = 1, rc;
+
+	va_start(ap, command);
+	do {
+		if (i >= argv_len) {
+			void *p;
+
+			argv_len = argv_len != 0 ? argv_len * 2 : 4;
+			p = realloc(argv, sizeof (*argv)*argv_len);
+			if (p != NULL) {
+				argv = p;
+			} else {
+				syslog(LOG_ERR, "Out of memory in start_child");
+				free(argv);
+				return (-1);
+			}
+		}
+
+		argv[i] = va_arg(ap, const char *);
+	} while (argv[i++] != NULL);
+	va_end(ap);
+	argv[0] = command;
+
+	rc = start_childv(command, argv);
+	free(argv);
+
+	return (rc);
+}
+
+uint32_t	timer_expire = TIMER_INFINITY;
+
+/*
+ * Schedules a SIGALRM in delay seconds, unless one is already
+ * scheduled sooner.  If one is already scheduled later than
+ * delay seconds from now, that one will be replaced.
+ */
+void
+start_timer(uint32_t now, uint32_t delay)
+{
+	if (now + delay > timer_expire)
+		return;
+
+	timer_expire = now + delay;
+	(void) alarm(delay);
+}
+
+boolean_t
+valid_graphical_user(boolean_t query)
+{
+	struct utmpx *utp;
+	char *user = NULL;
+	const char HOMESTR[] = "HOME=";
+	char buf[1024]; /* == sysconf(_SC_GETPW_R_SIZE_MAX) == NSS_BUFSIZ */
+	static char home_dir[PATH_MAX + sizeof (HOMESTR)];
+	struct passwd passwd;
+	struct passwd *pw;
+	boolean_t popup_ok;
+
+	/*
+	 * Check to see if our SMF property says popups are OK.
+	 */
+	if ((lookup_boolean_property(OUR_PG, query ? "popup_query" :
+	    "popup_info", &popup_ok) == 0) && !popup_ok)
+		return (B_FALSE);
+
+	/*
+	 * Look for someone logged into the console from host ":0" (i.e.,
+	 * the X display.  Down the road, we should generalize this so
+	 * ":0" is not hard-coded.
+	 */
+	setutxent();
+	while ((utp = getutxent()) != NULL) {
+		if ((utp->ut_type == USER_PROCESS) &&
+		    (strcmp(utp->ut_line, "console") == 0) &&
+		    (strcmp(utp->ut_host, ":0") == 0)) {
+			user = strdup(utp->ut_user);
+			break;
+		}
+	}
+	endutxent();
+	dprintf("utmpx: done %s", user != NULL ? user : "");
+
+	if (user == NULL)
+		return (B_FALSE);
+
+	pw = getpwnam_r(user, &passwd, buf, sizeof (buf));
+	if (pw == NULL) {
+		syslog(LOG_ERR, "couldn't get user %s: %m", user);
+		free(user);
+		return (B_FALSE);
+	}
+	free(user);
+
+	/*
+	 * We shouldn't be dumping this into our environment or changing
+	 * our uid/gid but instead starting up the zenity processes with
+	 * this display as this user.  RFE to change this.
+	 */
+	(void) putenv("DISPLAY=:0.0");
+
+	(void) strlcpy(home_dir, HOMESTR, sizeof (home_dir));
+	(void) strlcat(home_dir, pw->pw_dir, sizeof (home_dir));
+	(void) putenv(home_dir);
+
+	return (pw != NULL);
+}
+
+void
+lookup_zonename(char *zonename, size_t zonesize)
+{
+	zoneid_t zoneid = getzoneid();
+
+	if (getzonenamebyid(zoneid, zonename, zonesize) >= 0)
+		return;
+	syslog(LOG_ERR, "could not determine zone name");
+	(void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/variables.h	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,60 @@
+/*
+ * 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 _VARIABLES_H
+#define	_VARIABLES_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <libdlwlan.h>
+#include <sys/zone.h>
+
+extern struct np_event *equeue;
+
+extern pthread_mutex_t queue_mutex;
+extern pthread_cond_t queue_cond;
+extern pthread_t routing, scan;
+
+extern llp_t *link_layer_profile;
+
+extern sigset_t original_sigmask;
+extern pid_t ppid;
+
+extern boolean_t shutting_down;
+
+extern uint32_t timer_expire;
+
+extern uint_t wlan_scan_interval;
+extern dladm_wlan_strength_t wireless_scan_level;
+
+extern const char *OUR_FMRI;
+extern const char *OUR_PG;
+
+extern boolean_t debug;
+
+extern char zonename[ZONENAME_MAX];
+
+#endif /* _VARIABLES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,1596 @@
+/*
+ * 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"
+
+/*
+ * This file containes all the routines to handle wireless (more
+ * accurately, 802.11 "WiFi" family only at this moment) operations.
+ * This is only phase 0 work so the handling is pretty simple.
+ *
+ * When the daemon starts up, for each WiFi interface detected, it'll
+ * spawn a thread doing an access point (AP) scanning.  After the scans
+ * finish and if one of the WiFi interfaces is chosen to be active, the
+ * code will pop up a window showing the scan results and wait for the
+ * user's input on which AP to connect to and then complete the AP
+ * connection and IP interface set up.  WEP is supported to connect to
+ * those APs which require it.  The code also maintains a list of known
+ * WiFi APs in the file KNOWN_WIFI_NETS.  Whenever the code successfully
+ * connects to an AP, the AP's ESSID/BSSID will be added to that file.
+ * This file is used in the following way.
+ *
+ * If the AP scan results contain one known AP, the code will automatically
+ * connect to that AP without asking the user.  But if the detected signal
+ * strength of that AP is weaker than some other AP's, the code will still
+ * pop up a window asking for user input.
+ *
+ * If the AP scan results contain more than one known APs, the code will
+ * pop up a window listing those known APs only.  If the user does not
+ * make a choice, the full list of available APs will be shown.
+ *
+ * If the AP scan results contain no known AP, the full list of available
+ * APs is shown and the user is asked which AP to connect to.
+ *
+ * Note that not all APs broadcast the Beacon.  And some events may
+ * happen during the AP scan such that not all available APs are found.
+ * So the code also allows a user to manually input an AP's data in the
+ * pop up window.  This allows a user to connect to the aforementioned
+ * "hidden" APs.
+ *
+ * The code also periodically (specified by wlan_scan_interval) checks
+ * for the health of the AP connection.  If the signal strength of the
+ * connected AP drops below a threshold (specified by wireless_scan_level),
+ * the code will try to do another scan to find out other APs available.
+ * If there is currently no connected AP, a scan will also be done
+ * periodically to look for available APs.  In both cases, if there are
+ * new APs, the above AP connection procedure will be performed.
+ *
+ * One limitation of the current code is that a user cannot initiate a
+ * WiFi APs scan manually.  A manual scan can only be done when the code
+ * shows a pop up window asking for user input.  Suppose there is no
+ * connected AP and periodic scan is going on.  If a user wants to
+ * connect to a hidden AP, this is only possible if there is another
+ * non-hidden AP available such that after a periodic scan, the pop up
+ * window is shown.  This will be fixed in a later phase.
+ */
+
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <syslog.h>
+#include <assert.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <stropts.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libinetutil.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+static pthread_mutex_t wifi_mutex;
+static pthread_mutexattr_t wifi_mutex_attr;
+static pthread_mutex_t wifi_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t wifi_init_cond = PTHREAD_COND_INITIALIZER;
+
+typedef enum {
+	SUCCESS = 0,
+	FAILURE,
+	TRY_AGAIN
+} return_vals_t;
+
+/*
+ * Is a wireless interface doing a scan currently?  We only allow one
+ * wireless interface to do a scan at any one time.  This is to
+ * avoid unnecessary interference.  The following variable is used
+ * to store the interface doing the scan.  It is protected by
+ * wifi_init_mutex.
+ */
+static struct interface *wifi_scan_intf = NULL;
+
+/* used entries have non NULL memebers */
+static struct wireless_lan *wlans = NULL;
+static uint_t wireless_lan_count = 0; /* allocated */
+static uint_t wireless_lan_used = 0; /* used entries */
+
+static int wepkey_string_to_secobj_value(char *, uint8_t *, uint_t *);
+static int store_wepkey(char *, char *, char *);
+static dladm_wlan_wepkey_t *retrieve_wepkey(const char *, const char *);
+
+static boolean_t add_wlan_entry(struct interface *, char *, char *, char *,
+    boolean_t);
+static boolean_t already_in_visited_wlan_list(const struct wireless_lan *);
+static boolean_t check_wlan(const char *, const char *);
+static boolean_t connect_or_autoconf(struct wireless_lan *, const char *);
+static return_vals_t connect_to_new_wlan(const struct wireless_lan *, int,
+    const char *);
+static boolean_t find_wlan_entry(struct interface *, char *, char *);
+static void free_wireless_lan(struct wireless_lan *);
+static struct wireless_lan *get_specific_lan(void);
+static void get_user_wepkey(struct wireless_lan *);
+static char *get_zenity_response(const char *);
+static boolean_t wlan_autoconf(const char *ifname);
+static int zenity_height(int);
+static boolean_t get_scan_results(void *, dladm_wlan_attr_t *);
+
+#define	WIRELESS_LAN_INIT_COUNT	8
+
+struct visited_wlans_list *visited_wlan_list = NULL;
+
+/*
+ * The variable wlan_scan_interval controls the interval in seconds
+ * between periodic scans.
+ */
+uint_t wlan_scan_interval = 120;
+
+/*
+ * The variable wireless_scan_level specifies the lowest signal level
+ * when a periodic wireless scan needs to be done.
+ */
+dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK;
+
+void
+init_mutexes(void)
+{
+	(void) pthread_mutexattr_init(&wifi_mutex_attr);
+	(void) pthread_mutexattr_settype(&wifi_mutex_attr,
+	    PTHREAD_MUTEX_RECURSIVE);
+	(void) pthread_mutex_init(&wifi_mutex, &wifi_mutex_attr);
+}
+
+/*
+ * wlan is expected to be non-NULL.
+ */
+static void
+get_user_wepkey(struct wireless_lan *wlan)
+{
+	char zenity_cmd[1024];
+	char buf[1024];
+	FILE *zcptr;
+
+	/*
+	 * First, test if we have wepkey stored as secobj. If so,
+	 * no need to prompt for it.
+	 */
+	wlan->cooked_wepkey = retrieve_wepkey(wlan->essid, wlan->bssid);
+	if (wlan->cooked_wepkey != NULL) {
+		dprintf("get_user_wepkey: retrieve_wepkey() returns non NULL");
+		return;
+	}
+
+	(void) snprintf(zenity_cmd, sizeof (zenity_cmd),
+	    "%s --entry --text=\"%s %s\""
+	    " --title=\"%s\" --hide-text", ZENITY,
+	    gettext("Enter WEP key for WiFi network"), wlan->essid,
+	    gettext("Enter WEP key"));
+
+	if (!valid_graphical_user(B_TRUE))
+		return;
+
+	zcptr = popen(zenity_cmd, "r");
+	if (zcptr != NULL) {
+		if (fgets(buf, sizeof (buf), zcptr) != NULL) {
+			wlan->raw_wepkey = strdup(buf);
+			if (wlan->raw_wepkey != NULL) {
+				/* Store WEP key persistently */
+				if (store_wepkey(wlan->essid, wlan->bssid,
+				    wlan->raw_wepkey) != 0) {
+					syslog(LOG_ERR,
+					    "get_user_wepkey: failed to store"
+					    " user specified WEP key");
+				}
+			} else {
+				syslog(LOG_ERR,
+				    "get_user_wepkey: strdup failed");
+			}
+		}
+		(void) pclose(zcptr);
+	} else {
+		syslog(LOG_ERR, "Could not run %s: %m", ZENITY);
+	}
+}
+
+static boolean_t
+find_wlan_entry(struct interface *intf, char *essid, char *bssid)
+{
+	int i;
+
+	(void) pthread_mutex_lock(&wifi_mutex);
+	/* Check if the new entry is already there. */
+	for (i = 0; i < wireless_lan_used; i++) {
+		/*
+		 * Assume that essid and bssid are already NULL terminated.
+		 * Note that we also check for the interface name here.
+		 * If there is only one wireless interface, it should not
+		 * matter.  But if there are more than 1, then it is safer
+		 * to use the interface which finds the AP to connect to
+		 * it.
+		 */
+		if (strcmp(wlans[i].essid, essid) == 0 &&
+		    strcmp(wlans[i].bssid, bssid) == 0 &&
+		    strcmp(wlans[i].wl_if_name, intf->if_name) == 0) {
+			(void) pthread_mutex_unlock(&wifi_mutex);
+			return (B_TRUE);
+		}
+	}
+	(void) pthread_mutex_unlock(&wifi_mutex);
+	return (B_FALSE);
+}
+
+static void
+free_wireless_lan(struct wireless_lan *wlp)
+{
+	free(wlp->essid);
+	wlp->essid = NULL;
+	free(wlp->bssid);
+	wlp->bssid = NULL;
+	free(wlp->signal_strength);
+	wlp->signal_strength = NULL;
+	free(wlp->raw_wepkey);
+	wlp->raw_wepkey = NULL;
+	free(wlp->cooked_wepkey);
+	wlp->cooked_wepkey = NULL;
+	free(wlp->wl_if_name);
+	wlp->wl_if_name = NULL;
+}
+
+static boolean_t
+add_wlan_entry(struct interface *intf, char *essid, char *bssid,
+    char *signal_strength, boolean_t wep)
+{
+	int n;
+
+	(void) pthread_mutex_lock(&wifi_mutex);
+
+	if (wireless_lan_used == wireless_lan_count) {
+		int newcnt;
+		struct wireless_lan *r;
+
+		newcnt = (wireless_lan_count == 0) ?
+		    WIRELESS_LAN_INIT_COUNT : wireless_lan_count * 2;
+		r = realloc(wlans, newcnt * sizeof (*wlans));
+		if (r == NULL) {
+			syslog(LOG_ERR, "add_wlan_entry: realloc failed");
+			(void) pthread_mutex_unlock(&wifi_mutex);
+			return (B_FALSE);
+		}
+		(void) memset((void *)(r + wireless_lan_count), 0,
+		    (newcnt - wireless_lan_count) * sizeof (*r));
+		wireless_lan_count = newcnt;
+		wlans = r;
+	}
+
+	n = wireless_lan_used;
+	wlans[n].essid = strdup(essid);
+	wlans[n].bssid = strdup(bssid);
+	wlans[n].signal_strength = strdup(signal_strength);
+	wlans[n].wl_if_name = strdup(intf->if_name);
+	wlans[n].need_wepkey = wep;
+	wlans[n].raw_wepkey = NULL;
+	wlans[n].cooked_wepkey = NULL;
+	if (wlans[n].essid == NULL || wlans[n].bssid == NULL ||
+	    wlans[n].signal_strength == NULL || wlans[n].wl_if_name == NULL) {
+		syslog(LOG_ERR, "add_wlan_entry: strdup failed");
+		free_wireless_lan(&(wlans[n]));
+		(void) pthread_mutex_unlock(&wifi_mutex);
+		return (B_FALSE);
+	}
+	wireless_lan_used++;
+	(void) pthread_mutex_unlock(&wifi_mutex);
+	return (B_TRUE);
+}
+
+static void
+clear_lan_entries(void)
+{
+	int i;
+
+	(void) pthread_mutex_lock(&wifi_mutex);
+	for (i = 0; i < wireless_lan_used; i++)
+		free_wireless_lan(&(wlans[i]));
+	wireless_lan_used = 0;
+	(void) pthread_mutex_unlock(&wifi_mutex);
+}
+
+/*
+ * Verify if a WiFi NIC is associated with the given ESSID.  If the given
+ * ESSID is NULL, and if the NIC is already connected,  return true.
+ * Otherwise,
+ *
+ * 1. If the NIC is associated with the given ESSID, return true.
+ * 2. If the NIC is not associated with any AP, return false.
+ * 3. If the NIC is associated with a different AP, tell the driver
+ *    to disassociate with it and then return false.
+ */
+static boolean_t
+check_wlan(const char *intf, const char *exp_essid)
+{
+	dladm_wlan_linkattr_t attr;
+	dladm_status_t status;
+	char cur_essid[DLADM_STRSIZE];
+	char errmsg[DLADM_STRSIZE];
+
+	status = dladm_wlan_get_linkattr(intf, &attr);
+	if (status != DLADM_STATUS_OK) {
+		dprintf("check_wlan: dladm_wlan_get_linkattr() failed: %s",
+		    dladm_status2str(status, errmsg));
+		return (B_FALSE);
+	}
+	if (attr.la_status == DLADM_WLAN_LINKSTATUS_DISCONNECTED)
+		return (B_FALSE);
+	if (exp_essid == NULL)
+		return (B_TRUE);
+	(void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid, cur_essid);
+
+	/* Is the NIC associated with the expected one? */
+	if (strcmp(cur_essid, exp_essid) == 0)
+		return (B_TRUE);
+
+	/* Tell the driver to disassociate with the current AP. */
+	if (dladm_wlan_disconnect(intf) != DLADM_STATUS_OK)
+		dprintf("check_wlan: dladm_wlan_disconnect() fails");
+	return (B_FALSE);
+}
+
+/*
+ * Given a wireless interface, use it to scan for available networks.
+ */
+boolean_t
+scan_wireless_nets(struct interface *intf)
+{
+	boolean_t	new_ap = B_FALSE;
+	dladm_status_t	status;
+	int		num_ap;
+
+	assert(intf->if_type == IF_WIRELESS);
+	/*
+	 * If there is already a scan in progress, wait until the
+	 * scan is done to avoid interference.  But if the interface
+	 * doing the scan is the same as the one requesting the new
+	 * scan, just return.
+	 *
+	 * Whenever a wireless scan is in progress, all the other
+	 * threads checking the wireless AP list should wait.
+	 */
+	(void) pthread_mutex_lock(&wifi_init_mutex);
+	while (wifi_scan_intf != NULL) {
+		dprintf("scan_wireless_nets in progress: old %s new %s",
+		    wifi_scan_intf->if_name, intf->if_name);
+		if (strcmp(wifi_scan_intf->if_name, intf->if_name) == 0) {
+			(void) pthread_mutex_unlock(&wifi_init_mutex);
+			return (B_FALSE);
+		}
+		(void) pthread_cond_wait(&wifi_init_cond, &wifi_init_mutex);
+	}
+	wifi_scan_intf = intf;
+	(void) pthread_mutex_unlock(&wifi_init_mutex);
+
+	/*
+	 * Since only one scan is allowed at any one time, no need to grab
+	 * a lock in checking wireless_lan_used.
+	 */
+	num_ap = wireless_lan_used;
+	status = dladm_wlan_scan(intf->if_name, intf, get_scan_results);
+	if (status != DLADM_STATUS_OK)
+		syslog(LOG_NOTICE, "cannot scan link '%s'", intf->if_name);
+	else
+		new_ap = (wireless_lan_used > num_ap);
+
+	(void) pthread_mutex_lock(&wifi_init_mutex);
+	wifi_scan_intf = NULL;
+	(void) pthread_cond_signal(&wifi_init_cond);
+	(void) pthread_mutex_unlock(&wifi_init_mutex);
+
+	return (new_ap);
+}
+
+/* ARGSUSED */
+static void
+wireless_scan(struct interface *ifp, void *arg)
+{
+	if (ifp->if_type == IF_WIRELESS) {
+		dprintf("periodic_wireless_scan: %s", ifp->if_name);
+		if (scan_wireless_nets(ifp)) {
+			dprintf("new AP added: %s", ifp->if_name);
+			gen_newif_event(ifp);
+		}
+	}
+}
+
+static boolean_t
+get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
+{
+
+	boolean_t 	wep;
+	char		essid_name[DLADM_STRSIZE];
+	char		bssid_name[DLADM_STRSIZE];
+	char		strength[DLADM_STRSIZE];
+
+	(void) dladm_wlan_essid2str(&attrp->wa_essid, essid_name);
+	(void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name);
+	(void) dladm_wlan_strength2str(&attrp->wa_strength, strength);
+
+	wep = (attrp->wa_secmode == DLADM_WLAN_SECMODE_WEP);
+
+	if (!find_wlan_entry(arg, essid_name, bssid_name) &&
+	    add_wlan_entry(arg, essid_name, bssid_name, strength, wep)) {
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+/* ARGSUSED */
+void *
+periodic_wireless_scan(void *arg)
+{
+	/*
+	 * No periodic scan if the "-i" option is used to change the
+	 * interval to 0.
+	 */
+	if (wlan_scan_interval == 0)
+		return (NULL);
+
+	for (;;) {
+		int ret;
+		dladm_wlan_linkattr_t attr;
+		llp_t *cur_llp;
+		struct interface *ifp;
+
+		ret = poll(NULL, 0, wlan_scan_interval * MILLISEC);
+
+		/*
+		 * We assume that once an llp is created, it will never be
+		 * deleted in the lifetime of the process.  So it is OK
+		 * to do this assignment without a lock.
+		 */
+		cur_llp = link_layer_profile;
+
+		/*
+		 * We do a scan if
+		 *
+		 * 1. There is no active profile.  Or
+		 * 2. We are now disconnected from the AP.  Or
+		 * 3. The signal strength falls below a certain specified level.
+		 */
+		if (ret == 0) {
+			if (cur_llp != NULL) {
+				if (cur_llp->llp_type != IF_WIRELESS ||
+				    dladm_wlan_get_linkattr(cur_llp->llp_lname,
+				    &attr) != DLADM_STATUS_OK) {
+					continue;
+				}
+				if (attr.la_status ==
+				    DLADM_WLAN_LINKSTATUS_CONNECTED &&
+				    attr.la_wlan_attr.wa_strength >
+				    wireless_scan_level) {
+					continue;
+				}
+				/*
+				 * Clear the IF_DHCPFAILED and IF_DHCPSTARTED
+				 * flags on this interface; this is a "fresh
+				 * start" for the interface, so we should
+				 * retry dhcp.
+				 */
+				ifp = get_interface(cur_llp->llp_lname);
+				if (ifp != NULL) {
+					ifp->if_lflags &= ~IF_DHCPFAILED;
+					ifp->if_lflags &= ~IF_DHCPSTARTED;
+				}
+				/*
+				 * Deactivate the original llp.
+				 * If we reached this point, we either were
+				 * not connected, or were connected with
+				 * "very weak" signal strength; so we're
+				 * assuming that having this llp active was
+				 * not very useful.  So we deactivate.
+				 */
+				llp_deactivate();
+			}
+			/* We should start from fresh. */
+			clear_lan_entries();
+			walk_interface(wireless_scan, NULL);
+		} else if (ret == -1) {
+			if (errno == EINTR)
+				continue;
+			syslog(LOG_INFO, "periodic_wireless_scan: poll failed");
+			return (NULL);
+		}
+	}
+	/* NOTREACHED */
+	return (NULL);
+}
+
+/*
+ * Below are functions used to handle storage/retrieval of WEP keys
+ * for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
+ * and dladm_get_secobj().
+ */
+
+/*
+ * Convert wepkey hexascii string to raw secobj value. This
+ * code is very similar to convert_secobj() in dladm.c, it would
+ * be good to have a libdladm function to convert values.
+ */
+static int
+wepkey_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp)
+{
+	size_t buf_len = strlen(buf);
+
+	dprintf("before: wepkey_string_to_secobj_value: buf_len = %d", buf_len);
+	if (buf_len == 0) {
+		syslog(LOG_ERR,
+		    "wepkey_string_to_secobj_value: empty WEP key");
+		return (-1);
+	}
+
+	if (buf[buf_len - 1] == '\n')
+		buf[--buf_len] = '\0';
+
+	dprintf("after: wepkey_string_to_secobj_value: buf_len = %d", buf_len);
+	switch (buf_len) {
+	case 5:		/* ASCII key sizes */
+	case 13:
+		(void) memcpy(obj_val, buf, (uint_t)buf_len);
+		*obj_lenp = (uint_t)buf_len;
+		break;
+	case 10:
+	case 26:	/* Hex key sizes, not preceded by 0x */
+		if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
+		    != 0) {
+			syslog(LOG_ERR,
+			    "wepkey_string_to_secobj_value: invalid WEP key");
+			return (-1);
+		}
+		break;
+	case 12:
+	case 28:	/* Hex key sizes, preceded by 0x */
+		if (strncmp(buf, "0x", 2) != 0 ||
+		    hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
+		    obj_lenp) != 0) {
+			syslog(LOG_ERR,
+			    "wepkey_string_to_secobj_value: invalid WEP key");
+			return (-1);
+		}
+		break;
+	default:
+		syslog(LOG_ERR,
+		    "wepkey_string_to_secobj_value: invalid WEP key length");
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Print the key format into the appropriate field, then convert any ":"
+ * characters to ".", as ":[1-4]" is the slot indicator, which otherwise
+ * would trip us up.  The third parameter is expected to be of size
+ * DLADM_SECOBJ_NAME_MAX.
+ */
+static void
+set_key_name(const char *essid, const char *bssid, char *name, size_t nsz)
+{
+	int i;
+
+	if (bssid == NULL)
+		(void) snprintf(name, nsz, "nwam-%s", essid);
+	else
+		(void) snprintf(name, nsz, "nwam-%s-%s", essid, bssid);
+	for (i = 0; i < strlen(name); i++)
+		if (name[i] == ':')
+			name[i] = '.';
+}
+
+static int
+store_wepkey(char *essid, char *bssid, char *raw_wepkey)
+{
+	uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
+	uint_t obj_len;
+	char obj_name[DLADM_SECOBJ_NAME_MAX];
+	dladm_status_t status;
+	char errmsg[DLADM_STRSIZE];
+
+	/*
+	 * Name wepkey object for this WLAN so it can be later retrieved
+	 * (name is unique for each ESSID/BSSID combination).
+	 */
+	set_key_name(essid, bssid, obj_name, sizeof (obj_name));
+	dprintf("store_wepkey: obj_name is %s", obj_name);
+
+	if (wepkey_string_to_secobj_value(raw_wepkey, obj_val, &obj_len) != 0) {
+		/* above function logs internally on failure */
+		return (-1);
+	}
+
+	status = dladm_set_secobj(obj_name, DLADM_SECOBJ_CLASS_WEP,
+	    obj_val, obj_len,
+	    DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_TEMP);
+	if (status != DLADM_STATUS_OK) {
+		syslog(LOG_ERR, "store_wepkey: could not create secure object "
+		    "'%s' for wepkey: %s", obj_name,
+		    dladm_status2str(status, errmsg));
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * retrieve_wepkey returns NULL if no wepkey was recovered from dladm
+ */
+static dladm_wlan_wepkey_t *
+retrieve_wepkey(const char *essid, const char *bssid)
+{
+	dladm_status_t status;
+	char errmsg[DLADM_STRSIZE];
+	dladm_wlan_wepkey_t *cooked_wepkey;
+	dladm_secobj_class_t class;
+
+	/*
+	 * Newly-allocated wepkey must be freed by caller, or by
+	 * subsequent call to retrieve_wepkey().
+	 */
+	if ((cooked_wepkey = malloc(sizeof (dladm_wlan_wepkey_t))) == NULL) {
+		syslog(LOG_ERR, "retrieve_wepkey: malloc failed");
+		return (NULL);
+	}
+
+	/* Set name appropriately to retrieve wepkey for this WLAN */
+	set_key_name(essid, bssid, cooked_wepkey->wk_name,
+	    DLADM_SECOBJ_NAME_MAX);
+	dprintf("retrieve_wepkey: len = %d, object = %s\n",
+	    strlen(cooked_wepkey->wk_name), cooked_wepkey->wk_name);
+	cooked_wepkey->wk_len = DLADM_SECOBJ_NAME_MAX;
+	cooked_wepkey->wk_idx = 1;
+
+	/* Try the kernel first, then fall back to persistent storage. */
+	status = dladm_get_secobj(cooked_wepkey->wk_name, &class,
+	    cooked_wepkey->wk_val, &cooked_wepkey->wk_len,
+	    DLADM_OPT_TEMP);
+	if (status != DLADM_STATUS_OK) {
+		dprintf("retrieve_wepkey: dladm_get_secobj(TEMP) failed: %s",
+		    dladm_status2str(status, errmsg));
+		status = dladm_get_secobj(cooked_wepkey->wk_name, &class,
+		    cooked_wepkey->wk_val, &cooked_wepkey->wk_len,
+		    DLADM_OPT_PERSIST);
+	}
+
+	switch (status) {
+	case DLADM_STATUS_OK:
+		dprintf("retrieve_wepkey: dladm_get_secobj succeeded: len %d",
+		    cooked_wepkey->wk_len);
+		break;
+	case DLADM_STATUS_NOTFOUND:
+		/*
+		 * We do not want an error in the case that the secobj
+		 * is not found, since we then prompt for it.
+		 */
+		free(cooked_wepkey);
+		return (NULL);
+	default:
+		syslog(LOG_ERR, "retrieve_wepkey: could not get wepkey "
+		    "from secure object '%s': %s", cooked_wepkey->wk_name,
+		    dladm_status2str(status, errmsg));
+		free(cooked_wepkey);
+		return (NULL);
+	}
+
+	return (cooked_wepkey);
+}
+
+/* Create the KNOWN_WIFI_NETS using info from the interface list.  */
+void
+create_known_wifi_nets_file(void)
+{
+	FILE *fp;
+	int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+
+	/* Create the NWAM directory in case it does not exist. */
+	if (mkdir(LLPDIR, dirmode) != 0) {
+		if (errno != EEXIST) {
+			syslog(LOG_ERR, "could not create %s: %m", LLPDIR);
+			return;
+		}
+	}
+	if ((fp = fopen(KNOWN_WIFI_NETS, "a+")) == NULL) {
+		syslog(LOG_ERR, "could not open %s: %m", KNOWN_WIFI_NETS);
+		return;
+	}
+	dprintf("Creating %s", KNOWN_WIFI_NETS);
+	(void) fclose(fp);
+}
+
+/*
+ * Add an entry to known_wifi_nets file given the parameters.
+ */
+void
+update_known_wifi_nets_file(const char *essid, const char *bssid)
+{
+	FILE *fp;
+
+	dprintf("update_known_wifi_nets_file(%s, %s)", essid, STRING(bssid));
+	fp = fopen(KNOWN_WIFI_NETS, "a+");
+	if (fp == NULL) {
+		if (errno != ENOENT) {
+			syslog(LOG_ERR, "fopen(%s) failed: %m",
+			    KNOWN_WIFI_NETS);
+			return;
+		}
+
+		/*
+		 * If there is none, we should create one instead.
+		 * For now, we will use the order of seeing each new net
+		 * for the priority.  We should have a priority field
+		 * in the known_wifi_nets file eventually...
+		 */
+		create_known_wifi_nets_file();
+		fp = fopen(KNOWN_WIFI_NETS, "a");
+		if (fp == NULL) {
+			syslog(LOG_ERR, "second fopen(%s) failed: %m",
+			    KNOWN_WIFI_NETS);
+			return;
+		}
+	}
+	/* now see if this info is already in the file */
+	if (known_wifi_nets_lookup(essid, bssid) == B_FALSE) {
+		/* now add this to the file */
+		(void) fprintf(fp, "%s\t%s\n", essid,
+		    bssid == NULL ? "" : bssid);
+	}
+	(void) fclose(fp);
+}
+
+/*
+ * Check if the given AP (ESSID, BSSID pair) is on the known AP list.
+ */
+boolean_t
+known_wifi_nets_lookup(const char *new_essid, const char *new_bssid)
+{
+	FILE *fp;
+	char line[LINE_MAX];
+	char *cp, *lasts, *essid, *bssid;
+	int line_num;
+	boolean_t found = B_FALSE;
+
+	/*
+	 * For now the file format is:
+	 * essid\tbssid
+	 * (essid followed by tab followed by bssid)
+	 */
+	fp = fopen(KNOWN_WIFI_NETS, "r+");
+	if (fp == NULL) {
+		if (errno != ENOENT) {
+			syslog(LOG_ERR, "fopen(%s) failed: %m",
+			    KNOWN_WIFI_NETS);
+			return (B_FALSE);
+		}
+		create_known_wifi_nets_file();
+		return (B_FALSE);
+	}
+	for (line_num = 1; fgets(line, sizeof (line), fp) != NULL; line_num++) {
+		if (line[strlen(line) - 1] == '\n')
+			line[strlen(line) - 1] = '\0';
+
+		cp = line;
+		while (isspace(*cp))
+			cp++;
+
+		if (*cp == '#' || *cp == '\0')
+			continue;
+
+		if ((essid = strtok_r(cp, "\t", &lasts)) == NULL) {
+			syslog(LOG_ERR, "%s:%d: not enough tokens; "
+			    "ignoring entry", KNOWN_WIFI_NETS, line_num);
+			continue;
+		}
+		bssid = strtok_r(NULL, "\t", &lasts);
+		if (strcmp(essid, new_essid) != 0)
+			continue;
+		if (new_bssid == NULL) {
+			/*
+			 * no BSSID specified => ESSID match
+			 * is good enough.
+			 */
+			found = B_TRUE;
+		} else if (bssid != NULL && strcmp(bssid, new_bssid) == 0) {
+			/* Match on both is always good. */
+			found = B_TRUE;
+		}
+		if (found)
+			break;
+	}
+	(void) fclose(fp);
+	return (found);
+}
+
+/*
+ * reqlan->essid is required (i.e., cannot be NULL)
+ * reqlan->bssid is optional (i.e., may be NULL)
+ */
+boolean_t
+connect_chosen_lan(struct wireless_lan *reqlan, const char *ifname)
+{
+	uint_t	keycount;
+	dladm_wlan_wepkey_t *key;
+	dladm_wlan_attr_t attr;
+	dladm_status_t status;
+	uint_t flags = DLADM_WLAN_CONNECT_NOSCAN;
+	int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
+	char errmsg[DLADM_STRSIZE];
+
+	(void) memset(&attr, 0, sizeof (attr));
+	/* try to apply essid selected by the user */
+	if (reqlan->essid == NULL)
+		return (B_FALSE);
+	dprintf("connect_chosen_lan(%s, %s, %s)", reqlan->essid,
+	    STRING(reqlan->bssid), ifname);
+
+	/* If it is already connected to the required AP, just return. */
+	if (check_wlan(ifname, reqlan->essid))
+		return (B_TRUE);
+
+	if (dladm_wlan_str2essid(reqlan->essid, &attr.wa_essid) !=
+	    DLADM_STATUS_OK) {
+		syslog(LOG_ERR,
+		    "connect_chosen_lan: invalid ESSID '%s' for '%s'",
+		    reqlan->essid, ifname);
+		return (B_FALSE);
+	}
+	attr.wa_valid = DLADM_WLAN_ATTR_ESSID;
+	if (reqlan->bssid != NULL) {
+		if (dladm_wlan_str2bssid(reqlan->bssid, &attr.wa_bssid) !=
+		    DLADM_STATUS_OK) {
+			syslog(LOG_ERR,
+			    "connect_chosen_lan: invalid BSSID '%s' for '%s'",
+			    reqlan->bssid, ifname);
+			return (B_FALSE);
+		}
+		attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
+	}
+
+	/* First check for the wepkey */
+	if (reqlan->need_wepkey) {
+		get_user_wepkey(reqlan);
+		if (reqlan->cooked_wepkey == NULL)
+			return (B_FALSE);
+		attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
+		attr.wa_secmode = DLADM_WLAN_SECMODE_WEP;
+		key = reqlan->cooked_wepkey;
+		keycount = 1;
+		dprintf("connect_chosen_lan: retrieved key");
+	} else {
+		key = NULL;
+		keycount = 0;
+	}
+
+	/*
+	 * Connect; only scan if a bssid was not specified.
+	 * If it times out and we were trying with a bssid,
+	 * try a second time with just the ESSID.
+	 */
+
+	status = dladm_wlan_connect(ifname, &attr, timeout, key, keycount,
+	    flags);
+	dprintf("connect_chosen_lan: dladm_wlan_connect returned %s",
+	    dladm_status2str(status, errmsg));
+	if (status == DLADM_STATUS_TIMEDOUT && reqlan->bssid != NULL) {
+		syslog(LOG_INFO, "connect_chosen_lan: failed for (%s, %s), "
+		    "trying again with just (%s)",
+		    reqlan->essid, reqlan->bssid, reqlan->essid);
+		attr.wa_valid &= ~DLADM_WLAN_ATTR_BSSID;
+		flags = 0;
+		status = dladm_wlan_connect(ifname, &attr, timeout, key,
+		    keycount, flags);
+	}
+	if (status != DLADM_STATUS_OK) {
+		syslog(LOG_ERR,
+		    "connect_chosen_lan: connect to '%s' failed on '%s': %s",
+		    reqlan->essid, ifname, dladm_status2str(status, errmsg));
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * First attempt to connect to the network specified by essid.
+ * If that fails, attempt to connect using autoconf.
+ */
+static boolean_t
+connect_or_autoconf(struct wireless_lan *reqlan, const char *ifname)
+{
+	if (!connect_chosen_lan(reqlan, ifname)) {
+		syslog(LOG_WARNING,
+		    "Could not connect to chosen WLAN %s, going to auto-conf",
+		    reqlan->essid);
+		return (wlan_autoconf(ifname));
+	}
+	return (B_TRUE);
+}
+
+/*
+ * The +1 is for the extra "compare" row, the 24 is for the font spacing
+ * and the 125 if extra for the buttons et al.
+ */
+static int
+zenity_height(int rows)
+{
+	return (((rows + 1) * 24) + 125);
+}
+
+static return_vals_t
+connect_to_new_wlan(const struct wireless_lan *lanlist, int num,
+    const char *ifname)
+{
+	int i, rtn, j = 0;
+	struct interface *intf;
+	struct wireless_lan *reqlan;
+	char buf[2048];
+	char *endbuf = buf;
+	size_t buflen = 0;
+	char zenity_cmd[2048];
+	char *other_str = gettext("Other");
+	char *rescan_str = gettext("Rescan");
+	boolean_t autoconf = B_FALSE;
+
+	dprintf("connect_to_new_wlan(..., %d, %s)", num, ifname);
+
+	if (num == 0) {
+		display(gettext("No Wifi networks found; continuing in case "
+		    "you know of any which do not broadcast."));
+	}
+
+	if ((intf = get_interface(ifname)) == NULL) {
+		dprintf("connect_to_new_wlan: cannot find wireless interface: "
+		    "%s", ifname);
+		return (FAILURE);
+	}
+
+	/* build list for display */
+	buf[0] = '\0';
+	for (i = 0; i < num; i++) {
+		if ((lanlist[i].essid == NULL) || (lanlist[i].bssid == NULL)) {
+			syslog(LOG_WARNING, "wifi list entry %d broken: "
+			    "essid %s, bssid %s; ignoring", i,
+			    STRING(lanlist[i].essid), STRING(lanlist[i].bssid));
+			continue;
+		}
+		/*
+		 * Only use the interface which finds the AP to connect to it.
+		 */
+		if (strcmp(lanlist[i].wl_if_name, ifname) != 0) {
+			dprintf("connect_to_new_wlan: wrong interface (%s) for "
+			    "%s (should be %s)", ifname, lanlist[i].essid,
+			    lanlist[i].wl_if_name);
+			continue;
+		}
+
+		j++;
+		/*
+		 * Zenity uses a space as its delimiter, so put the ESSID in
+		 * quotes so it won't get confused if there is a space in the
+		 * ESSID name.
+		 */
+		if (sizeof (buf) - 1 > buflen) {
+			buflen += snprintf(endbuf, sizeof (buf) - buflen,
+			    "%d '%s' %s %s '%s' ", j,
+			    lanlist[i].essid, lanlist[i].bssid,
+			    lanlist[i].need_wepkey ? "WEP" : "none",
+			    lanlist[i].signal_strength);
+			endbuf = buf + buflen;
+		}
+	}
+	if (sizeof (buf) - 1 > buflen) {
+		/*
+		 * All columns except the first are empty for the "Other"
+		 * and "Rescan" rows.
+		 */
+		(void) snprintf(endbuf, sizeof (buf) - buflen,
+		    "\"%s\" \"\" \"\" \"\" \"\" \"%s\" \"\" \"\" \"\" \"\"",
+		    other_str, rescan_str);
+	}
+
+	(void) snprintf(zenity_cmd, sizeof (zenity_cmd),
+	    "%s --list --title=\"%s\""
+	    " --height=%d --width=500 --column=\"#\" --column=\"%s\""
+	    " --column=\"%s\" --column=\"%s\" --column=\"%s\""
+	    " %s ", ZENITY, gettext("Choose WiFi network you wish to activate"),
+	    zenity_height(j), "ESSID", "BSSID", gettext("Encryption"),
+	    gettext("Signal"), buf);
+
+	/* present list to user and get selection */
+	rtn = get_user_preference(zenity_cmd, other_str, rescan_str, &reqlan,
+	    lanlist);
+	switch (rtn) {
+	case 1:
+		/* user chose "other"; pop-up for specific essid */
+		reqlan = get_specific_lan();
+		break;
+	case 2:
+		/* user chose "Rescan" */
+		(void) scan_wireless_nets(intf);
+		return (TRY_AGAIN);
+	case -1:
+		reqlan = NULL;
+		break;
+	default:
+		/* common case: reqlan was set in get_user_preference() */
+		break;
+	}
+
+	if ((reqlan == NULL) || (reqlan->essid == NULL)) {
+		dprintf("did not get user preference; attempting autoconf");
+		return (wlan_autoconf(ifname) ? SUCCESS : FAILURE);
+	}
+	dprintf("get_user_preference() returned essid %s, bssid %s, encr %s",
+	    reqlan->essid, STRING(reqlan->bssid),
+	    reqlan->need_wepkey ? "WEP" : "none");
+
+	/* set wepkey before first time connection */
+	if (reqlan->need_wepkey && reqlan->raw_wepkey == NULL &&
+	    reqlan->cooked_wepkey == NULL)
+		get_user_wepkey(reqlan);
+
+	/*
+	 * now attempt to connect to selection, backing
+	 * off to autoconf if the connect fails
+	 */
+	if (connect_chosen_lan(reqlan, ifname)) {
+		/* succeeded, so add entry to known_essid_list_file */
+		update_known_wifi_nets_file(reqlan->essid, reqlan->bssid);
+	} else {
+		/* failed to connect; try auto-conf */
+		syslog(LOG_WARNING, "Could not connect to chosen WLAN "
+		    "%s; going to auto-conf", reqlan->essid);
+		autoconf = B_TRUE;
+	}
+	free_wireless_lan(reqlan);
+
+	if (!autoconf)
+		return (SUCCESS);
+	return (wlan_autoconf(ifname) ? SUCCESS : FAILURE);
+}
+
+struct wireless_lan *
+prompt_for_visited(void)
+{
+	char buf[1024];
+	size_t buflen = 0;
+	char *endbuf = buf;
+	char zenity_cmd[1024];
+	char *select_str = gettext("Select from all available WiFi networks");
+	char *rescan_str = gettext("Rescan");
+	struct wireless_lan *req_conf = NULL, *list;
+	int i = 0;
+
+	/* Build zenity command string */
+	buf[0] = '\0';
+	if (visited_wlan_list->total > 0) {
+		struct visited_wlans *vlp;
+		struct wireless_lan *wlp;
+
+		list = calloc(visited_wlan_list->total,
+		    sizeof (struct wireless_lan));
+		if (list == NULL) {
+			syslog(LOG_ERR, "prompt_for_visited: calloc failed");
+			return (NULL);
+		}
+		for (vlp = visited_wlan_list->head;
+		    vlp != NULL; vlp = vlp->next) {
+
+			wlp = vlp->wifi_net;
+			if (wlp->essid == NULL || wlp->bssid == NULL) {
+				dprintf("Invalid essid/bssid values");
+				continue;
+			}
+			i++;
+			if (sizeof (buf) - 1 > buflen) {
+				buflen += snprintf(endbuf,
+				    sizeof (buf) - buflen,
+				    "%d '%s' %s %s '%s' ",
+				    i, wlp->essid, wlp->bssid,
+				    wlp->need_wepkey ? "WEP" : gettext("none"),
+				    wlp->signal_strength);
+				endbuf = buf + buflen;
+			}
+			list[i-1].essid = wlp->essid;
+			list[i-1].bssid = wlp->bssid;
+			list[i-1].need_wepkey = wlp->need_wepkey;
+			list[i-1].raw_wepkey = wlp->raw_wepkey;
+			list[i-1].cooked_wepkey = wlp->cooked_wepkey;
+			list[i-1].signal_strength = wlp->signal_strength;
+			list[i-1].wl_if_name = wlp->wl_if_name;
+		}
+	}
+	if (sizeof (buf) - 1 > buflen) {
+		(void) snprintf(endbuf, sizeof (buf) - buflen,
+		    "\"%s\"", select_str);
+	}
+
+	(void) snprintf(zenity_cmd, sizeof (zenity_cmd),
+	    "%s --list --title=\"%s\""
+	    " --height=%d --width=670 --column=\"#\" --column=\"%s\""
+	    " --column=\"%s\" --column=\"%s\" --column=\"%s\""
+	    " %s", ZENITY, gettext("Choose from pre-visited WiFi network"),
+	    zenity_height(i), "ESSID", "BSSID", gettext("Encryption"),
+	    gettext("Signal"), buf);
+
+	/*
+	 * If the user doesn't make a choice or something goes wrong
+	 * (get_user_preference() returned -1), or if the user chooses
+	 * the "select from all available" string (get_user_preference()
+	 * returned 1), we simply return NULL: there was no selection
+	 * made from the visited list.  If the user *did* make a choice
+	 * (get_user_preference() returned 0), return the alloc'd struct.
+	 */
+	if (get_user_preference(zenity_cmd, select_str,	rescan_str,
+	    &req_conf, list) != 0)
+		req_conf = NULL;
+
+	free(list);
+	return (req_conf);
+}
+
+static boolean_t
+wlan_autoconf(const char *ifname)
+{
+	dladm_status_t status;
+	boolean_t autoconf;
+
+	if (lookup_boolean_property(OUR_PG, "autoconf", &autoconf) == 0) {
+		if (!autoconf)
+			return (B_FALSE);
+	}
+
+	/* If the NIC is already associated with something, just return. */
+	if (check_wlan(ifname, NULL))
+		return (B_TRUE);
+
+	/*
+	 * Do autoconf, relying on the heuristics used by dladm_wlan_connect()
+	 * to cycle through WLANs detected in priority order, attempting
+	 * to connect.
+	 */
+	status = dladm_wlan_connect(ifname, NULL,
+	    DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, NULL, 0, 0);
+	if (status != DLADM_STATUS_OK) {
+		char errmsg[DLADM_STRSIZE];
+
+		syslog(LOG_ERR,
+		    "wlan_autoconf: dladm_wlan_connect failed for '%s': %s",
+		    ifname, dladm_status2str(status, errmsg));
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Returns:
+ * B_TRUE if this info is already in visited_wlan_list
+ * B_FALSE if not
+ */
+static boolean_t
+already_in_visited_wlan_list(const struct wireless_lan *new_wlan)
+{
+	struct visited_wlans *vwlp;
+
+	vwlp = visited_wlan_list->head;
+	while (vwlp != NULL && vwlp->wifi_net != NULL) {
+		if (strcmp(vwlp->wifi_net->essid, new_wlan->essid) == 0) {
+			dprintf("%s already in visited_wlan_list",
+			    vwlp->wifi_net->essid);
+			return (B_TRUE);
+		} else {
+			vwlp = vwlp->next;
+		}
+	}
+	return (B_FALSE);
+}
+
+static char *
+get_zenity_response(const char *cmd)
+{
+	char buf[1024];
+	size_t buf_len;
+	char *rtnp;
+	FILE *cptr;
+	int ret;
+
+	if (!valid_graphical_user(B_TRUE))
+		return (NULL);
+
+	cptr = popen(cmd, "r");
+	if (cptr == NULL) {
+		syslog(LOG_ERR, "Could not run %s: %m", ZENITY);
+		return (NULL);
+	}
+	if (fgets(buf, sizeof (buf), cptr) != NULL) {
+		buf_len = strlen(buf);
+		if (buf_len > 0 && buf[buf_len - 1] == '\n')
+			buf[buf_len - 1] = '\0';
+		dprintf("get_zenity_resp: zenity returned '%s'", buf);
+	} else {
+		buf[0] = '\0';
+		dprintf("get_zenity_resp: zenity returned nothing");
+	}
+	ret = pclose(cptr);
+	/*
+	 * We should probably make sure that those ZENITY_* exit
+	 * environment variables are not set first...
+	 */
+	if (ret == -1 || !WIFEXITED(ret) || (WEXITSTATUS(ret) == 255)) {
+		dprintf("get_zenity_resp: %s did not exit normally", ZENITY);
+		return (NULL);
+	}
+	if (WEXITSTATUS(ret) == 1) {
+		dprintf("get_zenity_resp: user cancelled");
+		return (NULL);
+	}
+	if ((rtnp = strdup(buf)) == NULL)
+		syslog(LOG_ERR, "get_zenity_response: strdup failed");
+
+	return (rtnp);
+}
+
+/*
+ * get_user_preference():  Present a list of essid/bssid pairs to the
+ * user via zenity (passed in in a pre-formatted zenity string in the
+ * param cmd).  If there's a final list item ("Other", "Select from
+ * full list", etc.) that may be selected and should be differentiated,
+ * that item should be passed in in compare param.
+ *
+ * Four possible return values:
+ * -1: No response from user, or other error.  *req_lan is undefined.
+ *  0: essid/bssid pair was selected.  *req_lan has these values in
+ *     a malloc'd buffer, which the caller is responsible for freeing.
+ *  1: a compare string ("Other") was given, and the user response matched
+ *     that string.  *req_lan is undefined.
+ *  2: a compare string ("Rescan") was given, and the user response matched
+ *     that string.  *req_lan is undefined.
+ */
+int
+get_user_preference(const char *cmd, const char *compare_other,
+    const char *compare_rescan, struct wireless_lan **req_lan,
+    const struct wireless_lan *list)
+{
+	char *response;
+	struct wireless_lan *wlp;
+	const struct wireless_lan *sel;
+	int answer;
+
+	assert(req_lan != NULL);
+	wlp = calloc(1, sizeof (struct wireless_lan));
+	if (wlp == NULL) {
+		syslog(LOG_ERR, "malloc failed");
+		return (-1);
+	}
+
+	response = get_zenity_response(cmd);
+	if (response == NULL) {
+		free(wlp);
+		return (-1);
+	}
+	if (strcmp(response, compare_other) == 0) {
+		free(response);
+		free(wlp);
+		return (1);
+	}
+	if (strcmp(response, compare_rescan) == 0) {
+		free(response);
+		free(wlp);
+		return (2);
+	}
+	answer = atoi(response);
+	if (answer <= 0) {
+		dprintf("%s returned invalid string", ZENITY);
+		free(response);
+		free(wlp);
+		return (-1);
+	}
+	sel = &list[answer - 1];
+
+	if ((wlp->essid = strdup(sel->essid)) == NULL)
+		goto dup_error;
+
+	if ((sel->bssid != NULL) && ((wlp->bssid = strdup(sel->bssid)) == NULL))
+		goto dup_error;
+
+	wlp->need_wepkey = sel->need_wepkey;
+
+	if ((sel->raw_wepkey != NULL) &&
+	    ((wlp->raw_wepkey = strdup(sel->raw_wepkey)) == NULL))
+		goto dup_error;
+
+	if (sel->cooked_wepkey != NULL) {
+		wlp->cooked_wepkey = malloc(sizeof (dladm_wlan_wepkey_t));
+		if (wlp->cooked_wepkey == NULL)
+			goto dup_error;
+		*(wlp->cooked_wepkey) = *(sel->cooked_wepkey);
+	}
+
+	if ((sel->signal_strength != NULL) &&
+	    ((wlp->signal_strength = strdup(sel->signal_strength)) == NULL))
+		goto dup_error;
+
+	if ((sel->wl_if_name != NULL) &&
+	    ((wlp->wl_if_name = strdup(sel->wl_if_name)) == NULL))
+		goto dup_error;
+
+	dprintf("selected: %s, %s, %s, '%s', %s", wlp->essid,
+	    STRING(wlp->bssid), wlp->need_wepkey ? "WEP" : "none",
+	    STRING(wlp->signal_strength), STRING(wlp->wl_if_name));
+
+	free(response);
+
+	*req_lan = wlp;
+	return (0);
+
+dup_error:
+	syslog(LOG_ERR, "get_user_preference: strdup failed");
+	free_wireless_lan(wlp);
+	free(wlp);
+	free(response);
+	return (-1);
+}
+
+/*
+ * Returns a pointer to an alloc'd struct wireless lan if a response
+ * is received from the user; only the essid will be valid in this
+ * case.  If no response received, or other failure, returns NULL.
+ */
+static struct wireless_lan *
+get_specific_lan(void)
+{
+	char specify_str[1024];
+	char *response;
+	struct wireless_lan *wlp;
+
+	/*
+	 * TRANSLATION_NOTE: the token "ESSID" should not be translated
+	 * in the phrase below.
+	 */
+	(void) snprintf(specify_str, sizeof (specify_str), ZENITY
+	    " --entry --title=\"%s\" --text=\"%s\"",
+	    gettext("Specify WiFi network"), gettext("Enter ESSID"));
+
+	response = get_zenity_response(specify_str);
+	if (response == NULL)
+		return (NULL);
+
+	wlp = calloc(1, sizeof (struct wireless_lan));
+	if (wlp == NULL) {
+		syslog(LOG_ERR, "malloc failed: %m");
+		free(response);
+		return (NULL);
+	}
+	wlp->essid = response;
+
+	(void) snprintf(specify_str, sizeof (specify_str), ZENITY
+	    " --list --title=\"%s\" --text=\"%s\" --column=\"%s\" none wep",
+	    gettext("Security"), gettext("Enter security"),
+	    gettext("Type"));
+
+	response = get_zenity_response(specify_str);
+	if (response != NULL && strcmp(response, "wep") == 0)
+		wlp->need_wepkey = B_TRUE;
+
+	free(response);
+	return (wlp);
+}
+
+/*
+ * Returns:
+ * B_TRUE if things go well
+ * B_FALSE if we were unable to connect to anything
+ */
+boolean_t
+handle_wireless_lan(const char *ifname)
+{
+	const struct wireless_lan *cur_wlans;
+	int i, num_wlans;
+	struct wireless_lan *req_conf = NULL;
+	boolean_t result;
+	dladm_wlan_strength_t strongest = DLADM_WLAN_STRENGTH_VERY_WEAK;
+	dladm_wlan_strength_t strength;
+	return_vals_t connect_result;
+
+start_over:
+	if (visited_wlan_list == NULL) {
+		if ((visited_wlan_list = calloc(1,
+		    sizeof (struct visited_wlans_list))) == NULL) {
+			syslog(LOG_ERR, "handle_wireless_lan: calloc failed");
+			return (B_FALSE);
+		}
+	}
+
+	/*
+	 * We wait while a scan is in progress.  Since we allow a user
+	 * to initiate a re-scan, we can proceed even when no scan
+	 * has been done to fill in the AP list.
+	 */
+	(void) pthread_mutex_lock(&wifi_init_mutex);
+	while (wifi_scan_intf != NULL)
+		(void) pthread_cond_wait(&wifi_init_cond, &wifi_init_mutex);
+	(void) pthread_mutex_unlock(&wifi_init_mutex);
+
+	(void) pthread_mutex_lock(&wifi_mutex);
+	num_wlans = wireless_lan_used;
+	cur_wlans = wlans;
+
+	/*
+	 * Try to see if any of the wifi nets currently available
+	 * has been used previously. If more than one available
+	 * nets has been used before, then prompt user with
+	 * all the applicable previously wifi nets, and ask which
+	 * one to connect to.
+	 */
+	for (i = 0; i < num_wlans; i++) {
+		struct visited_wlans *new_wlan;
+
+		/* Find the AP with the highest signal. */
+		if (dladm_wlan_str2strength(cur_wlans[i].signal_strength,
+		    &strength) != DLADM_STATUS_OK) {
+			continue;
+		}
+		if (strength > strongest)
+			strongest = strength;
+
+		if (!known_wifi_nets_lookup(cur_wlans[i].essid,
+		    cur_wlans[i].bssid))
+			continue;
+
+		if (already_in_visited_wlan_list(&cur_wlans[i])) {
+			/* don't have to add it again */
+			continue;
+		}
+
+		/* add this to the visited_wlan_list */
+		dprintf("adding essid %s, bssid %s to visited list",
+		    cur_wlans[i].essid, STRING(cur_wlans[i].bssid));
+
+		new_wlan = calloc(1, sizeof (struct visited_wlans));
+		if (new_wlan == NULL) {
+			syslog(LOG_ERR, "handle_wireless_lan: calloc failed");
+			result = B_FALSE;
+			connect_result = FAILURE;
+			goto all_done;
+		}
+		new_wlan->wifi_net = calloc(1, sizeof (struct wireless_lan));
+		if (new_wlan->wifi_net == NULL) {
+			free(new_wlan);
+			syslog(LOG_ERR, "handle_wireless_lan: calloc failed");
+			result = B_FALSE;
+			connect_result = FAILURE;
+			goto all_done;
+		}
+		new_wlan->wifi_net->essid = strdup(cur_wlans[i].essid);
+		new_wlan->wifi_net->bssid = strdup(cur_wlans[i].bssid);
+		new_wlan->wifi_net->raw_wepkey = NULL;
+		new_wlan->wifi_net->cooked_wepkey = NULL;
+		new_wlan->wifi_net->need_wepkey = cur_wlans[i].need_wepkey;
+		new_wlan->wifi_net->signal_strength =
+		    strdup(cur_wlans[i].signal_strength);
+		new_wlan->wifi_net->wl_if_name =
+		    strdup(cur_wlans[i].wl_if_name);
+		if (new_wlan->wifi_net->essid == NULL ||
+		    new_wlan->wifi_net->bssid == NULL ||
+		    new_wlan->wifi_net->signal_strength == NULL ||
+		    new_wlan->wifi_net->wl_if_name == NULL) {
+			syslog(LOG_ERR, "handle_wireless_lan: strdup failed");
+			free_wireless_lan(new_wlan->wifi_net);
+			free(new_wlan->wifi_net);
+			free(new_wlan);
+			result = B_FALSE;
+			connect_result = FAILURE;
+			goto all_done;
+		}
+
+		new_wlan->next = visited_wlan_list->head;
+		visited_wlan_list->head = new_wlan;
+		visited_wlan_list->total++;
+	}
+
+	if (visited_wlan_list->total == 1) {
+		struct wireless_lan *target = visited_wlan_list->head->wifi_net;
+
+		/*
+		 * only one previously visited wifi net, connect to it
+		 * (falling back to autoconf if the connect fails) if there
+		 * is no AP with a better signal strength.
+		 */
+		if (dladm_wlan_str2strength(target->signal_strength,
+		    &strength) == DLADM_STATUS_OK) {
+			if (strength < strongest)
+				goto connect_any;
+		}
+		result = connect_or_autoconf(target, ifname);
+		connect_result = result ? SUCCESS : FAILURE;
+
+	} else if (visited_wlan_list->total > 1) {
+		/*
+		 * more than one previously visited wifi nets seen.
+		 * prompt user for which one should we connect to
+		 */
+		if ((req_conf = prompt_for_visited()) != NULL) {
+			result = connect_or_autoconf(req_conf, ifname);
+			connect_result = result ? SUCCESS : FAILURE;
+		} else {
+			/*
+			 * The user didn't make a choice; offer the full list.
+			 */
+			connect_result = connect_to_new_wlan(cur_wlans,
+			    num_wlans, ifname);
+			result = (connect_result == SUCCESS);
+		}
+	} else {
+connect_any:
+		/* last case, no previously visited wlan found */
+		connect_result = connect_to_new_wlan(cur_wlans, num_wlans,
+		    ifname);
+		result = (connect_result == SUCCESS);
+	}
+
+all_done:
+	/*
+	 * We locked down the list above; free it now that we're done.
+	 */
+	(void) pthread_mutex_unlock(&wifi_mutex);
+	if (visited_wlan_list != NULL) {
+		struct visited_wlans *vwlp = visited_wlan_list->head, *next;
+
+		for (; vwlp != NULL; vwlp = next) {
+			if (vwlp->wifi_net != NULL) {
+				free_wireless_lan(vwlp->wifi_net);
+				free(vwlp->wifi_net);
+				vwlp->wifi_net = NULL;
+			}
+			next = vwlp->next;
+			free(vwlp);
+		}
+		free(visited_wlan_list);
+		visited_wlan_list = NULL;
+	}
+	if (req_conf != NULL) {
+		free_wireless_lan(req_conf);
+		free(req_conf);
+		req_conf = NULL;
+	}
+	if (connect_result == TRY_AGAIN) {
+		dprintf("end of handle_wireless_lan() TRY_AGAIN");
+		goto start_over;
+	}
+	return (result);
+}
--- a/usr/src/cmd/svc/milestone/Makefile	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/cmd/svc/milestone/Makefile	Fri Mar 30 17:01:13 2007 -0700
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
@@ -103,6 +103,7 @@
 	manifest-import \
 	net-loopback \
 	net-init \
+	net-nwam \
 	net-physical \
 	net-routing-setup \
 	net-svc \
--- a/usr/src/cmd/svc/milestone/net-loopback	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/cmd/svc/milestone/net-loopback	Fri Mar 30 17:01:13 2007 -0700
@@ -53,31 +53,8 @@
 # in the boot process) and set forwarding flags.
 #
 
-# ipV4 loopback
+# IPv4 loopback
 /sbin/ifconfig lo0 plumb 127.0.0.1 up
 
-# Configure the v6 loopback if any IPv6 interfaces are configured.
-interface_names="`echo /etc/hostname6.*[0-9] 2>/dev/null`"
-if [ "$interface_names" != "/etc/hostname6.*[0-9]" ]; then
-        ORIGIFS="$IFS"
-        IFS="$IFS."
-        set -- $interface_names
-        IFS="$ORIGIFS"
-        while [ $# -ge 2 ]; do
-                shift
-                if [ $# -gt 1 -a "$2" != "/etc/hostname6" ]; then
-                        while [ $# -gt 1 -a "$1" != "/etc/hostname6" ]; do
-                                shift
-                        done
-                else
-                        inet6_list="$inet6_list $1"
-                        shift
-                fi
-        done
-fi
-
-if [ -n "$inet6_list" ]; then
-	/sbin/ifconfig lo0 inet6 plumb ::1 up
-else
-	exit $SMF_EXIT_OK
-fi
+# IPv6 loopback
+/sbin/ifconfig lo0 inet6 plumb ::1 up
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svc/milestone/net-nwam	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,66 @@
+#!/sbin/sh
+#
+# 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.
+#
+
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+. /lib/svc/share/smf_include.sh
+
+#
+# In a shared-IP zone we need this service to be up, but all of the work
+# it tries to do is irrelevant (and will actually lead to the service
+# failing if we try to do it), so just bail out.
+# In the global zone and exclusive-IP zones we proceed.
+#
+smf_configure_ip || exit $SMF_EXIT_OK
+
+case "$1" in
+'refresh')
+	/usr/bin/pkill -HUP -z `smf_zonename` nwamd
+	;;
+
+'start')
+	if smf_is_globalzone; then
+		# Initialize security objects.
+		/sbin/dladm init-secobj
+	fi
+	# start nwamd in foreground; it will daemonize itself
+	if /lib/inet/nwamd ; then
+		exit $SMF_EXIT_OK
+	else
+		exit $SMF_EXIT_ERR_FATAL
+	fi
+	;;
+
+'stop')
+	/usr/bin/pkill -z `smf_zonename` nwamd
+	;;
+
+*)
+	echo "Usage: $0 { start | stop | refresh }"
+	exit $SMF_EXIT_ERR_FATAL
+	;;
+esac
+exit $SMF_EXIT_OK
--- a/usr/src/cmd/svc/milestone/net-svc	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/cmd/svc/milestone/net-svc	Fri Mar 30 17:01:13 2007 -0700
@@ -54,6 +54,7 @@
 	;;
 esac
 
+interface=$2
 
 # If boot variables are not set, set variables we use
 [ -z "$_INIT_UTS_NODENAME" ] && _INIT_UTS_NODENAME=`/usr/bin/uname -n`
@@ -134,72 +135,6 @@
 }
 
 #
-# update_hosts_file
-# This routine updates the /etc/inet/hosts file with the hostname and IP
-# address that was obtained via DHCP.
-#
-update_hosts_file ()
-{
-	filename=hosts
-	# Delete any old lines added by dhcp.
-	/usr/bin/sed -e '/# Added by DHCP$/d' /etc/inet/${filename} \
-	    > /tmp/${filename}_clear.$$
-
-	shift $#	# Clear $0-9 first in case grep fails
-	set -- `/usr/bin/grep "^[ 	]*$ipaddr[ 	]" \
-	    /tmp/${filename}_clear.$$ 2>/dev/null`
-
-	if [ $# -gt 0 ]; then
-		#
-		# IP address is already in the file. Ensure the
-		# associated hostname is the same as the Hostname
-		# property returned by the DHCP server.
-		#
-		/usr/bin/sed -e "/^[ 	]*${ipaddr}[ 	]/d" \
-		    /tmp/${filename}_clear.$$ >/tmp/${filename}.$$
-		echo "${ipaddr}\t${hostname}\t# Added by DHCP" \
-		    >>/tmp/${filename}.$$
-	else
-		#
-		# IP address is missing from the respective file.  Now check
-		# to see if the hostname is present with a different IP.
-		#
-		shift $#	# Clear $0-9 in case grep fails
-		set -- `/usr/bin/grep -s -v '^#' /tmp/${filename}_clear.$$ | \
-		    /usr/bin/egrep "[	 ]${hostname}([	 ]|$)"`
-
-		if [ $# -gt 0 ]; then
-			#
-			# Hostname is present in the file. Rewrite this line
-			# to have the new IP address and the DHCP comment.
-			#
-			/usr/bin/sed -e "/^[ 	]*${1}[ 	]/d" \
-			    /tmp/${filename}_clear.$$ >/tmp/${filename}.$$
-
-			shift	# Shift off $1 (the old IP)
-
-			echo "$ipaddr $*\c" | /usr/bin/tr ' ' '\t' \
-			    >>/tmp/${filename}.$$
-
-			echo "\t# Added by DHCP" >>/tmp/${filename}.$$
-		else
-			#
-			# Hostname is not present in the named file.
-			# Add a new line for the host at the end of
-			# the new respective file.
-			#
-			/usr/bin/mv /tmp/${filename}_clear.$$ \
-			    /tmp/${filename}.$$
-			echo "${ipaddr}\t${hostname}\t# Added by DHCP" \
-			    >>/tmp/${filename}.$$
-		fi
-	fi
-
-	/usr/bin/rm -f /tmp/${filename}_clear.$$
-	mv_file /tmp/${filename}.$$ /etc/inet/${filename} 444
-}
-
-#
 # We now need to reset the netmask and broadcast address for our network
 # interfaces.  Since this may result in a name service lookup, we want to
 # now wait for NIS to come up if we previously started it.
@@ -224,7 +159,11 @@
 smf_netstrategy
 
 if [ "$_INIT_NET_STRATEGY" = "dhcp" ]; then
-	dnsservers=`/sbin/dhcpinfo DNSserv`
+	if [ -n "$interface" ]; then
+		dnsservers=`/sbin/dhcpinfo -i $interface DNSserv`
+	else
+		dnsservers=`/sbin/dhcpinfo DNSserv`
+	fi
 else
 	dnsservers=""
 fi
@@ -242,7 +181,11 @@
 	if [ ! -f /etc/resolv.conf ]; then
 		/usr/bin/touch /etc/resolv.conf
 	fi
-	dnsdomain=`/sbin/dhcpinfo DNSdmain`
+	if [ -n "$interface" ]; then
+		dnsdomain=`/sbin/dhcpinfo -i $interface DNSdmain`
+	else
+		dnsdomain=`/sbin/dhcpinfo DNSdmain`
+	fi
 	export dnsservers dnsdomain
 	/usr/bin/nawk </etc/resolv.conf >/tmp/resolv.conf.$$ '
 		function writedomain() {
@@ -283,22 +226,19 @@
 	mv_file /tmp/nsswitch.conf.$$ /etc/nsswitch.conf 644
 fi
 
-if [ "$_INIT_NET_STRATEGY" = "dhcp" ]; then
-
-	hostname=`/usr/bin/uname -n`
-	ipaddr=`/sbin/dhcpinfo Yiaddr`
-	update_hosts_file
+# Clean up any old DHCP-added entries (except loopback) in the hosts file.
+if /usr/bin/grep '# Added by DHCP$' /etc/inet/hosts >/dev/null 2>&1; then
+	/usr/bin/nawk '{
+		if (index($0, "# Added by DHCP") == 0 ||
+		    $1 == "127.0.0.1" || $1 == "::1") {
+			print $0
+		}
+	}' /etc/inet/hosts > /tmp/hosts.$$
+	mv_file /tmp/hosts.$$ /etc/inet/hosts 444
+fi
 
-else
-	# We're not using a dhcp strategy, so host entries added by
-	# DHCP should be removed from /etc/inet/hosts.
-
-	if /usr/bin/grep '# Added by DHCP$' /etc/inet/hosts >/dev/null 2>&1;
-	    then
-		/usr/bin/sed -e '/# Added by DHCP$/d' \
-		    /etc/inet/hosts > /tmp/hosts.$$
-		mv_file /tmp/hosts.$$ /etc/inet/hosts 444
-	fi
+if [ -z "$SMF_FMRI" ] || [ "$SMF_FMRI" = "svc:/network/physical:nwam" ]; then
+	exit 0
 fi
 
 #
--- a/usr/src/cmd/svc/milestone/network-physical.xml	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/cmd/svc/milestone/network-physical.xml	Fri Mar 30 17:01:13 2007 -0700
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
 <!--
- Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  Use is subject to license terms.
 
  CDDL HEADER START
@@ -65,14 +65,10 @@
 		<propval name='duration' type='astring' value='transient' />
 	</property_group>
 
-	</instance>
-
-	<stability value='Unstable' />
-
 	<template>
 		<common_name>
 			<loctext xml:lang='C'>
-				physical network interfaces
+				physical network interfaces			
 			</loctext>
 		</common_name>
 		<documentation>
@@ -80,6 +76,66 @@
 				manpath='/usr/share/man' />
 		</documentation>
 	</template>
+
+	</instance>
+
+	<instance name='nwam' enabled='false'>
+
+	<exec_method
+		type='method'
+		name='start'
+		exec='/lib/svc/method/net-nwam start'
+		timeout_seconds='600' />
+
+	<exec_method
+		type='method'
+		name='stop'
+		exec='/lib/svc/method/net-nwam stop'
+		timeout_seconds='60' />
+
+	<exec_method
+		type='method'
+		name='refresh'
+		exec='/lib/svc/method/net-nwam refresh'
+		timeout_seconds='60' />
+
+	<property_group name='general' type='framework'>
+		<!-- to start/stop NWAM services -->
+		<propval name='action_authorization' type='astring'
+			value='solaris.smf.manage.nwam' />
+		<propval name='value_authorization' type='astring'
+			value='solaris.smf.manage.nwam' />
+	</property_group>
+
+	<property_group name='nwamd' type='application'>
+		<stability value='Unstable' />
+		<propval name='debug' type='boolean' value='false' />
+		<propval name='use_net_svc' type='boolean' value='true' />
+		<propval name='popup_info' type='boolean' value='true' />
+		<propval name='popup_query' type='boolean' value='true' />
+		<propval name='autoconf' type='boolean' value='true' />
+		<propval name='dhcp_wait_time' type='count' value='60' />
+		<propval name='scan_interval' type='count' value='120' />
+		<propval name='value_authorization' type='astring'
+			value='solaris.smf.value.nwam' />
+	</property_group>
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+				physical network interface autoconfiguration
+			</loctext>
+		</common_name>
+		<documentation>
+			<manpage title='nwamd' section='1M'
+				manpath='/usr/share/man' />
+		</documentation>
+	</template>
+
+	</instance>
+
+	<stability value='Unstable' />
+
 </service>
 
 </service_bundle>
--- a/usr/src/lib/libsecdb/auth_attr.txt	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/lib/libsecdb/auth_attr.txt	Fri Mar 30 17:01:13 2007 -0700
@@ -18,7 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # /etc/security/auth_attr
@@ -90,6 +90,7 @@
 solaris.smf.manage.cron:::Manage Cron Service States::help=SmfCronStates.html
 solaris.smf.manage.hal:::Manage HAL Service States::help=SmfHALStates.html
 solaris.smf.manage.name-service-cache:::Manage Name Service Cache Daemon Service States::help=SmfNscdStates.html
+solaris.smf.manage.nwam:::Manage Network Auto-Magic Service States::help=SmfNWAMStates.html
 solaris.smf.manage.power:::Manage Power Management Service States::help=SmfPowerStates.html
 solaris.smf.manage.routing:::Manage Routing Service States::help=SmfRoutingStates.html
 solaris.smf.manage.rmvolmgr:::Manage Rmvolmgr Service States::help=SmfRmvolmgrStates.html
@@ -98,6 +99,7 @@
 solaris.smf.manage.ssh:::Manage Secure Shell Service States::help=SmfSshStates.html
 solaris.smf.manage.system-log:::Manage Syslog Service States::help=SmfSyslogStates.html
 solaris.smf.value.:::Change Values of SMF Service Properties::help=SmfValueHeader.html
+solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html
 solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html
 #
 solaris.system.:::Machine Administration::help=SysHeader.html
--- a/usr/src/lib/libsecdb/help/auths/Makefile	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/lib/libsecdb/help/auths/Makefile	Fri Mar 30 17:01:13 2007 -0700
@@ -18,7 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #ident	"%Z%%M%	%I%	%E% SMI"
@@ -75,12 +75,14 @@
 	SmfModifyHeader.html \
 	SmfModifyMethod.html \
 	SmfNscdStates.html \
+	SmfNWAMStates.html \
 	SmfPowerStates.html \
 	SmfRoutingStates.html \
 	SmfSendmailStates.html \
 	SmfSshStates.html \
 	SmfSyslogStates.html \
 	SmfValueHeader.html \
+	SmfValueNWAM.html \
 	SmfValueRouting.html \
 	NetworkHeader.html \
 	WifiConfig.html \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsecdb/help/auths/SmfNWAMStates.html	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+    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.
+-->
+<!-- SCCS keyword
+#ident	"%Z%%M%	%I%	%E% SMI"
+-->
+<!--
+   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Manage NWAM Service States is in the Authorizations Include
+column, it grants the authorization to enable, disable, or restart
+Network Auto-Magic (NWAM) services.
+<p>
+If Manage NWAM Service States is grayed, then you are not entitled
+to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libsecdb/help/auths/SmfValueNWAM.html	Fri Mar 30 17:01:13 2007 -0700
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+    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.
+-->
+<!-- SCCS keyword
+#ident	"%Z%%M%	%I%	%E% SMI"
+-->
+<!--
+   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Value NWAM Properties is in the Authorizations Include
+column, it grants the the authorization to change Network Auto-Magic
+(NWAM) service property values.
+<P> 
+If Value NWAM Properties is grayed, then you are not entitled to
+Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
--- a/usr/src/lib/libsecdb/prof_attr.txt	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/lib/libsecdb/prof_attr.txt	Fri Mar 30 17:01:13 2007 -0700
@@ -20,7 +20,7 @@
 #
 
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # ident	"%Z%%M%	%I%	%E% SMI"
@@ -49,7 +49,7 @@
 Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log;help=RtMaintAndRepair.html
 Media Backup:::Backup files and file systems:help=RtMediaBkup.html
 Media Restore:::Restore files and file systems from backups:help=RtMediaRestore.html
-Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing;profiles=Network Wifi Management;help=RtNetMngmnt.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam;profiles=Network Wifi Management;help=RtNetMngmnt.html
 Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh;profiles=Network Wifi Security,Network Link Security;help=RtNetSecure.html
 Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html
 Network Wifi Security:::Manage wifi network security:auths=solaris.network.wifi.wep;help=RtNetWifiSecure.html
--- a/usr/src/lib/libsocket/inet/link_addr.c	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/lib/libsocket/inet/link_addr.c	Fri Mar 30 17:01:13 2007 -0700
@@ -2,9 +2,8 @@
  * 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.
+ * 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.
@@ -19,8 +18,9 @@
  *
  * CDDL HEADER END
  */
+
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -106,7 +106,7 @@
 		return (NULL);
 	}
 
-	if ((netaddr = malloc(numcolons)) == NULL) {
+	if ((netaddr = malloc(numcolons + 1)) == NULL) {
 		*maclen = 0;
 		return (NULL);
 	}
--- a/usr/src/pkgdefs/SUNW0on/prototype_com	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com	Fri Mar 30 17:01:13 2007 -0700
@@ -234,12 +234,14 @@
 f none usr/lib/help/auths/locale/SmfModifyHeader.html 444 root bin
 f none usr/lib/help/auths/locale/SmfModifyMethod.html 444 root bin
 f none usr/lib/help/auths/locale/SmfNscdStates.html 444 root bin
+f none usr/lib/help/auths/locale/SmfNWAMStates.html 444 root bin
 f none usr/lib/help/auths/locale/SmfPowerStates.html 444 root bin
 f none usr/lib/help/auths/locale/SmfRoutingStates.html 444 root bin
 f none usr/lib/help/auths/locale/SmfSendmailStates.html 444 root bin
 f none usr/lib/help/auths/locale/SmfSshStates.html 444 root bin
 f none usr/lib/help/auths/locale/SmfSyslogStates.html 444 root bin
 f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin
+f none usr/lib/help/auths/locale/SmfValueNWAM.html 444 root bin
 f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin
 f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin
 f none usr/lib/help/auths/locale/WifiConfig.html 444 root bin
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com	Fri Mar 30 17:01:13 2007 -0700
@@ -327,6 +327,8 @@
 s none etc/wtmpx=../var/adm/wtmpx
 d none etc/rpcsec 755 root sys
 d none lib 755 root bin
+d none lib/inet 755 root bin
+f none lib/inet/nwamd 555 root bin
 d none lib/svc 0755 root bin
 d none lib/svc/bin 0755 root bin
 f none lib/svc/bin/lsvcrun 0555 root sys
@@ -353,6 +355,7 @@
 f none lib/svc/method/mpxio-upgrade 0555 root bin
 f none lib/svc/method/net-init 0555 root bin
 f none lib/svc/method/net-loopback 0555 root bin
+f none lib/svc/method/net-nwam	0555 root bin
 f none lib/svc/method/net-physical 0555 root bin
 f none lib/svc/method/net-routing-setup 0555 root bin
 f none lib/svc/method/net-svc 0555 root bin
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com	Fri Mar 30 16:18:11 2007 -0700
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com	Fri Mar 30 17:01:13 2007 -0700
@@ -479,12 +479,14 @@
 f none usr/lib/help/auths/locale/C/SmfModifyHeader.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfModifyMethod.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfNscdStates.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfNWAMStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfPowerStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfRoutingStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfSendmailStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfSshStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfSyslogStates.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfValueNWAM.html 444 root bin
 f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin
 f none usr/lib/help/auths/locale/C/SysDate.html 444 root bin
 f none usr/lib/help/auths/locale/C/SysHeader.html 444 root bin