summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Carlson <james.d.carlson@sun.com>2008-09-19 16:27:53 -0400
committerJames Carlson <james.d.carlson@sun.com>2008-09-19 16:27:53 -0400
commitb00044a2eb43864b8718585d21949611a2ee59ef (patch)
treea53823fd16a577e94a5ce5b708d46d557ff943df
parent0ea48bbf7e086befa73c3f191a062c3a82cc5397 (diff)
downloadillumos-joyent-b00044a2eb43864b8718585d21949611a2ee59ef.tar.gz
PSARC 2008/482 NWAM Phase 0.5 (picea)
6648862 nwam is annoying when no network is available 6642066 NWAM Phase 0 needs better observability 6604418 weak interfaces turn nwam into a bouncing dog that misses its master 6745880 starting or restarting nwam always costs a link reinit
-rw-r--r--usr/src/Makefile.lint1
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/Makefile11
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/defines.h22
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/door.c918
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/events.c437
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/functions.h86
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/interface.c801
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/llp.c1001
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/main.c104
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c234
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/structures.h159
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/util.c189
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/variables.h7
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/wireless.c2301
-rw-r--r--usr/src/cmd/svc/milestone/network-physical.xml7
-rw-r--r--usr/src/cmd/svc/shell/net_include.sh6
-rw-r--r--usr/src/head/auth_list.h3
-rw-r--r--usr/src/lib/Makefile3
-rw-r--r--usr/src/lib/libbsm/audit_event.txt7
-rw-r--r--usr/src/lib/libbsm/common/adt.xml30
-rw-r--r--usr/src/lib/libnwam/Makefile52
-rw-r--r--usr/src/lib/libnwam/Makefile.com52
-rw-r--r--usr/src/lib/libnwam/common/door.c748
-rw-r--r--usr/src/lib/libnwam/common/libnwam.h171
-rw-r--r--usr/src/lib/libnwam/common/llib-lnwam29
-rw-r--r--usr/src/lib/libnwam/common/mapfile-vers47
-rw-r--r--usr/src/lib/libnwam/i386/Makefile29
-rw-r--r--usr/src/lib/libnwam/sparc/Makefile29
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt1
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile1
-rw-r--r--usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html38
-rw-r--r--usr/src/lib/libsecdb/help/profiles/Makefile3
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html37
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt6
-rw-r--r--usr/src/pkgdefs/Makefile2
-rw-r--r--usr/src/pkgdefs/SUNW0on/prototype_com3
-rw-r--r--usr/src/pkgdefs/SUNWcslr/prototype_com3
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com3
-rw-r--r--usr/src/pkgdefs/SUNWnwamintr/Makefile35
-rw-r--r--usr/src/pkgdefs/SUNWnwamintr/pkginfo.tmpl43
-rw-r--r--usr/src/pkgdefs/SUNWnwamintr/prototype_com47
-rw-r--r--usr/src/pkgdefs/SUNWnwamintr/prototype_i38645
-rw-r--r--usr/src/pkgdefs/SUNWnwamintr/prototype_sparc45
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/Makefile35
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl43
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/prototype_com46
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/prototype_i38645
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/prototype_sparc45
48 files changed, 5722 insertions, 2288 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index ec458d0c76..bd8cf275e2 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -361,6 +361,7 @@ COMMON_SUBDIRS = \
lib/libmtmalloc \
lib/libnsl \
lib/libnvpair \
+ lib/libnwam \
lib/libpam \
lib/libpctx \
lib/libpicl \
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/Makefile b/usr/src/cmd/cmd-inet/lib/nwamd/Makefile
index 818e63f807..de5fcbe951 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/Makefile
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/Makefile
@@ -20,9 +20,7 @@
#
#
-# ident "%Z%%M% %I% %E% SMI"
-#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# usr/src/cmd/cmd-inet/lib/nwamd/Makefile
@@ -32,7 +30,7 @@
include ../../../../lib/Makefile.lib
PROG= nwamd
-OBJS= events.o interface.o llp.o main.o \
+OBJS= door.o 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
@@ -44,7 +42,8 @@ POFILES= interface.po wireless.po
ROOTCMDDIR= $(ROOTFS_LIBDIR)/inet
-LDLIBS += -lsocket -lnsl -linetcfg -linetutil -lumem -lscf -ldladm -lgen
+LDLIBS += -lsocket -lnsl -linetcfg -linetutil -lumem -lscf -ldladm \
+ -lgen -ldoor -lsecdb -lbsm -lsysevent -lnvpair
.KEEP_STATE:
@@ -61,7 +60,7 @@ install: $(ROOTCMD)
check: $(SRCS) $(HEADERS)
$(CSTYLE) -cpP $(SRCS) $(HEADERS)
-$(ROOTCMD): all
+$(ROOTCMD): $(PROG)
clean:
$(RM) $(OBJS)
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/defines.h b/usr/src/cmd/cmd-inet/lib/nwamd/defines.h
index 5610a77fbf..c288af0184 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/defines.h
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/defines.h
@@ -20,37 +20,35 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 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 DEV_LOCAL_SVC_FMRI "svc:/system/device/local: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 LLPDIRNAME "/etc/nwam"
+#define LLPDIRMODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
+#define LLPFILE LLPDIRNAME "/llp"
+#define LLPFILETMP LLPDIRNAME "/llp.tmp"
+#define KNOWN_WIFI_NETS LLPDIRNAME "/known_wifi_nets"
+#define KNOWN_WIFI_TMP LLPDIRNAME "/known_wifi_nets.tmp"
+
+#define DOOR_FILENAME "/etc/svc/volatile/nwam_door"
+#define DOOR_FILEMODE S_IRUSR | S_IRGRP | S_IROTH
#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 NWAM_IF_WAIT_DELTA_MAX 300 /* 5 minutes poll rate max */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/door.c b/usr/src/cmd/cmd-inet/lib/nwamd/door.c
new file mode 100644
index 0000000000..c2481a93f9
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/door.c
@@ -0,0 +1,918 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains the routines that service the libdoor(3LIB) interface.
+ * This interface is intended for use by an external GUI utility to provide
+ * status information to users and allow control over nwam behavior in certain
+ * situations.
+ *
+ * The daemon has one active thread for each door call. Typically, a client
+ * will make a blocking call to await new events, and if an active client is
+ * busy, we will enqueue a small number of events here. If too many are
+ * enqueued, then we begin dropping events, and a single special "lost" event
+ * is placed in the queue. Clients are expected to start over at that point.
+ *
+ * For client events that require a response from the client, the server must
+ * assume a response if "lost" occurs or if there are no clients.
+ *
+ * When no clients are present, we just drop events. No history is maintained.
+ *
+ * Thread cancellation notes: In general, we disable cancellation for all
+ * calls, as allowing cancellation would require special handlers throughout
+ * the nwamd code to deal with the release of locks taken in various contexts.
+ * Instead, we allow it to run to completion on the assumption that all calls
+ * are expected to run without significant blocking.
+ *
+ * The one exception to this is the event-wait function, which intentionally
+ * blocks indefinitely. This request must enable cancellation so that an idle
+ * client can be terminated cleanly.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <door.h>
+#include <alloca.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <syslog.h>
+#include <string.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <auth_list.h>
+#include <secdb.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+/* Idle time before declaring a client to be dead. */
+uint_t door_idle_time = 10;
+
+static int door_fd = -1;
+
+static uid_t cur_user = (uid_t)-1;
+static adt_session_data_t *cur_ah;
+
+/*
+ * event_queue is a simple circular queue of fixed size. 'evput' is the next
+ * location to write, and 'evget' is the next waiting event. The queue size is
+ * chosen so that it's extremely unlikely that a functioning GUI could get this
+ * far behind on events and still be at all usable. (Too large, and we'd wait
+ * too long backing off to automatic mode on a broken GUI. Too small, and an
+ * interface up/down transient would cause us to switch to automatic mode too
+ * easily.)
+ */
+#define MAX_DESCR_EVENTS 64
+static nwam_descr_event_t event_queue[MAX_DESCR_EVENTS];
+static nwam_descr_event_t *evput = event_queue, *evget = event_queue;
+static struct wireless_lan *current_wlans;
+static size_t current_wlansize;
+
+/*
+ * This lock protects the event queue and the current_wlans list.
+ */
+static pthread_mutex_t event_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * sleep_cv is used to block waiting for new events to appear in an empty
+ * queue. client_cv is used to wait for event client threads to wake up and
+ * return before shutting down the daemon.
+ */
+static pthread_cond_t sleep_cv, client_cv;
+static uint_t sleeping_clients;
+static boolean_t active_clients;
+static uint32_t client_expire;
+
+/*
+ * Register a "user logout" event with the auditing system.
+ * A "logout" occurs when the GUI stops calling the event wait system (detected
+ * either by idle timer or queue overflow), or when a different authorized user
+ * calls the daemon (the previous one is logged out), or when the daemon itself
+ * is shut down.
+ */
+static void
+audit_detach(void)
+{
+ adt_event_data_t *event;
+
+ event = adt_alloc_event(cur_ah, ADT_nwam_detach);
+ if (event == NULL)
+ syslog(LOG_ERR, "audit failure: detach allocation: %m");
+ else if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0)
+ syslog(LOG_ERR, "audit failure: detach put: %m");
+ adt_free_event(event);
+ (void) adt_end_session(cur_ah);
+ cur_ah = NULL;
+ cur_user = (uid_t)-1;
+}
+
+/*
+ * Register either a normal "user login" event (if 'attached' is set) or a
+ * failed login (if 'attached' is not set) with auditing.
+ */
+static void
+audit_attach(ucred_t *ucr, boolean_t attached)
+{
+ adt_session_data_t *ah;
+ adt_event_data_t *event;
+ int retv, status, retval;
+
+ if (adt_start_session(&ah, NULL, 0) != 0) {
+ syslog(LOG_ERR, "audit failure: session start: %m");
+ return;
+ }
+
+ if (adt_set_from_ucred(ah, ucr, ADT_NEW) != 0) {
+ syslog(LOG_ERR, "audit failure: session credentials: %m");
+ goto failure;
+ }
+ if ((event = adt_alloc_event(ah, ADT_nwam_attach)) == NULL) {
+ syslog(LOG_ERR, "audit failure: audit allocation: %m");
+ goto failure;
+ }
+ event->adt_nwam_attach.auth_used = NET_AUTOCONF_AUTH;
+ if (attached) {
+ status = ADT_SUCCESS;
+ retval = ADT_SUCCESS;
+ } else {
+ status = ADT_FAILURE;
+ retval = ADT_FAIL_VALUE_AUTH;
+ }
+ retv = adt_put_event(event, status, retval);
+ adt_free_event(event);
+ if (retv != 0) {
+ syslog(LOG_ERR, "audit failure: attach put: %m");
+ goto failure;
+ }
+
+ /*
+ * Only successful attach records result in a detach. All else have
+ * (at most) a single failure record, and nothing else. Thus, we do
+ * not set cur_ah until we know we've written an attach record.
+ */
+ if (attached) {
+ cur_ah = ah;
+ return;
+ }
+
+failure:
+ (void) adt_end_session(ah);
+}
+
+/* Convert descriptive event to a text name for debug log */
+static const char *
+descr_event_name(libnwam_descr_evtype_t evt)
+{
+ /*
+ * Cast to int so that compiler and lint don't complain about extra
+ * 'default' case, and so that we can handle stray values.
+ */
+ switch ((int)evt) {
+ case deInitial:
+ return ("Initial");
+ case deInterfaceUp:
+ return ("InterfaceUp");
+ case deInterfaceDown:
+ return ("InterfaceDown");
+ case deInterfaceAdded:
+ return ("InterfaceAdded");
+ case deInterfaceRemoved:
+ return ("InterfaceRemoved");
+ case deWlanConnectFail:
+ return ("WlanConnectFail");
+ case deWlanDisconnect:
+ return ("WlanDisconnect");
+ case deWlanConnected:
+ return ("WlanConnected");
+ case deLLPSelected:
+ return ("LLPSelected");
+ case deLLPUnselected:
+ return ("LLPUnselected");
+ case deULPActivated:
+ return ("ULPActivated");
+ case deULPDeactivated:
+ return ("ULPDeactivated");
+ case deScanChange:
+ return ("ScanChange");
+ case deScanSame:
+ return ("ScanSame");
+ case deWlanKeyNeeded:
+ return ("WlanKeyNeeded");
+ case deWlanSelectionNeeded:
+ return ("WlanSelectionNeeded");
+ default:
+ return ("unknown");
+ }
+}
+
+/*
+ * This is called only by ndcWaitEvent, which holds event_lock until it has
+ * copied out the data from the entry.
+ */
+static const nwam_descr_event_t *
+get_descr_event(void)
+{
+ nwam_descr_event_t *nde;
+ static const nwam_descr_event_t init = { deInitial };
+
+ if (!active_clients) {
+ syslog(LOG_INFO, "new active door client detected");
+ active_clients = B_TRUE;
+ return (&init);
+ }
+ if ((nde = evget) == evput)
+ return (NULL);
+ if ((evget = nde + 1) >= event_queue + MAX_DESCR_EVENTS)
+ evget = event_queue;
+ /* If this event has a new WLAN snapshot, then update */
+ if (nde->nde_wlans != NULL) {
+ free(current_wlans);
+ current_wlans = nde->nde_wlans;
+ current_wlansize = nde->nde_wlansize;
+ nde->nde_wlans = NULL;
+ }
+ return (nde);
+}
+
+/*
+ * {start,put}_descr_event are called by the reporting functions. This
+ * function starts a new descriptive event and returns with the lock held (if
+ * the return value is non-NULL).
+ */
+static nwam_descr_event_t *
+start_descr_event(libnwam_descr_evtype_t evt)
+{
+ nwam_descr_event_t *nde, *ndenext;
+
+ if (!active_clients || pthread_mutex_lock(&event_lock) != 0) {
+ dprintf("dropping event %s; no active client",
+ descr_event_name(evt));
+ return (NULL);
+ }
+ nde = evput;
+ if ((ndenext = nde + 1) >= event_queue + MAX_DESCR_EVENTS)
+ ndenext = event_queue;
+ if (ndenext == evget) {
+ syslog(LOG_INFO, "descr event queue overflow");
+ active_clients = B_FALSE;
+ (void) np_queue_add_event(EV_RESELECT, NULL);
+ evput = evget = event_queue;
+ audit_detach();
+ (void) pthread_mutex_unlock(&event_lock);
+ return (NULL);
+ } else {
+ nde->nde_type = evt;
+ return (nde);
+ }
+}
+
+/* Finish reporting the event; must not be called if nde is NULL */
+static void
+put_descr_event(nwam_descr_event_t *nde, const char *ifname)
+{
+ if (ifname != NULL) {
+ dprintf("putting descr event %s %s",
+ descr_event_name(nde->nde_type), ifname);
+ (void) strlcpy(nde->nde_interface, ifname,
+ sizeof (nde->nde_interface));
+ if (++nde >= event_queue + MAX_DESCR_EVENTS)
+ nde = event_queue;
+ evput = nde;
+ (void) pthread_cond_signal(&sleep_cv);
+ }
+ /* Cannot drop the lock unless we've acquired it. */
+ assert(nde != NULL);
+ (void) pthread_mutex_unlock(&event_lock);
+}
+
+/*
+ * Finish reporting an event that sets the WLAN snapshot. If there's no
+ * client, then update the saved snapshot right now, as we won't be queuing the
+ * event.
+ */
+static boolean_t
+commit_wlans(nwam_descr_event_t *nde, const struct wireless_lan *wlans,
+ int wlan_cnt, const char *ifname)
+{
+ size_t wlansize;
+ struct wireless_lan *saved_wlans;
+
+ wlansize = sizeof (*saved_wlans) * wlan_cnt;
+ if ((saved_wlans = malloc(wlansize)) == NULL) {
+ if (nde != NULL)
+ put_descr_event(nde, NULL);
+ return (B_FALSE);
+ }
+ (void) memcpy(saved_wlans, wlans, wlansize);
+
+ if (nde != NULL) {
+ nde->nde_wlansize = wlansize;
+ nde->nde_wlans = saved_wlans;
+ put_descr_event(nde, ifname);
+ return (B_TRUE);
+ } else {
+ /* If the UI isn't running, then save the cached results */
+ if (pthread_mutex_lock(&event_lock) == 0) {
+ free(current_wlans);
+ current_wlans = saved_wlans;
+ current_wlansize = wlansize;
+ (void) pthread_mutex_unlock(&event_lock);
+ } else {
+ free(saved_wlans);
+ }
+ return (B_FALSE);
+ }
+}
+
+void
+report_interface_up(const char *ifname, struct in_addr addr, int prefixlen)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deInterfaceUp)) != NULL) {
+ nde->nde_v4address = addr;
+ nde->nde_prefixlen = prefixlen;
+ put_descr_event(nde, ifname);
+ }
+}
+
+void
+report_interface_down(const char *ifname, libnwam_diag_cause_t cause)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deInterfaceDown)) != NULL) {
+ nde->nde_cause = cause;
+ put_descr_event(nde, ifname);
+ }
+}
+
+void
+report_interface_added(const char *ifname)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deInterfaceAdded)) != NULL)
+ put_descr_event(nde, ifname);
+}
+
+void
+report_interface_removed(const char *ifname)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deInterfaceRemoved)) != NULL)
+ put_descr_event(nde, ifname);
+}
+
+void
+report_wlan_connect_fail(const char *ifname)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deWlanConnectFail)) != NULL)
+ put_descr_event(nde, ifname);
+}
+
+void
+report_wlan_disconnect(const struct wireless_lan *wlan)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deWlanDisconnect)) != NULL) {
+ nde->nde_attrs = wlan->attrs;
+ put_descr_event(nde, wlan->wl_if_name);
+ }
+}
+
+void
+report_wlan_connected(const struct wireless_lan *wlan)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deWlanConnected)) != NULL) {
+ nde->nde_attrs = wlan->attrs;
+ put_descr_event(nde, wlan->wl_if_name);
+ }
+}
+
+void
+report_llp_selected(const char *ifname)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deLLPSelected)) != NULL)
+ put_descr_event(nde, ifname);
+}
+
+void
+report_llp_unselected(const char *ifname, libnwam_diag_cause_t cause)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deLLPUnselected)) != NULL) {
+ nde->nde_cause = cause;
+ put_descr_event(nde, ifname);
+ }
+}
+
+void
+report_ulp_activated(const char *ulpname)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deULPActivated)) != NULL)
+ put_descr_event(nde, ulpname);
+}
+
+void
+report_ulp_deactivated(const char *ulpname)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deULPDeactivated)) != NULL)
+ put_descr_event(nde, ulpname);
+}
+
+void
+report_scan_complete(const char *ifname, boolean_t changed,
+ const struct wireless_lan *wlans, int wlan_cnt)
+{
+ nwam_descr_event_t *nde;
+
+ nde = start_descr_event(changed ? deScanChange : deScanSame);
+ (void) commit_wlans(nde, wlans, wlan_cnt, ifname);
+}
+
+boolean_t
+request_wlan_key(struct wireless_lan *wlan)
+{
+ nwam_descr_event_t *nde;
+
+ if ((nde = start_descr_event(deWlanKeyNeeded)) != NULL) {
+ nde->nde_attrs = wlan->attrs;
+ put_descr_event(nde, wlan->wl_if_name);
+ return (B_TRUE);
+ } else {
+ return (B_FALSE);
+ }
+}
+
+boolean_t
+request_wlan_selection(const char *ifname, const struct wireless_lan *wlans,
+ int wlan_cnt)
+{
+ nwam_descr_event_t *nde;
+
+ nde = start_descr_event(deWlanSelectionNeeded);
+ return (commit_wlans(nde, wlans, wlan_cnt, ifname));
+}
+
+/* ARGSUSED */
+static void
+thread_cancel_handler(void *arg)
+{
+ if (--sleeping_clients == 0) {
+ client_expire = NSEC_TO_SEC(gethrtime()) + door_idle_time;
+ (void) pthread_cond_signal(&client_cv);
+ /*
+ * On the wrong thread; must call start_timer from the main
+ * thread.
+ */
+ if (client_expire < timer_expire)
+ (void) np_queue_add_event(EV_DOOR_TIME, NULL);
+ }
+ (void) pthread_mutex_unlock(&event_lock);
+}
+
+/*
+ * A timer is set when there are waiting event collectors. If there haven't
+ * been any collectors for "a long time," then we assume that the user
+ * interface has been terminated or is jammed.
+ */
+void
+check_door_life(uint32_t now)
+{
+ if (active_clients && sleeping_clients == 0) {
+ if (client_expire > now) {
+ start_timer(now, client_expire - now);
+ } else {
+ syslog(LOG_INFO,
+ "no active door clients left; flushing queue");
+ if (pthread_mutex_lock(&event_lock) == 0) {
+ active_clients = B_FALSE;
+ if (evput != evget) {
+ (void) np_queue_add_event(EV_RESELECT,
+ NULL);
+ }
+ evput = evget = event_queue;
+ audit_detach();
+ (void) pthread_mutex_unlock(&event_lock);
+ }
+ }
+ }
+}
+
+/*
+ * This is called for an unrecognized UID. We check to see if the user is
+ * authorized to issue commands to the NWAM daemon.
+ */
+static boolean_t
+update_cur_user(ucred_t *ucr)
+{
+ struct passwd *pwd;
+ uid_t uid = ucred_getruid(ucr);
+ boolean_t attached = B_FALSE;
+
+ if ((pwd = getpwuid(uid)) == NULL) {
+ syslog(LOG_DEBUG, "unable to translate uid %d to a name", uid);
+ } else if (chkauthattr(NET_AUTOCONF_AUTH, pwd->pw_name) == 0) {
+ syslog(LOG_DEBUG, "user %s (%d) does not have %s", pwd->pw_name,
+ uid, NET_AUTOCONF_AUTH);
+ } else {
+ attached = B_TRUE;
+ }
+ if (pthread_mutex_lock(&event_lock) == 0) {
+ if (attached) {
+ audit_detach();
+ cur_user = uid;
+ }
+ audit_attach(ucr, attached);
+ (void) pthread_mutex_unlock(&event_lock);
+ } else {
+ attached = B_FALSE;
+ }
+ return (attached);
+}
+
+/* ARGSUSED */
+static void
+nwam_door_server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
+ uint_t ndesc)
+{
+ /* LINTED: alignment */
+ nwam_door_cmd_t *ndc = (nwam_door_cmd_t *)argp;
+ int retv = -1;
+ ucred_t *ucr = NULL;
+ libnwam_interface_type_t ift;
+
+ if (arg_size < sizeof (*ndc) || door_ucred(&ucr) != 0) {
+ retv = EINVAL;
+ (void) door_return((char *)&retv, sizeof (retv), NULL, 0);
+ return;
+ }
+
+ if (ucred_getruid(ucr) != cur_user && !update_cur_user(ucr)) {
+ ucred_free(ucr);
+ retv = EPERM;
+ (void) door_return((char *)&retv, sizeof (retv), NULL, 0);
+ return;
+ }
+ ucred_free(ucr);
+
+ /*
+ * Only the blocking event wait can be canceled, and then only when
+ * headed for a block.
+ */
+ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+
+ switch (ndc->ndc_type) {
+ case ndcNull:
+ dprintf("door: null event from client");
+ retv = 0;
+ break;
+
+ case ndcWaitEvent: {
+ const nwam_descr_event_t *nde;
+ nwam_descr_event_t ndcopy;
+
+ if ((retv = pthread_mutex_lock(&event_lock)) != 0)
+ break;
+ if ((nde = get_descr_event()) != NULL) {
+ ndcopy = *nde;
+ (void) pthread_mutex_unlock(&event_lock);
+ dprintf("door: returning waiting event %s",
+ descr_event_name(ndcopy.nde_type));
+ (void) door_return((char *)&ndcopy, sizeof (ndcopy),
+ NULL, 0);
+ return;
+ }
+
+ (void) pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_cleanup_push(thread_cancel_handler, NULL);
+ sleeping_clients++;
+ while ((nde = get_descr_event()) == NULL &&
+ door_fd != -1) {
+ if (pthread_cond_wait(&sleep_cv, &event_lock) != 0)
+ break;
+ }
+ if (nde != NULL)
+ ndcopy = *nde;
+ pthread_cleanup_pop(1);
+ if (nde == NULL) {
+ retv = EBADF;
+ break;
+ }
+ dprintf("door: returning waited-for event %s",
+ descr_event_name(ndcopy.nde_type));
+ (void) door_return((char *)&ndcopy, sizeof (ndcopy), NULL, 0);
+ return;
+ }
+
+ case ndcGetLLPList: {
+ nwam_llp_data_t *nld;
+ llp_t *llplist, *llpstack, *llp;
+ size_t llpsize;
+ uint_t count;
+ char selected[LIFNAMSIZ], locked[LIFNAMSIZ];
+
+ /*
+ * Note that door_return never returns here, so we can't just
+ * use malloc'd memory. Copy over to a stack-allocated buffer
+ * and do the string pointer fix-ups.
+ */
+ if ((retv = pthread_mutex_lock(&machine_lock)) != 0)
+ break;
+ errno = 0;
+ llplist = get_llp_list(&llpsize, &count, selected, locked);
+ (void) pthread_mutex_unlock(&machine_lock);
+ if (llplist != NULL) {
+ nld = alloca(sizeof (*nld) + llpsize);
+ nld->nld_count = count;
+ (void) strlcpy(nld->nld_selected, selected,
+ sizeof (nld->nld_selected));
+ (void) strlcpy(nld->nld_locked, locked,
+ sizeof (nld->nld_locked));
+ llpstack = (llp_t *)(nld + 1);
+ (void) memcpy(llpstack, llplist, llpsize);
+ llp = llpstack;
+ while (count-- > 0) {
+ if (llp->llp_ipv4addrstr != NULL)
+ llp->llp_ipv4addrstr -=
+ (uintptr_t)llplist;
+ if (llp->llp_ipv6addrstr != NULL)
+ llp->llp_ipv6addrstr -=
+ (uintptr_t)llplist;
+ llp++;
+ }
+ free(llplist);
+ llpsize += sizeof (*nld);
+ } else {
+ retv = errno;
+ dprintf("door: no LLP list to get");
+ break;
+ }
+ dprintf("door: get llp list returning %d entries",
+ nld->nld_count);
+ (void) door_return((char *)nld, llpsize, NULL, 0);
+ return;
+ }
+
+ case ndcSetLLPPriority:
+ if ((retv = pthread_mutex_lock(&machine_lock)) != 0)
+ break;
+ dprintf("door: set priority on %s to %d",
+ ndc->ndc_interface, ndc->ndc_priority);
+ retv = set_llp_priority(ndc->ndc_interface, ndc->ndc_priority);
+ (void) pthread_mutex_unlock(&machine_lock);
+ break;
+
+ case ndcLockLLP:
+ if ((retv = pthread_mutex_lock(&machine_lock)) != 0)
+ break;
+ if (ndc->ndc_interface[0] == '\0')
+ dprintf("door: unlocking llp selection");
+ else
+ dprintf("door: locking to %s", ndc->ndc_interface);
+ retv = set_locked_llp(ndc->ndc_interface);
+ (void) pthread_mutex_unlock(&machine_lock);
+ break;
+
+ case ndcGetWlanList: {
+ char *wlans;
+ size_t wlansize;
+
+ /*
+ * We protect ourselves here against a malicious or confused
+ * user. The list is stable only while we're holding the lock,
+ * and the lock can't be held during the door return.
+ */
+ if ((retv = pthread_mutex_lock(&event_lock)) != 0)
+ break;
+ if (current_wlans == NULL) {
+ (void) pthread_mutex_unlock(&event_lock);
+ dprintf("door: no WLAN list to get");
+ retv = ENXIO;
+ break;
+ }
+ wlans = alloca(wlansize = current_wlansize);
+ (void) memcpy(wlans, current_wlans, wlansize);
+ (void) pthread_mutex_unlock(&event_lock);
+ dprintf("door: get wlan list returning %lu bytes",
+ (ulong_t)wlansize);
+ (void) door_return(wlans, wlansize, NULL, 0);
+ return;
+ }
+
+ case ndcGetKnownAPList: {
+ nwam_known_ap_t *nka;
+ libnwam_known_ap_t *kalist, *kastack, *kap;
+ size_t kasize;
+ uint_t count;
+
+ /*
+ * Note that door_return never returns here, so we can't just
+ * use malloc'd memory. Copy over to a stack-allocated buffer
+ * and do the string pointer fix-ups.
+ */
+ errno = 0;
+ kalist = get_known_ap_list(&kasize, &count);
+ if (kalist != NULL) {
+ nka = alloca(sizeof (*nka) + kasize);
+ nka->nka_count = count;
+ kastack = (libnwam_known_ap_t *)(nka + 1);
+ (void) memcpy(kastack, kalist, kasize);
+ kap = kastack;
+ while (count-- > 0) {
+ kap->ka_bssid -= (uintptr_t)kalist;
+ kap->ka_essid -= (uintptr_t)kalist;
+ kap++;
+ }
+ free(kalist);
+ kasize += sizeof (*nka);
+ } else {
+ retv = errno;
+ dprintf("door: no known AP list to get");
+ break;
+ }
+ dprintf("door: get known AP list returning %u entries",
+ nka->nka_count);
+ (void) door_return((char *)nka, kasize, NULL, 0);
+ return;
+ }
+
+ case ndcAddKnownAP:
+ dprintf("door: adding known AP %s %s",
+ ndc->ndc_essid, ndc->ndc_bssid);
+ retv = add_known_ap(ndc->ndc_essid, ndc->ndc_bssid);
+ break;
+
+ case ndcDeleteKnownAP:
+ dprintf("door: removing known AP %s %s",
+ ndc->ndc_essid, ndc->ndc_bssid);
+ retv = delete_known_ap(ndc->ndc_essid, ndc->ndc_bssid);
+ break;
+
+ case ndcSelectWlan:
+ if ((retv = pthread_mutex_lock(&machine_lock)) != 0)
+ break;
+ dprintf("door: selecting WLAN on %s as %s %s",
+ ndc->ndc_interface, ndc->ndc_essid, ndc->ndc_bssid);
+ /*
+ * Check if we're already connected to the requested
+ * ESSID/BSSID. If so, then this request succeeds without
+ * changing anything. Otherwise, tear down the interface
+ * (disconnecting from the WLAN) and set up again.
+ */
+ if (check_wlan_connected(ndc->ndc_interface, ndc->ndc_essid,
+ ndc->ndc_bssid)) {
+ retv = 0;
+ } else {
+ takedowninterface(ndc->ndc_interface, dcSelect);
+ if (link_layer_profile != NULL)
+ link_layer_profile->llp_waiting = B_TRUE;
+ retv = set_specific_lan(ndc->ndc_interface,
+ ndc->ndc_essid, ndc->ndc_bssid);
+ }
+ (void) pthread_mutex_unlock(&machine_lock);
+ break;
+
+ case ndcWlanKey:
+ if ((retv = pthread_mutex_lock(&machine_lock)) != 0)
+ break;
+ dprintf("door: selecting WLAN key on %s for %s %s",
+ ndc->ndc_interface, ndc->ndc_essid, ndc->ndc_bssid);
+ retv = set_wlan_key(ndc->ndc_interface, ndc->ndc_essid,
+ ndc->ndc_bssid, ndc->ndc_key);
+ (void) pthread_mutex_unlock(&machine_lock);
+ break;
+
+ case ndcStartRescan:
+ dprintf("door: rescan requested on %s",
+ ndc->ndc_interface);
+ ift = get_if_type(ndc->ndc_interface);
+ if (ift != IF_UNKNOWN && ift != IF_WIRELESS) {
+ retv = EINVAL;
+ break;
+ }
+ if ((retv = pthread_mutex_lock(&machine_lock)) != 0)
+ break;
+ retv = launch_wireless_scan(ndc->ndc_interface);
+ (void) pthread_mutex_unlock(&machine_lock);
+ break;
+
+ default:
+ dprintf("door: unknown request type %d", (int)ndc->ndc_type);
+ break;
+ }
+ if (retv != 0)
+ dprintf("door: returning to caller with error %d (%s)",
+ retv, strerror(retv));
+ (void) door_return((char *)&retv, sizeof (retv), NULL, 0);
+}
+
+static void
+door_cleanup(void)
+{
+ if (door_fd != -1) {
+ syslog(LOG_DEBUG, "closing door");
+ (void) door_revoke(door_fd);
+ door_fd = -1;
+ }
+ (void) unlink(DOOR_FILENAME);
+}
+
+void
+terminate_door(void)
+{
+ door_cleanup();
+ if (pthread_mutex_lock(&event_lock) != 0)
+ return;
+ if (sleeping_clients != 0)
+ syslog(LOG_DEBUG, "waiting on %d sleeping clients",
+ sleeping_clients);
+ while (sleeping_clients != 0) {
+ (void) pthread_cond_broadcast(&sleep_cv);
+ if (pthread_cond_wait(&client_cv, &event_lock) != 0)
+ break;
+ }
+ free(current_wlans);
+ current_wlans = NULL;
+ audit_detach();
+ (void) pthread_mutex_unlock(&event_lock);
+}
+
+void
+initialize_door(void)
+{
+ int did;
+
+ /* Do a low-overhead "touch" on the file that will be the door node. */
+ syslog(LOG_DEBUG, "opening door");
+ did = open(DOOR_FILENAME,
+ O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK,
+ DOOR_FILEMODE);
+ if (did != -1) {
+ (void) close(did);
+ } else if (errno != EEXIST) {
+ syslog(LOG_ERR, "unable to create control door node: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ (void) atexit(door_cleanup);
+
+ /* Create the door. */
+ door_fd = door_create(nwam_door_server, NULL, DOOR_REFUSE_DESC);
+ if (door_fd == -1) {
+ syslog(LOG_ERR, "unable to create control door: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Attach the door to the file. */
+ (void) fdetach(DOOR_FILENAME);
+ if (fattach(door_fd, DOOR_FILENAME) == -1) {
+ syslog(LOG_ERR, "unable to attach control door: %m");
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/events.c b/usr/src/cmd/cmd-inet/lib/nwamd/events.c
index 1ae42e38e2..845e98fa97 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/events.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/events.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 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.
@@ -40,6 +38,11 @@
* 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.
*
+ * hotplug_handler() is called for EC_DEV_ADD and EC_DEV_REMOVE hotplug events
+ * of class ESC_NETWORK - i.e. hotplug insertion/removal of network card -
+ * and plumbs/unplumbs the interface, adding/removing it from running
+ * configuration (the interface and llp lists).
+ *
* routing_events() reads routing messages off of an IPv4 routing socket and
* by calling addevent_*() functions places appropriate events on the queue.
*
@@ -49,32 +52,35 @@
*/
#include <arpa/inet.h>
-#include <assert.h>
#include <errno.h>
#include <libsysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dev.h>
+#include <libnvpair.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;
+struct np_event *equeue;
+static struct np_event *equeue_end;
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
pthread_t routing, scan;
+static sysevent_handle_t *sysevent_handle;
+
+static void hotplug_handler(sysevent_t *ev);
static void printaddrs(int mask, void *address);
static char *printaddr(void **address);
static void *getaddr(int addrid, int mask, void *address);
@@ -104,25 +110,40 @@ union rtm_buf
};
void
-free_event(struct np_event *e)
+free_event(struct np_event *npe)
{
- free(e->npe_name);
- free(e);
+ free(npe);
}
-void
-np_queue_add_event(struct np_event *e)
+boolean_t
+np_queue_add_event(enum np_event_type evt, const char *ifname)
{
+ struct np_event *npe;
+ size_t slen;
+
+ slen = ifname == NULL ? 0 : (strlen(ifname) + 1);
+ if ((npe = calloc(1, sizeof (*npe) + slen)) == NULL) {
+ syslog(LOG_ERR, "event %s alloc for %s failed",
+ npe_type_str(evt), STRING(ifname));
+ return (B_FALSE);
+ }
+ if (ifname != NULL)
+ npe->npe_name = strcpy((char *)(npe + 1), ifname);
+ npe->npe_type = evt;
+
(void) pthread_mutex_lock(&queue_mutex);
+ dprintf("adding event type %s name %s to queue",
+ npe_type_str(evt), STRING(ifname));
if (equeue_end != NULL) {
- equeue_end->npe_next = e;
- equeue_end = e;
+ equeue_end->npe_next = npe;
+ equeue_end = npe;
} else {
- equeue = equeue_end = e;
+ equeue = equeue_end = npe;
}
equeue_end->npe_next = NULL;
(void) pthread_cond_signal(&queue_cond);
(void) pthread_mutex_unlock(&queue_mutex);
+ return (B_TRUE);
}
/*
@@ -154,99 +175,36 @@ 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;
-
+ case EV_LINKDROP:
+ return ("LINKDROP");
+ case EV_LINKUP:
+ return ("LINKUP");
+ case EV_LINKFADE:
+ return ("LINKFADE");
+ case EV_LINKDISC:
+ return ("LINKDISC");
+ case EV_NEWAP:
+ return ("NEWAP");
+ case EV_USER:
+ return ("USER");
+ case EV_TIMER:
+ return ("TIMER");
+ case EV_SHUTDOWN:
+ return ("SHUTDOWN");
+ case EV_NEWADDR:
+ return ("NEWADDR");
+ case EV_RESELECT:
+ return ("RESELECT");
+ case EV_DOOR_TIME:
+ return ("DOOR_TIME");
+ case EV_ADDIF:
+ return ("ADDIF");
+ case EV_REMIF:
+ return ("REMIF");
+ case EV_TAKEDOWN:
+ return ("TAKEDOWN");
default:
- dprintf("unhandled type in addevent_routing_msghdr %d",
- ifm->ifm_type);
- break;
+ return ("unknown");
}
}
@@ -273,6 +231,98 @@ rtmtype_str(int type)
}
}
+/*
+ * At present, we only handle EC_DEV_ADD/EC_DEV_REMOVE sysevents of
+ * subclass ESC_NETWORK. These signify hotplug addition/removal.
+ *
+ * The sysevents are converted into NWAM events so that we can process them in
+ * the main loop. If we didn't do this, we'd either have bad pointer
+ * references or need to have reference counts on everything. Serializing
+ * through the event mechanism is much simpler.
+ */
+static void
+hotplug_handler(sysevent_t *ev)
+{
+ int32_t instance;
+ char *driver;
+ char ifname[LIFNAMSIZ];
+ nvlist_t *attr_list;
+ char *event_class = sysevent_get_class_name(ev);
+ char *event_subclass = sysevent_get_subclass_name(ev);
+ int retv;
+
+ dprintf("hotplug_handler: event %s/%s", event_class,
+ event_subclass);
+
+ /* Make sure sysevent is of expected class/subclass */
+ if ((strcmp(event_class, EC_DEV_ADD) != 0 &&
+ strcmp(event_class, EC_DEV_REMOVE) != 0) ||
+ strcmp(event_subclass, ESC_NETWORK) != 0) {
+ syslog(LOG_ERR, "hotplug_handler: unexpected sysevent "
+ "class/subclass %s/%s", event_class, event_subclass);
+ return;
+ }
+
+ /*
+ * Retrieve driver name and instance attributes, and combine to
+ * get interface name.
+ */
+ if (sysevent_get_attr_list(ev, &attr_list) != 0) {
+ syslog(LOG_ERR, "hotplug_handler: sysevent_get_attr_list: %m");
+ return;
+ }
+ retv = nvlist_lookup_string(attr_list, DEV_DRIVER_NAME, &driver);
+ if (retv == 0)
+ retv = nvlist_lookup_int32(attr_list, DEV_INSTANCE, &instance);
+ if (retv != 0) {
+ syslog(LOG_ERR, "handle_hotplug_interface: nvlist_lookup "
+ "of attributes failed: %s", strerror(retv));
+ } else {
+ (void) snprintf(ifname, LIFNAMSIZ, "%s%d", driver, instance);
+ (void) np_queue_add_event(strcmp(event_class, EC_DEV_ADD) == 0 ?
+ EV_ADDIF : EV_REMIF, ifname);
+ }
+ nvlist_free(attr_list);
+}
+
+static void
+hotplug_events_unregister(void)
+{
+ /* Unsubscribe to sysevents */
+ sysevent_unbind_handle(sysevent_handle);
+ sysevent_handle = NULL;
+}
+
+static void
+hotplug_events_register(void)
+{
+ const char *subclass = ESC_NETWORK;
+
+ sysevent_handle = sysevent_bind_handle(hotplug_handler);
+ if (sysevent_handle == NULL) {
+ syslog(LOG_ERR, "sysevent_bind_handle: %s", strerror(errno));
+ return;
+ }
+ /*
+ * Subscribe to ESC_NETWORK subclass of EC_DEV_ADD and EC_DEV_REMOVE
+ * events. As a result, we get sysevent notification of hotplug
+ * add/remove events, which we handle above in hotplug_event_handler().
+ */
+ if (sysevent_subscribe_event(sysevent_handle, EC_DEV_ADD, &subclass, 1)
+ != 0 || sysevent_subscribe_event(sysevent_handle, EC_DEV_REMOVE,
+ &subclass, 1) != 0) {
+ syslog(LOG_ERR, "sysevent_subscribe_event: %s",
+ strerror(errno));
+ hotplug_events_unregister();
+ }
+}
+
+/*
+ * This thread reads routing socket events and sends them to the main state
+ * machine. We must be careful with access to interface data structures here,
+ * as we're not the main thread, which may delete things. Holding a pointer is
+ * not allowed.
+ */
/* ARGSUSED */
static void *
routing_events(void *arg)
@@ -299,10 +349,9 @@ routing_events(void *arg)
dprintf("routing socket %d", rtsock);
for (;;) {
- struct interface *ifp;
- char *addrs, *if_name;
+ char *addrs;
struct sockaddr_dl *addr_dl;
- struct sockaddr *addr;
+ struct sockaddr_in *addr_in;
rtm = &buffer.r.rtm;
n = read(rtsock, &buffer, sizeof (buffer));
@@ -336,6 +385,56 @@ routing_events(void *arg)
}
switch (rtm->rtm_type) {
+ case RTM_DELADDR: {
+ uint64_t ifflags;
+
+ /*
+ * Check for failure due to CR 6745448: if we get a
+ * report that an address has been deleted, then check
+ * for interface up, datalink down, and actual address
+ * non-zero. If that combination is seen, then this is
+ * a DHCP cached lease, and we need to remove it from
+ * the system, or it'll louse up the kernel routes
+ * (which aren't smart enough to avoid dead
+ * interfaces).
+ */
+ ifa = (void *)rtm;
+ addrs = (char *)ifa + sizeof (*ifa);
+
+ dprintf("routing message DELADDR: index %d flags %x",
+ ifa->ifam_index, ifa->ifam_flags);
+ printaddrs(ifa->ifam_addrs, addrs);
+
+ if (ifa->ifam_index == 0) {
+ /* what is this? */
+ dprintf("tossing index 0 routing event");
+ break;
+ }
+
+ addr_in = getaddr(RTA_IFA, ifa->ifam_addrs, addrs);
+ if (addr_in == NULL) {
+ dprintf("no RTA_IFA in RTM_DELADDR message");
+ break;
+ }
+
+ addr_dl = getaddr(RTA_IFP, ifa->ifam_addrs, addrs);
+ if (addr_dl == NULL) {
+ dprintf("no RTA_IFP in RTM_DELADDR message");
+ break;
+ }
+
+ addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
+
+ if (addr_in->sin_addr.s_addr == INADDR_ANY) {
+ ifflags = get_ifflags(addr_dl->sdl_data,
+ AF_INET);
+ if ((ifflags & IFF_UP) &&
+ !(ifflags & IFF_RUNNING))
+ zero_out_v4addr(addr_dl->sdl_data);
+ }
+ break;
+ }
+
case RTM_NEWADDR:
ifa = (void *)rtm;
addrs = (char *)ifa + sizeof (*ifa);
@@ -344,106 +443,62 @@ routing_events(void *arg)
ifa->ifam_index, ifa->ifam_flags);
printaddrs(ifa->ifam_addrs, addrs);
- if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
- ifa->ifam_addrs, addrs)) == NULL)
+ if (ifa->ifam_index == 0) {
+ /* what is this? */
+ dprintf("tossing index 0 routing event");
break;
+ }
+
+ addr_in = getaddr(RTA_IFA, ifa->ifam_addrs, addrs);
+ if (addr_in == NULL) {
+ dprintf("no RTA_IFA in RTM_NEWADDR message");
+ break;
+ }
- if ((addr_dl = (struct sockaddr_dl *)getaddr
- (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
+ addr_dl = getaddr(RTA_IFP, ifa->ifam_addrs, addrs);
+ if (addr_dl == NULL) {
+ dprintf("no RTA_IFP in RTM_NEWADDR message");
break;
+ }
+
/*
* We don't use the lladdr in this structure so we can
* run over it.
*/
addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
- 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);
- }
+ update_interface_v4_address(addr_dl->sdl_data,
+ addr_in->sin_addr.s_addr);
break;
- case RTM_IFINFO:
- {
- boolean_t plugged_in;
+ case RTM_IFINFO:
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)
+ if (ifm->ifm_index == 0) {
+ dprintf("tossing index 0 routing event");
+ break;
+ }
+
+ addr_dl = getaddr(RTA_IFP, ifm->ifm_addrs, addrs);
+ if (addr_dl == NULL) {
+ dprintf("no RTA_IFP in RTM_IFINFO message");
break;
+ }
+
/*
* We don't use the lladdr in this structure so we can
* run over it.
*/
addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
- 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
- * DHCP 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_DHCPFLAGS;
- }
- 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);
- }
+ update_interface_flags(addr_dl->sdl_data,
+ ifm->ifm_flags);
break;
- }
+
default:
dprintf("routing message %s socket %d discarded",
rtmtype_str(rtm->rtm_type), rtsock);
@@ -564,7 +619,6 @@ boolean_t
start_event_collection(void)
{
int err;
- boolean_t check_cache = B_TRUE;
/*
* if these are ever created/destroyed repetitively then we will
@@ -586,7 +640,14 @@ start_event_collection(void)
dprintf("scan thread: %d", scan);
}
- walk_interface(start_if_info_collect, &check_cache);
+ /*
+ * This function registers a callback which will get a dedicated thread
+ * for handling of hotplug sysevents when they occur.
+ */
+ hotplug_events_register();
+
+ dprintf("initial interface scan");
+ walk_interface(start_if_info_collect, "check");
return (B_TRUE);
}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/functions.h b/usr/src/cmd/cmd-inet/lib/nwamd/functions.h
index bb88f4d2d9..2c429fcefb 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/functions.h
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/functions.h
@@ -27,58 +27,99 @@
#ifndef _FUNCTIONS_H
#define _FUNCTIONS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
+/* door.c: door-based control/status interface */
+extern void initialize_door(void);
+extern void terminate_door(void);
+extern void report_interface_up(const char *, struct in_addr, int);
+extern void report_interface_down(const char *, libnwam_diag_cause_t);
+extern void report_interface_added(const char *);
+extern void report_interface_removed(const char *);
+extern void report_wlan_connect_fail(const char *);
+extern void report_wlan_disconnect(const struct wireless_lan *);
+extern void report_wlan_connected(const struct wireless_lan *);
+extern void report_llp_selected(const char *);
+extern void report_llp_unselected(const char *, libnwam_diag_cause_t);
+extern void report_ulp_activated(const char *);
+extern void report_ulp_deactivated(const char *);
+extern void report_scan_complete(const char *, boolean_t,
+ const struct wireless_lan *, int);
+extern boolean_t request_wlan_key(struct wireless_lan *);
+extern boolean_t request_wlan_selection(const char *,
+ const struct wireless_lan *, int);
+extern void check_door_life(uint32_t);
/* events.c: event queue handling */
extern void free_event(struct np_event *);
-extern void np_queue_add_event(struct np_event *);
+extern boolean_t np_queue_add_event(enum np_event_type, const char *);
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 void remove_interface(const char *);
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 libnwam_interface_type_t find_if_type(const char *);
+extern const char *if_type_str(libnwam_interface_type_t);
+extern void update_interface_v4_address(const char *, in_addr_t);
+extern void update_interface_flags(const char *, int);
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 *,
+extern return_vals_t bringupinterface(const char *, const char *, const char *,
boolean_t);
-extern void takedowninterface(const char *, boolean_t, boolean_t);
-extern void take_down_all_ifs(const char *);
-extern void check_interface_timer(struct interface *, void *);
+extern void takedowninterface(const char *, libnwam_diag_cause_t);
+extern void clear_cached_address(const char *);
+extern void check_interface_timers(uint32_t);
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 *);
+extern boolean_t is_interface_ok(const char *);
+extern libnwam_interface_type_t get_if_type(const char *);
+extern void get_interface_state(const char *, boolean_t *, boolean_t *);
+extern void print_interface_status(void);
/* wireless.c: wifi link handling */
-extern void init_mutexes(void);
-extern boolean_t connect_chosen_lan(struct wireless_lan *, struct interface *);
+extern void initialize_wireless(void);
+extern void add_wireless_if(const char *);
+extern void remove_wireless_if(const char *);
extern struct wireless_lan *prompt_for_visited(void);
-extern boolean_t handle_wireless_lan(struct interface *);
-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 return_vals_t handle_wireless_lan(const char *);
+extern libnwam_known_ap_t *get_known_ap_list(size_t *, uint_t *);
+extern int add_known_ap(const char *, const char *);
+extern int delete_known_ap(const char *, const char *);
extern void *periodic_wireless_scan(void *);
+extern boolean_t check_wlan_connected(const char *, const char *, const char *);
+extern int set_specific_lan(const char *, const char *, const char *);
+extern int set_wlan_key(const char *, const char *, const char *, const char *);
+extern int launch_wireless_scan(const char *);
+extern void disconnect_wlan(const char *);
+extern void get_wireless_state(const char *, boolean_t *, boolean_t *);
+extern void print_wireless_status(void);
/* llp.c: link layer profile handling */
+extern void initialize_llp(void);
extern void llp_parse_config(void);
+extern void llp_add_file(const llp_t *);
+extern llp_t *llp_add(const char *);
+extern void llp_delete(llp_t *);
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 void llp_swap(llp_t *, libnwam_diag_cause_t);
extern char *llp_prnm(llp_t *);
+extern void llp_write_changed_priority(llp_t *);
+extern int set_llp_priority(const char *, int);
+extern int set_locked_llp(const char *);
+extern llp_t *get_llp_list(size_t *, uint_t *, char *, char *);
+extern void llp_reselect(void);
+extern void llp_get_name_and_type(char *, size_t, libnwam_interface_type_t *);
+extern libnwam_ipv4src_t llp_get_ipv4src(const char *);
+extern void print_llp_status(void);
/* state_machine.c: state machine handling */
extern void state_machine(struct np_event *);
@@ -87,13 +128,10 @@ extern void cleanup(void);
/* util.c: utility & ipc functions */
extern void dprintf(const char *, ...);
extern uint64_t get_ifflags(const char *, sa_family_t);
-extern boolean_t is_plugged_in(struct interface *);
+extern void zero_out_v4addr(const char *);
extern int start_childv(const char *, char const * const *);
extern int start_child(const char *, ...);
extern void start_timer(uint32_t, uint32_t);
-extern boolean_t valid_graphical_user(boolean_t);
extern void lookup_zonename(char *, size_t);
-extern struct sockaddr *dupsockaddr(const struct sockaddr *);
-extern boolean_t cmpsockaddr(const struct sockaddr *, const struct sockaddr *);
#endif /* _FUNCTIONS_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/interface.c b/usr/src/cmd/cmd-inet/lib/nwamd/interface.c
index a455d91435..397990d935 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/interface.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/interface.c
@@ -24,16 +24,12 @@
* 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.
+ * and various support functions. It also contains 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').
@@ -72,6 +68,20 @@
* 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.
+ *
+ * The ifs_head and associated list pointers are protected by ifs_lock. Only
+ * the main thread may modify the list (single writer), and it does so with the
+ * lock held. As a consequence, the main thread alone may read the list (and
+ * examine pointers) without holding any locks. All other threads must hold
+ * ifs_lock for the duration of any examination of the data structures, and
+ * must not deal directly in interface pointers. (A thread may also hold
+ * machine_lock to block the main thread entirely in order to manipulate the
+ * data; such use is isolated to the door interface.)
+ *
+ * Functions in this file have comments noting where the main thread alone is
+ * the caller. These functions do not need to acquire the lock.
+ *
+ * If you hold both ifs_lock and llp_lock, you must take ifs_lock first.
*/
#include <errno.h>
@@ -80,22 +90,17 @@
#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 <sys/wait.h>
#include <libdllink.h>
#include "defines.h"
@@ -103,35 +108,19 @@
#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 pthread_mutex_t ifs_lock = PTHREAD_MUTEX_INITIALIZER;
-static char upper_layer_profile[MAXHOSTNAMELEN];
+static struct interface *ifs_head;
+static struct interface *ifs_wired, *ifs_wired_last;
+static struct interface *ifs_wireless, *ifs_wireless_last;
-static void print_interface_list();
-static struct interface *get_next_interface(struct interface *);
+static char upper_layer_profile[MAXHOSTNAMELEN];
#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;
@@ -152,15 +141,13 @@ show_if_status(const char *ifname)
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);
+ report_interface_up(ifname, sin.sin_addr, prefixlen);
}
/*
* If this interface matches the currently active llp, return B_TRUE.
* Otherwise, return B_FALSE.
+ * Called only from main thread.
*/
boolean_t
interface_is_active(const struct interface *ifp)
@@ -181,13 +168,25 @@ start_dhcp(struct interface *ifp)
uint32_t now_s;
uint64_t timer_s;
- if ((ifp->if_lflags & IF_DHCPSTARTED) != 0) {
+ if (ifp->if_lflags & IF_DHCPSTARTED) {
dprintf("start_dhcp: already started; returning");
return;
}
ifp->if_lflags |= IF_DHCPSTARTED;
- (void) start_child(IFCONFIG, ifp->if_name, "dhcp", "wait", "0", NULL);
+ /*
+ * If we need to use DHCP and DHCP is already controlling the
+ * interface, we don't need to do anything. Otherwise, start it now.
+ */
+ if (!(ifp->if_flags & IFF_DHCPRUNNING)) {
+ dprintf("launching DHCP on %s", ifp->if_name);
+ (void) start_child(IFCONFIG, ifp->if_name, "dhcp", "wait", "0",
+ NULL);
+ } else {
+ dprintf("DHCP already running on %s; resetting timer",
+ ifp->if_name);
+ }
+ ifp->if_lflags &= ~IF_DHCPFAILED;
/* start dhcp timer */
res = lookup_count_property(OUR_PG, "dhcp_wait_time", &timer_s);
@@ -429,7 +428,7 @@ void
activate_upper_layer_profile(boolean_t do_dhcp, const char *ifname)
{
FILE *f;
- char buffer[1024];
+ char buffer[1024], *cp;
size_t buflen;
size_t offset;
const char bringup[] = "/bringup";
@@ -452,33 +451,65 @@ activate_upper_layer_profile(boolean_t do_dhcp, const char *ifname)
}
}
f = popen(ULP_DIR "/check-conditions", "r");
- if (f == NULL)
+ if (f == NULL) {
+ /* note that this doesn't happen if the file is missing */
+ syslog(LOG_ERR, "popen: check-conditions: %m");
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,
+ offset = strlcpy(buffer, ULP_DIR "/", sizeof (buffer));
+ cp = 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));
+ f);
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);
+ /* Need to check for script error before interpreting result */
+ res = pclose(f);
+ if (res == -1) {
+ syslog(LOG_ERR, "check-conditions: pclose: %m");
+ return;
+ }
+ if (WIFEXITED(res)) {
+ if (WEXITSTATUS(res) == 0) {
+ if (cp == NULL || *cp == '\0') {
+ syslog(LOG_DEBUG,
+ "check-conditions returned no information");
+ } else {
+ (void) strlcpy(upper_layer_profile,
+ buffer + offset,
+ sizeof (upper_layer_profile));
+ (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);
+ report_ulp_activated(upper_layer_profile);
+ }
+ } else if (access(ULP_DIR "/check-conditions", X_OK) == 0) {
+ syslog(LOG_ERR,
+ "check-conditions exited with status %d",
+ WEXITSTATUS(res));
+ } else if (errno == ENOENT) {
+ syslog(LOG_DEBUG, "check-conditions not present");
+ } else {
+ syslog(LOG_ERR, "check-conditions: %m");
+ }
+ } else if (WIFSIGNALED(res)) {
+ syslog(LOG_ERR, "check-conditions exit on SIG%s",
+ strsignal(WTERMSIG(res)));
+ } else {
+ syslog(LOG_ERR,
+ "check-conditions terminated in unknown manner");
+ }
}
void
@@ -499,33 +530,54 @@ deactivate_upper_layer_profile(void)
syslog(LOG_NOTICE, "upper layer profile %s deactivated",
upper_layer_profile);
+ report_ulp_deactivated(upper_layer_profile);
+
upper_layer_profile[0] = '\0';
}
/*
- * Returns B_TRUE if the interface is successfully brought up;
- * B_FALSE if bringup fails.
+ * Returns SUCCESS if the interface is successfully brought up,
+ * FAILURE if bringup fails, or WAITING if we'll need to wait on the GUI to run.
+ * Called only in the main thread or a thread holding machine_lock.
*/
-boolean_t
+return_vals_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);
+ return (FAILURE);
}
/* check current state; no point going on if flags are 0 */
- if ((ifflags = get_ifflags(ifname, intf->if_family)) == 0) {
+ if ((intf->if_flags = get_ifflags(ifname, intf->if_family)) == 0) {
dprintf("bringupinterface(%s): get_ifflags() returned 0",
ifname);
- return (B_FALSE);
+ return (FAILURE);
+ }
+
+ if (intf->if_type == IF_WIRELESS) {
+ switch (handle_wireless_lan(ifname)) {
+ case WAITING:
+ intf->if_up_attempted = B_TRUE;
+ return (WAITING);
+ case FAILURE:
+ syslog(LOG_INFO, "Could not connect to any WLAN, not "
+ "bringing %s up", ifname);
+ return (FAILURE);
+ }
+ }
+ intf->if_up_attempted = B_TRUE;
+
+ /* physical level must now be up; bail out if not */
+ intf->if_flags = get_ifflags(ifname, intf->if_family);
+ if (!(intf->if_flags & IFF_RUNNING)) {
+ dprintf("bringupinterface(%s): physical layer down", ifname);
+ return (FAILURE);
}
/*
@@ -541,63 +593,36 @@ bringupinterface(const char *ifname, const char *host, const char *ipv6addr,
ipv6addr, "up", NULL);
}
}
+ intf->if_v6onlink = ipv6onlink;
- 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(intf)) {
- syslog(LOG_INFO, "Could not connect to any WLAN, not "
- "bringing %s up", ifname);
- return (B_FALSE);
- }
- }
-
- if (do_dhcp) {
+ if (strcmp(host, "dhcp") == 0) {
start_dhcp(intf);
} else {
(void) start_child(IFCONFIG, ifname, host, NULL);
(void) start_child(IFCONFIG, ifname, "up", NULL);
}
- return (B_TRUE);
+ syslog(LOG_DEBUG, "brought up %s", ifname);
+
+ return (SUCCESS);
}
+/* Called only in the main thread */
void
-takedowninterface(const char *ifname, boolean_t popup, boolean_t v6onlink)
+takedowninterface(const char *ifname, libnwam_diag_cause_t cause)
{
uint64_t flags;
struct interface *ifp;
- dprintf("takedowninterface(%s, %s, %s)", ifname,
- BOOLEAN_TO_STRING(popup), BOOLEAN_TO_STRING(v6onlink));
+ dprintf("takedowninterface(%s, %d)", ifname, (int)cause);
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 ((flags & IFF_DHCPRUNNING) != 0) {
+ if (flags & IFF_DHCPRUNNING) {
/*
* We generally prefer doing a release, as that tells the
* server that it can relinquish the lease, whereas drop is
@@ -612,14 +637,14 @@ takedowninterface(const char *ifname, boolean_t popup, boolean_t v6onlink)
NULL);
}
} else {
- if ((flags & IFF_UP) != 0)
+ if (flags & IFF_UP)
(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) {
+ if (ifp == NULL || ifp->if_v6onlink) {
/*
* Unplumbing the link local interface causes dhcp and ndpd to
* remove other addresses they have added.
@@ -627,53 +652,37 @@ takedowninterface(const char *ifname, boolean_t popup, boolean_t v6onlink)
(void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL);
}
- if (ifp->if_type == IF_WIRELESS)
- (void) dladm_wlan_disconnect(ifp->if_linkid);
+ if (ifp == NULL || ifp->if_up_attempted)
+ report_interface_down(ifname, cause);
- dprintf("takedown interface, free cached ip address");
if (ifp != NULL) {
- 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);
+ if (ifp->if_type == IF_WIRELESS)
+ disconnect_wlan(ifp->if_name);
+ dprintf("takedown interface, zero cached ip address");
+ ifp->if_flags = flags;
+ ifp->if_lflags &= ~IF_DHCPSTARTED & ~IF_DHCPACQUIRED;
+ ifp->if_ipv4addr = INADDR_ANY;
+ ifp->if_up_attempted = B_FALSE;
}
}
-/*
- * 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.
- */
+/* Called only in the main thread */
void
-take_down_all_ifs(const char *ignore_if)
+clear_cached_address(const char *ifname)
{
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, B_FALSE,
- ifp->if_family == AF_INET6);
- }
- }
-}
+ uint64_t ifflags;
-static struct interface *
-get_next_interface(struct interface *ifp)
-{
- return (ifp == NULL ? ifs_head : ifp->if_next);
+ if ((ifp = get_interface(ifname)) == NULL) {
+ dprintf("clear_cached_address: can't find interface struct "
+ "for %s", ifname);
+ return;
+ }
+ ifflags = get_ifflags(ifname, AF_INET);
+ if ((ifflags & IFF_UP) && !(ifflags & IFF_RUNNING))
+ zero_out_v4addr(ifname);
+ ifp->if_ipv4addr = INADDR_ANY;
+ ifp->if_lflags &= ~IF_DHCPFLAGS;
}
/*
@@ -685,70 +694,73 @@ get_next_interface(struct interface *ifp)
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;
+ struct interface **headpp, **lastpp;
+ struct interface *pchain, *nextp;
+
+ if (pthread_mutex_lock(&ifs_lock) != 0)
+ return;
switch (ifp->if_type) {
case IF_WIRELESS:
- first_wireless = (ifs_wireless == NULL);
- wpp = &ifs_wireless;
- endp = NULL;
+ /*
+ * Wireless entries are in the wireless list, and are chained
+ * after the wired entries. If there are no wired entries, then
+ * chain on main list.
+ */
+ headpp = &ifs_wireless;
+ lastpp = &ifs_wireless_last;
+ pchain = ifs_wired_last;
+ nextp = NULL;
break;
case IF_WIRED:
- first_wired = (ifs_wired == NULL);
- wpp = &ifs_wired;
- endp = ifs_wireless;
+ /*
+ * Wired entries are on the wired list, and are chained before
+ * the wireless entries.
+ */
+ headpp = &ifs_wired;
+ lastpp = &ifs_wired_last;
+ pchain = NULL;
+ nextp = ifs_wireless;
break;
default:
/* don't add to the list */
+ (void) pthread_mutex_unlock(&ifs_lock);
return;
}
- /* set list head if this is the first entry */
- if (ifs_head == NULL) {
- ifs_head = *wpp = ifp;
- ifp->if_next = NULL;
- return;
+ /* Connect into the correct list */
+ if (*lastpp == NULL) {
+ /*
+ * If there's a previous list, then wire to the end of
+ * that, as we're the new head here.
+ */
+ if (pchain != NULL)
+ pchain->if_next = ifp;
+ *headpp = ifp;
+ } else {
+ (*lastpp)->if_next = ifp;
}
+ *lastpp = ifp;
- 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;
- }
+ ifp->if_next = nextp;
+
+ /* Fix up the main list; it's always wired-first */
+ ifs_head = ifs_wired == NULL ? ifs_wireless : ifs_wired;
+
+ (void) pthread_mutex_unlock(&ifs_lock);
}
/*
* 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.
+ * errno upon error.
*/
struct interface *
add_interface(sa_family_t family, const char *name, uint64_t flags)
{
struct interface *i;
- datalink_id_t linkid = DATALINK_INVALID_LINKID;
- enum interface_type iftype;
+ libnwam_interface_type_t iftype;
if (name == NULL)
return (NULL);
@@ -780,77 +792,109 @@ add_interface(sa_family_t family, const char *name, uint64_t flags)
return (NULL);
}
- if ((i = malloc(sizeof (*i))) == NULL) {
+ if ((i = calloc(1, 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;
+ (void) strlcpy(i->if_name, name, sizeof (i->if_name));
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;
-
- /*
- * If linkid is DATALINK_INVALID_LINKID, it is an IP-layer only
- * interface.
- */
- (void) dladm_name2info(name, &linkid, NULL, NULL, NULL);
- i->if_linkid = linkid;
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 ");
+ (i->if_flags & IFF_RUNNING) ? "" : "not ");
interface_list_insert(i);
+ if (iftype == IF_WIRELESS)
+ add_wireless_if(name);
+
return (i);
}
/*
+ * This is called only by the main thread.
+ */
+void
+remove_interface(const char *ifname)
+{
+ struct interface *ifp, *prevp = NULL;
+
+ if (pthread_mutex_lock(&ifs_lock) != 0)
+ return;
+ for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) {
+ if (strcmp(ifname, ifp->if_name) == 0) {
+ if (prevp == NULL)
+ ifs_head = ifp->if_next;
+ else
+ prevp->if_next = ifp->if_next;
+ if (ifp == ifs_wired_last) {
+ if ((ifs_wired_last = prevp) == NULL)
+ ifs_wired = NULL;
+ } else if (ifp == ifs_wired) {
+ ifs_wired = ifp->if_next;
+ }
+ if (ifp == ifs_wireless_last) {
+ if (prevp != NULL &&
+ prevp->if_type != IF_WIRELESS)
+ prevp = NULL;
+ if ((ifs_wireless_last = prevp) == NULL)
+ ifs_wireless = NULL;
+ } else if (ifp == ifs_wireless) {
+ ifs_wireless = ifp->if_next;
+ }
+ break;
+ }
+ prevp = ifp;
+ }
+ (void) pthread_mutex_unlock(&ifs_lock);
+
+ remove_wireless_if(ifname);
+
+ if (ifp != NULL && ifp->if_thr != 0) {
+ (void) pthread_cancel(ifp->if_thr);
+ (void) pthread_join(ifp->if_thr, NULL);
+ }
+ free(ifp);
+}
+
+/*
* Searches for an interface and returns the interface structure if found.
- * Returns NULL otherwise. errno is set upon error exit.
+ * Returns NULL otherwise. The caller must either be holding ifs_lock, or be
+ * in the main thread.
*/
struct interface *
get_interface(const char *name)
{
- struct interface *i;
+ struct interface *ifp;
if (name == NULL)
return (NULL);
- for (i = ifs_head; i != NULL; i = i->if_next) {
- if (strcmp(name, i->if_name) == 0) {
- return (i);
- }
+ for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) {
+ if (strcmp(name, ifp->if_name) == 0)
+ break;
}
-
- return (NULL);
+ return (ifp);
}
/*
- * Checks interface flags and, if IFF_DHCPRUNNING and !IFF_UP, does
- * an 'ifconfig ifname dhcp drop'.
+ * Check to see whether the interface could be started. If the IFF_RUNNING
+ * flag is set, then we're in good shape. Otherwise, wireless interfaces are
+ * special: we'll attempt to connect to an Access Point as part of the start-up
+ * procedure, and IFF_RUNNING won't be present until that's done, so assume
+ * that all wireless interfaces are good to go. This is just an optimization;
+ * we could start everything.
*/
-void
-check_drop_dhcp(struct interface *ifp)
+static boolean_t
+is_startable(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);
+ ifp->if_flags = get_ifflags(ifp->if_name, ifp->if_family);
+ if (ifp->if_flags & IFF_RUNNING)
+ return (B_TRUE);
+ return (ifp->if_type == IF_WIRELESS);
}
/*
@@ -860,94 +904,63 @@ check_drop_dhcp(struct interface *ifp)
*
* 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...
+ * determine which interface should be set up.
+ *
+ * ifs_lock is not held on entry. The caller will cancel this thread and wait
+ * for it to exit if the interface is to be deleted.
*/
static void *
gather_interface_info(void *arg)
{
struct interface *i = arg;
- llp_t *llp;
-
- assert(i != NULL);
+ int retv;
dprintf("Start gathering info for %s", i->if_name);
switch (i->if_type) {
case IF_WIRELESS:
- (void) scan_wireless_nets(i);
+ /* This generates EV_NEWAP when successful */
+ retv = launch_wireless_scan(i->if_name);
+ if (retv != 0)
+ dprintf("didn't launch wireless scan: %s",
+ strerror(retv));
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);
+ if (llp_get_ipv4src(i->if_name) == IPV4SRC_DHCP) {
+ /*
+ * 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 (is_startable(i))
+ start_dhcp(i);
+ }
+ (void) np_queue_add_event(EV_LINKUP, i->if_name);
break;
- default:
- /* For other types, do not do anything. */
- return (NULL);
}
- gen_newif_event(i);
-
dprintf("Done gathering info for %s", i->if_name);
+ i->if_thr = 0;
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.
+ * the interface and arg as parameters, and with the ifs_lock held.
*/
void
walk_interface(void (*walker)(struct interface *, void *), void *arg)
{
- struct interface *i;
+ struct interface *ifp;
- for (i = ifs_head; i != NULL; i = i->if_next)
- walker(i, arg);
+ if (pthread_mutex_lock(&ifs_lock) != 0)
+ return;
+ for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next)
+ walker(ifp, arg);
+ (void) pthread_mutex_unlock(&ifs_lock);
}
static void
@@ -1014,11 +1027,6 @@ initialize_interfaces(void)
unsigned int wait_time = 1;
boolean_t found_nonlo_if;
- 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.
*/
@@ -1090,12 +1098,13 @@ initialize_interfaces(void)
}
/*
- * Walker function used to start info gathering of each interface.
+ * Walker function used to start info gathering of each interface. Caller
+ * holds ifs_lock.
*/
void
start_if_info_collect(struct interface *ifp, void *arg)
{
- pthread_t if_thr;
+ int retv;
pthread_attr_t attr;
/*
@@ -1104,15 +1113,17 @@ start_if_info_collect(struct interface *ifp, void *arg)
* event after we initialize interfaces before the routing thread
* is launched.
*/
- if (arg != NULL && *(boolean_t *)arg)
+ if (arg != NULL)
ifp->if_flags = get_ifflags(ifp->if_name, ifp->if_family);
/*
* Only if the cable of the wired interface is
* plugged in, start gathering info from it.
*/
- if (!is_plugged_in(ifp))
+ if (!is_startable(ifp)) {
+ dprintf("not gathering info on %s; not running", ifp->if_name);
return;
+ }
/*
* This is a "fresh start" for the interface, so clear old DHCP flags.
@@ -1121,12 +1132,14 @@ start_if_info_collect(struct interface *ifp, void *arg)
(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");
+ if ((retv = pthread_create(&ifp->if_thr, &attr, gather_interface_info,
+ ifp)) != 0) {
+ syslog(LOG_ERR, "create interface gathering thread: %s",
+ strerror(retv));
exit(EXIT_FAILURE);
} else {
- dprintf("interface info thread: %d", if_thr);
+ dprintf("interface info thread for %s: %d", ifp->if_name,
+ ifp->if_thr);
}
}
@@ -1135,12 +1148,10 @@ start_if_info_collect(struct interface *ifp, void *arg)
* If timer has expired, generate a timer event for the
* interface.
*/
-/* ARGSUSED */
-void
-check_interface_timer(struct interface *ifp, void *arg)
+static void
+iftimer(struct interface *ifp, void *arg)
{
- uint32_t now = *(uint32_t *)arg;
- struct np_event *ev;
+ uint32_t now = (uint32_t)(uintptr_t)arg;
if (ifp->if_timer_expire == 0)
return;
@@ -1152,27 +1163,20 @@ check_interface_timer(struct interface *ifp, void *arg)
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);
+ (void) np_queue_add_event(EV_TIMER, ifp->if_name);
}
-enum interface_type
+void
+check_interface_timers(uint32_t now)
+{
+ walk_interface(iftimer, (void *)(uint32_t)now);
+}
+
+libnwam_interface_type_t
find_if_type(const char *name)
{
uint32_t media;
- enum interface_type type;
+ libnwam_interface_type_t type;
if (name == NULL) {
dprintf("find_if_type: no ifname; returning IF_UNKNOWN");
@@ -1199,7 +1203,7 @@ find_if_type(const char *name)
}
const char *
-if_type_str(enum interface_type type)
+if_type_str(libnwam_interface_type_t type)
{
switch (type) {
case IF_WIRED:
@@ -1213,3 +1217,170 @@ if_type_str(enum interface_type type)
return ("unknown type");
}
}
+
+/*
+ * This is called by the routing socket thread to update the IPv4 address on an
+ * interface. The routing socket thread cannot touch the interface structures
+ * without holding the global lock, because interface structures can be
+ * deleted.
+ */
+void
+update_interface_v4_address(const char *ifname, in_addr_t addr)
+{
+ struct in_addr in;
+ struct interface *ifp;
+
+ if (pthread_mutex_lock(&ifs_lock) == 0) {
+ if ((ifp = get_interface(ifname)) == NULL) {
+ dprintf("no interface struct for %s; ignoring message",
+ ifname);
+ } else if (ifp->if_ipv4addr != addr) {
+ ifp->if_ipv4addr = addr;
+ in.s_addr = addr;
+ dprintf("cached new address %s for link %s",
+ inet_ntoa(in), ifname);
+ (void) np_queue_add_event(EV_NEWADDR, ifname);
+ } else {
+ dprintf("same address on %s; no event", ifname);
+ }
+ (void) pthread_mutex_unlock(&ifs_lock);
+ }
+}
+
+/*
+ * This is called by the routing socket thread to update the flags on a given
+ * IPv4 interface. If the interface has changed state, then we launch an event
+ * or a thread as appropriate.
+ */
+void
+update_interface_flags(const char *ifname, int newflags)
+{
+ struct interface *ifp;
+ int oldflags;
+
+ if (pthread_mutex_lock(&ifs_lock) == 0) {
+ if ((ifp = get_interface(ifname)) == NULL) {
+ dprintf("no interface data for %s; ignoring message",
+ ifname);
+ } else {
+ /*
+ * Check for toggling of the IFF_RUNNING flag.
+ *
+ * On any change in the flag value, we turn off the
+ * DHCP flags; the change in the RUNNING state
+ * indicates a "fresh start" for the interface, so we
+ * should try dhcp again.
+ *
+ * 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.
+ */
+ oldflags = ifp->if_flags;
+ if ((oldflags & IFF_RUNNING) !=
+ (newflags & IFF_RUNNING)) {
+ ifp->if_lflags &= ~IF_DHCPFLAGS;
+ }
+ if (!(newflags & IFF_DHCPRUNNING))
+ ifp->if_lflags &= ~IF_DHCPFLAGS;
+ ifp->if_flags = newflags;
+ if (!(oldflags & IFF_RUNNING) &&
+ (newflags & IFF_RUNNING)) {
+ start_if_info_collect(ifp, NULL);
+ } else if ((oldflags & IFF_RUNNING) &&
+ !(newflags & IFF_RUNNING)) {
+ (void) np_queue_add_event(EV_LINKDROP, ifname);
+ } else {
+ dprintf("no-event flag change on %s: %x -> %x",
+ ifp->if_name, oldflags, newflags);
+ }
+ }
+ (void) pthread_mutex_unlock(&ifs_lock);
+ }
+}
+
+/*
+ * Called only in main thread. Note that wireless interfaces are considered
+ * "ok" even if the IFF_RUNNING bit isn't set. This is because AP attach
+ * occurs as part of the LLP selection process.
+ */
+boolean_t
+is_interface_ok(const char *ifname)
+{
+ boolean_t is_ok = B_FALSE;
+ struct interface *ifp;
+
+ if ((ifp = get_interface(ifname)) != NULL &&
+ !(ifp->if_lflags & IF_DHCPFAILED) && is_startable(ifp))
+ is_ok = B_TRUE;
+ return (is_ok);
+}
+
+/*
+ * Return the interface type for a given interface name.
+ */
+libnwam_interface_type_t
+get_if_type(const char *ifname)
+{
+ libnwam_interface_type_t ift = IF_UNKNOWN;
+ struct interface *ifp;
+
+ if (pthread_mutex_lock(&ifs_lock) == 0) {
+ if ((ifp = get_interface(ifname)) != NULL)
+ ift = ifp->if_type;
+ (void) pthread_mutex_unlock(&ifs_lock);
+ }
+ return (ift);
+}
+
+/*
+ * Get the interface state for storing in llp_t. This is used only with the
+ * doors interface to return status flags.
+ */
+void
+get_interface_state(const char *ifname, boolean_t *dhcp_failed,
+ boolean_t *link_up)
+{
+ struct interface *ifp;
+
+ *dhcp_failed = *link_up = B_FALSE;
+ if (pthread_mutex_lock(&ifs_lock) == 0) {
+ if ((ifp = get_interface(ifname)) != NULL) {
+ if (ifp->if_lflags & IF_DHCPFAILED)
+ *dhcp_failed = B_TRUE;
+ if (ifp->if_flags & IFF_UP)
+ *link_up = B_TRUE;
+ }
+ (void) pthread_mutex_unlock(&ifs_lock);
+ }
+}
+
+/*
+ * Dump out the interface state via debug messages.
+ */
+void
+print_interface_status(void)
+{
+ struct interface *ifp;
+ struct in_addr ina;
+
+ if (pthread_mutex_lock(&ifs_lock) == 0) {
+ if (upper_layer_profile[0] != '\0')
+ dprintf("upper layer profile %s active",
+ upper_layer_profile);
+ else
+ dprintf("no upper layer profile active");
+ for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) {
+ ina.s_addr = ifp->if_ipv4addr;
+ dprintf("I/F %s af %d flags %llX lflags %X type %d "
+ "expire %u v6 %son-link up %sattempted addr %s",
+ ifp->if_name, ifp->if_family, ifp->if_flags,
+ ifp->if_lflags, ifp->if_type, ifp->if_timer_expire,
+ ifp->if_v6onlink ? "" : "not ",
+ ifp->if_up_attempted ? "" : "not ",
+ inet_ntoa(ina));
+ }
+ (void) pthread_mutex_unlock(&ifs_lock);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/llp.c b/usr/src/cmd/cmd-inet/lib/nwamd/llp.c
index fee7bfcb24..737aea412b 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/llp.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/llp.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 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
@@ -52,13 +50,25 @@
* 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.
+ * The llp list is protected by the global llp_lock, which must be
+ * pthread_mutex_lock()'d before reading or writing the list. Only the main
+ * thread can write to the list; this allows the main thread to deal with read
+ * access to structure pointers without holding locks and without the
+ * complexity of reference counts. All other threads must hold llp_lock for
+ * the duration of any read access to the data, and must not deal directly in
+ * structure pointers. (A thread may also hold machine_lock to block the main
+ * thread entirely in order to manipulate the data; such use is isolated to the
+ * door interface.)
+ *
+ * Functions in this file have comments noting where the main thread alone is
+ * the caller. These functions do not need to acquire the lock.
+ *
+ * If you hold both ifs_lock and llp_lock, you must take ifs_lock first.
*/
#include <stdio.h>
#include <stdlib.h>
+#include <unistd.h>
#include <limits.h>
#include <strings.h>
#include <string.h>
@@ -82,17 +92,26 @@
/* 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;
+/* Accessed only from main thread or with llp_lock held */
+llp_t *link_layer_profile;
+
+static struct qelem llp_list;
+static llp_t *locked_llp;
/*
* 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 uint32_t llp_highest_pri;
static void print_llp_list(void);
+void
+initialize_llp(void)
+{
+ llp_list.q_forw = llp_list.q_back = &llp_list;
+}
+
char *
llp_prnm(llp_t *llp)
{
@@ -104,27 +123,51 @@ llp_prnm(llp_t *llp)
return (llp->llp_lname);
}
+/*
+ * This function removes a given LLP from the global list and discards it.
+ * Called only from the main thread.
+ */
+void
+llp_delete(llp_t *llp)
+{
+ if (pthread_mutex_lock(&llp_lock) == 0) {
+ if (llp == locked_llp)
+ locked_llp = NULL;
+ assert(llp != link_layer_profile);
+ remque(&llp->llp_links);
+ (void) pthread_mutex_unlock(&llp_lock);
+ free(llp->llp_ipv6addrstr);
+ free(llp->llp_ipv4addrstr);
+ free(llp);
+ }
+}
+
static void
-llp_list_free(llp_t *head)
+llp_list_free(void)
{
- llp_t **llpp;
- llp_t *llpfree;
+ int retv;
+ llp_t *llp;
- if (pthread_mutex_lock(&llp_lock) != 0) {
+ locked_llp = NULL;
+ if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
/* Something very serious is wrong... */
- syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %m");
+ syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %s",
+ strerror(retv));
return;
}
- llpp = &head;
- while (*llpp != NULL) {
- llpfree = *llpp;
- *llpp = llpfree->llp_next;
- free(llpfree->llp_ipv4addrstr);
- free(llpfree);
+ while (llp_list.q_forw != &llp_list) {
+ llp = (llp_t *)llp_list.q_forw;
+ remque(&llp->llp_links);
+ free(llp->llp_ipv6addrstr);
+ free(llp->llp_ipv4addrstr);
+ free(llp);
}
(void) pthread_mutex_unlock(&llp_lock);
}
+/*
+ * Called either from main thread or with llp_lock held.
+ */
llp_t *
llp_lookup(const char *link)
{
@@ -133,17 +176,13 @@ llp_lookup(const char *link)
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) {
+ for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
+ llp = (llp_t *)llp->llp_links.q_forw) {
if (strcmp(link, llp->llp_lname) == 0)
break;
}
- (void) pthread_mutex_unlock(&llp_lock);
+ if (llp == (llp_t *)&llp_list)
+ llp = NULL;
return (llp);
}
@@ -158,56 +197,57 @@ llp_lookup(const char *link)
llp_t *
llp_high_pri(llp_t *a, llp_t *b)
{
- if (a == NULL)
+ if (a == NULL || a->llp_links.q_forw == NULL)
+ return (b);
+ else if (b == NULL || b->llp_links.q_forw == NULL)
+ return (a);
+
+ /* Check for locked LLP selection for user interface */
+ if (a == locked_llp)
+ return (a);
+ else if (b == locked_llp)
+ return (b);
+
+ if (a->llp_failed && !b->llp_failed)
return (b);
- else if (b == NULL)
+ if (!a->llp_failed && b->llp_failed)
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.
+ * Chooses the highest priority link that corresponds to an available
+ * interface. Called only in the main thread.
*/
llp_t *
llp_best_avail(void)
{
- llp_t *p, *rtnllp = NULL;
- struct interface *ifp;
+ llp_t *llp, *rtnllp;
- /* 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);
+ if ((rtnllp = locked_llp) == NULL) {
+ for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
+ llp = (llp_t *)llp->llp_links.q_forw) {
+ if (is_interface_ok(llp->llp_lname))
+ rtnllp = llp_high_pri(llp, rtnllp);
+ }
}
- (void) pthread_mutex_unlock(&llp_lock);
return (rtnllp);
}
/*
- * Returns B_TRUE if llp is successfully activated;
- * B_FALSE if activation fails.
+ * Called only by the main thread. Note that this leaves link_layer_profile
+ * set to NULL only in the case of abject failure, and then leaves llp_failed
+ * set.
*/
-boolean_t
+static void
llp_activate(llp_t *llp)
{
- boolean_t rtn;
char *host;
/*
* Choosing "dhcp" as a hostname is unsupported right now.
@@ -216,40 +256,33 @@ llp_activate(llp_t *llp)
*/
char *dhcpstr = "dhcp";
- llp_deactivate();
+ assert(link_layer_profile == NULL);
host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr :
llp->llp_ipv4addrstr;
- if (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
+ report_llp_selected(llp->llp_lname);
+ switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
llp->llp_ipv6onlink)) {
+ case SUCCESS:
+ llp->llp_failed = B_FALSE;
+ llp->llp_waiting = B_FALSE;
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));
+ break;
+ case FAILURE:
+ llp->llp_failed = B_TRUE;
+ llp->llp_waiting = B_FALSE;
+ dprintf("llp_activate: failed to bring up %s", llp_prnm(llp));
+ report_llp_unselected(llp->llp_lname, dcFailed);
link_layer_profile = NULL;
- rtn = B_FALSE;
+ break;
+ case WAITING:
+ llp->llp_failed = B_FALSE;
+ llp->llp_waiting = B_TRUE;
+ link_layer_profile = llp;
+ dprintf("llp_activate: waiting for %s", llp_prnm(llp));
}
-
- 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, 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;
}
/*
@@ -263,222 +296,120 @@ llp_deactivate(void)
* 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.
+ * Called only by the main thread.
*/
void
-llp_swap(llp_t *newllp)
+llp_swap(llp_t *newllp, libnwam_diag_cause_t cause)
{
- char *upifname;
+ int minpri;
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 {
+ if (link_layer_profile != NULL) {
dprintf("taking down current link layer profile (%s)",
llp_prnm(link_layer_profile));
- llp_deactivate();
+ report_llp_unselected(link_layer_profile->llp_lname, cause);
+ link_layer_profile->llp_waiting = B_FALSE;
+ link_layer_profile = NULL;
}
- if (newllp != NULL) {
+
+ /*
+ * Establish the new link layer profile. If we have trouble setting
+ * it, then try to get another. Note that llp_activate sets llp_failed
+ * on failure, so this loop is guaranteed to terminate.
+ */
+ while (newllp != NULL) {
dprintf("bringing up new link layer profile (%s)",
llp_prnm(newllp));
- (void) llp_activate(newllp);
+ llp_activate(newllp);
+ newllp = NULL;
+ if (link_layer_profile == NULL &&
+ (newllp = llp_best_avail()) != NULL &&
+ newllp->llp_failed)
+ newllp = NULL;
}
-}
-
-/*
- *
- * 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;
+ /*
+ * Knock down all interfaces that are at a lower (higher-numbered)
+ * priority than the new one. If there isn't a new one, then leave
+ * everything as it is.
+ */
+ if (link_layer_profile == NULL) {
+ minpri = -1;
+ if (locked_llp != NULL)
+ dprintf("taking down all but %s", llp_prnm(locked_llp));
+ } else {
+ minpri = link_layer_profile->llp_pri;
+ dprintf("taking down remaining interfaces below priority %d",
+ minpri);
+ }
+ for (newllp = (llp_t *)llp_list.q_forw; newllp != (llp_t *)&llp_list;
+ newllp = (llp_t *)newllp->llp_links.q_forw) {
+ if (newllp == link_layer_profile)
+ continue;
+ if ((link_layer_profile != NULL && newllp->llp_pri > minpri) ||
+ (locked_llp != NULL && newllp != locked_llp))
+ takedowninterface(newllp->llp_lname, cause);
+ else
+ clear_cached_address(newllp->llp_lname);
}
}
/*
- * 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.
+ * Create the named LLP with default settings. Called only in main thread.
*/
-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)
+llp_t *
+llp_add(const char *name)
{
- FILE *fp;
- int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ int retv;
+ llp_t *llp;
- /* 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 ((llp = calloc(1, sizeof (llp_t))) == NULL) {
+ syslog(LOG_ERR, "cannot allocate LLP: %m");
+ return (NULL);
}
- if ((fp = fopen(LLPFILE, "w")) == NULL) {
- syslog(LOG_ERR, "create LLP config file: %m");
- return;
+
+ if (strlcpy(llp->llp_lname, name, sizeof (llp->llp_lname)) >=
+ sizeof (llp->llp_lname)) {
+ syslog(LOG_ERR, "llp: link name '%s' too long; ignoring entry",
+ name);
+ free(llp);
+ return (NULL);
}
- 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;
+ llp->llp_fileorder = llp->llp_pri =
+ atomic_add_32_nv(&llp_highest_pri, 1);
+ llp->llp_ipv4src = IPV4SRC_DHCP;
+ llp->llp_type = find_if_type(llp->llp_lname);
+ llp->llp_ipv6onlink = B_TRUE;
/*
* 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 (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) {
+ syslog(LOG_ERR, "llp: wrong type of interface for %s", name);
+ free(llp);
+ return (NULL);
+ }
- if (pthread_mutex_lock(&llp_lock) != 0) {
+ if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
/* Something very serious is wrong... */
- syslog(LOG_ERR, "llp_list_append: cannot lock mutex: %m");
- return;
+ syslog(LOG_ERR, "llp: cannot lock mutex: %s", strerror(retv));
+ free(llp);
+ return (NULL);
}
- while (*wpp != NULL)
- wpp = &(*wpp)->llp_next;
- *wpp = llp;
- llp->llp_next = NULL;
+ insque(&llp->llp_links, llp_list.q_back);
(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);
+ dprintf("created llp for link %s, priority %d", llp->llp_lname,
+ llp->llp_pri);
+ return (llp);
}
/*
@@ -491,39 +422,43 @@ create_and_add_llp(const char *name, ipv4src_t ipv4src, const char *addrstr,
* 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.
+ *
+ * ifs_lock is held when this function is called. Called only in main thread.
*/
static void
find_and_add_llp(struct interface *ifp, void *arg)
{
- FILE *fp = (FILE *)arg;
+ FILE *fp = arg;
+
+ if (ifp->if_type != IF_TUN && llp_lookup(ifp->if_name) == NULL) {
+ switch (ifp->if_family) {
+ case AF_INET:
+ (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name);
+ break;
- 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);
+ case AF_INET6:
+ (void) fprintf(fp, "%s\tipv6\n", ifp->if_name);
+ break;
+
+ default:
+ syslog(LOG_ERR, "interface %s family %d?!",
+ ifp->if_name, ifp->if_family);
+ return;
+ }
+ dprintf("Added %s to %s", ifp->if_name, LLPFILE);
/* If we run out of memory, ignore this interface for now. */
- create_and_add_llp(ifp->if_name, IPV4SRC_DHCP, NULL,
- B_TRUE, NULL);
+ (void) llp_add(ifp->if_name);
}
}
-/*
- * 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)
+ for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list;
+ wp = (llp_t *)wp->llp_links.q_forw)
dprintf("==> %s", wp->llp_lname);
}
@@ -533,7 +468,7 @@ print_llp_list(void)
* 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.
+ * The second field can be either static, dhcp, ipv6, noipv6, or priority.
* 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.
@@ -545,6 +480,10 @@ print_llp_list(void)
* 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.
+ * If the second field is priority, then the next field is an integer
+ * specifying the link priority.
+ *
+ * Called only in main thread.
*/
void
llp_parse_config(void)
@@ -553,50 +492,35 @@ llp_parse_config(void)
static const char DHCP[] = "dhcp";
static const char IPV6[] = "ipv6";
static const char NOIPV6[] = "noipv6";
+ static const char PRIORITY[] = "priority";
FILE *fp;
char line[LINE_MAX];
- char *cp, *lasts, *lstr, *srcstr, *addrstr, *v6addrstr;
+ char *cp, *lasts, *lstr, *srcstr, *addrstr;
int lnum;
- ipv4src_t ipv4src;
- boolean_t ipv6onlink;
+ llp_t *llp;
+
+ /* Create the NWAM directory in case it does not exist. */
+ if (mkdir(LLPDIRNAME, LLPDIRMODE) != 0 &&
+ errno != EEXIST) {
+ syslog(LOG_ERR, "could not create %s: %m", LLPDIRNAME);
+ return;
+ }
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");
+ if ((fp = fopen(LLPFILE, "w+")) == NULL) {
+ syslog(LOG_ERR, "create LLP config file: %m");
return;
}
}
- if (llp_head != NULL)
- llp_list_free(llp_head);
- llp_head = NULL;
+ llp_list_free();
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';
@@ -615,51 +539,474 @@ llp_parse_config(void)
"ignoring entry", lnum);
continue;
}
+
+ if ((llp = llp_lookup(lstr)) == NULL &&
+ (llp = llp_add(lstr)) == NULL) {
+ syslog(LOG_ERR, "llp:%d: cannot add 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",
+ "for static config", lnum);
+ } else if ((addrstr = strdup(addrstr)) == NULL) {
+ syslog(LOG_ERR, "llp:%d: cannot save address",
lnum);
- continue;
+ } else {
+ free(llp->llp_ipv4addrstr);
+ llp->llp_ipv4src = IPV4SRC_STATIC;
+ llp->llp_ipv4addrstr = addrstr;
}
- ipv4src = IPV4SRC_STATIC;
+
} else if (strcasecmp(srcstr, DHCP) == 0) {
- ipv4src = IPV4SRC_DHCP;
+ llp->llp_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);
- }
+ llp->llp_ipv6onlink = B_TRUE;
+ if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
+ (void) 0;
+ } else if ((addrstr = strdup(addrstr)) == NULL) {
+ syslog(LOG_ERR, "llp:%d: cannot save address",
+ lnum);
} else {
- v6addrstr = NULL;
+ free(llp->llp_ipv6addrstr);
+ llp->llp_ipv6addrstr = addrstr;
}
+
} else if (strcasecmp(srcstr, NOIPV6) == 0) {
- ipv6onlink = B_FALSE;
+ llp->llp_ipv6onlink = B_FALSE;
+
+ } else if (strcasecmp(srcstr, PRIORITY) == 0) {
+ if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
+ syslog(LOG_ERR,
+ "llp:%d: missing priority value", lnum);
+ } else {
+ llp->llp_pri = atoi(addrstr);
+ }
+
} else {
- syslog(LOG_ERR, "llp:%d: unrecognized "
- "field; ignoring entry", lnum);
- continue;
+ syslog(LOG_ERR, "llp:%d: unrecognized field '%s'", lnum,
+ srcstr);
}
-
- 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.
+ * file for future reference.
*/
- add_missing_if_llp(fp);
+ walk_interface(find_and_add_llp, fp);
(void) fclose(fp);
print_llp_list();
}
+
+/*
+ * Called only from the main thread.
+ */
+void
+llp_add_file(const llp_t *llp)
+{
+ FILE *fp;
+
+ if ((fp = fopen(LLPFILE, "a")) == NULL)
+ return;
+ (void) fprintf(fp, "%s\tdhcp\n", llp->llp_lname);
+ (void) fclose(fp);
+}
+
+/*
+ * This function rewrites the LLP configuration file entry for a given
+ * interface and keyword. If the keyword is present, then it is updated if
+ * removeonly is B_FALSE, otherwise it's removed. If the keyword is not
+ * present, then it is added immediately after the last entry for that
+ * interface if removeonly is B_FALSE, otherwise no action is taken. User
+ * comments are preserved.
+ *
+ * To preserve file integrity, this is called only from the main thread.
+ */
+static void
+llp_update_config(const char *ifname, const char *keyword, const char *optval,
+ boolean_t removeonly)
+{
+ FILE *fpin, *fpout;
+ char line[LINE_MAX];
+ char *cp, *lstr, *keystr, *valstr, *lasts;
+ boolean_t matched_if, copying;
+ long match_pos;
+
+ if ((fpin = fopen(LLPFILE, "r")) == NULL)
+ return;
+ if ((fpout = fopen(LLPFILETMP, "w")) == NULL) {
+ syslog(LOG_ERR, "create LLP temporary config file: %m");
+ (void) fclose(fpin);
+ return;
+ }
+ matched_if = copying = B_FALSE;
+restart:
+ while (fgets(line, sizeof (line), fpin) != NULL) {
+ cp = line + strlen(line) - 1;
+ if (cp >= line && *cp == '\n')
+ *cp = '\0';
+
+ cp = line;
+ while (isspace(*cp))
+ cp++;
+
+ lstr = NULL;
+ if (copying || *cp == '#' ||
+ (lstr = strtok_r(cp, " \t", &lasts)) == NULL ||
+ strcmp(lstr, ifname) != 0) {
+ if (!matched_if || copying) {
+ /*
+ * It's ugly to write through the pointer
+ * returned as the third argument of strtok_r,
+ * but doing so saves a data copy.
+ */
+ if (lstr != NULL && lasts != NULL)
+ lasts[-1] = '\t';
+ (void) fprintf(fpout, "%s\n", line);
+ }
+ continue;
+ }
+
+ if (lasts != NULL)
+ lasts[-1] = '\t';
+
+ /*
+ * If we've found the keyword, then process removal or update
+ * of the value.
+ */
+ if ((keystr = strtok_r(NULL, " \t", &lasts)) != NULL &&
+ strcmp(keystr, keyword) == 0) {
+ matched_if = copying = B_TRUE;
+ if (removeonly)
+ continue;
+ valstr = strtok_r(NULL, " \t", &lasts);
+ if ((valstr == NULL && optval == NULL) ||
+ (valstr != NULL && optval != NULL &&
+ strcmp(valstr, optval) == 0)) {
+ /* Value identical; abort update */
+ goto no_change;
+ }
+ if (optval == NULL) {
+ (void) fprintf(fpout, "%s\t%s\n", ifname,
+ keyword);
+ } else {
+ (void) fprintf(fpout, "%s\t%s %s\n", ifname,
+ keyword, optval);
+ }
+ continue;
+ }
+
+ /* Otherwise, record the last possible insertion point */
+ matched_if = B_TRUE;
+ match_pos = ftell(fpin);
+ if (lasts != NULL)
+ lasts[-1] = '\t';
+ (void) fprintf(fpout, "%s\n", line);
+ }
+ if (!copying) {
+ /* keyword not encountered; we're done if deleting */
+ if (removeonly)
+ goto no_change;
+ /* need to add keyword and value */
+ if (optval == NULL) {
+ (void) fprintf(fpout, "%s\t%s\n", ifname, keyword);
+ } else {
+ (void) fprintf(fpout, "%s\t%s %s\n", ifname, keyword,
+ optval);
+ }
+ /* copy the rest of the file */
+ (void) fseek(fpin, match_pos, SEEK_SET);
+ copying = B_TRUE;
+ goto restart;
+ }
+ (void) fclose(fpin);
+ (void) fclose(fpout);
+ if (rename(LLPFILETMP, LLPFILE) != 0) {
+ syslog(LOG_ERR, "rename LLP temporary config file: %m");
+ (void) unlink(LLPFILETMP);
+ }
+ return;
+
+no_change:
+ (void) fclose(fpin);
+ (void) fclose(fpout);
+ (void) unlink(LLPFILETMP);
+}
+
+/*
+ * This is called back from the main thread by the state machine.
+ */
+void
+llp_write_changed_priority(llp_t *llp)
+{
+ if (llp->llp_pri == llp->llp_fileorder) {
+ llp_update_config(llp->llp_lname, "priority", NULL, B_TRUE);
+ } else {
+ char prival[32];
+
+ (void) snprintf(prival, sizeof (prival), "%d", llp->llp_pri);
+ llp_update_config(llp->llp_lname, "priority", prival, B_FALSE);
+ }
+}
+
+/*
+ * Called by the door interface: set LLP priority and schedule an LLP update if
+ * this interface has changed.
+ */
+int
+set_llp_priority(const char *ifname, int prio)
+{
+ llp_t *llp;
+ int retv;
+
+ if (prio < 0)
+ return (EINVAL);
+
+ if ((retv = pthread_mutex_lock(&llp_lock)) != 0)
+ return (retv);
+ if ((llp = llp_lookup(ifname)) != NULL) {
+ llp->llp_failed = B_FALSE;
+ if (llp->llp_pri != prio) {
+ llp->llp_pri = prio;
+ (void) np_queue_add_event(EV_USER, ifname);
+ }
+ retv = 0;
+ } else {
+ retv = ENXIO;
+ }
+ (void) pthread_mutex_unlock(&llp_lock);
+ return (retv);
+}
+
+/*
+ * Called by the door interface: set a locked LLP and schedule an LLP update if
+ * the locked LLP has changed.
+ */
+int
+set_locked_llp(const char *ifname)
+{
+ llp_t *llp;
+ int retv;
+
+ if ((retv = pthread_mutex_lock(&llp_lock)) != 0)
+ return (retv);
+ if (ifname[0] == '\0') {
+ if (locked_llp != NULL) {
+ ifname = locked_llp->llp_lname;
+ locked_llp = NULL;
+ (void) np_queue_add_event(EV_USER, ifname);
+ }
+ } else if ((llp = llp_lookup(ifname)) != NULL) {
+ locked_llp = llp;
+ if (llp != link_layer_profile)
+ (void) np_queue_add_event(EV_USER, ifname);
+ } else {
+ retv = ENXIO;
+ }
+ (void) pthread_mutex_unlock(&llp_lock);
+ return (retv);
+}
+
+/* Copy string to pre-allocated buffer. */
+static void
+strinsert(char **dest, const char *src, char **buf)
+{
+ if (*dest != NULL) {
+ *dest = strcpy(*buf, src);
+ *buf += strlen(src) + 1;
+ }
+}
+
+/*
+ * Sample the list of LLPs and copy to a single buffer for return through the
+ * door interface.
+ */
+llp_t *
+get_llp_list(size_t *lsize, uint_t *countp, char *selected, char *locked)
+{
+ llp_t *llplist, *llpl, *llp;
+ char *strptr;
+ uint_t nllp;
+ size_t strspace;
+ int retv;
+
+ *lsize = 0;
+ if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
+ errno = retv;
+ return (NULL);
+ }
+ (void) strlcpy(selected, link_layer_profile == NULL ? "" :
+ link_layer_profile->llp_lname, LIFNAMSIZ);
+ (void) strlcpy(locked, locked_llp == NULL ? "" :
+ locked_llp->llp_lname, LIFNAMSIZ);
+ nllp = 0;
+ strspace = 0;
+ for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
+ llp = (llp_t *)llp->llp_links.q_forw) {
+ nllp++;
+ if (llp->llp_ipv4addrstr != NULL)
+ strspace += strlen(llp->llp_ipv4addrstr) + 1;
+ if (llp->llp_ipv6addrstr != NULL)
+ strspace += strlen(llp->llp_ipv6addrstr) + 1;
+ }
+ *countp = nllp;
+ /* Note that malloc doesn't guarantee a NULL return for zero count */
+ llplist = nllp == 0 ? NULL :
+ malloc(sizeof (*llplist) * nllp + strspace);
+ if (llplist != NULL) {
+ *lsize = sizeof (*llplist) * nllp + strspace;
+ llpl = llplist;
+ strptr = (char *)(llplist + nllp);
+ for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list;
+ llp = (llp_t *)llp->llp_links.q_forw) {
+ *llpl = *llp;
+ strinsert(&llpl->llp_ipv4addrstr, llp->llp_ipv4addrstr,
+ &strptr);
+ strinsert(&llpl->llp_ipv6addrstr, llp->llp_ipv6addrstr,
+ &strptr);
+ llpl++;
+ }
+ }
+ (void) pthread_mutex_unlock(&llp_lock);
+
+ /* Add in the special door-only state flags */
+ llpl = llplist;
+ while (nllp-- > 0) {
+ get_interface_state(llpl->llp_lname, &llpl->llp_dhcp_failed,
+ &llpl->llp_link_up);
+ if (llpl->llp_type == IF_WIRELESS) {
+ get_wireless_state(llpl->llp_lname,
+ &llpl->llp_need_wlan, &llpl->llp_need_key);
+ }
+ llpl++;
+ }
+ return (llplist);
+}
+
+/*
+ * This is called for the special case when there are outstanding requests sent
+ * to the user interface, and the user interface disappears. We handle this
+ * case by re-running bringupinterface() without deselecting. That function
+ * will call the wireless and DHCP-related parts again, and they should proceed
+ * in automatic mode, because the UI is now gone.
+ *
+ * Called only by the main thread or by a thread holding machine_lock.
+ */
+void
+llp_reselect(void)
+{
+ llp_t *llp;
+ const char *host;
+
+ /*
+ * If there's no active profile, or if the active profile isn't waiting
+ * on the UI, then just return; nothing to do.
+ */
+ if ((llp = link_layer_profile) == NULL || !llp->llp_waiting)
+ return;
+
+ host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? "dhcp" :
+ llp->llp_ipv4addrstr;
+
+ dprintf("llp_reselect: bringing up %s", llp_prnm(llp));
+ switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr,
+ llp->llp_ipv6onlink)) {
+ case SUCCESS:
+ llp->llp_failed = B_FALSE;
+ llp->llp_waiting = B_FALSE;
+ dprintf("llp_reselect: activated llp for %s", llp_prnm(llp));
+ break;
+ case FAILURE:
+ llp->llp_failed = B_TRUE;
+ llp->llp_waiting = B_FALSE;
+ dprintf("llp_reselect: failed to bring up %s", llp_prnm(llp));
+ report_llp_unselected(llp->llp_lname, dcFailed);
+ link_layer_profile = NULL;
+ break;
+ case WAITING:
+ llp->llp_failed = B_FALSE;
+ dprintf("llp_reselect: waiting for %s", llp_prnm(llp));
+ }
+}
+
+/*
+ * This is used by the wireless module to check on the selected LLP. We don't
+ * do periodic rescans if a wireless interface is current and if its connection
+ * state is good.
+ */
+void
+llp_get_name_and_type(char *ifname, size_t ifnlen,
+ libnwam_interface_type_t *iftype)
+{
+ *ifname = '\0';
+ *iftype = IF_UNKNOWN;
+
+ if (pthread_mutex_lock(&llp_lock) == 0) {
+ if (link_layer_profile != NULL) {
+ (void) strlcpy(ifname, link_layer_profile->llp_lname,
+ ifnlen);
+ *iftype = link_layer_profile->llp_type;
+ }
+ (void) pthread_mutex_unlock(&llp_lock);
+ }
+}
+
+/*
+ * This is called by the interface.c module to check if an interface needs to
+ * run DHCP. It's intentionally called without ifs_lock held.
+ */
+libnwam_ipv4src_t
+llp_get_ipv4src(const char *ifname)
+{
+ libnwam_ipv4src_t src = IPV4SRC_DHCP;
+ llp_t *llp;
+
+ if (pthread_mutex_lock(&llp_lock) == 0) {
+ if ((llp = llp_lookup(ifname)) != NULL)
+ src = llp->llp_ipv4src;
+ (void) pthread_mutex_unlock(&llp_lock);
+ }
+ return (src);
+}
+
+/*
+ * Dump out the LLP state via debug messages.
+ */
+void
+print_llp_status(void)
+{
+ llp_t *llp;
+
+ if (pthread_mutex_lock(&llp_lock) == 0) {
+ if (link_layer_profile == NULL)
+ dprintf("no LLP selected");
+ else
+ dprintf("LLP %s selected",
+ link_layer_profile->llp_lname);
+ if (locked_llp == NULL)
+ dprintf("no LLP locked");
+ else
+ dprintf("LLP %s locked", locked_llp->llp_lname);
+ for (llp = (llp_t *)llp_list.q_forw;
+ llp != (llp_t *)&llp_list;
+ llp = (llp_t *)llp->llp_links.q_forw) {
+ dprintf("LLP %s pri %d file order %d type %d "
+ "%sfailed %swaiting src %d v4addr %s v6addr %s "
+ "v6 %son-link",
+ llp->llp_lname, llp->llp_pri, llp->llp_fileorder,
+ (int)llp->llp_type, llp->llp_failed ? "" : "not ",
+ llp->llp_waiting ? "" : "not ",
+ (int)llp->llp_ipv4src,
+ STRING(llp->llp_ipv4addrstr),
+ STRING(llp->llp_ipv6addrstr),
+ llp->llp_ipv6onlink ? "not " : "");
+ }
+ (void) pthread_mutex_unlock(&llp_lock);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/main.c b/usr/src/cmd/cmd-inet/lib/nwamd/main.c
index f065de988c..0b78b0aa2e 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/main.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/main.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* nwamd - NetWork Auto-Magic Daemon
*/
@@ -62,6 +60,7 @@ boolean_t fg = B_FALSE;
boolean_t shutting_down;
sigset_t original_sigmask;
char zonename[ZONENAME_MAX];
+pthread_mutex_t machine_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* nwamd
@@ -161,11 +160,14 @@ lookup_daemon_properties(void)
{
boolean_t debug_set;
uint64_t scan_interval;
+ uint64_t idle_time;
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;
+ if (lookup_count_property(OUR_PG, "idle_time", &idle_time) == 0)
+ door_idle_time = idle_time;
dprintf("Read daemon configuration properties.");
}
@@ -173,14 +175,13 @@ lookup_daemon_properties(void)
static void *
sighandler(void *arg)
{
- struct np_event *ev;
sigset_t sigset;
int sig;
uint32_t now;
(void) sigfillset(&sigset);
- for (;;) {
+ while (!shutting_down) {
sig = sigwait(&sigset);
dprintf("signal %d caught", sig);
switch (sig) {
@@ -192,7 +193,8 @@ sighandler(void *arg)
*/
timer_expire = TIMER_INFINITY;
now = NSEC_TO_SEC(gethrtime());
- walk_interface(check_interface_timer, &now);
+ check_interface_timers(now);
+ check_door_life(now);
break;
case SIGHUP:
/*
@@ -200,25 +202,27 @@ sighandler(void *arg)
*/
lookup_daemon_properties();
break;
+ case SIGINT:
+ /*
+ * Undocumented "print debug status" signal.
+ */
+ print_llp_status();
+ print_interface_status();
+ print_wireless_status();
+ break;
default:
syslog(LOG_NOTICE, "%s received, shutting down",
strsignal(sig));
shutting_down = B_TRUE;
- if ((ev = malloc(sizeof (*ev))) == NULL) {
+ if (!np_queue_add_event(EV_SHUTDOWN, 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);
}
+ return (NULL);
}
static void
@@ -270,8 +274,10 @@ change_user_set_privs(void)
(void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE);
(void) priv_addset(priv_set, PRIV_NET_PRIVADDR);
(void) priv_addset(priv_set, PRIV_NET_RAWACCESS);
+ (void) priv_addset(priv_set, PRIV_PROC_AUDIT);
(void) priv_addset(priv_set, PRIV_PROC_OWNER);
(void) priv_addset(priv_set, PRIV_PROC_SETID);
+ (void) priv_addset(priv_set, PRIV_SYS_CONFIG);
(void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG);
(void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG);
(void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG);
@@ -299,12 +305,27 @@ change_user_set_privs(void)
priv_freeset(priv_set);
}
+static void
+init_machine_mutex(void)
+{
+ pthread_mutexattr_t attrs;
+
+ (void) pthread_mutexattr_init(&attrs);
+ (void) pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ERRORCHECK);
+ if (pthread_mutex_init(&machine_lock, &attrs) != 0) {
+ syslog(LOG_ERR, "unable to set up machine lock");
+ exit(EXIT_FAILURE);
+ }
+ (void) pthread_mutexattr_destroy(&attrs);
+}
+
int
main(int argc, char *argv[])
{
int c;
int scan_lev;
struct np_event *e;
+ enum np_event_type etype;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
@@ -342,46 +363,45 @@ main(int argc, char *argv[])
if (!fg)
daemonize();
+ initialize_llp();
+
init_signalhandling();
- init_mutexes();
+ initialize_wireless();
lookup_zonename(zonename, sizeof (zonename));
+ init_machine_mutex();
+
initialize_interfaces();
llp_parse_config();
+ initialize_door();
+
(void) start_event_collection();
- while ((e = np_queue_get_event()) != NULL) { /* forever */
+ while ((e = np_queue_get_event()) != NULL) {
- 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;
+ etype = e->npe_type;
+ syslog(LOG_INFO, "got event type %s", npe_type_str(etype));
+ if (etype == EV_SHUTDOWN)
+ terminate_door();
+ if (pthread_mutex_lock(&machine_lock) != 0) {
+ syslog(LOG_ERR, "mutex lock");
+ exit(EXIT_FAILURE);
}
+ state_machine(e);
+ (void) pthread_mutex_unlock(&machine_lock);
+ free_event(e);
+ if (etype == EV_SHUTDOWN)
+ break;
}
- return (0);
+ syslog(LOG_DEBUG, "terminating routing and scanning threads");
+ (void) pthread_cancel(routing);
+ (void) pthread_cancel(scan);
+ (void) pthread_join(routing, NULL);
+ (void) pthread_join(scan, NULL);
+ syslog(LOG_INFO, "nwamd shutting down");
+ return (EXIT_SUCCESS);
}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c b/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c
index 95cc0585bc..81bf6210c7 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 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.
*
@@ -39,36 +37,31 @@
*
* state_machine() calls high level routines in llp.c and interface.c to act on
* the state of the machine in response to events.
+ *
+ * This function is called by the main thread in the program with machine_lock
+ * held. This is the only thread that can add or remove interface and LLP
+ * structures, and thus it's safe for this function to use those structures
+ * without locks. See also the locking comments in the interface.c and llp.c
+ * block comments.
*/
-#include <assert.h>
+#include <stdarg.h>
+#include <stdio.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"
-static struct sockaddr sinzero = { AF_INET, 0 };
-
void
state_machine(struct np_event *e)
{
@@ -88,8 +81,9 @@ state_machine(struct np_event *e)
break;
}
flags = get_ifflags(evif->if_name, evif->if_family);
- if ((flags & IFF_DHCPRUNNING) == 0 || ((flags & IFF_UP) &&
- !cmpsockaddr(evif->if_ipaddr, &sinzero))) {
+ if ((!(evif->if_lflags & IF_DHCPSTARTED) &&
+ !(flags & IFF_DHCPRUNNING)) || ((flags & IFF_UP) &&
+ evif->if_ipv4addr != INADDR_ANY)) {
/*
* Either DHCP came up successfully, or we're no
* longer trying to do DHCP on this interface;
@@ -99,6 +93,10 @@ state_machine(struct np_event *e)
"(ifflags 0x%llx)", evif->if_name, flags);
break;
}
+ if (evif->if_lflags & IF_DHCPFAILED) {
+ dprintf("ignoring timer; interface already failed");
+ break;
+ }
/*
* dhcp has not yet completed; give up on it for
* now and, if it is the currently active llp,
@@ -107,28 +105,90 @@ state_machine(struct np_event *e)
dprintf("giving up on dhcp on %s (ifflags 0x%llx)",
evif->if_name, flags);
evif->if_lflags |= IF_DHCPFAILED;
- if (interface_is_active(evif))
- llp_swap(llp_best_avail());
+ if (interface_is_active(evif)) {
+ if ((prefllp = llp_best_avail()) != NULL) {
+ llp_swap(prefllp, dcTimer);
+ } else {
+ dprintf("DHCP timed out, but no better link is "
+ "available");
+ report_interface_down(evif->if_name, dcTimer);
+ }
+ } else {
+ dprintf("DHCP failed on inactive link");
+ report_interface_down(evif->if_name, dcTimer);
+ }
break;
- case EV_ROUTING:
+ case EV_NEWAP:
+ if ((evllp = llp_lookup(e->npe_name)) == NULL) {
+ dprintf("state_machine: no llp for %s; ignoring "
+ "EV_NEWAP event", STRING(e->npe_name));
+ break;
+ }
+
+ if (evllp == link_layer_profile) {
+ llp_reselect();
+ } else {
+ evllp->llp_waiting = B_FALSE;
+ evllp->llp_failed = B_FALSE;
+ prefllp = llp_best_avail();
+ if (prefllp == NULL) {
+ dprintf("new APs on %s, but no best link is "
+ "available", llp_prnm(evllp));
+ } else if (prefllp != link_layer_profile) {
+ dprintf("state_machine: new APs on link %s "
+ "caused new preferred llp: %s (was %s)",
+ llp_prnm(evllp), llp_prnm(prefllp),
+ llp_prnm(link_layer_profile));
+ llp_swap(prefllp, dcNewAP);
+ }
+ }
+ break;
+
+ case EV_LINKDROP:
+ case EV_LINKUP:
+ case EV_LINKFADE:
+ case EV_LINKDISC:
+ case EV_USER:
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));
+ "(%p) for %s; ignoring EV_%s event",
+ (void *)evif, (void *)evllp, STRING(e->npe_name),
+ npe_type_str(e->npe_type));
break;
}
+
+ if (e->npe_type == EV_LINKUP || e->npe_type == EV_USER)
+ evllp->llp_failed = B_FALSE;
+
+ /*
+ * If we're here because wireless has disconnected, then clear
+ * the DHCP failure flag on this interface; this is a "fresh
+ * start" for the interface, so we should retry DHCP.
+ */
+ if (e->npe_type == EV_LINKFADE || e->npe_type == EV_LINKDISC)
+ evif->if_lflags &= ~IF_DHCPFAILED;
+
prefllp = llp_best_avail();
- if (prefllp != link_layer_profile) {
+ if (prefllp == NULL) {
+ dprintf("state changed on %s, but no best link is "
+ "available", llp_prnm(evllp));
+ } else 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);
+ llp_swap(prefllp,
+ e->npe_type == EV_LINKDROP ? dcUnplugged :
+ e->npe_type == EV_LINKFADE ? dcFaded :
+ e->npe_type == EV_LINKDISC ? dcGone :
+ e->npe_type == EV_USER ? dcUser :
+ dcBetter);
}
-
+ if (e->npe_type == EV_USER)
+ llp_write_changed_priority(evllp);
break;
case EV_NEWADDR:
@@ -139,19 +199,45 @@ state_machine(struct np_event *e)
(void *)evif, (void *)evllp, STRING(e->npe_name));
break;
}
+ evllp->llp_failed = B_FALSE;
if (evllp->llp_ipv4src == IPV4SRC_DHCP) {
flags = get_ifflags(evif->if_name, evif->if_family);
if (!(flags & IFF_DHCPRUNNING) || !(flags & IFF_UP) ||
- cmpsockaddr(evif->if_ipaddr, &sinzero)) {
+ evif->if_ipv4addr == INADDR_ANY) {
/*
* We don't have a DHCP lease. If we used to
* have one, then switch to another profile.
+ * Note that if *we* took the interface down
+ * (which happens if this isn't the one
+ * preferred interface), then this doesn't
+ * signal a DHCP failure.
*/
- if ((evif->if_lflags & IF_DHCPACQUIRED) != 0) {
+ if (!(flags & IFF_DHCPRUNNING))
+ evif->if_lflags &= ~IF_DHCPSTARTED;
+ if (evif->if_lflags & IF_DHCPACQUIRED) {
evif->if_lflags &= ~IF_DHCPACQUIRED;
- evif->if_lflags |= IF_DHCPFAILED;
- if (interface_is_active(evif))
- llp_swap(llp_best_avail());
+ if (interface_is_active(evif)) {
+ evif->if_lflags |=
+ IF_DHCPFAILED;
+ prefllp = llp_best_avail();
+ if (prefllp != NULL) {
+ dprintf("DHCP has "
+ "failed, switch "
+ "interfaces");
+ llp_swap(prefllp,
+ dcDHCP);
+ } else {
+ dprintf("DHCP failed, "
+ "but no better link"
+ " is available");
+ report_interface_down(
+ evif->if_name,
+ dcDHCP);
+ }
+ } else {
+ dprintf("DHCP not acquired and "
+ "not active");
+ }
}
break;
}
@@ -162,7 +248,7 @@ state_machine(struct np_event *e)
*/
evif->if_timer_expire = 0;
evif->if_lflags |= IF_DHCPACQUIRED;
- if ((evif->if_lflags & IF_DHCPFAILED) != 0) {
+ if (evif->if_lflags & IF_DHCPFAILED) {
evif->if_lflags &= ~IF_DHCPFAILED;
dhcp_restored = B_TRUE;
}
@@ -173,15 +259,14 @@ state_machine(struct np_event *e)
dprintf("state_machine: dhcp completed on "
"higher priority llp (%s); swapping",
llp_prnm(evllp));
- llp_swap(evllp);
+ llp_swap(evllp, dcBetter);
} 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, B_FALSE,
- evllp->llp_ipv6onlink);
+ takedowninterface(evllp->llp_lname, dcUnwanted);
break;
}
}
@@ -202,6 +287,80 @@ state_machine(struct np_event *e)
}
break;
+ case EV_ADDIF:
+ /* Plumb the interface */
+ if (start_child(IFCONFIG, e->npe_name, "plumb", NULL) != 0) {
+ syslog(LOG_ERR, "could not plumb interface %s",
+ e->npe_name);
+ return;
+ }
+ report_interface_added(e->npe_name);
+ evllp = llp_lookup(e->npe_name);
+ /* Add interface to interface list. */
+ evif = add_interface(AF_INET, e->npe_name, 0);
+ if (evllp == NULL) {
+ /*
+ * Create a new llp entry, and add it to llp list
+ * and /etc/nwam/llp. By default, the llp
+ * has a DHCP IPv4 address source, and IPv6 is
+ * used on the link. We don't plumb the
+ * IPv6 link yet - this is done for us by
+ * bringupinterface().
+ */
+ if ((evllp = llp_add(e->npe_name)) != NULL)
+ llp_add_file(evllp);
+ }
+
+ /*
+ * start_if_info_collect will launch the gather_interface_info
+ * thread, which will start a scan for wireless interfaces, or
+ * start DHCP on wired interfaces.
+ */
+ start_if_info_collect(evif, "check");
+ break;
+
+ case EV_REMIF:
+ /* Unplumb the interface */
+ if (start_child(IFCONFIG, e->npe_name, "unplumb", NULL) != 0) {
+ syslog(LOG_ERR, "could not unplumb interface %s",
+ e->npe_name);
+ return;
+ }
+ evllp = llp_lookup(e->npe_name);
+ remove_interface(e->npe_name);
+ if (evllp != NULL) {
+ /* Unplumb IPv6 interface if IPv6 is used on the link */
+ if (evllp->llp_ipv6onlink) {
+ (void) start_child(IFCONFIG, e->npe_name,
+ "inet6", "unplumb", NULL);
+ }
+ /* If this llp is active, deactivate it. */
+ if (evllp == link_layer_profile)
+ llp_swap(NULL, dcRemoved);
+ llp_delete(evllp);
+ }
+ report_interface_removed(e->npe_name);
+ break;
+
+ case EV_TAKEDOWN:
+ takedowninterface(e->npe_name, dcSelect);
+ break;
+
+ case EV_RESELECT:
+ if (link_layer_profile == NULL &&
+ (prefllp = llp_best_avail()) != NULL) {
+ dprintf("reselect: activating LLP %s",
+ llp_prnm(prefllp));
+ llp_swap(prefllp, dcNone);
+ } else {
+ llp_reselect();
+ }
+ break;
+
+ case EV_DOOR_TIME:
+ check_door_life(NSEC_TO_SEC(gethrtime()));
+ break;
+
case EV_SHUTDOWN:
/* Cleanup not expecting to see any more events after this */
cleanup();
@@ -217,10 +376,9 @@ state_machine(struct np_event *e)
void
cleanup(void)
{
+ deactivate_upper_layer_profile();
if (link_layer_profile != NULL) {
- deactivate_upper_layer_profile();
- takedowninterface(link_layer_profile->llp_lname, B_FALSE,
- link_layer_profile->llp_ipv6onlink);
+ takedowninterface(link_layer_profile->llp_lname, dcShutdown);
}
/*
* Since actions taken in nwamd result in dhcpagent being
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
index caf0815d32..94d750a3fe 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
@@ -27,34 +27,45 @@
#ifndef _STRUCTURES_H
#define _STRUCTURES_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
+#include <search.h>
#include <net/if.h>
#include <libscf.h>
#include <libdlwlan.h>
+#include <libnwam.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
+ * that we originally implemented. It's a bit of a mix
* right now.
*/
enum np_event_type {
- EV_ROUTING,
- EV_SYS,
- EV_TIMER,
- EV_SHUTDOWN,
- EV_NEWADDR
+ EV_LINKDROP, /* IFF_RUNNING flag dropped */
+ EV_LINKUP, /* Wired link is up */
+ EV_LINKFADE, /* Wireless link has poor signal */
+ EV_LINKDISC, /* Wireless link has disconnected */
+ EV_NEWAP, /* New AP in list / wireless link up */
+ EV_USER, /* User altered interface priority */
+ EV_TIMER, /* Timer(s) have expired */
+ EV_SHUTDOWN, /* Nwamd is shutting down */
+ EV_NEWADDR, /* Address established on interface */
+ EV_RESELECT, /* Client disconnect; retry operations */
+ EV_DOOR_TIME, /* Door server needs new timer */
+ EV_ADDIF, /* New interface detected */
+ EV_REMIF, /* Old interface removed */
+ EV_TAKEDOWN /* Take interface down; AP reselected */
};
-enum interface_type {
- IF_UNKNOWN,
- IF_WIRED,
- IF_WIRELESS,
- IF_TUN
-};
+/*
+ * Three-valued return types; used for cases where the processing terminates in
+ * a wait for the user.
+ */
+typedef enum {
+ SUCCESS = 0,
+ FAILURE,
+ WAITING
+} return_vals_t;
struct np_event {
enum np_event_type npe_type;
@@ -88,14 +99,17 @@ struct np_event {
* only 'down' the v4 interface.
*/
struct interface {
- char *if_name;
- datalink_id_t if_linkid;
+ char if_name[LIFNAMSIZ];
sa_family_t if_family;
uint64_t if_flags;
uint32_t if_lflags;
- enum interface_type if_type;
+ libnwam_interface_type_t if_type;
uint32_t if_timer_expire;
- struct sockaddr *if_ipaddr;
+ boolean_t if_v6onlink;
+ boolean_t if_up_attempted;
+ dladm_wlan_strength_t if_strength;
+ in_addr_t if_ipv4addr;
+ pthread_t if_thr;
struct interface *if_next;
};
@@ -122,25 +136,6 @@ struct interface {
#define IF_DHCPFLAGS (IF_DHCPFAILED | IF_DHCPSTARTED | IF_DHCPACQUIRED)
/*
- * 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.
@@ -149,28 +144,57 @@ typedef enum {
* protected by llp_lock.
*/
typedef struct llp {
- struct llp *llp_next;
+ struct qelem llp_links;
char llp_lname[LIFNAMSIZ];
int llp_pri; /* lower number => higher priority */
- enum interface_type llp_type;
- ipv4src_t llp_ipv4src;
+ int llp_fileorder;
+ libnwam_interface_type_t llp_type;
+ boolean_t llp_failed; /* interface bringup failed */
+ boolean_t llp_waiting; /* waiting for user interface */
+ libnwam_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 */
+
+ /* These are used only with door communication */
+ boolean_t llp_dhcp_failed;
+ boolean_t llp_link_up;
+ boolean_t llp_need_wlan;
+ boolean_t llp_need_key;
} llp_t;
/*
- * These entries are user allocated and should be managed by whomever
+ * The wireless module uses a separate thread to check AP status and scan for
+ * AP changes. Some wireless operations (such as scanning) may take a long
+ * time. For that reason, we maintain our own wireless interface structure to
+ * keep interface-specific wireless state.
+ */
+typedef struct wireless_if_s {
+ struct qelem wi_links;
+ char wi_name[LIFNAMSIZ];
+ datalink_id_t wi_linkid;
+ boolean_t wi_scan_running;
+ boolean_t wi_wireless_done;
+ boolean_t wi_need_key;
+ dladm_wlan_strength_t wi_strength;
+} wireless_if_t;
+
+/*
+ * These entries are user allocated and should be managed by whoever
* originates the structure.
*/
struct wireless_lan {
+ dladm_wlan_attr_t attrs;
+ boolean_t known;
+ boolean_t connected;
+ boolean_t scanned;
+ boolean_t rescan;
char *essid;
char *bssid;
char *signal_strength;
char *raw_key;
dladm_wlan_key_t *cooked_key;
- dladm_wlan_secmode_t sec_mode;
- char *wl_if_name;
+ char wl_if_name[LIFNAMSIZ];
};
/*
@@ -186,5 +210,52 @@ typedef struct scf_resources {
scf_value_t *sr_val;
} scf_resources_t;
+/*
+ * These are used to deliver events to the GUI. See door.c and libnwam.
+ */
+typedef struct nwam_descr_event_s {
+ libnwam_descr_evtype_t nde_type;
+ libnwam_diag_cause_t nde_cause;
+ struct in_addr nde_v4address;
+ int nde_prefixlen;
+ dladm_wlan_attr_t nde_attrs;
+ struct wireless_lan *nde_wlans;
+ size_t nde_wlansize;
+ char nde_interface[LIFNAMSIZ];
+} nwam_descr_event_t;
+
+typedef enum nwam_door_cmd_type_e {
+ ndcNull,
+ ndcWaitEvent,
+ ndcGetLLPList,
+ ndcSetLLPPriority,
+ ndcLockLLP,
+ ndcGetWlanList,
+ ndcSelectWlan,
+ ndcWlanKey,
+ ndcStartRescan,
+ ndcGetKnownAPList,
+ ndcAddKnownAP,
+ ndcDeleteKnownAP
+} nwam_door_cmd_type_t;
+
+typedef struct nwam_door_cmd_s {
+ nwam_door_cmd_type_t ndc_type;
+ char ndc_interface[LIFNAMSIZ];
+ int ndc_priority;
+ char ndc_essid[DLADM_STRSIZE];
+ char ndc_bssid[DLADM_STRSIZE];
+ char ndc_key[DLADM_STRSIZE];
+} nwam_door_cmd_t;
+
+typedef struct nwam_llp_data_s {
+ uint_t nld_count; /* number of llp_t struct following */
+ char nld_selected[LIFNAMSIZ];
+ char nld_locked[LIFNAMSIZ];
+} nwam_llp_data_t;
+
+typedef struct nwam_known_ap_s {
+ uint_t nka_count; /* number of libnwam_known_ap_t */
+} nwam_known_ap_t;
#endif /* _STRUCTURES_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/util.c b/usr/src/cmd/cmd-inet/lib/nwamd/util.c
index bad83540af..4321e78ee4 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/util.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/util.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 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
@@ -33,7 +31,6 @@
* - 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
*/
@@ -43,19 +40,16 @@
#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 <netinet/in.h>
+#include <arpa/inet.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>
@@ -82,15 +76,6 @@ dprintf(const char *fmt, ...)
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)
{
@@ -123,6 +108,34 @@ get_ifflags(const char *name, sa_family_t family)
return (flags);
}
+/* This is just a work-around for CR 6745448: clear out a toxic interface */
+void
+zero_out_v4addr(const char *name)
+{
+ icfg_if_t intf;
+ icfg_handle_t h;
+ struct sockaddr_in sinv;
+ socklen_t sinlen;
+ int pfxlen;
+
+ (void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
+ intf.if_protocol = AF_INET;
+
+ if (icfg_open(&h, &intf) != ICFG_SUCCESS)
+ return;
+
+ sinlen = sizeof (sinv);
+ if (icfg_get_addr(h, (struct sockaddr *)&sinv, &sinlen, &pfxlen,
+ B_FALSE) == ICFG_SUCCESS &&
+ sinv.sin_addr.s_addr != INADDR_ANY) {
+ dprintf("bug workaround: clear out address %s on %s",
+ inet_ntoa(sinv.sin_addr), name);
+ sinv.sin_addr.s_addr = INADDR_ANY;
+ (void) icfg_set_addr(h, (const struct sockaddr *)&sinv, sinlen);
+ }
+ icfg_close(h);
+}
+
/*
*
* This starts a child process determined by command. If command contains a
@@ -249,71 +262,6 @@ start_timer(uint32_t now, uint32_t 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. Note that the entry we want is usually
- * an ordinary user process but sometimes if a session leader has
- * exited, it can come from a DEAD_PROCESS, as is known to happen
- * when the user logs in via gdm(1m).
- */
- setutxent();
- while ((utp = getutxent()) != NULL) {
- if (((utp->ut_type == USER_PROCESS) ||
- (utp->ut_type == DEAD_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)
{
@@ -324,78 +272,3 @@ lookup_zonename(char *zonename, size_t zonesize)
syslog(LOG_ERR, "could not determine zone name");
(void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
}
-
-/* return B_TRUE if sin_family and sin_addr are the same, B_FALSE if not */
-boolean_t
-cmpsockaddr(const struct sockaddr *addr1, const struct sockaddr *addr2)
-{
- struct sockaddr_in *sina, *sinb;
- struct sockaddr_in6 *sin6a, *sin6b;
-
- if (addr1 == addr2)
- return (B_TRUE);
-
- if (addr1 == NULL || addr2 == NULL)
- return (B_FALSE);
-
- 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).
- */
-struct sockaddr *
-dupsockaddr(const 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);
- }
-}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/variables.h b/usr/src/cmd/cmd-inet/lib/nwamd/variables.h
index 2a4794f1be..a9efd4ee4c 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/variables.h
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/variables.h
@@ -20,20 +20,19 @@
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 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 machine_lock;
extern pthread_mutex_t queue_mutex;
extern pthread_cond_t queue_cond;
extern pthread_t routing, scan;
@@ -50,6 +49,8 @@ extern uint32_t timer_expire;
extern uint_t wlan_scan_interval;
extern dladm_wlan_strength_t wireless_scan_level;
+extern uint_t door_idle_time;
+
extern const char *OUR_FMRI;
extern const char *OUR_PG;
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
index f3d2a14c14..4be6af66c8 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
@@ -24,41 +24,32 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
- * This file containes all the routines to handle wireless (more
+ * This file contains 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/WPA 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.
+ * code will send a message to the GUI, which then must gather the results.
*
- * 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.
+ * WEP/WPA 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 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 one known AP (plus any number of unknown
+ * APs), the code will automatically connect to that AP without contacting the
+ * GUI. But if the detected signal strength of that one known AP is weaker
+ * than any of the unknown APs, the code will block on the GUI.
*
- * 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.
+ * If the AP scan results contain more than one known APs or no known APs, the
+ * GUI is notified.
*
* 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.
+ * Thus, the GUI can specify an AP's data.
*
* The code also periodically (specified by wlan_scan_interval) checks
* for the health of the AP connection. If the signal strength of the
@@ -68,13 +59,8 @@
* 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.
+ * Lock ordering note: wifi_mutex and wifi_init_mutex are not held at the same
+ * time.
*/
#include <unistd.h>
@@ -83,7 +69,6 @@
#include <stdio.h>
#include <strings.h>
#include <syslog.h>
-#include <assert.h>
#include <limits.h>
#include <errno.h>
#include <sys/stat.h>
@@ -93,8 +78,6 @@
#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>
@@ -113,15 +96,6 @@
(sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
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;
typedef enum {
ESSID = 0,
@@ -130,18 +104,29 @@ typedef enum {
} known_wifi_nets_fields_t;
/*
+ * List of wireless interfaces; protected by wifi_mutex.
+ */
+static struct qelem wi_list;
+static uint_t wi_link_count;
+
+/*
* 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;
+static char wifi_scan_intf[LIFNAMSIZ];
+static pthread_mutex_t wifi_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t wifi_init_cond = PTHREAD_COND_INITIALIZER;
-/* 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 */
+/*
+ * Array of wireless LAN entries; protected by wifi_mutex.
+ */
+static struct wireless_lan *wlans;
+static uint_t wireless_lan_count; /* allocated */
+static uint_t wireless_lan_used; /* used entries */
+static boolean_t new_ap_found;
static int key_string_to_secobj_value(char *, uint8_t *, uint_t *,
dladm_secobj_class_t);
@@ -149,34 +134,24 @@ static int store_key(struct wireless_lan *);
static dladm_wlan_key_t *retrieve_key(const char *, const char *,
dladm_secobj_class_t);
-static boolean_t add_wlan_entry(struct interface *, char *, char *, char *,
- dladm_wlan_secmode_t);
-static boolean_t already_in_visited_wlan_list(const struct wireless_lan *);
-static boolean_t check_wlan(struct interface *, const char *);
-static boolean_t connect_or_autoconf(struct wireless_lan *, struct interface *);
-static return_vals_t connect_to_new_wlan(const struct wireless_lan *, int,
- struct interface *);
-static boolean_t find_wlan_entry(struct interface *, char *, char *);
+static struct wireless_lan *add_wlan_entry(const char *, const char *,
+ const char *, dladm_wlan_attr_t *);
+static boolean_t check_wlan(const wireless_if_t *, const char *, const char *,
+ boolean_t);
+static return_vals_t connect_or_autoconf(struct wireless_lan *,
+ wireless_if_t *);
+static struct wireless_lan *find_wlan_entry(const char *, const char *,
+ const char *);
static void free_wireless_lan(struct wireless_lan *);
-static struct wireless_lan *get_specific_lan(void);
-static void get_user_key(struct wireless_lan *);
-static int get_user_preference(char *const *, const char *, const char *,
- struct wireless_lan **, const struct wireless_lan *);
-static char **alloc_argv(int, size_t);
-static void free_argv(char **);
-static char **build_wlanlist_zargv(const struct wireless_lan *, int,
- const char *, int, const char **, int);
-static char *get_zenity_response(char *const *);
-static boolean_t wlan_autoconf(struct interface *);
-static int zenity_height(int);
+static return_vals_t get_user_key(struct wireless_lan *);
+static boolean_t wlan_autoconf(const wireless_if_t *);
static boolean_t get_scan_results(void *, dladm_wlan_attr_t *);
+static int add_known_wifi_nets_file(const char *, const char *);
static boolean_t known_wifi_nets_lookup(const char *, const char *, char *);
-
+static return_vals_t connect_chosen_lan(struct wireless_lan *, wireless_if_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.
@@ -189,167 +164,116 @@ uint_t wlan_scan_interval = 120;
*/
dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK;
-/*
- * Some constants for zenity args
- */
-/*
- * For a wlan table, command begins with 5 general args:
- * cmdname (argv[0]), window type (list), title, height, and width.
- */
-#define ZENITY_LIST_INIT_ARGS 5
-/*
- * Columns: index, ESSID, BSSID, Encryption Type, Signal Strength
- */
-#define ZENITY_COLUMNS_PER_WLAN 5
-/*
- * Cap the length of an individual arg at 64 bytes.
- * Longest args tend to be extra row strings.
- */
-#define ZENITY_ARG_LEN 64
-/*
- * Typical zenity return buffers are index number strings
- * or extra row strings; 1024 should be sufficient.
- */
-#define ZENITY_RTN_BUF_SIZE 1024
-
-/*
- * Alloc an array of (cnt + 1) pointers, where the first cnt pointers
- * point to an alloc'd buffer of specified len. The last pointer in
- * the array is NULL.
- */
-static char **
-alloc_argv(int cnt, size_t buflen)
+void
+initialize_wireless(void)
{
- int i;
- char **argv;
+ pthread_mutexattr_t wifi_mutex_attr;
- if ((argv = calloc(cnt + 1, sizeof (char *))) == NULL) {
- syslog(LOG_ERR, "calloc failed: %m");
- return (NULL);
- }
- for (i = 0; i < cnt; i++) {
- if ((argv[i] = malloc(buflen)) == NULL) {
- syslog(LOG_ERR, "malloc failed: %m");
- free_argv(argv);
- return (NULL);
+ (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);
+ wi_list.q_forw = wi_list.q_back = &wi_list;
+}
+
+void
+add_wireless_if(const char *ifname)
+{
+ wireless_if_t *wip;
+
+ if ((wip = calloc(1, sizeof (*wip))) != NULL) {
+ (void) strlcpy(wip->wi_name, ifname, sizeof (wip->wi_name));
+ (void) dladm_name2info(ifname, &wip->wi_linkid, NULL, NULL,
+ NULL);
+ if (pthread_mutex_lock(&wifi_mutex) == 0) {
+ insque(&wip->wi_links, wi_list.q_back);
+ wi_link_count++;
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ } else {
+ free(wip);
}
}
- argv[cnt] = NULL;
-
- return (argv);
}
-/*
- * Free an argv. Assumes that the first NULL pointer encountered
- * indicates the end of the array: that is, that the array is null-
- * terminated and that no other elements are NULL.
- */
-static void
-free_argv(char **argv)
+static wireless_if_t *
+find_wireless_if(const char *ifname)
{
- int i = 0;
-
- if (argv == NULL)
- return;
+ wireless_if_t *wip;
- while (argv[i] != NULL)
- free(argv[i++]);
- free(argv);
+ for (wip = (wireless_if_t *)wi_list.q_forw;
+ wip != (wireless_if_t *)&wi_list;
+ wip = (wireless_if_t *)wip->wi_links.q_forw) {
+ if (strcmp(wip->wi_name, ifname) == 0)
+ return (wip);
+ }
+ return (NULL);
}
void
-init_mutexes(void)
+remove_wireless_if(const char *ifname)
{
- (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);
+ wireless_if_t *wip;
+
+ if (pthread_mutex_lock(&wifi_mutex) == 0) {
+ if ((wip = find_wireless_if(ifname)) != NULL) {
+ remque(&wip->wi_links);
+ wi_link_count--;
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ free(wip);
+ }
}
/*
* wlan is expected to be non-NULL.
*/
-static void
+static return_vals_t
get_user_key(struct wireless_lan *wlan)
{
dladm_secobj_class_t class;
- int zargc, cur;
- char **zargv;
/*
* First, test if we have key stored as secobj. If so,
* no need to prompt for it.
*/
- class = (wlan->sec_mode == DLADM_WLAN_SECMODE_WEP ?
+ class = (wlan->attrs.wa_secmode == DLADM_WLAN_SECMODE_WEP ?
DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
wlan->cooked_key = retrieve_key(wlan->essid, wlan->bssid, class);
if (wlan->cooked_key != NULL) {
dprintf("get_user_key: retrieve_key() returns non NULL");
- return;
- }
-
- if (!valid_graphical_user(B_TRUE))
- return;
-
- /*
- * build zenity 'entry' argv, with text hidden:
- * 'zenity --entry --title=foo --text=bar --hide-text'
- * Five args needed.
- */
- zargc = 5;
- if ((zargv = alloc_argv(zargc, ZENITY_ARG_LEN)) == NULL)
- return;
-
- cur = 0;
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, ZENITY);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--entry");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--title=%s",
- gettext("Enter Key"));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--text=%s %s",
- gettext("Enter key for WiFi network"), wlan->essid);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--hide-text");
-
- wlan->raw_key = get_zenity_response(zargv);
- if (wlan->raw_key == NULL) {
- dprintf("get_user_key: failed to obtain user-specified key");
- goto cleanup;
- }
-
- /* Store key persistently */
- if (store_key(wlan) != 0) {
- syslog(LOG_ERR, "get_user_key: failed to store user-specified "
- "key");
+ return (SUCCESS);
+ } else if (request_wlan_key(wlan)) {
+ return (WAITING);
+ } else {
+ return (FAILURE);
}
-
-cleanup:
- free_argv(zargv);
}
-static boolean_t
-find_wlan_entry(struct interface *intf, char *essid, char *bssid)
+/*
+ * This function assumes that wifi_mutex is held. If bssid is specified, then
+ * an exact match is returned. If it's not specified, then the best match is
+ * returned.
+ */
+static struct wireless_lan *
+find_wlan_entry(const char *ifname, const char *essid, const char *bssid)
{
- int i;
+ struct wireless_lan *wlan, *best;
- (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);
+ best = NULL;
+ for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
+ if (strcmp(wlan->essid, essid) != 0 ||
+ strcmp(wlan->wl_if_name, ifname) != 0)
+ continue;
+ if (bssid[0] == '\0') {
+ if (best == NULL ||
+ wlan->attrs.wa_strength > best->attrs.wa_strength)
+ best = wlan;
+ } else {
+ if (strcmp(wlan->bssid, bssid) == 0)
+ return (wlan);
}
}
- (void) pthread_mutex_unlock(&wifi_mutex);
- return (B_FALSE);
+ return (best);
}
static void
@@ -357,7 +281,9 @@ free_wireless_lan(struct wireless_lan *wlp)
{
free(wlp->essid);
wlp->essid = NULL;
- free(wlp->bssid);
+ /* empty string is not allocated */
+ if (wlp->bssid != NULL && wlp->bssid[0] != '\0')
+ free(wlp->bssid);
wlp->bssid = NULL;
free(wlp->signal_strength);
wlp->signal_strength = NULL;
@@ -365,189 +291,435 @@ free_wireless_lan(struct wireless_lan *wlp)
wlp->raw_key = NULL;
free(wlp->cooked_key);
wlp->cooked_key = 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, dladm_wlan_secmode_t sec)
+/*
+ * This function assumes that wifi_mutex is held.
+ */
+static struct wireless_lan *
+add_wlan_entry(const char *ifname, const char *essid, const char *bssid,
+ dladm_wlan_attr_t *attrp)
{
- int n;
-
- (void) pthread_mutex_lock(&wifi_mutex);
+ char strength[DLADM_STRSIZE];
+ struct wireless_lan *wlan;
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) {
+ wlan = realloc(wlans, newcnt * sizeof (*wlans));
+ if (wlan == NULL) {
syslog(LOG_ERR, "add_wlan_entry: realloc failed");
- (void) pthread_mutex_unlock(&wifi_mutex);
- return (B_FALSE);
+ return (NULL);
}
- (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].sec_mode = sec;
- wlans[n].raw_key = NULL;
- wlans[n].cooked_key = NULL;
- if (wlans[n].essid == NULL || wlans[n].bssid == NULL ||
- wlans[n].signal_strength == NULL || wlans[n].wl_if_name == NULL) {
+ wlans = wlan;
+ }
+
+ (void) dladm_wlan_strength2str(&attrp->wa_strength, strength);
+
+ wlan = wlans + wireless_lan_used;
+ (void) memset(wlan, 0, sizeof (*wlan));
+ wlan->attrs = *attrp;
+ wlan->essid = strdup(essid);
+ /* do not do allocation for zero-length */
+ wlan->bssid = *bssid == '\0' ? "" : strdup(bssid);
+ wlan->signal_strength = strdup(strength);
+ (void) strlcpy(wlan->wl_if_name, ifname, sizeof (wlan->wl_if_name));
+ wlan->scanned = B_TRUE;
+ if (wlan->essid == NULL || wlan->bssid == NULL ||
+ wlan->signal_strength == NULL) {
syslog(LOG_ERR, "add_wlan_entry: strdup failed");
- free_wireless_lan(&(wlans[n]));
- (void) pthread_mutex_unlock(&wifi_mutex);
- return (B_FALSE);
+ free_wireless_lan(wlan);
+ return (NULL);
}
wireless_lan_used++;
- (void) pthread_mutex_unlock(&wifi_mutex);
- return (B_TRUE);
+ new_ap_found = B_TRUE;
+ return (wlan);
}
-static void
-clear_lan_entries(void)
+/*
+ * Remove entries that are no longer seen on the network. The caller does not
+ * hold wifi_mutex, but is the only thread that can modify the wlan list.
+ * Retain connected entries, as lack of visibility in a scan may just be a
+ * temporary condition (driver problem) and may not reflect an actual
+ * disconnect.
+ */
+static boolean_t
+clear_unscanned_entries(const char *ifname)
{
- int i;
+ struct wireless_lan *wlan, *wlput;
+ boolean_t dropped;
- (void) pthread_mutex_lock(&wifi_mutex);
- for (i = 0; i < wireless_lan_used; i++)
- free_wireless_lan(&(wlans[i]));
- wireless_lan_used = 0;
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ return (B_FALSE);
+ wlput = wlans;
+ dropped = B_FALSE;
+ for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
+ if (strcmp(ifname, wlan->wl_if_name) != 0 || wlan->scanned ||
+ wlan->connected) {
+ if (wlput != wlan)
+ *wlput = *wlan;
+ wlput++;
+ } else {
+ dprintf("dropping unseen AP %s %s", wlan->essid,
+ wlan->bssid);
+ dropped = B_TRUE;
+ free_wireless_lan(wlan);
+ }
+ }
+ wireless_lan_used = wlput - wlans;
(void) pthread_mutex_unlock(&wifi_mutex);
+ return (dropped);
}
/*
- * 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.
+ * Verify if a WiFi NIC is associated with the given ESSID and BSSID. 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.
+ * 1. If the NIC is associated with the given ESSID/BSSID, 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.
+ * 3. If the NIC is associated with a different AP, tear down IP interface,
+ * tell the driver to disassociate with AP, and then return false.
*/
static boolean_t
-check_wlan(struct interface *intf, const char *exp_essid)
+check_wlan(const wireless_if_t *wip, const char *exp_essid,
+ const char *exp_bssid, boolean_t sendevent)
{
dladm_wlan_linkattr_t attr;
dladm_status_t status;
char cur_essid[DLADM_STRSIZE];
+ char cur_bssid[DLADM_STRSIZE];
char errmsg[DLADM_STRSIZE];
- status = dladm_wlan_get_linkattr(intf->if_linkid, &attr);
+ status = dladm_wlan_get_linkattr(wip->wi_linkid, &attr);
if (status != DLADM_STATUS_OK) {
dprintf("check_wlan: dladm_wlan_get_linkattr() for %s "
- "failed: %s", intf->if_name,
+ "failed: %s", wip->wi_name,
dladm_status2str(status, errmsg));
return (B_FALSE);
}
if (attr.la_status == DLADM_WLAN_LINK_DISCONNECTED)
return (B_FALSE);
+
+ /* If we're expecting "any" connection, then we're done. */
if (exp_essid == NULL)
return (B_TRUE);
+
+ /* Is the NIC associated with the expected access point? */
(void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid, cur_essid);
+ if (strcmp(cur_essid, exp_essid) != 0) {
+ dprintf("wrong ESSID: have %s expect %s; taking down",
+ cur_essid, exp_essid);
+ goto unexpected;
+ }
+
+ if (exp_bssid == NULL)
+ return (B_TRUE);
- /* Is the NIC associated with the expected one? */
- if (strcmp(cur_essid, exp_essid) == 0)
+ (void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid, cur_bssid);
+ if (strcmp(cur_bssid, exp_bssid) == 0)
return (B_TRUE);
+ dprintf("wrong BSSID: have %s expect %s; taking down",
+ cur_bssid, exp_bssid);
- /* Tell the driver to disassociate with the current AP. */
- if (dladm_wlan_disconnect(intf->if_linkid) != DLADM_STATUS_OK) {
- dprintf("check_wlan: dladm_wlan_disconnect() for %s fails",
- intf->if_name);
+unexpected:
+ if (sendevent) {
+ /* If not, then shut the interface down normally */
+ (void) np_queue_add_event(EV_TAKEDOWN, wip->wi_name);
+ (void) dladm_wlan_disconnect(wip->wi_linkid);
}
return (B_FALSE);
}
/*
- * Given a wireless interface, use it to scan for available networks.
+ * Examine all WLANs associated with an interface, and update the 'connected'
+ * and 'known' attributes appropriately. The caller holds wifi_mutex.
*/
-boolean_t
-scan_wireless_nets(struct interface *intf)
+static boolean_t
+update_connected_wlan(wireless_if_t *wip)
{
- boolean_t new_ap = B_FALSE;
+ dladm_wlan_linkattr_t attr;
+ struct wireless_lan *wlan, *lastconn, *newconn;
+ char essid[DLADM_STRSIZE];
+ char bssid[DLADM_STRSIZE];
+ boolean_t connected, wasconn;
+ int retries = 0;
+
+ /*
+ * This is awful, but some wireless drivers (particularly 'ath') will
+ * erroneously report "disconnected" if queried right after a scan. If
+ * we see 'down' reported here, we retry a few times to make sure.
+ */
+ while (retries++ < 4) {
+ if (dladm_wlan_get_linkattr(wip->wi_linkid, &attr) !=
+ DLADM_STATUS_OK)
+ attr.la_status = DLADM_WLAN_LINK_DISCONNECTED;
+ else if (attr.la_status == DLADM_WLAN_LINK_CONNECTED)
+ break;
+ }
+ if (attr.la_status == DLADM_WLAN_LINK_CONNECTED) {
+ (void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid, essid);
+ (void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid, bssid);
+ connected = B_TRUE;
+ wip->wi_wireless_done = B_TRUE;
+ dprintf("update: %s is connected to %s %s", wip->wi_name, essid,
+ bssid);
+ } else {
+ connected = B_FALSE;
+ dprintf("update: %s is currently unconnected", wip->wi_name);
+ }
+ wasconn = B_FALSE;
+ lastconn = newconn = NULL;
+ for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
+ if (strcmp(wlan->wl_if_name, wip->wi_name) != 0)
+ continue;
+ if (connected && strcmp(wlan->essid, essid) == 0 &&
+ strcmp(wlan->bssid, bssid) == 0) {
+ wasconn = wlan->connected;
+ wlan->connected = connected;
+ newconn = wlan;
+ } else {
+ if (wlan->connected)
+ lastconn = wlan;
+ wlan->connected = B_FALSE;
+ }
+ }
+ if (newconn == NULL && connected) {
+ newconn = add_wlan_entry(wip->wi_name, essid, bssid,
+ &attr.la_wlan_attr);
+ if (newconn != NULL)
+ newconn->connected = connected;
+ }
+ if (lastconn != NULL)
+ report_wlan_disconnect(lastconn);
+ if (newconn != NULL && !wasconn && connected) {
+ /*
+ * If we're already connected but not yet known, then it's
+ * clear that this is a "known wlan" for the user. He must
+ * have issued a dladm connect-wifi command to get here.
+ */
+ if (!newconn->known) {
+ newconn->known = B_TRUE;
+ (void) add_known_wifi_nets_file(newconn->essid,
+ newconn->bssid);
+ }
+ report_wlan_connected(newconn);
+ }
+ return (connected);
+}
+
+/*
+ * Given a wireless interface, use it to scan for available networks. The
+ * caller must not hold wifi_mutex.
+ */
+static void
+scan_wireless_nets(const char *ifname)
+{
+ boolean_t already_done;
+ boolean_t dropped;
+ boolean_t new_found;
dladm_status_t status;
- int num_ap;
+ int i;
+ datalink_id_t linkid;
+ wireless_if_t *wip;
- 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.
+ * If there is already a scan in progress, defer until the scan is done
+ * to avoid radio interference. But if the interface doing the scan is
+ * the same as the one requesting the new scan, then wait for it to
+ * finish, and then we're done.
*/
- (void) pthread_mutex_lock(&wifi_init_mutex);
- while (wifi_scan_intf != NULL) {
+ if (pthread_mutex_lock(&wifi_init_mutex) != 0)
+ return;
+ already_done = B_FALSE;
+ while (wifi_scan_intf[0] != '\0') {
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) {
+ wifi_scan_intf, ifname);
+ if (strcmp(wifi_scan_intf, ifname) == 0)
+ already_done = B_TRUE;
+ (void) pthread_cond_wait(&wifi_init_cond, &wifi_init_mutex);
+ if (already_done) {
(void) pthread_mutex_unlock(&wifi_init_mutex);
- return (B_FALSE);
+ return;
}
- (void) pthread_cond_wait(&wifi_init_cond, &wifi_init_mutex);
}
- wifi_scan_intf = intf;
+ (void) strlcpy(wifi_scan_intf, ifname, sizeof (wifi_scan_intf));
(void) pthread_mutex_unlock(&wifi_init_mutex);
+ /* Grab the linkid from the wireless interface */
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ goto scan_end;
+ if ((wip = find_wireless_if(ifname)) == NULL) {
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ dprintf("aborted scan on %s; unable to locate interface",
+ ifname);
+ goto scan_end;
+ }
+ linkid = wip->wi_linkid;
+ (void) pthread_mutex_unlock(&wifi_mutex);
+
/*
- * Since only one scan is allowed at any one time, no need to grab
- * a lock in checking wireless_lan_used.
+ * Since only one scan is allowed at any one time, and only scans can
+ * modify the list, there's no need to grab a lock in checking
+ * wireless_lan_used or the wlans list itself, or for the new_ap_found
+ * global.
+ *
+ * All other threads must hold the mutex when reading this data, and
+ * this thread must hold the mutex only when writing portions that
+ * those other threads may read.
*/
- num_ap = wireless_lan_used;
- status = dladm_wlan_scan(intf->if_linkid, 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);
+ for (i = 0; i < wireless_lan_used; i++)
+ wlans[i].scanned = B_FALSE;
+ new_ap_found = B_FALSE;
+ dprintf("starting scan on %s", ifname);
+ status = dladm_wlan_scan(linkid, (char *)ifname, get_scan_results);
+ if (status == DLADM_STATUS_OK) {
+ dropped = clear_unscanned_entries(ifname);
+ } else {
+ dropped = B_FALSE;
+ syslog(LOG_NOTICE, "cannot scan link '%s'", ifname);
+ }
+
+scan_end:
+ /* Need to sample this global before clearing out scan lock */
+ new_found = new_ap_found;
+
+ if (pthread_mutex_lock(&wifi_mutex) == 0) {
+ if ((wip = find_wireless_if(ifname)) != NULL) {
+ wip->wi_scan_running = B_FALSE;
+ (void) update_connected_wlan(wip);
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ }
(void) pthread_mutex_lock(&wifi_init_mutex);
- wifi_scan_intf = NULL;
- (void) pthread_cond_signal(&wifi_init_cond);
+ wifi_scan_intf[0] = '\0';
+ (void) pthread_cond_broadcast(&wifi_init_cond);
(void) pthread_mutex_unlock(&wifi_init_mutex);
- return (new_ap);
+ if (status == DLADM_STATUS_OK)
+ report_scan_complete(ifname, dropped || new_found, wlans,
+ wireless_lan_used);
+
+ if (new_found) {
+ dprintf("new AP added: %s", ifname);
+ (void) np_queue_add_event(EV_NEWAP, ifname);
+ }
}
-/* ARGSUSED */
+/*
+ * Rescan all wireless interfaces. This routine intentionally does not hold
+ * wifi_mutex during the scan, as scans can take a long time to accomplish, and
+ * there may be more than one wireless interface. The counter is used to make
+ * sure that we don't run "forever" if the list is changing quickly.
+ */
static void
-wireless_scan(struct interface *ifp, void *arg)
+rescan_wifi_no_lock(void)
{
- 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);
- }
+ uint_t cnt = 0;
+ wireless_if_t *wip;
+ char ifname[LIFNAMSIZ];
+
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ return;
+ wip = (wireless_if_t *)wi_list.q_forw;
+ while (cnt++ < wi_link_count && wip != (wireless_if_t *)&wi_list) {
+ (void) strlcpy(ifname, wip->wi_name, sizeof (ifname));
+ dprintf("periodic wireless scan: %s", ifname);
+ /* Even less than "very weak" */
+ wip->wi_strength = 0;
+ wip->wi_scan_running = B_TRUE;
+ (void) pthread_mutex_unlock(&wifi_mutex);
+
+ scan_wireless_nets(ifname);
+
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ return;
+ if ((wip = find_wireless_if(ifname)) == NULL)
+ wip = (wireless_if_t *)&wi_list;
+ else
+ wip = (wireless_if_t *)wip->wi_links.q_forw;
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+}
+
+/*
+ * This thread is given the name of the interface to scan, and must free that
+ * name when done.
+ */
+static void *
+scan_thread(void *arg)
+{
+ char *ifname = arg;
+
+ scan_wireless_nets(ifname);
+ free(ifname);
+
+ return (NULL);
+}
+
+/*
+ * Launch a thread to scan the given wireless interface. We copy the interface
+ * name over to allocated storage because it's not possible to hand off a lock
+ * on the interface list to the new thread, and the caller's storage (our input
+ * argument) isn't guaranteed to be stable after we return to the caller.
+ */
+int
+launch_wireless_scan(const char *ifname)
+{
+ int retv;
+ wireless_if_t *wip;
+ pthread_t if_thr;
+ pthread_attr_t attr;
+ char *winame;
+
+ if ((winame = strdup(ifname)) == NULL)
+ return (ENOMEM);
+
+ if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0) {
+ free(winame);
+ return (retv);
}
+
+ if ((wip = find_wireless_if(ifname)) == NULL) {
+ retv = ENXIO;
+ } else if (wip->wi_scan_running) {
+ retv = EINPROGRESS;
+ } else {
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr,
+ PTHREAD_CREATE_DETACHED);
+ retv = pthread_create(&if_thr, &attr, scan_thread, winame);
+ if (retv == 0)
+ wip->wi_scan_running = B_TRUE;
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+
+ /* If thread not started, then discard the name. */
+ if (retv != 0)
+ free(winame);
+
+ return (retv);
}
+/*
+ * Caller does not hold wifi_mutex.
+ */
static boolean_t
get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
{
- dladm_wlan_secmode_t sec;
+ const char *ifname = arg;
+ wireless_if_t *wip;
+ struct wireless_lan *wlan;
char essid_name[DLADM_STRSIZE];
char bssid_name[DLADM_STRSIZE];
- char strength[DLADM_STRSIZE];
+ boolean_t retv;
(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);
-
- sec = attrp->wa_secmode;
/*
* Check whether ESSID is "hidden".
@@ -556,25 +728,48 @@ get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
*/
if (essid_name[0] == '\0') {
if (known_wifi_nets_lookup(essid_name, bssid_name,
- essid_name)) {
+ essid_name) &&
+ dladm_wlan_str2essid(essid_name, &attrp->wa_essid) ==
+ DLADM_STATUS_OK) {
dprintf("Using ESSID %s with BSSID %s",
essid_name, bssid_name);
}
}
- if (!find_wlan_entry(arg, essid_name, bssid_name) &&
- add_wlan_entry(arg, essid_name, bssid_name, strength, sec)) {
- return (B_TRUE);
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ return (B_FALSE);
+
+ if ((wip = find_wireless_if(ifname)) == NULL) {
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ return (B_FALSE);
}
- return (B_FALSE);
+
+ /* Remember the strongest we encounter */
+ if (attrp->wa_strength > wip->wi_strength)
+ wip->wi_strength = attrp->wa_strength;
+
+ wlan = find_wlan_entry(ifname, essid_name, bssid_name);
+ if (wlan != NULL) {
+ if (wlan->rescan)
+ new_ap_found = B_TRUE;
+ wlan->rescan = B_FALSE;
+ wlan->scanned = B_TRUE;
+ wlan->attrs = *attrp;
+ retv = B_TRUE;
+ } else if (add_wlan_entry(ifname, essid_name, bssid_name, attrp) !=
+ NULL) {
+ retv = B_TRUE;
+ } else {
+ retv = B_FALSE;
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ return (retv);
}
/* ARGSUSED */
void *
periodic_wireless_scan(void *arg)
{
- datalink_id_t linkid;
-
/*
* No periodic scan if the "-i" option is used to change the
* interval to 0.
@@ -585,73 +780,94 @@ periodic_wireless_scan(void *arg)
for (;;) {
int ret;
dladm_wlan_linkattr_t attr;
- llp_t *cur_llp;
- struct interface *ifp;
+ char ifname[LIFNAMSIZ];
+ libnwam_interface_type_t ift;
+ datalink_id_t linkid;
ret = poll(NULL, 0, wlan_scan_interval * MILLISEC);
+ if (ret == -1) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_INFO, "periodic_wireless_scan: poll failed");
+ break;
+ }
- /*
- * 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;
+ /* Get current profile name, if any */
+ llp_get_name_and_type(ifname, sizeof (ifname), &ift);
/*
* We do a scan if
*
* 1. There is no active profile. Or
- * 2. We are now disconnected from the AP. Or
+ * 2. Profile is wireless and we're not connected to 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)
- continue;
+ if (ifname[0] != '\0') {
+ if (ift != IF_WIRELESS)
+ continue;
- if (dladm_name2info(cur_llp->llp_lname, &linkid,
- NULL, NULL, NULL) != DLADM_STATUS_OK ||
- dladm_wlan_get_linkattr(linkid, &attr) !=
- DLADM_STATUS_OK) {
- continue;
- }
+ /*
+ * If these things fail, it means that our wireless
+ * link isn't viable. Proceed in that way.
+ */
+ if (dladm_name2info(ifname, &linkid, NULL, NULL,
+ NULL) != DLADM_STATUS_OK ||
+ dladm_wlan_get_linkattr(linkid, &attr) !=
+ DLADM_STATUS_OK) {
+ attr.la_status = DLADM_WLAN_LINK_DISCONNECTED;
+ attr.la_wlan_attr.wa_strength = 0;
+ }
+
+ if (attr.la_status == DLADM_WLAN_LINK_CONNECTED &&
+ attr.la_wlan_attr.wa_strength >
+ wireless_scan_level) {
+ continue;
+ }
+ }
+
+ /* Rescan the wireless interfaces */
+ rescan_wifi_no_lock();
+ if (ifname[0] != '\0') {
+ wireless_if_t *wip;
+
+ /*
+ * If we're still connected and there's nothing better
+ * around, then there's no point in switching now.
+ */
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ continue;
+ if ((wip = find_wireless_if(ifname)) != NULL) {
if (attr.la_status ==
DLADM_WLAN_LINK_CONNECTED &&
- attr.la_wlan_attr.wa_strength >
- wireless_scan_level) {
+ wip->wi_strength <=
+ attr.la_wlan_attr.wa_strength) {
+ (void) pthread_mutex_unlock(&
+ wifi_mutex);
continue;
}
- /*
- * Clear the DHCP 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_DHCPFLAGS;
-
- /*
- * 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);
+ (void) pthread_mutex_unlock(&wifi_mutex);
+
+ /*
+ * Try to work around known driver bugs: if the driver
+ * says we're disconnected, then tell it to disconnect
+ * for sure.
+ */
+ (void) dladm_wlan_disconnect(linkid);
+
+ /*
+ * Deactivate the original AP. 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.
+ */
+ (void) np_queue_add_event(
+ (attr.la_status == DLADM_WLAN_LINK_CONNECTED ?
+ EV_LINKFADE : EV_LINKDISC), ifname);
}
}
- /* NOTREACHED */
return (NULL);
}
@@ -674,9 +890,8 @@ key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
dprintf("before: key_string_to_secobj_value: buf_len = %d", buf_len);
if (buf_len == 0) {
- syslog(LOG_ERR,
- "key_string_to_secobj_value: empty key");
- return (-1);
+ /* length zero means "delete" */
+ return (0);
}
if (buf[buf_len - 1] == '\n')
@@ -738,13 +953,17 @@ key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
* 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.
+ *
+ * (Note that much of the system uses DLADM_WLAN_MAX_KEYNAME_LEN, which is 64
+ * rather than 32, but that dladm_get_secobj will fail if a length greater than
+ * DLD_SECOBJ_NAME_MAX is seen, and that's 32. This is all horribly broken.)
*/
static void
set_key_name(const char *essid, const char *bssid, char *name, size_t nsz)
{
int i, rtn, len;
- if (bssid == NULL)
+ if (bssid[0] == '\0')
rtn = snprintf(name, nsz, "nwam-%s", essid);
else
rtn = snprintf(name, nsz, "nwam-%s-%s", essid, bssid);
@@ -771,7 +990,7 @@ store_key(struct wireless_lan *wlan)
set_key_name(wlan->essid, wlan->bssid, obj_name, sizeof (obj_name));
dprintf("store_key: obj_name is %s", obj_name);
- class = (wlan->sec_mode == DLADM_WLAN_SECMODE_WEP ?
+ class = (wlan->attrs.wa_secmode == DLADM_WLAN_SECMODE_WEP ?
DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
if (key_string_to_secobj_value(wlan->raw_key, obj_val, &obj_len,
class) != 0) {
@@ -779,6 +998,20 @@ store_key(struct wireless_lan *wlan)
return (-1);
}
+ /* we've validated the new key, so remove the old one */
+ status = dladm_unset_secobj(obj_name,
+ DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
+ if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND) {
+ syslog(LOG_ERR, "store_key: could not remove old secure object "
+ "'%s' for key: %s", obj_name,
+ dladm_status2str(status, errmsg));
+ return (-1);
+ }
+
+ /* if we're just deleting the key, then we're done */
+ if (wlan->raw_key[0] == '\0')
+ return (0);
+
status = dladm_set_secobj(obj_name, class,
obj_val, obj_len,
DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE);
@@ -825,9 +1058,12 @@ retrieve_key(const char *essid, const char *bssid, dladm_secobj_class_t req)
return (NULL);
}
- /* Set name appropriately to retrieve key for this WLAN */
- set_key_name(essid, bssid, cooked_key->wk_name,
- DLADM_SECOBJ_NAME_MAX);
+ /*
+ * Set name appropriately to retrieve key for this WLAN. Note that we
+ * cannot use the actual wk_name buffer size, as it's two times too
+ * large for dladm_get_secobj.
+ */
+ set_key_name(essid, bssid, cooked_key->wk_name, DLADM_SECOBJ_NAME_MAX);
dprintf("retrieve_key: len = %d, object = %s\n",
strlen(cooked_key->wk_name), cooked_key->wk_name);
cooked_key->wk_len = sizeof (cooked_key->wk_val);
@@ -875,66 +1111,111 @@ retrieve_key(const char *essid, const char *bssid, dladm_secobj_class_t req)
return (cooked_key);
}
-/* Create the KNOWN_WIFI_NETS using info from the interface list. */
-void
-create_known_wifi_nets_file(void)
+/*
+ * Add an entry to known_wifi_nets file given the parameters. The caller holds
+ * wifi_mutex.
+ */
+static int
+add_known_wifi_nets_file(const char *essid, const char *bssid)
{
- FILE *fp;
- int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ int retv;
+ FILE *fp = NULL;
+
+ dprintf("add_known_wifi_nets_file(%s, %s)", essid, bssid);
/* 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;
+ if (mkdir(LLPDIRNAME, LLPDIRMODE) != 0 &&
+ errno != EEXIST) {
+ retv = errno;
+ syslog(LOG_ERR, "could not create %s: %m", LLPDIRNAME);
+ } else if ((fp = fopen(KNOWN_WIFI_NETS, "a+")) == NULL) {
+ retv = errno;
+ syslog(LOG_ERR, "fopen(%s) failed: %m", KNOWN_WIFI_NETS);
+ } else if (known_wifi_nets_lookup(essid, bssid, NULL)) {
+ retv = EEXIST;
+ } else {
+ /* now add this to the file */
+ (void) fprintf(fp, "%s\t%s\n", essid, bssid);
+ retv = 0;
}
- dprintf("Creating %s", KNOWN_WIFI_NETS);
- (void) fclose(fp);
+ if (fp != NULL)
+ (void) fclose(fp);
+ return (retv);
}
-/*
- * Add an entry to known_wifi_nets file given the parameters.
- */
-void
-update_known_wifi_nets_file(const char *essid, const char *bssid)
+static int
+delete_known_wifi_nets_file(const char *essid, const char *bssid)
{
- FILE *fp;
+ FILE *fpin, *fpout;
+ char line[LINE_MAX];
+ char *cp;
+ int retv;
+ size_t essidlen, bssidlen;
+ boolean_t found;
- 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 ((fpin = fopen(KNOWN_WIFI_NETS, "r")) == NULL)
+ return (errno);
+
+ if ((fpout = fopen(KNOWN_WIFI_TMP, "w")) == NULL) {
+ retv = errno;
+ (void) fclose(fpin);
+ return (retv);
+ }
+
+ found = B_FALSE;
+ essidlen = strlen(essid);
+ bssidlen = strlen(bssid);
+ while (fgets(line, sizeof (line), fpin) != NULL) {
+ cp = line;
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '#' || *cp == '\0' ||
+ strncmp(essid, cp, essidlen) != 0 ||
+ (cp[essidlen] != '\0' && !isspace(cp[essidlen]))) {
+ (void) fputs(line, fpout);
+ continue;
}
+ /* skip over the essid to examine bssid */
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ while (isspace(*cp))
+ cp++;
+
/*
- * 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...
+ * Deleting with bssid empty means "all entries under this
+ * essid." As a result, deleting a wildcard entry for a bssid
+ * means deleting all entries for that bssid.
*/
- 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;
+
+ if (bssidlen == 0 ||
+ (strncmp(bssid, cp, bssidlen) == 0 &&
+ (cp[bssidlen] == '\0' || isspace(cp[bssidlen])))) {
+ /* delete this entry */
+ found = B_TRUE;
+ continue;
}
+
+ (void) fputs(line, fpout);
}
- /* now see if this info is already in the file */
- if (!known_wifi_nets_lookup(essid, bssid, NULL)) {
- /* now add this to the file */
- (void) fprintf(fp, "%s\t%s\n", essid,
- bssid == NULL ? "" : bssid);
+
+ (void) fclose(fpin);
+ (void) fclose(fpout);
+
+ if (found) {
+ if (rename(KNOWN_WIFI_TMP, KNOWN_WIFI_NETS) == 0) {
+ retv = 0;
+ } else {
+ retv = errno;
+ (void) unlink(KNOWN_WIFI_TMP);
+ }
+ } else {
+ retv = ENXIO;
+ (void) unlink(KNOWN_WIFI_TMP);
}
- (void) fclose(fp);
+
+ return (retv);
}
/*
@@ -959,16 +1240,9 @@ known_wifi_nets_lookup(const char *new_essid, const char *new_bssid,
* 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();
+ fp = fopen(KNOWN_WIFI_NETS, "r");
+ if (fp == NULL)
return (B_FALSE);
- }
for (line_num = 1; fgets(line, sizeof (line), fp) != NULL; line_num++) {
cp = line;
@@ -1012,12 +1286,179 @@ known_wifi_nets_lookup(const char *new_essid, const char *new_bssid,
return (found);
}
+static uint_t
+extract_known_aps(FILE *fp, libnwam_known_ap_t *kap, char *sbuf, size_t *totstr)
+{
+ char line[LINE_MAX];
+ char *cp;
+ char *tok[MAX_FIELDS];
+ size_t accstr = 0;
+ uint_t count = 0;
+ char key[DLADM_SECOBJ_NAME_MAX];
+ uint8_t keyval[DLADM_SECOBJ_VAL_MAX];
+ dladm_secobj_class_t class;
+ uint_t keylen;
+
+ while (fgets(line, sizeof (line), fp) != NULL) {
+ cp = line;
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '#' || *cp == '\0')
+ continue;
+
+ if (bufsplit(cp, MAX_FIELDS, tok) != MAX_FIELDS)
+ continue;
+
+ if (totstr != NULL)
+ accstr += strlen(tok[BSSID]) + strlen(tok[ESSID]) + 2;
+ count++;
+
+ if (kap != NULL) {
+ kap->ka_essid = strcpy(sbuf, tok[ESSID]);
+ sbuf += strlen(sbuf) + 1;
+ kap->ka_bssid = strcpy(sbuf, tok[BSSID]);
+ sbuf += strlen(sbuf) + 1;
+ set_key_name(tok[ESSID], tok[BSSID], key, sizeof (key));
+ keylen = sizeof (keyval);
+ if (dladm_get_secobj(key, &class, keyval, &keylen,
+ DLADM_OPT_ACTIVE) == DLADM_STATUS_OK)
+ kap->ka_haskey = B_TRUE;
+ else
+ kap->ka_haskey = B_FALSE;
+ kap++;
+ }
+ }
+ if (totstr != NULL)
+ *totstr = accstr;
+ return (count);
+}
+
+libnwam_known_ap_t *
+get_known_ap_list(size_t *kasizep, uint_t *countp)
+{
+ FILE *fp;
+ libnwam_known_ap_t *kap = NULL;
+ size_t kasize;
+ uint_t count;
+ int retv;
+
+ if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0) {
+ errno = retv;
+ return (kap);
+ }
+ if ((fp = fopen(KNOWN_WIFI_NETS, "r")) != NULL) {
+ count = extract_known_aps(fp, NULL, NULL, &kasize);
+ rewind(fp);
+ kasize += count * sizeof (*kap);
+ if (count != 0 && (kap = malloc(kasize)) != NULL) {
+ (void) extract_known_aps(fp, kap, (char *)(kap + count),
+ NULL);
+ *kasizep = kasize;
+ *countp = count;
+ }
+ (void) fclose(fp);
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ return (kap);
+}
+
+int
+add_known_ap(const char *essid, const char *bssid)
+{
+ int retv;
+ char ifname[LIFNAMSIZ];
+ libnwam_interface_type_t ift;
+ struct wireless_lan *wlan, *savedwlan;
+
+ /*
+ * First check the current LLP. If there is one, then its connection
+ * state determines what to do after adding the known AP to the list.
+ * If not, then we act if there are no connected APs.
+ */
+ llp_get_name_and_type(ifname, sizeof (ifname), &ift);
+
+ if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0)
+ return (retv);
+
+ retv = add_known_wifi_nets_file(essid, bssid);
+ if (retv == 0 && (ift == IF_UNKNOWN || ift == IF_WIRELESS)) {
+ boolean_t any_connected, one_matches;
+
+ /*
+ * If this is in our list of scanned APs and if no interface is
+ * connected, then we have a reevaluation event.
+ */
+ any_connected = one_matches = B_FALSE;
+ for (wlan = wlans; wlan < wlans + wireless_lan_used;
+ wlan++) {
+ /*
+ * If LLP is selected, then ignore all others. Only
+ * the state of this one interface is at issue.
+ */
+ if (ifname[0] != '\0' &&
+ strcmp(ifname, wlan->wl_if_name) != 0)
+ continue;
+ if (wlan->connected)
+ any_connected = B_TRUE;
+ if (strcmp(essid, wlan->essid) == 0 &&
+ (bssid[0] == '\0' ||
+ strcmp(bssid, wlan->bssid) == 0)) {
+ one_matches = B_TRUE;
+ savedwlan = wlan;
+ }
+ }
+ if (!any_connected && one_matches) {
+ (void) np_queue_add_event(EV_RESELECT,
+ savedwlan->wl_if_name);
+ }
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ return (retv);
+}
+
+int
+delete_known_ap(const char *essid, const char *bssid)
+{
+ int retv;
+ struct wireless_lan *wlan;
+ wireless_if_t *wip;
+
+ if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0)
+ return (retv);
+
+ retv = delete_known_wifi_nets_file(essid, bssid);
+ if (retv == 0) {
+ for (wlan = wlans; wlan < wlans + wireless_lan_used;
+ wlan++) {
+ if (wlan->connected &&
+ strcmp(essid, wlan->essid) == 0 &&
+ (bssid[0] == '\0' ||
+ strcmp(bssid, wlan->bssid) == 0)) {
+ wlan->connected = B_FALSE;
+ report_wlan_disconnect(wlan);
+ wip = find_wireless_if(wlan->wl_if_name);
+ if (wip != NULL) {
+ wip->wi_wireless_done = B_FALSE;
+ wip->wi_need_key = B_FALSE;
+ (void) dladm_wlan_disconnect(wip->
+ wi_linkid);
+ }
+ (void) np_queue_add_event(EV_RESELECT,
+ wlan->wl_if_name);
+ }
+ }
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ return (retv);
+}
+
/*
- * reqlan->essid is required (i.e., cannot be NULL)
- * reqlan->bssid is optional (i.e., may be NULL)
+ * reqlan->essid is required (i.e., cannot be zero-length)
+ * reqlan->bssid is optional (i.e., may be zero-length)
*/
-boolean_t
-connect_chosen_lan(struct wireless_lan *reqlan, struct interface *intf)
+static return_vals_t
+connect_chosen_lan(struct wireless_lan *reqlan, wireless_if_t *wip)
{
uint_t keycount;
dladm_wlan_key_t *key;
@@ -1026,44 +1467,50 @@ connect_chosen_lan(struct wireless_lan *reqlan, struct interface *intf)
uint_t flags = DLADM_WLAN_CONNECT_NOSCAN;
int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
char errmsg[DLADM_STRSIZE];
+ return_vals_t rval;
+
+ wip->wi_need_key = B_FALSE;
(void) memset(&attr, 0, sizeof (attr));
/* try to apply essid selected by the user */
if (reqlan->essid == NULL)
- return (B_FALSE);
+ return (FAILURE);
dprintf("connect_chosen_lan(%s, %s, %s)", reqlan->essid,
- STRING(reqlan->bssid), intf->if_name);
+ reqlan->bssid, wip->wi_name);
/* If it is already connected to the required AP, just return. */
- if (check_wlan(intf, reqlan->essid))
- return (B_TRUE);
+ if (check_wlan(wip, reqlan->essid, NULL, B_TRUE))
+ return (SUCCESS);
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, intf->if_name);
- return (B_FALSE);
+ reqlan->essid, wip->wi_name);
+ return (FAILURE);
}
attr.wa_valid = DLADM_WLAN_ATTR_ESSID;
- if (reqlan->bssid != NULL) {
+ if (reqlan->bssid[0] != '\0') {
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, intf->if_name);
- return (B_FALSE);
+ reqlan->bssid, wip->wi_name);
+ return (FAILURE);
}
attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
}
/* First check for the key */
- if (NEED_ENC(reqlan->sec_mode)) {
- get_user_key(reqlan);
- if (reqlan->cooked_key == NULL)
- return (B_FALSE);
+ if (NEED_ENC(reqlan->attrs.wa_secmode)) {
+ /* Note that this happens only for known APs from the list */
+ if ((rval = get_user_key(reqlan)) != SUCCESS) {
+ if (rval == WAITING)
+ wip->wi_need_key = B_TRUE;
+ return (rval);
+ }
attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
- attr.wa_secmode = reqlan->sec_mode;
+ attr.wa_secmode = reqlan->attrs.wa_secmode;
key = reqlan->cooked_key;
keycount = 1;
dprintf("connect_chosen_lan: retrieved key");
@@ -1078,325 +1525,231 @@ connect_chosen_lan(struct wireless_lan *reqlan, struct interface *intf)
* try a second time with just the ESSID.
*/
- status = dladm_wlan_connect(intf->if_linkid, &attr, timeout, key,
+ status = dladm_wlan_connect(wip->wi_linkid, &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) {
+ if (status == DLADM_STATUS_TIMEDOUT && reqlan->bssid[0] != '\0') {
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(intf->if_linkid, &attr, timeout,
+ status = dladm_wlan_connect(wip->wi_linkid, &attr, timeout,
key, keycount, flags);
}
- if (status != DLADM_STATUS_OK) {
+ if (status == DLADM_STATUS_OK) {
+ return (SUCCESS);
+ } else {
syslog(LOG_ERR,
"connect_chosen_lan: connect to '%s' failed on '%s': %s",
- reqlan->essid, intf->if_name,
+ reqlan->essid, wip->wi_name,
dladm_status2str(status, errmsg));
- return (B_FALSE);
+ return (FAILURE);
}
- 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, struct interface *intf)
+static return_vals_t
+connect_or_autoconf(struct wireless_lan *reqlan, wireless_if_t *wip)
{
- if (!connect_chosen_lan(reqlan, intf)) {
+ return_vals_t rval;
+
+ rval = connect_chosen_lan(reqlan, wip);
+ if (rval == FAILURE) {
+ report_wlan_connect_fail(wip->wi_name);
+ reqlan->rescan = B_TRUE;
syslog(LOG_WARNING,
"Could not connect to chosen WLAN %s, going to auto-conf",
reqlan->essid);
- return (wlan_autoconf(intf));
+ rval = (wlan_autoconf(wip) ? SUCCESS : FAILURE);
}
- return (B_TRUE);
+ return (rval);
}
/*
- * 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.
+ * Check that the wireless LAN is connected to the desired ESSID/BSSID. This
+ * is used by the GUI to check for connectivity before doing anything
+ * destructive.
*/
-static int
-zenity_height(int rows)
-{
- return (((rows + 1) * 24) + 125);
-}
-
-/*
- * Construct an arg vector for zenity which displays a list of wlans.
- * Additional options may be added at the end of the list of wlans with
- * the "extra_rows" arg.
- *
- * Parameters include:
- * lanlist: a linked list of wlans; one row per list node.
- * nlans: the number of nodes in lanlist.
- * title: the string that should be the zenity window title.
- * width: the width of the zenity window.
- * extra_rows: pointer to an array of strings; each string will
- * appear on its own row in the table, in the first column
- * of that row.
- * nrows: the number of extra row strings.
- *
- * A pointer to the arg vector is returned; the caller must free that
- * memory using free_argv().
- */
-static char **
-build_wlanlist_zargv(const struct wireless_lan *lanlist, int nlans,
- const char *title, int width, const char **extra_rows, int nrows)
+boolean_t
+check_wlan_connected(const char *ifname, const char *essid, const char *bssid)
{
- int cur, i, j;
- int zargc, hdrargc, wlanargc;
- char **zargv;
+ wireless_if_t *wip;
+ boolean_t retv;
- /*
- * There are three sections of arguments: the initial args, specifying
- * general formatting info; the column titles (one arg per column);
- * and the row data (one row per wlan, plus any extra rows; and one
- * arg per column per row).
- */
- hdrargc = ZENITY_LIST_INIT_ARGS + ZENITY_COLUMNS_PER_WLAN;
- wlanargc = (nlans + nrows) * ZENITY_COLUMNS_PER_WLAN;
- zargc = hdrargc + wlanargc;
- if ((zargv = alloc_argv(zargc, ZENITY_ARG_LEN)) == NULL)
- return (NULL);
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ return (B_FALSE);
- /* initial args */
- cur = 0;
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, ZENITY);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--list");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--title=%s", title);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--height=%d",
- zenity_height(nlans + nrows));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--width=%d", width);
-
- /* column titles */
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--column=#");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--column=ESSID");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--column=BSSID");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--column=%s",
- gettext("Encryption"));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--column=%s",
- gettext("Signal"));
-
- /* wlan rows */
- for (i = 0; i < nlans; i++) {
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "%d", i + 1);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "%s",
- lanlist[i].essid);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "%s",
- lanlist[i].bssid);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "%s",
- WLAN_ENC(lanlist[i].sec_mode));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "%s",
- lanlist[i].signal_strength);
- }
-
- /* extra rows */
- for (i = 0; i < nrows; i++) {
- /* all columns are empty except the first */
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, extra_rows[i]);
- for (j = 0; j < ZENITY_COLUMNS_PER_WLAN - 1; j++)
- *zargv[cur++] = '\0';
- }
-
- return (zargv);
+ if ((wip = find_wireless_if(ifname)) == NULL) {
+ retv = B_FALSE;
+ } else {
+ if (essid[0] == '\0' && bssid[0] == '\0')
+ essid = NULL;
+ retv = check_wlan(wip, essid, bssid, B_FALSE);
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
+ return (retv);
}
-static return_vals_t
-connect_to_new_wlan(const struct wireless_lan *lanlist, int nlans,
- struct interface *intf)
+/*
+ * This is the entry point for GUI "select access point" requests. We attempt
+ * to do what the GUI requested. If we fail, then there will be a new request
+ * enqueued for the GUI to act on.
+ * Returns:
+ * 0 - ok (or more data requested with new event)
+ * ENXIO - no such interface
+ * ENODEV - requested access point unknown
+ * EINVAL - failed to perform requested action
+ */
+int
+set_specific_lan(const char *ifname, const char *essid, const char *bssid)
{
- int i, dlist_cnt;
- int rtn;
- char **zargv;
- const char *title = gettext("Choose WiFi network you wish to activate");
- const char *extra_rows[2];
- struct wireless_lan *dlist, *reqlan;
- boolean_t autoconf = B_FALSE;
-
- dprintf("connect_to_new_wlan(..., %d, %s)", nlans, intf->if_name);
-
- if (nlans == 0) {
- display(gettext("No Wifi networks found; continuing in case "
- "you know of any which do not broadcast."));
- }
-
- /* build list of wlans to be displayed */
- if ((dlist = calloc(nlans, sizeof (struct wireless_lan))) == NULL)
- return (FAILURE);
+ libnwam_interface_type_t ift;
+ wireless_if_t *wip;
+ struct wireless_lan *wlan, local_wlan;
+ int retv;
+ boolean_t key_wait = B_FALSE;
- for (i = 0, dlist_cnt = 0; i < nlans; 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, intf->if_name) != 0) {
- dprintf("connect_to_new_wlan: wrong interface (%s) for "
- "%s (should be %s)", intf->if_name,
- lanlist[i].essid, lanlist[i].wl_if_name);
- continue;
- }
+ ift = get_if_type(ifname);
+ if (ift != IF_UNKNOWN && ift != IF_WIRELESS)
+ return (EINVAL);
- dlist[dlist_cnt++] = lanlist[i];
- }
+ if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0)
+ return (retv);
- extra_rows[0] = gettext("Other");
- extra_rows[1] = gettext("Rescan");
- /* width = 500 is the result of trial-and-error testing */
- zargv = build_wlanlist_zargv(dlist, dlist_cnt, title, 500, extra_rows,
- sizeof (extra_rows) / sizeof (extra_rows[0]));
- if (zargv == NULL) {
- free(dlist);
- return (FAILURE);
+ if ((wip = find_wireless_if(ifname)) == NULL) {
+ retv = ENXIO;
+ goto done;
}
- /* present list to user and get selection */
- rtn = get_user_preference(zargv, extra_rows[0], extra_rows[1],
- &reqlan, dlist);
- 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);
- rtn = TRY_AGAIN;
- goto cleanup;
- case -1:
- reqlan = NULL;
- break;
- default:
- /* common case: reqlan was set in get_user_preference() */
- break;
+ /* This is an autoconf request. */
+ if (essid[0] == '\0' && bssid[0] == '\0') {
+ retv = (wlan_autoconf(wip) ? 0 : EINVAL);
+ goto done;
}
- if ((reqlan == NULL) || (reqlan->essid == NULL)) {
- dprintf("did not get user preference; attempting autoconf");
- rtn = wlan_autoconf(intf) ? SUCCESS : FAILURE;
- goto cleanup;
+ if ((wlan = find_wlan_entry(ifname, essid, bssid)) == NULL) {
+ local_wlan.essid = (char *)essid;
+ local_wlan.bssid = (char *)bssid;
+ wlan = &local_wlan;
}
- dprintf("get_user_preference() returned essid %s, bssid %s, encr %s",
- reqlan->essid, STRING(reqlan->bssid),
- WLAN_ENC(reqlan->sec_mode));
- /* set key before first time connection */
- if (NEED_ENC(reqlan->sec_mode) && reqlan->raw_key == NULL &&
- reqlan->cooked_key == NULL)
- get_user_key(reqlan);
+ retv = 0;
/*
- * now attempt to connect to selection, backing
- * off to autoconf if the connect fails
+ * now attempt to connect to selection
*/
- if (connect_chosen_lan(reqlan, intf)) {
+ switch (connect_chosen_lan(wlan, wip)) {
+ case WAITING:
+ key_wait = B_TRUE;
+ break;
+
+ case SUCCESS:
/*
* Succeeded, so add entry to known_essid_list_file;
- * but first make sure the reqlan->bssid isn't empty.
+ * but first make sure the wlan->bssid isn't empty.
+ * Note that empty bssid is never allocated.
*/
- if (reqlan->bssid == NULL) {
+ if (wlan->bssid[0] == '\0') {
dladm_status_t status;
dladm_wlan_linkattr_t attr;
- char bssid[DLADM_STRSIZE];
+ char lclbssid[DLADM_STRSIZE];
- status = dladm_wlan_get_linkattr(intf->if_linkid,
+ status = dladm_wlan_get_linkattr(wip->wi_linkid,
&attr);
if (status == DLADM_STATUS_OK) {
(void) dladm_wlan_bssid2str(
- &attr.la_wlan_attr.wa_bssid, bssid);
- reqlan->bssid = strdup(bssid);
+ &attr.la_wlan_attr.wa_bssid, lclbssid);
+ wlan->bssid = strdup(lclbssid);
} else {
dprintf("failed to get linkattr after "
- "connecting to %s", reqlan->essid);
+ "connecting to %s", wlan->essid);
}
}
- 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)
- rtn = wlan_autoconf(intf) ? SUCCESS : FAILURE;
- else
- rtn = SUCCESS;
-
-cleanup:
- free_argv(zargv);
- free(dlist);
-
- return (rtn);
-}
-
-struct wireless_lan *
-prompt_for_visited(void)
-{
- int dlist_cnt;
- char **zargv;
- const char *title = gettext("Choose from pre-visited WiFi network");
- const char *extra_rows[1];
- struct wireless_lan *req_conf, *dlist;
- struct visited_wlans *vlp;
- struct wireless_lan *wlp;
-
- /* build list of wlans to be displayed */
- dlist = calloc(visited_wlan_list->total, sizeof (struct wireless_lan));
- if (dlist == NULL) {
- syslog(LOG_ERR, "prompt_for_visited: calloc failed");
- return (NULL);
- }
- dlist_cnt = 0;
- 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;
+ if (wlan->bssid != NULL && wlan->bssid[0] != '\0') {
+ wlan->known = B_TRUE;
+ (void) add_known_wifi_nets_file(wlan->essid,
+ wlan->bssid);
+ if (wlan == &local_wlan && local_wlan.bssid != bssid)
+ free(local_wlan.bssid);
+ } else {
+ /* Don't leave it as NULL (for simplicity) */
+ wlan->bssid = "";
+ retv = EINVAL;
}
- dlist[dlist_cnt++] = *wlp;
+ break;
+
+ default:
+ retv = EINVAL;
+ break;
}
- extra_rows[0] = gettext("Select from all available WiFi networks");
- /* width = 670 is the result of trial-and-error testing */
- zargv = build_wlanlist_zargv(dlist, dlist_cnt, title, 670, extra_rows,
- sizeof (extra_rows) / sizeof (extra_rows[0]));
- if (zargv == NULL) {
- free(dlist);
- return (NULL);
+done:
+ if (retv == 0 && !update_connected_wlan(wip))
+ retv = EINVAL;
+ if (retv != 0) {
+ /*
+ * Failed to connect. Set 'rescan' flag so that we treat this
+ * AP as new if it's seen again, because the wireless radio may
+ * have just been off briefly while we were trying to connect.
+ */
+ syslog(LOG_WARNING, "Could not connect to chosen WLAN %s",
+ wlan->essid);
+ report_wlan_connect_fail(ifname);
+ wip->wi_need_key = B_FALSE;
+ wip->wi_wireless_done = B_FALSE;
+ wlan->rescan = B_TRUE;
}
+ (void) pthread_mutex_unlock(&wifi_mutex);
/*
- * 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 this is the selected profile, then go ahead and bring up IP now.
*/
- if (get_user_preference(zargv, extra_rows[0], NULL, &req_conf,
- dlist) != 0)
- req_conf = NULL;
+ if (retv == 0 && !key_wait)
+ (void) np_queue_add_event(EV_RESELECT, ifname);
+ return (retv);
+}
- free(dlist);
- free_argv(zargv);
- return (req_conf);
+int
+set_wlan_key(const char *ifname, const char *essid, const char *bssid,
+ const char *key)
+{
+ libnwam_interface_type_t ift;
+ struct wireless_lan *wlan;
+ int retv;
+
+ ift = get_if_type(ifname);
+ if (ift == IF_UNKNOWN)
+ return (ENXIO);
+ if (ift != IF_WIRELESS)
+ return (EINVAL);
+
+ if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0)
+ return (retv);
+
+ if ((wlan = find_wlan_entry(ifname, essid, bssid)) == NULL)
+ retv = ENODEV;
+ else if ((wlan->raw_key = strdup(key)) == NULL)
+ retv = ENOMEM;
+ else if (store_key(wlan) != 0)
+ retv = EINVAL;
+ else
+ retv = 0;
+ (void) pthread_mutex_unlock(&wifi_mutex);
+
+ if (retv == 0)
+ retv = set_specific_lan(ifname, essid, bssid);
+
+ return (retv);
}
static boolean_t
-wlan_autoconf(struct interface *intf)
+wlan_autoconf(const wireless_if_t *wip)
{
dladm_status_t status;
boolean_t autoconf;
@@ -1407,7 +1760,7 @@ wlan_autoconf(struct interface *intf)
}
/* If the NIC is already associated with something, just return. */
- if (check_wlan(intf, NULL))
+ if (check_wlan(wip, NULL, NULL, B_TRUE))
return (B_TRUE);
/*
@@ -1415,348 +1768,75 @@ wlan_autoconf(struct interface *intf)
* to cycle through WLANs detected in priority order, attempting
* to connect.
*/
- status = dladm_wlan_connect(intf->if_linkid, NULL,
+ status = dladm_wlan_connect(wip->wi_linkid, 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",
- intf->if_name, dladm_status2str(status, errmsg));
+ wip->wi_name, 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(char *const *zargv)
-{
- int pfds[2];
- pid_t pid;
- int status, i;
- char inbuf[ZENITY_RTN_BUF_SIZE];
- ssize_t n, bytes_read;
- char *rtnp = NULL;
-
- if (!valid_graphical_user(B_TRUE))
- return (NULL);
-
- if (pipe(pfds) < 0) {
- syslog(LOG_ERR, "pipe() failed: %m");
- return (NULL);
- }
- if ((pid = fork()) < 0) {
- syslog(LOG_ERR, "fork() failed: %m");
- return (NULL);
- } else if (pid == 0) {
- /*
- * child: close read side of pipe, point stdout at write side
- */
- (void) close(pfds[0]);
- if (dup2(pfds[1], STDOUT_FILENO) < 0) {
- syslog(LOG_ERR, "dup2() failed: %m");
- _exit(EXIT_FAILURE);
- }
- (void) close(pfds[1]);
- (void) execv(ZENITY, zargv);
- syslog(LOG_ERR, "execv() failed: %m");
- _exit(EXIT_FAILURE);
- } else {
- /*
- * parent: close write side of pipe, read from read side
- * to get zenity output from child.
- */
- (void) close(pfds[1]);
- (void) waitpid(pid, &status, 0);
- if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
- i = WIFSIGNALED(status) ? WTERMSIG(status) :
- WSTOPSIG(status);
- syslog(LOG_ERR, "%s %s with signal %d (%s)",
- ZENITY, (WIFSIGNALED(status) ? "terminated" :
- "stopped"), i, strsignal(i));
- return (NULL);
- }
- bytes_read = 0;
- do {
- n = read(pfds[0], inbuf + bytes_read,
- sizeof (inbuf) - bytes_read);
- if (n < 0) {
- if (errno != EINTR) {
- syslog(LOG_ERR, "read() failed: %m");
- break;
- }
- } else {
- bytes_read += n;
- }
- if (bytes_read == sizeof (inbuf)) {
- bytes_read--;
- syslog(LOG_WARNING, "get_zenity_response: too "
- "much data; input read will be limited to "
- "%d bytes", bytes_read);
- break;
- }
- } while (n != 0);
- (void) close(pfds[0]);
- if (bytes_read == 0) {
- syslog(LOG_ERR, "failed to read zenity output");
- return (NULL);
- }
- if (inbuf[bytes_read - 1] == '\n')
- inbuf[bytes_read - 1] = '\0';
- else
- inbuf[bytes_read] = '\0';
-
- if ((rtnp = strdup(inbuf)) == 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.
+ * This function searches through the wlans[] array and determines which ones
+ * have been visited before.
*
- * 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.
- */
-static int
-get_user_preference(char *const *zargv, 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, "calloc failed");
- return (-1);
- }
-
- response = get_zenity_response(zargv);
- if (response == NULL) {
- free(wlp);
- return (-1);
- }
- if (compare_other != NULL && strcmp(response, compare_other) == 0) {
- free(response);
- free(wlp);
- return (1);
- }
- if (compare_rescan != NULL && 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->sec_mode = sel->sec_mode;
-
- if ((sel->raw_key != NULL) &&
- ((wlp->raw_key = strdup(sel->raw_key)) == NULL))
- goto dup_error;
-
- if (sel->cooked_key != NULL) {
- wlp->cooked_key = malloc(sizeof (dladm_wlan_key_t));
- if (wlp->cooked_key == NULL)
- goto dup_error;
- *(wlp->cooked_key) = *(sel->cooked_key);
- }
-
- 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), WLAN_ENC(wlp->sec_mode),
- 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)
-{
- int zargc, cur;
- char **zargv;
- char *response;
- struct wireless_lan *wlp = NULL;
-
- /*
- * build zenity 'entry' argv to get an ESSID:
- * 'zenity --entry --title=foo --text=bar'
- * Four args needed.
- */
- zargc = 4;
- if ((zargv = alloc_argv(zargc, ZENITY_ARG_LEN)) == NULL)
- return (NULL);
-
- cur = 0;
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, ZENITY);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--entry");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--title=%s",
- gettext("Specify WiFi Network"));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--text=%s %s",
- gettext("Enter"), "ESSID");
-
- response = get_zenity_response(zargv);
- if (response == NULL)
- goto cleanup;
- free_argv(zargv);
-
- wlp = calloc(1, sizeof (struct wireless_lan));
- if (wlp == NULL) {
- syslog(LOG_ERR, "calloc failed: %m");
- goto cleanup;
- }
- wlp->essid = response;
- wlp->sec_mode = DLADM_WLAN_SECMODE_NONE;
-
- /*
- * build zenity 'list' argv to get security mode:
- * 'zenity --list --title=foo --text=bar --column=baz <3 modes>'
- * Eight args needed.
- */
- zargc = 8;
- if ((zargv = alloc_argv(zargc, ZENITY_ARG_LEN)) == NULL) {
- /* assume the default security mode, "none" */
- return (wlp);
- }
-
- cur = 0;
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, ZENITY);
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--list");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--title=%s",
- gettext("Security"));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--text=%s",
- gettext("Enter security mode"));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "--column=%s",
- gettext("Type"));
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "None");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "WEP");
- (void) snprintf(zargv[cur++], ZENITY_ARG_LEN, "WPA");
-
- response = get_zenity_response(zargv);
- /* "none" was set as the default earlier */
- if (response != NULL) {
- if (strcmp(response, "WEP") == 0)
- wlp->sec_mode = DLADM_WLAN_SECMODE_WEP;
- else if (strcmp(response, "WPA") == 0)
- wlp->sec_mode = DLADM_WLAN_SECMODE_WPA;
- }
-cleanup:
- free_argv(zargv);
- free(response);
-
- return (wlp);
-}
-
-/*
- * Returns:
- * B_TRUE if things go well
- * B_FALSE if we were unable to connect to anything
+ * If exactly one has been visited before, and it has the highest signal
+ * strength, then we attempt to connect to it right away.
+ *
+ * In all other cases -- if none have been visited before, or more than one was
+ * visited, or if the one that was visited doesn't have the highest signal
+ * strength, or if the automatic connect attempt fails for any reason -- then
+ * we hand over the data to the GUI for resolution. The user will have to be
+ * prompted for a choice.
+ *
+ * If no GUI exists, we'll get back FAILURE (instead of WAITING), which will
+ * cause the autoconf mechanism to run instead.
*/
-boolean_t
-handle_wireless_lan(struct interface *intf)
+return_vals_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;
+ wireless_if_t *wip;
+ struct wireless_lan *cur_wlan, *max_wlan;
+ struct wireless_lan *most_recent;
+ boolean_t many_present;
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)
+ if (pthread_mutex_lock(&wifi_init_mutex) != 0)
+ return (FAILURE);
+ while (wifi_scan_intf[0] != '\0')
(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;
+ if (pthread_mutex_lock(&wifi_mutex) != 0)
+ return (FAILURE);
+
+ if ((wip = find_wireless_if(ifname)) == NULL) {
+ connect_result = FAILURE;
+ goto finished;
+ }
+
+ if (wip->wi_wireless_done) {
+ dprintf("handle_wireless_lan: skipping policy scan; done");
+ connect_result = SUCCESS;
+ goto finished;
+ }
+
+ dprintf("handle_wireless_lan: starting policy scan");
+ cur_wlan = wlans;
+ max_wlan = wlans + wireless_lan_used;
+ most_recent = NULL;
+ many_present = B_FALSE;
/*
* Try to see if any of the wifi nets currently available
@@ -1765,140 +1845,143 @@ start_over:
* 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;
-
+ for (; cur_wlan < max_wlan; cur_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 (cur_wlan->attrs.wa_strength > strongest)
+ strongest = cur_wlan->attrs.wa_strength;
- if (!known_wifi_nets_lookup(cur_wlans[i].essid,
- cur_wlans[i].bssid, NULL))
- continue;
+ if (known_wifi_nets_lookup(cur_wlan->essid, cur_wlan->bssid,
+ NULL))
+ cur_wlan->known = B_TRUE;
- 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_key = NULL;
- new_wlan->wifi_net->cooked_key = NULL;
- new_wlan->wifi_net->sec_mode = cur_wlans[i].sec_mode;
- 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;
+ if (cur_wlan->known || cur_wlan->connected) {
+ /*
+ * The ESSID comparison here mimics what the "already
+ * in visited wlan list" function once did, but
+ * slightly better as we also pay attention to signal
+ * strength to pick the best of the duplicates.
+ */
+ if (most_recent == NULL) {
+ most_recent = cur_wlan;
+ } else if (strcmp(cur_wlan->essid,
+ most_recent->essid) != 0) {
+ many_present = B_TRUE;
+ } else if (cur_wlan->attrs.wa_strength >
+ most_recent->attrs.wa_strength) {
+ if (most_recent->connected) {
+ (void) dladm_wlan_disconnect(
+ wip->wi_linkid);
+ most_recent->connected = B_FALSE;
+ report_wlan_disconnect(most_recent);
+ wip->wi_wireless_done = B_FALSE;
+ }
+ most_recent = cur_wlan;
+ }
}
- new_wlan->next = visited_wlan_list->head;
- visited_wlan_list->head = new_wlan;
- visited_wlan_list->total++;
+ /* Reset any security information we may have had. */
+ free(cur_wlan->raw_key);
+ cur_wlan->raw_key = NULL;
+ free(cur_wlan->cooked_key);
+ cur_wlan->cooked_key = NULL;
}
- 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, intf);
- 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, intf);
- connect_result = result ? SUCCESS : FAILURE;
+ if (most_recent != NULL && !many_present &&
+ most_recent->attrs.wa_strength >= strongest) {
+ if (most_recent->connected) {
+ dprintf("%s already connected to %s", ifname,
+ most_recent->essid);
+ connect_result = SUCCESS;
} else {
- /*
- * The user didn't make a choice; offer the full list.
- */
- connect_result = connect_to_new_wlan(cur_wlans,
- num_wlans, intf);
- result = (connect_result == SUCCESS);
+ dprintf("%s auto-connect to %s", ifname,
+ most_recent->essid);
+ connect_result = connect_or_autoconf(most_recent, wip);
}
+ } else if (request_wlan_selection(ifname, wlans, wireless_lan_used)) {
+ dprintf("%s is unknown and not connected; requested help",
+ ifname);
+ connect_result = WAITING;
} else {
-connect_any:
- /* last case, no previously visited wlan found */
- connect_result = connect_to_new_wlan(cur_wlans, num_wlans,
- intf);
- result = (connect_result == SUCCESS);
+ dprintf("%s has no connected AP or GUI; try auto", ifname);
+ connect_result = wlan_autoconf(wip) ? SUCCESS : FAILURE;
}
-all_done:
- /*
- * We locked down the list above; free it now that we're done.
- */
+finished:
+ if (connect_result == SUCCESS && !update_connected_wlan(wip))
+ connect_result = FAILURE;
(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;
+
+ return (connect_result);
+}
+
+void
+disconnect_wlan(const char *ifname)
+{
+ wireless_if_t *wip;
+ struct wireless_lan *wlan;
+
+ if (pthread_mutex_lock(&wifi_mutex) == 0) {
+ if ((wip = find_wireless_if(ifname)) != NULL) {
+ wip->wi_wireless_done = B_FALSE;
+ wip->wi_need_key = B_FALSE;
+ (void) dladm_wlan_disconnect(wip->wi_linkid);
+ }
+ for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
+ if (strcmp(ifname, wlan->wl_if_name) == 0 &&
+ wlan->connected) {
+ wlan->connected = B_FALSE;
+ report_wlan_disconnect(wlan);
}
- next = vwlp->next;
- free(vwlp);
}
- free(visited_wlan_list);
- visited_wlan_list = NULL;
+ (void) pthread_mutex_unlock(&wifi_mutex);
}
- if (req_conf != NULL) {
- free_wireless_lan(req_conf);
- free(req_conf);
- req_conf = NULL;
+}
+
+void
+get_wireless_state(const char *ifname, boolean_t *need_wlan,
+ boolean_t *need_key)
+{
+ wireless_if_t *wip;
+
+ *need_wlan = *need_key = B_FALSE;
+ if (pthread_mutex_lock(&wifi_mutex) == 0) {
+ if ((wip = find_wireless_if(ifname)) != NULL) {
+ *need_key = wip->wi_need_key;
+ if (!wip->wi_need_key && !wip->wi_wireless_done)
+ *need_wlan = B_TRUE;
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
}
- if (connect_result == TRY_AGAIN) {
- dprintf("end of handle_wireless_lan() TRY_AGAIN");
- goto start_over;
+}
+
+void
+print_wireless_status(void)
+{
+ wireless_if_t *wip;
+ struct wireless_lan *wlan;
+
+ if (pthread_mutex_lock(&wifi_mutex) == 0) {
+ for (wip = (wireless_if_t *)wi_list.q_forw;
+ wip != (wireless_if_t *)&wi_list;
+ wip = (wireless_if_t *)wip->wi_links.q_forw) {
+ dprintf("WIF %s linkid %d scan %srunning "
+ "wireless %sdone %sneed key strength %d",
+ wip->wi_name, wip->wi_linkid,
+ wip->wi_scan_running ? "" : "not ",
+ wip->wi_wireless_done ? "" : "not ",
+ wip->wi_need_key ? "" : "don't ",
+ wip->wi_strength);
+ }
+ for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
+ dprintf("WLAN I/F %s ESS %s BSS %s signal %s key %sset "
+ "%sknown %sconnected %sscanned",
+ wlan->wl_if_name, wlan->essid, wlan->bssid,
+ wlan->signal_strength,
+ wlan->raw_key == NULL ? "un" : "",
+ wlan->known ? "" : "not ",
+ wlan->connected ? "" : "not ",
+ wlan->scanned ? "" : "not ");
+ }
+ (void) pthread_mutex_unlock(&wifi_mutex);
}
- return (result);
}
diff --git a/usr/src/cmd/svc/milestone/network-physical.xml b/usr/src/cmd/svc/milestone/network-physical.xml
index 9995b0d48d..ae967e1e1a 100644
--- a/usr/src/cmd/svc/milestone/network-physical.xml
+++ b/usr/src/cmd/svc/milestone/network-physical.xml
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
- Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2008 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
CDDL HEADER START
@@ -23,8 +23,6 @@
CDDL HEADER END
- ident "%Z%%M% %I% %E% SMI"
-
NOTE: This service manifest is not editable; its contents will
be overwritten by package or patch operations, including
operating system upgrade. Make customizations in a different
@@ -111,11 +109,10 @@
<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='false' />
<propval name='dhcp_wait_time' type='count' value='60' />
<propval name='scan_interval' type='count' value='120' />
+ <propval name='idle_time' type='count' value='10' />
<propval name='value_authorization' type='astring'
value='solaris.smf.value.nwam' />
</property_group>
diff --git a/usr/src/cmd/svc/shell/net_include.sh b/usr/src/cmd/svc/shell/net_include.sh
index 989a780384..51c87a40a8 100644
--- a/usr/src/cmd/svc/shell/net_include.sh
+++ b/usr/src/cmd/svc/shell/net_include.sh
@@ -23,8 +23,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
-#
# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
# All rights reserved.
#
@@ -595,8 +593,8 @@ net_reconfigure ()
# Is this a reconfigure boot? If not, then there's nothing
# for us to do.
#
- svcprop -q -p system/reconfigure system/svc/restarter:default
- if [ $? -ne 0 ]; then
+ reconfig=`svcprop -c -p system/reconfigure system/svc/restarter:default`
+ if [ $? -ne 0 -o "$reconfig" = false ]; then
return 0
fi
diff --git a/usr/src/head/auth_list.h b/usr/src/head/auth_list.h
index 4931ac9519..76f0ceb47a 100644
--- a/usr/src/head/auth_list.h
+++ b/usr/src/head/auth_list.h
@@ -42,11 +42,12 @@ extern "C" {
#define CRONUSER_AUTH "solaris.jobs.user"
#define DEFAULT_DEV_ALLOC_AUTH "solaris.device.allocate"
#define DEVICE_REVOKE_AUTH "solaris.device.revoke"
+#define LINK_SEC_AUTH "solaris.network.link.security"
#define MAILQ_AUTH "solaris.mail.mailq"
+#define NET_AUTOCONF_AUTH "solaris.network.autoconf"
#define SET_DATE_AUTH "solaris.system.date"
#define WIFI_CONFIG_AUTH "solaris.network.wifi.config"
#define WIFI_WEP_AUTH "solaris.network.wifi.wep"
-#define LINK_SEC_AUTH "solaris.network.link.security"
/*
* Authorizations used by Trusted Solaris.
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index b0cb3d2094..4905a502e7 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -122,6 +122,7 @@ SUBDIRS += \
libmapmalloc \
libmtmalloc \
libnls \
+ libnwam \
libsmbios \
libtecla \
libumem \
@@ -383,6 +384,7 @@ HDRSUBDIRS= \
libmtmalloc \
libnvpair \
libnsl \
+ libnwam \
libpam \
libpctx \
libpicl \
@@ -523,6 +525,7 @@ libinetcfg: libnsl libsocket libdlpi
libkmf: libcryptoutil pkcs11 openssl
libnsl: libmd5 libscf
libmapid: libresolv
+libnwam: libdoor
libuuid: libdlpi
libinetutil: libsocket
libsecdb: libnsl
diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt
index 848ace7e37..2be51f5c53 100644
--- a/usr/src/lib/libbsm/audit_event.txt
+++ b/usr/src/lib/libbsm/audit_event.txt
@@ -22,8 +22,6 @@
#
# CDDL HEADER END
#
-# ident "%Z%%M% %I% %E% SMI"
-#
# Audit Event Database
#
# File Format:
@@ -499,6 +497,11 @@
6291:AUE_smf_change_prop:change service instance property:as
6292:AUE_smf_delete_prop:delete service instance property:as
#
+# nwamd(1M) events
+#
+6300:AUE_nwam_attach:attach nwam user:ss
+6301:AUE_nwam_detach:detach nwam user:ss
+#
# Trusted Extensions events:
#
9035:AUE_sl_change:Workspace label change:ap
diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml
index 19dd0ec37e..ada693bf8d 100644
--- a/usr/src/lib/libbsm/common/adt.xml
+++ b/usr/src/lib/libbsm/common/adt.xml
@@ -1838,8 +1838,36 @@ Use is subject to license terms.
</entry>
</event>
+<!-- NWAM events -->
+
+ <event id="AUE_nwam_attach" header="0" idNo="97" omit="JNI">
+ <title>Network Autoconfig Client</title>
+ <program>nwamd</program>
+ <see>nwamd(1M)</see>
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="auth_used">
+ <internal token="uauth"/>
+ <external opt="required" type="char *"/>
+ <comment>authorization used</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_nwam_detach" instance_of="AUE_generic_basic" header="0"
+ idNo="98" omit="JNI">
+ <title>Network Autoconfig Client</title>
+ <program>nwamd</program>
+ <see>nwamd(1M)</see>
+ </event>
+
<!-- add new events here with the next higher idNo -->
-<!-- Highest idNo is 96, so next is 97, then fix this comment -->
+<!-- Highest idNo is 98, so next is 99, then fix this comment -->
<!-- end of C Only events -->
diff --git a/usr/src/lib/libnwam/Makefile b/usr/src/lib/libnwam/Makefile
new file mode 100644
index 0000000000..9110a09447
--- /dev/null
+++ b/usr/src/lib/libnwam/Makefile
@@ -0,0 +1,52 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include $(SRC)/lib/Makefile.lib
+
+HDRS = libnwam.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check:
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libnwam/Makefile.com b/usr/src/lib/libnwam/Makefile.com
new file mode 100644
index 0000000000..8ef4d2ba07
--- /dev/null
+++ b/usr/src/lib/libnwam/Makefile.com
@@ -0,0 +1,52 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY = libnwam.a
+VERS = .1
+OBJECTS = door.o
+
+include ../../Makefile.lib
+include ../../Makefile.rootfs
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+NWAMDIR= $(SRC)/cmd/cmd-inet/lib/nwamd
+SRCDIR = ../common
+SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c)
+
+$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC)
+
+LDLIBS += -ldoor -ldladm -lc
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRCDIR) -I$(NWAMDIR)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libnwam/common/door.c b/usr/src/lib/libnwam/common/door.c
new file mode 100644
index 0000000000..a48dd69aa9
--- /dev/null
+++ b/usr/src/lib/libnwam/common/door.c
@@ -0,0 +1,748 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains the library interface for the nwamd libdoor(3LIB)
+ * service. This library is intended for use by an external GUI utility to
+ * provide status information to users and allow control over nwam behavior in
+ * certain situations.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <door.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <libdladm.h>
+
+#include <libnwam.h>
+
+/* Special include files; data structures shared with nwamd */
+#include <defines.h>
+#include <structures.h>
+
+static int door_fd = -1;
+
+/*
+ * This lock protects the library against descriptor leaks caused by multiple
+ * threads attempting to race at opening the door. It also protects the door
+ * users by preventing reinit while busy.
+ */
+static pthread_mutex_t door_lock = PTHREAD_MUTEX_INITIALIZER;
+static uint_t door_users;
+
+#define BUFFER_SIZE 256
+
+/* ARGSUSED */
+static void
+door_cancel_handler(void *arg)
+{
+ (void) pthread_mutex_lock(&door_lock);
+ door_users--;
+ (void) pthread_mutex_unlock(&door_lock);
+}
+
+/*
+ * This wraps around door_call(3C), and makes sure that interrupts and error
+ * cases are handled in a reasonable way and that we always have an accurate
+ * door user count.
+ */
+static int
+make_door_call(nwam_door_cmd_t *cmd, void **bufp, size_t *size,
+ size_t *rsize)
+{
+ door_arg_t arg;
+ int retv;
+
+ (void) memset(&arg, 0, sizeof (arg));
+ arg.data_ptr = (char *)cmd;
+ arg.data_size = sizeof (*cmd);
+ arg.rbuf = *bufp;
+ arg.rsize = *rsize;
+
+ /*
+ * In order to avoid blocking and starving out the init routine, we
+ * check here for a descriptor before attempting to take a lock.
+ */
+ if (door_fd == -1) {
+ errno = EBADF;
+ return (-1);
+ }
+
+ if ((retv = pthread_mutex_lock(&door_lock)) != 0) {
+ errno = retv;
+ return (-1);
+ }
+ pthread_cleanup_push(door_cancel_handler, NULL);
+ door_users++;
+ (void) pthread_mutex_unlock(&door_lock);
+ /* The door_call function doesn't restart, so take care of that */
+ do {
+ errno = 0;
+ if ((retv = door_call(door_fd, &arg)) == 0)
+ break;
+ } while (errno == EINTR);
+ pthread_cleanup_pop(1);
+
+ /* No legitimate door call on our server returns without data */
+ if (retv == 0 && arg.data_size == 0) {
+ retv = -1;
+ errno = EBADF;
+ }
+
+ *bufp = arg.rbuf;
+ *size = arg.data_size;
+ *rsize = arg.rsize;
+
+ return (retv);
+}
+
+/*
+ * This is a common clean-up function for the door-calling routines. It checks
+ * for the daemon's standard error return mechanism (single integer with an
+ * errno) and for the special case of an oversized return buffer.
+ */
+static int
+handle_errors(int retv, void *dbuf, void *obuf, size_t dsize, size_t rsize)
+{
+ int err = errno;
+
+ if (retv == 0 && dsize == sizeof (int) && (err = *(int *)dbuf) != 0)
+ retv = -1;
+ if (dbuf != obuf)
+ (void) munmap(dbuf, rsize);
+ errno = err;
+ return (retv);
+}
+
+/*
+ * Convert the internal libdladm representation of WLAN attributes into a text
+ * representation that we can send to the client. The passed-in 'strbuf'
+ * parameter points to a buffer that's known to be large enough to hold all of
+ * the strings.
+ */
+static char *
+wlan_convert(libnwam_wlan_attr_t *wla, dladm_wlan_attr_t *wa, char *strbuf)
+{
+ static const struct {
+ uint_t flag;
+ char *(*cvt)(void *, char *);
+ size_t offsf;
+ size_t offst;
+ } cvtable[] = {
+ {
+ DLADM_WLAN_ATTR_ESSID,
+ (char *(*)(void *, char *))dladm_wlan_essid2str,
+ offsetof(dladm_wlan_attr_t, wa_essid),
+ offsetof(libnwam_wlan_attr_t, wla_essid)
+ },
+ {
+ DLADM_WLAN_ATTR_BSSID,
+ (char *(*)(void *, char *))dladm_wlan_bssid2str,
+ offsetof(dladm_wlan_attr_t, wa_bssid),
+ offsetof(libnwam_wlan_attr_t, wla_bssid)
+ },
+ {
+ DLADM_WLAN_ATTR_SECMODE,
+ (char *(*)(void *, char *))dladm_wlan_secmode2str,
+ offsetof(dladm_wlan_attr_t, wa_secmode),
+ offsetof(libnwam_wlan_attr_t, wla_secmode)
+ },
+ {
+ DLADM_WLAN_ATTR_STRENGTH,
+ (char *(*)(void *, char *))dladm_wlan_strength2str,
+ offsetof(dladm_wlan_attr_t, wa_strength),
+ offsetof(libnwam_wlan_attr_t, wla_strength)
+ },
+ {
+ DLADM_WLAN_ATTR_MODE,
+ (char *(*)(void *, char *))dladm_wlan_mode2str,
+ offsetof(dladm_wlan_attr_t, wa_mode),
+ offsetof(libnwam_wlan_attr_t, wla_mode)
+ },
+ {
+ DLADM_WLAN_ATTR_SPEED,
+ (char *(*)(void *, char *))dladm_wlan_speed2str,
+ offsetof(dladm_wlan_attr_t, wa_speed),
+ offsetof(libnwam_wlan_attr_t, wla_speed)
+ },
+ {
+ DLADM_WLAN_ATTR_AUTH,
+ (char *(*)(void *, char *))dladm_wlan_auth2str,
+ offsetof(dladm_wlan_attr_t, wa_auth),
+ offsetof(libnwam_wlan_attr_t, wla_auth)
+ },
+ {
+ DLADM_WLAN_ATTR_BSSTYPE,
+ (char *(*)(void *, char *))dladm_wlan_bsstype2str,
+ offsetof(dladm_wlan_attr_t, wa_bsstype),
+ offsetof(libnwam_wlan_attr_t, wla_bsstype)
+ },
+ { 0, NULL, 0 }
+ };
+ int i;
+ char **cptr;
+
+ for (i = 0; cvtable[i].cvt != NULL; i++) {
+ /* LINTED: pointer alignment */
+ cptr = (char **)((char *)wla + cvtable[i].offst);
+ if (wa->wa_valid & cvtable[i].flag) {
+ *cptr = cvtable[i].cvt((char *)wa + cvtable[i].offsf,
+ strbuf);
+ strbuf += strlen(strbuf) + 1;
+ } else {
+ *cptr = "";
+ }
+ }
+ /* This one element is a simple integer, not a string */
+ if (wa->wa_valid & DLADM_WLAN_ATTR_CHANNEL)
+ wla->wla_channel = wa->wa_channel;
+ return (strbuf);
+}
+
+/*
+ * Wait for an event from the daemon and return it to the caller in allocated
+ * storage.
+ */
+libnwam_event_data_t *
+libnwam_wait_event(void)
+{
+ libnwam_event_data_t *led = NULL;
+ int retv;
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ nwam_descr_event_t *nde;
+ boolean_t has_wlan_attrs;
+ char *str;
+
+ cmd.ndc_type = ndcWaitEvent;
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ if (retv == 0 && cmd_size == sizeof (int))
+ retv = *(int *)cmd_ret;
+ if (retv == 0 && cmd_size == sizeof (*nde)) {
+ nde = cmd_ret;
+ has_wlan_attrs = (nde->nde_type == deWlanKeyNeeded ||
+ nde->nde_type == deWlanDisconnect ||
+ nde->nde_type == deWlanConnected);
+ led = calloc(1, sizeof (*led) + strlen(nde->nde_interface) + 1 +
+ (has_wlan_attrs ? DLADM_STRSIZE * WLA_NUM_STRS : 0));
+ if (led != NULL) {
+ led->led_type = nde->nde_type;
+ if (led->led_type == deInterfaceUp) {
+ led->led_v4address = nde->nde_v4address;
+ led->led_prefixlen = nde->nde_prefixlen;
+ }
+ if (led->led_type == deInterfaceDown ||
+ led->led_type == deLLPUnselected)
+ led->led_cause = nde->nde_cause;
+ str = (char *)(led + 1);
+ (void) strcpy(led->led_interface = str,
+ nde->nde_interface);
+ str += strlen(str) + 1;
+ if (has_wlan_attrs)
+ (void) wlan_convert(&led->led_wlan,
+ &nde->nde_attrs, str);
+ }
+ }
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ if (led == NULL && retv > 0)
+ errno = retv;
+ return (led);
+}
+
+/* Free an allocated event */
+void
+libnwam_free_event(libnwam_event_data_t *led)
+{
+ free(led);
+}
+
+/*
+ * Get a list of Lower-Layer Profiles (interfaces) in a single allocated array.
+ */
+libnwam_llp_t *
+libnwam_get_llp_list(uint_t *numllp)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+ const nwam_llp_data_t *nld;
+ const llp_t *llp, *maxllp;
+ size_t strsize;
+ libnwam_llp_t *nllp, *nllpret;
+ char *sbuf;
+
+ *numllp = 0;
+
+ cmd.ndc_type = ndcGetLLPList;
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ nld = cmd_ret;
+ if (retv != 0 || cmd_size < sizeof (*nld) ||
+ cmd_size < sizeof (*nld) + nld->nld_count * sizeof (*llp)) {
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ errno = retv == 0 ? EBADF : retv;
+ return (NULL);
+ }
+ /* Figure room needed to return strings to caller */
+ llp = (const llp_t *)(nld + 1);
+ maxllp = llp + nld->nld_count;
+ strsize = 0;
+ while (llp < maxllp) {
+ strsize += strlen(llp->llp_lname) + 1;
+ llp++;
+ }
+ nllpret = malloc(nld->nld_count * sizeof (*nllp) + strsize);
+ if (nllpret != NULL) {
+ nllp = nllpret;
+ sbuf = (char *)(nllp + nld->nld_count);
+ llp = (const llp_t *)(nld + 1);
+ /* Convert internal to external structures */
+ while (llp < maxllp) {
+ nllp->llp_interface = strcpy(sbuf, llp->llp_lname);
+ sbuf += strlen(llp->llp_lname) + 1;
+ nllp->llp_pri = llp->llp_pri;
+ nllp->llp_type = llp->llp_type;
+ nllp->llp_ipv4src = llp->llp_ipv4src;
+ nllp->llp_primary =
+ strcmp(nld->nld_selected, llp->llp_lname) == 0;
+ nllp->llp_locked =
+ strcmp(nld->nld_locked, llp->llp_lname) == 0;
+ nllp->llp_link_failed = llp->llp_failed;
+ nllp->llp_dhcp_failed = llp->llp_dhcp_failed;
+ nllp->llp_link_up = llp->llp_link_up;
+ nllp->llp_need_wlan = llp->llp_need_wlan;
+ nllp->llp_need_key = llp->llp_need_key;
+ nllp++;
+ llp++;
+ }
+ *numllp = nld->nld_count;
+ }
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ return (nllpret);
+}
+
+/* Free an LLP list */
+void
+libnwam_free_llp_list(libnwam_llp_t *llp)
+{
+ free(llp);
+}
+
+/* Set the priority for a single LLP */
+int
+libnwam_set_llp_priority(const char *ifname, int prio)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcSetLLPPriority;
+ (void) strlcpy(cmd.ndc_interface, ifname, sizeof (cmd.ndc_interface));
+ cmd.ndc_priority = prio;
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/* Lock a single LLP as selected */
+int
+libnwam_lock_llp(const char *ifname)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcLockLLP;
+ if (ifname != NULL) {
+ (void) strlcpy(cmd.ndc_interface, ifname,
+ sizeof (cmd.ndc_interface));
+ } else {
+ cmd.ndc_interface[0] = '\0';
+ }
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/* Get an array listing the scanned WLANs (Access Points) and attributes */
+libnwam_wlan_t *
+libnwam_get_wlan_list(uint_t *numwlans)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+ struct wireless_lan *wllp, *maxwllp;
+ size_t count, strsize;
+ libnwam_wlan_t *wlan, *wlanret;
+ char *sbuf;
+
+ cmd.ndc_type = ndcGetWlanList;
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ if (retv != 0 || cmd_size < sizeof (*wllp)) {
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ errno = retv == 0 ? EBADF : retv;
+ *numwlans = 0;
+ return (NULL);
+ }
+ /* Figure amount of storage needed for strings */
+ wllp = cmd_ret;
+ count = cmd_size / sizeof (*wllp);
+ maxwllp = wllp + count;
+ strsize = 0;
+ while (wllp < maxwllp) {
+ strsize += strlen(wllp->wl_if_name) + 1;
+ wllp++;
+ }
+ wlanret = malloc(count * (sizeof (*wlan) + DLADM_STRSIZE *
+ WLA_NUM_STRS) + strsize);
+ if (wlanret != NULL) {
+ wlan = wlanret;
+ sbuf = (char *)(wlan + count);
+ wllp = cmd_ret;
+ /* Convert internal to external structures */
+ while (wllp < maxwllp) {
+ wlan->wlan_interface = strcpy(sbuf, wllp->wl_if_name);
+ sbuf += strlen(wllp->wl_if_name) + 1;
+ wlan->wlan_known = wllp->known;
+ wlan->wlan_haskey = wllp->cooked_key != NULL;
+ wlan->wlan_connected = wllp->connected;
+ sbuf = wlan_convert(&wlan->wlan_attrs, &wllp->attrs,
+ sbuf);
+ wllp++;
+ wlan++;
+ }
+ } else {
+ count = 0;
+ }
+ *numwlans = count;
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ return (wlanret);
+}
+
+/* Free array of WLANs */
+void
+libnwam_free_wlan_list(libnwam_wlan_t *wlans)
+{
+ free(wlans);
+}
+
+/* Get the non-volatile list of known user-specified Access Points */
+libnwam_known_ap_t *
+libnwam_get_known_ap_list(uint_t *numkas)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+ nwam_known_ap_t *nka;
+ libnwam_known_ap_t *kabuf, *kastart, *maxkabuf;
+ libnwam_known_ap_t *kap, *kapret;
+ char *sbuf;
+ uint_t count;
+
+ cmd.ndc_type = ndcGetKnownAPList;
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ nka = cmd_ret;
+ if (retv != 0 || cmd_size < sizeof (*nka) ||
+ nka->nka_count * sizeof (*kabuf) >= cmd_size) {
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ errno = retv == 0 ? EBADF : retv;
+ *numkas = 0;
+ return (NULL);
+ }
+ if ((kapret = malloc(cmd_size)) != NULL) {
+ kap = kapret;
+ sbuf = (char *)(kap + nka->nka_count);
+ kabuf = kastart = (libnwam_known_ap_t *)(nka + 1);
+ maxkabuf = kabuf + nka->nka_count;
+ cmd_size -= sizeof (*nka);
+ /*
+ * Buffer returned from daemon has string offsets in place of
+ * pointers; convert back to pointers for user.
+ */
+ while (kabuf < maxkabuf) {
+ if ((uintptr_t)kabuf->ka_essid >= cmd_size ||
+ memchr((char *)kastart + (uintptr_t)kabuf->ka_essid,
+ 0, cmd_size - (uintptr_t)kabuf->ka_essid) == NULL)
+ break;
+ kap->ka_essid = strcpy(sbuf,
+ (char *)kastart + (uintptr_t)kabuf->ka_essid);
+ sbuf += strlen(sbuf) + 1;
+ if ((uintptr_t)kabuf->ka_bssid >= cmd_size ||
+ memchr((char *)kastart + (uintptr_t)kabuf->ka_bssid,
+ 0, cmd_size - (uintptr_t)kabuf->ka_bssid) == NULL)
+ break;
+ kap->ka_bssid = strcpy(sbuf,
+ (char *)kastart + (uintptr_t)kabuf->ka_bssid);
+ sbuf += strlen(sbuf) + 1;
+ kap->ka_haskey = kabuf->ka_haskey;
+ kabuf++;
+ kap++;
+ }
+ count = kap - kapret;
+ } else {
+ count = 0;
+ }
+ *numkas = count;
+ if (cmd_ret != cmd_buf)
+ (void) munmap(cmd_ret, cmd_rsize);
+ return (kapret);
+}
+
+/* Free a Known AP list */
+void
+libnwam_free_known_ap_list(libnwam_known_ap_t *kas)
+{
+ free(kas);
+}
+
+/*
+ * Add a new AP to the "known" list so that we'll automatically connect.
+ * BSSID is optional.
+ */
+int
+libnwam_add_known_ap(const char *essid, const char *bssid)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcAddKnownAP;
+ (void) strlcpy(cmd.ndc_essid, essid, sizeof (cmd.ndc_essid));
+ (void) strlcpy(cmd.ndc_bssid, bssid, sizeof (cmd.ndc_bssid));
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/*
+ * Delete an AP from the "known" list so that we won't connect to it
+ * automatically. BSSID is optional.
+ */
+int
+libnwam_delete_known_ap(const char *essid, const char *bssid)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcDeleteKnownAP;
+ (void) strlcpy(cmd.ndc_essid, essid, sizeof (cmd.ndc_essid));
+ (void) strlcpy(cmd.ndc_bssid, bssid, sizeof (cmd.ndc_bssid));
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/*
+ * Select a particular Access Point (WLAN) for use on a given interface. This
+ * may disconnect from the current AP if the link is already connected.
+ */
+int
+libnwam_select_wlan(const char *ifname, const char *essid, const char *bssid)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcSelectWlan;
+ (void) strlcpy(cmd.ndc_interface, ifname, sizeof (cmd.ndc_interface));
+ (void) strlcpy(cmd.ndc_essid, essid, sizeof (cmd.ndc_essid));
+ (void) strlcpy(cmd.ndc_bssid, bssid, sizeof (cmd.ndc_bssid));
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/*
+ * Set the encryption key needed for a given AP. The key string is cleartext.
+ */
+int
+libnwam_wlan_key(const char *ifname, const char *essid, const char *bssid,
+ const char *key)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcWlanKey;
+ (void) strlcpy(cmd.ndc_interface, ifname, sizeof (cmd.ndc_interface));
+ (void) strlcpy(cmd.ndc_essid, essid, sizeof (cmd.ndc_essid));
+ (void) strlcpy(cmd.ndc_bssid, bssid, sizeof (cmd.ndc_bssid));
+ (void) strlcpy(cmd.ndc_key, key, sizeof (cmd.ndc_key));
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/* Initiate wireless scan on the indicated interface */
+int
+libnwam_start_rescan(const char *ifname)
+{
+ nwam_door_cmd_t cmd;
+ uintptr_t cmd_buf[BUFFER_SIZE];
+ void *cmd_ret = cmd_buf;
+ size_t cmd_rsize = sizeof (cmd_buf);
+ size_t cmd_size;
+ int retv;
+
+ cmd.ndc_type = ndcStartRescan;
+ (void) strlcpy(cmd.ndc_interface, ifname, sizeof (cmd.ndc_interface));
+ retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
+ return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
+}
+
+/* Shut down the library. */
+int
+libnwam_fini(void)
+{
+ int retv;
+
+ if ((retv = pthread_mutex_lock(&door_lock)) != 0) {
+ errno = retv;
+ return (-1);
+ }
+ if (door_fd != -1) {
+ retv = close(door_fd);
+ door_fd = -1;
+ }
+ (void) pthread_mutex_unlock(&door_lock);
+ return (retv);
+}
+
+/*
+ * Initialize the library for use. Waittime is the number of seconds
+ * (approximate) to wait for the daemon to become available if it isn't ready
+ * immediately. This may be -1 to wait forever, or 0 to open the connection to
+ * the daemon without waiting (i.e., fail if not ready now).
+ */
+int
+libnwam_init(int waittime)
+{
+ nwam_door_cmd_t cmd;
+ door_arg_t arg;
+ int newfd;
+ int retv;
+
+ for (;;) {
+ if ((retv = pthread_mutex_lock(&door_lock)) != 0) {
+ errno = retv;
+ break;
+ }
+ if (door_users != 0) {
+ errno = EBUSY;
+ (void) pthread_mutex_unlock(&door_lock);
+ break;
+ }
+ if (door_fd != -1) {
+ (void) close(door_fd);
+ door_fd = -1;
+ }
+ newfd = open(DOOR_FILENAME,
+ O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_NOCTTY);
+ if (newfd != -1) {
+ /* Make a dummy call to make sure daemon is running */
+ cmd.ndc_type = ndcNull;
+ (void) memset(&arg, 0, sizeof (arg));
+ arg.data_ptr = (char *)&cmd;
+ arg.data_size = sizeof (cmd);
+ arg.rbuf = (char *)&retv;
+ arg.rsize = sizeof (retv);
+ if (door_call(newfd, &arg) == 0) {
+ if (arg.rbuf != (char *)&retv)
+ (void) munmap(arg.rbuf, arg.data_size);
+ if (arg.data_size == sizeof (int)) {
+ if (retv == 0) {
+ /* Call worked; we're done. */
+ door_fd = newfd;
+ (void) pthread_mutex_unlock(
+ &door_lock);
+ return (0);
+ } else {
+ errno = retv;
+ }
+ } else {
+ errno = EINVAL;
+ }
+ /*
+ * Zero data means daemon terminated.
+ * Otherwise, this is a permanent error. No
+ * point in waiting around and retrying for a
+ * permanent problem.
+ */
+ if (arg.data_size != 0) {
+ (void) pthread_mutex_unlock(&door_lock);
+ (void) close(newfd);
+ break;
+ }
+ }
+ (void) close(newfd);
+ }
+ (void) pthread_mutex_unlock(&door_lock);
+ if (waittime == 0)
+ break;
+ if (waittime > 0)
+ waittime--;
+ /*
+ * We could do something smarter here, but decline to for now.
+ * This is "good enough" for NWAM Phase 0.5.
+ */
+ (void) sleep(1);
+ }
+ return (-1);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam.h b/usr/src/lib/libnwam/common/libnwam.h
new file mode 100644
index 0000000000..88e4afc8d4
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam.h
@@ -0,0 +1,171 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBNWAM_H
+#define _LIBNWAM_H
+
+#include <netinet/in.h>
+
+/*
+ * This file defines the programming interface for libnwam. It is a private
+ * (undocumented, subject to change) interface shared between the NWAM GUI and
+ * nwamd.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct libnwam_wlan_attr_s {
+ const char *wla_essid;
+ const char *wla_bssid;
+ const char *wla_secmode;
+ const char *wla_strength;
+ const char *wla_mode;
+ const char *wla_speed;
+ const char *wla_auth;
+ const char *wla_bsstype;
+ int wla_channel;
+} libnwam_wlan_attr_t;
+
+/* Number of strings above; used for internal allocation purposes */
+#define WLA_NUM_STRS 8
+
+/*
+ * The descriptive event types shared with nwamd and with the GUI client. With
+ * all events other than deInitial, led_interface is always valid. The other
+ * fields are present when indicated, and otherwise must be left unused.
+ */
+typedef enum libnwam_descr_evtype_e {
+ deInitial, /* no other fields; new active client */
+ deInterfaceUp, /* led_v4address led_prefixlen */
+ deInterfaceDown, /* led_cause */
+ deInterfaceAdded,
+ deInterfaceRemoved,
+ deWlanConnectFail,
+ deWlanDisconnect, /* led_wlan */
+ deWlanConnected, /* led_wlan */
+ deLLPSelected,
+ deLLPUnselected, /* led_cause */
+ deULPActivated,
+ deULPDeactivated,
+ deScanChange,
+ deScanSame,
+ deWlanKeyNeeded, /* led_wlan */
+ deWlanSelectionNeeded
+} libnwam_descr_evtype_t;
+
+typedef enum libnwam_diag_cause_e {
+ dcNone = 0, /* no cause */
+ dcDHCP, /* DHCP left interface down or with zero addr */
+ dcTimer, /* gave up on DHCP; switching to next best */
+ dcUnplugged, /* interface lost RUNNING flag */
+ dcUser, /* user changed priority */
+ dcBetter, /* higher-priority interface became RUNNING */
+ dcNewAP, /* scan completed on higher-priority i/f */
+ dcGone, /* periodic wireless scan showed disconnect */
+ dcFaded, /* periodic scan showed "very weak" signal */
+ dcAllDown, /* all-but-one taken down (initial LLP) */
+ dcUnwanted, /* another higher-priority interface is up */
+ dcShutdown, /* daemon is being shut down */
+ dcSelect, /* different AP selected (forced down/up) */
+ dcRemoved, /* interface removed from system */
+ dcFailed /* interface bring-up failed */
+} libnwam_diag_cause_t;
+
+typedef struct libnwam_event_data_s {
+ libnwam_descr_evtype_t led_type;
+ libnwam_diag_cause_t led_cause;
+ struct in_addr led_v4address; /* deInterfaceUp only */
+ int led_prefixlen; /* deInterfaceUp only */
+ libnwam_wlan_attr_t led_wlan;
+ char *led_interface;
+} libnwam_event_data_t;
+
+typedef enum libnwam_ipv4src_e {
+ IPV4SRC_STATIC,
+ IPV4SRC_DHCP
+} libnwam_ipv4src_t;
+
+typedef enum libnwam_interface_type_e {
+ IF_UNKNOWN,
+ IF_WIRED,
+ IF_WIRELESS,
+ IF_TUN
+} libnwam_interface_type_t;
+
+typedef struct libnwam_llp_s {
+ const char *llp_interface;
+ int llp_pri; /* lower number => higher priority */
+ libnwam_interface_type_t llp_type;
+ libnwam_ipv4src_t llp_ipv4src;
+ boolean_t llp_primary; /* selected primary interface */
+ boolean_t llp_locked; /* selected is locked */
+ boolean_t llp_link_failed; /* unusable due to link failure */
+ boolean_t llp_dhcp_failed; /* unusable due to DHCP failure */
+ boolean_t llp_link_up; /* datalink layer is up */
+ boolean_t llp_need_wlan; /* wlan/AP not yet selected */
+ boolean_t llp_need_key; /* wlan key not set */
+} libnwam_llp_t;
+
+typedef struct libnwam_wlan_s {
+ libnwam_wlan_attr_t wlan_attrs;
+ const char *wlan_interface;
+ boolean_t wlan_known;
+ boolean_t wlan_haskey;
+ boolean_t wlan_connected;
+} libnwam_wlan_t;
+
+typedef struct libnwam_known_ap_s {
+ const char *ka_essid;
+ const char *ka_bssid;
+ boolean_t ka_haskey;
+} libnwam_known_ap_t;
+
+extern libnwam_event_data_t *libnwam_wait_event(void);
+extern void libnwam_free_event(libnwam_event_data_t *);
+extern libnwam_llp_t *libnwam_get_llp_list(uint_t *);
+extern void libnwam_free_llp_list(libnwam_llp_t *);
+extern int libnwam_set_llp_priority(const char *, int);
+extern int libnwam_lock_llp(const char *);
+extern libnwam_wlan_t *libnwam_get_wlan_list(uint_t *);
+extern void libnwam_free_wlan_list(libnwam_wlan_t *);
+extern libnwam_known_ap_t *libnwam_get_known_ap_list(uint_t *);
+extern void libnwam_free_known_ap_list(libnwam_known_ap_t *);
+extern int libnwam_add_known_ap(const char *, const char *);
+extern int libnwam_delete_known_ap(const char *, const char *);
+extern int libnwam_select_wlan(const char *, const char *, const char *);
+extern int libnwam_wlan_key(const char *, const char *, const char *,
+ const char *);
+extern int libnwam_start_rescan(const char *);
+extern int libnwam_fini(void);
+extern int libnwam_init(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBNWAM_H */
diff --git a/usr/src/lib/libnwam/common/llib-lnwam b/usr/src/lib/libnwam/common/llib-lnwam
new file mode 100644
index 0000000000..396526a369
--- /dev/null
+++ b/usr/src/lib/libnwam/common/llib-lnwam
@@ -0,0 +1,29 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libnwam.h>
diff --git a/usr/src/lib/libnwam/common/mapfile-vers b/usr/src/lib/libnwam/common/mapfile-vers
new file mode 100644
index 0000000000..c3293a9228
--- /dev/null
+++ b/usr/src/lib/libnwam/common/mapfile-vers
@@ -0,0 +1,47 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+SUNWprivate_1.1 {
+ global:
+ libnwam_wait_event;
+ libnwam_free_event;
+ libnwam_get_llp_list;
+ libnwam_free_llp_list;
+ libnwam_set_llp_priority;
+ libnwam_lock_llp;
+ libnwam_get_wlan_list;
+ libnwam_free_wlan_list;
+ libnwam_get_known_ap_list;
+ libnwam_free_known_ap_list;
+ libnwam_add_known_ap;
+ libnwam_delete_known_ap;
+ libnwam_select_wlan;
+ libnwam_wlan_key;
+ libnwam_start_rescan;
+ libnwam_fini;
+ libnwam_init;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libnwam/i386/Makefile b/usr/src/lib/libnwam/i386/Makefile
new file mode 100644
index 0000000000..1d2aa5ae17
--- /dev/null
+++ b/usr/src/lib/libnwam/i386/Makefile
@@ -0,0 +1,29 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# lib/libnwam/i386/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libnwam/sparc/Makefile b/usr/src/lib/libnwam/sparc/Makefile
new file mode 100644
index 0000000000..93ab0970d2
--- /dev/null
+++ b/usr/src/lib/libnwam/sparc/Makefile
@@ -0,0 +1,29 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# lib/libnwam/sparc/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index f24cb88ed2..093e7a3b38 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -82,6 +82,7 @@ solaris.mms.io.read:::Read Permission for MMS Volumes::help=AuthMMSIORead.html
solaris.mms.io.write:::Read and Write Permission for MMS Volumes::help=AuthMMSIOWrite.html
#
solaris.network.:::Network::help=NetworkHeader.html
+solaris.network.autoconf:::Network Auto-Magic Configuration::help=NetworkAutoconf.html
solaris.network.link.security:::Link Security::help=LinkSecurity.html
solaris.network.wifi.config:::Wifi Config::help=WifiConfig.html
solaris.network.wifi.wep:::Wifi Wep::help=WifiWep.html
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index ab610e0cc8..e2779afef0 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -108,6 +108,7 @@ HTMLENTS = \
SmfValueVscan.html \
SmfVscanStates.html \
SmfWpaStates.html \
+ NetworkAutoconf.html \
NetworkHeader.html \
WifiConfig.html \
WifiWep.html \
diff --git a/usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html b/usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html
new file mode 100644
index 0000000000..afdd492da9
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html
@@ -0,0 +1,38 @@
+<html>
+
+<!--
+ Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+-->
+
+<head>
+<!--
+meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+-->
+</head>
+<body>
+When Network Auto-Magic Configuration is in the Authorizations
+Included column, it grants permission to direct the activity of the
+nwamd(1M) daemon through the GUI interface, allowing the user to set
+link preferences and control Wireless Access Point selection.
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/help/profiles/Makefile b/usr/src/lib/libsecdb/help/profiles/Makefile
index aa6f9b2011..5b4a584789 100644
--- a/usr/src/lib/libsecdb/help/profiles/Makefile
+++ b/usr/src/lib/libsecdb/help/profiles/Makefile
@@ -21,8 +21,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
-#
# lib/libsecdb/help/profiles/Makefile
#
@@ -58,6 +56,7 @@ HTMLENTS = \
RtNDMPMngmnt.html \
RtNameServiceAdmin.html \
RtNameServiceSecure.html \
+ RtNetAutoconf.html \
RtNetIPsec.html \
RtNetMngmnt.html \
RtNetSecure.html \
diff --git a/usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html b/usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html
new file mode 100644
index 0000000000..28f5de6ccd
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html
@@ -0,0 +1,37 @@
+<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 2008 Sun Microsystems, Inc. All rights reserved.
+-- Use is subject to license terms.
+-->
+<head>
+<title></title>
+</head>
+<body>
+When Network Autoconf is in the Rights Included column, it grants the
+right to manage the behavior and priorities of the nwamd(1M) network
+auto-magic daemon.
+<p>
+If Network Autoconf is grayed, then you are not entitled to Add or
+Remove this right.
+<p>
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index c3815f128a..4c09ff6b49 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -23,7 +23,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#
#
# /etc/security/prof_attr
@@ -33,7 +32,7 @@
All:::Execute any command as the user or role:help=RtAll.html
Audit Control:::Configure Solaris Auditing:auths=solaris.audit.config,solaris.jobs.admin;help=RtAuditCtrl.html
Audit Review:::Review Solaris Auditing logs:auths=solaris.audit.read;help=RtAuditReview.html
-Console User:::Manage System as the Console User:profiles=Suspend To RAM,Suspend To Disk,Brightness,CPU Power Management;auths=solaris.system.shutdown;help=RtConsUser.html
+Console User:::Manage System as the Console User:profiles=Suspend To RAM,Suspend To Disk,Brightness,CPU Power Management,Network Autoconf;auths=solaris.system.shutdown;help=RtConsUser.html
Contract Observer:::Reliably observe any/all contract events:help=RtContractObserver.html
Device Management:::Control Access to Removable Media:auths=solaris.device.*;help=RtDeviceMngmnt.html
Printer Management:::Manage printers, daemons, spooling:auths=solaris.print.*,solaris.label.print,solaris.smf.manage.discovery.printers.*,solaris.smf.value.discovery.printers.*;help=RtPrntAdmin.html
@@ -59,7 +58,8 @@ MMS Administrator:::MMS Media Manager Administrator:auths=solaris.smf.manage.mms
MMS Operator:::MMS Media Manager Operator:auths=solaris.smf.manage.mms,solaris.mms.media.*,solaris.mms.request.*,solaris.mms.device.state.*,solaris.mms.device.log.*
MMS User:::MMS Tape User:auths=solaris.mms.io.*
NDMP Management:::Manage the NDMP service:auths=solaris.smf.manage.ndmp,solaris.smf.value.ndmp,solaris.smf.read.ndmp;help=RtNdmpMngmnt.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,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns;profiles=Network Wifi Management,Inetd Management;help=RtNetMngmnt.html
+Network Autoconf:::Manage network auto-magic configuration via nwamd:auths=solaris.network.autoconf;help=RtNetAutoconf.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,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns;profiles=Network Wifi Management,Inetd Management,Network Autoconf;help=RtNetMngmnt.html
Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh,solaris.smf.value.tnd;profiles=Network Wifi Security,Network Link Security,Network IPsec Management;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
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index 221992069e..9c69626855 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -298,6 +298,8 @@ COMMON_SUBDIRS= \
SUNWnisu \
SUNWntpr \
SUNWntpu \
+ SUNWnwamintr \
+ SUNWnwamintu \
SUNWocf \
SUNWocfd \
SUNWocfh \
diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com
index 1b728b6d78..79add75d8f 100644
--- a/usr/src/pkgdefs/SUNW0on/prototype_com
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com
@@ -18,7 +18,6 @@
#
# CDDL HEADER END
#
-#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -281,6 +280,7 @@ f none usr/lib/help/auths/locale/SmfValueSMB.html 444 root bin
f none usr/lib/help/auths/locale/AuthReadSMB.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueVscan.html 444 root bin
f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin
+f none usr/lib/help/auths/locale/NetworkAutoconf.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
f none usr/lib/help/auths/locale/WifiWep.html 444 root bin
@@ -349,6 +349,7 @@ f none usr/lib/help/profiles/locale/RtMediaBkup.html 444 root bin
f none usr/lib/help/profiles/locale/RtMediaRestore.html 444 root bin
f none usr/lib/help/profiles/locale/RtNameServiceAdmin.html 444 root bin
f none usr/lib/help/profiles/locale/RtNameServiceSecure.html 444 root bin
+f none usr/lib/help/profiles/locale/RtNetAutoconf.html 444 root bin
f none usr/lib/help/profiles/locale/RtNetIPsec.html 444 root bin
f none usr/lib/help/profiles/locale/RtNetMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/RtNetSecure.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWcslr/prototype_com b/usr/src/pkgdefs/SUNWcslr/prototype_com
index a427ce2385..7491ecf060 100644
--- a/usr/src/pkgdefs/SUNWcslr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcslr/prototype_com
@@ -22,8 +22,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
# This required package information file contains a list of package contents.
# The 'pkgmk' command uses this file to identify the contents of a package
# and their location on the development machine when building the package.
@@ -104,6 +102,7 @@ s none lib/libnsl.so=libnsl.so.1
f none lib/libnsl.so.1 755 root bin
s none lib/libnvpair.so=libnvpair.so.1
f none lib/libnvpair.so.1 755 root bin
+f none lib/libnwam.so.1 755 root bin
s none lib/libpam.so=libpam.so.1
f none lib/libpam.so.1 755 root bin
s none lib/libposix4.so.1=librt.so.1
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index 80fce41035..ce8b6de5ae 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -18,7 +18,6 @@
#
# CDDL HEADER END
#
-#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -473,6 +472,7 @@ f none usr/lib/help/auths/locale/C/JobsGrant.html 444 root bin
f none usr/lib/help/auths/locale/C/LoginEnable.html 444 root bin
f none usr/lib/help/auths/locale/C/LoginHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/LoginRemote.html 444 root bin
+f none usr/lib/help/auths/locale/C/NetworkAutoconf.html 444 root bin
f none usr/lib/help/auths/locale/C/NetworkHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/PriAdmin.html 444 root bin
f none usr/lib/help/auths/locale/C/ProfmgrHeader.html 444 root bin
@@ -571,6 +571,7 @@ f none usr/lib/help/profiles/locale/C/RtMediaBkup.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtMediaRestore.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtNameServiceAdmin.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtNameServiceSecure.html 444 root bin
+f none usr/lib/help/profiles/locale/C/RtNetAutoconf.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtNetIPsec.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtNetMngmnt.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtNetSecure.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWnwamintr/Makefile b/usr/src/pkgdefs/SUNWnwamintr/Makefile
new file mode 100644
index 0000000000..0ff58fdf86
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintr/Makefile
@@ -0,0 +1,35 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWnwamintr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWnwamintr/pkginfo.tmpl
new file mode 100644
index 0000000000..8a69335727
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintr/pkginfo.tmpl
@@ -0,0 +1,43 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWnwamintr"
+NAME="Solaris NWAM Internal Files - root"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Solaris NWAM internal files - root"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
diff --git a/usr/src/pkgdefs/SUNWnwamintr/prototype_com b/usr/src/pkgdefs/SUNWnwamintr/prototype_com
new file mode 100644
index 0000000000..835cbb48cd
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintr/prototype_com
@@ -0,0 +1,47 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWnwamintr
+#
+d none lib 755 root bin
+s none lib/libnwam.so=./libnwam.so.1
+f none lib/llib-lnwam 644 root bin
+f none lib/llib-lnwam.ln 644 root bin
diff --git a/usr/src/pkgdefs/SUNWnwamintr/prototype_i386 b/usr/src/pkgdefs/SUNWnwamintr/prototype_i386
new file mode 100644
index 0000000000..6f85cc6c1b
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintr/prototype_i386
@@ -0,0 +1,45 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWnwamintr
+#
diff --git a/usr/src/pkgdefs/SUNWnwamintr/prototype_sparc b/usr/src/pkgdefs/SUNWnwamintr/prototype_sparc
new file mode 100644
index 0000000000..91200ec6a1
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintr/prototype_sparc
@@ -0,0 +1,45 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWnwamintr
+#
diff --git a/usr/src/pkgdefs/SUNWnwamintu/Makefile b/usr/src/pkgdefs/SUNWnwamintu/Makefile
new file mode 100644
index 0000000000..0ff58fdf86
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintu/Makefile
@@ -0,0 +1,35 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl
new file mode 100644
index 0000000000..bde594d6c6
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl
@@ -0,0 +1,43 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWnwamintu"
+NAME="Solaris NWAM Internal Files - usr"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Solaris NWAM internal files - usr"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
diff --git a/usr/src/pkgdefs/SUNWnwamintu/prototype_com b/usr/src/pkgdefs/SUNWnwamintu/prototype_com
new file mode 100644
index 0000000000..051cd0f76e
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintu/prototype_com
@@ -0,0 +1,46 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWnwamintu
+#
+d none usr 755 root sys
+d none usr/include 755 root bin
+f none usr/include/libnwam.h 644 root bin
diff --git a/usr/src/pkgdefs/SUNWnwamintu/prototype_i386 b/usr/src/pkgdefs/SUNWnwamintu/prototype_i386
new file mode 100644
index 0000000000..d3632da695
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintu/prototype_i386
@@ -0,0 +1,45 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWnwamintu
+#
diff --git a/usr/src/pkgdefs/SUNWnwamintu/prototype_sparc b/usr/src/pkgdefs/SUNWnwamintu/prototype_sparc
new file mode 100644
index 0000000000..7f3f9c31b7
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWnwamintu/prototype_sparc
@@ -0,0 +1,45 @@
+#
+# 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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+#
+
+#!search <pathname pathname ...> # where to find pkg objects
+#!include <filename> # include another 'prototype' file
+#!default <mode> <owner> <group> # default used if not specified on entry
+#!<param>=<value> # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+# SUNWnwamintu
+#