summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/Makefile.lint6
-rw-r--r--usr/src/cmd/Adm/group1
-rw-r--r--usr/src/cmd/Adm/sun/passwd4
-rw-r--r--usr/src/cmd/Adm/sun/shadow2
-rw-r--r--usr/src/cmd/cmd-inet/lib/Makefile6
-rw-r--r--usr/src/cmd/cmd-inet/lib/netcfgd/Makefile (renamed from usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl)48
-rw-r--r--usr/src/cmd/cmd-inet/lib/netcfgd/netcfgd.c184
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/Makefile56
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/README441
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/conditions.c812
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/conditions.h52
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/create_loc_auto30
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/create_loc_nonet31
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/defines.h58
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/dlpi_events.c172
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/door.c918
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/door_if.c669
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/enm.c909
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/events.c1251
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/events.h120
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/functions.h140
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/interface.c1407
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ipf.conf.dfl (renamed from usr/src/pkgdefs/SUNWnwamintr/pkginfo.tmpl)35
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ipf6.conf.dfl40
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.c553
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.h37
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/llp.c1074
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/llp.h76
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/loc.c608
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/logging.c104
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/main.c607
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ncp.c706
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ncp.h59
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ncu.c1996
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ncu.h235
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ncu_ip.c1670
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/ncu_phys.c1925
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/objects.c474
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/objects.h171
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/routing_events.c637
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c431
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/structures.h262
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/sysevent_events.c185
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/util.c739
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/util.h103
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/variables.h68
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/wireless.c2493
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/Makefile12
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers2
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamadm/Makefile49
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.c1416
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.xcl95
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile70
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c4332
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h181
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.xcl159
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y904
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l291
-rw-r--r--usr/src/cmd/dladm/Makefile4
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_db.c4
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_main.c6
-rw-r--r--usr/src/cmd/fs.d/nfs/lib/selfcheck.c10
-rw-r--r--usr/src/cmd/ipf/svc/ipfilter.xml14
-rw-r--r--usr/src/cmd/svc/milestone/Makefile10
-rw-r--r--usr/src/cmd/svc/milestone/name-services.xml8
-rw-r--r--usr/src/cmd/svc/milestone/net-ipqos48
-rw-r--r--usr/src/cmd/svc/milestone/net-loc697
-rw-r--r--usr/src/cmd/svc/milestone/net-netmask76
-rw-r--r--usr/src/cmd/svc/milestone/net-nwam542
-rw-r--r--usr/src/cmd/svc/milestone/net-physical2
-rw-r--r--usr/src/cmd/svc/milestone/net-svc171
-rw-r--r--usr/src/cmd/svc/milestone/network-ipqos.xml98
-rw-r--r--usr/src/cmd/svc/milestone/network-location.xml246
-rw-r--r--usr/src/cmd/svc/milestone/network-netcfg.xml103
-rw-r--r--usr/src/cmd/svc/milestone/network-netmask.xml116
-rw-r--r--usr/src/cmd/svc/milestone/network-physical.xml55
-rw-r--r--usr/src/cmd/svc/seed/Makefile3
-rw-r--r--usr/src/cmd/svc/shell/ipf_include.sh21
-rw-r--r--usr/src/cmd/svc/shell/net_include.sh112
-rw-r--r--usr/src/head/auth_list.h9
-rw-r--r--usr/src/lib/Makefile4
-rw-r--r--usr/src/lib/libbsm/audit_event.txt11
-rw-r--r--usr/src/lib/libbsm/common/adt.xml116
-rw-r--r--usr/src/lib/libdladm/common/libdladm.c20
-rw-r--r--usr/src/lib/libdladm/common/libdlflow.c4
-rw-r--r--usr/src/lib/libdladm/common/secobj.c7
-rw-r--r--usr/src/lib/libinetcfg/Makefile5
-rw-r--r--usr/src/lib/libinetcfg/Makefile.com6
-rw-r--r--usr/src/lib/libinetcfg/common/inetcfg.c1041
-rw-r--r--usr/src/lib/libinetcfg/common/inetcfg.h58
-rw-r--r--usr/src/lib/libinetcfg/common/llib-linetcfg9
-rw-r--r--usr/src/lib/libinetcfg/common/mapfile-vers8
-rw-r--r--usr/src/lib/libnwam/Makefile23
-rw-r--r--usr/src/lib/libnwam/Makefile.com38
-rw-r--r--usr/src/lib/libnwam/README536
-rw-r--r--usr/src/lib/libnwam/amd64/Makefile (renamed from usr/src/pkgdefs/SUNWnwamintr/Makefile)14
-rw-r--r--usr/src/lib/libnwam/common/door.c758
-rw-r--r--usr/src/lib/libnwam/common/libnwam.h1162
-rw-r--r--usr/src/lib/libnwam/common/libnwam_audit.c82
-rw-r--r--usr/src/lib/libnwam/common/libnwam_backend.c466
-rw-r--r--usr/src/lib/libnwam/common/libnwam_enm.c658
-rw-r--r--usr/src/lib/libnwam/common/libnwam_error.c78
-rw-r--r--usr/src/lib/libnwam/common/libnwam_events.c357
-rw-r--r--usr/src/lib/libnwam/common/libnwam_files.c951
-rw-r--r--usr/src/lib/libnwam/common/libnwam_impl.h326
-rw-r--r--usr/src/lib/libnwam/common/libnwam_known_wlan.c855
-rw-r--r--usr/src/lib/libnwam/common/libnwam_loc.c1182
-rw-r--r--usr/src/lib/libnwam/common/libnwam_ncp.c1719
-rw-r--r--usr/src/lib/libnwam/common/libnwam_object.c744
-rw-r--r--usr/src/lib/libnwam/common/libnwam_priv.h167
-rw-r--r--usr/src/lib/libnwam/common/libnwam_util.c991
-rw-r--r--usr/src/lib/libnwam/common/libnwam_values.c1147
-rw-r--r--usr/src/lib/libnwam/common/libnwam_wlan.c76
-rw-r--r--usr/src/lib/libnwam/common/llib-lnwam3
-rw-r--r--usr/src/lib/libnwam/common/mapfile-vers183
-rw-r--r--usr/src/lib/libnwam/i386/Makefile4
-rw-r--r--usr/src/lib/libnwam/libnwam.xcl200
-rw-r--r--usr/src/lib/libnwam/sparc/Makefile3
-rw-r--r--usr/src/lib/libnwam/sparcv9/Makefile (renamed from usr/src/pkgdefs/SUNWnwamintu/Makefile)14
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt8
-rw-r--r--usr/src/lib/libsecdb/exec_attr.txt6
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile8
-rw-r--r--usr/src/lib/libsecdb/help/auths/NetworkAutoconfRead.html (renamed from usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html)9
-rw-r--r--usr/src/lib/libsecdb/help/auths/NetworkAutoconfSelect.html37
-rw-r--r--usr/src/lib/libsecdb/help/auths/NetworkAutoconfWlan.html38
-rw-r--r--usr/src/lib/libsecdb/help/auths/NetworkAutoconfWrite.html37
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfLocationStates.html36
-rw-r--r--usr/src/lib/libsecdb/help/auths/SysSyseventRead.html39
-rw-r--r--usr/src/lib/libsecdb/help/auths/SysSyseventWrite.html39
-rw-r--r--usr/src/lib/libsecdb/help/profiles/Makefile5
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtNetAutoconfAdmin.html38
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtNetAutoconfUser.html (renamed from usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html)11
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt7
-rw-r--r--usr/src/lib/libsecdb/user_attr.txt4
-rw-r--r--usr/src/lib/libsysevent/libsysevent.c3
-rw-r--r--usr/src/pkgdefs/Makefile2
-rw-r--r--usr/src/pkgdefs/SUNW0on/prototype_com11
-rw-r--r--usr/src/pkgdefs/SUNWarcr/prototype_com4
-rw-r--r--usr/src/pkgdefs/SUNWcnetr/postinstall13
-rw-r--r--usr/src/pkgdefs/SUNWcnetr/prototype_com20
-rw-r--r--usr/src/pkgdefs/SUNWcslr/prototype_com3
-rw-r--r--usr/src/pkgdefs/SUNWcsr/prototype_com10
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com13
-rw-r--r--usr/src/pkgdefs/SUNWhea/prototype_com1
-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/prototype_com46
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/prototype_i38645
-rw-r--r--usr/src/pkgdefs/SUNWnwamintu/prototype_sparc45
-rw-r--r--usr/src/pkgdefs/common_files/i.ftpusers4
-rw-r--r--usr/src/pkgdefs/common_files/i.group17
-rw-r--r--usr/src/pkgdefs/common_files/i.passwd34
-rw-r--r--usr/src/pkgdefs/common_files/i.shadow23
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3863
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc3
-rw-r--r--usr/src/tools/pmodes/exceptions.h10
-rw-r--r--usr/src/tools/protocmp/stdusers.c5
-rw-r--r--usr/src/tools/scripts/bfu.sh62
-rw-r--r--usr/src/uts/common/sys/param.h3
160 files changed, 38104 insertions, 9232 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint
index 3995f7d9f7..8600211ab7 100644
--- a/usr/src/Makefile.lint
+++ b/usr/src/Makefile.lint
@@ -20,7 +20,7 @@
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -68,6 +68,8 @@ COMMON_SUBDIRS = \
cmd/clinfo \
cmd/cmd-crypto \
cmd/cmd-inet/lib \
+ cmd/cmd-inet/lib/netcfgd \
+ cmd/cmd-inet/lib/nwamd \
cmd/cmd-inet/sbin \
cmd/cmd-inet/usr.bin \
cmd/cmd-inet/usr.lib/bridged \
@@ -85,6 +87,8 @@ COMMON_SUBDIRS = \
cmd/cmd-inet/usr.sadm \
cmd/cmd-inet/usr.sbin \
cmd/cmd-inet/usr.sbin/ilbadm \
+ cmd/cmd-inet/usr.sbin/nwamadm \
+ cmd/cmd-inet/usr.sbin/nwamcfg \
cmd/col \
cmd/compress \
cmd/consadm \
diff --git a/usr/src/cmd/Adm/group b/usr/src/cmd/Adm/group
index 0cb3ea4c0e..2b8dcf49ef 100644
--- a/usr/src/cmd/Adm/group
+++ b/usr/src/cmd/Adm/group
@@ -16,6 +16,7 @@ smmsp::25:
gdm::50:
upnp::52:
xvm::60:
+netadm::65:
mysql::70:
openldap::75:
webservd::80:
diff --git a/usr/src/cmd/Adm/sun/passwd b/usr/src/cmd/Adm/sun/passwd
index e395f80c4f..89c1ebdc08 100644
--- a/usr/src/cmd/Adm/sun/passwd
+++ b/usr/src/cmd/Adm/sun/passwd
@@ -6,7 +6,9 @@ adm:x:4:4:Admin:/var/adm:
lp:x:71:8:Line Printer Admin:/usr/spool/lp:
uucp:x:5:5:uucp Admin:/usr/lib/uucp:
nuucp:x:9:9:uucp Admin:/var/spool/uucppublic:/usr/lib/uucp/uucico
-dladm:x:15:3:Datalink Admin:/:
+dladm:x:15:65:Datalink Admin:/:
+netadm:x:16:65:Network Admin:/:
+netcfg:x:17:65:Network Configuration Admin:/:
smmsp:x:25:25:SendMail Message Submission Program:/:
listen:x:37:4:Network Admin:/usr/net/nls:
gdm:x:50:50:GDM Reserved UID:/var/lib/gdm:
diff --git a/usr/src/cmd/Adm/sun/shadow b/usr/src/cmd/Adm/sun/shadow
index da4e2f3988..fb36627446 100644
--- a/usr/src/cmd/Adm/sun/shadow
+++ b/usr/src/cmd/Adm/sun/shadow
@@ -7,6 +7,8 @@ lp:NP:6445::::::
uucp:NP:6445::::::
nuucp:NP:6445::::::
dladm:*LK*:::::::
+netadm:*LK*:::::::
+netcfg:*LK*:::::::
smmsp:NP:6445::::::
listen:*LK*:::::::
gdm:*LK*:::::::
diff --git a/usr/src/cmd/cmd-inet/lib/Makefile b/usr/src/cmd/cmd-inet/lib/Makefile
index 3818acfa67..f497fc41b5 100644
--- a/usr/src/cmd/cmd-inet/lib/Makefile
+++ b/usr/src/cmd/cmd-inet/lib/Makefile
@@ -19,13 +19,11 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
-SUBDIRS= nwamd
+SUBDIRS= nwamd netcfgd
MSGSUBDIRS= nwamd
include ../../Makefile.cmd
diff --git a/usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl b/usr/src/cmd/cmd-inet/lib/netcfgd/Makefile
index bde594d6c6..1c480c7957 100644
--- a/usr/src/pkgdefs/SUNWnwamintu/pkginfo.tmpl
+++ b/usr/src/cmd/cmd-inet/lib/netcfgd/Makefile
@@ -18,26 +18,32 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+
+#
+# Copyright 2010 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"
+# usr/src/cmd/cmd-inet/lib/netcfgd/Makefile
+#
+
+PROG= netcfgd
+
+include ../../../Makefile.cmd
+
+ROOTCMDDIR= $(ROOT)/lib/inet
+
+LDLIBS += -lnwam -lumem
+
+.KEEP_STATE:
+
+.PARALLEL:
+
+all: $(PROG)
+
+install: all $(ROOTCMD)
+
+clean:
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/lib/netcfgd/netcfgd.c b/usr/src/cmd/cmd-inet/lib/netcfgd/netcfgd.c
new file mode 100644
index 0000000000..5002e9e808
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/netcfgd/netcfgd.c
@@ -0,0 +1,184 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * netcfgd - network configuration daemon. At present, this daemon implements
+ * the configuration backend for libnwam (via calls to nwam_backend_init()
+ * and nwam_backend_fini()). Initialization of the backend creates a door
+ * that libnwam calls use to read, update and destroy persistent configuration.
+ *
+ * More long-term, netcfgd will be used to manage other sources of configuration
+ * data and the backend functionality currently contained in libnwam will be
+ * generalized.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <wait.h>
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+static const char *progname;
+static boolean_t fg = B_FALSE;
+
+/*
+ * This function allows you to drop a dtrace probe here and trace
+ * complete strings (not just those containing formatting). It's
+ * important that we actually format the strings so we could trace them
+ * even if we choose not to log them.
+ */
+static void
+log_out(int severity, const char *str)
+{
+ if (fg) {
+ (void) fprintf(stderr, "%s: %s\n", progname, str);
+ } else {
+ syslog(severity, str);
+ }
+}
+
+/* PRINTFLIKE2 */
+void
+nlog(int severity, const char *fmt, ...)
+{
+ va_list ap;
+ char *vbuf;
+
+ va_start(ap, fmt);
+ if (vasprintf(&vbuf, fmt, ap) != -1) {
+ log_out(severity, vbuf);
+ free(vbuf);
+ }
+ va_end(ap);
+}
+
+static void
+start_logging(void)
+{
+ if (!fg)
+ openlog(progname, LOG_PID, LOG_DAEMON);
+
+ nlog(LOG_DEBUG, "%s started", progname);
+}
+
+static void
+daemonize(void)
+{
+ pid_t pid;
+
+ /*
+ * A little bit of magic here. By the first fork+setsid, we
+ * disconnect from our current controlling terminal and become
+ * a session group leader. By forking again without calling
+ * setsid again, we make certain that we are not the session
+ * group leader and can never reacquire a controlling terminal.
+ */
+ if ((pid = fork()) == -1) {
+ nlog(LOG_ERR, "fork 1 failed");
+ exit(EXIT_FAILURE);
+ }
+ if (pid != 0) {
+ (void) wait(NULL);
+ nlog(LOG_DEBUG, "child %ld exited, daemonizing", pid);
+ _exit(0);
+ }
+ if (setsid() == (pid_t)-1) {
+ nlog(LOG_ERR, "setsid");
+ exit(EXIT_FAILURE);
+ }
+ if ((pid = fork()) == -1) {
+ nlog(LOG_ERR, "fork 2 failed");
+ exit(EXIT_FAILURE);
+ }
+ if (pid != 0) {
+ _exit(0);
+ }
+ (void) chdir("/");
+ (void) umask(022);
+}
+
+/* ARGSUSED */
+static void
+graceful_shutdown(int signo)
+{
+ nwam_backend_fini();
+ exit(EXIT_SUCCESS);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ nwam_error_t err;
+
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ fg = B_TRUE;
+ break;
+ default:
+ (void) fprintf(stderr, "%s: unrecognized option %c\n",
+ progname, optopt);
+ exit(EXIT_FAILURE);
+ }
+ }
+ start_logging();
+
+ if (!fg)
+ daemonize();
+
+ (void) signal(SIGTERM, graceful_shutdown);
+ (void) signal(SIGQUIT, graceful_shutdown);
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, SIG_IGN);
+ (void) atexit(nwam_backend_fini);
+
+ if ((err = nwam_backend_init()) != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "couldn't initialize libnwam backend: %s",
+ nwam_strerror(err));
+ exit(EXIT_FAILURE);
+ }
+
+ for (;;)
+ (void) pause();
+
+ /* NOTREACHED */
+ return (EXIT_SUCCESS);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/Makefile b/usr/src/cmd/cmd-inet/lib/nwamd/Makefile
index 02c930618d..9d163b1aba 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/Makefile
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/Makefile
@@ -20,7 +20,7 @@
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# usr/src/cmd/cmd-inet/lib/nwamd/Makefile
@@ -30,20 +30,41 @@
include ../../../../lib/Makefile.lib
PROG= nwamd
-OBJS= door.o events.o interface.o llp.o main.o \
- state_machine.o util.o wireless.o
+OBJS= conditions.o dlpi_events.o door_if.o enm.o\
+ events.o known_wlans.o llp.o loc.o logging.o\
+ main.o ncp.o ncu.o ncu_phys.o ncu_ip.o objects.o\
+ routing_events.o sysevent_events.o util.o
SRCS= $(OBJS:%.o=%.c)
-HEADERS= defines.h functions.h structures.h variables.h
+HEADERS= conditions.h events.h known_wlans.h llp.h ncp.h ncu.h\
+ objects.h
+LOCFILES= create_loc_auto create_loc_nonet
+NONETLOCFILES= ipf.conf.dfl ipf6.conf.dfl
+
+ROOTCFGDIR= $(ROOTETC)/nwam
+ROOTLOCDIR= $(ROOTCFGDIR)/loc
+NONETLOCDIR= $(ROOTLOCDIR)/NoNet
+LOCDIRS= $(NONETLOCDIR)
+ROOTCFGFILES= $(LOCFILES:%=$(ROOTLOCDIR)/%) \
+ $(NONETLOCFILES:%=$(NONETLOCDIR)/%)
include ../../../Makefile.cmd
+$(ROOTCFGFILES) := FILEMODE= 644
+
POFILE= $(PROG).po
-POFILES= interface.po wireless.po
ROOTCMDDIR= $(ROOTFS_LIBDIR)/inet
-LDLIBS += -lsocket -lnsl -linetcfg -linetutil -lumem -lscf -ldladm \
- -lgen -lsecdb -lbsm -lsysevent -lnvpair
+LDLIBS += -ldhcpagent -ldhcputil -ldladm -ldlpi -lgen \
+ -linetcfg -linetutil -lkstat -lnsl -lnvpair -lnwam \
+ -lsecdb -lscf -lsocket -lsysevent -lumem -luutil
+
+#
+# Instrument with CTF data to ease debugging.
+#
+CTFCONVERT_HOOK = && $(CTFCONVERT_O)
+CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS)
+$(OBJS) := CFLAGS += $(CTF_FLAGS)
.KEEP_STATE:
@@ -52,20 +73,35 @@ LDLIBS += -lsocket -lnsl -linetcfg -linetutil -lumem -lscf -ldladm \
all: $(PROG)
$(PROG): $(OBJS)
- $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(CTFMERGE_HOOK)
$(POST_PROCESS)
-install: $(ROOTCMD)
+install: $(ROOTCMD) $(ROOTLOCDIR) $(ROOTCFGFILES)
check: $(SRCS) $(HEADERS)
$(CSTYLE) -cpP $(SRCS) $(HEADERS)
-$(ROOTCMD): $(PROG)
+$(ROOTCMD): all
clean:
$(RM) $(OBJS)
lint: lint_SRCS
+$(ROOTCFGDIR):
+ $(INS.dir)
+
+$(ROOTLOCDIR): $(ROOTCFGDIR)
+ $(INS.dir)
+
+$(LOCDIRS): $(ROOTLOCDIR)
+ $(INS.dir)
+
+$(ROOTLOCDIR)/%: $(ROOTLOCDIR) %
+ $(INS.file)
+
+$(NONETLOCDIR)/%: $(NONETLOCDIR) %
+ $(INS.file)
+
include ../../../Makefile.targ
include ../../Makefile.msg
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/README b/usr/src/cmd/cmd-inet/lib/nwamd/README
new file mode 100644
index 0000000000..e94866e6a3
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/README
@@ -0,0 +1,441 @@
+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 2010 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
+
+Implementation Overview for the NetWork AutoMagic daemon
+John Beck, Renee Danson, Michael Hunter, Alan Maguire, Kacheong Poon,
+Garima Tripathi, Jan Xie, Anurag Maskey
+[Structure and some content shamelessly stolen from Peter Memishian's
+dhcpagent architecture overview.]
+
+INTRODUCTION
+============
+
+Details about the NWAM requirements, architecture, and design are
+available via the NWAM opensolaris project at
+http://opensolaris.org/os/project/nwam. The point of this document is
+to place details relevant to somebody attempting to understand the
+implementation close to the source code.
+
+THE BASICS
+==========
+
+SOURCE FILE ORGANIZATION
+=======================
+event sources:
+ dlpi_events.c
+ routing_events.c
+ sysevent_events.c
+
+object-specific event handlers:
+ enm.c
+ known_wlans.c
+ loc.c
+ ncp.c
+ ncu_ip.c
+ ncu_phys.c
+
+legacy config upgrade
+ llp.c
+
+generic code:
+ objects.c
+ events.c
+ conditions.c
+ logging.c
+ util.c
+
+nwam door requests:
+ door_if.c
+
+entry point:
+ main.c
+
+OVERVIEW
+========
+
+Here we discuss the essential objects and subtle aspects of the NWAM
+daemon implementation. Note that there is of course much more that is
+not discussed here, but after this overview you should be able to fend
+for yourself in the source code.
+
+Events and Objects
+==================
+
+Events come to NWAM from a variety of different sources asyncronously.
+
+o routing socket
+o dlpi
+o sysevents
+o doors
+
+Routing sockets and dlpi (DL_NOTE_LINK_UP|DOWN events) are handled by
+dedicated threads. Sysevents and doors are both seen as callbacks into
+the process proper and will often post their results to the main event
+queue. All event sources post events onto the main event queue. In
+addition state changes of objects and door requests (requesting current
+state or a change of state, specification of a WiFi key etc) can
+lead to additional events. We have daemon-internal events (object
+initialization, periodic state checks) which are simply enqueued
+on the event queue, and external events which are both enqueued on
+the event queue and sent to registered listeners (via nwam_event_send()).
+
+So the structure of the daemon is a set of threads that drive event
+generation. Events are posted either directly onto the event queue
+or are delayed by posting onto the pending event queue. SIGALARMs
+are set for the event delay, and when the SIGALARM is received
+pending events that have expired are moved onto the event queue
+proper. Delayed enqueueing is useful for periodic checks.
+
+Decisions to change conditions based upon object state changes are
+delayed until after bursts of events. This is achieved by marking a
+flag when it is deemed checking is necessary and then the next time the
+queue is empty performing those checks. A typical event profile will
+be one event (e.g. a link down) causing a flurry of other events (e.g.
+related interface down). By waiting until all the consequences of the
+initial event have been carried out to make higher level decisions we
+implicitly debounce those higher level decisions.
+
+At the moment queue quiet actually means that the queue has been quiet
+for some short period of time (.1s). Typically the flurry of events we
+want to work through are internally generated and are back to back in
+the queue. We wait a bit longer in case there are reprucussions from
+what we do that cause external events to be posted on us. We are not
+interested in waiting for longer term things to happen but merely to
+catch immediate changes.
+
+When running, the daemon will consist of a number of threads:
+
+o the event handling thread: a thread blocking until events appear on the
+ event queue, processing each event in order. Events that require
+ time-consuming processing are spawned in worker threads (e.g. WiFi
+ connect, DHCP requests etc).
+o door request threads: the door infrastructure manages server threads
+ which process synchronous NWAM client requests (e.g. get state of an
+ object, connect to a specific WLAN, initiate a scan on a link etc).
+o various wifi/IP threads: threads which do asynchronous work such as
+ DHCP requests, WLAN scans etc that cannot hold up event processing in
+ the main event handling thread.
+o routing socket threads: process routing socket messages of interest
+ (address additons/deletions) and package them as NWAM messages.
+o dlpi threads: used to monitor for DL_NOTE_LINK messages on links
+
+The daemon is structured around a set of objects representing NCPs[1],
+NCUs[2], ENMs[3] and known WLANs and a set of state machines which
+consume events which act on those objects. Object lists are maintained
+for each object type, and these contain both a libnwam handle (to allow
+reading the object directly) and an optional object data pointer which
+can point to state information used to configure the object.
+
+Events can be associated with specific objects (e.g. link up), or associated
+with no object in particular (e.g. shutdown).
+
+Each object type registers a set of event handler functions with the event
+framework such that when an event occurs, the appropriate handler for the
+object type is used. The event handlers are usually called
+nwamd_handle_*_event().
+
+[1] NCP Network Configuration Profile; the set of link- and IP-layer
+configuration units which collectively specify how a system should be
+connected to the network
+
+[2] NCU Network Configuration Unit; the individual components of an NCP
+
+[3] ENM External Network Modifiers; user executable scripts often used
+to configure a VPN
+
+Doors and External Events
+=========================
+
+The command interface to nwamd is thread a door at NWAM_DOOR
+(/etc/svc/volatile/nwam/nwam_door). This door allows external program to send
+messages to nwamd. The way doors work is to provide a mechanism for
+another process to execute code in your process space. This looks like
+a CSPish send/receive/reply in that the receiving process provide a
+syncronization point (via door_create(3C)), the calling process uses
+that syncronization point to rendezvous with and provide arguments (via
+door_call(3C), and then the receive process reply (via
+door_return(3C))) passing back data as required. The OS makes it such
+that the memory used to pass data via door_call(3C) is mapped into the
+receiving process which can write back into it and then transparently
+have it mapped back to the calling process.
+
+As well as handling internal events of interest, the daemon also needs
+to send events of interest (link up/down, WLAN scan/connect results etc)
+to (possibly) multiple NWAM client listeners. This is done via
+System V message queues. On registering for events via a libnwam door
+request into the daemon (nwam_events_register()), a per-client
+(identified by pid) message queue file is created. The
+daemon sends messages to all listeners by examining the list of
+message queue files (allowing registration to be robust across
+daemon restarts) and sending events to each listener. This is done
+via the libnwam function nwam_event_send() which hides the IPC
+mechanism from the daemon.
+
+Objects
+=======
+Four object lists are maintained within the daemon - one each for
+the configuration objects libnwam manages. i.e.:
+
+o ENMs
+o locations
+o known WLANs
+o NCUs of the current active NCP
+
+Objects have an associated libnwam handle and an optional data
+field (which is used for NCUs only).
+
+Locking is straightforward - nwamd_object_init() will initialize
+an object of a particular type in the appropriate object list,
+returning it with the object lock held. When it is no longer needed,
+nwamd_object_unlock() should be called on the object.
+
+To retrieve an existing object, nwamd_object_find() should be
+called - again this returns the object in a locked state.
+
+nwamd_object_lock() is deliberately not exposed outside of objects.c,
+since object locking is implicit in the above creation/retrieval
+functions.
+
+An object is removed from the object list (with handle destroyed)
+via nwamd_object_fini() - the object data (if any) is returned
+from this call to allow deallocation.
+
+Object state
+============
+nwamd deals with 3 broad types of object that need to maintain
+internal state: NCUs, ENMs and locations (known WLANs are configuration
+objects but don't have a state beyond simply being present).
+NWAM objects all share a basic set of states:
+
+State Description
+===== ===========
+uninitialized object representation not present on system or in nwamd
+initialized object representation present in system and in nwamd
+disabled disabled manually
+offline external conditions are not satisfied
+offline* external conditions are satisfied, trying to move online
+online* external conditions no longer satisfied, trying to move offline
+online conditions satisfied and configured
+maintenance error occurred in applying configuration
+
+These deliberately mimic SMF states.
+
+The states of interest are offline, offline* and online.
+
+An object (link/interface NCU, ENM or location) should only move online
+when its conditions are satisfied _and_ its configuration has been successfully
+applied. This occurs when an ENM method has run or a link is up, or an
+interface has at least one address assigned.
+
+To understand the distinction between offline and offline*, consider the case
+where a link is of prioritized activation, and either is a lower priority
+group - and hence inactive (due to cable being unplugged or inability to
+connect to wifi) - or a higher priority group - and hence active. In general,
+we want to distinguish between two cases:
+
+1) when we are actively configuring the link with a view to moving online
+(offline*), as would be the case when the link's priority group is
+active.
+2) when external policy-based conditions prevent a link from being active.
+offline should be used for such cases. Links in priority groups above and
+below the currently-active group will be offline, since policy precludes them
+from activating (as less-prioritized links).
+
+So we see that offline and offline* can thus be used to distinguish between
+cases that have the potentiality to move online (offline*) from a policy
+perspective - i.e. conditions on the location allow it, or link prioritization
+allows it - and cases where external conditions dictate that it should not
+(offline).
+
+Once an object reaches offline*, its configuration processes should kick in.
+This is where auxiliary state is useful, as it allows us to distinguish between
+various states in that configuration process. For example, a link can be
+waiting for WLAN selection or key data, or an interface can be waiting for
+DHCP response. This auxiliary state can then also be used diagnostically by
+libnwam consumers to determine the current status of a link, interface, ENM
+etc.
+
+WiFi links present a problem however. On the one hand, we want them
+to be inactive when they are not part of the current priority grouping,
+while on the other we want to watch out for new WLANs appearing in
+scan data if the WiFi link is of a higher priority than the currently-selected
+group. The reason we watch out for these is they represent the potential
+to change priority grouping to a more preferred group. To accommodate this,
+WiFi links of the same or lower (more preferred) priority group will always
+be trying to connect (and thus be offline* if they cannot).
+
+It might appear unnecessary to have a separate state value/machine for
+auxiliary state - why can't we simply add the auxiliary state machine to the
+global object state machine? Part of the answer is that there are times we
+need to run through the same configuration state machine when the global
+object state is different - in paticular either offline* or online. Consider
+WiFi - we want to do periodic scans to find a "better" WLAN - we can easily
+do this by running back through the link state machine of auxiliary
+states, but we want to stay online while we do it, since we are still
+connected (if the WLAN disconnects of course we go to LINK_DOWN and offline).
+
+Another reason we wish to separate the more general states (offline, online
+etc) from the more specific ones (WIFI_NEED_SELECTION etc) is to ensure
+that the representation of configuration objects closely matches the way
+SMF works.
+
+For an NCU physical link, the following link-specific auxiliary states are
+used:
+
+Auxiliary state Description
+=============== ===========
+
+LINK_WIFI_SCANNING Scan in progress
+LINK_WIFI_NEED_SELECTION Need user to specify WLAN
+LINK_WIFI_NEED_KEY Need user to specify a WLAN key for selection
+LINK_WIFI_CONNECTING Connecting to current selection
+
+A WiFI link differs from a wired one in that it always has the
+potential to be available - it just depends if visited WLANs are in range.
+So such links - if they are higher in the priority grouping than the
+currently-active priority group - should always be able to scan, as they
+are always "trying" to be activated.
+
+Wired links that do not support DL_NOTE_LINK_UP/DOWN are problematic,
+since we have to simply assume a cable is plugged in. If an IP NCU
+is activated above such a link, and that NCU uses DHCP, a timeout
+will be triggered eventually (user-configurable via the nwamd/ncu_wait_time
+SMF property of the network/physical:nwam instance) which will cause
+us to give up on the link.
+
+For an IP interface NCU, the following auxiliary states are suggested.
+
+Auxiliary state Description
+=============== ===========
+
+NWAM_AUX_STATE_IF_WAITING_FOR_ADDR Waiting for an address to be assigned
+NWAM_AUX_STATE_IF_DHCP_TIMED_OUT DHCP timed out on interface
+
+A link can have multiple logical interfaces plumbed on it consisting
+of a mix of static and DHCP-acquired addresses. This means that
+we need to decide how to aggregate the state of these logical
+interfaces into the NCU state. The concept of "up" we use here
+does not correspond to IFF_UP or IFF_RUNNING, but rather
+when we get (via getting RTM_NEWADDR events with non-zero
+addresses) at least one address assigned to the link.
+
+We use this concept of up as it represents the potential for
+network communication - e.g. after assigning a static
+address, if the location specifies nameserver etc, it
+is possible to communicate over the network. One important
+edge case here is that when DHCP information comes
+in, we need to reassess location activation conditions and
+possibly change or reapply the current location. The problem
+is that if we have a static/DHCP mix, and if we rely on
+the IP interface's notion of "up" to trigger location activation,
+we will likely first apply the location when the static address
+has been assigned and before the DHCP information has
+been returned (which may include nameserver info). So
+the solution is that on getting an RTM_NEWADDR, we
+check if the (logical) interface associated is DHCP, and
+even if the interface NCU is already up, we reassess
+location activation. This will lead to a reapplication of
+the current location or possibly a location switch.
+
+In order to move through the various states, a generic
+API is supplied
+
+nwam_error_t
+nwamd_object_set_state(nwamd_object_t obj, nwamd_state_t state,
+ nwamd_aux_state_t aux_state);
+
+This function creates an OBJECT_STATE event containing
+the new state/aux_state and enqueues it in the event
+queue. Each object registers its own handler for this
+event, and in response to the current state/aux state and
+desired aux state it responds appropriately in the event
+handling thread, spawning other threads to carry out
+actions as appropriate. The object state event is
+then sent to any registered listeners.
+
+So for NCUs, we define a handle_object_state() function
+to run the state machine for the NCU object.
+
+Link state and NCP policy
+=========================
+
+NCPs can be either:
+
+o prioritized: where the constituent link NCUs specify priority group
+ numbers (where lower are more favoured) and grouping types. These
+ are used to allow link NCUs to be either grouped separately (exclusive)
+ or together (shared or all).
+o manual: their activation is governed by the value of their enabled
+ property.
+o a combination of the above.
+
+IP interface NCUs interit their activation from the links below them,
+so an IP interface NCU will be active if its underlying link is (assuming
+it hasn't been disabled).
+
+At startup, and at regular intervals (often triggered by NWAM
+events), the NCP policy needs to be reassessed. There
+are a number of causes for NCP policy to be reassessed -
+
+o a periodic check of link state that occurs every N seconds
+o a link goes from offline(*) to online (cable plug/wifi connect)
+o a link goes from online to offline (cable unplug/wifi disconnect).
+
+Any of these should cause the link selecton algorithm to rerun.
+
+The link selection algorithm works as follows:
+
+Starting from the lowest priority grouping value, assess all links
+in that priority group.
+
+The current priority-group is considered failed if:
+
+o "exclusive" NCUs exist and none are offline*/online,
+o "shared" NCUs exist and none are offline*/online,
+o "all" NCUs exist and all are not offline*/online,
+o no NCUs are offline*/online.
+
+We do not invalidate a link that is offline* since its configuration
+is in progress. This has the unfortunate side-effect that
+wired links that do not do DL_NOTE_LINK_UP/DOWN will never
+fail. If such links wish to be skipped, their priority group value
+should be increased (prioritizing wireless links).
+
+One a priority group has been selected, all links in groups above
+_and_ below it need to be moved offline.
+
+Location Activation
+===================
+A basic set of system-supplied locations are supplied - NoNet and
+Automatic. nwamd will apply the NoNet location until such a time
+as an interface NCU is online, at which point it will switch
+to the Automatic location. If a user-supplied location is supplied,
+and it is either manually enabled or its conditions are satisfied, it
+will be preferred and activated instead. Only one location can be
+active at once since each location has its own specification of nameservices
+etc.
+
+ENM Activation
+==============
+ENMs are either manual or conditional in activation and will be
+activated if they are enabled (manual) or if the conditions
+are met (conditional). Multiple ENMs can be active at once.
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/conditions.c b/usr/src/cmd/cmd-inet/lib/nwamd/conditions.c
new file mode 100644
index 0000000000..c564dd094d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/conditions.c
@@ -0,0 +1,812 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inet/ip.h>
+#include <inetcfg.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlwlan.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <libnwam.h>
+#include "conditions.h"
+#include "ncu.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * conditions.c - contains routines which check state to see if activation
+ * conditions for NWAM objects are satisfied and rates activation conditions to
+ * help determine which is most specific.
+ *
+ * If the activation-mode is CONDITIONAL_ANY or CONDITIONAL_ALL, the conditions
+ * property is set to a string made up of conditional expressions. Each
+ * expression is made up of a condition that can be assigned a boolean value,
+ * e.g. "system-domain is sun.com" or "ncu ip:bge0 is-not active". If the
+ * activation-mode is CONDITIONAL_ANY, the condition will be satisfied if any
+ * one of the conditions is true; if the activation-mode is CONDITIONAL_ALL,
+ * the condition is satisfied only if all of the conditions are true.
+ */
+
+uint64_t condition_check_interval = CONDITION_CHECK_INTERVAL_DEFAULT;
+
+extern int getdomainname(char *, int);
+
+/* NCP, NCU, ENM and location conditions */
+static boolean_t test_condition_ncp(nwam_condition_t condition,
+ const char *ncp_name);
+static boolean_t test_condition_ncu(nwam_condition_t condition,
+ const char *ncu_name);
+static boolean_t test_condition_enm(nwam_condition_t condition,
+ const char *enm_name);
+static boolean_t test_condition_loc(nwam_condition_t condition,
+ const char *loc_name);
+
+/* IP address conditions */
+static boolean_t test_condition_ip_address(nwam_condition_t condition,
+ const char *ip_address);
+
+/* domainname conditions */
+static boolean_t test_condition_sys_domain(nwam_condition_t condition,
+ const char *domainname);
+static boolean_t test_condition_adv_domain(nwam_condition_t condition,
+ const char *domainname);
+
+/* WLAN conditions */
+static boolean_t test_condition_wireless_essid(nwam_condition_t condition,
+ const char *essid);
+static boolean_t test_condition_wireless_bssid(nwam_condition_t condition,
+ const char *essid);
+
+struct nwamd_condition_map {
+ nwam_condition_object_type_t object_type;
+ boolean_t (*condition_func)(nwam_condition_t, const char *);
+} condition_map[] =
+{
+ { NWAM_CONDITION_OBJECT_TYPE_NCP, test_condition_ncp },
+ { NWAM_CONDITION_OBJECT_TYPE_NCU, test_condition_ncu },
+ { NWAM_CONDITION_OBJECT_TYPE_ENM, test_condition_enm },
+ { NWAM_CONDITION_OBJECT_TYPE_LOC, test_condition_loc },
+ { NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS, test_condition_ip_address },
+ { NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN, test_condition_sys_domain },
+ { NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN, test_condition_adv_domain },
+ { NWAM_CONDITION_OBJECT_TYPE_ESSID, test_condition_wireless_essid },
+ { NWAM_CONDITION_OBJECT_TYPE_BSSID, test_condition_wireless_bssid }
+};
+
+/*
+ * This function takes which kind of conditions (is or is not) we are testing
+ * the object against and an object and applies the conditon to the object.
+ */
+static boolean_t
+test_condition_object_state(nwam_condition_t condition,
+ nwam_object_type_t object_type, const char *object_name)
+{
+ nwamd_object_t object;
+ nwam_state_t state;
+
+ object = nwamd_object_find(object_type, object_name);
+ if (object == NULL)
+ return (B_FALSE);
+
+ state = object->nwamd_object_state;
+ nwamd_object_release(object);
+
+ switch (condition) {
+ case NWAM_CONDITION_IS:
+ return (state == NWAM_STATE_ONLINE);
+ case NWAM_CONDITION_IS_NOT:
+ return (state != NWAM_STATE_ONLINE);
+ default:
+ return (B_FALSE);
+ }
+}
+
+static boolean_t
+test_condition_ncp(nwam_condition_t condition, const char *name)
+{
+ boolean_t active;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ active = (strcasecmp(active_ncp, name) == 0);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ switch (condition) {
+ case NWAM_CONDITION_IS:
+ return (active);
+ case NWAM_CONDITION_IS_NOT:
+ return (active != B_TRUE);
+ default:
+ return (B_FALSE);
+ }
+}
+
+static boolean_t
+test_condition_ncu(nwam_condition_t condition, const char *name)
+{
+ char *real_name, *ncu_name;
+ nwam_ncu_handle_t ncuh;
+ nwam_ncu_type_t ncu_type;
+ boolean_t rv;
+
+ /* names are case-insensitive, so get real name from libnwam */
+ if (nwam_ncu_read(active_ncph, name, NWAM_NCU_TYPE_INTERFACE, 0, &ncuh)
+ == NWAM_SUCCESS) {
+ ncu_type = NWAM_NCU_TYPE_INTERFACE;
+ } else if (nwam_ncu_read(active_ncph, name, NWAM_NCU_TYPE_LINK, 0,
+ &ncuh) == NWAM_SUCCESS) {
+ ncu_type = NWAM_NCU_TYPE_LINK;
+ } else {
+ return (B_FALSE);
+ }
+ if (nwam_ncu_get_name(ncuh, &real_name) != NWAM_SUCCESS) {
+ nwam_ncu_free(ncuh);
+ return (B_FALSE);
+ }
+ nwam_ncu_free(ncuh);
+
+ /*
+ * Name may be either unqualified or qualified by NCU type
+ * (interface:/link:). Need to translate unqualified names
+ * to qualified, specifying interface:name if an interface
+ * NCU is present, otherwise link:ncu.
+ */
+ if (nwam_ncu_name_to_typed_name(real_name, ncu_type, &ncu_name)
+ != NWAM_SUCCESS) {
+ free(real_name);
+ return (B_FALSE);
+ }
+ free(real_name);
+
+ rv = test_condition_object_state(condition, NWAM_OBJECT_TYPE_NCU,
+ ncu_name);
+ free(ncu_name);
+ return (rv);
+}
+
+static boolean_t
+test_condition_enm(nwam_condition_t condition, const char *enm_name)
+{
+ nwam_enm_handle_t enmh;
+ char *real_name;
+ boolean_t rv;
+
+ /* names are case-insensitive, so get real name from libnwam */
+ if (nwam_enm_read(enm_name, 0, &enmh) != NWAM_SUCCESS)
+ return (B_FALSE);
+ if (nwam_enm_get_name(enmh, &real_name) != NWAM_SUCCESS) {
+ nwam_enm_free(enmh);
+ return (B_FALSE);
+ }
+ nwam_enm_free(enmh);
+
+ rv = test_condition_object_state(condition, NWAM_OBJECT_TYPE_ENM,
+ real_name);
+ free(real_name);
+ return (rv);
+}
+
+static boolean_t
+test_condition_loc(nwam_condition_t condition, const char *loc_name)
+{
+ nwam_loc_handle_t loch;
+ char *real_name;
+ boolean_t rv;
+
+ /* names are case-insensitive, so get real name from libnwam */
+ if (nwam_loc_read(loc_name, 0, &loch) != NWAM_SUCCESS)
+ return (B_FALSE);
+ if (nwam_loc_get_name(loch, &real_name) != NWAM_SUCCESS) {
+ nwam_loc_free(loch);
+ return (B_FALSE);
+ }
+ nwam_loc_free(loch);
+
+ rv = test_condition_object_state(condition, NWAM_OBJECT_TYPE_LOC,
+ real_name);
+ free(real_name);
+ return (rv);
+}
+
+static boolean_t
+test_condition_domain(nwam_condition_t condition, const char *target_domain,
+ const char *found_domain)
+{
+ int i, len_t, len_f;
+ char target[MAXHOSTNAMELEN], found[MAXHOSTNAMELEN];
+
+ len_t = target_domain == NULL ? 0 : strlen(target_domain);
+ len_f = found_domain == NULL ? 0 : strlen(found_domain);
+
+ /* convert target_domain and found_domain to lowercase for strstr() */
+ for (i = 0; i < len_t; i++)
+ target[i] = tolower(target_domain[i]);
+ target[len_t] = '\0';
+
+ for (i = 0; i < len_f; i++)
+ found[i] = tolower(found_domain[i]);
+ found[len_f] = '\0';
+
+ switch (condition) {
+ case NWAM_CONDITION_IS:
+ return (found_domain != NULL && strcmp(found, target) == 0);
+ case NWAM_CONDITION_IS_NOT:
+ return (found_domain == NULL || strcmp(found, target) != 0);
+ case NWAM_CONDITION_CONTAINS:
+ return (found_domain != NULL && strstr(found, target) != NULL);
+ case NWAM_CONDITION_DOES_NOT_CONTAIN:
+ return (found_domain == NULL || strstr(found, target) == NULL);
+ default:
+ return (B_FALSE);
+ }
+}
+
+struct ncu_adv_domains {
+ struct ncu_adv_domains *next;
+ char *dns_domain;
+ char *nis_domain;
+};
+
+static int
+get_adv_domains(nwamd_object_t obj, void *arg)
+{
+ nwamd_ncu_t *ncu = (nwamd_ncu_t *)obj->nwamd_object_data;
+ struct ncu_adv_domains **headpp = (struct ncu_adv_domains **)arg;
+ struct ncu_adv_domains *adp;
+ char *dns, *nis;
+
+ if (ncu->ncu_type != NWAM_NCU_TYPE_INTERFACE)
+ return (0);
+
+ dns = nwamd_get_dhcpinfo_data("DNSdmain", ncu->ncu_name);
+ nis = nwamd_get_dhcpinfo_data("NISdmain", ncu->ncu_name);
+
+ if (dns != NULL || nis != NULL) {
+ adp = (struct ncu_adv_domains *)malloc(sizeof (*adp));
+ if (adp == NULL)
+ return (1);
+ adp->dns_domain = dns;
+ adp->nis_domain = nis;
+ adp->next = *headpp;
+ *headpp = adp;
+ }
+
+ return (0);
+}
+
+static boolean_t
+test_condition_sys_domain(nwam_condition_t condition, const char *domainname)
+{
+ char cur_domainname[MAXHOSTNAMELEN];
+
+ if (getdomainname(cur_domainname, MAXHOSTNAMELEN) != 0)
+ return (B_FALSE);
+
+ return (test_condition_domain(condition, domainname, cur_domainname));
+}
+
+static boolean_t
+test_condition_adv_domain(nwam_condition_t condition, const char *domainname)
+{
+ struct ncu_adv_domains *adv_domains = NULL;
+ struct ncu_adv_domains *adp, *prev;
+ boolean_t positive, rtn;
+
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, get_adv_domains,
+ &adv_domains);
+
+ positive = (condition == NWAM_CONDITION_IS ||
+ condition == NWAM_CONDITION_CONTAINS);
+
+ /*
+ * Walk the advertised domain list. Our test function tests one
+ * single domain, but we're dealing with a list: if our condition
+ * is positive ('is' or 'contains'), the test function for each
+ * domain results are or'd together; if our condition is negative
+ * ('is-not' or 'does-not-contain'), the test function results must
+ * be and'd. Thus our short-circuit exit value depends on our
+ * condition: if the test function returns TRUE it implies immediate
+ * success for a positive condition; if it returns FALSE it implies
+ * immediate failure for a negative condition.
+ */
+ adp = adv_domains;
+ while (adp != NULL) {
+ if ((test_condition_domain(condition, domainname,
+ adp->dns_domain) == positive) ||
+ (test_condition_domain(condition, domainname,
+ adp->nis_domain) == positive)) {
+ rtn = positive;
+ break;
+ }
+ adp = adp->next;
+ }
+ if (adp == NULL) {
+ /*
+ * We did not short-circuit; we therefore failed if our
+ * condition was positive, and succeeded if our condition
+ * was negative.
+ */
+ rtn = !positive;
+ }
+
+ /* now free the domain list */
+ adp = adv_domains;
+ while (adp != NULL) {
+ prev = adp;
+ adp = prev->next;
+ free(prev->dns_domain);
+ free(prev->nis_domain);
+ free(prev);
+ }
+
+ return (rtn);
+}
+
+/*
+ * Returns true if prefixlen bits of addr1 match prefixlen bits of addr2.
+ */
+static boolean_t
+prefixmatch(uchar_t *addr1, uchar_t *addr2, int prefixlen)
+{
+ uchar_t mask[IPV6_ABITS/8];
+ int i, j = 0;
+
+ if (prefixlen == 0)
+ return (B_TRUE);
+
+ while (prefixlen > 0) {
+ if (prefixlen >= 8) {
+ mask[j++] = 0xFF;
+ prefixlen -= 8;
+ } else {
+ mask[j] |= 1 << (8 - prefixlen);
+ prefixlen--;
+ }
+ }
+ /* Ensure at least one byte is tested */
+ if (j == 0) j++;
+
+ for (i = 0; i < j; i++) {
+ if ((addr1[i] & mask[i]) != (addr2[i] & mask[i]))
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+struct nwamd_ipaddr_condition_walk_arg {
+ nwam_condition_t condition;
+ struct sockaddr_storage sockaddr;
+ int prefixlen;
+ boolean_t res;
+};
+
+static int
+check_ipaddr(icfg_if_t *intf, void *arg)
+{
+ struct nwamd_ipaddr_condition_walk_arg *wa = arg;
+ struct sockaddr_storage sockaddr;
+ icfg_handle_t h;
+ socklen_t addrlen = intf->if_protocol == AF_INET ?
+ sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
+ int prefixlen = 0;
+ boolean_t match = B_FALSE;
+ uchar_t *addr1, *addr2;
+
+ if (icfg_open(&h, intf) != ICFG_SUCCESS)
+ return (0);
+
+ if (icfg_get_addr(h, (struct sockaddr *)&sockaddr, &addrlen,
+ &prefixlen, B_TRUE) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "check_ipaddr: icfg_get_addr: %s",
+ strerror(errno));
+ return (0);
+ }
+
+ if (intf->if_protocol == AF_INET) {
+ addr1 = (uchar_t *)&(((struct sockaddr_in *)
+ &sockaddr)->sin_addr.s_addr);
+ addr2 = (uchar_t *)&(((struct sockaddr_in *)
+ &(wa->sockaddr))->sin_addr.s_addr);
+ } else {
+ addr1 = (uchar_t *)&(((struct sockaddr_in6 *)
+ &sockaddr)->sin6_addr.s6_addr);
+ addr2 = (uchar_t *)&(((struct sockaddr_in6 *)
+ &(wa->sockaddr))->sin6_addr.s6_addr);
+ }
+
+ match = prefixmatch(addr1, addr2, wa->prefixlen);
+ icfg_close(h);
+
+ nlog(LOG_DEBUG, "check_ipaddr: match %d\n", match);
+ switch (wa->condition) {
+ case NWAM_CONDITION_IS:
+ case NWAM_CONDITION_IS_IN_RANGE:
+ wa->res = match;
+ if (match)
+ return (1);
+ return (0);
+ case NWAM_CONDITION_IS_NOT:
+ case NWAM_CONDITION_IS_NOT_IN_RANGE:
+ wa->res = !match;
+ return (0);
+ default:
+ return (0);
+ }
+}
+
+static boolean_t
+test_condition_ip_address(nwam_condition_t condition,
+ const char *ip_address_string)
+{
+ int proto;
+ char *copy, *ip_address, *prefixlen_string, *lasts;
+ socklen_t addrlen = sizeof (struct sockaddr_in);
+ socklen_t addr6len = sizeof (struct sockaddr_in6);
+ struct nwamd_ipaddr_condition_walk_arg wa;
+
+ if ((copy = strdup(ip_address_string)) == NULL)
+ return (B_FALSE);
+
+ if ((ip_address = strtok_r(copy, " \t/", &lasts)) == NULL) {
+ free(copy);
+ return (B_FALSE);
+ }
+
+ prefixlen_string = strtok_r(NULL, " \t", &lasts);
+
+ if (icfg_str_to_sockaddr(AF_INET, ip_address,
+ (struct sockaddr *)&(wa.sockaddr), &addrlen) == ICFG_SUCCESS) {
+ proto = AF_INET;
+ wa.prefixlen = IP_ABITS;
+ } else if (icfg_str_to_sockaddr(AF_INET6, ip_address,
+ (struct sockaddr *)&(wa.sockaddr), &addr6len) == ICFG_SUCCESS) {
+ proto = AF_INET6;
+ wa.prefixlen = IPV6_ABITS;
+ } else {
+ nlog(LOG_ERR, "test_condition_ip_address: "
+ "icfg_str_to_sockaddr: %s", strerror(errno));
+ free(copy);
+ return (B_FALSE);
+ }
+
+ if (prefixlen_string != NULL)
+ wa.prefixlen = atoi(prefixlen_string);
+
+ wa.condition = condition;
+
+ switch (condition) {
+ case NWAM_CONDITION_IS:
+ case NWAM_CONDITION_IS_IN_RANGE:
+ wa.res = B_FALSE;
+ break;
+ case NWAM_CONDITION_IS_NOT:
+ case NWAM_CONDITION_IS_NOT_IN_RANGE:
+ wa.res = B_TRUE;
+ break;
+ default:
+ free(copy);
+ return (B_FALSE);
+ }
+
+ (void) icfg_iterate_if(proto, ICFG_PLUMBED, &wa, check_ipaddr);
+
+ free(copy);
+
+ return (wa.res);
+}
+
+struct nwamd_wlan_condition_walk_arg {
+ nwam_condition_t condition;
+ const char *exp_essid;
+ const char *exp_bssid;
+ uint_t num_connected;
+ boolean_t res;
+};
+
+static int
+check_wlan(const char *linkname, void *arg)
+{
+ struct nwamd_wlan_condition_walk_arg *wa = arg;
+ datalink_id_t linkid;
+ dladm_wlan_linkattr_t attr;
+ dladm_status_t status;
+ char cur_essid[DLADM_STRSIZE];
+ char cur_bssid[DLADM_STRSIZE];
+ char errmsg[DLADM_STRSIZE];
+
+ if ((status = dladm_name2info(dld_handle, linkname, &linkid, NULL, NULL,
+ NULL)) != DLADM_STATUS_OK) {
+ nlog(LOG_DEBUG, "check_wlan: dladm_name2info() for %s "
+ "failed: %s", linkname,
+ dladm_status2str(status, errmsg));
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ status = dladm_wlan_get_linkattr(dld_handle, linkid, &attr);
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_DEBUG, "check_wlan: dladm_wlan_get_linkattr() for %s "
+ "failed: %s", linkname,
+ dladm_status2str(status, errmsg));
+ return (DLADM_WALK_CONTINUE);
+ }
+ if (attr.la_status == DLADM_WLAN_LINK_DISCONNECTED)
+ return (DLADM_WALK_TERMINATE);
+
+ wa->num_connected++;
+
+ if (wa->exp_essid != NULL) {
+ /* Is the NIC associated with the expected access point? */
+ (void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid,
+ cur_essid);
+ switch (wa->condition) {
+ case NWAM_CONDITION_IS:
+ wa->res = strcmp(cur_essid, wa->exp_essid) == 0;
+ if (wa->res)
+ return (DLADM_WALK_TERMINATE);
+ break;
+ case NWAM_CONDITION_IS_NOT:
+ wa->res = strcmp(cur_essid, wa->exp_essid) != 0;
+ if (!wa->res)
+ return (DLADM_WALK_TERMINATE);
+ break;
+ case NWAM_CONDITION_CONTAINS:
+ wa->res = strstr(cur_essid, wa->exp_essid) != NULL;
+ if (wa->res)
+ return (DLADM_WALK_TERMINATE);
+ break;
+ case NWAM_CONDITION_DOES_NOT_CONTAIN:
+ wa->res = strstr(cur_essid, wa->exp_essid) == NULL;
+ if (!wa->res)
+ return (DLADM_WALK_TERMINATE);
+ break;
+ default:
+ return (DLADM_WALK_TERMINATE);
+ }
+ return (DLADM_WALK_CONTINUE);
+ }
+ if (wa->exp_bssid != NULL) {
+ /* Is the NIC associated with the expected access point? */
+ (void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid,
+ cur_bssid);
+ switch (wa->condition) {
+ case NWAM_CONDITION_IS:
+ wa->res = strcmp(cur_bssid, wa->exp_bssid) == 0;
+ if (wa->res)
+ return (DLADM_WALK_TERMINATE);
+ break;
+ case NWAM_CONDITION_IS_NOT:
+ wa->res = strcmp(cur_bssid, wa->exp_bssid) != 0;
+ if (!wa->res)
+ return (DLADM_WALK_TERMINATE);
+ break;
+ default:
+ return (DLADM_WALK_TERMINATE);
+ }
+ return (DLADM_WALK_CONTINUE);
+ }
+ /*
+ * Neither an ESSID or BSSID match is required - being connected to a
+ * WLAN is enough.
+ */
+ switch (wa->condition) {
+ case NWAM_CONDITION_IS:
+ wa->res = B_TRUE;
+ return (DLADM_WALK_TERMINATE);
+ default:
+ wa->res = B_FALSE;
+ return (DLADM_WALK_TERMINATE);
+ }
+ /*NOTREACHED*/
+ return (DLADM_WALK_CONTINUE);
+}
+
+static boolean_t
+test_condition_wireless_essid(nwam_condition_t condition,
+ const char *essid)
+{
+ struct nwamd_wlan_condition_walk_arg wa;
+
+ wa.condition = condition;
+ wa.exp_essid = essid;
+ wa.exp_bssid = NULL;
+ wa.num_connected = 0;
+ wa.res = B_FALSE;
+
+ (void) dladm_walk(check_wlan, dld_handle, &wa, DATALINK_CLASS_PHYS,
+ DL_WIFI, DLADM_OPT_ACTIVE);
+
+ return (wa.num_connected > 0 && wa.res == B_TRUE);
+}
+
+static boolean_t
+test_condition_wireless_bssid(nwam_condition_t condition,
+ const char *bssid)
+{
+ struct nwamd_wlan_condition_walk_arg wa;
+
+ wa.condition = condition;
+ wa.exp_bssid = bssid;
+ wa.exp_essid = NULL;
+ wa.num_connected = 0;
+ wa.res = B_FALSE;
+
+ (void) dladm_walk(check_wlan, dld_handle, &wa, DATALINK_CLASS_PHYS,
+ DL_WIFI, DLADM_OPT_ACTIVE);
+
+ return (wa.num_connected > 0 && wa.res == B_TRUE);
+}
+
+/*
+ * This function takes an activation mode and a string representation of a
+ * condition and evaluates it.
+ */
+boolean_t
+nwamd_check_conditions(nwam_activation_mode_t activation_mode,
+ char **condition_strings, uint_t num_conditions)
+{
+ boolean_t ret;
+ nwam_condition_t condition;
+ nwam_condition_object_type_t object_type;
+ char *object_name;
+ int i, j;
+
+ for (i = 0; i < num_conditions; i++) {
+
+ if (nwam_condition_string_to_condition(condition_strings[i],
+ &object_type, &condition, &object_name) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "check_conditions: invalid condition %s",
+ condition_strings[i]);
+ return (B_FALSE);
+ }
+ ret = B_FALSE;
+
+ for (j = 0; j < (sizeof (condition_map) /
+ sizeof (struct nwamd_condition_map)); j++) {
+ if (condition_map[j].object_type == object_type)
+ ret = condition_map[j].condition_func(condition,
+ object_name);
+ }
+
+ free(object_name);
+
+ if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY &&
+ ret) {
+ return (B_TRUE);
+ }
+ if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ALL &&
+ !ret) {
+ return (B_FALSE);
+ }
+ }
+ if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY && ret)
+ return (B_TRUE);
+ if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ALL && ret)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
+ * In rating activation conditions, we take the best-rated CONDITIONAL_ANY
+ * condition, or sum all the CONDITIONAL_ALL condition ratings. This allows
+ * us to compare between location activation conditions to pick the best.
+ */
+uint64_t
+nwamd_rate_conditions(nwam_activation_mode_t activation_mode,
+ char **conditions, uint_t num_conditions)
+{
+ nwam_condition_t condition;
+ nwam_condition_object_type_t object_type;
+ char *object_name;
+ int i;
+ uint64_t rating = 0, total_rating = 0;
+
+ for (i = 0; i < num_conditions; i++) {
+
+ object_name = NULL;
+ if (nwam_condition_string_to_condition(conditions[i],
+ &object_type, &condition, &object_name) != NWAM_SUCCESS ||
+ nwam_condition_rate(object_type, condition, &rating)
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_rate_conditions: could not rate "
+ "condition");
+ free(object_name);
+ return (0);
+ }
+ free(object_name);
+
+ if (activation_mode == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY) {
+ if (rating > total_rating)
+ total_rating = rating;
+ } else if (activation_mode ==
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ALL) {
+ total_rating += rating;
+ }
+ }
+ return (total_rating);
+}
+
+/*
+ * Different from nwamd_triggered_check_all_conditions() in that this
+ * function enqueues a timed check event.
+ */
+void
+nwamd_set_timed_check_all_conditions(void)
+{
+ nwamd_event_t check_event = nwamd_event_init
+ (NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS, NWAM_OBJECT_TYPE_UNKNOWN,
+ 0, NULL);
+ if (check_event != NULL) {
+ /* Add another timed event to recheck conditions */
+ nwamd_event_enqueue_timed(check_event,
+ condition_check_interval > CONDITION_CHECK_INTERVAL_MIN ?
+ condition_check_interval : CONDITION_CHECK_INTERVAL_MIN);
+ }
+}
+
+/*
+ * Does not enqueue another check event.
+ */
+void
+nwamd_check_all_conditions(void)
+{
+ nwamd_enm_check_conditions();
+ nwamd_loc_check_conditions();
+}
+
+void
+nwamd_create_timed_condition_check_event(void)
+{
+ nwamd_event_t check_event = nwamd_event_init
+ (NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS, NWAM_OBJECT_TYPE_UNKNOWN,
+ 0, NULL);
+ if (check_event != NULL)
+ nwamd_event_enqueue(check_event);
+}
+
+void
+nwamd_create_triggered_condition_check_event(uint32_t when)
+{
+ nwamd_event_t check_event;
+
+ if (!nwamd_event_enqueued(NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS,
+ NWAM_OBJECT_TYPE_UNKNOWN, NULL)) {
+ check_event = nwamd_event_init
+ (NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS,
+ NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
+ if (check_event != NULL)
+ nwamd_event_enqueue_timed(check_event, when);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/conditions.h b/usr/src/cmd/cmd-inet/lib/nwamd/conditions.h
new file mode 100644
index 0000000000..7cf1cf946a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/conditions.h
@@ -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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CONDITIONS_H
+#define _CONDITIONS_H
+
+#include <libnwam.h>
+
+#define CONDITION_CHECK_INTERVAL_DEFAULT 120
+#define CONDITION_CHECK_INTERVAL_MIN 30
+
+extern uint64_t condition_check_interval;
+
+/* Common condition check function */
+extern boolean_t nwamd_check_conditions(nwam_activation_mode_t, char **,
+ uint_t);
+/* Rate condition (used to pick best location condition) */
+extern uint64_t nwamd_rate_conditions(nwam_activation_mode_t, char **,
+ uint_t);
+
+/* Check activation conditions */
+extern void nwamd_set_timed_check_all_conditions(void);
+extern void nwamd_check_all_conditions(void);
+
+/* Create condition check events */
+extern void nwamd_create_timed_condition_check_event(void);
+extern void nwamd_create_triggered_condition_check_event(uint32_t);
+
+#endif /* _CONDITIONS_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/create_loc_auto b/usr/src/cmd/cmd-inet/lib/nwamd/create_loc_auto
new file mode 100644
index 0000000000..f3174cf2db
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/create_loc_auto
@@ -0,0 +1,30 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+create loc Automatic
+set activation-mode=system
+set nameservices=dns
+set nameservices-config-file=/etc/nsswitch.dns
+set dns-nameservice-configsrc=dhcp
+end
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/create_loc_nonet b/usr/src/cmd/cmd-inet/lib/nwamd/create_loc_nonet
new file mode 100644
index 0000000000..e3693148d6
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/create_loc_nonet
@@ -0,0 +1,31 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+create loc NoNet
+set activation-mode=system
+set nameservices=files
+set nameservices-config-file=/etc/nsswitch.files
+set ipfilter-config-file=/etc/nwam/loc/NoNet/ipf.conf
+set ipfilter-v6-config-file=/etc/nwam/loc/NoNet/ipf6.conf
+end
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/defines.h b/usr/src/cmd/cmd-inet/lib/nwamd/defines.h
deleted file mode 100644
index 8179a2d0a0..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/defines.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _DEFINES_H
-#define _DEFINES_H
-
-#include <sys/time.h>
-
-#define PKILL "/usr/bin/pkill"
-#define IFCONFIG "/sbin/ifconfig"
-#define NET_SVC_METHOD "/lib/svc/method/net-svc"
-#define DEV_FS_ROOT_FMRI "svc:/system/filesystem/root:default"
-#define PFEXEC "/usr/bin/pfexec"
-
-#define ULP_DIR "/etc/nwam/ulp"
-#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))
-
-#define NWAM_DEFAULT_DHCP_WAIT_TIME 60 /* 1 minute */
-#define NWAM_IF_WAIT_DELTA_MAX 300 /* 5 minutes poll rate max */
-
-#define TIMER_INFINITY 0xffffffff /* we use uint32s for timers */
-#define NSEC_TO_SEC(nsec) (nsec) / NANOSEC
-
-#endif /* _DEFINES_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/dlpi_events.c b/usr/src/cmd/cmd-inet/lib/nwamd/dlpi_events.c
new file mode 100644
index 0000000000..8789fed32b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/dlpi_events.c
@@ -0,0 +1,172 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <libdlpi.h>
+#include <libnwam.h>
+#include <net/if.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+#include "events.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * dlpi_events.c - this file contains routines to retrieve
+ * DL_NOTE_LINK_[UP|DOWN] events from the system and packages them for high
+ * level processing. Holding a dlpi_handle to a link prevents the
+ * associated driver unloading that can happen when IP is not plumbed,
+ * so it is vital to ensure that the handle is open for the lifetime
+ * of the WiFi connection.
+ */
+
+/*
+ * This is a callback function executed when dlpi_recv() gets a DL_NOTE_LINK_UP.
+ * It packages up the event for consumption by the link state machine.
+ */
+/* ARGSUSED0 */
+static void
+nwamd_dlpi_notify(dlpi_handle_t dhp, dlpi_notifyinfo_t *info, void *arg)
+{
+ nwamd_event_t ev;
+ char *name = arg;
+
+ if (info->dni_note & DL_NOTE_LINK_UP)
+ ev = nwamd_event_init_link_state(name, B_TRUE);
+ else
+ ev = nwamd_event_init_link_state(name, B_FALSE);
+ if (ev != NULL)
+ nwamd_event_enqueue(ev);
+}
+
+/*
+ * We are only intested in DL_NOTE_LINK_UP events which we've registered for
+ * in nwamd_dlpi_add_link(). But we have to keep calling dlpi_recv() to
+ * force the notification callback to be executed.
+ */
+static void *
+nwamd_dlpi_thread(void *arg)
+{
+ int rc;
+ dlpi_handle_t *dh = arg;
+
+ do {
+ rc = dlpi_recv(*dh, NULL, NULL, NULL, NULL, -1, NULL);
+ } while (rc == DLPI_SUCCESS);
+ nlog(LOG_ERR, "dlpi_recv failed: %s", dlpi_strerror(rc));
+ return (NULL);
+}
+
+/*
+ * This is called when we want to start receiving notifications from state
+ * changes on a link.
+ */
+void
+nwamd_dlpi_add_link(nwamd_object_t obj)
+{
+ nwamd_ncu_t *ncu = obj->nwamd_object_data;
+ nwamd_link_t *link;
+ dlpi_notifyid_t id;
+ int rc;
+
+ nlog(LOG_DEBUG, "nwamd_dlpi_add_link: ncu %p (%s) type %d",
+ ncu, obj->nwamd_object_name, ncu != NULL ? ncu->ncu_type : -1);
+
+ assert(ncu != NULL && ncu->ncu_type == NWAM_NCU_TYPE_LINK);
+
+ link = &ncu->ncu_node.u_link;
+
+ /* Already running? */
+ if (link->nwamd_link_dlpi_thread != 0) {
+ nlog(LOG_DEBUG, "nwamd_dlpi_add_link(%s) already running",
+ obj->nwamd_object_name);
+ return;
+ }
+
+ rc = dlpi_open(ncu->ncu_name, &link->nwamd_link_dhp, 0);
+ if (rc != DLPI_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_dlpi_add_link: dlpi_open(%s) = %s",
+ ncu->ncu_name, dlpi_strerror(rc));
+ return;
+ }
+
+ nwamd_set_unset_link_properties(ncu, B_TRUE);
+
+ rc = dlpi_enabnotify(link->nwamd_link_dhp,
+ DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN, nwamd_dlpi_notify,
+ ncu->ncu_name, &id);
+ if (rc != DLPI_SUCCESS) {
+ nlog(LOG_ERR,
+ "nwamd_dlpi_add_link: dlpi_enabnotify(%s) = %s",
+ obj->nwamd_object_name, dlpi_strerror(rc));
+ dlpi_close(link->nwamd_link_dhp);
+ return;
+ }
+
+ rc = pthread_create(&link->nwamd_link_dlpi_thread, NULL,
+ nwamd_dlpi_thread, &link->nwamd_link_dhp);
+ if (rc != 0) {
+ nlog(LOG_ERR, "nwamd_dlpi_add_link: couldn't create "
+ "dlpi thread for %s: %s", obj->nwamd_object_name,
+ strerror(rc));
+ dlpi_close(link->nwamd_link_dhp);
+ }
+}
+
+/*
+ * This function is called when we are no longer interested in receiving
+ * notification from state changes on a link.
+ */
+void
+nwamd_dlpi_delete_link(nwamd_object_t obj)
+{
+ nwamd_ncu_t *ncu = obj->nwamd_object_data;
+
+ nlog(LOG_DEBUG, "nwamd_dlpi_delete_link: ncu %p (%s) type %d",
+ ncu, obj->nwamd_object_name, ncu != NULL ? ncu->ncu_type : -1);
+
+ if (ncu->ncu_node.u_link.nwamd_link_dlpi_thread != 0) {
+ (void) pthread_cancel(
+ ncu->ncu_node.u_link.nwamd_link_dlpi_thread);
+ (void) pthread_join(ncu->ncu_node.u_link.nwamd_link_dlpi_thread,
+ NULL);
+ ncu->ncu_node.u_link.nwamd_link_dlpi_thread = 0;
+ /* Unset properties before closing */
+ nwamd_set_unset_link_properties(ncu, B_FALSE);
+ }
+
+ dlpi_close(ncu->ncu_node.u_link.nwamd_link_dhp);
+ ncu->ncu_node.u_link.nwamd_link_dhp = NULL;
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/door.c b/usr/src/cmd/cmd-inet/lib/nwamd/door.c
deleted file mode 100644
index 6143487040..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/door.c
+++ /dev/null
@@ -1,918 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 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, ndc->ndc_secmode);
- (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/door_if.c b/usr/src/cmd/cmd-inet/lib/nwamd/door_if.c
new file mode 100644
index 0000000000..02ee82896a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/door_if.c
@@ -0,0 +1,669 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <auth_attr.h>
+#include <auth_list.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <door.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libnwam_priv.h>
+#include <libuutil.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <sys/mman.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "conditions.h"
+#include "events.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * door_if.c
+ * This file contains functions which implement the command interface to
+ * nwam via the door NWAM_DOOR. Doors provide a LPC mechanism that allows
+ * for threads in one process to cause code to execute in another process.
+ * Doors also provide the ability to pass data and file descriptors. See
+ * libdoor(3LIB) for more information.
+ *
+ * This file exports two functions, nwamd_door_initialize() (which sets up
+ * the door) and nwamd_door_fini(), which removes it.
+ *
+ * It sets up the static routine nwamd_door_switch() to be called when a client
+ * calls the door (via door_call(3C)). The structure nwam_request_t is
+ * passed as data and contains data to specify the type of action requested
+ * and any data need to meet that request. A table consisting of entries
+ * for each door request, the associated authorization and the function to
+ * process that request is used to handle the various requests.
+ */
+
+struct nwamd_door_req_entry
+{
+ int ndre_type;
+ char *ndre_auth;
+ nwam_error_t (*ndre_fn)(nwamd_door_arg_t *, ucred_t *, struct passwd *);
+};
+
+static nwam_error_t nwamd_door_req_event_register(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_event_unregister(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_wlan_scan(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_wlan_scan_results(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_wlan_select(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_wlan_set_key(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_action(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_state(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+static nwam_error_t nwamd_door_req_priority_group(nwamd_door_arg_t *,
+ ucred_t *, struct passwd *);
+
+/*
+ * This table defines the set of door commands available, the required
+ * authorizations for each command, and the function that carries out
+ * each command.
+ */
+struct nwamd_door_req_entry door_req_table[] =
+{
+
+ { NWAM_REQUEST_TYPE_EVENT_REGISTER, AUTOCONF_READ_AUTH,
+ nwamd_door_req_event_register },
+ { NWAM_REQUEST_TYPE_EVENT_UNREGISTER, AUTOCONF_READ_AUTH,
+ nwamd_door_req_event_unregister },
+ { NWAM_REQUEST_TYPE_WLAN_SCAN, AUTOCONF_WLAN_AUTH,
+ nwamd_door_req_wlan_scan },
+ { NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS, AUTOCONF_READ_AUTH,
+ nwamd_door_req_wlan_scan_results },
+ { NWAM_REQUEST_TYPE_WLAN_SELECT, AUTOCONF_WLAN_AUTH,
+ nwamd_door_req_wlan_select },
+ { NWAM_REQUEST_TYPE_WLAN_SET_KEY, AUTOCONF_WLAN_AUTH,
+ nwamd_door_req_wlan_set_key },
+ /* Requires WRITE, SELECT or WLAN auth depending on action */
+ { NWAM_REQUEST_TYPE_ACTION, NULL, nwamd_door_req_action },
+ { NWAM_REQUEST_TYPE_STATE, AUTOCONF_READ_AUTH,
+ nwamd_door_req_state },
+ { NWAM_REQUEST_TYPE_PRIORITY_GROUP, AUTOCONF_READ_AUTH,
+ nwamd_door_req_priority_group },
+};
+
+int doorfd = -1;
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_event_register(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ nwam_error_t err;
+
+ err = nwam_event_queue_init
+ (req->nwda_data.nwdad_register_info.nwdad_name);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_door_req_event_register: "
+ "could not register events for %s",
+ req->nwda_data.nwdad_register_info.nwdad_name);
+ }
+
+ return (err);
+}
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_event_unregister(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ nwam_event_queue_fini(req->nwda_data.nwdad_register_info.nwdad_name);
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED1 */
+static nwam_error_t
+nwamd_door_req_wlan_scan(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ nlog(LOG_DEBUG,
+ "nwamd_door_req_wlan_scan: processing WLAN scan request: "
+ "link %s", req->nwda_data.nwdad_wlan_info.nwdad_name);
+
+ return (nwamd_wlan_scan(req->nwda_data.nwdad_wlan_info.nwdad_name));
+}
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_wlan_scan_results(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ nwamd_object_t obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+ uint_t num_wlans;
+
+ nlog(LOG_DEBUG, "nwamd_door_req_wlan_scan_results: processing WLAN "
+ "scan results request: link %s",
+ req->nwda_data.nwdad_wlan_info.nwdad_name);
+
+ obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK,
+ req->nwda_data.nwdad_wlan_info.nwdad_name);
+ if (obj == NULL) {
+ nlog(LOG_ERR,
+ "nwamd_door_req_wlan_scan_results: link %s not found",
+ req->nwda_data.nwdad_wlan_info.nwdad_name);
+ return (NWAM_ENTITY_NOT_FOUND);
+ }
+
+ ncu = obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+ num_wlans = link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num;
+
+ if (num_wlans > 0) {
+ (void) memcpy
+ (req->nwda_data.nwdad_wlan_info.nwdad_wlans,
+ link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
+ num_wlans * sizeof (nwam_wlan_t));
+ }
+ req->nwda_data.nwdad_wlan_info.nwdad_num_wlans = num_wlans;
+ nlog(LOG_DEBUG,
+ "nwamd_door_req_wlan_scan_results: returning %d scan results",
+ num_wlans);
+ nwamd_object_release(obj);
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_wlan_select(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ nlog(LOG_DEBUG,
+ "nwamd_door_req_wlan_select: processing WLAN selection : "
+ "link %s ESSID %s , BSSID %s",
+ req->nwda_data.nwdad_wlan_info.nwdad_name,
+ req->nwda_data.nwdad_wlan_info.nwdad_essid,
+ req->nwda_data.nwdad_wlan_info.nwdad_bssid);
+ return (nwamd_wlan_select
+ (req->nwda_data.nwdad_wlan_info.nwdad_name,
+ req->nwda_data.nwdad_wlan_info.nwdad_essid,
+ req->nwda_data.nwdad_wlan_info.nwdad_bssid,
+ req->nwda_data.nwdad_wlan_info.nwdad_security_mode,
+ req->nwda_data.nwdad_wlan_info.nwdad_add_to_known_wlans));
+}
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_wlan_set_key(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ nlog(LOG_DEBUG,
+ "nwamd_door_req_wlan_set_key: processing WLAN key input : "
+ "link %s ESSID %s BSSID %s",
+ req->nwda_data.nwdad_wlan_info.nwdad_name,
+ req->nwda_data.nwdad_wlan_info.nwdad_essid,
+ req->nwda_data.nwdad_wlan_info.nwdad_bssid);
+ return (nwamd_wlan_set_key
+ (req->nwda_data.nwdad_wlan_info.nwdad_name,
+ req->nwda_data.nwdad_wlan_info.nwdad_essid, NULL,
+ req->nwda_data.nwdad_wlan_info.nwdad_security_mode,
+ req->nwda_data.nwdad_wlan_info.nwdad_keyslot,
+ req->nwda_data.nwdad_wlan_info.nwdad_key));
+}
+
+static nwam_error_t
+nwamd_door_req_action(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd)
+{
+ char name[NWAM_MAX_NAME_LEN];
+ char parent[NWAM_MAX_NAME_LEN];
+ nwam_action_t action = req->nwda_data.nwdad_object_action.nwdad_action;
+ nwam_object_type_t object_type =
+ req->nwda_data.nwdad_object_action.nwdad_object_type;
+ char *obj_type_str = (char *)nwam_object_type_to_string(object_type);
+ nwam_error_t err;
+
+ /* Check for name, parent overrun */
+ if (strlcpy(name, req->nwda_data.nwdad_object_action.nwdad_name,
+ sizeof (name)) == NWAM_MAX_NAME_LEN ||
+ strlcpy(parent, req->nwda_data.nwdad_object_action.nwdad_parent,
+ sizeof (parent)) == NWAM_MAX_NAME_LEN)
+ return (NWAM_INVALID_ARG);
+
+ /*
+ * Check authorizations against actions.
+ * - ENABLE/DISABLE requires SELECT auth
+ * - ADD/DESTROY/REFRESH on Known WLANs requires WLAN auth
+ * - ADD/DESTROY on other objects requires WRITE auth
+ * - REFRESH on other objects requires either WRITE or SELECT auth
+ */
+ if (action == NWAM_ACTION_ENABLE || action == NWAM_ACTION_DISABLE) {
+ if (chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0) {
+ nwam_record_audit_event(ucr,
+ action == NWAM_ACTION_ENABLE ?
+ ADT_nwam_enable : ADT_nwam_disable, name,
+ obj_type_str, ADT_FAILURE, ADT_FAIL_VALUE_AUTH);
+ nlog(LOG_ERR, "nwamd_door_req_action: "
+ "need %s for %s action", AUTOCONF_SELECT_AUTH,
+ nwam_action_to_string(action));
+ return (NWAM_PERMISSION_DENIED);
+ }
+ } else if (object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN) {
+ if (chkauthattr(AUTOCONF_WLAN_AUTH, pwd->pw_name) == 0) {
+ nlog(LOG_ERR, "nwamd_door_req_action: "
+ "need %s for %s action on Known WLAN",
+ AUTOCONF_WLAN_AUTH, nwam_action_to_string(action));
+ return (NWAM_PERMISSION_DENIED);
+ }
+ } else if (action == NWAM_ACTION_ADD || action == NWAM_ACTION_DESTROY) {
+ if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0) {
+ nlog(LOG_ERR, "nwamd_door_req_action: "
+ "need %s for %s action", AUTOCONF_WRITE_AUTH,
+ nwam_action_to_string(action));
+ return (NWAM_PERMISSION_DENIED);
+ }
+ } else if (action == NWAM_ACTION_REFRESH) {
+ if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0 &&
+ chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0) {
+ nlog(LOG_ERR, "nwamd_door_req_action: "
+ "need either %s or %s for %s action",
+ AUTOCONF_WRITE_AUTH, AUTOCONF_SELECT_AUTH,
+ nwam_action_to_string(action));
+ return (NWAM_PERMISSION_DENIED);
+ }
+ } else {
+ nlog(LOG_ERR, "nwamd_door_req_action: received unknown "
+ "action %d (%s)", action, nwam_action_to_string(action));
+ return (NWAM_INVALID_ARG);
+ }
+
+ switch (action) {
+ case NWAM_ACTION_ENABLE:
+ case NWAM_ACTION_DISABLE:
+ nwam_record_audit_event(ucr,
+ action == NWAM_ACTION_ENABLE ?
+ ADT_nwam_enable : ADT_nwam_disable, name,
+ obj_type_str, ADT_SUCCESS, ADT_SUCCESS);
+
+ nlog(LOG_DEBUG, "nwamd_door_req_action: %s %s",
+ action == NWAM_ACTION_ENABLE ? "enabling" : "disabling",
+ name);
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_ENM:
+ err = nwamd_enm_action(name, action);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ err = nwamd_loc_action(name, action);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ err = nwamd_ncu_action(name, parent, action);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ if (action == NWAM_ACTION_DISABLE) {
+ nlog(LOG_ERR, "nwamd_door_req_action: "
+ "NCPs cannot be disabled");
+ err = NWAM_INVALID_ARG;
+ } else {
+ err = nwamd_ncp_action(name, action);
+ }
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_door_req_action: received invalid "
+ "object type %d (%s)", object_type,
+ nwam_object_type_to_string(object_type));
+ return (NWAM_INVALID_ARG);
+ }
+ break;
+
+ case NWAM_ACTION_ADD:
+ case NWAM_ACTION_REFRESH:
+ /*
+ * Called whenever an object is committed in the library.
+ * Reread that committed object into nwamd.
+ */
+ nlog(LOG_DEBUG, "door_switch: refreshing %s", name);
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_ENM:
+ err = nwamd_enm_action(name, action);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ err = nwamd_loc_action(name, action);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ err = nwamd_known_wlan_action(name, action);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ err = nwamd_ncu_action(name, parent, action);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ err = nwamd_ncp_action(name, action);
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_door_req_action: received invalid "
+ "object type %d (%s)", object_type,
+ nwam_object_type_to_string(object_type));
+ err = NWAM_INVALID_ARG;
+ break;
+ }
+ break;
+
+ case NWAM_ACTION_DESTROY:
+ /* Object was destroyed, remove from nwamd */
+ nlog(LOG_DEBUG, "door_switch: removing %s", name);
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_ENM:
+ err = nwamd_enm_action(name, NWAM_ACTION_DESTROY);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ err = nwamd_loc_action(name, NWAM_ACTION_DESTROY);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ err = nwamd_known_wlan_action(name,
+ NWAM_ACTION_DESTROY);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ err = nwamd_ncu_action(name, parent,
+ NWAM_ACTION_DESTROY);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (strcmp(name, active_ncp) == 0) {
+ nlog(LOG_ERR, "nwamd_door_req_action: %s is "
+ "active, cannot destroy", parent);
+ err = NWAM_ENTITY_IN_USE;
+ } else {
+ err = nwamd_ncp_action(name,
+ NWAM_ACTION_DESTROY);
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_door_req_action: received invalid "
+ "object type %d (%s)", object_type,
+ nwam_object_type_to_string(object_type));
+ err = NWAM_INVALID_ARG;
+ break;
+ }
+ break;
+
+ default:
+ nlog(LOG_ERR, "nwamd_door_req_action: received unknown "
+ "action %d (%s)", action, nwam_action_to_string(action));
+ err = NWAM_INVALID_ARG;
+ break;
+ }
+
+ if (err == NWAM_SUCCESS) {
+ /*
+ * At this point, we've successfully carried out an action.
+ * Configuration may have changed, so we need to recheck
+ * conditions, however we want to avoid a flurry of condition
+ * check events, so we enqueue a triggered condition check
+ * if none is due in the next few seconds.
+ */
+ nwamd_create_triggered_condition_check_event(NEXT_FEW_SECONDS);
+ } else {
+ nlog(LOG_ERR, "nwamd_door_req_action: could not carry out "
+ "%s action on %s: %s", nwam_action_to_string(action),
+ name, nwam_strerror(err));
+ }
+
+ return (err);
+}
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_state(nwamd_door_arg_t *req, ucred_t *ucr, struct passwd *pwd)
+{
+ char name[NWAM_MAX_NAME_LEN];
+ nwamd_object_t obj;
+ nwam_object_type_t object_type =
+ req->nwda_data.nwdad_object_state.nwdad_object_type;
+ boolean_t is_active = B_FALSE;
+
+ /* Check for name, parent overrun */
+ if (strlcpy(name, req->nwda_data.nwdad_object_state.nwdad_name,
+ sizeof (name)) == NWAM_MAX_NAME_LEN)
+ return (NWAM_INVALID_ARG);
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCP:
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ is_active = (strcmp(active_ncp, name) == 0);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ if (is_active) {
+ req->nwda_data.nwdad_object_state.nwdad_state =
+ NWAM_STATE_ONLINE;
+ req->nwda_data.nwdad_object_state.
+ nwdad_aux_state = NWAM_AUX_STATE_ACTIVE;
+ nlog(LOG_DEBUG,
+ "nwamd_door_req_state: NCP %s is active", name);
+ } else {
+ req->nwda_data.nwdad_object_state.nwdad_state =
+ NWAM_STATE_DISABLED;
+ req->nwda_data.nwdad_object_state.
+ nwdad_aux_state =
+ NWAM_AUX_STATE_MANUAL_DISABLE;
+ nlog(LOG_DEBUG, "nwamd_door_req_state: "
+ "NCP %s is inactive", name);
+ }
+ break;
+
+ case NWAM_OBJECT_TYPE_LOC:
+ case NWAM_OBJECT_TYPE_NCU:
+ case NWAM_OBJECT_TYPE_ENM:
+ obj = nwamd_object_find(object_type, name);
+ if (obj == NULL) {
+ nlog(LOG_ERR, "nwamd_door_req_state: %s %s not found",
+ nwam_object_type_to_string(object_type), name);
+ return (NWAM_ENTITY_NOT_FOUND);
+ }
+ nlog(LOG_DEBUG, "nwamd_door_req_state: %s %s is %s",
+ nwam_object_type_to_string(object_type), name,
+ nwam_state_to_string(obj->nwamd_object_state));
+ req->nwda_data.nwdad_object_state.nwdad_state =
+ obj->nwamd_object_state;
+ req->nwda_data.nwdad_object_state.nwdad_aux_state =
+ obj->nwamd_object_aux_state;
+ nwamd_object_release(obj);
+ break;
+
+ default:
+ nlog(LOG_ERR, "nwamd_door_req_state: received invalid "
+ "object type %d (%s)", object_type,
+ nwam_object_type_to_string(object_type));
+ req->nwda_status = NWAM_REQUEST_STATUS_UNKNOWN;
+ return (NWAM_INVALID_ARG);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED */
+static nwam_error_t
+nwamd_door_req_priority_group(nwamd_door_arg_t *req, ucred_t *ucr,
+ struct passwd *pwd)
+{
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ nlog(LOG_DEBUG, "nwamd_door_req_priority_group: "
+ "retrieving active priority-group: %d",
+ current_ncu_priority_group);
+ req->nwda_data.nwdad_priority_group_info.nwdad_priority =
+ current_ncu_priority_group;
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED */
+static void
+nwamd_door_switch(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
+ uint_t n_desc)
+{
+ nwamd_door_arg_t *req;
+ ucred_t *ucr = NULL;
+ uid_t uid;
+ struct passwd *pwd = NULL;
+ boolean_t found = B_FALSE;
+ int i;
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ req = (nwamd_door_arg_t *)argp;
+ req->nwda_error = NWAM_SUCCESS;
+
+ if (door_ucred(&ucr) != 0) {
+ nlog(LOG_ERR, "nwamd_door_switch: door_ucred failed: %s",
+ strerror(errno));
+ req->nwda_error = NWAM_ERROR_INTERNAL;
+ req->nwda_status = NWAM_REQUEST_STATUS_FAILED;
+ goto done;
+ }
+ uid = ucred_getruid(ucr);
+
+ if ((pwd = getpwuid(uid)) == NULL) {
+ nlog(LOG_ERR, "nwamd_door_switch: getpwuid failed: %s",
+ strerror(errno));
+ endpwent();
+ req->nwda_error = NWAM_ERROR_INTERNAL;
+ req->nwda_status = NWAM_REQUEST_STATUS_FAILED;
+ goto done;
+ }
+
+ /*
+ * Find door request entry in table, check auths and call the function
+ * handling the request.
+ */
+ for (i = 0;
+ i < sizeof (door_req_table) / sizeof (struct nwamd_door_req_entry);
+ i++) {
+ if (req->nwda_type != door_req_table[i].ndre_type)
+ continue;
+
+ found = B_TRUE;
+
+ if (door_req_table[i].ndre_auth != NULL &&
+ chkauthattr(door_req_table[i].ndre_auth,
+ pwd->pw_name) == 0) {
+ nlog(LOG_ERR,
+ "nwamd_door_switch: need %s for request type %d",
+ door_req_table[i].ndre_auth, req->nwda_type);
+ req->nwda_error = NWAM_PERMISSION_DENIED;
+ break;
+ }
+ req->nwda_error = door_req_table[i].ndre_fn(req, ucr, pwd);
+ break;
+ }
+ if (!found) {
+ nlog(LOG_ERR,
+ "nwamd_door_switch: received unknown request type %d",
+ req->nwda_type);
+ req->nwda_status = NWAM_REQUEST_STATUS_UNKNOWN;
+ } else {
+ if (req->nwda_error == NWAM_SUCCESS)
+ req->nwda_status = NWAM_REQUEST_STATUS_OK;
+ else
+ req->nwda_status = NWAM_REQUEST_STATUS_FAILED;
+ }
+
+done:
+ ucred_free(ucr);
+ endpwent();
+
+ if (door_return((char *)req, sizeof (nwamd_door_arg_t), NULL, 0)
+ == -1) {
+ nlog(LOG_ERR, "door_switch: type %d door_return failed: %s",
+ req->nwda_type, strerror(errno));
+ }
+}
+
+/*
+ * We initialize the nwamd door here. Failure to have this happen is critical
+ * to the daemon so we log a message and pass up notice to the caller who
+ * will most likely abort trying to start. This routine is meant to only
+ * be called once.
+ */
+void
+nwamd_door_init(void)
+{
+ const int door_mode = 0644;
+ struct stat buf;
+
+ if ((doorfd = door_create(nwamd_door_switch, NULL,
+ DOOR_NO_CANCEL | DOOR_REFUSE_DESC)) == -1)
+ pfail("Unable to create door: %s", strerror(errno));
+
+ if (stat(NWAM_DOOR, &buf) < 0) {
+ int nwam_door_fd;
+
+ if ((nwam_door_fd = creat(NWAM_DOOR, door_mode)) < 0) {
+ int err = errno;
+ (void) door_revoke(doorfd);
+ doorfd = -1;
+ pfail("Couldn't create door: %s", strerror(err));
+ }
+ (void) close(nwam_door_fd);
+ } else {
+ if (buf.st_mode != door_mode) {
+ if (chmod(NWAM_DOOR, door_mode) == -1) {
+ nlog(LOG_ERR, "couldn't change mode of %s: %s",
+ NWAM_DOOR, strerror(errno));
+ }
+ }
+ }
+ /* cleanup anything hanging around from a previous invocation */
+ (void) fdetach(NWAM_DOOR);
+
+ /* Place our door in the file system so that others can find us. */
+ if (fattach(doorfd, NWAM_DOOR) < 0) {
+ int err = errno;
+ (void) door_revoke(doorfd);
+ doorfd = -1;
+ pfail("Couldn't attach door: %s", strerror(err));
+ }
+}
+
+void
+nwamd_door_fini(void)
+{
+ if (doorfd != -1) {
+ nlog(LOG_DEBUG, "nwamd_door_fini: closing door");
+ (void) door_revoke(doorfd);
+ doorfd = -1;
+ }
+ (void) unlink(NWAM_DOOR);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/enm.c b/usr/src/cmd/cmd-inet/lib/nwamd/enm.c
new file mode 100644
index 0000000000..b641731f1c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/enm.c
@@ -0,0 +1,909 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inet/ip.h>
+#include <inetcfg.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlwlan.h>
+#include <libscf.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <libnwam.h>
+#include "conditions.h"
+#include "events.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * enm.c - contains routines which handle ENM (external network modifier)
+ * abstraction. ENMs represent scripts or services that can be activated either
+ * manually or in response to network conditions.
+ */
+
+#define CTRUN "/usr/bin/ctrun"
+
+static int
+enm_create_init_fini_event(nwam_enm_handle_t enmh, void *data)
+{
+ boolean_t *init = data;
+ char *name;
+ nwamd_event_t enm_event;
+
+ if (nwam_enm_get_name(enmh, &name) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "enm_init_fini: could not get ENM name");
+ return (0);
+ }
+
+ enm_event = nwamd_event_init(*init ?
+ NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
+ NWAM_OBJECT_TYPE_ENM, 0, name);
+ if (enm_event != NULL)
+ nwamd_event_enqueue(enm_event);
+ free(name);
+
+ return (0);
+}
+
+/*
+ * Walk all ENMs, creating init events for each.
+ */
+void
+nwamd_init_enms(void)
+{
+ boolean_t init = B_TRUE;
+
+ (void) nwam_walk_enms(enm_create_init_fini_event, &init, 0, NULL);
+}
+
+/*
+ * Walk all ENMs, creating fini events for each.
+ */
+void
+nwamd_fini_enms(void)
+{
+ boolean_t init = B_FALSE;
+
+ (void) nwam_walk_enms(enm_create_init_fini_event, &init, 0, NULL);
+}
+
+static boolean_t
+enm_is_enabled(nwam_enm_handle_t enmh)
+{
+ nwam_value_t enabledval;
+ boolean_t enabled = B_FALSE;
+
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ENABLED,
+ &enabledval) != NWAM_SUCCESS) {
+ /* It's legal for a conditional ENM to not specify "enabled" */
+ return (B_FALSE);
+ }
+ if (nwam_value_get_boolean(enabledval, &enabled) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "enm_is_enabled: could not retrieve "
+ "enabled value");
+ }
+ nwam_value_free(enabledval);
+ return (enabled);
+}
+
+static int64_t
+enm_get_activation_mode(nwam_enm_handle_t enmh)
+{
+ uint64_t activation;
+ int64_t ret;
+ nwam_value_t activationval;
+
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ACTIVATION_MODE,
+ &activationval) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "enm_get_activation_mode: could not retrieve "
+ "activation mode value");
+ return (-1);
+ }
+ if (nwam_value_get_uint64(activationval, &activation) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "enm_get_activation_mode: could not retrieve "
+ "activation mode value");
+ ret = -1;
+ } else {
+ ret = activation;
+ }
+ nwam_value_free(activationval);
+
+ return (ret);
+}
+
+static void *
+nwamd_enm_activate_deactivate_thread(void *arg)
+{
+ char *object_name = arg;
+ nwamd_object_t object;
+ nwam_enm_handle_t enmh;
+ nwam_value_t scriptval = NULL;
+ nwam_state_t state;
+ nwam_aux_state_t aux_state;
+ char *script;
+ boolean_t going_online, disable_succeeded = B_FALSE;
+ int ret;
+
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
+ "could not find ENM %s", object_name);
+ free(object_name);
+ return (NULL);
+ }
+ /* object_name was malloc() before this thread was created, free() it */
+ free(object_name);
+
+ enmh = object->nwamd_object_handle;
+
+ going_online =
+ (object->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE);
+ /*
+ * We're starting if current state is offline* and stopping otherwise.
+ */
+ if (nwam_enm_get_prop_value(enmh,
+ going_online ? NWAM_ENM_PROP_START : NWAM_ENM_PROP_STOP,
+ &scriptval) != NWAM_SUCCESS ||
+ nwam_value_get_string(scriptval, &script) != NWAM_SUCCESS) {
+ /*
+ * If we're stopping, it's not an error for no script to
+ * be specified.
+ */
+ nlog(going_online ? LOG_ERR : LOG_DEBUG,
+ "nwamd_enm_activate_deactivate_thread: "
+ "no script specified for enm %s",
+ object->nwamd_object_name);
+ if (going_online) {
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_METHOD_MISSING;
+ } else {
+ disable_succeeded = B_TRUE;
+ }
+ } else {
+ char *copy = NULL, *lasts;
+ const char **newargv, **argv = NULL;
+ int i = 0;
+
+ nlog(LOG_DEBUG, "nwamd_enm_activate_deactivate_thread: "
+ "running script %s for ENM %s", script,
+ object->nwamd_object_name);
+
+ /*
+ * The script may take a number of arguments. We need to
+ * create a string array consisting of the wrapper command
+ * (ctrun), ENM script name, arguments and NULL array
+ * terminator. Start with an array of size equal to the
+ * string length (since the number of arguments will always
+ * be less than this) and shrink array to the actual number
+ * of arguments when we have parsed the string.
+ */
+ if ((copy = strdup(script)) == NULL ||
+ (argv = calloc(strlen(script), sizeof (char *))) == NULL) {
+ ret = 1;
+ goto err;
+ }
+ argv[i++] = CTRUN;
+ argv[i++] = strtok_r(copy, " ", &lasts);
+ if (argv[1] == NULL) {
+ ret = 1;
+ goto err;
+ }
+
+ for (; (argv[i] = strtok_r(NULL, " ", &lasts)) != NULL; i++) {}
+
+ newargv = realloc(argv, (i + 1) * sizeof (char *));
+ argv = newargv;
+
+ ret = nwamd_start_childv(CTRUN, argv);
+
+err:
+ /*
+ * If script execution fails and we're not destroying the
+ * object, go to maintenance.
+ */
+ if (ret != 0) {
+ nlog(LOG_ERR, "nwamd_enm_activate_deactivate_thread: "
+ "execution of '%s' failed for ENM %s",
+ script, object->nwamd_object_name);
+ if (object->nwamd_object_aux_state !=
+ NWAM_AUX_STATE_UNINITIALIZED) {
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_METHOD_FAILED;
+ } else {
+ state = NWAM_STATE_UNINITIALIZED;
+ aux_state = NWAM_AUX_STATE_UNINITIALIZED;
+ }
+ } else {
+ if (going_online) {
+ state = NWAM_STATE_ONLINE;
+ aux_state = NWAM_AUX_STATE_ACTIVE;
+ } else {
+ disable_succeeded = B_TRUE;
+ }
+ }
+ free(argv);
+ free(copy);
+ }
+ nwam_value_free(scriptval);
+
+ if (disable_succeeded) {
+ /*
+ * If aux state is "manual disable", we know
+ * this was a disable request, otherwise it was
+ * _fini request or a condition satisfaction
+ * failure.
+ */
+ switch (object->nwamd_object_aux_state) {
+ case NWAM_AUX_STATE_MANUAL_DISABLE:
+ state = NWAM_STATE_DISABLED;
+ aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
+ break;
+ case NWAM_AUX_STATE_UNINITIALIZED:
+ state = NWAM_STATE_UNINITIALIZED;
+ aux_state = NWAM_AUX_STATE_UNINITIALIZED;
+ break;
+ default:
+ state = NWAM_STATE_OFFLINE;
+ aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
+ break;
+ }
+ }
+
+ /* If state/aux state are uninitialized/unintialized, destroy the ENM */
+ if (state == NWAM_STATE_UNINITIALIZED &&
+ aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
+ object->nwamd_object_state = state;
+ object->nwamd_object_aux_state = aux_state;
+ (void) nwamd_object_release_and_destroy_after_preserve(object);
+ } else {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ object->nwamd_object_name, state, aux_state);
+ (void) nwamd_object_release_after_preserve(object);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Run start/stop method for ENM in a separate thread. The object lock is not
+ * held across threads, so we duplicate the object name for the method
+ * execution thread. Returns true if thread is successfully launched.
+ */
+boolean_t
+nwamd_enm_run_method(nwamd_object_t object)
+{
+ char *name;
+ pthread_t script;
+
+ /*
+ * Launch separate thread to wait for execution of script
+ * to complete. Do not hold object lock across threads.
+ */
+ if ((name = strdup(object->nwamd_object_name)) == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_run_method: %s: out of memory",
+ object->nwamd_object_name);
+ return (B_FALSE);
+ }
+
+ if (pthread_create(&script, NULL,
+ nwamd_enm_activate_deactivate_thread, name) != 0) {
+ nlog(LOG_ERR, "nwamd_enm_run_method: could not create "
+ "enm script thread for %s", name);
+ free(name);
+ return (B_FALSE);
+ }
+ /* "name" will be freed by the newly-created thread. */
+
+ /* detach thread so that it doesn't become a zombie */
+ (void) pthread_detach(script);
+
+ return (B_TRUE);
+}
+
+/*
+ * Activate the ENM, either in response to an enable event or conditions
+ * being satisfied.
+ */
+static void
+nwamd_enm_activate(const char *object_name)
+{
+ nwamd_object_t object;
+ nwam_value_t fmrival;
+ char *fmri, *smf_state;
+ int ret;
+ nwam_enm_handle_t enmh;
+ nwam_state_t state;
+ nwam_aux_state_t aux_state;
+ nwam_error_t err;
+ boolean_t ran_method = B_FALSE;
+
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_activate: could not find ENM %s",
+ object_name);
+ return;
+ }
+ state = object->nwamd_object_state;
+ aux_state = object->nwamd_object_aux_state;
+ enmh = object->nwamd_object_handle;
+
+ nlog(LOG_DEBUG, "nwamd_enm_activate: activating ENM %s",
+ object->nwamd_object_name);
+
+ err = nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival);
+ switch (err) {
+ case NWAM_SUCCESS:
+
+ if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_enm_activate: could not retrieve "
+ "FMRI string for ENM %s",
+ object->nwamd_object_name);
+ nwam_value_free(fmrival);
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
+ break;
+ }
+
+ if ((smf_state = smf_get_state(fmri)) == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_activate: invalid FMRI %s "
+ "for ENM %s", fmri, object->nwamd_object_name);
+ nwam_value_free(fmrival);
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
+ break;
+ }
+
+ nlog(LOG_DEBUG, "nwamd_enm_activate: activating %s for ENM %s",
+ fmri, object->nwamd_object_name);
+
+ if (strcmp(smf_state, SCF_STATE_STRING_ONLINE) == 0)
+ ret = smf_restart_instance(fmri);
+ else if (strcmp(smf_state, SCF_STATE_STRING_OFFLINE) == 0)
+ ret = smf_restart_instance(fmri);
+ else if (strcmp(smf_state, SCF_STATE_STRING_DISABLED) == 0)
+ ret = smf_enable_instance(fmri, SMF_TEMPORARY);
+ else
+ ret = smf_restore_instance(fmri);
+
+ if (ret == 0) {
+ state = NWAM_STATE_ONLINE;
+ aux_state = NWAM_AUX_STATE_ACTIVE;
+ } else {
+ nlog(LOG_ERR, "nwamd_enm_activate: failed to enable "
+ "FMRI %s for ENM %s", fmri,
+ object->nwamd_object_name);
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_METHOD_FAILED;
+ }
+ free(smf_state);
+ nwam_value_free(fmrival);
+ break;
+ default:
+ /*
+ * Must be a method-based ENM with start (and stop) script(s).
+ */
+ if (!nwamd_enm_run_method(object)) {
+ /* Could not launch method execution thread */
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_METHOD_FAILED;
+ } else {
+ ran_method = B_TRUE;
+ }
+ break;
+ }
+
+ if (state != object->nwamd_object_state ||
+ aux_state != object->nwamd_object_aux_state) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ object->nwamd_object_name, state, aux_state);
+ }
+
+ /*
+ * If the method thread was created, we drop the lock to the ENM
+ * object without decreasing the reference count, ensuring it will not
+ * be destroyed until method execution has completed.
+ */
+ if (ran_method) {
+ nwamd_object_release_and_preserve(object);
+ } else {
+ nwamd_object_release(object);
+ }
+}
+
+/* Deactivates the ENM. */
+static void
+nwamd_enm_deactivate(const char *object_name)
+{
+ nwamd_object_t object;
+ nwam_enm_handle_t enmh;
+ nwam_value_t fmrival;
+ char *fmri, *smf_state;
+ int ret;
+ nwam_state_t state;
+ nwam_aux_state_t aux_state;
+ boolean_t destroying = B_FALSE;
+
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM, object_name);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_deactivate: could not find ENM %s",
+ object_name);
+ return;
+ }
+
+ state = object->nwamd_object_state;
+ aux_state = object->nwamd_object_aux_state;
+ enmh = object->nwamd_object_handle;
+ state = object->nwamd_object_state;
+ /* If destroying, we don't care about method failure/config err */
+ destroying = (aux_state == NWAM_AUX_STATE_UNINITIALIZED);
+
+ nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating enm %s",
+ object->nwamd_object_name);
+
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival)
+ != NWAM_SUCCESS) {
+ /*
+ * Must be a method-based ENM with start (and stop) script(s).
+ * Script execution thread will take care of the rest.
+ * If the method thread was created, we drop the lock to the ENM
+ * object without decreasing the reference count, ensuring it
+ * will not be destroyed until method execution has completed.
+ */
+ if (nwamd_enm_run_method(object)) {
+ nwamd_object_release_and_preserve(object);
+ return;
+ }
+ /* Could not launch method execution thread */
+ if (!destroying) {
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_METHOD_FAILED;
+ }
+ } else {
+ if (nwam_value_get_string(fmrival, &fmri) != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "nwamd_enm_deactivate: could not retrieve "
+ "FMRI string for ENM %s",
+ object->nwamd_object_name);
+ if (!destroying) {
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state = NWAM_AUX_STATE_INVALID_CONFIG;
+ }
+ } else {
+ if ((smf_state = smf_get_state(fmri)) == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_deactivate: invalid "
+ "FMRI %s for ENM %s", fmri,
+ object->nwamd_object_name);
+ nwam_value_free(fmrival);
+ if (!destroying) {
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state =
+ NWAM_AUX_STATE_INVALID_CONFIG;
+ }
+ goto done;
+ }
+ free(smf_state);
+
+ nlog(LOG_DEBUG, "nwamd_enm_deactivate: deactivating %s "
+ "for ENM %s", fmri, object->nwamd_object_name);
+
+ ret = smf_disable_instance(fmri, SMF_TEMPORARY);
+
+ if (ret != 0) {
+ nlog(LOG_ERR, "nwamd_enm_deactivate: "
+ "smf_disable_instance(%s) failed for "
+ "ENM %s: %s", fmri,
+ object->nwamd_object_name,
+ scf_strerror(scf_error()));
+ if (!destroying) {
+ state = NWAM_STATE_MAINTENANCE;
+ aux_state =
+ NWAM_AUX_STATE_METHOD_FAILED;
+ }
+ }
+ }
+ nwam_value_free(fmrival);
+ }
+done:
+ if (state == object->nwamd_object_state &&
+ aux_state == object->nwamd_object_aux_state) {
+ /*
+ * If aux state is "manual disable", we know
+ * this was a disable request, otherwise it was
+ * a _fini request or a condition satisfaction
+ * failure.
+ */
+ switch (object->nwamd_object_aux_state) {
+ case NWAM_AUX_STATE_MANUAL_DISABLE:
+ state = NWAM_STATE_DISABLED;
+ aux_state = NWAM_AUX_STATE_MANUAL_DISABLE;
+ break;
+ case NWAM_AUX_STATE_UNINITIALIZED:
+ state = NWAM_STATE_UNINITIALIZED;
+ aux_state = NWAM_AUX_STATE_UNINITIALIZED;
+ break;
+ default:
+ state = NWAM_STATE_OFFLINE;
+ aux_state = NWAM_AUX_STATE_CONDITIONS_NOT_MET;
+ break;
+ }
+ }
+
+ /* Only change state if we aren't destroying the ENM */
+ if (!destroying && (state != object->nwamd_object_state ||
+ aux_state != object->nwamd_object_aux_state)) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ object->nwamd_object_name, state, aux_state);
+ }
+
+ /* If state/aux state are uninitialized/unintialized, destroy the ENM */
+ if (state == NWAM_STATE_UNINITIALIZED &&
+ aux_state == NWAM_AUX_STATE_UNINITIALIZED) {
+ (void) nwamd_object_release_and_destroy(object);
+ } else {
+ (void) nwamd_object_release(object);
+ }
+}
+
+/*
+ * Determine whether an ENM should be (de)activated.
+ */
+/* ARGSUSED1 */
+static int
+nwamd_enm_check(nwamd_object_t object, void *data)
+{
+ nwam_enm_handle_t enmh;
+ nwam_value_t conditionval;
+ int64_t eactivation;
+ boolean_t enabled, satisfied;
+ char **conditions;
+ nwam_state_t state;
+ uint_t nelem;
+
+ state = object->nwamd_object_state;
+
+ enmh = object->nwamd_object_handle;
+
+ eactivation = enm_get_activation_mode(enmh);
+ if (eactivation == -1)
+ return (0);
+
+ switch (eactivation) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ enabled = enm_is_enabled(enmh);
+
+ if (enabled) {
+ nlog(LOG_DEBUG, "nwamd_enm_check: %s is enabled",
+ object->nwamd_object_name);
+ switch (state) {
+ case NWAM_STATE_ONLINE:
+ case NWAM_STATE_MAINTENANCE:
+ /* Do nothing */
+ break;
+ default:
+ if (nwamd_enm_action(object->nwamd_object_name,
+ NWAM_ACTION_ENABLE) != 0) {
+ nlog(LOG_ERR,
+ "nwamd_enm_check: enable failed "
+ "for enm %s",
+ object->nwamd_object_name);
+ }
+ break;
+ }
+ } else {
+ nlog(LOG_DEBUG, "nwamd_enm_check: %s is disabled",
+ object->nwamd_object_name);
+ switch (state) {
+ case NWAM_STATE_ONLINE:
+ if (nwamd_enm_action(object->nwamd_object_name,
+ NWAM_ACTION_DISABLE) != 0) {
+ nlog(LOG_ERR, "nwamd_enm_check: "
+ "disable failed for enm %s",
+ object->nwamd_object_name);
+ }
+ break;
+ case NWAM_STATE_MAINTENANCE:
+ /* Do nothing */
+ break;
+ case NWAM_STATE_DISABLED:
+ /* Do nothing */
+ break;
+ default:
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ object->nwamd_object_name,
+ NWAM_STATE_DISABLED,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ break;
+ }
+ }
+ break;
+
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
+ if (nwam_enm_get_prop_value(enmh,
+ NWAM_ENM_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
+ "condition value");
+ break;
+ }
+ if (nwam_value_get_string_array(conditionval,
+ &conditions, &nelem) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_enm_check: could not retrieve "
+ "condition value");
+ nwam_value_free(conditionval);
+ break;
+ }
+ satisfied = nwamd_check_conditions((uint64_t)eactivation,
+ conditions, nelem);
+
+ nlog(LOG_DEBUG, "nwamd_enm_check: conditions for enm %s "
+ "%s satisfied", object->nwamd_object_name,
+ satisfied ? "is" : "is not");
+ if (state != NWAM_STATE_ONLINE && satisfied) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ object->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_METHOD_RUNNING);
+ }
+ if (state == NWAM_STATE_ONLINE && !satisfied) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ object->nwamd_object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ nwam_value_free(conditionval);
+ break;
+
+ }
+ return (0);
+}
+
+void
+nwamd_enm_check_conditions(void)
+{
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_ENM, nwamd_enm_check, NULL);
+}
+
+int
+nwamd_enm_action(const char *enm, nwam_action_t action)
+{
+ nwamd_event_t event = nwamd_event_init_object_action
+ (NWAM_OBJECT_TYPE_ENM, enm, NULL, action);
+ if (event == NULL)
+ return (1);
+ nwamd_event_enqueue(event);
+ return (0);
+}
+
+/*
+ * Event handling functions.
+ */
+
+/* Handle ENM initialization/refresh event */
+void
+nwamd_enm_handle_init_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+ nwam_enm_handle_t enmh;
+ nwam_error_t err;
+ boolean_t manual_disabled = B_FALSE;
+
+ if ((err = nwam_enm_read(event->event_object, 0, &enmh))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_enm_handle_init_event: could not "
+ "read object '%s': %s", event->event_object,
+ nwam_strerror(err));
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
+ event->event_object)) != NULL) {
+ nwam_enm_free(object->nwamd_object_handle);
+ object->nwamd_object_handle = enmh;
+ } else {
+ object = nwamd_object_init(NWAM_OBJECT_TYPE_ENM,
+ event->event_object, enmh, NULL);
+ object->nwamd_object_state = NWAM_STATE_OFFLINE;
+ object->nwamd_object_aux_state =
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET;
+ }
+ manual_disabled = (enm_get_activation_mode(enmh) ==
+ NWAM_ACTIVATION_MODE_MANUAL && !enm_is_enabled(enmh));
+
+ /*
+ * If this ENM is ONLINE, and not manual and disabled (since in
+ * that case it was online but we've just set enabled = false as part
+ * of a disable action), then it is still active but refreshing.
+ * Change states to re-activate itself.
+ */
+ if (!manual_disabled &&
+ object->nwamd_object_state == NWAM_STATE_ONLINE) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_METHOD_RUNNING);
+ }
+ nwamd_object_release(object);
+}
+
+/* Handle ENM finish event */
+void
+nwamd_enm_handle_fini_event(nwamd_event_t event)
+{
+ nwamd_event_t state_event;
+
+ nlog(LOG_DEBUG, "nwamd_enm_handle_fini_event(%s)", event->event_object);
+
+ /*
+ * Simulate a state event so that the state machine can correctly
+ * deactivate the ENM and free up the handle.
+ */
+ state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_ENM,
+ event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_UNINITIALIZED);
+ if (state_event == NULL) {
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ nwamd_enm_handle_state_event(state_event);
+ nwamd_event_fini(state_event);
+ /*
+ * Do not free the handle and object.
+ * nwamd_enm_activate_deactivate_thread() and
+ * nwamd_enm_deactivate() does this after running the stop script
+ * and disabling the FMRI respectively.
+ */
+}
+
+void
+nwamd_enm_handle_action_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+
+ switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
+ case NWAM_ACTION_ENABLE:
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
+ event->event_object);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
+ "could not find enm %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
+ nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
+ "enm %s already online, nothing to do",
+ event->event_object);
+ nwamd_object_release(object);
+ return;
+ }
+ nwamd_object_release(object);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_METHOD_RUNNING);
+ break;
+ case NWAM_ACTION_DISABLE:
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
+ event->event_object);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_handle_action_event: "
+ "could not find enm %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
+ nlog(LOG_DEBUG, "nwamd_enm_handle_action_event: "
+ "enm %s already disabled, nothing to do",
+ event->event_object);
+ nwamd_object_release(object);
+ return;
+ }
+ nwamd_object_release(object);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_ENM,
+ event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ break;
+ case NWAM_ACTION_ADD:
+ case NWAM_ACTION_REFRESH:
+ nwamd_enm_handle_init_event(event);
+ break;
+ case NWAM_ACTION_DESTROY:
+ nwamd_enm_handle_fini_event(event);
+ break;
+ default:
+ nlog(LOG_INFO, "nwam_enm_handle_action_event: "
+ "unexpected action");
+ nwamd_event_do_not_send(event);
+ break;
+ }
+}
+
+void
+nwamd_enm_handle_state_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+ nwam_state_t new_state;
+ nwam_aux_state_t new_aux_state;
+
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_ENM,
+ event->event_object)) == NULL) {
+ nlog(LOG_ERR, "nwamd_enm_handle_state_event: "
+ "state event for nonexistent ENM %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
+ new_aux_state =
+ event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
+
+ if (new_state == object->nwamd_object_state &&
+ new_aux_state == object->nwamd_object_aux_state) {
+ nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: "
+ "ENM %s already in state (%s , %s)",
+ object->nwamd_object_name, nwam_state_to_string(new_state),
+ nwam_aux_state_to_string(new_aux_state));
+ nwamd_object_release(object);
+ return;
+ }
+
+ object->nwamd_object_state = new_state;
+ object->nwamd_object_aux_state = new_aux_state;
+
+ nlog(LOG_DEBUG, "nwamd_enm_handle_state_event: changing state for ENM "
+ "%s to (%s , %s)", object->nwamd_object_name,
+ nwam_state_to_string(object->nwamd_object_state),
+ nwam_aux_state_to_string(object->nwamd_object_aux_state));
+
+ nwamd_object_release(object);
+
+ /*
+ * State machine for ENMs.
+ */
+ switch (new_state) {
+ case NWAM_STATE_OFFLINE_TO_ONLINE:
+ nwamd_enm_activate(event->event_object);
+ break;
+ case NWAM_STATE_ONLINE_TO_OFFLINE:
+ nwamd_enm_deactivate(event->event_object);
+ break;
+ case NWAM_STATE_DISABLED:
+ case NWAM_STATE_OFFLINE:
+ case NWAM_STATE_UNINITIALIZED:
+ case NWAM_STATE_MAINTENANCE:
+ case NWAM_STATE_DEGRADED:
+ default:
+ /* do nothing */
+ break;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/events.c b/usr/src/cmd/cmd-inet/lib/nwamd/events.c
index 743726ba55..ff4cd71e7d 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/events.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/events.c
@@ -20,639 +20,858 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-/*
- * This file contains routines to retrieve events from the system and package
- * them for high level processing.
- *
- * struct np_event is the basic event structure. The np_event structure and
- * its npe_name member are allocated using malloc(3c). free_event() frees both
- * the npe_name member and the associated np_event structure.
- *
- * np_queue_add_event() and np_queue_get_event() provide functionality for
- * adding events to a queue and blocking on that queue for an event.
- *
- * Functions of the form addevent_*() provide the mechanism to cook down a
- * higher level event into an np_event and put it on the queue.
- *
- * 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.
- *
- * start_event_collection() creates a thread to run routing_events() and one
- * to run periodic_wireless_scan() in. Finally it does an initial collection
- * of information from each interface currently known.
- */
-
-#include <arpa/inet.h>
+#include <atomic.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 <execinfo.h>
+#include <libuutil.h>
#include <pthread.h>
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/fcntl.h>
+#include <strings.h>
#include <syslog.h>
+#include <sys/time.h>
#include <unistd.h>
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
+#include "conditions.h"
+#include "events.h"
+#include "objects.h"
+#include "util.h"
-struct np_event *equeue;
-static struct np_event *equeue_end;
+/*
+ * events.c - contains routines which create/destroy event sources,
+ * handle the event queue and process events from that queue.
+ */
-pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
-pthread_t routing, scan;
+/* Add new event sources here. */
+struct nwamd_event_source {
+ char *name;
+ void (*events_init)(void);
+ void (*events_fini)(void);
+} event_sources[] = {
+ { "routing_events",
+ nwamd_routing_events_init, nwamd_routing_events_fini },
+ { "sysevent_events",
+ nwamd_sysevent_events_init, nwamd_sysevent_events_fini },
+};
-static sysevent_handle_t *sysevent_handle;
+/* Counter for event ids */
+static uint64_t event_id_counter = 0;
-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);
+static uu_list_pool_t *event_pool = NULL;
+static uu_list_t *event_queue = NULL;
+static pthread_mutex_t event_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t event_queue_cond = PTHREAD_COND_INITIALIZER;
-union rtm_buf
+static int nwamd_event_compare(const void *, const void *, void *);
+
+static const char *
+nwamd_event_name(int event_type)
{
- /* Routing information. */
- struct
- {
- struct rt_msghdr rtm;
- struct sockaddr_storage addr[RTAX_MAX];
- } r;
-
- /* Interface information. */
- struct
- {
- struct if_msghdr ifm;
- struct sockaddr_storage addr[RTAX_MAX];
- } im;
-
- /* Interface address information. */
- struct
- {
- struct ifa_msghdr ifa;
- struct sockaddr_storage addr[RTAX_MAX];
- } ia;
-};
+ if (event_type <= NWAM_EVENT_MAX)
+ return (nwam_event_type_to_string(event_type));
+
+ switch (event_type) {
+ case NWAM_EVENT_TYPE_OBJECT_INIT:
+ return ("OBJECT_INIT");
+ case NWAM_EVENT_TYPE_OBJECT_FINI:
+ return ("OBJECT_FINI");
+ case NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS:
+ return ("TIMED_CHECK_CONDITIONS");
+ case NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS:
+ return ("TRIGGERED_CHECK_CONDITIONS");
+ case NWAM_EVENT_TYPE_NCU_CHECK:
+ return ("NCU_CHECK");
+ case NWAM_EVENT_TYPE_TIMER:
+ return ("TIMER");
+ case NWAM_EVENT_TYPE_UPGRADE:
+ return ("UPGRADE");
+ case NWAM_EVENT_TYPE_PERIODIC_SCAN:
+ return ("PERIODIC_SCAN");
+ case NWAM_EVENT_TYPE_QUEUE_QUIET:
+ return ("QUEUE_QUIET");
+ default:
+ return ("N/A");
+ }
+}
void
-free_event(struct np_event *npe)
+nwamd_event_sources_init(void)
{
- free(npe);
+ int i;
+
+ /*
+ * Now we can safely initialize event sources.
+ */
+ for (i = 0;
+ i < sizeof (event_sources) / sizeof (struct nwamd_event_source);
+ i++) {
+ if (event_sources[i].events_init != NULL)
+ event_sources[i].events_init();
+ }
}
-boolean_t
-np_queue_add_event(enum np_event_type evt, const char *ifname)
+void
+nwamd_event_sources_fini(void)
{
- struct np_event *npe;
- size_t slen;
+ int i;
- 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 = npe;
- equeue_end = npe;
- } else {
- equeue = equeue_end = npe;
+ for (i = 0;
+ i < sizeof (event_sources) / sizeof (struct nwamd_event_source);
+ i++) {
+ if (event_sources[i].events_init != NULL)
+ event_sources[i].events_fini();
}
- equeue_end->npe_next = NULL;
- (void) pthread_cond_signal(&queue_cond);
- (void) pthread_mutex_unlock(&queue_mutex);
- return (B_TRUE);
}
/*
- * Blocking getevent. This routine will block until there is an event for
- * it to return.
+ * Comparison function for events, passed in as callback to
+ * uu_list_pool_create(). Compare by time, so that timer
+ * event queue can be sorted by nearest time to present.
*/
-struct np_event *
-np_queue_get_event(void)
+/* ARGSUSED */
+static int
+nwamd_event_compare(const void *l_arg, const void *r_arg, void *private)
{
- struct np_event *rv = NULL;
+ nwamd_event_t l = (nwamd_event_t)l_arg;
+ nwamd_event_t r = (nwamd_event_t)r_arg;
+ int rv;
- (void) pthread_mutex_lock(&queue_mutex);
+ rv = l->event_time.tv_sec - r->event_time.tv_sec;
+ if (rv == 0)
+ rv = l->event_time.tv_nsec - r->event_time.tv_nsec;
- while (equeue == NULL)
- (void) pthread_cond_wait(&queue_cond, &queue_mutex);
-
- rv = equeue;
- equeue = equeue->npe_next;
- if (equeue == NULL)
- equeue_end = NULL;
-
- (void) pthread_mutex_unlock(&queue_mutex);
-
- rv->npe_next = NULL;
return (rv);
}
-const char *
-npe_type_str(enum np_event_type type)
+void
+nwamd_event_queue_init(void)
{
- switch (type) {
- 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:
- return ("unknown");
- }
+ event_pool = uu_list_pool_create("event_queue_pool",
+ sizeof (struct nwamd_event),
+ offsetof(struct nwamd_event, event_node),
+ nwamd_event_compare, UU_LIST_POOL_DEBUG);
+ if (event_pool == NULL)
+ pfail("uu_list_pool_create failed with error %d", uu_error());
+ event_queue = uu_list_create(event_pool, NULL, UU_LIST_SORTED);
+ if (event_queue == NULL)
+ pfail("uu_list_create failed with error %d", uu_error());
}
-static const char *
-rtmtype_str(int type)
+void
+nwamd_event_queue_fini(void)
{
- static char typestr[12]; /* strlen("type ") + enough for an int */
-
- switch (type) {
- case RTM_ADD:
- return ("ADD");
- case RTM_DELETE:
- return ("DELETE");
- case RTM_NEWADDR:
- return ("NEWADDR");
- case RTM_DELADDR:
- return ("DELADDR");
- case RTM_IFINFO:
- return ("IFINFO");
- default:
- (void) snprintf(typestr, sizeof (typestr), "type %d",
- type);
- return (typestr);
- }
+ void *cookie = NULL;
+ nwamd_event_t event;
+
+ while ((event = uu_list_teardown(event_queue, &cookie)) != NULL)
+ nwamd_event_fini(event);
+ uu_list_destroy(event_queue);
+ if (event_pool != NULL)
+ uu_list_pool_destroy(event_pool);
}
-/*
- * 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)
+nwamd_event_t
+nwamd_event_init(int32_t type, nwam_object_type_t object_type,
+ size_t size, const char *object_name)
{
- 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;
+ nwamd_event_t event;
+
+ event = calloc(1, sizeof (struct nwamd_event));
+ if (event == NULL) {
+ nlog(LOG_ERR, "nwamd_event_init: could not create %s event for "
+ "object %s", nwamd_event_name(type),
+ object_name != NULL ? object_name : "<no object>");
+ return (NULL);
}
- /*
- * 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;
+ /* Is this an externally-visible event? */
+ if (type <= NWAM_EVENT_MAX) {
+ event->event_send = B_TRUE;
+ event->event_msg = calloc(1, sizeof (struct nwam_event) + size);
+ if (event->event_msg == NULL) {
+ nlog(LOG_ERR,
+ "nwamd_event_init: could not create %s event",
+ nwamd_event_name(type));
+ free(event);
+ return (NULL);
+ }
+ event->event_msg->nwe_type = type;
+ event->event_msg->nwe_size = sizeof (struct nwam_event) + size;
+ } else {
+ event->event_send = B_FALSE;
+ event->event_msg = NULL;
}
- 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));
+
+ event->event_type = type;
+
+ if (object_name != NULL) {
+ (void) strlcpy(event->event_object, object_name,
+ NWAM_MAX_NAME_LEN);
+ event->event_object_type = object_type;
} 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);
+ event->event_object[0] = '\0';
}
- nvlist_free(attr_list);
+
+ /* Set event id */
+ event->event_id = atomic_add_64_nv(&event_id_counter, 1);
+ (void) clock_gettime(CLOCK_REALTIME, &event->event_time);
+
+ return (event);
}
-static void
-hotplug_events_unregister(void)
+void
+nwamd_event_do_not_send(nwamd_event_t event)
{
- /* Unsubscribe to sysevents */
- sysevent_unbind_handle(sysevent_handle);
- sysevent_handle = NULL;
+ nlog(LOG_DEBUG, "nwamd_event_do_not_send: cancelling delivery of "
+ "event %s for object %s", nwamd_event_name(event->event_type),
+ event->event_object[0] != '\0' ?
+ event->event_object : "<no object>");
+ event->event_send = B_FALSE;
}
-static void
-hotplug_events_register(void)
+void
+nwamd_event_fini(nwamd_event_t event)
{
- 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;
+ if (event != NULL) {
+ free(event->event_msg);
+ free(event);
}
- /*
- * 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();
+}
+
+nwamd_event_t
+nwamd_event_init_object_action(nwam_object_type_t object_type,
+ const char *object_name, const char *parent_name,
+ nwam_action_t object_action)
+{
+ nwamd_event_t event;
+
+ event = nwamd_event_init(NWAM_EVENT_TYPE_OBJECT_ACTION,
+ object_type, 0, object_name);
+ if (event == NULL)
+ return (NULL);
+
+ event->event_msg->nwe_data.nwe_object_action.nwe_action = object_action;
+ event->event_msg->nwe_data.nwe_object_action.nwe_object_type =
+ object_type;
+ (void) strlcpy(event->event_msg->nwe_data.nwe_object_action.nwe_name,
+ object_name,
+ sizeof (event->event_msg->nwe_data.nwe_object_action.nwe_name));
+ if (parent_name == NULL) {
+ event->event_msg->nwe_data.nwe_object_action.nwe_parent[0] =
+ '\0';
+ return (event);
}
+ (void) strlcpy
+ (event->event_msg->nwe_data.nwe_object_action.nwe_parent,
+ parent_name,
+ sizeof (event->event_msg->nwe_data.nwe_object_action.nwe_parent));
+ return (event);
}
-/*
- * 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)
+nwamd_event_t
+nwamd_event_init_object_state(nwam_object_type_t object_type,
+ const char *object_name, nwam_state_t state, nwam_aux_state_t aux_state)
{
- int rtsock;
- int n;
- union rtm_buf buffer;
- struct rt_msghdr *rtm;
- struct ifa_msghdr *ifa;
- struct if_msghdr *ifm;
+ nwamd_event_t event;
- /*
- * We use v4 interfaces as proxies for links so those are the only
- * routing messages we need to listen to. Look at the comments in
- * structures.h for more information about the split between the
- * llp and interfaces.
- */
- rtsock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
- if (rtsock == -1) {
- syslog(LOG_ERR, "failed to open routing socket: %m");
- exit(EXIT_FAILURE);
+ event = nwamd_event_init(NWAM_EVENT_TYPE_OBJECT_STATE,
+ object_type, 0, object_name);
+ if (event == NULL)
+ return (NULL);
+
+ event->event_msg->nwe_data.nwe_object_state.nwe_state = state;
+ event->event_msg->nwe_data.nwe_object_state.nwe_aux_state = aux_state;
+ event->event_msg->nwe_data.nwe_object_state.nwe_object_type =
+ object_type;
+ (void) strlcpy(event->event_msg->nwe_data.nwe_object_state.nwe_name,
+ object_name,
+ sizeof (event->event_msg->nwe_data.nwe_object_state.nwe_name));
+
+ return (event);
+}
+
+nwamd_event_t
+nwamd_event_init_priority_group_change(int64_t priority)
+{
+ nwamd_event_t event;
+
+ event = nwamd_event_init(NWAM_EVENT_TYPE_PRIORITY_GROUP,
+ NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
+ if (event == NULL)
+ return (NULL);
+
+ event->event_msg->nwe_data.nwe_priority_group_info.nwe_priority =
+ priority;
+
+ return (event);
+}
+
+nwamd_event_t
+nwamd_event_init_link_action(const char *name, nwam_action_t link_action)
+{
+ nwamd_event_t event;
+ nwam_error_t err;
+ char *object_name;
+
+ if ((err = nwam_ncu_name_to_typed_name(name, NWAM_NCU_TYPE_LINK,
+ &object_name)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_event_init_link_action: "
+ "nwam_ncu_name_to_typed_name: %s",
+ nwam_strerror(err));
+ return (NULL);
}
+ event = nwamd_event_init(NWAM_EVENT_TYPE_LINK_ACTION,
+ NWAM_OBJECT_TYPE_NCU, 0, object_name);
+ free(object_name);
+ if (event == NULL)
+ return (NULL);
- dprintf("routing socket %d", rtsock);
+ (void) strlcpy(event->event_msg->nwe_data.nwe_link_action.nwe_name,
+ name,
+ sizeof (event->event_msg->nwe_data.nwe_link_action.nwe_name));
+ event->event_msg->nwe_data.nwe_link_action.nwe_action = link_action;
- for (;;) {
- char *addrs;
- struct sockaddr_dl *addr_dl;
- struct sockaddr_in *addr_in;
+ return (event);
+}
- rtm = &buffer.r.rtm;
- n = read(rtsock, &buffer, sizeof (buffer));
- if (n == -1 && errno == EAGAIN) {
- continue;
- } else if (n == -1) {
- syslog(LOG_ERR, "error reading routing socket "
- "%d: %m", rtsock);
- /* Low likelihood. What's recovery path? */
- continue;
- }
+nwamd_event_t
+nwamd_event_init_link_state(const char *name, boolean_t up)
+{
+ nwamd_event_t event;
+ nwam_error_t err;
+ char *object_name;
+
+ if ((err = nwam_ncu_name_to_typed_name(name, NWAM_NCU_TYPE_LINK,
+ &object_name)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_event_init_link_state: "
+ "nwam_ncu_name_to_typed_name: %s",
+ nwam_strerror(err));
+ return (NULL);
+ }
- if (rtm->rtm_msglen < n) {
- syslog(LOG_ERR, "only read %d bytes from "
- "routing socket but message claims to be "
- "of length %d", rtm->rtm_msglen);
- continue;
- }
+ event = nwamd_event_init(NWAM_EVENT_TYPE_LINK_STATE,
+ NWAM_OBJECT_TYPE_NCU, 0, object_name);
+ free(object_name);
+ if (event == NULL)
+ return (NULL);
- if (rtm->rtm_version != RTM_VERSION) {
- syslog(LOG_ERR, "tossing routing message of "
- "version %d type %d", rtm->rtm_version,
- rtm->rtm_type);
- continue;
- }
+ (void) strlcpy(event->event_msg->nwe_data.nwe_link_state.nwe_name, name,
+ sizeof (event->event_msg->nwe_data.nwe_link_state.nwe_name));
+ event->event_msg->nwe_data.nwe_link_state.nwe_link_up = up;
- if (rtm->rtm_msglen != n) {
- dprintf("routing message of %d size came from "
- "read of %d on socket %d", rtm->rtm_msglen,
- n, rtsock);
- }
+ return (event);
+}
- switch (rtm->rtm_type) {
- case RTM_DELADDR: {
- uint64_t ifflags;
+nwamd_event_t
+nwamd_event_init_if_state(const char *linkname, uint32_t flags,
+ uint32_t addr_added, uint32_t index, struct sockaddr *addr)
+{
+ nwamd_event_t event;
+ nwam_error_t err;
+ char *object_name;
+
+ /* linkname does not contain the lifnum */
+ if ((err = nwam_ncu_name_to_typed_name(linkname,
+ NWAM_NCU_TYPE_INTERFACE, &object_name)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_event_init_if_state: "
+ "nwam_ncu_name_to_typed_name: %s",
+ nwam_strerror(err));
+ return (NULL);
+ }
- /*
- * 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);
+ event = nwamd_event_init(NWAM_EVENT_TYPE_IF_STATE,
+ NWAM_OBJECT_TYPE_NCU, 0, object_name);
+ free(object_name);
+ if (event == NULL)
+ return (NULL);
- dprintf("routing message DELADDR: index %d flags %x",
- ifa->ifam_index, ifa->ifam_flags);
- printaddrs(ifa->ifam_addrs, addrs);
+ (void) strlcpy(event->event_msg->nwe_data.nwe_if_state.nwe_name,
+ linkname,
+ sizeof (event->event_msg->nwe_data.nwe_if_state.nwe_name));
+ event->event_msg->nwe_data.nwe_if_state.nwe_flags = flags;
+ event->event_msg->nwe_data.nwe_if_state.nwe_index = index;
+ event->event_msg->nwe_data.nwe_if_state.nwe_addr_added = addr_added;
+ event->event_msg->nwe_data.nwe_if_state.nwe_addr_valid = (addr != NULL);
+
+ if (addr != NULL) {
+ bcopy(addr, &(event->event_msg->nwe_data.nwe_if_state.nwe_addr),
+ addr->sa_family == AF_INET ? sizeof (struct sockaddr_in) :
+ sizeof (struct sockaddr_in6));
+ }
- if (ifa->ifam_index == 0) {
- /* what is this? */
- dprintf("tossing index 0 routing event");
- break;
- }
+ return (event);
+}
- addr_in = getaddr(RTA_IFA, ifa->ifam_addrs, addrs);
- if (addr_in == NULL) {
- dprintf("no RTA_IFA in RTM_DELADDR message");
- break;
- }
+nwamd_event_t
+nwamd_event_init_wlan(const char *name, int32_t type, boolean_t connected,
+ nwam_wlan_t *wlans, uint_t num_wlans)
+{
+ size_t size = 0;
+ char *object_name;
+ nwamd_event_t event;
+ nwam_error_t err;
- addr_dl = getaddr(RTA_IFP, ifa->ifam_addrs, addrs);
- if (addr_dl == NULL) {
- dprintf("no RTA_IFP in RTM_DELADDR message");
- break;
- }
+ switch (type) {
+ case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
+ case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
+ size = sizeof (nwam_wlan_t) * (num_wlans - 1);
+ break;
+ case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
+ case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_event_init_wlan: unexpected "
+ "event type %s (%d)", nwamd_event_name(type), type);
+ return (NULL);
+ }
+ if ((err = nwam_ncu_name_to_typed_name(name, NWAM_NCU_TYPE_LINK,
+ &object_name)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_event_init_wlan: "
+ "nwam_ncu_name_to_typed_name: %s",
+ nwam_strerror(err));
+ return (NULL);
+ }
- addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
+ event = nwamd_event_init(type, NWAM_OBJECT_TYPE_NCU, size, object_name);
+ free(object_name);
+ if (event == NULL)
+ return (NULL);
- 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;
- }
+ (void) strlcpy(event->event_msg->nwe_data.nwe_wlan_info.nwe_name, name,
+ sizeof (event->event_msg->nwe_data.nwe_wlan_info.nwe_name));
+ event->event_msg->nwe_data.nwe_wlan_info.nwe_connected = connected;
+ event->event_msg->nwe_data.nwe_wlan_info.nwe_num_wlans = num_wlans;
- case RTM_NEWADDR:
- ifa = (void *)rtm;
- addrs = (char *)ifa + sizeof (*ifa);
+ /* copy the wlans */
+ (void) memcpy(event->event_msg->nwe_data.nwe_wlan_info.nwe_wlans, wlans,
+ num_wlans * sizeof (nwam_wlan_t));
- dprintf("routing message NEWADDR: index %d flags %x",
- ifa->ifam_index, ifa->ifam_flags);
- printaddrs(ifa->ifam_addrs, addrs);
+ return (event);
+}
- if (ifa->ifam_index == 0) {
- /* what is this? */
- dprintf("tossing index 0 routing event");
- break;
- }
+nwamd_event_t
+nwamd_event_init_ncu_check(void)
+{
+ return (nwamd_event_init(NWAM_EVENT_TYPE_NCU_CHECK,
+ NWAM_OBJECT_TYPE_NCP, 0, NULL));
+}
- addr_in = getaddr(RTA_IFA, ifa->ifam_addrs, addrs);
- if (addr_in == NULL) {
- dprintf("no RTA_IFA in RTM_NEWADDR message");
- break;
- }
+nwamd_event_t
+nwamd_event_init_init(void)
+{
+ return (nwamd_event_init(NWAM_EVENT_TYPE_INIT,
+ NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
+}
- addr_dl = getaddr(RTA_IFP, ifa->ifam_addrs, addrs);
- if (addr_dl == NULL) {
- dprintf("no RTA_IFP in RTM_NEWADDR message");
- break;
- }
+nwamd_event_t
+nwamd_event_init_shutdown(void)
+{
+ return (nwamd_event_init(NWAM_EVENT_TYPE_SHUTDOWN,
+ NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL));
+}
- /*
- * We don't use the lladdr in this structure so we can
- * run over it.
- */
- addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
+/*
+ * Add event to the event list.
+ */
+void
+nwamd_event_enqueue(nwamd_event_t event)
+{
+ nwamd_event_enqueue_timed(event, 0);
+}
- update_interface_v4_address(addr_dl->sdl_data,
- addr_in->sin_addr.s_addr);
- break;
+/*
+ * Schedule an event to be added to the event list for future processing.
+ * The event will be scheduled in delta_seconds seconds mod schedule delay and
+ * time resolution.
+ */
+void
+nwamd_event_enqueue_timed(nwamd_event_t event, int delta_seconds)
+{
+ uu_list_index_t idx;
- 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);
+ nlog(LOG_DEBUG, "enqueueing event %lld %d (%s) for object %s in %ds",
+ event->event_id, event->event_type,
+ nwamd_event_name(event->event_type),
+ event->event_object[0] != 0 ? event->event_object : "none",
+ delta_seconds);
- if (ifm->ifm_index == 0) {
- dprintf("tossing index 0 routing event");
- break;
- }
+ (void) clock_gettime(CLOCK_REALTIME, &event->event_time);
+ event->event_time.tv_sec += delta_seconds;
- addr_dl = getaddr(RTA_IFP, ifm->ifm_addrs, addrs);
- if (addr_dl == NULL) {
- dprintf("no RTA_IFP in RTM_IFINFO message");
- break;
- }
+ uu_list_node_init(event, &event->event_node, event_pool);
- /*
- * We don't use the lladdr in this structure so we can
- * run over it.
- */
- addr_dl->sdl_data[addr_dl->sdl_nlen] = 0;
+ (void) pthread_mutex_lock(&event_queue_mutex);
- update_interface_flags(addr_dl->sdl_data,
- ifm->ifm_flags);
- break;
+ /*
+ * Find appropriate location to insert the event based on time.
+ */
+ (void) uu_list_find(event_queue, event, NULL, &idx);
+ (void) uu_list_insert(event_queue, event, idx);
- default:
- dprintf("routing message %s socket %d discarded",
- rtmtype_str(rtm->rtm_type), rtsock);
- break;
- }
- }
- /* NOTREACHED */
- return (NULL);
+ (void) pthread_cond_signal(&event_queue_cond);
+ (void) pthread_mutex_unlock(&event_queue_mutex);
}
-static char *
-printaddr(void **address)
+/*
+ * Is the specified event enqueued on the event (or pending event queue)
+ * for execution in when seconds? An object may be specified also.
+ */
+boolean_t
+nwamd_event_enqueued(int32_t event_type, nwam_object_type_t object_type,
+ const char *object)
{
- static char buffer[80];
- sa_family_t family = *(sa_family_t *)*address;
- struct sockaddr_in *s4 = *address;
- struct sockaddr_in6 *s6 = *address;
- struct sockaddr_dl *dl = *address;
-
- switch (family) {
- case AF_UNSPEC:
- (void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
- sizeof (buffer));
- *address = (char *)*address + sizeof (*s4);
- break;
- case AF_INET:
- (void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
- sizeof (buffer));
- *address = (char *)*address + sizeof (*s4);
- break;
- case AF_INET6:
- (void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
- sizeof (buffer));
- *address = (char *)*address + sizeof (*s6);
- break;
- case AF_LINK:
- (void) snprintf(buffer, sizeof (buffer), "link %.*s",
- dl->sdl_nlen, dl->sdl_data);
- *address = (char *)*address + sizeof (*dl);
- break;
- default:
- /*
- * We can't reliably update the size of this thing
- * because we don't know what its type is. So bump
- * it by a sockaddr_in and see what happens. The
- * caller should really make sure this never happens.
- */
- *address = (char *)*address + sizeof (*s4);
- (void) snprintf(buffer, sizeof (buffer),
- "unknown address family %d", family);
- break;
+ nwamd_event_t event;
+
+ (void) pthread_mutex_lock(&event_queue_mutex);
+ for (event = uu_list_first(event_queue);
+ event != NULL;
+ event = uu_list_next(event_queue, event)) {
+ if (event->event_type != event_type)
+ continue;
+ if (object_type != NWAM_OBJECT_TYPE_UNKNOWN &&
+ event->event_object_type != object_type)
+ continue;
+ if (object != NULL && strcmp(object, event->event_object) != 0)
+ continue;
+ (void) pthread_mutex_unlock(&event_queue_mutex);
+ return (B_TRUE);
}
- return (buffer);
+ (void) pthread_mutex_unlock(&event_queue_mutex);
+
+ return (B_FALSE);
}
-static void
-printaddrs(int mask, void *address)
+/*
+ * Is the time in the past.
+ */
+static boolean_t
+in_past(struct timespec t)
{
- if (mask == 0)
- return;
- if (mask & RTA_DST)
- dprintf("destination address: %s", printaddr(&address));
- if (mask & RTA_GATEWAY)
- dprintf("gateway address: %s", printaddr(&address));
- if (mask & RTA_NETMASK)
- dprintf("netmask: %s", printaddr(&address));
- if (mask & RTA_GENMASK)
- dprintf("cloning mask: %s", printaddr(&address));
- if (mask & RTA_IFP)
- dprintf("interface name: %s", printaddr(&address));
- if (mask & RTA_IFA)
- dprintf("interface address: %s", printaddr(&address));
- if (mask & RTA_AUTHOR)
- dprintf("author: %s", printaddr(&address));
- if (mask & RTA_BRD)
- dprintf("broadcast address: %s", printaddr(&address));
+ struct timespec now;
+
+ (void) clock_gettime(CLOCK_REALTIME, &now);
+ if (t.tv_sec < now.tv_sec)
+ return (B_TRUE);
+ if (t.tv_sec > now.tv_sec)
+ return (B_FALSE);
+ if (t.tv_nsec < now.tv_nsec)
+ return (B_TRUE);
+ return (B_FALSE);
}
-static void
-nextaddr(void **address)
+/*
+ * Remove event at head of event list for processing. This takes a number of
+ * nanoseconds to wait. If the number is 0 then it blocks. If there is
+ * nothing on the queue then it returns an event which says that the queue
+ * is quiet.
+ */
+static nwamd_event_t
+nwamd_event_dequeue(long nsec)
{
- sa_family_t family = *(sa_family_t *)*address;
+ nwamd_event_t event;
+
+ (void) pthread_mutex_lock(&event_queue_mutex);
+ event = uu_list_first(event_queue);
+ if (event == NULL && nsec == 0) {
+ do {
+ (void) pthread_cond_wait(&event_queue_cond,
+ &event_queue_mutex);
+ } while ((event = uu_list_first(event_queue)) == NULL);
+ } else {
+ struct timespec waitcap;
- switch (family) {
- case AF_UNSPEC:
- case AF_INET:
- *address = (char *)*address + sizeof (struct sockaddr_in);
- break;
- case AF_INET6:
- *address = (char *)*address + sizeof (struct sockaddr_in6);
- break;
- case AF_LINK:
- *address = (char *)*address + sizeof (struct sockaddr_dl);
- break;
- default:
- syslog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
- break;
+ if (nsec != 0) {
+ (void) clock_gettime(CLOCK_REALTIME, &waitcap);
+ waitcap.tv_nsec += nsec;
+ waitcap.tv_sec += NSEC_TO_SEC(waitcap.tv_nsec);
+ waitcap.tv_nsec = NSEC_TO_FRACNSEC(waitcap.tv_nsec);
+ }
+
+ /*
+ * Keep going as long as the first event hasn't matured and
+ * we havn't passed our maximum wait time.
+ */
+ while ((event == NULL || !in_past(event->event_time)) &&
+ (nsec == 0 || !in_past(waitcap))) {
+ struct timespec eventwait;
+
+ /*
+ * Three cases:
+ * no maximum waittime - just use the event
+ * both an event and cap - take the least one
+ * just a maximum waittime - use it
+ */
+ if (nsec == 0) {
+ eventwait = event->event_time;
+ } else if (event != NULL) {
+ uint64_t diff;
+ diff = SEC_TO_NSEC(event->event_time.tv_sec -
+ waitcap.tv_sec) +
+ event->event_time.tv_nsec - waitcap.tv_nsec;
+
+ if (diff > 0)
+ eventwait = waitcap;
+ else
+ eventwait = event->event_time;
+ } else {
+ /*
+ * Note that if the event is NULL then nsec is
+ * nonzero and waitcap is valid.
+ */
+ eventwait = waitcap;
+ }
+
+ (void) pthread_cond_timedwait(&event_queue_cond,
+ &event_queue_mutex, &eventwait);
+ event = uu_list_first(event_queue);
+ }
}
+
+ /*
+ * At this point we've met the guard contition of the while loop.
+ * The event at the top of the queue might be mature in which case
+ * we use it. Otherwise we hit our cap and we need to enqueue a
+ * quiesced queue event.
+ */
+ if (event != NULL && in_past(event->event_time)) {
+ uu_list_remove(event_queue, event);
+ uu_list_node_fini(event, &event->event_node, event_pool);
+ } else {
+ event = nwamd_event_init(NWAM_EVENT_TYPE_QUEUE_QUIET,
+ NWAM_OBJECT_TYPE_UNKNOWN, 0, NULL);
+ }
+
+ if (event != NULL)
+ nlog(LOG_DEBUG,
+ "dequeueing event %lld of type %d (%s) for object %s",
+ event->event_id, event->event_type,
+ nwamd_event_name(event->event_type),
+ event->event_object[0] != 0 ? event->event_object :
+ "none");
+
+ (void) pthread_mutex_unlock(&event_queue_mutex);
+
+ return (event);
}
-static void *
-getaddr(int addrid, int mask, void *address)
+void
+nwamd_event_send(nwam_event_t event_msg)
{
- int i;
- void *p = address;
+ nwam_error_t err;
- if ((mask & addrid) == 0)
- return (NULL);
+ if (shutting_down && event_msg->nwe_type != NWAM_EVENT_TYPE_SHUTDOWN) {
+ nlog(LOG_DEBUG, "nwamd_event_send: tossing event as nwamd "
+ "is shutting down");
+ return;
+ }
+
+ err = nwam_event_send(event_msg);
- for (i = 1; i < addrid; i <<= 1) {
- if (i & mask)
- nextaddr(&p);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_event_send: nwam_event_send: %s",
+ nwam_strerror(err));
}
- return (p);
}
-boolean_t
-start_event_collection(void)
+/*
+ * Run state machine for object. Method is run if
+ * - event method is non-null
+ * - event method is valid for current object state (determined by
+ * ORing the current state against the set of valid states for the method).
+ *
+ * If these criteria are met, the method is run.
+ */
+static void
+nwamd_event_run_method(nwamd_event_t event)
{
- int err;
+ nwamd_event_method_t *event_methods;
+ int i;
- /*
- * if these are ever created/destroyed repetitively then we will
- * have to change this.
- */
+ event_methods = nwamd_object_event_methods(event->event_object_type);
- if (err = pthread_create(&routing, NULL, routing_events, NULL)) {
- syslog(LOG_ERR, "pthread_create routing: %s", strerror(err));
- exit(EXIT_FAILURE);
- } else {
- dprintf("routing thread: %d", routing);
+ /* If we're shutting down, only fini events are accepted for objects */
+ if (shutting_down && event->event_type != NWAM_EVENT_TYPE_OBJECT_FINI) {
+ nlog(LOG_DEBUG, "nwamd_event_run_method: tossing non-fini "
+ "event %s for object %s",
+ nwamd_event_name(event->event_type), event->event_object);
+ return;
}
- if (wlan_scan_interval != 0) {
- err = pthread_create(&scan, NULL, periodic_wireless_scan, NULL);
- if (err != 0) {
- syslog(LOG_ERR, "pthread_create wireless scan: %s",
- strerror(err));
- exit(EXIT_FAILURE);
- } else {
- dprintf("wireless scan thread: %d", scan);
+ for (i = 0;
+ event_methods[i].event_type != NWAM_EVENT_TYPE_NOOP;
+ i++) {
+ if (event_methods[i].event_type ==
+ event->event_type &&
+ event_methods[i].event_method != NULL) {
+ nlog(LOG_DEBUG,
+ "(%p) %s: running method for event %s",
+ (void *)event, event->event_object,
+ nwamd_event_name(event->event_type));
+ /* run method */
+ event_methods[i].event_method(event);
+ return;
}
+ }
+ nlog(LOG_DEBUG, "(%p) %s: no matching method for event %d (%s)",
+ (void *)event, event->event_object, event->event_type,
+ nwamd_event_name(event->event_type));
+}
+
+/*
+ * Called when we are checking to see what should be activated. First activate
+ * all of the manual NCUs. Then see if we can find a valid priority group.
+ * If we can, activate it. Otherwise try all the priority groups starting
+ * with the lowest one that makes sense.
+ */
+static void
+nwamd_activate_ncus(void) {
+ int64_t prio = INVALID_PRIORITY_GROUP;
+ boolean_t selected;
+
+ nwamd_ncp_activate_manual_ncus();
+ selected = nwamd_ncp_check_priority_group(&prio);
+ if (selected) {
+ /*
+ * Activate chosen priority group and stop anything going on in
+ * lesser priority groups.
+ */
+ nwamd_ncp_activate_priority_group(prio);
+ nwamd_ncp_deactivate_priority_group_all(prio + 1);
} else {
- dprintf("periodic wireless scan disabled");
+ /*
+ * Nothing unique could be started so try them all. Once one
+ * of them gets into a reasonable state then we will prune
+ * everything below it (see first part of this conditional).
+ */
+ int64_t oldprio = INVALID_PRIORITY_GROUP;
+ while (nwamd_ncp_find_next_priority_group(++oldprio, &prio)) {
+ nwamd_ncp_activate_priority_group(prio);
+ oldprio = prio;
+ }
}
+}
+
+/*
+ * Event handler thread
+ *
+ * The complexity in this code comes about from wanting to delay the decision
+ * making process until after bursts of events. Keep roughly polling (waiting
+ * for .1s) until we see the queue quiet event and then block.
+ */
+void
+nwamd_event_handler(void)
+{
+ boolean_t got_shutdown_event = B_FALSE;
+ boolean_t check_conditions = B_FALSE;
+ boolean_t ncu_check = B_FALSE;
+ int queue_quiet_time = 0;
+ nwamd_event_t event;
/*
- * This function registers a callback which will get a dedicated thread
- * for handling of hotplug sysevents when they occur.
+ * Dequeue events and process them. In most cases, events have
+ * an assocated object type, and we use this to retrieve
+ * the function that will process the event.
*/
- hotplug_events_register();
+ while (!got_shutdown_event) {
+ event = nwamd_event_dequeue(queue_quiet_time);
+ /* keep pulling events as long as they are close together */
+ queue_quiet_time = SEC_TO_NSEC(1)/10;
+
+ /*
+ * This is an event with no associated object.
+ */
+ if (event->event_object[0] == '\0') {
+ switch (event->event_type) {
+ case NWAM_EVENT_TYPE_NOOP:
+ case NWAM_EVENT_TYPE_INIT:
+ /*
+ * The only action for an INIT event
+ * is to relay it to event listeners,
+ * which is done below.
+ */
+ break;
+ case NWAM_EVENT_TYPE_PRIORITY_GROUP:
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ current_ncu_priority_group =
+ event->event_msg->nwe_data.
+ nwe_priority_group_info.nwe_priority;
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ break;
+ case NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS:
+ if (!shutting_down) {
+ nwamd_set_timed_check_all_conditions();
+ check_conditions = B_TRUE;
+ }
+ break;
+ case NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS:
+ if (!shutting_down)
+ check_conditions = B_TRUE;
+ break;
+ case NWAM_EVENT_TYPE_NCU_CHECK:
+ if (!shutting_down)
+ ncu_check = B_TRUE;
+ break;
+ case NWAM_EVENT_TYPE_UPGRADE:
+ if (!shutting_down) {
+ /*
+ * Upgrade events have no associated
+ * object.
+ */
+ nwamd_event_run_method(event);
+ }
+ break;
+ case NWAM_EVENT_TYPE_SHUTDOWN:
+ got_shutdown_event = B_TRUE;
+ break;
- dprintf("initial interface scan");
- walk_interface(start_if_info_collect, "check");
+ /*
+ * We want to delay processing of condition and ncu
+ * checking until after short bursts of events. So we
+ * keep track of times we've scheduled checking and
+ * wait for the queue to quiesce.
+ */
+ case NWAM_EVENT_TYPE_QUEUE_QUIET:
+ queue_quiet_time = 0; /* now we can block */
+ if (!shutting_down && check_conditions) {
+ nwamd_check_all_conditions();
+ check_conditions = B_FALSE;
+ }
+
+ if (!shutting_down && ncu_check) {
+ nwamd_activate_ncus();
+ ncu_check = B_FALSE;
+ }
+ break;
- return (B_TRUE);
+ default:
+ nlog(LOG_ERR,
+ "event %d (%s)had no object associated "
+ "with it", event->event_type,
+ nwamd_event_name(event->event_type));
+ break;
+ }
+ } else {
+ /*
+ * Event has an associated object - run event method
+ * for that object type (if any).
+ */
+ nwamd_event_run_method(event);
+ }
+ /*
+ * Send associated message to listeners if event type is
+ * externally visible.
+ */
+ if (event->event_send)
+ nwamd_event_send(event->event_msg);
+
+ nwamd_event_fini(event);
+ }
+ /* If we get here, we got a shutdown event. */
+ nwamd_event_queue_fini();
+ nwamd_object_lists_fini();
}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/events.h b/usr/src/cmd/cmd-inet/lib/nwamd/events.h
new file mode 100644
index 0000000000..485cae55c5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/events.h
@@ -0,0 +1,120 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _EVENTS_H
+#define _EVENTS_H
+
+#include <door.h>
+#include <libsysevent.h>
+#include <libuutil.h>
+#include <pthread.h>
+#include <ucontext.h>
+
+#include <libnwam.h>
+#include <libnwam_priv.h>
+
+struct nwamd_object;
+typedef struct nwamd_object *nwamd_object_t;
+
+#include "ncp.h"
+
+/* Define internal-to-nwamd events here */
+#define NWAM_EVENT_TYPE_OBJECT_INIT NWAM_EVENT_MAX + 1
+#define NWAM_EVENT_TYPE_OBJECT_FINI NWAM_EVENT_MAX + 2
+#define NWAM_EVENT_TYPE_TIMED_CHECK_CONDITIONS NWAM_EVENT_MAX + 3
+#define NWAM_EVENT_TYPE_TRIGGERED_CHECK_CONDITIONS NWAM_EVENT_MAX + 4
+#define NWAM_EVENT_TYPE_NCU_CHECK NWAM_EVENT_MAX + 5
+#define NWAM_EVENT_TYPE_TIMER NWAM_EVENT_MAX + 6
+#define NWAM_EVENT_TYPE_UPGRADE NWAM_EVENT_MAX + 7
+#define NWAM_EVENT_TYPE_PERIODIC_SCAN NWAM_EVENT_MAX + 8
+#define NWAM_EVENT_TYPE_QUEUE_QUIET NWAM_EVENT_MAX + 9
+
+#define NEXT_FEW_SECONDS 5
+
+/*
+ * Forward definition.
+ */
+/*
+ * Wrapper structure for libnwam event (nwam_events_msg_t), containing
+ * event id (used to uniquely identify events on the event queue),
+ * associated object (if any), and uu_list_node.
+ */
+typedef struct nwamd_event {
+ int32_t event_type;
+ uint64_t event_id;
+ struct timespec event_time;
+ char event_object[NWAM_MAX_NAME_LEN];
+ nwam_object_type_t event_object_type;
+ uu_list_node_t event_node;
+ boolean_t event_send;
+ nwam_event_t event_msg;
+} *nwamd_event_t;
+
+typedef struct nwamd_event_method {
+ int32_t event_type;
+ void (*event_method)(nwamd_event_t);
+} nwamd_event_method_t;
+
+extern sysevent_handle_t *shp;
+
+/* Event generator init/fini code */
+extern void nwamd_routing_events_init(void);
+extern void nwamd_routing_events_fini(void);
+extern void nwamd_sysevent_events_init(void);
+extern void nwamd_sysevent_events_fini(void);
+
+/* Event init/enqueueing */
+extern void nwamd_event_queue_init(void);
+extern void nwamd_event_queue_fini(void);
+extern void nwamd_event_sources_init(void);
+extern void nwamd_event_sources_fini(void);
+extern nwamd_event_t nwamd_event_init(int32_t, nwam_object_type_t, size_t,
+ const char *);
+extern void nwamd_event_do_not_send(nwamd_event_t);
+extern nwamd_event_t nwamd_event_init_object_action(nwam_object_type_t,
+ const char *, const char *, nwam_action_t);
+extern nwamd_event_t nwamd_event_init_object_state(nwam_object_type_t,
+ const char *, nwam_state_t, nwam_aux_state_t);
+extern nwamd_event_t nwamd_event_init_priority_group_change(int64_t);
+extern nwamd_event_t nwamd_event_init_link_action(const char *, nwam_action_t);
+extern nwamd_event_t nwamd_event_init_link_state(const char *, boolean_t);
+extern nwamd_event_t nwamd_event_init_if_state(const char *, uint32_t,
+ uint32_t, uint32_t, struct sockaddr *);
+extern nwamd_event_t nwamd_event_init_wlan(const char *, int32_t, boolean_t,
+ nwam_wlan_t *, uint_t);
+extern nwamd_event_t nwamd_event_init_ncu_check(void);
+extern nwamd_event_t nwamd_event_init_init(void);
+extern nwamd_event_t nwamd_event_init_shutdown(void);
+extern void nwamd_event_enqueue(nwamd_event_t);
+extern void nwamd_event_enqueue_timed(nwamd_event_t, int);
+extern void nwamd_event_enqueue_expired_events(void);
+extern boolean_t nwamd_event_enqueued(int32_t, nwam_object_type_t,
+ const char *);
+extern void nwamd_event_send(nwam_event_t);
+extern void nwamd_event_fini(nwamd_event_t);
+extern void nwamd_event_handler(void);
+
+#endif /* _EVENTS_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/functions.h b/usr/src/cmd/cmd-inet/lib/nwamd/functions.h
deleted file mode 100644
index 3b9fcbcfa7..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/functions.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _FUNCTIONS_H
-#define _FUNCTIONS_H
-
-/* 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 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 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 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 return_vals_t bringupinterface(const char *, const char *, const char *,
- boolean_t);
-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 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 initialize_wireless(void);
-extern void terminate_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 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 wireless_verify(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 *,
- 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 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 *);
-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 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 void lookup_zonename(char *, size_t);
-
-#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
deleted file mode 100644
index e8c27373bf..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/interface.c
+++ /dev/null
@@ -1,1407 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * 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 a set of functions
- * to read property values stored in the SMF repository. Finally, it
- * contains the functions required for the "gather info" threads.
- *
- * The daemon maintains a list of structures that represent each IPv4
- * interface found on the system (after doing 'ifconfig -a plumb').
- * This list represents the objects manipulated by the daemon; while
- * the list of llp_t structures represents the configuration details
- * requested by the user (either the automatic defaults or entries in
- * /etc/nwam/llp). IPv6 interfaces are not tracked in the interfaces
- * list; rather, when the decision is made to make an interface active,
- * IPv6 is brought up in addition to IPv4 (assuming the LLP configuration
- * includes IPv6; this is the default for automatic configuration).
- *
- * When an interface is taken down, we unplumb the IPv6 link-local interface
- * completely, so that dhcpagent and in.ndpd will remove any addresses they've
- * added. Events are watched on the IPv4 interface alone, which is always
- * present for this version of NWAM.
- *
- * Interfaces are brought up and torn down by a sequence of ifconfig
- * commands (currently posix_spawn'd() by nwamd; the longer-term direction
- * here is to use libinetcfg).
- *
- * Upper Layer Profile management is controlled by user-provided scripts,
- * which should be created in /etc/nwam/ulp. One script,
- * /etc/nwam/ulp/check-conditions, checks the current network setup and
- * returns the name of the ULP which should be active under the current
- * conditions. A ULP is specified by two scripts, found in
- * /etc/nwam/ulp/<ulp name>: bringup and teardown. All scripts are
- * optional; if they do not exist or are not executable, nwamd will
- * simply move on.
- *
- * When an interface has been successfully brought up (signalled by the
- * assignment of an IP address to the interface), the daemon will first
- * teardown the existing ULP (if there is one) by running the teardown
- * script for that ULP. It will then run the check-conditions script;
- * if the name of a ULP is returned, it runs the bringup script for that
- * ULP.
- *
- * A "gather info" thread is initiated for an interface when it becomes
- * available. For a wired interface, "available" means the IFF_RUNNING
- * flag is set; wireless interfaces are considered to always be available,
- * so a wireless interface's gather info thread will run once, when it is
- * found at startup. This thread will do a scan on a wireless interface,
- * and initiate DHCP on a wired interface. It will then generate an event
- * for the state machine that indicates the availability of a new interface.
- *
- * 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>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <libscf.h>
-#include <sys/types.h>
-#include <arpa/inet.h>
-#include <inetcfg.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <sys/sysmacros.h>
-#include <sys/wait.h>
-#include <libdllink.h>
-#include <zone.h>
-
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
-
-static pthread_mutex_t ifs_lock = PTHREAD_MUTEX_INITIALIZER;
-
-static struct interface *ifs_head;
-static struct interface *ifs_wired, *ifs_wired_last;
-static struct interface *ifs_wireless, *ifs_wireless_last;
-
-static char upper_layer_profile[MAXHOSTNAMELEN];
-
-#define LOOPBACK_IF "lo0"
-
-void
-show_if_status(const char *ifname)
-{
- icfg_if_t intf;
- icfg_handle_t h;
- struct sockaddr_in sin;
- socklen_t addrlen = sizeof (struct sockaddr_in);
- int prefixlen = 0;
-
- (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name));
- /* We only display new addr info for v4 interfaces */
- intf.if_protocol = AF_INET;
- if (icfg_open(&h, &intf) != ICFG_SUCCESS) {
- syslog(LOG_ERR, "icfg_open failed on interface %s", ifname);
- return;
- }
- if (icfg_get_addr(h, (struct sockaddr *)&sin, &addrlen, &prefixlen,
- B_TRUE) != ICFG_SUCCESS) {
- syslog(LOG_ERR, "icfg_get_addr failed on interface %s", ifname);
- icfg_close(h);
- return;
- }
- icfg_close(h);
- 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)
-{
- if (link_layer_profile == NULL || ifp == NULL)
- return (B_FALSE);
-
- return (strcmp(ifp->if_name, link_layer_profile->llp_lname) == 0);
-}
-
-/*
- * Execute 'ifconfig ifname dhcp wait 0'.
- */
-static void
-start_dhcp(struct interface *ifp)
-{
- int res;
- uint32_t now_s;
- uint64_t timer_s;
-
- if (ifp->if_lflags & IF_DHCPSTARTED) {
- dprintf("start_dhcp: already started; returning");
- return;
- }
- ifp->if_lflags |= IF_DHCPSTARTED;
-
- /*
- * 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);
- if (res == -1)
- timer_s = NWAM_DEFAULT_DHCP_WAIT_TIME;
-
- now_s = NSEC_TO_SEC(gethrtime());
- ifp->if_timer_expire = now_s + timer_s;
-
- start_timer(now_s, timer_s);
-}
-
-static boolean_t
-check_svc_up(const char *fmri, int wait_time)
-{
- int i;
- char *state;
-
- for (i = 1; i <= wait_time; i++) {
- state = smf_get_state(fmri);
- if (state == NULL) {
- syslog(LOG_ERR, "smf_get_state(%s) returned \"%s\"",
- fmri, scf_strerror(scf_error()));
- } else {
- if (strcmp(SCF_STATE_STRING_ONLINE, state) == 0) {
- free(state);
- return (B_TRUE);
- }
- free(state);
- }
- (void) sleep(1);
- }
- return (B_FALSE);
-}
-
-boolean_t
-ulp_is_active(void)
-{
- return (upper_layer_profile[0] != '\0');
-}
-
-/*
- * Inputs:
- * res is a pointer to the scf_resources_t to be released.
- */
-static void
-release_scf_resources(scf_resources_t *res)
-{
- scf_value_destroy(res->sr_val);
- scf_property_destroy(res->sr_prop);
- scf_pg_destroy(res->sr_pg);
- scf_snapshot_destroy(res->sr_snap);
- scf_instance_destroy(res->sr_inst);
- (void) scf_handle_unbind(res->sr_handle);
- scf_handle_destroy(res->sr_handle);
-}
-
-/*
- * Inputs:
- * lpg is the property group to look up
- * lprop is the property within that group to look up
- * Outputs:
- * res is a pointer to an scf_resources_t. This is an internal
- * structure that holds all the handles needed to get a specific
- * property from the running snapshot; on a successful return it
- * contains the scf_value_t that should be passed to the desired
- * scf_value_get_foo() function, and must be freed after use by
- * calling release_scf_resources(). On a failure return, any
- * resources that may have been assigned to res are released, so
- * the caller does not need to do any cleanup in the failure case.
- * Returns:
- * 0 on success
- * -1 on failure
- */
-static int
-get_property_value(const char *lpg, const char *lprop, scf_resources_t *res)
-{
- res->sr_inst = NULL;
- res->sr_snap = NULL;
- res->sr_pg = NULL;
- res->sr_prop = NULL;
- res->sr_val = NULL;
-
- if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
- syslog(LOG_ERR, "scf_handle_create() failed: %s",
- scf_strerror(scf_error()));
- return (-1);
- }
-
- if (scf_handle_bind(res->sr_handle) != 0) {
- scf_handle_destroy(res->sr_handle);
- syslog(LOG_ERR, "scf_handle_destroy() failed: %s",
- scf_strerror(scf_error()));
- return (-1);
- }
- if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
- syslog(LOG_ERR, "scf_instance_create() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if (scf_handle_decode_fmri(res->sr_handle, OUR_FMRI, NULL, NULL,
- res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
- syslog(LOG_ERR, "scf_handle_decode_fmri() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
- syslog(LOG_ERR, "scf_snapshot_create() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if (scf_instance_get_snapshot(res->sr_inst, "running",
- res->sr_snap) != 0) {
- syslog(LOG_ERR, "scf_instance_get_snapshot() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
- syslog(LOG_ERR, "scf_pg_create() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if (scf_instance_get_pg_composed(res->sr_inst, res->sr_snap, lpg,
- res->sr_pg) != 0) {
- syslog(LOG_ERR, "scf_instance_get_pg_composed(%s) failed: %s",
- lpg, scf_strerror(scf_error()));
- goto failure;
- }
- if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
- syslog(LOG_ERR, "scf_property_create() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if (scf_pg_get_property(res->sr_pg, lprop, res->sr_prop) != 0) {
- syslog(LOG_ERR, "scf_pg_get_property(%s) failed: %s",
- lprop, scf_strerror(scf_error()));
- goto failure;
- }
- if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
- syslog(LOG_ERR, "scf_value_create() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
- syslog(LOG_ERR, "scf_property_get_value() failed: %s",
- scf_strerror(scf_error()));
- goto failure;
- }
- return (0);
-
-failure:
- release_scf_resources(res);
- return (-1);
-}
-
-/*
- * Inputs:
- * lpg is the property group to look up
- * lprop is the property within that group to look up
- * Outputs:
- * answer is a pointer to the property value
- * Returns:
- * 0 on success
- * -1 on failure
- * If successful, the property value is retured in *answer.
- * Otherwise, *answer is undefined, and it is up to the caller to decide
- * how to handle that case.
- */
-int
-lookup_boolean_property(const char *lpg, const char *lprop, boolean_t *answer)
-{
- int result = -1;
- scf_resources_t res;
- uint8_t prop_val;
-
- if (get_property_value(lpg, lprop, &res) != 0) {
- /*
- * an error was already logged by get_property_value,
- * and it released any resources assigned to res before
- * returning.
- */
- return (result);
- }
- if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
- syslog(LOG_ERR, "scf_value_get_boolean() failed: %s",
- scf_strerror(scf_error()));
- goto cleanup;
- }
- *answer = (boolean_t)prop_val;
- dprintf("lookup_boolean_property(%s, %s) returns %s", lpg, lprop,
- *answer ? "TRUE" : "FALSE");
- result = 0;
-cleanup:
- release_scf_resources(&res);
- return (result);
-}
-
-/*
- * Inputs:
- * lpg is the property group to look up
- * lprop is the property within that group to look up
- * Outputs:
- * answer is a pointer to the property value
- * Returns:
- * 0 on success
- * -1 on failure
- * If successful, the property value is retured in *answer.
- * Otherwise, *answer is undefined, and it is up to the caller to decide
- * how to handle that case.
- */
-int
-lookup_count_property(const char *lpg, const char *lprop, uint64_t *answer)
-{
- int result = -1;
- scf_resources_t res;
-
- if (get_property_value(lpg, lprop, &res) != 0) {
- /*
- * an error was already logged by get_property_value,
- * and it released any resources assigned to res before
- * returning.
- */
- return (result);
- }
- if (scf_value_get_count(res.sr_val, answer) != 0) {
- syslog(LOG_ERR, "scf_value_get_count() failed: %s",
- scf_strerror(scf_error()));
- goto cleanup;
- }
- dprintf("lookup_count_property(%s, %s) returns %lld", lpg, lprop,
- *answer);
- result = 0;
-cleanup:
- release_scf_resources(&res);
- return (result);
-}
-
-void
-activate_upper_layer_profile(boolean_t do_dhcp, const char *ifname)
-{
- FILE *f;
- char buffer[1024], *cp;
- size_t buflen;
- size_t offset;
- const char bringup[] = "/bringup";
- boolean_t should;
- int res;
-
- /*
- * exec the net-svc script to update local config with
- * any DNS information learned from the DHCP server.
- */
- if (do_dhcp) {
- res = lookup_boolean_property(OUR_PG, "use_net_svc", &should);
- /*
- * If the look-up failed, try anyway: only avoid this if we
- * know for sure not to.
- */
- if ((res == 0 && should) || (res == -1)) {
- (void) start_child(NET_SVC_METHOD, "start", ifname,
- NULL);
- }
- }
- f = popen(ULP_DIR "/check-conditions", "r");
- 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 = strlcpy(buffer, ULP_DIR "/", sizeof (buffer));
- cp = fgets(buffer + offset,
- MIN(sizeof (upper_layer_profile), sizeof (buffer) - offset),
- f);
- buflen = strlen(buffer);
- if (buffer[buflen - 1] == '\n')
- buffer[--buflen] = '\0';
-
- /* 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
-deactivate_upper_layer_profile(void)
-{
- char buffer[1024];
-
- /*
- * If ULP wasn't defined...
- */
- if (!ulp_is_active())
- return;
-
- (void) snprintf(buffer, sizeof (buffer), ULP_DIR "/%s/teardown",
- upper_layer_profile);
- (void) start_child(PFEXEC, "-P", "basic", buffer, NULL);
-
- syslog(LOG_NOTICE, "upper layer profile %s deactivated",
- upper_layer_profile);
-
- report_ulp_deactivated(upper_layer_profile);
-
- upper_layer_profile[0] = '\0';
-}
-
-/*
- * 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.
- */
-return_vals_t
-bringupinterface(const char *ifname, const char *host, const char *ipv6addr,
- boolean_t ipv6onlink)
-{
- struct interface *intf;
-
- intf = get_interface(ifname);
- if (intf == NULL) {
- syslog(LOG_ERR, "could not bring up interface %s: not in list",
- ifname);
- return (FAILURE);
- }
-
- /* check current state; no point going on if flags are 0 */
- if ((intf->if_flags = get_ifflags(ifname, intf->if_family)) == 0) {
- dprintf("bringupinterface(%s): get_ifflags() returned 0",
- ifname);
- 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);
- }
-
- /*
- * If the link layer profile says that we want v6 then plumb it and
- * bring it up; if there's a static address, configure it as well.
- */
- if (ipv6onlink) {
- dprintf("bringupinterface: configuring ipv6");
- (void) start_child(IFCONFIG, ifname, "inet6", "plumb", "up",
- NULL);
- if (ipv6addr) {
- (void) start_child(IFCONFIG, ifname, "inet6", "addif",
- ipv6addr, "up", NULL);
- }
- }
- intf->if_v6onlink = ipv6onlink;
-
- if (strcmp(host, "dhcp") == 0) {
- start_dhcp(intf);
- } else {
- (void) start_child(IFCONFIG, ifname, host, NULL);
- (void) start_child(IFCONFIG, ifname, "up", NULL);
- }
-
- syslog(LOG_DEBUG, "brought up %s", ifname);
-
- return (SUCCESS);
-}
-
-/* Called only in the main thread */
-void
-takedowninterface(const char *ifname, libnwam_diag_cause_t cause)
-{
- uint64_t flags;
- struct interface *ifp;
-
- dprintf("takedowninterface(%s, %d)", ifname, (int)cause);
-
- if ((ifp = get_interface(ifname)) == NULL) {
- dprintf("takedowninterface: can't find interface struct for %s",
- ifname);
- }
-
- flags = get_ifflags(ifname, AF_INET);
- if (flags & IFF_DHCPRUNNING) {
- /*
- * We generally prefer doing a release, as that tells the
- * server that it can relinquish the lease, whereas drop is
- * just a client-side operation. But if we never came up,
- * release will fail, because dhcpagent does not allow an
- * interface without a lease to release, so we have to drop in
- * that case. So try release first, then fall back to drop.
- */
- if (start_child(IFCONFIG, ifname, "dhcp", "wait", "2",
- "release", NULL) != 0) {
- (void) start_child(IFCONFIG, ifname, "dhcp", "wait",
- "2", "drop", NULL);
- }
- } else {
- 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 (ifp == NULL || ifp->if_v6onlink) {
- /*
- * Unplumbing the link local interface causes dhcp and ndpd to
- * remove other addresses they have added.
- */
- (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL);
- }
-
- if (ifp == NULL || ifp->if_up_attempted)
- report_interface_down(ifname, cause);
-
- if (ifp != NULL) {
- /* We're no longer expecting the interface to be up */
- ifp->if_flags = flags & ~IFF_UP;
- if (ifp->if_type == IF_WIRELESS) {
- /* and if it's wireless, it's not running, either */
- ifp->if_flags &= ~IFF_RUNNING;
- disconnect_wlan(ifp->if_name);
- }
- dprintf("takedown interface, zero cached ip address");
- ifp->if_lflags &= ~IF_DHCPSTARTED & ~IF_DHCPACQUIRED;
- ifp->if_ipv4addr = INADDR_ANY;
- ifp->if_up_attempted = B_FALSE;
- }
-}
-
-/*
- * Called only in the main thread
- *
- * For IPv6, unplumbing the link local interface causes dhcp and ndpd to remove
- * other addresses they have added. We watch for routing socket events on the
- * IPv4 interface, which is always enabled, so no need to keep IPv6 around on a
- * switch.
- */
-void
-clear_cached_address(const char *ifname)
-{
- struct interface *ifp;
- uint64_t ifflags;
-
- if ((ifp = get_interface(ifname)) == NULL) {
- dprintf("clear_cached_address: can't find interface struct "
- "for %s", ifname);
- (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL);
- return;
- }
- if (ifp->if_v6onlink)
- (void) start_child(IFCONFIG, ifname, "inet6", "unplumb", NULL);
- 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;
-}
-
-/*
- * Add an interface struct to the interface list. The list is
- * partially ordered; all the wired interfaces appear first,
- * followed by all the wireless interfaces. New interfaces are
- * added at the end of the appropriate list section.
- */
-static void
-interface_list_insert(struct interface *ifp)
-{
- struct interface **headpp, **lastpp;
- struct interface *pchain, *nextp;
-
- if (pthread_mutex_lock(&ifs_lock) != 0)
- return;
-
- switch (ifp->if_type) {
- case IF_WIRELESS:
- /*
- * 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:
- /*
- * 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;
- }
-
- /* 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;
-
- 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.
- */
-struct interface *
-add_interface(sa_family_t family, const char *name, uint64_t flags)
-{
- struct interface *i;
- libnwam_interface_type_t iftype;
-
- if (name == NULL)
- return (NULL);
-
- dprintf("add_interface: found interface %s", name);
- if (family == AF_INET6) {
- /*
- * we don't track IPv6 interfaces separately from their
- * v4 counterparts; a link either has v4 only, or both
- * v4 and v6, so we only maintain a v4 interface struct.
- */
- dprintf("not adding v6 interface for %s", name);
- return (NULL);
- } else if (family != AF_INET) {
- /*
- * the classic "shouldn't happen"...
- */
- dprintf("not adding af %d interface for %s", family, name);
- return (NULL);
- }
-
- if ((iftype = find_if_type(name)) == IF_TUN) {
- /*
- * for now, we're ignoring tunnel interfaces (we expect
- * them to be entirely manipulated by higher layer profile
- * activation/deactivation scripts)
- */
- dprintf("%s is a tunnel interface; ignoring", name);
- return (NULL);
- }
-
- if ((i = calloc(1, sizeof (*i))) == NULL) {
- dprintf("add_interface: malloc failed");
- return (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;
-
- 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_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. The caller must either be holding ifs_lock, or be
- * in the main thread.
- */
-struct interface *
-get_interface(const char *name)
-{
- struct interface *ifp;
-
- if (name == NULL)
- return (NULL);
-
- for (ifp = ifs_head; ifp != NULL; ifp = ifp->if_next) {
- if (strcmp(name, ifp->if_name) == 0)
- break;
- }
- return (ifp);
-}
-
-/*
- * 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.
- */
-static boolean_t
-is_startable(struct interface *ifp)
-{
- 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);
-}
-
-/*
- * For wireless interface, we will try to find out available wireless
- * network; for wired, if dhcp should be used, start it now to try to
- * avoid delays there.
- *
- * For the real code, we should pass back the network information
- * gathered. Note that the state engine will then use the llp to
- * determine which interface should be set up.
- *
- * 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;
- int retv;
-
- dprintf("Start gathering info for %s", i->if_name);
-
- switch (i->if_type) {
- case IF_WIRELESS:
- /* 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:
- 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;
- }
-
- dprintf("Done gathering info for %s", i->if_name);
- i->if_thr = 0;
- return (NULL);
-}
-
-/*
- * 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, and with the ifs_lock held.
- */
-void
-walk_interface(void (*walker)(struct interface *, void *), void *arg)
-{
- struct interface *ifp;
-
- 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
-print_interface_list(void)
-{
- struct interface *wp;
-
- dprintf("Walking interface list; starting with wired interfaces");
- for (wp = ifs_head; wp != NULL; wp = wp->if_next) {
- if (wp == ifs_wireless)
- dprintf("Now wireless interfaces");
- dprintf("==> %s", wp->if_name);
- }
-}
-
-static boolean_t
-link_belongs_to_this_zone(const char *linkname)
-{
- zoneid_t zoneid;
- char zonename[ZONENAME_MAX];
- int ret;
-
- zoneid = getzoneid();
- if (zoneid == GLOBAL_ZONEID) {
- datalink_id_t linkid;
- dladm_status_t status;
- char errstr[DLADM_STRSIZE];
-
- if ((status = dladm_name2info(dld_handle, linkname, &linkid,
- NULL, NULL, NULL)) != DLADM_STATUS_OK) {
- dprintf("link_belongs_to_this_zone: could not get "
- "linkid for %s: %s", linkname,
- dladm_status2str(status, errstr));
- return (B_FALSE);
- }
- zoneid = ALL_ZONES;
- ret = zone_check_datalink(&zoneid, linkid);
- if (ret == 0) {
- (void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX);
- dprintf("link_belongs_to_this_zone: %s is used by "
- "non-global zone: %s", linkname, zonename);
- return (B_FALSE);
- }
- }
- return (B_TRUE);
-}
-
-/*
- * Walker function passed to dladm_walk() below - the linkname is
- * passed as the first argument, and is guaranteed to be non-NULL.
- */
-/* ARGSUSED */
-static int
-do_add_interface(const char *name, void *arg)
-{
- uint64_t flags;
-
- /* Do not add links belonging to other zones */
- if (!link_belongs_to_this_zone(name))
- return (DLADM_WALK_CONTINUE);
-
- (void) start_child(IFCONFIG, name, "plumb", NULL);
- flags = get_ifflags(name, AF_INET);
-
- /* If adding fails, just ignore that interface... */
- (void) add_interface(AF_INET, name, flags);
-
- return (DLADM_WALK_CONTINUE);
-}
-
-/*
- * Walker function passed to icfg_iterate_if() below - the icfg_if_it *
- * argument is guaranteed to be non-NULL by icfg_iterate_if(),
- * since the function it uses to generate the list - icfg_get_if_list()) -
- * guarantees this.
- */
-/* ARGSUSED */
-static int
-do_unplumb_if(icfg_if_t *intf, void *arg)
-{
- uint64_t flags = get_ifflags(intf->if_name, intf->if_protocol);
-
- /* We don't touch loopback interface. */
- if (flags & IFF_LOOPBACK)
- return (ICFG_SUCCESS);
-
- (void) start_child(IFCONFIG, intf->if_name,
- intf->if_protocol == AF_INET6 ? "inet6" : "inet", "unplumb", NULL);
-
- return (ICFG_SUCCESS);
-}
-
-void
-initialize_interfaces(void)
-{
- /*
- * Bring down all interfaces bar lo0.
- */
- (void) icfg_iterate_if(AF_INET, ICFG_PLUMBED, NULL, do_unplumb_if);
- (void) icfg_iterate_if(AF_INET6, ICFG_PLUMBED, NULL, do_unplumb_if);
-
- /*
- * In case dhcpagent is running... If it is running, when
- * we do another DHCP command on the same interface later, it may
- * be confused. Just kill dhcpagent to simplify handling.
- */
- dprintf("killing dhcpagent");
- (void) start_child(PKILL, "-z", zonename, "dhcpagent", NULL);
-
- /*
- * Though our dependence on svc:/network/datalink-management
- * and use of dladm_walk() assures us of being able to find
- * all available links, we still need to be sure that
- * svc:/system/filesystem/root is up, as that ensures that
- * permissions are set up properly on the things we need to
- * open to plumb ip on links.
- */
- if (!check_svc_up(DEV_FS_ROOT_FMRI, 60))
- syslog(LOG_ERR, DEV_FS_ROOT_FMRI " never came up");
-
- /*
- * Since network/physical depends on network/datalink-management,
- * we know that service is up and can provide us with a complete
- * list of links by the time nwamd is started. Use dladm_walk()
- * to walk that complete list of links, filtering for just the
- * physical links (we're explicitly avoiding management of virtual
- * links such as vnics/vlans/aggrs at this point). We continue
- * to plumb each link (in do_add_interface() now) to preserve the
- * old 'ifconfig -a plumb' behavior.
- */
- (void) dladm_walk(do_add_interface, dld_handle, NULL,
- DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
-
- print_interface_list();
-
-}
-
-/*
- * Walker function used to start info gathering of each interface. Caller
- * holds ifs_lock.
- */
-void
-start_if_info_collect(struct interface *ifp, void *arg)
-{
- int retv;
- pthread_attr_t attr;
-
- /*
- * In certain cases we need to refresh the cached flags value as
- * it may be stale. Notably, we can miss a DL_NOTE_LINK_DOWN
- * event after we initialize interfaces before the routing thread
- * is launched.
- */
- 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_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.
- */
- ifp->if_lflags &= ~IF_DHCPFLAGS;
-
- (void) pthread_attr_init(&attr);
- (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- 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 for %s: %d", ifp->if_name,
- ifp->if_thr);
- }
-}
-
-/*
- * Walker function used to check timer for each interface.
- * If timer has expired, generate a timer event for the
- * interface.
- */
-static void
-iftimer(struct interface *ifp, void *arg)
-{
- uint32_t now = (uint32_t)(uintptr_t)arg;
-
- if (ifp->if_timer_expire == 0)
- return;
-
- if (ifp->if_timer_expire > now) {
- start_timer(now, ifp->if_timer_expire - now);
- return;
- }
-
- ifp->if_timer_expire = 0;
-
- (void) np_queue_add_event(EV_TIMER, ifp->if_name);
-}
-
-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;
- libnwam_interface_type_t type;
-
- if (name == NULL) {
- dprintf("find_if_type: no ifname; returning IF_UNKNOWN");
- return (IF_UNKNOWN);
- }
-
- type = IF_WIRED;
- if (dladm_name2info(dld_handle, name, NULL, NULL, NULL, &media) !=
- DLADM_STATUS_OK) {
- if (strncmp(name, "ip.tun", 6) == 0 ||
- strncmp(name, "ip6.tun", 7) == 0 ||
- strncmp(name, "ip.6to4tun", 10) == 0)
- /*
- * We'll need to update our tunnel detection once
- * the clearview/tun project is integrated; tunnel
- * names won't necessarily be ip.tunN.
- */
- type = IF_TUN;
- } else if (media == DL_WIFI) {
- type = IF_WIRELESS;
- }
-
- return (type);
-}
-
-const char *
-if_type_str(libnwam_interface_type_t type)
-{
- switch (type) {
- case IF_WIRED:
- return ("wired");
- case IF_WIRELESS:
- return ("wireless");
- case IF_TUN:
- return ("tunnel");
- case IF_UNKNOWN:
- default:
- return ("unknown type");
- }
-}
-
-/*
- * 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/pkgdefs/SUNWnwamintr/pkginfo.tmpl b/usr/src/cmd/cmd-inet/lib/nwamd/ipf.conf.dfl
index 8a69335727..4be5b45c14 100644
--- a/usr/src/pkgdefs/SUNWnwamintr/pkginfo.tmpl
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ipf.conf.dfl
@@ -18,26 +18,19 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+#
+# Copyright 2010 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"
+
+# Start by blocking everything.
+block in log all
+block out log all
+
+# Allow loopback traffic
+pass in quick on lo0
+pass out quick on lo0
+
+# Allow DHCP: in to client port, out to server port
+pass in quick proto udp from any to any port = 68
+pass out quick proto udp from any to any port = 67
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ipf6.conf.dfl b/usr/src/cmd/cmd-inet/lib/nwamd/ipf6.conf.dfl
new file mode 100644
index 0000000000..6cb5656731
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ipf6.conf.dfl
@@ -0,0 +1,40 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+# Start by blocking everything.
+block in log all
+block out log all
+
+# Allow loopback traffic
+pass in quick on lo0
+pass out quick on lo0
+
+# Allow DHCP: in to client port, out to server port
+pass in quick proto udp from any to any port = 546
+pass out quick proto udp from any to any port = 547
+
+# Allow ICMP for IPv6 for Neighbor advertisements
+pass in quick proto ipv6-icmp from any to any
+pass out quick proto ipv6-icmp from any to any
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.c b/usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.c
new file mode 100644
index 0000000000..249758e6e7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.c
@@ -0,0 +1,553 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlwlan.h>
+#include <libgen.h>
+#include <libnwam.h>
+
+#include "events.h"
+#include "known_wlans.h"
+#include "ncu.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * known_wlans.c - contains routines which handle the known WLAN abstraction.
+ */
+
+#define KNOWN_WIFI_NETS_FILE "/etc/nwam/known_wifi_nets"
+
+/* enum for parsing each line of /etc/nwam/known_wifi_nets */
+typedef enum {
+ ESSID = 0,
+ BSSID,
+ MAX_FIELDS
+} known_wifi_nets_fields_t;
+
+/* Structure for one BSSID */
+typedef struct bssid {
+ struct qelem bssid_links;
+ char *bssid;
+} bssid_t;
+
+/* Structure for an ESSID and its BSSIDs */
+typedef struct kw {
+ struct qelem kw_links;
+ char kw_essid[NWAM_MAX_NAME_LEN];
+ uint32_t kw_num_bssids;
+ struct qelem kw_bssids;
+} kw_t;
+
+/* Holds the linked-list of ESSIDs to make Known WLANs out of */
+static struct qelem kw_list;
+
+/* Used in walking secobjs looking for an ESSID prefix match. */
+struct nwamd_secobj_arg {
+ char nsa_essid_prefix[DLADM_WLAN_MAX_KEYNAME_LEN];
+ char nsa_keyname[DLADM_WLAN_MAX_KEYNAME_LEN];
+ dladm_wlan_key_t *nsa_key;
+ uint64_t nsa_secmode;
+};
+
+static void
+kw_list_init(void)
+{
+ kw_list.q_forw = kw_list.q_back = &kw_list;
+}
+
+static void
+kw_list_free(void)
+{
+ kw_t *kw;
+ bssid_t *b;
+
+ while (kw_list.q_forw != &kw_list) {
+ kw = (kw_t *)kw_list.q_forw;
+
+ /* free kw_bssids */
+ while (kw->kw_bssids.q_forw != &kw->kw_bssids) {
+ b = (bssid_t *)kw->kw_bssids.q_forw;
+ remque(&b->bssid_links);
+ free(b->bssid);
+ free(b);
+ }
+ remque(&kw->kw_links);
+ free(kw);
+ }
+}
+
+/* Returns the entry in kw_list for the given ESSID. NULL if non-existent */
+static kw_t *
+kw_lookup(const char *essid)
+{
+ kw_t *kw;
+
+ if (essid == NULL)
+ return (NULL);
+
+ for (kw = (kw_t *)kw_list.q_forw;
+ kw != (kw_t *)&kw_list;
+ kw = (kw_t *)kw->kw_links.q_forw) {
+ if (strcmp(essid, kw->kw_essid) == 0)
+ return (kw);
+ }
+ return (NULL);
+}
+
+/* Adds an ESSID/BSSID combination to kw_list. Returns B_TRUE on success. */
+static boolean_t
+kw_add(const char *essid, const char *bssid)
+{
+ kw_t *kw;
+ bssid_t *b;
+
+ if ((b = calloc(1, sizeof (bssid_t))) == NULL) {
+ nlog(LOG_ERR, "kw_add: cannot allocate for bssid_t: %m");
+ return (B_FALSE);
+ }
+ if ((kw = calloc(1, sizeof (kw_t))) == NULL) {
+ nlog(LOG_ERR, "kw_add: cannot allocate for kw_t: %m");
+ free(b);
+ return (B_FALSE);
+ }
+ kw->kw_bssids.q_forw = kw->kw_bssids.q_back = &kw->kw_bssids;
+
+ b->bssid = strdup(bssid);
+ (void) strlcpy(kw->kw_essid, essid, sizeof (kw->kw_essid));
+ kw->kw_num_bssids = 1;
+
+ insque(&b->bssid_links, kw->kw_bssids.q_back);
+ insque(&kw->kw_links, kw_list.q_back);
+
+ nlog(LOG_DEBUG, "kw_add: added Known WLAN %s, BSSID %s", essid, bssid);
+ return (B_TRUE);
+}
+
+/*
+ * Add the BSSID to the given kw. Since /etc/nwam/known_wifi_nets is
+ * populated such that the wifi networks visited later are towards the end
+ * of the file, remove the give kw from its current position and append it
+ * to the end of kw_list. This ensures that kw_list is in the reverse
+ * order of visited wifi networks. Returns B_TRUE on success.
+ */
+static boolean_t
+kw_update(kw_t *kw, const char *bssid)
+{
+ bssid_t *b;
+
+ if ((b = calloc(1, sizeof (bssid_t))) == NULL) {
+ nlog(LOG_ERR, "kw_update: cannot allocate for bssid_t: %m");
+ return (B_FALSE);
+ }
+
+ b->bssid = strdup(bssid);
+ insque(&b->bssid_links, kw->kw_bssids.q_back);
+ kw->kw_num_bssids++;
+
+ /* remove kw from current position */
+ remque(&kw->kw_links);
+ /* and insert at end */
+ insque(&kw->kw_links, kw_list.q_back);
+
+ nlog(LOG_DEBUG, "kw_update: appended BSSID %s to Known WLAN %s",
+ bssid, kw->kw_essid);
+ return (B_TRUE);
+}
+
+/*
+ * Parses /etc/nwam/known_wifi_nets and populates kw_list, with the oldest
+ * wifi networks first in the list. Returns the number of unique entries
+ * in kw_list (to use for priority values).
+ */
+static int
+parse_known_wifi_nets(void)
+{
+ FILE *fp;
+ char line[LINE_MAX];
+ char *cp, *tok[MAX_FIELDS];
+ int lnum, num_kw = 0;
+ kw_t *kw;
+
+ kw_list_init();
+
+ /*
+ * The file format is:
+ * essid\tbssid (essid followed by tab followed by bssid)
+ */
+ fp = fopen(KNOWN_WIFI_NETS_FILE, "r");
+ if (fp == NULL)
+ return (0);
+ for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
+
+ cp = line;
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '#' || *cp == '\0')
+ continue;
+
+ if (bufsplit(cp, MAX_FIELDS, tok) != MAX_FIELDS) {
+ syslog(LOG_ERR, "%s:%d: wrong number of tokens; "
+ "ignoring entry", KNOWN_WIFI_NETS_FILE, lnum);
+ continue;
+ }
+
+ if ((kw = kw_lookup(tok[ESSID])) == NULL) {
+ if (!kw_add(tok[ESSID], tok[BSSID])) {
+ nlog(LOG_ERR,
+ "%s:%d: cannot add entry (%s,%s) to list",
+ KNOWN_WIFI_NETS_FILE, lnum,
+ tok[ESSID], tok[BSSID]);
+ } else {
+ num_kw++;
+ }
+ } else {
+ if (!kw_update(kw, tok[BSSID])) {
+ nlog(LOG_ERR,
+ "%s:%d:cannot update entry (%s,%s) to list",
+ KNOWN_WIFI_NETS_FILE, lnum,
+ tok[ESSID], tok[BSSID]);
+ }
+ }
+ /* next line ... */
+ }
+
+ (void) fclose(fp);
+ return (num_kw);
+}
+
+/*
+ * Walk security objects looking for one that matches the essid prefix.
+ * Store the key and keyname if a match is found - we use the last match
+ * as the key for the known WLAN, since it is the most recently updated.
+ */
+/* ARGSUSED0 */
+static boolean_t
+find_secobj_matching_prefix(dladm_handle_t dh, void *arg,
+ const char *secobjname)
+{
+ struct nwamd_secobj_arg *nsa = arg;
+
+ if (strncmp(nsa->nsa_essid_prefix, secobjname,
+ strlen(nsa->nsa_essid_prefix)) == 0) {
+ nlog(LOG_DEBUG, "find_secobj_matching_prefix: "
+ "found secobj with prefix %s : %s\n",
+ nsa->nsa_essid_prefix, secobjname);
+ /* Free last key found (if any) */
+ if (nsa->nsa_key != NULL)
+ free(nsa->nsa_key);
+ /* Retrive key so we can get security mode */
+ nsa->nsa_key = nwamd_wlan_get_key_named(secobjname, 0);
+ (void) strlcpy(nsa->nsa_keyname, secobjname,
+ sizeof (nsa->nsa_keyname));
+ switch (nsa->nsa_key->wk_class) {
+ case DLADM_SECOBJ_CLASS_WEP:
+ nsa->nsa_secmode = DLADM_WLAN_SECMODE_WEP;
+ nlog(LOG_DEBUG, "find_secobj_matching_prefix: "
+ "got WEP key %s", nsa->nsa_keyname);
+ break;
+ case DLADM_SECOBJ_CLASS_WPA:
+ nsa->nsa_secmode = DLADM_WLAN_SECMODE_WPA;
+ nlog(LOG_DEBUG, "find_secobj_matching_prefix: "
+ "got WPA key %s", nsa->nsa_keyname);
+ break;
+ default:
+ /* shouldn't happen */
+ nsa->nsa_secmode = DLADM_WLAN_SECMODE_NONE;
+ nlog(LOG_ERR, "find_secobj_matching_prefix: "
+ "key class for key %s was invalid",
+ nsa->nsa_keyname);
+ break;
+ }
+ }
+ return (B_TRUE);
+}
+
+
+/* Upgrade /etc/nwam/known_wifi_nets file to new libnwam-based config model */
+void
+upgrade_known_wifi_nets_config(void)
+{
+ kw_t *kw;
+ bssid_t *b;
+ nwam_known_wlan_handle_t kwh;
+ char **bssids;
+ nwam_error_t err;
+ uint64_t priority;
+ int i, num_kw;
+ struct nwamd_secobj_arg nsa;
+
+ nlog(LOG_INFO, "Upgrading %s to Known WLANs", KNOWN_WIFI_NETS_FILE);
+
+ /* Parse /etc/nwam/known_wifi_nets */
+ num_kw = parse_known_wifi_nets();
+
+ /* Create Known WLANs for each unique ESSID */
+ for (kw = (kw_t *)kw_list.q_forw, priority = num_kw-1;
+ kw != (kw_t *)&kw_list;
+ kw = (kw_t *)kw->kw_links.q_forw, priority--) {
+ nwam_value_t priorityval = NULL;
+ nwam_value_t bssidsval = NULL;
+ nwam_value_t secmodeval = NULL;
+ nwam_value_t keynameval = NULL;
+
+ nlog(LOG_DEBUG, "Creating Known WLAN %s", kw->kw_essid);
+
+ if ((err = nwam_known_wlan_create(kw->kw_essid, &kwh))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not create known wlan: %s", kw->kw_essid,
+ nwam_strerror(err));
+ continue;
+ }
+
+ /* priority of this ESSID */
+ if ((err = nwam_value_create_uint64(priority, &priorityval))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not create priority value: %s", kw->kw_essid,
+ nwam_strerror(err));
+ nwam_known_wlan_free(kwh);
+ continue;
+ }
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
+ nwam_value_free(priorityval);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not set priority value: %s", kw->kw_essid,
+ nwam_strerror(err));
+ nwam_known_wlan_free(kwh);
+ continue;
+ }
+
+ /* loop through kw->kw_bssids and create an array of bssids */
+ bssids = calloc(kw->kw_num_bssids, sizeof (char *));
+ if (bssids == NULL) {
+ nwam_known_wlan_free(kwh);
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not calloc for bssids: %m", kw->kw_essid);
+ continue;
+ }
+ for (b = (bssid_t *)kw->kw_bssids.q_forw, i = 0;
+ b != (bssid_t *)&kw->kw_bssids;
+ b = (bssid_t *)b->bssid_links.q_forw, i++) {
+ bssids[i] = strdup(b->bssid);
+ }
+
+ /* BSSIDs for this ESSID */
+ if ((err = nwam_value_create_string_array(bssids,
+ kw->kw_num_bssids, &bssidsval)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not create bssids value: %s", kw->kw_essid,
+ nwam_strerror(err));
+ for (i = 0; i < kw->kw_num_bssids; i++)
+ free(bssids[i]);
+ free(bssids);
+ nwam_known_wlan_free(kwh);
+ continue;
+ }
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, bssidsval);
+ nwam_value_free(bssidsval);
+ for (i = 0; i < kw->kw_num_bssids; i++)
+ free(bssids[i]);
+ free(bssids);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not set bssids: %s", kw->kw_essid,
+ nwam_strerror(err));
+ nwam_known_wlan_free(kwh);
+ continue;
+ }
+
+ /*
+ * Retrieve last key matching ESSID prefix if any, and set
+ * the retrieved key name and security mode.
+ */
+ nwamd_set_key_name(kw->kw_essid, NULL, nsa.nsa_essid_prefix,
+ sizeof (nsa.nsa_essid_prefix));
+ nsa.nsa_key = NULL;
+ nsa.nsa_secmode = DLADM_WLAN_SECMODE_NONE;
+ (void) dladm_walk_secobj(dld_handle, &nsa,
+ find_secobj_matching_prefix, DLADM_OPT_PERSIST);
+ if (nsa.nsa_key != NULL) {
+ if ((err = nwam_value_create_string(nsa.nsa_keyname,
+ &keynameval)) == NWAM_SUCCESS) {
+ (void) nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYNAME, keynameval);
+ }
+ free(nsa.nsa_key);
+ nwam_value_free(keynameval);
+ }
+
+ if ((err = nwam_value_create_uint64(nsa.nsa_secmode,
+ &secmodeval)) != NWAM_SUCCESS ||
+ (err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_SECURITY_MODE, secmodeval))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not set security mode: %s",
+ kw->kw_essid, nwam_strerror(err));
+ nwam_value_free(secmodeval);
+ nwam_known_wlan_free(kwh);
+ continue;
+ }
+
+ /* commit, no collision checking by libnwam */
+ err = nwam_known_wlan_commit(kwh,
+ NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK);
+ nwam_known_wlan_free(kwh);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade wlan %s: "
+ "could not commit wlan: %s", kw->kw_essid,
+ nwam_strerror(err));
+ }
+ /* next ... */
+ }
+
+ kw_list_free();
+}
+
+nwam_error_t
+known_wlan_get_keyname(const char *essid, char *name)
+{
+ nwam_known_wlan_handle_t kwh = NULL;
+ nwam_value_t keynameval = NULL;
+ char *keyname;
+ nwam_error_t err;
+
+ if ((err = nwam_known_wlan_read(essid, 0, &kwh)) != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYNAME, &keynameval)) == NWAM_SUCCESS &&
+ (err = nwam_value_get_string(keynameval, &keyname))
+ == NWAM_SUCCESS) {
+ (void) strlcpy(name, keyname, NWAM_MAX_VALUE_LEN);
+ }
+ if (keynameval != NULL)
+ nwam_value_free(keynameval);
+
+ if (kwh != NULL)
+ nwam_known_wlan_free(kwh);
+
+ return (err);
+}
+
+nwam_error_t
+known_wlan_get_keyslot(const char *essid, uint_t *keyslotp)
+{
+ nwam_known_wlan_handle_t kwh = NULL;
+ nwam_value_t keyslotval = NULL;
+ uint64_t slot;
+ nwam_error_t err;
+
+ if ((err = nwam_known_wlan_read(essid, 0, &kwh)) != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYSLOT, &keyslotval)) == NWAM_SUCCESS &&
+ (err = nwam_value_get_uint64(keyslotval, &slot)) == NWAM_SUCCESS) {
+ *keyslotp = (uint_t)slot;
+ } else {
+ if (err == NWAM_ENTITY_NOT_FOUND)
+ err = NWAM_SUCCESS;
+ *keyslotp = 1;
+ }
+ if (keyslotval != NULL)
+ nwam_value_free(keyslotval);
+ if (kwh != NULL)
+ nwam_known_wlan_free(kwh);
+ return (err);
+}
+
+/* Performs a scan on a wifi link NCU */
+/* ARGSUSED */
+static int
+nwamd_ncu_known_wlan_committed(nwamd_object_t object, void *data)
+{
+ nwamd_ncu_t *ncu_data = object->nwamd_object_data;
+
+ if (ncu_data->ncu_type != NWAM_NCU_TYPE_LINK)
+ return (0);
+
+ /* network selection will be done only if possible */
+ if (ncu_data->ncu_node.u_link.nwamd_link_media == DL_WIFI)
+ (void) nwamd_wlan_scan(ncu_data->ncu_name);
+ return (0);
+}
+
+/* Handle known WLAN initialization/refresh event */
+/* ARGSUSED */
+void
+nwamd_known_wlan_handle_init_event(nwamd_event_t known_wlan_event)
+{
+ /*
+ * Since the Known WLAN list has changed, do a rescan so that the
+ * best network is selected.
+ */
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
+ nwamd_ncu_known_wlan_committed, NULL);
+}
+
+void
+nwamd_known_wlan_handle_action_event(nwamd_event_t known_wlan_event)
+{
+ switch (known_wlan_event->event_msg->nwe_data.nwe_object_action.
+ nwe_action) {
+ case NWAM_ACTION_ADD:
+ case NWAM_ACTION_REFRESH:
+ nwamd_known_wlan_handle_init_event(known_wlan_event);
+ break;
+ case NWAM_ACTION_DESTROY:
+ /* Nothing needs to be done for destroy */
+ break;
+ /* all other events are invalid for known WLANs */
+ case NWAM_ACTION_ENABLE:
+ case NWAM_ACTION_DISABLE:
+ default:
+ nlog(LOG_INFO, "nwam_known_wlan_handle_action_event: "
+ "unexpected action");
+ break;
+ }
+}
+
+int
+nwamd_known_wlan_action(const char *known_wlan, nwam_action_t action)
+{
+ nwamd_event_t known_wlan_event = nwamd_event_init_object_action
+ (NWAM_OBJECT_TYPE_KNOWN_WLAN, known_wlan, NULL, action);
+ if (known_wlan_event == NULL)
+ return (1);
+ nwamd_event_enqueue(known_wlan_event);
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.h b/usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.h
new file mode 100644
index 0000000000..d5fb13a52c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/known_wlans.h
@@ -0,0 +1,37 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KNOWN_WLANS_H
+#define _KNOWN_WLANS_H
+
+#include <libnwam.h>
+#include <syslog.h>
+
+void upgrade_known_wifi_nets_config(void);
+nwam_error_t known_wlan_get_keyname(const char *, char *);
+nwam_error_t known_wlan_get_keyslot(const char *, uint_t *);
+
+#endif /* _KNOWN_WLANS_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/llp.c b/usr/src/cmd/cmd-inet/lib/nwamd/llp.c
index 737aea412b..c963216731 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/llp.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/llp.c
@@ -20,83 +20,43 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
- * This file contains the routines that manipulate Link Layer Profiles
- * (aka LLPs) and various support functions. This includes parsing and
- * updating of the /etc/nwam/llp file.
- *
- * The daemon maintains a list of llp_t structures that represent the
- * provided configuration information for a link. After the llp file
- * is read, entries are added to the LLP list for any known links
- * (identified by checking the interface list, which is based on the
- * v4 interfaces present after 'ifconfig -a plumb') which were not
- * represented in the llp file. These entries contain the default
- * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the
- * v4 interface, and accept router- and DHCPv6-assigned addresses on
- * the v6 interface. The default entries created by the daemon are
- * also added to the llp file.
- *
- * LLP priority is assigned based on two factors: the order within
- * the llp file, with earlier entries having higher priority; and
- * a preference for wired interfaces before wireless. Entries that
- * are added to the file by the daemon are added *after* any existing
- * entries; within the added block, wired entries are added before
- * wireless. Thus if the llp file is never modified externally, wired
- * will generally be ordered before wireless. However, if the
- * administrator creates the file with wireless entries before wired,
- * that priority order will be respected.
- *
- * The llp list 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.
+ * This file is here for legacy support.
*/
+#include <atomic.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <libdllink.h>
+#include <libscf.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
-#include <strings.h>
#include <string.h>
-#include <ctype.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <syslog.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <atomic.h>
-#include <pthread.h>
-#include <signal.h>
+#include <strings.h>
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
+#include <libnwam.h>
+#include "known_wlans.h"
+#include "llp.h"
+#include "ncu.h"
+#include "util.h"
-/* Lock to protect the llp list. */
-static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER;
+/*
+ * This file formerly contained the routines that manipulate Link Layer
+ * Profiles (aka LLPs) and various support functions. Now only code
+ * necessary for parsing the legacy /etc/nwam/llp file on upgrade is included,
+ * since this legacy configuration needs to be translated into the User NCP.
+ */
-/* Accessed only from main thread or with llp_lock held */
-llp_t *link_layer_profile;
+#define OUR_OLD_DHCP_WAIT_TIME_PROP_NAME "dhcp_wait_time"
+#define OUR_OLD_USE_NET_SVC_PROP_NAME "use_net_svc"
+#define OUR_OLD_IDLE_TIME_PROP_NAME "idle_time"
static struct qelem llp_list;
-static llp_t *locked_llp;
/*
* Global variable to hold the highest priority. Need to use the atomic
@@ -104,57 +64,45 @@ static llp_t *locked_llp;
*/
static uint32_t llp_highest_pri;
-static void print_llp_list(void);
+/* Specifies if static address has been configured in /etc/nwam/llp */
+static boolean_t static_configured = B_FALSE;
-void
-initialize_llp(void)
-{
- llp_list.q_forw = llp_list.q_back = &llp_list;
-}
-
-char *
-llp_prnm(llp_t *llp)
-{
- if (llp == NULL)
- return ("null_llp");
- else if (llp->llp_lname == NULL)
- return ("null_lname");
- else
- return (llp->llp_lname);
-}
-
-/*
- * 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)
+static enum interface_type
+find_if_type(const char *name)
{
- 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);
- }
+ uint32_t media;
+ enum interface_type type;
+
+ if (name == NULL) {
+ nlog(LOG_DEBUG, "find_if_type: no ifname; "
+ "returning IF_UNKNOWN");
+ return (IF_UNKNOWN);
+ }
+
+ type = IF_WIRED;
+ if (dladm_name2info(dld_handle, name, NULL, NULL, NULL, &media) !=
+ DLADM_STATUS_OK) {
+ if (strncmp(name, "ip.tun", 6) == 0 ||
+ strncmp(name, "ip6.tun", 7) == 0 ||
+ strncmp(name, "ip.6to4tun", 10) == 0)
+ /*
+ * We'll need to update our tunnel detection once
+ * the clearview/tun project is integrated; tunnel
+ * names won't necessarily be ip.tunN.
+ */
+ type = IF_TUN;
+ } else if (media == DL_WIFI) {
+ type = IF_WIRELESS;
+ }
+
+ return (type);
}
static void
llp_list_free(void)
{
- int retv;
llp_t *llp;
- 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: %s",
- strerror(retv));
- return;
- }
while (llp_list.q_forw != &llp_list) {
llp = (llp_t *)llp_list.q_forw;
remque(&llp->llp_links);
@@ -162,13 +110,15 @@ llp_list_free(void)
free(llp->llp_ipv4addrstr);
free(llp);
}
- (void) pthread_mutex_unlock(&llp_lock);
}
-/*
- * Called either from main thread or with llp_lock held.
- */
-llp_t *
+static void
+initialize_llp(void)
+{
+ llp_list.q_forw = llp_list.q_back = &llp_list;
+}
+
+static llp_t *
llp_lookup(const char *link)
{
llp_t *llp;
@@ -187,194 +137,21 @@ llp_lookup(const char *link)
}
/*
- * Choose the higher priority llp of the two passed in. If one is
- * NULL, the other will be higher priority. If both are NULL, NULL
- * is returned.
- *
- * Assumes that both are available (i.e. doesn't check IFF_RUNNING
- * or IF_DHCPFAILED flag values).
- */
-llp_t *
-llp_high_pri(llp_t *a, llp_t *b)
-{
- if (a == NULL || 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);
- 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.
- */
- return ((a->llp_pri <= b->llp_pri) ? a : b);
-}
-
-/*
- * 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 *llp, *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);
- }
- }
-
- return (rtnllp);
-}
-
-/*
- * 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.
- */
-static void
-llp_activate(llp_t *llp)
-{
- char *host;
- /*
- * Choosing "dhcp" as a hostname is unsupported right now.
- * We use hostname="dhcp" as a keyword telling bringupinterface()
- * to use dhcp on the interface.
- */
- char *dhcpstr = "dhcp";
-
- assert(link_layer_profile == NULL);
-
- host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr :
- llp->llp_ipv4addrstr;
-
- 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));
- 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;
- 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));
- }
-}
-
-/*
- * Replace the currently active link layer profile with the one
- * specified. And since we're changing the lower layer stuff,
- * we need to first deactivate the current upper layer profile.
- * An upper layer profile will be reactivated later, when we get
- * confirmation that the new llp is fully up (has an address
- * assigned).
- *
- * If the new llp is the same as the currently active one, don't
- * do anything.
- *
- * Called only by the main thread.
- */
-void
-llp_swap(llp_t *newllp, libnwam_diag_cause_t cause)
-{
- int minpri;
-
- if (newllp == link_layer_profile)
- return;
-
- deactivate_upper_layer_profile();
-
- if (link_layer_profile != NULL) {
- dprintf("taking down current link layer profile (%s)",
- llp_prnm(link_layer_profile));
- report_llp_unselected(link_layer_profile->llp_lname, cause);
- link_layer_profile->llp_waiting = B_FALSE;
- link_layer_profile = 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));
- llp_activate(newllp);
- newllp = NULL;
- if (link_layer_profile == NULL &&
- (newllp = llp_best_avail()) != NULL &&
- newllp->llp_failed)
- newllp = NULL;
- }
-
- /*
- * 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);
- }
-}
-
-/*
* Create the named LLP with default settings. Called only in main thread.
*/
-llp_t *
+static llp_t *
llp_add(const char *name)
{
- int retv;
llp_t *llp;
if ((llp = calloc(1, sizeof (llp_t))) == NULL) {
- syslog(LOG_ERR, "cannot allocate LLP: %m");
+ nlog(LOG_ERR, "llp_add: cannot allocate LLP: %m");
return (NULL);
}
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",
+ nlog(LOG_ERR, "llp_add: linkname '%s' too long; ignoring entry",
name);
free(llp);
return (NULL);
@@ -391,102 +168,20 @@ llp_add(const char *name)
* create llps for wired and wireless interfaces.
*/
if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) {
- syslog(LOG_ERR, "llp: wrong type of interface for %s", name);
+ nlog(LOG_ERR, "llp_add: wrong type of interface for %s", name);
free(llp);
return (NULL);
}
-
- if ((retv = pthread_mutex_lock(&llp_lock)) != 0) {
- /* Something very serious is wrong... */
- syslog(LOG_ERR, "llp: cannot lock mutex: %s", strerror(retv));
- free(llp);
- return (NULL);
- }
-
insque(&llp->llp_links, llp_list.q_back);
- (void) pthread_mutex_unlock(&llp_lock);
-
- dprintf("created llp for link %s, priority %d", llp->llp_lname,
+ nlog(LOG_DEBUG, "llp_add: "
+ "created llp for link %s, priority %d", llp->llp_lname,
llp->llp_pri);
return (llp);
}
-/*
- * Walker function to pass to walk_interface() to find out if
- * an interface description is missing from LLPFILE. If it is,
- * add it.
- *
- * Currently, IF_TUN type interfaces are special-cased: they are
- * only handled as user-enabled, layered links (which may be created
- * as part of a higher-layer profile, for example). Thus, they
- * shouldn't be considered when looking at the llp list, so don't
- * add them here.
- *
- * 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 = 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;
-
- 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. */
- (void) llp_add(ifp->if_name);
- }
-}
-
-static void
-print_llp_list(void)
-{
- llp_t *wp;
-
- dprintf("Walking llp list");
- 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);
-}
-
-/*
- * This function parses /etc/nwam/llp which describes the phase 0 link layer
- * profile. The file is line oriented with each line containing tab or space
- * delimited fields. Each address family (IPv4, IPv6) is described on a
- * separate line.
- * The first field is a link name.
- * The second field can be either static, dhcp, ipv6, 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.
- * If the second field is dhcp then dhcp will be used on the interface.
- * If the second field is ipv6 then an ipv6 interface is plumbed up. The
- * outcome of this is that if offered by the network in.ndpd and dhcp
- * will conspire to put addresses on additional ipv6 logical interfaces.
- * If the next field is non-null then it is taken to be an IPv6 address
- * and possible prefix which are applied to the interface.
- * If the second field is noipv6 then no ipv6 interfaces will be put on that
- * link.
- * 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)
+static int
+parse_llp_config(void)
{
static const char STATICSTR[] = "static";
static const char DHCP[] = "dhcp";
@@ -499,27 +194,17 @@ llp_parse_config(void)
int lnum;
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;
- }
+ initialize_llp();
fp = fopen(LLPFILE, "r+");
if (fp == NULL) {
- if (errno != ENOENT) {
- syslog(LOG_ERR, "open LLP config file: %m");
- return;
- }
- if ((fp = fopen(LLPFILE, "w+")) == NULL) {
- syslog(LOG_ERR, "create LLP config file: %m");
- return;
- }
+ if (errno == ENOENT)
+ return (errno);
+ nlog(LOG_ERR, "parse_llp_config: "
+ "open legacy LLP config file: %m");
+ return (-1);
}
- llp_list_free();
-
for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) {
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = '\0';
@@ -531,29 +216,31 @@ llp_parse_config(void)
if (*cp == '#' || *cp == '\0')
continue;
- dprintf("parsing llp conf file line %d...", lnum);
+ nlog(LOG_DEBUG, "parse_llp_config: "
+ "parsing legacy LLP conf file line %d...", lnum);
if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) ||
((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) {
- syslog(LOG_ERR, "llp:%d: not enough tokens; "
- "ignoring entry", lnum);
+ nlog(LOG_ERR, "parse_llp_config: line %d: "
+ "not enough tokens; ignoring entry", lnum);
continue;
}
if ((llp = llp_lookup(lstr)) == NULL &&
(llp = llp_add(lstr)) == NULL) {
- syslog(LOG_ERR, "llp:%d: cannot add entry", lnum);
+ nlog(LOG_ERR, "parse_llp_config: line %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", lnum);
+ nlog(LOG_ERR, "parse_llp_config: line %d: "
+ "missing ipaddr for static config", lnum);
} else if ((addrstr = strdup(addrstr)) == NULL) {
- syslog(LOG_ERR, "llp:%d: cannot save address",
- lnum);
+ nlog(LOG_ERR, "parse_llp_config: line %d: "
+ "cannot save address", lnum);
} else {
free(llp->llp_ipv4addrstr);
llp->llp_ipv4src = IPV4SRC_STATIC;
@@ -568,8 +255,8 @@ llp_parse_config(void)
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);
+ nlog(LOG_ERR, "parse_llp_config: line %d: "
+ "cannot save address", lnum);
} else {
free(llp->llp_ipv6addrstr);
llp->llp_ipv6addrstr = addrstr;
@@ -580,433 +267,254 @@ llp_parse_config(void)
} else if (strcasecmp(srcstr, PRIORITY) == 0) {
if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) {
- syslog(LOG_ERR,
- "llp:%d: missing priority value", lnum);
+ nlog(LOG_ERR,
+ "parse_llp_config: line %d: "
+ "missing priority value", lnum);
} else {
llp->llp_pri = atoi(addrstr);
}
} else {
- syslog(LOG_ERR, "llp:%d: unrecognized field '%s'", lnum,
- srcstr);
+ nlog(LOG_ERR, "parse_llp_config: line %d: "
+ "unrecognized field '%s'", lnum, srcstr);
}
}
- /*
- * 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.
- */
- walk_interface(find_and_add_llp, fp);
-
(void) fclose(fp);
-
- print_llp_list();
+ return (0);
}
/*
- * Called only from the main thread.
+ * Translate legacy LLP config into the user NCP.
*/
-void
-llp_add_file(const llp_t *llp)
+static int
+upgrade_llp_config(void)
{
- 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;
+ llp_t *wp;
+ nwam_ncp_handle_t user_ncp;
+ nwam_ncu_handle_t phys_ncu = NULL, ip_ncu = NULL;
+ nwam_error_t err;
+ uint64_t uintval;
+ char *strval;
+ const char *prop;
+
+ switch (parse_llp_config()) {
+ case -1:
+ return (0);
+ case ENOENT:
+ return (ENOENT);
+ default:
+ break;
+ }
+ if ((err = nwam_ncp_create(NWAM_NCP_NAME_USER, 0, &user_ncp))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade_llp_config: "
+ "could not create user NCP: %s", nwam_strerror(err));
+ llp_list_free();
+ return (0);
}
- 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;
- }
+ nlog(LOG_DEBUG, "upgrade_llp_config: walking llp list");
- /* 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);
+ for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list;
+ wp = (llp_t *)wp->llp_links.q_forw) {
+
+ nlog(LOG_DEBUG, "upgrade_llp_config: "
+ "upgrading llp %s", wp->llp_lname);
+
+ if (nwam_ncu_create(user_ncp, wp->llp_lname,
+ NWAM_NCU_TYPE_INTERFACE, NWAM_NCU_CLASS_IP, &ip_ncu)
+ != NWAM_SUCCESS ||
+ nwam_ncu_create(user_ncp, wp->llp_lname, NWAM_NCU_TYPE_LINK,
+ NWAM_NCU_CLASS_PHYS, &phys_ncu) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
+ "could not create NCUs: %s", wp->llp_lname,
+ nwam_strerror(err));
+ break;
}
- /* 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);
-}
+ /* Link NCU properties */
+ prop = NWAM_NCU_PROP_ACTIVATION_MODE;
+ uintval = NWAM_ACTIVATION_MODE_PRIORITIZED;
+ if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
+ != NWAM_SUCCESS)
+ break;
-/*
- * 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];
+ prop = NWAM_NCU_PROP_PRIORITY_MODE;
+ uintval = NWAM_PRIORITY_MODE_EXCLUSIVE;
+ if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
+ != NWAM_SUCCESS)
+ break;
- (void) snprintf(prival, sizeof (prival), "%d", llp->llp_pri);
- llp_update_config(llp->llp_lname, "priority", prival, B_FALSE);
- }
-}
+ prop = NWAM_NCU_PROP_PRIORITY_GROUP;
+ uintval = wp->llp_pri;
+ if ((err = nwamd_set_ncu_uint(phys_ncu, &uintval, 1, prop))
+ != NWAM_SUCCESS)
+ break;
-/*
- * 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);
+ /* IP NCU properties */
+ if (wp->llp_ipv4addrstr != NULL) {
+ /* Set v4 address and specify static addrsrc */
+ prop = NWAM_NCU_PROP_IPV4_ADDRSRC;
+ uintval = NWAM_ADDRSRC_STATIC;
+ if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
+ prop)) != NWAM_SUCCESS)
+ break;
+
+ prop = NWAM_NCU_PROP_IPV4_ADDR;
+ strval = wp->llp_ipv4addrstr;
+ if ((err = nwamd_set_ncu_string(ip_ncu, &strval, 1,
+ prop)) != NWAM_SUCCESS)
+ break;
+
+ static_configured = B_TRUE;
}
- 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);
+ if (wp->llp_ipv6addrstr != NULL) {
+ /* Set v6 address and specify static addrsrc */
+ prop = NWAM_NCU_PROP_IPV6_ADDRSRC;
+ uintval = NWAM_ADDRSRC_STATIC;
+ if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
+ prop)) != NWAM_SUCCESS)
+ break;
+
+ prop = NWAM_NCU_PROP_IPV6_ADDR;
+ strval = wp->llp_ipv6addrstr;
+ if ((err = nwamd_set_ncu_string(ip_ncu, &strval, 1,
+ prop)) != NWAM_SUCCESS)
+ break;
+
+ static_configured = B_TRUE;
}
- } 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;
- }
-}
+ if (!wp->llp_ipv6onlink) {
+ prop = NWAM_NCU_PROP_IP_VERSION;
+ uintval = IPV4_VERSION;
+ if ((err = nwamd_set_ncu_uint(ip_ncu, &uintval, 1,
+ prop)) != NWAM_SUCCESS)
+ break;
+ }
-/*
- * 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++;
+ if ((err = nwam_ncu_commit(ip_ncu, 0)) != NWAM_SUCCESS ||
+ (err = nwam_ncu_commit(phys_ncu, 0)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
+ "could not commit NCUs: %s", wp->llp_lname,
+ nwam_strerror(err));
+ /* Schedule a retry - root filesystem may be readonly */
+ llp_list_free();
+ return (EAGAIN);
}
+ nwam_ncu_free(ip_ncu);
+ nwam_ncu_free(phys_ncu);
}
- (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++;
+
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "upgrade_llp_config: llp %s: "
+ "could not set value for property %s: %s", wp->llp_lname,
+ prop, nwam_strerror(err));
+ nwam_ncu_free(ip_ncu);
+ nwam_ncu_free(phys_ncu);
+ exit(EXIT_FAILURE);
}
- return (llplist);
+ llp_list_free();
+ return (0);
}
/*
- * 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.
+ * Upgrade legacy llp and known_wifi_nets files. Note - it is possible that
+ * the root filesystem is not writable at this point, so we need to schedule
+ * a retry of the upgrade operation in the event that committing the new
+ * config fails.
*/
+/* ARGSUSED0 */
void
-llp_reselect(void)
+nwamd_handle_upgrade(nwamd_event_t event)
{
- 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));
+ nwamd_event_t upgrade_event;
+ uint64_t dhcp_wait_time, idle_time;
+ boolean_t use_net_svc;
+
+ switch (upgrade_llp_config()) {
+ case -1:
+ case ENOENT:
+ /* Nothing readable to upgrade */
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;
+ case EAGAIN:
+ /*
+ * Schedule retry in NWAMD_READONLY_RETRY_INTERVAL seconds
+ * as root fs may be readonly.
+ *
+ * The upgrade event is of type NCU, but has no associated
+ * object (we use the event type to map to the appropriate
+ * event/method mappings, so to find the NCU upgrade event
+ * method we specify type NCU while not specifying an
+ * object since all NCUs have to be upgraded.
+ */
+ upgrade_event = nwamd_event_init(NWAM_EVENT_TYPE_UPGRADE,
+ NWAM_OBJECT_TYPE_NCP, 0, NULL);
+ if (upgrade_event == NULL)
+ return;
+ nwamd_event_enqueue_timed(upgrade_event,
+ NWAMD_READONLY_RETRY_INTERVAL);
+ return;
+ default:
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);
+ /*
+ * If static_configured is set, then at least one static address is
+ * configured in /etc/nwam/llp. Enable the User NCP in this case.
+ */
+ if (static_configured) {
+ nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
+ "static address configured, enabling User NCP");
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ (void) strlcpy(active_ncp, NWAM_NCP_NAME_USER,
+ NWAM_MAX_NAME_LEN);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
}
-}
-/*
- * 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;
+ /* upgrade /etc/nwam/known_wifi_nets */
+ upgrade_known_wifi_nets_config();
- if (pthread_mutex_lock(&llp_lock) == 0) {
- if ((llp = llp_lookup(ifname)) != NULL)
- src = llp->llp_ipv4src;
- (void) pthread_mutex_unlock(&llp_lock);
+ /*
+ * SMF property nwamd/dhcp_wait_time in Phase 0/0.5 has been
+ * replaced by nwamd/ncu_wait_time property. If the dhcp_wait_time
+ * property exists (which means it has been changed by the user),
+ * set its value to ncu_wait_time and remove the property.
+ */
+ if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_OLD_DHCP_WAIT_TIME_PROP_NAME, &dhcp_wait_time) == 0) {
+ (void) nwamd_set_count_property(OUR_FMRI, OUR_PG,
+ OUR_NCU_WAIT_TIME_PROP_NAME, dhcp_wait_time);
+ (void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
+ OUR_OLD_DHCP_WAIT_TIME_PROP_NAME);
+ nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
+ "converted '%s' to '%s' with value of %lld",
+ OUR_OLD_DHCP_WAIT_TIME_PROP_NAME,
+ OUR_NCU_WAIT_TIME_PROP_NAME, dhcp_wait_time);
}
- 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);
- }
+ /*
+ * If the user has changed Phase 0/0.5 properties that don't exist in
+ * Phase 1, manifest-import reports a warning; but those properties are
+ * not removed. nwamd/use_net_svc and nwamd/idle_time are two
+ * properties that don't exist in Phase 1. If they exist, remove them.
+ */
+ if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_OLD_IDLE_TIME_PROP_NAME, &idle_time) == 0) {
+ (void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
+ OUR_OLD_IDLE_TIME_PROP_NAME);
+ }
+ if (nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
+ OUR_OLD_USE_NET_SVC_PROP_NAME, &use_net_svc) == 0) {
+ (void) nwamd_delete_scf_property(OUR_FMRI, OUR_PG,
+ OUR_OLD_USE_NET_SVC_PROP_NAME);
+ }
+
+ nlog(LOG_DEBUG, "nwamd_handle_upgrade: "
+ "creating version property, setting to 1\n");
+ (void) nwamd_set_count_property(OUR_FMRI, OUR_PG,
+ OUR_VERSION_PROP_NAME, 1U);
+ (void) smf_refresh_instance(OUR_FMRI);
}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/llp.h b/usr/src/cmd/cmd-inet/lib/nwamd/llp.h
new file mode 100644
index 0000000000..80f5b2160f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/llp.h
@@ -0,0 +1,76 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LLP_H
+#define _LLP_H
+
+#include <search.h>
+#include <libnwam.h>
+#include <syslog.h>
+
+#include "events.h"
+
+/*
+ * This file is here for legacy support.
+ */
+
+#define LLPDIR "/etc/nwam"
+#define LLPFILE LLPDIR"/llp"
+
+enum interface_type {
+ IF_UNKNOWN, IF_WIRED, IF_WIRELESS, IF_TUN
+};
+
+typedef enum {
+ IPV4SRC_STATIC,
+ IPV4SRC_DHCP
+} ipv4src_t;
+
+/*
+ * This structure contains a representation of legacy LLP configuration
+ * which previously represented the intended configuration of the system as
+ * differentiated from the actual IPv4 configuration of the system represented
+ * by the interface structures.
+ *
+ * llp structures are held on the list llp_head.
+ */
+typedef struct llp {
+ struct qelem llp_links;
+ char llp_lname[LIFNAMSIZ];
+ uint32_t llp_pri; /* lower number => higher priority */
+ int llp_fileorder;
+ enum interface_type llp_type;
+ ipv4src_t llp_ipv4src;
+ char *llp_ipv4addrstr; /* if ipsrc is STATIC */
+ char *llp_ipv6addrstr; /* if the user provided a static addr */
+ boolean_t llp_ipv6onlink; /* true if we plumb up a v6 interface */
+} llp_t;
+
+extern llp_t *link_layer_profile;
+
+void nwamd_handle_upgrade(nwamd_event_t);
+
+#endif /* _LLP_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/loc.c b/usr/src/cmd/cmd-inet/lib/nwamd/loc.c
new file mode 100644
index 0000000000..258f0e3b4f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/loc.c
@@ -0,0 +1,608 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <inet/ip.h>
+#include <inetcfg.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlwlan.h>
+#include <libscf.h>
+#include <limits.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <libnwam.h>
+#include "conditions.h"
+#include "events.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * loc.c - contains routines which handle location abstraction.
+ */
+
+pthread_mutex_t active_loc_mutex = PTHREAD_MUTEX_INITIALIZER;
+char active_loc[NWAM_MAX_NAME_LEN];
+
+static int
+loc_create_init_fini_event(nwam_loc_handle_t loch, void *data)
+{
+ boolean_t *init = data;
+ char *name;
+ nwamd_event_t event;
+
+ if (nwam_loc_get_name(loch, &name) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "loc_init_fini: could not get loc name");
+ return (0);
+ }
+
+ event = nwamd_event_init(*init ?
+ NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
+ NWAM_OBJECT_TYPE_LOC, 0, name);
+ if (event != NULL)
+ nwamd_event_enqueue(event);
+ free(name);
+
+ return (0);
+}
+
+/*
+ * Walk all locs, creating init events for each.
+ */
+void
+nwamd_init_locs(void)
+{
+ boolean_t init = B_TRUE;
+
+ /* Unset active location */
+ (void) pthread_mutex_lock(&active_loc_mutex);
+ active_loc[0] = '\0';
+ (void) pthread_mutex_unlock(&active_loc_mutex);
+ (void) nwam_walk_locs(loc_create_init_fini_event, &init, 0, NULL);
+}
+
+/*
+ * Walk all locs, creating fini events for each.
+ */
+void
+nwamd_fini_locs(void)
+{
+ boolean_t init = B_FALSE;
+
+ (void) nwam_walk_locs(loc_create_init_fini_event, &init, 0, NULL);
+}
+
+static boolean_t
+loc_is_enabled(nwam_loc_handle_t loch)
+{
+ nwam_value_t enabledval;
+ boolean_t enabled = B_FALSE;
+
+ if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ENABLED,
+ &enabledval) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "loc_is_enabled: could not retrieve "
+ "enabled value");
+ return (B_FALSE);
+ }
+ if (nwam_value_get_boolean(enabledval, &enabled)
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "loc_is_enabled: could not retrieve "
+ "enabled value");
+ nwam_value_free(enabledval);
+ return (B_FALSE);
+ }
+ nwam_value_free(enabledval);
+ return (enabled);
+}
+
+static int64_t
+loc_get_activation_mode(nwam_loc_handle_t loch)
+{
+ nwam_error_t err;
+ uint64_t activation;
+ nwam_value_t activationval;
+
+ if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
+ &activationval) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "loc_get_activation_mode: could not retrieve "
+ "activation mode value");
+ return (-1);
+ }
+ err = nwam_value_get_uint64(activationval, &activation);
+ nwam_value_free(activationval);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "loc_get_activation_mode: could not retrieve "
+ "activation mode value");
+ return (-1);
+ }
+
+ return ((int64_t)activation);
+}
+
+/* Enables the location. */
+static void
+nwamd_loc_activate(const char *object_name)
+{
+ char *enabled;
+
+ nlog(LOG_DEBUG, "nwamd_loc_activate: activating loc %s",
+ object_name);
+
+ /*
+ * Find currently enabled location and change its state to disabled
+ * if it is a manual location, or offline (if it is not).
+ * Only manual locations reach disabled, since conditional and
+ * system locations which are manually disabled simply revert to
+ * their conditions for activation.
+ */
+ if ((enabled = malloc(NWAM_MAX_NAME_LEN)) != NULL &&
+ nwamd_lookup_string_property(NET_LOC_FMRI, NET_LOC_PG,
+ NET_LOC_SELECTED_PROP, enabled, NWAM_MAX_NAME_LEN) == 0) {
+ /* Only change state if current != new */
+ if (strcmp(enabled, object_name) != 0) {
+ boolean_t do_disable = B_FALSE;
+ nwamd_object_t eobj = nwamd_object_find
+ (NWAM_OBJECT_TYPE_LOC, enabled);
+ if (eobj == NULL) {
+ nlog(LOG_ERR, "nwamd_loc_activate: cannot "
+ "find old location %s", enabled);
+ free(enabled);
+ return;
+ }
+ /*
+ * Disable if the old location was manual, since the
+ * only way a manual location can deactivate is if
+ * it is disabled.
+ */
+ do_disable =
+ (loc_get_activation_mode(eobj->nwamd_object_handle)
+ == (int64_t)NWAM_ACTIVATION_MODE_MANUAL);
+ nwamd_object_release(eobj);
+
+ if (do_disable) {
+ nlog(LOG_DEBUG, "nwamd_loc_activate: "
+ "disable needed for old location %s",
+ enabled);
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_LOC, enabled,
+ NWAM_STATE_DISABLED,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ } else {
+ nlog(LOG_DEBUG, "nwamd_loc_activate: "
+ "offline needed for old location %s",
+ enabled);
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_LOC, enabled,
+ NWAM_STATE_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ }
+ }
+ free(enabled);
+
+ if (nwamd_set_string_property(NET_LOC_FMRI, NET_LOC_PG,
+ NET_LOC_SELECTED_PROP, object_name) == 0) {
+ char *state = smf_get_state(NET_LOC_FMRI);
+ nlog(LOG_INFO, "nwam_loc_activate: set %s/%s to %s; "
+ "service is in %s state", NET_LOC_PG, NET_LOC_SELECTED_PROP,
+ object_name, state == NULL ? "unknown" : state);
+ free(state);
+ (void) smf_restore_instance(NET_LOC_FMRI);
+ if (smf_refresh_instance(NET_LOC_FMRI) == 0) {
+ (void) pthread_mutex_lock(&active_loc_mutex);
+ (void) strlcpy(active_loc, object_name,
+ sizeof (active_loc));
+ (void) pthread_mutex_unlock(&active_loc_mutex);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
+ object_name,
+ NWAM_STATE_ONLINE, NWAM_AUX_STATE_ACTIVE);
+ } else {
+ nlog(LOG_ERR, "nwamd_loc_activate: "
+ "%s could not be refreshed", NET_LOC_FMRI);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
+ object_name,
+ NWAM_STATE_MAINTENANCE,
+ NWAM_AUX_STATE_METHOD_FAILED);
+ }
+ }
+}
+
+struct nwamd_loc_check_walk_arg {
+ nwamd_object_t winning_object;
+ uint64_t winning_rating;
+};
+
+/*
+ * Determine which location should be activated.
+ */
+static int
+nwamd_loc_check(nwamd_object_t object, void *data)
+{
+ struct nwamd_loc_check_walk_arg *wa = data;
+ nwam_loc_handle_t loch = object->nwamd_object_handle;
+ nwam_value_t conditionval;
+ int64_t lactivation;
+ uint64_t rating, activation;
+ boolean_t satisfied;
+ char **conditions;
+ uint_t nelem;
+
+ lactivation = loc_get_activation_mode(object->nwamd_object_handle);
+
+ if (lactivation == -1)
+ return (0);
+
+ activation = (uint64_t)lactivation;
+ switch (activation) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ if (loc_is_enabled(loch)) {
+ /* Manually enabled locations should always win out. */
+ nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
+ object->nwamd_object_name);
+ wa->winning_object = object;
+ wa->winning_rating = UINT64_MAX;
+ } else {
+ nlog(LOG_DEBUG, "nwamd_loc_check: %s is disabled",
+ object->nwamd_object_name);
+ if (object->nwamd_object_state != NWAM_STATE_DISABLED) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
+ object->nwamd_object_name,
+ NWAM_STATE_DISABLED,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ }
+ }
+
+ return (0);
+
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
+ if (loc_is_enabled(loch)) {
+ /* Manually enabled locations should always win out. */
+ nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
+ object->nwamd_object_name);
+ wa->winning_object = object;
+ wa->winning_rating = UINT64_MAX;
+ }
+
+ if (nwam_loc_get_prop_value(loch,
+ NWAM_LOC_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_loc_check: could not retrieve "
+ "condition value");
+ return (0);
+ }
+ if (nwam_value_get_string_array(conditionval,
+ &conditions, &nelem) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_loc_check: could not retrieve "
+ "condition value");
+ nwam_value_free(conditionval);
+ return (0);
+ }
+ satisfied = nwamd_check_conditions(activation, conditions,
+ nelem);
+
+ if (satisfied) {
+ rating = nwamd_rate_conditions(activation,
+ conditions, nelem);
+ if (rating > wa->winning_rating) {
+ wa->winning_object = object;
+ wa->winning_rating = rating;
+ }
+ }
+ nwam_value_free(conditionval);
+ return (0);
+
+ case NWAM_ACTIVATION_MODE_SYSTEM:
+ if (loc_is_enabled(loch)) {
+ /* Manually enabled locations should always win out. */
+ nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
+ object->nwamd_object_name);
+ wa->winning_object = object;
+ wa->winning_rating = UINT64_MAX;
+ }
+
+ /* Either NoNet, Automatic or Legacy location, so skip. */
+
+ return (0);
+ default:
+ return (0);
+ }
+ /*NOTREACHED*/
+ return (0);
+}
+
+static int
+nwamd_ncu_online_check(nwamd_object_t object, void *data)
+{
+ boolean_t *online = data;
+ nwamd_ncu_t *ncu_data = object->nwamd_object_data;
+
+ if (ncu_data->ncu_type != NWAM_NCU_TYPE_INTERFACE)
+ return (0);
+
+ if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
+ /* An online IP NCU found, stop walk */
+ *online = B_TRUE;
+ return (1);
+ }
+ return (0);
+}
+
+void
+nwamd_loc_check_conditions(void)
+{
+ struct nwamd_loc_check_walk_arg wa = { NULL, 0 };
+ const char *winning_loc;
+ boolean_t ncu_online = B_FALSE;
+ boolean_t is_active;
+
+ /*
+ * Walk the NCUs to find out if at least one IP NCU is online. If so,
+ * check the activation-mode and conditions. If not, enable the NoNet
+ * location.
+ */
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, nwamd_ncu_online_check,
+ &ncu_online);
+
+ if (!ncu_online) {
+ winning_loc = NWAM_LOC_NAME_NO_NET;
+ } else {
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_LOC, nwamd_loc_check,
+ &wa);
+ if (wa.winning_object != NULL)
+ winning_loc = wa.winning_object->nwamd_object_name;
+ else
+ winning_loc = NWAM_LOC_NAME_AUTOMATIC;
+ }
+ nlog(LOG_INFO, "nwamd_loc_check_conditions: winning loc is %s",
+ winning_loc);
+
+ /* If the winning location is already active, do nothing */
+ (void) pthread_mutex_lock(&active_loc_mutex);
+ is_active = (strcmp(active_loc, winning_loc) == 0);
+ (void) pthread_mutex_unlock(&active_loc_mutex);
+ if (is_active)
+ return;
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC, winning_loc,
+ NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_METHOD_RUNNING);
+}
+
+int
+nwamd_loc_action(const char *loc, nwam_action_t action)
+{
+ nwamd_event_t event = nwamd_event_init_object_action
+ (NWAM_OBJECT_TYPE_LOC, loc, NULL, action);
+ if (event == NULL)
+ return (1);
+ nwamd_event_enqueue(event);
+ return (0);
+}
+
+/*
+ * Event handling functions.
+ */
+
+/* Handle loc initialization/refresh event */
+void
+nwamd_loc_handle_init_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+ nwam_loc_handle_t loch;
+ nwam_error_t err;
+ boolean_t manual_disabled = B_FALSE;
+ nwam_state_t state;
+
+ if ((err = nwam_loc_read(event->event_object, 0, &loch))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_loc_handle_init_event: could not "
+ "read object '%s': %s", event->event_object,
+ nwam_strerror(err));
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
+ event->event_object)) != NULL) {
+ nwam_loc_free(object->nwamd_object_handle);
+ object->nwamd_object_handle = loch;
+ } else {
+ object = nwamd_object_init(NWAM_OBJECT_TYPE_LOC,
+ event->event_object, loch, NULL);
+ object->nwamd_object_state = NWAM_STATE_OFFLINE;
+ object->nwamd_object_aux_state =
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET;
+ }
+ manual_disabled = (loc_get_activation_mode(loch) ==
+ NWAM_ACTIVATION_MODE_MANUAL && !loc_is_enabled(loch));
+ state = object->nwamd_object_state;
+ nwamd_object_release(object);
+
+ /*
+ * If this location is ONLINE, and not manual and disabled (since in
+ * that case it was online but we've just set enabled = false as part
+ * of a disable action), then it is still active but refreshing.
+ * Change states to re-activate itself.
+ */
+ if (!manual_disabled && state == NWAM_STATE_ONLINE) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
+ event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_METHOD_RUNNING);
+ }
+}
+
+/* Handle loc finish event */
+void
+nwamd_loc_handle_fini_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+
+ nlog(LOG_DEBUG, "nwamd_loc_handle_fini_event(%s)",
+ event->event_object);
+
+ /* Don't disable the location, as this can enable the Automatic loc */
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
+ event->event_object)) == NULL) {
+ nlog(LOG_ERR, "nwamd_loc_handle_fini_event: "
+ "loc %s not found", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ nwamd_object_release_and_destroy(object);
+}
+
+void
+nwamd_loc_handle_action_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+
+ switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
+ case NWAM_ACTION_ENABLE:
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
+ event->event_object);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_loc_handle_action_event: "
+ "could not find location %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
+ nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: "
+ "location %s already online, nothing to do",
+ event->event_object);
+ nwamd_object_release(object);
+ return;
+ }
+ nwamd_object_release(object);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
+ event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_METHOD_RUNNING);
+ break;
+ case NWAM_ACTION_DISABLE:
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
+ event->event_object);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_loc_handle_action_event: "
+ "could not find location %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
+ nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: "
+ "location %s already disabled, nothing to do",
+ event->event_object);
+ nwamd_object_release(object);
+ return;
+ }
+ nwamd_object_release(object);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
+ event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ break;
+ case NWAM_ACTION_ADD:
+ case NWAM_ACTION_REFRESH:
+ nwamd_loc_handle_init_event(event);
+ break;
+ case NWAM_ACTION_DESTROY:
+ nwamd_loc_handle_fini_event(event);
+ break;
+ default:
+ nlog(LOG_INFO, "nwam_loc_handle_action_event: "
+ "unexpected action");
+ break;
+ }
+}
+
+void
+nwamd_loc_handle_state_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+ nwam_state_t new_state;
+ nwam_aux_state_t new_aux_state;
+
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
+ event->event_object)) == NULL) {
+ nlog(LOG_ERR, "nwamd_loc_handle_state_event: "
+ "state event for nonexistent loc %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
+ new_aux_state =
+ event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
+
+ if (new_state == object->nwamd_object_state &&
+ new_aux_state == object->nwamd_object_aux_state) {
+ nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: "
+ "loc %s already in state (%s , %s)",
+ object->nwamd_object_name,
+ nwam_state_to_string(new_state),
+ nwam_aux_state_to_string(new_aux_state));
+ nwamd_object_release(object);
+ return;
+ }
+
+ object->nwamd_object_state = new_state;
+ object->nwamd_object_aux_state = new_aux_state;
+
+ nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: changing state for loc "
+ "%s to (%s , %s)", object->nwamd_object_name,
+ nwam_state_to_string(object->nwamd_object_state),
+ nwam_aux_state_to_string(object->nwamd_object_aux_state));
+
+ nwamd_object_release(object);
+
+ /*
+ * State machine for location.
+ */
+ switch (new_state) {
+ case NWAM_STATE_OFFLINE_TO_ONLINE:
+ nwamd_loc_activate(event->event_object);
+ break;
+ case NWAM_STATE_ONLINE_TO_OFFLINE:
+ /*
+ * Don't need to deactivate current location - condition check
+ * will activate another.
+ */
+ nwamd_loc_check_conditions();
+ break;
+ case NWAM_STATE_DISABLED:
+ case NWAM_STATE_OFFLINE:
+ case NWAM_STATE_UNINITIALIZED:
+ case NWAM_STATE_MAINTENANCE:
+ case NWAM_STATE_DEGRADED:
+ default:
+ /* do nothing */
+ break;
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/logging.c b/usr/src/cmd/cmd-inet/lib/nwamd/logging.c
new file mode 100644
index 0000000000..ebf97bb710
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/logging.c
@@ -0,0 +1,104 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "util.h"
+
+/*
+ * logging.c - contains various logging functions.
+ */
+
+boolean_t debug = B_TRUE;
+
+/*
+ * This idea for having this function is so that you can drop a dtrace probe
+ * here and trace complete strings (not just those containing formatting). Its
+ * important that we actually format the debug strings so we could trace them
+ * even if we choose not to send them to syslog.
+ */
+static void
+log_out(int severity, const char *str)
+{
+ if (severity == LOG_DEBUG && !debug)
+ return;
+
+ syslog(severity, str);
+}
+
+static void
+log_format(int severity, const char *fmt, va_list ap, char *buf, int bufsize)
+{
+ int offset;
+ char vbuf[256];
+
+ if (buf == NULL) {
+ buf = vbuf;
+ bufsize = sizeof (vbuf);
+ }
+
+ offset = snprintf(buf, bufsize, "%d: ", pthread_self());
+ (void) vsnprintf(buf + offset, bufsize - offset, fmt, ap);
+
+ log_out(severity, buf);
+}
+
+/*
+ * This function takes a syslog severity and uses it to determine what to do
+ * with the message (currently send it to syslog).
+ */
+void
+nlog(int severity, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ log_format(severity, fmt, ap, NULL, 0);
+ va_end(ap);
+}
+
+void
+pfail(const char *fmt, ...)
+{
+ char *msg;
+ va_list ap;
+
+ msg = malloc(256);
+
+ va_start(ap, fmt);
+ log_format(LOG_ERR, fmt, ap, msg, 256);
+ va_end(ap);
+
+ if (msg == NULL)
+ msg = "ran out of memory exiting. see log.";
+
+ (void) puts(msg);
+ exit(EXIT_FAILURE);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/main.c b/usr/src/cmd/cmd-inet/lib/nwamd/main.c
index ab3a36d078..04e9c540c4 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/main.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/main.c
@@ -24,85 +24,70 @@
* Use is subject to license terms.
*/
-/*
- * nwamd - NetWork Auto-Magic Daemon
- */
-
+#include <errno.h>
#include <fcntl.h>
+#include <inetcfg.h>
+#include <libdllink.h>
+#include <libintl.h>
+#include <libnwam.h>
+#include <locale.h>
#include <priv.h>
#include <pthread.h>
-#include <pwd.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <syslog.h>
#include <unistd.h>
-#include <locale.h>
-#include <libintl.h>
-#include <errno.h>
-
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
-#define TIMESPECGT(x, y) ((x.tv_sec > y.tv_sec) || \
- ((x.tv_sec == y.tv_sec) && (x.tv_nsec > y.tv_nsec)))
+#include <libnwam.h>
+#include "conditions.h"
+#include "events.h"
+#include "llp.h"
+#include "ncp.h"
+#include "objects.h"
+#include "util.h"
-const char *OUR_FMRI = "svc:/network/physical:nwam";
-const char *OUR_PG = "nwamd";
+/*
+ * nwamd - NetWork Auto-Magic Daemon
+ */
boolean_t fg = B_FALSE;
-boolean_t shutting_down;
+dladm_handle_t dld_handle = NULL;
+boolean_t shutting_down = B_FALSE;
+
sigset_t original_sigmask;
static sigset_t sigwaitset;
-char zonename[ZONENAME_MAX];
-pthread_mutex_t machine_lock = PTHREAD_MUTEX_INITIALIZER;
-dladm_handle_t dld_handle = NULL;
+
+static void nwamd_refresh(void);
+static void graceful_shutdown(void);
/*
* nwamd
*
* This is the Network Auto-Magic daemon. For further high level information
* see the Network Auto-Magic project and the Approachability communities
- * on opensolaris.org, and nwamd(1M).
+ * on opensolaris.org, nwamd(1M), and the README in the source directory.
*
- * The general structure of the code is as a set of threads collecting
- * system events which are fed into a state machine which alters system
- * state based on configuration.
+ * The general structure of the code is as a set of event source threads
+ * which feed events into the event handling thread. Some of these events
+ * are internal-only (e.g UPGRADE), but some also involve propogation
+ * to external listeners (who register via a door call into the daemon).
*
* signal management
* Due to being threaded, a simple set of signal handlers would not work
- * very well for nwamd. Instead nwamd blocks signals at startup and
- * then starts a thread which sits in sigwait(2) waiting for signals.
- * When a signal is received the signal handling thread dispatches it.
- * It handles:
- * - shutting down, done by creating an event which is passed through the
- * system allowing the various subsystems to do any necessary cleanup.
- * - SIGALRM for timers.
- * - SIGHUP for instance refresh, which tells us to look up various
- * properties from SMF(5).
+ * very well for nwamd. Instead nwamd blocks signals in all but the
+ * signal handling thread at startup.
*
- * subprocess management
- * nwamd starts several different subprocesses to manage the system. Some
- * of those start other processes (e.g. `ifconfig <if> dhcp` ends up starting
- * dhcpagent if necessary). Due to the way we manage signals if we started
- * those up without doing anything special their signal mask would mostly
- * block signals. So we restore the signal mask when we start subprocesses.
- * This is especially important with respect to DHCP as later when we exit
- * we need to kill the dhcpagent process which we started; for details, see
- * the block comment in state_machine.c in its cleanup() function.
*/
/*
* In this file there are several utility functions which might otherwise
* belong in util.c, but since they are only called from main(), they can
* live here as static functions:
- * - syslog set-up
+ * - nlog set-up
* - daemonizing
* - looking up SMF(5) properties
* - signal handling
@@ -127,23 +112,17 @@ daemonize(void)
* setsid again, we make certain that we are not the session
* group leader and can never reacquire a controlling terminal.
*/
- if ((pid = fork()) == (pid_t)-1) {
- syslog(LOG_ERR, "fork 1 failed");
- exit(EXIT_FAILURE);
- }
+ if ((pid = fork()) == (pid_t)-1)
+ pfail("fork 1 failed");
if (pid != 0) {
(void) wait(NULL);
- dprintf("child %ld exited, daemonizing", pid);
+ nlog(LOG_DEBUG, "child %ld exited, daemonizing", pid);
_exit(0);
}
- if (setsid() == (pid_t)-1) {
- syslog(LOG_ERR, "setsid");
- exit(EXIT_FAILURE);
- }
- if ((pid = fork()) == (pid_t)-1) {
- syslog(LOG_ERR, "fork 2 failed");
- exit(EXIT_FAILURE);
- }
+ if (setsid() == (pid_t)-1)
+ pfail("setsid");
+ if ((pid = fork()) == (pid_t)-1)
+ pfail("fork 2 failed");
if (pid != 0) {
_exit(0);
}
@@ -151,112 +130,89 @@ daemonize(void)
(void) umask(022);
}
-/*
- * Look up nwamd property values and set daemon variables appropriately.
- * This function will be called on startup and via the signal handling
- * thread on receiving a HUP (which occurs when the nwam service is
- * refreshed).
- */
-static void
-lookup_daemon_properties(void)
-{
- boolean_t debug_set;
- uint64_t scan_interval;
- uint64_t idle_time;
- boolean_t strict_bssid_set;
-
- 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;
- if (lookup_boolean_property(OUR_PG, "strict_bssid",
- &strict_bssid_set) == 0)
- strict_bssid = strict_bssid_set;
- dprintf("Read daemon configuration properties.");
-}
-
/* ARGSUSED */
static void *
sighandler(void *arg)
{
- int sig, err;
- uint32_t now;
+ uint64_t propval;
+ int sig;
+ uid_t uid = getuid();
while (!shutting_down) {
sig = sigwait(&sigwaitset);
- dprintf("signal %d caught", sig);
+ nlog(LOG_DEBUG, "signal %s caught", strsignal(sig));
+
+ /*
+ * Signal handling is different if the Phase 1 manifest
+ * have not been yet been imported. The two if-statements
+ * below highlight this. Signals must be handled
+ * differently because there is no event handling thread
+ * and event queue.
+ *
+ * When manifest-import imports the Phase 1 manifest, it
+ * refreshes NWAM. The NWAM Phase 1 properties must be
+ * available. If not, NWAM receveid a signal too soon.
+ * "ncu_wait_time" is a Phase 1 SMF property that did not
+ * exist in Phase 0/0.5.
+ */
+ if (uid == 0 &&
+ nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_NCU_WAIT_TIME_PROP_NAME, &propval) != 0) {
+ nlog(LOG_ERR, "WARN: Phase 1 properties not available. "
+ "Ignoring signal ...");
+ continue;
+ }
+
+ /*
+ * The Phase 1 manifest has been imported. If the
+ * "version" property exists (it is added by nwamd upon
+ * upgrade to Phase 1), then it means that we have already
+ * successfully run nwam before. If it doesn't, then we
+ * arrived here just after the new manifest was imported.
+ * manifest-import refreshes nwam after the import. Exit
+ * nwamd so that svc.startd(1M) can start nwamd correctly
+ * as specified in the imported manifest.
+ */
+ if (uid == 0 &&
+ nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_VERSION_PROP_NAME, &propval) != 0) {
+ if (sig == SIGHUP) {
+ pfail("WARN: Phase 1 properties available, "
+ "but NWAM has not been upgraded. "
+ "Exiting to let svc.startd restart NWAM");
+ } else {
+ nlog(LOG_ERR, "WARN: Ignoring signal as NWAM "
+ "has not been upgraded yet");
+ continue;
+ }
+ }
+
switch (sig) {
- case SIGALRM:
- /*
- * We may have multiple interfaces with
- * scheduled timers; walk the list and
- * create a timer event for each one.
- */
- timer_expire = TIMER_INFINITY;
- now = NSEC_TO_SEC(gethrtime());
- check_interface_timers(now);
- check_door_life(now);
- break;
+ case SIGTHAW:
case SIGHUP:
/*
- * Refresh action - reread configuration properties.
+ * Resumed from suspend or refresh. Clear up all
+ * objects so their states start from scratch;
+ * then refresh().
*/
- lookup_daemon_properties();
- /*
- * Check if user restarted scanning.
- */
- if (scan == 0 && wlan_scan_interval != 0) {
- err = pthread_create(&scan, NULL,
- periodic_wireless_scan, NULL);
- if (err != 0) {
- syslog(LOG_NOTICE,
- "pthread_create wireless scan: %s",
- strerror(err));
- } else {
- dprintf("wireless scan thread: %d",
- scan);
- }
- }
+ nwamd_fini_enms();
+ nwamd_fini_ncus();
+ nwamd_fini_locs();
+ nwamd_refresh();
break;
- case SIGINT:
+ case SIGUSR1:
/*
- * Undocumented "print debug status" signal.
+ * Undocumented "log ncu list" signal.
*/
- print_llp_status();
- print_interface_status();
- print_wireless_status();
- break;
- case SIGTHAW:
- /*
- * It seems unlikely that this is helpful, but it can't
- * hurt: when waking up from a sleep, check if the
- * wireless interface is still viable. There've been
- * bugs in this area.
- */
- if (pthread_mutex_lock(&machine_lock) == 0) {
- if (link_layer_profile != NULL &&
- link_layer_profile->llp_type ==
- IF_WIRELESS) {
- wireless_verify(
- link_layer_profile->llp_lname);
- }
- (void) pthread_mutex_unlock(&machine_lock);
- }
+ nwamd_log_ncus();
break;
case SIGTERM:
- syslog(LOG_NOTICE, "%s received, shutting down",
+ nlog(LOG_DEBUG, "%s received, shutting down",
strsignal(sig));
- shutting_down = B_TRUE;
- if (!np_queue_add_event(EV_SHUTDOWN, NULL)) {
- dprintf("could not allocate shutdown event");
- cleanup();
- exit(EXIT_FAILURE);
- }
+ graceful_shutdown();
break;
default:
- syslog(LOG_NOTICE, "unexpected signal %s received; "
+ nlog(LOG_DEBUG, "unexpected signal %s received, "
"ignoring", strsignal(sig));
break;
}
@@ -271,136 +227,195 @@ init_signalhandling(void)
pthread_t sighand;
int err;
- /*
- * Construct the set of signals that we explicitly want
- * to deal with. These will be blocked now, while we're
- * still single-threaded; this block will be inherited by
- * all the threads we create. The signal handling thread
- * will then sigwait() this same set of signals, and will
- * thus receive and process any that are sent to the process.
- */
- (void) sigemptyset(&sigwaitset);
- (void) sigaddset(&sigwaitset, SIGHUP);
- (void) sigaddset(&sigwaitset, SIGINT);
- (void) sigaddset(&sigwaitset, SIGALRM);
- (void) sigaddset(&sigwaitset, SIGTERM);
- (void) sigaddset(&sigwaitset, SIGTHAW);
- (void) pthread_sigmask(SIG_BLOCK, &sigwaitset, &original_sigmask);
-
- /*
- * now start the signal handling thread...
- */
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err = pthread_create(&sighand, &attr, sighandler, NULL)) {
- syslog(LOG_ERR, "pthread_create system: %s", strerror(err));
+ nlog(LOG_ERR, "pthread_create system: %s", strerror(err));
exit(EXIT_FAILURE);
} else {
- dprintf("signal handler thread: %d", sighand);
+ nlog(LOG_DEBUG, "signal handler thread: %d", sighand);
}
(void) pthread_attr_destroy(&attr);
}
+/*
+ * Construct the set of signals that we explicitly want to deal with.
+ * We block these while we're still single-threaded; this block will
+ * be inherited by all the threads we create. When we are ready to
+ * start handling signals, we will start the signal handling thread,
+ * which will sigwait() this same set of signals, and will thus receive
+ * and handle any that are sent to the process.
+ */
static void
-change_user_set_privs(void)
+block_signals(void)
{
- priv_set_t *priv_set;
+ (void) sigemptyset(&sigwaitset);
+ (void) sigaddset(&sigwaitset, SIGHUP);
+ (void) sigaddset(&sigwaitset, SIGUSR1);
+ (void) sigaddset(&sigwaitset, SIGUSR2);
+ (void) sigaddset(&sigwaitset, SIGTERM);
+ (void) sigaddset(&sigwaitset, SIGTHAW);
+ (void) pthread_sigmask(SIG_BLOCK, &sigwaitset, &original_sigmask);
+}
- priv_set = priv_allocset();
- if (getppriv(PRIV_PERMITTED, priv_set) == -1) {
- dprintf("getppriv %s", strerror(errno));
+/*
+ * Look up nwamd property values and set daemon variables appropriately.
+ * This function will be called on startup and via the signal handling
+ * thread on receiving a HUP (which occurs when the nwam service is
+ * refreshed).
+ */
+static void
+lookup_daemon_properties(void)
+{
+ char *active_ncp_tmp;
+ char *scan_level_tmp;
+
+ (void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
+ OUR_DEBUG_PROP_NAME, &debug);
+ (void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
+ OUR_AUTOCONF_PROP_NAME, &wireless_autoconf);
+ (void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
+ OUR_STRICT_BSSID_PROP_NAME, &wireless_strict_bssid);
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if ((active_ncp_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL ||
+ nwamd_lookup_string_property(OUR_FMRI, OUR_PG,
+ OUR_ACTIVE_NCP_PROP_NAME, active_ncp_tmp, NWAM_MAX_NAME_LEN) != 0) {
+ (void) strlcpy(active_ncp, NWAM_NCP_NAME_AUTOMATIC,
+ NWAM_MAX_NAME_LEN);
} else {
- char *p;
-
- p = priv_set_to_str(priv_set, ',', 0);
- dprintf("started with privs %s", p != NULL ? p : "Unknown");
- free(p);
+ (void) strlcpy(active_ncp, active_ncp_tmp, NWAM_MAX_NAME_LEN);
}
-
- /* always start with the basic set */
- priv_basicset(priv_set);
- (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF);
- (void) priv_addset(priv_set, PRIV_FILE_DAC_READ);
- (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);
- (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG);
- (void) priv_addset(priv_set, PRIV_SYS_RESOURCE);
-
- if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
- syslog(LOG_ERR, "setppriv inheritable: %m");
- priv_freeset(priv_set);
- exit(EXIT_FAILURE);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ free(active_ncp_tmp);
+
+ if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_CONDITION_CHECK_INTERVAL_PROP_NAME,
+ &condition_check_interval) != 0)
+ condition_check_interval = CONDITION_CHECK_INTERVAL_DEFAULT;
+
+ if ((scan_level_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL ||
+ nwamd_lookup_string_property(OUR_FMRI, OUR_PG,
+ OUR_WIRELESS_SCAN_LEVEL_PROP_NAME, scan_level_tmp,
+ NWAM_MAX_NAME_LEN) != 0) {
+ wireless_scan_level = WIRELESS_SCAN_LEVEL_DEFAULT;
+ } else {
+ if (dladm_wlan_str2strength(scan_level_tmp,
+ &wireless_scan_level) != DLADM_STATUS_OK)
+ wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK;
}
+ free(scan_level_tmp);
- if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) {
- syslog(LOG_ERR, "setppriv permitted: %m");
- priv_freeset(priv_set);
- exit(EXIT_FAILURE);
- }
+ if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_WIRELESS_SCAN_INTERVAL_PROP_NAME, &wireless_scan_interval) != 0)
+ wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT;
- if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
- syslog(LOG_ERR, "setppriv effective: %m");
- priv_freeset(priv_set);
- exit(EXIT_FAILURE);
- }
+ if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
+ OUR_NCU_WAIT_TIME_PROP_NAME, &ncu_wait_time) != 0)
+ ncu_wait_time = NCU_WAIT_TIME_DEFAULT;
- priv_freeset(priv_set);
+ nlog(LOG_DEBUG, "Read daemon configuration properties.");
}
+/*
+ * Re-read the SMF properties.
+ * Reset ncu priority group (since the NCUs will have to walk
+ * through their state machines again) and schedule a check
+ * Re-read objects from libnwam.
+ * Also, run condition checking for locations and ENMs.
+ */
static void
-init_machine_mutex(void)
+nwamd_refresh(void)
{
- pthread_mutexattr_t attrs;
+ lookup_daemon_properties();
- (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);
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ current_ncu_priority_group = INVALID_PRIORITY_GROUP;
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ nwamd_init_ncus();
+ nwamd_init_enms();
+ nwamd_init_locs();
+
+ nwamd_create_ncu_check_event(0);
+ nwamd_create_triggered_condition_check_event(0);
+}
+
+static void
+graceful_shutdown(void)
+{
+ nwamd_event_t event;
+
+ shutting_down = B_TRUE;
+ nwamd_event_sources_fini();
+ nwamd_door_fini();
+ nwamd_fini_enms();
+ nwamd_fini_ncus();
+ nwamd_fini_locs();
+
+ event = nwamd_event_init_shutdown();
+ if (event == NULL)
+ pfail("nwamd could not create shutdown event, exiting");
+ nwamd_event_enqueue(event);
}
int
main(int argc, char *argv[])
{
int c;
- int scan_lev;
- struct np_event *e;
- enum np_event_type etype;
+ uint64_t version;
+ nwamd_event_t event;
+ dladm_status_t rc;
+ uid_t uid = getuid();
+
+ /*
+ * Block the signals we care about (and which might cause us to
+ * exit based on default disposition) until we're ready to start
+ * handling them properly...see init_signalhandling() below.
+ */
+ block_signals();
+
+ /*
+ * In the first boot after upgrade, manifest-import hasn't run yet.
+ * Thus, the NWAM Phase 1 SMF properties are not available yet. In
+ * Phase 0/0.5, nwamd ran as root. Thus in this case, just
+ * daemonize() nwamd. When manifest-import imports the Phase 1
+ * manifest, NWAM is refreshed. Also, setup signal handling to
+ * catch the refresh signal. Kill nwamd then and let svc.startd(1M)
+ * start nwamd again (this time correctly as netadm).
+ */
+ if (uid == 0) {
+ nlog(LOG_ERR, "Warning: Phase 1 properties not available yet");
+
+ daemonize();
+ init_signalhandling();
+ (void) pause();
+
+ return (EXIT_SUCCESS);
+ }
+
+ if (uid != UID_NETADM) {
+ /*
+ * This shouldn't happen normally. On upgrade the service might
+ * need reloading.
+ */
+ pfail("nwamd should run as uid %d, not uid %d\n", UID_NETADM,
+ uid);
+ }
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
- shutting_down = B_FALSE;
start_logging();
- syslog(LOG_INFO, "nwamd pid %d started", getpid());
+ nlog(LOG_INFO, "nwamd pid %d started", getpid());
while ((c = getopt(argc, argv, "fs:")) != -1) {
switch (c) {
case 'f':
fg = B_TRUE;
break;
- case 's':
- scan_lev = atoi(optarg);
- if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK &&
- scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) {
- wireless_scan_level = scan_lev;
- } else {
- syslog(LOG_ERR, "invalid signal "
- "strength: %s", optarg);
- }
- break;
default:
- syslog(LOG_ERR, "unrecognized option %c",
+ nlog(LOG_ERR, "unrecognized option %c",
optopt);
break;
}
@@ -408,65 +423,101 @@ main(int argc, char *argv[])
lookup_daemon_properties();
+ if (!fg)
+ daemonize();
+
/*
- * The dladm handle *must* be opened before privileges are dropped
- * by nwamd. The device privilege requirements from
- * /etc/security/device_policy may not be loaded yet. These are
- * loaded by svc:/system/filesystem/root, which comes online after
- * svc:/network/physical.
+ * The dladm handle *must* be opened before privileges are dropped.
+ * The device privilege requirements, which are stored in
+ * /etc/security/device_policy, may not be loaded yet, as that's
+ * done by svc:/system/filesystem/root. If they are not loaded,
+ * then one must have *all* privs in order to open /dev/dld, which
+ * is one of the steps performed in dladm_open().
*/
- if (dladm_open(&dld_handle) != DLADM_STATUS_OK) {
- syslog(LOG_ERR, "failed to open dladm handle");
- exit(EXIT_FAILURE);
+ rc = dladm_open(&dld_handle);
+ if (rc != DLADM_STATUS_OK) {
+ char status_str[DLADM_STRSIZE];
+ (void) dladm_status2str(rc, status_str);
+ pfail("failed to open dladm handle: %s", status_str);
}
- change_user_set_privs();
+ /*
+ * Handle upgrade of legacy config. Absence of version property
+ * (which did not exist in phase 0 or 0.5) is the indication that
+ * we need to upgrade to phase 1 (version 1).
+ */
+ if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, OUR_VERSION_PROP_NAME,
+ &version) != 0)
+ nwamd_handle_upgrade(NULL);
- if (!fg)
- daemonize();
+ /*
+ * Initialize lists handling internal representations of objects.
+ */
+ nwamd_object_lists_init();
- initialize_llp();
+ /*
+ * Start the event handling thread before starting event sources,
+ * including signal handling, so we are ready to handle incoming
+ * events.
+ */
+ nwamd_event_queue_init();
init_signalhandling();
- initialize_wireless();
-
- lookup_zonename(zonename, sizeof (zonename));
+ /* Enqueue init event */
+ event = nwamd_event_init_init();
+ if (event == NULL)
+ pfail("nwamd could not create init event, exiting");
+ nwamd_event_enqueue(event);
+ /*
+ * Collect initial user configuration.
+ */
- init_machine_mutex();
+ /*
+ * Walk the physical interfaces and update the Automatic NCP to
+ * contain the IP and link NCUs for the interfaces that exist in
+ * the system.
+ */
+ nwamd_walk_physical_configuration();
- initialize_interfaces();
+ /*
+ * We should initialize the door at the point that we can respond to
+ * user requests about the system but before we start actually process
+ * state changes or effecting the system.
+ */
+ nwamd_door_init();
- llp_parse_config();
+ /*
+ * Initialize data objects.
+ *
+ * Enabling an NCP involves refreshing nwam, which initializes the
+ * objects (ncu, enm, loc, known wlan). Thus, no need to
+ * explicitly initialize these objects here. The refresh also
+ * enqueues and NCU activation checking event. Location and ENM
+ * condition checking are triggered by changes in NCU states.
+ */
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (nwamd_ncp_action(active_ncp, NWAM_ACTION_ENABLE) != 0)
+ pfail("Initial enable failed for active NCP %s", active_ncp);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
- initialize_door();
+ /*
+ * Enqueue an event to start periodic checking of activation conditions.
+ */
+ nwamd_create_timed_condition_check_event();
- (void) start_event_collection();
+ nwamd_drop_unneeded_privs();
+ /*
+ * Start the various agents (hooks on fds, threads) which collect events
+ */
+ nwamd_event_sources_init();
- while ((e = np_queue_get_event()) != NULL) {
+ /*
+ * nwamd_event_handler() only returns on shutdown.
+ */
+ nwamd_event_handler();
- 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;
- }
- syslog(LOG_DEBUG, "terminating routing and scanning threads");
- (void) pthread_cancel(routing);
- (void) pthread_join(routing, NULL);
- if (scan != 0) {
- (void) pthread_cancel(scan);
- (void) pthread_join(scan, NULL);
- }
dladm_close(dld_handle);
- syslog(LOG_INFO, "nwamd shutting down");
+
return (EXIT_SUCCESS);
}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncp.c b/usr/src/cmd/cmd-inet/lib/nwamd/ncp.c
new file mode 100644
index 0000000000..55bbbf707c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncp.c
@@ -0,0 +1,706 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <libdllink.h>
+#include <libdlstat.h>
+#include <libnwam.h>
+#include <libscf.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <values.h>
+
+#include "conditions.h"
+#include "events.h"
+#include "objects.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "util.h"
+
+/*
+ * ncp.c - handles NCP actions.
+ */
+
+char active_ncp[NWAM_MAX_NAME_LEN];
+nwam_ncp_handle_t active_ncph = NULL;
+int64_t current_ncu_priority_group = INVALID_PRIORITY_GROUP;
+/*
+ * active_ncp_mutex protects active_ncp, active_ncph and
+ * current_ncu_priority_group.
+ */
+pthread_mutex_t active_ncp_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * The variable ncu_wait_time specifies how long to wait to obtain a
+ * DHCP lease before giving up on that NCU and moving on to the next/lower
+ * priority-group.
+ */
+uint64_t ncu_wait_time = NCU_WAIT_TIME_DEFAULT;
+
+/*
+ * Specifies if this is the first time the NCP has been enabled. True
+ * on startup so that we can differentiate between when we start up
+ * with a given NCP versus when we are asked to reenable it.
+ */
+boolean_t initial_ncp_enable = B_TRUE;
+
+/*
+ * nwamd_ncp_handle_enable_event() should be called in the event handling
+ * loop in response to an _ENABLE event, triggered as a result of an
+ * nwam_ncp_enable() call from a libnwam consumer. To enable the new NCP,
+ * we first call nwamd_fini_ncus() on the old NCP. This results in enqueueing
+ * of a set of _FINI events for each NCU. These events are handled and in
+ * order to tear down config, (online*, uninitialized) state change events
+ * are created and consumed directly by the fini event handler (these events
+ * are not enqueued as this would result in state events for the old NCP
+ * appearing after the new NCP has been enabled. After the _FINI events are
+ * enqueued, we enqueue an NCP _OBJECT_STATE event for the new NCP. Since
+ * it is enqueued after the _FINI events, we are guaranteed no events for the
+ * old NCP will appear after the new NCP is activated.
+ */
+void
+nwamd_ncp_handle_enable_event(nwamd_event_t event)
+{
+ char *new_ncp = event->event_object;
+ nwam_ncp_handle_t new_ncph;
+ nwam_error_t err;
+
+ if (new_ncp[0] == '\0')
+ return;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (strcmp(active_ncp, new_ncp) == 0 && !initial_ncp_enable) {
+ nlog(LOG_DEBUG, "nwamd_ncp_handle_enable_event: "
+ "%s is already active", new_ncp);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ return;
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ nlog(LOG_DEBUG, "nwamd_ncp_handle_enable_event: activating NCP %s",
+ new_ncp);
+
+ /*
+ * To activate new NCP, run nwamd_fini_ncus(), reset the active
+ * priority-group, set the active_ncp property and refresh the
+ * daemon. The refresh action will trigger a re-read of the NCUs
+ * for the activated NCP.
+ */
+
+ nwamd_fini_ncus();
+
+ err = nwam_ncp_read(new_ncp, 0, &new_ncph);
+ switch (err) {
+ case NWAM_ENTITY_NOT_FOUND:
+ err = nwam_ncp_create(new_ncp, 0, &new_ncph);
+ break;
+ case NWAM_SUCCESS:
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: error %s",
+ nwam_strerror(err));
+ return;
+ }
+ nwam_ncp_free(new_ncph);
+
+ if (err == NWAM_SUCCESS) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCP, new_ncp,
+ NWAM_STATE_ONLINE, NWAM_AUX_STATE_ACTIVE);
+ } else {
+ nlog(LOG_ERR, "nwamd_ncp_handle_enable_event: error %s",
+ nwam_strerror(err));
+ return;
+ }
+}
+
+void
+nwamd_ncp_handle_action_event(nwamd_event_t event)
+{
+ switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
+ case NWAM_ACTION_ENABLE:
+ nwamd_ncp_handle_enable_event(event);
+ break;
+ case NWAM_ACTION_ADD:
+ case NWAM_ACTION_DESTROY:
+ /* nothing to do */
+ break;
+ default:
+ nlog(LOG_INFO, "nwam_ncp_handle_action_event: "
+ "unexpected action");
+ nwamd_event_do_not_send(event);
+ break;
+ }
+}
+
+/*
+ * The only state events we create are (online, active) events which are
+ * generated as part of an NCP enable action (see above).
+ */
+void
+nwamd_ncp_handle_state_event(nwamd_event_t event)
+{
+ char *new_ncp = event->event_object;
+ nwam_ncp_handle_t new_ncph, old_ncph;
+ nwam_error_t err;
+
+ /* The NCP to be activated should always exist. */
+ if ((err = nwam_ncp_read(new_ncp, 0, &new_ncph)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncp_handle_state_event: "
+ "cannot read NCP %s: : %s", new_ncp, nwam_strerror(err));
+ nwamd_event_do_not_send(event);
+ return;
+ }
+
+ /*
+ * To activate new NCP, reset the active priority-group, set the
+ * active_ncp property and refresh the daemon. The refresh action will
+ * trigger a re-read of the NCUs for the activated NCP.
+ */
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ old_ncph = active_ncph;
+ active_ncph = new_ncph;
+ nwam_ncp_free(old_ncph);
+ current_ncu_priority_group = INVALID_PRIORITY_GROUP;
+ (void) strlcpy(active_ncp, event->event_object,
+ sizeof (active_ncp));
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ (void) nwamd_set_string_property(OUR_FMRI, OUR_PG,
+ OUR_ACTIVE_NCP_PROP_NAME, new_ncp);
+ (void) smf_refresh_instance(OUR_FMRI);
+ initial_ncp_enable = B_FALSE;
+}
+
+int
+nwamd_ncp_action(const char *ncp, nwam_action_t action)
+{
+ nwamd_event_t event = nwamd_event_init_object_action
+ (NWAM_OBJECT_TYPE_NCP, ncp, NULL, action);
+ if (event == NULL)
+ return (1);
+ nwamd_event_enqueue(event);
+ return (0);
+}
+
+/*
+ * Below this point are routines handling NCU prioritization
+ * policy for the active NCP.
+ */
+
+struct priority_group_cbarg {
+ uint64_t minpriority;
+ uint64_t currpriority;
+ boolean_t found;
+};
+
+/* Callback used to find next pg in NCP that is >= start_pg */
+static int
+find_next_priority_group_cb(nwamd_object_t object, void *data)
+{
+ struct priority_group_cbarg *cbarg = data;
+ uint64_t priority;
+ nwamd_ncu_t *ncu = object->nwamd_object_data;
+
+ if (ncu->ncu_node.u_link.nwamd_link_activation_mode !=
+ NWAM_ACTIVATION_MODE_PRIORITIZED)
+ return (0);
+
+ priority = ncu->ncu_node.u_link.nwamd_link_priority_group;
+
+ if (priority >= cbarg->minpriority && priority < cbarg->currpriority) {
+ cbarg->found = B_TRUE;
+ cbarg->currpriority = priority;
+ }
+ return (0);
+}
+
+
+/* Set current_pg to next pg in NCP that is >= start_pg */
+boolean_t
+nwamd_ncp_find_next_priority_group(int64_t minpriority,
+ int64_t *nextpriorityp)
+{
+ struct priority_group_cbarg cbarg;
+
+ cbarg.minpriority = minpriority;
+ cbarg.currpriority = MAXINT;
+ cbarg.found = B_FALSE;
+
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
+ find_next_priority_group_cb, &cbarg);
+
+ if (cbarg.found) {
+ nlog(LOG_DEBUG, "nwamd_ncp_find_next_priority_group: "
+ "next priority group >= %lld is %lld",
+ minpriority, cbarg.currpriority);
+ *nextpriorityp = cbarg.currpriority;
+ return (B_TRUE);
+ } else {
+ nlog(LOG_DEBUG, "nwamd_ncp_find_next_priority_group: "
+ "no priority groups >= %lld exist", minpriority);
+ return (B_FALSE);
+ }
+}
+
+/*
+ * Struct for walking NCUs in the selected priority group. We count
+ * how many of the exclusive, all and shared NCUs are online, and
+ * if activate_or_deactivate is true, we either activate or deactivate
+ * (depending on the value of activate) offline/online NCUs.
+ */
+struct nwamd_ncu_check_walk_arg {
+ boolean_t manual; /* enable manual NCUs only */
+ int64_t priority_group; /* interested priority-group for this walk */
+ uint64_t exclusive_ncus;
+ uint64_t exclusive_online_ncus;
+ uint64_t shared_ncus;
+ uint64_t shared_online_ncus;
+ uint64_t all_ncus;
+ uint64_t all_online_ncus;
+ boolean_t activate_or_deactivate;
+ boolean_t activate;
+};
+
+/*
+ * This function serves a number of purposes:
+ * - it supports activation/deactivation of manual NCUs in the current NCP
+ * (when wa->manual is true, wa->activate determines if we activate or
+ * deactivate the current NCU)
+ * - it supports checking/activation of a particular priority group in
+ * the active NCP. This works as follows:
+ *
+ * Count up numbers of exclusive, shared and all NCUs, and how many of each
+ * are online. If an NCU is waiting for IP address to be assigned, it is
+ * also considered online. If activate_or_deactivate is true, we also
+ * either activate (if activate is true) or deactivate prioritized NCUs
+ * that are offline or online.
+ */
+static int
+nwamd_ncu_check_or_activate(nwamd_object_t object, void *data)
+{
+ struct nwamd_ncu_check_walk_arg *wa = data;
+ nwamd_ncu_t *ncu;
+ uint64_t priority_group, priority_mode;
+ nwamd_object_t if_obj;
+ nwam_state_t state, if_state;
+ nwam_aux_state_t aux_state, if_aux_state;
+ char *name;
+
+ state = object->nwamd_object_state;
+ aux_state = object->nwamd_object_aux_state;
+ name = object->nwamd_object_name;
+ ncu = object->nwamd_object_data;
+
+ /* skip NCUs in UNINITIALIZED state */
+ if (state == NWAM_STATE_UNINITIALIZED) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "skipping uninitialized ncu %s", name);
+ return (0);
+ }
+ if (!wa->manual && wa->priority_group == INVALID_PRIORITY_GROUP)
+ return (0);
+
+ if (ncu->ncu_type != NWAM_NCU_TYPE_LINK) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "skipping interface NCU %s", name);
+ return (0);
+ }
+ if (!wa->manual && ncu->ncu_node.u_link.nwamd_link_activation_mode !=
+ NWAM_ACTIVATION_MODE_PRIORITIZED) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "skipping non-prioritized NCU %s", name);
+ return (0);
+ }
+ if (wa->manual && ncu->ncu_node.u_link.nwamd_link_activation_mode !=
+ NWAM_ACTIVATION_MODE_MANUAL) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "skipping non-manual NCU %s", name);
+ return (0);
+ }
+
+ priority_group = ncu->ncu_node.u_link.nwamd_link_priority_group;
+ priority_mode = ncu->ncu_node.u_link.nwamd_link_priority_mode;
+ /* Only work with NCUs in the requested priority-group */
+ if (!wa->manual && priority_group != wa->priority_group) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "skipping NCU %s in different priority-group", name);
+ return (0);
+ }
+ /* Get the state of the corresponding interface NCU */
+ if ((if_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE,
+ ncu->ncu_name)) == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_check_or_activate: "
+ "interface NCU of %s not found, skipping", name);
+ return (0);
+ }
+ if_state = if_obj->nwamd_object_state;
+ if_aux_state = if_obj->nwamd_object_aux_state;
+ nwamd_object_release(if_obj);
+
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: %s ncu %s",
+ wa->activate_or_deactivate ?
+ (wa->activate ? "activating" : "deactivating") :
+ "checking", name);
+
+ if (wa->manual) {
+ if (wa->activate_or_deactivate && wa->activate) {
+ if (state == NWAM_STATE_OFFLINE && ncu->ncu_enabled) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "moving NCU %s to offline* from offline",
+ name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ }
+ if (state != NWAM_STATE_DISABLED &&
+ !ncu->ncu_enabled) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "moving NCU %s to online* (disabling)",
+ name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ }
+ }
+ return (0);
+ }
+ switch (priority_mode) {
+ case NWAM_PRIORITY_MODE_EXCLUSIVE:
+ wa->exclusive_ncus++;
+ if (state == NWAM_STATE_ONLINE &&
+ (if_state == NWAM_STATE_ONLINE ||
+ if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
+ wa->exclusive_online_ncus++;
+
+ /*
+ * For exclusive NCUs, we activate offline NCUs as long
+ * as no other exclusive NCUs are active.
+ */
+ if (wa->activate_or_deactivate && wa->activate) {
+ if (state == NWAM_STATE_OFFLINE &&
+ wa->exclusive_online_ncus == 0) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "moving NCU %s to offline* from offline",
+ name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ }
+ }
+ if (wa->activate_or_deactivate && !wa->activate) {
+ if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "deactivating NCU %s", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ }
+ /*
+ * If we are activating or checking the priority group and
+ * too many exclusive NCUs are online, take this NCU down.
+ */
+ if ((wa->activate_or_deactivate && wa->activate) ||
+ !wa->activate_or_deactivate) {
+ if (state == NWAM_STATE_ONLINE &&
+ if_state == NWAM_STATE_ONLINE &&
+ wa->exclusive_online_ncus > 1) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "moving NCU %s to online* since another "
+ "NCU is already active",
+ name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ }
+ break;
+ case NWAM_PRIORITY_MODE_SHARED:
+ wa->shared_ncus++;
+ if (state == NWAM_STATE_ONLINE &&
+ (if_state == NWAM_STATE_ONLINE ||
+ if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
+ wa->shared_online_ncus++;
+
+ if (wa->activate_or_deactivate && wa->activate) {
+ if (state == NWAM_STATE_OFFLINE) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "activating NCU %s", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ }
+ }
+ if (wa->activate_or_deactivate && !wa->activate) {
+ if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "deactivating NCU %s", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ }
+ break;
+ case NWAM_PRIORITY_MODE_ALL:
+ wa->all_ncus++;
+ if (state == NWAM_STATE_ONLINE &&
+ (if_state == NWAM_STATE_ONLINE ||
+ if_aux_state == NWAM_AUX_STATE_IF_WAITING_FOR_ADDR))
+ wa->all_online_ncus++;
+
+ /*
+ * For "all" NCUs, activate/deactivate all offline/online
+ * NCUs.
+ */
+ if (wa->activate_or_deactivate && wa->activate) {
+ if (state == NWAM_STATE_OFFLINE) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "activating NCU %s", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ }
+ }
+ if (wa->activate_or_deactivate && !wa->activate) {
+ if (aux_state != NWAM_AUX_STATE_CONDITIONS_NOT_MET) {
+ nlog(LOG_DEBUG, "nwamd_ncu_check_or_activate: "
+ "deactivating NCU %s", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ name, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ }
+
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_ncu_check_or_activate: "
+ "invalid priority-mode");
+ break;
+ }
+
+ return (0);
+}
+
+void
+nwamd_ncp_activate_priority_group(int64_t priority)
+{
+ struct nwamd_ncu_check_walk_arg wa;
+ nwamd_event_t check_event, priority_event;
+
+ if (priority == INVALID_PRIORITY_GROUP)
+ return;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (priority == current_ncu_priority_group) {
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ return;
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ nlog(LOG_DEBUG, "nwamd_ncp_activate_priority_group: "
+ "activating priority group %lld", priority);
+
+ wa.manual = B_FALSE;
+ wa.priority_group = priority;
+ wa.exclusive_ncus = 0;
+ wa.exclusive_online_ncus = 0;
+ wa.shared_ncus = 0;
+ wa.shared_online_ncus = 0;
+ wa.all_ncus = 0;
+ wa.all_online_ncus = 0;
+ wa.activate_or_deactivate = B_TRUE;
+ wa.activate = B_TRUE;
+
+ if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
+ nwamd_ncu_check_or_activate, &wa) != 0) {
+ nlog(LOG_ERR, "nwamd_ncp_activate_priority_group: "
+ "nwamd_walk_objects() failed");
+ return;
+ }
+
+ /*
+ * Enqueue event to update current_ncu_priority_group and send to
+ * any event listeners.
+ */
+ priority_event = nwamd_event_init_priority_group_change(priority);
+ if (priority_event == NULL)
+ return;
+ nwamd_event_enqueue(priority_event);
+
+ /*
+ * Now we've activated a new priority group, enqueue an event
+ * to check up on the state of this priority group.
+ */
+ check_event = nwamd_event_init_ncu_check();
+ if (check_event == NULL)
+ return;
+ nwamd_event_enqueue_timed(check_event, ncu_wait_time);
+}
+
+void
+nwamd_ncp_deactivate_priority_group(int64_t priority)
+{
+ struct nwamd_ncu_check_walk_arg wa;
+
+ if (priority == INVALID_PRIORITY_GROUP)
+ return;
+
+ nlog(LOG_DEBUG, "nwamd_ncp_deactivate_priority_group: "
+ "deactivating priority group %lld", priority);
+
+ wa.manual = B_FALSE;
+ wa.priority_group = priority;
+ wa.exclusive_ncus = 0;
+ wa.exclusive_online_ncus = 0;
+ wa.shared_ncus = 0;
+ wa.shared_online_ncus = 0;
+ wa.all_ncus = 0;
+ wa.all_online_ncus = 0;
+ wa.activate_or_deactivate = B_TRUE;
+ wa.activate = B_FALSE;
+
+ if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
+ nwamd_ncu_check_or_activate, &wa) != 0) {
+ nlog(LOG_ERR, "nwamd_ncp_deactivate_priority_group: "
+ "nwamd_walk_objects() failed");
+ return;
+ }
+}
+
+/*
+ * This function deactivates all priority groups at level 'priority' and lower
+ * (which is, numerically, all priorities >= priority).
+ */
+void
+nwamd_ncp_deactivate_priority_group_all(int64_t priority)
+{
+ if (priority == INVALID_PRIORITY_GROUP)
+ return;
+
+ nlog(LOG_DEBUG, "nwamd_ncp_deactivate_priority_group_all: "
+ "deactivating priority group less than or equal to %lld", priority);
+
+ do {
+ nwamd_ncp_deactivate_priority_group(priority);
+ } while (nwamd_ncp_find_next_priority_group(priority + 1, &priority));
+}
+
+/*
+ * Returns 'true' if it found the highest priority group no higher then what
+ * is passed that should be activated and sets *priority to that.
+ */
+boolean_t
+nwamd_ncp_check_priority_group(int64_t *priority)
+{
+ struct nwamd_ncu_check_walk_arg wa;
+ boolean_t conditions_met = B_FALSE;
+
+ nlog(LOG_DEBUG, "nwamd_ncp_check_priority_group: "
+ "checking priority group %lld", *priority);
+
+ if (*priority == INVALID_PRIORITY_GROUP) {
+ if (!nwamd_ncp_find_next_priority_group(0, priority))
+ return (B_FALSE);
+ }
+
+ while (!conditions_met) {
+ (void) memset(&wa, 0, sizeof (wa));
+ wa.manual = B_FALSE;
+ wa.priority_group = *priority;
+ wa.activate_or_deactivate = B_FALSE;
+
+ if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
+ nwamd_ncu_check_or_activate, &wa) != 0) {
+ nlog(LOG_ERR, "nwamd_ncp_check_priority_group: "
+ "nwamd_walk_objects() failed");
+ return (B_FALSE);
+ }
+
+ /*
+ * Are activation conditons satisifed? In other words:
+ * - exactly one of the exclusive NCUs is online
+ * - 1 or more shared NCUs are online
+ * - all of the all NCUs are online.
+ * If any of these is untrue, conditions are not satisfied.
+ */
+ conditions_met = B_TRUE;
+ if (wa.exclusive_ncus > 0 && wa.exclusive_online_ncus != 1)
+ conditions_met = B_FALSE;
+ if (wa.shared_ncus > 0 && wa.shared_online_ncus == 0)
+ conditions_met = B_FALSE;
+ if (wa.all_ncus > 0 && wa.all_ncus != wa.all_online_ncus)
+ conditions_met = B_FALSE;
+ if (wa.exclusive_online_ncus == 0 &&
+ wa.shared_online_ncus == 0 && wa.all_online_ncus == 0)
+ conditions_met = B_FALSE;
+
+ if (conditions_met) {
+ return (B_TRUE);
+ } else {
+ /*
+ * If there is a next pg, activate it. If not, do
+ * nothing - we're stuck here unless an event occurs
+ * for our or a higher pg.
+ */
+ if (!nwamd_ncp_find_next_priority_group
+ (wa.priority_group + 1, priority)) {
+ nlog(LOG_DEBUG, "ran out of prio groups");
+ return (B_FALSE);
+ }
+ }
+ }
+ return (B_FALSE);
+}
+
+void
+nwamd_ncp_activate_manual_ncus(void)
+{
+ struct nwamd_ncu_check_walk_arg wa;
+
+ nlog(LOG_DEBUG, "nwamd_ncp_activate_manual_ncus: activating NCUs");
+
+ wa.manual = B_TRUE;
+ wa.activate_or_deactivate = B_TRUE;
+ wa.activate = B_TRUE;
+
+ if (nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU,
+ nwamd_ncu_check_or_activate, &wa) != 0) {
+ nlog(LOG_ERR, "nwamd_ncp_activate_manual_ncus: "
+ "nwamd_walk_objects() failed");
+ return;
+ }
+}
+
+void
+nwamd_create_ncu_check_event(uint64_t when)
+{
+ nwamd_event_t check_event = nwamd_event_init_ncu_check();
+ if (check_event != NULL)
+ nwamd_event_enqueue_timed(check_event, when);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncp.h b/usr/src/cmd/cmd-inet/lib/nwamd/ncp.h
new file mode 100644
index 0000000000..c401e8d2d0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncp.h
@@ -0,0 +1,59 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NCP_H
+#define _NCP_H
+
+#include <inetcfg.h>
+#include <libdladm.h>
+#include <libdlpi.h>
+#include <libdlwlan.h>
+#include <libnwam.h>
+#include <libuutil.h>
+#include <pthread.h>
+
+/* Time between NCU checks */
+#define NCU_WAIT_TIME_DEFAULT 120
+
+/* Value of priority-group at start and reset */
+#define INVALID_PRIORITY_GROUP -1LL
+
+extern char active_ncp[];
+extern nwam_ncp_handle_t active_ncph;
+extern int64_t current_ncu_priority_group;
+extern uint64_t ncu_wait_time;
+
+boolean_t nwamd_ncp_find_next_priority_group(int64_t, int64_t *);
+void nwamd_ncp_activate_priority_group(int64_t);
+void nwamd_ncp_deactivate_priority_group(int64_t);
+void nwamd_ncp_deactivate_priority_group_all(int64_t);
+boolean_t nwamd_ncp_check_priority_group(int64_t *);
+void nwamd_ncp_activate_manual_ncus(void);
+
+/* Create ncu check event */
+void nwamd_create_ncu_check_event(uint64_t);
+
+#endif /* _NCP_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c
new file mode 100644
index 0000000000..9a49ce37b4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.c
@@ -0,0 +1,1996 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <libdlaggr.h>
+#include <libdllink.h>
+#include <libdlstat.h>
+#include <libnwam.h>
+#include <libscf.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <values.h>
+
+#include "conditions.h"
+#include "events.h"
+#include "objects.h"
+#include "ncp.h"
+#include "util.h"
+
+/*
+ * ncu.c - handles various NCU tasks - intialization/refresh, state machine
+ * for NCUs etc.
+ */
+
+#define VBOX_IFACE_PREFIX "vboxnet"
+
+/*
+ * Find ncu of specified type for link/interface name.
+ */
+nwamd_object_t
+nwamd_ncu_object_find(nwam_ncu_type_t type, const char *name)
+{
+ nwam_error_t err;
+ char *object_name;
+ nwamd_object_t ncu_obj = NULL;
+
+ if ((err = nwam_ncu_name_to_typed_name(name, type, &object_name))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncu_find: nwam_ncu_name_to_typed_name "
+ "returned %s", nwam_strerror(err));
+ return (NULL);
+ }
+ ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, object_name);
+
+ free(object_name);
+ return (ncu_obj);
+}
+
+nwam_error_t
+nwamd_set_ncu_string(nwam_ncu_handle_t ncuh, char **strval, uint_t cnt,
+ const char *prop)
+{
+ nwam_error_t err;
+ nwam_value_t val;
+
+ if ((err = nwam_value_create_string_array(strval, cnt, &val))
+ != NWAM_SUCCESS)
+ return (err);
+ err = nwam_ncu_set_prop_value(ncuh, prop, val);
+ nwam_value_free(val);
+ return (err);
+}
+
+nwam_error_t
+nwamd_set_ncu_uint(nwam_ncu_handle_t ncuh, uint64_t *uintval, uint_t cnt,
+ const char *prop)
+{
+ nwam_error_t err;
+ nwam_value_t val;
+
+ if ((err = nwam_value_create_uint64_array(uintval, cnt, &val))
+ != NWAM_SUCCESS)
+ return (err);
+ err = nwam_ncu_set_prop_value(ncuh, prop, val);
+ nwam_value_free(val);
+ return (err);
+}
+
+nwam_error_t
+nwamd_get_ncu_string(nwam_ncu_handle_t ncuh, nwam_value_t *val, char ***strval,
+ uint_t *cnt, const char *prop)
+{
+ nwam_error_t err;
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
+ return (err);
+ return (nwam_value_get_string_array(*val, strval, cnt));
+}
+
+nwam_error_t
+nwamd_get_ncu_uint(nwam_ncu_handle_t ncuh, nwam_value_t *val,
+ uint64_t **uintval, uint_t *cnt, const char *prop)
+{
+ nwam_error_t err;
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, prop, val)) != NWAM_SUCCESS)
+ return (err);
+ return (nwam_value_get_uint64_array(*val, uintval, cnt));
+}
+
+/*
+ * Run link/interface state machine in response to a state change
+ * or enable/disable action event.
+ */
+static void
+nwamd_ncu_state_machine(const char *object_name)
+{
+ nwamd_object_t object;
+ nwamd_ncu_t *ncu;
+ link_state_t link_state;
+ nwamd_event_t event;
+ nwam_wlan_t key_wlan, connected_wlan;
+ nwamd_link_t *link;
+ char linkname[NWAM_MAX_NAME_LEN];
+ boolean_t up;
+
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, object_name))
+ == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_state_machine: "
+ "request for nonexistent NCU %s", object_name);
+ return;
+ }
+
+ ncu = object->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ switch (object->nwamd_object_aux_state) {
+ case NWAM_AUX_STATE_INITIALIZED:
+ if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
+ /*
+ * For wired/wireless links, need to get link
+ * up/down events and even if these are not supported,
+ * dlpi_open()ing the link prevents the driver from
+ * being unloaded.
+ */
+ nwamd_dlpi_add_link(object);
+
+ if (link->nwamd_link_media == DL_WIFI) {
+ /*
+ * First, if we're unexpectedly connected,
+ * disconnect.
+ */
+ if (!link->nwamd_link_wifi_connected &&
+ nwamd_wlan_connected(object)) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_state_machine: "
+ "WiFi unexpectedly connected, "
+ "disconnecting...");
+ (void) dladm_wlan_disconnect(dld_handle,
+ link->nwamd_link_id);
+ nwamd_set_selected_connected(ncu,
+ B_FALSE, B_FALSE);
+ }
+ /* move to scanning aux state */
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, object->nwamd_object_state,
+ NWAM_AUX_STATE_LINK_WIFI_SCANNING);
+ } else {
+ /*
+ * If initial wired link state is unknown, we
+ * will need to assume the link is up, since
+ * we won´t get DL_NOTE_LINK_UP/DOWN events.
+ */
+ link_state = nwamd_get_link_state
+ (ncu->ncu_name);
+ if (link_state == LINK_STATE_UP ||
+ link_state == LINK_STATE_UNKNOWN) {
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_ONLINE,
+ NWAM_AUX_STATE_UP);
+ } else {
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_NCU,
+ object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_DOWN);
+ }
+ }
+ } else {
+ /*
+ * In the current implementation, initialization has to
+ * start from scratch since the complexity of minimizing
+ * configuration change is considerable (e.g. if we
+ * refresh and had DHCP running on the physical
+ * interface, and now have changed to static assignment,
+ * we need to remove DHCP etc). To avoid all this,
+ * unplumb before re-plumbing the protocols and
+ * addresses we wish to configure. In the future, it
+ * would be good to try and minimize configuration
+ * changes.
+ */
+ nwamd_unplumb_interface(ncu, 0, AF_INET);
+ nwamd_unplumb_interface(ncu, 0, AF_INET6);
+
+ /*
+ * Enqueue a WAITING_FOR_ADDR aux state change so that
+ * we are eligible to receive the IF_STATE events
+ * associated with static, DHCP, DHCPv6 and autoconf
+ * address assignment. The latter two can happen
+ * quite quickly after plumbing so we need to be ready.
+ */
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_IF_WAITING_FOR_ADDR);
+
+ if (ncu->ncu_node.u_if.nwamd_if_ipv4)
+ nwamd_plumb_interface(ncu, 0, AF_INET);
+
+ if (ncu->ncu_node.u_if.nwamd_if_ipv6)
+ nwamd_plumb_interface(ncu, 0, AF_INET6);
+
+ /*
+ * Configure addresses. Configure any static addresses
+ * and start DHCP if required. If DHCP is not required,
+ * do a DHCPINFORM to get other networking config
+ * parameters. RTM_NEWADDRs - translated into IF_STATE
+ * events - will then finish the job of bringing us
+ * online.
+ */
+ nwamd_configure_interface_addresses(ncu);
+
+ if (ncu->ncu_node.u_if.nwamd_if_dhcp_requested)
+ nwamd_start_dhcp(ncu);
+ else
+ nwamd_dhcp_inform(ncu);
+ }
+ break;
+
+ case NWAM_AUX_STATE_IF_DHCP_TIMED_OUT:
+ case NWAM_AUX_STATE_IF_WAITING_FOR_ADDR:
+ /*
+ * nothing to do here - RTM_NEWADDRs will trigger IF_STATE
+ * events to move us online.
+ */
+ break;
+
+ case NWAM_AUX_STATE_LINK_WIFI_SCANNING:
+ /* launch scan thread */
+ (void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
+ (void) nwamd_wlan_scan(linkname);
+ /* Create periodic scan event */
+ nwamd_ncu_create_periodic_scan_event(object);
+ break;
+
+ case NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION:
+ /* send "need choice" event */
+ event = nwamd_event_init_wlan
+ (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_NEED_CHOICE, B_FALSE,
+ link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
+ link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num);
+ if (event == NULL)
+ break;
+ nwamd_event_enqueue(event);
+ nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE);
+ break;
+
+ case NWAM_AUX_STATE_LINK_WIFI_NEED_KEY:
+ /*
+ * Send "need key" event. Set selected to true, connected
+ * and have_key to false. Do not fill in WLAN details as
+ * multiple WLANs may match the ESSID name, and each may
+ * have a different speed and channel.
+ */
+ bzero(&key_wlan, sizeof (key_wlan));
+ (void) strlcpy(key_wlan.nww_essid, link->nwamd_link_wifi_essid,
+ sizeof (key_wlan.nww_essid));
+ (void) strlcpy(key_wlan.nww_bssid, link->nwamd_link_wifi_bssid,
+ sizeof (key_wlan.nww_bssid));
+ key_wlan.nww_security_mode =
+ link->nwamd_link_wifi_security_mode;
+ key_wlan.nww_selected = B_TRUE;
+ key_wlan.nww_connected = B_FALSE;
+ key_wlan.nww_have_key = B_FALSE;
+ event = nwamd_event_init_wlan
+ (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_NEED_KEY, B_FALSE,
+ &key_wlan, 1);
+ if (event == NULL)
+ break;
+ nwamd_event_enqueue(event);
+ break;
+
+ case NWAM_AUX_STATE_LINK_WIFI_CONNECTING:
+ (void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
+ nwamd_wlan_connect(linkname);
+ break;
+
+ case NWAM_AUX_STATE_UP:
+ case NWAM_AUX_STATE_DOWN:
+ up = (object->nwamd_object_aux_state == NWAM_AUX_STATE_UP);
+ if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
+ if (link->nwamd_link_media == DL_WIFI) {
+ /*
+ * Connected/disconnected - send WLAN
+ * connection report.
+ */
+ link->nwamd_link_wifi_connected = up;
+ nwamd_set_selected_connected(ncu, B_TRUE, up);
+
+ (void) strlcpy(connected_wlan.nww_essid,
+ link->nwamd_link_wifi_essid,
+ sizeof (connected_wlan.nww_essid));
+ (void) strlcpy(connected_wlan.nww_bssid,
+ link->nwamd_link_wifi_bssid,
+ sizeof (connected_wlan.nww_bssid));
+ connected_wlan.nww_security_mode =
+ link->nwamd_link_wifi_security_mode;
+ event = nwamd_event_init_wlan
+ (ncu->ncu_name,
+ NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT, up,
+ &connected_wlan, 1);
+ if (event == NULL)
+ break;
+ nwamd_event_enqueue(event);
+
+ /*
+ * If disconnected, restart the state machine
+ * for the WiFi link (WiFi is always trying
+ * to connect).
+ *
+ * If connected, start signal strength
+ * monitoring thread.
+ */
+ if (!up && ncu->ncu_enabled) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_state_machine: "
+ "wifi disconnect - start over "
+ "after %dsec interval",
+ WIRELESS_RETRY_INTERVAL);
+ link->nwamd_link_wifi_connected =
+ B_FALSE;
+ /* propogate down event to IP NCU */
+ nwamd_propogate_link_up_down_to_ip
+ (ncu->ncu_name, B_FALSE);
+ nwamd_object_set_state_timed
+ (NWAM_OBJECT_TYPE_NCU, object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED,
+ WIRELESS_RETRY_INTERVAL);
+ } else {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_state_machine: "
+ "wifi connected, start monitoring");
+ (void) strlcpy(linkname, ncu->ncu_name,
+ sizeof (linkname));
+ nwamd_wlan_monitor_signal(linkname);
+ }
+ }
+ }
+
+ /* If not in ONLINE/OFFLINE state yet, change state */
+ if ((up && object->nwamd_object_state != NWAM_STATE_ONLINE) ||
+ (!up && object->nwamd_object_state != NWAM_STATE_OFFLINE)) {
+ nlog(LOG_DEBUG, "nwamd_ncu_state_machine: "
+ "%s is moving %s", object_name,
+ up ? "online" : "offline");
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name,
+ up ? NWAM_STATE_ONLINE : NWAM_STATE_OFFLINE,
+ up ? NWAM_AUX_STATE_UP : NWAM_AUX_STATE_DOWN);
+
+ if (ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE) {
+ if (up) {
+ /*
+ * Moving online, add v4/v6 default
+ * routes (if any).
+ */
+ nwamd_add_default_routes(ncu);
+ } else {
+ /*
+ * If this is an interface NCU and we
+ * got a down event, it is a consequence
+ * of NCU refresh, so reapply addresses
+ * by reinitializing.
+ */
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_NCU, object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ }
+ }
+ } else {
+ nlog(LOG_DEBUG, "nwamd_ncu_state_machine: "
+ "%s is %s", object_name,
+ up ? "online" : "offline");
+ }
+ /*
+ * NCU is UP or DOWN, trigger all condition checking, even if
+ * the NCU is already in the ONLINE state - an ENM may depend
+ * on NCU activity.
+ */
+ nwamd_create_triggered_condition_check_event(NEXT_FEW_SECONDS);
+ break;
+
+ case NWAM_AUX_STATE_CONDITIONS_NOT_MET:
+ /*
+ * Link/interface is moving offline. Nothing to do except
+ * for WiFi, where we disconnect. Don't unplumb IP on
+ * a link since it may be a transient change.
+ */
+ if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
+ if (link->nwamd_link_media == DL_WIFI) {
+ (void) dladm_wlan_disconnect(dld_handle,
+ link->nwamd_link_id);
+ link->nwamd_link_wifi_connected = B_FALSE;
+ nwamd_set_selected_connected(ncu, B_FALSE,
+ B_FALSE);
+ }
+ } else {
+ /*
+ * Unplumb here. In the future we may elaborate on
+ * the approach used and not unplumb for WiFi
+ * until we reconnect to a different WLAN (i.e. with
+ * a different ESSID).
+ */
+ nwamd_unplumb_interface(ncu, 0, AF_INET);
+ nwamd_unplumb_interface(ncu, 0, AF_INET6);
+ }
+ if (object->nwamd_object_state != NWAM_STATE_OFFLINE) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_OFFLINE,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ break;
+
+ case NWAM_AUX_STATE_MANUAL_DISABLE:
+ /* Manual disable, set enabled state appropriately. */
+ ncu->ncu_enabled = B_FALSE;
+ /* FALLTHROUGH */
+ case NWAM_AUX_STATE_UNINITIALIZED:
+ case NWAM_AUX_STATE_NOT_FOUND:
+ /*
+ * Link/interface NCU has been disabled/deactivated/removed.
+ * For WiFi links disconnect, and for IP interfaces we unplumb.
+ */
+ if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
+ if (link->nwamd_link_media == DL_WIFI) {
+ (void) dladm_wlan_disconnect(dld_handle,
+ link->nwamd_link_id);
+ link->nwamd_link_wifi_connected = B_FALSE;
+ nwamd_set_selected_connected(ncu, B_FALSE,
+ B_FALSE);
+ }
+ nwamd_dlpi_delete_link(object);
+ } else {
+ /* Unplumb here. */
+ if (ncu->ncu_node.u_if.nwamd_if_ipv4) {
+ nwamd_unplumb_interface(ncu, 0, AF_INET);
+ }
+ if (ncu->ncu_node.u_if.nwamd_if_ipv6) {
+ nwamd_unplumb_interface(ncu, 0, AF_INET6);
+ }
+ /* trigger location condition checking */
+ nwamd_create_triggered_condition_check_event(0);
+ }
+
+ switch (object->nwamd_object_aux_state) {
+ case NWAM_AUX_STATE_MANUAL_DISABLE:
+ /* Change state to DISABLED if manually disabled */
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_DISABLED,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ /* Note that NCU has been disabled */
+ ncu->ncu_enabled = B_FALSE;
+ break;
+ case NWAM_AUX_STATE_NOT_FOUND:
+ /* Change state to UNINITIALIZED for device removal */
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_UNINITIALIZED,
+ NWAM_AUX_STATE_NOT_FOUND);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_ncu_state_machine: unexpected state");
+ break;
+ }
+
+ nwamd_object_release(object);
+}
+
+static int
+ncu_create_init_fini_event(nwam_ncu_handle_t ncuh, void *data)
+{
+ boolean_t *init = data;
+ char *name, *typedname;
+ nwam_error_t err;
+ nwam_value_t typeval = NULL;
+ uint64_t *type;
+ uint_t numvalues;
+ nwamd_event_t ncu_event;
+
+ if (nwam_ncu_get_name(ncuh, &name) != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "ncu_create_init_fini_event: could not get NCU name");
+ return (0);
+ }
+
+ nlog(LOG_DEBUG, "ncu_create_init_fini_event(%s, %p)", name, data);
+
+ if ((err = nwamd_get_ncu_uint(ncuh, &typeval, &type, &numvalues,
+ NWAM_NCU_PROP_TYPE)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "ncu_create_init_fini_event: "
+ "could not get NCU type: %s", nwam_strerror(err));
+ free(name);
+ nwam_value_free(typeval);
+ return (0);
+ }
+
+ /* convert name to typedname for event */
+ if ((err = nwam_ncu_name_to_typed_name(name, *type, &typedname))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "ncu_create_init_fini_event: "
+ "NCU name translation failed: %s", nwam_strerror(err));
+ free(name);
+ return (0);
+ }
+ free(name);
+ nwam_value_free(typeval);
+
+ ncu_event = nwamd_event_init(*init ?
+ NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
+ NWAM_OBJECT_TYPE_NCU, 0, typedname);
+ if (ncu_event != NULL)
+ nwamd_event_enqueue(ncu_event);
+ free(typedname);
+
+ return (0);
+}
+
+/*
+ * Initialization - walk the NCUs, creating initialization events for each
+ * NCU. nwamd_ncu_handle_init_event() will check if the associated
+ * physical link exists or not.
+ */
+void
+nwamd_init_ncus(void)
+{
+ boolean_t init = B_TRUE;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (active_ncph != NULL) {
+ nlog(LOG_DEBUG, "nwamd_init_ncus: "
+ "(re)intializing NCUs for NCP %s", active_ncp);
+ (void) nwam_ncp_walk_ncus(active_ncph,
+ ncu_create_init_fini_event, &init, NWAM_FLAG_NCU_TYPE_ALL,
+ NULL);
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+}
+
+void
+nwamd_fini_ncus(void)
+{
+ boolean_t init = B_FALSE;
+
+ /* We may not have an active NCP on initialization, so skip fini */
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (active_ncph != NULL) {
+ nlog(LOG_DEBUG, "nwamd_fini_ncus: deinitializing NCUs for %s",
+ active_ncp);
+ (void) nwam_ncp_walk_ncus(active_ncph,
+ ncu_create_init_fini_event, &init, NWAM_FLAG_NCU_TYPE_ALL,
+ NULL);
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+}
+
+/*
+ * Most properties of this type don't need to be cached locally. Only those
+ * interesting to the daemon are stored in an nwamd_ncu_t.
+ */
+static void
+populate_common_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
+{
+ nwam_value_t ncu_prop;
+ nwam_error_t err;
+ boolean_t enablevalue;
+ uint_t numvalues;
+ char **parent;
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED,
+ &ncu_prop)) != NWAM_SUCCESS) {
+ char *name;
+ (void) nwam_ncu_name_to_typed_name(ncu_data->ncu_name,
+ ncu_data->ncu_type, &name);
+ nlog(LOG_ERR, "nwam_ncu_get_prop_value %s ENABLED failed: %s",
+ name, nwam_strerror(err));
+ free(name);
+ ncu_data->ncu_enabled = B_TRUE;
+ } else {
+ if ((err = nwam_value_get_boolean(ncu_prop, &enablevalue)) !=
+ NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwam_value_get_boolean ENABLED failed: "
+ "%s", nwam_strerror(err));
+ } else {
+ ncu_data->ncu_enabled = enablevalue;
+ }
+ nwam_value_free(ncu_prop);
+ }
+
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &parent,
+ &numvalues, NWAM_NCU_PROP_PARENT_NCP)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwam_ncu_get_prop_value %s PARENT failed: %s",
+ ncu_data->ncu_name, nwam_strerror(err));
+ } else {
+ (void) strlcpy(ncu_data->ncu_parent, parent[0],
+ sizeof (ncu_data->ncu_parent));
+ nwam_value_free(ncu_prop);
+ }
+}
+
+/*
+ * Read in link properties.
+ */
+static void
+populate_link_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
+{
+ nwam_value_t ncu_prop;
+ nwam_error_t err;
+ char **mac_addr;
+ uint64_t *uintval;
+ uint_t numvalues;
+
+ /* activation-mode */
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval, &numvalues,
+ NWAM_NCU_PROP_ACTIVATION_MODE)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "populate_link_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_ACTIVATION_MODE, nwam_strerror(err));
+ } else {
+ ncu_data->ncu_node.u_link.nwamd_link_activation_mode =
+ uintval[0];
+ nwam_value_free(ncu_prop);
+ }
+
+ /* priority-group and priority-mode for prioritized activation */
+ if (ncu_data->ncu_node.u_link.nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_PRIORITIZED) {
+ /* ncus with prioritized activation are always enabled */
+ ncu_data->ncu_enabled = B_TRUE;
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval,
+ &numvalues, NWAM_NCU_PROP_PRIORITY_MODE))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "populate_link_ncu_properties: "
+ "could not get %s value: %s",
+ NWAM_NCU_PROP_PRIORITY_MODE, nwam_strerror(err));
+ } else {
+ ncu_data->ncu_node.u_link.nwamd_link_priority_mode =
+ uintval[0];
+ nwam_value_free(ncu_prop);
+ }
+
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval,
+ &numvalues, NWAM_NCU_PROP_PRIORITY_GROUP))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "populate_link_ncu_properties: "
+ "could not get %s value: %s",
+ NWAM_NCU_PROP_PRIORITY_GROUP, nwam_strerror(err));
+ } else {
+ ncu_data->ncu_node.u_link.nwamd_link_priority_group =
+ uintval[0];
+ nwam_value_free(ncu_prop);
+ }
+ }
+
+ /* link-mac-addr */
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &mac_addr, &numvalues,
+ NWAM_NCU_PROP_LINK_MAC_ADDR)) != NWAM_SUCCESS) {
+ nlog(LOG_DEBUG,
+ "populate_link_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_LINK_MAC_ADDR, nwam_strerror(err));
+ ncu_data->ncu_node.u_link.nwamd_link_mac_addr = NULL;
+ } else {
+ ncu_data->ncu_node.u_link.nwamd_link_mac_addr =
+ strdup(*mac_addr);
+ ncu_data->ncu_node.u_link.nwamd_link_mac_addr_len =
+ strlen(*mac_addr);
+ nwam_value_free(ncu_prop);
+ }
+
+ /* link-mtu */
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &uintval, &numvalues,
+ NWAM_NCU_PROP_LINK_MTU)) != NWAM_SUCCESS) {
+ nlog(LOG_DEBUG,
+ "populate_link_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_LINK_MTU, nwam_strerror(err));
+ ncu_data->ncu_node.u_link.nwamd_link_mtu = 0;
+ } else {
+ ncu_data->ncu_node.u_link.nwamd_link_mtu = uintval[0];
+ nwam_value_free(ncu_prop);
+ }
+
+ /* link-autopush */
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop,
+ &ncu_data->ncu_node.u_link.nwamd_link_autopush,
+ &ncu_data->ncu_node.u_link.nwamd_link_num_autopush,
+ NWAM_NCU_PROP_LINK_AUTOPUSH)) != NWAM_SUCCESS) {
+ nlog(LOG_DEBUG,
+ "populate_link_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_LINK_AUTOPUSH, nwam_strerror(err));
+ ncu_data->ncu_node.u_link.nwamd_link_num_autopush = 0;
+ }
+}
+
+static void
+populate_ip_ncu_properties(nwam_ncu_handle_t ncuh, nwamd_ncu_t *ncu_data)
+{
+ nwamd_if_t *nif = &ncu_data->ncu_node.u_if;
+ struct nwamd_if_address **nifa, *nifai, *nifait;
+ char *prefix;
+ boolean_t static_addr = B_FALSE;
+ uint64_t *addrsrcvalue;
+ nwam_value_t ncu_prop;
+ nwam_error_t err;
+ char **addrvalue;
+ uint_t numvalues;
+ uint64_t *ipversion;
+ int i;
+
+ nif->nwamd_if_ipv4 = B_FALSE;
+ nif->nwamd_if_ipv6 = B_FALSE;
+ nif->nwamd_if_dhcp_requested = B_FALSE;
+ nif->nwamd_if_stateful_requested = B_FALSE;
+ nif->nwamd_if_stateless_requested = B_FALSE;
+ nif->nwamd_if_ipv4_default_route_set = B_FALSE;
+ nif->nwamd_if_ipv6_default_route_set = B_FALSE;
+
+ /* ip-version */
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &ipversion, &numvalues,
+ NWAM_NCU_PROP_IP_VERSION)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "populate_ip_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_IP_VERSION, nwam_strerror(err));
+ } else {
+ for (i = 0; i < numvalues; i++) {
+ switch (ipversion[i]) {
+ case IPV4_VERSION:
+ nif->nwamd_if_ipv4 = B_TRUE;
+ break;
+ case IPV6_VERSION:
+ nif->nwamd_if_ipv6 = B_TRUE;
+ break;
+ default:
+ nlog(LOG_ERR, "bogus ip version %lld",
+ ipversion[i]);
+ break;
+ }
+ }
+ nwam_value_free(ncu_prop);
+ }
+
+ /* Free the old list. */
+ for (nifai = nif->nwamd_if_list; nifai != NULL; nifai = nifait) {
+ nifait = nifai->next;
+ nifai->next = NULL;
+ free(nifai);
+ }
+ nif->nwamd_if_list = NULL;
+ nifa = &(nif->nwamd_if_list);
+
+ if (!nif->nwamd_if_ipv4)
+ goto skip_ipv4;
+
+ /* ipv4-addrsrc */
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &addrsrcvalue,
+ &numvalues, NWAM_NCU_PROP_IPV4_ADDRSRC)) != NWAM_SUCCESS) {
+ nlog(nif->nwamd_if_ipv4 ? LOG_ERR : LOG_DEBUG,
+ "populate_ip_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_IPV4_ADDRSRC, nwam_strerror(err));
+ } else {
+ for (i = 0; i < numvalues; i++) {
+ switch (addrsrcvalue[i]) {
+ case NWAM_ADDRSRC_DHCP:
+ nif->nwamd_if_dhcp_requested = B_TRUE;
+ break;
+ case NWAM_ADDRSRC_STATIC:
+ static_addr = B_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ nwam_value_free(ncu_prop);
+ }
+ if (nif->nwamd_if_dhcp_requested) {
+ if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
+ (*nifa)->address.sa_family = AF_INET;
+ (*nifa)->dhcp_if = B_TRUE;
+ nifa = &((*nifa)->next);
+ *nifa = NULL;
+ }
+ }
+
+ /* ipv4-addr */
+ if (static_addr) {
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
+ &numvalues, NWAM_NCU_PROP_IPV4_ADDR)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "populate_ip_ncu_properties: "
+ "could not get %s value; %s",
+ NWAM_NCU_PROP_IPV4_ADDR, nwam_strerror(err));
+ } else {
+ struct sockaddr_in *s;
+
+ for (i = 0; i < numvalues; i++) {
+ if ((*nifa = calloc(sizeof (**nifa), 1))
+ == NULL) {
+ nlog(LOG_ERR, "couldn't allocate nwamd"
+ "address");
+ continue;
+ }
+ (*nifa)->address.sa_family = AF_INET;
+ /*LINTED*/
+ s = (struct sockaddr_in *)&(*nifa)->address;
+ s->sin_family = AF_INET;
+ s->sin_port = 0;
+ prefix = strchr(addrvalue[i], '/');
+ if (prefix != NULL) {
+ *prefix++ = 0;
+ (*nifa)->prefix = atoi(prefix);
+ }
+ (void) inet_pton(AF_INET, addrvalue[i],
+ &(s->sin_addr));
+ nifa = &((*nifa)->next);
+ }
+ *nifa = NULL;
+
+ nwam_value_free(ncu_prop);
+ }
+ }
+
+ /* get default route, if any */
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
+ &numvalues, NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE)) == NWAM_SUCCESS) {
+ /* Only one default route is allowed. */
+ nif->nwamd_if_ipv4_default_route.sin_family = AF_INET;
+ (void) inet_pton(AF_INET, addrvalue[0],
+ &(nif->nwamd_if_ipv4_default_route.sin_addr));
+ nif->nwamd_if_ipv4_default_route_set = B_TRUE;
+ nwam_value_free(ncu_prop);
+ }
+
+skip_ipv4:
+
+ if (!nif->nwamd_if_ipv6)
+ goto skip_ipv6;
+
+ /* ipv6-addrsrc */
+ static_addr = B_FALSE;
+ if ((err = nwamd_get_ncu_uint(ncuh, &ncu_prop, &addrsrcvalue,
+ &numvalues, NWAM_NCU_PROP_IPV6_ADDRSRC)) != NWAM_SUCCESS) {
+ nlog(nif->nwamd_if_ipv6 ? LOG_ERR : LOG_DEBUG,
+ "populate_ip_ncu_properties: could not get %s value: %s",
+ NWAM_NCU_PROP_IPV6_ADDRSRC, nwam_strerror(err));
+ } else {
+ for (i = 0; i < numvalues; i++) {
+ switch (addrsrcvalue[i]) {
+ case NWAM_ADDRSRC_DHCP:
+ nif->nwamd_if_stateful_requested = B_TRUE;
+ break;
+ case NWAM_ADDRSRC_AUTOCONF:
+ nif->nwamd_if_stateless_requested = B_TRUE;
+ break;
+ case NWAM_ADDRSRC_STATIC:
+ static_addr = B_TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ nwam_value_free(ncu_prop);
+ }
+ if (nif->nwamd_if_stateful_requested) {
+ if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
+ (*nifa)->address.sa_family = AF_INET6;
+ (*nifa)->dhcp_if = B_TRUE;
+ nifa = &((*nifa)->next);
+ *nifa = NULL;
+ }
+ }
+ if (nif->nwamd_if_stateless_requested) {
+ if ((*nifa = calloc(sizeof (**nifa), 1)) != NULL) {
+ (*nifa)->address.sa_family = AF_INET6;
+ (*nifa)->stateless_if = B_TRUE;
+ nifa = &((*nifa)->next);
+ *nifa = NULL;
+ }
+ }
+
+ /* ipv6-addr */
+ if (static_addr) {
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
+ &numvalues, NWAM_NCU_PROP_IPV6_ADDR)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "populate_ip_ncu_properties: "
+ "could not get %s value; %s",
+ NWAM_NCU_PROP_IPV6_ADDR, nwam_strerror(err));
+ } else {
+ struct sockaddr_in6 *s;
+
+ for (i = 0; i < numvalues; i++) {
+ if ((*nifa = calloc(sizeof (**nifa), 1))
+ == NULL) {
+ nlog(LOG_ERR, "couldn't allocate nwamd"
+ "address");
+ continue;
+ }
+ (*nifa)->address.sa_family = AF_INET6;
+ /*LINTED*/
+ s = (struct sockaddr_in6 *)&(*nifa)->address;
+ s->sin6_family = AF_INET6;
+ s->sin6_port = 0;
+ prefix = strchr(addrvalue[i], '/');
+ if (prefix != NULL) {
+ *prefix++ = 0;
+ (*nifa)->prefix = atoi(prefix);
+ }
+ (void) inet_pton(AF_INET6, addrvalue[i],
+ &(s->sin6_addr));
+ nifa = &((*nifa)->next);
+ }
+ *nifa = NULL;
+
+ nwam_value_free(ncu_prop);
+ }
+ }
+
+ /* get default route, if any */
+ if ((err = nwamd_get_ncu_string(ncuh, &ncu_prop, &addrvalue,
+ &numvalues, NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE)) == NWAM_SUCCESS) {
+ /* Only one default route is allowed. */
+ nif->nwamd_if_ipv6_default_route.sin6_family = AF_INET6;
+ (void) inet_pton(AF_INET6, addrvalue[0],
+ &(nif->nwamd_if_ipv6_default_route.sin6_addr));
+ nif->nwamd_if_ipv6_default_route_set = B_TRUE;
+ nwam_value_free(ncu_prop);
+ }
+
+skip_ipv6:
+ ;
+}
+
+static nwamd_ncu_t *
+nwamd_ncu_init(nwam_ncu_type_t ncu_type, const char *name)
+{
+ nwamd_ncu_t *rv;
+
+ nlog(LOG_DEBUG, "nwamd_ncu_init(%d, %s)", ncu_type, name);
+
+ if ((rv = calloc(1, sizeof (*rv))) == NULL)
+ return (NULL);
+
+ rv->ncu_type = ncu_type;
+ rv->ncu_name = strdup(name);
+ rv->ncu_enabled = B_FALSE;
+
+ /* Initialize link/interface-specific data */
+ if (rv->ncu_type == NWAM_NCU_TYPE_LINK) {
+ (void) bzero(&rv->ncu_node.u_link, sizeof (nwamd_link_t));
+ (void) dladm_name2info(dld_handle, name,
+ &rv->ncu_node.u_link.nwamd_link_id, NULL, NULL,
+ &rv->ncu_node.u_link.nwamd_link_media);
+ (void) pthread_mutex_init(
+ &rv->ncu_node.u_link.nwamd_link_wifi_mutex, NULL);
+ rv->ncu_node.u_link.nwamd_link_wifi_priority = MAXINT;
+ } else {
+ (void) bzero(&rv->ncu_node.u_if, sizeof (nwamd_if_t));
+ }
+
+ return (rv);
+}
+
+void
+nwamd_ncu_free(nwamd_ncu_t *ncu)
+{
+ if (ncu != NULL) {
+ assert(ncu->ncu_type == NWAM_NCU_TYPE_LINK ||
+ ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE);
+ if (ncu->ncu_type == NWAM_NCU_TYPE_LINK) {
+ struct nwamd_link *l = &ncu->ncu_node.u_link;
+ int i;
+
+ free(l->nwamd_link_wifi_key);
+ free(l->nwamd_link_mac_addr);
+ for (i = 0; i < l->nwamd_link_num_autopush; i++)
+ free(l->nwamd_link_autopush[i]);
+ } else if (ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE) {
+ struct nwamd_if_address *nifa;
+
+ nifa = ncu->ncu_node.u_if.nwamd_if_list;
+ while (nifa != NULL) {
+ struct nwamd_if_address *n;
+
+ n = nifa;
+ nifa = nifa->next;
+ free(n);
+ }
+ }
+ free(ncu->ncu_name);
+ free(ncu);
+ }
+}
+
+static int
+nwamd_ncu_display(nwamd_object_t ncu_obj, void *data)
+{
+ nwamd_ncu_t *ncu = (nwamd_ncu_t *)ncu_obj->nwamd_object_data;
+ data = data;
+ nlog(LOG_DEBUG, "NCU (%p) %s state %s, %s",
+ (void *)ncu, ncu_obj->nwamd_object_name,
+ nwam_state_to_string(ncu_obj->nwamd_object_state),
+ nwam_aux_state_to_string(ncu_obj->nwamd_object_aux_state));
+ return (0);
+}
+
+void
+nwamd_log_ncus(void)
+{
+ nlog(LOG_DEBUG, "NCP %s", active_ncp);
+ (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, nwamd_ncu_display,
+ NULL);
+}
+
+int
+nwamd_ncu_action(const char *ncu, const char *parent, nwam_action_t action)
+{
+ nwamd_event_t ncu_event = nwamd_event_init_object_action
+ (NWAM_OBJECT_TYPE_NCU, ncu, parent, action);
+ if (ncu_event == NULL)
+ return (1);
+ nwamd_event_enqueue(ncu_event);
+ return (0);
+}
+
+static void
+add_phys_ncu_to_ncp(nwam_ncp_handle_t ncph, const char *name)
+{
+ dladm_status_t dlrtn;
+ uint32_t media;
+ boolean_t is_wireless;
+ nwam_error_t err;
+ nwam_ncu_handle_t ncuh;
+ uint64_t uintval;
+
+ if ((dlrtn = dladm_name2info(dld_handle, name, NULL, NULL, NULL,
+ &media)) != DLADM_STATUS_OK) {
+ char errmsg[DLADM_STRSIZE];
+ nlog(LOG_ERR, "failed to get media type for %s: %s", name,
+ dladm_status2str(dlrtn, errmsg));
+ return;
+ }
+ is_wireless = (media == DL_WIFI);
+
+ if ((err = nwam_ncu_create(ncph, name, NWAM_NCU_TYPE_LINK,
+ NWAM_NCU_CLASS_PHYS, &ncuh)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "failed to create link ncu for %s: %s", name,
+ nwam_strerror(err));
+ if (err == NWAM_ENTITY_READ_ONLY) {
+ nwamd_event_t retry_event;
+
+ /*
+ * Root filesystem may be read-only, retry in
+ * a few seconds.
+ */
+ nlog(LOG_DEBUG, "Retrying addition of phys ncu for %s",
+ name);
+ retry_event = nwamd_event_init_link_action(name,
+ NWAM_ACTION_ADD);
+ if (retry_event != NULL) {
+ nwamd_event_enqueue_timed(retry_event,
+ NWAMD_READONLY_RETRY_INTERVAL);
+ }
+ }
+ return;
+ }
+
+ uintval = NWAM_ACTIVATION_MODE_PRIORITIZED;
+ if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
+ NWAM_NCU_PROP_ACTIVATION_MODE)) != NWAM_SUCCESS) {
+ goto finish;
+ }
+
+ uintval = is_wireless ? 1 : 0;
+ if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
+ NWAM_NCU_PROP_PRIORITY_GROUP)) != NWAM_SUCCESS) {
+ goto finish;
+ }
+
+ uintval = is_wireless ? NWAM_PRIORITY_MODE_EXCLUSIVE :
+ NWAM_PRIORITY_MODE_SHARED;
+ if ((err = nwamd_set_ncu_uint(ncuh, &uintval, 1,
+ NWAM_NCU_PROP_PRIORITY_MODE)) != NWAM_SUCCESS) {
+ goto finish;
+ }
+
+ err = nwam_ncu_commit(ncuh, 0);
+
+finish:
+ nwam_ncu_free(ncuh);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "failed to create automatic link ncu for %s: %s",
+ name, nwam_strerror(err));
+ }
+}
+
+static void
+add_ip_ncu_to_ncp(nwam_ncp_handle_t ncph, const char *name)
+{
+ nwam_error_t err;
+ nwam_ncu_handle_t ncuh;
+
+ if ((err = nwam_ncu_create(ncph, name, NWAM_NCU_TYPE_INTERFACE,
+ NWAM_NCU_CLASS_IP, &ncuh)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "failed to create ip ncu for %s: %s", name,
+ nwam_strerror(err));
+ /*
+ * Root filesystem may be read-only, but no need to
+ * retry here since add_phys_ncu_to_ncp() enqueues
+ * a retry event which will lead to add_ip_ncu_to_ncp()
+ * being called.
+ */
+ return;
+ }
+
+ /* IP NCU has the default values, so nothing else to do */
+ err = nwam_ncu_commit(ncuh, 0);
+
+finish:
+ nwam_ncu_free(ncuh);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "failed to create ip ncu for %s: %s", name,
+ nwam_strerror(err));
+ }
+}
+
+static void
+remove_ncu_from_ncp(nwam_ncp_handle_t ncph, const char *name,
+ nwam_ncu_type_t type)
+{
+ nwam_error_t err;
+ nwam_ncu_handle_t ncuh;
+
+ if ((err = nwam_ncu_read(ncph, name, type, 0, &ncuh)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "failed to read automatic ncu %s: %s", name,
+ nwam_strerror(err));
+ return;
+ }
+
+ err = nwam_ncu_destroy(ncuh, 0);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "failed to delete automatic ncu %s: %s", name,
+ nwam_strerror(err));
+ }
+}
+
+/*
+ * Device represented by NCU has been added or removed for the active
+ * User NCP. If an associated NCU of the given type is found, transition it
+ * to the appropriate state.
+ */
+void
+ncu_action_change_state(nwam_action_t action, nwam_ncu_type_t type,
+ const char *name)
+{
+ nwamd_object_t ncu_obj = NULL;
+ nwamd_ncu_t *ncu;
+
+ if ((ncu_obj = nwamd_ncu_object_find(type, name)) == NULL)
+ return;
+
+ ncu = ncu_obj->nwamd_object_data;
+
+ /*
+ * If device has been added, transition from uninitialized to offline.
+ * If device has been removed, transition to uninitialized (via online*
+ * if the NCU is currently enabled in order to tear down config).
+ */
+ if (action == NWAM_ACTION_ADD) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE, NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ } else {
+ if (ncu->ncu_enabled) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_NOT_FOUND);
+ } else {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_UNINITIALIZED,
+ NWAM_AUX_STATE_NOT_FOUND);
+ }
+ }
+ nwamd_object_release(ncu_obj);
+}
+
+/*
+ * Called with hotplug sysevent or when nwam is started and walking the
+ * physical interfaces. Add/remove both link and interface NCUs from the
+ * Automatic NCP. Assumes that both link and interface NCUs don't exist.
+ */
+void
+nwamd_ncu_handle_link_action_event(nwamd_event_t event)
+{
+ nwam_ncp_handle_t ncph;
+ nwam_ncu_type_t type;
+ nwam_action_t action =
+ event->event_msg->nwe_data.nwe_link_action.nwe_action;
+ nwam_error_t err;
+ char *name;
+ boolean_t automatic_ncp_active = B_FALSE;
+
+ if (action != NWAM_ACTION_ADD && action != NWAM_ACTION_REMOVE) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
+ "invalid link action %s", nwam_action_to_string(action));
+ nwamd_event_do_not_send(event);
+ return;
+ }
+
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_link_action_event: "
+ "link action '%s' event on %s", nwam_action_to_string(action),
+ event->event_object[0] == 0 ? "n/a" : event->event_object);
+
+ if ((err = nwam_ncu_typed_name_to_name(event->event_object, &type,
+ &name)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
+ "translation from typedname error: %s", nwam_strerror(err));
+ nwamd_event_do_not_send(event);
+ return;
+ }
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) == 0 &&
+ active_ncph != NULL) {
+ automatic_ncp_active = B_TRUE;
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ /*
+ * We could use active_ncph for cases where the Automatic NCP is active,
+ * but that would involve holding the active_ncp_mutex for too long.
+ */
+ if ((err = nwam_ncp_read(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph))
+ == NWAM_ENTITY_NOT_FOUND) {
+ /* Automatic NCP doesn't exist, create it */
+ if ((err = nwam_ncp_create(NWAM_NCP_NAME_AUTOMATIC, 0,
+ &ncph)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR,
+ "nwamd_ncu_handle_link_action_event: "
+ "could not create %s NCP: %s",
+ NWAM_NCP_NAME_AUTOMATIC,
+ nwam_strerror(err));
+ goto cleanup_exit;
+ }
+ } else if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_link_action_event: "
+ "failed to read Automatic NCP: %s",
+ nwam_strerror(err));
+ goto cleanup_exit;
+ }
+
+ /* add or remove NCUs from Automatic NCP */
+ if (action == NWAM_ACTION_ADD) {
+ add_phys_ncu_to_ncp(ncph, name);
+ add_ip_ncu_to_ncp(ncph, name);
+ } else {
+ /*
+ * Order is important here, remove IP NCU first to prevent
+ * propogation of down event from link to IP. No need to
+ * create REFRESH or DESTROY events. They are generated by
+ * nwam_ncu_commit() and nwam_ncu_destroy().
+ */
+ remove_ncu_from_ncp(ncph, name, NWAM_NCU_TYPE_INTERFACE);
+ remove_ncu_from_ncp(ncph, name, NWAM_NCU_TYPE_LINK);
+ }
+ nwam_ncp_free(ncph);
+
+ /*
+ * If the Automatic NCP is not active, and the associated NCUs
+ * exist, they must be moved into the appropriate states given the
+ * action that has occurred.
+ */
+ if (!automatic_ncp_active) {
+ ncu_action_change_state(action, NWAM_NCU_TYPE_INTERFACE, name);
+ ncu_action_change_state(action, NWAM_NCU_TYPE_LINK, name);
+ }
+
+ /* Need NCU check to evaluate state in light of added/removed NCUs */
+ if (!nwamd_event_enqueued(NWAM_EVENT_TYPE_NCU_CHECK,
+ NWAM_OBJECT_TYPE_NCP, NULL)) {
+ nwamd_create_ncu_check_event(NEXT_FEW_SECONDS);
+ }
+
+cleanup_exit:
+ free(name);
+}
+
+/*
+ * Figure out if this link is part of an aggregation. This is fairly
+ * inefficient since we generate this list for every query and search
+ * linearly. A better way would be to generate the list of links in an
+ * aggregation once and then check each link against it.
+ */
+struct link_aggr_search_data {
+ datalink_id_t linkid;
+ boolean_t under;
+};
+
+static int
+ncu_aggr_search(const char *name, void *data)
+{
+ struct link_aggr_search_data *lasd = data;
+ dladm_aggr_grp_attr_t ginfo;
+ datalink_id_t linkid;
+ int i;
+
+ if (dladm_name2info(dld_handle, name, &linkid, NULL, NULL, NULL) !=
+ DLADM_STATUS_OK)
+ return (DLADM_WALK_CONTINUE);
+ if (dladm_aggr_info(dld_handle, linkid, &ginfo, DLADM_OPT_ACTIVE)
+ != DLADM_STATUS_OK || ginfo.lg_nports == 0)
+ return (DLADM_WALK_CONTINUE);
+
+ for (i = 0; i < ginfo.lg_nports; i++) {
+ if (lasd->linkid == ginfo.lg_ports[i].lp_linkid) {
+ lasd->under = B_TRUE;
+ return (DLADM_WALK_TERMINATE);
+ }
+ }
+ free(ginfo.lg_ports);
+ return (DLADM_WALK_CONTINUE);
+}
+
+static boolean_t
+nwamd_link_belongs_to_an_aggr(const char *name)
+{
+ struct link_aggr_search_data lasd;
+
+ if (dladm_name2info(dld_handle, name, &lasd.linkid, NULL, NULL, NULL)
+ != DLADM_STATUS_OK)
+ return (B_FALSE);
+ lasd.under = B_FALSE;
+ (void) dladm_walk(ncu_aggr_search, dld_handle, &lasd,
+ DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ return (lasd.under);
+}
+
+/*
+ * If NCU doesn't exist for interface with given name, enqueue a ADD
+ * LINK_ACTION event.
+ */
+static int
+ncu_create_link_action_event(const char *name, void *data)
+{
+ nwam_ncp_handle_t ncph = data;
+ nwam_ncu_handle_t ncuh;
+ nwamd_event_t link_event;
+
+ /* Do not generate an event if this is a VirtualBox interface. */
+ if (strncmp(name, VBOX_IFACE_PREFIX, strlen(VBOX_IFACE_PREFIX)) == 0)
+ return (DLADM_WALK_CONTINUE);
+
+ /* Do not generate an event if this link belongs to another zone. */
+ if (!nwamd_link_belongs_to_this_zone(name))
+ return (DLADM_WALK_CONTINUE);
+
+ /* Do not generate an event if this link belongs to an aggregation. */
+ if (nwamd_link_belongs_to_an_aggr(name)) {
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ /* Don't create an event if the NCU already exists. */
+ if (ncph != NULL && nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0,
+ &ncuh) == NWAM_SUCCESS) {
+ nwam_ncu_free(ncuh);
+ return (DLADM_WALK_CONTINUE);
+ }
+
+ nlog(LOG_DEBUG, "ncu_create_link_action_event: adding ncus for %s",
+ name);
+
+ link_event = nwamd_event_init_link_action(name, NWAM_ACTION_ADD);
+ if (link_event != NULL)
+ nwamd_event_enqueue(link_event);
+
+ return (DLADM_WALK_CONTINUE);
+}
+
+/*
+ * Check if interface exists for this NCU. If not, enqueue a REMOVE
+ * LINK_ACTION event.
+ */
+/* ARGSUSED */
+static int
+nwamd_destroy_ncu(nwam_ncu_handle_t ncuh, void *data)
+{
+ char *name;
+ uint32_t flags;
+ nwamd_event_t link_event;
+
+ if (nwam_ncu_get_name(ncuh, &name) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_destroy_ncu: could not get NCU name");
+ return (0);
+ }
+
+ /* Interfaces that exist return DLADM_OPT_ACTIVE flag */
+ if ((dladm_name2info(dld_handle, name, NULL, &flags, NULL, NULL)
+ == DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) &&
+ !nwamd_link_belongs_to_an_aggr(name)) {
+ free(name);
+ return (0);
+ }
+
+ nlog(LOG_DEBUG, "nwamd_destroy_ncu: destroying ncus for %s", name);
+
+ link_event = nwamd_event_init_link_action(name, NWAM_ACTION_REMOVE);
+ if (link_event != NULL)
+ nwamd_event_enqueue(link_event);
+ free(name);
+ return (0);
+}
+
+/*
+ * Called when nwamd is starting up.
+ *
+ * Walk all NCUs and destroy any NCU from the Automatic NCP without an
+ * underlying interface (assumption here is that the interface was removed
+ * when nwam was disabled).
+ *
+ * Walk the physical interfaces and create ADD LINK_ACTION event, which
+ * will create appropriate interface and link NCUs in the Automatic NCP.
+ */
+void
+nwamd_walk_physical_configuration(void)
+{
+ nwam_ncp_handle_t ncph;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) == 0 &&
+ active_ncph != NULL) {
+ ncph = active_ncph;
+ } else {
+ if (nwam_ncp_read(NWAM_NCP_NAME_AUTOMATIC, 0, &ncph)
+ != NWAM_SUCCESS) {
+ ncph = NULL;
+ }
+ }
+
+ /* destroy NCUs for interfaces that don't exist */
+ if (ncph != NULL) {
+ (void) nwam_ncp_walk_ncus(ncph, nwamd_destroy_ncu, NULL,
+ NWAM_FLAG_NCU_TYPE_LINK, NULL);
+ }
+
+ /* create NCUs for interfaces without NCUs */
+ (void) dladm_walk(ncu_create_link_action_event, dld_handle, ncph,
+ DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+
+ if (strcmp(active_ncp, NWAM_NCP_NAME_AUTOMATIC) != 0 ||
+ active_ncph == NULL) {
+ nwam_ncp_free(ncph);
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+}
+
+/*
+ * Handle NCU initialization/refresh event.
+ */
+void
+nwamd_ncu_handle_init_event(nwamd_event_t event)
+{
+ nwamd_object_t object = NULL;
+ nwam_ncu_handle_t ncuh;
+ nwamd_ncu_t *ncu = NULL;
+ nwam_error_t err;
+ nwam_ncu_type_t type;
+ char *name;
+ uint32_t flags;
+ boolean_t new = B_TRUE;
+
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event(%s)",
+ event->event_object);
+
+ /* Get base linkname rather than interface:linkname or link:linkname */
+ err = nwam_ncu_typed_name_to_name(event->event_object,
+ &type, &name);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_init_event: "
+ "nwam_ncu_typed_name_to_name returned %s",
+ nwam_strerror(err));
+ nwamd_event_do_not_send(event);
+ return;
+ }
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (active_ncph == NULL) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_init_event: active NCP handle NULL");
+ nwamd_event_do_not_send(event);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ return;
+ }
+ err = nwam_ncu_read(active_ncph, event->event_object,
+ type, 0, &ncuh);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ if (err != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_init_event: "
+ "could not read object '%s': %s",
+ event->event_object, nwam_strerror(err));
+ free(name);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object)) != NULL)
+ new = B_FALSE;
+
+ /*
+ * For new NCUs, or interface NCUs, we (re)initialize data from scratch.
+ * For link NCUs, we want to retain object data.
+ */
+ switch (type) {
+ case NWAM_NCU_TYPE_LINK:
+ if (new) {
+ ncu = nwamd_ncu_init(type, name);
+ } else {
+ ncu = object->nwamd_object_data;
+ nwam_ncu_free(object->nwamd_object_handle);
+ }
+ populate_common_ncu_properties(ncuh, ncu);
+ populate_link_ncu_properties(ncuh, ncu);
+ break;
+ case NWAM_NCU_TYPE_INTERFACE:
+ if (!new) {
+ nwam_ncu_free(object->nwamd_object_handle);
+ nwamd_ncu_free(object->nwamd_object_data);
+ }
+ ncu = nwamd_ncu_init(type, name);
+ populate_common_ncu_properties(ncuh, ncu);
+ populate_ip_ncu_properties(ncuh, ncu);
+ break;
+ default:
+ nlog(LOG_ERR, "unknown ncu type %d", type);
+ free(name);
+ nwam_ncu_free(ncuh);
+ nwamd_event_do_not_send(event);
+ nwamd_object_release(object);
+ return;
+ }
+
+ if (new) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event: didn't find "
+ "ncu so create it %s", name);
+ object = nwamd_object_init(NWAM_OBJECT_TYPE_NCU,
+ event->event_object, ncuh, ncu);
+ } else {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_init_event: refreshing "
+ "ncu %s", name);
+ object->nwamd_object_data = ncu;
+ object->nwamd_object_handle = ncuh;
+ }
+
+ /*
+ * If the physical link for this NCU doesn't exist in the system,
+ * the state should be UNINITIALIZED/NOT_FOUND. Interfaces that
+ * exist return DLADM_OPT_ACTIVE flag.
+ */
+ if (dladm_name2info(dld_handle, name, NULL, &flags, NULL, NULL)
+ != DLADM_STATUS_OK || !(flags & DLADM_OPT_ACTIVE)) {
+ nlog(LOG_DEBUG, "nwam_ncu_handle_init_event: "
+ "interface for NCU %s doesn't exist",
+ event->event_object);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object->nwamd_object_name, NWAM_STATE_UNINITIALIZED,
+ NWAM_AUX_STATE_NOT_FOUND);
+ free(name);
+ nwamd_object_release(object);
+ return;
+ }
+
+ /*
+ * If NCU is being initialized (rather than refreshed), the
+ * object_state is INITIALIZED (from nwamd_object_init()).
+ */
+ if (object->nwamd_object_state == NWAM_STATE_INITIALIZED) {
+ /*
+ * If the NCU is disabled, initial state should be DISABLED.
+ *
+ * Otherwise, the initial state will be
+ * OFFLINE/CONDITIONS_NOT_MET, and the link selection
+ * algorithm will do the rest.
+ */
+ if (!ncu->ncu_enabled) {
+ object->nwamd_object_state = NWAM_STATE_DISABLED;
+ object->nwamd_object_aux_state =
+ NWAM_AUX_STATE_MANUAL_DISABLE;
+ } else {
+ object->nwamd_object_state = NWAM_STATE_OFFLINE;
+ object->nwamd_object_aux_state =
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET;
+ }
+ } else {
+ nwamd_link_t *link = &ncu->ncu_node.u_link;
+
+ /*
+ * Refresh NCU. Deal with disabled cases first, moving NCUs
+ * that are not disabled - but have the enabled value set - to
+ * the disabled state. Then handle cases where the NCU was
+ * disabled but is no longer. Finally, deal with refresh of
+ * link and interface NCUs, as these are handled differently.
+ */
+ if (!ncu->ncu_enabled) {
+ if (object->nwamd_object_state != NWAM_STATE_DISABLED) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object->nwamd_object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ }
+ goto done;
+ } else {
+ if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
+ int64_t c;
+
+ /*
+ * Try to activate the NCU if manual or
+ * prioritized (when priority <= current).
+ */
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ c = current_ncu_priority_group;
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ if (link->nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_MANUAL ||
+ (link->nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_PRIORITIZED &&
+ link->nwamd_link_priority_mode <= c)) {
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_NCU,
+ object->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ } else {
+ nwamd_object_set_state
+ (NWAM_OBJECT_TYPE_NCU,
+ object->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ }
+ goto done;
+ }
+ }
+
+ switch (type) {
+ case NWAM_NCU_TYPE_LINK:
+ if (ncu->ncu_node.u_link.nwamd_link_media == DL_WIFI) {
+ /*
+ * Do rescan. If the current state and the
+ * active priority-group do not allow wireless
+ * network selection, then it won't happen.
+ */
+ (void) nwamd_wlan_scan(ncu->ncu_name);
+ }
+ break;
+ case NWAM_NCU_TYPE_INTERFACE:
+ /*
+ * If interface NCU is offline*, online or in
+ * maintenance, mark it down (from there, it will be
+ * reinitialized to reapply addresses).
+ */
+ if (object->nwamd_object_state != NWAM_STATE_OFFLINE) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object->nwamd_object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_DOWN);
+ } else {
+ object->nwamd_object_state = NWAM_STATE_OFFLINE;
+ object->nwamd_object_aux_state =
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET;
+ }
+ break;
+ }
+ }
+
+done:
+ if (type == NWAM_NCU_TYPE_LINK &&
+ !nwamd_event_enqueued(NWAM_EVENT_TYPE_NCU_CHECK,
+ NWAM_OBJECT_TYPE_NCP, NULL)) {
+ nwamd_create_ncu_check_event(NEXT_FEW_SECONDS);
+ }
+ free(name);
+ nwamd_object_release(object);
+}
+
+void
+nwamd_ncu_handle_fini_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+ nwamd_event_t state_event;
+
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_fini_event(%s)",
+ event->event_object);
+
+ /*
+ * Simulate a state event so that the state machine can correctly
+ * disable the NCU. Then free up allocated objects.
+ */
+ state_event = nwamd_event_init_object_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_UNINITIALIZED);
+ if (state_event == NULL) {
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ nwamd_ncu_handle_state_event(state_event);
+ nwamd_event_fini(state_event);
+
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object)) == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_fini_event: "
+ "ncu %s not found", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ nwamd_object_release_and_destroy(object);
+}
+
+void
+nwamd_ncu_handle_action_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (strcmp(event->event_msg->nwe_data.nwe_object_action.nwe_parent,
+ active_ncp) != 0) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: action for "
+ "inactive NCP %s, nothing to do",
+ event->event_msg->nwe_data.nwe_object_action.nwe_parent);
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ return;
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
+ case NWAM_ACTION_ENABLE:
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_action_event: "
+ "could not find ncu %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: "
+ "ncu %s already online, nothing to do",
+ event->event_object);
+ nwamd_object_release(object);
+ return;
+ }
+ nwamd_object_release(object);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_INITIALIZED);
+ break;
+ case NWAM_ACTION_DISABLE:
+ object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object);
+ if (object == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_action_event: "
+ "could not find ncu %s", event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_action_event: "
+ "ncu %s already disabled, nothing to do",
+ event->event_object);
+ nwamd_object_release(object);
+ return;
+ }
+ nwamd_object_release(object);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_MANUAL_DISABLE);
+ break;
+ case NWAM_ACTION_ADD:
+ case NWAM_ACTION_REFRESH:
+ nwamd_ncu_handle_init_event(event);
+ break;
+ case NWAM_ACTION_DESTROY:
+ nwamd_ncu_handle_fini_event(event);
+ break;
+ default:
+ nlog(LOG_INFO, "nwam_ncu_handle_action_event: "
+ "unexpected action");
+ nwamd_event_do_not_send(event);
+ break;
+ }
+}
+
+void
+nwamd_ncu_handle_state_event(nwamd_event_t event)
+{
+ nwamd_object_t object;
+ nwam_state_t old_state, new_state;
+ nwam_aux_state_t new_aux_state;
+ nwamd_ncu_t *ncu;
+ boolean_t is_link, enabled, prioritized = B_FALSE;
+ char linkname[NWAM_MAX_NAME_LEN];
+ nwam_event_t m = event->event_msg;
+
+ if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object)) == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_state_event %lld: "
+ "state event for nonexistent NCU %s", event->event_id,
+ event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ ncu = object->nwamd_object_data;
+ old_state = object->nwamd_object_state;
+ new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
+ new_aux_state =
+ event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
+
+ /*
+ * For NCU state changes, we need to supply the parent NCP name also,
+ * regardless of whether the event is handled or not. It is best to
+ * fill this in here as we have the object lock - when we create
+ * object state events we sometimes do not have the object lock, but
+ * at this point in consuming the events (and prior to the associated
+ * event message being sent out) we do.
+ */
+ (void) strlcpy(m->nwe_data.nwe_object_state.nwe_parent, ncu->ncu_parent,
+ sizeof (m->nwe_data.nwe_object_state.nwe_parent));
+
+ /*
+ * If we receive a state change event moving this NCU to
+ * DHCP_TIMED_OUT or UP state but this NCU is already ONLINE, then
+ * ignore this state change event.
+ */
+ if ((new_aux_state == NWAM_AUX_STATE_IF_DHCP_TIMED_OUT ||
+ new_aux_state == NWAM_AUX_STATE_UP) &&
+ object->nwamd_object_state == NWAM_STATE_ONLINE) {
+ nlog(LOG_INFO, "nwamd_ncu_handle_state_event: "
+ "NCU %s already online, not going to '%s' state",
+ object->nwamd_object_name,
+ nwam_aux_state_to_string(new_aux_state));
+ nwamd_event_do_not_send(event);
+ nwamd_object_release(object);
+ return;
+ }
+
+ if (new_state == object->nwamd_object_state &&
+ new_aux_state == object->nwamd_object_aux_state) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
+ "NCU %s already in state (%s, %s)",
+ object->nwamd_object_name, nwam_state_to_string(new_state),
+ nwam_aux_state_to_string(new_aux_state));
+ nwamd_object_release(object);
+ return;
+ }
+
+ if (old_state == NWAM_STATE_MAINTENANCE &&
+ (new_state == NWAM_STATE_ONLINE ||
+ (new_state == NWAM_STATE_OFFLINE_TO_ONLINE &&
+ new_aux_state != NWAM_AUX_STATE_INITIALIZED))) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
+ "NCU %s cannot transition from state %s to state (%s, %s)",
+ object->nwamd_object_name, nwam_state_to_string(old_state),
+ nwam_state_to_string(new_state),
+ nwam_aux_state_to_string(new_aux_state));
+ nwamd_event_do_not_send(event);
+ nwamd_object_release(object);
+ return;
+ }
+
+ object->nwamd_object_state = new_state;
+ object->nwamd_object_aux_state = new_aux_state;
+
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: changing state for NCU "
+ "%s to (%s, %s)", object->nwamd_object_name,
+ nwam_state_to_string(object->nwamd_object_state),
+ nwam_aux_state_to_string(object->nwamd_object_aux_state));
+
+ is_link = (ncu->ncu_type == NWAM_NCU_TYPE_LINK);
+ if (is_link)
+ (void) strlcpy(linkname, ncu->ncu_name, sizeof (linkname));
+ prioritized = (ncu->ncu_type == NWAM_NCU_TYPE_LINK &&
+ ncu->ncu_node.u_link.nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_PRIORITIZED);
+ enabled = ncu->ncu_enabled;
+
+ nwamd_object_release(object);
+
+ /*
+ * State machine for NCUs
+ */
+ switch (new_state) {
+ case NWAM_STATE_OFFLINE_TO_ONLINE:
+ if (enabled) {
+ nwamd_ncu_state_machine(event->event_object);
+ } else {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_state_event: "
+ "cannot move disabled NCU %s online",
+ event->event_object);
+ nwamd_event_do_not_send(event);
+ }
+ break;
+
+ case NWAM_STATE_ONLINE_TO_OFFLINE:
+ nwamd_ncu_state_machine(event->event_object);
+ break;
+
+ case NWAM_STATE_ONLINE:
+ /*
+ * We usually don't need to do anything when we're in the
+ * ONLINE state. However, for WiFi we can be in INIT or
+ * SCAN aux states while being ONLINE.
+ */
+ nwamd_ncu_state_machine(event->event_object);
+ break;
+
+ case NWAM_STATE_OFFLINE:
+ /* Reassess priority group now member is offline */
+ if (prioritized) {
+ nwamd_create_ncu_check_event(0);
+ }
+ break;
+
+ case NWAM_STATE_DISABLED:
+ case NWAM_STATE_UNINITIALIZED:
+ case NWAM_STATE_MAINTENANCE:
+ case NWAM_STATE_DEGRADED:
+ default:
+ /* do nothing */
+ break;
+ }
+
+ if (is_link) {
+ if ((new_state == NWAM_STATE_ONLINE_TO_OFFLINE &&
+ new_aux_state != NWAM_AUX_STATE_UNINITIALIZED &&
+ new_aux_state != NWAM_AUX_STATE_NOT_FOUND) ||
+ new_state == NWAM_STATE_DISABLED) {
+ /*
+ * Going offline, propogate down event to IP NCU. Do
+ * not propogate event if new aux state is uninitialized
+ * or not found as these auxiliary states signify
+ * that an NCP switch/device removal is in progress.
+ */
+ nwamd_propogate_link_up_down_to_ip(linkname, B_FALSE);
+ }
+ if (new_state == NWAM_STATE_ONLINE) {
+ /* gone online, propogate up event to IP NCU */
+ nwamd_propogate_link_up_down_to_ip(linkname, B_TRUE);
+ }
+ } else {
+ /* If IP NCU is online, reasses priority group */
+ if (new_state == NWAM_STATE_ONLINE)
+ nwamd_create_ncu_check_event(0);
+ }
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h
new file mode 100644
index 0000000000..ed64ba36b4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncu.h
@@ -0,0 +1,235 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NCU_H
+#define _NCU_H
+
+#include <dhcpagent_ipc.h>
+#include <dhcpagent_util.h>
+#include <inetcfg.h>
+#include <libdladm.h>
+#include <libdlpi.h>
+#include <libdlwlan.h>
+#include <libnwam.h>
+#include <libnwam_priv.h>
+#include <libuutil.h>
+#include <pthread.h>
+#include <sys/mac.h>
+
+#include "events.h"
+
+extern pthread_mutex_t active_ncp_mutex;
+extern pthread_mutex_t active_loc_mutex;
+extern char active_loc[];
+extern uint64_t wireless_scan_interval;
+extern dladm_wlan_strength_t wireless_scan_level;
+extern boolean_t wireless_autoconf;
+extern boolean_t wireless_strict_bssid;
+
+/*
+ * NCPs are collections of NCUs. At the moment there is one NCP in the system
+ * and its expected there will never be many. There is a lock on the NCP which
+ * must be obtained to add or remove anything from the NCP.
+ *
+ * NCUs are also kept in a uu list for easy walking. Each NCU has a lock which
+ * is used to protect manipulation of its contents. One of its members is a
+ * reference count which is initialized to 1 when its placed on the NCP. As
+ * references are passed around that should be manipulated as necessary
+ * (helper functions YYY provided). It is removed from the NCP by
+ * ncu_destroy() but the memory containing it is not returned to the free pool
+ * until the reference count falls to 0.
+ *
+ * As we add
+ * more complex system objects their relationship becomes more complex. That
+ * is represented by the links within the NCUs. Reference counts should be
+ * used to maintain the consistency of these links. Care should be used when
+ * walking more complex structures that might contain cycles.
+ */
+
+/* Stores details of last/current WiFi scans */
+typedef struct nwamd_wifi_scan {
+ char nwamd_wifi_scan_link[NWAM_MAX_NAME_LEN];
+ nwam_wlan_t nwamd_wifi_scan_last[NWAMD_MAX_NUM_WLANS];
+ uint_t nwamd_wifi_scan_last_num;
+ nwam_wlan_t nwamd_wifi_scan_curr[NWAMD_MAX_NUM_WLANS];
+ uint_t nwamd_wifi_scan_curr_num;
+ boolean_t nwamd_wifi_scan_changed;
+ uint32_t nwamd_wifi_scan_last_time;
+} nwamd_wifi_scan_t;
+
+typedef struct nwamd_link {
+ pthread_mutex_t nwamd_link_wifi_mutex;
+ pthread_t nwamd_link_wifi_scan_thread;
+ pthread_t nwamd_link_wifi_monitor_thread;
+ char nwamd_link_wifi_essid[DLADM_STRSIZE];
+ char nwamd_link_wifi_bssid[DLADM_STRSIZE];
+ char nwamd_link_wifi_keyname[DLADM_STRSIZE];
+ char nwamd_link_wifi_signal_strength[DLADM_STRSIZE];
+ boolean_t nwamd_link_wifi_add_to_known_wlans;
+ boolean_t nwamd_link_wifi_connected;
+ uint32_t nwamd_link_wifi_security_mode;
+ dladm_wlan_key_t *nwamd_link_wifi_key;
+ nwamd_wifi_scan_t nwamd_link_wifi_scan;
+ uint64_t nwamd_link_wifi_priority;
+ boolean_t nwamd_link_wifi_autoconf;
+ uint32_t nwamd_link_id;
+ uint32_t nwamd_link_media;
+ uint64_t nwamd_link_flags;
+ dlpi_handle_t nwamd_link_dhp;
+ pthread_t nwamd_link_dlpi_thread;
+ uint64_t nwamd_link_activation_mode;
+ uint64_t nwamd_link_priority_mode;
+ uint64_t nwamd_link_priority_group;
+ char *nwamd_link_mac_addr;
+ size_t nwamd_link_mac_addr_len;
+ uint64_t nwamd_link_mtu;
+ char **nwamd_link_autopush;
+ uint_t nwamd_link_num_autopush;
+} nwamd_link_t;
+
+struct nwamd_if_address {
+ size_t prefix;
+ struct sockaddr address;
+ char pad[sizeof (struct sockaddr_storage)];
+ boolean_t configured;
+ boolean_t dhcp_if;
+ boolean_t stateless_if;
+ char ifname[LIFNAMSIZ];
+ struct nwamd_if_address *next;
+};
+
+typedef struct nwamd_if {
+ boolean_t nwamd_if_dhcp_requested;
+ boolean_t nwamd_if_dhcp_configured;
+ boolean_t nwamd_if_stateful_requested;
+ boolean_t nwamd_if_stateful_configured;
+ boolean_t nwamd_if_stateless_requested;
+ boolean_t nwamd_if_stateless_configured;
+ struct nwamd_if_address *nwamd_if_list;
+ struct sockaddr_in nwamd_if_ipv4_default_route;
+ boolean_t nwamd_if_ipv4_default_route_set;
+ struct sockaddr_in6 nwamd_if_ipv6_default_route;
+ boolean_t nwamd_if_ipv6_default_route_set;
+ boolean_t nwamd_if_ipv4;
+ boolean_t nwamd_if_ipv6;
+} nwamd_if_t;
+
+typedef struct nwamd_ncu {
+ nwam_ncu_type_t ncu_type;
+ char *ncu_name;
+ char ncu_parent[NWAM_MAX_NAME_LEN];
+ boolean_t ncu_enabled; /* whether NCU has been enabled or not */
+ union {
+ nwamd_link_t u_link;
+ nwamd_if_t u_if;
+ } ncu_node;
+} nwamd_ncu_t;
+
+#define ncu_link ncu_node.u_link
+#define ncu_if ncu_node.u_if
+
+#define LOOPBACK_IF "lo0"
+
+struct nwamd_dhcp_thread_arg {
+ char *name;
+ dhcp_ipc_type_t type;
+ int timeout;
+ volatile uint32_t *guard;
+};
+
+#define WIRELESS_SCAN_INTERVAL_DEFAULT 120
+#define WIRELESS_SCAN_INTERVAL_MIN 30
+#define WIRELESS_SCAN_REQUESTED_INTERVAL_MIN 10
+#define WIRELESS_MONITOR_SIGNAL_INTERVAL 10
+#define WIRELESS_RETRY_INTERVAL 30
+#define WIRELESS_SCAN_LEVEL_DEFAULT DLADM_WLAN_STRENGTH_WEAK
+#define NWAMD_DHCP_RETRIES 5
+#define NWAMD_DHCP_RETRY_WAIT_TIME 10
+#define NWAMD_READONLY_RETRY_INTERVAL 5
+
+/*
+ * This dladm handle is opened before interfaces are initialized and
+ * closed only when nwamd shuts down.
+ */
+extern dladm_handle_t dld_handle;
+
+extern nwamd_object_t nwamd_ncu_object_find(nwam_ncu_type_t, const char *);
+extern void nwamd_log_ncus(void);
+extern void nwamd_ncu_free(nwamd_ncu_t *);
+
+/* WLAN functions */
+extern void nwamd_set_selected_connected(nwamd_ncu_t *, boolean_t, boolean_t);
+extern nwam_error_t nwamd_wlan_select(const char *, const char *, const char *,
+ uint32_t, boolean_t);
+extern nwam_error_t nwamd_wlan_set_key(const char *, const char *, const char *,
+ uint32_t, uint_t, char *);
+extern nwam_error_t nwamd_wlan_scan(const char *);
+extern void nwamd_wlan_connect(const char *);
+extern boolean_t nwamd_wlan_connected(nwamd_object_t);
+extern void nwamd_wlan_monitor_signal(const char *);
+extern void nwamd_ncu_create_periodic_scan_event(nwamd_object_t);
+extern dladm_wlan_key_t *nwamd_wlan_get_key_named(const char *, uint32_t);
+extern void nwamd_set_key_name(const char *, const char *, char *, size_t);
+
+/* Link functions */
+extern link_state_t nwamd_get_link_state(const char *);
+extern char *nwamd_sockaddr_to_str(const struct sockaddr *, char *, size_t);
+extern void nwamd_propogate_link_up_down_to_ip(const char *, boolean_t);
+extern void nwamd_set_unset_link_properties(nwamd_ncu_t *, boolean_t);
+/* DLPI event hooking */
+extern void nwamd_dlpi_add_link(nwamd_object_t);
+extern void nwamd_dlpi_delete_link(nwamd_object_t);
+
+/* IP functions */
+extern void nwamd_update_addresses_unconfigured(nwamd_ncu_t *, sa_family_t);
+extern boolean_t nwamd_static_addresses_configured(nwamd_ncu_t *, sa_family_t);
+extern void nwamd_plumb_interface(nwamd_ncu_t *, uint_t, int);
+extern void nwamd_unplumb_interface(nwamd_ncu_t *, uint_t, int);
+extern void nwamd_start_dhcp(nwamd_ncu_t *);
+extern void nwamd_dhcp_inform(nwamd_ncu_t *);
+extern boolean_t nwamd_dhcp_managing(int, nwamd_ncu_t *);
+extern void nwamd_configure_interface_addresses(nwamd_ncu_t *);
+extern char *nwamd_get_dhcpinfo_data(const char *, char *);
+extern void nwamd_dhcp_release(const char *);
+extern void nwamd_add_default_routes(nwamd_ncu_t *);
+extern void nwamd_add_route(struct sockaddr *, struct sockaddr *,
+ struct sockaddr *, const char *);
+
+/* NCU value set/get functions */
+extern nwam_error_t nwamd_set_ncu_uint(nwam_ncu_handle_t, uint64_t *, uint_t,
+ const char *);
+extern nwam_error_t nwamd_set_ncu_string(nwam_ncu_handle_t, char **, uint_t,
+ const char *);
+extern nwam_error_t nwamd_get_ncu_uint(nwam_ncu_handle_t, nwam_value_t *,
+ uint64_t **, uint_t *, const char *);
+extern nwam_error_t nwamd_get_ncu_string(nwam_ncu_handle_t, nwam_value_t *,
+ char ***, uint_t *, const char *);
+
+extern void nwamd_walk_physical_configuration(void);
+
+char *nwamd_link_to_ifname(const char *, int, char *, int);
+
+#endif /* _NCU_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncu_ip.c b/usr/src/cmd/cmd-inet/lib/nwamd/ncu_ip.c
new file mode 100644
index 0000000000..4415953c50
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncu_ip.c
@@ -0,0 +1,1670 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <dhcpagent_ipc.h>
+#include <dhcp_inittab.h>
+#include <dhcp_symbol.h>
+#include <dhcpagent_util.h>
+#include <errno.h>
+#include <execinfo.h>
+#include <inetcfg.h>
+#include <libnwam.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <libscf.h>
+
+#include "conditions.h"
+#include "events.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * ncu_ip.c - contains routines that are IP interface-specific for NCUs.
+ */
+
+#define STATELESS_RUNNING (IFF_RUNNING | IFF_UP | IFF_ADDRCONF)
+#define DHCP_RUNNING (IFF_RUNNING | IFF_UP | IFF_DHCPRUNNING)
+
+static void *start_dhcp_thread(void *);
+static void nwamd_down_interface(const char *, uint_t, int);
+static boolean_t stateless_running(const nwamd_ncu_t *);
+
+char *
+nwamd_sockaddr_to_str(const struct sockaddr *sockaddr, char *str, size_t len)
+{
+ if (icfg_sockaddr_to_str(sockaddr->sa_family, sockaddr, str, len) !=
+ ICFG_SUCCESS) {
+ return (NULL);
+ } else {
+ return (str);
+ }
+}
+
+static void
+nwamd_log_if_address(int severity, struct nwamd_if_address *nifa)
+{
+ char str[INET6_ADDRSTRLEN];
+
+ nlog(severity, "%s address %s is %s",
+ nifa->address.sa_family == AF_INET ? "IPv4" : "IPv6",
+ nwamd_sockaddr_to_str(&nifa->address, str, sizeof (str)),
+ nifa->configured ? "configured" : "not configured");
+}
+
+void
+nwamd_propogate_link_up_down_to_ip(const char *linkname, boolean_t up)
+{
+ nwamd_object_t ip_ncu = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE,
+ linkname);
+ nwamd_ncu_t *ncu;
+
+ if (ip_ncu == NULL) {
+ nlog(LOG_DEBUG, "nwamd_propogate_link_up_down_to_ip: no IP NCU "
+ "for link %s, cannot propogate %s event", linkname,
+ up ? "up" : "down");
+ return;
+ }
+ ncu = ip_ncu->nwamd_object_data;
+
+ if (ncu->ncu_enabled) {
+ if (ip_ncu->nwamd_object_aux_state ==
+ NWAM_AUX_STATE_UNINITIALIZED) {
+ nlog(LOG_DEBUG,
+ "nwamd_propogate_link_up_down_to_ip: will not "
+ "propogate link %s event as IP NCU %s is being "
+ "removed", up ? "up" : "down", linkname);
+ } else {
+ nlog(LOG_DEBUG,
+ "nwamd_propogate_link_up_down_to_ip: propogating "
+ "link %s event to interface %s",
+ up ? "up" : "down", linkname);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ip_ncu->nwamd_object_name,
+ up ?
+ NWAM_STATE_OFFLINE_TO_ONLINE :
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ up ? NWAM_AUX_STATE_INITIALIZED :
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET);
+ }
+ } else {
+ nlog(LOG_DEBUG,
+ "nwamd_propogate_link_up_down_to_ip: not propogating "
+ "link %s event to interface %s, IP NCU is disabled",
+ up ? "up" : "down", linkname);
+ }
+ nwamd_object_release(ip_ncu);
+}
+
+/*
+ * Returns the value associated with the given symbol for the given
+ * interface. The interface may be NULL, in which case the primary
+ * interface is used.
+ * This function substitutes the need to call dhcpinfo(1), thus it is
+ * very similar to the implementation of dhcpinfo(1).
+ * When multiple values need to be returned (e.g., nameservers), they
+ * are separated by a space ' '.
+ */
+char *
+nwamd_get_dhcpinfo_data(const char *sym_name, char *ifname)
+{
+ dhcp_symbol_t *entry;
+ dhcp_optnum_t optnum;
+ dhcp_ipc_request_t *request;
+ dhcp_ipc_reply_t *reply;
+ DHCP_OPT *opt;
+ size_t opt_len;
+ char *value; /* return value */
+ int err;
+ char errmsg[LINE_MAX];
+
+ /* if interface is not given, change it to empty string */
+ if (ifname == NULL)
+ ifname = "";
+
+ /* find code and category in dhcp_inittab(4) */
+ entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD |
+ ITAB_CAT_VENDOR | ITAB_CAT_FIELD, ITAB_CONS_INFO, sym_name);
+
+ if (entry == NULL) {
+ (void) snprintf(errmsg, LINE_MAX, "unknown identifier: %s",
+ sym_name);
+ goto fail;
+ }
+
+ /* allocate request */
+ optnum.code = entry->ds_code;
+ optnum.category = entry->ds_category;
+ optnum.size = entry->ds_max * inittab_type_to_size(entry);
+ request = dhcp_ipc_alloc_request(DHCP_GET_TAG, ifname, &optnum,
+ sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
+ if (request == NULL) {
+ (void) snprintf(errmsg, LINE_MAX, "failed dhcp alloc request");
+ goto fail;
+ }
+
+ /* make the request */
+ err = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
+ if (err != 0 || reply->return_code != 0) {
+ (void) snprintf(errmsg, LINE_MAX, "%s",
+ dhcp_ipc_strerror(err == 0 ? reply->return_code : err));
+ }
+
+ /* get data from the reply */
+ opt = dhcp_ipc_get_data(reply, &opt_len, NULL);
+ if (opt_len == 0) {
+ (void) snprintf(errmsg, LINE_MAX, "invalid data");
+ goto fail;
+ }
+
+ /* check protocol error */
+ if (opt_len < 2 || (opt_len -2 != opt->len)) {
+ (void) snprintf(errmsg, LINE_MAX, "data length mismatch");
+ goto fail;
+ }
+ opt_len -= 2;
+
+ /* decode the data into ascii */
+ value = inittab_decode(entry, opt->value, opt_len, B_TRUE);
+ if (value == NULL) {
+ (void) snprintf(errmsg, LINE_MAX, "cannot decode reply");
+ goto fail;
+ }
+
+ free(request);
+ free(reply);
+ return (value);
+
+fail:
+ nlog(LOG_DEBUG, "get_dhcpinfo_data() failed: %s", errmsg);
+ free(request);
+ free(reply);
+ return (NULL);
+}
+
+void
+nwamd_dhcp_release(const char *ifname)
+{
+ dhcp_ipc_reply_t *reply = NULL;
+ dhcp_ipc_request_t *request;
+ int rc;
+
+ /* Now allocate and send the request */
+ request = dhcp_ipc_alloc_request(DHCP_RELEASE, ifname, NULL, 0,
+ DHCP_TYPE_NONE);
+ if (request == NULL) {
+ nlog(LOG_DEBUG, "nwamd_dhcp_release: dhcp_ipc_alloc_request : "
+ "%s", strerror(errno));
+ return;
+ }
+ rc = dhcp_ipc_make_request(request, &reply, 1);
+ free(request);
+ free(reply);
+ reply = NULL;
+ if (rc != 0) {
+ /* Fall back to drop request */
+ request = dhcp_ipc_alloc_request(DHCP_DROP, ifname, NULL, 0,
+ DHCP_TYPE_NONE);
+ if (request == NULL) {
+ nlog(LOG_DEBUG, "nwamd_dhcp_release: "
+ "dhcp_ipc_alloc_request : %s", strerror(errno));
+ return;
+ }
+ (void) dhcp_ipc_make_request(request, &reply, 1);
+ free(request);
+ free(reply);
+ }
+}
+
+static boolean_t
+add_ip_address(const char *ifname, struct nwamd_if_address *nifa,
+ boolean_t logical_if)
+{
+ icfg_handle_t h, newh;
+ icfg_if_t intf;
+ uint64_t flags;
+ int rc;
+ struct sockaddr_in bcastaddr;
+ char str[INET6_ADDRSTRLEN];
+
+ (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name));
+ intf.if_protocol = nifa->address.sa_family;
+
+ nlog(LOG_DEBUG, "add_ip_address: %s address %s for link %s",
+ logical_if ? "adding" : "setting",
+ nwamd_sockaddr_to_str(&nifa->address, str, sizeof (str)),
+ intf.if_name);
+
+ if (icfg_open(&h, &intf) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "add_ip_address: icfg_open failed on %s", ifname);
+ return (B_FALSE);
+ }
+ /*
+ * When working with the physical interface, we need to be careful
+ * to set the prefixlen and broadcast addresses before setting the
+ * IP address, otherwise RTM_DELADDRs for the old broadcast/netmask
+ * will confuse us into thinking we've lost the address we've just
+ * assigned.
+ */
+ if (logical_if) {
+ rc = icfg_add_addr(h, &newh,
+ (const struct sockaddr *)&nifa->address,
+ intf.if_protocol == AF_INET ?
+ sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6));
+ } else {
+ newh = h;
+
+ /* Make sure DHCP is no longer running */
+ if (icfg_get_flags(newh, &flags) == ICFG_SUCCESS) {
+ if (flags & IFF_DHCPRUNNING) {
+ nlog(LOG_DEBUG, "add_ip_address: "
+ "turning off DHCP for %s", ifname);
+ nwamd_dhcp_release(ifname);
+ }
+ }
+ /*
+ * Set interface IFF_UP if not already. Do this and
+ * setting of prefixlen/broadcast addresses as otherwise
+ * these can trigger an RTM_DELADDR that makes it appear
+ * that the address has gone away.
+ */
+ rc = icfg_set_addr(newh,
+ (const struct sockaddr *)&nifa->address,
+ intf.if_protocol == AF_INET ?
+ sizeof (struct sockaddr_in) :
+ sizeof (struct sockaddr_in6));
+ }
+ if (rc != ICFG_SUCCESS) {
+ nlog(LOG_DEBUG, "add_ip_address: add of ipaddr failed "
+ "for %s: %d", ifname, rc);
+ goto out;
+ }
+
+ if (nifa->prefix != 0) {
+ if ((rc = icfg_set_prefixlen(newh, nifa->prefix))
+ != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "add_ip_address: icfg_set_prefix %d "
+ "failed on %s: %s", nifa->prefix, ifname,
+ icfg_errmsg(rc));
+ } else if (intf.if_protocol == AF_INET) {
+ /* Set broadcast address based on address, prefixlen */
+ bcastaddr.sin_addr.s_addr =
+ /*LINTED*/
+ ((struct sockaddr_in *)&nifa->address)
+ ->sin_addr.s_addr |
+ htonl(0xffffffff >> nifa->prefix);
+
+ if ((rc = icfg_set_broadcast(newh, &bcastaddr))
+ != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "add_ip_address: "
+ "icfg_set_broadcast(%s) failed on %s: %s",
+ inet_ntoa(bcastaddr.sin_addr), ifname,
+ icfg_errmsg(rc));
+ }
+ }
+ }
+ if (rc == ICFG_SUCCESS) {
+ if (icfg_get_flags(newh, &flags) == ICFG_SUCCESS) {
+ if ((flags & IFF_UP) == 0)
+ rc = icfg_set_flags(newh, flags | IFF_UP);
+ } else {
+ nlog(LOG_DEBUG, "add_ip_address: couldn't bring up %s",
+ ifname);
+ }
+ }
+
+out:
+ /* Check if address was a duplicate */
+ if (rc == ICFG_DAD_FOUND || (flags & IFF_DUPLICATE) != 0) {
+ char *object_name;
+ nwam_error_t err;
+
+ nlog(LOG_INFO, "add_ip_address: "
+ "duplicate address detected on %s", ifname);
+ if ((err = nwam_ncu_name_to_typed_name(ifname,
+ NWAM_NCU_TYPE_INTERFACE, &object_name)) == NWAM_SUCCESS) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_MAINTENANCE,
+ NWAM_AUX_STATE_IF_DUPLICATE_ADDR);
+ free(object_name);
+ } else {
+ nlog(LOG_ERR, "add_ip_address: could not "
+ "create state event for %s: %s", ifname,
+ nwam_strerror(err));
+ }
+ rc = ICFG_DAD_FOUND;
+ }
+
+ if (h != newh)
+ icfg_close(newh);
+ icfg_close(h);
+
+ return (rc == ICFG_SUCCESS);
+}
+
+void
+nwamd_add_default_routes(nwamd_ncu_t *ncu)
+{
+ nwamd_if_t *nif = &ncu->ncu_node.u_if;
+ char str[INET6_ADDRSTRLEN];
+
+ if (nif->nwamd_if_ipv4 && nif->nwamd_if_ipv4_default_route_set) {
+ struct sockaddr_in v4dest, v4mask;
+
+ v4dest.sin_addr.s_addr = htonl(INADDR_ANY);
+ v4dest.sin_family = AF_INET;
+
+ v4mask.sin_addr.s_addr = 0;
+ v4mask.sin_family = AF_INET;
+
+ nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default "
+ "route %s", nwamd_sockaddr_to_str
+ ((struct sockaddr *)&nif->nwamd_if_ipv4_default_route, str,
+ sizeof (str)));
+ nwamd_add_route((struct sockaddr *)&v4dest,
+ (struct sockaddr *)&v4mask,
+ (struct sockaddr *)&nif->nwamd_if_ipv4_default_route,
+ ncu->ncu_name);
+ }
+
+ if (nif->nwamd_if_ipv6 && nif->nwamd_if_ipv6_default_route_set) {
+ struct sockaddr_in6 v6dest, v6mask;
+
+ (void) bzero(&v6dest, sizeof (struct sockaddr_in6));
+ v6dest.sin6_family = AF_INET6;
+
+ (void) bzero(&v6mask, sizeof (struct sockaddr_in6));
+ v6mask.sin6_family = AF_INET6;
+
+ nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default "
+ "route %s", nwamd_sockaddr_to_str
+ ((struct sockaddr *)&nif->nwamd_if_ipv6_default_route, str,
+ sizeof (str)));
+ nwamd_add_route((struct sockaddr *)&v6dest,
+ (struct sockaddr *)&v6mask,
+ (struct sockaddr *)&nif->nwamd_if_ipv6_default_route,
+ ncu->ncu_name);
+ }
+}
+
+void
+nwamd_dhcp_inform(nwamd_ncu_t *ncu)
+{
+ struct nwamd_dhcp_thread_arg *arg;
+ char *name = NULL;
+ pthread_attr_t attr;
+
+ arg = malloc(sizeof (*arg));
+ if (arg == NULL) {
+ nlog(LOG_ERR, "nwamd_dhcp_inform: error allocating memory "
+ "for dhcp request");
+ free(name);
+ return;
+ }
+
+ arg->name = strdup(ncu->ncu_name);
+ arg->type = DHCP_INFORM;
+ arg->timeout = DHCP_IPC_WAIT_DEFAULT;
+
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) {
+ nlog(LOG_ERR, "Cannot start dhcp thread");
+ free(name);
+ free(arg);
+ (void) pthread_attr_destroy(&attr);
+ return;
+ }
+ (void) pthread_attr_destroy(&attr);
+}
+
+static boolean_t
+addresses_match(const struct sockaddr *addr1, const struct sockaddr *addr2)
+{
+ if (addr1->sa_family != addr2->sa_family)
+ return (B_FALSE);
+
+ switch (addr1->sa_family) {
+ case AF_INET:
+ /*LINTED*/
+ return (memcmp(&((struct sockaddr_in *)addr1)->sin_addr,
+ /*LINTED*/
+ &((struct sockaddr_in *)addr2)->sin_addr,
+ sizeof (struct in_addr)) == 0);
+ case AF_INET6:
+ /*LINTED*/
+ return (memcmp(&((struct sockaddr_in6 *)addr1)->sin6_addr,
+ /*LINTED*/
+ &((struct sockaddr_in6 *)addr2)->sin6_addr,
+ sizeof (struct in6_addr)) == 0);
+ default:
+ return (B_FALSE);
+ }
+}
+
+/*
+ * Returns the nwamd_if_address structure for the given static address,
+ * NULL if not found.
+ */
+static struct nwamd_if_address *
+find_static_address(const struct sockaddr *addr, const nwamd_ncu_t *ncu)
+{
+ struct nwamd_if_address *n, *nifa = ncu->ncu_node.u_if.nwamd_if_list;
+ char str[INET6_ADDRSTRLEN];
+
+ nlog(LOG_DEBUG, "find_static_address %s",
+ nwamd_sockaddr_to_str(addr, str, sizeof (str)));
+ for (n = nifa; n != NULL; n = n->next) {
+ if (addresses_match(addr, &n->address))
+ return (n);
+ }
+ return (NULL);
+}
+
+/*
+ * Returns the nwamd_if_address structure representing the non-static address
+ * in the NCU. dhcp is used to detemrine if the DHCP (stateful for v6)
+ * structure is needed or the stateless/autoconf structure for the given
+ * family. dhcp should be B_TRUE if looking for v4. Will only return the
+ * nwamd_if_address if the relevant address is configured (v4 DHCP, v6
+ * stateless/stateful) for the NCU.
+ *
+ * Returns NULL if structure is not found.
+ */
+static struct nwamd_if_address *
+find_nonstatic_address(const nwamd_ncu_t *ncu, ushort_t family, boolean_t dhcp)
+{
+ struct nwamd_if_address *n, *nifa = ncu->ncu_node.u_if.nwamd_if_list;
+ const nwamd_if_t *u_if = &ncu->ncu_node.u_if;
+
+ nlog(LOG_DEBUG, "find_nonstatic_address: %s",
+ dhcp ? "dhcp" : "stateless");
+ for (n = nifa; n != NULL; n = n->next) {
+ if (family == AF_INET) {
+ if (!dhcp)
+ return (NULL);
+ if (n->address.sa_family == family && n->dhcp_if &&
+ u_if->nwamd_if_dhcp_configured)
+ return (n);
+ } else if (family == AF_INET6) {
+ if (n->address.sa_family == family) {
+ if (dhcp && n->dhcp_if &&
+ u_if->nwamd_if_stateful_configured)
+ return (n);
+ else if (!dhcp && n->stateless_if &&
+ u_if->nwamd_if_stateless_configured)
+ return (n);
+ }
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Sets "configured" nwam_if_address value for corresponding address.
+ * Used when we process IF_STATE events to handle RTM_NEWADDR/DELADDRs.
+ */
+static boolean_t
+update_address_configured_value(const struct sockaddr *configured_addr,
+ nwamd_ncu_t *ncu, boolean_t configured)
+{
+ struct nwamd_if_address *n;
+ char str[INET6_ADDRSTRLEN];
+
+ nlog(LOG_DEBUG, "update_address_configured_value(%s, %s, %s)",
+ nwamd_sockaddr_to_str(configured_addr, str, sizeof (str)),
+ ncu->ncu_name, configured ? "configure" : "unconfigure");
+ n = find_static_address(configured_addr, ncu);
+ if (n) {
+ n->configured = configured;
+ nlog(LOG_DEBUG, "update_address_configured_value: marking "
+ "address %s",
+ nwamd_sockaddr_to_str(&n->address, str, sizeof (str)));
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+void
+nwamd_update_addresses_unconfigured(nwamd_ncu_t *ncu, sa_family_t af)
+{
+ struct nwamd_if_address *n, *nifa = ncu->ncu_node.u_if.nwamd_if_list;
+
+ for (n = nifa; n != NULL; n = n->next)
+ if (af == AF_UNSPEC || n->address.sa_family == af) {
+ n->configured = B_FALSE;
+ nwamd_log_if_address(LOG_DEBUG, n);
+ }
+}
+
+/*
+ * Are one or more static addresses configured?
+ */
+boolean_t
+nwamd_static_addresses_configured(nwamd_ncu_t *ncu, sa_family_t family)
+{
+ struct nwamd_if_address *n;
+
+ for (n = ncu->ncu_node.u_if.nwamd_if_list; n != NULL; n = n->next) {
+ if ((family == AF_UNSPEC || family == n->address.sa_family) &&
+ n->configured && !n->dhcp_if && !n->stateless_if)
+ return (B_TRUE);
+ }
+ nlog(LOG_DEBUG, "no static addresses configured for %s", ncu->ncu_name);
+ return (B_FALSE);
+}
+
+/*
+ * Is DHCP probably managing an address on this index. We decide that it is
+ * probably managing an address if there is an interface with IFF_DHCP set
+ * that isn't in our set of static addresses. Note that IFF_DHCP gets set
+ * on static addresses when we do a dhcp inform and if that list has changed
+ * recently then the result of this function could be erronous.
+ */
+boolean_t
+nwamd_dhcp_managing(int protocol, nwamd_ncu_t *ncu)
+{
+ icfg_if_t *iflist;
+ icfg_handle_t ifh;
+ int numif, i;
+ struct sockaddr_storage addr;
+ socklen_t len;
+ int prefixlen;
+ uint64_t flags;
+ boolean_t rv = B_FALSE;
+
+ if (icfg_get_if_list(&iflist, &numif, protocol, ICFG_PLUMBED) !=
+ ICFG_SUCCESS) {
+ return (B_TRUE);
+ }
+ for (i = 0; i < numif; i++) {
+ if (strncmp(iflist[i].if_name, ncu->ncu_name,
+ strlen(ncu->ncu_name)) != 0)
+ continue;
+
+ if (icfg_open(&ifh, &iflist[i]) != ICFG_SUCCESS)
+ continue;
+
+ /* is this address an expected static one? */
+ len = sizeof (addr);
+ if (icfg_get_addr(ifh, (struct sockaddr *)&addr, &len,
+ &prefixlen, B_FALSE) != ICFG_SUCCESS ||
+ find_static_address((struct sockaddr *)&addr, ncu)
+ != NULL) {
+ icfg_close(ifh);
+ continue;
+ }
+
+ /*
+ * For IPv4, DHCPRUNNING flag is set when dhcpagent is in
+ * the process of getting an address, but doesn't have one
+ * yet (interface has 0.0.0.0). For IPv6, DHCPRUNNING flag
+ * is set on the link-local address if trying to get a
+ * stateful address. In both cases, consider the interface
+ * as not being managed by DHCP and skip checking of flags.
+ */
+ if ((protocol == AF_INET &&
+ ((struct sockaddr_in *)&addr)->sin_addr.s_addr ==
+ INADDR_ANY) ||
+ (protocol == AF_INET6 &&
+ IN6_IS_ADDR_LINKLOCAL(
+ &((struct sockaddr_in6 *)&addr)->sin6_addr))) {
+ icfg_close(ifh);
+ continue;
+ }
+
+ if (icfg_get_flags(ifh, &flags) == ICFG_SUCCESS &&
+ (flags & IFF_DHCPRUNNING)) {
+ /*
+ * If we get here we have an address that has the
+ * DHCP flag set and isn't an expected static address.
+ */
+ icfg_close(ifh);
+ rv = B_TRUE;
+ break;
+ }
+ }
+
+ icfg_free_if_list(iflist);
+ return (rv);
+}
+
+static boolean_t
+nwamd_v4_requested(nwamd_ncu_t *ncu)
+{
+ boolean_t anyv4_requested;
+ nwamd_if_t *u_if;
+
+ anyv4_requested = B_FALSE;
+ u_if = &ncu->ncu_node.u_if;
+ if (u_if->nwamd_if_dhcp_requested) {
+ anyv4_requested = B_TRUE;
+ } else {
+ struct nwamd_if_address *a;
+ for (a = u_if->nwamd_if_list;
+ a != NULL && a->address.sa_family != AF_INET;
+ a = a->next)
+ /* Empty loop body */;
+ if (a != NULL)
+ anyv4_requested = B_TRUE;
+ }
+
+ return (anyv4_requested);
+}
+
+static boolean_t
+nwamd_v6_requested(nwamd_ncu_t *ncu)
+{
+ boolean_t anyv6_requested;
+ nwamd_if_t *u_if;
+
+ anyv6_requested = B_FALSE;
+ u_if = &ncu->ncu_node.u_if;
+ if (u_if->nwamd_if_stateful_requested ||
+ u_if->nwamd_if_stateless_requested) {
+ anyv6_requested = B_TRUE;
+ } else {
+ struct nwamd_if_address *a;
+ for (a = u_if->nwamd_if_list;
+ a != NULL && a->address.sa_family != AF_INET6;
+ a = a->next)
+ /* Empty loop body */;
+ if (a != NULL)
+ anyv6_requested = B_TRUE;
+ }
+
+ return (anyv6_requested);
+}
+
+/*
+ * Bring up the ncu if we have the right combination of requested configuration
+ * and actual configuration and up is true, or bring down the ncu if no
+ * addresses are configured, and up is false.
+ */
+static void
+interface_ncu_up_down(nwamd_ncu_t *ncu, boolean_t up)
+{
+ boolean_t ncu_online;
+ char *name;
+
+ assert(ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE);
+
+ /*
+ * If V4 with or without V6 is configured then one of its interfaces
+ * needs to be up for the ncu to come online. If only V6 is requested
+ * then one of its interfaces needs to be up for the ncu to come online.
+ */
+ ncu_online = B_FALSE;
+ if (nwamd_v4_requested(ncu)) {
+ if (nwamd_dhcp_managing(AF_INET, ncu) ||
+ nwamd_static_addresses_configured(ncu, AF_INET))
+ ncu_online = B_TRUE;
+ } else if (nwamd_v6_requested(ncu)) {
+ if ((nwamd_dhcp_managing(AF_INET6, ncu) ||
+ stateless_running(ncu) ||
+ nwamd_static_addresses_configured(ncu, AF_INET6)))
+ ncu_online = B_TRUE;
+ }
+
+ if (nwam_ncu_name_to_typed_name(ncu->ncu_name, ncu->ncu_type, &name) !=
+ NWAM_SUCCESS) {
+ nlog(LOG_DEBUG, "interface_ncu_up_down: "
+ "nwam_ncu_name_to_typed_name failed");
+ return;
+ }
+ if (ncu_online && up) {
+ nlog(LOG_DEBUG, "interface_ncu_up_down: "
+ "bringing %s up", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name,
+ NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_UP);
+ } else if (!ncu_online && !up) {
+ nlog(LOG_DEBUG, "interface_ncu_up_down: "
+ "bringing %s down", name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_DOWN);
+ }
+
+ free(name);
+}
+
+static void
+interface_ncu_up(nwamd_ncu_t *ncu)
+{
+ interface_ncu_up_down(ncu, B_TRUE);
+}
+
+static void
+interface_ncu_down(nwamd_ncu_t *ncu)
+{
+ interface_ncu_up_down(ncu, B_FALSE);
+}
+
+/* Callback to find if DHCP is running on the interface index */
+static int
+flags_set_for_ifindex_cb(icfg_if_t *intf, void *arg, uint64_t flags_wanted)
+{
+ int *indexp = arg;
+ icfg_handle_t h;
+ int index;
+ uint64_t flags = 0;
+
+ if (icfg_open(&h, intf) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "flags_set_for_ifindex_cb: icfg_open failed");
+ return (0);
+ }
+ if (icfg_get_index(h, &index) != ICFG_SUCCESS) {
+ nlog(LOG_ERR,
+ "flags_set_for_ifindex_cb: icfg_get_index failed");
+ icfg_close(h);
+ return (0);
+ }
+ if (index != *indexp) {
+ icfg_close(h);
+ return (0);
+ }
+
+ if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
+ nlog(LOG_ERR,
+ "flags_set_for_ifindex_cb: icfg_get_flags failed");
+ }
+ icfg_close(h);
+ return ((flags & flags_wanted) == flags_wanted);
+}
+
+static int
+stateless_running_for_ifindex_cb(icfg_if_t *intf, void *arg)
+{
+ return (flags_set_for_ifindex_cb(intf, arg,
+ IFF_RUNNING | IFF_ADDRCONF | IFF_UP));
+}
+
+/*
+ * Is autoconf running on the interface with specified ifindex?
+ */
+static boolean_t
+stateless_running_for_ifindex(int ifindex)
+{
+ return (icfg_iterate_if(AF_INET6, ICFG_PLUMBED, &ifindex,
+ stateless_running_for_ifindex_cb) != 0);
+}
+
+static boolean_t
+stateless_running(const nwamd_ncu_t *ncu)
+{
+ int index;
+ icfg_if_t intf;
+ icfg_handle_t ifh;
+
+ intf.if_protocol = AF_INET6;
+ (void) strlcpy(intf.if_name, ncu->ncu_name, sizeof (intf.if_name));
+ if (icfg_open(&ifh, &intf) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "stateless_running: icfg_open(%s) failed",
+ ncu->ncu_name);
+ return (B_FALSE);
+ }
+
+ if (icfg_get_index(ifh, &index) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "stateless_running: icfg_get_index(%s) failed",
+ ncu->ncu_name);
+ return (B_FALSE);
+ }
+
+ icfg_close(ifh);
+
+ return (stateless_running_for_ifindex(index));
+}
+
+void
+nwamd_configure_interface_addresses(nwamd_ncu_t *ncu)
+{
+ struct nwamd_if_address *nifa = ncu->ncu_node.u_if.nwamd_if_list;
+ struct nwamd_if_address *n;
+ int num_configured_v4 = 0;
+ boolean_t add_logical_if;
+
+ nlog(LOG_DEBUG, "nwamd_configure_interface_addresses(%s)",
+ ncu->ncu_name);
+
+ /*
+ * Add static addresses. For IPv4, we only use the physical interface
+ * (i.e. not a logical interface) if DHCP has not been requested and
+ * this is the first address to be configured.
+ */
+ for (n = nifa; n != NULL; n = n->next) {
+ if (n->configured || n->dhcp_if || n->stateless_if)
+ continue;
+ switch (n->address.sa_family) {
+ case AF_INET:
+ add_logical_if = (num_configured_v4 > 0 ||
+ ncu->ncu_node.u_if.nwamd_if_dhcp_requested);
+ num_configured_v4++;
+ break;
+ case AF_INET6:
+ add_logical_if = B_TRUE;
+ break;
+ }
+ n->configured = add_ip_address(ncu->ncu_name, n,
+ add_logical_if);
+ }
+}
+
+static int
+lifnum_from_ifname(const char *ifname)
+{
+ char *lifstr = strchr(ifname, ':');
+
+ if (lifstr != NULL) {
+ lifstr++;
+ return (atoi(lifstr));
+ }
+ return (0);
+}
+
+/*
+ * Copies the ifname (with lifnum) associated with the given address.
+ * Returns B_TRUE if a match is found, B_FASLE otherwise.
+ */
+static boolean_t
+ifname_for_addr(const struct sockaddr *caddr, char *ifname, int len)
+{
+ struct sockaddr_in6 addr;
+ int numif, i, prefixlen;
+ icfg_if_t *iflist;
+ icfg_handle_t ifh;
+ socklen_t slen;
+
+ if (icfg_get_if_list(&iflist, &numif, caddr->sa_family, ICFG_PLUMBED)
+ != ICFG_SUCCESS) {
+ nlog(LOG_DEBUG, "ifname_for_addr: icfg_get_if_list failed");
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < numif; i++) {
+ if (icfg_open(&ifh, &iflist[i]) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "ifname_for_addr: icfg_open %s failed",
+ iflist[i].if_name);
+ continue;
+ }
+
+ slen = sizeof (addr);
+ if (icfg_get_addr(ifh, (struct sockaddr *)&addr,
+ &slen, &prefixlen, B_FALSE) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "ifname_for_addr: "
+ "icfg_get_addr %s failed", iflist[i].if_name);
+ } else {
+ /* Compare addresses */
+ if (addresses_match((struct sockaddr *)&addr, caddr)) {
+ (void) strlcpy(ifname, iflist[i].if_name, len);
+ icfg_close(ifh);
+ icfg_free_if_list(iflist);
+ return (B_TRUE);
+ }
+ }
+ icfg_close(ifh);
+ }
+ icfg_free_if_list(iflist);
+ return (B_FALSE);
+}
+
+/*
+ * This event tells us that an interface address has appeared or disappeared,
+ * or that the interface flags on an interface have changed.
+ */
+void
+nwamd_ncu_handle_if_state_event(nwamd_event_t event)
+{
+ nwam_event_t evm;
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwam_state_t state;
+ nwam_aux_state_t aux_state;
+
+ ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object);
+ if (ncu_obj == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_if_state_event: no object %s",
+ event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ ncu = ncu_obj->nwamd_object_data;
+ evm = event->event_msg;
+ state = ncu_obj->nwamd_object_state;
+ aux_state = ncu_obj->nwamd_object_aux_state;
+
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
+ "if %s, state (%s, %s)", event->event_object,
+ nwam_state_to_string(state), nwam_aux_state_to_string(aux_state));
+
+ /* Ensure object is in correct state to handle IF state events */
+ switch (state) {
+ case NWAM_STATE_OFFLINE_TO_ONLINE:
+ if (aux_state != NWAM_AUX_STATE_IF_WAITING_FOR_ADDR &&
+ aux_state != NWAM_AUX_STATE_IF_DHCP_TIMED_OUT) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
+ "if %s is in invalid aux state %s for IF_STATE "
+ "events", event->event_object,
+ nwam_aux_state_to_string(aux_state));
+ nwamd_event_do_not_send(event);
+ nwamd_object_release(ncu_obj);
+ return;
+ }
+ break;
+ case NWAM_STATE_ONLINE:
+ /*
+ * We can get addresses from DHCP after we've taken the interface down.
+ * We deal with those below.
+ */
+ case NWAM_STATE_ONLINE_TO_OFFLINE:
+ case NWAM_STATE_OFFLINE:
+ break;
+ default:
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
+ "if %s is in invalid state %s for IF_STATE events",
+ event->event_object, nwam_state_to_string(state));
+ nwamd_event_do_not_send(event);
+ nwamd_object_release(ncu_obj);
+ return;
+ }
+
+ if (evm->nwe_data.nwe_if_state.nwe_addr_valid) {
+ struct nwam_event_if_state *if_state;
+ boolean_t stateless_running;
+ char addrstr[INET6_ADDRSTRLEN], ifname[LIFNAMSIZ];
+ boolean_t v4dhcp_running;
+ boolean_t v6dhcp_running;
+ struct nwamd_if_address *nifa;
+ struct sockaddr *addr;
+ boolean_t static_addr;
+ icfg_if_t intf;
+ icfg_handle_t ifh;
+ nwamd_if_t *u_if;
+ ushort_t family;
+ uint64_t flags = 0;
+ int lifnum;
+
+ if_state = &evm->nwe_data.nwe_if_state;
+ u_if = &ncu->ncu_node.u_if;
+ family = if_state->nwe_addr.ss_family;
+ addr = (struct sockaddr *)&if_state->nwe_addr;
+
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_if_state_event: addr %s %s",
+ nwamd_sockaddr_to_str(addr, addrstr, sizeof (addrstr)),
+ evm->nwe_data.nwe_if_state.nwe_addr_added ?
+ "added" : "removed");
+
+ /* determine the interface name with lifnum */
+ if (if_state->nwe_addr_added) {
+ /* figure out the ifname for the address */
+ if (!ifname_for_addr(addr, ifname, sizeof (ifname))) {
+ nlog(LOG_ERR,
+ "nwamd_ncu_handle_if_state_event:"
+ "could not find ifname for %s", addrstr);
+ nwamd_event_do_not_send(event);
+ goto exit;
+ }
+ } else {
+ /*
+ * Figure out the ifname that had the address that was
+ * removed. The address is already gone from the
+ * interface, so cannot walk the interface list.
+ */
+ struct nwamd_if_address *n;
+
+ if ((n = find_static_address(addr, ncu)) == NULL &&
+ (n = find_nonstatic_address(ncu, family, B_TRUE))
+ == NULL &&
+ (n = find_nonstatic_address(ncu, family, B_FALSE))
+ == NULL) {
+ nlog(LOG_ERR,
+ "nwamd_ncu_handle_if_state_event: "
+ "could not find nwamd_if_address for %s",
+ addrstr);
+ nwamd_event_do_not_send(event);
+ goto exit;
+ }
+ (void) strlcpy(ifname, n->ifname, sizeof (ifname));
+ }
+
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
+ "ifname for %s is %s", addrstr, ifname);
+
+ /*
+ * Get interface flags using nwe_ifname as it is logical
+ * interface name.
+ */
+ intf.if_protocol = family;
+ (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name));
+ lifnum = lifnum_from_ifname(intf.if_name);
+
+ if (icfg_open(&ifh, &intf) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_if_state_event: can't "
+ "find if %s", intf.if_name);
+ nwamd_event_do_not_send(event);
+ goto exit;
+ }
+ if (icfg_get_flags(ifh, &flags) != ICFG_SUCCESS) {
+ nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: can't "
+ "get flags for %s", icfg_if_name(ifh));
+ /*
+ * If the interface is unplumbed, icfg_get_flags()
+ * will fail. Don't exit, continue with empty flags.
+ */
+ if (if_state->nwe_addr_added) {
+ icfg_close(ifh);
+ goto exit;
+ }
+ }
+
+ if (family == AF_INET && !if_state->nwe_addr_added) {
+ /*
+ * 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).
+ */
+ /*LINTED*/
+ if (((struct sockaddr_in *)addr)->sin_addr.s_addr
+ == INADDR_ANY) {
+ socklen_t slen;
+ struct sockaddr_in s;
+ int pfxlen;
+
+ if ((flags & IFF_UP) &&
+ !(flags & IFF_RUNNING) &&
+ icfg_get_addr(ifh, (struct sockaddr *)&s,
+ &slen, &pfxlen, B_FALSE) == ICFG_SUCCESS &&
+ s.sin_addr.s_addr != INADDR_ANY) {
+ nlog(LOG_DEBUG, "bug workaround: "
+ "clear out addr %s on %s",
+ inet_ntoa(s.sin_addr), ifname);
+ s.sin_addr.s_addr = INADDR_ANY;
+ (void) icfg_set_addr(ifh,
+ (const struct sockaddr *)&s, slen);
+ }
+ icfg_close(ifh);
+ goto exit;
+ }
+ }
+
+ /*
+ * Has address really been removed? Sometimes spurious
+ * RTM_DELADDRs are generated, so we need to ensure that
+ * the address is really gone. If IFF_DUPLICATE is set,
+ * we're getting the RTM_DELADDR due to DAD, so don't test
+ * in that case.
+ */
+ if (!if_state->nwe_addr_added && !(flags & IFF_DUPLICATE)) {
+ struct sockaddr_storage ifaddr;
+ socklen_t len;
+ int plen;
+
+ len = family == AF_INET ? sizeof (struct sockaddr_in) :
+ sizeof (struct sockaddr_in6);
+ if (icfg_get_addr(ifh, (struct sockaddr *)&ifaddr, &len,
+ &plen, B_FALSE) == ICFG_SUCCESS &&
+ addresses_match(addr, (struct sockaddr *)&ifaddr)) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_if_state_event: "
+ "address %s is not really gone from %s, "
+ "ignoring IF_STATE event",
+ addrstr, intf.if_name);
+ icfg_close(ifh);
+ nwamd_event_do_not_send(event);
+ goto exit;
+ }
+ }
+ icfg_close(ifh);
+
+ stateless_running = (family == AF_INET6) &&
+ ((flags & STATELESS_RUNNING) == STATELESS_RUNNING);
+ v4dhcp_running = (family == AF_INET) &&
+ ((flags & DHCP_RUNNING) == DHCP_RUNNING);
+ v6dhcp_running = (family == AF_INET6) &&
+ ((flags & DHCP_RUNNING) == DHCP_RUNNING);
+ static_addr = (find_static_address(addr, ncu) != NULL);
+
+ if (if_state->nwe_addr_added) {
+ /*
+ * Address has been added.
+ *
+ * We need to make sure that we really want to keep
+ * this address. There is a race where we requested an
+ * address but by the time we got here we don't really
+ * want it and need to remove it.
+ *
+ * [Note that since we use DHCP inform on interfaces
+ * with static addresses that they will also have the
+ * DHCP flag set on the interface.]
+ *
+ * Once we decide we want the address adjust the ncu
+ * state accordingly. For example if this address is
+ * enough move online.
+ */
+
+ /* Figure out if we want to keep this address. */
+ if (static_addr) {
+ nifa = find_static_address(addr, ncu);
+ assert(nifa != NULL);
+ nifa->configured = B_TRUE;
+ (void) strlcpy(nifa->ifname, ifname,
+ sizeof (nifa->ifname));
+ } else if (u_if->nwamd_if_dhcp_requested &&
+ v4dhcp_running) {
+ u_if->nwamd_if_dhcp_configured = B_TRUE;
+ nifa = find_nonstatic_address(ncu, family,
+ B_TRUE);
+ assert(nifa != NULL);
+ (void) strlcpy(nifa->ifname, ifname,
+ sizeof (nifa->ifname));
+ } else if (u_if->nwamd_if_stateful_requested &&
+ v6dhcp_running) {
+ u_if->nwamd_if_stateful_configured = B_TRUE;
+ nifa = find_nonstatic_address(ncu, family,
+ B_TRUE);
+ assert(nifa != NULL);
+ (void) strlcpy(nifa->ifname, ifname,
+ sizeof (nifa->ifname));
+ } else if (u_if->nwamd_if_stateless_requested &&
+ stateless_running) {
+ u_if->nwamd_if_stateless_configured = B_TRUE;
+ nifa = find_nonstatic_address(ncu, family,
+ B_FALSE);
+ assert(nifa != NULL);
+ (void) strlcpy(nifa->ifname, ifname,
+ sizeof (nifa->ifname));
+ } else {
+ /*
+ * This is something we didn't expect. Remove
+ * it by unplumbing the logical interface.
+ */
+ if (u_if->nwamd_if_dhcp_requested &&
+ v4dhcp_running)
+ nwamd_dhcp_release(ncu->ncu_name);
+ if (lifnum == 0) {
+ nwamd_down_interface(ncu->ncu_name,
+ lifnum, family);
+ interface_ncu_down(ncu);
+ } else {
+ nwamd_unplumb_interface(ncu, lifnum,
+ family);
+ }
+ goto exit;
+ }
+
+ /*
+ * The address looks valid so mark configured and
+ * move online if we either have a v4 address if
+ * v4 is configured or a v6 address if only v6 is
+ * configured.
+ */
+ (void) update_address_configured_value(addr, ncu,
+ B_TRUE);
+ if (state != NWAM_STATE_ONLINE)
+ interface_ncu_up(ncu);
+
+ /*
+ * Refresh network/location since we may also have other
+ * DHCP information. We might have to restore it first
+ * in case it is in maintenance.
+ */
+ nlog(LOG_DEBUG, "nwamd_handle_if_state_event: "
+ "refreshing %s as we may have other "
+ "DHCP information", NET_LOC_FMRI);
+ (void) smf_restore_instance(NET_LOC_FMRI);
+ if (smf_refresh_instance(NET_LOC_FMRI) != 0) {
+ nlog(LOG_ERR,
+ "nwamd_ncu_handle_if_state_"
+ "event: refresh of %s "
+ "failed", NET_LOC_FMRI);
+ }
+ } else if (state == NWAM_STATE_ONLINE ||
+ state == NWAM_STATE_OFFLINE_TO_ONLINE) {
+ /*
+ * Address has been removed. Only pay attention to
+ * disappearing addresses if we are online or coming
+ * online.
+ *
+ * Undo whatever configuration is necessary. Note
+ * that this may or may not cause the NCU to go down.
+ * We can get RTM_DELADDRs for duplicate addresses
+ * so deal with this seperately.
+ */
+ if (static_addr) {
+ (void) update_address_configured_value(addr,
+ ncu, B_FALSE);
+ } else if (family == AF_INET) {
+ u_if->nwamd_if_dhcp_configured = B_FALSE;
+ } else if (family == AF_INET6) {
+ /*
+ * The address is already gone. I'm not sure
+ * how we figure out if this address is
+ * stateful (DHCP) or stateless. When we
+ * are managing IPv6 more explicitly this will
+ * have to be done more carefully.
+ */
+ u_if->nwamd_if_stateful_configured = B_FALSE;
+ u_if->nwamd_if_stateless_configured = B_FALSE;
+ }
+
+ if (flags & IFF_DUPLICATE) {
+ nlog(LOG_INFO,
+ "nwamd_ncu_handle_if_state_event: "
+ "duplicate address detected on %s",
+ ncu->ncu_name);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object,
+ NWAM_STATE_MAINTENANCE,
+ NWAM_AUX_STATE_IF_DUPLICATE_ADDR);
+ } else {
+ interface_ncu_down(ncu);
+ }
+ }
+ }
+exit:
+ nwamd_object_release(ncu_obj);
+}
+
+void
+nwamd_ncu_handle_if_action_event(nwamd_event_t event)
+{
+ nwamd_object_t ncu_obj;
+
+ nlog(LOG_DEBUG, "if action event %s",
+ event->event_object[0] == '\0' ? "n/a" : event->event_object);
+
+ ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object);
+ if (ncu_obj == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_if_action_event: no object");
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ nwamd_object_release(ncu_obj);
+}
+
+/*
+ * This function downs any logical interface and just zeros the address off of
+ * the physical interface (logical interface 0). If you want to unplumb 0 then
+ * you need to call nwamd_unplumb_interface() directly.
+ */
+static void
+nwamd_down_interface(const char *linkname, uint_t lifnum, int family)
+{
+ uint64_t flags;
+ icfg_if_t intf;
+ icfg_handle_t h;
+ icfg_error_t rc;
+
+ if (linkname == NULL) {
+ nlog(LOG_ERR, "nwamd_down_interface: linkname null");
+ return;
+ }
+
+ (void) nwamd_link_to_ifname(linkname, lifnum, intf.if_name,
+ sizeof (intf.if_name));
+ intf.if_protocol = family;
+
+ rc = icfg_open(&h, &intf);
+ if (rc != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_down_interface: icfg_open failed for %s: "
+ "%s", intf.if_name, icfg_errmsg(rc));
+ return;
+ }
+
+ if (lifnum == 0) {
+ struct sockaddr_in6 addr;
+
+ (void) memset(&addr, 0, sizeof (addr));
+ addr.sin6_family = family;
+ if (icfg_set_addr(h, (struct sockaddr *)&addr,
+ family == AF_INET ? sizeof (struct sockaddr_in) :
+ sizeof (struct sockaddr_in6)) != ICFG_SUCCESS)
+ nlog(LOG_ERR, "nwamd_down_interface couldn't zero "
+ "address on %s", h->ifh_interface.if_name);
+ } else {
+ if (icfg_get_flags(h, &flags) == ICFG_SUCCESS) {
+ if (icfg_set_flags(h, flags & ~IFF_UP) != ICFG_SUCCESS)
+ nlog(LOG_ERR, "nwamd_down_interface: couldn't "
+ "bring %s down", h->ifh_interface.if_name);
+ } else {
+ nlog(LOG_ERR, "nwamd_down_interface: icfg_get_flags "
+ "failed on %s", h->ifh_interface.if_name);
+ }
+ }
+
+ icfg_close(h);
+}
+
+static void
+nwamd_plumb_unplumb_interface(nwamd_ncu_t *ncu, uint_t lifnum,
+ int af, boolean_t plumb)
+{
+ uint64_t flags;
+ icfg_if_t intf;
+ icfg_handle_t h;
+ icfg_error_t rc;
+ nwamd_if_t *u_if;
+ const char *linkname = ncu->ncu_name;
+
+ if (linkname == NULL) {
+ nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: linkname null");
+ return;
+ }
+
+ (void) nwamd_link_to_ifname(linkname, lifnum, intf.if_name,
+ sizeof (intf.if_name));
+ intf.if_protocol = af;
+
+ nlog(LOG_DEBUG, "nwamd_plumb_unplumb_interface: %s %s on link %s",
+ plumb ? "plumbing" : "unplumbing",
+ af == AF_INET ? "IPv4" : "IPv6", linkname);
+
+ /*
+ * Before unplumbing, do a DHCP release if lifnum is 0. Otherwise
+ * dhcpagent can get confused.
+ */
+ if (!plumb && af == AF_INET && lifnum == 0)
+ nwamd_dhcp_release(ncu->ncu_name);
+
+ rc = icfg_open(&h, &intf);
+ if (rc != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: "
+ "icfg_open failed for %s: %s", intf.if_name,
+ icfg_errmsg(rc));
+ return;
+ }
+ rc = plumb ? icfg_plumb(h) : icfg_unplumb(h);
+
+ if (rc != ICFG_SUCCESS) {
+ if ((plumb && rc != ICFG_EXISTS) ||
+ (!plumb && rc != ICFG_NO_EXIST)) {
+ nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: "
+ "%s %s failed for %s: %s",
+ plumb ? "plumb" : "unplumb",
+ af == AF_INET ? "IPv4" : "IPv6",
+ intf.if_name, icfg_errmsg(rc));
+ }
+ } else if (plumb) {
+ if (icfg_get_flags(h, &flags) == ICFG_SUCCESS &&
+ (flags & IFF_UP) == 0) {
+ if (icfg_set_flags(h, flags | IFF_UP) != ICFG_SUCCESS)
+ nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: "
+ "couldn't bring %s up",
+ h->ifh_interface.if_name);
+ } else {
+ nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: "
+ "icfg_get_flags failed on %s",
+ h->ifh_interface.if_name);
+ }
+ }
+
+ u_if = &ncu->ncu_node.u_if;
+ if (!plumb) {
+ nwamd_update_addresses_unconfigured(ncu, af);
+ switch (af) {
+ case AF_INET:
+ u_if->nwamd_if_dhcp_configured = B_FALSE;
+ break;
+ case AF_INET6:
+ u_if->nwamd_if_stateful_configured = B_FALSE;
+ u_if->nwamd_if_stateless_configured = B_FALSE;
+ break;
+ }
+ }
+
+ icfg_close(h);
+}
+
+void
+nwamd_plumb_interface(nwamd_ncu_t *ncu, uint_t lifnum, int af)
+{
+ nwamd_plumb_unplumb_interface(ncu, lifnum, af, B_TRUE);
+}
+
+void
+nwamd_unplumb_interface(nwamd_ncu_t *ncu, uint_t lifnum, int af)
+{
+ nwamd_plumb_unplumb_interface(ncu, lifnum, af, B_FALSE);
+}
+
+static void *
+start_dhcp_thread(void *arg)
+{
+ struct nwamd_dhcp_thread_arg *thread_arg;
+ dhcp_ipc_reply_t *reply = NULL;
+ dhcp_ipc_request_t *request;
+ dhcp_ipc_type_t type;
+ int timeout;
+ char *name;
+ int rc, retries = 0;
+
+ thread_arg = (struct nwamd_dhcp_thread_arg *)arg;
+ timeout = thread_arg->timeout;
+ name = thread_arg->name;
+ type = thread_arg->type;
+
+ /* Try starting agent, though it may already be there */
+ nwamd_to_root();
+ rc = dhcp_start_agent(DHCP_IPC_MAX_WAIT);
+ nwamd_from_root();
+ if (rc == -1) {
+ nlog(LOG_DEBUG, "Unable to start %s", DHCP_AGENT_PATH);
+ goto failed;
+ }
+retry:
+ /* Now allocate and send the request */
+ request = dhcp_ipc_alloc_request(type, name, NULL, 0,
+ DHCP_TYPE_NONE);
+ if (request == NULL) {
+ nlog(LOG_DEBUG, "start_dhcp: dhcp_ipc_alloc_request : %s",
+ strerror(errno));
+ goto failed;
+ }
+
+ rc = dhcp_ipc_make_request(request, &reply, timeout);
+ free(request);
+ if (rc != 0) {
+ nlog(LOG_DEBUG, "start_dhcp %s: %s", name,
+ dhcp_ipc_strerror(rc));
+ goto failed;
+ }
+
+ rc = reply->return_code;
+ if (rc != 0) {
+ if (rc == DHCP_IPC_E_TIMEOUT && timeout == 0) {
+ goto failed;
+ }
+
+ /*
+ * DHCP timed out: change state for this NCU and enqueue
+ * event to check NCU priority-groups. Only care for
+ * DHCP requests (not informs).
+ */
+ if (rc == DHCP_IPC_E_TIMEOUT && type != DHCP_INFORM) {
+ char *object_name;
+
+ nlog(LOG_INFO, "start_dhcp: DHCP timed out for %s",
+ name);
+ if (nwam_ncu_name_to_typed_name(name,
+ NWAM_NCU_TYPE_INTERFACE, &object_name)
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "start_dhcp: "
+ "nwam_ncu_name_to_typed_name failed");
+ goto failed;
+ }
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_IF_DHCP_TIMED_OUT);
+ nwamd_create_ncu_check_event(0);
+
+ free(object_name);
+ goto failed;
+
+ } else if (rc == DHCP_IPC_E_RUNNING) {
+ /*
+ * DHCP is already running. Check if IP address is
+ * already configured on the interface.
+ */
+
+ icfg_handle_t h;
+ icfg_if_t intf;
+ struct sockaddr_in sin;
+ socklen_t alen = sizeof (struct sockaddr_in);
+ int plen, index;
+ uint64_t flags;
+ nwamd_event_t ip_event;
+
+ nlog(LOG_ERR, "start_dhcp: DHCP already running on %s",
+ name);
+
+ (void) strlcpy(intf.if_name, name,
+ sizeof (intf.if_name));
+ intf.if_protocol = AF_INET;
+
+ if (icfg_open(&h, &intf) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "start_dhcp: "
+ "icfg_open failed on %s", name);
+ goto failed;
+ }
+
+ /* Get address */
+ if (icfg_get_addr(h, (struct sockaddr *)&sin, &alen,
+ &plen, B_FALSE) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "start_dhcp: "
+ "icfg_get_addr failed on %s: %s",
+ name, strerror(errno));
+ goto bail;
+ }
+ /* Check if 0.0.0.0 */
+ if (sin.sin_addr.s_addr == INADDR_ANY) {
+ nlog(LOG_ERR, "start_dhcp: empty address on %s",
+ name);
+ goto bail;
+ }
+
+ /* valid address exists, get the flags, index of intf */
+ if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "start_dhcp: "
+ "icfg_get_flags failed on %s", name);
+ goto bail;
+ }
+ if (icfg_get_index(h, &index) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "start_dhcp: "
+ "icfg_get_index failed on %s", name);
+ goto bail;
+ }
+
+ /* synthesize an IF_STATE event with the intf's flags */
+ ip_event = nwamd_event_init_if_state(name, flags,
+ B_TRUE, index, (struct sockaddr *)&sin);
+ if (ip_event != NULL)
+ nwamd_event_enqueue(ip_event);
+bail:
+ icfg_close(h);
+ goto failed;
+
+ } else if ((rc == DHCP_IPC_E_SOCKET ||
+ rc == DHCP_IPC_E_INVIF) && retries++ < NWAMD_DHCP_RETRIES) {
+ /*
+ * Retry DHCP request as we may have been unplumbing
+ * as part of the configuration phase.
+ */
+ nlog(LOG_ERR, "start_dhcp %s: %s; will retry in %d sec",
+ name, dhcp_ipc_strerror(rc),
+ rc == DHCP_IPC_E_INVIF ?
+ NWAMD_DHCP_RETRY_WAIT_TIME : 0);
+ if (rc == DHCP_IPC_E_INVIF)
+ (void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME);
+ goto retry;
+ } else {
+ nlog(LOG_ERR, "start_dhcp %s: %s", name,
+ dhcp_ipc_strerror(rc));
+ goto failed;
+ }
+ }
+
+ /* If status was the command, then output the results */
+ if (DHCP_IPC_CMD(type) == DHCP_STATUS) {
+ nlog(LOG_DEBUG, "%s", dhcp_status_hdr_string());
+ nlog(LOG_DEBUG, "%s", dhcp_status_reply_to_string(reply));
+ }
+
+failed:
+ free(reply);
+ if (arg != NULL) {
+ free(name);
+ free(arg);
+ }
+ return (NULL);
+}
+
+void
+nwamd_start_dhcp(nwamd_ncu_t *ncu)
+{
+ struct nwamd_dhcp_thread_arg *arg;
+ char *name = NULL;
+ pthread_attr_t attr;
+
+ nlog(LOG_DEBUG, "nwamd_start_dhcp: starting DHCP for %s %d",
+ ncu->ncu_name, ncu->ncu_type);
+
+ arg = malloc(sizeof (*arg));
+ if (arg == NULL) {
+ nlog(LOG_ERR, "nwamd_start_dhcp: error allocating memory "
+ "for dhcp request");
+ free(name);
+ return;
+ }
+
+ arg->name = strdup(ncu->ncu_name);
+ arg->type = DHCP_START;
+ arg->timeout = ncu_wait_time;
+
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) {
+ nlog(LOG_ERR, "nwamd_start_dhcp: cannot start dhcp thread");
+ free(name);
+ free(arg);
+ (void) pthread_attr_destroy(&attr);
+ return;
+ }
+ (void) pthread_attr_destroy(&attr);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/ncu_phys.c b/usr/src/cmd/cmd-inet/lib/nwamd/ncu_phys.c
new file mode 100644
index 0000000000..2f284d5492
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/ncu_phys.c
@@ -0,0 +1,1925 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <execinfo.h>
+#include <kstat.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlstat.h>
+#include <libdlwlan.h>
+#include <libinetutil.h>
+#include <libnwam.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <libdlpi.h>
+#include <ucontext.h>
+
+#include "events.h"
+#include "llp.h"
+#include "objects.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "known_wlans.h"
+#include "util.h"
+
+/*
+ * ncu_phys.c - contains routines that are physical-link specific.
+ * Mostly WiFi code.
+ */
+
+char *
+nwamd_link_to_ifname(const char *linkname, int lifnum, char *ifname, int len)
+{
+ if (lifnum == 0) {
+ (void) strlcpy(ifname, linkname, len);
+ } else {
+ (void) snprintf(ifname, len, "%s:%d", linkname, lifnum);
+ }
+ return (ifname);
+}
+
+/*
+ * Get link state from kstats. Used to determine initial link state for
+ * cases where drivers do not support DL_NOTE_LINK_UP/DOWN. If link
+ * state is LINK_STATE_UNKNOWN, we assume the link is up and the IP NCU
+ * timeout will cause us to move on to other links.
+ */
+link_state_t
+nwamd_get_link_state(const char *name)
+{
+ kstat_ctl_t *kcp;
+ kstat_t *ksp;
+ char module[DLPI_LINKNAME_MAX];
+ uint_t instance;
+ link_state_t link_state = LINK_STATE_UNKNOWN;
+
+ if ((kcp = kstat_open()) == NULL)
+ return (link_state);
+
+ if (dlpi_parselink(name, module, &instance) != DLPI_SUCCESS)
+ goto out;
+
+ if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL) {
+ /*
+ * The kstat query could fail if the underlying MAC
+ * driver was already detached.
+ */
+ goto out;
+ }
+
+ if (kstat_read(kcp, ksp, NULL) == -1)
+ goto out;
+
+ (void) dladm_kstat_value(ksp, "link_state", KSTAT_DATA_UINT32,
+ &link_state);
+
+out:
+ (void) kstat_close(kcp);
+
+ return (link_state);
+}
+
+/*
+ * Set/unset link propeties. At present, these are MAC address, link MTU and
+ * autopush modules. We set MAC address last as setting it may cause a chip
+ * reset which can prevent other device property setting succeeding.
+ */
+void
+nwamd_set_unset_link_properties(nwamd_ncu_t *ncu, boolean_t set)
+{
+ dlpi_handle_t dh = ncu->ncu_node.u_link.nwamd_link_dhp;
+ char *addr = set ? ncu->ncu_node.u_link.nwamd_link_mac_addr : NULL;
+ uint64_t mtu = set ? ncu->ncu_node.u_link.nwamd_link_mtu : 0;
+ char **autopush = set ? ncu->ncu_node.u_link.nwamd_link_autopush : NULL;
+ uint_t num_autopush = set ?
+ ncu->ncu_node.u_link.nwamd_link_num_autopush : 0;
+ uchar_t *hwaddr = NULL, curraddr[DLPI_PHYSADDR_MAX];
+ size_t hwaddrlen = DLPI_PHYSADDR_MAX;
+ int retval;
+ dladm_status_t status;
+ char mtustr[DLADM_PROP_VAL_MAX];
+ char *cp;
+ char errmsg[DLADM_STRSIZE];
+ uint_t cnt = 1;
+
+ /*
+ * Set MTU here - either default value (if mtu == 0 indicating it has
+ * not been set) or specified value.
+ */
+ if (mtu == 0) {
+ cp = mtustr;
+ status = dladm_get_linkprop(dld_handle,
+ ncu->ncu_node.u_link.nwamd_link_id, DLADM_PROP_VAL_DEFAULT,
+ "mtu", &cp, &cnt);
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
+ "dladm_get_linkprop failed: %s",
+ dladm_status2str(status, errmsg));
+ return;
+ }
+ } else {
+ (void) snprintf(mtustr, DLADM_PROP_VAL_MAX, "%lld", mtu);
+ }
+
+ cp = mtustr;
+
+ nlog(LOG_DEBUG, "nwamd_set_unset_link_properties: setting MTU of %s "
+ "for link %s", mtustr, ncu->ncu_name);
+ status = dladm_set_linkprop(dld_handle,
+ ncu->ncu_node.u_link.nwamd_link_id, "mtu", &cp, 1,
+ DLADM_OPT_ACTIVE);
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
+ "dladm_set_linkprop failed: %s",
+ dladm_status2str(status, errmsg));
+ }
+
+ nlog(LOG_DEBUG, "nwamd_set_unset_link_properties: setting %d "
+ "autopush module for link %s", num_autopush, ncu->ncu_name);
+ status = dladm_set_linkprop(dld_handle,
+ ncu->ncu_node.u_link.nwamd_link_id, "autopush", autopush,
+ num_autopush, DLADM_OPT_ACTIVE);
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
+ "dladm_set_linkprop failed for autopush property: %s",
+ dladm_status2str(status, errmsg));
+ }
+
+ /*
+ * Set physical address - either factory (if link_mac_addr is NULL
+ * or we are unsetting properties) or specified MAC address string.
+ */
+ if (addr == NULL) {
+ if ((hwaddr = calloc(1, DLPI_PHYSADDR_MAX)) == NULL) {
+ nlog(LOG_ERR,
+ "nwamd_set_unset_link_properties: malloc() failed");
+ return;
+ }
+ if ((retval = dlpi_get_physaddr(dh, DL_FACT_PHYS_ADDR,
+ hwaddr, &hwaddrlen)) != DLPI_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_set_unset_link_properties: "
+ "could not get physical address for %s: %s",
+ ncu->ncu_name, dlpi_strerror(retval));
+ free(hwaddr);
+ return;
+ }
+ } else {
+ int addrlen = hwaddrlen;
+ if ((hwaddr = _link_aton(addr, &addrlen)) == NULL) {
+ if (addrlen == -1) {
+ nlog(LOG_ERR,
+ "nwamd_set_unset_link_properties: "
+ "%s: bad address for %s",
+ addr, ncu->ncu_name);
+ return;
+ } else {
+ nlog(LOG_ERR, "nwamd_set_unset_link_properties:"
+ " malloc() failed");
+ return;
+ }
+ }
+ hwaddrlen = addrlen;
+ }
+
+ /*
+ * Only set physical address if desired address differs from current -
+ * this avoids unnecessary chip resets for some drivers.
+ */
+ retval = dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, curraddr,
+ &hwaddrlen);
+ if (retval != DLPI_SUCCESS || bcmp(curraddr, hwaddr, hwaddrlen) != 0) {
+ retval = dlpi_set_physaddr(dh, DL_CURR_PHYS_ADDR, hwaddr,
+ hwaddrlen);
+ if (retval != DLPI_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_set_unset_link_properties:"
+ "failed setting mac address on %s: %s",
+ ncu->ncu_name, dlpi_strerror(retval));
+ }
+ }
+ free(hwaddr);
+}
+
+#define WLAN_ENC(sec) \
+ ((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : \
+ (sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
+
+#define NEED_ENC(sec) \
+ (sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
+
+#define WIRELESS_LAN_INIT_COUNT 8
+
+/*
+ * The variable wireless_scan_level specifies the signal level
+ * that we will initiate connections to previously-visited APs
+ * at when we are in the connected state.
+ */
+dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_WEAK;
+
+/*
+ * The variable wireless_scan_interval specifies how often the periodic
+ * scan occurs.
+ */
+uint64_t wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT;
+
+/*
+ * The variable wireless_autoconf specifies if we use dladm_wlan_autoconf()
+ * to connect.
+ */
+boolean_t wireless_autoconf = B_FALSE;
+
+/*
+ * The variable wireless_strict_bssid specifies if we only connect
+ * to WLANs with BSSIDs that we previously connected to.
+ */
+boolean_t wireless_strict_bssid = B_FALSE;
+
+/*
+ * We need to ensure scan or connect threads do not run concurrently
+ * on any links - otherwise we get radio interference. Acquire this
+ * lock on entering scan/connect threads to prevent this.
+ */
+pthread_mutex_t wireless_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void
+scanconnect_entry(void)
+{
+ (void) pthread_mutex_lock(&wireless_mutex);
+}
+
+static void
+scanconnect_exit(void)
+{
+ (void) pthread_mutex_unlock(&wireless_mutex);
+}
+
+/*
+ * Below are functions used to handle storage/retrieval of keys
+ * for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
+ * and dladm_get_secobj().
+ */
+
+/*
+ * Convert key hexascii string to raw secobj value. This
+ * code is very similar to convert_secobj() in dladm.c, it would
+ * be good to have a libdladm function to convert values.
+ */
+static int
+key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
+ dladm_secobj_class_t class)
+{
+ size_t buf_len = strlen(buf);
+
+ nlog(LOG_DEBUG, "before: key_string_to_secobj_value: buf_len = %d",
+ buf_len);
+ if (buf_len == 0) {
+ /* length zero means "delete" */
+ return (0);
+ }
+
+ if (buf[buf_len - 1] == '\n')
+ buf[--buf_len] = '\0';
+
+ nlog(LOG_DEBUG, "after: key_string_to_secobj_value: buf_len = %d",
+ buf_len);
+
+ if (class == DLADM_SECOBJ_CLASS_WPA) {
+ /*
+ * Per IEEE802.11i spec, the Pre-shared key (PSK) length should
+ * be between 8 and 63.
+ */
+ if (buf_len < 8 || buf_len > 63) {
+ nlog(LOG_ERR,
+ "key_string_to_secobj_value:"
+ " invalid WPA key length: buf_len = %d", buf_len);
+ return (-1);
+ }
+ (void) memcpy(obj_val, buf, (uint_t)buf_len);
+ *obj_lenp = buf_len;
+ return (0);
+ }
+
+ switch (buf_len) {
+ case 5: /* ASCII key sizes */
+ case 13:
+ (void) memcpy(obj_val, buf, (uint_t)buf_len);
+ *obj_lenp = (uint_t)buf_len;
+ break;
+ case 10:
+ case 26: /* Hex key sizes, not preceded by 0x */
+ if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
+ != 0) {
+ nlog(LOG_ERR,
+ "key_string_to_secobj_value: invalid WEP key");
+ return (-1);
+ }
+ break;
+ case 12:
+ case 28: /* Hex key sizes, preceded by 0x */
+ if (strncmp(buf, "0x", 2) != 0 ||
+ hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
+ obj_lenp) != 0) {
+ nlog(LOG_ERR,
+ "key_string_to_secobj_value: invalid WEP key");
+ return (-1);
+ }
+ break;
+ default:
+ syslog(LOG_ERR,
+ "key_string_to_secobj_value: invalid WEP key length");
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Print the key name format into the appropriate field, then convert any ":"
+ * characters to ".", as ":[1-4]" is the slot indicator, which otherwise
+ * would trip us up. Invalid characters for secobj names are ignored.
+ * The fourth 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.)
+ */
+void
+nwamd_set_key_name(const char *essid, const char *bssid, char *name, size_t nsz)
+{
+ int i, j;
+ char secobj_name[DLADM_WLAN_MAX_KEYNAME_LEN];
+
+ /* create a concatenated string with essid and bssid */
+ if (bssid == NULL || bssid[0] == '\0') {
+ (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s",
+ essid);
+ } else {
+ (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s-%s",
+ essid, bssid);
+ }
+
+ /* copy only valid chars to the return string, terminating with \0 */
+ i = 0; /* index into secobj_name */
+ j = 0; /* index into name */
+ while (secobj_name[i] != '\0') {
+ if (j == nsz - 1)
+ break;
+
+ if (secobj_name[i] == ':') {
+ name[j] = '.';
+ j++;
+ } else if (isalnum(secobj_name[i]) ||
+ secobj_name[i] == '.' || secobj_name[i] == '-' ||
+ secobj_name[i] == '_') {
+ name[j] = secobj_name[i];
+ j++;
+ }
+ i++;
+ }
+ name[j] = '\0';
+}
+
+nwam_error_t
+nwamd_wlan_set_key(const char *linkname, const char *essid, const char *bssid,
+ uint32_t security_mode, uint_t keyslot, char *raw_key)
+{
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+ uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
+ uint_t obj_len = sizeof (obj_val);
+ char obj_name[DLADM_SECOBJ_NAME_MAX];
+ dladm_status_t status;
+ char errmsg[DLADM_STRSIZE];
+ dladm_secobj_class_t class;
+
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
+ == NULL) {
+ nlog(LOG_ERR, "nwamd_wlan_set_key: could not find object "
+ "for link %s", linkname);
+ return (NWAM_ENTITY_NOT_FOUND);
+ }
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ nlog(LOG_DEBUG, "nwamd_wlan_set_key: running for link %s", linkname);
+ /*
+ * Name key object for this WLAN so it can be later retrieved
+ * (name is unique for each ESSID/BSSID combination).
+ */
+ nwamd_set_key_name(essid, bssid, obj_name, sizeof (obj_name));
+ nlog(LOG_DEBUG, "store_key: obj_name is %s", obj_name);
+
+ class = (security_mode == DLADM_WLAN_SECMODE_WEP ?
+ DLADM_SECOBJ_CLASS_WEP : DLADM_SECOBJ_CLASS_WPA);
+ if (key_string_to_secobj_value(raw_key, obj_val, &obj_len,
+ class) != 0) {
+ /* above function logs internally on failure */
+ nwamd_object_release(ncu_obj);
+ return (NWAM_ERROR_INTERNAL);
+ }
+
+ /* we've validated the new key, so remove the old one */
+ status = dladm_unset_secobj(dld_handle, obj_name,
+ DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST);
+ if (status != DLADM_STATUS_OK && status != DLADM_STATUS_NOTFOUND) {
+ nlog(LOG_ERR, "store_key: could not remove old secure object "
+ "'%s' for key: %s", obj_name,
+ dladm_status2str(status, errmsg));
+ nwamd_object_release(ncu_obj);
+ return (NWAM_ERROR_INTERNAL);
+ }
+
+ /* if we're just deleting the key, then we're done */
+ if (raw_key[0] == '\0') {
+ nwamd_object_release(ncu_obj);
+ return (NWAM_SUCCESS);
+ }
+
+ status = dladm_set_secobj(dld_handle, obj_name, class,
+ obj_val, obj_len,
+ DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE);
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "store_key: could not create secure object "
+ "'%s' for key: %s", obj_name,
+ dladm_status2str(status, errmsg));
+ nwamd_object_release(ncu_obj);
+ return (NWAM_ERROR_INTERNAL);
+ }
+ link->nwamd_link_wifi_key = nwamd_wlan_get_key_named(obj_name,
+ security_mode);
+ (void) strlcpy(link->nwamd_link_wifi_keyname, obj_name,
+ sizeof (link->nwamd_link_wifi_keyname));
+ link->nwamd_link_wifi_security_mode = security_mode;
+ if (security_mode == DLADM_WLAN_SECMODE_WEP) {
+ link->nwamd_link_wifi_key->wk_idx =
+ (keyslot >= 1 && keyslot <= 4) ? keyslot : 1;
+ }
+
+ /* If link NCU is offline* or online, (re)connect. */
+ switch (ncu_obj->nwamd_object_state) {
+ case NWAM_STATE_ONLINE:
+ /* if changing the key of the connected WLAN, reconnect */
+ if (strcmp(essid, link->nwamd_link_wifi_essid) == 0)
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE,
+ NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
+ break;
+ case NWAM_STATE_OFFLINE_TO_ONLINE:
+ /* if we are waiting for the key, connect */
+ if (ncu_obj->nwamd_object_aux_state ==
+ NWAM_AUX_STATE_LINK_WIFI_NEED_KEY)
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
+ break;
+ default:
+ break;
+ }
+ nwamd_object_release(ncu_obj);
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * returns NULL if no key was recovered from libdladm. Passing in
+ * security mode of 0 means we don't care what key type it is.
+ */
+dladm_wlan_key_t *
+nwamd_wlan_get_key_named(const char *name, uint32_t security_mode)
+{
+ dladm_status_t status;
+ char errmsg[DLADM_STRSIZE];
+ dladm_wlan_key_t *cooked_key;
+ dladm_secobj_class_t class;
+
+ if (security_mode == DLADM_WLAN_SECMODE_NONE)
+ return (NULL);
+
+ /*
+ * Newly-allocated key must be freed by caller, or by
+ * subsequent call to nwamd_wlan_get_key_named().
+ */
+ if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) {
+ nlog(LOG_ERR, "nwamd_wlan_get_key_named: malloc failed");
+ return (NULL);
+ }
+
+ /*
+ * 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.
+ */
+ (void) strlcpy(cooked_key->wk_name, name, DLADM_SECOBJ_NAME_MAX);
+ nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: len = %d, object = %s\n",
+ strlen(cooked_key->wk_name), cooked_key->wk_name);
+ cooked_key->wk_len = sizeof (cooked_key->wk_val);
+ cooked_key->wk_idx = 1;
+
+ /* Try the kernel first, then fall back to persistent storage. */
+ status = dladm_get_secobj(dld_handle, cooked_key->wk_name, &class,
+ cooked_key->wk_val, &cooked_key->wk_len,
+ DLADM_OPT_ACTIVE);
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: "
+ "dladm_get_secobj(TEMP) failed: %s",
+ dladm_status2str(status, errmsg));
+ status = dladm_get_secobj(dld_handle, cooked_key->wk_name,
+ &class, cooked_key->wk_val, &cooked_key->wk_len,
+ DLADM_OPT_PERSIST);
+ }
+
+ switch (status) {
+ case DLADM_STATUS_OK:
+ nlog(LOG_DEBUG, "nwamd_wlan_get_key_named: "
+ "dladm_get_secobj succeeded: len %d", cooked_key->wk_len);
+ break;
+ case DLADM_STATUS_NOTFOUND:
+ /*
+ * We do not want an error in the case that the secobj
+ * is not found, since we then prompt for it.
+ */
+ free(cooked_key);
+ return (NULL);
+ default:
+ nlog(LOG_ERR, "nwamd_wlan_get_key_named: could not get key "
+ "from secure object '%s': %s", cooked_key->wk_name,
+ dladm_status2str(status, errmsg));
+ free(cooked_key);
+ return (NULL);
+ }
+
+ if (security_mode != 0) {
+ switch (class) {
+ case DLADM_SECOBJ_CLASS_WEP:
+ if (security_mode == DLADM_WLAN_SECMODE_WEP)
+ return (cooked_key);
+ break;
+ case DLADM_SECOBJ_CLASS_WPA:
+ if (security_mode == DLADM_WLAN_SECMODE_WPA)
+ return (cooked_key);
+ break;
+ default:
+ /* shouldn't happen */
+ nlog(LOG_ERR, "nwamd_wlan_get_key: invalid class %d",
+ class);
+ break;
+ }
+ /* key type mismatch */
+ nlog(LOG_ERR, "nwamd_wlan_get_key: key type mismatch"
+ " from secure object '%s'", cooked_key->wk_name);
+ free(cooked_key);
+ return (NULL);
+ }
+
+ return (cooked_key);
+}
+
+static dladm_wlan_key_t *
+nwamd_wlan_get_key(const char *essid, const char *bssid, uint32_t security_mode)
+{
+ char keyname[DLADM_SECOBJ_NAME_MAX];
+
+ nwamd_set_key_name(essid, bssid, keyname, DLADM_SECOBJ_NAME_MAX);
+
+ return (nwamd_wlan_get_key_named(keyname, security_mode));
+}
+
+/*
+ * Checks if a wireless network can be selected or not. A wireless network
+ * CANNOT be selected if the NCU is DISABLED, or the NCU is OFFLINE or
+ * ONLINE* and has lower priority than the currently active priority-group.
+ * Called with object lock held.
+ */
+static boolean_t
+wireless_selection_possible(nwamd_object_t object)
+{
+ nwamd_ncu_t *ncu = object->nwamd_object_data;
+
+ if (ncu->ncu_node.u_link.nwamd_link_media != DL_WIFI)
+ return (B_FALSE);
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ if (object->nwamd_object_state == NWAM_STATE_DISABLED ||
+ ((object->nwamd_object_state == NWAM_STATE_OFFLINE ||
+ object->nwamd_object_state == NWAM_STATE_ONLINE_TO_OFFLINE) &&
+ ncu->ncu_node.u_link.nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_PRIORITIZED &&
+ (current_ncu_priority_group == INVALID_PRIORITY_GROUP ||
+ ncu->ncu_node.u_link.nwamd_link_priority_group >
+ current_ncu_priority_group))) {
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+ return (B_FALSE);
+ }
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ return (B_TRUE);
+}
+
+/*
+ * Update the selected and/or connected values for the
+ * scan data. If these change, we need to trigger a scan
+ * event since the updated values need to be communicated
+ * to the GUI.
+ */
+void
+nwamd_set_selected_connected(nwamd_ncu_t *ncu, boolean_t selected,
+ boolean_t connected)
+{
+ nwamd_link_t *link = &ncu->ncu_node.u_link;
+ nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan;
+ int i;
+ boolean_t trigger_scan_event = B_FALSE;
+
+ for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
+ if (strcmp(s->nwamd_wifi_scan_curr[i].nww_essid,
+ link->nwamd_link_wifi_essid) != 0 ||
+ (link->nwamd_link_wifi_bssid[0] != '\0' &&
+ strcmp(s->nwamd_wifi_scan_curr[i].nww_bssid,
+ link->nwamd_link_wifi_bssid) != 0))
+ continue;
+ if (selected) {
+ if (!s->nwamd_wifi_scan_curr[i].nww_selected)
+ trigger_scan_event = B_TRUE;
+ s->nwamd_wifi_scan_curr[i].nww_selected = B_TRUE;
+ } else {
+ if (s->nwamd_wifi_scan_curr[i].nww_selected)
+ trigger_scan_event = B_TRUE;
+ s->nwamd_wifi_scan_curr[i].nww_selected = B_FALSE;
+ }
+ if (connected) {
+ if (!s->nwamd_wifi_scan_curr[i].nww_connected)
+ trigger_scan_event = B_TRUE;
+ s->nwamd_wifi_scan_curr[i].nww_connected = B_TRUE;
+ } else {
+ if (s->nwamd_wifi_scan_curr[i].nww_connected)
+ trigger_scan_event = B_TRUE;
+ s->nwamd_wifi_scan_curr[i].nww_connected = B_FALSE;
+ }
+ }
+
+ if (trigger_scan_event || s->nwamd_wifi_scan_changed) {
+ nwamd_event_t scan_event = nwamd_event_init_wlan
+ (ncu->ncu_name, NWAM_EVENT_TYPE_WLAN_SCAN_REPORT, connected,
+ s->nwamd_wifi_scan_curr, s->nwamd_wifi_scan_curr_num);
+ if (scan_event != NULL) {
+ /* Avoid sending same scan data multiple times */
+ s->nwamd_wifi_scan_changed = B_FALSE;
+ nwamd_event_enqueue(scan_event);
+ }
+ }
+}
+
+nwam_error_t
+nwamd_wlan_select(const char *linkname, const char *essid, const char *bssid,
+ uint32_t security_mode, boolean_t add_to_known_wlans)
+{
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+ char key[DLADM_STRSIZE];
+ boolean_t found_old_key = B_FALSE, found_key = B_FALSE;
+
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
+ == NULL) {
+ nlog(LOG_ERR, "nwamd_wlan_select: could not find object "
+ "for link %s", linkname);
+ return (NWAM_ENTITY_NOT_FOUND);
+ }
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ /*
+ * If wireless selection is not possible because of the current
+ * state or priority-group, then stop.
+ */
+ if (!wireless_selection_possible(ncu_obj)) {
+ nwamd_object_release(ncu_obj);
+ return (NWAM_ENTITY_INVALID_STATE);
+ }
+
+ /* unset selected, connected flag for previously connected wlan */
+ nwamd_set_selected_connected(ncu, B_FALSE, B_FALSE);
+
+ (void) strlcpy(link->nwamd_link_wifi_essid, essid,
+ sizeof (link->nwamd_link_wifi_essid));
+ (void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
+ sizeof (link->nwamd_link_wifi_bssid));
+ link->nwamd_link_wifi_security_mode = security_mode;
+ link->nwamd_link_wifi_add_to_known_wlans = add_to_known_wlans;
+
+ /* Disconnect to allow new selection to go ahead */
+ (void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id);
+
+ /* set selected flag for newly-selected WLAN */
+ nwamd_set_selected_connected(ncu, B_TRUE, B_FALSE);
+
+ /* does this WLAN require a key? If so go to NEED_KEY */
+ if (NEED_ENC(link->nwamd_link_wifi_security_mode)) {
+ /*
+ * First, if a key name may have been specified for a
+ * known WLAN. If so, use it. Otherwise, try both the
+ * new nwamd key name format (ESSID) and old (ESSID/BSSID).
+ * The user may have set the key without adding a known WLAN,
+ * so we need to try all these options to save going to
+ * NEED_KEY state.
+ */
+ if (known_wlan_get_keyname(link->nwamd_link_wifi_essid,
+ link->nwamd_link_wifi_keyname) == NWAM_SUCCESS &&
+ (link->nwamd_link_wifi_key = nwamd_wlan_get_key_named
+ (link->nwamd_link_wifi_keyname,
+ link->nwamd_link_wifi_security_mode)) != NULL) {
+ (void) known_wlan_get_keyslot
+ (link->nwamd_link_wifi_essid,
+ &link->nwamd_link_wifi_key->wk_idx);
+ nlog(LOG_DEBUG, "nwamd_wlan_select: got known WLAN "
+ "key %s, slot %d", link->nwamd_link_wifi_keyname,
+ link->nwamd_link_wifi_key->wk_idx);
+ found_key = B_TRUE;
+ } else if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key
+ (link->nwamd_link_wifi_essid, NULL,
+ link->nwamd_link_wifi_security_mode)) != NULL) {
+ nwamd_set_key_name(link->nwamd_link_wifi_essid, NULL,
+ link->nwamd_link_wifi_keyname,
+ DLADM_SECOBJ_NAME_MAX);
+ nlog(LOG_DEBUG, "nwamd_wlan_select: got WLAN key %s",
+ link->nwamd_link_wifi_keyname);
+ found_key = B_TRUE;
+ } else if ((link->nwamd_link_wifi_key = nwamd_wlan_get_key
+ (link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid,
+ link->nwamd_link_wifi_security_mode)) != NULL) {
+ /*
+ * Found old key format - prepare to save
+ * it as new ESSID-only key, but don't
+ * do it until we're released the object
+ * lock (since nwamd_wlan_set_key()
+ * takes the object lock).
+ */
+ (void) strlcpy(key,
+ (char *)link->nwamd_link_wifi_key->wk_val,
+ link->nwamd_link_wifi_key->wk_len + 1);
+ found_old_key = B_TRUE;
+ found_key = B_TRUE;
+ nwamd_set_key_name(link->nwamd_link_wifi_essid, NULL,
+ link->nwamd_link_wifi_keyname,
+ DLADM_SECOBJ_NAME_MAX);
+ nlog(LOG_DEBUG, "nwamd_wlan_select: got old format "
+ "WLAN key, converting to %s",
+ link->nwamd_link_wifi_keyname);
+ } else {
+ nlog(LOG_ERR, "nwamd_wlan_select: could not "
+ "find key for WLAN '%s'",
+ link->nwamd_link_wifi_essid);
+ }
+ } else {
+ free(link->nwamd_link_wifi_key);
+ link->nwamd_link_wifi_key = NULL;
+ link->nwamd_link_wifi_keyname[0] = '\0';
+ }
+
+ if (NEED_ENC(link->nwamd_link_wifi_security_mode) && !found_key) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_LINK_WIFI_NEED_KEY);
+ } else {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_LINK_WIFI_CONNECTING);
+ }
+ nwamd_object_release(ncu_obj);
+
+ if (found_old_key) {
+ (void) nwamd_wlan_set_key(linkname, essid, NULL, security_mode,
+ 1, key);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * See if BSSID is in visited list of BSSIDs for known WLAN. Used for
+ * strict BSSID matching (depends on wireless_strict_bssid property value).
+ */
+static boolean_t
+bssid_match(nwam_known_wlan_handle_t kwh, const char *bssid)
+{
+ nwam_value_t bssidsval;
+ nwam_error_t err;
+ char **bssids;
+ uint_t nelem, i;
+ boolean_t found = B_FALSE;
+
+ if ((err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "bssid_match: %s", nwam_strerror(err));
+ return (B_FALSE);
+ }
+ if ((err = nwam_value_get_string_array(bssidsval, &bssids, &nelem))
+ != NWAM_SUCCESS) {
+ nwam_value_free(bssidsval);
+ return (B_FALSE);
+ }
+ for (i = 0; i < nelem; i++) {
+ if (strcmp(bssid, bssids[i]) == 0) {
+ found = B_TRUE;
+ break;
+ }
+ }
+ nwam_value_free(bssidsval);
+
+ return (found);
+}
+
+/* Find most prioritized AP with strongest signal in scan data. */
+static int
+find_best_wlan_cb(nwam_known_wlan_handle_t kwh, void *data)
+{
+ nwamd_ncu_t *ncu = data;
+ nwamd_link_t *link = &ncu->ncu_node.u_link;
+ nwamd_wifi_scan_t *s = &link->nwamd_link_wifi_scan;
+ nwam_error_t err;
+ char *name = NULL;
+ int i;
+ dladm_wlan_strength_t curr_strength = 0;
+ dladm_wlan_strength_t max_strength = 0;
+ boolean_t found = B_FALSE;
+
+ if ((err = nwam_known_wlan_get_name(kwh, &name)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "find_best_wlan_cb: could not look up name: %s",
+ nwam_strerror(err));
+ return (0);
+ }
+
+ if (link->nwamd_link_wifi_connected) {
+ (void) dladm_wlan_str2strength
+ (link->nwamd_link_wifi_signal_strength, &curr_strength);
+ }
+
+ /*
+ * If we're >= scan level, don't pick another Known WLAN if still
+ * connected (even if a Known WLAN with higher priority is available).
+ * If the user wants to connect to a different Known WLAN, it can be
+ * done from the GUI or select-wifi subcommand of nwamadm(1M).
+ */
+ if (curr_strength >= wireless_scan_level &&
+ link->nwamd_link_wifi_connected) {
+ free(name);
+ return (1);
+ }
+
+ for (i = 0; i < s->nwamd_wifi_scan_curr_num; i++) {
+ nwam_wlan_t *cur_wlan = &(s->nwamd_wifi_scan_curr[i]);
+ boolean_t b_match = bssid_match(kwh, cur_wlan->nww_bssid);
+
+ /*
+ * We need to either match the scanned essid, or in the case
+ * where the essid was not broadcast, match the scanned bssid.
+ */
+ if (strcmp(cur_wlan->nww_essid, name) != 0 &&
+ !(cur_wlan->nww_essid[0] == '\0' && b_match))
+ continue;
+ /*
+ * If wireless_strict_bssid is specified, need to match
+ * BSSID too.
+ */
+ if (wireless_strict_bssid && !b_match)
+ continue;
+ /*
+ * Found a match. Since we walk known WLANs in
+ * priority order, it's guaranteed to be the
+ * most prioritized. It may not be the strongest though -
+ * we continue the walk and record the strength along
+ * with the ESSID and BSSID, so that if we encounter
+ * another AP with the same ESSID but a higher signal strength,
+ * we will choose it - but only if the currently-connected
+ * WLAN is at or below wireless_scan_level.
+ */
+ (void) dladm_wlan_str2strength
+ (cur_wlan->nww_signal_strength, &curr_strength);
+
+ if (curr_strength > max_strength) {
+ (void) strlcpy(link->nwamd_link_wifi_essid,
+ cur_wlan->nww_essid,
+ sizeof (link->nwamd_link_wifi_essid));
+ /* set BSSID if wireless_strict_bssid is specified */
+ if (wireless_strict_bssid) {
+ (void) strlcpy(link->nwamd_link_wifi_bssid,
+ cur_wlan->nww_bssid,
+ sizeof (link->nwamd_link_wifi_bssid));
+ }
+ (void) strlcpy(link->nwamd_link_wifi_signal_strength,
+ cur_wlan->nww_signal_strength,
+ sizeof (link->nwamd_link_wifi_signal_strength));
+ link->nwamd_link_wifi_security_mode =
+ cur_wlan->nww_security_mode;
+ found = B_TRUE;
+ }
+ (void) dladm_wlan_str2strength
+ (link->nwamd_link_wifi_signal_strength, &max_strength);
+ }
+ free(name);
+ return (found ? 1 : 0);
+}
+
+static boolean_t
+nwamd_find_known_wlan(nwamd_object_t ncu_obj)
+{
+ nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data;
+ int ret;
+
+ /*
+ * Walk known WLANs, finding lowest priority (preferred) WLAN
+ * in our scan results.
+ */
+ (void) nwam_walk_known_wlans(find_best_wlan_cb, ncu,
+ NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret);
+
+ return (ret == 1);
+}
+
+/*
+ * WLAN scan code for WIFI link NCUs.
+ */
+
+/* Create periodic scan event for object. Called with object lock held. */
+void
+nwamd_ncu_create_periodic_scan_event(nwamd_object_t ncu_obj)
+{
+ nwamd_event_t scan_event;
+
+ if (wireless_scan_interval == 0) {
+ nlog(LOG_DEBUG, "nwamd_ncu_create_periodic_scan_event: "
+ "wireless_scan_interval set to 0 so no periodic scanning");
+ return;
+ }
+ scan_event = nwamd_event_init(NWAM_EVENT_TYPE_PERIODIC_SCAN,
+ NWAM_OBJECT_TYPE_NCU, 0, ncu_obj->nwamd_object_name);
+ if (scan_event != NULL) {
+ nwamd_event_enqueue_timed(scan_event,
+ wireless_scan_interval > WIRELESS_SCAN_INTERVAL_MIN ?
+ wireless_scan_interval : WIRELESS_SCAN_INTERVAL_MIN);
+ }
+}
+
+/* Handle periodic scan event (which puts link into WIFI_INIT state */
+void
+nwamd_ncu_handle_periodic_scan_event(nwamd_event_t event)
+{
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+
+ ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
+ event->event_object);
+ if (ncu_obj == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_periodic_scan_event: "
+ "no object %s", event->event_object);
+ return;
+ }
+ ncu = ncu_obj->nwamd_object_data;
+
+ /* Only rescan if state is offline* or online */
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_periodic_scan_event: doing rescan..");
+
+ if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE ||
+ ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE) {
+ /* rescan, then create periodic scan event */
+ (void) nwamd_wlan_scan(ncu->ncu_name);
+ nwamd_ncu_create_periodic_scan_event(ncu_obj);
+ }
+ nwamd_object_release(ncu_obj);
+}
+
+static boolean_t
+get_scan_results(void *arg, dladm_wlan_attr_t *attrp)
+{
+ nwamd_wifi_scan_t *s = arg;
+ const char *linkname = s->nwamd_wifi_scan_link;
+ char essid_name[DLADM_STRSIZE];
+ char bssid_name[DLADM_STRSIZE];
+ char strength[DLADM_STRSIZE];
+ uint_t i, index = 0;
+ boolean_t found = B_FALSE;
+
+ (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);
+
+ index = s->nwamd_wifi_scan_curr_num;
+ if (index == NWAMD_MAX_NUM_WLANS) {
+ nlog(LOG_ERR, "get_scan_results: truncating WLAN scan results "
+ "for link %s: ommiting (%s, %s)", linkname, essid_name,
+ bssid_name);
+ return (B_TRUE);
+ }
+
+ (void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_essid, essid_name,
+ sizeof (s->nwamd_wifi_scan_curr[index].nww_essid));
+ (void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_bssid, bssid_name,
+ sizeof (s->nwamd_wifi_scan_curr[index].nww_bssid));
+ (void) strlcpy(s->nwamd_wifi_scan_curr[index].nww_signal_strength,
+ strength,
+ sizeof (s->nwamd_wifi_scan_curr[index].nww_signal_strength));
+ s->nwamd_wifi_scan_curr[index].nww_security_mode = attrp->wa_secmode;
+ s->nwamd_wifi_scan_curr[index].nww_speed = attrp->wa_speed;
+ s->nwamd_wifi_scan_curr[index].nww_channel = attrp->wa_channel;
+ s->nwamd_wifi_scan_curr[index].nww_bsstype = attrp->wa_bsstype;
+
+ /*
+ * We fill in actual values for selected/connected/key later when we
+ * reacquire the object lock.
+ */
+ s->nwamd_wifi_scan_curr[index].nww_selected = B_FALSE;
+ s->nwamd_wifi_scan_curr[index].nww_connected = B_FALSE;
+ s->nwamd_wifi_scan_curr[index].nww_have_key = B_FALSE;
+ s->nwamd_wifi_scan_curr[index].nww_keyindex = 1;
+ s->nwamd_wifi_scan_curr_num++;
+
+ /* Check if this AP was in previous scan results */
+ for (i = 0; i < s->nwamd_wifi_scan_last_num; i++) {
+ found = (strcmp(s->nwamd_wifi_scan_last[i].nww_essid,
+ essid_name) == 0 &&
+ strcmp(s->nwamd_wifi_scan_last[i].nww_bssid,
+ bssid_name) == 0);
+ if (found)
+ break;
+ }
+ if (!found)
+ s->nwamd_wifi_scan_changed = B_TRUE;
+
+ nlog(LOG_DEBUG, "get_scan_results(%s, %d): ESSID %s, BSSID %s",
+ linkname, index, essid_name, bssid_name);
+
+ return (B_TRUE);
+}
+
+/*
+ * Check if we're connected to the expected WLAN, or in the case of autoconf
+ * record the WLAN we're connected to.
+ */
+boolean_t
+nwamd_wlan_connected(nwamd_object_t ncu_obj)
+{
+ nwamd_ncu_t *ncu = ncu_obj->nwamd_object_data;
+ nwamd_link_t *link = &ncu->ncu_node.u_link;
+ dladm_wlan_linkattr_t attr;
+ char essid[DLADM_STRSIZE];
+ char bssid[DLADM_STRSIZE];
+ boolean_t connected = B_FALSE;
+ 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 it's really down.
+ */
+ while (retries++ < 4) {
+ if (dladm_wlan_get_linkattr(dld_handle, link->nwamd_link_id,
+ &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;
+ nlog(LOG_DEBUG, "nwamd_wlan_connected: %s connected to %s %s",
+ ncu->ncu_name, essid, bssid);
+ } else {
+ return (B_FALSE);
+ }
+ /*
+ * If we're using autoconf, we have no control over what we connect to,
+ * so rather than verifying ESSSID, simply record ESSID/BSSID.
+ */
+ if (link->nwamd_link_wifi_autoconf) {
+ (void) strlcpy(link->nwamd_link_wifi_essid, essid,
+ sizeof (link->nwamd_link_wifi_essid));
+ (void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
+ sizeof (link->nwamd_link_wifi_bssid));
+ }
+ /*
+ * Are we connected to expected WLAN? Note:
+ * we'd like to verify BSSID, but we cannot due to CR 6772510.
+ */
+ if (strcmp(essid, link->nwamd_link_wifi_essid) == 0) {
+ /* Update connected signal strength */
+ (void) dladm_wlan_strength2str(&attr.la_wlan_attr.wa_strength,
+ link->nwamd_link_wifi_signal_strength);
+
+ /* Store current BSSID */
+ (void) strlcpy(link->nwamd_link_wifi_bssid, bssid,
+ sizeof (link->nwamd_link_wifi_bssid));
+
+ if (attr.la_wlan_attr.wa_strength < wireless_scan_level) {
+ /*
+ * We're connected, but we've dropped below
+ * scan threshold. Initiate a scan.
+ */
+ nlog(LOG_DEBUG, "nwamd_wlan_connected: "
+ "connected but signal under threshold...");
+ (void) nwamd_wlan_scan(ncu->ncu_name);
+ }
+ return (connected);
+ } else if (strlen(essid) == 0) {
+ /*
+ * For hidden WLANs, no ESSID is specified, so we cannot verify
+ * WLAN name.
+ */
+ nlog(LOG_DEBUG,
+ "nwamd_wlan_connected: connected to hidden WLAN, cannot "
+ "verify connection details");
+ return (connected);
+ } else {
+ (void) nlog(LOG_ERR,
+ "nwamd_wlan_connected: wrong AP on %s; expected %s %s",
+ ncu->ncu_name, link->nwamd_link_wifi_essid,
+ link->nwamd_link_wifi_bssid);
+ (void) dladm_wlan_disconnect(dld_handle, link->nwamd_link_id);
+ link->nwamd_link_wifi_connected = B_FALSE;
+ return (B_FALSE);
+ }
+}
+
+/*
+ * WLAN scan thread. Called with the per-link WiFi mutex held.
+ */
+static void *
+wlan_scan_thread(void *arg)
+{
+ char *linkname = arg;
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+ dladm_status_t status;
+ char essid[DLADM_STRSIZE];
+ char bssid[DLADM_STRSIZE];
+ uint32_t now, link_id;
+ nwamd_wifi_scan_t s;
+ int i;
+
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
+ == NULL) {
+ nlog(LOG_ERR, "wlan_scan_thread: could not find object "
+ "for link %s", linkname);
+ free(linkname);
+ return (NULL);
+ }
+
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ /*
+ * It is possible multiple scan threads have queued up waiting for the
+ * object lock. We try to prevent excessive scanning by limiting the
+ * interval between scans to WIRELESS_SCAN_REQUESTED_INTERVAL_MIN sec.
+ */
+ now = NSEC_TO_SEC(gethrtime());
+ if ((now - link->nwamd_link_wifi_scan.nwamd_wifi_scan_last_time) <
+ WIRELESS_SCAN_REQUESTED_INTERVAL_MIN) {
+ nlog(LOG_DEBUG, "wlan_scan_thread: last scan for %s "
+ "was < %d sec ago, ignoring scan request",
+ linkname, WIRELESS_SCAN_REQUESTED_INTERVAL_MIN);
+ nwamd_object_release(ncu_obj);
+ free(linkname);
+ return (NULL);
+ }
+
+ /*
+ * Prepare scan data - copy link name and copy previous "current"
+ * scan results from the nwamd_link_t to the last scan results for
+ * the next scan so that we can compare results to find if things
+ * have changed since last time.
+ */
+ (void) bzero(&s, sizeof (nwamd_wifi_scan_t));
+ (void) strlcpy(s.nwamd_wifi_scan_link, ncu->ncu_name,
+ sizeof (s.nwamd_wifi_scan_link));
+ s.nwamd_wifi_scan_last_num =
+ link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr_num;
+ if (s.nwamd_wifi_scan_last_num > 0) {
+ (void) memcpy(s.nwamd_wifi_scan_last,
+ link->nwamd_link_wifi_scan.nwamd_wifi_scan_curr,
+ s.nwamd_wifi_scan_last_num * sizeof (nwam_wlan_t));
+ }
+ link_id = link->nwamd_link_id;
+ nwamd_object_release(ncu_obj);
+
+ nlog(LOG_DEBUG, "wlan_scan_thread: initiating scan on %s",
+ s.nwamd_wifi_scan_link);
+
+ scanconnect_entry();
+ status = dladm_wlan_scan(dld_handle, link_id, &s, get_scan_results);
+ s.nwamd_wifi_scan_last_time = NSEC_TO_SEC(gethrtime());
+ if (!s.nwamd_wifi_scan_changed) {
+ /* Scan may have lost WLANs, if so this qualifies as change */
+ s.nwamd_wifi_scan_changed = (s.nwamd_wifi_scan_curr_num !=
+ s.nwamd_wifi_scan_last_num);
+ }
+ scanconnect_exit();
+
+ if (status != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "wlan_scan_thread: cannot scan link %s",
+ s.nwamd_wifi_scan_link);
+ free(linkname);
+ return (NULL);
+ }
+
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
+ == NULL) {
+ nlog(LOG_ERR, "wlan_scan_thread: could not find object "
+ "for link %s after doing scan", linkname);
+ free(linkname);
+ return (NULL);
+ }
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ /* For new scan data, add key info from known WLANs */
+ for (i = 0; i < s.nwamd_wifi_scan_curr_num; i++) {
+ if (NEED_ENC(s.nwamd_wifi_scan_curr[i].nww_security_mode)) {
+ char keyname[NWAM_MAX_VALUE_LEN];
+ dladm_wlan_key_t *key = NULL;
+
+ if (known_wlan_get_keyname
+ (s.nwamd_wifi_scan_curr[i].nww_essid, keyname)
+ == NWAM_SUCCESS &&
+ (key = nwamd_wlan_get_key_named(keyname,
+ s.nwamd_wifi_scan_curr[i].nww_security_mode))
+ != NULL) {
+ s.nwamd_wifi_scan_curr[i].nww_have_key =
+ B_TRUE;
+ s.nwamd_wifi_scan_curr[i].nww_keyindex =
+ s.nwamd_wifi_scan_curr[i].
+ nww_security_mode ==
+ DLADM_WLAN_SECMODE_WEP ?
+ key->wk_idx : 1;
+ free(key);
+ }
+ }
+ }
+ /* Copy scan data into nwamd_link_t */
+ link->nwamd_link_wifi_scan = s;
+ /* Set selected, connected and send scan event if we've got new data */
+ nwamd_set_selected_connected(ncu,
+ link->nwamd_link_wifi_essid[0] != '\0',
+ link->nwamd_link_wifi_connected);
+
+ /*
+ * If wireless selection is not possible because of the current
+ * state or priority-group, then this was just a scan request.
+ * Nothing else to do.
+ */
+ if (!wireless_selection_possible(ncu_obj)) {
+ nwamd_object_release(ncu_obj);
+ free(linkname);
+ return (NULL);
+ }
+
+ /*
+ * Check if WLAN is on our known WLAN list. If no
+ * previously-visited WLANs are found in scan data, set
+ * new state to NEED_SELECTION (provided we're not currently
+ * connected, as can be the case during a periodic scan or
+ * monitor-triggered scan where the signal strength recovers.
+ */
+ if (!nwamd_find_known_wlan(ncu_obj)) {
+ if (!nwamd_wlan_connected(ncu_obj)) {
+ if (link->nwamd_link_wifi_connected) {
+ nlog(LOG_DEBUG, "wlan_scan_thread: "
+ "unexpected disconnect after scan");
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_DOWN);
+ } else {
+ nlog(LOG_DEBUG, "wlan_scan_thread: "
+ "no known WLANs - ask user");
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION);
+ }
+ } else {
+ /* still connected. if not online, change to online */
+ nlog(LOG_DEBUG, "wlan_scan_thread: still connected to "
+ "%s %s", link->nwamd_link_wifi_essid,
+ link->nwamd_link_wifi_bssid);
+ if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_UP);
+ }
+ }
+ nwamd_object_release(ncu_obj);
+
+ } else {
+ nlog(LOG_DEBUG, "wlan_scan_thread: found known WLAN %s %s",
+ link->nwamd_link_wifi_essid, link->nwamd_link_wifi_bssid);
+
+ if (!nwamd_wlan_connected(ncu_obj)) {
+ /* Copy selected ESSID/BSSID, unlock, call select */
+ (void) strlcpy(essid, link->nwamd_link_wifi_essid,
+ sizeof (essid));
+ (void) strlcpy(bssid, link->nwamd_link_wifi_bssid,
+ sizeof (bssid));
+ nwamd_object_release(ncu_obj);
+ (void) nwamd_wlan_select(linkname, essid, bssid,
+ link->nwamd_link_wifi_security_mode, B_TRUE);
+ } else {
+ /* still connected. if not online, change to online */
+ nlog(LOG_DEBUG, "wlan_scan_thread: still connected to "
+ "known WLAN %s %s", link->nwamd_link_wifi_essid,
+ link->nwamd_link_wifi_bssid);
+ if (ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_UP);
+ }
+ nwamd_object_release(ncu_obj);
+ }
+ }
+ free(linkname);
+ return (NULL);
+}
+
+nwam_error_t
+nwamd_wlan_scan(const char *linkname)
+{
+ pthread_t wifi_thread;
+ char *link = strdup(linkname);
+
+ if (link == NULL) {
+ nlog(LOG_ERR, "nwamd_wlan_scan: out of memory");
+ return (NWAM_NO_MEMORY);
+ }
+
+ nlog(LOG_DEBUG, "nwamd_wlan_scan: WLAN scan for %s",
+ link);
+
+ if (pthread_create(&wifi_thread, NULL, wlan_scan_thread,
+ link) != 0) {
+ nlog(LOG_ERR, "nwamd_wlan_scan: could not start scan");
+ free(link);
+ return (NWAM_ERROR_INTERNAL);
+ }
+ /* detach thread so that it doesn't become a zombie */
+ (void) pthread_detach(wifi_thread);
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * WLAN connection code.
+ */
+
+/*
+ * Callback used on each known WLAN - if the BSSID is matched, set
+ * the ESSID of the hidden WLAN to the known WLAN name.
+ */
+static int
+find_bssid_cb(nwam_known_wlan_handle_t kwh, void *data)
+{
+ nwamd_link_t *link = data;
+ nwam_error_t err;
+ nwam_value_t bssidval;
+ char **bssids, *name;
+ uint_t num_bssids, i;
+
+ if ((err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidval)) != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "find_bssid_cb: nwam_known_wlan_get_prop: %s",
+ nwam_strerror(err));
+ return (0);
+ }
+ if ((err = nwam_value_get_string_array(bssidval, &bssids, &num_bssids))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "find_bssid_cb: nwam_value_get_string_array: %s",
+ nwam_strerror(err));
+ nwam_value_free(bssidval);
+ return (0);
+ }
+ for (i = 0; i < num_bssids; i++) {
+ if (strcmp(bssids[i], link->nwamd_link_wifi_bssid) == 0) {
+ if ((err = nwam_known_wlan_get_name(kwh, &name))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "find_bssid_cb: "
+ "nwam_known_wlan_get_name: %s",
+ nwam_strerror(err));
+ continue;
+ }
+ (void) strlcpy(link->nwamd_link_wifi_essid, name,
+ sizeof (link->nwamd_link_wifi_essid));
+ free(name);
+ nwam_value_free(bssidval);
+ /* Found ESSID for BSSID so terminate walk */
+ return (1);
+ }
+ }
+ nwam_value_free(bssidval);
+
+ return (0);
+}
+
+/*
+ * We may have encountered a BSSID for a hidden WLAN before and as a result
+ * may have a known WLAN entry with this BSSID. Walk known WLANs, searching
+ * for a BSSID match. Called with object lock held.
+ */
+static void
+check_if_hidden_wlan_was_visited(nwamd_link_t *link)
+{
+ (void) nwam_walk_known_wlans(find_bssid_cb, link,
+ NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL);
+}
+
+static dladm_status_t
+do_connect(uint32_t link_id, dladm_wlan_attr_t *attrp, dladm_wlan_key_t *key,
+ uint_t keycount, uint_t flags)
+{
+ dladm_status_t status;
+ char errmsg[DLADM_STRSIZE];
+
+ scanconnect_entry();
+ status = dladm_wlan_connect(dld_handle, link_id, attrp,
+ DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT, key, keycount, flags);
+ scanconnect_exit();
+
+ nlog(LOG_DEBUG, "nwamd_do_connect: dladm_wlan_connect returned %s",
+ dladm_status2str(status, errmsg));
+
+ return (status);
+}
+
+static void *
+wlan_connect_thread(void *arg)
+{
+ char *linkname = arg;
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+ nwam_error_t err;
+ uint_t keycount;
+ uint32_t link_id;
+ dladm_wlan_key_t *key = NULL;
+ dladm_wlan_attr_t attr;
+ dladm_status_t status;
+ boolean_t autoconf = B_FALSE;
+
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
+ == NULL) {
+ nlog(LOG_ERR, "wlan_connect_thread: could not find object "
+ "for link %s", linkname);
+ free(linkname);
+ return (NULL);
+ }
+
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ if (!wireless_selection_possible(ncu_obj)) {
+ nlog(LOG_DEBUG, "wlan_connect_thread: %s in invalid state or "
+ "has lower priority", ncu->ncu_name);
+ goto done;
+ }
+
+ (void) memset(&attr, 0, sizeof (attr));
+ /* try to apply essid selected by the user */
+ if (link->nwamd_link_wifi_essid[0] == '\0')
+ check_if_hidden_wlan_was_visited(link);
+
+ /* If it is already connected to the required AP, just return. */
+ if (nwamd_wlan_connected(ncu_obj)) {
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ ncu_obj->nwamd_object_state, NWAM_AUX_STATE_UP);
+ goto done;
+ }
+
+ if (dladm_wlan_str2essid(link->nwamd_link_wifi_essid, &attr.wa_essid)
+ != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "wlan_connect_thread: invalid ESSID '%s' "
+ "for '%s'", link->nwamd_link_wifi_essid, ncu->ncu_name);
+ goto done;
+ }
+ attr.wa_valid = DLADM_WLAN_ATTR_ESSID;
+
+ /* note: bssid logic here is non-functional */
+ if (link->nwamd_link_wifi_bssid[0] != '\0') {
+ if (dladm_wlan_str2bssid(link->nwamd_link_wifi_bssid,
+ &attr.wa_bssid) != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "wlan_connect_thread: invalid BSSID '%s'",
+ "for '%s'", link->nwamd_link_wifi_bssid,
+ ncu->ncu_name);
+ } else {
+ attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
+ }
+ }
+
+ /* First check for the key */
+ if (NEED_ENC(link->nwamd_link_wifi_security_mode)) {
+ if (link->nwamd_link_wifi_key == NULL) {
+ nlog(LOG_ERR, "wlan_connect_thread: could not find "
+ "key for WLAN '%s'", link->nwamd_link_wifi_essid);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_LINK_WIFI_NEED_KEY);
+ goto done;
+ }
+ /* Make a copy of the key as we need to unlock the object */
+ if ((key = calloc(1, sizeof (dladm_wlan_key_t))) == NULL) {
+ nlog(LOG_ERR, "wlan_connect_thread: out of memory");
+ goto done;
+ }
+ (void) memcpy(key, link->nwamd_link_wifi_key,
+ sizeof (dladm_wlan_key_t));
+
+ attr.wa_valid |= DLADM_WLAN_ATTR_SECMODE;
+ attr.wa_secmode = link->nwamd_link_wifi_security_mode;
+ keycount = 1;
+ nlog(LOG_DEBUG, "wlan_connect_thread: retrieved key");
+ } else {
+ key = NULL;
+ keycount = 0;
+ }
+
+ /*
+ * Connect; only scan if a bssid was not specified. If it times out,
+ * try a second time using autoconf. Drop the object lock during the
+ * connect attempt since connecting may take some time, and access to
+ * the link object during that period would be impossible if we held the
+ * lock.
+ */
+
+ link->nwamd_link_wifi_autoconf = B_FALSE;
+ link_id = link->nwamd_link_id;
+
+ nwamd_object_release(ncu_obj);
+
+ status = do_connect(link_id, &attr, key, keycount,
+ DLADM_WLAN_CONNECT_NOSCAN);
+ if (status != DLADM_STATUS_OK) {
+ /* Connect failed, try autoconf */
+ if (!wireless_autoconf || (status = do_connect(link_id, &attr,
+ NULL, 0, 0)) != DLADM_STATUS_OK) {
+ nlog(LOG_ERR, "wlan_connect_thread: connect failed for "
+ "%s", linkname);
+ goto done_unlocked;
+ }
+ if (status == DLADM_STATUS_OK)
+ autoconf = B_TRUE;
+ }
+
+ /* Connect succeeded, reacquire object */
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK, linkname))
+ == NULL) {
+ nlog(LOG_ERR, "wlan_connect_thread: could not find object "
+ "for link %s", linkname);
+ goto done_unlocked;
+ }
+
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ if (autoconf)
+ link->nwamd_link_wifi_autoconf = B_TRUE;
+
+ /*
+ * If WLAN is WEP/WPA, we would like to test the connection as the key
+ * may be wrong. It is difficult to find a reliable test that works
+ * across APs however. Do nothing for now.
+ */
+ link->nwamd_link_wifi_connected = nwamd_wlan_connected(ncu_obj);
+
+ if (link->nwamd_link_wifi_connected) {
+ if (link->nwamd_link_wifi_add_to_known_wlans) {
+ /* add to known WLANs */
+ nlog(LOG_DEBUG, "wlan_connect_thread: "
+ "add '%s' to known WLANs",
+ link->nwamd_link_wifi_essid);
+ if ((err = nwam_known_wlan_add_to_known_wlans
+ (link->nwamd_link_wifi_essid,
+ link->nwamd_link_wifi_bssid[0] != '\0' ?
+ link->nwamd_link_wifi_bssid : NULL,
+ link->nwamd_link_wifi_security_mode,
+ link->nwamd_link_wifi_security_mode ==
+ DLADM_WLAN_SECMODE_WEP ?
+ (uint_t)link->nwamd_link_wifi_key->wk_idx : 1,
+ NEED_ENC(link->nwamd_link_wifi_security_mode) ?
+ link->nwamd_link_wifi_keyname : NULL))
+ != NWAM_SUCCESS) {
+ nlog(LOG_ERR, "wlan_connect_thread: "
+ "could not add to known WLANs: %s",
+ nwam_strerror(err));
+ }
+ }
+ nwamd_set_selected_connected(ncu, B_TRUE, B_TRUE);
+ nlog(LOG_DEBUG, "wlan_connect_thread: connect "
+ "succeeded, setting state online");
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name, NWAM_STATE_ONLINE,
+ NWAM_AUX_STATE_UP);
+ }
+
+done:
+ nwamd_object_release(ncu_obj);
+done_unlocked:
+ free(linkname);
+ free(key);
+
+ return (NULL);
+}
+
+void
+nwamd_wlan_connect(const char *linkname)
+{
+ pthread_t wifi_thread;
+ char *link = strdup(linkname);
+
+ if (link == NULL) {
+ nlog(LOG_ERR, "nwamd_wlan_connect: out of memory");
+ return;
+ }
+
+ nlog(LOG_DEBUG, "nwamd_wlan_connect: WLAN connect for %s",
+ link);
+
+ if (pthread_create(&wifi_thread, NULL, wlan_connect_thread, link) != 0)
+ nlog(LOG_ERR, "nwamd_wlan_connect: could not start connect");
+
+ /* detach thread so that it doesn't become a zombie */
+ (void) pthread_detach(wifi_thread);
+}
+
+/*
+ * Launch signal strength-monitoring thread which periodically
+ * checks connection and signal strength. If we become disconnected
+ * or signal drops below threshold specified by wireless_scan_level,
+ * initiate a scan. The scan initiation is taken care of by
+ * the call to nwamd_wlan_connected().
+ */
+static void *
+wlan_monitor_signal_thread(void *arg)
+{
+ char *linkname = arg;
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+ boolean_t first_time = B_TRUE;
+
+ for (;;) {
+ if ((ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_LINK,
+ linkname)) == NULL) {
+ nlog(LOG_ERR, "wlan_monitor_signal_thread: could "
+ "not find object for link %s", linkname);
+ break;
+ }
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+
+ /* If the NCU is DISABLED/OFFLINE, exit the monitoring thread */
+ if (ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE ||
+ ncu_obj->nwamd_object_state == NWAM_STATE_DISABLED) {
+ nlog(LOG_INFO, "wlan_monitor_signal_thread: "
+ "%s is %s, stopping thread", linkname,
+ nwam_state_to_string(ncu_obj->nwamd_object_state));
+ link->nwamd_link_wifi_monitor_thread = 0;
+ nwamd_object_release(ncu_obj);
+ break;
+ }
+
+ /*
+ * First time thru loop, we check if there is another
+ * link monitoring thread in operation - if so exit this
+ * thread.
+ */
+ if (first_time) {
+ first_time = B_FALSE;
+
+ if (link->nwamd_link_wifi_monitor_thread != 0) {
+ /* Already have a monitor thread for link? */
+ nwamd_object_release(ncu_obj);
+ break;
+ } else {
+ link->nwamd_link_wifi_monitor_thread =
+ pthread_self();
+ }
+ }
+ if (!nwamd_wlan_connected(ncu_obj)) {
+ nlog(LOG_ERR, "wlan_monitor_signal_thread: "
+ "disconnect occured for WLAN on link %s", linkname);
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ ncu_obj->nwamd_object_name,
+ NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_DOWN);
+ link->nwamd_link_wifi_monitor_thread = 0;
+ nwamd_object_release(ncu_obj);
+ break;
+ }
+ nwamd_object_release(ncu_obj);
+ (void) sleep(WIRELESS_MONITOR_SIGNAL_INTERVAL);
+ }
+ free(linkname);
+
+ return (NULL);
+}
+
+void
+nwamd_wlan_monitor_signal(const char *linkname)
+{
+ pthread_t wifi_thread;
+ char *link = strdup(linkname);
+
+ if (link == NULL) {
+ nlog(LOG_ERR, "nwamd_wlan_monitor_signal: out of memory");
+ return;
+ }
+
+ nlog(LOG_DEBUG, "nwamd_wlan_monitor_signal: WLAN monitor for %s",
+ link);
+
+ if (pthread_create(&wifi_thread, NULL, wlan_monitor_signal_thread,
+ link) != 0) {
+ nlog(LOG_ERR, "nwamd_wlan_monitor_signal: could not monitor "
+ "link %s", link);
+ free(link);
+ return;
+ }
+
+ /* detach thread so that it doesn't become a zombie */
+ (void) pthread_detach(wifi_thread);
+}
+
+void
+nwamd_ncu_handle_link_state_event(nwamd_event_t event)
+{
+ nwam_event_t evm;
+ nwamd_object_t ncu_obj;
+ nwamd_ncu_t *ncu;
+ nwamd_link_t *link;
+
+ ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object);
+ if (ncu_obj == NULL) {
+ nlog(LOG_ERR, "nwamd_ncu_handle_link_state_event: no object %s",
+ event->event_object);
+ nwamd_event_do_not_send(event);
+ return;
+ }
+ ncu = ncu_obj->nwamd_object_data;
+ link = &ncu->ncu_node.u_link;
+ evm = event->event_msg;
+
+ /*
+ * We ignore link state events for WiFi because it is very flaky.
+ * Instead we use the monitor thread and drive WiFi state changes from
+ * there.
+ */
+ if (link->nwamd_link_media == DL_WIFI) {
+ nwamd_object_release(ncu_obj);
+ return;
+ }
+
+ /*
+ * If it's a link up event and we're not disabled, go online.
+ */
+ if (evm->nwe_data.nwe_link_state.nwe_link_up &&
+ ncu_obj->nwamd_object_state != NWAM_STATE_DISABLED) {
+
+ if (link->nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_PRIORITIZED) {
+ int64_t priority_group;
+
+ (void) pthread_mutex_lock(&active_ncp_mutex);
+ priority_group = current_ncu_priority_group;
+ (void) pthread_mutex_unlock(&active_ncp_mutex);
+
+ /* compare priority groups */
+ if (link->nwamd_link_priority_group > priority_group) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_link_state_event: "
+ "got LINK UP event for priority group "
+ "%lld, less preferred than current %lld, "
+ "ignoring",
+ link->nwamd_link_priority_group,
+ priority_group);
+
+ } else if (link->nwamd_link_priority_group ==
+ priority_group) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_link_state_event: "
+ "got LINK UP event for priority group "
+ "%lld, same as current %lld",
+ link->nwamd_link_priority_group,
+ priority_group);
+ /*
+ * Change link state to UP. It will be
+ * propagated to IP state machine. Only do
+ * the NCU check if and when the interface
+ * NCU is online.
+ */
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_UP);
+ } else {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_link_state_event: "
+ "got LINK UP event for priority group "
+ "%lld, more preferred than current %lld",
+ link->nwamd_link_priority_group,
+ priority_group);
+
+ /*
+ * We need to mark the link as up so that when
+ * it is activated we will bring the interface
+ * up.
+ */
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object,
+ NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_UP);
+ nwamd_object_release(ncu_obj);
+ nwamd_ncp_deactivate_priority_group
+ (priority_group);
+ nwamd_ncp_activate_priority_group
+ (link->nwamd_link_priority_group);
+ return;
+ }
+
+ } else if (link->nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_MANUAL) {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: "
+ "got LINK UP event for manual NCU %s",
+ ncu_obj->nwamd_object_name);
+
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
+ NWAM_AUX_STATE_UP);
+ }
+ }
+
+ /*
+ * If the link is down then start or continue transition down.
+ */
+ if (!evm->nwe_data.nwe_link_state.nwe_link_up &&
+ (ncu_obj->nwamd_object_state == NWAM_STATE_ONLINE ||
+ ncu_obj->nwamd_object_state == NWAM_STATE_OFFLINE_TO_ONLINE)) {
+
+ if (link->nwamd_link_activation_mode ==
+ NWAM_ACTIVATION_MODE_PRIORITIZED) {
+ nlog(LOG_DEBUG,
+ "nwamd_ncu_handle_link_state_event: "
+ "got LINK DOWN for priority group %lld",
+ link->nwamd_link_priority_group);
+ /* Moving to offline checks priority group */
+ } else {
+ nlog(LOG_DEBUG, "nwamd_ncu_handle_link_state_event: "
+ "got LINK DOWN event for manual NCU %s",
+ ncu_obj->nwamd_object_name);
+ }
+ nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
+ event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
+ NWAM_AUX_STATE_DOWN);
+ }
+
+ nwamd_object_release(ncu_obj);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/objects.c b/usr/src/cmd/cmd-inet/lib/nwamd/objects.c
new file mode 100644
index 0000000000..501bfa6077
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/objects.c
@@ -0,0 +1,474 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h>
+#include <syslog.h>
+#include <libuutil.h>
+#include <errno.h>
+
+#include "events.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * objects.c - contains routines which manipulate object lists of NCUs,
+ * locations, ENMs and known WLANs.
+ */
+
+typedef struct nwamd_object_list {
+ nwam_object_type_t object_type;
+ uu_list_t *object_list;
+ nwamd_event_method_t *object_event_methods;
+ pthread_rwlock_t object_list_lock;
+} nwamd_object_list_t;
+
+nwamd_event_method_t enm_event_methods[] =
+{
+ { NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_enm_handle_init_event },
+ { NWAM_EVENT_TYPE_OBJECT_FINI, nwamd_enm_handle_fini_event },
+ { NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_enm_handle_action_event },
+ { NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_enm_handle_state_event },
+ { NWAM_EVENT_TYPE_NOOP, NULL }
+};
+
+nwamd_event_method_t loc_event_methods[] =
+{
+ { NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_loc_handle_init_event },
+ { NWAM_EVENT_TYPE_OBJECT_FINI, nwamd_loc_handle_fini_event },
+ { NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_loc_handle_action_event },
+ { NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_loc_handle_state_event },
+ { NWAM_EVENT_TYPE_NOOP, NULL }
+};
+
+nwamd_event_method_t ncu_event_methods[] =
+{
+ { NWAM_EVENT_TYPE_IF_STATE, nwamd_ncu_handle_if_state_event },
+ { NWAM_EVENT_TYPE_IF_ACTION, nwamd_ncu_handle_if_action_event },
+ { NWAM_EVENT_TYPE_LINK_STATE, nwamd_ncu_handle_link_state_event },
+ { NWAM_EVENT_TYPE_LINK_ACTION, nwamd_ncu_handle_link_action_event },
+ { NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_ncu_handle_init_event },
+ { NWAM_EVENT_TYPE_OBJECT_FINI, nwamd_ncu_handle_fini_event },
+ { NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_ncu_handle_action_event },
+ { NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_ncu_handle_state_event },
+ { NWAM_EVENT_TYPE_PERIODIC_SCAN, nwamd_ncu_handle_periodic_scan_event },
+ { NWAM_EVENT_TYPE_NOOP, NULL }
+};
+
+nwamd_event_method_t ncp_event_methods[] =
+{
+ { NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_ncp_handle_action_event },
+ { NWAM_EVENT_TYPE_OBJECT_STATE, nwamd_ncp_handle_state_event },
+ { NWAM_EVENT_TYPE_UPGRADE, nwamd_handle_upgrade },
+ { NWAM_EVENT_TYPE_NOOP, NULL }
+};
+
+nwamd_event_method_t known_wlan_event_methods[] =
+{
+ { NWAM_EVENT_TYPE_OBJECT_INIT, nwamd_known_wlan_handle_init_event },
+ { NWAM_EVENT_TYPE_OBJECT_FINI, NULL },
+ { NWAM_EVENT_TYPE_OBJECT_ACTION, nwamd_known_wlan_handle_action_event },
+ { NWAM_EVENT_TYPE_NOOP, NULL }
+};
+
+/* Should be kept in same order as object types */
+nwamd_object_list_t object_lists[] = {
+ { NWAM_OBJECT_TYPE_NCP, NULL, ncp_event_methods,
+ PTHREAD_RWLOCK_INITIALIZER },
+ { NWAM_OBJECT_TYPE_NCU, NULL, ncu_event_methods,
+ PTHREAD_RWLOCK_INITIALIZER },
+ { NWAM_OBJECT_TYPE_LOC, NULL, loc_event_methods,
+ PTHREAD_RWLOCK_INITIALIZER },
+ { NWAM_OBJECT_TYPE_ENM, NULL, enm_event_methods,
+ PTHREAD_RWLOCK_INITIALIZER },
+ { NWAM_OBJECT_TYPE_KNOWN_WLAN, NULL, known_wlan_event_methods,
+ PTHREAD_RWLOCK_INITIALIZER }
+};
+
+uu_list_pool_t *object_list_pool = NULL;
+
+/*
+ * Comparison function for objects, passed in as callback to
+ * uu_list_pool_create().
+ */
+/* ARGSUSED */
+static int
+nwamd_object_compare(const void *l_arg, const void *r_arg, void *private)
+{
+ nwamd_object_t l = (nwamd_object_t)l_arg;
+ nwamd_object_t r = (nwamd_object_t)r_arg;
+ int rv;
+
+ (void) pthread_mutex_lock(&l->nwamd_object_mutex);
+ if (l != r)
+ (void) pthread_mutex_lock(&r->nwamd_object_mutex);
+
+ rv = strcmp(l->nwamd_object_name, r->nwamd_object_name);
+ if (l != r)
+ (void) pthread_mutex_unlock(&r->nwamd_object_mutex);
+ (void) pthread_mutex_unlock(&l->nwamd_object_mutex);
+
+ return (rv);
+}
+
+void
+nwamd_object_lists_init(void)
+{
+ int i;
+
+ object_list_pool = uu_list_pool_create("object_list_pool",
+ sizeof (struct nwamd_object),
+ offsetof(struct nwamd_object, nwamd_object_node),
+ nwamd_object_compare, UU_LIST_POOL_DEBUG);
+ if (object_list_pool == NULL)
+ pfail("uu_list_pool_create failed with error %d", uu_error());
+
+ for (i = 0;
+ i < sizeof (object_lists) / sizeof (struct nwamd_object_list);
+ i++) {
+ object_lists[i].object_list = uu_list_create(object_list_pool,
+ NULL, 0);
+ if (object_lists[i].object_list == NULL)
+ pfail("uu_list_create failed with error %d",
+ uu_error());
+ }
+}
+
+void
+nwamd_object_lists_fini(void)
+{
+ int i;
+ nwamd_object_t object;
+ void *cookie = NULL;
+
+ for (i = 0;
+ i < sizeof (object_lists) / sizeof (struct nwamd_object_list);
+ i++) {
+ while ((object = uu_list_teardown(object_lists[i].object_list,
+ &cookie)) != NULL) {
+ free(object);
+ }
+ uu_list_destroy(object_lists[i].object_list);
+ }
+ if (object_list_pool != NULL)
+ uu_list_pool_destroy(object_list_pool);
+}
+
+static nwamd_object_list_t *
+nwamd_get_object_list(nwam_object_type_t type)
+{
+ assert(type < sizeof (object_lists) / sizeof (object_lists[0]));
+ return (&object_lists[type]);
+}
+
+static int
+nwamd_object_list_lock(nwam_object_type_t type)
+{
+ nwamd_object_list_t *object_list = nwamd_get_object_list(type);
+
+ (void) pthread_rwlock_wrlock(&object_list->object_list_lock);
+ return (0);
+}
+
+static int
+nwamd_object_list_rlock(nwam_object_type_t type)
+{
+ nwamd_object_list_t *object_list = nwamd_get_object_list(type);
+
+ if (pthread_rwlock_rdlock(&object_list->object_list_lock) == -1) {
+ nlog(LOG_ERR, "cannot get lock for object list: %s",
+ strerror(errno));
+ return (-1);
+ }
+ return (0);
+}
+
+static void
+nwamd_object_list_unlock(nwam_object_type_t type)
+{
+ nwamd_object_list_t *object_list = nwamd_get_object_list(type);
+
+ (void) pthread_rwlock_unlock(&object_list->object_list_lock);
+}
+
+/*
+ * Initialize object and return it in locked state.
+ */
+nwamd_object_t
+nwamd_object_init(nwam_object_type_t type, const char *name, void *handle,
+ void *data)
+{
+ nwamd_object_t object;
+ struct nwamd_object_list *object_list = nwamd_get_object_list(type);
+
+ object = calloc(1, sizeof (struct nwamd_object));
+ if (object == NULL)
+ return (NULL);
+
+ (void) strlcpy(object->nwamd_object_name, name, NWAM_MAX_NAME_LEN);
+
+ /* 1 for the list and 1 for the returned object */
+ object->nwamd_object_refcount = 2;
+ object->nwamd_object_handle = handle;
+ object->nwamd_object_data = data;
+ object->nwamd_object_type = type;
+ object->nwamd_object_state = NWAM_STATE_INITIALIZED;
+ object->nwamd_object_aux_state = NWAM_AUX_STATE_INITIALIZED;
+
+ /* Add object to appropriate object list */
+ if (nwamd_object_list_lock(type) != 0) {
+ nlog(LOG_ERR, "nwamd_object_init: could not lock list to init "
+ "object %s", name);
+ free(object);
+ return (NULL);
+ }
+
+ if (pthread_mutex_init(&object->nwamd_object_mutex, NULL) == -1) {
+ nlog(LOG_ERR, "pthread_mutex_init failed: %s",
+ strerror(errno));
+ free(object);
+ nwamd_object_list_unlock(type);
+ return (NULL);
+ }
+ (void) pthread_mutex_lock(&object->nwamd_object_mutex);
+
+ uu_list_node_init(object, &object->nwamd_object_node, object_list_pool);
+ (void) uu_list_insert_after(object_list->object_list,
+ uu_list_last(object_list->object_list), object);
+
+ nwamd_object_list_unlock(type);
+
+ return (object);
+}
+
+/*
+ * Find object in object list, returning it holding a lock and with the
+ * reference count incremented. The opposite function to this is
+ * nwamd_object_release().
+ */
+nwamd_object_t
+nwamd_object_find(nwam_object_type_t type, const char *name)
+{
+ nwamd_object_t object;
+ struct nwamd_object_list *object_list = nwamd_get_object_list(type);
+
+ assert(name != NULL);
+
+ if (nwamd_object_list_rlock(type) != 0)
+ return (NULL);
+
+ for (object = uu_list_first(object_list->object_list);
+ object != NULL;
+ object = uu_list_next(object_list->object_list, object)) {
+ if (strcmp(object->nwamd_object_name, name) == 0)
+ break;
+ }
+ if (object != NULL) {
+ (void) pthread_mutex_lock(&object->nwamd_object_mutex);
+ object->nwamd_object_refcount++;
+ }
+ nwamd_object_list_unlock(type);
+
+ return (object);
+}
+
+/* Removes object from list, destroy mutex, and free storage. */
+static void
+nwamd_object_fini(nwamd_object_t object, nwam_object_type_t objtype)
+{
+ nwamd_object_t o;
+ struct nwamd_object_list *object_list;
+
+ assert(object != NULL);
+
+ object_list = nwamd_get_object_list(objtype);
+
+ for (o = uu_list_first(object_list->object_list);
+ o != NULL;
+ o = uu_list_next(object_list->object_list, o)) {
+ if (o == object) {
+ uu_list_remove(object_list->object_list, object);
+ (void) pthread_mutex_unlock(
+ &object->nwamd_object_mutex);
+ (void) pthread_mutex_destroy(
+ &object->nwamd_object_mutex);
+ uu_list_node_fini(object, &object->nwamd_object_node,
+ object_list_pool);
+ switch (objtype) {
+ case NWAM_OBJECT_TYPE_NCU:
+ nwamd_ncu_free(object->nwamd_object_data);
+ nwam_ncu_free(object->nwamd_object_handle);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ nwam_loc_free(object->nwamd_object_handle);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ nwam_enm_free(object->nwamd_object_handle);
+ break;
+ default:
+ nlog(LOG_ERR, "nwamd_object_fini: "
+ "got unexpected object type %d", objtype);
+ break;
+ }
+ free(object);
+ break;
+ }
+ }
+}
+
+static void
+nwamd_object_decref(nwamd_object_t object, int num)
+{
+ nwam_object_type_t objtype;
+
+ assert(object->nwamd_object_refcount >= num);
+ object->nwamd_object_refcount -= num;
+ if (object->nwamd_object_refcount == 0) {
+ /*
+ * We need to maintain the locking hierarchy of owning the
+ * list lock before we get the object lock when we are
+ * destroying the object. If we merely release and then
+ * reacquire in the right order we might not find the right
+ * object. Instead we bump the ref count so that it can't
+ * be destroyed, we drop the object lock, we acquire the
+ * list lock, we acquire the object lock, decrement the ref
+ * count, check to make sure we are really destroying it and
+ * somebody else hasn't gotten it, and then, if its unref'd,
+ * destroying it.
+ */
+ object->nwamd_object_refcount++;
+ objtype = object->nwamd_object_type;
+ (void) pthread_mutex_unlock(&object->nwamd_object_mutex);
+ (void) nwamd_object_list_lock(objtype);
+ (void) pthread_mutex_lock(&object->nwamd_object_mutex);
+ if (--object->nwamd_object_refcount != 0)
+ (void) pthread_mutex_unlock(
+ &object->nwamd_object_mutex);
+ else
+ nwamd_object_fini(object, objtype);
+ nwamd_object_list_unlock(objtype);
+ } else {
+ (void) pthread_mutex_unlock(&object->nwamd_object_mutex);
+ }
+}
+
+/*
+ * Drop mutex without decreasing reference count. Used where we wish to
+ * let go of an object but ensure it will not go away.
+ */
+void
+nwamd_object_release_and_preserve(nwamd_object_t object)
+{
+ (void) pthread_mutex_unlock(&object->nwamd_object_mutex);
+}
+
+void
+nwamd_object_release(nwamd_object_t object)
+{
+ nwamd_object_decref(object, 1);
+}
+
+void
+nwamd_object_release_and_destroy(nwamd_object_t object)
+{
+ nwamd_object_decref(object, 2);
+}
+
+void
+nwamd_object_release_and_destroy_after_preserve(nwamd_object_t object)
+{
+ nwamd_object_decref(object, 3);
+}
+
+void
+nwamd_object_release_after_preserve(nwamd_object_t object)
+{
+ nwamd_object_decref(object, 2);
+}
+
+void
+nwamd_object_set_state_timed(nwam_object_type_t type, const char *name,
+ nwam_state_t state, nwam_aux_state_t aux_state, uint32_t when)
+{
+ nwamd_event_t event = nwamd_event_init_object_state(type, name,
+ state, aux_state);
+
+ nlog(LOG_INFO, "nwamd_object_set_state: state event (%s, %s) for %s",
+ nwam_state_to_string(state),
+ nwam_aux_state_to_string(aux_state), name);
+ if (event != NULL)
+ nwamd_event_enqueue_timed(event, when);
+}
+
+void
+nwamd_object_set_state(nwam_object_type_t type, const char *name,
+ nwam_state_t state, nwam_aux_state_t aux_state)
+{
+ nwamd_object_set_state_timed(type, name, state, aux_state, 0);
+}
+
+nwamd_event_method_t *
+nwamd_object_event_methods(nwam_object_type_t type)
+{
+ struct nwamd_object_list *object_list = nwamd_get_object_list(type);
+
+ return (object_list->object_event_methods);
+}
+
+/*
+ * Walk all objects of specified type calling callback function cb.
+ * Object is locked for duration of callback.
+ */
+int
+nwamd_walk_objects(nwam_object_type_t type, int (*cb)(nwamd_object_t, void *),
+ void *data)
+{
+ nwamd_object_t object;
+ struct nwamd_object_list *object_list = nwamd_get_object_list(type);
+ int ret = 0;
+
+ if (nwamd_object_list_rlock(type) != 0)
+ return (-1);
+
+ for (object = uu_list_first(object_list->object_list);
+ object != NULL;
+ object = uu_list_next(object_list->object_list, object)) {
+ (void) pthread_mutex_lock(&object->nwamd_object_mutex);
+ ret = cb(object, data);
+ (void) pthread_mutex_unlock(&object->nwamd_object_mutex);
+ if (ret != 0) {
+ nwamd_object_list_unlock(type);
+ return (ret);
+ }
+ }
+ nwamd_object_list_unlock(type);
+
+ return (0);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/objects.h b/usr/src/cmd/cmd-inet/lib/nwamd/objects.h
new file mode 100644
index 0000000000..d2e4853738
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/objects.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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _OBJECTS_H
+#define _OBJECTS_H
+
+#include <door.h>
+#include <libsysevent.h>
+#include <libuutil.h>
+#include <pthread.h>
+
+#include <libnwam.h>
+#include "events.h"
+#include "ncp.h"
+#include "ncu.h"
+
+/*
+ * Wrapper structure for libnwam object, containing name, type,
+ * associated object handle and optional object data field, and uu_list_node.
+ */
+struct nwamd_object {
+ char nwamd_object_name[NWAM_MAX_NAME_LEN];
+ nwam_object_type_t nwamd_object_type;
+
+ /*
+ * These two elements provide a reference count for the structure and
+ * a lock for the data including reference count.
+ */
+ int nwamd_object_refcount;
+ pthread_mutex_t nwamd_object_mutex;
+
+ void *nwamd_object_handle; /* can point at ENMs, locations, etc. */
+ nwamd_ncu_t *nwamd_object_data;
+ nwam_state_t nwamd_object_state;
+ nwam_aux_state_t nwamd_object_aux_state;
+ uu_list_node_t nwamd_object_node;
+};
+
+/* Object init/enqueueing */
+extern void nwamd_object_lists_init(void);
+extern void nwamd_object_lists_fini(void);
+extern nwamd_object_t nwamd_object_init(nwam_object_type_t, const char *,
+ void *, void *);
+extern nwamd_object_t nwamd_object_find(nwam_object_type_t, const char *);
+extern void nwamd_object_release_and_preserve(nwamd_object_t);
+extern void nwamd_object_release(nwamd_object_t);
+extern void nwamd_object_release_and_destroy(nwamd_object_t);
+extern void nwamd_object_release_after_preserve(nwamd_object_t);
+extern void nwamd_object_release_and_destroy_after_preserve(nwamd_object_t);
+extern void nwamd_object_set_state(nwam_object_type_t, const char *,
+ nwam_state_t, nwam_aux_state_t);
+extern void nwamd_object_set_state_timed(nwam_object_type_t, const char *,
+ nwam_state_t, nwam_aux_state_t, uint32_t);
+extern nwamd_event_method_t *nwamd_object_event_methods(nwam_object_type_t);
+extern int nwamd_walk_objects(nwam_object_type_t,
+ int (*)(nwamd_object_t, void *), void *);
+extern int nwamd_object_update(const char *, nwam_object_type_t);
+
+/* Known WLAN functions (no wlan objects, so no init/fini functions) */
+/* event methods */
+extern void nwamd_known_wlan_handle_init_event(nwamd_event_t);
+
+/* refresh/destroy a known WLAN */
+extern int nwamd_known_wlan_action(const char *, nwam_action_t);
+
+/* ENM functions */
+/* Init/fini functions for ENMs */
+extern void nwamd_init_enms(void);
+extern void nwamd_fini_enms(void);
+
+/* ENM condition check function */
+extern void nwamd_enm_check_conditions(void);
+
+/* event methods */
+extern void nwamd_enm_handle_init_event(nwamd_event_t);
+extern void nwamd_enm_handle_fini_event(nwamd_event_t);
+
+/* enable/disable an enm */
+extern int nwamd_enm_action(const char *, nwam_action_t);
+
+/* reread an enm from the repository */
+extern int nwamd_enm_refresh(const char *);
+
+/* loc functions */
+/* Init/fini functions for locs */
+extern void nwamd_init_locs(void);
+extern void nwamd_fini_locs(void);
+
+/* loc condition check function */
+extern void nwamd_loc_check_conditions(void);
+
+/* on shutdown, revert to legacy location */
+extern void nwamd_loc_revert_to_legacy(void);
+
+/* event methods */
+extern void nwamd_loc_handle_init_event(nwamd_event_t);
+extern void nwamd_loc_handle_fini_event(nwamd_event_t);
+
+/* enable/disable a loc */
+extern int nwamd_loc_action(const char *, nwam_action_t);
+
+/* reread a loc from the repository */
+extern int nwamd_loc_refresh(const char *);
+
+/* NCU functions */
+extern void nwamd_init_ncus(void);
+extern void nwamd_fini_ncus(void);
+
+/* enable an ncp/ncu */
+extern int nwamd_ncp_action(const char *, nwam_action_t);
+extern int nwamd_ncu_action(const char *, const char *, nwam_action_t);
+
+/*
+ * Event callbacks.
+ */
+extern void nwamd_ncu_handle_init_event(nwamd_event_t);
+extern void nwamd_ncu_handle_fini_event(nwamd_event_t);
+extern void nwamd_ncu_handle_if_state_event(nwamd_event_t);
+extern void nwamd_ncu_handle_if_action_event(nwamd_event_t);
+extern void nwamd_ncu_handle_link_state_event(nwamd_event_t);
+extern void nwamd_ncu_handle_link_action_event(nwamd_event_t);
+extern void nwamd_ncu_handle_init_event(nwamd_event_t);
+extern void nwamd_ncu_handle_fini_event(nwamd_event_t);
+extern void nwamd_ncu_handle_action_event(nwamd_event_t);
+extern void nwamd_ncu_handle_state_event(nwamd_event_t);
+
+extern void nwamd_ncp_handle_action_event(nwamd_event_t);
+extern void nwamd_ncp_handle_state_event(nwamd_event_t);
+extern void nwamd_ncu_handle_periodic_scan_event(nwamd_event_t);
+extern void nwamd_ncp_handle_enable_event(nwamd_event_t);
+extern void nwamd_handle_upgrade(nwamd_event_t);
+
+extern void nwamd_enm_handle_action_event(nwamd_event_t);
+extern void nwamd_enm_handle_state_event(nwamd_event_t);
+
+extern void nwamd_loc_handle_action_event(nwamd_event_t);
+extern void nwamd_loc_handle_state_event(nwamd_event_t);
+
+extern void nwamd_known_wlan_handle_action_event(nwamd_event_t);
+
+extern void nwamd_add_phys_ncu_auto(nwam_ncp_handle_t, const char *);
+extern void nwamd_rem_phys_ncu_auto(nwam_ncp_handle_t, const char *);
+extern void add_auto_link(nwam_ncp_handle_t, const char *);
+extern void add_auto_ip(nwam_ncp_handle_t, const char *);
+extern void rem_auto_link(nwam_ncp_handle_t, const char *);
+extern void rem_auto_ip(nwam_ncp_handle_t, const char *);
+
+#endif /* _OBJECTS_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/routing_events.c b/usr/src/cmd/cmd-inet/lib/nwamd/routing_events.c
new file mode 100644
index 0000000000..8a7a2910d4
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/routing_events.c
@@ -0,0 +1,637 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+
+#include <inetcfg.h>
+#include <libnwam.h>
+#include "events.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "util.h"
+
+/*
+ * routing_events.c - this file contains routines to retrieve routing socket
+ * events and package them for high level processing.
+ */
+
+#define RTMBUFSZ sizeof (struct rt_msghdr) + \
+ (RTAX_MAX * sizeof (struct sockaddr_storage))
+
+static void printaddrs(int, void *);
+static char *printaddr(void **);
+static void *getaddr(int, int, void *);
+static void setaddr(int, int *, void *, struct sockaddr *);
+
+union rtm_buf
+{
+ /* Routing information. */
+ struct
+ {
+ struct rt_msghdr rtm;
+ struct sockaddr_storage addr[RTAX_MAX];
+ } r;
+
+ /* Interface information. */
+ struct
+ {
+ struct if_msghdr ifm;
+ struct sockaddr_storage addr[RTAX_MAX];
+ } im;
+
+ /* Interface address information. */
+ struct
+ {
+ struct ifa_msghdr ifa;
+ struct sockaddr_storage addr[RTAX_MAX];
+ } ia;
+};
+
+static int v4_sock = -1;
+static int v6_sock = -1;
+static pthread_t v4_routing, v6_routing;
+static int seq = 0;
+
+static const char *
+rtmtype_str(int type)
+{
+ static char typestr[12]; /* strlen("type ") + enough for an int */
+
+ switch (type) {
+ case RTM_NEWADDR:
+ return ("NEWADDR");
+ case RTM_DELADDR:
+ return ("DELADDR");
+ case RTM_CHGADDR:
+ return ("CHGADDR");
+ case RTM_FREEADDR:
+ return ("FREEADDR");
+ case RTM_IFINFO:
+ return ("IFINFO");
+ default:
+ (void) snprintf(typestr, sizeof (typestr), "type %d", type);
+ return (typestr);
+ }
+}
+
+/* ARGSUSED0 */
+static void *
+routing_events_v4(void *arg)
+{
+ int n;
+ union rtm_buf buffer;
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifa;
+ struct if_msghdr *ifm;
+ char *addrs, *if_name;
+ struct sockaddr_dl *addr_dl;
+ struct sockaddr *addr;
+ nwamd_event_t ip_event;
+
+ nlog(LOG_DEBUG, "v4 routing socket %d", v4_sock);
+
+ for (;;) {
+ rtm = &buffer.r.rtm;
+ n = read(v4_sock, &buffer, sizeof (buffer));
+ if (n == -1 && errno == EAGAIN) {
+ continue;
+ } else if (n == -1) {
+ nlog(LOG_ERR, "error reading routing socket "
+ "%d: %m", v4_sock);
+ /* Low likelihood. What's recovery path? */
+ continue;
+ }
+
+ if (rtm->rtm_msglen < n) {
+ nlog(LOG_ERR, "only read %d bytes from "
+ "routing socket but message claims to be "
+ "of length %d", rtm->rtm_msglen);
+ continue;
+ }
+
+ if (rtm->rtm_version != RTM_VERSION) {
+ nlog(LOG_ERR, "tossing routing message of "
+ "version %d type %d", rtm->rtm_version,
+ rtm->rtm_type);
+ continue;
+ }
+
+ if (rtm->rtm_msglen != n) {
+ nlog(LOG_DEBUG, "routing message of %d size came from "
+ "read of %d on socket %d", rtm->rtm_msglen,
+ n, v4_sock);
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_CHGADDR:
+ case RTM_FREEADDR:
+
+ ifa = (void *)rtm;
+ addrs = (char *)ifa + sizeof (*ifa);
+
+ nlog(LOG_DEBUG, "v4 routing message %s: "
+ "index %d flags %x", rtmtype_str(rtm->rtm_type),
+ ifa->ifam_index, ifa->ifam_flags);
+ printaddrs(ifa->ifam_addrs, addrs);
+
+ if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
+ ifa->ifam_addrs, addrs)) == NULL)
+ break;
+
+ /* Ignore routing socket messages for 0.0.0.0 */
+ /*LINTED*/
+ if (((struct sockaddr_in *)addr)->sin_addr.s_addr
+ == INADDR_ANY) {
+ nlog(LOG_DEBUG, "routing_events_v4: "
+ "tossing message for 0.0.0.0");
+ break;
+ }
+
+ if ((addr_dl = (struct sockaddr_dl *)getaddr
+ (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
+ 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; /* no lifnum */
+
+ if (ifa->ifam_index == 0) {
+ nlog(LOG_DEBUG, "tossing index 0 message");
+ break;
+ }
+ if (ifa->ifam_type != rtm->rtm_type) {
+ nlog(LOG_INFO,
+ "routing_events_v4: unhandled type %d",
+ ifa->ifam_type);
+ break;
+ }
+
+ /* Create and enqueue IF_STATE event */
+ ip_event = nwamd_event_init_if_state(if_name,
+ ifa->ifam_flags,
+ (rtm->rtm_type == RTM_NEWADDR ||
+ rtm->rtm_type == RTM_CHGADDR ? B_TRUE : B_FALSE),
+ ifa->ifam_index, addr);
+ if (ip_event != NULL)
+ nwamd_event_enqueue(ip_event);
+ break;
+
+ case RTM_IFINFO:
+
+ ifm = (void *)rtm;
+ addrs = (char *)ifm + sizeof (*ifm);
+ nlog(LOG_DEBUG, "v4 routing message %s: "
+ "index %d flags %x", rtmtype_str(rtm->rtm_type),
+ ifm->ifm_index, ifm->ifm_flags);
+ printaddrs(ifm->ifm_addrs, addrs);
+
+ if ((addr_dl = (struct sockaddr_dl *)getaddr(RTA_IFP,
+ ifm->ifm_addrs, addrs)) == NULL)
+ break;
+ /*
+ * 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; /* no lifnum */
+
+ if (ifm->ifm_index == 0) {
+ nlog(LOG_DEBUG, "tossing index 0 message");
+ break;
+ }
+ if (ifm->ifm_type != RTM_IFINFO) {
+ nlog(LOG_DEBUG,
+ "routing_events_v4: unhandled type %d",
+ ifm->ifm_type);
+ break;
+ }
+
+ /* Create and enqueue IF_STATE event */
+ ip_event = nwamd_event_init_if_state(if_name,
+ ifm->ifm_flags, B_FALSE, ifm->ifm_index, NULL);
+ if (ip_event != NULL)
+ nwamd_event_enqueue(ip_event);
+ break;
+
+ default:
+ nlog(LOG_DEBUG, "v4 routing message %s discarded",
+ rtmtype_str(rtm->rtm_type));
+ break;
+ }
+ }
+ /* NOTREACHED */
+ return (NULL);
+}
+
+/* ARGSUSED0 */
+static void *
+routing_events_v6(void *arg)
+{
+ int n;
+ union rtm_buf buffer;
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifa;
+ struct if_msghdr *ifm;
+ char *addrs, *if_name;
+ struct sockaddr_dl *addr_dl;
+ struct sockaddr *addr;
+ nwamd_event_t ip_event;
+
+ nlog(LOG_DEBUG, "v6 routing socket %d", v6_sock);
+
+ for (;;) {
+
+ rtm = &buffer.r.rtm;
+ n = read(v6_sock, &buffer, sizeof (buffer));
+ if (n == -1 && errno == EAGAIN) {
+ continue;
+ } else if (n == -1) {
+ nlog(LOG_ERR, "error reading routing socket "
+ "%d: %m", v6_sock);
+ /* Low likelihood. What's recovery path? */
+ continue;
+ }
+
+ if (rtm->rtm_msglen < n) {
+ nlog(LOG_ERR, "only read %d bytes from "
+ "routing socket but message claims to be "
+ "of length %d", rtm->rtm_msglen);
+ continue;
+ }
+
+ if (rtm->rtm_version != RTM_VERSION) {
+ nlog(LOG_ERR, "tossing routing message of "
+ "version %d type %d", rtm->rtm_version,
+ rtm->rtm_type);
+ continue;
+ }
+
+ if (rtm->rtm_msglen != n) {
+ nlog(LOG_DEBUG, "routing message of %d size came from "
+ "read of %d on socket %d", rtm->rtm_msglen,
+ n, v6_sock);
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_CHGADDR:
+ case RTM_FREEADDR:
+
+ ifa = (void *)rtm;
+ addrs = (char *)ifa + sizeof (*ifa);
+
+ nlog(LOG_DEBUG, "v6 routing message %s: "
+ "index %d flags %x", rtmtype_str(rtm->rtm_type),
+ ifa->ifam_index, ifa->ifam_flags);
+ printaddrs(ifa->ifam_addrs, addrs);
+
+ if ((addr = (struct sockaddr *)getaddr(RTA_IFA,
+ ifa->ifam_addrs, addrs)) == NULL)
+ break;
+
+ /* Ignore messages for link local address */
+ /*LINTED*/
+ if (IN6_IS_ADDR_LINKLOCAL(
+ &((struct sockaddr_in6 *)addr)->sin6_addr)) {
+ nlog(LOG_INFO, "routing_events_v6: "
+ "tossing message for link local address");
+ break;
+ }
+
+ if ((addr_dl = (struct sockaddr_dl *)getaddr
+ (RTA_IFP, ifa->ifam_addrs, addrs)) == NULL)
+ 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; /* no lifnum */
+
+ if (ifa->ifam_index == 0) {
+ nlog(LOG_DEBUG, "tossing index 0 message");
+ break;
+ }
+ if (ifa->ifam_type != rtm->rtm_type) {
+ nlog(LOG_DEBUG,
+ "routing_events_v6: unhandled type %d",
+ ifa->ifam_type);
+ break;
+ }
+
+ /* Create and enqueue IF_STATE event */
+ ip_event = nwamd_event_init_if_state(if_name,
+ ifa->ifam_flags,
+ (rtm->rtm_type == RTM_NEWADDR ||
+ rtm->rtm_type == RTM_CHGADDR ? B_TRUE : B_FALSE),
+ ifa->ifam_index, addr);
+ if (ip_event != NULL)
+ nwamd_event_enqueue(ip_event);
+ break;
+
+ case RTM_IFINFO:
+
+ ifm = (void *)rtm;
+ addrs = (char *)ifm + sizeof (*ifm);
+ nlog(LOG_DEBUG, "v6 routing message %s: "
+ "index %d flags %x", rtmtype_str(rtm->rtm_type),
+ ifm->ifm_index, ifm->ifm_flags);
+ printaddrs(ifm->ifm_addrs, addrs);
+
+ if ((addr_dl = (struct sockaddr_dl *)getaddr(RTA_IFP,
+ ifm->ifm_addrs, addrs)) == NULL)
+ break;
+ /*
+ * 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; /* no lifnum */
+
+ if (ifm->ifm_index == 0) {
+ nlog(LOG_DEBUG, "tossing index 0 message");
+ break;
+ }
+ if (ifm->ifm_type != RTM_IFINFO) {
+ nlog(LOG_DEBUG,
+ "routing_events_v6: unhandled type %d",
+ ifm->ifm_type);
+ break;
+ }
+
+ /* Create and enqueue IF_STATE event */
+ ip_event = nwamd_event_init_if_state(if_name,
+ ifm->ifm_flags, B_FALSE, ifm->ifm_index, NULL);
+ if (ip_event != NULL)
+ nwamd_event_enqueue(ip_event);
+ break;
+
+ default:
+ nlog(LOG_DEBUG, "v6 routing message %s discarded",
+ rtmtype_str(rtm->rtm_type));
+ break;
+ }
+ }
+ /* NOTREACHED */
+ return (NULL);
+}
+
+void
+nwamd_routing_events_init(void)
+{
+ pthread_attr_t attr;
+
+ /*
+ * Initialize routing sockets here so that we know the routing threads
+ * (and any requests to add a route) will be working with a valid socket
+ * by the time we start handling events.
+ */
+ v4_sock = socket(AF_ROUTE, SOCK_RAW, AF_INET);
+ if (v4_sock == -1)
+ pfail("failed to open v4 routing socket: %m");
+
+ v6_sock = socket(AF_ROUTE, SOCK_RAW, AF_INET6);
+ if (v6_sock == -1)
+ pfail("failed to open v6 routing socket: %m");
+
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (pthread_create(&v4_routing, &attr, routing_events_v4, NULL) != 0 ||
+ pthread_create(&v6_routing, &attr, routing_events_v6, NULL) != 0)
+ pfail("routing thread creation failed");
+ (void) pthread_attr_destroy(&attr);
+}
+
+void
+nwamd_routing_events_fini(void)
+{
+ (void) pthread_cancel(v4_routing);
+ (void) pthread_cancel(v6_routing);
+}
+
+void
+nwamd_add_route(struct sockaddr *dest, struct sockaddr *mask,
+ struct sockaddr *gateway, const char *ifname)
+{
+ char rtbuf[RTMBUFSZ];
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf;
+ void *addrs = rtbuf + sizeof (struct rt_msghdr);
+ struct sockaddr_dl sdl;
+ icfg_if_t intf;
+ icfg_handle_t h;
+ int rlen, index;
+ int af;
+
+ af = gateway->sa_family;
+
+ /* set interface for default route to be associated with */
+ (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name));
+ intf.if_protocol = af;
+ if (icfg_open(&h, &intf) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_add_route: "
+ "icfg_open failed on %s", ifname);
+ return;
+ }
+ if (icfg_get_index(h, &index) != ICFG_SUCCESS) {
+ nlog(LOG_ERR, "nwamd_add_route: "
+ "icfg_get_index failed on %s", ifname);
+ }
+ icfg_close(h);
+ (void) bzero(&sdl, sizeof (struct sockaddr_dl));
+ sdl.sdl_family = AF_LINK;
+ sdl.sdl_index = index;
+
+ (void) bzero(rtm, RTMBUFSZ);
+ rtm->rtm_pid = getpid();
+ rtm->rtm_type = RTM_ADD;
+ rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_msglen = sizeof (rtbuf);
+ setaddr(RTA_DST, &rtm->rtm_addrs, &addrs, dest);
+ setaddr(RTA_GATEWAY, &rtm->rtm_addrs, &addrs, gateway);
+ setaddr(RTA_NETMASK, &rtm->rtm_addrs, &addrs, mask);
+ setaddr(RTA_IFP, &rtm->rtm_addrs, &addrs, (struct sockaddr *)&sdl);
+
+ if ((rlen = write(af == AF_INET ? v4_sock : v6_sock,
+ rtbuf, rtm->rtm_msglen)) < 0) {
+ nlog(LOG_ERR, "nwamd_add_route: "
+ "got error %s writing to routing socket", strerror(errno));
+ } else if (rlen < rtm->rtm_msglen) {
+ nlog(LOG_ERR, "nwamd_add_route: "
+ "only wrote %d bytes of %d to routing socket\n",
+ rlen, rtm->rtm_msglen);
+ }
+}
+
+static char *
+printaddr(void **address)
+{
+ static char buffer[80];
+ sa_family_t family = *(sa_family_t *)*address;
+ struct sockaddr_in *s4 = *address;
+ struct sockaddr_in6 *s6 = *address;
+ struct sockaddr_dl *dl = *address;
+
+ switch (family) {
+ case AF_UNSPEC:
+ (void) inet_ntop(AF_UNSPEC, &s4->sin_addr, buffer,
+ sizeof (buffer));
+ *address = (char *)*address + sizeof (*s4);
+ break;
+ case AF_INET:
+ (void) inet_ntop(AF_INET, &s4->sin_addr, buffer,
+ sizeof (buffer));
+ *address = (char *)*address + sizeof (*s4);
+ break;
+ case AF_INET6:
+ (void) inet_ntop(AF_INET6, &s6->sin6_addr, buffer,
+ sizeof (buffer));
+ *address = (char *)*address + sizeof (*s6);
+ break;
+ case AF_LINK:
+ (void) snprintf(buffer, sizeof (buffer), "link %.*s",
+ dl->sdl_nlen, dl->sdl_data);
+ *address = (char *)*address + sizeof (*dl);
+ break;
+ default:
+ /*
+ * We can't reliably update the size of this thing
+ * because we don't know what its type is. So bump
+ * it by a sockaddr_in and see what happens. The
+ * caller should really make sure this never happens.
+ */
+ *address = (char *)*address + sizeof (*s4);
+ (void) snprintf(buffer, sizeof (buffer),
+ "unknown address family %d", family);
+ break;
+ }
+ return (buffer);
+}
+
+static void
+printaddrs(int mask, void *address)
+{
+ if (mask == 0)
+ return;
+ if (mask & RTA_DST)
+ nlog(LOG_DEBUG, "destination address: %s", printaddr(&address));
+ if (mask & RTA_GATEWAY)
+ nlog(LOG_DEBUG, "gateway address: %s", printaddr(&address));
+ if (mask & RTA_NETMASK)
+ nlog(LOG_DEBUG, "netmask: %s", printaddr(&address));
+ if (mask & RTA_GENMASK)
+ nlog(LOG_DEBUG, "cloning mask: %s", printaddr(&address));
+ if (mask & RTA_IFP)
+ nlog(LOG_DEBUG, "interface name: %s", printaddr(&address));
+ if (mask & RTA_IFA)
+ nlog(LOG_DEBUG, "interface address: %s", printaddr(&address));
+ if (mask & RTA_AUTHOR)
+ nlog(LOG_DEBUG, "author: %s", printaddr(&address));
+ if (mask & RTA_BRD)
+ nlog(LOG_DEBUG, "broadcast address: %s", printaddr(&address));
+}
+
+static void
+nextaddr(void **address)
+{
+ sa_family_t family = *(sa_family_t *)*address;
+
+ switch (family) {
+ case AF_UNSPEC:
+ case AF_INET:
+ *address = (char *)*address + sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ *address = (char *)*address + sizeof (struct sockaddr_in6);
+ break;
+ case AF_LINK:
+ *address = (char *)*address + sizeof (struct sockaddr_dl);
+ break;
+ default:
+ nlog(LOG_ERR, "unknown af (%d) while parsing rtm", family);
+ break;
+ }
+}
+
+static void *
+getaddr(int addrid, int mask, void *addresses)
+{
+ int i;
+ void *p = addresses;
+
+ if ((mask & addrid) == 0)
+ return (NULL);
+
+ for (i = 1; i < addrid; i <<= 1) {
+ if (i & mask)
+ nextaddr(&p);
+ }
+ return (p);
+}
+
+static void
+setaddr(int addrid, int *maskp, void *addressesp, struct sockaddr *address)
+{
+ struct sockaddr *p = *((struct sockaddr **)addressesp);
+
+ *maskp |= addrid;
+
+ switch (address->sa_family) {
+ case AF_INET:
+ (void) memcpy(p, address, sizeof (struct sockaddr_in));
+ break;
+ case AF_INET6:
+ (void) memcpy(p, address, sizeof (struct sockaddr_in6));
+ break;
+ case AF_LINK:
+ (void) memcpy(p, address, sizeof (struct sockaddr_dl));
+ break;
+ default:
+ nlog(LOG_ERR, "setaddr: unknown af (%d) while setting addr",
+ address->sa_family);
+ break;
+ }
+ nextaddr(addressesp);
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c b/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c
deleted file mode 100644
index 469f5f54f2..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/state_machine.c
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * This file contains the core logic of nwamd.
- *
- * This functionality is built around state_machine() which consumes an event.
- * The events which are input to this function are generated by either a change
- * in interface state or a signal. The events which correspond to signals are
- * either external requests to shutdown or a timer event. The interface events
- * indicate if an interface has acquired a new address (via DHCP) or if a new
- * interface has appeared on the system. The latter event is used to detect new
- * links.
- *
- * state_machine() calls high level routines in llp.c and interface.c to act on
- * the state of the machine in response to events.
- *
- * 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 <stdarg.h>
-#include <stdio.h>
-#include <arpa/inet.h>
-#include <libsysevent.h>
-#include <net/if.h>
-#include <net/route.h>
-#include <netinet/in.h>
-#include <sys/nvpair.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <syslog.h>
-
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
-
-void
-state_machine(struct np_event *e)
-{
- struct interface *evif;
- llp_t *evllp, *prefllp;
- uint64_t flags;
- boolean_t dhcp_restored = B_FALSE;
-
- dprintf("state_machine(event type: %s, name: %s)",
- npe_type_str(e->npe_type), STRING(e->npe_name));
- switch (e->npe_type) {
- case EV_TIMER:
- /* Our timer popped; check our dhcp status. */
- if ((evif = get_interface(e->npe_name)) == NULL) {
- dprintf("couldn't find waiting interface; "
- "ignoring EV_TIMER event");
- break;
- }
- flags = get_ifflags(evif->if_name, evif->if_family);
- if ((!(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;
- * so no need to worry about the timer expiring.
- */
- dprintf("timer popped for %s, but dhcp state is okay "
- "(ifflags 0x%llx)", evif->if_name, flags);
- break;
- }
- 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,
- * switch to the next best.
- */
- dprintf("giving up on dhcp on %s (ifflags 0x%llx)",
- evif->if_name, flags);
- evif->if_lflags |= IF_DHCPFAILED;
- if (interface_is_active(evif)) {
- 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_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_%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 == 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,
- 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);
- } else if (prefllp == evllp) {
- /*
- * If this is a negative event on our preferred link,
- * then we need to pay closer attention. (We can
- * ignore negative events on other links.)
- */
- switch (e->npe_type) {
- case EV_LINKFADE:
- case EV_LINKDISC:
- /*
- * If the link has faded or disconnected, then
- * it's a wireless link, and something has gone
- * wrong with the connection to the AP. The
- * above tests mean that we do intend to stay
- * with this link for now, so we have to
- * recover it by attempting to reconnect.
- * Invoking reselect will do that by calling
- * bringupinterface.
- */
- dprintf("disconnect on preferred llp; "
- "attempting reconnect");
- prefllp->llp_waiting = B_TRUE;
- llp_reselect();
- break;
- case EV_LINKDROP:
- /*
- * If link status has dropped on a wireless
- * interface, then we need to check whether
- * we're still connected. We're probably not,
- * and this will cause us to attempt
- * reconnection.
- */
- if (prefllp->llp_type == IF_WIRELESS)
- wireless_verify(e->npe_name);
- break;
- }
- }
- if (e->npe_type == EV_USER)
- llp_write_changed_priority(evllp);
- break;
-
- case EV_NEWADDR:
- if ((evif = get_interface(e->npe_name)) == NULL ||
- (evllp = llp_lookup(e->npe_name)) == NULL) {
- dprintf("state_machine: either no intf (%p) or no llp "
- "(%p) for %s; ignoring EV_NEWADDR event",
- (void *)evif, (void *)evllp, STRING(e->npe_name));
- break;
- }
- 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) ||
- 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 (!(flags & IFF_DHCPRUNNING))
- evif->if_lflags &= ~IF_DHCPSTARTED;
- if (evif->if_lflags & IF_DHCPACQUIRED) {
- evif->if_lflags &= ~IF_DHCPACQUIRED;
- 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;
- }
-
- /*
- * We have a DHCP lease. If we'd previously failed
- * to get one, record that DHCP has been restored.
- */
- evif->if_timer_expire = 0;
- evif->if_lflags |= IF_DHCPACQUIRED;
- if (evif->if_lflags & IF_DHCPFAILED) {
- evif->if_lflags &= ~IF_DHCPFAILED;
- dhcp_restored = B_TRUE;
- }
- }
- if (evllp != link_layer_profile) {
- if (dhcp_restored &&
- llp_high_pri(evllp, link_layer_profile) == evllp) {
- dprintf("state_machine: dhcp completed on "
- "higher priority llp (%s); swapping",
- llp_prnm(evllp));
- llp_swap(evllp, 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, dcUnwanted);
- break;
- }
- }
- /*
- * An address has been assigned to the current active link.
- * Notify the user, and activate the upper layer profile.
- *
- * Since other changes to the link (netmask change, broadcast
- * addr change, etc.) can cause a NEWADDR event (XXX would
- * be good if our event generator could do a better job
- * filtering!), only do this if there is not currently an
- * active ulp.
- */
- if (!ulp_is_active()) {
- show_if_status(evllp->llp_lname);
- activate_upper_layer_profile(evllp->llp_ipv4src ==
- IPV4SRC_DHCP, evllp->llp_lname);
- }
- break;
-
- case EV_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();
- return;
- /* NOTREACHED */
-
- default:
- dprintf("unknown event");
- break;
- }
-}
-
-void
-cleanup(void)
-{
- terminate_wireless();
- deactivate_upper_layer_profile();
- if (link_layer_profile != NULL)
- takedowninterface(link_layer_profile->llp_lname, dcShutdown);
-
- /*
- * Since actions taken in nwamd result in dhcpagent being
- * launched, it's under our contract. Thus, it needs to be
- * stopped when our stop method is executed. But it needs
- * to stick around long enough for us to release any leases
- * we might have; thus, we don't want the stop method to
- * explicitly kill it. We do it here, when we know we've
- * finished any dhcp cleanup that needed to be done.
- */
- dprintf("killing dhcpagent");
- (void) start_child(PKILL, "-z", zonename, "dhcpagent", NULL);
-}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h b/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
deleted file mode 100644
index 0679df44ce..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/structures.h
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _STRUCTURES_H
-#define _STRUCTURES_H
-
-#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. It's a bit of a mix
- * right now.
- */
-enum np_event_type {
- 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 */
-};
-
-/*
- * 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;
- char *npe_name;
- struct np_event *npe_next;
-};
-
-/*
- * This structure is used to represent the current state of the system. We
- * maintain these for IPv4 as proxies for links in the system. This is
- * differentiated from the LLP which contains the intended configuration of
- * the system.
- *
- * Currently these are stored on ifs_head with the wired interfaces sorted
- * together and the wireless ones sorted together with ifs_wired and
- * ifs_wireless pointed at these sublists. Access to these lists is not
- * currently MT-safe.
- *
- * We explicitly do not maintain IPv6 interface structures. In the current
- * state machine, IPv6 interfaces are completely dependent on their IPv4
- * counterparts. For example, we make a decision to bring up an interface
- * based on routing socket messages read from an AF_INET socket; we will
- * always bring up v4, and may additionally bring up v6. Also, when we
- * start up, we find all interfaces in the system by doing 'ifconfig -a
- * plumb', which will plumb v4 on all links; we always keep the v4 interface
- * plumbed, taking it up and down depending on whether it's currently in use
- * or not. v6 interfaces are not plumbed initially; when we decide a link
- * should be active (and its llp tells us to do v6), we'll mark the (already
- * existing) v4 interface up, and 'plumb up' the v6 interface. Conversely,
- * when a link is no longer active, we 'down unplumb' the v6 interface, but
- * only 'down' the v4 interface.
- */
-struct interface {
- char if_name[LIFNAMSIZ];
- sa_family_t if_family;
- uint64_t if_flags;
- uint32_t if_lflags;
- libnwam_interface_type_t if_type;
- uint32_t if_timer_expire;
- 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;
-};
-
-/*
- * interface local flag values
- */
-/*
- * IF_DHCPFAILED: indicates that we timed out a dhcp request. Will be
- * cleared if the request eventually succeeds, or if the IFF_RUNNING flag
- * is toggled off/on (i.e. the cable is unplugged/plugged)
- */
-#define IF_DHCPFAILED 0x01
-/*
- * IF_DHCPSTARTED: much like IFF_DHCPRUNNING; but means specifically that
- * we have inititated dhcp on this interface. Used to prevent overlapping
- * invocations of dhcp.
- */
-#define IF_DHCPSTARTED 0x02
-/*
- * IF_DHCPACQUIRED: indicates that dhcp successfully acquired a lease.
- */
-#define IF_DHCPACQUIRED 0x04
-
-#define IF_DHCPFLAGS (IF_DHCPFAILED | IF_DHCPSTARTED | IF_DHCPACQUIRED)
-
-/*
- * This structure contains the intended configuration of the system as
- * differentiated from the actual IPv4 configuration of the system represented
- * by the interface structures.
- *
- * llp structures are held on the list llp_head. Access to this list is
- * protected by llp_lock.
- */
-typedef struct llp {
- struct qelem llp_links;
- char llp_lname[LIFNAMSIZ];
- int llp_pri; /* lower number => higher priority */
- 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;
-
-/*
- * 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;
- char wl_if_name[LIFNAMSIZ];
-};
-
-/*
- * A holder for all the resources needed to get a property value
- * using libscf.
- */
-typedef struct scf_resources {
- scf_handle_t *sr_handle;
- scf_instance_t *sr_inst;
- scf_snapshot_t *sr_snap;
- scf_propertygroup_t *sr_pg;
- scf_property_t *sr_prop;
- scf_value_t *sr_val;
-} scf_resources_t;
-
-/*
- * 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];
- char ndc_secmode[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/sysevent_events.c b/usr/src/cmd/cmd-inet/lib/nwamd/sysevent_events.c
new file mode 100644
index 0000000000..933cd7e8af
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/sysevent_events.c
@@ -0,0 +1,185 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <libsysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dev.h>
+#include <sys/types.h>
+#include <libnvpair.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "events.h"
+#include "ncp.h"
+#include "ncu.h"
+#include "objects.h"
+#include "util.h"
+
+/*
+ * sysevent_events.c - this file contains routines to retrieve sysevents
+ * from the system and package them for high level processing.
+ */
+
+static sysevent_handle_t *sysevent_handle;
+
+/*
+ * At present, we only handle EC_DEV_ADD/EC_DEV_REMOVE sysevents of
+ * subclass ESC_NETWORK. These signify hotplug addition/removal.
+ * For EC_DEV_ADD, we:
+ * - extract the driver/instance sysevent attributes
+ * - combine these to get interface name and create associated NCUs
+ * at the link/IP level if required
+ * - enable those instances
+ * For EC_DEV_REMOVE, we:
+ * - disable the associated link/IP NCUs
+ */
+static void
+sysevent_handler(sysevent_t *ev)
+{
+ int32_t instance;
+ char *driver;
+ char if_name[LIFNAMSIZ];
+ boolean_t link_added;
+ nvlist_t *attr_list;
+ char *event_class = sysevent_get_class_name(ev);
+ char *event_subclass = sysevent_get_subclass_name(ev);
+ nwamd_event_t link_event = NULL;
+
+ nlog(LOG_DEBUG, "sysevent_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) {
+ nlog(LOG_ERR, "sysevent_handler: unexpected sysevent "
+ "class/subclass %s/%s", event_class, event_subclass);
+ return;
+ }
+
+ link_added = (strcmp(event_class, EC_DEV_ADD) == 0);
+
+ /*
+ * Retrieve driver name and instance attributes, and combine to
+ * get interface name.
+ */
+ if (sysevent_get_attr_list(ev, &attr_list) != 0) {
+ nlog(LOG_ERR, "sysevent_handler: sysevent_get_attr_list: %m");
+ return;
+ }
+ if (nvlist_lookup_string(attr_list, DEV_DRIVER_NAME, &driver) != 0 ||
+ nvlist_lookup_int32(attr_list, DEV_INSTANCE, &instance) != 0) {
+ nlog(LOG_ERR, "sysevent_handler: nvlist_lookup "
+ "of attributes failed: %m");
+ nvlist_free(attr_list);
+ return;
+ }
+ (void) snprintf(if_name, LIFNAMSIZ, "%s%d", driver, instance);
+ nvlist_free(attr_list);
+
+ /* Ignore sysevent events for other zones */
+ if (!nwamd_link_belongs_to_this_zone(if_name))
+ return;
+
+ /* Create event for link */
+ link_event = nwamd_event_init_link_action(if_name,
+ link_added ? NWAM_ACTION_ADD : NWAM_ACTION_REMOVE);
+ if (link_event != NULL)
+ nwamd_event_enqueue(link_event);
+}
+
+/* ARGSUSED0 */
+static void *
+sysevent_initialization(void *arg)
+{
+ const char *subclass = ESC_NETWORK;
+
+ do {
+ nwamd_to_root();
+ sysevent_handle = sysevent_bind_handle(sysevent_handler);
+ nwamd_from_root();
+
+ (void) sleep(1);
+ } while (sysevent_handle == NULL);
+
+ /*
+ * 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 sysevent_handler().
+ */
+ if (sysevent_subscribe_event(sysevent_handle, EC_DEV_ADD, &subclass, 1)
+ != 0 ||
+ sysevent_subscribe_event(sysevent_handle, EC_DEV_REMOVE, &subclass,
+ 1) != 0)
+ pfail("sysevent_subscribe_event: %s", strerror(errno));
+
+ return (NULL);
+}
+
+/*
+ * We can't initialize in the main thread because we may need to wait until
+ * svc:/system/sysevent:default finishes starting up. So we create a thread to
+ * initialize in.
+ */
+void
+nwamd_sysevent_events_init(void)
+{
+ int rc;
+ pthread_attr_t attr;
+
+ rc = pthread_attr_init(&attr);
+ if (rc != 0) {
+ pfail("nwamd_sysevents_init: pthread_attr_init failed: %s",
+ strerror(rc));
+ }
+
+ rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (rc != 0) {
+ pfail("nwamd_sysevents_init: pthread_attr_setdetachstate "
+ "failed: %s", strerror(rc));
+ }
+
+ rc = pthread_create(NULL, &attr, sysevent_initialization, NULL);
+ if (rc != 0) {
+ pfail("nwamd_sysevents_init: couldn't start sysevent init "
+ "thread: %s", strerror(rc));
+ }
+
+ (void) pthread_attr_destroy(&attr);
+}
+
+void
+nwamd_sysevent_events_fini(void)
+{
+ if (sysevent_handle != NULL) {
+ nwamd_to_root();
+ sysevent_unbind_handle(sysevent_handle);
+ nwamd_from_root();
+ }
+ sysevent_handle = NULL;
+}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/util.c b/usr/src/cmd/cmd-inet/lib/nwamd/util.c
index 4321e78ee4..24aba0e249 100644
--- a/usr/src/cmd/cmd-inet/lib/nwamd/util.c
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/util.c
@@ -20,120 +20,91 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
- * util.c contains a set of miscellaneous utility functions which:
- * - syslog(LOG_DEBUG, ...) if debugging is enabled
- * - check for an IP interface being marked running
- * - look up all flags for an IP interface
+ * util.c contains a set of miscellaneous utility functions which,
+ * among other things:
* - start a child process
- * - schedule a timer
* - look up the zone name
+ * - look up/set SMF properties
+ * - drop/escalate privs
*/
+#include <assert.h>
+#include <errno.h>
+#include <inetcfg.h>
+#include <libdllink.h>
+#include <limits.h>
+#include <libscf.h>
+#include <net/if.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <spawn.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
-#include <pthread.h>
#include <string.h>
+#include <strings.h>
#include <stropts.h>
-#include <syslog.h>
-#include <sys/types.h>
#include <sys/socket.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <spawn.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <wait.h>
-#include <inetcfg.h>
-#include <errno.h>
#include <zone.h>
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
+#include "util.h"
+#include "llp.h"
extern char **environ;
-boolean_t debug = B_FALSE;
+extern sigset_t original_sigmask;
-/* PRINTFLIKE1 */
-void
-dprintf(const char *fmt, ...)
-{
- va_list ap;
- char vbuf[1024];
+/*
+ * A holder for all the resources needed to get a property value
+ * using libscf.
+ */
+typedef struct scf_resources {
+ scf_handle_t *sr_handle;
+ scf_instance_t *sr_inst;
+ scf_snapshot_t *sr_snap;
+ scf_propertygroup_t *sr_pg;
+ scf_property_t *sr_prop;
+ scf_value_t *sr_val;
+ scf_transaction_t *sr_tx;
+ scf_transaction_entry_t *sr_ent;
+} scf_resources_t;
+
+static pthread_mutex_t uid_mutex = PTHREAD_MUTEX_INITIALIZER;
+static uid_t uid;
+static int uid_cnt;
- va_start(ap, fmt);
- if (debug) {
- (void) vsnprintf(vbuf, sizeof (vbuf), fmt, ap);
- syslog(LOG_DEBUG, "%d: %s", pthread_self(), vbuf);
+void
+nwamd_to_root(void) {
+ (void) pthread_mutex_lock(&uid_mutex);
+ if (uid == 0)
+ uid = getuid();
+ if (uid_cnt++ == 0) {
+ nwamd_escalate_privs();
+ if (setuid(0) == -1)
+ nlog(LOG_ERR, "setuid(0) failed %s", strerror(errno));
}
- va_end(ap);
+ (void) pthread_mutex_unlock(&uid_mutex);
}
-uint64_t
-get_ifflags(const char *name, sa_family_t family)
-{
- icfg_if_t intf;
- icfg_handle_t h;
- uint64_t flags = 0;
-
- (void) strlcpy(intf.if_name, name, sizeof (intf.if_name));
- intf.if_protocol = family;
-
- if (icfg_open(&h, &intf) != ICFG_SUCCESS)
- return (0);
-
- if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
- /*
- * Interfaces can be ripped out from underneath us (for example
- * by DHCP). We don't want to spam the console for those.
- */
- if (errno == ENOENT)
- dprintf("get_ifflags: icfg_get_flags failed for '%s'",
- name);
- else
- syslog(LOG_ERR, "get_ifflags: icfg_get_flags %s af "
- "%d: %m", name, family);
- /* just to be sure... */
- flags = 0;
- }
- icfg_close(h);
-
- return (flags);
-}
-
-/* This 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);
+nwamd_from_root(void) {
+ (void) pthread_mutex_lock(&uid_mutex);
+ assert(uid_cnt > 0);
+ if (--uid_cnt == 0) {
+ if (setuid(uid) == -1)
+ nlog(LOG_ERR, "setuid(%d) failed %s", uid,
+ strerror(errno));
+ nwamd_drop_unneeded_privs();
+ }
+ (void) pthread_mutex_unlock(&uid_mutex);
}
/*
@@ -149,7 +120,7 @@ zero_out_v4addr(const char *name)
* NOTE: original_sigmask must be set before this function is called.
*/
int
-start_childv(const char *command, char const * const *argv)
+nwamd_start_childv(const char *command, char const * const *argv)
{
posix_spawnattr_t attr;
sigset_t fullset;
@@ -164,111 +135,591 @@ start_childv(const char *command, char const * const *argv)
n -= strlcat(vbuf, argv[i], n);
}
if (argv[i] != NULL || n < 0)
- syslog(LOG_ERR, "start_childv can't log full arg vector");
+ nlog(LOG_ERR, "nwamd_start_childv can't log full arg vector");
if ((rc = posix_spawnattr_init(&attr)) != 0) {
- dprintf("posix_spawnattr_init %d %s\n", rc, strerror(rc));
+ nlog(LOG_DEBUG, "posix_spawnattr_init %d %s\n",
+ rc, strerror(rc));
return (-1);
}
(void) sigfillset(&fullset);
if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
- dprintf("setsigdefault %d %s\n", rc, strerror(rc));
+ nlog(LOG_DEBUG, "setsigdefault %d %s\n", rc, strerror(rc));
return (-1);
}
if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
- dprintf("setsigmask %d %s\n", rc, strerror(rc));
+ nlog(LOG_DEBUG, "setsigmask %d %s\n", rc, strerror(rc));
return (-1);
}
if ((rc = posix_spawnattr_setflags(&attr,
POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
- dprintf("setflags %d %s\n", rc, strerror(rc));
+ nlog(LOG_DEBUG, "setflags %d %s\n", rc, strerror(rc));
return (-1);
}
if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
environ)) > 0) {
- dprintf("posix_spawnp failed errno %d", rc);
+ nlog(LOG_DEBUG, "posix_spawnp failed errno %d", rc);
return (-1);
}
if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
- dprintf("posix_spawn_attr_destroy %d %s\n", rc, strerror(rc));
+ nlog(LOG_DEBUG, "posix_spawn_attr_destroy %d %s\n",
+ rc, strerror(rc));
return (-1);
}
(void) waitpid(pid, &status, 0);
if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
- syslog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
+ nlog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
(WIFSIGNALED(status) ? "terminated" : "stopped"), i,
strsignal(i));
return (-2);
} else {
- syslog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
+ nlog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
WEXITSTATUS(status));
return (WEXITSTATUS(status));
}
}
-int
-start_child(const char *command, ...)
+/*
+ * For global zone, check if the link is used by a non-global
+ * zone, note that the non-global zones doesn't need this check,
+ * because zoneadm has taken care of this when the zone boots.
+ * In the global zone, we ignore events for local-zone-owned links
+ * since these are taken care of by the local zone's network
+ * configuration services.
+ */
+boolean_t
+nwamd_link_belongs_to_this_zone(const char *linkname)
{
- const char **argv = NULL;
- int argv_len = 0;
- va_list ap;
- int i = 1, rc;
-
- va_start(ap, command);
- do {
- if (i >= argv_len) {
- void *p;
-
- argv_len = argv_len != 0 ? argv_len * 2 : 4;
- p = realloc(argv, sizeof (*argv)*argv_len);
- if (p != NULL) {
- argv = p;
- } else {
- syslog(LOG_ERR, "Out of memory in start_child");
- free(argv);
- return (-1);
- }
+ zoneid_t zoneid;
+ char zonename[ZONENAME_MAX];
+ int ret;
+
+ zoneid = getzoneid();
+ if (zoneid == GLOBAL_ZONEID) {
+ datalink_id_t linkid;
+ dladm_status_t status;
+ char errstr[DLADM_STRSIZE];
+
+ if ((status = dladm_name2info(dld_handle, linkname, &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: "
+ "could not get linkid for %s: %s",
+ linkname, dladm_status2str(status, errstr));
+ return (B_FALSE);
+ }
+ zoneid = ALL_ZONES;
+ ret = zone_check_datalink(&zoneid, linkid);
+ if (ret == 0) {
+ (void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX);
+ nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: "
+ "%s is used by non-global zone: %s",
+ linkname, zonename);
+ return (B_FALSE);
}
+ }
+ return (B_TRUE);
+}
- argv[i] = va_arg(ap, const char *);
- } while (argv[i++] != NULL);
- va_end(ap);
- argv[0] = command;
+void
+nwamd_drop_unneeded_privs(void)
+{
+ priv_set_t *priv_set, *allpriv_set;
+
+ /* build up our minimal set of privs; start with the basic set */
+ priv_set = priv_str_to_set("basic", ",", NULL);
+ allpriv_set = priv_str_to_set("zone", ",", NULL);
+ if (priv_set == NULL || allpriv_set == NULL)
+ pfail("converting privilege sets: %s", strerror(errno));
+
+ (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF);
+ (void) priv_addset(priv_set, PRIV_FILE_DAC_READ);
+ (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE);
+ (void) priv_addset(priv_set, PRIV_NET_RAWACCESS);
+ (void) priv_addset(priv_set, PRIV_NET_PRIVADDR);
+ (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_MOUNT);
+ (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG);
+ (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG);
+ (void) priv_addset(priv_set, PRIV_SYS_RESOURCE);
+
+ /*
+ * Since our zone might not have all these privs,
+ * just ask for those that are available.
+ */
+ priv_intersect(allpriv_set, priv_set);
+
+ if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
+ priv_freeset(allpriv_set);
+ priv_freeset(priv_set);
+ pfail("setppriv inheritable: %s", strerror(errno));
+ }
+ /*
+ * Need to ensure permitted set contains all privs so we can escalate
+ * later.
+ */
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, allpriv_set) == -1) {
+ priv_freeset(allpriv_set);
+ priv_freeset(priv_set);
+ pfail("setppriv permitted: %s", strerror(errno));
+ }
+ /*
+ * We need to find a smaller set of privs that are important to us.
+ * Otherwise we really are not gaining much by doing this.
+ */
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
+ priv_freeset(allpriv_set);
+ priv_freeset(priv_set);
+ pfail("setppriv effective: %s", strerror(errno));
+ }
- rc = start_childv(command, argv);
- free(argv);
+ priv_freeset(priv_set);
+ priv_freeset(allpriv_set);
+}
- return (rc);
+void
+nwamd_escalate_privs(void)
+{
+ priv_set_t *priv_set;
+ priv_set = priv_str_to_set("zone", ",", NULL);
+ if (priv_set == NULL)
+ pfail("creating privilege set: %s", strerror(errno));
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
+ priv_freeset(priv_set);
+ pfail("setppriv effective: %s", strerror(errno));
+ }
+ priv_freeset(priv_set);
}
-uint32_t timer_expire = TIMER_INFINITY;
+/*
+ * Inputs:
+ * res is a pointer to the scf_resources_t to be released.
+ */
+static void
+release_scf_resources(scf_resources_t *res)
+{
+ scf_entry_destroy(res->sr_ent);
+ scf_transaction_destroy(res->sr_tx);
+ scf_value_destroy(res->sr_val);
+ scf_property_destroy(res->sr_prop);
+ scf_pg_destroy(res->sr_pg);
+ scf_snapshot_destroy(res->sr_snap);
+ scf_instance_destroy(res->sr_inst);
+ (void) scf_handle_unbind(res->sr_handle);
+ scf_handle_destroy(res->sr_handle);
+}
/*
- * Schedules a SIGALRM in delay seconds, unless one is already
- * scheduled sooner. If one is already scheduled later than
- * delay seconds from now, that one will be replaced.
+ * Inputs:
+ * fmri is the instance to look up
+ * Outputs:
+ * res is a pointer to an scf_resources_t. This is an internal
+ * structure that holds all the handles needed to get a specific
+ * property from the running snapshot; on a successful return it
+ * contains the scf_value_t that should be passed to the desired
+ * scf_value_get_foo() function, and must be freed after use by
+ * calling release_scf_resources(). On a failure return, any
+ * resources that may have been assigned to res are released, so
+ * the caller does not need to do any cleanup in the failure case.
+ * Returns:
+ * 0 on success
+ * -1 on failure
*/
-void
-start_timer(uint32_t now, uint32_t delay)
+
+static int
+create_scf_resources(const char *fmri, scf_resources_t *res)
+{
+ res->sr_tx = NULL;
+ res->sr_ent = NULL;
+ res->sr_inst = NULL;
+ res->sr_snap = NULL;
+ res->sr_pg = NULL;
+ res->sr_prop = NULL;
+ res->sr_val = NULL;
+
+ if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
+ return (-1);
+ }
+
+ if (scf_handle_bind(res->sr_handle) != 0) {
+ scf_handle_destroy(res->sr_handle);
+ return (-1);
+ }
+ if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
+ res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
+ goto failure;
+ }
+ if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ if (scf_instance_get_snapshot(res->sr_inst, "running",
+ res->sr_snap) != 0) {
+ goto failure;
+ }
+ if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
+ goto failure;
+ }
+ return (0);
+
+failure:
+ nlog(LOG_ERR, "create_scf_resources failed: %s",
+ scf_strerror(scf_error()));
+ release_scf_resources(res);
+ return (-1);
+}
+
+/*
+ * Inputs:
+ * fmri is the instance to look up
+ * pg is the property group to look up
+ * prop is the property within that group to look up
+ * running specifies if running snapshot is to be used
+ * Outputs:
+ * res is a pointer to an scf_resources_t. This is an internal
+ * structure that holds all the handles needed to get a specific
+ * property from the running snapshot; on a successful return it
+ * contains the scf_value_t that should be passed to the desired
+ * scf_value_get_foo() function, and must be freed after use by
+ * calling release_scf_resources(). On a failure return, any
+ * resources that may have been assigned to res are released, so
+ * the caller does not need to do any cleanup in the failure case.
+ * Returns:
+ * 0 on success
+ * -1 on failure
+ */
+static int
+get_property_value(const char *fmri, const char *pg, const char *prop,
+ boolean_t running, scf_resources_t *res)
{
- if (now + delay > timer_expire)
- return;
+ if (create_scf_resources(fmri, res) != 0)
+ return (-1);
+
+ if (scf_instance_get_pg_composed(res->sr_inst,
+ running ? res->sr_snap : NULL, pg, res->sr_pg) != 0) {
+ goto failure;
+ }
+ if (scf_pg_get_property(res->sr_pg, prop, res->sr_prop) != 0) {
+ goto failure;
+ }
+ if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
+ goto failure;
+ }
+ return (0);
- timer_expire = now + delay;
- (void) alarm(delay);
+failure:
+ release_scf_resources(res);
+ return (-1);
}
-void
-lookup_zonename(char *zonename, size_t zonesize)
+/*
+ * Inputs:
+ * lfmri is the instance fmri to look up
+ * lpg is the property group to look up
+ * lprop is the property within that group to look up
+ * Outputs:
+ * answer is a pointer to the property value
+ * Returns:
+ * 0 on success
+ * -1 on failure
+ * If successful, the property value is retured in *answer.
+ * Otherwise, *answer is undefined, and it is up to the caller to decide
+ * how to handle that case.
+ */
+int
+nwamd_lookup_boolean_property(const char *lfmri, const char *lpg,
+ const char *lprop, boolean_t *answer)
+{
+ int result = -1;
+ scf_resources_t res;
+ uint8_t prop_val;
+
+ if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
+
+ /*
+ * an error was already logged by get_property_value,
+ * and it released any resources assigned to res before
+ * returning.
+ */
+ return (result);
+ }
+ if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
+ goto cleanup;
+ }
+ *answer = (boolean_t)prop_val;
+ result = 0;
+cleanup:
+ release_scf_resources(&res);
+ return (result);
+}
+
+/*
+ * Inputs:
+ * lfmri is the instance fmri to look up
+ * lpg is the property group to look up
+ * lprop is the property within that group to look up
+ * buf is the place to put the answer
+ * bufsz is the size of buf
+ * Outputs:
+ *
+ * Returns:
+ * 0 on success
+ * -1 on failure
+ * If successful, the property value is retured in buf.
+ * Otherwise, buf is undefined, and it is up to the caller to decide
+ * how to handle that case.
+ */
+int
+nwamd_lookup_string_property(const char *lfmri, const char *lpg,
+ const char *lprop, char *buf, size_t bufsz)
{
- zoneid_t zoneid = getzoneid();
+ int result = -1;
+ scf_resources_t res;
+
+ if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
+ /*
+ * The above function fails when trying to get a
+ * non-persistent property group from the running snapshot.
+ * Try going for the non-running snapshot.
+ */
+ if (get_property_value(lfmri, lpg, lprop, B_FALSE, &res) != 0) {
+ /*
+ * an error was already logged by get_property_value,
+ * and it released any resources assigned to res before
+ * returning.
+ */
+ return (result);
+ }
+ }
+ if (scf_value_get_astring(res.sr_val, buf, bufsz) == 0)
+ goto cleanup;
+
+ result = 0;
+cleanup:
+ release_scf_resources(&res);
+ return (result);
+}
- if (getzonenamebyid(zoneid, zonename, zonesize) >= 0)
- return;
- syslog(LOG_ERR, "could not determine zone name");
- (void) strlcpy(zonename, GLOBAL_ZONENAME, zonesize);
+/*
+ * Inputs:
+ * lfmri is the instance fmri to look up
+ * lpg is the property group to look up
+ * lprop is the property within that group to look up
+ * Outputs:
+ * answer is a pointer to the property value
+ * Returns:
+ * 0 on success
+ * -1 on failure
+ * If successful, the property value is retured in *answer.
+ * Otherwise, *answer is undefined, and it is up to the caller to decide
+ * how to handle that case.
+ */
+int
+nwamd_lookup_count_property(const char *lfmri, const char *lpg,
+ const char *lprop, uint64_t *answer)
+{
+ int result = -1;
+ scf_resources_t res;
+
+ if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
+
+ /*
+ * an error was already logged by get_property_value,
+ * and it released any resources assigned to res before
+ * returning.
+ */
+ return (result);
+ }
+ if (scf_value_get_count(res.sr_val, answer) != 0) {
+ goto cleanup;
+ }
+ result = 0;
+cleanup:
+ release_scf_resources(&res);
+ return (result);
+}
+
+static int
+set_property_value(scf_resources_t *res, const char *propname,
+ scf_type_t proptype)
+{
+ int result = -1;
+ boolean_t new;
+
+retry:
+ new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0);
+
+ if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) {
+ goto failure;
+ }
+ if (new) {
+ if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
+ propname, proptype) == -1) {
+ goto failure;
+ }
+ } else {
+ if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
+ propname, proptype) == -1) {
+ goto failure;
+ }
+ }
+
+ if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
+ goto failure;
+ }
+
+ result = scf_transaction_commit(res->sr_tx);
+ if (result == 0) {
+ scf_transaction_reset(res->sr_tx);
+ if (scf_pg_update(res->sr_pg) == -1) {
+ goto failure;
+ }
+ nlog(LOG_INFO, "set_property_value: transaction commit failed "
+ "for %s; retrying", propname);
+ goto retry;
+ }
+ if (result == -1)
+ goto failure;
+ return (0);
+
+failure:
+ return (-1);
+}
+
+int
+nwamd_set_count_property(const char *fmri, const char *pg, const char *prop,
+ uint64_t count)
+{
+ scf_resources_t res;
+
+ if (create_scf_resources(fmri, &res) != 0)
+ return (-1);
+
+ if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
+ SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
+ if (scf_error() != SCF_ERROR_EXISTS)
+ goto failure;
+ if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
+ res.sr_pg) != 0)
+ goto failure;
+ }
+
+ scf_value_set_count(res.sr_val, (uint64_t)count);
+
+ if (set_property_value(&res, prop, SCF_TYPE_COUNT) != 0)
+ goto failure;
+
+ release_scf_resources(&res);
+ return (0);
+
+failure:
+ nlog(LOG_INFO, "nwamd_set_count_property: scf failure %s while "
+ "setting %s", scf_strerror(scf_error()), prop);
+ release_scf_resources(&res);
+ return (-1);
+}
+
+int
+nwamd_set_string_property(const char *fmri, const char *pg, const char *prop,
+ const char *str)
+{
+ scf_resources_t res;
+
+ if (create_scf_resources(fmri, &res) != 0)
+ return (-1);
+
+ if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
+ SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
+ if (scf_error() != SCF_ERROR_EXISTS)
+ goto failure;
+ if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
+ res.sr_pg) != 0)
+ goto failure;
+ }
+
+ if (scf_value_set_astring(res.sr_val, str) != 0)
+ goto failure;
+
+ if (set_property_value(&res, prop, SCF_TYPE_ASTRING) != 0)
+ goto failure;
+
+ release_scf_resources(&res);
+ return (0);
+
+failure:
+ nlog(LOG_INFO, "nwamd_set_string_property: scf failure %s while "
+ "setting %s", scf_strerror(scf_error()), prop);
+ release_scf_resources(&res);
+ return (-1);
+}
+
+/*
+ * Deletes property prop from property group pg in SMF instance fmri.
+ * Returns 0 on success, -1 on failure.
+ */
+int
+nwamd_delete_scf_property(const char *fmri, const char *pg, const char *prop)
+{
+ scf_resources_t res;
+ int result = -1;
+
+ if (create_scf_resources(fmri, &res) != 0)
+ return (-1);
+
+ if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
+ SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
+ if (scf_error() != SCF_ERROR_EXISTS)
+ goto failure;
+ if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
+ res.sr_pg) != 0)
+ goto failure;
+ }
+
+ if (scf_pg_get_property(res.sr_pg, prop, res.sr_prop) != 0)
+ goto failure;
+retry:
+ if (scf_transaction_start(res.sr_tx, res.sr_pg) == -1)
+ goto failure;
+
+ if (scf_transaction_property_delete(res.sr_tx, res.sr_ent, prop) == -1)
+ goto failure;
+
+ result = scf_transaction_commit(res.sr_tx);
+ if (result == 0) {
+ scf_transaction_reset(res.sr_tx);
+ if (scf_pg_update(res.sr_pg) == -1)
+ goto failure;
+ goto retry;
+ }
+ if (result == -1)
+ goto failure;
+
+ release_scf_resources(&res);
+ return (0);
+failure:
+ release_scf_resources(&res);
+ return (-1);
}
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/util.h b/usr/src/cmd/cmd-inet/lib/nwamd/util.h
new file mode 100644
index 0000000000..db5a435087
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/util.h
@@ -0,0 +1,103 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <dhcpagent_ipc.h>
+#include <libdlwlan.h>
+#include <libnwam.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/note.h>
+#include <sys/time.h>
+#include <sys/zone.h>
+#include <syslog.h>
+
+#include "events.h"
+#include "llp.h"
+#include "ncu.h"
+
+/*
+ * A few functions here from files other than util.c, saves having
+ * .h files for one or two functions.
+ */
+
+#define OUR_FMRI NWAM_FMRI
+#define OUR_PG NWAM_PG
+#define OUR_DEBUG_PROP_NAME "debug"
+#define OUR_AUTOCONF_PROP_NAME "autoconf"
+#define OUR_STRICT_BSSID_PROP_NAME "strict_bssid"
+#define OUR_ACTIVE_NCP_PROP_NAME NWAM_PROP_ACTIVE_NCP
+#define OUR_CONDITION_CHECK_INTERVAL_PROP_NAME "condition_check_interval"
+#define OUR_WIRELESS_SCAN_INTERVAL_PROP_NAME "scan_interval"
+#define OUR_WIRELESS_SCAN_LEVEL_PROP_NAME "scan_level"
+#define OUR_NCU_WAIT_TIME_PROP_NAME "ncu_wait_time"
+#define OUR_VERSION_PROP_NAME "version"
+#define NET_LOC_FMRI "svc:/network/location:default"
+#define NET_LOC_PG "location"
+#define NET_LOC_SELECTED_PROP "selected"
+
+#define NSEC_TO_SEC(nsec) (nsec) / (long)NANOSEC
+#define NSEC_TO_FRACNSEC(nsec) (nsec) % (long)NANOSEC
+#define SEC_TO_NSEC(sec) (sec) * (long)NANOSEC
+
+extern boolean_t debug;
+extern boolean_t shutting_down;
+
+/* logging.c: log support functions */
+extern void nlog(int, const char *, ...);
+extern void pfail(const char *fmt, ...);
+extern int syslog_stack(uintptr_t addr, int sig, void *arg);
+
+/* door_if.c: door interface functions */
+extern void nwamd_door_init(void);
+extern void nwamd_door_fini(void);
+
+/* util.c: utility & ipc functions */
+extern int nwamd_start_childv(const char *, const char * const *);
+extern boolean_t nwamd_link_belongs_to_this_zone(const char *);
+extern void nwamd_to_root(void);
+extern void nwamd_from_root(void);
+extern void nwamd_drop_unneeded_privs(void);
+extern void nwamd_escalate_privs(void);
+
+/* SCF helper functions */
+extern int nwamd_lookup_boolean_property(const char *, const char *,
+ const char *, boolean_t *);
+extern int nwamd_lookup_count_property(const char *, const char *, const char *,
+ uint64_t *);
+extern int nwamd_lookup_string_property(const char *, const char *,
+ const char *, char *, size_t);
+
+extern int nwamd_set_count_property(const char *, const char *, const char *,
+ uint64_t);
+extern int nwamd_set_string_property(const char *, const char *, const char *,
+ const char *);
+
+extern int nwamd_delete_scf_property(const char *, const char *, const char *);
+
+#endif /* _UTIL_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/variables.h b/usr/src/cmd/cmd-inet/lib/nwamd/variables.h
deleted file mode 100644
index dfb2427830..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/variables.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _VARIABLES_H
-#define _VARIABLES_H
-
-#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;
-
-extern llp_t *link_layer_profile;
-
-extern sigset_t original_sigmask;
-extern pid_t ppid;
-
-extern boolean_t shutting_down;
-
-extern uint32_t timer_expire;
-
-extern uint_t wlan_scan_interval;
-extern dladm_wlan_strength_t wireless_scan_level;
-extern boolean_t strict_bssid;
-
-extern uint_t door_idle_time;
-
-extern const char *OUR_FMRI;
-extern const char *OUR_PG;
-
-extern boolean_t debug;
-
-extern char zonename[ZONENAME_MAX];
-
-/*
- * This dladm handle is opened before interfaces are initialized and
- * closed only when nwamd shuts down.
- */
-extern dladm_handle_t dld_handle;
-
-#endif /* _VARIABLES_H */
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c b/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
deleted file mode 100644
index 8d938a8384..0000000000
--- a/usr/src/cmd/cmd-inet/lib/nwamd/wireless.c
+++ /dev/null
@@ -1,2493 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * 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 send a message to the GUI, which then must gather the results.
- *
- * 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 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 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.
- * 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
- * connected AP drops below a threshold (specified by wireless_scan_level),
- * the code will try to do another scan to find out other APs available.
- * If there is currently no connected AP, a scan will also be done
- * periodically to look for available APs. In both cases, if there are
- * new APs, the above AP connection procedure will be performed.
- *
- * As a way to deal with the innumerable bugs that seem to plague wireless
- * interfaces with respect to concurrent operations, we completely exclude all
- * connect operations on all interfaces when another connect or scan is
- * running, and exclude all scans on all interfaces when another connect or
- * scan is running. This is done using wifi_scan_intf.
- *
- * Much of the BSSID handling logic in this module is questionable due to
- * underlying bugs such as CR 6772510. There's likely little that we can do
- * about this.
- *
- * Lock ordering note: wifi_mutex and wifi_init_mutex are not held at the same
- * time.
- */
-
-#include <unistd.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <strings.h>
-#include <syslog.h>
-#include <limits.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <syslog.h>
-#include <pthread.h>
-#include <sys/wait.h>
-#include <stropts.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <libdladm.h>
-#include <libdllink.h>
-#include <libinetutil.h>
-#include <libgen.h>
-
-#include "defines.h"
-#include "structures.h"
-#include "functions.h"
-#include "variables.h"
-
-#define WLAN_ENC(sec) \
- ((sec == DLADM_WLAN_SECMODE_WPA ? "WPA" : \
- (sec == DLADM_WLAN_SECMODE_WEP ? "WEP" : "none")))
-
-#define NEED_ENC(sec) \
- (sec == DLADM_WLAN_SECMODE_WPA || sec == DLADM_WLAN_SECMODE_WEP)
-
-static pthread_mutex_t wifi_mutex;
-
-typedef enum {
- ESSID = 0,
- BSSID,
- MAX_FIELDS
-} 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 const char *wifi_scan_intf;
-static boolean_t connect_running;
-static pthread_mutex_t wifi_init_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t wifi_init_cond = PTHREAD_COND_INITIALIZER;
-
-/*
- * 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);
-static int store_key(struct wireless_lan *);
-static dladm_wlan_key_t *retrieve_key(const char *, const char *,
- dladm_secobj_class_t);
-
-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 struct wireless_lan *find_wlan_entry(const char *, const char *,
- const char *);
-static void free_wireless_lan(struct wireless_lan *);
-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
-
-/*
- * The variable wlan_scan_interval controls the interval in seconds
- * between periodic scans.
- */
-uint_t wlan_scan_interval = 120;
-
-/*
- * The variable wireless_scan_level specifies the lowest signal level
- * when a periodic wireless scan needs to be done.
- */
-dladm_wlan_strength_t wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK;
-
-/*
- * This controls whether we are strict about matching BSSID in the known wifi
- * networks file. By default, we're not strict.
- */
-boolean_t strict_bssid;
-
-void
-initialize_wireless(void)
-{
- pthread_mutexattr_t wifi_mutex_attr;
-
- (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(dld_handle, 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);
- }
- }
-}
-
-static wireless_if_t *
-find_wireless_if(const char *ifname)
-{
- wireless_if_t *wip;
-
- 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
-remove_wireless_if(const char *ifname)
-{
- 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 return_vals_t
-get_user_key(struct wireless_lan *wlan)
-{
- dladm_secobj_class_t class;
-
- /*
- * First, test if we have key stored as secobj. If so,
- * no need to prompt for it.
- */
- 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 (SUCCESS);
- } else if (request_wlan_key(wlan)) {
- return (WAITING);
- } else {
- return (FAILURE);
- }
-}
-
-/*
- * 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)
-{
- struct wireless_lan *wlan, *best;
-
- 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);
- }
- }
- return (best);
-}
-
-static void
-free_wireless_lan(struct wireless_lan *wlp)
-{
- free(wlp->essid);
- wlp->essid = NULL;
- /* 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;
- free(wlp->raw_key);
- wlp->raw_key = NULL;
- free(wlp->cooked_key);
- wlp->cooked_key = NULL;
-}
-
-/*
- * 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)
-{
- char strength[DLADM_STRSIZE];
- struct wireless_lan *wlan;
-
- if (wireless_lan_used == wireless_lan_count) {
- int newcnt;
-
- newcnt = (wireless_lan_count == 0) ?
- WIRELESS_LAN_INIT_COUNT : wireless_lan_count * 2;
- wlan = realloc(wlans, newcnt * sizeof (*wlans));
- if (wlan == NULL) {
- syslog(LOG_ERR, "add_wlan_entry: realloc failed");
- return (NULL);
- }
- wireless_lan_count = newcnt;
- 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(wlan);
- return (NULL);
- }
- wireless_lan_used++;
- new_ap_found = B_TRUE;
- return (wlan);
-}
-
-/*
- * 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, but only if there are no scanned connected entries.
- */
-static boolean_t
-clear_unscanned_entries(const char *ifname)
-{
- struct wireless_lan *wlan, *wlput;
- boolean_t has_unscanned_connected;
- boolean_t copied_scanned_connected;
- uint_t dropcnt;
-
- if (pthread_mutex_lock(&wifi_mutex) != 0)
- return (B_FALSE);
- wlput = wlans;
- dropcnt = 0;
- has_unscanned_connected = copied_scanned_connected = B_FALSE;
- for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
- if (strcmp(ifname, wlan->wl_if_name) != 0) {
- if (wlput != wlan)
- *wlput = *wlan;
- wlput++;
- } else if (wlan->scanned) {
- if (wlan->connected)
- copied_scanned_connected = B_TRUE;
- if (wlput != wlan)
- *wlput = *wlan;
- wlput++;
- } else {
- if (wlan->connected)
- has_unscanned_connected = B_TRUE;
- dprintf("dropping unseen AP %s %s", wlan->essid,
- wlan->bssid);
- dropcnt++;
- free_wireless_lan(wlan);
- }
- }
- if (has_unscanned_connected && !copied_scanned_connected) {
- for (wlan = wlans; wlan < wlans + wireless_lan_used; wlan++) {
- if (strcmp(ifname, wlan->wl_if_name) == 0 &&
- wlan->connected) {
- dprintf("keeping unscanned but connected AP "
- "%s %s", wlan->essid, wlan->bssid);
- if (wlput != wlan)
- *wlput = *wlan;
- wlput++;
- dropcnt--;
- }
- }
- }
- wireless_lan_used = wlput - wlans;
- (void) pthread_mutex_unlock(&wifi_mutex);
- return (dropcnt != 0);
-}
-
-/*
- * 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/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, tear down IP interface,
- * tell the driver to disassociate with AP, and then return false.
- */
-static boolean_t
-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(dld_handle, wip->wi_linkid, &attr);
- if (status != DLADM_STATUS_OK) {
- dprintf("check_wlan: dladm_wlan_get_linkattr() for %s "
- "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);
-
- (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);
-
-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(dld_handle, wip->wi_linkid);
- }
- return (B_FALSE);
-}
-
-/*
- * Examine all WLANs associated with an interface, verify the expected WLAN,
- * and update the 'connected' attribute appropriately. The caller holds
- * wifi_mutex and deals with the 'known' flag. If the expected WLAN is NULL,
- * then we expect to be connected to just "any" (autoconf) network.
- */
-static boolean_t
-update_connected_wlan(wireless_if_t *wip, struct wireless_lan *exp_wlan)
-{
- dladm_wlan_linkattr_t attr;
- struct wireless_lan *wlan, *lastconn, *newconn;
- char essid[DLADM_STRSIZE];
- char bssid[DLADM_STRSIZE];
- boolean_t connected, wasconn;
-
- if (dladm_wlan_get_linkattr(dld_handle, wip->wi_linkid, &attr) !=
- DLADM_STATUS_OK)
- attr.la_status = DLADM_WLAN_LINK_DISCONNECTED;
- 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 reports connection to %s %s", wip->wi_name,
- essid, bssid);
- } else {
- connected = B_FALSE;
- dprintf("update: %s is currently unconnected", wip->wi_name);
- }
-
- /*
- * First, verify that if we're connected, then we should be and that
- * we're connected to the expected AP.
- */
- if (exp_wlan != NULL) {
- /*
- * If we're connected to the wrong one, then disconnect. Note:
- * we'd like to verify BSSID, but we cannot due to CR 6772510.
- */
- if (connected && strcmp(exp_wlan->essid, essid) != 0) {
- dprintf("update: wrong AP on %s; expected %s %s",
- exp_wlan->wl_if_name, exp_wlan->essid,
- exp_wlan->bssid);
- (void) dladm_wlan_disconnect(dld_handle,
- wip->wi_linkid);
- connected = B_FALSE;
- }
- /* If we're not in the expected state, then report disconnect */
- if (exp_wlan->connected != connected) {
- exp_wlan->connected = B_FALSE;
- if (connected) {
- dprintf("update: unexpected connection to %s "
- "%s; clearing", essid, bssid);
- (void) dladm_wlan_disconnect(dld_handle,
- wip->wi_linkid);
- } else {
- dprintf("update: not connected to %s %s as "
- "expected", exp_wlan->essid,
- exp_wlan->bssid);
- report_wlan_disconnect(exp_wlan);
- }
- connected = B_FALSE;
- }
- }
-
- /*
- * State is now known to be good, so make the list entries match.
- */
- 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;
- /* missing bssid check */
- if (connected && strcmp(wlan->essid, essid) == 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)
- report_wlan_connected(newconn);
- return (connected);
-}
-
-/*
- * If there is already a scan or connect in progress, defer until the operation
- * is done to avoid radio interference *and* significant driver bugs.
- *
- * Returns B_TRUE when the lock is taken and the caller must call
- * scanconnect_exit. Returns B_FALSE when lock not taken; caller must not call
- * scanconnect_exit.
- *
- * If we happen to be doing a scan, and the interface doing the scan is the
- * same as the one requesting a new scan, then wait for it to finish, and then
- * report that we're done by returning B_FALSE (no lock taken).
- */
-static boolean_t
-scanconnect_entry(const char *ifname, boolean_t is_connect)
-{
- boolean_t already_done;
-
- if (pthread_mutex_lock(&wifi_init_mutex) != 0)
- return (B_FALSE);
- already_done = B_FALSE;
- while (wifi_scan_intf != NULL) {
- dprintf("%s in progress on %s; blocking %s of %s",
- connect_running ? "connect" : "scan", wifi_scan_intf,
- is_connect ? "connect" : "scan", ifname);
- if (!is_connect && !connect_running &&
- strcmp(wifi_scan_intf, ifname) == 0)
- already_done = B_TRUE;
- (void) pthread_cond_wait(&wifi_init_cond, &wifi_init_mutex);
- if (already_done || shutting_down) {
- (void) pthread_mutex_unlock(&wifi_init_mutex);
- return (B_FALSE);
- }
- }
- dprintf("now exclusively %s on %s",
- is_connect ? "connecting" : "scanning", ifname);
- wifi_scan_intf = ifname;
- connect_running = is_connect;
- (void) pthread_mutex_unlock(&wifi_init_mutex);
- return (B_TRUE);
-}
-
-static void
-scanconnect_exit(void)
-{
- (void) pthread_mutex_lock(&wifi_init_mutex);
- dprintf("done exclusively %s on %s",
- connect_running ? "connecting" : "scanning", wifi_scan_intf);
- wifi_scan_intf = NULL;
- (void) pthread_cond_broadcast(&wifi_init_cond);
- (void) pthread_mutex_unlock(&wifi_init_mutex);
-}
-
-/*
- * Return B_TRUE if we're in the midst of connecting on a given wireless
- * interface. We shouldn't try to take such an interface down.
- */
-static boolean_t
-connecting_on(const char *ifname)
-{
- boolean_t in_progress;
-
- if (pthread_mutex_lock(&wifi_init_mutex) != 0)
- return (B_FALSE);
- in_progress = (wifi_scan_intf != NULL && connect_running &&
- strcmp(ifname, wifi_scan_intf) == 0);
- (void) pthread_mutex_unlock(&wifi_init_mutex);
- return (in_progress);
-}
-
-/*
- * Terminate all waiting transient threads as soon as possible. This assumes
- * that the shutting_down flag has already been set.
- */
-void
-terminate_wireless(void)
-{
- (void) pthread_cond_broadcast(&wifi_init_cond);
-}
-
-/*
- * 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 dropped;
- boolean_t new_found;
- dladm_status_t status;
- int i;
- datalink_id_t linkid;
- wireless_if_t *wip;
-
- /*
- * Wait for scan/connect to finish, and return if error or if this
- * interface is already done.
- */
- if (!scanconnect_entry(ifname, B_FALSE))
- return;
-
- /* 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, 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.
- */
- 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(dld_handle, 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;
-
- /*
- * Due to common driver bugs, it's necessary to check the state of the
- * interface right after doing a scan. If it's connected and we didn't
- * expect it to be, or if we're accidentally connected to the wrong AP,
- * then disconnect now and reconnect.
- */
- if (pthread_mutex_lock(&wifi_mutex) == 0) {
- if ((wip = find_wireless_if(ifname)) != NULL) {
- dladm_wlan_linkattr_t attr;
- struct wireless_lan *wlan;
- char essid[DLADM_STRSIZE];
- char bssid[DLADM_STRSIZE];
- boolean_t connected;
- int retries = 0;
-
- wip->wi_scan_running = B_FALSE;
-
- /*
- * 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 it's really down.
- */
- while (retries++ < 4) {
- if (dladm_wlan_get_linkattr(dld_handle,
- 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;
- dprintf("scan: %s reports connection to %s "
- "%s", ifname, essid, bssid);
- } else {
- connected = B_FALSE;
- dprintf("scan: %s is currently unconnected",
- ifname);
- }
- /* Disconnect from wrong AP first */
- for (wlan = wlans; wlan < wlans + wireless_lan_used;
- wlan++) {
- if (strcmp(wlan->wl_if_name, ifname) != 0)
- continue;
- /* missing bssid check */
- if (strcmp(wlan->essid, essid) == 0) {
- /*
- * This is the one we are currently
- * connected to. See if we should be
- * here.
- */
- if (!connected || !wlan->connected)
- (void) dladm_wlan_disconnect(
- dld_handle, linkid);
- break;
- }
- }
- /* Connect to right AP by reporting disconnect */
- for (wlan = wlans; wlan < wlans + wireless_lan_used;
- wlan++) {
- if (strcmp(wlan->wl_if_name, ifname) != 0)
- continue;
- if (wlan->connected) {
- /* missing bssid check */
- if (connected &&
- strcmp(wlan->essid, essid) == 0)
- break;
- /*
- * We weren't where we were supposed to
- * be. Try to reconnect now.
- */
- (void) np_queue_add_event(EV_LINKDISC,
- ifname);
- }
- }
- }
- (void) pthread_mutex_unlock(&wifi_mutex);
- }
-
- scanconnect_exit();
-
- 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);
- }
-}
-
-/*
- * 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
-rescan_wifi_no_lock(void)
-{
- 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)
-{
- const char *ifname = arg;
- wireless_if_t *wip;
- struct wireless_lan *wlan;
- char essid_name[DLADM_STRSIZE];
- char bssid_name[DLADM_STRSIZE];
- boolean_t retv;
-
- (void) dladm_wlan_essid2str(&attrp->wa_essid, essid_name);
- (void) dladm_wlan_bssid2str(&attrp->wa_bssid, bssid_name);
-
- /*
- * Check whether ESSID is "hidden".
- * If so try to substitute it with the ESSID from the
- * known_wifi_nets with the same BSSID
- */
- if (essid_name[0] == '\0') {
- if (known_wifi_nets_lookup(essid_name, bssid_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 (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);
- }
-
- /* 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 ((wlan = add_wlan_entry(ifname, essid_name, bssid_name,
- attrp)) != NULL) {
- /* search cannot return NULL at this point due to add */
- wlan->connected =
- find_wlan_entry(ifname, essid_name, "")->connected;
- retv = B_TRUE;
- } else {
- retv = B_FALSE;
- }
- (void) pthread_mutex_unlock(&wifi_mutex);
- return (retv);
-}
-
-/*
- * This is called when IP reports that the link layer is down. It just
- * verifies that we're still connected as expected. If not, then cover for the
- * known driver bugs (by disconnecting) and send an event so that we'll attempt
- * to recover. No scan is done; if a scan is needed, we'll do one the next
- * time the timer pops.
- *
- * Note that we don't retry in case of error. Since IP has reported the
- * interface as down, the best case here is that we detect a link failure and
- * start the connection process over again.
- */
-void
-wireless_verify(const char *ifname)
-{
- datalink_id_t linkid;
- dladm_wlan_linkattr_t attr;
- wireless_if_t *wip;
- struct wireless_lan *wlan;
- boolean_t is_failure;
-
- /*
- * If these calls fail, it means that the wireless link is down.
- */
- if (dladm_name2info(dld_handle, ifname, &linkid, NULL, NULL, NULL) !=
- DLADM_STATUS_OK ||
- dladm_wlan_get_linkattr(dld_handle, linkid, &attr) !=
- DLADM_STATUS_OK) {
- attr.la_status = DLADM_WLAN_LINK_DISCONNECTED;
- }
-
- /*
- * If the link is down, then work around a known driver bug (by forcing
- * disconnect), and then deliver an event so that the state machine can
- * retry.
- */
- if (attr.la_status != DLADM_WLAN_LINK_CONNECTED) {
- if (connecting_on(ifname))
- return;
- is_failure = B_TRUE;
- if (pthread_mutex_lock(&wifi_mutex) == 0) {
- if ((wip = find_wireless_if(ifname)) != NULL) {
- /*
- * Link down while waiting for user to supply
- * key is *not* a failure case.
- */
- if (!wip->wi_wireless_done &&
- wip->wi_need_key) {
- is_failure = B_FALSE;
- } else {
- wip->wi_wireless_done = B_FALSE;
- wip->wi_need_key = B_FALSE;
- }
- }
- if (is_failure) {
- for (wlan = wlans;
- wlan < wlans + wireless_lan_used; wlan++) {
- if (strcmp(wlan->wl_if_name, ifname) ==
- 0) {
- if (wlan->connected)
- report_wlan_disconnect(
- wlan);
- wlan->connected = B_FALSE;
- }
- }
- }
- (void) pthread_mutex_unlock(&wifi_mutex);
- }
- if (is_failure) {
- dprintf("wireless check indicates disconnect");
- (void) dladm_wlan_disconnect(dld_handle, linkid);
- (void) np_queue_add_event(EV_LINKDISC, ifname);
- }
- }
-}
-
-/* ARGSUSED */
-void *
-periodic_wireless_scan(void *arg)
-{
- for (;;) {
- int ret, intv;
- dladm_wlan_linkattr_t attr;
- char ifname[LIFNAMSIZ];
- libnwam_interface_type_t ift;
- datalink_id_t linkid;
- char essid[DLADM_STRSIZE];
- struct wireless_lan *wlan;
-
- /*
- * Stop the scanning process if the user changes the interval
- * to zero dynamically. Reset the thread ID to a known-invalid
- * value. (Copy to a local variable to avoid race condition in
- * case SIGINT hits between this test and the call to poll().)
- */
- if ((intv = wlan_scan_interval) == 0) {
- dprintf("periodic wireless scan halted");
- break;
- }
-
- ret = poll(NULL, 0, intv * MILLISEC);
- if (ret == -1) {
- if (errno == EINTR)
- continue;
- syslog(LOG_INFO, "periodic_wireless_scan: poll failed");
- break;
- }
-
- /*
- * Just one more check before doing a scan that might now be
- * unwanted
- */
- if (wlan_scan_interval == 0) {
- dprintf("periodic wireless scan halted");
- break;
- }
-
- /* 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. Profile is wireless and we're not connected to the AP. Or
- * 3. The signal strength falls below a certain specified level.
- */
- if (ifname[0] != '\0') {
- if (ift != IF_WIRELESS)
- continue;
-
- /*
- * If these things fail, it means that our wireless
- * link isn't viable. Proceed in that way.
- */
- if (dladm_name2info(dld_handle, ifname, &linkid, NULL,
- NULL, NULL) != DLADM_STATUS_OK ||
- dladm_wlan_get_linkattr(dld_handle, 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) {
- /*
- * Double-check the ESSID. Some drivers
- * (notably 'iwh') have a habit of randomly
- * reconnecting themselves to APs that you
- * never requested.
- */
- (void) dladm_wlan_essid2str(
- &attr.la_wlan_attr.wa_essid, essid);
- if (pthread_mutex_lock(&wifi_mutex) != 0)
- continue;
- for (wlan = wlans;
- wlan < wlans + wireless_lan_used; wlan++) {
- if (wlan->connected &&
- strcmp(wlan->wl_if_name, ifname) ==
- 0)
- break;
- }
- if (wlan >= wlans + wireless_lan_used ||
- strcmp(wlan->essid, essid) == 0) {
- (void) pthread_mutex_unlock(
- &wifi_mutex);
- continue;
- }
- dprintf("%s is connected to %s instead of %s",
- ifname, essid, wlan->essid);
- (void) pthread_mutex_unlock(&wifi_mutex);
- }
- }
-
- /* 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 &&
- wip->wi_strength <=
- attr.la_wlan_attr.wa_strength) {
- (void) pthread_mutex_unlock(&
- wifi_mutex);
- continue;
- }
- wip->wi_wireless_done = B_FALSE;
- wip->wi_need_key = B_FALSE;
- }
- (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(dld_handle, linkid);
-
- /*
- * Tell the state machine that we've lost this link so
- * that it can do something about the problem.
- */
- (void) np_queue_add_event(
- (attr.la_status == DLADM_WLAN_LINK_CONNECTED ?
- EV_LINKFADE : EV_LINKDISC), ifname);
- }
- }
- scan = 0;
- (void) pthread_detach(pthread_self());
- return (NULL);
-}
-
-/*
- * Below are functions used to handle storage/retrieval of keys
- * for a given WLAN. The keys are stored/retrieved using dladm_set_secobj()
- * and dladm_get_secobj().
- */
-
-/*
- * Convert key hexascii string to raw secobj value. This
- * code is very similar to convert_secobj() in dladm.c, it would
- * be good to have a libdladm function to convert values.
- */
-static int
-key_string_to_secobj_value(char *buf, uint8_t *obj_val, uint_t *obj_lenp,
- dladm_secobj_class_t class)
-{
- size_t buf_len = strlen(buf);
-
- dprintf("before: key_string_to_secobj_value: buf_len = %d", buf_len);
- if (buf_len == 0) {
- /* length zero means "delete" */
- return (0);
- }
-
- if (buf[buf_len - 1] == '\n')
- buf[--buf_len] = '\0';
-
- dprintf("after: key_string_to_secobj_value: buf_len = %d", buf_len);
-
- if (class == DLADM_SECOBJ_CLASS_WPA) {
- /*
- * Per IEEE802.11i spec, the Pre-shared key (PSK) length should
- * be between 8 and 63.
- */
- if (buf_len < 8 || buf_len > 63) {
- syslog(LOG_ERR,
- "key_string_to_secobj_value:"
- " invalid WPA key length: buf_len = %d", buf_len);
- return (-1);
- }
- (void) memcpy(obj_val, buf, (uint_t)buf_len);
- *obj_lenp = buf_len;
- return (0);
- }
-
- switch (buf_len) {
- case 5: /* ASCII key sizes */
- case 13:
- (void) memcpy(obj_val, buf, (uint_t)buf_len);
- *obj_lenp = (uint_t)buf_len;
- break;
- case 10:
- case 26: /* Hex key sizes, not preceded by 0x */
- if (hexascii_to_octet(buf, (uint_t)buf_len, obj_val, obj_lenp)
- != 0) {
- syslog(LOG_ERR,
- "key_string_to_secobj_value: invalid WEP key");
- return (-1);
- }
- break;
- case 12:
- case 28: /* Hex key sizes, preceded by 0x */
- if (strncmp(buf, "0x", 2) != 0 ||
- hexascii_to_octet(buf + 2, (uint_t)buf_len - 2, obj_val,
- obj_lenp) != 0) {
- syslog(LOG_ERR,
- "key_string_to_secobj_value: invalid WEP key");
- return (-1);
- }
- break;
- default:
- syslog(LOG_ERR,
- "key_string_to_secobj_value: invalid WEP key length");
- return (-1);
- }
- return (0);
-}
-
-/*
- * Print the key name format into the appropriate field, then convert any ":"
- * characters to ".", as ":[1-4]" is the slot indicator, which otherwise
- * would trip us up. Invalid characters for secobj names are ignored.
- * The fourth 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, j;
- char secobj_name[DLADM_WLAN_MAX_KEYNAME_LEN];
-
- /* create a concatenated string with essid and bssid */
- if (bssid[0] == '\0') {
- (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s",
- essid);
- } else {
- (void) snprintf(secobj_name, sizeof (secobj_name), "nwam-%s-%s",
- essid, bssid);
- }
-
- /* copy only valid chars to the return string, terminating with \0 */
- i = 0; /* index into secobj_name */
- j = 0; /* index into name */
- while (secobj_name[i] != '\0') {
- if (j == nsz - 1)
- break;
-
- if (secobj_name[i] == ':') {
- name[j] = '.';
- j++;
- } else if (isalnum(secobj_name[i]) ||
- secobj_name[i] == '.' || secobj_name[i] == '-' ||
- secobj_name[i] == '_') {
- name[j] = secobj_name[i];
- j++;
- }
- i++;
- }
- name[j] = '\0';
-}
-
-static int
-store_key(struct wireless_lan *wlan)
-{
- uint8_t obj_val[DLADM_SECOBJ_VAL_MAX];
- uint_t obj_len = sizeof (obj_val);
- char obj_name[DLADM_SECOBJ_NAME_MAX];
- dladm_status_t status;
- char errmsg[DLADM_STRSIZE];
- dladm_secobj_class_t class;
-
- /*
- * Name key object for this WLAN so it can be later retrieved
- * (name is unique for each ESSID/BSSID combination).
- */
- set_key_name(wlan->essid, wlan->bssid, obj_name, sizeof (obj_name));
- dprintf("store_key: obj_name is %s", obj_name);
-
- 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) {
- /* above function logs internally on failure */
- return (-1);
- }
-
- /* we've validated the new key, so remove the old one */
- status = dladm_unset_secobj(dld_handle, 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(dld_handle, obj_name, class,
- obj_val, obj_len,
- DLADM_OPT_CREATE | DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE);
- if (status != DLADM_STATUS_OK) {
- syslog(LOG_ERR, "store_key: could not create secure object "
- "'%s' for key: %s", obj_name,
- dladm_status2str(status, errmsg));
- return (-1);
- }
- /*
- * We don't really need to retrieve the key we just stored, but
- * we do need to set the cooked key, and the function below takes
- * care of allocating memory and setting the length and slot ID
- * besides just copying the value, so it is simpler just to call
- * the retrieve function instead of doing it all here.
- *
- * Since we just stored the key, retrieve_key() "shouldn't"
- * fail. If it does fail, it's not the end of the world; a NULL
- * value for wlan->cooked_key simply means this particular
- * attempt to connect will fail, and alternative connection
- * options will be used.
- */
- wlan->cooked_key = retrieve_key(wlan->essid, wlan->bssid, class);
- return (0);
-}
-
-/*
- * retrieve_key returns NULL if no key was recovered from libdladm
- */
-static dladm_wlan_key_t *
-retrieve_key(const char *essid, const char *bssid, dladm_secobj_class_t req)
-{
- dladm_status_t status;
- char errmsg[DLADM_STRSIZE];
- dladm_wlan_key_t *cooked_key;
- dladm_secobj_class_t class;
-
- /*
- * Newly-allocated key must be freed by caller, or by
- * subsequent call to retrieve_key().
- */
- if ((cooked_key = malloc(sizeof (dladm_wlan_key_t))) == NULL) {
- syslog(LOG_ERR, "retrieve_key: malloc failed");
- return (NULL);
- }
-
- /*
- * 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);
- cooked_key->wk_idx = 1;
-
- /* Try the kernel first, then fall back to persistent storage. */
- status = dladm_get_secobj(dld_handle, cooked_key->wk_name, &class,
- cooked_key->wk_val, &cooked_key->wk_len,
- DLADM_OPT_ACTIVE);
- if (status != DLADM_STATUS_OK) {
- dprintf("retrieve_key: dladm_get_secobj(TEMP) failed: %s",
- dladm_status2str(status, errmsg));
- status = dladm_get_secobj(dld_handle, cooked_key->wk_name,
- &class, cooked_key->wk_val, &cooked_key->wk_len,
- DLADM_OPT_PERSIST);
- }
-
- switch (status) {
- case DLADM_STATUS_OK:
- dprintf("retrieve_key: dladm_get_secobj succeeded: len %d",
- cooked_key->wk_len);
- break;
- case DLADM_STATUS_NOTFOUND:
- /*
- * We do not want an error in the case that the secobj
- * is not found, since we then prompt for it.
- */
- free(cooked_key);
- return (NULL);
- default:
- syslog(LOG_ERR, "retrieve_key: could not get key "
- "from secure object '%s': %s", cooked_key->wk_name,
- dladm_status2str(status, errmsg));
- free(cooked_key);
- return (NULL);
- }
-
- if (class != req) { /* the key mismatch */
- syslog(LOG_ERR, "retrieve_key: key type mismatch"
- " from secure object '%s'", cooked_key->wk_name);
- free(cooked_key);
- return (NULL);
- }
-
- return (cooked_key);
-}
-
-/*
- * 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)
-{
- 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(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;
- }
- if (fp != NULL)
- (void) fclose(fp);
- return (retv);
-}
-
-static int
-delete_known_wifi_nets_file(const char *essid, const char *bssid)
-{
- FILE *fpin, *fpout;
- char line[LINE_MAX];
- char *cp;
- int retv;
- size_t essidlen, bssidlen;
- boolean_t found;
-
- 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++;
-
- /*
- * 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.
- */
-
- 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);
- }
-
- (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);
- }
-
- return (retv);
-}
-
-/*
- * Check if the given AP (ESSID, BSSID pair) is on the known AP list.
- * If found_essid is non-NULL and the match is found (B_TRUE is returned)
- * the matched ESSID is copied out into buffer pointed by found_essid.
- * The buffer is expected to be at least DLADM_STRSIZE bytes long.
- */
-static boolean_t
-known_wifi_nets_lookup(const char *new_essid, const char *new_bssid,
- char *found_essid)
-{
- FILE *fp;
- char line[LINE_MAX];
- char *cp;
- char *tok[MAX_FIELDS];
- int line_num;
- boolean_t found = B_FALSE;
-
- /*
- * For now the file format is:
- * essid\tbssid
- * (essid followed by tab followed by bssid)
- */
- fp = fopen(KNOWN_WIFI_NETS, "r");
- if (fp == NULL)
- return (B_FALSE);
- for (line_num = 1; fgets(line, sizeof (line), fp) != NULL; line_num++) {
-
- cp = line;
- while (isspace(*cp))
- cp++;
-
- if (*cp == '#' || *cp == '\0')
- continue;
-
- if (bufsplit(cp, MAX_FIELDS, tok) != MAX_FIELDS) {
- syslog(LOG_ERR, "%s:%d: wrong number of tokens; "
- "ignoring entry", KNOWN_WIFI_NETS, line_num);
- continue;
- }
-
- /*
- * If we're searching on ESSID alone, then any match on a
- * specific ESSID will do.
- */
- if (*new_bssid == '\0') {
- if (*new_essid != '\0' &&
- strcmp(tok[ESSID], new_essid) == 0) {
- found = B_TRUE;
- break;
- }
- }
- /*
- * If BSSID match is found we check ESSID, which should
- * either match as well, or be an empty string.
- * In latter case we'll retrieve the ESSID from known_wifi_nets
- * later.
- */
- else if (strcmp(tok[BSSID], new_bssid) == 0) {
- /*
- * Got BSSID match, either ESSID was not specified,
- * or it should match
- */
- if (*new_essid == '\0' ||
- strcmp(tok[ESSID], new_essid) == 0) {
- found = B_TRUE;
- break;
- }
- }
- }
-
- if (found) {
- if (found_essid != NULL)
- (void) strlcpy(found_essid, tok[ESSID], DLADM_STRSIZE);
- }
-
- (void) fclose(fp);
- 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(dld_handle, 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(dld_handle,
- 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 zero-length)
- * reqlan->bssid is optional (i.e., may be zero-length)
- */
-static return_vals_t
-connect_chosen_lan(struct wireless_lan *reqlan, wireless_if_t *wip)
-{
- uint_t keycount;
- dladm_wlan_key_t *key;
- dladm_wlan_attr_t attr;
- dladm_status_t status;
- uint_t flags = DLADM_WLAN_CONNECT_NOSCAN;
- int timeout = DLADM_WLAN_CONNECT_TIMEOUT_DEFAULT;
- char errmsg[DLADM_STRSIZE];
- 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 (FAILURE);
- dprintf("connect_chosen_lan(%s, %s, %s)", reqlan->essid,
- reqlan->bssid, wip->wi_name);
-
- /* If it is already connected to the required AP, just return. */
- 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, wip->wi_name);
- return (FAILURE);
- }
- attr.wa_valid = DLADM_WLAN_ATTR_ESSID;
-
- /* note: bssid logic here is non-functional */
- 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, wip->wi_name);
- return (FAILURE);
- }
- attr.wa_valid |= DLADM_WLAN_ATTR_BSSID;
- }
-
- /* First check for the key */
- 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->attrs.wa_secmode;
- key = reqlan->cooked_key;
- keycount = 1;
- dprintf("connect_chosen_lan: retrieved key");
- } else {
- key = NULL;
- keycount = 0;
- }
-
- /*
- * Connect; only scan if a bssid was not specified.
- * If it times out and we were trying with a bssid,
- * try a second time with just the ESSID.
- */
-
- status = dladm_wlan_connect(dld_handle, wip->wi_linkid, &attr, timeout,
- key, keycount, flags);
- dprintf("connect_chosen_lan: dladm_wlan_connect returned %s",
- dladm_status2str(status, errmsg));
- /*
- * This doesn't work due to CR 6772510.
- */
-#ifdef CR6772510_FIXED
- 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(dld_handle, wip->wi_linkid, &attr,
- timeout, key, keycount, flags);
- }
-#endif /* CR6772510_FIXED */
- if (status == DLADM_STATUS_OK) {
- return (SUCCESS);
- } else {
- syslog(LOG_ERR,
- "connect_chosen_lan: connect to '%s' failed on '%s': %s",
- reqlan->essid, wip->wi_name,
- dladm_status2str(status, errmsg));
- return (FAILURE);
- }
-}
-
-/*
- * 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.
- */
-boolean_t
-check_wlan_connected(const char *ifname, const char *essid, const char *bssid)
-{
- wireless_if_t *wip;
- boolean_t retv;
-
- if (pthread_mutex_lock(&wifi_mutex) != 0)
- return (B_FALSE);
-
- 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);
-}
-
-/*
- * This thread performs the blocking actions related to a wireless connection
- * request. The attempt to connect isn't started until all other connects and
- * scans have finished, and while the connect is in progress, no new connects
- * or scans can be started.
- */
-static void *
-connect_thread(void *arg)
-{
- struct wireless_lan *req_wlan = arg;
- wireless_if_t *wip;
- struct wireless_lan *wlan = NULL;
-
- if (!scanconnect_entry(req_wlan->wl_if_name, B_TRUE))
- goto failure_noentry;
-
- if (pthread_mutex_lock(&wifi_mutex) != 0)
- goto failure_unlocked;
-
- if ((wip = find_wireless_if(req_wlan->wl_if_name)) == NULL)
- goto failure;
-
- /* This is an autoconf request. */
- if (req_wlan->essid[0] == '\0' && req_wlan->bssid[0] == '\0') {
- if (!wlan_autoconf(wip) && !update_connected_wlan(wip, NULL))
- goto failure;
- else
- goto done;
- }
-
- wlan = find_wlan_entry(req_wlan->wl_if_name, req_wlan->essid,
- req_wlan->bssid);
- if (wlan == NULL)
- wlan = req_wlan;
-
- /*
- * now attempt to connect to selection
- */
- switch (connect_chosen_lan(wlan, wip)) {
- case WAITING:
- break;
-
- case SUCCESS: {
- dladm_status_t status;
- dladm_wlan_linkattr_t attr;
- char lclssid[DLADM_STRSIZE];
- char unnecessary_buf[DLADM_STRSIZE];
-
- /*
- * Successful connection to user-chosen AP; add entry to
- * known_essid_list_file. First make sure the wlan->bssid
- * isn't empty. Note that empty bssid is never allocated.
- *
- * We would like to query the driver only in the case where the
- * BSSID is not known, but it turns out that due to CR 6772510,
- * the actual BSSID we connect to is arbitrary. Nothing we can
- * do about that; just get the new value and live with it.
- */
- status = dladm_wlan_get_linkattr(dld_handle, wip->wi_linkid,
- &attr);
- if (status != DLADM_STATUS_OK) {
- dprintf("failed to get linkattr on %s after connecting "
- "to %s: %s", wlan->wl_if_name, wlan->essid,
- dladm_status2str(status, unnecessary_buf));
- goto failure;
- }
- (void) dladm_wlan_essid2str(&attr.la_wlan_attr.wa_essid,
- lclssid);
- if (strcmp(req_wlan->essid, lclssid) != 0) {
- dprintf("connected to strange network: expected %s got "
- "%s", req_wlan->essid, lclssid);
- goto failure;
- }
- (void) dladm_wlan_bssid2str(&attr.la_wlan_attr.wa_bssid,
- lclssid);
- if (wlan == req_wlan || strcmp(wlan->bssid, lclssid) != 0) {
- wlan = add_wlan_entry(req_wlan->wl_if_name,
- req_wlan->essid, lclssid, &attr.la_wlan_attr);
- if (wlan == NULL)
- goto failure;
- }
- if (wlan->bssid[0] == '\0' && lclssid[0] != '\0')
- wlan->bssid = strdup(lclssid);
- if (wlan->bssid == NULL || wlan->bssid[0] == '\0') {
- /* Don't leave it as NULL (for simplicity) */
- wlan->bssid = "";
- goto failure;
- }
- wlan->connected = B_TRUE;
- if (!update_connected_wlan(wip, wlan))
- goto failure;
- wlan->known = B_TRUE;
- (void) add_known_wifi_nets_file(wlan->essid, wlan->bssid);
- /* We're done; trigger IP bring-up. */
- (void) np_queue_add_event(EV_RESELECT, wlan->wl_if_name);
- report_wlan_connected(wlan);
- break;
- }
-
- default:
- goto failure;
- }
-
-done:
- (void) pthread_mutex_unlock(&wifi_mutex);
- scanconnect_exit();
- free_wireless_lan(req_wlan);
- return (NULL);
-
-failure:
- /*
- * 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.
- */
- if (wip != NULL) {
- wip->wi_need_key = B_FALSE;
- wip->wi_wireless_done = B_FALSE;
- (void) dladm_wlan_disconnect(dld_handle, wip->wi_linkid);
- }
- if (wlan != NULL)
- wlan->rescan = B_TRUE;
- (void) pthread_mutex_unlock(&wifi_mutex);
-
-failure_unlocked:
- scanconnect_exit();
-failure_noentry:
- syslog(LOG_WARNING, "could not connect to chosen WLAN %s on %s",
- req_wlan->essid, req_wlan->wl_if_name);
- report_wlan_connect_fail(req_wlan->wl_if_name);
- free_wireless_lan(req_wlan);
- return (NULL);
-}
-
-/*
- * This is the entry point for GUI "select access point" requests. It verifies
- * the parameters and then launches a new thread to perform the connect
- * operation. When it returns success (0), the user should expect future
- * events indicating progress.
- *
- * Returns:
- * 0 - ok (or more data requested with new event)
- * ENXIO - no such interface
- * ENODEV - interface is not wireless
- * EINVAL - failed to perform requested action
- */
-int
-set_specific_lan(const char *ifname, const char *essid, const char *bssid)
-{
- libnwam_interface_type_t ift;
- pthread_t conn_thr;
- pthread_attr_t attr;
- struct wireless_lan *wlan;
- int retv;
-
- if ((ift = get_if_type(ifname)) == IF_UNKNOWN)
- return (ENXIO);
- if (ift != IF_WIRELESS)
- return (EINVAL);
-
- if ((wlan = calloc(1, sizeof (struct wireless_lan))) == NULL)
- return (ENOMEM);
- (void) strlcpy(wlan->wl_if_name, ifname, sizeof (wlan->wl_if_name));
- wlan->essid = strdup(essid);
- wlan->bssid = *bssid == '\0' ? "" : strdup(bssid);
- if (wlan->essid == NULL || wlan->bssid == NULL) {
- free_wireless_lan(wlan);
- return (ENOMEM);
- }
- (void) pthread_attr_init(&attr);
- (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- retv = pthread_create(&conn_thr, &attr, connect_thread, wlan);
- if (retv == 0)
- dprintf("started connect thread %d for %s %s %s", conn_thr,
- ifname, essid, bssid);
- else
- free_wireless_lan(wlan);
- return (retv);
-}
-
-int
-set_wlan_key(const char *ifname, const char *essid, const char *bssid,
- const char *key, const char *secmode)
-{
- libnwam_interface_type_t ift;
- struct wireless_lan *wlan, local_wlan;
- wireless_if_t *wip;
- int retv;
- boolean_t need_key;
- dladm_wlan_secmode_t smode = DLADM_WLAN_SECMODE_WEP;
-
- ift = get_if_type(ifname);
- if (ift == IF_UNKNOWN)
- return (ENXIO);
- if (ift != IF_WIRELESS)
- return (EINVAL);
-
- if (*secmode != '\0' &&
- dladm_wlan_str2secmode(secmode, &smode) != DLADM_STATUS_OK)
- return (EINVAL);
-
- if ((retv = pthread_mutex_lock(&wifi_mutex)) != 0)
- return (retv);
-
- if ((wlan = find_wlan_entry(ifname, essid, bssid)) == NULL) {
- /* If not seen in scan, then secmode is required */
- if (*secmode == '\0') {
- retv = ENODEV;
- goto done;
- }
- /* Prohibit a completely blank entry */
- if (*essid == '\0' && *bssid == '\0') {
- retv = EINVAL;
- goto done;
- }
- (void) memset(&local_wlan, 0, sizeof (local_wlan));
- wlan = &local_wlan;
- (void) strlcpy(wlan->wl_if_name, ifname,
- sizeof (wlan->wl_if_name));
- wlan->essid = (char *)essid;
- wlan->bssid = (char *)bssid;
- wlan->raw_key = (char *)key;
- wlan->attrs.wa_secmode = smode;
- } else {
- /* If seen in scan, then secmode given (if any) must match */
- if (*secmode != '\0' && smode != wlan->attrs.wa_secmode) {
- retv = EINVAL;
- goto done;
- }
- /* save a copy of the new key in the scan entry */
- if ((wlan->raw_key = strdup(key)) == NULL) {
- retv = ENOMEM;
- goto done;
- }
- }
-
- if (store_key(wlan) != 0)
- retv = EINVAL;
- else
- retv = 0;
-
-done:
- wip = find_wireless_if(ifname);
- need_key = wip != NULL && wip->wi_need_key;
- (void) pthread_mutex_unlock(&wifi_mutex);
-
- if (retv == 0 && need_key)
- retv = set_specific_lan(ifname, essid, bssid);
-
- return (retv);
-}
-
-static boolean_t
-wlan_autoconf(const wireless_if_t *wip)
-{
- dladm_status_t status;
- boolean_t autoconf;
-
- if (lookup_boolean_property(OUR_PG, "autoconf", &autoconf) == 0) {
- if (!autoconf)
- return (B_FALSE);
- }
-
- /* If the NIC is already associated with something, just return. */
- if (check_wlan(wip, NULL, NULL, B_TRUE))
- return (B_TRUE);
-
- /*
- * Do autoconf, relying on the heuristics used by dladm_wlan_connect()
- * to cycle through WLANs detected in priority order, attempting
- * to connect.
- */
- status = dladm_wlan_connect(dld_handle, 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",
- wip->wi_name, dladm_status2str(status, errmsg));
- return (B_FALSE);
- }
- return (B_TRUE);
-}
-
-/*
- * This function searches through the wlans[] array and determines which ones
- * have been visited before.
- *
- * 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.
- */
-return_vals_t
-handle_wireless_lan(const char *ifname)
-{
- wireless_if_t *wip;
- struct wireless_lan *cur_wlan, *max_wlan, *strong_wlan = NULL;
- struct wireless_lan *most_recent;
- boolean_t many_present;
- dladm_wlan_strength_t strongest = DLADM_WLAN_STRENGTH_VERY_WEAK;
- return_vals_t connect_result = FAILURE;
-
- /*
- * We wait while a scan or another connect is in progress, and then
- * block other connects/scans. Since we allow a user to initiate a
- * re-scan, we can proceed even when no scan has yet been done to fill
- * in the AP list.
- */
- if (!scanconnect_entry(ifname, B_TRUE))
- return (FAILURE);
-
- if (pthread_mutex_lock(&wifi_mutex) != 0) {
- scanconnect_exit();
- return (FAILURE);
- }
-
- if ((wip = find_wireless_if(ifname)) == NULL)
- goto finished;
-
- if (wip->wi_wireless_done) {
- dprintf("handle_wireless_lan: skipping policy scan; done");
- /* special case; avoid interface update */
- (void) pthread_mutex_unlock(&wifi_mutex);
- scanconnect_exit();
- return (SUCCESS);
- }
-
- 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
- * has been used previously. If more than one available
- * nets has been used before, then prompt user with
- * all the applicable previously wifi nets, and ask which
- * one to connect to.
- */
- for (; cur_wlan < max_wlan; cur_wlan++) {
- /* Find the AP with the highest signal. */
- if (cur_wlan->attrs.wa_strength > strongest) {
- strongest = cur_wlan->attrs.wa_strength;
- strong_wlan = cur_wlan;
- }
-
- if (known_wifi_nets_lookup(cur_wlan->essid, cur_wlan->bssid,
- NULL))
- cur_wlan->known = B_TRUE;
-
- if (!cur_wlan->known && !strict_bssid &&
- known_wifi_nets_lookup(cur_wlan->essid, "", NULL)) {
- dprintf("noticed new BSSID %s for ESSID %s on %s",
- cur_wlan->bssid, cur_wlan->essid, ifname);
- if (add_known_wifi_nets_file(cur_wlan->essid,
- cur_wlan->bssid) == 0)
- cur_wlan->known = B_TRUE;
- }
-
- 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) {
- if (!many_present)
- dprintf("both %s and %s are known and "
- "present on %s", cur_wlan->essid,
- most_recent->essid, ifname);
- many_present = B_TRUE;
- } else if (cur_wlan->attrs.wa_strength >
- most_recent->attrs.wa_strength) {
- if (most_recent->connected) {
- dprintf("found better BSS %s for ESS "
- "%s; disconnecting %s on %s",
- cur_wlan->bssid, cur_wlan->essid,
- most_recent->bssid, ifname);
- (void) dladm_wlan_disconnect(dld_handle,
- wip->wi_linkid);
- most_recent->connected = B_FALSE;
- report_wlan_disconnect(most_recent);
- wip->wi_wireless_done = B_FALSE;
- }
- most_recent = cur_wlan;
- } else if (cur_wlan->attrs.wa_strength <
- most_recent->attrs.wa_strength &&
- cur_wlan->connected) {
- dprintf("found better BSS %s for ESS %s; "
- "disconnecting %s on %s",
- most_recent->bssid, most_recent->essid,
- cur_wlan->bssid, ifname);
- (void) dladm_wlan_disconnect(dld_handle,
- wip->wi_linkid);
- cur_wlan->connected = B_FALSE;
- report_wlan_disconnect(cur_wlan);
- wip->wi_wireless_done = B_FALSE;
- }
- }
-
- /* 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;
- }
-
- /*
- * The Three Rules:
- *
- * 1. If no known AP is in range, then seek help.
- *
- * 2. If two or more known APs are in range, then seek help.
- *
- * 3. If a known AP is in range, and its signal strength is "weak"
- * or lower, and the strongest available is "very good" or
- * better, then seek help.
- */
- if (most_recent != NULL && (!many_present || most_recent->connected) &&
- (most_recent->attrs.wa_strength > DLADM_WLAN_STRENGTH_WEAK ||
- strongest < DLADM_WLAN_STRENGTH_VERY_GOOD)) {
- if (most_recent->connected) {
- dprintf("%s already connected to %s", ifname,
- most_recent->essid);
- connect_result = SUCCESS;
- } else {
- dprintf("%s connecting automatically to %s", ifname,
- most_recent->essid);
- connect_result = connect_chosen_lan(most_recent, wip);
- switch (connect_result) {
- case FAILURE:
- report_wlan_connect_fail(wip->wi_name);
- most_recent->rescan = B_TRUE;
- syslog(LOG_WARNING, "could not connect to "
- "chosen WLAN %s on %s, going to auto-conf",
- most_recent->essid, ifname);
- connect_result = wlan_autoconf(wip) ? SUCCESS :
- FAILURE;
- most_recent = NULL;
- break;
- case SUCCESS:
- most_recent->connected = B_TRUE;
- report_wlan_connected(most_recent);
- break;
- }
- }
- } else if (request_wlan_selection(ifname, wlans, wireless_lan_used)) {
- if (most_recent == NULL)
- dprintf("%s has no known WLANs; requested help",
- ifname);
- else if (many_present && !most_recent->connected)
- dprintf("%s has multiple known WLANs and is not "
- "connected; requested help", ifname);
- else
- dprintf("%s has known WLAN %s, but not strongest %s; "
- "requested help", ifname, most_recent->essid,
- strong_wlan->essid);
- connect_result = WAITING;
- } else {
- dprintf("%s has no connected AP or GUI; try auto", ifname);
- connect_result = wlan_autoconf(wip) ? SUCCESS : FAILURE;
- most_recent = NULL;
- }
-
-finished:
- if (connect_result == SUCCESS &&
- !update_connected_wlan(wip, most_recent))
- connect_result = FAILURE;
- (void) pthread_mutex_unlock(&wifi_mutex);
- scanconnect_exit();
-
- 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(dld_handle,
- 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);
- }
- }
- (void) pthread_mutex_unlock(&wifi_mutex);
- }
-}
-
-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);
- }
-}
-
-void
-print_wireless_status(void)
-{
- wireless_if_t *wip;
- struct wireless_lan *wlan;
- char strength[DLADM_STRSIZE];
-
- 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) {
- (void) dladm_wlan_strength2str(&wip->wi_strength,
- strength);
- dprintf("WIF %s linkid %d scan %srunning "
- "wireless %sdone %sneed key strength %s",
- wip->wi_name, wip->wi_linkid,
- wip->wi_scan_running ? "" : "not ",
- wip->wi_wireless_done ? "" : "not ",
- wip->wi_need_key ? "" : "don't ",
- 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);
- }
-}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
index 61a3f6e4ec..ce1ffcf27e 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
@@ -20,7 +20,7 @@
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -67,12 +67,12 @@ SRCS= $(PROGSRCS) $(OTHERSRC)
SUBDIRS= bootconfchk htable ifconfig ilbadm in.ftpd in.rdisc in.routed \
in.talkd inetadm inetconv ipmpstat ipqosconf ipsecutils \
- kssl/kssladm kssl/ksslcfg ping routeadm snoop sppptun \
- traceroute wificonfig
+ kssl/kssladm kssl/ksslcfg nwamadm nwamcfg ping routeadm \
+ snoop sppptun traceroute wificonfig
MSGSUBDIRS= bootconfchk htable ifconfig ilbadm in.ftpd in.routed in.talkd \
inetadm inetconv ipmpstat ipqosconf ipsecutils kssl/ksslcfg \
- routeadm sppptun snoop wificonfig
+ nwamadm nwamcfg routeadm sppptun snoop wificonfig
# As programs get lint-clean, add them here and to the 'lint' target.
# Eventually this hack should go away, and all in PROG should be
@@ -85,8 +85,8 @@ LINTCLEAN= 6to4relay arp in.rlogind in.rshd in.telnetd in.tftpd \
# with SUBDIRS. Also (sigh) deal with the commented-out build lines
# for the lint rule.
LINTSUBDIRS= bootconfchk ilbadm in.rdisc in.routed in.talkd inetadm \
- inetconv ipmpstat ipqosconf ipsecutils ping routeadm sppptun \
- traceroute wificonfig
+ inetconv ipmpstat ipqosconf ipsecutils nwamadm nwamcfg ping \
+ routeadm sppptun traceroute wificonfig
# And as programs are verified not to attempt to write into constants,
# -xstrconst should be used to ensure they stay that way.
CONSTCLEAN=
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers
index d66be91de2..d68723a4bb 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpusers
@@ -10,6 +10,8 @@ lp
uucp
nuucp
dladm
+netadm
+netcfg
smmsp
listen
gdm
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/Makefile
new file mode 100644
index 0000000000..d98073fe6d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/Makefile
@@ -0,0 +1,49 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# cmd/cmd-inet/usr.sbin/nwamadm/Makefile
+
+PROG= nwamadm
+
+include ../../../Makefile.cmd
+
+XGETFLAGS += -a -x $(PROG).xcl
+LDLIBS += -linetutil -lnsl -lnwam -lumem -lscf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+check: $(PROG).c
+ $(CSTYLE) -pP $(PROG).c
+
+clean:
+
+lint: lint_PROG
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.c b/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.c
new file mode 100644
index 0000000000..fb5d3cba23
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.c
@@ -0,0 +1,1416 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * nwamadm is a command interpreter to administer NWAM profiles. It
+ * is all in C (i.e., no lex/yacc), and all the argument passing is
+ * argc/argv based. main() calls the command's handler function,
+ * which first calls parse_argv() to parse the input arguments and set
+ * approriate variables for each command. The rest of the program is
+ * helper functions for the handler functions.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <libdlwlan.h>
+#include <libnwam.h>
+#include <libscf.h>
+#include <locale.h>
+#include <netinet/in.h>
+#include <ofmt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
+#endif
+
+typedef void (cmd_func_t)(int, char **);
+
+struct cmd {
+ uint_t cmd_num; /* command number */
+ const char *cmd_name; /* command name */
+ cmd_func_t *cmd_handler; /* function to call */
+ const char *cmd_usage; /* short form help */
+ const char *cmd_desc; /* command description */
+ boolean_t cmd_needs_nwamd; /* nwam needs to run */
+};
+
+/* constants for commands */
+#define CMD_HELP 0
+#define CMD_ENABLE 1
+#define CMD_DISABLE 2
+#define CMD_LIST 3
+#define CMD_SHOW_EVENTS 4
+#define CMD_SCAN_WIFI 5
+#define CMD_SELECT_WIFI 6
+
+#define CMD_MIN CMD_HELP
+#define CMD_MAX CMD_SELECT_WIFI
+
+/* functions to call */
+static cmd_func_t help_func, enable_func, disable_func, list_func;
+static cmd_func_t show_events_func, scan_wifi_func, select_wifi_func;
+static ofmt_cb_t print_list_cb;
+
+/* table of commands and usage */
+static struct cmd cmdtab[] = {
+ { CMD_HELP, "help", help_func,
+ "help",
+ "Print this usage message.", B_FALSE },
+ { CMD_ENABLE, "enable", enable_func,
+ "enable [-p <profile-type>] [-c <ncu-class>] <object-name>",
+ "Enable the specified profile.", B_FALSE },
+ { CMD_DISABLE, "disable", disable_func,
+ "disable [-p <profile-type>] [-c <ncu-class>] <object-name>",
+ "Disable the specified profile.", B_FALSE },
+ { CMD_LIST, "list", list_func,
+ "list [-x] [-p <profile-type>] [-c <ncu-class>] [<object-name>]",
+ "List profiles and their current states.", B_TRUE },
+ { CMD_SHOW_EVENTS, "show-events", show_events_func,
+ "show-events",
+ "Display all events.", B_TRUE },
+ { CMD_SCAN_WIFI, "scan-wifi", scan_wifi_func,
+ "scan-wifi <link-name>",
+ "Request a WiFi scan for the selected link.", B_TRUE },
+ { CMD_SELECT_WIFI, "select-wifi", select_wifi_func,
+ "select-wifi <link-name>",
+ "Make a WLAN selection from the last WiFi scan.", B_TRUE }
+};
+
+/* Structure for "nwamadm list" output */
+
+typedef struct profile_entry {
+ nwam_object_type_t p_type;
+ nwam_ncu_class_t p_ncu_class;
+ char p_name[NWAM_MAX_NAME_LEN];
+ nwam_state_t p_state;
+ nwam_aux_state_t p_aux_state;
+} profile_entry_t;
+
+/* widths of colums for printing */
+#define TYPE_WIDTH 12 /* width of TYPE column */
+#define PROFILE_WIDTH 15 /* width of PROFILE column */
+#define STATE_WIDTH 15 /* width of STATE column */
+#define AUXSTATE_WIDTH 36 /* width of AUXILIARY STATE column */
+
+#define EVENT_WIDTH 22 /* width of EVENT column */
+#define DESCRIPTION_WIDTH 56 /* width of DESCRIPTION column */
+
+/* id for columns of "nwamadm list" */
+typedef enum {
+ LIST_TYPE,
+ LIST_PROFILE,
+ LIST_STATE,
+ LIST_AUXSTATE
+} list_field_id_t;
+
+static const ofmt_field_t list_fields[] = {
+ /* header, width, id, callback */
+ { "TYPE", TYPE_WIDTH, LIST_TYPE, print_list_cb },
+ { "PROFILE", PROFILE_WIDTH, LIST_PROFILE, print_list_cb },
+ { "STATE", STATE_WIDTH, LIST_STATE, print_list_cb },
+ { "AUXILIARY STATE", AUXSTATE_WIDTH, LIST_AUXSTATE, print_list_cb },
+ { NULL, 0, 0, NULL }
+};
+
+/* Global variables */
+
+/* set early in main(), never modified thereafter, used all over the place */
+static char *execname;
+
+/* whether the auxilary states are to be printed or not */
+static boolean_t extended_list = B_FALSE;
+
+/* Functions */
+
+static const char *
+cmd_to_str(int cmd_num)
+{
+ assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
+ return (cmdtab[cmd_num].cmd_name);
+}
+
+/* returns description of given command */
+static const char *
+long_help(int cmd_num)
+{
+ assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
+ return (gettext(cmdtab[cmd_num].cmd_desc));
+}
+
+/*
+ * Called with explicit B_TRUE when help is explicitly required,
+ * B_FALSE for errors
+ */
+static void
+usage(boolean_t explicit)
+{
+ int i;
+ FILE *fd = explicit ? stdout : stderr;
+
+ (void) fprintf(fd, gettext("usage: <subcommand> <args> ...\n"));
+ for (i = CMD_MIN; i <= CMD_MAX; i++) {
+ (void) fprintf(fd, "\t%s\n", cmdtab[i].cmd_usage);
+ if (explicit)
+ (void) fprintf(fd, "\t\t%s\n\n", long_help(i));
+ }
+}
+
+/* PRINTFLIKE1 */
+static void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", execname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, "\n");
+
+ exit(EXIT_FAILURE);
+}
+
+/* PRINTFLIKE2 */
+static void
+die_nwamerr(nwam_error_t err, const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", execname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, ": %s\n", nwam_strerror(err));
+
+ exit(EXIT_FAILURE);
+}
+
+/* prints the usage for cmd_num and exits */
+static void
+die_usage(int cmd_num)
+{
+ assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
+
+ (void) fprintf(stderr, "%s: %s\n", gettext("usage"),
+ cmdtab[cmd_num].cmd_usage);
+ (void) fprintf(stderr, "\t%s\n", long_help(cmd_num));
+
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * Prints the usage and description of all commands
+ */
+/* ARGSUSED */
+static void
+help_func(int argc, char *argv[])
+{
+ usage(B_TRUE);
+}
+
+/* determines if the NCP is active or not. If so, sets arg and halts walk. */
+static int
+active_ncp_callback(nwam_ncp_handle_t ncph, void *arg)
+{
+ char **namep = arg;
+ nwam_state_t state = NWAM_STATE_UNINITIALIZED;
+ nwam_aux_state_t aux;
+
+ (void) nwam_ncp_get_state(ncph, &state, &aux);
+ if (state == NWAM_STATE_ONLINE) {
+ if (nwam_ncp_get_name(ncph, namep) != NWAM_SUCCESS)
+ *namep = NULL;
+ return (1);
+ }
+
+ return (0);
+}
+
+/* find the currently active NCP and returns its handle */
+static nwam_ncp_handle_t
+determine_active_ncp()
+{
+ char *active_ncp;
+ nwam_ncp_handle_t ncph;
+ nwam_error_t ret;
+
+ if (nwam_walk_ncps(active_ncp_callback, &active_ncp, 0, NULL)
+ == NWAM_WALK_HALTED) {
+ if (active_ncp == NULL)
+ return (NULL);
+
+ /* retrieve the NCP handle */
+ ret = nwam_ncp_read(active_ncp, 0, &ncph);
+ free(active_ncp);
+ if (ret == NWAM_SUCCESS)
+ return (ncph);
+ }
+
+ return (NULL);
+}
+
+/* check if the given name is a valid loc, test by reading the given loc */
+static boolean_t
+valid_loc(const char *name)
+{
+ nwam_loc_handle_t loch;
+
+ if (nwam_loc_read(name, 0, &loch) != NWAM_SUCCESS)
+ return (B_FALSE);
+ nwam_loc_free(loch);
+ return (B_TRUE);
+}
+
+static boolean_t
+valid_enm(const char *name)
+{
+ nwam_enm_handle_t enmh;
+
+ if (nwam_enm_read(name, 0, &enmh) != NWAM_SUCCESS)
+ return (B_FALSE);
+ nwam_enm_free(enmh);
+ return (B_TRUE);
+}
+
+static boolean_t
+valid_ncp(const char *name)
+{
+ nwam_ncp_handle_t ncph;
+
+ if (nwam_ncp_read(name, 0, &ncph) != NWAM_SUCCESS)
+ return (B_FALSE);
+ nwam_ncp_free(ncph);
+ return (B_TRUE);
+}
+
+static boolean_t
+valid_ncu(const char *name)
+{
+ nwam_ncp_handle_t ncph;
+ nwam_ncu_handle_t ncuh;
+ nwam_error_t ret;
+
+ if ((ncph = determine_active_ncp()) == NULL)
+ return (B_FALSE);
+
+ ret = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_ANY, 0, &ncuh);
+ nwam_ncp_free(ncph);
+ if (ret != NWAM_SUCCESS && ret != NWAM_ENTITY_MULTIPLE_VALUES)
+ return (B_FALSE);
+ nwam_ncu_free(ncuh);
+ return (B_TRUE);
+}
+
+/*
+ * Given a name, returns object type (loc, enm, ncp, or ncu) and how many
+ * objects matched that name.
+ */
+static nwam_object_type_t
+determine_object_type(const char *name, int *num)
+{
+ nwam_object_type_t type;
+ int n = 0;
+
+ /* see if a valid loc, enm, ncp and/or ncu exists with given name */
+ if (valid_loc(name)) {
+ n++;
+ type = NWAM_OBJECT_TYPE_LOC;
+ }
+ if (valid_enm(name)) {
+ n++;
+ type = NWAM_OBJECT_TYPE_ENM;
+ }
+ if (valid_ncp(name)) {
+ n++;
+ type = NWAM_OBJECT_TYPE_NCP;
+ }
+ if (valid_ncu(name)) {
+ n++;
+ type = NWAM_OBJECT_TYPE_NCU;
+ }
+
+ /* if n > 1, then it means *type was set multiple times, undo it */
+ if (n != 1)
+ type = NWAM_OBJECT_TYPE_UNKNOWN;
+
+ *num = n;
+ return (type);
+}
+
+/*
+ * Parses argv array and populates object_type and name.
+ * Program exits on failure.
+ */
+static void
+parse_argv(int argc, char *argv[], int cmd_num, nwam_object_type_t *object_type,
+ nwam_ncu_type_t *ncu_type, nwam_ncu_class_t *ncu_class, const char **name)
+{
+ int arg;
+ nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN;
+ uint64_t ncu = NWAM_NCU_TYPE_ANY;
+ uint64_t class = NWAM_NCU_CLASS_ANY;
+
+ /* check argv for option */
+ optind = 0;
+ while ((arg = getopt(argc, argv, "?p:c:x")) != EOF) {
+ switch (arg) {
+ case 'p':
+ type = nwam_string_to_object_type(optarg);
+ if (type == NWAM_OBJECT_TYPE_UNKNOWN)
+ die("Invalid profile-type: %s", optarg);
+ break;
+ case 'c':
+ if (nwam_value_string_get_uint64(NWAM_NCU_PROP_CLASS,
+ optarg, &class) != NWAM_SUCCESS) {
+ die("Invalid ncu-class: %s", optarg);
+ }
+ ncu = nwam_ncu_class_to_type(class);
+ if (ncu == NWAM_NCU_TYPE_ANY ||
+ ncu == NWAM_NCU_TYPE_UNKNOWN)
+ die("Invalid ncu-class: %s", optarg);
+ break;
+ case 'x':
+ /* -x is only for list */
+ if (cmd_num != CMD_LIST)
+ die("-x can only be used with 'list'");
+ extended_list = B_TRUE;
+ break;
+ case '?':
+ default:
+ die_usage(cmd_num);
+ }
+ }
+
+ if (ncu != NWAM_NCU_TYPE_ANY) {
+ /* If -c is given, -p must be NCU. If unspecified, assume NCU */
+ if (type != NWAM_OBJECT_TYPE_UNKNOWN &&
+ type != NWAM_OBJECT_TYPE_NCU)
+ die("'-c <ncu-class>' can only be used for ncu");
+
+ type = NWAM_OBJECT_TYPE_NCU;
+ }
+
+ /* name is mandatory for enable and disable, but not for list */
+ if (optind == (argc-1))
+ *name = argv[optind];
+ else if (argc != optind)
+ die("too many profile names given");
+ else if (cmd_num != CMD_LIST)
+ die("no profile name given");
+
+ /*
+ * No need to determine type for list.
+ * If -p is not given for enable or disable, then determine type.
+ */
+ if (cmd_num != CMD_LIST && type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ int num = 0;
+
+ type = determine_object_type(*name, &num);
+ if (num == 0) {
+ die("no profile matched '%s'", *name);
+ } else if (num > 1) {
+ die("more than one profile matched '%s' - use "
+ "'-p <profile-type>' to specify a profile type.",
+ *name);
+ }
+ }
+
+ *object_type = type;
+ *ncu_type = ncu;
+ *ncu_class = class;
+}
+
+/* Enables/Disables profiles depending on boolean */
+static nwam_error_t
+loc_action(const char *name, boolean_t enable, char **realnamep)
+{
+ nwam_loc_handle_t loch;
+ nwam_error_t ret;
+
+ if ((ret = nwam_loc_read(name, 0, &loch)) != NWAM_SUCCESS)
+ return (ret);
+
+ if (enable)
+ ret = nwam_loc_enable(loch);
+ else
+ ret = nwam_loc_disable(loch);
+
+ (void) nwam_loc_get_name(loch, realnamep);
+ nwam_loc_free(loch);
+ return (ret);
+}
+
+static nwam_error_t
+enm_action(const char *name, boolean_t enable, char **realnamep)
+{
+ nwam_enm_handle_t enmh;
+ nwam_error_t ret;
+
+ if ((ret = nwam_enm_read(name, 0, &enmh)) != NWAM_SUCCESS)
+ return (ret);
+
+ if (enable)
+ ret = nwam_enm_enable(enmh);
+ else
+ ret = nwam_enm_disable(enmh);
+
+ (void) nwam_enm_get_name(enmh, realnamep);
+ nwam_enm_free(enmh);
+ return (ret);
+}
+
+static nwam_error_t
+ncu_action(const char *name, nwam_ncp_handle_t ncph, nwam_ncu_type_t type,
+ boolean_t enable, char **realnamep)
+{
+ nwam_ncu_handle_t ncuh;
+ nwam_error_t ret;
+ boolean_t retrieved_ncph = B_FALSE;
+
+ if (ncph == NULL) {
+ if ((ncph = determine_active_ncp()) == NULL)
+ return (NWAM_ENTITY_NOT_FOUND);
+ retrieved_ncph = B_TRUE;
+ }
+
+ ret = nwam_ncu_read(ncph, name, type, 0, &ncuh);
+ switch (ret) {
+ case NWAM_SUCCESS:
+ if (enable)
+ ret = nwam_ncu_enable(ncuh);
+ else
+ ret = nwam_ncu_disable(ncuh);
+ (void) nwam_ncu_get_name(ncuh, realnamep);
+ nwam_ncu_free(ncuh);
+ break;
+ case NWAM_ENTITY_MULTIPLE_VALUES:
+ /* Call ncu_action() for link and interface types */
+ ret = ncu_action(name, ncph, NWAM_NCU_TYPE_LINK, enable,
+ realnamep);
+ if (ret != NWAM_SUCCESS)
+ break;
+
+ ret = ncu_action(name, ncph, NWAM_NCU_TYPE_INTERFACE, enable,
+ realnamep);
+ break;
+ }
+ if (retrieved_ncph)
+ nwam_ncp_free(ncph);
+
+ return (ret);
+}
+
+/*
+ * If more than one type of profile with the same name, return error.
+ * In such situations, the -p option must be used.
+ * If a location is enabled when a different one is already enabled, then
+ * that location is disabled automatically by nwamd.
+ */
+static void
+enable_func(int argc, char *argv[])
+{
+ nwam_error_t ret;
+ nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN;
+ nwam_ncu_type_t ncu_type = NWAM_NCU_TYPE_ANY;
+ nwam_ncu_class_t ncu_class = NWAM_NCU_CLASS_ANY;
+ const char *name;
+ char *realname = NULL;
+
+ /* parse_argv() returns only on success */
+ parse_argv(argc, argv, CMD_ENABLE, &type, &ncu_type, &ncu_class, &name);
+
+ /*
+ * NCPs and Locations don't need to disable the currently active
+ * profile - nwamd automatically switches to the new active profile.
+ * and will disable it if necessary.
+ */
+
+ /* activate given profile */
+ switch (type) {
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = loc_action(name, B_TRUE, &realname);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = enm_action(name, B_TRUE, &realname);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ {
+ nwam_ncp_handle_t ncph;
+
+ if ((ret = nwam_ncp_read(name, 0, &ncph)) != NWAM_SUCCESS)
+ break;
+
+ ret = nwam_ncp_enable(ncph);
+ (void) nwam_ncp_get_name(ncph, &realname);
+ nwam_ncp_free(ncph);
+ break;
+ }
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = ncu_action(name, NULL, ncu_type, B_TRUE, &realname);
+ break;
+ }
+
+ switch (ret) {
+ case NWAM_SUCCESS:
+ (void) printf(gettext("Enabling %s '%s'\n"),
+ nwam_object_type_to_string(type),
+ realname != NULL ? realname : name);
+ break;
+ case NWAM_ENTITY_NOT_MANUAL:
+ die("Only profiles with manual activation-mode can be enabled");
+ break;
+ default:
+ die_nwamerr(ret, "Could not enable %s '%s'",
+ nwam_object_type_to_string(type),
+ realname != NULL ? realname : name);
+ }
+ free(realname);
+}
+
+/*
+ * Disables a given profile. Similar to enable, the -p option must be used
+ * if more than one type of profile is matched by the given name.
+ */
+static void
+disable_func(int argc, char *argv[])
+{
+ nwam_error_t ret;
+ nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN;
+ nwam_ncu_type_t ncu_type = NWAM_NCU_TYPE_ANY;
+ nwam_ncu_class_t ncu_class = NWAM_NCU_CLASS_ANY;
+ const char *name;
+ char *realname = NULL;
+
+ /* parse_argv() returns only on success */
+ parse_argv(argc, argv, CMD_DISABLE, &type, &ncu_type, &ncu_class,
+ &name);
+
+ /* deactivate the given profile */
+ switch (type) {
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = loc_action(name, B_FALSE, &realname);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = enm_action(name, B_FALSE, &realname);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = ncu_action(name, NULL, ncu_type, B_FALSE, &realname);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ die("ncp's cannot be disabled. Enable a different ncp to "
+ "switch to that ncp");
+ }
+
+ switch (ret) {
+ case NWAM_SUCCESS:
+ (void) printf(gettext("Disabling %s '%s'\n"),
+ nwam_object_type_to_string(type),
+ realname != NULL ? realname : name);
+ break;
+ case NWAM_ENTITY_NOT_MANUAL:
+ die("Only profiles with manual activation-mode can be "
+ "disabled");
+ break;
+ default:
+ die_nwamerr(ret, "Could not disable %s '%s'",
+ nwam_object_type_to_string(type),
+ realname != NULL ? realname : name);
+ }
+ free(realname);
+}
+
+/* prints each column */
+static boolean_t
+print_list_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+ profile_entry_t *pent = ofarg->ofmt_cbarg;
+
+ switch (ofarg->ofmt_id) {
+ case LIST_TYPE:
+ /* ncu:ip or ncu:phys for NCUs; ncp, loc, enm for others */
+ if (pent->p_type == NWAM_OBJECT_TYPE_NCU) {
+ const char *class;
+ if (nwam_uint64_get_value_string(NWAM_NCU_PROP_CLASS,
+ pent->p_ncu_class, &class) != NWAM_SUCCESS)
+ class = ""; /* empty */
+ (void) snprintf(buf, bufsize, "%s:%s",
+ nwam_object_type_to_string(pent->p_type), class);
+ } else {
+ (void) strlcpy(buf,
+ nwam_object_type_to_string(pent->p_type), bufsize);
+ }
+ break;
+ case LIST_PROFILE:
+ (void) strlcpy(buf, pent->p_name, bufsize);
+ break;
+ case LIST_STATE:
+ (void) strlcpy(buf, nwam_state_to_string(pent->p_state),
+ bufsize);
+ break;
+ case LIST_AUXSTATE:
+ (void) strlcpy(buf,
+ nwam_aux_state_to_string(pent->p_aux_state), bufsize);
+ break;
+ default:
+ die("invalid print_list_cb() input: %d", ofarg->ofmt_id);
+ break;
+ }
+ return (B_TRUE);
+}
+
+/* returns the state and auxilliary state of the object */
+static nwam_state_t
+determine_object_state(nwam_object_type_t type, void *handle,
+ nwam_aux_state_t *aux_statep)
+{
+ nwam_state_t state;
+ nwam_aux_state_t astate;
+ nwam_error_t ret;
+
+ switch (type) {
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_state(handle, &state, &astate);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_state(handle, &state, &astate);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ ret = nwam_ncp_get_state(handle, &state, &astate);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_state(handle, &state, &astate);
+ break;
+ default:
+ /* NOTREACHED */
+ break;
+ }
+
+ if (ret == NWAM_PERMISSION_DENIED) {
+ die_nwamerr(ret, "could not get object state");
+ } else if (ret != NWAM_SUCCESS) {
+ state = NWAM_STATE_UNINITIALIZED;
+ astate = NWAM_AUX_STATE_UNINITIALIZED;
+ }
+
+ if (aux_statep != NULL)
+ *aux_statep = astate;
+ return (state);
+}
+
+/* populate profile_entry_t with values for object with given handle */
+static int
+add_to_profile_entry(nwam_object_type_t type, void *handle,
+ profile_entry_t *pent)
+{
+ char *name;
+ nwam_error_t ret;
+
+ pent->p_type = type;
+ if (type == NWAM_OBJECT_TYPE_NCU) {
+ nwam_ncu_class_t class;
+ if ((ret = nwam_ncu_get_ncu_class(handle, &class))
+ != NWAM_SUCCESS)
+ return (ret);
+ pent->p_ncu_class = class;
+ } else {
+ pent->p_ncu_class = -1;
+ }
+
+ switch (type) {
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_name(handle, &name);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_name(handle, &name);
+ break;
+ case NWAM_OBJECT_TYPE_NCP:
+ ret = nwam_ncp_get_name(handle, &name);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_name(handle, &name);
+ break;
+ default:
+ /* NOTREACHED */
+ break;
+ }
+ if (ret != NWAM_SUCCESS) {
+ return (ret);
+ }
+ (void) strlcpy(pent->p_name, name, sizeof (pent->p_name));
+ free(name);
+
+ pent->p_state = determine_object_state(type, handle,
+ &pent->p_aux_state);
+
+ return (NWAM_SUCCESS);
+}
+
+/* callback functions used by walk */
+
+static int
+list_ncu_cb(nwam_ncu_handle_t ncuh, void *arg)
+{
+ ofmt_handle_t ofmt = arg;
+ profile_entry_t pent;
+ nwam_error_t ret;
+
+ bzero(&pent, sizeof (profile_entry_t));
+ ret = add_to_profile_entry(NWAM_OBJECT_TYPE_NCU, ncuh, &pent);
+ if (ret != NWAM_SUCCESS)
+ die_nwamerr(ret, "could not add ncu to list");
+ ofmt_print(ofmt, &pent);
+ return (0);
+}
+
+static int
+list_ncp_cb(nwam_ncp_handle_t ncph, void *arg)
+{
+ ofmt_handle_t ofmt = arg;
+ profile_entry_t pent;
+ nwam_error_t ret;
+ nwam_state_t state;
+
+ bzero(&pent, sizeof (profile_entry_t));
+ ret = add_to_profile_entry(NWAM_OBJECT_TYPE_NCP, ncph, &pent);
+ if (ret != NWAM_SUCCESS)
+ die_nwamerr(ret, "could not add ncp to list");
+ ofmt_print(ofmt, &pent);
+
+ state = determine_object_state(NWAM_OBJECT_TYPE_NCP, ncph, NULL);
+ if (state == NWAM_STATE_ONLINE) {
+ (void) nwam_ncp_walk_ncus(ncph, list_ncu_cb, ofmt,
+ NWAM_FLAG_NCU_TYPE_ALL, NULL);
+ }
+ return (0);
+}
+
+static int
+list_loc_cb(nwam_loc_handle_t loch, void *arg)
+{
+ ofmt_handle_t ofmt = arg;
+ profile_entry_t pent;
+ nwam_error_t ret;
+
+ bzero(&pent, sizeof (profile_entry_t));
+ ret = add_to_profile_entry(NWAM_OBJECT_TYPE_LOC, loch, &pent);
+ if (ret != NWAM_SUCCESS)
+ die_nwamerr(ret, "could not add loc to list");
+ ofmt_print(ofmt, &pent);
+ return (0);
+}
+
+static int
+list_enm_cb(nwam_enm_handle_t enmh, void *arg)
+{
+ ofmt_handle_t ofmt = arg;
+ profile_entry_t pent;
+ nwam_error_t ret;
+
+ bzero(&pent, sizeof (profile_entry_t));
+ ret = add_to_profile_entry(NWAM_OBJECT_TYPE_ENM, enmh, &pent);
+ if (ret != NWAM_SUCCESS)
+ die_nwamerr(ret, "could not add enm to list");
+ ofmt_print(ofmt, &pent);
+ return (0);
+}
+
+/*
+ * lists all profiles and their state
+ */
+static void
+list_func(int argc, char *argv[])
+{
+ nwam_error_t ret = NWAM_SUCCESS;
+ nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN;
+ nwam_ncu_type_t ncu_type = NWAM_NCU_TYPE_ANY;
+ nwam_ncu_class_t ncu_class = NWAM_NCU_CLASS_ANY;
+ char *name = NULL;
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ char *default_fields = "type,profile,state";
+ char *extended_fields = "type,profile,state,auxiliary state";
+ char *fields = NULL;
+
+ /* parse_argv() returns only on success */
+ parse_argv(argc, argv, CMD_LIST, &type, &ncu_type, &ncu_class,
+ (const char **)&name);
+
+ if (extended_list)
+ fields = extended_fields;
+ else
+ fields = default_fields;
+ oferr = ofmt_open(fields, list_fields, 0, 0, &ofmt);
+ if (oferr != OFMT_SUCCESS) {
+ char buf[OFMT_BUFSIZE];
+ (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
+ die("ofmt_open() failed: %s", buf);
+ }
+
+ /* object-name given in command-line */
+ if (name != NULL) {
+ boolean_t found = B_FALSE;
+
+ /*
+ * If objects with different types have the same name
+ * (type = UNKNOWN), then try to open handle for each object
+ * and print if successful.
+ */
+ if (type == NWAM_OBJECT_TYPE_NCP ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ nwam_ncp_handle_t ncph;
+ if (nwam_ncp_read(name, 0, &ncph) == NWAM_SUCCESS) {
+ found = B_TRUE;
+ (void) list_ncp_cb(ncph, ofmt);
+ nwam_ncp_free(ncph);
+ }
+ }
+ if (type == NWAM_OBJECT_TYPE_NCU ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ nwam_ncp_handle_t ncph;
+ nwam_ncu_handle_t ncuh;
+
+ if ((ncph = determine_active_ncp()) != NULL) {
+ ret = nwam_ncu_read(ncph, name, ncu_type, 0,
+ &ncuh);
+ if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
+ found = B_TRUE;
+ if (nwam_ncu_read(ncph, name,
+ NWAM_NCU_TYPE_LINK, 0, &ncuh)
+ == NWAM_SUCCESS) {
+ (void) list_ncu_cb(ncuh, ofmt);
+ nwam_ncu_free(ncuh);
+ }
+ if (nwam_ncu_read(ncph, name,
+ NWAM_NCU_TYPE_INTERFACE, 0, &ncuh)
+ == NWAM_SUCCESS) {
+ (void) list_ncu_cb(ncuh, ofmt);
+ nwam_ncu_free(ncuh);
+ }
+ } else if (ret == NWAM_SUCCESS) {
+ found = B_TRUE;
+ (void) list_ncu_cb(ncuh, ofmt);
+ nwam_ncu_free(ncuh);
+ }
+ nwam_ncp_free(ncph);
+ }
+ }
+ if (type == NWAM_OBJECT_TYPE_LOC ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ nwam_loc_handle_t loch;
+ if (nwam_loc_read(name, 0, &loch) == NWAM_SUCCESS) {
+ found = B_TRUE;
+ (void) list_loc_cb(loch, ofmt);
+ nwam_loc_free(loch);
+ }
+ }
+ if (type == NWAM_OBJECT_TYPE_ENM ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ nwam_enm_handle_t enmh;
+ if (nwam_enm_read(name, 0, &enmh) == NWAM_SUCCESS) {
+ found = B_TRUE;
+ (void) list_enm_cb(enmh, ofmt);
+ nwam_enm_free(enmh);
+ }
+ }
+ /* If at least object is found, don't return error */
+ if (found)
+ ret = NWAM_SUCCESS;
+ else
+ ret = NWAM_ENTITY_NOT_FOUND;
+ }
+
+ /* object-name not given in command-line */
+ if (name == NULL) {
+ /*
+ * If type given (type != UNKNOWN), just walk objects in that
+ * type. Otherwise, walk all ncp, ncu, loc and enm.
+ */
+ if (type == NWAM_OBJECT_TYPE_NCP ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ ret = nwam_walk_ncps(list_ncp_cb, ofmt, 0, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+ /* no UNKNOWN for NCUs. They walked with active NCP above */
+ if (type == NWAM_OBJECT_TYPE_NCU) {
+ nwam_ncp_handle_t ncph;
+ if ((ncph = determine_active_ncp()) != NULL) {
+ ret = nwam_ncp_walk_ncus(ncph, list_ncu_cb,
+ ofmt, nwam_ncu_class_to_flag(ncu_class),
+ NULL);
+ nwam_ncp_free(ncph);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+ }
+ if (type == NWAM_OBJECT_TYPE_LOC ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ ret = nwam_walk_locs(list_loc_cb, ofmt,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+ if (type == NWAM_OBJECT_TYPE_ENM ||
+ type == NWAM_OBJECT_TYPE_UNKNOWN) {
+ ret = nwam_walk_enms(list_enm_cb, ofmt,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+ }
+
+done:
+ ofmt_close(ofmt);
+ if (ret == NWAM_ENTITY_NOT_FOUND && name != NULL)
+ die("no profile matched '%s'", name);
+ else if (ret != NWAM_SUCCESS)
+ die_nwamerr(ret, "list failed during walk");
+}
+
+/*
+ * Print NWAM events.
+ */
+static void
+eventhandler(nwam_event_t event)
+{
+ char description[DESCRIPTION_WIDTH];
+ char statestr[DESCRIPTION_WIDTH];
+ char objstr[DESCRIPTION_WIDTH];
+ char *object = NULL;
+ const char *action = NULL;
+ char *state = NULL;
+ boolean_t display = B_TRUE;
+ int i;
+ nwam_wlan_t *wlans;
+
+ (void) strlcpy(description, "-", sizeof (description));
+
+ switch (event->nwe_type) {
+ case NWAM_EVENT_TYPE_OBJECT_ACTION:
+ action = nwam_action_to_string
+ (event->nwe_data.nwe_object_action.nwe_action);
+ (void) snprintf(objstr, sizeof (objstr), "%s %s",
+ nwam_object_type_to_string
+ (event->nwe_data.nwe_object_action.nwe_object_type),
+ event->nwe_data.nwe_object_action.nwe_name);
+ object = objstr;
+ break;
+
+ case NWAM_EVENT_TYPE_OBJECT_STATE:
+ (void) snprintf(statestr, sizeof (statestr), "%s, %s",
+ nwam_state_to_string
+ (event->nwe_data.nwe_object_state.nwe_state),
+ nwam_aux_state_to_string
+ (event->nwe_data.nwe_object_state.nwe_aux_state));
+ state = statestr;
+
+ (void) snprintf(objstr, sizeof (objstr), "%s %s",
+ nwam_object_type_to_string
+ (event->nwe_data.nwe_object_state.nwe_object_type),
+ event->nwe_data.nwe_object_state.nwe_name);
+ object = objstr;
+ break;
+
+ case NWAM_EVENT_TYPE_PRIORITY_GROUP:
+ (void) snprintf(description, DESCRIPTION_WIDTH,
+ "priority-group: %d",
+ event->nwe_data.nwe_priority_group_info.nwe_priority);
+ break;
+
+ case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
+ (void) printf("%-*s \n", EVENT_WIDTH,
+ nwam_event_type_to_string(event->nwe_type));
+ wlans = event->nwe_data.nwe_wlan_info.nwe_wlans;
+ for (i = 0;
+ i < event->nwe_data.nwe_wlan_info.nwe_num_wlans;
+ i++) {
+ (void) snprintf(description, DESCRIPTION_WIDTH,
+ "%d: %c%c ESSID %s BSSID %s", i + 1,
+ wlans[i].nww_selected ? 'S' : '-',
+ wlans[i].nww_connected ? 'C' : '-',
+ wlans[i].nww_essid, wlans[i].nww_bssid);
+ (void) printf("%-*s %-*s\n", EVENT_WIDTH, "-",
+ DESCRIPTION_WIDTH, description);
+ }
+ display = B_FALSE;
+ break;
+
+ case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
+ (void) printf("%-*s \n", EVENT_WIDTH,
+ nwam_event_type_to_string(event->nwe_type));
+ display = B_FALSE;
+ break;
+
+ case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
+ (void) printf("%-*s \n", EVENT_WIDTH,
+ nwam_event_type_to_string(event->nwe_type));
+ display = B_FALSE;
+ break;
+
+ case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
+ (void) snprintf(description, DESCRIPTION_WIDTH,
+ gettext("connect to WLAN ESSID %s, BSSID %s %s"),
+ event->nwe_data.nwe_wlan_info.nwe_wlans[0].nww_essid,
+ event->nwe_data.nwe_wlan_info.nwe_wlans[0].nww_bssid,
+ event->nwe_data.nwe_wlan_info.nwe_connected ?
+ "succeeded" : "failed");
+ break;
+
+ case NWAM_EVENT_TYPE_INFO:
+ (void) snprintf(description, sizeof (description),
+ "%s", event->nwe_data.nwe_info.nwe_message);
+ break;
+
+ case NWAM_EVENT_TYPE_IF_ACTION:
+ action = nwam_action_to_string
+ (event->nwe_data.nwe_if_action.nwe_action);
+ object = event->nwe_data.nwe_if_action.nwe_name;
+ break;
+
+ case NWAM_EVENT_TYPE_IF_STATE:
+ object = event->nwe_data.nwe_if_state.nwe_name;
+ if (event->nwe_data.nwe_if_state.nwe_addr_valid) {
+ struct sockaddr_storage *address =
+ &(event->nwe_data.nwe_if_state.nwe_addr);
+ struct sockaddr_in *v4addr;
+ struct sockaddr_in6 *v6addr;
+ char addrstr[NWAM_MAX_VALUE_LEN];
+
+ switch (address->ss_family) {
+ case AF_INET:
+ v4addr = (struct sockaddr_in *)address;
+ (void) inet_ntop(AF_INET, &v4addr->sin_addr,
+ addrstr, sizeof (addrstr));
+ break;
+ case AF_INET6:
+ v6addr = (struct sockaddr_in6 *)address;
+ (void) inet_ntop(AF_INET6, &v6addr->sin6_addr,
+ addrstr, sizeof (addrstr));
+ break;
+ }
+ (void) snprintf(statestr, sizeof (statestr),
+ "index %d flags 0x%x address %s",
+ event->nwe_data.nwe_if_state.nwe_index,
+ event->nwe_data.nwe_if_state.nwe_flags, addrstr);
+ } else {
+ (void) snprintf(statestr, sizeof (statestr),
+ "(%d) flags %x",
+ event->nwe_data.nwe_if_state.nwe_index,
+ event->nwe_data.nwe_if_state.nwe_flags);
+ }
+ state = statestr;
+ break;
+
+ case NWAM_EVENT_TYPE_LINK_ACTION:
+ action = nwam_action_to_string
+ (event->nwe_data.nwe_link_action.nwe_action);
+ object = event->nwe_data.nwe_link_action.nwe_name;
+ break;
+
+ case NWAM_EVENT_TYPE_LINK_STATE:
+ state = event->nwe_data.nwe_link_state.nwe_link_up ?
+ "up" : "down";
+ object = event->nwe_data.nwe_link_state.nwe_name;
+ break;
+ }
+
+ if (object != NULL && action != NULL) {
+ (void) snprintf(description, sizeof (description),
+ "%s -> action %s", object, action);
+ } else if (object != NULL && state != NULL) {
+ (void) snprintf(description, sizeof (description),
+ "%s -> state %s", object, state);
+ }
+
+ if (display) {
+ (void) printf("%-*s %-*s\n", EVENT_WIDTH,
+ nwam_event_type_to_string(event->nwe_type),
+ DESCRIPTION_WIDTH,
+ description);
+ }
+}
+
+/*
+ * listens for events and displays them via the eventhandler() function above.
+ */
+/* ARGSUSED */
+static void
+show_events_func(int argc, char *argv[])
+{
+ nwam_error_t err;
+ nwam_event_t event;
+
+ err = nwam_events_init();
+
+ if (err != NWAM_SUCCESS)
+ die_nwamerr(err, "could not bind to receive events");
+
+ /* print header */
+ (void) printf("%-*s %-*s\n", EVENT_WIDTH, "EVENT",
+ DESCRIPTION_WIDTH, "DESCRIPTION");
+
+ do {
+ /*
+ * Needed for stdout redirection to ensure event output is
+ * regularly flushed to file.
+ */
+ (void) fflush(stdout);
+ err = nwam_event_wait(&event);
+ if (err == NWAM_SUCCESS) {
+ eventhandler(event);
+ nwam_event_free(event);
+ }
+ } while (err == NWAM_SUCCESS);
+ die_nwamerr(err, "event handling stopped");
+}
+
+/* May need to convert case-insensitive link name match to case-sensitive one */
+static nwam_error_t
+name_to_linkname(char *name, char **linknamep)
+{
+ nwam_error_t err;
+ nwam_ncp_handle_t ncph = NULL;
+ nwam_ncu_handle_t ncuh = NULL;
+
+ if ((ncph = determine_active_ncp()) == NULL)
+ return (NWAM_ENTITY_NOT_FOUND);
+
+ err = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0, &ncuh);
+ if (err == NWAM_SUCCESS)
+ err = nwam_ncu_get_name(ncuh, linknamep);
+
+ nwam_ncp_free(ncph);
+ nwam_ncu_free(ncuh);
+ return (err);
+}
+
+static void
+scan_wifi_func(int argc, char *argv[])
+{
+ nwam_error_t err;
+ char *linkname = NULL;
+
+ if (argc != 1)
+ die_usage(CMD_SCAN_WIFI);
+
+ if ((err = name_to_linkname(argv[0], &linkname)) != NWAM_SUCCESS)
+ die_nwamerr(err, "scan request failed for %s", argv[0]);
+
+ err = nwam_wlan_scan(linkname);
+
+ if (err != NWAM_SUCCESS)
+ die_nwamerr(err, "scan request failed for %s", linkname);
+
+ free(linkname);
+}
+
+static void
+select_wifi_func(int argc, char *argv[])
+{
+ nwam_error_t err;
+ char *linkname = NULL;
+ uint_t i, choice, num_wlans = 0;
+ uint32_t security_mode;
+ boolean_t have_key = B_FALSE;
+ nwam_wlan_t *wlans = NULL;
+ char choicestr[NWAM_MAX_VALUE_LEN];
+ char modestr[NWAM_MAX_VALUE_LEN];
+ char essid[NWAM_MAX_VALUE_LEN];
+ char bssid[NWAM_MAX_VALUE_LEN];
+
+ if (argc != 1)
+ die_usage(CMD_SELECT_WIFI);
+
+ if ((err = name_to_linkname(argv[0], &linkname)) != NWAM_SUCCESS) {
+ die_nwamerr(err, "could not retrieve scan results for %s",
+ argv[0]);
+ }
+ err = nwam_wlan_get_scan_results(linkname, &num_wlans, &wlans);
+
+ if (err != NWAM_SUCCESS) {
+ die_nwamerr(err, "could not retrieve scan results for %s",
+ linkname);
+ }
+ bssid[0] = '\0';
+
+ /* Loop until valid selection made */
+ for (;;) {
+ (void) printf("\n");
+ /* Display WLAN choices for user to select from */
+ for (i = 0; i < num_wlans; i++) {
+ (void) printf("%d: ESSID %s BSSID %s\n",
+ i + 1, wlans[i].nww_essid, wlans[i].nww_bssid);
+ }
+ (void) printf(gettext("%d: Other\n"), i + 1);
+
+ (void) printf(gettext("\nChoose WLAN to connect to [1-%d]: "),
+ i + 1);
+
+ if (fgets(choicestr, sizeof (choicestr), stdin) != NULL &&
+ (choice = atoi(choicestr)) >= 1 && choice <= (i + 1))
+ break;
+ }
+
+ if (choice == i + 1) {
+ nwam_known_wlan_handle_t kwh = NULL;
+ nwam_value_t keynameval = NULL;
+
+ /* "Other" was selected - ESSID/secmode must be specified. */
+ do {
+ (void) printf(gettext("\nEnter WLAN name: "));
+ while (fgets(essid, sizeof (essid), stdin) == NULL) {}
+ essid[strlen(essid) - 1] = '\0';
+ } while (strspn(essid, " \t") == strlen(essid));
+
+ for (;;) {
+ (void) printf(gettext("1: None\n"));
+ (void) printf(gettext("2: WEP\n"));
+ (void) printf(gettext("3: WPA\n"));
+ (void) printf(gettext("Enter security mode: "));
+ if (fgets(modestr, sizeof (choicestr), stdin) != NULL &&
+ (security_mode = atoi(modestr)) >= 1 &&
+ security_mode <= 3)
+ break;
+ }
+
+ /*
+ * We have to determine if we have a key for this ESSID from
+ * the known WLAN list, since we cannot determine this from
+ * the scan results.
+ */
+ if (nwam_known_wlan_read(essid, 0, &kwh) == NWAM_SUCCESS &&
+ nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYNAME, &keynameval) == NWAM_SUCCESS)
+ have_key = B_TRUE;
+ else
+ have_key = B_FALSE;
+
+ nwam_value_free(keynameval);
+ nwam_known_wlan_free(kwh);
+ } else {
+ (void) strlcpy(essid, wlans[choice - 1].nww_essid,
+ sizeof (essid));
+ (void) strlcpy(bssid, wlans[choice - 1].nww_bssid,
+ sizeof (bssid));
+ security_mode = wlans[choice - 1].nww_security_mode;
+ have_key = wlans[choice - 1].nww_have_key;
+ }
+
+ if (security_mode != DLADM_WLAN_SECMODE_NONE && !have_key) {
+ uint_t keyslot = 1;
+ char key[NWAM_MAX_VALUE_LEN];
+ char slotstr[NWAM_MAX_VALUE_LEN];
+
+ do {
+ (void) printf(gettext("\nEnter WLAN key for "
+ "ESSID %s: "), essid);
+ while (fgets(key, sizeof (key), stdin) == NULL) {}
+ key[strlen(key) - 1] = '\0';
+ } while (strspn(key, " \t") == strlen(key));
+
+ if (security_mode == DLADM_WLAN_SECMODE_WEP) {
+ for (;;) {
+ (void) printf(
+ gettext("\nEnter key slot [1-4]: "));
+ if (fgets(slotstr, sizeof (slotstr), stdin)
+ != NULL && (keyslot = atoi(slotstr)) >= 1 &&
+ keyslot <= 4)
+ break;
+ }
+ }
+
+ err = nwam_wlan_set_key(linkname, essid, NULL, security_mode,
+ keyslot, key);
+ if (err != NWAM_SUCCESS)
+ die_nwamerr(err, "could not set WiFi key");
+ }
+ err = nwam_wlan_select(linkname, essid, bssid[0] != '\0' ? bssid : NULL,
+ security_mode, B_TRUE);
+ if (err != NWAM_SUCCESS)
+ die_nwamerr(err, "could not select WLAN %s", essid);
+ free(wlans);
+ free(linkname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ char *state;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if ((execname = strrchr(argv[0], '/')) == NULL)
+ execname = argv[0];
+ else
+ execname++;
+
+ if (argc < 2) {
+ usage(B_FALSE);
+ exit(EXIT_FAILURE);
+ }
+
+ for (i = CMD_MIN; i <= CMD_MAX; i++) {
+ if (strcmp(argv[1], cmd_to_str(i)) == 0) {
+ if (cmdtab[i].cmd_needs_nwamd) {
+ state = smf_get_state(NWAM_FMRI);
+ if (state == NULL || strcmp(state,
+ SCF_STATE_STRING_ONLINE) != 0) {
+ free(state);
+ die("enable '%s' to use '%s %s'",
+ NWAM_FMRI, execname,
+ cmd_to_str(cmdtab[i].cmd_num));
+ }
+ free(state);
+ }
+
+ cmdtab[i].cmd_handler(argc - 2, &(argv[2]));
+
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
+ execname, argv[1]);
+ usage(B_FALSE);
+
+ return (1);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.xcl b/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.xcl
new file mode 100644
index 0000000000..3a68a077ae
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.xcl
@@ -0,0 +1,95 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+msgid ""
+msgid "\t%s\n"
+msgid "\t\t%s\n\n"
+msgid "\n"
+msgid " %s '%s'"
+msgid " %s '%s'\n"
+msgid "%-*s %-*s %-*s%s\n"
+msgid "%-*s %-*s\n"
+msgid "%-*s \n"
+msgid "%d"
+msgid "%d:"
+msgid "%d: ESSID %s BSSID %s"
+msgid "%d: ESSID %s BSSID %s\n"
+msgid "%s %s"
+msgid "%s -> action %s"
+msgid "%s -> state %s"
+msgid "%s"
+msgid "%s, %s"
+msgid "%s:"
+msgid "%s: "
+msgid "%s: %s\n"
+msgid "%s:%s"
+msgid "'%s %s'"
+msgid "'%s'"
+msgid "(%d) flags %x"
+msgid ","
+msgid "-"
+msgid "-c"
+msgid "-p"
+msgid "-x"
+msgid "?"
+msgid "?p:c:x"
+msgid "AUXILLARY STATE"
+msgid "BSSID"
+msgid "DESCRIPTION"
+msgid "ESSID"
+msgid "EVENT"
+msgid "PROFILE"
+msgid "STATE"
+msgid "TYPE"
+msgid "WEP"
+msgid "WLAN"
+msgid "WPA"
+msgid "WiFi"
+msgid "activation-mode"
+msgid "disable"
+msgid "enable"
+msgid "enm"
+msgid "help"
+msgid "index %d flags 0x%x address %s"
+msgid "interface"
+msgid "ip"
+msgid "key slot"
+msgid "link"
+msgid "linkname"
+msgid "list"
+msgid "loc"
+msgid "manual"
+msgid "ncp"
+msgid "ncu"
+msgid "ncu-class"
+msgid "object-name"
+msgid "ofmt_open"
+msgid "phys"
+msgid "print_list_cb"
+msgid "priority-group: %d"
+msgid "profile-type"
+msgid "scan-wifi"
+msgid "select-wifi"
+msgid "show-events"
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile
new file mode 100644
index 0000000000..7f32e692ee
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/Makefile
@@ -0,0 +1,70 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/cmd-inet/usr.sbin/nwamcfg/Makefile
+
+PROG= nwamcfg
+OBJS= nwamcfg.o nwamcfg_lex.o nwamcfg_grammar.tab.o
+
+include ../../../Makefile.cmd
+
+XGETFLAGS += -a -x $(PROG).xcl
+LFLAGS = -t
+LDLIBS += -ll -ltecla -lnwam -lumem
+YFLAGS += -d -b nwamcfg_grammar
+CLEANFILES += nwamcfg_lex.c nwamcfg_grammar.tab.c nwamcfg_grammar.tab.h
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTUSRSBINPROG)
+
+nwamcfg_lex.c: nwamcfg_lex.l nwamcfg_grammar.tab.h nwamcfg.h
+ $(LEX) $(LFLAGS) nwamcfg_lex.l > $@
+
+nwamcfg_grammar.tab.h nwamcfg_grammar.tab.c: nwamcfg_grammar.y nwamcfg.h
+ $(YACC) $(YFLAGS) nwamcfg_grammar.y
+
+nwamcfg_lex.o nwamcfg_grammar.tab.o := CCVERBOSE =
+
+check: $(PROG).c $(PROG).h
+ $(CSTYLE) -pP $(PROG).c $(PROG).h
+ $(HDRCHK) $(PROG).h
+
+clean:
+ $(RM) $(OBJS) $(CLEANFILES)
+
+#
+# We don't do lint of lex- and yacc- generated source files
+#
+lint:
+ $(LINT.c) -c $(PROG).c $(LDLIBS)
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c
new file mode 100644
index 0000000000..a4cc1212de
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c
@@ -0,0 +1,4332 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * nwamcfg is a lex/yacc based command interpreter used to manage network
+ * configurations. The lexer (see nwamcfg_lex.l) builds up tokens, which
+ * the grammar (see nwamcfg_grammar.y) builds up into commands, some of
+ * which takes resources and/or properties as arguments.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libnwam.h>
+#include <libtecla.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "nwamcfg.h"
+
+#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
+#endif
+
+struct help {
+ uint_t cmd_num;
+ const char *cmd_name;
+ const char *cmd_usage;
+};
+
+extern int yyparse(void);
+extern int lex_lineno;
+
+#define MAX_LINE_LEN 1024
+#define MAX_CMD_HIST 1024
+
+/* usage of commands */
+#define SHELP_CANCEL "cancel"
+#define SHELP_CLEAR "clear <prop-name>"
+#define SHELP_COMMIT "commit"
+#define SHELP_CREATE "create [-t <template>] <object-type> [<class>] " \
+ "<object-name>"
+#define SHELP_DESTROY "destroy {-a | <object-type> [<class>] <object-name>}"
+#define SHELP_END "end"
+#define SHELP_EXIT "exit"
+#define SHELP_EXPORT "export [-d] [-f <output-file>] " \
+ "[<object-type> [<class>] <object-name>]"
+#define SHELP_GET "get [-V] <prop-name>"
+#define SHELP_HELP "help [command-name]"
+#define SHELP_LIST "list [-a] [<object-type> [<class>] <object-name>]"
+#define SHELP_REVERT "revert"
+#define SHELP_SELECT "select <object-type> [<class>] <object-name>"
+#define SHELP_SET "set <prop-name>=<value1>[,<value2>...]"
+#define SHELP_VERIFY "verify"
+#define SHELP_WALK "walkprop [-a]"
+
+/*
+ * Scope Definitions:
+ * Locations, ENMs, NCPs and Known WLANs are one scope level below global (GBL).
+ * NCUs are one more level beneath the NCP scope.
+ * Because the commands in Locations/ENM/Known WLAN and NCP level are different,
+ * the scope are divided accordingly.
+ * GBL->LOC, GBL->ENM, GBL->WLAN or GBL->NCP->NCU
+ */
+#define NWAM_SCOPE_GBL 0
+#define NWAM_SCOPE_LOC 1
+#define NWAM_SCOPE_ENM 2
+#define NWAM_SCOPE_WLAN 3
+#define NWAM_SCOPE_NCP 4
+#define NWAM_SCOPE_NCU 5
+
+/* delimiter used for list of values */
+#define NWAM_VALUE_DELIMITER_CHAR ','
+#define NWAM_VALUE_DELIMITER_STR ","
+
+/* the max number of values for an enum used by some properties in libnwam */
+
+/*
+ * All arrays/tables are null-terminated, rather than defining the length of
+ * the array. When looping, check for NULL rather than using the size.
+ */
+
+static struct help helptab[] = {
+ { CMD_CANCEL, "cancel", SHELP_CANCEL },
+ { CMD_CLEAR, "clear", SHELP_CLEAR },
+ { CMD_COMMIT, "commit", SHELP_COMMIT },
+ { CMD_CREATE, "create", SHELP_CREATE },
+ { CMD_DESTROY, "destroy", SHELP_DESTROY },
+ { CMD_END, "end", SHELP_END },
+ { CMD_EXIT, "exit", SHELP_EXIT },
+ { CMD_EXPORT, "export", SHELP_EXPORT },
+ { CMD_GET, "get", SHELP_GET },
+ { CMD_HELP, "help", SHELP_HELP },
+ { CMD_LIST, "list", SHELP_LIST },
+ { CMD_REVERT, "revert", SHELP_REVERT },
+ { CMD_SELECT, "select", SHELP_SELECT },
+ { CMD_SET, "set", SHELP_SET },
+ { CMD_VERIFY, "verify", SHELP_VERIFY },
+ { CMD_WALKPROP, "walkprop", SHELP_WALK },
+ { 0, NULL, NULL }
+};
+
+/* These *must* match the order of the RT1_ define's from nwamcfg.h */
+static char *res1_types[] = {
+ "unknown",
+ "loc",
+ "ncp",
+ "enm",
+ "wlan",
+ NULL
+};
+
+/* These *must* match the order of the RT2_ define's from nwamcfg.h */
+static char *res2_types[] = {
+ "unknown",
+ "ncu",
+ NULL
+};
+
+/*
+ * No array for NCU_CLASS_. The #define's in nwamcfg.h matches the
+ * enum nwam_ncu_class_t in libnwam and thus uses libnwam functions to
+ * retrieve the string representation.
+ */
+
+/* These *MUST* match the order of the PT_ define's from nwamcfg.h */
+static char *pt_types[] = {
+ "unknown",
+ NWAM_NCU_PROP_ACTIVATION_MODE,
+ NWAM_NCU_PROP_ENABLED,
+ NWAM_NCU_PROP_TYPE,
+ NWAM_NCU_PROP_CLASS,
+ NWAM_NCU_PROP_PARENT_NCP,
+ NWAM_NCU_PROP_PRIORITY_GROUP,
+ NWAM_NCU_PROP_PRIORITY_MODE,
+ NWAM_NCU_PROP_LINK_MAC_ADDR,
+ NWAM_NCU_PROP_LINK_AUTOPUSH,
+ NWAM_NCU_PROP_LINK_MTU,
+ NWAM_NCU_PROP_IP_VERSION,
+ NWAM_NCU_PROP_IPV4_ADDRSRC,
+ NWAM_NCU_PROP_IPV4_ADDR,
+ NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE,
+ NWAM_NCU_PROP_IPV6_ADDRSRC,
+ NWAM_NCU_PROP_IPV6_ADDR,
+ NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE,
+ NWAM_LOC_PROP_CONDITIONS,
+ NWAM_ENM_PROP_FMRI,
+ NWAM_ENM_PROP_START,
+ NWAM_ENM_PROP_STOP,
+ NWAM_LOC_PROP_NAMESERVICES,
+ NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH,
+ NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
+ NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS,
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS,
+ NWAM_LOC_PROP_DEFAULT_DOMAIN,
+ NWAM_LOC_PROP_NFSV4_DOMAIN,
+ NWAM_LOC_PROP_IPFILTER_CONFIG_FILE,
+ NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE,
+ NWAM_LOC_PROP_IPNAT_CONFIG_FILE,
+ NWAM_LOC_PROP_IPPOOL_CONFIG_FILE,
+ NWAM_LOC_PROP_IKE_CONFIG_FILE,
+ NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY,
+ NWAM_KNOWN_WLAN_PROP_KEYNAME,
+ NWAM_KNOWN_WLAN_PROP_KEYSLOT,
+ NWAM_KNOWN_WLAN_PROP_SECURITY_MODE
+};
+
+/* properties table: maps PT_* constants to property names */
+typedef struct prop_table_entry {
+ int pte_type;
+ const char *pte_name;
+} prop_table_entry_t;
+
+/* NCU properties table */
+static prop_table_entry_t ncu_prop_table[] = {
+ { PT_TYPE, NWAM_NCU_PROP_TYPE },
+ { PT_CLASS, NWAM_NCU_PROP_CLASS },
+ { PT_PARENT, NWAM_NCU_PROP_PARENT_NCP },
+ { PT_ACTIVATION_MODE, NWAM_NCU_PROP_ACTIVATION_MODE },
+ { PT_ENABLED, NWAM_NCU_PROP_ENABLED },
+ { PT_PRIORITY_GROUP, NWAM_NCU_PROP_PRIORITY_GROUP },
+ { PT_PRIORITY_MODE, NWAM_NCU_PROP_PRIORITY_MODE },
+ { PT_LINK_MACADDR, NWAM_NCU_PROP_LINK_MAC_ADDR },
+ { PT_LINK_AUTOPUSH, NWAM_NCU_PROP_LINK_AUTOPUSH },
+ { PT_LINK_MTU, NWAM_NCU_PROP_LINK_MTU },
+ { PT_IP_VERSION, NWAM_NCU_PROP_IP_VERSION },
+ { PT_IPV4_ADDRSRC, NWAM_NCU_PROP_IPV4_ADDRSRC },
+ { PT_IPV4_ADDR, NWAM_NCU_PROP_IPV4_ADDR },
+ { PT_IPV4_DEFAULT_ROUTE, NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE },
+ { PT_IPV6_ADDRSRC, NWAM_NCU_PROP_IPV6_ADDRSRC },
+ { PT_IPV6_ADDR, NWAM_NCU_PROP_IPV6_ADDR },
+ { PT_IPV6_DEFAULT_ROUTE, NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE },
+ { 0, NULL }
+};
+
+/* ENM properties table */
+static prop_table_entry_t enm_prop_table[] = {
+ { PT_ENM_FMRI, NWAM_ENM_PROP_FMRI },
+ { PT_ENM_START, NWAM_ENM_PROP_START },
+ { PT_ENM_STOP, NWAM_ENM_PROP_STOP },
+ { PT_ACTIVATION_MODE, NWAM_ENM_PROP_ACTIVATION_MODE },
+ { PT_CONDITIONS, NWAM_ENM_PROP_CONDITIONS },
+ { PT_ENABLED, NWAM_ENM_PROP_ENABLED },
+ { 0, NULL }
+};
+
+/* LOCation properties table */
+static prop_table_entry_t loc_prop_table[] = {
+ { PT_ACTIVATION_MODE, NWAM_LOC_PROP_ACTIVATION_MODE },
+ { PT_CONDITIONS, NWAM_LOC_PROP_CONDITIONS },
+ { PT_ENABLED, NWAM_LOC_PROP_ENABLED },
+ { PT_LOC_NAMESERVICES, NWAM_LOC_PROP_NAMESERVICES },
+ { PT_LOC_NAMESERVICES_CONFIG, NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE },
+ { PT_LOC_DNS_CONFIGSRC, NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC },
+ { PT_LOC_DNS_DOMAIN, NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN },
+ { PT_LOC_DNS_SERVERS, NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS },
+ { PT_LOC_DNS_SEARCH, NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH },
+ { PT_LOC_NIS_CONFIGSRC, NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC },
+ { PT_LOC_NIS_SERVERS, NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS },
+ { PT_LOC_LDAP_CONFIGSRC, NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC },
+ { PT_LOC_LDAP_SERVERS, NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS },
+ { PT_LOC_DEFAULT_DOMAIN, NWAM_LOC_PROP_DEFAULT_DOMAIN },
+ { PT_LOC_NFSV4_DOMAIN, NWAM_LOC_PROP_NFSV4_DOMAIN },
+ { PT_LOC_IPF_CONFIG, NWAM_LOC_PROP_IPFILTER_CONFIG_FILE },
+ { PT_LOC_IPF_V6_CONFIG, NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE },
+ { PT_LOC_IPNAT_CONFIG, NWAM_LOC_PROP_IPNAT_CONFIG_FILE },
+ { PT_LOC_IPPOOL_CONFIG, NWAM_LOC_PROP_IPPOOL_CONFIG_FILE },
+ { PT_LOC_IKE_CONFIG, NWAM_LOC_PROP_IKE_CONFIG_FILE },
+ { PT_LOC_IPSECPOL_CONFIG, NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE },
+ { 0, NULL }
+};
+
+/* Known WLAN properties table */
+static prop_table_entry_t wlan_prop_table[] = {
+ { PT_WLAN_BSSIDS, NWAM_KNOWN_WLAN_PROP_BSSIDS },
+ { PT_WLAN_PRIORITY, NWAM_KNOWN_WLAN_PROP_PRIORITY },
+ { PT_WLAN_KEYNAME, NWAM_KNOWN_WLAN_PROP_KEYNAME },
+ { PT_WLAN_KEYSLOT, NWAM_KNOWN_WLAN_PROP_KEYSLOT },
+ { PT_WLAN_SECURITY_MODE, NWAM_KNOWN_WLAN_PROP_SECURITY_MODE },
+ { 0, NULL }
+};
+
+/* Returns the appropriate properties table for the given object type */
+static prop_table_entry_t *
+get_prop_table(nwam_object_type_t object_type)
+{
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ return (ncu_prop_table);
+ case NWAM_OBJECT_TYPE_LOC:
+ return (loc_prop_table);
+ case NWAM_OBJECT_TYPE_ENM:
+ return (enm_prop_table);
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ return (wlan_prop_table);
+ }
+ return (NULL);
+}
+
+/* Global variables */
+
+/* set early in main(), never modified thereafter, used all over the place */
+static char *execname;
+
+/* set in modifying functions, checked in read_input() */
+boolean_t saw_error = B_FALSE;
+
+/* set in yacc parser, checked in read_input() */
+boolean_t newline_terminated;
+
+/* set in main(), checked in lex error handler */
+boolean_t cmd_file_mode = B_FALSE;
+
+/* set in exit_func(), checked in read_input() */
+static boolean_t time_to_exit = B_FALSE;
+
+/* used in nerr() and nwamerr() */
+static char *cmd_file_name = NULL;
+
+/* used with cmd_file to destroy all configurations */
+static boolean_t remove_all_configurations = B_FALSE;
+
+/* checked in read_input() and other places */
+static boolean_t ok_to_prompt = B_FALSE;
+
+/* initialized in do_interactive(), checked in initialize() */
+static boolean_t interactive_mode;
+
+static boolean_t need_to_commit = B_FALSE;
+
+/* The gl_get_line() resource object */
+static GetLine *gl;
+
+/* set when create or read objects, used by other func */
+static nwam_loc_handle_t loc_h = NULL;
+static nwam_enm_handle_t enm_h = NULL;
+static nwam_known_wlan_handle_t wlan_h = NULL;
+static nwam_ncu_handle_t ncu_h = NULL;
+static nwam_ncp_handle_t ncp_h = NULL;
+
+static int current_scope = NWAM_SCOPE_GBL;
+
+/* obj1_* are used in NWAM_SCOPE_{NCP,LOC,ENM,WLAN} */
+static int obj1_type;
+static char obj1_name[NWAM_MAX_NAME_LEN + 1];
+
+/* obj2_* are used in NWAM_SCOPE_NCU only */
+static int obj2_type;
+static char obj2_name[NWAM_MAX_NAME_LEN + 1];
+
+/* arrays for tab-completion */
+/* commands at NWAM_SCOPE_GBL */
+static const char *global_scope_cmds[] = {
+ "create ",
+ "destroy ",
+ "end ",
+ "exit ",
+ "export ",
+ "help ",
+ "list ",
+ "select ",
+ NULL
+};
+
+static const char *global_create_cmds[] = {
+ "create loc ",
+ "create enm ",
+ "create ncp ",
+ "create wlan ",
+ "create -t ", /* template */
+ NULL
+};
+
+static const char *global_destroy_cmds[] = {
+ "destroy -a ",
+ "destroy loc ",
+ "destroy enm ",
+ "destroy ncp ",
+ "destroy wlan ",
+ NULL
+};
+
+static const char *global_export_cmds[] = {
+ "export ",
+ "export -d ", /* add destroy -a */
+ "export -f ", /* to file */
+ "export -d -f ", /* add destroy -a to file */
+ "export loc ",
+ "export enm ",
+ "export ncp ",
+ "export wlan ",
+ NULL
+};
+
+static const char *global_list_cmds[] = {
+ "list ",
+ "list loc ",
+ "list enm ",
+ "list ncp ",
+ "list wlan ",
+ "list -a loc ",
+ "list -a enm ",
+ "list -a wlan ",
+ NULL
+};
+
+static const char *global_select_cmds[] = {
+ "select loc ",
+ "select enm ",
+ "select ncp ",
+ "select wlan ",
+ NULL
+};
+
+/* commands at NWAM_SCOPE_LOC, _ENM, _WLAN and _NCU */
+static const char *non_ncp_scope_cmds[] = {
+ "cancel ",
+ "clear ",
+ "commit ",
+ "end ",
+ "exit ",
+ "export ",
+ "export -f ",
+ "get ",
+ "get -V ", /* value only */
+ "help ",
+ "list ",
+ "list -a ", /* all properties */
+ "revert ",
+ "set ",
+ "verify ",
+ "walkprop ",
+ "walkprop -a ", /* all properties */
+ NULL
+};
+
+/* commands at NWAM_SCOPE_NCP */
+static const char *ncp_scope_cmds[] = {
+ "cancel ",
+ "create ",
+ "destroy ",
+ "end ",
+ "exit ",
+ "export ",
+ "help ",
+ "list ",
+ "select ",
+ NULL
+};
+
+static const char *ncp_create_cmds[] = {
+ "create ncu ip ",
+ "create ncu phys ",
+ "create -t ", /* template */
+ NULL
+};
+
+static const char *ncp_destroy_cmds[] = {
+ "destroy ncu ",
+ "destroy ncu ip ",
+ "destroy ncu phys ",
+ NULL
+};
+
+static const char *ncp_export_cmds[] = {
+ "export ",
+ "export -f ", /* to file */
+ "export ncu ",
+ "export ncu ip ",
+ "export ncu phys ",
+ NULL
+};
+
+static const char *ncp_list_cmds[] = {
+ "list ",
+ "list ncu ",
+ "list ncu ip ",
+ "list ncu phys ",
+ "list -a ncu ",
+ "list -a ncu ip ",
+ "list -a ncu phys ",
+ NULL
+};
+
+static const char *ncp_select_cmds[] = {
+ "select ncu ",
+ "select ncu ip ",
+ "select ncu phys ",
+ NULL
+};
+
+/* Functions begin here */
+
+cmd_t *
+alloc_cmd(void)
+{
+ cmd_t *cmd = calloc(1, sizeof (cmd_t));
+ if (cmd == NULL) {
+ nerr("Out of memory");
+ return (NULL);
+ }
+ cmd->cmd_argc = 0;
+ cmd->cmd_argv[0] = NULL;
+
+ return (cmd);
+}
+
+void
+free_cmd(cmd_t *cmd)
+{
+ int i;
+
+ for (i = 0; i < cmd->cmd_argc; i++)
+ free(cmd->cmd_argv[i]);
+ free(cmd);
+}
+
+void
+array_free(void **array, int nelem)
+{
+ int i;
+ for (i = 0; i < nelem; i++)
+ free(array[i]);
+ free(array);
+}
+
+static boolean_t
+initial_match(const char *line1, const char *line2, int word_end)
+{
+ if (word_end <= 0)
+ return (B_TRUE);
+ return (strncmp(line1, line2, word_end) == 0);
+}
+
+static int
+add_stuff(WordCompletion *cpl, const char *line1, const char **list,
+ int word_end)
+{
+ int i, err;
+
+ for (i = 0; list[i] != NULL; i++) {
+ if (initial_match(line1, list[i], word_end)) {
+ err = cpl_add_completion(cpl, line1, 0, word_end,
+ list[i] + word_end, "", "");
+ if (err != 0)
+ return (err);
+ }
+ }
+ return (0);
+}
+
+/*
+ * To fill in the rest of a string when user types the tab key.
+ * First digital number is the length of the string, the second digital number
+ * is the min number of chars that is needed to uniquely identify a string.
+ */
+#define MINI_STR(l, s, m, n) strncmp(l, s, MAX(MIN(sizeof (s) - 1, m), n))
+
+/* ARGSUSED */
+static
+CPL_MATCH_FN(cmd_cpl_fn)
+{
+ /* tab-complete according to the current scope */
+ switch (current_scope) {
+ case NWAM_SCOPE_GBL:
+ if (MINI_STR(line, "create ", word_end, 2) == 0)
+ return (add_stuff(cpl, line, global_create_cmds,
+ word_end));
+ if (MINI_STR(line, "destroy ", word_end, 1) == 0)
+ return (add_stuff(cpl, line, global_destroy_cmds,
+ word_end));
+ if (MINI_STR(line, "export ", word_end, 3) == 0)
+ return (add_stuff(cpl, line, global_export_cmds,
+ word_end));
+ if (MINI_STR(line, "list ", word_end, 1) == 0)
+ return (add_stuff(cpl, line, global_list_cmds,
+ word_end));
+ if (MINI_STR(line, "select ", word_end, 1) == 0)
+ return (add_stuff(cpl, line, global_select_cmds,
+ word_end));
+ return (add_stuff(cpl, line, global_scope_cmds, word_end));
+ case NWAM_SCOPE_LOC:
+ case NWAM_SCOPE_ENM:
+ case NWAM_SCOPE_WLAN:
+ case NWAM_SCOPE_NCU:
+ return (add_stuff(cpl, line, non_ncp_scope_cmds, word_end));
+ case NWAM_SCOPE_NCP:
+ if (MINI_STR(line, "create ", word_end, 2) == 0)
+ return (add_stuff(cpl, line, ncp_create_cmds,
+ word_end));
+ if (MINI_STR(line, "destroy ", word_end, 1) == 0)
+ return (add_stuff(cpl, line, ncp_destroy_cmds,
+ word_end));
+ if (MINI_STR(line, "export ", word_end, 3) == 0)
+ return (add_stuff(cpl, line, ncp_export_cmds,
+ word_end));
+ if (MINI_STR(line, "list ", word_end, 1) == 0)
+ return (add_stuff(cpl, line, ncp_list_cmds, word_end));
+ if (MINI_STR(line, "select ", word_end, 1) == 0)
+ return (add_stuff(cpl, line, ncp_select_cmds,
+ word_end));
+ return (add_stuff(cpl, line, ncp_scope_cmds, word_end));
+ }
+ /* should never get here */
+ return (NULL);
+}
+
+const char *
+cmd_to_str(int cmd_num)
+{
+ assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
+ return (helptab[cmd_num].cmd_name);
+}
+
+/* Returns "loc", "enm", "wlan" or "ncp" as string */
+static const char *
+rt1_to_str(int res_type)
+{
+ assert(res_type >= RT1_MIN && res_type <= RT1_MAX);
+ return (res1_types[res_type]);
+}
+
+/* Returns "ncu" as string */
+static const char *
+rt2_to_str(int res_type)
+{
+ assert(res_type >= RT2_MIN && res_type <= RT2_MAX);
+ return (res2_types[res_type]);
+}
+
+/* Returns "ncp, "ncu", "loc", "enm", or "wlan" according to the scope */
+static const char *
+scope_to_str(int scope) {
+ switch (scope) {
+ case NWAM_SCOPE_GBL:
+ return ("global");
+ case NWAM_SCOPE_NCP:
+ return ("ncp");
+ case NWAM_SCOPE_NCU:
+ return ("ncu");
+ case NWAM_SCOPE_LOC:
+ return ("loc");
+ case NWAM_SCOPE_ENM:
+ return ("enm");
+ case NWAM_SCOPE_WLAN:
+ return ("wlan");
+ default:
+ return ("invalid");
+ }
+}
+
+/* Given an enm property and value, returns it as a string */
+static const char *
+propval_to_str(const char *propname, uint64_t value)
+{
+ const char *str;
+
+ if (nwam_uint64_get_value_string(propname, value, &str) == NWAM_SUCCESS)
+ return (str);
+ return (NULL);
+}
+
+/* Given an int for a prop, returns it as string */
+static const char *
+pt_to_str(int prop_type)
+{
+ assert(prop_type >= PT_MIN && prop_type <= PT_MAX);
+ return (pt_types[prop_type]);
+}
+
+/* Return B_TRUE if string starts with "t" or is 1, B_FALSE otherwise */
+static boolean_t
+str_to_boolean(const char *str)
+{
+ if (strncasecmp(str, "t", 1) == 0 || atoi(str) == 1)
+ return (B_TRUE);
+ else
+ return (B_FALSE);
+}
+
+/*
+ * This is a separate function rather than a set of define's because of the
+ * gettext() wrapping.
+ */
+
+/*
+ * TRANSLATION_NOTE
+ * Each string below should have \t follow \n whenever needed; the
+ * initial \t and the terminal \n will be provided by the calling function.
+ */
+
+static const char *
+long_help(int cmd_num)
+{
+ assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
+ switch (cmd_num) {
+ case CMD_CANCEL:
+ return (gettext("Cancels the current configuration "
+ "changes."));
+ case CMD_CLEAR:
+ return (gettext("Clears the value for the specified "
+ "property."));
+ case CMD_COMMIT:
+ return (gettext("Commits the current configuration."));
+ case CMD_CREATE:
+ return (gettext("Creates a new profile or resource."));
+ case CMD_DESTROY:
+ return (gettext("Destroys the specified profile or "
+ "resource."));
+ case CMD_END:
+ return (gettext("Ends specification of a resource."));
+ case CMD_EXIT:
+ return (gettext("Exits the program."));
+ case CMD_EXPORT:
+ return (gettext("Exports the configuration."));
+ case CMD_GET:
+ return (gettext("Gets the value of the specified "
+ "property."));
+ case CMD_HELP:
+ return (gettext("Prints help message."));
+ case CMD_LIST:
+ return (gettext("Lists existing objects."));
+ case CMD_REVERT:
+ return (gettext("Reverts to the previous "
+ "configuration."));
+ case CMD_SELECT:
+ return (gettext("Selects a resource to modify."));
+ case CMD_SET:
+ return (gettext("Sets the value of the specified "
+ "property."));
+ case CMD_VERIFY:
+ return (gettext("Verifies an object."));
+ case CMD_WALKPROP:
+ return (gettext("Iterates over properties."));
+ default:
+ return (gettext("Unknown command."));
+ }
+}
+
+void
+command_usage(int command)
+{
+ if (command < CMD_MIN || command > CMD_MAX) {
+ nerr("Unknown command");
+ } else {
+ nerr("%s: %s: %s", gettext("Error"), gettext("usage"),
+ helptab[command].cmd_usage);
+ }
+}
+
+static void
+long_usage(uint_t cmd_num)
+{
+ (void) printf("%s: %s\n", gettext("usage"),
+ helptab[cmd_num].cmd_usage);
+ (void) printf("\t%s\n", long_help(cmd_num));
+}
+
+/* Prints usage for command line options */
+static void
+cmd_line_usage()
+{
+ (void) printf("%s:\t%s\t\t\t\t(%s)\n", gettext("usage"), execname,
+ gettext("interactive-mode"));
+ (void) printf("\t%s <%s> [%s...]\n", execname, gettext("command"),
+ gettext("options"));
+ (void) printf("\t%s [-d] -f <%s>\n", execname, gettext("command-file"));
+ (void) printf("\t%s %s [<%s>]\n", execname, cmd_to_str(CMD_HELP),
+ gettext("command"));
+}
+
+/* Prints the line number of the current command if in command-file mode */
+static void
+print_lineno()
+{
+ static int last_lineno;
+
+ /* lex_lineno has already been incremented in the lexer; compensate */
+ if (cmd_file_mode && lex_lineno > last_lineno) {
+ if (strcmp(cmd_file_name, "-") == 0)
+ (void) fprintf(stderr, gettext("On line %d:\n"),
+ lex_lineno - 1);
+ else
+ (void) fprintf(stderr, gettext("On line %d of %s:\n"),
+ lex_lineno - 1, cmd_file_name);
+ last_lineno = lex_lineno;
+ }
+}
+
+/* PRINTFLIKE1 */
+void
+nerr(const char *format, ...)
+{
+ va_list alist;
+
+ print_lineno();
+
+ format = gettext(format);
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, "\n");
+
+ saw_error = B_TRUE;
+}
+
+/* PRINTFLIKE2 */
+static void
+nwamerr(nwam_error_t err, const char *format, ...)
+{
+ va_list alist;
+
+ print_lineno();
+
+ format = gettext(format);
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, ": %s\n", nwam_strerror(err));
+
+ saw_error = B_TRUE;
+}
+
+void
+properr(const char *prop)
+{
+ nerr("Invalid property: '%s'", prop);
+}
+
+/*
+ * If free_ncu_only == B_TRUE, only ncu handle is freed, ncp handle remains the
+ * same. Since nwam_ncp_free() takes care of its ncus, no need to explicitly
+ * call nwam_ncu_free() afterwards.
+ */
+static void
+free_handle(boolean_t free_ncu_only)
+{
+ if (ncp_h != NULL) {
+ if (!free_ncu_only) {
+ nwam_ncp_free(ncp_h);
+ ncp_h = NULL;
+ ncu_h = NULL;
+ } else if (ncu_h != NULL) {
+ nwam_ncu_free(ncu_h);
+ ncu_h = NULL;
+ }
+ }
+
+ if (enm_h != NULL) {
+ nwam_enm_free(enm_h);
+ enm_h = NULL;
+ }
+
+ if (loc_h != NULL) {
+ nwam_loc_free(loc_h);
+ loc_h = NULL;
+ }
+
+ if (wlan_h != NULL) {
+ nwam_known_wlan_free(wlan_h);
+ wlan_h = NULL;
+ }
+}
+
+/*
+ * On input, TRUE => yes, FALSE => no.
+ * On return, TRUE => 1, FALSE => no, could not ask => -1.
+ */
+static int
+ask_yesno(boolean_t default_answer, const char *question)
+{
+ char line[64]; /* should be enough to answer yes or no */
+
+ if (!ok_to_prompt) {
+ saw_error = B_TRUE;
+ return (-1);
+ }
+ for (;;) {
+ if (printf("%s (%s)? ", gettext(question),
+ default_answer ? "[y]/n" : "y/[n]") < 0)
+ return (-1);
+ if (fgets(line, sizeof (line), stdin) == NULL)
+ return (-1);
+
+ if (line[0] == '\n')
+ return (default_answer ? 1 : 0);
+ if (tolower(line[0]) == 'y')
+ return (1);
+ if (tolower(line[0]) == 'n')
+ return (0);
+ }
+}
+
+/* This is the back-end helper function for read_input() below. */
+static int
+cleanup()
+{
+ int answer;
+
+ if (!interactive_mode && !cmd_file_mode) {
+ /*
+ * If we're not in interactive mode, and we're not in command
+ * file mode, then we must be in commands-from-the-command-line
+ * mode. As such, we can't loop back and ask for more input.
+ * It was OK to prompt for such things as whether or not to
+ * really delete something in the command handler called from
+ * yyparse() above, but "really quit?" makes no sense in this
+ * context. So disable prompting.
+ */
+ ok_to_prompt = B_FALSE;
+ }
+ if (need_to_commit) {
+ answer = ask_yesno(B_FALSE,
+ "Configuration not saved; really quit");
+ switch (answer) {
+ case -1:
+ /* issue error here */
+ return (NWAM_ERR);
+ case 1:
+ /*
+ * don't want to save, just exit. handles are freed at
+ * end_func() or exit_func().
+ */
+ return (NWAM_OK);
+ default:
+ /* loop back to read input */
+ time_to_exit = B_FALSE;
+ yyin = stdin;
+ return (NWAM_REPEAT);
+ }
+ }
+ return (saw_error ? NWAM_ERR : NWAM_OK);
+}
+
+static int
+string_to_yyin(char *string)
+{
+ if ((yyin = tmpfile()) == NULL)
+ goto error;
+ if (fwrite(string, strlen(string), 1, yyin) != 1)
+ goto error;
+ if (fseek(yyin, 0, SEEK_SET) != 0)
+ goto error;
+
+ return (NWAM_OK);
+
+error:
+ nerr("problem creating temporary file");
+ return (NWAM_ERR);
+}
+
+/*
+ * read_input() is the driver of this program. It is a wrapper around
+ * yyparse(), printing appropriate prompts when needed, checking for
+ * exit conditions and reacting appropriately. This function is
+ * called when in interactive mode or command-file mode.
+ */
+static int
+read_input(void)
+{
+ boolean_t yyin_is_a_tty = isatty(fileno(yyin));
+ /*
+ * The prompt is "e> " or "e:t1:o1> " or "e:t1:o1:t2:o2> " where e is
+ * execname, t is resource type, o is object name.
+ */
+ char prompt[MAXPATHLEN + (2 * (NWAM_MAX_TYPE_LEN + NWAM_MAX_NAME_LEN))
+ + sizeof ("::::> ")];
+ char *line;
+
+ /* yyin should have been set to the appropriate (FILE *) if not stdin */
+ newline_terminated = B_TRUE;
+ for (;;) {
+ if (yyin_is_a_tty) {
+ if (newline_terminated) {
+ switch (current_scope) {
+ case NWAM_SCOPE_GBL:
+ (void) snprintf(prompt, sizeof (prompt),
+ "%s> ", execname);
+ break;
+ case NWAM_SCOPE_LOC:
+ case NWAM_SCOPE_ENM:
+ case NWAM_SCOPE_WLAN:
+ case NWAM_SCOPE_NCP:
+ (void) snprintf(prompt, sizeof (prompt),
+ "%s:%s:%s> ", execname,
+ rt1_to_str(obj1_type), obj1_name);
+
+ break;
+ case NWAM_SCOPE_NCU:
+ (void) snprintf(prompt, sizeof (prompt),
+ "%s:%s:%s:%s:%s> ", execname,
+ rt1_to_str(obj1_type), obj1_name,
+ rt2_to_str(obj2_type), obj2_name);
+ }
+ }
+ /*
+ * If the user hits ^C then we want to catch it and
+ * start over. If the user hits EOF then we want to
+ * bail out.
+ */
+ line = gl_get_line(gl, prompt, NULL, -1);
+ if (gl_return_status(gl) == GLR_SIGNAL) {
+ gl_abandon_line(gl);
+ continue;
+ }
+ if (line == NULL)
+ break;
+ if (string_to_yyin(line) != NWAM_OK)
+ break;
+ while (!feof(yyin)) {
+ yyparse();
+
+ /*
+ * If any command on a list of commands
+ * give an error, don't continue with the
+ * remaining commands.
+ */
+ if (saw_error || time_to_exit)
+ break;
+ }
+ } else {
+ yyparse();
+ }
+
+ /* Bail out on an error in command-file mode. */
+ if (saw_error && cmd_file_mode && !interactive_mode)
+ time_to_exit = B_TRUE;
+ if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
+ break;
+ }
+ return (cleanup());
+}
+
+/*
+ * This function is used in the interactive-mode scenario: it just calls
+ * read_input() until we are done.
+ */
+static int
+do_interactive(void)
+{
+ int err;
+
+ interactive_mode = B_TRUE;
+ do {
+ err = read_input();
+ } while (err == NWAM_REPEAT);
+ return (err);
+}
+
+/* Calls the help_func() to print the usage of all commands */
+void
+help_wrap()
+{
+ cmd_t *help_cmd;
+
+ if ((help_cmd = alloc_cmd()) == NULL)
+ exit(NWAM_ERR);
+ help_func(help_cmd);
+ free_cmd(help_cmd);
+}
+
+/* Check if the given command is allowed in the current scope */
+boolean_t
+check_scope(int cmd)
+{
+ /* allowed in all scopes */
+ switch (cmd) {
+ case CMD_END:
+ case CMD_EXIT:
+ case CMD_HELP:
+ case CMD_LIST:
+ case CMD_EXPORT:
+ return (B_TRUE);
+ }
+ /* scope-specific */
+ switch (current_scope) {
+ case NWAM_SCOPE_GBL:
+ switch (cmd) {
+ case CMD_CREATE:
+ case CMD_DESTROY:
+ case CMD_SELECT:
+ return (B_TRUE);
+ }
+ break;
+ case NWAM_SCOPE_LOC:
+ case NWAM_SCOPE_ENM:
+ case NWAM_SCOPE_WLAN:
+ case NWAM_SCOPE_NCU:
+ switch (cmd) {
+ case CMD_CANCEL:
+ case CMD_CLEAR:
+ case CMD_COMMIT:
+ case CMD_GET:
+ case CMD_REVERT:
+ case CMD_SET:
+ case CMD_VERIFY:
+ case CMD_WALKPROP:
+ return (B_TRUE);
+ }
+ break;
+ case NWAM_SCOPE_NCP:
+ switch (cmd) {
+ case CMD_CANCEL:
+ case CMD_CREATE:
+ case CMD_DESTROY:
+ case CMD_SELECT:
+ return (B_TRUE);
+ }
+ break;
+ default:
+ nerr("Invalid scope");
+ }
+ nerr("'%s' is not allowed at this scope", cmd_to_str(cmd));
+ return (B_FALSE);
+}
+
+/* Returns the active object type depending on which handle is not NULL */
+static nwam_object_type_t
+active_object_type()
+{
+ /* Check ncu_h before ncp_h, ncp_h must be loaded before ncu_h */
+ if (ncu_h != NULL)
+ return (NWAM_OBJECT_TYPE_NCU);
+ else if (ncp_h != NULL)
+ return (NWAM_OBJECT_TYPE_NCP);
+ else if (loc_h != NULL)
+ return (NWAM_OBJECT_TYPE_LOC);
+ else if (enm_h != NULL)
+ return (NWAM_OBJECT_TYPE_ENM);
+ else if (wlan_h != NULL)
+ return (NWAM_OBJECT_TYPE_KNOWN_WLAN);
+ else
+ return (NWAM_OBJECT_TYPE_UNKNOWN);
+}
+
+/* Retrive the name of the object from its handle */
+static nwam_error_t
+object_name_from_handle(nwam_object_type_t object_type, void *handle,
+ char **namep)
+{
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCP:
+ return (nwam_ncp_get_name(handle, namep));
+ case NWAM_OBJECT_TYPE_NCU:
+ return (nwam_ncu_get_name(handle, namep));
+ case NWAM_OBJECT_TYPE_LOC:
+ return (nwam_loc_get_name(handle, namep));
+ case NWAM_OBJECT_TYPE_ENM:
+ return (nwam_enm_get_name(handle, namep));
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ return (nwam_known_wlan_get_name(handle, namep));
+ }
+ return (NWAM_INVALID_ARG);
+}
+
+static void
+do_commit()
+{
+ nwam_error_t ret = NWAM_SUCCESS;
+ const char *errprop;
+
+ if (!need_to_commit)
+ return;
+
+ switch (active_object_type()) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_commit(ncu_h, 0);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_commit(enm_h, 0);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_commit(loc_h, 0);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_commit(wlan_h, 0);
+ break;
+ }
+
+ if (ret == NWAM_SUCCESS) {
+ need_to_commit = B_FALSE;
+ if (interactive_mode)
+ (void) printf(gettext("Committed changes\n"));
+ } else {
+ nwam_error_t verr;
+
+ /* Find property that caused failure */
+ switch (active_object_type()) {
+ case NWAM_OBJECT_TYPE_NCU:
+ verr = nwam_ncu_validate(ncu_h, &errprop);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ verr = nwam_enm_validate(enm_h, &errprop);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ verr = nwam_loc_validate(loc_h, &errprop);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ verr = nwam_known_wlan_validate(wlan_h, &errprop);
+ break;
+ }
+
+ if (verr != NWAM_SUCCESS)
+ nwamerr(ret, "Commit error on property '%s'", errprop);
+ else
+ nwamerr(ret, "Commit error");
+ }
+}
+
+/*
+ * Saves the current configuration to persistent storage.
+ */
+/* ARGSUSED */
+void
+commit_func(cmd_t *cmd)
+{
+ if (!need_to_commit) {
+ if (interactive_mode)
+ (void) printf(gettext("Nothing to commit\n"));
+ } else {
+ do_commit();
+ }
+}
+
+static void
+do_cancel()
+{
+ switch (current_scope) {
+ case NWAM_SCOPE_NCU:
+ current_scope = NWAM_SCOPE_NCP;
+ obj2_type = 0;
+ free_handle(B_TRUE);
+ break;
+ case NWAM_SCOPE_NCP:
+ case NWAM_SCOPE_ENM:
+ case NWAM_SCOPE_WLAN:
+ case NWAM_SCOPE_LOC:
+ current_scope = NWAM_SCOPE_GBL;
+ obj1_type = 0;
+ free_handle(B_FALSE);
+ break;
+ case NWAM_SCOPE_GBL:
+ free_handle(B_FALSE);
+ break;
+ default:
+ nerr("Invalid scope");
+ return;
+ }
+ need_to_commit = B_FALSE;
+}
+
+/*
+ * End operation on current scope and go up one scope.
+ * Changes are not saved, no prompt either.
+ */
+/* ARGSUSED */
+void
+cancel_func(cmd_t *cmd)
+{
+ do_cancel();
+}
+
+/*
+ * Removes leading and trailing quotes from a string.
+ * Caller must free returned string.
+ */
+static char *
+trim_quotes(const char *quoted_str)
+{
+ char *str;
+ int end;
+
+ /* export_func() and list_func() can pass NULL here */
+ if (quoted_str == NULL)
+ return (NULL);
+
+ /* remove leading quote */
+ if (quoted_str[0] == '"')
+ str = strdup(quoted_str + 1);
+ else
+ str = strdup(quoted_str);
+ if (str == NULL)
+ return (NULL);
+
+ /* remove trailing quote and newline */
+ end = strlen(str) - 1;
+ while (end >= 0 && (str[end] == '"' || str[end] == '\n'))
+ end--;
+ str[end+1] = 0;
+
+ return (str);
+}
+
+/*
+ * Creates a new resource and enters the scope of that resource.
+ * The new resource can also be a copy of an existing resource (-t option).
+ * If in interactive mode, then after creation call walkprop_func()
+ * to do walk the properties for the new object.
+ */
+void
+create_func(cmd_t *cmd)
+{
+ nwam_error_t ret = NWAM_SUCCESS;
+ int c;
+ boolean_t template = B_FALSE;
+ char *newname = NULL, *oldname = NULL;
+ cmd_t *walkprop_cmd;
+
+ /* make sure right command at the right scope */
+ if (current_scope == NWAM_SCOPE_GBL &&
+ cmd->cmd_res2_type == RT2_NCU) {
+ nerr("cannot create ncu at global scope");
+ return;
+ }
+ if (current_scope == NWAM_SCOPE_NCP &&
+ cmd->cmd_res2_type != RT2_NCU) {
+ nerr("Cannot create given object at this scope");
+ return;
+ }
+
+ assert(cmd->cmd_argc > 0);
+ optind = 0;
+ while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "t:")) != EOF) {
+ switch (c) {
+ case 't':
+ template = B_TRUE;
+ break;
+ default:
+ command_usage(CMD_CREATE);
+ return;
+ }
+ }
+
+ if (!template) {
+ /* no template given */
+ /* argv[0] is name */
+ newname = trim_quotes(cmd->cmd_argv[0]);
+ if (cmd->cmd_res1_type == RT1_ENM) {
+ ret = nwam_enm_create(newname, NULL, &enm_h);
+ } else if (cmd->cmd_res1_type == RT1_LOC) {
+ ret = nwam_loc_create(newname, &loc_h);
+ } else if (cmd->cmd_res1_type == RT1_WLAN) {
+ ret = nwam_known_wlan_create(newname, &wlan_h);
+ } else if (cmd->cmd_res1_type == RT1_NCP &&
+ current_scope == NWAM_SCOPE_GBL) {
+ ret = nwam_ncp_create(newname, 0, &ncp_h);
+ } else if (cmd->cmd_res2_type == RT2_NCU) {
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ /* ncp must already be read */
+ if (ncp_h == NULL) {
+ nerr("Create error: NCP has not been read");
+ goto done;
+ }
+
+ ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
+ ncu_type = nwam_ncu_class_to_type(ncu_class);
+ ret = nwam_ncu_create(ncp_h, newname, ncu_type,
+ ncu_class, &ncu_h);
+ }
+
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Create error");
+ goto done;
+ }
+
+ } else {
+ /* template given */
+ /* argv[0] is -t, argv[1] is old name, argv[2] is new name */
+ oldname = trim_quotes(cmd->cmd_argv[1]);
+ newname = trim_quotes(cmd->cmd_argv[2]);
+ if (cmd->cmd_res1_type == RT1_ENM) {
+ nwam_enm_handle_t oldenm_h;
+
+ ret = nwam_enm_read(oldname, 0, &oldenm_h);
+ if (ret != NWAM_SUCCESS)
+ goto read_error;
+ ret = nwam_enm_copy(oldenm_h, newname, &enm_h);
+ nwam_enm_free(oldenm_h);
+ } else if (cmd->cmd_res1_type == RT1_LOC) {
+ nwam_loc_handle_t oldloc_h;
+
+ ret = nwam_loc_read(oldname, 0, &oldloc_h);
+ if (ret != NWAM_SUCCESS)
+ goto read_error;
+ ret = nwam_loc_copy(oldloc_h, newname, &loc_h);
+ nwam_loc_free(oldloc_h);
+ } else if (cmd->cmd_res1_type == RT1_WLAN) {
+ nwam_known_wlan_handle_t oldwlan_h;
+
+ ret = nwam_known_wlan_read(oldname, 0, &oldwlan_h);
+ if (ret != NWAM_SUCCESS)
+ goto read_error;
+ ret = nwam_known_wlan_copy(oldwlan_h, newname, &wlan_h);
+ nwam_known_wlan_free(oldwlan_h);
+ } else if (cmd->cmd_res1_type == RT1_NCP &&
+ current_scope == NWAM_SCOPE_GBL) {
+ nwam_ncp_handle_t oldncp_h;
+
+ ret = nwam_ncp_read(oldname, 0, &oldncp_h);
+ if (ret != NWAM_SUCCESS)
+ goto read_error;
+ ret = nwam_ncp_copy(oldncp_h, newname, &ncp_h);
+ nwam_ncp_free(oldncp_h);
+ } else if (cmd->cmd_res2_type == RT2_NCU) {
+ nwam_ncu_handle_t oldncu_h;
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ /* ncp must already be read */
+ if (ncp_h == NULL) {
+ nerr("Copy error: NCP has not been read");
+ goto done;
+ }
+ ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
+ ncu_type = nwam_ncu_class_to_type(ncu_class);
+ ret = nwam_ncu_read(ncp_h, oldname, ncu_type, 0,
+ &oldncu_h);
+ if (ret != NWAM_SUCCESS)
+ goto read_error;
+ ret = nwam_ncu_copy(oldncu_h, newname, &ncu_h);
+ nwam_ncu_free(oldncu_h);
+ }
+
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Copy error");
+ goto done;
+ }
+ }
+
+ if (current_scope == NWAM_SCOPE_GBL) {
+ (void) strlcpy(obj1_name, newname, sizeof (obj1_name));
+ obj1_type = cmd->cmd_res1_type;
+ if (obj1_type == RT1_ENM)
+ current_scope = NWAM_SCOPE_ENM;
+ else if (obj1_type == RT1_LOC)
+ current_scope = NWAM_SCOPE_LOC;
+ else if (obj1_type == RT1_WLAN)
+ current_scope = NWAM_SCOPE_WLAN;
+ else if (obj1_type == RT1_NCP)
+ current_scope = NWAM_SCOPE_NCP;
+ } else {
+ (void) strlcpy(obj2_name, newname, sizeof (obj2_name));
+ current_scope = NWAM_SCOPE_NCU;
+ obj2_type = cmd->cmd_res2_type;
+ }
+ if (current_scope != NWAM_SCOPE_NCP)
+ need_to_commit = B_TRUE;
+
+ /* do a walk of the properties if in interactive mode */
+ if (interactive_mode && current_scope != NWAM_SCOPE_NCP) {
+ (void) printf(gettext("Created %s '%s'. "
+ "Walking properties ...\n"),
+ scope_to_str(current_scope), newname);
+ if ((walkprop_cmd = alloc_cmd()) == NULL)
+ goto done;
+ walkprop_func(walkprop_cmd);
+ free(walkprop_cmd);
+ }
+
+read_error:
+ if (ret != NWAM_SUCCESS)
+ nwamerr(ret, "Copy error reading '%s'", oldname);
+
+done:
+ free(oldname);
+ free(newname);
+}
+
+/* Processing of return value for destroy_*_callback() */
+static int
+destroy_ret(nwam_object_type_t object_type, nwam_error_t ret, void *handle)
+{
+ if (ret == NWAM_ENTITY_NOT_DESTROYABLE) {
+ /* log a message to stderr, but don't consider it an error */
+ char *name;
+ if (object_name_from_handle(object_type, handle, &name)
+ == NWAM_SUCCESS) {
+ (void) fprintf(stderr,
+ gettext("%s '%s' cannot be removed\n"),
+ nwam_object_type_to_string(object_type), name);
+ free(name);
+ }
+ return (0);
+ }
+
+ if (ret == NWAM_SUCCESS || ret == NWAM_ENTITY_IN_USE)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * NWAM_FLAG_DO_NOT_FREE is passed to nwam_*_destory() so that it does not
+ * free the handle. The calling nwam_walk_*() function frees this handle
+ * as it is the function that created the handle.
+ *
+ * Objects that are not destroyable or are active cannot be destroyed.
+ * Don't return error in these situations so the walk can continue.
+ */
+/* ARGSUSED */
+static int
+destroy_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
+{
+ /* The file is deleted, so NCUs are also removed */
+ nwam_error_t ret = nwam_ncp_destroy(ncp, NWAM_FLAG_DO_NOT_FREE);
+ return (destroy_ret(NWAM_OBJECT_TYPE_NCP, ret, ncp));
+}
+
+/* ARGSUSED */
+static int
+destroy_loc_callback(nwam_loc_handle_t loc, void *arg)
+{
+ nwam_error_t ret = nwam_loc_destroy(loc, NWAM_FLAG_DO_NOT_FREE);
+ return (destroy_ret(NWAM_OBJECT_TYPE_LOC, ret, loc));
+}
+
+/* ARGSUSED */
+static int
+destroy_enm_callback(nwam_enm_handle_t enm, void *arg)
+{
+ nwam_error_t ret = nwam_enm_destroy(enm, NWAM_FLAG_DO_NOT_FREE);
+ return (destroy_ret(NWAM_OBJECT_TYPE_ENM, ret, enm));
+}
+
+/* ARGSUSED */
+static int
+destroy_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
+{
+ nwam_error_t ret = nwam_known_wlan_destroy(wlan, NWAM_FLAG_DO_NOT_FREE);
+ return (destroy_ret(NWAM_OBJECT_TYPE_KNOWN_WLAN, ret, wlan));
+}
+
+/*
+ * Remove all existing configuration that are not read-only.
+ * walk through all ncps, locs, enms, wlans and destroy each one.
+ */
+static nwam_error_t
+destroy_all(void)
+{
+ nwam_error_t ret;
+
+ assert(remove_all_configurations);
+
+ ret = nwam_walk_ncps(destroy_ncp_callback, NULL, 0, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+
+ ret = nwam_walk_enms(destroy_enm_callback, NULL,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+
+ ret = nwam_walk_locs(destroy_loc_callback, NULL,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+
+ ret = nwam_walk_known_wlans(destroy_wlan_callback, NULL, 0, NULL);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+
+ if (interactive_mode)
+ (void) printf(gettext("All user-defined entities destroyed\n"));
+ remove_all_configurations = B_FALSE;
+
+done:
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Destroy error: "
+ "could not destroy all configurations");
+ }
+ return (ret);
+}
+
+/*
+ * Destroys an instance in persistent repository, and is permanent.
+ * If interactive mode, it is allowed at global scope only
+ * option -a destroys everything.
+ */
+void
+destroy_func(cmd_t *cmd)
+{
+ nwam_error_t ret;
+ char *name, *realname = NULL;
+
+ if (current_scope == NWAM_SCOPE_NCP &&
+ (cmd->cmd_res1_type == RT1_ENM || cmd->cmd_res1_type == RT1_LOC ||
+ cmd->cmd_res1_type == RT1_WLAN)) {
+ nerr("Destroy error: only NCUs can be destroyed in NCP scope");
+ return;
+ }
+
+ assert(cmd->cmd_argc > 0);
+
+ /* res1_type is -1 if -a flag is used */
+ if (cmd->cmd_res1_type == -1) {
+ int c;
+
+ if (current_scope != NWAM_SCOPE_GBL) {
+ nerr("Cannot destroy all configurations in a "
+ "non-global scope");
+ return;
+ }
+
+ optind = 0;
+ while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "a")) != EOF) {
+ switch (c) {
+ case 'a':
+ remove_all_configurations = B_TRUE;
+ break;
+ default:
+ command_usage(CMD_DESTROY);
+ return;
+ }
+ }
+ if (remove_all_configurations) {
+ (void) destroy_all();
+ return;
+ }
+ }
+
+ /* argv[0] is name */
+ name = trim_quotes(cmd->cmd_argv[0]);
+ if (cmd->cmd_res2_type == RT2_NCU) {
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ /* ncp must already be read */
+ if (ncp_h == NULL) {
+ nerr("Destroy ncu error: NCP has not been read");
+ return;
+ }
+ ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
+ ncu_type = nwam_ncu_class_to_type(ncu_class);
+ ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_NCU, ncu_h,
+ &realname);
+ ret = nwam_ncu_destroy(ncu_h, 0);
+ ncu_h = NULL;
+ } else if (cmd->cmd_res1_type == RT1_ENM) {
+ if ((ret = nwam_enm_read(name, 0, &enm_h)) != NWAM_SUCCESS)
+ goto done;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_ENM, enm_h,
+ &realname);
+ ret = nwam_enm_destroy(enm_h, 0);
+ enm_h = NULL;
+ } else if (cmd->cmd_res1_type == RT1_LOC) {
+ if ((ret = nwam_loc_read(name, 0, &loc_h)) != NWAM_SUCCESS)
+ goto done;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_LOC, loc_h,
+ &realname);
+ ret = nwam_loc_destroy(loc_h, 0);
+ loc_h = NULL;
+ } else if (cmd->cmd_res1_type == RT1_WLAN) {
+ if ((ret = nwam_known_wlan_read(name, 0, &wlan_h))
+ != NWAM_SUCCESS)
+ goto done;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_KNOWN_WLAN,
+ wlan_h, &realname);
+ ret = nwam_known_wlan_destroy(wlan_h, 0);
+ wlan_h = NULL;
+ } else if (cmd->cmd_res1_type == RT1_NCP) {
+ if ((ret = nwam_ncp_read(name, 0, &ncp_h)) != NWAM_SUCCESS)
+ goto done;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_NCP, ncp_h,
+ &realname);
+ ret = nwam_ncp_destroy(ncp_h, 0);
+ ncp_h = NULL;
+ } else {
+ nerr("Destroy error: unknown object-type");
+ }
+
+done:
+ if (ret == NWAM_ENTITY_IN_USE) {
+ nerr("Destroy error: active entity cannot be destroyed");
+ } else if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Destroy error");
+ } else if (interactive_mode) {
+ (void) printf(gettext("Destroyed %s '%s'\n"),
+ (cmd->cmd_res2_type == RT2_NCU ?
+ rt2_to_str(cmd->cmd_res2_type) :
+ rt1_to_str(cmd->cmd_res1_type)),
+ realname != NULL ? realname : name);
+ }
+ free(name);
+ free(realname);
+}
+
+/*
+ * End operation on current scope and go up one scope.
+ * Changes are saved.
+ */
+/* ARGSUSED */
+void
+end_func(cmd_t *cmd)
+{
+ /* if need_to_commit is set, commit changes */
+ if (need_to_commit)
+ do_commit();
+
+ /*
+ * Call do_cancel() to go up one scope. If commit fails,
+ * need_to_commit is not reset and users are asked if they want to end.
+ */
+ if (!need_to_commit ||
+ (need_to_commit && (ask_yesno(B_FALSE,
+ "Configuration not saved; really end")) == 1)) {
+ /* set time_to_exit if in global scope */
+ if (current_scope == NWAM_SCOPE_GBL)
+ time_to_exit = B_TRUE;
+ /* call do_cancel() to go up one scope */
+ do_cancel();
+ }
+}
+
+/*
+ * Exit immediately. Configuration changes are saved by calling end_func().
+ */
+/* ARGSUSED */
+void
+exit_func(cmd_t *cmd)
+{
+ cmd_t *end_cmd;
+
+ if (need_to_commit) {
+ if ((end_cmd = alloc_cmd()) == NULL) {
+ nerr("Exit error");
+ return;
+ }
+ end_func(end_cmd);
+ free_cmd(end_cmd);
+ }
+
+ /*
+ * If need_to_commit is still set, then the commit failed.
+ * Otherwise, exit.
+ */
+ if (!need_to_commit)
+ time_to_exit = B_TRUE;
+}
+
+void
+help_func(cmd_t *cmd)
+{
+ int i;
+
+ if (cmd->cmd_argc == 0) {
+ (void) printf(gettext("commands:\n"));
+ for (i = CMD_MIN; i <= CMD_MAX; i++)
+ (void) printf("\t%s\n", helptab[i].cmd_usage);
+ return;
+ }
+
+ for (i = CMD_MIN; i <= CMD_MAX; i++) {
+ if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
+ long_usage(i);
+ return;
+ }
+ }
+ (void) fprintf(stderr, gettext("Unknown command: '%s'\n"),
+ cmd->cmd_argv[0]);
+ help_wrap();
+}
+
+/*
+ * Revert configuration of an instance to latest previous version.
+ * Free the handle and read again.
+ */
+/* ARGSUSED */
+void
+revert_func(cmd_t *cmd)
+{
+ nwam_error_t ret;
+ char *name = NULL;
+ nwam_ncu_type_t ncu_type;
+
+ switch (active_object_type()) {
+ case NWAM_OBJECT_TYPE_NCU:
+ /* retrieve name and type to use later */
+ if ((ret = nwam_ncu_get_ncu_type(ncu_h, &ncu_type))
+ != NWAM_SUCCESS) {
+ nwamerr(ret, "Revert error: Get ncu type error");
+ return;
+ }
+ if ((ret = nwam_ncu_get_name(ncu_h, &name)) != NWAM_SUCCESS)
+ goto name_error;
+ nwam_ncu_free(ncu_h);
+ ncu_h = NULL;
+ ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ if ((ret = nwam_enm_get_name(enm_h, &name)) != NWAM_SUCCESS)
+ goto name_error;
+ nwam_enm_free(enm_h);
+ enm_h = NULL;
+ ret = nwam_enm_read(name, 0, &enm_h);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ if ((ret = nwam_loc_get_name(loc_h, &name)) != NWAM_SUCCESS)
+ goto name_error;
+ nwam_loc_free(loc_h);
+ loc_h = NULL;
+ ret = nwam_loc_read(name, 0, &loc_h);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ if ((ret = nwam_known_wlan_get_name(wlan_h, &name))
+ != NWAM_SUCCESS)
+ goto name_error;
+ nwam_known_wlan_free(wlan_h);
+ wlan_h = NULL;
+ ret = nwam_known_wlan_read(name, 0, &wlan_h);
+ break;
+ }
+
+ /* Exit this scope because handle already freed (call do_cancel()) */
+ free(name);
+ need_to_commit = B_FALSE;
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Revert error");
+ do_cancel();
+ }
+ return;
+
+name_error:
+ if (ret != NWAM_SUCCESS)
+ nwamerr(ret, "Revert error: get name error");
+}
+
+/*
+ * Load a resource from persistent repository and enter the scope
+ * of that resource.
+ */
+void
+select_func(cmd_t *cmd)
+{
+ nwam_error_t ret;
+ char *name, *realname = NULL;
+
+ assert(cmd->cmd_argc > 0);
+ if (current_scope == NWAM_SCOPE_NCP && cmd->cmd_res2_type != RT2_NCU) {
+ nerr("cannot select '%s' at this scope",
+ rt1_to_str(cmd->cmd_res1_type));
+ return;
+ }
+
+ /* argv[0] is name */
+ name = trim_quotes(cmd->cmd_argv[0]);
+ switch (cmd->cmd_res1_type) {
+ case RT1_LOC:
+ ret = nwam_loc_read(name, 0, &loc_h);
+ if (ret == NWAM_SUCCESS) {
+ current_scope = NWAM_SCOPE_LOC;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_LOC,
+ loc_h, &realname);
+ }
+ break;
+ case RT1_ENM:
+ ret = nwam_enm_read(name, 0, &enm_h);
+ if (ret == NWAM_SUCCESS) {
+ current_scope = NWAM_SCOPE_ENM;
+ (void) object_name_from_handle(NWAM_OBJECT_TYPE_ENM,
+ enm_h, &realname);
+ }
+ break;
+ case RT1_WLAN:
+ ret = nwam_known_wlan_read(name, 0, &wlan_h);
+ if (ret == NWAM_SUCCESS) {
+ current_scope = NWAM_SCOPE_WLAN;
+ (void) object_name_from_handle
+ (NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan_h, &realname);
+ }
+ break;
+ case RT1_NCP:
+ if (cmd->cmd_res2_type == RT2_NCU) {
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ /* ncp must already be read */
+ if (ncp_h == NULL) {
+ nerr("Select error: NCP has not been read");
+ free(name);
+ return;
+ }
+ ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
+ ncu_type = nwam_ncu_class_to_type(ncu_class);
+ ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
+ if (ret == NWAM_SUCCESS) {
+ current_scope = NWAM_SCOPE_NCU;
+ (void) object_name_from_handle
+ (NWAM_OBJECT_TYPE_NCU, ncu_h, &realname);
+ }
+ } else {
+ ret = nwam_ncp_read(name, 0, &ncp_h);
+ if (ret == NWAM_SUCCESS) {
+ current_scope = NWAM_SCOPE_NCP;
+ (void) object_name_from_handle
+ (NWAM_OBJECT_TYPE_NCP, ncp_h, &realname);
+ }
+ }
+ break;
+ default:
+ nerr("Select error: unknown object-type");
+ free(name);
+ return;
+ }
+
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Select error");
+ } else {
+ /* set the obj*_name or obj*_type depending on current scope */
+ if (current_scope == NWAM_SCOPE_NCU) {
+ obj2_type = RT2_NCU;
+ (void) strlcpy(obj2_name,
+ realname != NULL ? realname : name,
+ sizeof (obj2_name));
+ } else {
+ (void) strlcpy(obj1_name,
+ realname != NULL ? realname : name,
+ sizeof (obj1_name));
+ obj1_type = cmd->cmd_res1_type;
+ }
+ }
+ free(name);
+ free(realname);
+}
+
+/* Given an int for prop, returns it as string */
+static const char *
+pt_to_prop_name(nwam_object_type_t object_type, int pt_type)
+{
+ int i;
+ prop_table_entry_t *prop_table = get_prop_table(object_type);
+
+ for (i = 0; prop_table[i].pte_name != NULL; i++) {
+ if (pt_type == prop_table[i].pte_type)
+ return (prop_table[i].pte_name);
+ }
+ return (NULL);
+}
+
+/* Given a prop as a string, returns it as an int */
+static int
+prop_to_pt(nwam_object_type_t object_type, const char *prop)
+{
+ int i;
+ prop_table_entry_t *prop_table = get_prop_table(object_type);
+
+ for (i = 0; prop_table[i].pte_name != NULL; i++) {
+ if (strcmp(prop, prop_table[i].pte_name) == 0)
+ return (prop_table[i].pte_type);
+ }
+ return (-1);
+}
+
+/* Given a prop as an int, returns its type (nwam_value_type_t) */
+static nwam_value_type_t
+prop_value_type(nwam_object_type_t object_type, const char *prop)
+{
+ nwam_error_t ret;
+ nwam_value_type_t value_type;
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_prop_type(prop, &value_type);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_prop_type(prop, &value_type);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_prop_type(prop, &value_type);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_get_prop_type(prop, &value_type);
+ break;
+ }
+
+ if (ret != NWAM_SUCCESS)
+ value_type = NWAM_VALUE_TYPE_UNKNOWN;
+
+ return (value_type);
+}
+
+/*
+ * Converts input_str to an array nwam_value.
+ * If is_list_prop, break input_str into array of strings first.
+ */
+static nwam_value_t
+str_to_nwam_value(nwam_object_type_t object_type, char *input_str, int pt_type,
+ boolean_t is_list_prop)
+{
+ int i, n = 0, ret;
+ nwam_value_t data;
+ char **val;
+ int max_str_num;
+
+ nwam_value_type_t value_type;
+ int64_t *int_vals;
+ uint64_t *uint_vals;
+ boolean_t *boolean_vals;
+
+ /*
+ * Worst case is that each char separated by DELIMITER, so the
+ * max number of sub strings is half of string length + 1.
+ */
+ max_str_num = strlen(input_str) / 2 + 1;
+
+ val = calloc(max_str_num, sizeof (char *));
+ if (val == NULL) {
+ nerr("Out of memory");
+ return (NULL);
+ }
+
+ if (is_list_prop) {
+ char *tmp, *next;
+ /*
+ * Break down input_str and save as array of sub strings.
+ * Set num as the number of the sub strings.
+ * Use nwam_tokenize_by_unescaped_delim() rather than strtok()
+ * because DELIMITER may be escaped
+ */
+ tmp = (char *)input_str;
+ while ((tmp = nwam_tokenize_by_unescaped_delim(tmp,
+ NWAM_VALUE_DELIMITER_CHAR, &next)) != NULL) {
+ val[n++] = trim_quotes(tmp);
+ tmp = next;
+ }
+ } else {
+ val[n++] = trim_quotes(input_str);
+ }
+
+ /* initialize int_vals or booleans_vals depending on pt_type */
+ value_type = prop_value_type(object_type,
+ pt_to_prop_name(object_type, pt_type));
+ if (value_type == NWAM_VALUE_TYPE_INT64) {
+ int_vals = calloc(n, sizeof (int64_t));
+ if (int_vals == NULL) {
+ nerr("Out of memory");
+ array_free((void **)val, max_str_num);
+ return (NULL);
+ }
+ } else if (value_type == NWAM_VALUE_TYPE_UINT64) {
+ uint_vals = calloc(n, sizeof (uint64_t));
+ if (uint_vals == NULL) {
+ nerr("Out of memory");
+ array_free((void **)val, max_str_num);
+ return (NULL);
+ }
+ } else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
+ boolean_vals = calloc(n, sizeof (boolean_t));
+ if (boolean_vals == NULL) {
+ nerr("Out of memory");
+ array_free((void **)val, max_str_num);
+ return (NULL);
+ }
+ }
+ /* set the appropriate array */
+ for (i = 0; i < n; i++) {
+ switch (value_type) {
+ case NWAM_VALUE_TYPE_STRING:
+ /* nothing to do - val already has the char** array */
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ {
+ int_vals[i] = (int64_t)atoi(val[i]);
+ break;
+ }
+ case NWAM_VALUE_TYPE_UINT64:
+ {
+ uint64_t str_as_enum;
+ char *endptr;
+
+ ret = nwam_value_string_get_uint64(
+ pt_to_prop_name(object_type, pt_type),
+ val[i], &str_as_enum);
+ /*
+ * Returns _SUCCESS if value for enum is valid.
+ * Returns _INVALID_ARG if property is not an enum.
+ */
+ if (ret == NWAM_SUCCESS) {
+ uint_vals[i] = str_as_enum;
+ } else if (ret == NWAM_INVALID_ARG) {
+ uint_vals[i] = strtoul(val[i], &endptr, 10);
+ /* verify conversion is valid */
+ if (endptr == val[i]) {
+ free(uint_vals);
+ array_free((void **)val, max_str_num);
+ return (NULL);
+ }
+ } else {
+ free(uint_vals);
+ array_free((void **)val, max_str_num);
+ return (NULL);
+ }
+ break;
+ }
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ boolean_vals[i] = str_to_boolean(val[i]);
+ break;
+ default:
+ array_free((void **)val, max_str_num);
+ return (NULL);
+ }
+ }
+
+ /* create nwam_value_t */
+ if (value_type == NWAM_VALUE_TYPE_STRING) {
+ ret = nwam_value_create_string_array(val, n, &data);
+ } else if (value_type == NWAM_VALUE_TYPE_INT64) {
+ ret = nwam_value_create_int64_array(int_vals, n, &data);
+ free(int_vals);
+ } else if (value_type == NWAM_VALUE_TYPE_UINT64) {
+ ret = nwam_value_create_uint64_array(uint_vals, n, &data);
+ free(uint_vals);
+ } else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
+ ret = nwam_value_create_boolean_array(boolean_vals, n, &data);
+ free(boolean_vals);
+ }
+ array_free((void **)val, max_str_num);
+
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Failed creating nwam_value");
+ return (NULL);
+ }
+
+ return (data);
+}
+
+/*
+ * Displaying/Skipping of properties
+ * ---------------------------------
+ *
+ * This table shows if a specific property should be shown if some
+ * other property has a specific value. This table is used by
+ * show_prop_test(), which is called by set_func() and walkprop_func().
+ *
+ * An entry in the table looks like:
+ * { property1, property2, { val1, val2, -1 } }
+ * This is read as:
+ * "show property1 only if property2 has value val1 or val2"
+ *
+ * NB: If a property does not appear in this table, then that implies
+ * that the property is always shown.
+ *
+ * A property can have more than one rule. In such a case, the property is
+ * displayed only any of the rules is satisfied. This checking, however,
+ * is recursive. If a rule says that a property can be displayed, then the
+ * property that's checked should also satisfy its rules. In the above
+ * example, if property1 is to be displayed, then property2 should also
+ * satisfy its rules and be displayable. This recursion is necessary as
+ * properties that are not displayed (because rules are not satisfied) are
+ * not deleted.
+ */
+
+/* The most number of values in pde_checkvals below */
+#define NWAM_CHECKVALS_MAX 5
+
+typedef struct prop_display_entry {
+ const char *pde_name; /* property to show */
+ const char *pde_checkname; /* property to check */
+ int64_t pde_checkvals[NWAM_CHECKVALS_MAX]; /* show prop for these */
+} prop_display_entry_t;
+
+/* Rules for showing properties: commented for clarity */
+
+/*
+ * Rules for NCUs
+ * NB: There is no need to have an entry if a property is for IP only.
+ * This is taken care of in libnwam_ncp.c
+ */
+static prop_display_entry_t ncu_prop_display_entry_table[] = {
+ /* show priority-{group,mode} if activation == prioritized */
+ { NWAM_NCU_PROP_PRIORITY_GROUP, NWAM_NCU_PROP_ACTIVATION_MODE,
+ { NWAM_ACTIVATION_MODE_PRIORITIZED, -1 } },
+ { NWAM_NCU_PROP_PRIORITY_MODE, NWAM_NCU_PROP_ACTIVATION_MODE,
+ { NWAM_ACTIVATION_MODE_PRIORITIZED, -1 } },
+ /* show ipv4-addrsrc if ip-version == ipv4 */
+ { NWAM_NCU_PROP_IPV4_ADDRSRC, NWAM_NCU_PROP_IP_VERSION,
+ { IPV4_VERSION, -1 } },
+ /* show ipv4-addr if ipv4-addrsrc == static */
+ { NWAM_NCU_PROP_IPV4_ADDR, NWAM_NCU_PROP_IPV4_ADDRSRC,
+ { NWAM_ADDRSRC_STATIC, -1 } },
+ /* show ipv4-default-route if ip-version == ipv4 */
+ { NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
+ { IPV4_VERSION, -1 } },
+ /* show ipv6-addrsrc if ip-version == ipv6 */
+ { NWAM_NCU_PROP_IPV6_ADDRSRC, NWAM_NCU_PROP_IP_VERSION,
+ { IPV6_VERSION, -1 } },
+ /* show ipv6-addr if ipv6-addrsrc == static */
+ { NWAM_NCU_PROP_IPV6_ADDR, NWAM_NCU_PROP_IPV6_ADDRSRC,
+ { NWAM_ADDRSRC_STATIC, -1 } },
+ /* show ipv6-default-route if ip-version == ipv6 */
+ { NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
+ { IPV6_VERSION, -1 } },
+ { NULL, NULL, { -1 } }
+};
+
+/* Rules for ENMs */
+static prop_display_entry_t enm_prop_display_entry_table[] = {
+ /* show conditions if activation-mode == conditional-{all,any} */
+ { NWAM_ENM_PROP_CONDITIONS, NWAM_ENM_PROP_ACTIVATION_MODE,
+ { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ANY, -1 } },
+ { NULL, NULL, { -1 } }
+};
+
+/* Rules for LOCations */
+static prop_display_entry_t loc_prop_display_entry_table[] = {
+ /* show conditions if activation-mode == conditional-{all,any} */
+ { NWAM_LOC_PROP_CONDITIONS, NWAM_LOC_PROP_ACTIVATION_MODE,
+ { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ANY, -1 } },
+ /* show dns-nameservice-configsrc if nameservices == dns */
+ { NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
+ { NWAM_NAMESERVICES_DNS, -1 } },
+ /* show other DNS options if dns-nameservices-configsrc == manual */
+ { NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ { NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ { NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ /* show nis-nameservice-configsrc if nameservices == nis */
+ { NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
+ { NWAM_NAMESERVICES_NIS, -1 } },
+ /* show nis-nameservice-servers if nis-nameservice-configsrc = manual */
+ { NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS,
+ NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ /* show ldap-nameservice-configsrc if nameservices == ldap */
+ { NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
+ { NWAM_NAMESERVICES_LDAP, -1 } },
+ /* show ldap-nameservice-servers if ldap-nameservice-configsrc=manual */
+ { NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS,
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ /* show default-domain if {nis,ldap}-nameservice-configsrc == manual */
+ { NWAM_LOC_PROP_DEFAULT_DOMAIN, NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ { NWAM_LOC_PROP_DEFAULT_DOMAIN,
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
+ { NWAM_CONFIGSRC_MANUAL, -1 } },
+ { NULL, NULL, { -1 } }
+};
+
+/* Rules for Known WLANs */
+static prop_display_entry_t wlan_prop_display_entry_table[] = {
+ /* no rules for WLANs */
+ { NULL, NULL, { -1 } }
+};
+
+/* Returns the appropriate rules table for the given object type */
+static prop_display_entry_t *
+get_prop_display_table(nwam_object_type_t object_type)
+{
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ return (ncu_prop_display_entry_table);
+ case NWAM_OBJECT_TYPE_LOC:
+ return (loc_prop_display_entry_table);
+ case NWAM_OBJECT_TYPE_ENM:
+ return (enm_prop_display_entry_table);
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ return (wlan_prop_display_entry_table);
+ }
+ return (NULL);
+}
+
+/*
+ * Tests whether prop must be shown during a walk depending on the
+ * value of a different property.
+ *
+ * This function is also used by set_func() to determine whether the
+ * property being set should be allowed or not. If the property
+ * would not be displayed in a walk, then it should not be set.
+ *
+ * The checked_props and num_checked arguments are used to avoid circular
+ * dependencies between properties. When this function recursively calls
+ * itself, it adds the property that it just checked to the checked_props
+ * list.
+ */
+static boolean_t
+show_prop_test(nwam_object_type_t object_type, const char *prop,
+ prop_display_entry_t *display_list, char **checked_props, int num_checked)
+{
+ nwam_error_t ret;
+ nwam_value_t prop_val;
+ nwam_value_type_t prop_type;
+ int i, j, k;
+ boolean_t prop_found = B_FALSE, show_prop = B_FALSE;
+
+ /*
+ * Check if this property has already been checked previously in
+ * the recursion. If so, return B_FALSE so that the initial prop
+ * is not displayed.
+ */
+ for (i = 0; i < num_checked; i++) {
+ if (strcmp(prop, checked_props[i]) == 0) {
+ free(checked_props);
+ return (B_FALSE);
+ }
+ }
+
+ for (i = 0; display_list[i].pde_name != NULL; i++) {
+ if (strcmp(prop, display_list[i].pde_name) != 0)
+ continue;
+ prop_found = B_TRUE;
+
+ /* get the value(s) of the (other) property to check */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_prop_value(ncu_h,
+ display_list[i].pde_checkname, &prop_val);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_prop_value(loc_h,
+ display_list[i].pde_checkname, &prop_val);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_prop_value(enm_h,
+ display_list[i].pde_checkname, &prop_val);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ return (B_TRUE);
+ }
+ if (ret != NWAM_SUCCESS)
+ continue;
+
+ /* prop_val may contain a uint64 array or a boolean */
+ if (nwam_value_get_type(prop_val, &prop_type) != NWAM_SUCCESS)
+ continue;
+
+ if (prop_type == NWAM_VALUE_TYPE_UINT64) {
+ uint64_t *prop_uvals;
+ int64_t *check_uvals;
+ uint_t numvals;
+
+ if (nwam_value_get_uint64_array(prop_val, &prop_uvals,
+ &numvals) != NWAM_SUCCESS) {
+ nwam_value_free(prop_val);
+ continue;
+ }
+
+ /* for each value in uvals, check each value in table */
+ for (j = 0; j < numvals; j++) {
+ check_uvals = display_list[i].pde_checkvals;
+ for (k = 0; check_uvals[k] != -1; k++) {
+ /* show if uvals[j] matches */
+ if (prop_uvals[j] ==
+ (uint64_t)check_uvals[k]) {
+ show_prop = B_TRUE;
+ goto next_rule;
+ }
+ }
+ }
+ } else if (prop_type == NWAM_VALUE_TYPE_BOOLEAN) {
+ boolean_t bval;
+
+ if (nwam_value_get_boolean(prop_val, &bval) !=
+ NWAM_SUCCESS) {
+ nwam_value_free(prop_val);
+ continue;
+ }
+
+ for (k = 0;
+ display_list[i].pde_checkvals[k] != -1;
+ k++) {
+ /* show if bval matches */
+ if (bval == (boolean_t)
+ display_list[i].pde_checkvals[k]) {
+ show_prop = B_TRUE;
+ goto next_rule;
+ }
+ }
+ }
+
+next_rule:
+ nwam_value_free(prop_val);
+ /*
+ * If show_prop is set, then a rule is satisfied; no need to
+ * check other rules for this prop. However, recursively
+ * check if the checked prop (pde_checkname) satisfies its
+ * rules. Also, update the check_props array with this prop.
+ */
+ if (show_prop) {
+ char **newprops = realloc(checked_props,
+ ++num_checked * sizeof (char *));
+ if (newprops == NULL) {
+ free(checked_props);
+ return (B_FALSE);
+ }
+ checked_props = newprops;
+ checked_props[num_checked - 1] = (char *)prop;
+
+ return (show_prop_test(object_type,
+ display_list[i].pde_checkname, display_list,
+ checked_props, num_checked));
+ }
+ }
+
+ /*
+ * If we are here and prop_found is set, it means that no rules were
+ * satisfied by prop; return B_FALSE. If prop_found is not set, then
+ * prop did not have a rule so it must be displayed; return B_TRUE.
+ */
+ free(checked_props);
+ if (prop_found)
+ return (B_FALSE);
+ else
+ return (B_TRUE);
+}
+
+/*
+ * Returns true if the given property is read-only and cannot be modified.
+ */
+static boolean_t
+is_prop_read_only(nwam_object_type_t object_type, const char *prop)
+{
+ boolean_t ro;
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ if (nwam_ncu_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
+ return (B_TRUE);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ if (nwam_enm_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
+ return (B_TRUE);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ if (nwam_loc_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
+ return (B_TRUE);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ /* no read-only properties for WLANs */
+ return (B_FALSE);
+ }
+ return (B_FALSE);
+}
+
+/* Returns true if the property is multi-valued */
+static boolean_t
+is_prop_multivalued(nwam_object_type_t object_type, const char *prop)
+{
+ nwam_error_t ret;
+ boolean_t multi;
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_prop_multivalued(prop, &multi);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_prop_multivalued(prop, &multi);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_prop_multivalued(prop, &multi);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_prop_multivalued(prop, &multi);
+ break;
+ }
+
+ if (ret != NWAM_SUCCESS)
+ multi = B_FALSE;
+ return (multi);
+}
+
+/*
+ * Prints out error message specific to property that could not be set.
+ * Property description is used to help guide user in entering correct value.
+ */
+static void
+invalid_set_prop_msg(const char *prop, nwam_error_t err)
+{
+ const char *description;
+
+ if (err == NWAM_SUCCESS)
+ return;
+
+ if (err != NWAM_ENTITY_INVALID_VALUE) {
+ nwamerr(err, "Set error");
+ return;
+ }
+
+ switch (active_object_type()) {
+ case NWAM_OBJECT_TYPE_NCU:
+ (void) nwam_ncu_get_prop_description(prop, &description);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ (void) nwam_loc_get_prop_description(prop, &description);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ (void) nwam_enm_get_prop_description(prop, &description);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ (void) nwam_known_wlan_get_prop_description(prop,
+ &description);
+ break;
+ }
+ nerr("Set error: invalid value\n'%s' %s", prop, description);
+}
+
+/*
+ * Sets the property value.
+ * Read-only properties and objects cannot be set.
+ * "read-only" is a special in that it can be set on a read-only object.
+ * The object has to be committed before other properties can be set.
+ * Also uses show_prop_test() to test if the property being set would
+ * be skipped during a walk (as determined by the value of some other
+ * property). If so, then it cannot be set.
+ */
+void
+set_func(cmd_t *cmd)
+{
+ int pt_type = cmd->cmd_prop_type;
+ nwam_error_t ret = NWAM_SUCCESS;
+ nwam_value_t prop_value;
+ const char *prop;
+ boolean_t is_listprop = B_FALSE;
+ nwam_object_type_t object_type;
+ prop_display_entry_t *prop_table;
+ char **checked = NULL;
+
+ assert(cmd->cmd_argc > 0);
+
+ object_type = active_object_type();
+ prop_table = get_prop_display_table(object_type);
+
+ /* argv[0] is property value */
+ if ((prop = pt_to_prop_name(object_type, pt_type)) == NULL) {
+ nerr("Set error: invalid %s property: '%s'",
+ scope_to_str(current_scope), pt_to_str(pt_type));
+ return;
+ }
+
+ /* check if property can be set */
+ if (is_prop_read_only(object_type, prop)) {
+ nerr("Set error: property '%s' is read-only", prop);
+ return;
+ }
+ if (!show_prop_test(object_type, prop, prop_table, checked, 0)) {
+ if (interactive_mode) {
+ (void) printf(gettext("setting property '%s' "
+ "has no effect\n"), prop);
+ }
+ }
+
+ is_listprop = is_prop_multivalued(object_type, prop);
+ prop_value = str_to_nwam_value(object_type, cmd->cmd_argv[0], pt_type,
+ is_listprop);
+ if (prop_value == NULL) {
+ invalid_set_prop_msg(prop, NWAM_ENTITY_INVALID_VALUE);
+ return;
+ }
+
+ /* set the property value */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_set_prop_value(ncu_h, prop, prop_value);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_set_prop_value(loc_h, prop, prop_value);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_set_prop_value(enm_h, prop, prop_value);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_set_prop_value(wlan_h, prop, prop_value);
+ break;
+ }
+ nwam_value_free(prop_value);
+
+ /* delete other properties if needed */
+ if (ret == NWAM_SUCCESS)
+ need_to_commit = B_TRUE;
+ else
+ invalid_set_prop_msg(prop, ret);
+}
+
+static int
+list_callback(nwam_object_type_t object_type, void *handle,
+ boolean_t *list_msgp, const char *msg)
+{
+ nwam_error_t ret;
+ char *name;
+ nwam_ncu_class_t class;
+
+ if (*list_msgp) {
+ (void) printf("%s:\n", msg);
+ *list_msgp = B_FALSE;
+ }
+
+ ret = object_name_from_handle(object_type, handle, &name);
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "List error: failed to get name");
+ return (1);
+ }
+
+ /* If NCU, get its class and print */
+ if (object_type == NWAM_OBJECT_TYPE_NCU) {
+ if ((ret = nwam_ncu_get_ncu_class(handle, &class))
+ != NWAM_SUCCESS) {
+ nwamerr(ret, "List error: failed to get ncu class");
+ free(name);
+ return (1);
+ } else {
+ (void) printf("\t%s",
+ propval_to_str(NWAM_NCU_PROP_CLASS, class));
+ }
+ }
+ (void) printf("\t%s\n", name);
+
+ free(name);
+ return (0);
+}
+
+/* Print out name, type and status */
+static int
+list_loc_callback(nwam_loc_handle_t loc, void *arg)
+{
+ return (list_callback(NWAM_OBJECT_TYPE_LOC, loc, arg, "Locations"));
+}
+
+static int
+list_enm_callback(nwam_enm_handle_t enm, void *arg)
+{
+ return (list_callback(NWAM_OBJECT_TYPE_ENM, enm, arg, "ENMs"));
+}
+
+static int
+list_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
+{
+ return (list_callback(NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan, arg, "WLANs"));
+}
+
+static int
+list_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
+{
+ return (list_callback(NWAM_OBJECT_TYPE_NCP, ncp, arg, "NCPs"));
+}
+
+static int
+list_ncu_callback(nwam_ncu_handle_t ncu, void *arg)
+{
+ return (list_callback(NWAM_OBJECT_TYPE_NCU, ncu, arg, "NCUs"));
+}
+
+/* functions to convert a value to a string */
+/* ARGSUSED */
+static const char *
+str2str(void *s, const char *prop, char *str)
+{
+ (void) snprintf(str, NWAM_MAX_VALUE_LEN, "%s", s);
+ return (str);
+}
+
+/* ARGSUSED */
+static const char *
+str2qstr(void *s, const char *prop, char *qstr)
+{
+ /* quoted strings */
+ (void) snprintf(qstr, NWAM_MAX_VALUE_LEN, "\"%s\"", s);
+ return (qstr);
+}
+
+/* ARGSUSED */
+static const char *
+int2str(void *in, const char *prop, char *instr)
+{
+ (void) snprintf(instr, NWAM_MAX_VALUE_LEN, "%lld", *((int64_t *)in));
+ return (instr);
+}
+
+static const char *
+uint2str(void *uin, const char *prop, char *uintstr)
+{
+ /* returns NWAM_SUCCESS if prop is enum with string in uintstr */
+ if (nwam_uint64_get_value_string(prop, *((uint64_t *)uin),
+ (const char **)&uintstr) != NWAM_SUCCESS) {
+ (void) snprintf(uintstr, NWAM_MAX_VALUE_LEN, "%lld",
+ *((uint64_t *)uin));
+ }
+ return (uintstr);
+}
+
+/* ARGSUSED */
+static const char *
+bool2str(void *bool, const char *prop, char *boolstr)
+{
+ (void) snprintf(boolstr, NWAM_MAX_VALUE_LEN, "%s",
+ *((boolean_t *)bool) ? "true" : "false");
+ return (boolstr);
+}
+
+/*
+ * Print the value (enums are converted to string), use DELIMITER for
+ * array. If strings are to be "quoted", pass B_TRUE for quoted_strings.
+ */
+static void
+output_prop_val(const char *prop_name, nwam_value_t value, FILE *wf,
+ boolean_t quoted_strings)
+{
+ nwam_value_type_t value_type;
+ uint_t num;
+
+ /* arrays for values retrieved according to the type of value */
+ char **svals;
+ uint64_t *uvals;
+ int64_t *ivals;
+ boolean_t *bvals;
+
+ /* pointer to function to generate string representation of value */
+ const char *(*tostr)(void *, const char *, char *);
+ char str[NWAM_MAX_VALUE_LEN]; /* to store the string */
+ int i;
+
+ if (nwam_value_get_type(value, &value_type) != NWAM_SUCCESS) {
+ nerr("Get value type error");
+ return;
+ }
+
+ if (value_type == NWAM_VALUE_TYPE_STRING) {
+ if (nwam_value_get_string_array(value, &svals, &num) !=
+ NWAM_SUCCESS) {
+ nerr("Get string array error");
+ return;
+ }
+ tostr = quoted_strings ? str2qstr : str2str;
+ } else if (value_type == NWAM_VALUE_TYPE_INT64) {
+ if (nwam_value_get_int64_array(value, &ivals, &num) !=
+ NWAM_SUCCESS) {
+ nerr("Get int64 array error");
+ return;
+ }
+ tostr = int2str;
+ } else if (value_type == NWAM_VALUE_TYPE_UINT64) {
+ if (nwam_value_get_uint64_array(value, &uvals, &num) !=
+ NWAM_SUCCESS) {
+ nerr("Get uint64 array error");
+ return;
+ }
+ tostr = uint2str;
+ } else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
+ if (nwam_value_get_boolean_array(value, &bvals, &num) !=
+ NWAM_SUCCESS) {
+ nerr("Get boolean array error");
+ return;
+ }
+ tostr = bool2str;
+ }
+
+ /* now, loop and print each value */
+ for (i = 0; i < num; i++) {
+ void *val;
+
+ /* get the pointer to the ith value to pass to func() */
+ if (value_type == NWAM_VALUE_TYPE_STRING)
+ val = svals[i];
+ else if (value_type == NWAM_VALUE_TYPE_UINT64)
+ val = &(uvals[i]);
+ else if (value_type == NWAM_VALUE_TYPE_INT64)
+ val = &(ivals[i]);
+ else if (value_type == NWAM_VALUE_TYPE_BOOLEAN)
+ val = &(bvals[i]);
+
+ (void) fprintf(wf, "%s%s", tostr(val, prop_name, str),
+ i != num-1 ? NWAM_VALUE_DELIMITER_STR : "");
+ }
+}
+
+/* Prints the property names aligned (for list/get) or "prop=" (for export) */
+static int
+output_propname_common(const char *prop, nwam_value_t values, void *arg,
+ int width)
+{
+ FILE *of = (arg == NULL) ? stdout : arg;
+
+ /* arg is NULL for list/get, not NULL for export */
+ if (arg == NULL)
+ (void) fprintf(of, "\t%-*s\t", width, prop);
+ else
+ (void) fprintf(of, "%s=", prop);
+
+ if (values != NULL)
+ output_prop_val(prop, values, of, B_TRUE);
+
+ (void) fprintf(of, "\n");
+ return (0);
+}
+
+static int
+output_propname(const char *prop, nwam_value_t values, void *arg)
+{
+ return (output_propname_common(prop, values, arg, 16));
+}
+
+/* For locations because of longer property names */
+static int
+output_loc_propname(const char *prop, nwam_value_t values, void *arg)
+{
+ return (output_propname_common(prop, values, arg, 25));
+}
+
+/*
+ * all_props specifies whether properties that have not been set should be
+ * printed or not. ncp and ncu_type are used only when the object_type is
+ * NCU.
+ */
+static nwam_error_t
+listprop(nwam_object_type_t object_type, void *handle, const char *name,
+ boolean_t all_props, nwam_ncp_handle_t ncp, nwam_ncu_type_t ncu_type)
+{
+ nwam_error_t ret;
+ char *lname = NULL, *realname = NULL;
+ boolean_t lhandle = B_FALSE;
+ const char **props = NULL;
+ uint_t prop_num;
+ int i;
+ nwam_value_t vals;
+
+ /*
+ * handle is NULL if called from a scope higher than the object's
+ * scope, but name must be given; so get the handle.
+ */
+ if (handle == NULL) {
+ lname = trim_quotes(name); /* name may have quotes */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCP:
+ if ((ret = nwam_ncp_read(lname, 0,
+ (nwam_ncp_handle_t *)&handle)) != NWAM_SUCCESS)
+ goto readfail;
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_read(ncp, lname, ncu_type, 0,
+ (nwam_ncu_handle_t *)&handle);
+ if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
+ /*
+ * Multiple NCUs with the given name exists.
+ * Call listprop() for each NCU type.
+ */
+ if ((ret = listprop(object_type, NULL, lname,
+ all_props, ncp, NWAM_NCU_TYPE_LINK))
+ != NWAM_SUCCESS)
+ goto done;
+ ret = listprop(object_type, NULL, lname,
+ all_props, ncp, NWAM_NCU_TYPE_INTERFACE);
+ goto done;
+ } else if (ret != NWAM_SUCCESS) {
+ goto readfail;
+ }
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ if ((ret = nwam_loc_read(lname, 0,
+ (nwam_loc_handle_t *)&handle)) != NWAM_SUCCESS)
+ goto readfail;
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ if ((ret = nwam_enm_read(lname, 0,
+ (nwam_enm_handle_t *)&handle)) != NWAM_SUCCESS)
+ goto readfail;
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ if ((ret = nwam_known_wlan_read(lname, 0,
+ (nwam_known_wlan_handle_t *)&handle))
+ != NWAM_SUCCESS)
+ goto readfail;
+ break;
+ }
+ lhandle = B_TRUE;
+ }
+
+ if ((ret = object_name_from_handle(object_type, handle, &realname))
+ != NWAM_SUCCESS)
+ goto done;
+
+ /* get the property list */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCP:
+ {
+ /* walk NCUs */
+ boolean_t list_msg = B_TRUE;
+ ret = nwam_ncp_walk_ncus(handle, list_ncu_callback, &list_msg,
+ NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
+ goto done;
+ }
+ case NWAM_OBJECT_TYPE_NCU:
+ {
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ if ((ret = nwam_ncu_get_ncu_type(handle, &ncu_type))
+ != NWAM_SUCCESS)
+ goto done;
+ if ((ret = nwam_ncu_get_ncu_class(handle, &ncu_class))
+ != NWAM_SUCCESS)
+ goto done;
+
+ ret = nwam_ncu_get_default_proplist(ncu_type, ncu_class, &props,
+ &prop_num);
+ break;
+ }
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_default_proplist(&props, &prop_num);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_default_proplist(&props, &prop_num);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_get_default_proplist(&props, &prop_num);
+ break;
+ }
+ if (ret != NWAM_SUCCESS)
+ goto done;
+
+ /* print object type and name */
+ (void) printf("%s:%s\n", nwam_object_type_to_string(object_type),
+ realname);
+
+ /* Loop through the properties and print */
+ for (i = 0; i < prop_num; i++) {
+ /* get the existing value for this property */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_prop_value(handle, props[i], &vals);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_prop_value(handle, props[i], &vals);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_prop_value(handle, props[i], &vals);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_get_prop_value(handle, props[i],
+ &vals);
+ break;
+ }
+ if (ret != NWAM_SUCCESS) {
+ /* _ENTITY_NOT_FOUND is ok if listing for all props */
+ if (!all_props)
+ continue;
+ else if (ret != NWAM_ENTITY_NOT_FOUND)
+ continue;
+ }
+
+ /* print property and value */
+ if (object_type == NWAM_OBJECT_TYPE_LOC)
+ output_loc_propname(props[i], vals, NULL);
+ else
+ output_propname(props[i], vals, NULL);
+ nwam_value_free(vals);
+ }
+
+done:
+ free(lname);
+ free(realname);
+ if (props != NULL)
+ free(props);
+ if (lhandle) {
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCP:
+ nwam_ncp_free(handle);
+ break;
+ case NWAM_OBJECT_TYPE_NCU:
+ nwam_ncu_free(handle);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ nwam_loc_free(handle);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ nwam_enm_free(handle);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ nwam_known_wlan_free(handle);
+ break;
+ }
+ }
+ /* don't treat _ENTITY_NOT_FOUND as an error */
+ if (ret == NWAM_ENTITY_NOT_FOUND)
+ ret = NWAM_SUCCESS;
+ return (ret);
+
+readfail:
+ /* When nwam_*_read() fails */
+ free(lname);
+ return (ret);
+}
+
+/*
+ * List profiles or property and its values.
+ * If the -a option is specified, all properties are listed.
+ */
+void
+list_func(cmd_t *cmd)
+{
+ nwam_error_t ret = NWAM_SUCCESS;
+ boolean_t list_msg = B_TRUE;
+
+ boolean_t list_loc = B_FALSE, list_enm = B_FALSE;
+ boolean_t list_ncp = B_FALSE, list_ncu = B_FALSE;
+ boolean_t list_wlan = B_FALSE;
+
+ /* whether all properties should be listed, given by the -a option */
+ boolean_t all_props = B_FALSE;
+
+ /*
+ * list_props says whether the properties should be listed.
+ * Note that, here NCUs are treated as properties of NCPs.
+ */
+ boolean_t list_props = B_FALSE;
+
+ /* determine which properties to list, also validity tests */
+ if (current_scope == NWAM_SCOPE_GBL) {
+ /* res1_type is -1 if only "list -a" is used */
+ if (cmd->cmd_res1_type == -1) {
+ nerr("'list' requires an object to be specified with "
+ "the -a option in the global scope");
+ return;
+ }
+ if (cmd->cmd_res1_type == RT1_LOC) {
+ list_props = B_TRUE;
+ list_loc = B_TRUE;
+ } else if (cmd->cmd_res1_type == RT1_ENM) {
+ list_props = B_TRUE;
+ list_enm = B_TRUE;
+ } else if (cmd->cmd_res1_type == RT1_WLAN) {
+ list_props = B_TRUE;
+ list_wlan = B_TRUE;
+ } else if (cmd->cmd_res1_type == RT1_NCP) {
+ list_ncp = B_TRUE;
+ list_props = B_TRUE;
+ } else {
+ list_loc = B_TRUE;
+ list_enm = B_TRUE;
+ list_wlan = B_TRUE;
+ list_ncp = B_TRUE;
+ }
+ }
+ if ((current_scope == NWAM_SCOPE_LOC ||
+ current_scope == NWAM_SCOPE_ENM ||
+ current_scope == NWAM_SCOPE_WLAN ||
+ current_scope == NWAM_SCOPE_NCU) &&
+ (cmd->cmd_argc >= 1 && cmd->cmd_res1_type != -1)) {
+ nerr("Additional options are not allowed with the -a option "
+ "at this scope");
+ return;
+ }
+ if (current_scope == NWAM_SCOPE_LOC) {
+ list_loc = B_TRUE;
+ list_props = B_TRUE;
+ }
+ if (current_scope == NWAM_SCOPE_ENM) {
+ list_enm = B_TRUE;
+ list_props = B_TRUE;
+ }
+ if (current_scope == NWAM_SCOPE_WLAN) {
+ list_wlan = B_TRUE;
+ list_props = B_TRUE;
+ }
+ if (current_scope == NWAM_SCOPE_NCP) {
+ if (cmd->cmd_res1_type == RT1_ENM ||
+ cmd->cmd_res1_type == RT1_LOC ||
+ cmd->cmd_res1_type == RT1_WLAN) {
+ nerr("only ncu can be listed at this scope");
+ return;
+ }
+ if (cmd->cmd_res2_type == RT2_NCU) {
+ list_ncu = B_TRUE;
+ list_props = B_TRUE;
+ } else {
+ list_ncp = B_TRUE;
+ list_props = B_TRUE;
+ }
+ }
+ if (current_scope == NWAM_SCOPE_NCU) {
+ list_ncu = B_TRUE;
+ list_props = B_TRUE;
+ }
+
+ /* Check if the -a option is specified to list all properties */
+ if (cmd->cmd_res1_type == -1 || cmd->cmd_argc == 2) {
+ int c, argc = 1;
+ char **argv;
+ optind = 0;
+
+ /* if res1_type is -1, option is in argv[0], else in argv[1] */
+ if (cmd->cmd_res1_type == -1)
+ argv = cmd->cmd_argv;
+ else
+ argv = &(cmd->cmd_argv[1]);
+ while ((c = getopt(argc, argv, "a")) != EOF) {
+ switch (c) {
+ case 'a':
+ all_props = B_TRUE;
+ break;
+ default:
+ command_usage(CMD_LIST);
+ return;
+ }
+ }
+ if (cmd->cmd_res1_type == -1)
+ cmd->cmd_argv[0] = NULL;
+ }
+
+ /*
+ * Now, print objects and/or according to the flags set.
+ * name, if requested, is in argv[0].
+ */
+ if (list_ncp) {
+ list_msg = B_TRUE;
+ if (list_props) {
+ ret = listprop(NWAM_OBJECT_TYPE_NCP, ncp_h,
+ cmd->cmd_argv[0], all_props, NULL, -1);
+ } else {
+ ret = nwam_walk_ncps(list_ncp_callback, &list_msg, 0,
+ NULL);
+ }
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+
+ if (list_ncu) {
+ list_msg = B_TRUE;
+ if (ncp_h == NULL) {
+ nerr("NCP has not been read");
+ return;
+ }
+ if (list_props) {
+ nwam_ncu_class_t ncu_class;
+ nwam_ncu_type_t ncu_type;
+
+ /* determine the NCU type first */
+ if (ncu_h == NULL) {
+ ncu_class = (nwam_ncu_class_t)
+ cmd->cmd_ncu_class_type;
+ ncu_type = nwam_ncu_class_to_type(ncu_class);
+ } else {
+ if ((ret = nwam_ncu_get_ncu_type(ncu_h,
+ &ncu_type)) != NWAM_SUCCESS)
+ goto done;
+ }
+ ret = listprop(NWAM_OBJECT_TYPE_NCU, ncu_h,
+ cmd->cmd_argv[0], all_props, ncp_h, ncu_type);
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+ }
+
+ if (list_loc) {
+ list_msg = B_TRUE;
+ if (list_props) {
+ ret = listprop(NWAM_OBJECT_TYPE_LOC, loc_h,
+ cmd->cmd_argv[0], all_props, NULL, -1);
+ } else {
+ ret = nwam_walk_locs(list_loc_callback, &list_msg,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ }
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+
+ if (list_enm) {
+ list_msg = B_TRUE;
+ if (list_props) {
+ ret = listprop(NWAM_OBJECT_TYPE_ENM, enm_h,
+ cmd->cmd_argv[0], all_props, NULL, -1);
+ } else {
+ ret = nwam_walk_enms(list_enm_callback, &list_msg,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ }
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+
+ if (list_wlan) {
+ list_msg = B_TRUE;
+ if (list_props) {
+ ret = listprop(NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan_h,
+ cmd->cmd_argv[0], all_props, NULL, -1);
+ } else {
+ ret = nwam_walk_known_wlans(list_wlan_callback,
+ &list_msg, NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER,
+ NULL);
+ }
+ if (ret != NWAM_SUCCESS)
+ goto done;
+ }
+
+done:
+ if (ret != NWAM_SUCCESS)
+ nwamerr(ret, "List error");
+}
+
+static int
+write_export_command(nwam_object_type_t object_type, const char *prop,
+ nwam_value_t values, FILE *of)
+{
+ /* exclude read-only properties */
+ if (is_prop_read_only(object_type, prop))
+ return (0);
+
+ (void) fprintf(of, "set ");
+ output_propname(prop, values, of);
+ return (0);
+}
+
+static int
+export_ncu_callback(nwam_ncu_handle_t ncu, void *arg)
+{
+ char *name;
+ const char **props;
+ nwam_ncu_type_t type;
+ nwam_ncu_class_t class;
+ nwam_value_t vals;
+ nwam_error_t ret;
+ uint_t num;
+ int i;
+ FILE *of = arg;
+
+ assert(of != NULL);
+
+ /* get the NCU's type and class */
+ if ((ret = nwam_ncu_get_ncu_type(ncu, &type)) != NWAM_SUCCESS)
+ return (ret);
+ if ((ret = nwam_ncu_get_ncu_class(ncu, &class)) != NWAM_SUCCESS)
+ return (ret);
+
+ if ((ret = nwam_ncu_get_name(ncu, &name)) != NWAM_SUCCESS)
+ return (ret);
+
+ (void) fprintf(of, "create ncu %s \"%s\"\n",
+ propval_to_str(NWAM_NCU_PROP_CLASS, class), name);
+ free(name);
+ /*
+ * Because of dependencies between properties, they have to be
+ * exported in the same order as when they are walked.
+ */
+ if ((ret = nwam_ncu_get_default_proplist(type, class, &props, &num))
+ != NWAM_SUCCESS)
+ return (ret);
+ for (i = 0; i < num; i++) {
+ ret = nwam_ncu_get_prop_value(ncu, props[i], &vals);
+ if (ret == NWAM_SUCCESS) {
+ write_export_command(NWAM_OBJECT_TYPE_NCU, props[i],
+ vals, of);
+ nwam_value_free(vals);
+ }
+ }
+ (void) fprintf(of, "end\n");
+
+ free(props);
+ return (0);
+}
+
+static int
+export_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
+{
+ char *name;
+ nwam_error_t ret;
+ FILE *of = arg;
+
+ assert(of != NULL);
+
+ if ((ret = nwam_ncp_get_name(ncp, &name)) != NWAM_SUCCESS)
+ return (ret);
+
+ /* Do not export "automatic" NCP */
+ if (NWAM_NCP_AUTOMATIC(name)) {
+ free(name);
+ return (0);
+ }
+
+ (void) fprintf(of, "create ncp \"%s\"\n", name);
+ free(name);
+
+ /* now walk NCUs for this ncp */
+ ret = nwam_ncp_walk_ncus(ncp, export_ncu_callback, of,
+ NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Export ncp error: failed to walk ncus");
+ return (ret);
+ }
+ (void) fprintf(of, "end\n");
+ return (0);
+}
+
+static int
+export_enm_callback(nwam_enm_handle_t enm, void *arg)
+{
+ char *name;
+ const char **props;
+ nwam_value_t vals;
+ nwam_error_t ret;
+ uint_t num;
+ int i;
+ FILE *of = arg;
+
+ assert(of != NULL);
+
+ if ((ret = nwam_enm_get_name(enm, &name)) != NWAM_SUCCESS)
+ return (ret);
+
+ (void) fprintf(of, "create enm \"%s\"\n", name);
+ free(name);
+ /*
+ * Because of dependencies between properties, they have to be
+ * exported in the same order as when they are walked.
+ */
+ if ((ret = nwam_enm_get_default_proplist(&props, &num)) != NWAM_SUCCESS)
+ return (ret);
+ for (i = 0; i < num; i++) {
+ ret = nwam_enm_get_prop_value(enm, props[i], &vals);
+ if (ret == NWAM_SUCCESS) {
+ write_export_command(NWAM_OBJECT_TYPE_ENM, props[i],
+ vals, of);
+ nwam_value_free(vals);
+ }
+ }
+ (void) fprintf(of, "end\n");
+
+ free(props);
+ return (0);
+}
+
+static int
+export_loc_callback(nwam_loc_handle_t loc, void *arg)
+{
+ char *name;
+ const char **props;
+ nwam_value_t vals;
+ nwam_error_t ret;
+ uint_t num;
+ int i;
+ FILE *of = arg;
+
+ assert(of != NULL);
+
+ if ((ret = nwam_loc_get_name(loc, &name)) != NWAM_SUCCESS)
+ return (ret);
+
+ /* Do not export Automatic, NoNet or Legacy locations */
+ if (NWAM_LOC_NAME_PRE_DEFINED(name)) {
+ free(name);
+ return (0);
+ }
+
+ (void) fprintf(of, "create loc \"%s\"\n", name);
+ free(name);
+ /*
+ * Because of dependencies between properties, they have to be
+ * exported in the same order as when they are walked.
+ */
+ if ((ret = nwam_loc_get_default_proplist(&props, &num)) != NWAM_SUCCESS)
+ return (ret);
+ for (i = 0; i < num; i++) {
+ ret = nwam_loc_get_prop_value(loc, props[i], &vals);
+ if (ret == NWAM_SUCCESS) {
+ write_export_command(NWAM_OBJECT_TYPE_LOC, props[i],
+ vals, of);
+ nwam_value_free(vals);
+ }
+ }
+ (void) fprintf(of, "end\n");
+
+ free(props);
+ return (0);
+}
+
+static int
+export_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
+{
+ char *name;
+ const char **props;
+ nwam_value_t vals;
+ nwam_error_t ret;
+ uint_t num;
+ int i;
+ FILE *of = arg;
+
+ assert(of != NULL);
+
+ if ((ret = nwam_known_wlan_get_name(wlan, &name)) != NWAM_SUCCESS)
+ return (ret);
+
+ (void) fprintf(of, "create wlan \"%s\"\n", name);
+ free(name);
+ /*
+ * Because of dependencies between properties, they have to be
+ * exported in the same order as when they are walked.
+ */
+ if ((ret = nwam_known_wlan_get_default_proplist(&props, &num))
+ != NWAM_SUCCESS)
+ return (ret);
+ for (i = 0; i < num; i++) {
+ ret = nwam_known_wlan_get_prop_value(wlan, props[i], &vals);
+ if (ret == NWAM_SUCCESS) {
+ write_export_command(NWAM_OBJECT_TYPE_KNOWN_WLAN,
+ props[i], vals, of);
+ nwam_value_free(vals);
+ }
+ }
+ (void) fprintf(of, "end\n");
+
+ free(props);
+ return (0);
+}
+
+/*
+ * Writes configuration to screen or file (with -f option).
+ * Writes a "destroy -a" if option -d is given.
+ */
+void
+export_func(cmd_t *cmd)
+{
+ int c;
+ boolean_t need_to_close = B_FALSE, write_to_file = B_FALSE;
+ boolean_t add_destroy = B_FALSE, lhandle = B_FALSE;
+ char filepath[MAXPATHLEN];
+ nwam_error_t ret = NWAM_SUCCESS;
+ FILE *of = NULL; /* either filename or stdout */
+
+ /* what to export */
+ boolean_t export_ncp = B_FALSE, export_ncu = B_FALSE;
+ boolean_t export_loc = B_FALSE, export_enm = B_FALSE;
+ boolean_t export_wlan = B_FALSE;
+ char *name = NULL;
+
+ /* check for -d and -f flags */
+ filepath[0] = '\0';
+ optind = 0;
+ while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "df:")) != EOF) {
+ switch (c) {
+ case 'f':
+ write_to_file = B_TRUE;
+ break;
+ case 'd':
+ add_destroy = B_TRUE;
+ break;
+ default:
+ command_usage(CMD_EXPORT);
+ return;
+ }
+ }
+
+ /* determine where to export */
+ if (!write_to_file) {
+ of = stdout;
+ } else {
+ /*
+ * If -d was specified with -f, then argv[2] is filename,
+ * otherwise, argv[1] is filename.
+ */
+ (void) strlcpy(filepath,
+ (add_destroy ? cmd->cmd_argv[2] : cmd->cmd_argv[1]),
+ sizeof (filepath));
+ if ((of = fopen(filepath, "w")) == NULL) {
+ nerr(gettext("opening file '%s': %s"), filepath,
+ strerror(errno));
+ goto done;
+ }
+ setbuf(of, NULL);
+ need_to_close = B_TRUE;
+ }
+
+ if (add_destroy) {
+ /* only possible in global scope */
+ if (current_scope == NWAM_SCOPE_GBL) {
+ (void) fprintf(of, "destroy -a\n");
+ } else {
+ nerr("Option -d is not allowed in non-global scope");
+ goto done;
+ }
+ }
+
+ /* In the following scopes, only the -f argument is valid */
+ if (((current_scope == NWAM_SCOPE_LOC ||
+ current_scope == NWAM_SCOPE_ENM ||
+ current_scope == NWAM_SCOPE_WLAN ||
+ current_scope == NWAM_SCOPE_NCU) &&
+ cmd->cmd_argc != 0 && !write_to_file)) {
+ nerr("'export' does not take arguments at this scope");
+ goto done;
+ }
+ if (current_scope == NWAM_SCOPE_NCP) {
+ if (cmd->cmd_res1_type == RT1_ENM ||
+ cmd->cmd_res1_type == RT1_LOC ||
+ cmd->cmd_res1_type == RT1_WLAN) {
+ nerr("only ncu can be exported at this scope");
+ goto done;
+ }
+ }
+
+ /*
+ * Determine what objects to export depending on scope and command
+ * arguments. If -f is specified, then the object name is argv[2].
+ * Otherwise, argv[0] is name, unless exporting all in global
+ * scope in which case name is set back to NULL.
+ */
+ switch (current_scope) {
+ case NWAM_SCOPE_GBL:
+ name = (write_to_file ? trim_quotes(cmd->cmd_argv[2]) :
+ trim_quotes(cmd->cmd_argv[0]));
+ switch (cmd->cmd_res1_type) {
+ case RT1_LOC:
+ export_loc = B_TRUE;
+ break;
+ case RT1_ENM:
+ export_enm = B_TRUE;
+ break;
+ case RT1_WLAN:
+ export_wlan = B_TRUE;
+ break;
+ case RT1_NCP:
+ export_ncp = B_TRUE;
+ if (cmd->cmd_res2_type == RT2_NCU) {
+ nerr("cannot export ncu at from global scope");
+ goto done;
+ }
+ break;
+ default:
+ /* export everything */
+ export_loc = B_TRUE;
+ export_enm = B_TRUE;
+ export_wlan = B_TRUE;
+ export_ncp = B_TRUE; /* NCP will export the NCUs */
+ free(name);
+ name = NULL; /* exporting all, undo name */
+ break;
+ }
+ break;
+ case NWAM_SCOPE_LOC:
+ export_loc = B_TRUE;
+ ret = nwam_loc_get_name(loc_h, &name);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ break;
+ case NWAM_SCOPE_ENM:
+ export_enm = B_TRUE;
+ ret = nwam_enm_get_name(enm_h, &name);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ break;
+ case NWAM_SCOPE_WLAN:
+ export_wlan = B_TRUE;
+ ret = nwam_known_wlan_get_name(wlan_h, &name);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ break;
+ case NWAM_SCOPE_NCP:
+ if (cmd->cmd_res2_type == RT2_NCU) {
+ export_ncu = B_TRUE;
+ name = (write_to_file ? trim_quotes(cmd->cmd_argv[2]) :
+ trim_quotes(cmd->cmd_argv[0]));
+ } else {
+ export_ncp = B_TRUE;
+ ret = nwam_ncp_get_name(ncp_h, &name);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ }
+ break;
+ case NWAM_SCOPE_NCU:
+ export_ncu = B_TRUE;
+ ret = nwam_ncu_get_name(ncu_h, &name);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ break;
+ default:
+ nerr("Invalid scope");
+ goto done;
+ }
+
+ /* Now, export objects according to the flags set */
+ if (export_ncp) {
+ lhandle = B_FALSE;
+ if (name == NULL) {
+ /* export all NCPs */
+ ret = nwam_walk_ncps(export_ncp_callback, of, 0, NULL);
+ } else if (NWAM_NCP_AUTOMATIC(name)) {
+ nerr("'%s' ncp cannot be exported", name);
+ goto fail;
+ } else {
+ if (ncp_h == NULL) {
+ ret = nwam_ncp_read(name, 0, &ncp_h);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ lhandle = B_TRUE;
+ }
+ /* will export NCUs also */
+ ret = export_ncp_callback(ncp_h, of);
+ if (lhandle) {
+ nwam_ncp_free(ncp_h);
+ ncp_h = NULL;
+ }
+ }
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ }
+
+ if (export_ncu) {
+ if (name == NULL) {
+ /* export all NCUs */
+ ret = nwam_ncp_walk_ncus(ncp_h, export_ncu_callback, of,
+ NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
+ } else {
+ if (ncu_h == NULL) {
+ /* no NCU handle -> called from NCP scope */
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ ncu_class = (nwam_ncu_class_t)
+ cmd->cmd_ncu_class_type;
+ ncu_type = nwam_ncu_class_to_type(ncu_class);
+ ret = nwam_ncu_read(ncp_h, name,
+ ncu_type, 0, &ncu_h);
+ if (ret == NWAM_SUCCESS) {
+ /* one NCU with given name */
+ ret = export_ncu_callback(ncu_h, of);
+ nwam_ncu_free(ncu_h);
+ ncu_h = NULL;
+ } else if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
+ /* multiple NCUs with given name */
+ ret = nwam_ncu_read(ncp_h, name,
+ NWAM_NCU_TYPE_LINK, 0, &ncu_h);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ ret = export_ncu_callback(ncu_h, of);
+ nwam_ncu_free(ncu_h);
+ ncu_h = NULL;
+
+ ret = nwam_ncu_read(ncp_h, name,
+ NWAM_NCU_TYPE_INTERFACE, 0, &ncu_h);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ ret = export_ncu_callback(ncu_h, of);
+ nwam_ncu_free(ncu_h);
+ ncu_h = NULL;
+ } else {
+ goto fail;
+ }
+ } else {
+ /* NCU handle exists */
+ ret = export_ncu_callback(ncu_h, of);
+ }
+ }
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ }
+
+ if (export_loc) {
+ lhandle = B_FALSE;
+ if (name == NULL) {
+ /* export all locations */
+ ret = nwam_walk_locs(export_loc_callback, of,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ } else if (NWAM_LOC_NAME_PRE_DEFINED(name)) {
+ nerr("'%s' loc cannot be exported", name);
+ goto fail;
+ } else {
+ if (loc_h == NULL) {
+ ret = nwam_loc_read(name, 0, &loc_h);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ lhandle = B_TRUE;
+ }
+ ret = export_loc_callback(loc_h, of);
+ if (lhandle) {
+ nwam_loc_free(loc_h);
+ loc_h = NULL;
+ }
+ }
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ }
+
+ if (export_enm) {
+ lhandle = B_FALSE;
+ if (name == NULL) {
+ /* export all ENMs */
+ ret = nwam_walk_enms(export_enm_callback, of,
+ NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
+ } else {
+ if (enm_h == NULL) {
+ ret = nwam_enm_read(name, 0, &enm_h);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ lhandle = B_TRUE;
+ }
+ ret = export_enm_callback(enm_h, of);
+ if (lhandle) {
+ nwam_enm_free(enm_h);
+ enm_h = NULL;
+ }
+ }
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ }
+
+ if (export_wlan) {
+ lhandle = B_FALSE;
+ if (name == NULL) {
+ /* export all WLANs */
+ ret = nwam_walk_known_wlans(export_wlan_callback, of,
+ NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL);
+ } else {
+ if (wlan_h == NULL) {
+ ret = nwam_known_wlan_read(name, 0,
+ &wlan_h);
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ lhandle = B_TRUE;
+ }
+ ret = export_wlan_callback(wlan_h, of);
+ if (lhandle) {
+ nwam_known_wlan_free(wlan_h);
+ wlan_h = NULL;
+ }
+ }
+ if (ret != NWAM_SUCCESS)
+ goto fail;
+ }
+
+fail:
+ free(name);
+ if (ret != NWAM_SUCCESS)
+ nwamerr(ret, "Export error");
+
+done:
+ if (need_to_close)
+ (void) fclose(of);
+}
+
+/*
+ * Get property value. If the -V option is specified, only the value is
+ * printed without the property name.
+ */
+void
+get_func(cmd_t *cmd)
+{
+ nwam_error_t ret = NWAM_SUCCESS;
+ nwam_value_t prop_value;
+ const char *prop;
+ boolean_t value_only = B_FALSE;
+ nwam_object_type_t object_type = active_object_type();
+
+ /* check if option is -V to print value only */
+ if (cmd->cmd_argc == 1) {
+ int c;
+
+ optind = 0;
+ while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "V")) != EOF) {
+ switch (c) {
+ case 'V':
+ value_only = B_TRUE;
+ break;
+ default:
+ command_usage(CMD_GET);
+ return;
+ }
+ }
+ }
+
+ /* property to get is in cmd->cmd_prop_type */
+ if ((prop = pt_to_prop_name(object_type, cmd->cmd_prop_type)) == NULL) {
+ nerr("Get error: invalid %s property: '%s'",
+ scope_to_str(current_scope), pt_to_str(cmd->cmd_prop_type));
+ return;
+ }
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_prop_value(ncu_h, prop, &prop_value);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_prop_value(loc_h, prop, &prop_value);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_prop_value(enm_h, prop, &prop_value);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_get_prop_value(wlan_h, prop, &prop_value);
+ break;
+ }
+
+ if (ret != NWAM_SUCCESS) {
+ if (ret == NWAM_ENTITY_NOT_FOUND)
+ nerr("Get error: property '%s' has not been set", prop);
+ else
+ nwamerr(ret, "Get error");
+ return;
+ }
+
+ if (value_only) {
+ output_prop_val(prop, prop_value, stdout, B_FALSE);
+ (void) printf("\n");
+ } else {
+ output_propname(prop, prop_value, NULL);
+ }
+ nwam_value_free(prop_value);
+}
+
+/*
+ * Clears value of a property.
+ * Read-only properties cannot be cleared.
+ * If clearing a property invalidates the object, then that property
+ * cannot be cleared.
+ */
+void
+clear_func(cmd_t *cmd)
+{
+ nwam_error_t ret;
+ const char *prop;
+ nwam_object_type_t object_type = active_object_type();
+
+ /* property to clear is in cmd->cmd_prop_type */
+ if ((prop = pt_to_prop_name(object_type, cmd->cmd_prop_type)) == NULL) {
+ nerr("Clear error: invalid %s property: '%s'",
+ scope_to_str(current_scope), pt_to_str(cmd->cmd_prop_type));
+ return;
+ }
+ if (is_prop_read_only(object_type, prop)) {
+ nerr("Clear error: property '%s' is read-only", prop);
+ return;
+ }
+
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_delete_prop(ncu_h, prop);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_delete_prop(loc_h, prop);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_delete_prop(enm_h, prop);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_delete_prop(wlan_h, prop);
+ break;
+ }
+
+ if (ret != NWAM_SUCCESS) {
+ if (ret == NWAM_INVALID_ARG || ret == NWAM_ENTITY_NOT_FOUND) {
+ nerr("Clear error: property '%s' has not been set",
+ prop);
+ } else {
+ nwamerr(ret, "Clear error");
+ }
+ return;
+ }
+
+ need_to_commit = B_TRUE;
+}
+
+/*
+ * Prints all the choices available for an enum property [c1|c2|c3].
+ * Prints [true|false] for a boolean property.
+ */
+static void
+print_all_prop_choices(nwam_object_type_t object_type, const char *prop)
+{
+ uint64_t i = 0;
+ const char *str;
+ boolean_t choices = B_FALSE;
+ nwam_value_type_t value_type;
+ nwam_error_t ret;
+
+ /* Special case: print object-specific options for activation-mode */
+ if (strcmp(prop, NWAM_NCU_PROP_ACTIVATION_MODE) == 0) {
+ /* "manual" for all objects */
+ (void) printf(" [%s|",
+ propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
+ NWAM_ACTIVATION_MODE_MANUAL));
+ if (object_type == NWAM_OBJECT_TYPE_NCU) {
+ (void) printf("%s]",
+ propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
+ NWAM_ACTIVATION_MODE_PRIORITIZED));
+ } else {
+ (void) printf("%s|%s]",
+ propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ANY),
+ propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ALL));
+ }
+ return;
+ }
+
+ /* Special case: only "manual" configsrc is allowed for LDAP */
+ if (strcmp(prop, NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC) == 0) {
+ (void) printf(" [%s]",
+ propval_to_str(NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
+ NWAM_CONFIGSRC_MANUAL));
+ return;
+ }
+
+ value_type = prop_value_type(object_type, prop);
+ switch (value_type) {
+ case NWAM_VALUE_TYPE_UINT64:
+ /* uint64 may be an enum, will print nothing if not an enum */
+ while ((ret = nwam_uint64_get_value_string(prop, i++, &str))
+ == NWAM_SUCCESS || ret == NWAM_ENTITY_INVALID_VALUE) {
+ /* No string representation for i, continue. */
+ if (ret == NWAM_ENTITY_INVALID_VALUE)
+ continue;
+
+ if (!choices)
+ (void) printf("%s", " [");
+ (void) printf("%s%s", choices ? "|" : "", str);
+ choices = B_TRUE;
+ }
+ if (choices)
+ (void) putchar(']');
+ break;
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ (void) printf(" [%s|%s]", "true", "false");
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ break;
+ }
+}
+
+/*
+ * Walk through object properties.
+ * For newly-created object, the property name with no value is displayed, and
+ * the user can input a value for each property.
+ * For existing object, the current value is displayed and user input overwrites
+ * the existing one. If no input is given, the existing value remains.
+ * Read-only properties are not displayed.
+ * Read-only objects cannot be walked.
+ * If the -a option is specified, no properties are skipped.
+ */
+void
+walkprop_func(cmd_t *cmd)
+{
+ nwam_error_t ret = NWAM_SUCCESS;
+ nwam_value_t vals = NULL; /* freed in _wait_input() */
+ int i;
+ uint_t prop_num;
+ const char **props;
+ boolean_t read_only = B_FALSE, all_props = B_FALSE;
+
+ nwam_object_type_t object_type;
+ prop_display_entry_t *prop_table;
+
+ if (!interactive_mode) {
+ nerr("'walkprop' is only allowed in interactive mode");
+ return;
+ }
+
+ /* check if option -a is specified to show all properties */
+ if (cmd->cmd_argc == 1) {
+ int c;
+ optind = 0;
+ while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "a")) != EOF) {
+ switch (c) {
+ case 'a':
+ all_props = B_TRUE;
+ break;
+ default:
+ command_usage(CMD_WALKPROP);
+ return;
+ }
+ }
+ }
+
+ /* read-only objects cannot be walked */
+ if (obj1_type == RT1_NCP) {
+ /* must be in NCU scope, NCP scope doesn't get here */
+ (void) nwam_ncu_get_read_only(ncu_h, &read_only);
+ }
+ if (read_only) {
+ nerr("'walkprop' cannot be used in read-only objects");
+ return;
+ }
+
+ /* get the current object type and the prop_display_table */
+ object_type = active_object_type();
+ prop_table = get_prop_display_table(object_type);
+
+ /* get the property list depending on the object type */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ {
+ nwam_ncu_type_t ncu_type;
+ nwam_ncu_class_t ncu_class;
+
+ if ((ret = nwam_ncu_get_ncu_type(ncu_h, &ncu_type))
+ != NWAM_SUCCESS)
+ break;
+ if ((ret = nwam_ncu_get_ncu_class(ncu_h, &ncu_class))
+ != NWAM_SUCCESS)
+ break;
+
+ ret = nwam_ncu_get_default_proplist(ncu_type, ncu_class, &props,
+ &prop_num);
+ break;
+ }
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_default_proplist(&props, &prop_num);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_default_proplist(&props, &prop_num);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_get_default_proplist(&props, &prop_num);
+ break;
+ }
+ if (ret != NWAM_SUCCESS) {
+ nwamerr(ret, "Walkprop error: could not get property list");
+ return;
+ }
+
+ /* Loop through the properties */
+ if (all_props)
+ (void) printf(gettext("Walking all properties ...\n"));
+ for (i = 0; i < prop_num; i++) {
+ char line[NWAM_MAX_VALUE_LEN];
+ char **checked = NULL;
+
+ /* check if this property should be displayed */
+ if (is_prop_read_only(object_type, props[i]))
+ continue;
+ if (!all_props &&
+ !show_prop_test(object_type, props[i], prop_table,
+ checked, 0))
+ continue;
+
+ /* get the existing value for this property */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_get_prop_value(ncu_h, props[i], &vals);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_get_prop_value(loc_h, props[i], &vals);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_get_prop_value(enm_h, props[i], &vals);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_get_prop_value(wlan_h, props[i],
+ &vals);
+ break;
+ }
+ /* returns NWAM_ENTITY_NOT_FOUND if no existing value */
+ if (ret != NWAM_SUCCESS && ret != NWAM_ENTITY_NOT_FOUND)
+ continue;
+
+ /* print property */
+ (void) printf("%s", props[i]);
+ /* print the existing value(s) if they exist */
+ if (ret == NWAM_SUCCESS) {
+ (void) printf(" (");
+ output_prop_val(props[i], vals, stdout, B_TRUE);
+ (void) putchar(')');
+ nwam_value_free(vals);
+ }
+ /* print choices, won't print anything if there aren't any */
+ print_all_prop_choices(object_type, props[i]);
+ (void) printf("> ");
+
+ /* wait for user input */
+ if (fgets(line, sizeof (line), stdin) == NULL)
+ continue;
+
+ /* if user input new value, existing value is overrode */
+ if (line[0] != '\n') {
+ boolean_t is_listprop;
+ int pt_type = prop_to_pt(object_type, props[i]);
+
+ is_listprop = is_prop_multivalued(object_type,
+ props[i]);
+ vals = str_to_nwam_value(object_type, line, pt_type,
+ is_listprop);
+ if (vals == NULL) {
+ ret = NWAM_ENTITY_INVALID_VALUE;
+ goto repeat;
+ }
+
+ /* set the new value for the property */
+ switch (object_type) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_set_prop_value(ncu_h, props[i],
+ vals);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_set_prop_value(loc_h, props[i],
+ vals);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_set_prop_value(enm_h, props[i],
+ vals);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_set_prop_value(wlan_h,
+ props[i], vals);
+ break;
+ }
+ nwam_value_free(vals);
+
+ if (ret != NWAM_SUCCESS)
+ goto repeat;
+
+ need_to_commit = B_TRUE;
+ continue;
+
+repeat:
+ invalid_set_prop_msg(props[i], ret);
+ i--; /* decrement i to repeat */
+ }
+ }
+
+ free(props);
+}
+
+/*
+ * Verify whether all properties of a resource are valid.
+ */
+/* ARGSUSED */
+void
+verify_func(cmd_t *cmd)
+{
+ nwam_error_t ret;
+ const char *errprop;
+
+ switch (active_object_type()) {
+ case NWAM_OBJECT_TYPE_NCU:
+ ret = nwam_ncu_validate(ncu_h, &errprop);
+ break;
+ case NWAM_OBJECT_TYPE_LOC:
+ ret = nwam_loc_validate(loc_h, &errprop);
+ break;
+ case NWAM_OBJECT_TYPE_ENM:
+ ret = nwam_enm_validate(enm_h, &errprop);
+ break;
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ ret = nwam_known_wlan_validate(wlan_h, &errprop);
+ break;
+ }
+ if (ret != NWAM_SUCCESS)
+ nwamerr(ret, "Verify error on property '%s'", errprop);
+ else if (interactive_mode)
+ (void) printf(gettext("All properties verified\n"));
+}
+
+/*
+ * command-line mode (# nwamcfg list or # nwamcfg "select loc test; list")
+ */
+static int
+one_command_at_a_time(int argc, char *argv[])
+{
+ char *command;
+ size_t len = 2; /* terminal \n\0 */
+ int i, err;
+
+ for (i = 0; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+ if ((command = malloc(len)) == NULL) {
+ nerr("Out of memory");
+ return (NWAM_ERR);
+ }
+ (void) strlcpy(command, argv[0], len);
+ for (i = 1; i < argc; i++) {
+ (void) strlcat(command, " ", len);
+ (void) strlcat(command, argv[i], len);
+ }
+ (void) strlcat(command, "\n", len);
+ err = string_to_yyin(command);
+ free(command);
+ if (err != NWAM_OK)
+ return (err);
+ while (!feof(yyin)) {
+ yyparse();
+
+ /*
+ * If any command on a list of commands give an error,
+ * don't continue with the remaining commands.
+ */
+ if (saw_error || time_to_exit)
+ return (cleanup());
+ }
+
+ /* if there are changes to commit, commit it */
+ if (need_to_commit) {
+ do_commit();
+ /* if need_to_commit is not set, then there was a error */
+ if (need_to_commit)
+ return (NWAM_ERR);
+ }
+
+ if (!interactive_mode)
+ return (cleanup());
+ else {
+ yyin = stdin;
+ return (read_input());
+ }
+}
+
+/*
+ * cmd_file is slightly more complicated, as it has to open the command file
+ * and set yyin appropriately. Once that is done, though, it just calls
+ * read_input(), and only once, since prompting is not possible.
+ */
+static int
+cmd_file(char *file)
+{
+ FILE *infile;
+ int err;
+ struct stat statbuf;
+ boolean_t using_real_file = (strcmp(file, "-") != 0);
+
+ if (using_real_file) {
+ /*
+ * nerr() prints a line number in cmd_file_mode, which we do
+ * not want here, so temporarily unset it.
+ */
+ cmd_file_mode = B_FALSE;
+ if ((infile = fopen(file, "r")) == NULL) {
+ nerr(gettext("could not open file '%s': %s"),
+ file, strerror(errno));
+ return (1);
+ }
+ if ((err = fstat(fileno(infile), &statbuf)) != 0) {
+ nerr(gettext("could not stat file '%s': %s"),
+ file, strerror(errno));
+ err = 1;
+ goto done;
+ }
+ if (!S_ISREG(statbuf.st_mode)) {
+ nerr(gettext("'%s' is not a regular file."), file);
+ err = 1;
+ goto done;
+ }
+
+ /*
+ * If -d was passed on the command-line, we need to
+ * start by removing any existing configuration.
+ * Alternatively, the file may begin with 'destroy -a';
+ * but in that case, the line will go through the lexer
+ * and be processed as it's encountered in the file.
+ */
+ if (remove_all_configurations && destroy_all() != NWAM_SUCCESS)
+ goto done;
+
+ /* set up for lexer */
+ yyin = infile;
+ cmd_file_mode = B_TRUE;
+ ok_to_prompt = B_FALSE;
+ } else {
+ /*
+ * "-f -" is essentially the same as interactive mode,
+ * so treat it that way.
+ */
+ interactive_mode = B_TRUE;
+ }
+ /* NWAM_REPEAT is for interactive mode; treat it like NWAM_ERR here. */
+ if ((err = read_input()) == NWAM_REPEAT)
+ err = NWAM_ERR;
+ if (err == NWAM_OK)
+ (void) printf(gettext("Configuration read.\n"));
+
+done:
+ if (using_real_file)
+ (void) fclose(infile);
+ return (err);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int err;
+ char c;
+
+ /* This must be before anything goes to stdout. */
+ setbuf(stdout, NULL);
+
+ if ((execname = strrchr(argv[0], '/')) == NULL)
+ execname = argv[0];
+ else
+ execname++;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((c = getopt(argc, argv, "?hf:d")) != EOF) {
+ switch (c) {
+ case 'f':
+ cmd_file_name = optarg;
+ cmd_file_mode = B_TRUE;
+ break;
+ case '?':
+ case 'h':
+ cmd_line_usage();
+ return (NWAM_OK);
+ case 'd':
+ remove_all_configurations = B_TRUE;
+ break;
+ default:
+ cmd_line_usage();
+ return (NWAM_ERR);
+ }
+ }
+ /* -d can only be used with -f */
+ if (remove_all_configurations && !cmd_file_mode) {
+ nerr("Option -d can only be used with -f");
+ return (NWAM_ERR);
+ }
+
+ /*
+ * This may get set back to FALSE again in cmd_file() if cmd_file_name
+ * is a "real" file as opposed to "-" (i.e. meaning use stdin).
+ */
+ if (isatty(STDIN_FILENO))
+ ok_to_prompt = B_TRUE;
+ if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
+ exit(NWAM_ERR);
+ if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
+ exit(NWAM_ERR);
+ (void) sigset(SIGINT, SIG_IGN);
+
+ if (optind == argc) {
+ /* interactive or command-file mode */
+ if (!cmd_file_mode)
+ err = do_interactive();
+ else
+ err = cmd_file(cmd_file_name);
+ } else {
+ /* command-line mode */
+ err = one_command_at_a_time(argc - optind, &(argv[optind]));
+ }
+ (void) del_GetLine(gl);
+
+ return (err);
+}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h
new file mode 100644
index 0000000000..c9b1049c99
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.h
@@ -0,0 +1,181 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NWAMCFG_H
+#define _NWAMCFG_H
+
+/*
+ * header file for nwamcfg command
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NWAM_OK 0
+#define NWAM_ERR 1
+#define NWAM_REPEAT 2
+
+/* max length of "ncu", "ncp", "loc", "enm", "wlan" */
+#define NWAM_MAX_TYPE_LEN 5
+
+#define CMD_CANCEL 0
+#define CMD_CLEAR 1
+#define CMD_COMMIT 2
+#define CMD_CREATE 3
+#define CMD_DESTROY 4
+#define CMD_END 5
+#define CMD_EXIT 6
+#define CMD_EXPORT 7
+#define CMD_GET 8
+#define CMD_HELP 9
+#define CMD_LIST 10
+#define CMD_REVERT 11
+#define CMD_SELECT 12
+#define CMD_SET 13
+#define CMD_VERIFY 14
+#define CMD_WALKPROP 15
+
+#define CMD_MIN CMD_CANCEL
+#define CMD_MAX CMD_WALKPROP
+
+/* one-level resource types */
+#define RT1_UNKNOWN 0
+#define RT1_LOC 1
+#define RT1_NCP 2
+#define RT1_ENM 3
+#define RT1_WLAN 4
+
+#define RT1_MIN RT1_UNKNOWN
+#define RT1_MAX RT1_WLAN
+
+/* two-level resource types */
+#define RT2_UNKNOWN 0
+#define RT2_NCU 1
+
+#define RT2_MIN RT2_UNKNOWN
+#define RT2_MAX RT2_NCU
+
+/* class types for NCU's */
+#define NCU_CLASS_PHYS 0
+#define NCU_CLASS_IP 1
+#define NCU_CLASS_ANY 2
+
+#define NCU_CLASS_MIN NCU_CLASS_PHYS
+#define NCU_CLASS_MAX NCU_CLASS_ANY
+
+/* property types, matches NWAM_*_PROP_* from libnwam.h */
+#define PT_UNKNOWN 0
+#define PT_ACTIVATION_MODE 1
+#define PT_ENABLED 2
+#define PT_TYPE 3
+#define PT_CLASS 4
+#define PT_PARENT 5
+#define PT_PRIORITY_GROUP 6
+#define PT_PRIORITY_MODE 7
+#define PT_LINK_MACADDR 8
+#define PT_LINK_AUTOPUSH 9
+#define PT_LINK_MTU 10
+#define PT_IP_VERSION 11
+#define PT_IPV4_ADDRSRC 12
+#define PT_IPV4_ADDR 13
+#define PT_IPV4_DEFAULT_ROUTE 14
+#define PT_IPV6_ADDRSRC 15
+#define PT_IPV6_ADDR 16
+#define PT_IPV6_DEFAULT_ROUTE 17
+#define PT_CONDITIONS 18
+#define PT_ENM_FMRI 19
+#define PT_ENM_START 20
+#define PT_ENM_STOP 21
+#define PT_LOC_NAMESERVICES 22
+#define PT_LOC_NAMESERVICES_CONFIG 23
+#define PT_LOC_DNS_CONFIGSRC 24
+#define PT_LOC_DNS_DOMAIN 25
+#define PT_LOC_DNS_SERVERS 26
+#define PT_LOC_DNS_SEARCH 27
+#define PT_LOC_NIS_CONFIGSRC 28
+#define PT_LOC_NIS_SERVERS 29
+#define PT_LOC_LDAP_CONFIGSRC 30
+#define PT_LOC_LDAP_SERVERS 31
+#define PT_LOC_DEFAULT_DOMAIN 32
+#define PT_LOC_NFSV4_DOMAIN 33
+#define PT_LOC_IPF_CONFIG 34
+#define PT_LOC_IPF_V6_CONFIG 35
+#define PT_LOC_IPNAT_CONFIG 36
+#define PT_LOC_IPPOOL_CONFIG 37
+#define PT_LOC_IKE_CONFIG 38
+#define PT_LOC_IPSECPOL_CONFIG 39
+#define PT_WLAN_BSSIDS 40
+#define PT_WLAN_PRIORITY 41
+#define PT_WLAN_KEYNAME 42
+#define PT_WLAN_KEYSLOT 43
+#define PT_WLAN_SECURITY_MODE 44
+/*
+ * If any new PT_ are defined here, make sure it is added in the same
+ * order into the pt_types array in nwamcfg.c
+ */
+#define PT_MIN PT_UNKNOWN
+#define PT_MAX PT_WLAN_SECURITY_MODE
+
+#define MAX_SUBCMD_ARGS 3
+
+typedef struct cmd {
+ int cmd_num;
+ void (*cmd_handler)(struct cmd *);
+ int cmd_res1_type;
+ int cmd_res2_type;
+ int cmd_prop_type;
+ int cmd_ncu_class_type;
+ int cmd_argc;
+ char *cmd_argv[MAX_SUBCMD_ARGS + 1];
+} cmd_t;
+
+/* Fuctions for each command */
+typedef void (cmd_func_t)(cmd_t *);
+
+extern cmd_func_t cancel_func, clear_func, commit_func, create_func;
+extern cmd_func_t destroy_func, end_func, exit_func, export_func, get_func;
+extern cmd_func_t help_func, list_func, revert_func, select_func, set_func;
+extern cmd_func_t verify_func, walkprop_func;
+
+extern cmd_t *alloc_cmd(void);
+extern void free_cmd(cmd_t *cmd);
+
+extern boolean_t check_scope(int);
+extern const char *cmd_to_str(int);
+
+extern void nerr(const char *, ...);
+extern void properr(const char *);
+
+extern boolean_t saw_error;
+
+extern FILE *yyin;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NWAMCFG_H */
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.xcl b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.xcl
new file mode 100644
index 0000000000..c25592effd
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.xcl
@@ -0,0 +1,159 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+msgid ""
+msgid "\"%s\""
+msgid "\"%s\"\n"
+msgid "\0"
+msgid "\n"
+msgid "\n'%s' %s"
+msgid "\t%-*s\t"
+msgid "\t%s"
+msgid "\t%s "
+msgid "\t%s %s "
+msgid "\t%s <%s> [%s...]\n"
+msgid "\t%s [-d] -f <%s>\n"
+msgid "\t%s %s [<%s>]\n"
+msgid "\t%s\n"
+msgid " %s "
+msgid " '%s'"
+msgid " '%s': %s"
+msgid " [%s]"
+msgid " [%s|"
+msgid " [%s|%s]"
+msgid "%d:\n"
+msgid "%d\n"
+msgid "%lld"
+msgid "%s '%s'"
+msgid "%s '%s'."
+msgid "%s '%s'\n"
+msgid "%s (%s)? "
+msgid "%s \"%s\"\n"
+msgid "%s"
+msgid "%s%s"
+msgid "%s: %s\n"
+msgid "%s: %s: %s"
+msgid "> "
+msgid "%s> "
+msgid "%s:%s\n"
+msgid "%s:%s:%s> "
+msgid "%s:%s:%s:%s:%s> "
+msgid "%s:\n"
+msgid "%s:\t%s\t\t\t\t(%s)\n"
+msgid "%s="
+msgid "%s\n"
+msgid "%s]"
+msgid "%s|%s]"
+msgid "'%s'"
+msgid "'%s'\n"
+msgid ","
+msgid "-V"
+msgid "-a"
+msgid "-d"
+msgid "-f"
+msgid ": %s\n"
+msgid ": '%s'"
+msgid "Cancel"
+msgid "Clear"
+msgid "Commit"
+msgid "Copy"
+msgid "Create"
+msgid "Destroy"
+msgid "End"
+msgid "Exit"
+msgid "Export"
+msgid "Get"
+msgid "Help"
+msgid "List"
+msgid "Revert"
+msgid "Select"
+msgid "Set"
+msgid "Verify"
+msgid "Walkprop"
+msgid "cancel"
+msgid "class"
+msgid "clear"
+msgid "commit"
+msgid "create"
+msgid "destroy"
+msgid "end"
+msgid "exit"
+msgid "export"
+msgid "get"
+msgid "help"
+msgid "list"
+msgid "revert"
+msgid "select"
+msgid "set"
+msgid "verify"
+msgid "walkprop"
+msgid "NCP"
+msgid "NCU"
+msgid "ENM"
+msgid "LOC"
+msgid "WLAN"
+msgid "NCPs"
+msgid "NCUs"
+msgid "ENMs"
+msgid "LOCs"
+msgid "Locations"
+msgid "WLANs"
+msgid "ncp"
+msgid "ncu"
+msgid "loc"
+msgid "enm"
+msgid "wlan"
+msgid "known wlan"
+msgid "ip"
+msgid "phys"
+msgid "boolean"
+msgid "int64"
+msgid "uint64"
+msgid "string"
+msgid "unknown"
+msgid "scope"
+msgid "y/[n]"
+msgid "[y]/n"
+msgid "command-name"
+msgid "nwam_value"
+msgid "object-name"
+msgid "object-type"
+msgid "prop-name"
+msgid "template"
+msgid "value1"
+msgid "value2"
+msgid "create enm \"%s\"\n"
+msgid "create enm"
+msgid "create loc \"%s\"\n"
+msgid "create loc"
+msgid "create ncp \"%s\"\n"
+msgid "create ncp"
+msgid "create ncu "
+msgid "create ncu %s \"%s\"\n"
+msgid "create wlan \"%s\"\n"
+msgid "create wlan"
+msgid "destroy -a\n"
+msgid "end\n"
+msgid "set "
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y
new file mode 100644
index 0000000000..c40dc2c963
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.y
@@ -0,0 +1,904 @@
+%{
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "nwamcfg.h"
+
+static cmd_t *cmd = NULL; /* Command being processed */
+
+/* yacc externals */
+extern int yydebug;
+extern void yyerror(char *s);
+
+extern boolean_t newline_terminated;
+
+%}
+
+%union {
+ int ival;
+ char *strval;
+ cmd_t *cmd;
+}
+
+%start commands
+
+%token CANCEL CLEAR COMMIT CREATE DESTROY END EXIT EXPORT GET HELP
+%token LIST REVERT SELECT SET VERIFY WALKPROP
+%token LOC NCP NCU ENM WLAN
+%token PHYS IP
+%token TOKEN EQUAL OPTION
+%token UNKNOWN ACTIVATION_MODE CONDITIONS ENABLED
+%token TYPE CLASS PARENT PRIORITY_GROUP PRIORITY_MODE
+%token LINK_MACADDR LINK_AUTOPUSH LINK_MTU
+%token IP_VERSION IPV4_ADDRSRC IPV4_ADDR IPV4_DEFAULT_ROUTE
+%token IPV6_ADDRSRC IPV6_ADDR IPV6_DEFAULT_ROUTE
+%token ENM_STATE ENM_FMRI ENM_START ENM_STOP
+%token LOC_NAMESERVICES LOC_NAMESERVICES_CONFIG
+%token LOC_DNS_CONFIGSRC LOC_DNS_DOMAIN LOC_DNS_SERVERS LOC_DNS_SEARCH
+%token LOC_NIS_CONFIGSRC LOC_NIS_SERVERS
+%token LOC_LDAP_CONFIGSRC LOC_LDAP_SERVERS
+%token LOC_DEFAULT_DOMAIN LOC_NFSV4_DOMAIN
+%token LOC_IPF_CONFIG LOC_IPF_V6_CONFIG
+%token LOC_IPNAT_CONFIG LOC_IPPOOL_CONFIG LOC_IKE_CONFIG LOC_IPSECPOL_CONFIG
+%token WLAN_BSSIDS WLAN_PRIORITY WLAN_KEYNAME WLAN_KEYSLOT WLAN_SECURITY_MODE
+
+%type <strval> TOKEN EQUAL OPTION
+%type <ival> resource1_type LOC NCP ENM WLAN
+%type <ival> resource2_type NCU
+%type <ival> ncu_class_type PHYS IP
+%type <ival> property_type UNKNOWN ACTIVATION_MODE CONDITIONS ENABLED
+ TYPE CLASS PARENT PRIORITY_GROUP PRIORITY_MODE
+ LINK_MACADDR LINK_AUTOPUSH LINK_MTU
+ IP_VERSION IPV4_ADDRSRC IPV4_ADDR IPV4_DEFAULT_ROUTE
+ IPV6_ADDRSRC IPV6_ADDR IPV6_DEFAULT_ROUTE
+ ENM_STATE ENM_FMRI ENM_START ENM_STOP
+ LOC_NAMESERVICES LOC_NAMESERVICES_CONFIG
+ LOC_DNS_CONFIGSRC LOC_DNS_DOMAIN LOC_DNS_SERVERS LOC_DNS_SEARCH
+ LOC_NIS_CONFIGSRC LOC_NIS_SERVERS
+ LOC_LDAP_CONFIGSRC LOC_LDAP_SERVERS
+ LOC_DEFAULT_DOMAIN LOC_NFSV4_DOMAIN
+ LOC_IPF_CONFIG LOC_IPF_V6_CONFIG
+ LOC_IPNAT_CONFIG LOC_IPPOOL_CONFIG LOC_IKE_CONFIG LOC_IPSECPOL_CONFIG
+ WLAN_BSSIDS WLAN_PRIORITY WLAN_KEYNAME WLAN_KEYSLOT WLAN_SECURITY_MODE
+%type <cmd> command
+%type <cmd> cancel_command CANCEL
+%type <cmd> clear_command CLEAR
+%type <cmd> commit_command COMMIT
+%type <cmd> create_command CREATE
+%type <cmd> destroy_command DESTROY
+%type <cmd> end_command END
+%type <cmd> exit_command EXIT
+%type <cmd> export_command EXPORT
+%type <cmd> get_command GET
+%type <cmd> help_command HELP
+%type <cmd> list_command LIST
+%type <cmd> revert_command REVERT
+%type <cmd> select_command SELECT
+%type <cmd> set_command SET
+%type <cmd> verify_command VERIFY
+%type <cmd> walkprop_command WALKPROP
+%type <cmd> terminator
+
+%%
+
+commands: command terminator
+ {
+ if ($1 != NULL) {
+ if ($1->cmd_handler != NULL)
+ if (check_scope($1->cmd_num))
+ $1->cmd_handler($1);
+ free_cmd($1);
+ }
+ return (0);
+ }
+ | command error terminator
+ {
+ if ($1 != NULL)
+ free_cmd($1);
+ if (YYRECOVERING())
+ YYABORT;
+ yyclearin;
+ yyerrok;
+ }
+ | error terminator
+ {
+ if (YYRECOVERING())
+ YYABORT;
+ yyclearin;
+ yyerrok;
+ }
+ | terminator
+ {
+ return (0);
+ }
+
+command: cancel_command
+ | clear_command
+ | commit_command
+ | create_command
+ | destroy_command
+ | end_command
+ | exit_command
+ | export_command
+ | get_command
+ | help_command
+ | list_command
+ | revert_command
+ | select_command
+ | set_command
+ | verify_command
+ | walkprop_command
+
+terminator: '\n' { newline_terminated = B_TRUE; }
+ | ';' { newline_terminated = B_FALSE; }
+
+cancel_command: CANCEL
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_CANCEL;
+ $$->cmd_handler = &cancel_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+clear_command: CLEAR
+ {
+ command_usage(CMD_CLEAR);
+ YYERROR;
+ }
+ | CLEAR TOKEN
+ {
+ properr($2);
+ YYERROR;
+ }
+ | CLEAR property_type
+ {
+ /* clear prop */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_CLEAR;
+ $$->cmd_handler = &clear_func;
+ $$->cmd_prop_type = $2;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+commit_command: COMMIT
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_COMMIT;
+ $$->cmd_handler = &commit_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+create_command: CREATE
+ {
+ command_usage(CMD_CREATE);
+ YYERROR;
+ }
+ | CREATE TOKEN
+ {
+ command_usage(CMD_CREATE);
+ YYERROR;
+ }
+ | CREATE resource1_type
+ {
+ command_usage(CMD_CREATE);
+ YYERROR;
+ }
+ | CREATE resource2_type
+ {
+ command_usage(CMD_CREATE);
+ YYERROR;
+ }
+ | CREATE resource1_type TOKEN
+ {
+ /* create enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_CREATE;
+ $$->cmd_handler = &create_func;
+ $$->cmd_res1_type = $2;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | CREATE resource2_type ncu_class_type TOKEN
+ {
+ /* create ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_CREATE;
+ $$->cmd_handler = &create_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = $3;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = NULL;
+ }
+ | CREATE OPTION TOKEN resource1_type TOKEN
+ {
+ /* create -t old enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_CREATE;
+ $$->cmd_handler = &create_func;
+ $$->cmd_res1_type = $4;
+ $$->cmd_argc = 3;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = $5;
+ $$->cmd_argv[3] = NULL;
+ }
+ | CREATE OPTION TOKEN resource2_type ncu_class_type TOKEN
+ {
+ /* create -t old ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_CREATE;
+ $$->cmd_handler = &create_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $4;
+ $$->cmd_ncu_class_type = $5;
+ $$->cmd_argc = 3;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = $6;
+ $$->cmd_argv[3] = NULL;
+ }
+
+destroy_command: DESTROY
+ {
+ command_usage(CMD_DESTROY);
+ YYERROR;
+ }
+ | DESTROY OPTION
+ {
+ /* destroy -a */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_DESTROY;
+ $$->cmd_handler = &destroy_func;
+ $$->cmd_res1_type = -1; /* special value */
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = NULL;
+ }
+ | DESTROY resource1_type
+ {
+ command_usage(CMD_DESTROY);
+ YYERROR;
+ }
+ | DESTROY resource2_type
+ {
+ command_usage(CMD_DESTROY);
+ YYERROR;
+ }
+ | DESTROY resource1_type TOKEN
+ {
+ /* destroy enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_DESTROY;
+ $$->cmd_handler = &destroy_func;
+ $$->cmd_res1_type = $2;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | DESTROY resource2_type TOKEN
+ {
+ /* destroy ncu test (class inferred) */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_DESTROY;
+ $$->cmd_handler = &destroy_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = NCU_CLASS_ANY;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | DESTROY resource2_type ncu_class_type TOKEN
+ {
+ /* destroy ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_DESTROY;
+ $$->cmd_handler = &destroy_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = $3;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = NULL;
+ }
+
+end_command: END
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_END;
+ $$->cmd_handler = &end_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+exit_command: EXIT
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXIT;
+ $$->cmd_handler = &exit_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+export_command: EXPORT
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+ | EXPORT TOKEN
+ {
+ command_usage(CMD_EXPORT);
+ YYERROR;
+ }
+ | EXPORT OPTION
+ {
+ /* export -d */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = NULL;
+ }
+ | EXPORT OPTION TOKEN
+ {
+ /* export -f file */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_argc = 2;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = NULL;
+ }
+ | EXPORT OPTION OPTION TOKEN
+ {
+ /* export -d -f file */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_argc = 3;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = $4;
+ $$->cmd_argv[3] = NULL;
+ }
+ | EXPORT resource1_type TOKEN
+ {
+ /* export enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_res1_type = $2;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | EXPORT resource2_type TOKEN
+ {
+ /* export ncu test (all ncu's named test) */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = NCU_CLASS_ANY;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | EXPORT resource2_type ncu_class_type TOKEN
+ {
+ /* export ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = $3;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = NULL;
+ }
+ | EXPORT OPTION TOKEN resource1_type TOKEN
+ {
+ /* export -f file enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_res1_type = $4;
+ $$->cmd_argc = 3;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = $5;
+ $$->cmd_argv[3] = NULL;
+ }
+ | EXPORT OPTION TOKEN resource2_type TOKEN
+ {
+ /* export -f file ncu test (all ncu's named test) */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $4;
+ $$->cmd_ncu_class_type = NCU_CLASS_ANY;
+ $$->cmd_argc = 3;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = $5;
+ $$->cmd_argv[3] = NULL;
+ }
+ | EXPORT OPTION TOKEN resource2_type ncu_class_type TOKEN
+ {
+ /* export -f file ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_EXPORT;
+ $$->cmd_handler = &export_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $4;
+ $$->cmd_ncu_class_type = $5;
+ $$->cmd_argc = 3;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = $3;
+ $$->cmd_argv[2] = $6;
+ $$->cmd_argv[3] = NULL;
+ }
+
+get_command: GET
+ {
+ command_usage(CMD_GET);
+ YYERROR;
+ }
+ | GET TOKEN
+ {
+ properr($2);
+ YYERROR;
+ }
+ | GET property_type
+ {
+ /* get prop */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_GET;
+ $$->cmd_handler = &get_func;
+ $$->cmd_prop_type = $2;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+ | GET OPTION property_type
+ {
+ /* get -V prop */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_GET;
+ $$->cmd_handler = &get_func;
+ $$->cmd_prop_type = $3;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = NULL;
+ }
+
+help_command: HELP
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_HELP;
+ $$->cmd_handler = &help_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+ | HELP TOKEN
+ {
+ /* help command */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_HELP;
+ $$->cmd_handler = &help_func;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = NULL;
+ }
+
+list_command: LIST
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+ | LIST TOKEN
+ {
+ command_usage(CMD_LIST);
+ YYERROR;
+ }
+ | LIST OPTION
+ {
+ /* list -a */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = -1; /* special value */
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = NULL;
+ }
+ | LIST resource1_type
+ {
+ command_usage(CMD_LIST);
+ YYERROR;
+ }
+ | LIST resource2_type
+ {
+ command_usage(CMD_LIST);
+ YYERROR;
+ }
+ | LIST resource1_type TOKEN
+ {
+ /* list enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = $2;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | LIST resource2_type TOKEN
+ {
+ /* list ncu test (all ncu's named test) */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = NCU_CLASS_ANY;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | LIST resource2_type ncu_class_type TOKEN
+ {
+ /* list ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = $3;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = NULL;
+ }
+ | LIST OPTION resource1_type TOKEN
+ {
+ /* list -a enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = $3;
+ $$->cmd_argc = 2;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = $2;
+ $$->cmd_argv[2] = NULL;
+ }
+ | LIST OPTION resource2_type TOKEN
+ {
+ /* list -a ncu test (all ncu's named test) */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $3;
+ $$->cmd_ncu_class_type = NCU_CLASS_ANY;
+ $$->cmd_argc = 2;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = $2;
+ $$->cmd_argv[2] = NULL;
+ }
+ | LIST OPTION resource2_type ncu_class_type TOKEN
+ {
+ /* list -a ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_LIST;
+ $$->cmd_handler = &list_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $3;
+ $$->cmd_ncu_class_type = $4;
+ $$->cmd_argc = 2;
+ $$->cmd_argv[0] = $5;
+ $$->cmd_argv[1] = $2;
+ $$->cmd_argv[2] = NULL;
+ }
+
+revert_command: REVERT
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_REVERT;
+ $$->cmd_handler = &revert_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+select_command: SELECT
+ {
+ command_usage(CMD_SELECT);
+ YYERROR;
+ }
+ | SELECT TOKEN
+ {
+ command_usage(CMD_SELECT);
+ YYERROR;
+ }
+ | SELECT resource1_type
+ {
+ command_usage(CMD_SELECT);
+ YYERROR;
+ }
+ | SELECT resource2_type
+ {
+ command_usage(CMD_SELECT);
+ YYERROR;
+ }
+ | SELECT resource1_type TOKEN
+ {
+ /* select enm/loc/ncp test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_SELECT;
+ $$->cmd_handler = &select_func;
+ $$->cmd_res1_type = $2;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | SELECT resource2_type TOKEN
+ {
+ /* select ncu test (class inferred) */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_SELECT;
+ $$->cmd_handler = &select_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = NCU_CLASS_ANY;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $3;
+ $$->cmd_argv[1] = NULL;
+ }
+ | SELECT resource2_type ncu_class_type TOKEN
+ {
+ /* select ncu ip/phys test */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_SELECT;
+ $$->cmd_handler = &select_func;
+ $$->cmd_res1_type = RT1_NCP;
+ $$->cmd_res2_type = $2;
+ $$->cmd_ncu_class_type = $3;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = NULL;
+ }
+
+set_command: SET
+ {
+ command_usage(CMD_SET);
+ YYERROR;
+ }
+ | SET TOKEN
+ {
+ properr($2);
+ YYERROR;
+ }
+ | SET property_type EQUAL TOKEN
+ {
+ /* set prop=value */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_SET;
+ $$->cmd_handler = &set_func;
+ $$->cmd_prop_type = $2;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $4;
+ $$->cmd_argv[1] = NULL;
+ }
+
+verify_command: VERIFY
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_VERIFY;
+ $$->cmd_handler = &verify_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+
+walkprop_command: WALKPROP
+ {
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_WALKPROP;
+ $$->cmd_handler = &walkprop_func;
+ $$->cmd_argc = 0;
+ $$->cmd_argv[0] = NULL;
+ }
+ | WALKPROP OPTION
+ {
+ /* walkprop -a */
+ if (($$ = alloc_cmd()) == NULL)
+ YYERROR;
+ cmd = $$;
+ $$->cmd_num = CMD_WALKPROP;
+ $$->cmd_handler = &walkprop_func;
+ $$->cmd_argc = 1;
+ $$->cmd_argv[0] = $2;
+ $$->cmd_argv[1] = NULL;
+ }
+
+resource1_type: LOC { $$ = RT1_LOC; }
+ | NCP { $$ = RT1_NCP; }
+ | ENM { $$ = RT1_ENM; }
+ | WLAN { $$ = RT1_WLAN; }
+
+resource2_type: NCU { $$ = RT2_NCU; }
+
+ncu_class_type: PHYS { $$ = NCU_CLASS_PHYS; }
+ | IP { $$ = NCU_CLASS_IP; }
+
+property_type: UNKNOWN { $$ = PT_UNKNOWN; }
+ | ACTIVATION_MODE { $$ = PT_ACTIVATION_MODE; }
+ | CONDITIONS { $$ = PT_CONDITIONS; }
+ | ENABLED { $$ = PT_ENABLED; }
+ | TYPE { $$ = PT_TYPE; }
+ | CLASS { $$ = PT_CLASS; }
+ | PARENT { $$ = PT_PARENT; }
+ | PRIORITY_GROUP { $$ = PT_PRIORITY_GROUP; }
+ | PRIORITY_MODE { $$ = PT_PRIORITY_MODE; }
+ | LINK_MACADDR { $$ = PT_LINK_MACADDR; }
+ | LINK_AUTOPUSH { $$ = PT_LINK_AUTOPUSH; }
+ | LINK_MTU { $$ = PT_LINK_MTU; }
+ | IP_VERSION { $$ = PT_IP_VERSION; }
+ | IPV4_ADDRSRC { $$ = PT_IPV4_ADDRSRC; }
+ | IPV4_ADDR { $$ = PT_IPV4_ADDR; }
+ | IPV4_DEFAULT_ROUTE { $$ = PT_IPV4_DEFAULT_ROUTE; }
+ | IPV6_ADDRSRC { $$ = PT_IPV6_ADDRSRC; }
+ | IPV6_ADDR { $$ = PT_IPV6_ADDR; }
+ | IPV6_DEFAULT_ROUTE { $$ = PT_IPV6_DEFAULT_ROUTE; }
+ | ENM_FMRI { $$ = PT_ENM_FMRI; }
+ | ENM_START { $$ = PT_ENM_START; }
+ | ENM_STOP { $$ = PT_ENM_STOP; }
+ | LOC_NAMESERVICES { $$ = PT_LOC_NAMESERVICES; }
+ | LOC_NAMESERVICES_CONFIG { $$ = PT_LOC_NAMESERVICES_CONFIG; }
+ | LOC_DNS_CONFIGSRC { $$ = PT_LOC_DNS_CONFIGSRC; }
+ | LOC_DNS_DOMAIN { $$ = PT_LOC_DNS_DOMAIN; }
+ | LOC_DNS_SERVERS { $$ = PT_LOC_DNS_SERVERS; }
+ | LOC_DNS_SEARCH { $$ = PT_LOC_DNS_SEARCH; }
+ | LOC_NIS_CONFIGSRC { $$ = PT_LOC_NIS_CONFIGSRC; }
+ | LOC_NIS_SERVERS { $$ = PT_LOC_NIS_SERVERS; }
+ | LOC_LDAP_CONFIGSRC { $$ = PT_LOC_LDAP_CONFIGSRC; }
+ | LOC_LDAP_SERVERS { $$ = PT_LOC_LDAP_SERVERS; }
+ | LOC_DEFAULT_DOMAIN { $$ = PT_LOC_DEFAULT_DOMAIN; }
+ | LOC_NFSV4_DOMAIN { $$ = PT_LOC_NFSV4_DOMAIN; }
+ | LOC_IPF_CONFIG { $$ = PT_LOC_IPF_CONFIG; }
+ | LOC_IPF_V6_CONFIG { $$ = PT_LOC_IPF_V6_CONFIG; }
+ | LOC_IPNAT_CONFIG { $$ = PT_LOC_IPNAT_CONFIG; }
+ | LOC_IPPOOL_CONFIG { $$ = PT_LOC_IPPOOL_CONFIG; }
+ | LOC_IKE_CONFIG { $$ = PT_LOC_IKE_CONFIG; }
+ | LOC_IPSECPOL_CONFIG { $$ = PT_LOC_IPSECPOL_CONFIG; }
+ | WLAN_BSSIDS { $$ = PT_WLAN_BSSIDS; }
+ | WLAN_PRIORITY { $$ = PT_WLAN_PRIORITY; }
+ | WLAN_KEYNAME { $$ = PT_WLAN_KEYNAME; }
+ | WLAN_KEYSLOT { $$ = PT_WLAN_KEYSLOT; }
+ | WLAN_SECURITY_MODE { $$ = PT_WLAN_SECURITY_MODE; }
+
+%%
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l
new file mode 100644
index 0000000000..e6fc23ff20
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.l
@@ -0,0 +1,291 @@
+%{
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <string.h>
+#include <libintl.h>
+#include <sys/types.h>
+#include "nwamcfg.h"
+#include "nwamcfg_grammar.tab.h"
+
+int lex_lineno = 1; /* line number for error reporting */
+static int state = INITIAL;
+extern boolean_t cmd_file_mode;
+
+extern void yyerror(char *s);
+char *safe_strdup(char *s);
+%}
+
+%a 7000
+%p 5000
+%e 2000
+%n 800
+%o 12000
+
+%{
+/*
+ * The state below are for tokens.
+ */
+%}
+%s TSTATE
+%%
+
+<INITIAL>"#"[^\n]* { }
+
+<INITIAL>cancel {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return CANCEL;
+ }
+
+<INITIAL>clear {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return CLEAR;
+ }
+
+<INITIAL>commit {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return COMMIT;
+ }
+
+<INITIAL>create {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return CREATE;
+ }
+
+
+<INITIAL>destroy {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return DESTROY;
+ }
+
+<INITIAL>end {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return END;
+ }
+
+<INITIAL>exit|quit {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return EXIT;
+ }
+
+<INITIAL>export {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return EXPORT;
+ }
+
+<INITIAL>get {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return GET;
+ }
+
+<INITIAL>"?"|help {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return HELP;
+ }
+
+<INITIAL>list {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return LIST;
+ }
+
+<INITIAL>revert {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return REVERT;
+ }
+
+<INITIAL>select {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return SELECT;
+ }
+
+<INITIAL>set {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return SET;
+ }
+
+<INITIAL>verify {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return VERIFY;
+ }
+
+<INITIAL>walkprop {
+ BEGIN TSTATE;
+ state = TSTATE;
+ return WALKPROP;
+ }
+
+<TSTATE>[Ll][Oo][Cc] { return LOC; }
+<TSTATE>[Nn][Cc][Pp] { return NCP; }
+<TSTATE>[Ee][Nn][Mm] { return ENM; }
+<TSTATE>[Ww][Ll][Aa][Nn] { return WLAN; }
+
+<TSTATE>[Nn][Cc][Uu] { return NCU; }
+
+<TSTATE>[Pp][Hh][Yy][Ss] { return PHYS; }
+<TSTATE>[Ii][Pp] { return IP; }
+
+<TSTATE>unknown { return UNKNOWN; }
+<TSTATE>activation-mode { return ACTIVATION_MODE; }
+<TSTATE>conditions { return CONDITIONS; }
+<TSTATE>enabled { return ENABLED; }
+
+<TSTATE>type { return TYPE; }
+<TSTATE>class { return CLASS; }
+<TSTATE>parent { return PARENT; }
+<TSTATE>priority-group { return PRIORITY_GROUP; }
+<TSTATE>priority-mode { return PRIORITY_MODE; }
+<TSTATE>link-mac-addr { return LINK_MACADDR; }
+<TSTATE>link-autopush { return LINK_AUTOPUSH; }
+<TSTATE>link-mtu { return LINK_MTU; }
+<TSTATE>ip-version { return IP_VERSION; }
+<TSTATE>ipv4-addrsrc { return IPV4_ADDRSRC; }
+<TSTATE>ipv4-addr { return IPV4_ADDR; }
+<TSTATE>ipv4-default-route { return IPV4_DEFAULT_ROUTE; }
+<TSTATE>ipv6-addrsrc { return IPV6_ADDRSRC; }
+<TSTATE>ipv6-addr { return IPV6_ADDR; }
+<TSTATE>ipv6-default-route { return IPV6_DEFAULT_ROUTE; }
+
+<TSTATE>state { return ENM_STATE; }
+<TSTATE>fmri { return ENM_FMRI; }
+<TSTATE>start { return ENM_START; }
+<TSTATE>stop { return ENM_STOP; }
+
+<TSTATE>nameservices { return LOC_NAMESERVICES; }
+<TSTATE>nameservices-config-file { return LOC_NAMESERVICES_CONFIG; }
+<TSTATE>dns-nameservice-configsrc { return LOC_DNS_CONFIGSRC; }
+<TSTATE>dns-nameservice-domain { return LOC_DNS_DOMAIN; }
+<TSTATE>dns-nameservice-servers { return LOC_DNS_SERVERS; }
+<TSTATE>dns-nameservice-search { return LOC_DNS_SEARCH; }
+<TSTATE>nis-nameservice-configsrc { return LOC_NIS_CONFIGSRC; }
+<TSTATE>nis-nameservice-servers { return LOC_NIS_SERVERS; }
+<TSTATE>ldap-nameservice-configsrc { return LOC_LDAP_CONFIGSRC; }
+<TSTATE>ldap-nameservice-servers { return LOC_LDAP_SERVERS; }
+<TSTATE>default-domain { return LOC_DEFAULT_DOMAIN; }
+<TSTATE>nfsv4-domain { return LOC_NFSV4_DOMAIN; }
+<TSTATE>ipfilter-config-file { return LOC_IPF_CONFIG; }
+<TSTATE>ipfilter-v6-config-file { return LOC_IPF_V6_CONFIG; }
+<TSTATE>ipnat-config-file { return LOC_IPNAT_CONFIG; }
+<TSTATE>ippool-config-file { return LOC_IPPOOL_CONFIG; }
+<TSTATE>ike-config-file { return LOC_IKE_CONFIG; }
+<TSTATE>ipsecpolicy-config-file { return LOC_IPSECPOL_CONFIG; }
+
+<TSTATE>bssids { return WLAN_BSSIDS; }
+<TSTATE>priority { return WLAN_PRIORITY; }
+<TSTATE>keyname { return WLAN_KEYNAME; }
+<TSTATE>keyslot { return WLAN_KEYSLOT; }
+<TSTATE>security-mode { return WLAN_SECURITY_MODE; }
+
+<TSTATE>= { return EQUAL; }
+
+<TSTATE>\-[adftV] { /* matches options */
+ yylval.strval = safe_strdup(yytext);
+ return OPTION;
+ }
+
+<TSTATE>[^ \t\n\";=\[\]\(\)]+ { /* matches non-quoted values */
+ yylval.strval = safe_strdup(yytext);
+ return TOKEN;
+ }
+
+<TSTATE>\"[^\"\n]*[\"\n] { /* matches string with quotes */
+ yylval.strval = safe_strdup(yytext);
+ return TOKEN;
+ }
+
+<TSTATE>\".*\"\,\".*\" { /* matches string list of the form "a","b",.. */
+ yylval.strval = safe_strdup(yytext);
+ return TOKEN;
+ }
+
+";" {
+ BEGIN INITIAL;
+ return (yytext[0]);
+ }
+
+\n {
+ lex_lineno++;
+ BEGIN INITIAL;
+ return (yytext[0]);
+ }
+
+[ \t] ; /* ignore white space */
+
+. { /* matches all single otherwise unmatched characters */
+ return (yytext[0]);
+ }
+
+%%
+
+char *
+safe_strdup(char *s)
+{
+ char *result;
+
+ if ((result = strdup(s)) == NULL) {
+ yyerror("Out of memory");
+ exit(1);
+ }
+ return (result);
+}
+
+void
+yyerror(char *s)
+{
+ /* feof(yyin) is not an error; anything else is, so we set saw_error */
+ if (yytext[0] == '\0') {
+ if (!feof(yyin)) {
+ saw_error = B_TRUE;
+ (void) fprintf(stderr, gettext("%s, token expected\n"),
+ s);
+ }
+ return;
+ }
+
+ saw_error = B_TRUE;
+ if (cmd_file_mode) {
+ (void) fprintf(stderr, gettext("%s on line %d at '%s'\n"), s,
+ lex_lineno, (yytext[0] == '\n') ? "\\n" : yytext);
+ } else {
+ (void) fprintf(stderr, gettext("%s at '%s'\n\n"), s,
+ (yytext[0] == '\n') ? "\\n" : yytext);
+ }
+ help_wrap();
+}
diff --git a/usr/src/cmd/dladm/Makefile b/usr/src/cmd/dladm/Makefile
index f336828605..657ceee889 100644
--- a/usr/src/cmd/dladm/Makefile
+++ b/usr/src/cmd/dladm/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -40,7 +40,7 @@ LDLIBS += $(ZLAZYLOAD) -lrstp $(ZNOLAZYLOAD)
# For headers from librstp.
LINTFLAGS += -erroff=E_TRAILING_COMMA_IN_ENUM
-$(ROOTCFGDIR)/secobj.conf := FILEMODE= 600
+$(ROOTCFGDIR)/secobj.conf := FILEMODE= 660
lint := ZLAZYLOAD=
lint := ZNOLAZYLOAD=
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_db.c b/usr/src/cmd/dlmgmtd/dlmgmt_db.c
index acc7e22a8a..9f1a3406ef 100644
--- a/usr/src/cmd/dlmgmtd/dlmgmt_db.c
+++ b/usr/src/cmd/dlmgmtd/dlmgmt_db.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -296,7 +296,7 @@ dlmgmt_zopen_cb(zfarg_t *zfarg)
if ((fd = open(filename, oflags, mode)) == -1)
return (errno);
if (newfile) {
- if (chown(filename, UID_DLADM, GID_SYS) == -1) {
+ if (chown(filename, UID_DLADM, GID_NETADM) == -1) {
err = errno;
(void) close(fd);
return (err);
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_main.c b/usr/src/cmd/dlmgmtd/dlmgmt_main.c
index fc5f3be922..c02610bb5f 100644
--- a/usr/src/cmd/dlmgmtd/dlmgmt_main.c
+++ b/usr/src/cmd/dlmgmtd/dlmgmt_main.c
@@ -145,7 +145,7 @@ dlmgmt_door_attach(zoneid_t zoneid, char *rootdir)
return (err);
}
(void) close(fd);
- if (chown(doorpath, UID_DLADM, GID_SYS) == -1)
+ if (chown(doorpath, UID_DLADM, GID_NETADM) == -1)
return (errno);
/*
@@ -199,7 +199,7 @@ dlmgmt_zone_init(zoneid_t zoneid)
}
if ((chmod(tmpfsdir, 0755) < 0) ||
- (chown(tmpfsdir, UID_DLADM, GID_SYS) < 0)) {
+ (chown(tmpfsdir, UID_DLADM, GID_NETADM) < 0)) {
return (EPERM);
}
@@ -428,7 +428,7 @@ dlmgmt_set_privileges(void)
int err;
(void) setgroups(0, NULL);
- if (setegid(GID_SYS) == -1 || seteuid(UID_DLADM) == -1)
+ if (setegid(GID_NETADM) == -1 || seteuid(UID_DLADM) == -1)
err = errno;
else
err = dlmgmt_drop_privileges();
diff --git a/usr/src/cmd/fs.d/nfs/lib/selfcheck.c b/usr/src/cmd/fs.d/nfs/lib/selfcheck.c
index 75d9ae357e..a7f0c027c6 100644
--- a/usr/src/cmd/fs.d/nfs/lib/selfcheck.c
+++ b/usr/src/cmd/fs.d/nfs/lib/selfcheck.c
@@ -20,7 +20,7 @@
*/
/*
* selfcheck.c
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -52,7 +52,13 @@ self_check(char *hostname)
struct sockaddr_in6 ipv6addr;
family = AF_INET6;
- flags = AI_DEFAULT;
+ /*
+ * We cannot specify AI_DEFAULT since it includes AI_ADDRCONFIG.
+ * Localhost name resolution will fail if no IP interfaces other than
+ * loopback are plumbed and AI_ADDRCONFIG is specified, and this
+ * causes localhost mounts to fail.
+ */
+ flags = AI_V4MAPPED;
if ((s = socket(family, SOCK_DGRAM, 0)) < 0) {
syslog(LOG_ERR, "self_check: socket: %m");
diff --git a/usr/src/cmd/ipf/svc/ipfilter.xml b/usr/src/cmd/ipf/svc/ipfilter.xml
index 397112ba69..ce266ee9ef 100644
--- a/usr/src/cmd/ipf/svc/ipfilter.xml
+++ b/usr/src/cmd/ipf/svc/ipfilter.xml
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
- Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
CDDL HEADER START
@@ -73,7 +73,7 @@
</dependency>
<dependent
- name='network'
+ name='ipf_network'
grouping='optional_all'
restart_on='restart'>
<service_fmri value='svc:/milestone/network' />
@@ -120,6 +120,16 @@
<propval name='value_authorization' type='astring'
value='solaris.smf.value.firewall.config' />
</property_group>
+
+ <property_group name='config' type='application'>
+ <propval name='ipf6_config_file' type='astring'
+ value='/etc/ipf/ipf6.conf' />
+ <propval name='ipnat_config_file' type='astring'
+ value='/etc/ipf/ipnat.conf' />
+ <propval name='ippool_config_file' type='astring'
+ value='/etc/ipf/ippool.conf' />
+ </property_group>
+
</instance>
<stability value='Unstable' />
diff --git a/usr/src/cmd/svc/milestone/Makefile b/usr/src/cmd/svc/milestone/Makefile
index 8f51fd3e7e..21328730fd 100644
--- a/usr/src/cmd/svc/milestone/Makefile
+++ b/usr/src/cmd/svc/milestone/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -41,7 +41,12 @@ FSMANIFESTS= $(FSSVCS:%=$(ROOTSVCSYSTEMFILESYSTEM)/%)
NETSVCS= \
network-initial.xml \
network-iptun.xml \
+ network-ipqos.xml \
+ network-location.xml \
network-loopback.xml \
+ network-netcfg.xml \
+ network-netmask.xml \
+ network-netcfg.xml \
network-physical.xml \
network-routing-setup.xml \
network-service.xml
@@ -103,9 +108,12 @@ SVCMETHOD=\
identity-domain \
identity-node \
manifest-import \
+ net-loc \
net-loopback \
net-init \
net-iptun \
+ net-ipqos \
+ net-netmask \
net-nwam \
net-physical \
net-routing-setup \
diff --git a/usr/src/cmd/svc/milestone/name-services.xml b/usr/src/cmd/svc/milestone/name-services.xml
index 82ff48c991..556c6e8a20 100644
--- a/usr/src/cmd/svc/milestone/name-services.xml
+++ b/usr/src/cmd/svc/milestone/name-services.xml
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<!--
- Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
CDDL HEADER START
@@ -43,7 +43,7 @@
<dependency
name='dns'
grouping='optional_all'
- restart_on='none'
+ restart_on='refresh'
type='service'>
<service_fmri value='svc:/network/dns/client' />
</dependency>
@@ -51,7 +51,7 @@
<dependency
name='ldap'
grouping='optional_all'
- restart_on='none'
+ restart_on='refresh'
type='service'>
<service_fmri value='svc:/network/ldap/client' />
</dependency>
@@ -59,7 +59,7 @@
<dependency
name='nis_client'
grouping='optional_all'
- restart_on='none'
+ restart_on='refresh'
type='service'>
<service_fmri value='svc:/network/nis/client' />
</dependency>
diff --git a/usr/src/cmd/svc/milestone/net-ipqos b/usr/src/cmd/svc/milestone/net-ipqos
new file mode 100644
index 0000000000..62d1879071
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/net-ipqos
@@ -0,0 +1,48 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Load the IPQoS configuration.
+#
+
+. /lib/svc/share/smf_include.sh
+
+#
+# In a shared-IP zone we need this service to be up, but all of the
+# work it tries to do is irrelevant (and will actually lead to the
+# service failing if we try to do it), so just bail out.
+# In the global zone and exclusive-IP zones we proceed.
+#
+smf_configure_ip || exit $SMF_EXIT_OK
+
+#
+# This is backgrounded so that any remote hostname lookups it performs
+# don't unduely delay startup. Any messages go via syslog.
+#
+
+if [ -f /usr/sbin/ipqosconf -a -f /etc/inet/ipqosinit.conf ]; then
+ /usr/sbin/ipqosconf -s -a /etc/inet/ipqosinit.conf &
+fi
diff --git a/usr/src/cmd/svc/milestone/net-loc b/usr/src/cmd/svc/milestone/net-loc
new file mode 100644
index 0000000000..60f466d8d7
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/net-loc
@@ -0,0 +1,697 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. /lib/svc/share/smf_include.sh
+. /lib/svc/share/net_include.sh
+
+# FMRI consts
+AUTOFS_FMRI="svc:/system/filesystem/autofs"
+DNS_CLIENT_FMRI="svc:/network/dns/client"
+IPSEC_IKE_FMRI="svc:/network/ipsec/ike"
+IPSEC_POLICY_FMRI="svc:/network/ipsec/policy"
+IPFILTER_FMRI="svc:/network/ipfilter:default"
+LDAP_CLIENT_FMRI="svc:/network/ldap/client"
+LOCATION_FMRI="svc:/network/location:default"
+MAPID_FMRI="svc:/network/nfs/mapid:default"
+NIS_CLIENT_FMRI="svc:/network/nis/client"
+NWAM_FMRI="svc:/network/physical:nwam"
+
+# commands
+CP=/usr/bin/cp
+DHCPINFO=/sbin/dhcpinfo
+DOMAINNAME=/usr/bin/domainname
+GREP=/usr/bin/grep
+LDAPCLIENT=/usr/sbin/ldapclient
+MKDIR=/usr/bin/mkdir
+MV=/usr/bin/mv
+NAWK=/usr/bin/nawk
+NWAMADM=/usr/sbin/nwamadm
+NWAMCFG=/usr/sbin/nwamcfg
+RM=/usr/bin/rm
+SED=/usr/bin/sed
+SVCADM=/usr/sbin/svcadm
+SVCCFG=/usr/sbin/svccfg
+SVCPROP=/usr/bin/svcprop
+TOUCH=/usr/bin/touch
+
+# Path to directories
+ETC_DEFAULT_DOMAIN=/etc/defaultdomain
+NIS_BIND_PATH=/var/yp/binding
+LEGACY_LOC_PATH=/etc/nwam/loc/Legacy
+USER_LOC_PATH=/etc/nwam/loc/User
+SCRIPT_PATH=/etc/svc/volatile/nwam
+
+#
+# echoes DHCP controlled interfaces separated by commas
+#
+# Don't parse the output of ifconfig(1M) because interfaces that haven't
+# acquired a DHCP lease also have the DHCP flag set.
+#
+get_dhcp_interfaces () {
+ #
+ # 1. parse netstat(1M) output for v4 interfaces in BOUND
+ # or INFORMATION state
+ # 2. make a space-separated list of interface names
+ #
+ netstat -D -f inet | $NAWK '
+ $2 ~ /BOUND/ { printf "%s ", $1 }
+ $2 ~ /INFORMATION/ { printf "%s ", $1 }'
+}
+
+#
+# get_dhcpinfo <code/identifier>
+#
+# echoes the value received through each interface controlled by DHCP
+# returns:
+# 0 => property is set
+# 1 => property is not set
+#
+get_dhcpinfo () {
+ code=$1
+
+ # Get all interfaces with DHCP control, IFS is " "
+ interfaces=`get_dhcp_interfaces`
+
+ info=""
+ for intf in $interfaces; do
+ val=`$DHCPINFO -i $intf $code`
+ if [ $? -eq 0 ]; then
+ if [ "$info" = "" ]; then
+ info="$val"
+ else
+ info="$info,$val"
+ fi
+ fi
+ done
+ echo $info
+}
+
+#
+# set_smf_prop <fmri> <property name> <property value>
+#
+set_smf_prop () {
+ $SVCCFG -s $1 setprop $2 = astring: "$3" && return
+}
+
+#
+# refresh_svc <fmri>
+#
+# Refreshes the service.
+#
+refresh_svc () {
+ $SVCADM refresh $1
+}
+
+#
+# restart_svc <fmri>
+#
+# Restarts the service.
+#
+restart_svc () {
+ $SVCADM restart $1
+}
+
+#
+# start_svc <fmri>
+#
+# Starts the service. If the service is already enabled, restarts it. If
+# it is not enabled, temporarily enables it.
+#
+start_svc () {
+ if service_is_enabled $1; then
+ $SVCADM restart $1
+ else
+ $SVCADM enable -t $1
+ fi
+}
+
+#
+# stop_svc <fmri>
+#
+# Temporarily disables the service.
+#
+stop_svc () {
+ $SVCADM disable -t $1
+}
+
+#
+# copy_default <dir> <file>
+#
+# Copies <dir>/<file>.dfl to <dir>/<file>
+#
+copy_default () {
+ $CP -p $1/$2.dfl $1/$2
+}
+
+#
+# do_dns <location>
+#
+# Installs DNS information on /etc/resolv.conf for location
+#
+do_dns () {
+ loc=$1
+ file=/etc/resolv.conf
+
+ # Write out to temporary file first
+ $TOUCH $file.$$
+
+ DNS_CONFIGSRC=`nwam_get_loc_prop $loc dns-nameservice-configsrc`
+ (IFS=" ";
+ for configsrc in $DNS_CONFIGSRC; do
+ case "$configsrc" in
+ 'manual')
+ DNS_DOMAIN=`nwam_get_loc_prop $loc \
+ dns-nameservice-domain`
+ DNS_SERVERS=`nwam_get_loc_prop $loc \
+ dns-nameservice-servers`
+ DNS_SEARCH=`nwam_get_loc_prop $loc \
+ dns-nameservice-search`
+ ;;
+ 'dhcp')
+ DNS_DOMAIN=`get_dhcpinfo DNSdmain`
+ DNS_SERVERS=`get_dhcpinfo DNSserv`
+ # No DNS search info for IPv4
+ ;;
+ '*')
+ echo "Unrecognized DNS configsrc ${configsrc}; ignoring"
+ ;;
+ esac
+
+ # Write DNS settings
+ if [ -n "$DNS_DOMAIN" ]; then
+ echo "$DNS_DOMAIN" | $NAWK \
+ 'FS="," { for (i = 1; i <= NF; i++) \
+ print "domain ", $i }' >> $file.$$
+ fi
+ if [ -n "$DNS_SEARCH" ]; then
+ echo "$DNS_SEARCH" | $NAWK \
+ 'FS="," { printf("search"); \
+ for (i = 1; i <= NF; i++) printf(" %s", $i); \
+ printf("\n") }' >> $file.$$
+ fi
+ if [ -n "$DNS_SERVERS" ]; then
+ echo "$DNS_SERVERS" | $NAWK \
+ 'FS="," { for (i = 1; i <= NF; i++) \
+ print "nameserver ", $i }' >> $file.$$
+ fi
+ done
+ )
+ # Finally, copy our working version to the real thing
+ $MV -f $file.$$ $file
+ start_svc $DNS_CLIENT_FMRI
+}
+
+#
+# do_nis <location>
+#
+# Installs NIS information on /var/yp/binding/ for location
+#
+do_nis () {
+ loc=$1
+
+ NIS_CONFIGSRC=`nwam_get_loc_prop $loc nis-nameservice-configsrc`
+ (IFS=" ";
+ domainname_set=false
+ for configsrc in $NIS_CONFIGSRC; do
+ case "$configsrc" in
+ 'manual')
+ NIS_SERVERS=`nwam_get_loc_prop $loc \
+ nis-nameservice-servers`
+ DEFAULT_DOMAIN=`nwam_get_loc_prop $loc default-domain`
+ # user-specified default-domain always wins
+ $DOMAINNAME $DEFAULT_DOMAIN
+ $DOMAINNAME > $ETC_DEFAULT_DOMAIN
+ domainname_set=true
+ ;;
+ 'dhcp')
+ # Use only the first name
+ DEFAULT_DOMAIN=`get_dhcpinfo NISdmain | \
+ $NAWK 'FS="," { print $1 }'`
+ NIS_SERVERS=`get_dhcpinfo NISservs`
+ if [ "$domainname_set" = "false" ]; then
+ $DOMAINNAME $DEFAULT_DOMAIN
+ $DOMAINNAME > $ETC_DEFAULT_DOMAIN
+ domainname_set=true
+ fi
+ ;;
+ '*')
+ echo "Unrecognized NIS configsrc ${configsrc}; ignoring"
+ ;;
+ esac
+
+ # Place NIS settings in appropriate directory/file.
+ if [ ! -d "$NIS_BIND_PATH/$DEFAULT_DOMAIN" ]; then
+ $MKDIR -p $NIS_BIND_PATH/$DEFAULT_DOMAIN
+ fi
+ if [ -n "$NIS_SERVERS" ]; then
+ echo "$NIS_SERVERS" | $NAWK \
+ 'FS="," { for (i = 1; i <= NF; i++) print $i }' \
+ > $NIS_BIND_PATH/$DEFAULT_DOMAIN/ypservers
+ fi
+ done
+ )
+ start_svc $NIS_CLIENT_FMRI
+}
+
+#
+# do_ldap <location>
+#
+# Installs LDAP information using ldapclient(1M) for location
+#
+do_ldap () {
+ loc=$1
+
+ LDAP_CONFIGSRC=`nwam_get_loc_prop $loc ldap-nameservice-configsrc`
+ (IFS=" ";
+ for configsrc in $LDAP_CONFIGSRC; do
+ case "$configsrc" in
+ 'manual')
+ LDAP_SERVERS=`nwam_get_loc_prop $loc \
+ ldap-nameservice-servers`
+ DEFAULT_DOMAIN=`nwam_get_loc_prop $loc default-domain`
+ $DOMAINNAME $DEFAULT_DOMAIN
+ $DOMAINNAME > $ETC_DEFAULT_DOMAIN
+ ;;
+ '*')
+ echo "Unrecognized LDAP configsrc ${configsrc}; ignoring"
+ ;;
+ esac
+
+ # Use ldapclient(1M) to initialize LDAP client settings.
+ if [ -n "$DEFAULT_DOMAIN" -o -n "$LDAP_SERVERS" ]; then
+ # XXX need to check how to specify multiple LDAP servers.
+ $LDAPCLIENT init -a domainName=$DEFAULT_DOMAIN \
+ $LDAP_SERVERS
+ fi
+ done
+ )
+ start_svc $LDAP_CLIENT_FMRI
+}
+
+#
+# do_ns <location>
+#
+# Installs different nameservices for location
+#
+do_ns () {
+ loc=$1
+
+ #
+ # Disable nameservices temporarily while we reconfigure. Copy
+ # /etc/nsswitch.files to /etc/nsswitch.conf first so that only "files"
+ # are used.
+ #
+ $CP -p /etc/nsswitch.files /etc/nsswitch.conf
+ stop_svc $DNS_CLIENT_FMRI
+ stop_svc $NIS_CLIENT_FMRI
+ stop_svc $LDAP_CLIENT_FMRI
+
+ #
+ # Remove /etc/defaultdomain and unset domainname(1M). If NIS
+ # and/or LDAP is configured, they will create /etc/defaultdomain
+ # and set the domainname(1M).
+ #
+ $RM -f $ETC_DEFAULT_DOMAIN
+ $DOMAINNAME " "
+
+ NAMESERVICES_CONFIG_FILE=`nwam_get_loc_prop \
+ $loc nameservices-config-file`
+ NAMESERVICES=`nwam_get_loc_prop $loc nameservices`
+
+ if [ -f "$NAMESERVICES_CONFIG_FILE" ]; then
+ $CP -p $NAMESERVICES_CONFIG_FILE /etc/nsswitch.conf
+ else
+ echo "Failed to activate location ${loc}:\
+ missing nameservices-config-file property"
+ exit $SMF_EXIT_ERR_CONFIG
+ fi
+
+ (IFS=,;
+ for ns in $NAMESERVICES; do
+ case "$ns" in
+ 'files')
+ # no additional setup needed for files nameservice
+ ;;
+ 'dns')
+ do_dns $loc
+ ;;
+ 'nis')
+ do_nis $loc
+ ;;
+ 'ldap')
+ do_ldap $loc
+ ;;
+ '*')
+ echo "Unrecognized nameservices value ${ns}; ignoring"
+ ;;
+ esac
+ done
+ )
+
+ #
+ # Restart other related services
+ #
+ # We explicitly restart here, as restart will only have an
+ # effect if the service is already enabled. We don't want
+ # to enable the service if it's currently disabled.
+ #
+ restart_svc $AUTOFS_FMRI
+}
+
+#
+# do_sec <location>
+#
+# If config properties are set, update the SMF property and refresh the
+# service. If config properties are not set, delete the SMF property and
+# stop the service.
+#
+do_sec () {
+ loc=$1
+
+ ike_file=`nwam_get_loc_prop $loc ike-config-file`
+ pol_file=`nwam_get_loc_prop $loc ipsecpolicy-config-file`
+ ipf_file=`nwam_get_loc_prop $loc ipfilter-config-file`
+ ipf6_file=`nwam_get_loc_prop $loc ipfilter-v6-config-file`
+ ipnat_file=`nwam_get_loc_prop $loc ipnat-config-file`
+ ippool_file=`nwam_get_loc_prop $loc ippool-config-file`
+
+ # IKE
+ if [ -n "$ike_file" ]; then
+ set_smf_prop $IPSEC_IKE_FMRI config/config_file $ike_file
+ refresh_svc $IPSEC_IKE_FMRI
+ start_svc $IPSEC_IKE_FMRI
+ else
+ stop_svc $IPSEC_IKE_FMRI
+ fi
+
+ # IPsec
+ if [ -n "$pol_file" ]; then
+ set_smf_prop $IPSEC_POLICY_FMRI config/config_file $pol_file
+ refresh_svc $IPSEC_POLICY_FMRI
+ start_svc $IPSEC_POLICY_FMRI
+ else
+ stop_svc $IPSEC_POLICY_FMRI
+ fi
+
+ # IPFilter
+ refresh_ipf=false
+ if [ -n "$ipf_file" ]; then
+ if [ "$ipf_file" = "/none" ]; then
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy "none"
+ elif [ "$ipf_file" = "/deny" ]; then
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy "deny"
+ elif [ "$ipf_file" = "/allow" ]; then
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy "allow"
+ else
+ # custom policy with policy file
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy "custom"
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/custom_policy_file $ipf_file
+ fi
+ refresh_ipf=true
+ else
+ # change policy to "none", no need to clear custom_policy_file
+ set_smf_prop $IPFILTER_FMRI firewall_config_default/policy \
+ "none"
+ # IPFilter has to be refreshed to make the changes effective.
+ # Don't set $refresh_ipf as it keeps IPFilter online rather
+ # than disabled. Refresh after IPFilter is disabled below.
+ fi
+ if [ -n "$ipf6_file" ]; then
+ set_smf_prop $IPFILTER_FMRI config/ipf6_config_file $ipf6_file
+ refresh_ipf=true
+ fi
+ if [ -n "$ipnat_file" ]; then
+ set_smf_prop $IPFILTER_FMRI config/ipnat_config_file $ipnat_file
+ refresh_ipf=true
+ fi
+ if [ -n "$ippool_file" ]; then
+ set_smf_prop $IPFILTER_FMRI config/ippool_config_file \
+ $ippool_file
+ refresh_ipf=true
+ fi
+
+ if [ "$refresh_ipf" = "true" ]; then
+ refresh_svc $IPFILTER_FMRI
+ start_svc $IPFILTER_FMRI
+ else
+ stop_svc $IPFILTER_FMRI
+ refresh_svc $IPFILTER_FMRI
+ fi
+}
+
+#
+# update_nfs_file <new nfsv4 domain>
+#
+update_nfs_file () {
+ domain=$1
+ file=/etc/default/nfs
+
+ #
+ # For non-commented-out lines that set NFSMAPID_DOMAIN:
+ # if not previously added by nwam, comment out with a note
+ # if previously added by nwam, remove
+ # For commented-out lines that set NFSMAPID_DOMAIN:
+ # if not commented out by NWAM, leave as-is
+ # if commented out by NWAM, remove
+ # All other lines: leave as-is
+ #
+ $NAWK ' \
+ $0 ~ /^NFSMAPID_DOMAIN=/ {
+ if (index($0, "# Added by NWAM") == 0)
+ printf("#%s # Commented out by NWAM\n", $0);
+ }
+ $0 ~ /^#NFSMAPID_DOMAIN=/ {
+ if ($0 !~ /"# Commented out by NWAM"/)
+ printf("%s\n", $0);
+ }
+ $1 !~ /NFSMAPID_DOMAIN=/ {
+ printf("%s\n", $0);
+ }' $file >$file.$$
+
+ # Now add the desired value
+ echo "NFSMAPID_DOMAIN=$domain # Added by NWAM" >> $file.$$
+
+ # Finally, copy our working version to the real thing
+ $MV -f $file.$$ $file
+}
+
+#
+# do_nfsv4 <location>
+#
+# Updates NFSv4 domain for location
+#
+do_nfsv4 () {
+ loc=$1
+
+ nfsv4domain=`nwam_get_loc_prop $loc nfsv4-domain`
+ if [ $? -eq 0 ]; then
+ update_nfs_file $nfsv4domain
+ start_svc $MAPID_FMRI
+ else
+ stop_svc $MAPID_FMRI
+ fi
+}
+
+#
+# activate_loc <location>
+#
+# Activates the given location
+#
+activate_loc () {
+ loc=$1
+
+ echo activating $loc location
+
+ do_sec $loc
+ do_ns $loc
+ do_nfsv4 $loc
+}
+
+#
+# Script entry point
+#
+# Arguments to net-loc are
+# method ('start' or 'refresh')
+
+#
+# If nwam is not enabled, do nothing and return OK.
+#
+service_is_enabled $NWAM_FMRI || exit $SMF_EXIT_OK
+
+#
+# In a shared-IP zone we need this service to be up, but all of the work
+# it tries to do is irrelevant (and will actually lead to the service
+# failing if we try to do it), so just bail out.
+# In the global zone and exclusive-IP zones we proceed.
+#
+smf_configure_ip || exit $SMF_EXIT_OK
+
+case "$1" in
+
+'start')
+ #
+ # We need to create the default (NoNet and Automatic)
+ # locations, if they don't already exist. So: first check
+ # for the existence of each, and then run the appropriate
+ # nwamcfg script(s) as needed. Restart nwamd if a location is
+ # created, as it needs to read it in.
+ #
+ LOC_CREATED="false"
+ $NWAMCFG list loc Automatic >/dev/null 2>&1
+ if [ $? -eq 1 ]; then
+ $NWAMCFG -f /etc/nwam/loc/create_loc_auto
+ LOC_CREATED="true"
+ fi
+
+ $NWAMCFG list loc NoNet >/dev/null 2>&1
+ if [ $? -eq 1 ]; then
+ NONETPATH=/etc/nwam/loc/NoNet
+ NONETFILES="ipf.conf ipf6.conf"
+ for file in $NONETFILES; do
+ copy_default $NONETPATH $file
+ done
+ $NWAMCFG -f /etc/nwam/loc/create_loc_nonet
+ LOC_CREATED="true"
+ fi
+
+ if [ "$LOC_CREATED" = "true" ]; then
+ refresh_svc $NWAM_FMRI
+ fi
+
+ # location selection/activation happens below
+ ;;
+
+'refresh')
+
+ # location selection/activation happens below
+ ;;
+
+*)
+ echo "Usage: $0 start|refresh"
+ exit 1
+ ;;
+
+esac
+
+#
+# If the Legacy location doesn't exist and the file to create the Legacy
+# location exists, create the Legacy location. Make a copy of it as the user's
+# intentions before upgrade. Then activate the User location if nis is
+# involved. Because NIS affects more parts of the system (e.g. automounts) we
+# are not willing to make NIS part of the Automatic location (i.e. enable it
+# automatically based on external input) as we do with DHCP-driven DNS.
+#
+activate_user_loc=0
+$NWAMCFG list loc Legacy >/dev/null 2>&1
+if [ $? -eq 1 -a -f "$SCRIPT_PATH/create_loc_legacy" ]; then
+ #
+ # We built the script in and pointing to /etc/svc/volatile because we
+ # may not have a writable filesystem in net-nwam. So here we move the
+ # components and rewrite the script to point at the writable filesystem.
+ #
+ $CP -r $SCRIPT_PATH/Legacy $LEGACY_LOC_PATH
+ $MV $SCRIPT_PATH/create_loc_legacy $SCRIPT_PATH/vcreate_loc_legacy
+ $SED -e's,/etc/svc/volatile/nwam/Legacy,/etc/nwam/loc/Legacy,' \
+ $SCRIPT_PATH/vcreate_loc_legacy >$SCRIPT_PATH/create_loc_legacy
+ $NWAMCFG -f $SCRIPT_PATH/create_loc_legacy
+ loc_ver=`$SVCPROP -c -p location_upgrade/version $LOCATION_FMRI \
+ 2>/dev/null`
+ if [ $? -eq 1 ]; then
+ #
+ # We are rewriting configuration variables from the Legacy
+ # location to the User location. Use variable ULP to keep REs
+ # within a line.
+ #
+ ULP=$USER_LOC_PATH
+ $SED -e's,Legacy,User,' \
+ -e's,activation-mode=system,activation-mode=manual,' \
+ -e"s,\(ipfilter-config-file=\).*/\(.*\),\1$ULP/\2," \
+ -e"s,\(ipfilter-v6-config-file=\).*/\(.*\),\1$ULP/\2," \
+ -e"s,\(ipnat-config-file=\).*/\(.*\),\1$ULP/\2," \
+ -e"s,\(ippool-config-file=\).*/\(.*\),\1$ULP/\2," \
+ -e"s,\(ike-config-file=\).*/\(.*\),\1$ULP/\2," \
+ -e"s,\(ipsecpolicy-config-file=\).*/\(.*\),\1$ULP/\2," \
+ $SCRIPT_PATH/create_loc_legacy | \
+ $SED -e's,/etc/nwam/loc/User/none,/none,' \
+ -e's,/etc/nwam/loc/User/allow,/allow,' \
+ -e's,/etc/nwam/loc/User/deny,/deny,' \
+ >$SCRIPT_PATH/create_loc_user
+ #
+ # We are creating the User location here. The User location
+ # is an appromixation of the machine configuration when the
+ # user change or upgraded to this version of NWAM. First
+ # we make sure there isn't an existing User location or any
+ # existing User location data. We then copy all the data
+ # from the Legacy location and create a location pointing at
+ # that data. Lastly we create a version property to note
+ # that we have done this.
+ #
+ $NWAMCFG destroy loc User 2>/dev/null
+ $RM -rf $USER_LOC_PATH
+ $CP -r $LEGACY_LOC_PATH $USER_LOC_PATH
+ $RM -f $USER_LOC_PATH/resolv.conf
+ $NWAMCFG -f $SCRIPT_PATH/create_loc_user
+ # The User location is activated if 'nis' is in a non comment
+ # line of nsswitch.conf.
+ $GREP -v "^#" $USER_LOC_PATH/nsswitch.conf |\
+ $SED -e 's/[^:]*://' | $GREP nis >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ activate_user_loc=1
+ fi
+ $SVCCFG -s $SMF_FMRI addpg location_upgrade application \
+ 2>/dev/null
+ $SVCCFG -s $SMF_FMRI setprop location_upgrade/version = \
+ astring: "1"
+ fi
+fi
+
+#
+# Activate a location. If we've just finished upgrading, and
+# the User location should be activated, do that (and use nwamadm
+# to do so, so the enabled property gets set and nwamd knows this
+# selection has been made). Otherwise, if our location/selected
+# property has a value, we activate that location; else we activate
+# the NoNet location as a default value.
+#
+if [ $activate_user_loc -eq 1 ]; then
+ $NWAMADM enable -p loc User
+else
+ sel_loc=`$SVCPROP -c -p location/selected $SMF_FMRI 2>/dev/null`
+ if [ $? -eq 1 ]; then
+ # location hasn't been selected; default to NoNet
+ activate_loc NoNet
+ else
+ # activate selected location
+ activate_loc $sel_loc
+ fi
+fi
+
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/svc/milestone/net-netmask b/usr/src/cmd/svc/milestone/net-netmask
new file mode 100644
index 0000000000..383ecce911
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/net-netmask
@@ -0,0 +1,76 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Reset netmask and broadcast address whenever new information is
+# availabe from NIS.
+#
+
+. /lib/svc/share/smf_include.sh
+
+#
+# In a shared-IP zone we need this service to be up, but all of the
+# work it tries to do is irrelevant (and will actually lead to the
+# service failing if we try to do it), so just bail out.
+# In the global zone and exclusive-IP zones we proceed.
+#
+smf_configure_ip || exit $SMF_EXIT_OK
+
+#
+# wait_nis
+# Wait up to 5 seconds for ypbind to obtain a binding.
+#
+wait_nis ()
+{
+ for i in 1 2 3 4 5; do
+ server=`/usr/bin/ypwhich 2>/dev/null`
+ [ $? -eq 0 -a -n "$server" ] && return 0 || sleep 1
+ done
+ return 1
+}
+
+#
+# Reset the netmask and broadcast address for our network interfaces.
+# Since this may result in a name service lookup, we want to now wait
+# for NIS to come up if we previously started it.
+#
+domain=`/usr/bin/domainname 2>/dev/null`
+
+[ -z "$domain" ] || [ ! -d /var/yp/binding/$domain ] || wait_nis || \
+ echo "WARNING: Timed out waiting for NIS to come up" >& 2
+
+#
+# Re-set the netmask and broadcast addr for all IP interfaces. This
+# ifconfig is run here, after waiting for name services, so that
+# "netmask +" will find the netmask if it lives in a NIS map. The 'D'
+# in -auD tells ifconfig NOT to mess with the interface if it is
+# under DHCP control
+#
+/usr/sbin/ifconfig -auD4 netmask + broadcast +
+
+# Uncomment these lines to print complete network interface configuration
+# echo "network interface configuration:"
+# /usr/sbin/ifconfig -a
diff --git a/usr/src/cmd/svc/milestone/net-nwam b/usr/src/cmd/svc/milestone/net-nwam
index 2f906d6fd0..91e46f59bb 100644
--- a/usr/src/cmd/svc/milestone/net-nwam
+++ b/usr/src/cmd/svc/milestone/net-nwam
@@ -27,20 +27,486 @@
. /lib/svc/share/smf_include.sh
. /lib/svc/share/net_include.sh
+# FMRI constants
+IPSEC_IKE_FMRI="svc:/network/ipsec/ike"
+IPSEC_POLICY_FMRI="svc:/network/ipsec/policy"
+IPFILTER_FMRI="svc:/network/ipfilter:default"
+NIS_CLIENT_FMRI="svc:/network/nis/client:default"
+NET_PHYS_FMRI="svc:/network/physical:default"
+NET_NWAM_FMRI="svc:/network/physical:nwam"
+NET_LOC_FMRI="svc:/network/location:default"
+
+#
+# Default *.conf files
+# Set appropriate config SMF property to these files when NWAM is stopped
+# and corresponding config properties in the Legacy location are emtpy
+#
+IPF6_DEFAULT_CONFIG_FILE=/etc/ipf/ipf6.conf
+IPNAT_DEFAULT_CONFIG_FILE=/etc/ipf/ipnat.conf
+IPPOOL_DEFAULT_CONFIG_FILE=/etc/ipf/ippool.conf
+IPSEC_IKE_DEFAULT_CONFIG_FILE=/etc/inet/ike/config
+IPSEC_POLICY_DEFAULT_CONFIG_FILE=/etc/inet/ipsecinit.conf
+
+# commands
+BASENAME=/usr/bin/basename
+CAT=/usr/bin/cat
+CP=/usr/bin/cp
+DOMAINNAME=/usr/bin/domainname
+GREP=/usr/bin/grep
+LDAPCLIENT=/usr/sbin/ldapclient
+MKDIR=/usr/bin/mkdir
+MKFIFO=/usr/bin/mkfifo
+NAWK=/usr/bin/nawk
+NWAMCFG=/usr/sbin/nwamcfg
+RM=/usr/bin/rm
+SVCADM=/usr/sbin/svcadm
+SVCCFG=/usr/sbin/svccfg
+SVCPROP=/usr/bin/svcprop
+
+# Path to directories
+# We don't have a writable file system so we write to /etc/svc/volatile and
+# then later copy anything interesting to /etc/nwam.
+LEGACY_PATH=/etc/svc/volatile/nwam/Legacy
+NIS_BIND_PATH=/var/yp/binding
+
+#
+# copy_to_legacy_loc <file>
+#
+# Copies the file to the Legacy location directory
+#
+copy_to_legacy_loc() {
+ $MKDIR -p $LEGACY_PATH
+ if [ -f "$1" ]; then
+ $CP -p $1 $LEGACY_PATH
+ fi
+}
+
+#
+# copy_from_legacy_loc <destination file>
+#
+# Copies file with the same name from Legacy location to the given
+# destination file
+#
+copy_from_legacy_loc () {
+ DEST_DIR=`/usr/bin/dirname $1`
+ SRC_FILE="$LEGACY_PATH/`$BASENAME $1`"
+
+ # Make destination directory if needed
+ if [ ! -d "$DEST_DIR" ]; then
+ $MKDIR -p $DEST_DIR
+ fi
+
+ if [ -f "$SRC_FILE" ]; then
+ $CP -p $SRC_FILE $DEST_DIR
+ fi
+}
+
+#
+# write_loc_prop <property> <value> <file>
+#
+# Appends to <file> a nwamcfg command to set <property> to <value> if non-empty
+#
+write_loc_prop () {
+ prop=$1
+ val=$2
+ file=$3
+
+ if [ -n "$val" -a -n "$file" ]; then
+ echo "set $prop=$val" >> $file
+ fi
+}
+
+#
+# set_smf_prop <fmri> <property name> <property value>
+#
+set_smf_prop () {
+ $SVCCFG -s $1 setprop $2 = astring: "$3" && return
+}
+
+#
+# get_smf_prop <fmri> <property name>
+#
+get_smf_prop () {
+ $SVCPROP -p $2 $1
+}
+
#
-# In a shared-IP zone we need this service to be up, but all of the work
-# it tries to do is irrelevant (and will actually lead to the service
-# failing if we try to do it), so just bail out.
-# In the global zone and exclusive-IP zones we proceed.
+# Creates Legacy location from the current configuration
#
-smf_configure_ip || exit $SMF_EXIT_OK
+create_legacy_loc () {
+ CREATE_LOC_LEGACY_FILE=/etc/svc/volatile/nwam/create_loc_legacy
+
+ #
+ # Write nwamcfg commands to create Legacy location to
+ # $CREATE_LOC_LEGACY_FILE as values for properties are determined
+ # Note that some of the *_CONFIG_FILE variables point at copies of
+ # files we've made and others indicate where those copies should be
+ # if we are enabling the location.
+ #
+ echo "create loc Legacy" > $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "activation-mode" "system" $CREATE_LOC_LEGACY_FILE
+
+ NAMESERVICES=""
+ NAMESERVICES_CONFIG_FILE=""
+ DNS_NAMESERVICE_CONFIGSRC=""
+ DNS_NAMESERVICE_DOMAIN=""
+ DNS_NAMESERVICE_SERVERS=""
+ DNS_NAMESERVICE_SEARCH=""
+ NIS_NAMESERVICE_CONFIGSRC=""
+ NIS_NAMESERVICE_SERVERS=""
+ LDAP_NAMESERVICE_CONFIGSRC=""
+ LDAP_NAMESERVICE_SERVERS=""
+ DEFAULT_DOMAIN=""
+
+ # Copy /etc/nsswitch.conf file
+ copy_to_legacy_loc /etc/nsswitch.conf
+ NAMESERVICES_CONFIG_FILE="$LEGACY_PATH/nsswitch.conf"
+
+ # Gather DNS info from resolv.conf if present.
+ if [ -f /etc/resolv.conf ]; then
+ NAMESERVICES="dns,"
+ $GREP -i "added by dhcp" /etc/nsswitch.conf >/dev/null
+ if [ $? -eq 0 ]; then
+ DNS_NAMESERVICE_CONFIGSRC="dhcp"
+ else
+ DNS_NAMESERVICE_CONFIGSRC="manual"
+ DNS_NAMESERVICE_DOMAIN=`$NAWK '$1 == "domain" {\
+ print $2 }' < /etc/resolv.conf`
+ DNS_NAMESERVICE_SERVERS=`$NAWK '$1 == "nameserver" \
+ { printf "%s,", $2 }' < /etc/resolv.conf`
+ DNS_NAMESERVICE_SEARCH=`$NAWK '$1 == "search" \
+ { printf "%s,", $2 }' < /etc/resolv.conf`
+ copy_to_legacy_loc /etc/resolv.conf
+ fi
+ fi
+
+ # Gather NIS info from appropriate file if present.
+ if service_is_enabled $NIS_CLIENT_FMRI; then
+ NAMESERVICES="${NAMESERVICES}nis,"
+ NIS_NAMESERVICE_CONFIGSRC="manual"
+ DEFAULT_DOMAIN=`$CAT /etc/defaultdomain`
+
+ yp_servers=`$NAWK '{ printf "%s ", $1 }' \
+ < $NIS_BIND_PATH/$DEFAULT_DOMAIN/ypservers`
+ for serv in $yp_servers; do
+ if is_valid_addr $serv; then
+ addr="$serv,"
+ else
+ addr=`$GREP -iw $serv /etc/inet/hosts | \
+ $NAWK '{ printf "%s,", $1 }'`
+ fi
+ NIS_NAMESERVICE_SERVERS="${NIS_NAMESERVICE_SERVERS}$addr"
+ done
+ fi
+
+ # Gather LDAP info via ldapclient(1M).
+ if [ -f /var/ldap/ldap_client_file ]; then
+ copy_to_legacy /var/ldap/ldap_client_file
+ NAMESERVICES="${NAMESERVICES}ldap,"
+ LDAP_NAMESERVICE_CONFIGSRC="manual"
+ LDAP_NAMESERVICE_SERVERS=`$LDAPCLIENT list 2>/dev/null | \
+ $NAWK '$1 == "preferredServerList:" { print $2 }'`
+ DEFAULT_DOMAIN=`$CAT /etc/defaultdomain`
+ fi
+
+ # Now, write nwamcfg commands for nameservices
+ write_loc_prop "nameservices" $NAMESERVICES $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "nameservices-config-file" $NAMESERVICES_CONFIG_FILE \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "dns-nameservice-configsrc" $DNS_NAMESERVICE_CONFIGSRC \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "dns-nameservice-domain" $DNS_NAMESERVICE_DOMAIN \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "dns-nameservice-servers" $DNS_NAMESERVICE_SERVERS \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "dns-nameservice-search" $DNS_NAMESERVICE_SEARCH \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "nis-nameservice-configsrc" $NIS_NAMESERVICE_CONFIGSRC \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "nis-nameservice-servers" $NIS_NAMESERVICE_SERVERS \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "ldap-nameservice-configsrc" $LDAP_NAMESERVICE_CONFIGSRC\
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "ldap-nameservice-servers" $LDAP_NAMESERVICE_SERVERS \
+ $CREATE_LOC_LEGACY_FILE
+ write_loc_prop "default-domain" $DEFAULT_DOMAIN $CREATE_LOC_LEGACY_FILE
+
+ # Retrieve NFSv4 domain.
+ if [ -f /etc/default/nfs ]; then
+ copy_to_legacy_loc /etc/default/nfs
+ NFS_DOMAIN=`$NAWK '/^NFSMAPID_DOMAIN.*/ { FS="=" ; print $2 }' \
+ < /etc/default/nfs`
+ write_loc_prop "nfsv4-domain" \
+ $NFS_DOMAIN $CREATE_LOC_LEGACY_FILE
+ fi
+
+ IPF_CONFIG_FILE=""
+ IPF6_CONFIG_FILE=""
+ IPNAT_CONFIG_FILE=""
+ IPPOOL_CONFIG_FILE=""
+ IKE_CONFIG_FILE=""
+ IPSEC_POLICY_CONFIG_FILE=""
+
+ #
+ # IPFilter
+ #
+ # If the firewall policy is "custom", simply copy the
+ # custom_policy_file. If the firewall policy is "none", "allow" or
+ # "deny", save the value as "/<value>". When reverting back to the
+ # Legacy location, these values will have to be treated as special.
+ #
+ # For all configuration files, copy them to the Legacy directory.
+ # Use the respective properties to remember the original locations
+ # of the files so that they can be copied back there when NWAM is
+ # stopped.
+ #
+ if service_is_enabled $IPFILTER_FMRI; then
+ FIREWALL_POLICY=`get_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy`
+ if [ "$FIREWALL_POLICY" = "custom" ]; then
+ IPF_CONFIG_FILE=`get_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/custom_policy_file`
+ copy_to_legacy_loc $IPF_CONFIG_FILE
+ else
+ # save value as /none, /allow, or /deny
+ IPF_CONFIG_FILE="/$FIREWALL_POLICY"
+ fi
+ IPF6_CONFIG_FILE=`get_smf_prop $IPFILTER_FMRI \
+ config/ipf6_config_file`
+ copy_to_legacy_loc $IPF6_CONFIG_FILE
+
+ IPNAT_CONFIG_FILE=`get_smf_prop $IPFILTER_FMRI \
+ config/ipnat_config_file`
+ copy_to_legacy_loc $IPNAT_CONFIG_FILE
+
+ IPPOOL_CONFIG_FILE=`get_smf_prop $IPFILTER_FMRI \
+ config/ippool_config_file`
+ copy_to_legacy_loc $IPPOOL_CONFIG_FILE
+ fi
+
+ # IKE
+ if service_is_enabled $IPSEC_IKE_FMRI:default; then
+ IKE_CONFIG_FILE=`get_smf_prop $IPSEC_IKE_FMRI config/config_file`
+ copy_to_legacy_loc $IKE_CONFIG_FILE
+ fi
+
+ # IPsec
+ if service_is_enabled $IPSEC_POLICY_FMRI:default; then
+ IPSEC_POLICY_CONFIG_FILE=`get_smf_prop $IPSEC_POLICY_FMRI \
+ config/config_file`
+ copy_to_legacy_loc $IPSEC_POLICY_CONFIG_FILE
+ fi
+
+ if [ -n "$IPF_CONFIG_FILE" -a \( "$IPF_CONFIG_FILE" = "/allow" \
+ -o "$IPF_CONFIG_FILE" = "/deny" -o "$IPF_CONFIG_FILE" = "/none" \
+ -o -f "$IPF_CONFIG_FILE" \) ]; then
+ write_loc_prop "ipfilter-config-file" $IPF_CONFIG_FILE \
+ $CREATE_LOC_LEGACY_FILE
+ fi
+ if [ -n "$IPF6_CONFIG_FILE" -a -f "$IPF6_CONFIG_FILE" ]; then
+ write_loc_prop "ipfilter-v6-config-file" $IPF6_CONFIG_FILE \
+ $CREATE_LOC_LEGACY_FILE
+ fi
+ if [ -n "$IPNAT_CONFIG_FILE" -a -f "$IPNAT_CONFIG_FILE" ]; then
+ write_loc_prop "ipnat-config-file" $IPNAT_CONFIG_FILE \
+ $CREATE_LOC_LEGACY_FILE
+ fi
+ if [ -n "$IPPOOL_CONFIG_FILE" -a -f "$IPPOOL_CONFIG_FILE" ]; then
+ write_loc_prop "ippool-config-file" $IPPOOL_CONFIG_FILE \
+ $CREATE_LOC_LEGACY_FILE
+ fi
+ if [ -n "$IKE_CONFIG_FILE" -a -f "$IKE_CONFIG_FILE" ]; then
+ write_loc_prop "ike-config-file" $IKE_CONFIG_FILE \
+ $CREATE_LOC_LEGACY_FILE
+ fi
+ if [ -n "$IPSEC_POLICY_CONFIG_FILE" -a -f "$IPSEC_POLICY_CONFIG_FILE" ]
+ then
+ write_loc_prop "ipsecpolicy-config-file" \
+ $IPSEC_POLICY_CONFIG_FILE $CREATE_LOC_LEGACY_FILE
+ fi
+
+ # End
+ echo "end" >> $CREATE_LOC_LEGACY_FILE
+ # network/location will create the Legacy location with these commands.
+}
+
+#
+# Undoes the effects of the Legacy location creation
+#
+revert_to_legacy_loc () {
+ $SVCADM disable dns/client
+ $SVCADM disable nis/client
+ $SVCADM disable ldap/client
+
+ # copy nsswitch.conf to /etc/nsswitch.conf
+ copy_from_legacy_loc /etc/nsswitch.conf
+
+ # DNS - copy resolv.conf to /etc/resolv.conf
+ if [ -f "$LEGACY_PATH/resolv.conf" ]; then
+ copy_from_legacy_loc /etc/resolv.conf
+ $SVCADM enable dns/client
+ fi
+
+ # set /etc/defaultdomain and domainname(1M)
+ DEFAULT_DOMAIN=`nwam_get_loc_prop Legacy default-domain`
+ if [ -n "$DEFAULT_DOMAIN" ]; then
+ $DOMAINNAME $DEFAULT_DOMAIN
+ $DOMAINNAME > /etc/defaultdomain
+ fi
+
+ # NIS - directory and ypserver in /var/yp/binding/
+ NIS_CONFIGSRC=`nwam_get_loc_prop Legacy nis-nameservice-configsrc`
+ NIS_SERVERS=`nwam_get_loc_prop Legacy nis-nameservice-servers`
+ if [ -n "$NIS_CONFIGSRC" ]; then
+ if [ ! -d "$NIS_BIND_PATH/$DEFAULT_DOMAIN" ]; then
+ $MKDIR -p $NIS_BIND_PATH/$DEFAULT_DOMAIN
+ fi
+ if [ -n "$NIS_SERVERS" ]; then
+ echo "$NIS_SERVERS" | $NAWK \
+ 'FS="," { for (i = 1; i <= NF; i++) print $i }' \
+ > $NIS_BIND_PATH/$DEFAULT_DOMAIN/ypservers
+ fi
+ $SVCADM enable nis/client
+ fi
+
+ # LDAP - copy ldap_client_file to /var/ldap/ldap_client_file
+ if [ -f "$LEGACY_PATH/ldap_client_file" ]; then
+ copy_from_legacy_loc /var/ldap/ldap_client_file
+ $SVCADM enable ldap/client
+ fi
+
+ # Copy back nfs file
+ copy_from_legacy_loc /etc/default/nfs
+
+ # IPFilter, IPsec, and IKE
+ ipf_file=`nwam_get_loc_prop Legacy ipfilter-config-file`
+ ipf6_file=`nwam_get_loc_prop Legacy ipfilter-v6-config-file`
+ ipnat_file=`nwam_get_loc_prop Legacy ipnat-config-file`
+ ippool_file=`nwam_get_loc_prop Legacy ippool-config-file`
+ ike_file=`nwam_get_loc_prop Legacy ike-config-file`
+ pol_file=`nwam_get_loc_prop Legacy ipsecpolicy-config-file`
+
+ if [ -n "$ike_file" ]; then
+ copy_from_legacy_loc $ike_file
+ set_smf_prop $IPSEC_IKE_FMRI config/config_file $ike_file
+ $SVCADM refresh $IPSEC_IKE_FMRI
+ $SVCADM enable $IPSEC_IKE_FMRI
+ else
+ set_smf_prop $IPSEC_IKE_FMRI config/config_file \
+ $IPSEC_IKE_DEFAULT_CONFIG_FILE
+ $SVCADM disable $IPSEC_IKE_FMRI
+ fi
+ if [ -n "$pol_file" ]; then
+ copy_from_legacy_loc $pol_file
+ set_smf_prop $IPSEC_POLICY_FMRI config/config_file $pol_file
+ $SVCADM refresh $IPSEC_POLICY_FMRI
+ $SVCADM enable $IPSEC_POLICY_FMRI
+ else
+ set_smf_prop $IPSEC_POLICY_FMRI config/config_file \
+ $IPSEC_POLICY_DEFAULT_CONFIG_FILE
+ $SVCADM disable $IPSEC_POLICY_FMRI
+ fi
+
+ refresh_ipf=false
+ if [ -n "$ipf_file" ]; then
+ # change /none, /allow, and /deny to firewall policy
+ if [ "$ipf_file" = "/none" -o "$ipf_file" = "/allow" \
+ -o "$ipf_file" = "/deny" ]; then
+ policy=`echo "$ipf_file" | $NAWK 'FS="/" { print $2 }'`
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy $policy
+ # no need to clear custom_policy_file as it isn't "custom"
+ else
+ copy_from_legacy_loc $ipf_file
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/policy "custom"
+ set_smf_prop $IPFILTER_FMRI \
+ firewall_config_default/custom_policy_file $ipf_file
+ fi
+ refresh_ipf=true
+ fi
+ if [ -n "$ipf6_file" ]; then
+ copy_from_legacy_loc $ipf6_file
+ set_smf_prop $IPFILTER_FMRI config/ipf6_config_file $ipf6_file
+ refresh_ipf=true
+ else
+ set_smf_prop $IPFILTER_FMRI config/ipf6_config_file \
+ $IPF6_DEFAULT_CONFIG_FILE
+ fi
+ if [ -n "$ipnat_file" ]; then
+ copy_from_legacy_loc $ipnat_file
+ set_smf_prop $IPFILTER_FMRI config/ipnat_config_file $ipnat_file
+ refresh_ipf=true
+ else
+ set_smf_prop $IPFILTER_FMRI config/ipnat_config_file \
+ $IPNAT_DEFAULT_CONFIG_FILE
+ fi
+ if [ -n "$ippool_file" ]; then
+ copy_from_legacy_loc $ippool_file
+ set_smf_prop $IPFILTER_FMRI config/ippool_config_file \
+ $ippool_file
+ refresh_ipf=true
+ else
+ set_smf_prop $IPFILTER_FMRI config/ippool_config_file \
+ $IPPOOL_DEFAULT_CONFIG_FILE
+ fi
+
+ $SVCADM refresh $IPFILTER_FMRI
+ if [ "$refresh_ipf" = "true" ]; then
+ $SVCADM enable $IPFILTER_FMRI
+ else
+ $SVCADM disable $IPFILTER_FMRI
+ fi
+
+ # Remove the Legacy directory and location
+ $RM -rf $LEGACY_PATH
+ $NWAMCFG destroy loc Legacy
+}
+
+#
+# Script entry point
+#
+# Arguments to net-nwam are
+# method ( start | refresh | stop | -u | -c )
+#
+
+#
+# Create nwam directory in /etc/svc/volatile
+#
+if [ ! -d /etc/svc/volatile/nwam ]; then
+ $MKDIR -m 0755 /etc/svc/volatile/nwam
+fi
case "$1" in
'refresh')
/usr/bin/pkill -HUP -z `smf_zonename` nwamd
+ #
+ # Enable network/location. Needed on first boot post-install as
+ # network/location will not exist until after manifest-import runs.
+ #
+ if service_exists $NET_LOC_FMRI ; then
+ $SVCADM enable -t $NET_LOC_FMRI
+ fi
;;
'start')
+ # The real daemon is not started in a shared stack zone. But we need to
+ # create a dummy background process to preserve contract lifetime.
+ smf_configure_ip
+ if [ $? = "1" ] ; then
+ $RM -f /etc/svc/volatile/nwam/nwam_blocked
+ $MKFIFO /etc/svc/volatile/nwam/nwam_blocked
+ ($CAT </etc/svc/volatile/nwam/nwam_blocked >/dev/null) &
+ exit $SMF_EXIT_OK
+ fi
+
+ #
+ # Enable network/location.
+ #
+ if service_exists $NET_LOC_FMRI ; then
+ $SVCADM enable -t $NET_LOC_FMRI
+ fi
+
if smf_is_globalzone; then
net_reconfigure || exit $SMF_EXIT_ERR_CONFIG
@@ -64,11 +530,37 @@ case "$1" in
# Initialize security objects.
/sbin/dladm init-secobj
- # Bring up VNICs, VLANs and flows
+ #
+ # Initialize VNICs, VLANs and flows. Though they are brought
+ # up here, NWAM will not automatically manage VNICs and VLANs.
+ #
/sbin/dladm up-vnic
/sbin/dladm up-vlan
+ /sbin/dladm up-aggr
/sbin/flowadm init-flow
fi
+
+ #
+ # Ensure that the network/netcfg service is running since
+ # manifest-import has not yet run for the first boot after upgrade.
+ # We wouldn't need to do that if manifest-import ran earlier in
+ # boot, since there is an explicit dependency between
+ # network/netcfg and network/physical:nwam. This is similar to
+ # what network/physical does with network/datalink-management in
+ # net_reconfigure().
+ #
+ $SVCADM enable -ts svc:/network/netcfg:default
+
+ #
+ # We also need to create the Legacy location, which is used
+ # to restore non-NWAM settings that are overwritten when
+ # NWAM is enabled (e.g. resolv.conf, nsswitch.conf, etc.).
+ #
+ $NWAMCFG list loc Legacy >/dev/null 2>&1
+ if [ $? -eq 1 ]; then
+ create_legacy_loc
+ fi
+
# start nwamd in foreground; it will daemonize itself
if /lib/inet/nwamd ; then
exit $SMF_EXIT_OK
@@ -78,7 +570,26 @@ case "$1" in
;;
'stop')
+ # We need to make the dummy process we created above stop.
+ smf_configure_ip
+ if [ $? = "1" ] ; then
+ echo "stop" > /etc/svc/volatile/nwam/nwam_blocked
+ exit $SMF_EXIT_OK
+ fi
+
/usr/bin/pkill -z `smf_zonename` nwamd
+
+ #
+ # Restore the non-NWAM settings.
+ #
+ $NWAMCFG list loc Legacy >/dev/null 2>&1
+ if [ $? -eq 1 ]; then
+ echo "No Legacy location to revert to!"
+ exit $SMF_EXIT_OK
+ fi
+ revert_to_legacy_loc
+ # remove the location property group
+ $SVCCFG -s $NET_LOC_FMRI delpg location
;;
'-u')
@@ -87,27 +598,22 @@ case "$1" in
# network/physical:nwam will be disabled.
# There are various other parts of the system (nscd, nfs) that
# depend on continuing to have a working network. For this
- # reason we don't change the network configuration immediately.
-
- SVCADM=/usr/sbin/svcadm
- SVCCFG=/usr/sbin/svccfg
- net_phys=svc:/network/physical:default
- net_nwam=svc:/network/physical:nwam
-
+ # reason we don't change the network configuration immediately.
+ #
# Disable network/physical temporarily and make sure that will
# be enabled on reboot.
- $SVCADM disable -st $net_phys
- $SVCCFG -s $net_phys setprop general/enabled=true
+ $SVCADM disable -st $NET_PHYS_FMRI
+ $SVCCFG -s $NET_PHYS_FMRI setprop general/enabled=true
# If nwam is online then make sure that it's temporarily enabled.
- nwam_online=`/usr/bin/svcprop -t -p restarter/state $net_nwam`
+ nwam_online=`$SVCPROP -t -p restarter/state $NET_NWAM_FMRI`
if [ $? -eq 0 ]; then
set -- $nwam_online
- [ $3 = "online" ] && $SVCADM enable -st $net_nwam
+ [ $3 = "online" ] && $SVCADM enable -st $NET_NWAM_FMRI
fi
# Set nwam so that it won't be enabled upon reboot.
- $SVCCFG -s $net_nwam setprop general/enabled=false
+ $SVCCFG -s $NET_NWAM_FMRI setprop general/enabled=false
exit 0
;;
diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical
index 2e512093c3..63e4264204 100644
--- a/usr/src/cmd/svc/milestone/net-physical
+++ b/usr/src/cmd/svc/milestone/net-physical
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
diff --git a/usr/src/cmd/svc/milestone/net-svc b/usr/src/cmd/svc/milestone/net-svc
index bb019925db..0ede5cfb3f 100644
--- a/usr/src/cmd/svc/milestone/net-svc
+++ b/usr/src/cmd/svc/milestone/net-svc
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -31,6 +31,9 @@
#
. /lib/svc/share/smf_include.sh
+. /lib/svc/share/net_include.sh
+
+NWAM_FMRI="svc:/network/physical:nwam"
case "$1" in
'start')
@@ -40,11 +43,17 @@ case "$1" in
# service failing if we try to do it), so just bail out.
# In the global zone and exclusive-IP zones we proceed.
#
- smf_configure_ip || exit 0
- ;; # Fall through -- rest of script is the initialization code
+ smf_configure_ip || exit $SMF_EXIT_OK
+
+ #
+ # If nwam is enabled, the nwam service will handle the tasks performed
+ # by this service, so just bail out.
+ #
+ service_is_enabled $NWAM_FMRI && exit $SMF_EXIT_OK
+ ;; # fall through -- rest of script is the initialization code
'stop')
- exit 0
+ exit $SMF_EXIT_OK
;;
*)
@@ -53,69 +62,12 @@ case "$1" in
;;
esac
-NWAM_FMRI="svc:/network/physical:nwam"
-NETSVC_FMRI="svc:/network/service:default"
-
interface=$2
# If boot variables are not set, set variables we use
[ -z "$_INIT_UTS_NODENAME" ] && _INIT_UTS_NODENAME=`/usr/bin/uname -n`
#
-# This function removes the instance portion of the passed-in FMRI; for
-# example, 'svc:/network/service:default' becomes 'svc:/network/service'.
-#
-remove_fmri_inst () {
- echo $1 | awk -F: ' { printf "%s:%s", $1, $2 } '
-}
-
-#
-# This function returns true if this script was *not* invoked
-# by an instance of svc:/network/service.
-#
-fmri_is_not_netsvc () {
- FMRI_1=`remove_fmri_inst $SMF_FMRI`
- FMRI_2=`remove_fmri_inst $NETSVC_FMRI`
- [ "$FMRI_1" = "$FMRI_2" ] && return 1
- return 0
-}
-
-#
-# This function returns true if this script was *not* invoked
-# by the nwam instance of the network/physical service.
-#
-fmri_is_not_nwam () {
- [ "&SMF_FMRI" = "$NWAM_FMRI" ] && return 1
- return 0
-}
-
-#
-# This function returns true if the nwam service is not running, false
-# if it is. "running" is defined as "current state is online or next
-# state is online".
-#
-nwam_is_not_running() {
- state=`/usr/bin/svcprop -p restarter/state $NWAM_FMRI`
- nstate=`/usr/bin/svcprop -p restarter/next_state $NWAM_FMRI`
-
- [ "$state" = "online" -o "$nextstate" = "online" ] && return 1
- return 0
-}
-
-#
-# wait_nis
-# Wait up to 5 seconds for ypbind to obtain a binding.
-#
-wait_nis ()
-{
- for i in 1 2 3 4 5; do
- server=`/usr/bin/ypwhich 2>/dev/null`
- [ $? -eq 0 -a -n "$server" ] && return 0 || sleep 1
- done
- return 1
-}
-
-#
# This function takes two file names and the file mode as input. The two
# files are compared for differences (using cmp(1)) and if different, the
# second file is over written with the first. A chmod is done with the file
@@ -208,7 +160,7 @@ update_resolv ()
}
#
-# update_nss
+# update_nss()
# This routine takes as a parameter, the name of the respective policy
# to change in the nsswitch.conf (hosts or ipnodes) to update with dns.
#
@@ -277,70 +229,37 @@ cleanup_hosts ()
}
#
-# We now need to reset the netmask and broadcast address for our network
-# interfaces. Since this may result in a name service lookup, we want to
-# now wait for NIS to come up if we previously started it.
-#
-# Only do this in the non-nwam case.
-#
-if fmri_is_not_nwam; then
- domain=`/usr/bin/domainname 2>/dev/null`
-
- [ -z "$domain" ] || [ ! -d /var/yp/binding/$domain ] || wait_nis || \
- echo "WARNING: Timed out waiting for NIS to come up" >& 2
-
- #
- # Re-set the netmask and broadcast addr for all IP interfaces. This
- # ifconfig is run here, after waiting for name services, so that
- # "netmask +" will find the netmask if it lives in a NIS map. The 'D'
- # in -auD tells ifconfig NOT to mess with the interface if it is
- # under DHCP control
- #
- /usr/sbin/ifconfig -auD4 netmask + broadcast +
-fi
-
-# Uncomment these lines to print complete network interface configuration
-# echo "network interface configuration:"
-# /usr/sbin/ifconfig -a
-
-#
# If our network configuration strategy is DHCP, check for DNS
# configuration parameters obtained from the DHCP server.
#
-# If NWAM is enabled, it will invoke this script to do this configuration
-# whenever a DHCP lease is obtained; in that case, this configuration
-# should *not* happen when svc:network/service is starting, as it will
-# interfere with the configuration performed by NWAM.
+# Script execution starts here.
#
-if nwam_is_not_running || fmri_is_not_netsvc; then
+smf_netstrategy
- smf_netstrategy
-
- if [ "$_INIT_NET_STRATEGY" = "dhcp" ]; then
- dnsservers=`get_dhcp_var DNSserv`
- dnsdomain=`get_dhcp_var DNSdmain`
- else
- dnsservers=""
- dnsdomain=""
- fi
+if [ "$_INIT_NET_STRATEGY" = "dhcp" ]; then
+ dnsservers=`get_dhcp_var DNSserv`
+ dnsdomain=`get_dhcp_var DNSdmain`
+else
+ dnsservers=""
+ dnsdomain=""
+fi
- if [ -n "$dnsservers" ]; then
- #
- # add settings retrieved from dhcp server to /etc/resolv.conf
- #
- update_resolv "$dnsservers" "$dnsdomain"
+if [ -n "$dnsservers" ]; then
+ #
+ # add settings retrieved from dhcp server to /etc/resolv.conf
+ #
+ update_resolv "$dnsservers" "$dnsdomain"
- #
- # Add dns to the nsswitch file, if it isn't already there.
- #
- update_nss hosts
- update_nss ipnodes
+ #
+ # Add dns to the nsswitch file, if it isn't already there.
+ #
+ update_nss hosts
+ update_nss ipnodes
- elif dhcp_edits /etc/nsswitch.conf; then
- # If we added DNS to the hosts and ipnodes
- # policy in the nsswitch, remove it.
- cleanup_nss
- fi
+elif dhcp_edits /etc/nsswitch.conf; then
+ # If we added DNS to the hosts and ipnodes
+ # policy in the nsswitch, remove it.
+ cleanup_nss
fi
if dhcp_edits /etc/inet/hosts; then
@@ -349,19 +268,3 @@ if dhcp_edits /etc/inet/hosts; then
cleanup_hosts
fi
-#
-# If we were invoked by NWAM, can exit now (skipping the ipqos config)
-#
-if [ -z "$SMF_FMRI" ] || [ "$SMF_FMRI" = "$NWAM_FMRI" ]; then
- exit 0
-fi
-
-#
-# Load the IPQoS configuration.
-# This is backgrounded so that any remote hostname lookups it performs
-# don't unduely delay startup. Any messages go via syslog.
-#
-
-if [ -f /usr/sbin/ipqosconf -a -f /etc/inet/ipqosinit.conf ]; then
- /usr/sbin/ipqosconf -s -a /etc/inet/ipqosinit.conf &
-fi
diff --git a/usr/src/cmd/svc/milestone/network-ipqos.xml b/usr/src/cmd/svc/milestone/network-ipqos.xml
new file mode 100644
index 0000000000..c1fcdb0fcd
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/network-ipqos.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2010 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
+
+ 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
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:network-ipqos'>
+
+<!--
+ network/ipqos service loads the IPQoS configuration.
+-->
+
+<service
+ name='network/ipqos'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <dependency
+ name='init'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/initial' />
+ </dependency>
+
+ <dependency
+ name='filesystem'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/usr' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/net-ipqos'
+ timeout_seconds='600' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='3' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ IPQoS configuration
+ </loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+ Configuration the Quality of Service facility
+ of the Internet Protocol (IP).
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='ipqosconf' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/network-location.xml b/usr/src/cmd/svc/milestone/network-location.xml
new file mode 100644
index 0000000000..aad337f42f
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/network-location.xml
@@ -0,0 +1,246 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2010 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
+
+ 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
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:network-location'>
+
+<!--
+ network/location is used as a medium to refresh and restart SMF
+ services dependent on network/location whenever nwamd(1M) changes
+ locations. nwamd(1M) updates the different configuration files
+ according to the location being (de)activated and refreshes
+ network/location. This causes the services dependent on network/location
+ to restart and read in the updated configuration files.
+
+ The following services are dependent on network/location with a
+ "restart_on refresh" dependency:
+
+ svc:/network/dns/client:default
+ svc:/network/nis/client:default
+ svc:/network/ldap/client:default
+ svc:/system/name-service-cache:default
+ svc:/network/nfs/mapid:default
+
+ The following dependents will be refreshed (rather than restarted) and,
+ thus, have a "restart_on none" dependency:
+
+ svc:/network/ipfilter:default
+ svc:/network/ipsec/ike:default
+ svc:/network/ipsec/policy:default
+
+ The name of the location to be activated is set in the location/selected
+ property by nwamd. If this property group/property does not exist, the
+ NoNet location will be activated as a fallback.
+
+-->
+
+<service
+ name='network/location'
+ type='service'
+ version='1'>
+
+ <instance name='default' enabled='false'>
+
+ <!--
+ nwamd(1M) refreshes network/location when a new location is
+ activated, thus the "restart_on none" dependency.
+ -->
+ <dependency
+ name='network-physical'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/physical' />
+ </dependency>
+
+ <dependency
+ name='location_netcfg'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/netcfg:default' />
+ </dependency>
+
+ <dependency
+ name='filesystem'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/usr' />
+ </dependency>
+
+ <!--
+ This dependency on system/manifest-import is just for the
+ case of the boot after upgrade. We don't want
+ network/location starting up until network/netcfg has been
+ imported by manifest-import and enabled. On top of that,
+ we also want the new manifest for network/ipfilter to be
+ imported before network/location has started.
+ -->
+ <dependency
+ name='manifest-import'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/manifest-import:default' />
+ </dependency>
+
+ <!-- The following services are dependent on network/location. -->
+
+ <dependent
+ name='location_dns-client'
+ grouping='optional_all'
+ restart_on='refresh'>
+ <service_fmri value='svc:/network/dns/client' />
+ </dependent>
+
+ <dependent
+ name='location_nis-client'
+ grouping='optional_all'
+ restart_on='refresh'>
+ <service_fmri value='svc:/network/nis/client' />
+ </dependent>
+
+ <dependent
+ name='location_ldap-client'
+ grouping='optional_all'
+ restart_on='refresh'>
+ <service_fmri value='svc:/network/ldap/client' />
+ </dependent>
+
+ <dependent
+ name='location_name-service-cache'
+ grouping='optional_all'
+ restart_on='refresh'>
+ <service_fmri value='svc:/system/name-service-cache' />
+ </dependent>
+
+ <dependent
+ name='location_nfs-mapid'
+ grouping='optional_all'
+ restart_on='refresh'>
+ <service_fmri value='svc:/network/nfs/mapid' />
+ </dependent>
+
+ <dependent
+ name='location_identity-domain'
+ grouping='optional_all'
+ restart_on='refresh'>
+ <service_fmri value='svc:/system/identity:domain' />
+ </dependent>
+
+ <!--
+ The following three dependents will be refreshed by
+ /lib/svc/method/net-loc, rather than restarted,
+ when network/location is refreshed.
+ -->
+
+ <dependent
+ name='location_ipfilter'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/ipfilter' />
+ </dependent>
+
+ <dependent
+ name='location_ipsec-ike'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/ipsec/ike' />
+ </dependent>
+
+ <dependent
+ name='location_ipsec-policy'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/ipsec/policy' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/net-loc start'
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential user='netadm' group='netadm'
+ supp_groups='netadm' privileges='zone' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='refresh'
+ exec='/lib/svc/method/net-loc refresh'
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential user='netadm' group='netadm'
+ supp_groups='netadm' privileges='zone' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential user='netadm' group='netadm'
+ supp_groups='netadm' privileges='zone' />
+ </method_context>
+ </exec_method>
+
+ <property_group name='general' type='framework'>
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.location' />
+ </property_group>
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ network interface configuration
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='nwamd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+ </instance>
+
+ <stability value='Unstable' />
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/network-netcfg.xml b/usr/src/cmd/svc/milestone/network-netcfg.xml
new file mode 100644
index 0000000000..edc15da18f
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/network-netcfg.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2010 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
+
+ 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
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:network-netcfg'>
+
+<service
+ name='network/netcfg'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance />
+
+ <!--
+ NWAM requires netcfgd to retrieve config data, as does the
+ location service.
+ -->
+ <dependent
+ name='nwam_netcfg'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/physical:nwam' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/inet/netcfgd'
+ timeout_seconds='600' >
+ <method_context>
+ <method_credential user='netcfg' group='netadm'
+ privileges='zone'/>
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='3' >
+ <method_context>
+ <method_credential user='netcfg' group='netadm'
+ privileges='zone' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='refresh'
+ exec=':true'
+ timeout_seconds='60' >
+ </exec_method>
+
+ <property_group name='netcfgd' type='application' >
+ <propval name='debug' type='boolean' value='false' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Network configuration data management
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='netcfgd' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/network-netmask.xml b/usr/src/cmd/svc/milestone/network-netmask.xml
new file mode 100644
index 0000000000..31caeb3f1d
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/network-netmask.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2010 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
+
+ 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
+ file.
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:network-netmask'>
+
+<!--
+ network/netmask service resets the netmask and broadcast address
+ whenever new information is available from NIS.
+-->
+
+<service
+ name='network/netmask'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <dependency
+ name='init'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/initial' />
+ </dependency>
+
+ <dependency
+ name='nisplus'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/rpc/nisplus' />
+ </dependency>
+
+ <dependency
+ name='nis_server'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/nis/server' />
+ </dependency>
+
+ <dependency
+ name='nis_client'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/nis/client' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/net-netmask'
+ timeout_seconds='600' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='3' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Reset the netmask and broadcast address
+ </loctext>
+ </common_name>
+ <description>
+ <loctext xml:lang='C'>
+ Resetting of the netmask and broadcast
+ address whenever new information is
+ available from NIS.
+ </loctext>
+ </description>
+ <documentation>
+ <manpage title='ifconfig' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/network-physical.xml b/usr/src/cmd/svc/milestone/network-physical.xml
index ab357ebf81..6a098d1d35 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 2009 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
CDDL HEADER START
@@ -47,6 +47,20 @@
<instance name='default' enabled='true'>
+ <!--
+ physical:default and physical:nwam are mutually exclusive.
+ Use a one-way dependency for now since two-way exclude_all
+ does not work; enforcement of single_instance in the future
+ will fix this.
+ -->
+ <dependency
+ name='physical_nwam'
+ grouping='exclude_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/physical:nwam' />
+ </dependency>
+
<exec_method
type='method'
name='start'
@@ -66,7 +80,7 @@
<template>
<common_name>
<loctext xml:lang='C'>
- physical network interfaces
+ physical network interfaces
</loctext>
</common_name>
<documentation>
@@ -83,19 +97,34 @@
type='method'
name='start'
exec='/lib/svc/method/net-nwam start'
- timeout_seconds='600' />
+ timeout_seconds='120' >
+ <method_context>
+ <method_credential user='netadm' group='netadm'
+ supp_groups='netadm' privileges='zone' />
+ </method_context>
+ </exec_method>
<exec_method
type='method'
name='stop'
exec='/lib/svc/method/net-nwam stop'
- timeout_seconds='60' />
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential user='netadm' group='netadm'
+ supp_groups='netadm' privileges='zone' />
+ </method_context>
+ </exec_method>
<exec_method
type='method'
name='refresh'
exec='/lib/svc/method/net-nwam refresh'
- timeout_seconds='60' />
+ timeout_seconds='60' >
+ <method_context>
+ <method_credential user='netadm' group='netadm'
+ supp_groups='netadm' privileges='zone' />
+ </method_context>
+ </exec_method>
<property_group name='general' type='framework'>
<!-- to start/stop NWAM services -->
@@ -108,12 +137,14 @@
<property_group name='nwamd' type='application'>
<stability value='Unstable' />
<propval name='debug' type='boolean' value='false' />
- <propval name='use_net_svc' type='boolean' value='true' />
<propval name='autoconf' type='boolean' value='false' />
- <propval name='dhcp_wait_time' type='count' value='60' />
+ <propval name='ncu_wait_time' type='count' value='60' />
+ <propval name='condition_check_interval' type='count'
+ value='120' />
<propval name='scan_interval' type='count' value='120' />
- <propval name='idle_time' type='count' value='10' />
+ <propval name='scan_level' type='astring' value='weak' />
<propval name='strict_bssid' type='boolean' value='false' />
+ <propval name='active_ncp' type='astring' value='Automatic' />
<propval name='value_authorization' type='astring'
value='solaris.smf.value.nwam' />
</property_group>
@@ -128,12 +159,8 @@
<manpage title='nwamd' section='1M'
manpath='/usr/share/man' />
<doc_link
- name='Network Auto-Magic OpenSolaris Project Page'
- uri='http://opensolaris.org/os/projects/nwam/'
- />
- <doc_link
- name='Network Auto-Magic Operational Details'
- uri='http://opensolaris.org/os/projects/nwam/picea/'
+ name='Network Auto-Magic OpenSolaris Project Page'
+ uri='http://hub.opensolaris.org/bin/view/Project+nwam/'
/>
</documentation>
</template>
diff --git a/usr/src/cmd/svc/seed/Makefile b/usr/src/cmd/svc/seed/Makefile
index 920de235ee..7e69e27a54 100644
--- a/usr/src/cmd/svc/seed/Makefile
+++ b/usr/src/cmd/svc/seed/Makefile
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -60,6 +60,7 @@ COMMON_DESCRIPTIONS = \
../milestone/name-services.xml \
../milestone/network-initial.xml \
../milestone/network-loopback.xml \
+ ../milestone/network-netcfg.xml \
../milestone/network-physical.xml \
../milestone/restarter.xml \
../milestone/root-fs.xml \
diff --git a/usr/src/cmd/svc/shell/ipf_include.sh b/usr/src/cmd/svc/shell/ipf_include.sh
index b55cd6385e..3f2e53cfc5 100644
--- a/usr/src/cmd/svc/shell/ipf_include.sh
+++ b/usr/src/cmd/svc/shell/ipf_include.sh
@@ -20,14 +20,27 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+IPFILTER_FMRI="svc:/network/ipfilter:default"
ETC_IPF_DIR=/etc/ipf
-IP6FILCONF=$ETC_IPF_DIR/ipf6.conf
-IPNATCONF=$ETC_IPF_DIR/ipnat.conf
-IPPOOLCONF=$ETC_IPF_DIR/ippool.conf
+IP6FILCONF=`/usr/bin/svcprop -p config/ipf6_config_file $IPFILTER_FMRI \
+ 2>/dev/null`
+if [ $? -eq 1 ]; then
+ IP6FILCONF=$ETC_IPF_DIR/ipf6.conf
+fi
+IPNATCONF=`/usr/bin/svcprop -p config/ipnat_config_file $IPFILTER_FMRI \
+ 2>/dev/null`
+if [ $? -eq 1 ]; then
+ IPNATCONF=$ETC_IPF_DIR/ipnat.conf
+fi
+IPPOOLCONF=`/usr/bin/svcprop -p config/ippool_config_file $IPFILTER_FMRI \
+ 2>/dev/null`
+if [ $? -eq 1 ]; then
+ IPPOOLCONF=$ETC_IPF_DIR/ippool.conf
+fi
VAR_IPF_DIR=/var/run/ipf
IPFILCONF=$VAR_IPF_DIR/ipf.conf
IPFILOVRCONF=$VAR_IPF_DIR/ipf_ovr.conf
diff --git a/usr/src/cmd/svc/shell/net_include.sh b/usr/src/cmd/svc/shell/net_include.sh
index 84376a981b..cbc5b051b5 100644
--- a/usr/src/cmd/svc/shell/net_include.sh
+++ b/usr/src/cmd/svc/shell/net_include.sh
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
@@ -710,3 +710,113 @@ update_pvid()
}
}'
}
+
+#
+# service_exists fmri
+#
+# returns success (0) if the service exists, 1 otherwise.
+#
+service_exists()
+{
+ /usr/sbin/svccfg -s $1 listpg > /dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ return 0;
+ fi
+ return 1;
+}
+
+#
+# service_is_enabled fmri
+#
+# returns success (0) if the service is enabled (permanently or
+# temporarily), 1 otherwise.
+#
+service_is_enabled()
+{
+ #
+ # The -c option must be specified to use the composed view
+ # because the general/enabled property takes immediate effect.
+ # See Example 2 in svcprop(1).
+ #
+ # Look at the general_ovr/enabled (if it is present) first to
+ # determine the temporarily enabled state.
+ #
+ tstate=`/usr/bin/svcprop -c -p general_ovr/enabled $1 2>/dev/null`
+ if [ $? -eq 0 ]; then
+ [ "$tstate" = "true" ] && return 0
+ return 1
+ fi
+
+ state=`/usr/bin/svcprop -c -p general/enabled $1 2>/dev/null`
+ [ "$state" = "true" ] && return 0
+ return 1
+}
+
+#
+# is_valid_v4addr addr
+#
+# Returns 0 if a valid IPv4 address is given, 1 otherwise.
+#
+is_valid_v4addr()
+{
+ echo $1 | /usr/xpg4/bin/awk 'NF != 1 { exit 1 } \
+ $1 !~ /^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\
+ (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ \
+ { exit 1 }'
+ return $?
+}
+
+#
+# is_valid_v6addr addr
+#
+# Returns 0 if a valid IPv6 address is given, 1 otherwise.
+#
+is_valid_v6addr()
+{
+ echo $1 | /usr/xpg4/bin/awk 'NF != 1 { exit 1 } \
+ # 1:2:3:4:5:6:7:8
+ $1 !~ /^([a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/ &&
+ # 1:2:3::6:7:8
+ $1 !~ /^([a-fA-F0-9]{1,4}:){0,6}:([a-fA-F0-9]{1,4}:){0,6}\
+ [a-fA-F0-9]{1,4}$/ &&
+ # 1:2:3::
+ $1 !~ /^([a-fA-F0-9]{1,4}:){0,7}:$/ &&
+ # ::7:8
+ $1 !~ /^:(:[a-fA-F0-9]{1,4}){0,6}:[a-fA-F0-9]{1,4}$/ &&
+ # ::f:1.2.3.4
+ $1 !~ /^:(:[a-fA-F0-9]{1,4}){0,5}:\
+ ((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\
+ (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ &&
+ # a:b:c:d:e:f:1.2.3.4
+ $1 !~ /^([a-fA-F0-9]{1,4}:){6}\
+ ((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}\
+ (25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$/ \
+ { exit 1 }'
+ return $?
+}
+
+#
+# is_valid_addr addr
+#
+# Returns 0 if a valid IPv4 or IPv6 address is given, 1 otherwise.
+#
+is_valid_addr()
+{
+ is_valid_v4addr $1 || is_valid_v6addr $1
+}
+
+#
+# nwam_get_loc_prop location property
+#
+# echoes the value of the property for the given location
+# return:
+# 0 => property is set
+# 1 => property is not set
+#
+nwam_get_loc_prop()
+{
+ value=`/usr/sbin/nwamcfg "select loc $1; get -V $2" 2>/dev/null`
+ rtn=$?
+ echo $value
+ return $rtn
+}
diff --git a/usr/src/head/auth_list.h b/usr/src/head/auth_list.h
index c9b68fd1ef..8b44309ffe 100644
--- a/usr/src/head/auth_list.h
+++ b/usr/src/head/auth_list.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* This is an internal header file. Not to be shipped.
@@ -37,6 +37,10 @@ extern "C" {
* Names of authorizations currently in use in the system
*/
+#define AUTOCONF_READ_AUTH "solaris.network.autoconf.read"
+#define AUTOCONF_SELECT_AUTH "solaris.network.autoconf.select"
+#define AUTOCONF_WLAN_AUTH "solaris.network.autoconf.wlan"
+#define AUTOCONF_WRITE_AUTH "solaris.network.autoconf.write"
#define CDRW_AUTH "solaris.device.cdrw"
#define CRONADMIN_AUTH "solaris.jobs.admin"
#define CRONUSER_AUTH "solaris.jobs.user"
@@ -44,7 +48,6 @@ extern "C" {
#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 NET_ILB_CONFIG_AUTH "solaris.network.ilb.config"
#define NET_ILB_ENABLE_AUTH "solaris.network.ilb.enable"
#define SET_DATE_AUTH "solaris.system.date"
@@ -71,6 +74,8 @@ extern "C" {
#define PRINT_UNLABELED_AUTH "solaris.print.unlabeled"
#define SHUTDOWN_AUTH "solaris.system.shutdown"
#define SYS_ACCRED_SET_AUTH "solaris.label.range"
+#define SYSEVENT_READ_AUTH "solaris.system.sysevent.read"
+#define SYSEVENT_WRITE_AUTH "solaris.system.sysevent.write"
#define WIN_DOWNGRADE_SL_AUTH "solaris.label.win.downgrade"
#define WIN_UPGRADE_SL_AUTH "solaris.label.win.upgrade"
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 8868b24d6f..dd091dfce9 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -334,6 +334,7 @@ MSGSUBDIRS= \
libinetutil \
libinstzones \
libnsl \
+ libnwam \
libpam \
libpicl \
libpool \
@@ -593,7 +594,6 @@ libiscsit: libc libnvpair libstmf libuuid libnsl
libkmf: libcryptoutil pkcs11
libnsl: libmd5 libscf
libmapid: libresolv
-libnwam: libdoor
librdc: libsocket libnsl libnsctl libunistat libdscfg
libuuid: libdlpi
$(CLOSED_BUILD)libike: libipsecutil libxnet libcryptoutil
@@ -601,6 +601,7 @@ libinetutil: libsocket
libipsecutil: libtecla libsocket
libinstzones: libzonecfg libcontract
libpkg: libwanboot libscf libadm
+libnwam: libscf
libsecdb: libnsl
libsasl: libgss libsocket pkcs11 libmd
sasl_plugins: pkcs11 libgss libsocket libsasl
@@ -611,6 +612,7 @@ libsmbfs: libsocket libnsl libkrb5
libsocket: libnsl
libstmfproxy: libstmf libsocket libnsl libpthread
libsum: libast
+libsysevent: libsecdb
libldap5: libsasl libsocket libnsl libmd
libsldap: libldap5 libtsol libnsl libc libscf libresolv
libpool: libnvpair libexacct
diff --git a/usr/src/lib/libbsm/audit_event.txt b/usr/src/lib/libbsm/audit_event.txt
index 3bdacb0477..64453e4998 100644
--- a/usr/src/lib/libbsm/audit_event.txt
+++ b/usr/src/lib/libbsm/audit_event.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
@@ -508,8 +508,8 @@
#
# nwamd(1M) events
#
-6300:AUE_nwam_attach:attach nwam user:ss
-6301:AUE_nwam_detach:detach nwam user:ss
+6300:AUE_nwam_enable:enable nwam profile object:ss
+6301:AUE_nwam_disable:disable nwam profile object:ss
#
# ilbd(1M) events
#
@@ -526,6 +526,11 @@
6320:AUE_ilb_create_servergroup:create ILB server group:as
6321:AUE_ilb_delete_servergroup:delete ILB server group:as
#
+# netcfgd(1M) events
+#
+6330:AUE_netcfg_update:create or modify configuration object:ss
+6331:AUE_netcfg_remove:remove configuration object from repository:ss
+#
# TCSD(8) events
#
6400:AUE_tpm_takeownership:take ownership of TPM:as
diff --git a/usr/src/lib/libbsm/common/adt.xml b/usr/src/lib/libbsm/common/adt.xml
index 6acabbff70..d03446ba71 100644
--- a/usr/src/lib/libbsm/common/adt.xml
+++ b/usr/src/lib/libbsm/common/adt.xml
@@ -20,7 +20,7 @@
CDDL HEADER END
-Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+Copyright 2010 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
-->
@@ -1861,34 +1861,6 @@ 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>
-
<!-- TPM events recorded by tcsd(8) -->
<event id="AUE_generic_tpm" type="generic" omit="always">
@@ -2533,8 +2505,92 @@ Use is subject to license terms.
</entry>
</event>
+ <event id="AUE_nwam_enable" header="0" idNo="132" omit="JNI">
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="profile_type">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Type of profile being enabled</comment>
+ </entry>
+ <entry id="profile_name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Name of profile being enabled</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_nwam_disable" header="0" idNo="133" omit="JNI">
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="profile_type">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Type of profile being disabled</comment>
+ </entry>
+ <entry id="profile_name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Name of profile being disabled</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_netcfg_update" header="0" idNo="134" omit="JNI">
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="parent_file">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Back-end data file being updated</comment>
+ </entry>
+ <entry id="object_name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Name of object being updated</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
+ <event id="AUE_netcfg_remove" header="0" idNo="135" omit="JNI">
+ <entry id="subject">
+ <internal token="subject"/>
+ <external opt="none"/>
+ </entry>
+ <entry id="parent_file">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Back-end data file being modified</comment>
+ </entry>
+ <entry id="object_name">
+ <internal token="text"/>
+ <external opt="required" type="char *"/>
+ <comment>Name of object being removed</comment>
+ </entry>
+ <entry id="return">
+ <internal token="return"/>
+ <external opt="none"/>
+ </entry>
+ </event>
+
<!-- add new events here with the next higher idNo -->
-<!-- Highest idNo is 131, so next is 132, then fix this comment -->
+<!-- Highest idNo is 135, so next is 136, then fix this comment -->
<!-- end of C Only events -->
<!--
diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c
index 8939d23331..ad23a1a25b 100644
--- a/usr/src/lib/libdladm/common/libdladm.c
+++ b/usr/src/lib/libdladm/common/libdladm.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -724,15 +724,19 @@ i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
if (!writeop || status != DLADM_STATUS_OK)
goto done;
+ /* Set permissions on file to db_perms */
+ if (fchmod(nfd, db_perms) < 0) {
+ status = dladm_errno2status(errno);
+ goto done;
+ }
+
/*
- * Configuration files need to be owned by the 'dladm' user.
- * If we are invoked by root, the file ownership needs to be fixed.
+ * Configuration files need to be owned by the 'dladm' user and
+ * 'netadm' group.
*/
- if (getuid() == 0 || geteuid() == 0) {
- if (fchown(nfd, UID_DLADM, GID_SYS) < 0) {
- status = dladm_errno2status(errno);
- goto done;
- }
+ if (fchown(nfd, UID_DLADM, GID_NETADM) < 0) {
+ status = dladm_errno2status(errno);
+ goto done;
}
if (fflush(nfp) == EOF) {
diff --git a/usr/src/lib/libdladm/common/libdlflow.c b/usr/src/lib/libdladm/common/libdlflow.c
index 0f9a3e0af5..235b948504 100644
--- a/usr/src/lib/libdladm/common/libdlflow.c
+++ b/usr/src/lib/libdladm/common/libdlflow.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -57,7 +57,7 @@
#define DLADM_FLOW_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
#define DLADM_FLOW_DB_OWNER UID_DLADM
-#define DLADM_FLOW_DB_GROUP GID_SYS
+#define DLADM_FLOW_DB_GROUP GID_NETADM
#define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
#define MAXLINELEN 1024
diff --git a/usr/src/lib/libdladm/common/secobj.c b/usr/src/lib/libdladm/common/secobj.c
index 07f02ac996..fec08ca930 100644
--- a/usr/src/lib/libdladm/common/secobj.c
+++ b/usr/src/lib/libdladm/common/secobj.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -543,8 +543,9 @@ process_secobj_db(dladm_handle_t handle, void *arg, FILE *fp, FILE *nfp)
return (status);
}
-#define SECOBJ_RW_DB(handle, statep, writeop) \
- (i_dladm_rw_db(handle, "/etc/dladm/secobj.conf", S_IRUSR | S_IWUSR, \
+#define SECOBJ_RW_DB(handle, statep, writeop) \
+ (i_dladm_rw_db(handle, "/etc/dladm/secobj.conf", \
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, \
process_secobj_db, (statep), (writeop)))
static dladm_status_t
diff --git a/usr/src/lib/libinetcfg/Makefile b/usr/src/lib/libinetcfg/Makefile
index 108e33c201..4d7c98746e 100644
--- a/usr/src/lib/libinetcfg/Makefile
+++ b/usr/src/lib/libinetcfg/Makefile
@@ -19,15 +19,14 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
include $(SRC)/lib/Makefile.lib
HDRS = inetcfg.h
+SRCS = inetcfg.c
HDRDIR = common
SUBDIRS = $(MACH)
POFILE = libinetcfg.po
diff --git a/usr/src/lib/libinetcfg/Makefile.com b/usr/src/lib/libinetcfg/Makefile.com
index 1d1ec9e8a2..26e4604be1 100644
--- a/usr/src/lib/libinetcfg/Makefile.com
+++ b/usr/src/lib/libinetcfg/Makefile.com
@@ -18,11 +18,9 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
LIBRARY = libinetcfg.a
VERS = .1
@@ -35,7 +33,7 @@ include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -lc -lnsl -lsocket -ldlpi
+LDLIBS += -lc -lnsl -lsocket -ldladm -ldlpi -linetutil
SRCDIR = ../common
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
diff --git a/usr/src/lib/libinetcfg/common/inetcfg.c b/usr/src/lib/libinetcfg/common/inetcfg.c
index d5a27f23ea..cdcbf00329 100644
--- a/usr/src/lib/libinetcfg/common/inetcfg.c
+++ b/usr/src/lib/libinetcfg/common/inetcfg.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,15 +34,19 @@
#include <sys/sockio.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/varargs.h>
#include <net/route.h>
#include <netinet/in.h>
#include <inet/ip.h>
#include <arpa/inet.h>
#include <libintl.h>
+#include <libdladm.h>
+#include <libdllink.h>
#include <libdlpi.h>
-#include <inetcfg.h>
+#include <libinetutil.h>
+#include <zone.h>
-#define ICFG_FAMILY(handle) handle->ifh_interface.if_protocol
+#include <inetcfg.h>
#define ICFG_SOCKADDR_LEN(protocol) \
(protocol == AF_INET) ? \
@@ -50,6 +54,8 @@
(socklen_t)sizeof (struct sockaddr_in6)
#define ICFG_LOGICAL_SEP ':'
+#define LOOPBACK_IF "lo0"
+#define ARP_MOD_NAME "arp"
/*
* Maximum amount of time (in milliseconds) to wait for Duplicate Address
@@ -57,19 +63,70 @@
*/
#define DAD_WAIT_TIME 5000
-/*
- * Note: must be kept in sync with error codes in <inetcfg.h>
- */
-static char *errmsgs[ICFG_NERR] = {
-/* 0 ICFG_SUCCESS */ "Success",
-/* 1 ICFG_FAILURE */ "Failure",
-/* 2 ICFG_NOT_SET */ "Could not return non-existent value",
-/* 3 ICFG_BAD_ADDR */ "Invalid Address",
-/* 4 ICFG_BAD_PROT */ "Wrong protocol family for operation",
-/* 5 ICFG_DAD_FAILED */ "Duplicate address detection failure",
-/* 6 ICFG_DAD_FOUND */ "Duplicate address detected"
+/* error codes and text descriiption */
+static struct icfg_error_info {
+ icfg_error_t error_code;
+ const char *error_desc;
+} icfg_errors[] = {
+ { ICFG_SUCCESS, "No error occurred" },
+ { ICFG_FAILURE, "Generic failure" },
+ { ICFG_NO_MEMORY, "Insufficient memory" },
+ { ICFG_NOT_TUNNEL, "Tunnel operation attempted on non-tunnel" },
+ { ICFG_NOT_SET, "Could not return non-existent value" },
+ { ICFG_BAD_ADDR, "Invalid address" },
+ { ICFG_BAD_PROTOCOL, "Wrong protocol family for operation" },
+ { ICFG_DAD_FAILED, "Duplicate address detection failure" },
+ { ICFG_DAD_FOUND, "Duplicate address detected" },
+ { ICFG_IF_UP, "Interface is up" },
+ { ICFG_EXISTS, "Interface already exists" },
+ { ICFG_NO_EXIST, "Interface does not exist" },
+ { ICFG_INVALID_ARG, "Invalid argument" },
+ { ICFG_INVALID_NAME, "Invalid name" },
+ { ICFG_DLPI_INVALID_LINK, "Link does not exist" },
+ { ICFG_DLPI_FAILURE, "DLPI error" },
+ { ICFG_NO_PLUMB_IP, "Could not plumb IP stream" },
+ { ICFG_NO_PLUMB_ARP, "Could not plumb ARP stream" },
+ { ICFG_NO_UNPLUMB_IP, "Could not unplumb IP stream" },
+ { ICFG_NO_UNPLUMB_ARP, "Could not unplumb ARP stream" },
+ { ICFG_NO_IP_MUX, "No IP mux set" },
+ { 0, NULL }
};
+/* convert libdlpi error to libinetcfg error */
+icfg_error_t
+dlpi_error_to_icfg_error(int err)
+{
+ switch (err) {
+ case DLPI_SUCCESS:
+ return (ICFG_SUCCESS);
+ case DLPI_ELINKNAMEINVAL:
+ return (ICFG_INVALID_NAME);
+ case DLPI_ENOLINK:
+ case DLPI_EBADLINK:
+ return (ICFG_DLPI_INVALID_LINK);
+ case DLPI_EINVAL:
+ case DLPI_ENOTSTYLE2:
+ case DLPI_EBADMSG:
+ case DLPI_EINHANDLE:
+ case DLPI_EVERNOTSUP:
+ case DLPI_EMODENOTSUP:
+ return (ICFG_INVALID_ARG);
+ case DL_BADADDR:
+ return (ICFG_BAD_ADDR);
+ case DL_SYSERR:
+ switch (errno) {
+ case ENOMEM:
+ return (ICFG_NO_MEMORY);
+ case EINVAL:
+ return (ICFG_INVALID_ARG);
+ }
+ /* FALLTHROUGH */
+ case DLPI_FAILURE:
+ default:
+ return (ICFG_DLPI_FAILURE);
+ }
+}
+
/*
* Convert a prefix length to a netmask. Note that the mask array
* should zero'ed by the caller.
@@ -157,10 +214,15 @@ to_sockaddr_storage(sa_family_t af, const struct sockaddr *addr,
const char *
icfg_errmsg(int errcode)
{
- if ((errcode < ICFG_SUCCESS) || (errcode >= ICFG_NERR))
- return (dgettext(TEXT_DOMAIN, "<unknown error>"));
+ int i;
+
+ for (i = 0; icfg_errors[i].error_desc != NULL; i++) {
+ if (errcode == icfg_errors[i].error_code)
+ return (dgettext(TEXT_DOMAIN,
+ icfg_errors[i].error_desc));
+ }
- return (dgettext(TEXT_DOMAIN, errmsgs[errcode]));
+ return (dgettext(TEXT_DOMAIN, "<unknown error>"));
}
/*
@@ -169,7 +231,7 @@ icfg_errmsg(int errcode)
* responsible for freeing resources allocated by this API by calling the
* icfg_close() API.
*
- * Returns: ICFG_SUCCESS or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_NO_MEMORY or ICFG_FAILURE.
*/
int
icfg_open(icfg_handle_t *handle, const icfg_if_t *interface)
@@ -191,7 +253,7 @@ icfg_open(icfg_handle_t *handle, const icfg_if_t *interface)
family = interface->if_protocol;
if ((loc_handle = calloc(1, sizeof (struct icfg_handle))) == NULL) {
- return (ICFG_FAILURE);
+ return (ICFG_NO_MEMORY);
}
if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) {
@@ -221,6 +283,24 @@ icfg_close(icfg_handle_t handle)
}
/*
+ * Retrieves the interface name associated with the handle passed in.
+ */
+const char *
+icfg_if_name(icfg_handle_t handle)
+{
+ return (handle->ifh_interface.if_name);
+}
+
+/*
+ * Retrieves the protocol associated with the handle passed in.
+ */
+static int
+icfg_if_protocol(icfg_handle_t handle)
+{
+ return (handle->ifh_interface.if_protocol);
+}
+
+/*
* Any time that flags are changed on an interface where either the new or the
* existing flags have IFF_UP set, we'll get at least one RTM_IFINFO message to
* announce the flag status. Typically, there are two such messages: one
@@ -294,9 +374,9 @@ icfg_set_flags(icfg_handle_t handle, uint64_t flags)
int rtsock = -1;
int aware = RTAW_UNDER_IPMP;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if ((ret = icfg_get_flags(handle, &oflags)) != ICFG_SUCCESS)
return (ret);
@@ -310,7 +390,7 @@ icfg_set_flags(icfg_handle_t handle, uint64_t flags)
* changing an IPMP test address, we enable RTAW_UNDER_IPMP.
*/
if (flags & IFF_UP) {
- rtsock = socket(PF_ROUTE, SOCK_RAW, ICFG_FAMILY(handle));
+ rtsock = socket(PF_ROUTE, SOCK_RAW, icfg_if_protocol(handle));
if (rtsock != -1) {
(void) setsockopt(rtsock, SOL_ROUTE, RT_AWARE, &aware,
sizeof (aware));
@@ -345,9 +425,9 @@ icfg_set_metric(icfg_handle_t handle, int metric)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
lifr.lifr_metric = metric;
if (ioctl(handle->ifh_sock, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0) {
@@ -369,9 +449,9 @@ icfg_set_mtu(icfg_handle_t handle, uint_t mtu)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
lifr.lifr_mtu = mtu;
if (ioctl(handle->ifh_sock, SIOCSLIFMTU, (caddr_t)&lifr) < 0) {
@@ -393,9 +473,9 @@ icfg_set_index(icfg_handle_t handle, int index)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
lifr.lifr_index = index;
if (ioctl(handle->ifh_sock, SIOCSLIFINDEX, (caddr_t)&lifr) < 0) {
@@ -413,7 +493,7 @@ icfg_set_index(icfg_handle_t handle, int index)
*
* The address will be set to the value pointed to by 'addr'.
*
- * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL or ICFG_FAILURE.
*/
int
icfg_set_netmask(icfg_handle_t handle, const struct sockaddr_in *addr)
@@ -421,17 +501,17 @@ icfg_set_netmask(icfg_handle_t handle, const struct sockaddr_in *addr)
struct lifreq lifr;
int ret;
- if (ICFG_FAMILY(handle) != AF_INET) {
- return (ICFG_BAD_PROT);
+ if (icfg_if_protocol(handle) != AF_INET) {
+ return (ICFG_BAD_PROTOCOL);
}
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle),
+ if ((ret = to_sockaddr_storage(icfg_if_protocol(handle),
(struct sockaddr *)addr, sizeof (*addr),
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
lifr.lifr_addr.ss_family = AF_INET;
@@ -450,7 +530,7 @@ icfg_set_netmask(icfg_handle_t handle, const struct sockaddr_in *addr)
*
* The address will be set to the value pointed to by 'addr'.
*
- * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL or ICFG_FAILURE.
*/
int
icfg_set_broadcast(icfg_handle_t handle, const struct sockaddr_in *addr)
@@ -458,17 +538,17 @@ icfg_set_broadcast(icfg_handle_t handle, const struct sockaddr_in *addr)
struct lifreq lifr;
int ret;
- if (ICFG_FAMILY(handle) != AF_INET) {
- return (ICFG_BAD_PROT);
+ if (icfg_if_protocol(handle) != AF_INET) {
+ return (ICFG_BAD_PROTOCOL);
}
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle),
+ if ((ret = to_sockaddr_storage(icfg_if_protocol(handle),
(struct sockaddr *)addr, sizeof (*addr),
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
lifr.lifr_addr.ss_family = AF_INET;
@@ -494,11 +574,11 @@ icfg_set_prefixlen(icfg_handle_t handle, int prefixlen)
struct lifreq lifr;
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
- if (ICFG_FAMILY(handle) == AF_INET6) {
+ if (icfg_if_protocol(handle) == AF_INET6) {
struct sockaddr_in6 *sin6;
int ret;
@@ -552,7 +632,7 @@ icfg_set_addr(icfg_handle_t handle, const struct sockaddr *addr,
int aware = RTAW_UNDER_IPMP;
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), addr, addrlen,
+ if ((ret = to_sockaddr_storage(icfg_if_protocol(handle), addr, addrlen,
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
@@ -565,7 +645,7 @@ icfg_set_addr(icfg_handle_t handle, const struct sockaddr *addr,
return (ret);
if (flags & IFF_UP) {
- rtsock = socket(PF_ROUTE, SOCK_RAW, ICFG_FAMILY(handle));
+ rtsock = socket(PF_ROUTE, SOCK_RAW, icfg_if_protocol(handle));
if (rtsock != -1) {
(void) setsockopt(rtsock, SOL_ROUTE, RT_AWARE, &aware,
sizeof (aware));
@@ -574,7 +654,7 @@ icfg_set_addr(icfg_handle_t handle, const struct sockaddr *addr,
(void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
if (rtsock != -1)
@@ -599,7 +679,7 @@ icfg_set_addr(icfg_handle_t handle, const struct sockaddr *addr,
* The token will be set to the value contained in 'addr' and
* its associated prefixlen will be set to 'prefixlen'.
*
- * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL or ICFG_FAILURE.
*/
int
icfg_set_token(icfg_handle_t handle, const struct sockaddr_in6 *addr,
@@ -608,19 +688,19 @@ icfg_set_token(icfg_handle_t handle, const struct sockaddr_in6 *addr,
struct lifreq lifr;
int ret;
- if (ICFG_FAMILY(handle) != AF_INET6) {
- return (ICFG_BAD_PROT);
+ if (icfg_if_protocol(handle) != AF_INET6) {
+ return (ICFG_BAD_PROTOCOL);
}
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle),
+ if ((ret = to_sockaddr_storage(icfg_if_protocol(handle),
(struct sockaddr *)addr, sizeof (*addr),
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
lifr.lifr_addrlen = prefixlen;
if (ioctl(handle->ifh_sock, SIOCSLIFTOKEN, (caddr_t)&lifr) < 0) {
@@ -650,11 +730,11 @@ icfg_set_subnet(icfg_handle_t handle, const struct sockaddr *addr,
int ret;
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
- if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), addr, addrlen,
+ if ((ret = to_sockaddr_storage(icfg_if_protocol(handle), addr, addrlen,
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
@@ -686,11 +766,11 @@ icfg_set_dest_addr(icfg_handle_t handle, const struct sockaddr *addr,
int ret;
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
- if ((ret = to_sockaddr_storage(ICFG_FAMILY(handle), addr, addrlen,
+ if ((ret = to_sockaddr_storage(icfg_if_protocol(handle), addr, addrlen,
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
@@ -731,9 +811,9 @@ icfg_get_addr(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen,
struct lifreq lifr;
int ret;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0) {
if (force && ((errno == EADDRNOTAVAIL) ||
@@ -745,7 +825,7 @@ icfg_get_addr(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen,
}
}
- if ((ret = to_sockaddr(ICFG_FAMILY(handle), addr, addrlen,
+ if ((ret = to_sockaddr(icfg_if_protocol(handle), addr, addrlen,
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
@@ -768,7 +848,7 @@ icfg_get_addr(icfg_handle_t handle, struct sockaddr *addr, socklen_t *addrlen,
* obtaining the token address will be ignored and the address will be set
* to all 0's. Non-critical errors consist of EADDRNOTAVAIL and EINVAL.
*
- * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL or ICFG_FAILURE.
*/
int
icfg_get_token(icfg_handle_t handle, struct sockaddr_in6 *addr,
@@ -777,13 +857,13 @@ icfg_get_token(icfg_handle_t handle, struct sockaddr_in6 *addr,
struct lifreq lifr;
socklen_t addrlen = sizeof (*addr);
- if (ICFG_FAMILY(handle) != AF_INET6) {
- return (ICFG_BAD_PROT);
+ if (icfg_if_protocol(handle) != AF_INET6) {
+ return (ICFG_BAD_PROTOCOL);
}
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFTOKEN, (caddr_t)&lifr) < 0) {
if (force && ((errno == EADDRNOTAVAIL) || (errno == EINVAL))) {
@@ -795,7 +875,7 @@ icfg_get_token(icfg_handle_t handle, struct sockaddr_in6 *addr,
}
*prefixlen = lifr.lifr_addrlen;
- return (to_sockaddr(ICFG_FAMILY(handle), (struct sockaddr *)addr,
+ return (to_sockaddr(icfg_if_protocol(handle), (struct sockaddr *)addr,
&addrlen, &lifr.lifr_addr));
}
@@ -827,9 +907,9 @@ icfg_get_subnet(icfg_handle_t handle, struct sockaddr *addr,
struct lifreq lifr;
int ret;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFSUBNET, (caddr_t)&lifr) < 0) {
if (force && ((errno == EADDRNOTAVAIL) ||
@@ -841,7 +921,7 @@ icfg_get_subnet(icfg_handle_t handle, struct sockaddr *addr,
}
}
- if ((ret = to_sockaddr(ICFG_FAMILY(handle), addr, addrlen,
+ if ((ret = to_sockaddr(icfg_if_protocol(handle), addr, addrlen,
&lifr.lifr_addr)) != ICFG_SUCCESS) {
return (ret);
}
@@ -861,7 +941,7 @@ icfg_get_subnet(icfg_handle_t handle, struct sockaddr *addr,
* If no netmask address has been set for the interface, an address of
* all 0's will be returned.
*
- * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL or ICFG_FAILURE.
*/
int
icfg_get_netmask(icfg_handle_t handle, struct sockaddr_in *addr)
@@ -869,13 +949,13 @@ icfg_get_netmask(icfg_handle_t handle, struct sockaddr_in *addr)
struct lifreq lifr;
socklen_t addrlen = sizeof (*addr);
- if (ICFG_FAMILY(handle) != AF_INET) {
- return (ICFG_BAD_PROT);
+ if (icfg_if_protocol(handle) != AF_INET) {
+ return (ICFG_BAD_PROTOCOL);
}
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) {
if (errno != EADDRNOTAVAIL) {
@@ -884,7 +964,7 @@ icfg_get_netmask(icfg_handle_t handle, struct sockaddr_in *addr)
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
}
- return (to_sockaddr(ICFG_FAMILY(handle), (struct sockaddr *)addr,
+ return (to_sockaddr(icfg_if_protocol(handle), (struct sockaddr *)addr,
&addrlen, &lifr.lifr_addr));
}
@@ -899,7 +979,7 @@ icfg_get_netmask(icfg_handle_t handle, struct sockaddr_in *addr)
* If no broadcast address has been set for the interface, an address
* of all 0's will be returned.
*
- * Returns: ICFG_SUCCESS, ICFG_BAD_PROT or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL or ICFG_FAILURE.
*/
int
icfg_get_broadcast(icfg_handle_t handle, struct sockaddr_in *addr)
@@ -907,13 +987,13 @@ icfg_get_broadcast(icfg_handle_t handle, struct sockaddr_in *addr)
struct lifreq lifr;
socklen_t addrlen = sizeof (*addr);
- if (ICFG_FAMILY(handle) != AF_INET) {
- return (ICFG_BAD_PROT);
+ if (icfg_if_protocol(handle) != AF_INET) {
+ return (ICFG_BAD_PROTOCOL);
}
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFBRDADDR, (caddr_t)&lifr) < 0) {
if (errno != EADDRNOTAVAIL) {
@@ -922,7 +1002,7 @@ icfg_get_broadcast(icfg_handle_t handle, struct sockaddr_in *addr)
(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
}
- return (to_sockaddr(ICFG_FAMILY(handle), (struct sockaddr *)addr,
+ return (to_sockaddr(icfg_if_protocol(handle), (struct sockaddr *)addr,
&addrlen, &lifr.lifr_addr));
}
@@ -953,9 +1033,9 @@ icfg_get_dest_addr(icfg_handle_t handle, struct sockaddr *addr,
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFDSTADDR, (caddr_t)&lifr) < 0) {
if (errno != EADDRNOTAVAIL) {
@@ -966,7 +1046,7 @@ icfg_get_dest_addr(icfg_handle_t handle, struct sockaddr *addr,
sizeof (lifr.lifr_dstaddr));
}
- return (to_sockaddr(ICFG_FAMILY(handle), addr, addrlen,
+ return (to_sockaddr(icfg_if_protocol(handle), addr, addrlen,
&lifr.lifr_addr));
}
@@ -984,9 +1064,9 @@ icfg_get_groupname(icfg_handle_t handle, char *groupname, size_t len)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
(void) memset(lifr.lifr_groupname, 0, sizeof (lifr.lifr_groupname));
@@ -1004,28 +1084,18 @@ icfg_get_groupname(icfg_handle_t handle, char *groupname, size_t len)
}
/*
- * Returns the link info of the interface represented by the handle
- * argument into the buffer pointed to by the 'info' argument.
+ * Returns the groupinfo, if any, associated with the group identified in
+ * the gi_grname field of the passed-in lifgr structure. Upon successful
+ * return, the lifgr structure will be populated with the associated
+ * group info.
*
* Returns: ICFG_SUCCESS or ICFG_FAILURE.
*/
-int
-icfg_get_linkinfo(icfg_handle_t handle, lif_ifinfo_req_t *info)
+static int
+icfg_get_groupinfo(icfg_handle_t handle, lifgroupinfo_t *lifgr)
{
- struct lifreq lifr;
- char *cp;
-
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
- sizeof (lifr.lifr_name));
- if ((cp = strchr(lifr.lifr_name, ICFG_LOGICAL_SEP)) != NULL) {
- *cp = '\0';
- }
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
-
- if (ioctl(handle->ifh_sock, SIOCGLIFLNKINFO, (caddr_t)&lifr) < 0) {
+ if (ioctl(handle->ifh_sock, SIOCGLIFGROUPINFO, lifgr) < 0)
return (ICFG_FAILURE);
- }
- *info = lifr.lifr_ifinfo;
return (ICFG_SUCCESS);
}
@@ -1034,19 +1104,26 @@ icfg_get_linkinfo(icfg_handle_t handle, lif_ifinfo_req_t *info)
* Returns the flags value of the interface represented by the handle
* argument into the buffer pointed to by the 'flags' argument.
*
- * Returns: ICFG_SUCCESS or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_NO_EXIST or ICFG_FAILURE.
*/
int
icfg_get_flags(icfg_handle_t handle, uint64_t *flags)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ if (flags == NULL)
+ return (ICFG_INVALID_ARG);
+
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
- return (ICFG_FAILURE);
+ if (errno == ENXIO)
+ return (ICFG_NO_EXIST);
+ else
+ return (ICFG_FAILURE);
}
*flags = lifr.lifr_flags;
@@ -1064,9 +1141,13 @@ icfg_get_metric(icfg_handle_t handle, int *metric)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ if (metric == NULL)
+ return (ICFG_INVALID_ARG);
+
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) {
return (ICFG_FAILURE);
@@ -1087,9 +1168,13 @@ icfg_get_mtu(icfg_handle_t handle, uint_t *mtu)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ if (mtu == NULL)
+ return (ICFG_INVALID_ARG);
+
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFMTU, (caddr_t)&lifr) < 0) {
return (ICFG_FAILURE);
@@ -1110,9 +1195,13 @@ icfg_get_index(icfg_handle_t handle, int *index)
{
struct lifreq lifr;
- (void) strlcpy(lifr.lifr_name, handle->ifh_interface.if_name,
+ if (index == NULL)
+ return (ICFG_INVALID_ARG);
+
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = ICFG_FAMILY(handle);
+ lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
if (ioctl(handle->ifh_sock, SIOCGLIFINDEX, (caddr_t)&lifr) < 0) {
return (ICFG_FAILURE);
@@ -1166,13 +1255,13 @@ icfg_iterate_if(int proto, int type, void *arg,
* returned as an array of icfg_if_t structures. The number of interfaces in
* the array will be returned via the 'numif' argument. Since the array of
* interfaces is allocated by this API, the caller is responsible for freeing
- * the memory associated with this array by calling icfg_free_list().
+ * the memory associated with this array by calling icfg_free_if_list().
*
* The 'proto' argument is used by the caller to define which interfaces are
* to be listed by the API. The possible values for proto are AF_INET,
* AF_INET6, and AF_UNSPEC.
*
- * Returns: ICFG_SUCCESS or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_BAD_PROTOCOL, ICFG_NO_MEMORY or ICFG_FAILURE.
*/
static int
get_plumbed_if_list(icfg_if_t **list, int *numif, int proto) {
@@ -1196,7 +1285,7 @@ get_plumbed_if_list(icfg_if_t **list, int *numif, int proto) {
(proto != AF_INET) &&
(proto != AF_INET6)) {
errno = EINVAL;
- return (ICFG_FAILURE);
+ return (ICFG_BAD_PROTOCOL);
}
lifc_family = proto;
@@ -1227,7 +1316,7 @@ get_plumbed_if_list(icfg_if_t **list, int *numif, int proto) {
syserr = errno;
(void) close(sock);
errno = syserr;
- return (ICFG_FAILURE);
+ return (ICFG_NO_MEMORY);
}
/*
@@ -1252,7 +1341,7 @@ get_plumbed_if_list(icfg_if_t **list, int *numif, int proto) {
syserr = errno;
free(buf);
errno = syserr;
- return (ICFG_FAILURE);
+ return (ICFG_NO_MEMORY);
}
lifrp = lifc.lifc_req;
@@ -1311,7 +1400,7 @@ add_link_list(const char *link, void *arg)
* returned as an array of icfg_if_t structures. The number of interfaces in
* the array will be returned via the 'numif' argument.
*
- * Returns: ICFG_SUCCESS or ICFG_FAILURE.
+ * Returns: ICFG_SUCCESS, ICFG_NO_MEMORY or ICFG_FAILURE.
*/
static int
get_link_list(icfg_if_t **listp, int *numif) {
@@ -1327,10 +1416,18 @@ get_link_list(icfg_if_t **listp, int *numif) {
errno = lw.lw_err;
goto done;
}
+ if (lw.lw_num == 0) {
+ /* no links found, nothing else to do */
+ *listp = NULL;
+ *numif = 0;
+ return (ICFG_SUCCESS);
+ }
list = calloc(lw.lw_num, sizeof (icfg_if_t));
- if (list == NULL)
+ if (list == NULL) {
+ ret = ICFG_NO_MEMORY;
goto done;
+ }
*listp = list;
for (entry = lw.lw_list; entry != NULL; entry = entry->ll_next) {
@@ -1359,7 +1456,7 @@ done:
* the 'numif' argument. Since the array of interfaces is
* allocated by this API, the caller is responsible for freeing
* the memory associated with this array by calling
- * icfg_free_list().
+ * icfg_free_if_list().
*
* The 'proto' argument is used by the caller to define which
* interfaces are to be listed by the API. The possible values
@@ -1373,6 +1470,9 @@ done:
int
icfg_get_if_list(icfg_if_t **list, int *numif, int proto, int type)
{
+ if (list == NULL || numif == NULL)
+ return (ICFG_INVALID_ARG);
+
*list = NULL;
*numif = 0;
@@ -1408,11 +1508,23 @@ icfg_free_if_list(icfg_if_t *list)
boolean_t
icfg_is_logical(icfg_handle_t handle)
{
- return (strchr(handle->ifh_interface.if_name, ICFG_LOGICAL_SEP)
+ return (strchr(icfg_if_name(handle), ICFG_LOGICAL_SEP)
!= NULL);
}
/*
+ * Determines whether or not an interface name represents a loopback
+ * interface or not.
+ *
+ * Returns: B_TRUE if loopback, B_FALSE if not.
+ */
+static boolean_t
+icfg_is_loopback(icfg_handle_t handle)
+{
+ return (strcmp(icfg_if_name(handle), LOOPBACK_IF) == 0);
+}
+
+/*
* Given a sockaddr representation of an IPv4 or IPv6 address returns the
* string representation. Note that 'sockaddr' should point at the correct
* sockaddr structure for the address family (sockaddr_in for AF_INET or
@@ -1502,3 +1614,690 @@ icfg_str_to_sockaddr(sa_family_t af, const char *straddr,
return (ret);
}
+
+/*
+ * Adds the IP address contained in the 'addr' argument to the physical
+ * interface represented by the handle passed in. At present,
+ * additional IP addresses assigned to a physical interface are
+ * represented as logical interfaces.
+ *
+ * If the 'handle' argument is a handle to a physical interface, a logical
+ * interface will be created, named by the next unused logical unit number
+ * for that physical interface.
+ *
+ * If the 'handle' argument is a handle to an logical interface, then that
+ * logical interface is the one that will be created. If the logical
+ * interface model is abandoned in the future, passing in a logical
+ * interface name should result in ICFG_UNSUPPORTED being returned.
+ *
+ * If the 'new_handle' argument is not NULL, then a handle is created for the
+ * new IP address alias and returned to the caller via 'new_handle'.
+ * At present this handle refers to a logical interface, but in the future
+ * it may represent an IP address alias, and be used for setting/retrieving
+ * address-related information only.
+ *
+ *
+ * Returns: ICFG_SUCCESS, ICFG_BAD_ADDR, ICFG_DAD_FOUND, ICFG_EXISTS
+ * or ICFG_FAILURE.
+ */
+int
+icfg_add_addr(icfg_handle_t handle, icfg_handle_t *new_handle,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ struct lifreq lifr;
+ size_t addrsize;
+ int ret = ICFG_SUCCESS;
+ icfg_handle_t loc_handle;
+
+ if (addr->sa_family != icfg_if_protocol(handle))
+ return (ICFG_BAD_ADDR);
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ addrsize = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrsize = sizeof (struct sockaddr_in6);
+ break;
+ default:
+ return (ICFG_BAD_ADDR);
+ }
+
+ if (addrlen < addrsize) {
+ errno = ENOSPC;
+ return (ICFG_FAILURE);
+ }
+
+ /*
+ * See comments in ifconfig.c as to why this dance is necessary.
+ */
+ (void) memset(&lifr, 0, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
+ sizeof (lifr.lifr_name));
+
+ if (ioctl(handle->ifh_sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0) {
+ if (errno == EEXIST)
+ return (ICFG_EXISTS);
+ else
+ return (ICFG_FAILURE);
+ }
+
+ /* Create the handle for the new interface name. */
+ ret = icfg_open(&loc_handle, &(handle->ifh_interface));
+ if (ret != ICFG_SUCCESS) {
+ return (ret);
+ }
+ (void) strlcpy(loc_handle->ifh_interface.if_name,
+ lifr.lifr_name,
+ sizeof (loc_handle->ifh_interface.if_name));
+
+ if (addr != NULL)
+ ret = icfg_set_addr(loc_handle, addr, addrsize);
+
+ if (new_handle != NULL)
+ *new_handle = loc_handle;
+ else
+ icfg_close(loc_handle);
+
+ return (ret);
+}
+
+/*
+ * Removes specified IP address alias from physical interface. If the
+ * If the 'handle' argument is a handle to a physical interface, then
+ * the address alias removed must be specified by 'addr'. If the
+ * 'handle' argument is a handle for an IP address alias (currently
+ * represented as a logical interface), then that address alias
+ * (logical interface) is removed and the 'addr' argument is ignored.
+ *
+ * Under the logical interface model, an interface may only be removed
+ * if the interface is 'down'.
+ *
+ * Returns: ICFG_SUCCESS, ICFG_BAD_ADDR, ICFG_IF_UP, ICFG_NO_EXIST,
+ * or ICFG_FAILURE.
+ */
+int
+icfg_remove_addr(icfg_handle_t handle, const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct lifreq lifr;
+ size_t addrsize;
+
+ switch (icfg_if_protocol(handle)) {
+ case AF_INET:
+ addrsize = sizeof (struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addrsize = sizeof (struct sockaddr_in6);
+ break;
+ default:
+ return (ICFG_BAD_ADDR);
+ }
+
+ if (addr != NULL) {
+ if (addrlen < addrsize) {
+ errno = ENOSPC;
+ return (ICFG_FAILURE);
+ }
+ (void) memcpy(&lifr.lifr_addr, addr, addrsize);
+ } else {
+ (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+ }
+
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
+ sizeof (lifr.lifr_name));
+
+ if (ioctl(handle->ifh_sock, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0)
+ return (ICFG_FAILURE);
+
+ return (ICFG_SUCCESS);
+}
+
+/*
+ * Wrapper for sending a nontransparent I_STR ioctl().
+ * Returns: Result from ioctl().
+ *
+ * Same as in usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c
+ */
+static int
+strioctl(int s, int cmd, char *buf, int buflen)
+{
+ struct strioctl ioc;
+
+ (void) memset(&ioc, 0, sizeof (ioc));
+ ioc.ic_cmd = cmd;
+ ioc.ic_timout = 0;
+ ioc.ic_len = buflen;
+ ioc.ic_dp = buf;
+
+ return (ioctl(s, I_STR, (char *)&ioc));
+}
+
+/*
+ * Open a stream on /dev/udp{,6}, pop off all undesired modules (note that
+ * the user may have configured autopush to add modules above
+ * udp), and push the arp module onto the resulting stream.
+ * This is used to make IP+ARP be able to atomically track the muxid
+ * for the I_PLINKed STREAMS, thus it isn't related to ARP running the ARP
+ * protocol.
+ *
+ * Same as in usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c
+ */
+static int
+open_arp_on_udp(char *udp_dev_name)
+{
+ int fd;
+
+ if ((fd = open(udp_dev_name, O_RDWR)) == -1)
+ return (-1);
+
+ errno = 0;
+ while (ioctl(fd, I_POP, 0) != -1)
+ ;
+
+ if (errno == EINVAL && ioctl(fd, I_PUSH, ARP_MOD_NAME) != -1)
+ return (fd);
+
+ (void) close(fd);
+ return (-1);
+}
+
+/*
+ * We need to plink both the arp-device stream and the arp-ip-device stream.
+ * However the muxid is stored only in IP. Plumbing 2 streams individually
+ * is not atomic, and if ifconfig is killed, the resulting plumbing can
+ * be inconsistent. For eg. if only the arp stream is plumbed, we have lost
+ * the muxid, and the half-baked plumbing can neither be unplumbed nor
+ * replumbed, thus requiring a reboot. To avoid the above the following
+ * scheme is used.
+ *
+ * We ask IP to enforce atomicity of plumbing the arp and IP streams.
+ * This is done by pushing arp on to the mux (/dev/udp). ARP adds some
+ * extra information in the I_PLINK and I_PUNLINK ioctls to let IP know
+ * that the plumbing/unplumbing has to be done atomically. Ifconfig plumbs
+ * the IP stream first, and unplumbs it last. The kernel (IP) does not
+ * allow IP stream to be unplumbed without unplumbing arp stream. Similarly
+ * it does not allow arp stream to be plumbed before IP stream is plumbed.
+ * There is no need to use SIOCSLIFMUXID, since the whole operation is atomic,
+ * and IP uses the info in the I_PLINK message to get the muxid.
+ *
+ * a. STREAMS does not allow us to use /dev/ip itself as the mux. So we use
+ * /dev/udp{,6}.
+ * b. SIOCGLIFMUXID returns the muxid corresponding to the V4 or V6 stream
+ * depending on the open i.e. V4 vs V6 open. So we need to use /dev/udp
+ * or /dev/udp6 for SIOCGLIFMUXID and SIOCSLIFMUXID.
+ * c. We need to push ARP in order to get the required kernel support for
+ * atomic plumbings. The actual work done by ARP is explained in arp.c
+ * Without pushing ARP, we will still be able to plumb/unplumb. But
+ * it is not atomic, and is supported by the kernel for backward
+ * compatibility for other utilities like atmifconfig etc. In this case
+ * the utility must use SIOCSLIFMUXID.
+ *
+ * Returns: ICFG_SUCCESS, ICFG_EXISTS, ICFG_BAD_ADDR, ICFG_FAILURE,
+ * ICFG_DLPI_*, ICFG_NO_PLUMB_IP, ICFG_NO_PLUMB_ARP,
+ * ICFG_NO_UNPLUMB_ARP
+ */
+int
+icfg_plumb(icfg_handle_t handle)
+{
+ int ip_muxid;
+ int mux_fd, ip_fd, arp_fd;
+ uint_t ppa;
+ char *udp_dev_name;
+ char provider[DLPI_LINKNAME_MAX];
+ dlpi_handle_t dh_arp, dh_ip;
+ struct lifreq lifr;
+ int dlpi_ret, ret = ICFG_SUCCESS;
+ int saved_errno; /* to set errno after close() */
+ int dh_arp_ret; /* to track if dh_arp was successfully opened */
+ zoneid_t zoneid;
+
+ /* Logical and loopback interfaces are just added */
+ if (icfg_is_loopback(handle) || icfg_is_logical(handle))
+ return (icfg_add_addr(handle, NULL, NULL, 0));
+
+ /*
+ * If we're running in the global zone, we need to
+ * make sure this link is actually assigned to us.
+ *
+ * This is not an issue if we are not in the global
+ * zone, as we simply can't see links we don't own.
+ */
+ zoneid = getzoneid();
+ if (zoneid == GLOBAL_ZONEID) {
+ dladm_handle_t dlh;
+ dladm_status_t status;
+ datalink_id_t linkid;
+
+ if (dladm_open(&dlh) != DLADM_STATUS_OK)
+ return (ICFG_FAILURE);
+ status = dladm_name2info(dlh, icfg_if_name(handle), &linkid,
+ NULL, NULL, NULL);
+ dladm_close(dlh);
+ if (status != DLADM_STATUS_OK)
+ return (ICFG_INVALID_ARG);
+ zoneid = ALL_ZONES;
+ if (zone_check_datalink(&zoneid, linkid) == 0)
+ return (ICFG_INVALID_ARG);
+ }
+
+ /*
+ * We use DLPI_NOATTACH because the ip module will do the attach
+ * itself for DLPI style-2 devices.
+ */
+ if ((dlpi_ret = dlpi_open(icfg_if_name(handle), &dh_ip,
+ DLPI_NOATTACH)) != DLPI_SUCCESS) {
+ return (dlpi_error_to_icfg_error(dlpi_ret));
+ }
+ if ((dlpi_ret = dlpi_parselink(icfg_if_name(handle), provider,
+ &ppa)) != DLPI_SUCCESS) {
+ ret = dlpi_error_to_icfg_error(dlpi_ret);
+ goto done;
+ }
+
+ ip_fd = dlpi_fd(dh_ip);
+ if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
+ ret = ICFG_NO_PLUMB_IP;
+ goto done;
+ }
+
+ /*
+ * Push the ARP module onto the interface stream. IP uses
+ * this to send resolution requests up to ARP. We need to
+ * do this before the SLIFNAME ioctl is sent down because
+ * the interface becomes publicly known as soon as the SLIFNAME
+ * ioctl completes. Thus some other process trying to bring up
+ * the interface after SLIFNAME but before we have pushed ARP
+ * could hang. We pop the module again later if it is not needed.
+ */
+ if (ioctl(ip_fd, I_PUSH, ARP_MOD_NAME) == -1) {
+ ret = ICFG_NO_PLUMB_ARP;
+ goto done;
+ }
+
+ /*
+ * Set appropriate IFF flags. The kernel only allows us to
+ * modify IFF_IPv[46], IFF_BROADCAST, and IFF_XRESOLV in the
+ * SIOCSLIFNAME ioctl call; so we only need to set the ones
+ * from that set that we care about.
+ */
+ if (icfg_if_protocol(handle) == AF_INET6)
+ lifr.lifr_flags = IFF_IPV6;
+ else
+ lifr.lifr_flags = IFF_IPV4 | IFF_BROADCAST;
+
+ /* record the device and module names as interface name */
+ lifr.lifr_ppa = ppa;
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
+ sizeof (lifr.lifr_name));
+
+ /* set the interface name */
+ if (ioctl(ip_fd, SIOCSLIFNAME, (char *)&lifr) == -1) {
+ if (errno == EALREADY)
+ ret = ICFG_EXISTS;
+ else
+ ret = ICFG_NO_PLUMB_IP;
+ goto done;
+ }
+
+ /* Get the full set of existing flags for this stream */
+ if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1) {
+ if (errno == ENXIO)
+ ret = ICFG_NO_EXIST;
+ else
+ ret = ICFG_FAILURE;
+ goto done;
+ }
+
+ /* Check if arp is not actually needed */
+ if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) {
+ if (ioctl(ip_fd, I_POP, 0) == -1) {
+ ret = ICFG_NO_UNPLUMB_ARP;
+ goto done;
+ }
+ }
+
+ /*
+ * Open "/dev/udp" for use as a multiplexor to PLINK the
+ * interface stream under. We use "/dev/udp" instead of "/dev/ip"
+ * since STREAMS will not let you PLINK a driver under itself,
+ * and "/dev/ip" is typically the driver at the bottom of
+ * the stream for tunneling interfaces.
+ */
+ if (icfg_if_protocol(handle) == AF_INET6)
+ udp_dev_name = UDP6_DEV_NAME;
+ else
+ udp_dev_name = UDP_DEV_NAME;
+
+ if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1) {
+ ret = ICFG_NO_PLUMB_ARP;
+ goto done;
+ }
+
+ /* Check if arp is not needed */
+ if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) {
+ /*
+ * PLINK the interface stream so that ifconfig can exit
+ * without tearing down the stream.
+ */
+ if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
+ ret = ICFG_NO_PLUMB_IP;
+ goto done;
+ }
+ (void) close(mux_fd);
+ dlpi_close(dh_ip);
+ return (ICFG_SUCCESS);
+ }
+
+ /*
+ * This interface does use ARP, so set up a separate stream
+ * from the interface to ARP.
+ *
+ * Note: modules specified by the user are pushed
+ * only on the interface stream, not on the ARP stream.
+ *
+ * We use DLPI_NOATTACH because the arp module will do the attach
+ * itself for DLPI style-2 devices.
+ */
+ if ((dh_arp_ret = dlpi_open(icfg_if_name(handle), &dh_arp,
+ DLPI_NOATTACH)) != DLPI_SUCCESS) {
+ ret = dlpi_error_to_icfg_error(dh_arp_ret);
+ goto done;
+ }
+
+ arp_fd = dlpi_fd(dh_arp);
+ if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
+ ret = ICFG_NO_PLUMB_ARP;
+ goto done;
+ }
+
+ /*
+ * Tell ARP the name and unit number for this interface.
+ * Note that arp has no support for transparent ioctls.
+ */
+ if (strioctl(arp_fd, SIOCSLIFNAME, (char *)&lifr,
+ sizeof (lifr)) == -1) {
+ ret = ICFG_NO_PLUMB_ARP;
+ goto done;
+ }
+ /*
+ * PLINK the IP and ARP streams so that ifconfig can exit
+ * without tearing down the stream.
+ */
+ if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
+ ret = ICFG_NO_PLUMB_IP;
+ goto done;
+ }
+
+ if (ioctl(mux_fd, I_PLINK, arp_fd) == -1) {
+ (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
+ ret = ICFG_NO_PLUMB_ARP;
+ }
+
+done:
+ /* dlpi_close() may change errno, so save it */
+ saved_errno = errno;
+
+ dlpi_close(dh_ip);
+ if (dh_arp_ret == DLPI_SUCCESS)
+ dlpi_close(dh_arp);
+
+ if (mux_fd != -1)
+ (void) close(mux_fd);
+ if (ret != ICFG_SUCCESS)
+ errno = saved_errno;
+
+ return (ret);
+}
+
+static boolean_t
+ifaddr_down(ifaddrlistx_t *ifaddrp)
+{
+ icfg_handle_t addrh;
+ icfg_if_t addrif;
+ uint64_t addrflags;
+ boolean_t ret;
+
+ addrif.if_protocol = ifaddrp->ia_flags & IFF_IPV6 ? AF_INET6 : AF_INET;
+ (void) strlcpy(addrif.if_name, ifaddrp->ia_name,
+ sizeof (addrif.if_name));
+ if (icfg_open(&addrh, &addrif) != ICFG_SUCCESS)
+ return (B_FALSE);
+
+ if (icfg_get_flags(addrh, &addrflags) != ICFG_SUCCESS)
+ return (B_FALSE);
+
+ addrflags &= ~IFF_UP;
+ if (icfg_set_flags(addrh, addrflags) != ICFG_SUCCESS) {
+ ret = B_FALSE;
+ goto done;
+ }
+
+ /*
+ * Make sure that DAD activity (observable by IFF_DUPLICATE)
+ * has also been stopped. If we were successful in downing
+ * the address, the get_flags will fail, as the addr will no
+ * longer exist.
+ */
+ if ((icfg_get_flags(addrh, &addrflags) == ICFG_SUCCESS) &&
+ addrflags & IFF_DUPLICATE) {
+ struct sockaddr_storage ss;
+ socklen_t alen = sizeof (ss);
+ int plen;
+ /*
+ * getting/setting the address resets DAD; and since
+ * we've already turned off IFF_UP, DAD will remain
+ * disabled.
+ */
+ if ((icfg_get_addr(addrh, (struct sockaddr *)&ss, &alen, &plen,
+ B_FALSE) != ICFG_SUCCESS) ||
+ (icfg_set_addr(addrh, (struct sockaddr *)&ss, alen)
+ != ICFG_SUCCESS)) {
+ ret = B_FALSE;
+ goto done;
+ }
+ }
+ ret = B_TRUE;
+done:
+ icfg_close(addrh);
+ return (ret);
+}
+
+/*
+ * If this is a physical interface then remove it.
+ * If it is a logical interface name use SIOCLIFREMOVEIF to
+ * remove it. In both cases fail if it doesn't exist.
+ *
+ * Returns: ICFG_SUCCESS, ICFG_EXISTS, ICFG_NO_EXIST, ICFG_BAD_ADDR,
+ * ICFG_FAILURE, ICFG_NO_UNPLUMB_IP, ICFG_NO_UNPLUMB_ARP,
+ * ICFG_INVALID_ARG, ICFG_NO_IP_MUX
+ *
+ * Same as inetunplumb() in usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c
+ */
+int
+icfg_unplumb(icfg_handle_t handle)
+{
+ int ip_muxid, arp_muxid;
+ int mux_fd;
+ int muxid_fd;
+ char *udp_dev_name;
+ uint64_t flags;
+ boolean_t changed_arp_muxid = B_FALSE;
+ int save_errno;
+ struct lifreq lifr;
+ int ret = ICFG_SUCCESS;
+ boolean_t v6 = (icfg_if_protocol(handle) == AF_INET6);
+
+ /* Make sure interface exists to start with */
+ if ((ret = icfg_get_flags(handle, &flags)) != ICFG_SUCCESS) {
+ return (ret);
+ }
+
+ if (icfg_is_loopback(handle) || icfg_is_logical(handle)) {
+ char *strptr = strchr(icfg_if_name(handle), ICFG_LOGICAL_SEP);
+
+ /* Can't unplumb logical interface zero */
+ if (strptr != NULL && strcmp(strptr, ":0") == 0)
+ return (ICFG_INVALID_ARG);
+
+ return (icfg_remove_addr(handle, NULL, 0));
+ }
+
+ /*
+ * We used /dev/udp or udp6 to set up the mux. So we have to use
+ * the same now for PUNLINK also.
+ */
+ if (v6)
+ udp_dev_name = UDP6_DEV_NAME;
+ else
+ udp_dev_name = UDP_DEV_NAME;
+
+ if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1)
+ return (ICFG_NO_UNPLUMB_ARP);
+
+ if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1) {
+ ret = ICFG_NO_UNPLUMB_ARP;
+ goto done;
+ }
+
+ (void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
+ sizeof (lifr.lifr_name));
+ if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ ret = ICFG_FAILURE;
+ goto done;
+ }
+ flags = lifr.lifr_flags;
+
+ /*
+ * libinetcfg's only current consumer is nwamd; and we expect it to
+ * be replaced before any other consumers come along. NWAM does not
+ * currently support creation of IPMP groups, so icfg_plumb(), for
+ * example, does not do any IPMP-specific handling. However, it's
+ * possible nwamd might need to unplumb an IPMP group, so we include
+ * IPMP group handling here.
+ */
+again:
+ if (flags & IFF_IPMP) {
+ lifgroupinfo_t lifgr;
+ ifaddrlistx_t *ifaddrs, *ifaddrp;
+
+ /*
+ * There are two reasons the I_PUNLINK can fail with EBUSY:
+ * (1) if IP interfaces are in the group, or (2) if IPMP data
+ * addresses are administratively up. For case (1), we fail
+ * here with a specific error message. For case (2), we bring
+ * down the addresses prior to doing the I_PUNLINK. If the
+ * I_PUNLINK still fails with EBUSY then the configuration
+ * must have changed after our checks, in which case we branch
+ * back up to `again' and rerun this logic. The net effect is
+ * that unplumbing an IPMP interface will only fail with EBUSY
+ * if IP interfaces are in the group.
+ */
+ ret = icfg_get_groupname(handle, lifgr.gi_grname, LIFGRNAMSIZ);
+ if (ret != ICFG_SUCCESS)
+ return (ret);
+
+ ret = icfg_get_groupinfo(handle, &lifgr);
+ if (ret != ICFG_SUCCESS)
+ return (ret);
+
+ /* make sure the group is empty */
+ if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0))
+ return (ICFG_INVALID_ARG);
+
+ /*
+ * The kernel will fail the I_PUNLINK if the IPMP interface
+ * has administratively up addresses; bring 'em down.
+ */
+ if (ifaddrlistx(icfg_if_name(handle), IFF_UP|IFF_DUPLICATE,
+ 0, &ifaddrs) == -1)
+ return (ICFG_FAILURE);
+
+ ifaddrp = ifaddrs;
+ for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
+ if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
+ (!(ifaddrp->ia_flags && IFF_IPV6) && v6))
+ continue;
+
+ if (!ifaddr_down(ifaddrp)) {
+ ifaddrlistx_free(ifaddrs);
+ return (ICFG_FAILURE);
+ }
+ }
+ ifaddrlistx_free(ifaddrs);
+ }
+
+ if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ ret = ICFG_NO_IP_MUX;
+ goto done;
+ }
+ arp_muxid = lifr.lifr_arp_muxid;
+ ip_muxid = lifr.lifr_ip_muxid;
+ /*
+ * We don't have a good way of knowing whether the arp stream is
+ * plumbed. We can't rely on IFF_NOARP because someone could
+ * have turned it off later using "ifconfig xxx -arp".
+ */
+ if (arp_muxid != 0) {
+ if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
+ /*
+ * See the comment before the icfg_get_groupname() call.
+ */
+ if (errno == EBUSY && (flags & IFF_IPMP))
+ goto again;
+
+ if ((errno == EINVAL) &&
+ (flags & (IFF_NOARP | IFF_IPV6))) {
+ /*
+ * Some plumbing utilities set the muxid to
+ * -1 or some invalid value to signify that
+ * there is no arp stream. Set the muxid to 0
+ * before trying to unplumb the IP stream.
+ * IP does not allow the IP stream to be
+ * unplumbed if it sees a non-null arp muxid,
+ * for consistency of IP-ARP streams.
+ */
+ lifr.lifr_arp_muxid = 0;
+ (void) ioctl(muxid_fd, SIOCSLIFMUXID,
+ (caddr_t)&lifr);
+ changed_arp_muxid = B_TRUE;
+ } else {
+ ret = ICFG_NO_UNPLUMB_ARP;
+ }
+ }
+ }
+
+ if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
+ if (changed_arp_muxid) {
+ /*
+ * Some error occurred, and we need to restore
+ * everything back to what it was.
+ */
+ save_errno = errno;
+ lifr.lifr_arp_muxid = arp_muxid;
+ lifr.lifr_ip_muxid = ip_muxid;
+ (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
+ errno = save_errno;
+ }
+
+ /*
+ * See the comment before the icfg_get_groupname() call.
+ */
+ if (errno == EBUSY && (flags && IFF_IPMP))
+ goto again;
+
+ ret = ICFG_NO_UNPLUMB_IP;
+ }
+done:
+ /* close() may change errno, so save it */
+ save_errno = errno;
+
+ (void) close(muxid_fd);
+ if (mux_fd != -1)
+ (void) close(mux_fd);
+
+ if (ret != ICFG_SUCCESS)
+ errno = save_errno;
+
+ return (ret);
+}
diff --git a/usr/src/lib/libinetcfg/common/inetcfg.h b/usr/src/lib/libinetcfg/common/inetcfg.h
index fad1b9b0ca..5f3d8db0bf 100644
--- a/usr/src/lib/libinetcfg/common/inetcfg.h
+++ b/usr/src/lib/libinetcfg/common/inetcfg.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,15 +35,29 @@ extern "C" {
#endif
/* error codes */
-#define ICFG_SUCCESS 0 /* API was successful */
-#define ICFG_FAILURE 1 /* Generic failure */
-#define ICFG_NOT_SET 2 /* Could not return non-existent value */
-#define ICFG_BAD_ADDR 3 /* Invalid address */
-#define ICFG_BAD_PROT 4 /* Wrong protocol family for operation */
-#define ICFG_DAD_FAILED 5 /* Duplicate address detection failure */
-#define ICFG_DAD_FOUND 6 /* Duplicate address detected */
-
-#define ICFG_NERR (ICFG_DAD_FOUND + 1)
+typedef enum {
+ ICFG_SUCCESS, /* No error occurred */
+ ICFG_FAILURE, /* Generic failure */
+ ICFG_NO_MEMORY, /* Insufficient memory */
+ ICFG_NOT_TUNNEL, /* Tunnel operation attempted on non-tunnel */
+ ICFG_NOT_SET, /* Could not return non-existent value */
+ ICFG_BAD_ADDR, /* Invalid address */
+ ICFG_BAD_PROTOCOL, /* Wrong protocol family for operation */
+ ICFG_DAD_FAILED, /* Duplicate address detection failure */
+ ICFG_DAD_FOUND, /* Duplicate address detected */
+ ICFG_IF_UP, /* Interface is up */
+ ICFG_EXISTS, /* Interface already exists */
+ ICFG_NO_EXIST, /* Interface does not exist */
+ ICFG_INVALID_ARG, /* Invalid argument */
+ ICFG_INVALID_NAME, /* Invalid name */
+ ICFG_DLPI_INVALID_LINK, /* Invalid DLPI link */
+ ICFG_DLPI_FAILURE, /* Generic DLPI failure */
+ ICFG_NO_PLUMB_IP, /* Could not plumb IP stream */
+ ICFG_NO_PLUMB_ARP, /* Could not plumb ARP stream */
+ ICFG_NO_UNPLUMB_IP, /* Could not unplumb IP stream */
+ ICFG_NO_UNPLUMB_ARP, /* Could not unplumb ARP stream */
+ ICFG_NO_IP_MUX /* No IP mux set on the interface */
+} icfg_error_t;
/* valid types for icfg_get_if_list() */
#define ICFG_PLUMBED 0
@@ -59,9 +73,18 @@ typedef struct icfg_handle {
icfg_if_t ifh_interface; /* interface definition */
} *icfg_handle_t;
+/* retrieve error string */
extern const char *icfg_errmsg(int);
+
+/* handle functions */
extern int icfg_open(icfg_handle_t *, const icfg_if_t *);
extern void icfg_close(icfg_handle_t);
+extern boolean_t icfg_is_logical(icfg_handle_t);
+
+/* get interface name */
+extern const char *icfg_if_name(icfg_handle_t);
+
+/* set interface properties */
extern int icfg_set_flags(icfg_handle_t, uint64_t);
extern int icfg_set_metric(icfg_handle_t, int);
extern int icfg_set_mtu(icfg_handle_t, uint_t);
@@ -75,6 +98,8 @@ extern int icfg_set_subnet(icfg_handle_t, const struct sockaddr *, socklen_t,
int);
extern int icfg_set_dest_addr(icfg_handle_t, const struct sockaddr *,
socklen_t);
+
+/* get interface properties */
extern int icfg_get_addr(icfg_handle_t, struct sockaddr *, socklen_t *, int *,
boolean_t);
extern int icfg_get_token(icfg_handle_t, struct sockaddr_in6 *, int *,
@@ -89,16 +114,25 @@ extern int icfg_get_flags(icfg_handle_t, uint64_t *);
extern int icfg_get_metric(icfg_handle_t, int *);
extern int icfg_get_mtu(icfg_handle_t, uint_t *);
extern int icfg_get_index(icfg_handle_t, int *);
+
+/* retrieve interface list or iterate over all interface lists */
extern int icfg_get_if_list(icfg_if_t **, int *, int, int);
extern void icfg_free_if_list(icfg_if_t *);
extern int icfg_iterate_if(int, int, void *, int (*)(icfg_if_t *, void *));
-extern boolean_t icfg_is_logical(icfg_handle_t);
-extern int icfg_get_linkinfo(icfg_handle_t, lif_ifinfo_req_t *);
+
extern int icfg_sockaddr_to_str(sa_family_t, const struct sockaddr *,
char *, size_t);
extern int icfg_str_to_sockaddr(sa_family_t, const char *, struct sockaddr *,
socklen_t *);
+/* plumb or unplumb interfaces, add or remove IP */
+extern int icfg_add_addr(icfg_handle_t, icfg_handle_t *,
+ const struct sockaddr *, socklen_t);
+extern int icfg_remove_addr(icfg_handle_t, const struct sockaddr *, socklen_t);
+
+extern int icfg_plumb(icfg_handle_t);
+extern int icfg_unplumb(icfg_handle_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libinetcfg/common/llib-linetcfg b/usr/src/lib/libinetcfg/common/llib-linetcfg
index 4f047af714..e7c3e33679 100644
--- a/usr/src/lib/libinetcfg/common/llib-linetcfg
+++ b/usr/src/lib/libinetcfg/common/llib-linetcfg
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/* LINTLIBRARY */
/* PROTOLIB1 */
diff --git a/usr/src/lib/libinetcfg/common/mapfile-vers b/usr/src/lib/libinetcfg/common/mapfile-vers
index 3ecc82bd63..c331872916 100644
--- a/usr/src/lib/libinetcfg/common/mapfile-vers
+++ b/usr/src/lib/libinetcfg/common/mapfile-vers
@@ -19,10 +19,9 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-
#
# MAPFILE HEADER START
#
@@ -39,6 +38,7 @@
SUNWprivate_1.1 {
global:
+ icfg_add_addr;
icfg_close;
icfg_errmsg;
icfg_free_if_list;
@@ -54,9 +54,12 @@ SUNWprivate_1.1 {
icfg_get_netmask;
icfg_get_subnet;
icfg_get_token;
+ icfg_if_name;
icfg_is_logical;
icfg_iterate_if;
icfg_open;
+ icfg_plumb;
+ icfg_remove_addr;
icfg_set_addr;
icfg_set_broadcast;
icfg_set_dest_addr;
@@ -70,6 +73,7 @@ SUNWprivate_1.1 {
icfg_set_token;
icfg_sockaddr_to_str;
icfg_str_to_sockaddr;
+ icfg_unplumb;
local:
*;
};
diff --git a/usr/src/lib/libnwam/Makefile b/usr/src/lib/libnwam/Makefile
index 9110a09447..9257f62f25 100644
--- a/usr/src/lib/libnwam/Makefile
+++ b/usr/src/lib/libnwam/Makefile
@@ -19,15 +19,24 @@
# CDDL HEADER END
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib
-HDRS = libnwam.h
+HDRS = libnwam.h libnwam_priv.h
+SRCS = libnwam_audit.c libnwam_backend.c libnwam_enm.c \
+ libnwam_events.c libnwam_error.c libnwam_files.c \
+ libnwam_known_wlan.c libnwam_loc.c libnwam_ncp.c \
+ libnwam_object.c libnwam_util.c libnwam_values.c \
+ libnwam_wlan.c
HDRDIR = common
-SUBDIRS = $(MACH)
+SUBDIRS= $(MACH)
+POFILE = libnwam.po
+MSGFILES = common/*.c
+
+XGETFLAGS = -a -x libnwam.xcl
all := TARGET = all
clean := TARGET = clean
@@ -41,7 +50,11 @@ all clean clobber install lint: $(SUBDIRS)
install_h: $(ROOTHDRS)
-check:
+check: $(CHECKHDRS)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
$(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
diff --git a/usr/src/lib/libnwam/Makefile.com b/usr/src/lib/libnwam/Makefile.com
index f05345922d..e193da49d5 100644
--- a/usr/src/lib/libnwam/Makefile.com
+++ b/usr/src/lib/libnwam/Makefile.com
@@ -19,34 +19,42 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-LIBRARY = libnwam.a
-VERS = .1
-OBJECTS = door.o
+LIBRARY= libnwam.a
+VERS= .1
+OBJECTS= libnwam_audit.o \
+ libnwam_backend.o \
+ libnwam_enm.o \
+ libnwam_events.o \
+ libnwam_error.o \
+ libnwam_files.o \
+ libnwam_known_wlan.o \
+ libnwam_loc.o \
+ libnwam_ncp.o \
+ libnwam_object.o \
+ libnwam_util.o \
+ libnwam_values.o \
+ libnwam_wlan.o
include ../../Makefile.lib
include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lbsm -lc -ldladm -lnsl -lnvpair -lscf -lsecdb -lsocket
-NWAMDIR= $(SRC)/cmd/cmd-inet/lib/nwamd
SRCDIR = ../common
-SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c)
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
-$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC)
-
-LDLIBS += -ldladm -lc
-
-CFLAGS += $(CCVERBOSE)
-CPPFLAGS += -I$(SRCDIR) -I$(NWAMDIR)
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRCDIR) -D_REENTRANT
.KEEP_STATE:
-all: $(LIBS)
+all: $(LIBS)
-lint: lintcheck
+lint: lintcheck
-include ../../Makefile.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libnwam/README b/usr/src/lib/libnwam/README
new file mode 100644
index 0000000000..01cdc74b75
--- /dev/null
+++ b/usr/src/lib/libnwam/README
@@ -0,0 +1,536 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# lib/libnwam/README
+#
+
+NAME
+ libnwam - Network Auto-Magic (NWAM) configuration and event
+ management library
+
+DESCRIPTION
+
+The libnwam library is provided so that the various consumers of
+Network Auto-Magic (NWAM) configuration information - i.e. the NWAM
+GUI, the nwamcfg CLI and the NWAM daemon - have a consistent interface
+for retrieving and updating NWAM-related configuration data, abstracted
+from the actual manner in which the data is persistently stored. It
+provides functionality to interact with the key components of NWAM
+configuration, as described below. Additionally the library provides
+functionality for system events to flow from the NWAM daemon to a
+client (like the GUI panel applet).
+
+Each of these configuration components is referred to as an 'entity'.
+
+Network Configuration Units (NCUs): units that specify either link or
+interface (IP) configuration. An NCP consists of a set of NCUs, one for
+each datalink (physical, tunnel, aggregation etc), and one for each IP
+interface.
+
+Network Configuration Profiles (NCPs): A network configuration profile (NCP)
+comprises of a set of NCUs specifying configuration preferences to be applied
+when that profile is active.
+
+Locations: A location consists of additional configuration preferences
+to be applied when basic IP configuration is complete. Information
+such as name service specification, proxies, etc. can be specified.
+
+External Network Modifiers (ENMs): units that specify an external service
+or executable that modifies the network configuration. Properties of an
+ENM include an FMRI or Start and Stop exec strings, an optional environment
+which will be activated when the ENM is started, an activation type specifying
+when the ENM should be started (e.g. on user input, dependent on an NCU--
+either requiring or excluding a particular NCU, or always-on). Each ENM
+also has a read-only state property, which indicates whether or not the
+ENM has been activated by nwam.
+
+Known WiFi Networks (known WLANs): units that specify configuration
+data associated with known WiFi access points that the user visits. If
+a WLAN found by scanning is one of the known WLANs, NWAM will automatically
+connect. Priorities associated with known WLANs can also be manipulated
+allowing users to prioritize particular WLANs.
+
+Events
+
+The event interface allows a client of NWAM (usu. the GUI) to subscribe
+to a stream of system events such as link and interface up/down,
+wireless scans, and times when NWAM needs the user to be queried.
+
+Events types are in libnwam.h as NWAM_EVENTS_* and the actual bodies of
+the events are in nwam_events_msg_t. The semantics of the events have
+been simplified so that none require response. They are all
+notification.
+
+NWAM_EVENTS_SOURCE_{DEAD,BACK} provide notification that the nwam
+daemon has died or has reappeared. These are synthetic events in that
+they come from the library and not from nwamd.
+
+NWAM_EVENTS_NO_MAGIC provides notification that nwam couldn't make a
+determination of what to do without user guidance. This event provides
+information that can be used to ask the user if he wants to pick a
+wireless lan although in some cases nwam might have no idea what is
+intended.
+
+NWAM_EVENTS_IF_{STATE,REMOVED} provide information about changes in
+interface state or the removal of an interface.
+
+NWAM_EVENTS_LINK_{STATE, REMOVED} provides information about changes in
+link state or the removal of a link.
+
+NWAM_EVENTS_SCAN_REPORT is used to communicate information about
+wireless networks encountered.
+
+Persistent configuration state of entities is stored in project-private
+/etc/nwam configuration files, and the details of storage are hidden
+from libnwam consumers.
+
+Access to entities is attained via an entity-specific handle. These
+handles can be obtained via calls to nwam_<entity>_create() or
+nwam_<entity>_read(), and freed (in memory) via calls to nwam_<entity>_free().
+nwam_<entity>_create() will create an in-memory representation of the
+entity, but that entity will not be stored until nwam_<entity>_commit() is
+called. Persistently stored entitites are retrieved via nwam_<entity>_read(),
+can be modified in-memory, and later persistently committed to storage
+via nwam_<entity>_commit(). Entities can also be copied with
+nwam_<entity>_copy().
+
+All changes made after binding a handle to an entity, and prior to calling
+nwam_<entity>_commit() are carried out on the in-memory representation of that
+entity. nwam_<entity>_commit() updates persistent storage in an all-or-none
+transactional manner, applying the (possibly changed) in-memory representation
+to persistent storage atomically.
+
+To destroy an entity in persistent storage, nwam_<entity>_destroy() is
+used. This is distinct from nwam_<entity>_free(), which simply frees
+the in-memory representation - destroy both removes the object from
+persistent storage, and frees it in memory.
+
+To summarize, the pattern of interactions with an entity is
+ - nwam_<entity>_read(), nwam_<entity>_create() or nwam_<entity>_copy()
+ - possibly modify entity properties
+ - nwam_<entity>_commit() or nwam_<entity>_destroy()
+ - nwam_<entity>_handle_free()
+
+Unless otherwise stated functions in libnwam are MT-safe. The
+atomicity of _commit() and _read() operations is guaranteed - i.e.
+_commit() is guaranteed to deliver all property changes to persistent
+storage, while _read() is guaranteed to read a consistent view of the
+entity (i.e. _read() cannot collide with another entity _commit()ting
+changes). However, it is possible that a _read() will retrieve an
+outdated view of an entity, if after the _read() completes, another
+entity _commit()s changes. In other words, lost updates are possible.
+These are permitted given the nature of the entities changing NWAM
+configuration (the CLI and GUI) - it seems intuitive that the most
+recent update best captures user intent.
+
+Entity validation on an in-memory representation can be explicitly requested
+via a call to nwam_<entity>_validate(), and individual property values
+can be validated via nwam_<entity>_validate_prop().
+
+Storage and retrieval of properties is done via nwam_<entity>_set_prop_value()
+and nwam_<entity>_get_prop_value(). These functions require an nwam_value_t as
+a parameter, which is created via the nwam_value_create_<type>() family
+of functions. Data can be retrieved from the nwam_value_t via the
+nwam_value_get_<type>() family of functions. Once a property has been
+set, the associated nwam_value_t can be immediately freed. For retrieval,
+the nwam_value_t should be freed when the value(s) are no longer needed.
+A property can also be delete with nwam_<entity>_delete_prop().
+
+Implicit validation occurs as part of the nwam_<entity>_set_prop_value()
+and nwam_<entity>_commit() functions.
+
+INTERFACES
+
+For error handling:
+
+const char *nwam_strerror(nwam_error_t error);
+
+For values:
+
+Values can be any of the following types:
+
+ NWAM_VALUE_TYPE_BOOLEAN
+ NWAM_VALUE_TYPE_UINT64
+ NWAM_VALUE_TYPE_INT64
+ NWAM_VALUE_TYPE_STRING
+
+and are possibly multi-valued. An nwam_value_t must be created in order
+to set property values in libnwam, this is done via the follwing functions:
+
+nwam_error_t nwam_value_create_boolean(boolean_t, nwam_value_t *);
+nwam_error_t nwam_value_create_boolean_array(boolean_t *, uint_t,
+ nwam_value_t *);
+nwam_error_t nwam_value_create_uint64(uint64_t, nwam_value_t *);
+nwam_error_t nwam_value_create_uint64_array(uint64_t *, uint_t, nwam_value_t *);
+nwam_error_t nwam_value_create_int64(int64_t, nwam_value_t *);
+nwam_error_t nwam_value_create_int64_array(int64_t *, uint_t, nwam_value_t *);
+nwam_error_t nwam_value_create_string(char *, nwam_value_t *);
+nwam_error_t nwam_value_create_string_array(char **, uint_t, nwam_value_t *);
+
+Values are returned from the _get_prop_value() functions, and the data
+contained in them can be retrieved via the following functions:
+
+nwam_error_t nwam_value_get_boolean(nwam_value_t, boolean_t *);
+nwam_error_t nwam_value_get_boolean_array(nwam_value_t, boolean_t **, uint_t *);
+nwam_error_t nwam_value_get_uint64(nwam_value_t, uint64_t *);
+nwam_error_t nwam_value_get_uint64_array(nwam_value_t, uint64_t **, uint_t *);
+nwam_error_t nwam_value_get_int64(nwam_value_t, int64_t *);
+nwam_error_t nwam_value_get_int64_array(nwam_value_t, int64_t **, uint_t *);
+nwam_error_t nwam_value_get_string(nwam_value_t, char **);
+nwam_error_t nwam_value_get_string_array(nwam_value_t, char ***, uint_t *);
+
+Type and number of value can be retrieved via:
+
+nwam_error_t nwam_value_get_type(nwam_value_t, nwam_value_type_t *);
+nwam_error_t nwam_value_get_numvalues(nwam_value_t, uint_t *);
+
+and a value is freed using:
+
+void nwam_value_free(nwam_value_t);
+
+For property setting, a typical set of events is to create the value,
+call the appropriate set_prop_value() function, then free the value (values
+can be safely freed prior to commit). For retrieval, the type and
+number of values usually need to be tested before calling the appropriate
+retrieval function. In this case, the value should not be freed until
+the associated data is no longer needed.
+
+NCUs, locations and ENMs can all specify conditional activation conditions.
+Interfaces are provided to convert a conditional activation predicate into
+a string, or from a string to a parsed set of variables that comprise the
+condition. Additionally a function is provided to rate the specificity of
+the activation condition, used to compare location conditions to choose
+the most specific condition to activate in the case where the activation
+conditions of multiple locations are specified.
+
+nwam_error_t nwam_condition_to_condition_string(nwam_condition_object_type_t,
+ nwam_condition_t, const char *, char **);
+nwam_error_t nwam_condition_string_to_condition(const char *,
+ nwam_condition_object_type_t *, nwam_condition_t *, char **);
+nwam_error_t nwam_condition_rate(nwam_condition_object_type_t,
+ nwam_condition_t, uint64_t *);
+
+For NCP entities:
+
+nwam_error_t nwam_ncp_create(const char *name, uint64_t flags,
+ nwam_ncp_handle_t *ncp);
+nwam_error_t nwam_ncp_read(const char *name, uint64_t flags,
+ nwam_ncp_handle_t *ncp);
+nwam_error_t nwam_ncp_copy(nwam_ncp_handle_t ncp, const char *newname,
+ nwam_ncp_handle_t *newncp);
+nwam_error_t nwam_ncp_walk_ncus(nwam_ncp_handle_t ncp,
+ int(*cb)(nwam_ncu_handle_t, void *), void *data, uint64_t flags, int *ret);
+nwam_error_t nwam_ncp_get_name(nwam_ncp_handle_t ncp, char **name);
+nwam_error_t nwam_ncp_activate(nwam_ncp_handle_t ncp);
+nwam_error_t nwam_ncp_deactivate(nwam_ncp_handle_t ncp);
+nwam_error_t nwam_ncp_destroy(nwam_ncp_handle_t ncp, uint64_t flags);
+void nwam_ncp_free(nwam_ncp_handle_t ncp);
+
+Since the NCP simply consists of the NCUs that comprise it, there is
+no NCP-specific commit() function - we simply read the NCP, walk the
+constituent NCUs, reading, changing or committing them in turn. The
+walk can be modified via the flags option to only select specific NCU types
+and classes.
+
+Each NCP has a set of NCUs associated with it, each of which is created/modifed
+using the functions below.
+
+For NCU entities:
+
+nwam_error_t nwam_ncu_create(nwam_ncp_handle_t ncp, const char *name,
+ nwam_ncu_type_t type, nwam_ncu_class_t class, nwam_ncu_handle_t *ncu);
+nwam_error_t nwam_ncu_read(nwam_ncp_handle_t ncp, const char *name,
+ nwam_ncu_type_t type, uint64_t flags, nwam_ncu_handle_t *ncu);
+nwam_error_t nwam_ncu_copy(nwam_ncu_handle_t ncu, const char *newname,
+ nwam_ncu_handle_t *newncu);
+nwam_error_t nwam_ncu_commit(nwam_ncu_handle_t ncu, uint64_t flags);
+nwam_error_t nwam_ncu_destroy(nwam_ncu_handle_t ncu, uint64_t flags);
+nwam_error_t nwam_ncu_free(nwam_ncu_handle_t ncu);
+nwam_error_t nwam_ncu_validate(nwam_ncu_handle_t ncu, const char **errprop);
+nwam_error_t nwam_ncu_get_prop_value(nwam_ncu_handle_t ncu, const char *prop,
+ nwam_value_t *value);
+nwam_error_t nwam_ncu_get_prop_description(const char *prop,
+ const char **description);
+nwam_error_t nwam_ncu_delete_prop(nwam_ncu_handle_t ncu, const char *prop);
+nwam_error_t nwam_ncu_set_prop_value(nwam_ncu_handle_t ncu, const char *prop,
+ nwam_value_t value);
+nwam_error_t nwam_ncu_get_name(nwam_ncu_handle_t ncu, char **name);
+nwam_error_t nwam_ncu_set_name(nwam_ncu_handle_t ncu, const char *name);
+nwam_error_t nwam_ncu_get_read_only(nwam_ncu_handle_t ncu, boolean_t *readp);
+nwam_error_t nwam_ncu_validate_prop(nwam_ncu_handle_t ncu, const char *prop,
+ nwam_value_t value);
+nwam_error_t nwam_ncu_walk_props(nwam_ncu_handle_t ncu,
+ int (*func)(void *, const char *, nwam_value_t), void *data,
+ uint64_t flags, int *ret);
+nwam_error_t nwam_ncu_prop_get_type(const char *prop,
+ nwam_value_type_t *value_type);
+nwam_error_t nwam_ncu_get_ncp(nwam_ncu_handle_t ncu, nwam_ncp_handle_t *ncp);
+
+NCUs are manipulated via an nwam_ncu_handle_t.
+
+Each NCU has a set of properties associated with it. Each property can
+have mutiple values associated with it, which are set or retrieved via an
+nwam_value_t. The approach is similar to that used for Locations, with
+the difference that read/commit/destroy must specify an NCP. Only two
+NCPs are supported at present, the automatic and user NCPs. Modification
+of the former is restricted to nwamd itself, and attempts to modify
+the automatic NCP's consituent NCUs will result in an NWAM_ENTITY_READ_ONLY
+error.
+
+For Location entities:
+
+nwam_error_t nwam_loc_create(const char *name, nwam_loc_handle_t *loc);
+nwam_error_t nwam_loc_read(const char *name, uint64_t flags,
+ nwam_loc_handle_t *loc);
+nwam_error_t nwam_loc_copy(nwam_loc_handle_t loc, const char *newname,
+ nwam_loc_handle_t *newloc);
+nwam_error_t nwam_walk_locs(int (*cb)(nwam_loc_handle_t loc, void *arg),
+ void *arg, uint64_t flags, int *cbretp);
+nwam_error_t nwam_loc_commit(nwam_loc_handle_t loc, uint64_t flags);
+nwam_error_t nwam_loc_destroy(nwam_loc_handle_t loc, uint64_t flags);
+void nwam_loc_free(nwam_loc_handle_t loc);
+nwam_error_t nwam_loc_validate(nwam_loc_handle_t loc, const char *errprop);
+nwam_error_t nwam_loc_walk_props(nwam_loc_handle_t loc,
+ int (*cb)(const char *, nwam_value_t **, void *),
+ void *arg, uint64_t flags, int *cbret);
+nwam_error_t nwam_loc_validate_prop(nwam_loc_handle_t loc,
+ const char *prop, nwam_value_t value);
+nwam_error_t nwam_loc_prop_get_type(const char *prop,
+ nwam_value_type_t *value_type);
+nwam_error_t nwam_loc_get_prop_value(nwam_loc_handle_t loc, const char *prop,
+ nwam_value_t *value);
+nwam_error_t nwam_loc_get_prop_description(const char *prop,
+ const char **description);
+nwam_error_t nwam_loc_delete_prop(nwam_loc_handle_t loc, const char *prop);
+nwam_error_t nwam_loc_set_prop_value(nwam_loc_handle_t loc, const char *prop,
+ nwam_value_t value);
+nwam_error_t nwam_loc_get_name(nwam_loc_handle_t loc, char **name);
+nwam_error_t nwam_loc_set_name(nwam_loc_handle_t loc, const char *name);
+nwam_error_t nwam_loc_activate(nwam_loc_handle_t loc);
+nwam_error_t nwam_loc_deactivate(nwam_loc_handle_t loc);
+
+Locations are manipulated via an nwam_loc_handle_t.
+
+A loc handle maps to an in-memory representation of a location; operations via
+this interface manipulate the in-memory data. In-memory data is read from
+persistant storage via the nwam_loc_read() or nwam_walk_locs() functions, and
+written out to persistent storage via the nwam_loc_commit() function. A loc
+may be permanently removed from persistent storage with the nwam_loc_destroy()
+function. Interactions with persistent storage will be nonblocking by default;
+this behavior can be changed by passing the NWAM_FLAG_BLOCKING in the flags
+parameter.
+
+A typical sequence would be to allocate a loc handle, either by creating a
+new loc (nwam_loc_create()) or by reading one from persistent storage (nwam_
+loc_read() or nwam_walk_locs()). The various set/get/walk/validate/(de)activate
+functions may then be used to manipulate the loc; any changes made may then be
+committed to persistent storage via nwam_loc_commit(). A call to nwam_loc_
+free() is required to release in-memory resources associated with the handle.
+
+The flags parameter in the walk functions allows filtering of the locs that
+will be examined, depending on the state of each loc. Passing in
+NWAM_FLAG_STATE_ALL will examine all locs; specific state flags are defined
+in <libnwam.h>.
+
+Like NCUs, each loc has a set of properties associated with it. Loc properties
+are stored in nwam_value_t structures; see the Values section for how to store/
+retrieve using these.
+
+For ENM entities:
+
+nwam_error_t nwam_enm_create(const char *name, const char *fmri,
+ nwam_enm_handle_t *enm);
+nwam_error_t nwam_enm_read(const char *name, uint64_t flags,
+ nwam_enm_handle_t *enm);
+nwam_error_t nwam_enm_copy(nwam_enm_handle_t enm, const char *newname,
+ nwam_enm_handle_t *newenm);
+nwam_error_t nwam_walk_enms(int (*cb)(nwam_enm_handle_t enm, void *arg),
+ void *arg, uint64_t flags, int *cbretp);
+nwam_error_t nwam_enm_commit(nwam_enm_handle_t enm, uint64_t flags);
+nwam_error_t nwam_enm_destroy(nwam_enm_handle_t enm, uint64_t flags);
+void nwam_enm_free(nwam_enm_handle_t enm);
+nwam_error_t nwam_enm_validate(nwam_enm_handle_t enm, const char *errprop);
+nwam_error_t nwam_enm_walk_props(nwam_enm_handle_t enm,
+ int (*cb)(const char *, nwam_value_t **, void *),
+ void *arg, uint64_t flags, int *cbret);
+nwam_error_t nwam_enm_validate_prop(nwam_enm_handle_t enm,
+ const char *prop, nwam_value_t value);
+nwam_error_t nwam_enm_prop_get_type(const char *prop,
+ nwam_value_type_t *value_type);
+nwam_error_t nwam_enm_get_prop_value(nwam_enm_handle_t enm, const char *prop,
+ nwam_value_t *value);
+nwam_error_t nwam_enm_get_prop_description(const char *prop,
+ const char **description);
+nwam_error_t nwam_enm_delete_prop(nwam_enm_handle_t enm, const char *prop);
+nwam_error_t nwam_enm_set_prop_value(nwam_enm_handle_t enm, const char *prop,
+ nwam_value_t value);
+nwam_error_t nwam_enm_get_name(nwam_enm_handle_t enm, char **name);
+nwam_error_t nwam_enm_set_name(nwam_enm_handle_t enm, const char *name);
+nwam_error_t nwam_enm_activate(nwam_enm_handle_t enm);
+nwam_error_t nwam_enm_deactivate(nwam_enm_handle_t enm);
+
+ENMs are manipulated via an nwam_enm_handle_t, in a similar manner to
+NCUs and locations.
+
+The flags parameter in the walk functions allows filtering of the ENMs that
+will be examined, depending on the state of each ENM. Passing in
+NWAM_FLAG_STATE_ALL will examine all ENMs; specific state flags are defined
+in <libnwam.h>.
+
+Like NCUs, each ENM has a set of properties associated with it. ENM properties
+are all single valued, though the interface is aligned with the NCU interface,
+which allows for multi-valued properties. ENM properties are stored in
+nwam_value_t structures; see the Values section for how to store/retrieve
+using these.
+
+For known WLAN entities:
+
+nwam_error_t nwam_known_wlan_create(const char *name,
+ nwam_known_wlan_handle_t *kwhp);
+nwam_error_t nwam_known_wlan_read(const char *name, uint64_t flags,
+ nwam_known_wlan_handle_t *kwhp);
+nwam_error_t nwam_known_wlan_copy(nwam_known_wlan_handle_t kwh,
+ const char *newname, nwam_known_wlan_handle_t *newkwh);
+nwam_error_t nwam_walk_known_wlans(int (*cb)(nwam_known_wlan_handle_t, void *),
+ void *arg, uint64_t flags, int *cbretp);
+nwam_error_t nwam_known_wlan_commit(nwam_known_wlan_handle_t kwh,
+ uint64_t flags);
+nwam_error_t nwam_known_wlan_destroy(nwam_known_wlan_handle_t kwh,
+ uint64_t flags);
+void nwam_known_wlan_free(nwam_known_wlan_handle_t kwh);
+nwam_error_t nwam_known_wlan_validate(nwam_known_wlan_handle_t kwh,
+ const char *errprop);
+nwam_error_t nwam_known_wlan_walk_props(nwam_known_wlan_handle_t kwh,
+ int (*cb)(const char *, nwam_value_t **, void *),
+ void *arg, uint64_t flags, int *cbret);
+nwam_error_t nwam_known_wlan_validate_prop(nwam_known_wlan_handle_t kwh,
+ const char *prop, nwam_value_t value);
+nwam_error_t nwam_known_wlan_prop_get_type(const char *prop,
+ nwam_value_type_t *value_type);
+nwam_error_t nwam_known_wlan_get_prop_value(nwam_known_wlan_handle_t kwh,
+ const char *prop, nwam_value_t *value);
+nwam_error_t nwam_known_wlan_get_prop_description(const char *prop,
+ const char **description);
+nwam_error_t nwam_known_wlan_delete_prop(nwam_known_wlan_handle_t kwh,
+ const char *prop);
+nwam_error_t nwam_known_wlan_set_prop_value(nwam_known_wlan_handle_t kwh,
+ const char *prop, nwam_value_t value);
+nwam_error_t nwam_known_wlan_get_name(nwam_known_wlan_handle_t kwh,
+ char **name);
+nwam_error_t nwam_known_wlan_set_name(nwam_known_wlan_handle_t kwh,
+ const char *name);
+nwam_error_t nwam_known_wlan_add_to_known_wlan(const char *essid,
+ const char *bssid);
+nwam_error_t nwam_known_wlan_remove_from_known_wlan(const char *essid,
+ const char *bssid);
+
+Known WLANs are manipulated via an nwam_known_wlan_handle_t, in a similar
+manner to NCUs, locations and ENMs.
+
+Like ENMs, each known WLAN has a set of properties associated with it.
+Known WLAN properties are stored in nwam_value_t structures; see the Values
+section for how to store/retrieve using these.
+
+For WLANs, we define a set of functions to ask nwamd to initiate a scan,
+select a WLAN (and possibly add it to the known WLAN list) or set a WLAN
+key:
+
+
+extern nwam_error_t nwam_wlan_scan(const char *linkname);
+extern nwam_error_t nwam_wlan_get_scan_results(const char *linkname,
+ uint_t *num_resultsp, nwam_wlan_t **wlansp);
+extern nwam_error_t nwam_wlan_select(const char *linkname,
+ const char *essid, const char *bssid, boolean_t add_to_known_wlans);
+extern nwam_error_t nwam_wlan_set_key(const char *linkname, const char *essid,
+ const char *bssid, uint32_t security_mode, uint_t keyslot, const char *key);
+
+For Events:
+
+typedef struct nwam_event {
+ uint32_t type;
+
+ union {
+ struct {
+ nwam_object_type_t object_type;
+ char name[NWAM_MAX_NAME_LEN];
+ nwam_action_t action;
+ } object_action;
+
+
+ ... and so on for each message ...
+
+ } data;
+
+} *nwam_event_t;
+
+type comes from the set of constants NWAM_EVENT_TYPE_*.
+
+Registration and cancellation of registration are done via
+_init and _fini functions:
+
+extern nwam_error_t nwam_events_init(void);
+extern void nwam_events_fini(void);
+
+Events can then be recieved by calling nwam_event_wait():
+
+extern nwam_error_t nwam_event_wait(nwam_event_t *);
+
+The event can then be processed, and free via nwam_event_free();
+
+RETURN VALUES
+
+All functions return an nwam_error_t if they return an error. Possible
+errors are:
+
+ NWAM_SUCCESS No error occured
+ NWAM_LIST_END End of list
+ NWAM_INVALID_HANDLE Entity handle is invalid
+ NWAM_HANDLE_UNBOUND Handle not bound to entity
+ NWAM_INVALID_ARG Argument is invalid
+ NWAM_PERMISSION_DENIED Insufficient privileges for action
+ NWAM_NO_MEMORY Out of memory
+ NWAM_ENTITY_EXISTS Entity already exists
+ NWAM_ENTITY_IN_USE Another user is interacting with entity
+ NWAM_ENTITY_COMMITTED Entity already committed
+ NWAM_ENTITY_NOT_FOUND Entity not found
+ NWAM_ENTITY_TYPE_MISMATCH Entity value-type mismatch
+ NWAM_ENTITY_INVALID Validation of entity failed
+ NWAM_ENTITY_INVALID_MEMBER Entity member invalid
+ NWAM_ENTITY_INVALID_VALUE Validation of entity value failed
+ NWAM_ENTITY_NO_VALUE No value associated with entity
+ NWAM_ENTITY_MULTIPLE_VALUES, Multiple values for entity
+ NWAM_ENTITY_READ_ONLY, Entity is marked read only
+ NWAM_WALK_HALTED, Callback function returned nonzero
+ NWAM_ERROR_BIND, Could not bind to backend
+ NWAM_ERROR_BACKEND_INIT, Could not initialize backend
+ NWAM_ERROR_INTERNAL Internal error
+
+FILES
+ /lib/libnwam.so.1 shared object
+
+ATTRIBUTES
+
+
+SEE ALSO
+ nwamd(1M), nwamcfg(1M), nwamadm(1M)
diff --git a/usr/src/pkgdefs/SUNWnwamintr/Makefile b/usr/src/lib/libnwam/amd64/Makefile
index 0ff58fdf86..820bab63df 100644
--- a/usr/src/pkgdefs/SUNWnwamintr/Makefile
+++ b/usr/src/lib/libnwam/amd64/Makefile
@@ -18,18 +18,12 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
include ../Makefile.com
+include ../../Makefile.lib.64
-DATAFILES += depend
-
-.KEEP_STATE:
-
-all: $(FILES)
-
-install: all pkg
-
-include ../Makefile.targ
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libnwam/common/door.c b/usr/src/lib/libnwam/common/door.c
deleted file mode 100644
index 6feaff84d6..0000000000
--- a/usr/src/lib/libnwam/common/door.c
+++ /dev/null
@@ -1,758 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2009 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 <sys/mman.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_secmode(const char *ifname, const char *essid,
- const char *bssid, const char *key, const char *secmode)
-{
- 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));
- (void) strlcpy(cmd.ndc_secmode, secmode, sizeof (cmd.ndc_secmode));
- retv = make_door_call(&cmd, &cmd_ret, &cmd_size, &cmd_rsize);
- return (handle_errors(retv, cmd_ret, cmd_buf, cmd_size, cmd_rsize));
-}
-
-/* For compatibility with old nwam-manager binaries */
-int
-libnwam_wlan_key(const char *ifname, const char *essid, const char *bssid,
- const char *key)
-{
- return (libnwam_wlan_key_secmode(ifname, essid, bssid, key, ""));
-}
-
-/* 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
index 168beba2b4..b049d630c2 100644
--- a/usr/src/lib/libnwam/common/libnwam.h
+++ b/usr/src/lib/libnwam/common/libnwam.h
@@ -18,157 +18,1047 @@
*
* CDDL HEADER END
*/
-
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * This file contains data structures and APIs of libnwam.
+ * Implementation is MT safe.
+ */
#ifndef _LIBNWAM_H
#define _LIBNWAM_H
-#include <netinet/in.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <bsm/adt.h>
+#include <net/if.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <sys/types.h>
+#include <sys/socket.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.
+ * Note - several interface functions below are not utilized in ON, but are
+ * used by the GNOME nwam-manager. One example is nwam_enm_get_name().
*/
-#ifdef __cplusplus
-extern "C" {
-#endif
+/*
+ * Common definitions
+ */
+
+/* nwam FMRI and properties */
+#define NWAM_FMRI "svc:/network/physical:nwam"
+#define NWAM_PG "nwamd"
+#define NWAM_PROP_ACTIVE_NCP "active_ncp"
+
+/* nwam flags used for read/commit */
+/* Block waiting for commit if necessary */
+#define NWAM_FLAG_BLOCKING 0x00000001
+/* Committed object must be new */
+#define NWAM_FLAG_CREATE 0x00000002
+/* Tell destroy functions not to free handle */
+#define NWAM_FLAG_DO_NOT_FREE 0x00000004
+/* Object is being enabled/disabled */
+#define NWAM_FLAG_ENTITY_ENABLE 0x00000008
+/* Known WLAN being read, committed or destroyed */
+#define NWAM_FLAG_ENTITY_KNOWN_WLAN 0x00000010
+
+/* nwam flags used for selecting ncu type for walk */
+#define NWAM_FLAG_NCU_TYPE_LINK 0x00000001ULL << 32
+#define NWAM_FLAG_NCU_TYPE_INTERFACE 0x00000002ULL << 32
+#define NWAM_FLAG_NCU_TYPE_ALL (NWAM_FLAG_NCU_TYPE_LINK | \
+ NWAM_FLAG_NCU_TYPE_INTERFACE)
+
+/* nwam flags used for selecting ncu class for walk */
+#define NWAM_FLAG_NCU_CLASS_PHYS 0x00000100ULL << 32
+#define NWAM_FLAG_NCU_CLASS_IP 0x00010000ULL << 32
+#define NWAM_FLAG_NCU_CLASS_ALL_LINK NWAM_FLAG_NCU_CLASS_PHYS
+#define NWAM_FLAG_NCU_CLASS_ALL_INTERFACE NWAM_FLAG_NCU_CLASS_IP
+#define NWAM_FLAG_NCU_CLASS_ALL (NWAM_FLAG_NCU_CLASS_ALL_INTERFACE | \
+ NWAM_FLAG_NCU_CLASS_ALL_LINK)
+#define NWAM_FLAG_NCU_TYPE_CLASS_ALL (NWAM_FLAG_NCU_CLASS_ALL | \
+ NWAM_FLAG_NCU_TYPE_ALL)
+
+/* flags used for selecting activation for walk */
+#define NWAM_FLAG_ACTIVATION_MODE_MANUAL 0x000000001ULL << 32
+#define NWAM_FLAG_ACTIVATION_MODE_SYSTEM 0x000000002ULL << 32
+#define NWAM_FLAG_ACTIVATION_MODE_PRIORITIZED 0x000000004ULL << 32
+#define NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ANY 0x000000008ULL << 32
+#define NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ALL 0x000000010ULL << 32
+#define NWAM_FLAG_ACTIVATION_MODE_ALL (NWAM_FLAG_ACTIVATION_MODE_MANUAL |\
+ NWAM_FLAG_ACTIVATION_MODE_SYSTEM |\
+ NWAM_FLAG_ACTIVATION_MODE_PRIORITIZED |\
+ NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ANY |\
+ NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ALL)
+
+/* Walk known WLANs in order of priority (lowest first) */
+#define NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER 0x000010000ULL << 32
+/* Do not perform priority collision checking for known WLANs */
+#define NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK 0x000020000ULL << 32
+
+/* nwam return codes */
+typedef enum {
+ NWAM_SUCCESS, /* No error occured */
+ NWAM_LIST_END, /* End of list reached */
+ NWAM_INVALID_HANDLE, /* Entity handle is invalid */
+ NWAM_HANDLE_UNBOUND, /* Handle not bound to entity */
+ NWAM_INVALID_ARG, /* Argument is invalid */
+ NWAM_PERMISSION_DENIED, /* Insufficient privileges for action */
+ NWAM_NO_MEMORY, /* Out of memory */
+ NWAM_ENTITY_EXISTS, /* Entity already exists */
+ NWAM_ENTITY_IN_USE, /* Entity in use */
+ NWAM_ENTITY_COMMITTED, /* Entity already committed */
+ NWAM_ENTITY_NOT_FOUND, /* Entity not found */
+ NWAM_ENTITY_TYPE_MISMATCH, /* Entity type mismatch */
+ NWAM_ENTITY_INVALID, /* Validation of entity failed */
+ NWAM_ENTITY_INVALID_MEMBER, /* Entity member invalid */
+ NWAM_ENTITY_INVALID_STATE, /* Entity is not in appropriate state */
+ NWAM_ENTITY_INVALID_VALUE, /* Validation of entity value failed */
+ NWAM_ENTITY_MISSING_MEMBER, /* Required member is missing */
+ NWAM_ENTITY_NO_VALUE, /* No value associated with entity */
+ NWAM_ENTITY_MULTIPLE_VALUES, /* Multiple values for entity */
+ NWAM_ENTITY_READ_ONLY, /* Entity is marked read only */
+ NWAM_ENTITY_NOT_DESTROYABLE, /* Entity cannot be destroyed */
+ NWAM_ENTITY_NOT_MANUAL, /* Entity cannot be manually enabled/disabled */
+ NWAM_WALK_HALTED, /* Callback function returned nonzero */
+ NWAM_ERROR_BIND, /* Could not bind to backend */
+ NWAM_ERROR_BACKEND_INIT, /* Could not initialize backend */
+ NWAM_ERROR_INTERNAL /* Internal error */
+} nwam_error_t;
+
+#define NWAM_MAX_NAME_LEN 128
+#define NWAM_MAX_VALUE_LEN 256
+#define NWAM_MAX_FMRI_LEN NWAM_MAX_VALUE_LEN
+#define NWAM_MAX_NUM_VALUES 32
+#define NWAM_MAX_NUM_PROPERTIES 32
+
+/* used for getting and setting of properties */
+typedef enum {
+ NWAM_VALUE_TYPE_BOOLEAN,
+ NWAM_VALUE_TYPE_INT64,
+ NWAM_VALUE_TYPE_UINT64,
+ NWAM_VALUE_TYPE_STRING,
+ NWAM_VALUE_TYPE_UNKNOWN
+} nwam_value_type_t;
+
+/* Holds values of various types for getting and setting of properties */
+/* Forward definition */
+struct nwam_value;
+typedef struct nwam_value *nwam_value_t;
+
+/* Value-related functions. */
+extern nwam_error_t nwam_value_create_boolean(boolean_t, nwam_value_t *);
+extern nwam_error_t nwam_value_create_boolean_array(boolean_t *, uint_t,
+ nwam_value_t *);
+extern nwam_error_t nwam_value_create_int64(int64_t, nwam_value_t *);
+extern nwam_error_t nwam_value_create_int64_array(int64_t *, uint_t,
+ nwam_value_t *);
+extern nwam_error_t nwam_value_create_uint64(uint64_t, nwam_value_t *);
+extern nwam_error_t nwam_value_create_uint64_array(uint64_t *, uint_t,
+ nwam_value_t *);
+extern nwam_error_t nwam_value_create_string(char *, nwam_value_t *);
+extern nwam_error_t nwam_value_create_string_array(char **, uint_t,
+ nwam_value_t *);
+
+extern nwam_error_t nwam_value_get_boolean(nwam_value_t, boolean_t *);
+extern nwam_error_t nwam_value_get_boolean_array(nwam_value_t, boolean_t **,
+ uint_t *);
+extern nwam_error_t nwam_value_get_int64(nwam_value_t, int64_t *);
+extern nwam_error_t nwam_value_get_int64_array(nwam_value_t, int64_t **,
+ uint_t *);
+extern nwam_error_t nwam_value_get_uint64(nwam_value_t, uint64_t *);
+extern nwam_error_t nwam_value_get_uint64_array(nwam_value_t, uint64_t **,
+ uint_t *);
+extern nwam_error_t nwam_value_get_string(nwam_value_t, char **);
+extern nwam_error_t nwam_value_get_string_array(nwam_value_t, char ***,
+ uint_t *);
+
+extern nwam_error_t nwam_value_get_type(nwam_value_t, nwam_value_type_t *);
+extern nwam_error_t nwam_value_get_numvalues(nwam_value_t, uint_t *);
+
+extern void nwam_value_free(nwam_value_t);
+extern nwam_error_t nwam_value_copy(nwam_value_t, nwam_value_t *);
+
+extern nwam_error_t nwam_uint64_get_value_string(const char *, uint64_t,
+ const char **);
+extern nwam_error_t nwam_value_string_get_uint64(const char *, const char *,
+ uint64_t *);
+
+/*
+ * To retrieve a localized error string
+ */
+extern const char *nwam_strerror(nwam_error_t);
+
+/*
+ * State and auxiliary state describe the state of ENMs, NCUs and locations.
+ */
+typedef enum {
+ NWAM_STATE_UNINITIALIZED = 0x0,
+ NWAM_STATE_INITIALIZED = 0x1,
+ NWAM_STATE_OFFLINE = 0x2,
+ NWAM_STATE_OFFLINE_TO_ONLINE = 0x4,
+ NWAM_STATE_ONLINE_TO_OFFLINE = 0x8,
+ NWAM_STATE_ONLINE = 0x10,
+ NWAM_STATE_MAINTENANCE = 0x20,
+ NWAM_STATE_DEGRADED = 0x40,
+ NWAM_STATE_DISABLED = 0x80
+} nwam_state_t;
+
+#define NWAM_STATE_ANY (NWAM_STATE_UNINITIALIZED | \
+ NWAM_STATE_INITIALIZED | \
+ NWAM_STATE_OFFLINE | \
+ NWAM_STATE_OFFLINE_TO_ONLINE | \
+ NWAM_STATE_ONLINE_TO_OFFLINE | \
+ NWAM_STATE_ONLINE | \
+ NWAM_STATE_MAINTENANCE | \
+ NWAM_STATE_DEGRADED | \
+ NWAM_STATE_DISABLED)
+
+/*
+ * The auxiliary state denotes specific reasons why an object is in a particular
+ * state (e.g. "script failed", "disabled by administrator", "waiting for DHCP
+ * response").
+ */
+typedef enum {
+ /* General auxiliary states */
+ NWAM_AUX_STATE_UNINITIALIZED,
+ NWAM_AUX_STATE_INITIALIZED,
+ NWAM_AUX_STATE_CONDITIONS_NOT_MET,
+ NWAM_AUX_STATE_MANUAL_DISABLE,
+ NWAM_AUX_STATE_METHOD_FAILED,
+ NWAM_AUX_STATE_METHOD_MISSING,
+ NWAM_AUX_STATE_METHOD_RUNNING,
+ NWAM_AUX_STATE_INVALID_CONFIG,
+ NWAM_AUX_STATE_ACTIVE,
+ /* Link-specific auxiliary states */
+ NWAM_AUX_STATE_LINK_WIFI_SCANNING,
+ NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION,
+ NWAM_AUX_STATE_LINK_WIFI_NEED_KEY,
+ NWAM_AUX_STATE_LINK_WIFI_CONNECTING,
+ /* IP interface-specific auxiliary states */
+ NWAM_AUX_STATE_IF_WAITING_FOR_ADDR,
+ NWAM_AUX_STATE_IF_DHCP_TIMED_OUT,
+ NWAM_AUX_STATE_IF_DUPLICATE_ADDR,
+ /* Common link/interface auxiliary states */
+ NWAM_AUX_STATE_UP,
+ NWAM_AUX_STATE_DOWN,
+ NWAM_AUX_STATE_NOT_FOUND
+} nwam_aux_state_t;
+
+/* Activation modes */
+typedef enum {
+ NWAM_ACTIVATION_MODE_MANUAL,
+ NWAM_ACTIVATION_MODE_SYSTEM,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ANY,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
+ NWAM_ACTIVATION_MODE_PRIORITIZED
+} nwam_activation_mode_t;
+
+/*
+ * Conditions are of the form
+ *
+ * ncu|enm|loc name is|is-not active
+ * ip-address is|is-not|is-in-range|is-not-in-range ipaddr[/prefixlen]
+ * advertised-domain is|is-not|contains|does-not-contain string
+ * system-domain is|is-not|contains|does-not-contain string
+ * essid is|is-not|contains|does-not-contain string
+ * bssid is|is-not <string>
+ */
+
+typedef enum {
+ NWAM_CONDITION_IS,
+ NWAM_CONDITION_IS_NOT,
+ NWAM_CONDITION_IS_IN_RANGE,
+ NWAM_CONDITION_IS_NOT_IN_RANGE,
+ NWAM_CONDITION_CONTAINS,
+ NWAM_CONDITION_DOES_NOT_CONTAIN
+} nwam_condition_t;
+
+typedef enum {
+ NWAM_CONDITION_OBJECT_TYPE_NCP,
+ NWAM_CONDITION_OBJECT_TYPE_NCU,
+ NWAM_CONDITION_OBJECT_TYPE_ENM,
+ NWAM_CONDITION_OBJECT_TYPE_LOC,
+ NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS,
+ NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN,
+ NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN,
+ NWAM_CONDITION_OBJECT_TYPE_ESSID,
+ NWAM_CONDITION_OBJECT_TYPE_BSSID
+} nwam_condition_object_type_t;
+
+/*
+ * Activation condition-related functions that convert activation
+ * values to an appropriate string and back.
+ */
+extern nwam_error_t nwam_condition_to_condition_string(
+ nwam_condition_object_type_t, nwam_condition_t, const char *, char **);
+extern nwam_error_t nwam_condition_string_to_condition(const char *,
+ nwam_condition_object_type_t *, nwam_condition_t *, char **);
+
+/*
+ * Only one location can be active at one time. As a
+ * consequence, if the activation conditions of multiple
+ * locations are satisfied, we need to compare activation
+ * conditions to see if one is more specific than another.
+ *
+ * The following heuristics are applied to rate an
+ * activation condition:
+ * - "is" is the most specific condition
+ * - it is followed by "is-in-range" and "contains"
+ * - "is-not-in-range" and "does-not-contain" are next
+ * - finally "is-not" is least specific
+ *
+ * Regarding the objects these conditions apply to:
+ * - NCU, ENM and locations are most specific
+ * - system-domain is next
+ * - advertised-domain is next
+ * - IP address is next
+ * - wireless BSSID is next
+ * - wireless ESSID is least specific
+ *
+ */
+extern nwam_error_t nwam_condition_rate(nwam_condition_object_type_t,
+ nwam_condition_t, uint64_t *);
+
+/*
+ * Location definitions.
+ */
+
+#define NWAM_LOC_NAME_AUTOMATIC "Automatic"
+#define NWAM_LOC_NAME_NO_NET "NoNet"
+#define NWAM_LOC_NAME_LEGACY "Legacy"
+
+#define NWAM_LOC_NAME_PRE_DEFINED(name) \
+ (strcasecmp(name, NWAM_LOC_NAME_AUTOMATIC) == 0 || \
+ strcasecmp(name, NWAM_LOC_NAME_NO_NET) == 0 || \
+ strcasecmp(name, NWAM_LOC_NAME_LEGACY) == 0)
+
+/* Forward definition */
+struct nwam_handle;
+
+typedef struct nwam_handle *nwam_loc_handle_t;
+
+/* Location properties */
+
+typedef enum {
+ NWAM_NAMESERVICES_DNS,
+ NWAM_NAMESERVICES_FILES,
+ NWAM_NAMESERVICES_NIS,
+ NWAM_NAMESERVICES_LDAP
+} nwam_nameservices_t;
+
+typedef enum {
+ NWAM_CONFIGSRC_MANUAL,
+ NWAM_CONFIGSRC_DHCP
+} nwam_configsrc_t;
+
+#define NWAM_LOC_PROP_ACTIVATION_MODE "activation-mode"
+#define NWAM_LOC_PROP_CONDITIONS "conditions"
+#define NWAM_LOC_PROP_ENABLED "enabled"
-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 *,
+/* Nameservice location properties */
+#define NWAM_LOC_PROP_NAMESERVICES "nameservices"
+#define NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE "nameservices-config-file"
+#define NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC "dns-nameservice-configsrc"
+#define NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN "dns-nameservice-domain"
+#define NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS "dns-nameservice-servers"
+#define NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH "dns-nameservice-search"
+#define NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC "nis-nameservice-configsrc"
+#define NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS "nis-nameservice-servers"
+#define NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC "ldap-nameservice-configsrc"
+#define NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS "ldap-nameservice-servers"
+#define NWAM_LOC_PROP_DEFAULT_DOMAIN "default-domain"
+
+/* NFSv4 domain */
+#define NWAM_LOC_PROP_NFSV4_DOMAIN "nfsv4-domain"
+
+/* IPfilter configuration */
+#define NWAM_LOC_PROP_IPFILTER_CONFIG_FILE "ipfilter-config-file"
+#define NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE "ipfilter-v6-config-file"
+#define NWAM_LOC_PROP_IPNAT_CONFIG_FILE "ipnat-config-file"
+#define NWAM_LOC_PROP_IPPOOL_CONFIG_FILE "ippool-config-file"
+
+/* IPsec configuration */
+#define NWAM_LOC_PROP_IKE_CONFIG_FILE "ike-config-file"
+#define NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE "ipsecpolicy-config-file"
+
+/*
+ * NCP/NCU definitions.
+ */
+
+#define NWAM_NCP_NAME_AUTOMATIC "Automatic"
+#define NWAM_NCP_NAME_USER "User"
+
+#define NWAM_NCP_AUTOMATIC(name) \
+ (strcasecmp(name, NWAM_NCP_NAME_AUTOMATIC) == 0)
+
+typedef struct nwam_handle *nwam_ncp_handle_t;
+
+typedef struct nwam_handle *nwam_ncu_handle_t;
+
+typedef enum {
+ NWAM_NCU_TYPE_UNKNOWN = -1,
+ NWAM_NCU_TYPE_LINK,
+ NWAM_NCU_TYPE_INTERFACE,
+ NWAM_NCU_TYPE_ANY
+} nwam_ncu_type_t;
+
+typedef enum {
+ NWAM_NCU_CLASS_UNKNOWN = -1,
+ NWAM_NCU_CLASS_PHYS,
+ NWAM_NCU_CLASS_IP,
+ NWAM_NCU_CLASS_ANY
+} nwam_ncu_class_t;
+
+typedef enum {
+ NWAM_ADDRSRC_DHCP,
+ NWAM_ADDRSRC_AUTOCONF,
+ NWAM_ADDRSRC_STATIC
+} nwam_addrsrc_t;
+
+typedef enum {
+ NWAM_PRIORITY_MODE_EXCLUSIVE,
+ NWAM_PRIORITY_MODE_SHARED,
+ NWAM_PRIORITY_MODE_ALL
+} nwam_priority_mode_t;
+
+/* NCU properties common to all type/classes */
+#define NWAM_NCU_PROP_TYPE "type"
+#define NWAM_NCU_PROP_CLASS "class"
+#define NWAM_NCU_PROP_PARENT_NCP "parent"
+#define NWAM_NCU_PROP_ACTIVATION_MODE "activation-mode"
+#define NWAM_NCU_PROP_ENABLED "enabled"
+#define NWAM_NCU_PROP_PRIORITY_GROUP "priority-group"
+#define NWAM_NCU_PROP_PRIORITY_MODE "priority-mode"
+
+/* Link NCU properties */
+#define NWAM_NCU_PROP_LINK_MAC_ADDR "link-mac-addr"
+#define NWAM_NCU_PROP_LINK_AUTOPUSH "link-autopush"
+#define NWAM_NCU_PROP_LINK_MTU "link-mtu"
+
+/* IP NCU properties */
+#define NWAM_NCU_PROP_IP_VERSION "ip-version"
+#define NWAM_NCU_PROP_IPV4_ADDRSRC "ipv4-addrsrc"
+#define NWAM_NCU_PROP_IPV4_ADDR "ipv4-addr"
+#define NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE "ipv4-default-route"
+#define NWAM_NCU_PROP_IPV6_ADDRSRC "ipv6-addrsrc"
+#define NWAM_NCU_PROP_IPV6_ADDR "ipv6-addr"
+#define NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE "ipv6-default-route"
+
+/* Some properties should only be set on creation */
+#define NWAM_NCU_PROP_SETONCE(prop) \
+ (strcmp(prop, NWAM_NCU_PROP_TYPE) == 0 || \
+ strcmp(prop, NWAM_NCU_PROP_CLASS) == 0 || \
+ strcmp(prop, NWAM_NCU_PROP_PARENT_NCP) == 0)
+/*
+ * ENM definitions
+ */
+
+typedef struct nwam_handle *nwam_enm_handle_t;
+
+#define NWAM_ENM_PROP_ACTIVATION_MODE "activation-mode"
+#define NWAM_ENM_PROP_CONDITIONS "conditions"
+#define NWAM_ENM_PROP_ENABLED "enabled"
+
+/* FMRI associated with ENM */
+#define NWAM_ENM_PROP_FMRI "fmri"
+
+/* Start/stop scripts associated with ENM */
+#define NWAM_ENM_PROP_START "start"
+#define NWAM_ENM_PROP_STOP "stop"
+
+/*
+ * Known Wireless LAN info (known WLAN) definitions.
+ */
+
+typedef struct nwam_handle *nwam_known_wlan_handle_t;
+
+#define NWAM_KNOWN_WLAN_PROP_BSSIDS "bssids"
+#define NWAM_KNOWN_WLAN_PROP_PRIORITY "priority"
+#define NWAM_KNOWN_WLAN_PROP_KEYNAME "keyname"
+#define NWAM_KNOWN_WLAN_PROP_KEYSLOT "keyslot"
+#define NWAM_KNOWN_WLAN_PROP_SECURITY_MODE "security-mode"
+
+/*
+ * Location Functions
+ */
+
+/* Create a location */
+extern nwam_error_t nwam_loc_create(const char *, nwam_loc_handle_t *);
+
+/* Copy a location */
+extern nwam_error_t nwam_loc_copy(nwam_loc_handle_t, const char *,
+ nwam_loc_handle_t *);
+
+/* Read a location from persistent storage */
+extern nwam_error_t nwam_loc_read(const char *, uint64_t,
+ nwam_loc_handle_t *);
+
+/* Validate in-memory representation of a location */
+extern nwam_error_t nwam_loc_validate(nwam_loc_handle_t, const char **);
+
+/* Commit in-memory representation of a location to persistent storage */
+extern nwam_error_t nwam_loc_commit(nwam_loc_handle_t, uint64_t);
+
+/* Destroy a location in persistent storage */
+extern nwam_error_t nwam_loc_destroy(nwam_loc_handle_t, uint64_t);
+
+/* Free in-memory representation of a location */
+extern void nwam_loc_free(nwam_loc_handle_t);
+
+/* read all locs from persistent storage and walk through each at a time */
+extern nwam_error_t nwam_walk_locs(int (*)(nwam_loc_handle_t, void *), void *,
+ uint64_t, int *);
+
+/* get/set loc name */
+extern nwam_error_t nwam_loc_get_name(nwam_loc_handle_t, char **);
+extern nwam_error_t nwam_loc_set_name(nwam_loc_handle_t, const char *);
+extern boolean_t nwam_loc_can_set_name(nwam_loc_handle_t);
+
+/* activate/deactivate loc */
+extern nwam_error_t nwam_loc_enable(nwam_loc_handle_t);
+extern nwam_error_t nwam_loc_disable(nwam_loc_handle_t);
+
+/* walk all properties of an in-memory loc */
+extern nwam_error_t nwam_loc_walk_props(nwam_loc_handle_t,
+ int (*)(const char *, nwam_value_t, void *),
+ void *, uint64_t, int *);
+
+/* delete/get/set validate loc property */
+extern nwam_error_t nwam_loc_delete_prop(nwam_loc_handle_t,
const char *);
-#pragma weak libnwam_wlan_key_secmode
-extern int libnwam_wlan_key_secmode(const char *, const char *, const char *,
+extern nwam_error_t nwam_loc_get_prop_value(nwam_loc_handle_t,
+ const char *, nwam_value_t *);
+extern nwam_error_t nwam_loc_set_prop_value(nwam_loc_handle_t,
+ const char *, nwam_value_t);
+extern nwam_error_t nwam_loc_validate_prop(nwam_loc_handle_t, const char *,
+ nwam_value_t);
+
+/* Get the read-only value for a particular loc property */
+extern nwam_error_t nwam_loc_prop_read_only(const char *, boolean_t *);
+
+/* Whether the property is multi-valued or not */
+extern nwam_error_t nwam_loc_prop_multivalued(const char *, boolean_t *);
+
+/* Retrieve data type */
+extern nwam_error_t nwam_loc_get_prop_type(const char *, nwam_value_type_t *);
+
+/* Retrieve description */
+extern nwam_error_t nwam_loc_get_prop_description(const char *, const char **);
+
+/* get default loc props */
+extern nwam_error_t nwam_loc_get_default_proplist(const char ***, uint_t *);
+
+/* get sstate of loc from nwamd */
+extern nwam_error_t nwam_loc_get_state(nwam_loc_handle_t, nwam_state_t *,
+ nwam_aux_state_t *);
+
+/* Get whether the loc has manual activation-mode or not */
+extern nwam_error_t nwam_loc_is_manual(nwam_loc_handle_t, boolean_t *);
+
+/*
+ * NCP/NCU functions
+ */
+
+/* Create an ncp */
+extern nwam_error_t nwam_ncp_create(const char *, uint64_t,
+ nwam_ncp_handle_t *);
+
+/* Read an ncp from persistent storage */
+extern nwam_error_t nwam_ncp_read(const char *, uint64_t, nwam_ncp_handle_t *);
+
+/* Make a copy of existing ncp */
+extern nwam_error_t nwam_ncp_copy(nwam_ncp_handle_t, const char *,
+ nwam_ncp_handle_t *);
+
+/* Walk ncps */
+extern nwam_error_t nwam_walk_ncps(int (*)(nwam_ncp_handle_t, void *),
+ void *, uint64_t, int *);
+
+/* Get ncp name */
+extern nwam_error_t nwam_ncp_get_name(nwam_ncp_handle_t, char **);
+
+/* Get the read-only value for this ncp */
+extern nwam_error_t nwam_ncp_get_read_only(nwam_ncp_handle_t, boolean_t *);
+
+/* Destroy ncp */
+extern nwam_error_t nwam_ncp_destroy(nwam_ncp_handle_t, uint64_t);
+
+/*
+ * Walk all ncus associated with ncp. Specific types/classes of ncu can
+ * be selected via flags, or all via NWAM_FLAG_ALL.
+ */
+extern nwam_error_t nwam_ncp_walk_ncus(nwam_ncp_handle_t,
+ int(*)(nwam_ncu_handle_t, void *), void *, uint64_t, int *);
+
+/* Activate ncp */
+extern nwam_error_t nwam_ncp_enable(nwam_ncp_handle_t);
+
+/* Free in-memory representation of ncp */
+extern void nwam_ncp_free(nwam_ncp_handle_t);
+
+/* Get state of NCP from nwamd */
+extern nwam_error_t nwam_ncp_get_state(nwam_ncp_handle_t, nwam_state_t *,
+ nwam_aux_state_t *);
+
+/* Get the active priority-group */
+extern nwam_error_t nwam_ncp_get_active_priority_group(int64_t *);
+
+/* Create an ncu or read it from persistent storage */
+extern nwam_error_t nwam_ncu_create(nwam_ncp_handle_t, const char *,
+ nwam_ncu_type_t, nwam_ncu_class_t, nwam_ncu_handle_t *);
+extern nwam_error_t nwam_ncu_read(nwam_ncp_handle_t, const char *,
+ nwam_ncu_type_t, uint64_t, nwam_ncu_handle_t *);
+
+/* Destroy an ncu in persistent storage or free the in-memory representation */
+extern nwam_error_t nwam_ncu_destroy(nwam_ncu_handle_t, uint64_t);
+extern void nwam_ncu_free(nwam_ncu_handle_t);
+
+/* make a copy of existing ncu */
+extern nwam_error_t nwam_ncu_copy(nwam_ncu_handle_t, const char *,
+ nwam_ncu_handle_t *);
+
+/* Commit ncu changes to persistent storage */
+extern nwam_error_t nwam_ncu_commit(nwam_ncu_handle_t, uint64_t);
+
+/* activate/deactivate an individual NCU (must be part of the active NCP) */
+extern nwam_error_t nwam_ncu_enable(nwam_ncu_handle_t);
+extern nwam_error_t nwam_ncu_disable(nwam_ncu_handle_t);
+
+/* Get state of NCU from nwamd */
+extern nwam_error_t nwam_ncu_get_state(nwam_ncu_handle_t, nwam_state_t *,
+ nwam_aux_state_t *);
+
+/* Get NCU type */
+extern nwam_error_t nwam_ncu_get_ncu_type(nwam_ncu_handle_t, nwam_ncu_type_t *);
+
+/* Get NCU class */
+extern nwam_error_t nwam_ncu_get_ncu_class(nwam_ncu_handle_t,
+ nwam_ncu_class_t *);
+
+/* Validate ncu content */
+extern nwam_error_t nwam_ncu_validate(nwam_ncu_handle_t, const char **);
+
+/* Walk all properties in in-memory representation of ncu */
+extern nwam_error_t nwam_ncu_walk_props(nwam_ncu_handle_t,
+ int (*)(const char *, nwam_value_t, void *),
+ void *, uint64_t, int *);
+
+/* Get/set name of ncu, get parent ncp */
+extern nwam_error_t nwam_ncu_get_name(nwam_ncu_handle_t, char **);
+extern nwam_error_t nwam_ncu_name_to_typed_name(const char *, nwam_ncu_type_t,
+ char **);
+extern nwam_error_t nwam_ncu_typed_name_to_name(const char *, nwam_ncu_type_t *,
+ char **);
+extern nwam_error_t nwam_ncu_get_default_proplist(nwam_ncu_type_t,
+ nwam_ncu_class_t, const char ***, uint_t *);
+extern nwam_error_t nwam_ncu_get_ncp(nwam_ncu_handle_t, nwam_ncp_handle_t *);
+
+/* delete/get/set/validate property from/in in-memory representation of ncu */
+extern nwam_error_t nwam_ncu_delete_prop(nwam_ncu_handle_t,
+ const char *);
+extern nwam_error_t nwam_ncu_get_prop_value(nwam_ncu_handle_t,
+ const char *, nwam_value_t *);
+extern nwam_error_t nwam_ncu_set_prop_value(nwam_ncu_handle_t,
+ const char *, nwam_value_t);
+
+extern nwam_error_t nwam_ncu_validate_prop(nwam_ncu_handle_t, const char *,
+ nwam_value_t);
+
+/* Retrieve data type */
+extern nwam_error_t nwam_ncu_get_prop_type(const char *, nwam_value_type_t *);
+/* Retrieve prop description */
+extern nwam_error_t nwam_ncu_get_prop_description(const char *, const char **);
+
+/* Get the read-only value from the handle or parent NCP */
+extern nwam_error_t nwam_ncu_get_read_only(nwam_ncu_handle_t, boolean_t *);
+
+/* Get the read-only value for a particular NCU property */
+extern nwam_error_t nwam_ncu_prop_read_only(const char *, boolean_t *);
+
+/* Whether the property is multi-valued or not */
+extern nwam_error_t nwam_ncu_prop_multivalued(const char *, boolean_t *);
+
+/* Get whether the NCU has manual activation-mode or not */
+extern nwam_error_t nwam_ncu_is_manual(nwam_ncu_handle_t, boolean_t *);
+
+/* Get the flag from the given class for walks */
+extern uint64_t nwam_ncu_class_to_flag(nwam_ncu_class_t);
+
+/* Get the NCU type from the given class */
+extern nwam_ncu_type_t nwam_ncu_class_to_type(nwam_ncu_class_t);
+
+/* ENM functions */
+/*
+ * Obtain a specific enm handle, either be creating a new enm
+ * or reading an existing one from persistent storage.
+ */
+extern nwam_error_t nwam_enm_create(const char *, const char *,
+ nwam_enm_handle_t *);
+extern nwam_error_t nwam_enm_read(const char *, uint64_t, nwam_enm_handle_t *);
+
+/* Make a copy of existing enm */
+extern nwam_error_t nwam_enm_copy(nwam_enm_handle_t, const char *,
+ nwam_enm_handle_t *);
+
+/*
+ * Obtain handles for all existing enms. Caller-specified callback
+ * function will be called once for each enm, passing the handle and
+ * the caller-specified arg.
+ */
+extern nwam_error_t nwam_walk_enms(int (*)(nwam_enm_handle_t, void *), void *,
+ uint64_t, int *);
+
+/*
+ * Commit an enm to persistent storage. Does not free the handle.
+ */
+extern nwam_error_t nwam_enm_commit(nwam_enm_handle_t, uint64_t);
+
+/*
+ * Remove an enm from persistent storage.
+ */
+extern nwam_error_t nwam_enm_destroy(nwam_enm_handle_t, uint64_t);
+
+/*
+ * Free an enm handle
+ */
+extern void nwam_enm_free(nwam_enm_handle_t);
+
+/*
+ * Validate an enm, or a specific enm property. If validating
+ * an entire enm, the invalid property type is returned.
+ */
+extern nwam_error_t nwam_enm_validate(nwam_enm_handle_t, const char **);
+extern nwam_error_t nwam_enm_validate_prop(nwam_enm_handle_t, const char *,
+ nwam_value_t);
+
+/* Retrieve data type */
+extern nwam_error_t nwam_enm_get_prop_type(const char *, nwam_value_type_t *);
+/* Retrieve prop description */
+extern nwam_error_t nwam_enm_get_prop_description(const char *, const char **);
+
+/*
+ * Delete/get/set enm property values.
+ */
+extern nwam_error_t nwam_enm_delete_prop(nwam_enm_handle_t,
+ const char *);
+extern nwam_error_t nwam_enm_get_prop_value(nwam_enm_handle_t,
+ const char *, nwam_value_t *);
+extern nwam_error_t nwam_enm_set_prop_value(nwam_enm_handle_t,
+ const char *, nwam_value_t);
+
+extern nwam_error_t nwam_enm_get_default_proplist(const char ***, uint_t *);
+
+/* Get the read-only value for a particular ENM property */
+extern nwam_error_t nwam_enm_prop_read_only(const char *, boolean_t *);
+
+/* Whether the property is multi-valued or not */
+extern nwam_error_t nwam_enm_prop_multivalued(const char *, boolean_t *);
+
+/*
+ * Walk all properties of a specific enm. For each property, specified
+ * callback function is called. Caller is responsible for freeing memory
+ * allocated for each property.
+ */
+extern nwam_error_t nwam_enm_walk_props(nwam_enm_handle_t,
+ int (*)(const char *, nwam_value_t, void *),
+ void *, uint64_t, int *);
+
+/*
+ * Get/set the name of an enm. When getting the name, the library will
+ * allocate a buffer; the caller is responsible for freeing the memory.
+ */
+extern nwam_error_t nwam_enm_get_name(nwam_enm_handle_t, char **);
+extern nwam_error_t nwam_enm_set_name(nwam_enm_handle_t, const char *);
+extern boolean_t nwam_enm_can_set_name(nwam_enm_handle_t);
+
+/*
+ * Start/stop an enm.
+ */
+extern nwam_error_t nwam_enm_enable(nwam_enm_handle_t);
+extern nwam_error_t nwam_enm_disable(nwam_enm_handle_t);
+
+/*
+ * Get state of ENM from nwamd.
+ */
+extern nwam_error_t nwam_enm_get_state(nwam_enm_handle_t, nwam_state_t *,
+ nwam_aux_state_t *);
+
+/*
+ * Get whether the ENM has manual activation-mode or not.
+ */
+extern nwam_error_t nwam_enm_is_manual(nwam_enm_handle_t, boolean_t *);
+
+/*
+ * Known Wireless LAN (WLAN) info.
+ */
+
+/* Create a known WLAN */
+extern nwam_error_t nwam_known_wlan_create(const char *,
+ nwam_known_wlan_handle_t *);
+
+/* Read a known WLAN from persistent storage */
+extern nwam_error_t nwam_known_wlan_read(const char *, uint64_t,
+ nwam_known_wlan_handle_t *);
+
+/*
+ * Destroy a known WLAN in persistent storage or free the in-memory
+ * representation.
+ */
+extern nwam_error_t nwam_known_wlan_destroy(nwam_known_wlan_handle_t, uint64_t);
+extern void nwam_known_wlan_free(nwam_known_wlan_handle_t);
+
+/* make a copy of existing known WLAN */
+extern nwam_error_t nwam_known_wlan_copy(nwam_known_wlan_handle_t, const char *,
+ nwam_known_wlan_handle_t *);
+
+/* Commit known WLAN changes to persistent storage */
+extern nwam_error_t nwam_known_wlan_commit(nwam_known_wlan_handle_t, uint64_t);
+
+/* Validate known WLAN content */
+extern nwam_error_t nwam_known_wlan_validate(nwam_known_wlan_handle_t,
+ const char **);
+
+/* Walk known WLANs */
+extern nwam_error_t nwam_walk_known_wlans
+ (int(*)(nwam_known_wlan_handle_t, void *), void *, uint64_t, int *);
+
+/* get/set known WLAN name */
+extern nwam_error_t nwam_known_wlan_get_name(nwam_known_wlan_handle_t, char **);
+extern nwam_error_t nwam_known_wlan_set_name(nwam_known_wlan_handle_t,
+ const char *);
+extern boolean_t nwam_known_wlan_can_set_name(nwam_known_wlan_handle_t);
+
+/* walk all properties of an in-memory known WLAN */
+extern nwam_error_t nwam_known_wlan_walk_props(nwam_known_wlan_handle_t,
+ int (*)(const char *, nwam_value_t, void *),
+ void *, uint64_t, int *);
+
+/* delete/get/set/validate known WLAN property */
+extern nwam_error_t nwam_known_wlan_delete_prop(nwam_known_wlan_handle_t,
+ const char *);
+extern nwam_error_t nwam_known_wlan_get_prop_value(nwam_known_wlan_handle_t,
+ const char *, nwam_value_t *);
+extern nwam_error_t nwam_known_wlan_set_prop_value(nwam_known_wlan_handle_t,
+ const char *, nwam_value_t);
+extern nwam_error_t nwam_known_wlan_validate_prop(nwam_known_wlan_handle_t,
+ const char *, nwam_value_t);
+
+/* Retrieve data type */
+extern nwam_error_t nwam_known_wlan_get_prop_type(const char *,
+ nwam_value_type_t *);
+/* Retrieve prop description */
+extern nwam_error_t nwam_known_wlan_get_prop_description(const char *,
+ const char **);
+
+/* get default known WLAN props */
+extern nwam_error_t nwam_known_wlan_get_default_proplist(const char ***,
+ uint_t *);
+
+/* Whether the property is multi-valued or not */
+extern nwam_error_t nwam_known_wlan_prop_multivalued(const char *, boolean_t *);
+
+/* Add a bssid to the known WLANs */
+extern nwam_error_t nwam_known_wlan_add_to_known_wlans(const char *,
+ const char *, uint32_t, uint_t, const char *);
+
+/* Remove a bssid from known WLANs */
+extern nwam_error_t nwam_known_wlan_remove_from_known_wlans(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
+/*
+ * nwam_wlan_t is used for scan/need choice/need key events and by
+ * nwam_wlan_get_scan_results(). The following fields are valid:
+ *
+ * - for scan and need choice event, ESSID, BSSID, signal strength, security
+ * mode, speed, channel, bsstype, key index, and if we already have a key
+ * (have_key), if the WLAN is the current selection (selected) and
+ * if the current WLAN is connected (connected).
+ * - for need key events, ESSID, security mode, have_key, selected and connected
+ * values are set. The rest of the fields are not set since multiple WLANs
+ * may match the ESSID and have different speeds, channels etc. If an
+ * ESSID/BSSID selection is specified, the BSSID will be set also.
+ *
+ */
+typedef struct {
+ char nww_essid[NWAM_MAX_NAME_LEN];
+ char nww_bssid[NWAM_MAX_NAME_LEN];
+ char nww_signal_strength[NWAM_MAX_NAME_LEN];
+ uint32_t nww_security_mode; /* a dladm_wlan_secmode_t */
+ uint32_t nww_speed; /* a dladm_wlan_speed_t */
+ uint32_t nww_channel; /* a dladm_wlan_channel_t */
+ uint32_t nww_bsstype; /* a dladm_wlan_bsstype_t */
+ uint_t nww_keyindex;
+ boolean_t nww_have_key;
+ boolean_t nww_selected;
+ boolean_t nww_connected;
+} nwam_wlan_t;
+
+/*
+ * Active WLAN definitions. Used to scan WLANs/choose a WLAN/set a WLAN key.
+ */
+extern nwam_error_t nwam_wlan_scan(const char *);
+extern nwam_error_t nwam_wlan_get_scan_results(const char *, uint_t *,
+ nwam_wlan_t **);
+extern nwam_error_t nwam_wlan_select(const char *, const char *, const char *,
+ uint32_t, boolean_t);
+extern nwam_error_t nwam_wlan_set_key(const char *, const char *, const char *,
+ uint32_t, uint_t, const char *);
+
+/*
+ * Event notification definitions
+ */
+#define NWAM_EVENT_TYPE_NOOP 0
+#define NWAM_EVENT_TYPE_INIT 1
+#define NWAM_EVENT_TYPE_SHUTDOWN 2
+#define NWAM_EVENT_TYPE_OBJECT_ACTION 3
+#define NWAM_EVENT_TYPE_OBJECT_STATE 4
+#define NWAM_EVENT_TYPE_PRIORITY_GROUP 5
+#define NWAM_EVENT_TYPE_INFO 6
+#define NWAM_EVENT_TYPE_WLAN_SCAN_REPORT 7
+#define NWAM_EVENT_TYPE_WLAN_NEED_CHOICE 8
+#define NWAM_EVENT_TYPE_WLAN_NEED_KEY 9
+#define NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT 10
+#define NWAM_EVENT_TYPE_IF_ACTION 11
+#define NWAM_EVENT_TYPE_IF_STATE 12
+#define NWAM_EVENT_TYPE_LINK_ACTION 13
+#define NWAM_EVENT_TYPE_LINK_STATE 14
+#define NWAM_EVENT_MAX NWAM_EVENT_TYPE_LINK_STATE
+
+#define NWAM_EVENT_STATUS_OK 0
+#define NWAM_EVENT_STATUS_NOT_HANDLED 1
+
+#define NWAM_EVENT_NETWORK_OBJECT_UNDEFINED 0
+#define NWAM_EVENT_NETWORK_OBJECT_LINK 1
+#define NWAM_EVENT_NETWORK_OBJECT_INTERFACE 2
+
+#define NWAM_EVENT_REQUEST_UNDEFINED 0
+#define NWAM_EVENT_REQUEST_WLAN 1
+#define NWAM_EVENT_REQUEST_KEY 2
+
+/*
+ * Actions for nwamd to perform, used in conjunction with
+ * nwam_request_type_t in nwam_door_arg_t.
+ * Add string representations to nwam_action_to_string() in libnwam_util.c.
+ */
+typedef enum {
+ NWAM_ACTION_UNKNOWN = -1,
+ NWAM_ACTION_ADD,
+ NWAM_ACTION_REMOVE,
+ NWAM_ACTION_REFRESH,
+ NWAM_ACTION_ENABLE,
+ NWAM_ACTION_DISABLE,
+ NWAM_ACTION_DESTROY
+} nwam_action_t;
+
+typedef enum {
+ NWAM_OBJECT_TYPE_UNKNOWN = -1,
+ NWAM_OBJECT_TYPE_NCP = 0,
+ NWAM_OBJECT_TYPE_NCU = 1,
+ NWAM_OBJECT_TYPE_LOC = 2,
+ NWAM_OBJECT_TYPE_ENM = 3,
+ NWAM_OBJECT_TYPE_KNOWN_WLAN = 4
+} nwam_object_type_t;
+
+typedef struct nwam_event *nwam_event_t;
+struct nwam_event {
+ int nwe_type;
+ uint32_t nwe_size;
+
+ union {
+ struct nwam_event_object_action {
+ nwam_object_type_t nwe_object_type;
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ char nwe_parent[NWAM_MAX_NAME_LEN];
+ nwam_action_t nwe_action;
+ } nwe_object_action;
+
+ struct nwam_event_object_state {
+ nwam_object_type_t nwe_object_type;
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ char nwe_parent[NWAM_MAX_NAME_LEN];
+ nwam_state_t nwe_state;
+ nwam_aux_state_t nwe_aux_state;
+ } nwe_object_state;
+
+ struct nwam_event_priority_group_info {
+ int64_t nwe_priority;
+ } nwe_priority_group_info;
+
+ struct nwam_event_info {
+ char nwe_message[NWAM_MAX_VALUE_LEN];
+ } nwe_info;
+
+ /*
+ * wlan_info stores both scan results and the single
+ * WLAN we require a key for in the case of _WLAN_NEED_KEY
+ * events. For _WLAN_CONNECTION_REPORT events, it stores
+ * the WLAN the connection succeeded/failed for, indicating
+ * success/failure using the 'connected' boolean.
+ */
+ struct nwam_event_wlan_info {
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ boolean_t nwe_connected;
+ uint16_t nwe_num_wlans;
+ nwam_wlan_t nwe_wlans[1];
+ /*
+ * space may be allocated by user here for the
+ * number of wlans
+ */
+ } nwe_wlan_info;
+
+ struct nwam_event_if_action {
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ nwam_action_t nwe_action;
+ } nwe_if_action;
+
+ struct nwam_event_if_state {
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ uint32_t nwe_flags;
+ uint32_t nwe_index;
+ uint32_t nwe_addr_valid; /* boolean */
+ uint32_t nwe_addr_added; /* boolean */
+ struct sockaddr_storage nwe_addr;
+ /* might be longer then sizeof(if_state) for addr */
+ } nwe_if_state;
+
+ struct nwam_event_link_state {
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ boolean_t nwe_link_up;
+ /* link_state_t from sys/mac.h */
+ } nwe_link_state;
+
+ struct nwam_event_link_action {
+ char nwe_name[NWAM_MAX_NAME_LEN];
+ nwam_action_t nwe_action;
+ } nwe_link_action;
+ } nwe_data;
+};
+
+/* NWAM client functions, used to register/unregister and receive events */
+extern nwam_error_t nwam_events_init(void);
+extern void nwam_events_fini(void);
+extern nwam_error_t nwam_event_wait(nwam_event_t *);
+extern void nwam_event_free(nwam_event_t);
+
+/* Event-related string conversion functions */
+extern const char *nwam_action_to_string(nwam_action_t);
+extern const char *nwam_event_type_to_string(int);
+extern const char *nwam_state_to_string(nwam_state_t);
+extern const char *nwam_aux_state_to_string(nwam_aux_state_t);
+
+extern const char *nwam_object_type_to_string(nwam_object_type_t);
+extern nwam_object_type_t nwam_string_to_object_type(const char *);
+
+/* Utility strtok_r-like function */
+extern char *nwam_tokenize_by_unescaped_delim(char *, char, char **);
+
+#ifdef __cplusplus
}
#endif
-#endif /* _LIBNWAM_H */
+#endif /* _LIBNWAM_H */
diff --git a/usr/src/lib/libnwam/common/libnwam_audit.c b/usr/src/lib/libnwam/common/libnwam_audit.c
new file mode 100644
index 0000000000..34a31f48a4
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_audit.c
@@ -0,0 +1,82 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+
+#include <libnwam_priv.h>
+
+/*
+ * Record libnwam's audit events (enable, disable, update and remove profiles).
+ */
+void
+nwam_record_audit_event(const ucred_t *ucr, au_event_t eid,
+ char *name, char *descr_arg, int status, int error)
+{
+ adt_session_data_t *ah;
+ adt_event_data_t *edata;
+
+ if (adt_start_session(&ah, NULL, 0) != 0)
+ return;
+
+ if (adt_set_from_ucred(ah, ucr, ADT_NEW) != 0) {
+ (void) adt_end_session(ah);
+ return;
+ }
+
+ if ((edata = adt_alloc_event(ah, eid)) == NULL) {
+ (void) adt_end_session(ah);
+ return;
+ }
+
+ switch (eid) {
+ case ADT_nwam_enable:
+ edata->adt_nwam_enable.profile_name = name;
+ edata->adt_nwam_enable.profile_type = descr_arg;
+ break;
+ case ADT_nwam_disable:
+ edata->adt_nwam_disable.profile_name = name;
+ edata->adt_nwam_disable.profile_type = descr_arg;
+ break;
+ case ADT_netcfg_update:
+ edata->adt_netcfg_update.object_name = name;
+ edata->adt_netcfg_update.parent_file = descr_arg;
+ break;
+ case ADT_netcfg_remove:
+ edata->adt_netcfg_remove.object_name = name;
+ edata->adt_netcfg_remove.parent_file = descr_arg;
+ break;
+ default:
+ goto out;
+ }
+
+ (void) adt_put_event(edata, status, error);
+
+out:
+ adt_free_event(edata);
+ (void) adt_end_session(ah);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_backend.c b/usr/src/lib/libnwam/common/libnwam_backend.c
new file mode 100644
index 0000000000..829e5fc303
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_backend.c
@@ -0,0 +1,466 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <auth_attr.h>
+#include <auth_list.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <pwd.h>
+#include <secdb.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Communicate with and implement library backend (running in netcfgd) to
+ * retrieve or change NWAM configuration.
+ */
+
+static int backend_door_client_fd = -1;
+
+/*
+ * Check if uid has proper auths. flags is used to check auths for
+ * enable/disable of profiles and manipulation of Known WLANs.
+ */
+static nwam_error_t
+nwam_check_auths(uid_t uid, boolean_t write, uint64_t flags)
+{
+ struct passwd *pwd;
+ nwam_error_t err = NWAM_SUCCESS;
+
+ if ((pwd = getpwuid(uid)) == NULL) {
+ endpwent();
+ return (NWAM_PERMISSION_DENIED);
+ }
+
+ if (flags & NWAM_FLAG_ENTITY_ENABLE) {
+ /* Enabling/disabling profile - need SELECT auth */
+ if (chkauthattr(AUTOCONF_SELECT_AUTH, pwd->pw_name) == 0)
+ err = NWAM_PERMISSION_DENIED;
+
+ } else if (flags & NWAM_FLAG_ENTITY_KNOWN_WLAN) {
+ /* Known WLAN activity - need WLAN auth */
+ if (chkauthattr(AUTOCONF_WLAN_AUTH, pwd->pw_name) == 0)
+ err = NWAM_PERMISSION_DENIED;
+
+ } else {
+ /*
+ * First, check for WRITE, since it implies READ. If this
+ * auth is not present, and write is true, fail, otherwise
+ * check for READ.
+ */
+ if (chkauthattr(AUTOCONF_WRITE_AUTH, pwd->pw_name) == 0) {
+ if (write) {
+ err = NWAM_PERMISSION_DENIED;
+ } else {
+ if (chkauthattr(AUTOCONF_READ_AUTH,
+ pwd->pw_name) == 0)
+ err = NWAM_PERMISSION_DENIED;
+ }
+ }
+ }
+
+ endpwent();
+ return (err);
+}
+
+static nwam_error_t
+nwam_create_backend_door_arg(nwam_backend_door_cmd_t cmd,
+ const char *dbname, const char *objname, uint64_t flags,
+ void *obj, nwam_backend_door_arg_t *arg)
+{
+ nwam_error_t err;
+ size_t datalen = 0;
+ caddr_t dataptr;
+
+ switch (cmd) {
+ case NWAM_BACKEND_DOOR_CMD_READ_REQ:
+ /*
+ * For a read request, we want the full buffer to be
+ * available for the backend door to write to.
+ */
+ datalen = NWAM_BACKEND_DOOR_ARG_SIZE;
+ break;
+
+ case NWAM_BACKEND_DOOR_CMD_UPDATE_REQ:
+ /*
+ * An update request may either specify an object list
+ * (which we pack into the buffer immediately after the
+ * backend door request) or may not specify an object
+ * (signifying a request to create the container of the
+ * object).
+ */
+ if (obj == NULL) {
+ datalen = 0;
+ break;
+ }
+ /* Data immediately follows the descriptor */
+ dataptr = (caddr_t)arg + sizeof (nwam_backend_door_arg_t);
+ datalen = NWAM_BACKEND_DOOR_ARG_SIZE;
+ /* pack object list for update request, adjusting datalen */
+ if ((err = nwam_pack_object_list(obj, (char **)&dataptr,
+ &datalen)) != NWAM_SUCCESS)
+ return (err);
+ break;
+
+ case NWAM_BACKEND_DOOR_CMD_REMOVE_REQ:
+ /* A remove request has no associated object list. */
+ datalen = 0;
+ break;
+
+ default:
+ return (NWAM_INVALID_ARG);
+ }
+
+ arg->nwbda_cmd = cmd;
+ arg->nwbda_flags = flags;
+ arg->nwbda_datalen = datalen;
+ arg->nwbda_result = NWAM_SUCCESS;
+
+ if (dbname != NULL)
+ (void) strlcpy(arg->nwbda_dbname, dbname, MAXPATHLEN);
+ else
+ arg->nwbda_dbname[0] = '\0';
+
+ if (objname != NULL)
+ (void) strlcpy(arg->nwbda_object, objname, NWAM_MAX_NAME_LEN);
+ else
+ arg->nwbda_object[0] = '\0';
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * If the arg datalen is non-zero, unpack the object list associated with
+ * the backend door argument.
+ */
+static nwam_error_t
+nwam_read_object_from_backend_door_arg(nwam_backend_door_arg_t *arg,
+ char *dbname, char *name, void *objp)
+{
+ nwam_error_t err;
+ caddr_t dataptr = (caddr_t)arg + sizeof (nwam_backend_door_arg_t);
+
+ if (arg->nwbda_result != NWAM_SUCCESS)
+ return (arg->nwbda_result);
+
+ if (arg->nwbda_datalen > 0) {
+ if ((err = nwam_unpack_object_list((char *)dataptr,
+ arg->nwbda_datalen, objp)) != NWAM_SUCCESS)
+ return (err);
+ } else {
+ *((char **)objp) = NULL;
+ }
+
+ /*
+ * If "dbname" and "name" are non-NULL, copy in the actual dbname
+ * and name values from the door arg since both may have been changed
+ * from case-insensitive to case-sensitive matches. They will be the
+ * same length as they only differ in case.
+ */
+ if (dbname != NULL && strcmp(dbname, arg->nwbda_dbname) != 0)
+ (void) strlcpy(dbname, arg->nwbda_dbname, strlen(dbname) + 1);
+ if (name != NULL && strcmp(name, arg->nwbda_object) != 0)
+ (void) strlcpy(name, arg->nwbda_object, strlen(name) + 1);
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED */
+void
+nwam_backend_door_server(void *cookie, char *arg, size_t arg_size,
+ door_desc_t *dp, uint_t ndesc)
+{
+ /* LINTED: alignment */
+ nwam_backend_door_arg_t *req = (nwam_backend_door_arg_t *)arg;
+ nwam_error_t err;
+ void *obj, *newobj = NULL;
+ ucred_t *ucr = NULL;
+ uid_t uid;
+ boolean_t write = B_TRUE;
+
+ /* Check arg size */
+ if (arg_size < sizeof (nwam_backend_door_arg_t)) {
+ req->nwbda_result = NWAM_INVALID_ARG;
+ (void) door_return((char *)req,
+ sizeof (nwam_backend_door_arg_t), NULL, 0);
+ }
+
+ if (door_ucred(&ucr) != 0) {
+ req->nwbda_result = NWAM_ERROR_INTERNAL;
+ (void) door_return((char *)req, arg_size, NULL, 0);
+ }
+
+ /* Check auths */
+ uid = ucred_getruid(ucr);
+
+ if (req->nwbda_cmd == NWAM_BACKEND_DOOR_CMD_READ_REQ)
+ write = B_FALSE;
+ if ((err = nwam_check_auths(uid, write, req->nwbda_flags))
+ != NWAM_SUCCESS) {
+ if (write) {
+ nwam_record_audit_event(ucr,
+ req->nwbda_cmd == NWAM_BACKEND_DOOR_CMD_UPDATE_REQ ?
+ ADT_netcfg_update : ADT_netcfg_remove,
+ (char *)req->nwbda_object,
+ (char *)req->nwbda_dbname, ADT_FAILURE,
+ ADT_FAIL_VALUE_AUTH);
+ }
+ req->nwbda_result = err;
+ goto door_return;
+ }
+
+ switch (req->nwbda_cmd) {
+ case NWAM_BACKEND_DOOR_CMD_READ_REQ:
+ if ((req->nwbda_result = nwam_read_object_from_files_backend
+ (strlen(req->nwbda_dbname) > 0 ? req->nwbda_dbname : NULL,
+ strlen(req->nwbda_object) > 0 ? req->nwbda_object : NULL,
+ req->nwbda_flags, &newobj)) != NWAM_SUCCESS) {
+ break;
+ }
+ if (newobj != NULL) {
+ size_t datalen = arg_size -
+ sizeof (nwam_backend_door_arg_t);
+ caddr_t dataptr = (caddr_t)req +
+ sizeof (nwam_backend_door_arg_t);
+
+ if ((req->nwbda_result = nwam_pack_object_list(newobj,
+ (char **)&dataptr, &datalen)) != NWAM_SUCCESS)
+ req->nwbda_datalen = 0;
+ else
+ req->nwbda_datalen = datalen;
+ nwam_free_object_list(newobj);
+ } else {
+ req->nwbda_datalen = 0;
+ }
+ break;
+
+ case NWAM_BACKEND_DOOR_CMD_UPDATE_REQ:
+ if (req->nwbda_datalen == 0) {
+ obj = NULL;
+ } else {
+ if ((req->nwbda_result =
+ nwam_read_object_from_backend_door_arg
+ (req, NULL, NULL, &obj)) != NWAM_SUCCESS)
+ break;
+ }
+ req->nwbda_result = nwam_update_object_in_files_backend(
+ req->nwbda_dbname[0] == 0 ? NULL : req->nwbda_dbname,
+ req->nwbda_object[0] == 0 ? NULL : req->nwbda_object,
+ req->nwbda_flags, obj);
+ nwam_free_object_list(obj);
+ if (req->nwbda_result == NWAM_SUCCESS) {
+ req->nwbda_datalen = 0;
+ nwam_record_audit_event(ucr, ADT_netcfg_update,
+ (char *)req->nwbda_object,
+ (char *)req->nwbda_dbname, ADT_SUCCESS,
+ ADT_SUCCESS);
+ }
+ break;
+
+ case NWAM_BACKEND_DOOR_CMD_REMOVE_REQ:
+ req->nwbda_result = nwam_remove_object_from_files_backend
+ (strlen(req->nwbda_dbname) > 0 ? req->nwbda_dbname : NULL,
+ strlen(req->nwbda_object) > 0 ? req->nwbda_object : NULL,
+ req->nwbda_flags);
+ if (req->nwbda_result == NWAM_SUCCESS) {
+ nwam_record_audit_event(ucr, ADT_netcfg_update,
+ (char *)req->nwbda_object,
+ (char *)req->nwbda_dbname, ADT_SUCCESS,
+ ADT_SUCCESS);
+ }
+ break;
+
+ default:
+ req->nwbda_result = NWAM_INVALID_ARG;
+ break;
+ }
+
+door_return:
+ ucred_free(ucr);
+
+ (void) door_return((char *)req, arg_size, NULL, 0);
+}
+
+static int backend_door_fd = -1;
+
+void
+nwam_backend_fini(void)
+{
+ if (backend_door_fd != -1) {
+ (void) door_revoke(backend_door_fd);
+ backend_door_fd = -1;
+ }
+ (void) unlink(NWAM_BACKEND_DOOR_FILE);
+}
+
+nwam_error_t
+nwam_backend_init(void)
+{
+ int did;
+ struct stat statbuf;
+
+ /* Create the door directory if it doesn't already exist */
+ if (stat(NWAM_DOOR_DIR, &statbuf) < 0) {
+ if (mkdir(NWAM_DOOR_DIR, (mode_t)0755) < 0)
+ return (NWAM_ERROR_BACKEND_INIT);
+ } else {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+ return (NWAM_ERROR_BACKEND_INIT);
+ }
+
+ if (chmod(NWAM_DOOR_DIR, 0755) < 0 ||
+ chown(NWAM_DOOR_DIR, UID_NETADM, GID_NETADM) < 0)
+ return (NWAM_ERROR_BACKEND_INIT);
+
+ /* Do a low-overhead "touch" on the file that will be the door node. */
+ did = open(NWAM_BACKEND_DOOR_FILE,
+ O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_NONBLOCK,
+ S_IRUSR | S_IRGRP | S_IROTH);
+
+ if (did != -1)
+ (void) close(did);
+ else if (errno != EEXIST)
+ return (NWAM_ERROR_BACKEND_INIT);
+
+ /* Create the door. */
+ backend_door_fd = door_create(nwam_backend_door_server, NULL,
+ DOOR_REFUSE_DESC);
+ if (backend_door_fd == -1)
+ return (NWAM_ERROR_BACKEND_INIT);
+
+ /* Attach the door to the file. */
+ (void) fdetach(NWAM_BACKEND_DOOR_FILE);
+ if (fattach(backend_door_fd, NWAM_BACKEND_DOOR_FILE) == -1) {
+ (void) door_revoke(backend_door_fd);
+ return (NWAM_ERROR_BACKEND_INIT);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+nwam_backend_door_call(nwam_backend_door_cmd_t cmd, char *dbname,
+ char *objname, uint64_t flags, void *obj)
+{
+ uchar_t reqbuf[NWAM_BACKEND_DOOR_ARG_SIZE];
+ /* LINTED: alignment */
+ nwam_backend_door_arg_t *req = (nwam_backend_door_arg_t *)&reqbuf;
+ nwam_error_t err, reserr;
+
+ if ((err = nwam_create_backend_door_arg(cmd, dbname, objname, flags,
+ obj, req)) != NWAM_SUCCESS)
+ return (err);
+
+ if (nwam_make_door_call(NWAM_BACKEND_DOOR_FILE, &backend_door_client_fd,
+ req, sizeof (reqbuf)) != 0)
+ return (NWAM_ERROR_BIND);
+
+ reserr = req->nwbda_result;
+
+ if (cmd == NWAM_BACKEND_DOOR_CMD_READ_REQ) {
+ err = nwam_read_object_from_backend_door_arg(req, dbname,
+ objname, obj);
+ }
+
+ return (err == NWAM_SUCCESS ? reserr : err);
+}
+
+/*
+ * Read object specified by objname from backend dbname, retrieving an object
+ * list representation.
+ *
+ * If dbname is NULL, obj is a list of string arrays consisting of the list
+ * of backend dbnames.
+ *
+ * If objname is NULL, read all objects in the specified dbname and create
+ * an object list containing a string array which represents each object.
+ *
+ * Otherwise obj will point to a list of the properties for the object
+ * specified by objname in the backend dbname.
+ */
+/* ARGSUSED2 */
+nwam_error_t
+nwam_read_object_from_backend(char *dbname, char *objname,
+ uint64_t flags, void *obj)
+{
+ nwam_error_t err = nwam_check_auths(getuid(), B_FALSE, flags);
+
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_READ_REQ,
+ dbname, objname, flags, obj));
+}
+
+/*
+ * Read in all objects from backend dbname and update object corresponding
+ * to objname with properties recorded in proplist, writing the results to
+ * the backend dbname.
+ */
+nwam_error_t
+nwam_update_object_in_backend(char *dbname, char *objname,
+ uint64_t flags, void *obj)
+{
+ nwam_error_t err = nwam_check_auths(getuid(), B_TRUE, flags);
+
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_UPDATE_REQ,
+ dbname, objname, flags, obj));
+}
+
+/*
+ * Remove specified object from backend by reading in the list of objects,
+ * removing objname and writing the remainder.
+ *
+ * If objname is NULL, remove the backend dbname.
+ */
+nwam_error_t
+nwam_remove_object_from_backend(char *dbname, char *objname, uint64_t flags)
+{
+ nwam_error_t err = nwam_check_auths(getuid(), B_TRUE, flags);
+
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_backend_door_call(NWAM_BACKEND_DOOR_CMD_REMOVE_REQ,
+ dbname, objname, flags, NULL));
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_enm.c b/usr/src/lib/libnwam/common/libnwam_enm.c
new file mode 100644
index 0000000000..8967496e63
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_enm.c
@@ -0,0 +1,658 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libscf.h>
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Functions to support creating, modifying, destroying, querying the
+ * state of and changing the state of ENM (External Network Modifier)
+ * objects. ENMs represent services or scripts that can be enabled
+ * either manually or conditionally for a combination of the set of
+ * available conditions (an IP address is present, a location is active etc).
+ */
+
+typedef nwam_error_t (*nwam_enm_prop_validate_func_t)(nwam_value_t);
+
+static nwam_error_t valid_enm_activation_mode(nwam_value_t);
+
+struct nwam_prop_table_entry enm_prop_table_entries[] = {
+ {NWAM_ENM_PROP_ACTIVATION_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
+ valid_enm_activation_mode,
+ "specifies the ENM activation mode - valid values are:\n"
+ "\'manual\', \'conditional-any\' and \'conditional-all\'",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_ENM_PROP_CONDITIONS, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, nwam_valid_condition,
+ "specifies the activation condition. Conditions are of the form:\n"
+ "ncp|ncu|enm|loc name is|is-not active\n"
+ "ip-address is|is-not|is-in-range|is-not-in-range| 1.2.3.4[/24]\n"
+ "advertised-domain is|is-not|contains|does-not-contain string\n"
+ "system-domain is|is-not|contains|does-not-contain string\n"
+ "essid is|is-not|contains|does-not-contain string\n"
+ "bssid is|is-not string",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_ENM_PROP_ENABLED, NWAM_VALUE_TYPE_BOOLEAN, B_TRUE, 0, 1,
+ nwam_valid_boolean,
+ "specifies if manual ENM is to be enabled",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_ENM_PROP_FMRI, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_fmri,
+ "specifies SMF FMRI of service to be enabled on ENM activation",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_ENM_PROP_START, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_file,
+ "specifies absolute path to start script to be run on ENM "
+ "activation",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_ENM_PROP_STOP, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_file,
+ "specifies absolute path to stop script to be run on ENM "
+ "deactivation",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY}
+};
+
+#define NWAM_NUM_ENM_PROPS (sizeof (enm_prop_table_entries) / \
+ sizeof (*enm_prop_table_entries))
+
+struct nwam_prop_table enm_prop_table =
+ { NWAM_NUM_ENM_PROPS, enm_prop_table_entries };
+
+static uint64_t
+nwam_enm_activation_to_flag(nwam_activation_mode_t activation)
+{
+ switch (activation) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ return (NWAM_FLAG_ACTIVATION_MODE_MANUAL);
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
+ return (NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ANY);
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
+ return (NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ALL);
+ default:
+ return (0);
+ }
+}
+
+nwam_error_t
+nwam_enm_read(const char *name, uint64_t flags, nwam_enm_handle_t *enmhp)
+{
+ return (nwam_read(NWAM_OBJECT_TYPE_ENM, NWAM_ENM_CONF_FILE, name,
+ flags, enmhp));
+}
+
+nwam_error_t
+nwam_enm_create(const char *name, const char *fmri, nwam_enm_handle_t *enmhp)
+{
+ nwam_error_t err;
+ nwam_value_t actval = NULL, falseval = NULL, fmrival = NULL;
+
+ assert(enmhp != NULL && name != NULL);
+
+ if ((err = nwam_create(NWAM_OBJECT_TYPE_ENM, NWAM_ENM_CONF_FILE, name,
+ enmhp)) != NWAM_SUCCESS)
+ return (err);
+
+ /*
+ * Create new object list for ENM. The initial activation mode is set,
+ * and the FMRI property is set, if specified.
+ */
+ if ((err = nwam_alloc_object_list(&((*enmhp)->nwh_data)))
+ != NWAM_SUCCESS)
+ goto finish;
+
+ if ((err = nwam_value_create_uint64(NWAM_ACTIVATION_MODE_MANUAL,
+ &actval)) != NWAM_SUCCESS ||
+ ((fmri != NULL) &&
+ (err = nwam_value_create_string((char *)fmri, &fmrival))
+ != NWAM_SUCCESS) ||
+ (err = nwam_value_create_boolean(B_FALSE, &falseval))
+ != NWAM_SUCCESS) {
+ goto finish;
+ }
+ if ((err = nwam_set_prop_value((*enmhp)->nwh_data,
+ NWAM_ENM_PROP_ACTIVATION_MODE, actval)) == NWAM_SUCCESS &&
+ (err = nwam_set_prop_value((*enmhp)->nwh_data,
+ NWAM_ENM_PROP_ENABLED, falseval)) == NWAM_SUCCESS) {
+ if (fmri != NULL) {
+ err = nwam_set_prop_value((*enmhp)->nwh_data,
+ NWAM_ENM_PROP_FMRI, fmrival);
+ }
+ }
+
+finish:
+ nwam_value_free(actval);
+ nwam_value_free(falseval);
+ if (fmrival != NULL)
+ nwam_value_free(fmrival);
+
+ if (err != NWAM_SUCCESS) {
+ nwam_enm_free(*enmhp);
+ *enmhp = NULL;
+ }
+
+ return (err);
+}
+
+nwam_error_t
+nwam_enm_get_name(nwam_enm_handle_t enmh, char **namep)
+{
+ return (nwam_get_name(enmh, namep));
+}
+
+nwam_error_t
+nwam_enm_set_name(nwam_enm_handle_t enmh, const char *name)
+{
+ return (nwam_set_name(enmh, name));
+}
+
+boolean_t
+nwam_enm_can_set_name(nwam_enm_handle_t enmh)
+{
+ return (!enmh->nwh_committed);
+}
+
+/* ARGSUSED2 */
+static int
+enm_selectcb(struct nwam_handle *hp, uint64_t flags, void *data)
+{
+ nwam_enm_handle_t enmh = hp;
+ uint64_t activation, actflag, walkfilter;
+ nwam_value_t actval;
+
+ /*
+ * Get a bitmapped flag value corresponding to this enm's
+ * activation value - if the activation value is not recognized,
+ * actflag will be set to 0, and will thus fail to match
+ * any bit flag passed in by the caller.
+ */
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ACTIVATION_MODE,
+ &actval) != NWAM_SUCCESS) {
+ return (NWAM_INVALID_ARG);
+ }
+ if (nwam_value_get_uint64(actval, &activation) != NWAM_SUCCESS) {
+ nwam_value_free(actval);
+ return (NWAM_INVALID_ARG);
+ }
+
+ actflag = nwam_enm_activation_to_flag(activation);
+ nwam_value_free(actval);
+ if ((walkfilter = flags & NWAM_WALK_FILTER_MASK) == 0)
+ walkfilter = NWAM_FLAG_ACTIVATION_MODE_ALL;
+ if (actflag & walkfilter)
+ return (NWAM_SUCCESS);
+ return (NWAM_INVALID_ARG);
+}
+
+nwam_error_t
+nwam_walk_enms(int(*cb)(nwam_enm_handle_t, void *), void *data, uint64_t flags,
+ int *retp)
+{
+ nwam_error_t err = nwam_valid_flags(flags,
+ NWAM_FLAG_ACTIVATION_MODE_ALL | NWAM_FLAG_BLOCKING);
+
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_walk(NWAM_OBJECT_TYPE_ENM, NWAM_ENM_CONF_FILE,
+ cb, data, flags, retp, enm_selectcb));
+}
+
+void
+nwam_enm_free(nwam_enm_handle_t enmh)
+{
+ nwam_free(enmh);
+}
+
+nwam_error_t
+nwam_enm_copy(nwam_enm_handle_t oldenmh, const char *newname,
+ nwam_enm_handle_t *newenmhp)
+{
+ return (nwam_copy(NWAM_ENM_CONF_FILE, oldenmh, newname, newenmhp));
+}
+
+nwam_error_t
+nwam_enm_delete_prop(nwam_enm_handle_t enmh, const char *propname)
+{
+ nwam_error_t err;
+ boolean_t ro;
+ void *olddata;
+ boolean_t manual;
+
+ assert(enmh != NULL && propname != NULL);
+
+ if ((err = nwam_enm_prop_read_only(propname, &ro)) != NWAM_SUCCESS)
+ return (err);
+ if (ro) {
+ /*
+ * If the activation-mode is not manual, allow the enabled
+ * property to be deleted.
+ */
+ if (strcmp(propname, NWAM_ENM_PROP_ENABLED) != 0)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ if ((err = nwam_enm_is_manual(enmh, &manual)) != NWAM_SUCCESS)
+ return (err);
+ if (manual)
+ return (NWAM_ENTITY_READ_ONLY);
+ }
+
+ /*
+ * Duplicate data, remove property and validate. If validation
+ * fails, revert to data duplicated prior to remove.
+ */
+ if ((err = nwam_dup_object_list(enmh->nwh_data, &olddata))
+ != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_delete_prop(enmh->nwh_data, propname))
+ != NWAM_SUCCESS) {
+ nwam_free_object_list(enmh->nwh_data);
+ enmh->nwh_data = olddata;
+ return (err);
+ }
+ if ((err = nwam_enm_validate(enmh, NULL)) != NWAM_SUCCESS) {
+ nwam_free_object_list(enmh->nwh_data);
+ enmh->nwh_data = olddata;
+ return (err);
+ }
+ nwam_free_object_list(olddata);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_enm_set_prop_value(nwam_enm_handle_t enmh, const char *propname,
+ nwam_value_t value)
+{
+ nwam_error_t err;
+ boolean_t ro;
+
+ assert(enmh != NULL && propname != NULL && value != NULL);
+
+ if ((err = nwam_enm_validate_prop(enmh, propname, value))
+ != NWAM_SUCCESS ||
+ (err = nwam_enm_prop_read_only(propname, &ro)) != NWAM_SUCCESS)
+ return (err);
+ if (ro)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ return (nwam_set_prop_value(enmh->nwh_data, propname, value));
+}
+
+nwam_error_t
+nwam_enm_get_prop_value(nwam_enm_handle_t enmh, const char *propname,
+ nwam_value_t *valuep)
+{
+ return (nwam_get_prop_value(enmh->nwh_data, propname, valuep));
+}
+
+nwam_error_t
+nwam_enm_walk_props(nwam_enm_handle_t enmh,
+ int (*cb)(const char *, nwam_value_t, void *),
+ void *data, uint64_t flags, int *retp)
+{
+ return (nwam_walk_props(enmh, cb, data, flags, retp));
+}
+
+nwam_error_t
+nwam_enm_commit(nwam_enm_handle_t enmh, uint64_t flags)
+{
+ nwam_error_t err;
+
+ assert(enmh != NULL && enmh->nwh_data != NULL);
+
+ if ((err = nwam_enm_validate(enmh, NULL)) != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_commit(NWAM_ENM_CONF_FILE, enmh, flags));
+}
+
+nwam_error_t
+nwam_enm_destroy(nwam_enm_handle_t enmh, uint64_t flags)
+{
+ return (nwam_destroy(NWAM_ENM_CONF_FILE, enmh, flags));
+}
+
+nwam_error_t
+nwam_enm_get_prop_description(const char *propname, const char **descriptionp)
+{
+ return (nwam_get_prop_description(enm_prop_table, propname,
+ descriptionp));
+}
+
+nwam_error_t
+nwam_enm_prop_read_only(const char *propname, boolean_t *readp)
+{
+ return (nwam_prop_read_only(enm_prop_table, propname, readp));
+}
+
+/* Property-specific value validation functions follow */
+
+static nwam_error_t
+valid_enm_activation_mode(nwam_value_t value)
+{
+ uint64_t activation_mode;
+
+ if (nwam_value_get_uint64(value, &activation_mode) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ switch (activation_mode) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
+ return (NWAM_SUCCESS);
+ }
+ return (NWAM_ENTITY_INVALID_VALUE);
+}
+
+nwam_error_t
+nwam_enm_validate(nwam_enm_handle_t enmh, const char **errpropp)
+{
+ uint64_t activation;
+ nwam_value_t activationval, enabledval, fmrival = NULL, startval = NULL;
+ nwam_value_t conditionval = NULL;
+ char **conditions, *name;
+ uint_t i, numvalues;
+ nwam_condition_object_type_t object_type;
+ nwam_condition_t condition;
+
+ assert(enmh != NULL);
+
+ /*
+ * Make sure enm is internally consistent: must have either
+ * an fmri or a start string; and if activation type is conditional,
+ * the condition string must be specified.
+ */
+ if ((nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_FMRI, &fmrival)
+ != NWAM_SUCCESS) &&
+ (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_START, &startval)
+ != NWAM_SUCCESS)) {
+ if (fmrival != NULL) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_START;
+ nwam_value_free(fmrival);
+ } else {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_FMRI;
+ }
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ if (fmrival != NULL)
+ nwam_value_free(fmrival);
+ if (startval != NULL)
+ nwam_value_free(startval);
+
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ACTIVATION_MODE,
+ &activationval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_ACTIVATION_MODE;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ if (nwam_value_get_uint64(activationval, &activation)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_ACTIVATION_MODE;
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ nwam_value_free(activationval);
+
+ if (activation == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY ||
+ activation == NWAM_ACTIVATION_MODE_CONDITIONAL_ALL) {
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_CONDITIONS,
+ &conditionval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_CONDITIONS;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ /*
+ * Are conditions self-referential? In other words, do any
+ * of the activation conditions refer to this ENM?
+ */
+ if (nwam_value_get_string_array(conditionval, &conditions,
+ &numvalues) != NWAM_SUCCESS) {
+ nwam_value_free(conditionval);
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_CONDITIONS;
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ if (nwam_enm_get_name(enmh, &name) != NWAM_SUCCESS) {
+ nwam_value_free(conditionval);
+ return (NWAM_INVALID_ARG);
+ }
+ for (i = 0; i < numvalues; i++) {
+ char *object_name = NULL;
+
+ if (nwam_condition_string_to_condition(conditions[i],
+ &object_type, &condition, &object_name)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_CONDITIONS;
+ free(name);
+ nwam_value_free(conditionval);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ if (object_name != NULL &&
+ object_type == NWAM_CONDITION_OBJECT_TYPE_ENM &&
+ strcmp(object_name, name) == 0) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_CONDITIONS;
+ free(name);
+ free(object_name);
+ nwam_value_free(conditionval);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ free(object_name);
+ }
+ free(name);
+ nwam_value_free(conditionval);
+ }
+
+ if (activation == NWAM_ACTIVATION_MODE_MANUAL) {
+ if (nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ENABLED,
+ &enabledval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_ENM_PROP_ENABLED;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ nwam_value_free(enabledval);
+ }
+
+ return (nwam_validate(enm_prop_table, enmh, errpropp));
+}
+
+nwam_error_t
+nwam_enm_validate_prop(nwam_enm_handle_t enmh, const char *propname,
+ nwam_value_t value)
+{
+ assert(enmh != NULL);
+
+ return (nwam_validate_prop(enm_prop_table, enmh, propname, value));
+}
+
+/*
+ * Given a property, return expected property data type
+ */
+nwam_error_t
+nwam_enm_get_prop_type(const char *propname, nwam_value_type_t *typep)
+{
+ return (nwam_get_prop_type(enm_prop_table, propname, typep));
+}
+
+nwam_error_t
+nwam_enm_prop_multivalued(const char *propname, boolean_t *multip)
+{
+ return (nwam_prop_multivalued(enm_prop_table, propname, multip));
+}
+
+/*
+ * Determine if the ENM has manual activation-mode or not.
+ */
+nwam_error_t
+nwam_enm_is_manual(nwam_enm_handle_t enmh, boolean_t *manualp)
+{
+ nwam_error_t err;
+ nwam_value_t actval;
+ uint64_t activation;
+
+ assert(enmh != NULL);
+
+ if ((err = nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ACTIVATION_MODE,
+ &actval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(actval, &activation);
+ nwam_value_free(actval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ if (activation == NWAM_ACTIVATION_MODE_MANUAL)
+ *manualp = B_TRUE;
+ else
+ *manualp = B_FALSE;
+ return (NWAM_SUCCESS);
+}
+
+/* Determine if ENM is enabled or not */
+static nwam_error_t
+nwam_enm_is_enabled(nwam_enm_handle_t enmh, boolean_t *enabledp)
+{
+ nwam_error_t err;
+ nwam_value_t enabledval;
+
+ assert(enmh != NULL);
+
+ if ((err = nwam_enm_get_prop_value(enmh, NWAM_ENM_PROP_ENABLED,
+ &enabledval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_boolean(enabledval, enabledp);
+ nwam_value_free(enabledval);
+ return (err);
+}
+
+/* Update the enabled property */
+static nwam_error_t
+nwam_enm_update_enabled(nwam_enm_handle_t enmh, boolean_t enabled)
+{
+ nwam_error_t err;
+ nwam_value_t enabledval;
+
+ if ((err = nwam_value_create_boolean(enabled, &enabledval))
+ != NWAM_SUCCESS)
+ return (err);
+ err = nwam_set_prop_value(enmh->nwh_data, NWAM_ENM_PROP_ENABLED,
+ enabledval);
+ nwam_value_free(enabledval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ return (nwam_enm_commit(enmh, NWAM_FLAG_ENTITY_ENABLE));
+}
+
+nwam_error_t
+nwam_enm_enable(nwam_enm_handle_t enmh)
+{
+ nwam_error_t err;
+ boolean_t manual, enabled;
+
+ assert(enmh != NULL);
+
+ /* Only enms with manual activation-mode can be enabled */
+ if ((err = nwam_enm_is_manual(enmh, &manual)) != NWAM_SUCCESS)
+ return (err);
+ if (!manual)
+ return (NWAM_ENTITY_NOT_MANUAL);
+
+ /* Make sure ENM is not enabled */
+ if ((err = nwam_enm_is_enabled(enmh, &enabled)) != NWAM_SUCCESS)
+ return (err);
+ if (enabled)
+ return (NWAM_SUCCESS);
+
+ if ((err = nwam_enm_update_enabled(enmh, B_TRUE)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_enable(NULL, enmh);
+
+ /* nwamd may not be running, that's okay. */
+ if (err == NWAM_ERROR_BIND)
+ return (NWAM_SUCCESS);
+ else
+ return (err);
+}
+
+nwam_error_t
+nwam_enm_disable(nwam_enm_handle_t enmh)
+{
+ nwam_error_t err;
+ boolean_t manual, enabled;
+
+ assert(enmh != NULL);
+
+ /* Only enms with manual activation-mode can be disabled */
+ if ((err = nwam_enm_is_manual(enmh, &manual)) != NWAM_SUCCESS)
+ return (err);
+ if (!manual)
+ return (NWAM_ENTITY_NOT_MANUAL);
+
+ /* Make sure ENM is enabled */
+ if ((err = nwam_enm_is_enabled(enmh, &enabled)) != NWAM_SUCCESS)
+ return (err);
+ if (!enabled)
+ return (NWAM_SUCCESS);
+
+ if ((err = nwam_enm_update_enabled(enmh, B_FALSE)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_disable(NULL, enmh);
+
+ /* nwamd may not be running, that's okay. */
+ if (err == NWAM_ERROR_BIND)
+ return (NWAM_SUCCESS);
+ else
+ return (err);
+}
+
+nwam_error_t
+nwam_enm_get_default_proplist(const char ***prop_list, uint_t *numvaluesp)
+{
+ return (nwam_get_default_proplist(enm_prop_table,
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY, prop_list, numvaluesp));
+}
+
+nwam_error_t
+nwam_enm_get_state(nwam_enm_handle_t enmh, nwam_state_t *statep,
+ nwam_aux_state_t *auxp)
+{
+ return (nwam_get_state(NULL, enmh, statep, auxp));
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_error.c b/usr/src/lib/libnwam/common/libnwam_error.c
new file mode 100644
index 0000000000..c80bb8a7c8
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_error.c
@@ -0,0 +1,78 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <libnwam.h>
+#include <libintl.h>
+
+static struct nwam_error_info {
+ nwam_error_t error_code;
+ const char *error_desc;
+} nwam_errors[] = {
+ {NWAM_SUCCESS, "no error"},
+ {NWAM_LIST_END, "end of list reached"},
+ {NWAM_INVALID_HANDLE, "entity handle is invalid"},
+ {NWAM_HANDLE_UNBOUND, "handle not bound to entity"},
+ {NWAM_INVALID_ARG, "argument is invalid"},
+ {NWAM_PERMISSION_DENIED, "Insufficient permissions for action"},
+ {NWAM_NO_MEMORY, "out of memory"},
+ {NWAM_ENTITY_EXISTS, "entity exists"},
+ {NWAM_ENTITY_IN_USE, "entity in use"},
+ {NWAM_ENTITY_COMMITTED, "entity already committed"},
+ {NWAM_ENTITY_NOT_FOUND, "entity not found"},
+ {NWAM_ENTITY_TYPE_MISMATCH, "entity type mismatch"},
+ {NWAM_ENTITY_INVALID, "validation of entity failed"},
+ {NWAM_ENTITY_INVALID_MEMBER, "entity has invalid member"},
+ {NWAM_ENTITY_INVALID_STATE, "entity is in incorrect state"},
+ {NWAM_ENTITY_INVALID_VALUE, "validation of entity value failed"},
+ {NWAM_ENTITY_MISSING_MEMBER, "entity is missing required member"},
+ {NWAM_ENTITY_NO_VALUE, "no value associated with entity"},
+ {NWAM_ENTITY_MULTIPLE_VALUES, "multiple values for entity"},
+ {NWAM_ENTITY_READ_ONLY, "entity is read only"},
+ {NWAM_ENTITY_NOT_DESTROYABLE, "entity cannot be destroyed"},
+ {NWAM_ENTITY_NOT_MANUAL, "entity cannot be manually enabled/disabled"},
+ {NWAM_WALK_HALTED, "callback function returned nonzero"},
+ {NWAM_ERROR_BIND, "could not bind to backend server"},
+ {NWAM_ERROR_BACKEND_INIT, "could not initialize backend"},
+ {NWAM_ERROR_INTERNAL, "internal error"}
+};
+
+#define NWAM_NUM_ERRORS (sizeof (nwam_errors) / sizeof (*nwam_errors))
+
+const char *
+nwam_strerror(nwam_error_t code)
+{
+ struct nwam_error_info *cur, *end;
+
+ cur = nwam_errors;
+ end = cur + NWAM_NUM_ERRORS;
+
+ for (; cur < end; cur++) {
+ if (code == cur->error_code)
+ return (dgettext(TEXT_DOMAIN, cur->error_desc));
+ }
+
+ return (dgettext(TEXT_DOMAIN, "unknown error"));
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_events.c b/usr/src/lib/libnwam/common/libnwam_events.c
new file mode 100644
index 0000000000..79aea0ffb7
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_events.c
@@ -0,0 +1,357 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/msg.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Implementation of event notification mechanism used by the GUI and
+ * nwamadm. Clients register for events via nwam_events_init() and
+ * unregister via nwam_events_fini(). nwamd sends events via nwam_event_send()
+ * and applications block waiting for a new event to be delivered in
+ * nwam_event_wait(). Events are implemented as System V message queues,
+ * one per event client. The event mechanism has to be resilient to
+ * nwamd restarts so that clients do not lose the event connection.
+ */
+
+#define NWAM_EVENT_MSG_DIR "/etc/svc/volatile/nwam/"
+#define NWAM_EVENT_MSG_FILE "nwam_event_msgs"
+#define NWAM_EVENT_MSG_FILE_PREFIX NWAM_EVENT_MSG_DIR NWAM_EVENT_MSG_FILE
+#define NWAM_EVENT_MAX_NUM_WLANS 32
+#define NWAM_EVENT_MAX_SIZE (sizeof (struct nwam_event) + \
+ (NWAM_EVENT_MAX_NUM_WLANS * sizeof (nwam_wlan_t)))
+#define NWAM_EVENT_WAIT_TIME 10
+#define NWAM_EVENT_MAX_NUM_PENDING 25
+
+/*
+ * This is protecting simultaneous access to the msqid and its configuration.
+ */
+static pthread_mutex_t event_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int event_msqid = -1;
+
+static nwam_error_t
+nwam_event_alloc(nwam_event_t *eventp)
+{
+ assert(eventp != NULL);
+
+ *eventp = calloc(1, NWAM_EVENT_MAX_SIZE);
+ if (*eventp == NULL)
+ return (NWAM_NO_MEMORY);
+ return (NWAM_SUCCESS);
+}
+
+void
+nwam_event_free(nwam_event_t event)
+{
+ if (event != NULL)
+ free(event);
+}
+
+/*
+ * Get next event in queue.
+ */
+nwam_error_t
+nwam_event_wait(nwam_event_t *eventp)
+{
+ nwam_error_t err;
+ nwam_event_t event;
+
+ assert(eventp != NULL);
+
+ if ((err = nwam_event_alloc(&event)) != NWAM_SUCCESS)
+ return (err);
+ while (msgrcv(event_msqid, (struct msgbuf *)event, NWAM_EVENT_MAX_SIZE,
+ 0, 0) == -1) {
+ switch (errno) {
+ case EAGAIN:
+ case EBUSY:
+ /*
+ * We see this errno eventhough it isn't
+ * documented. Try again. If this causes
+ * a busy loop then grab a trace otherwise
+ * it's a brace 'til we can figure out why it
+ * happens.
+ */
+ continue;
+
+ default:
+ nwam_event_free(event);
+ return (nwam_errno_to_nwam_error(errno));
+ }
+ }
+
+ /* Resize event down from maximum size */
+ if ((*eventp = realloc(event, event->nwe_size)) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Register for receipt of events from nwamd. Event delivery is
+ * done via a System V message queue.
+ */
+nwam_error_t
+nwam_events_init(void)
+{
+ char eventmsgfile[MAXPATHLEN];
+ nwam_error_t err;
+ nwam_error_t rc = NWAM_SUCCESS;
+ key_t key;
+
+ (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s.%d",
+ NWAM_EVENT_MSG_FILE_PREFIX, getpid());
+
+ (void) pthread_mutex_lock(&event_mutex);
+
+ if (event_msqid != -1) {
+ rc = NWAM_ENTITY_IN_USE;
+ goto exit;
+ }
+
+ if ((err = nwam_request_register_unregister
+ (NWAM_REQUEST_TYPE_EVENT_REGISTER, eventmsgfile)) != NWAM_SUCCESS) {
+ rc = err;
+ goto exit;
+ }
+
+ if ((key = ftok(eventmsgfile, 0)) == -1) {
+ rc = nwam_errno_to_nwam_error(errno);
+ goto exit;
+ }
+
+ /* Get system-wide message queue ID */
+ if ((event_msqid = msgget(key, 0444)) == -1) {
+ rc = nwam_errno_to_nwam_error(errno);
+ goto exit;
+ }
+
+exit:
+ (void) pthread_mutex_unlock(&event_mutex);
+
+ return (rc);
+}
+
+/*
+ * Un-register for receipt of events from nwamd. Make a request to nwamd
+ * to destroy the message queue.
+ */
+void
+nwam_events_fini(void)
+{
+ char eventmsgfile[MAXPATHLEN];
+
+ (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s.%d",
+ NWAM_EVENT_MSG_FILE_PREFIX, getpid());
+
+ (void) pthread_mutex_lock(&event_mutex);
+
+ (void) nwam_request_register_unregister
+ (NWAM_REQUEST_TYPE_EVENT_UNREGISTER, eventmsgfile);
+
+ event_msqid = -1;
+
+ (void) pthread_mutex_unlock(&event_mutex);
+}
+
+/*
+ * Create an event queue. Called by nwamd to create System V message queues
+ * for clients to listen for events.
+ */
+nwam_error_t
+nwam_event_queue_init(const char *eventmsgfile)
+{
+ int fd;
+ key_t key;
+
+ if ((fd = open(eventmsgfile, O_RDWR | O_CREAT | O_TRUNC, 0644)) == -1)
+ return (nwam_errno_to_nwam_error(errno));
+ (void) close(fd);
+
+ if ((key = ftok(eventmsgfile, 0)) == -1)
+ return (nwam_errno_to_nwam_error(errno));
+
+ if (msgget(key, 0644 | IPC_CREAT) == -1)
+ return (nwam_errno_to_nwam_error(errno));
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Send event to registered listeners via the set of registered System V
+ * message queues.
+ */
+nwam_error_t
+nwam_event_send(nwam_event_t event)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct msqid_ds buf;
+ key_t key;
+ int msqid;
+ char eventmsgfile[MAXPATHLEN];
+ nwam_error_t err = NWAM_SUCCESS;
+
+ if ((dirp = opendir(NWAM_EVENT_MSG_DIR)) == NULL) {
+ return (nwam_errno_to_nwam_error(errno));
+ }
+
+ /*
+ * For each file matching our event message queue file prefix,
+ * check the queue is still being read, and if so send the message.
+ */
+ while ((dp = readdir(dirp)) != NULL) {
+ if (strncmp(dp->d_name, NWAM_EVENT_MSG_FILE,
+ strlen(NWAM_EVENT_MSG_FILE)) != 0)
+ continue;
+
+ (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s/%s",
+ NWAM_EVENT_MSG_DIR, dp->d_name);
+
+ if ((key = ftok(eventmsgfile, 0)) == -1) {
+ int errno_save = errno;
+ syslog(LOG_INFO, "nwam_event_send: ftok: %s",
+ strerror(errno_save));
+ err = nwam_errno_to_nwam_error(errno_save);
+ continue;
+ }
+
+ if ((msqid = msgget(key, 0644)) == -1) {
+ int errno_save = errno;
+ syslog(LOG_INFO, "nwam_event_send: msgget: %s",
+ strerror(errno_save));
+ err = nwam_errno_to_nwam_error(errno_save);
+ continue;
+ }
+
+ /* Retrieve stats to analyse queue activity */
+ if (msgctl(msqid, IPC_STAT, &buf) == -1) {
+ int errno_save = errno;
+ syslog(LOG_INFO, "nwam_event_send: msgctl: %s",
+ strerror(errno_save));
+ err = nwam_errno_to_nwam_error(errno_save);
+ continue;
+ }
+ /*
+ * If buf.msg_qnum > NWAM_EVENT_MAX_NUM_PENDING
+ * _and_ msg_stime is more than 10s after msg_rtime -
+ * indicating message(s) have been hanging around unclaimed -
+ * we destroy the queue as the client has most likely gone
+ * away. This can happen if a registered client hits Ctrl^C.
+ */
+ if (buf.msg_qnum > NWAM_EVENT_MAX_NUM_PENDING &&
+ ((buf.msg_stime + NWAM_EVENT_WAIT_TIME) > buf.msg_rtime)) {
+ nwam_event_queue_fini(eventmsgfile);
+ continue;
+ }
+
+ /*
+ * This shouldn't ever block. If it does then log an error and
+ * clean up the queue.
+ */
+ if (msgsnd(msqid, (struct msgbuf *)event, event->nwe_size,
+ IPC_NOWAIT) == -1) {
+ int errno_save = errno;
+ syslog(LOG_ERR, "nwam_event_send: msgsnd: %s, "
+ "destroying message queue %s", strerror(errno_save),
+ eventmsgfile);
+ nwam_event_queue_fini(eventmsgfile);
+ err = nwam_errno_to_nwam_error(errno_save);
+ continue;
+ }
+
+ }
+ (void) closedir(dirp);
+
+ return (err);
+}
+
+/*
+ * Destroy an event queue. Called by nwamd to destroy the associated message
+ * queue.
+ */
+void
+nwam_event_queue_fini(const char *eventmsgfile)
+{
+ key_t key;
+ int msqid;
+
+ if ((key = ftok(eventmsgfile, 0)) != -1 &&
+ (msqid = msgget(key, 0644)) != -1 &&
+ msgctl(msqid, IPC_RMID, NULL) != -1)
+ (void) unlink(eventmsgfile);
+}
+
+/*
+ * Stop sending events. Called by nwamd to destroy each System V message queue
+ * registered.
+ */
+void
+nwam_event_send_fini(void)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ char eventmsgfile[MAXPATHLEN];
+
+ (void) pthread_mutex_lock(&event_mutex);
+
+ if ((dirp = opendir(NWAM_EVENT_MSG_DIR)) == NULL) {
+ (void) pthread_mutex_unlock(&event_mutex);
+ return;
+ }
+
+ /*
+ * For each file matching our event message queue file prefix,
+ * destroy the queue and message file.
+ */
+ while ((dp = readdir(dirp)) != NULL) {
+ if (strncmp(dp->d_name, NWAM_EVENT_MSG_FILE,
+ strlen(NWAM_EVENT_MSG_FILE)) != 0)
+ continue;
+
+ (void) snprintf(eventmsgfile, sizeof (eventmsgfile), "%s/%s",
+ NWAM_EVENT_MSG_DIR, dp->d_name);
+
+ nwam_event_queue_fini(eventmsgfile);
+ }
+ (void) pthread_mutex_unlock(&event_mutex);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_files.c b/usr/src/lib/libnwam/common/libnwam_files.c
new file mode 100644
index 0000000000..5727f68062
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_files.c
@@ -0,0 +1,951 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Implementation of files backend for libnwam configuration objects.
+ * /etc/dladm/datalink.conf-like format is used.
+ */
+#define NWAM_FILE_LINE_MAX 2048
+#define NWAM_FILE_PROP_ESCAPE '\\'
+#define NWAM_FILE_PROP_DELIMITER ';'
+#define NWAM_FILE_PROP_ASSIGN '='
+#define NWAM_FILE_VALUE_DELIMITER ','
+#define NWAM_FILE_BOOLEAN_TRUE "true"
+#define NWAM_FILE_BOOLEAN_FALSE "false"
+
+/*
+ * strtok_r-like function that takes a string, finds the next unescaped
+ * delimiter char after in, nullifies it and sets nextp to point to the
+ * remaining string (if any). Returns in, setting nextp to NULL if no such
+ * delimiter is found.
+ */
+char *
+nwam_tokenize_by_unescaped_delim(char *in, char delim, char **nextp)
+{
+ boolean_t escaped = B_FALSE;
+ size_t totlen;
+
+ if (in == NULL)
+ return (NULL);
+
+ totlen = strlen(in);
+
+ for (*nextp = in; (*nextp - in) < strlen(in); (*nextp)++) {
+ if ((*nextp)[0] == NWAM_FILE_PROP_ESCAPE) {
+ escaped = !escaped;
+ } else if (!escaped && (*nextp)[0] == delim) {
+ /* Nullify delimiter */
+ (*nextp)[0] = '\0';
+ /*
+ * If more string left to go, nextp points to string
+ * after delimiter, otherwise NULL.
+ */
+ (*nextp)++;
+ *nextp = ((*nextp - in) < totlen) ? (*nextp) : NULL;
+ return (in);
+ } else {
+ escaped = B_FALSE;
+ }
+ }
+ *nextp = NULL;
+ return (in);
+}
+
+/* Add escape chars to value string */
+static void
+value_add_escapes(char *in, char *out)
+{
+ int i, j = 0;
+
+ /*
+ * It is safe to use strlen() as we sanitycheck string length on value
+ * creation, so no string longer than NWAM_MAX_VALUE_LEN is accepted.
+ */
+ for (i = 0; i < strlen(in); i++) {
+ switch (in[i]) {
+ case NWAM_FILE_VALUE_DELIMITER:
+ case NWAM_FILE_PROP_DELIMITER:
+ case NWAM_FILE_PROP_ESCAPE:
+ out[j++] = NWAM_FILE_PROP_ESCAPE;
+ out[j++] = in[i];
+ break;
+ default:
+ out[j++] = in[i];
+ break;
+ }
+ }
+ out[j] = '\0';
+}
+
+static char *
+value_remove_escapes(char *in)
+{
+ char *out;
+ int i, j = 0;
+
+ if ((out = strdup(in)) == NULL)
+ return (NULL);
+
+ /*
+ * It is safe to use strlen() as we sanitycheck string length on value
+ * creation (i.e. before they are written to the file), so no string
+ * longer than NWAM_MAX_VALUE_LEN is accepted.
+ */
+ for (i = 0; i < strlen(in); i++) {
+ if (in[i] == NWAM_FILE_PROP_ESCAPE)
+ out[j++] = in[++i];
+ else
+ out[j++] = in[i];
+ }
+ out[j] = '\0';
+ return (out);
+}
+
+
+/*
+ * Parse line into name and object list of properties.
+ * Each line has the format:
+ *
+ * objname [prop=type:val1[,val2..];..]
+ */
+nwam_error_t
+nwam_line_to_object(char *line, char **objname, void *proplist)
+{
+ char *next = line, *prop, *nextprop, *propname, *proptypestr, *nextval;
+ char **valstr, **newvalstr;
+ boolean_t *valbool, *newvalbool;
+ int64_t *valint, *newvalint;
+ uint64_t *valuint, *newvaluint;
+ uint_t nelem, i;
+ nwam_value_type_t proptype;
+ nwam_value_t val = NULL;
+ nwam_error_t err;
+
+ if ((err = nwam_alloc_object_list(proplist)) != NWAM_SUCCESS)
+ return (err);
+
+ *objname = line;
+
+ if ((*objname = nwam_tokenize_by_unescaped_delim(line, '\t', &prop))
+ == NULL) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_ENTITY_INVALID);
+ }
+
+ while ((prop = nwam_tokenize_by_unescaped_delim(prop,
+ NWAM_FILE_PROP_DELIMITER, &nextprop)) != NULL) {
+ /*
+ * Parse property into name=type,val[,val]
+ */
+ if ((propname = nwam_tokenize_by_unescaped_delim(prop,
+ NWAM_FILE_PROP_ASSIGN, &next)) == NULL ||
+ (proptypestr = nwam_tokenize_by_unescaped_delim(next,
+ NWAM_FILE_VALUE_DELIMITER, &next)) == NULL) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_ENTITY_INVALID);
+ }
+ if ((proptype = nwam_string_to_value_type(proptypestr)) ==
+ NWAM_VALUE_TYPE_UNKNOWN) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_ENTITY_INVALID);
+ }
+ valbool = NULL;
+ valint = NULL;
+ valstr = NULL;
+ switch (proptype) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ valbool = calloc(NWAM_MAX_NUM_VALUES,
+ sizeof (boolean_t));
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ valint = calloc(NWAM_MAX_NUM_VALUES,
+ sizeof (int64_t));
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ valuint = calloc(NWAM_MAX_NUM_VALUES,
+ sizeof (uint64_t));
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ valstr = calloc(NWAM_MAX_NUM_VALUES,
+ sizeof (char *));
+ break;
+ default:
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ if (valbool == NULL && valint == NULL && valuint == NULL &&
+ valstr == NULL) {
+ /* Memory allocation failed */
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_NO_MEMORY);
+ }
+ nelem = 0;
+ while ((nextval = nwam_tokenize_by_unescaped_delim(next,
+ NWAM_FILE_VALUE_DELIMITER, &next)) != NULL) {
+ nelem++;
+ switch (proptype) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ if (strncmp(nextval, NWAM_FILE_BOOLEAN_TRUE,
+ strlen(nextval)) == 0) {
+ valbool[nelem - 1] = B_TRUE;
+ } else if (strncmp(nextval,
+ NWAM_FILE_BOOLEAN_FALSE, strlen(nextval))
+ == 0) {
+ valbool[nelem - 1] = B_FALSE;
+ } else {
+ nwam_free_object_list
+ (*((char **)proplist));
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ valint[nelem - 1] = (int64_t)atoll(nextval);
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ valuint[nelem - 1] = (uint64_t)atoll(nextval);
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ valstr[nelem - 1] =
+ value_remove_escapes(nextval);
+ break;
+ default:
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ }
+ switch (proptype) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ if ((newvalbool = realloc(valbool,
+ nelem * sizeof (boolean_t))) == NULL) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_NO_MEMORY);
+ }
+ if ((err = nwam_value_create_boolean_array(newvalbool,
+ nelem, &val)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value(*((char **)proplist),
+ propname, val)) != NWAM_SUCCESS) {
+ free(newvalbool);
+ nwam_value_free(val);
+ nwam_free_object_list(*((char **)proplist));
+ return (err);
+ }
+ free(newvalbool);
+ nwam_value_free(val);
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ if ((newvalint = realloc(valint,
+ nelem * sizeof (int64_t))) == NULL) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_NO_MEMORY);
+ }
+ if ((err = nwam_value_create_int64_array(newvalint,
+ nelem, &val)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value(*((char **)proplist),
+ propname, val)) != NWAM_SUCCESS) {
+ free(newvalint);
+ nwam_value_free(val);
+ nwam_free_object_list(*((char **)proplist));
+ return (err);
+ }
+ free(newvalint);
+ nwam_value_free(val);
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ if ((newvaluint = realloc(valuint,
+ nelem * sizeof (uint64_t))) == NULL) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_NO_MEMORY);
+ }
+ if ((err = nwam_value_create_uint64_array(newvaluint,
+ nelem, &val)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value(*((char **)proplist),
+ propname, val)) != NWAM_SUCCESS) {
+ free(newvaluint);
+ nwam_value_free(val);
+ nwam_free_object_list(*((char **)proplist));
+ return (err);
+ }
+ free(newvaluint);
+ nwam_value_free(val);
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ if ((newvalstr = realloc(valstr,
+ nelem * sizeof (char *))) == NULL) {
+ nwam_free_object_list(*((char **)proplist));
+ return (NWAM_NO_MEMORY);
+ }
+ if ((err = nwam_value_create_string_array(newvalstr,
+ nelem, &val)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value(*((char **)proplist),
+ propname, val)) != NWAM_SUCCESS) {
+ for (i = 0; i < nelem; i++)
+ free(newvalstr[i]);
+ free(newvalstr);
+ nwam_value_free(val);
+ nwam_free_object_list(*((char **)proplist));
+ return (err);
+ }
+ for (i = 0; i < nelem; i++)
+ free(newvalstr[i]);
+ free(newvalstr);
+ nwam_value_free(val);
+ break;
+ }
+ prop = nextprop;
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Create list of NCP files used for walk of NCPs and for case-insensitive
+ * matching of NCP name to file.
+ */
+static nwam_error_t
+create_ncp_file_list(char ***ncpfilesp, uint_t *num_filesp)
+{
+ DIR *dirp = NULL;
+ struct dirent *dp;
+ char *ncpname, **ncpfiles = NULL;
+ nwam_error_t err = NWAM_SUCCESS;
+ uint_t i;
+
+ ncpfiles = calloc(NWAM_MAX_NUM_OBJECTS, sizeof (char *));
+ if (ncpfiles == NULL)
+ return (NWAM_NO_MEMORY);
+ *num_filesp = 0;
+
+ /*
+ * Construct NCP list by finding all files in NWAM directory
+ * that match the NCP filename format.
+ */
+ if ((dirp = opendir(NWAM_CONF_DIR)) == NULL) {
+ err = nwam_errno_to_nwam_error(errno);
+ goto done;
+ }
+
+ while ((dp = readdir(dirp)) != NULL) {
+ uint_t filenamelen;
+
+ /* Ensure filename is valid */
+ if (nwam_ncp_file_to_name(dp->d_name, &ncpname) != NWAM_SUCCESS)
+ continue;
+ free(ncpname);
+ filenamelen = strlen(NWAM_CONF_DIR) + strlen(dp->d_name) + 1;
+ if ((ncpfiles[*num_filesp] = malloc(filenamelen)) == NULL) {
+ err = NWAM_NO_MEMORY;
+ goto done;
+ }
+ (void) strlcpy(ncpfiles[*num_filesp], NWAM_CONF_DIR,
+ strlen(NWAM_CONF_DIR) + 1);
+ (void) strlcpy(ncpfiles[*num_filesp] + strlen(NWAM_CONF_DIR),
+ dp->d_name, filenamelen - strlen(NWAM_CONF_DIR));
+ (*num_filesp)++;
+ }
+done:
+ if (dirp != NULL)
+ (void) closedir(dirp);
+
+ if (err != NWAM_SUCCESS) {
+ for (i = 0; i < *num_filesp; i++)
+ free(ncpfiles[i]);
+ free(ncpfiles);
+ } else {
+ *ncpfilesp = realloc(ncpfiles, sizeof (char *) * (*num_filesp));
+ if (*ncpfilesp == NULL)
+ err = NWAM_NO_MEMORY;
+ }
+ return (err);
+}
+
+/*
+ * Read object specified by objname from file, converting it to
+ * an object list. If filename is NULL, a list of configuration object
+ * containers is returned, represented as an object lists with elements "enms"
+ * "locs" and "ncps". Each of these is a list of configuration files for each
+ * object. This corresponds to the enm.conf file, loc.conf file and list of
+ * ncp conf files. If objname is NULL, read all objects, and create
+ * an nvlist with one element - "object-list" - which has as its values
+ * the names of the objects found. Otherwise obj points to an object list
+ * of properties for the first object in the file that case-insensitively
+ * matches objname. We write the found name into objname so that it can be
+ * returned to the caller (and set in the object handle).
+ */
+/* ARGSUSED2 */
+nwam_error_t
+nwam_read_object_from_files_backend(char *filename, char *objname,
+ uint64_t flags, void *obj)
+{
+ char line[NWAM_FILE_LINE_MAX];
+ char *cp, *foundobjname, **objnames = NULL, **ncpfiles = NULL;
+ uint_t num_files = 0;
+ FILE *fp = NULL;
+ nwam_error_t err;
+ void *objlist = NULL, *proplist = NULL;
+ uint_t i = 0, j = 0;
+ nwam_value_t objnamesval = NULL;
+
+ assert(obj != NULL);
+
+ *((char **)obj) = NULL;
+
+ if (filename == NULL) {
+ nwam_value_t enmval = NULL, locval = NULL, ncpval = NULL;
+
+ /*
+ * When the filename is not specified, it signifies a
+ * request for the list of configuration object containers -
+ * in this case files.
+ *
+ * A list of all object files is returned. For ENMs
+ * and locations, only the default loc.conf and enm.conf
+ * files are used, but for NCPs we need to walk the
+ * files in the NWAM directory retrieving each one that
+ * matches the NCP pattern.
+ */
+ if ((err = nwam_alloc_object_list(&objlist)) != NWAM_SUCCESS)
+ return (err);
+
+ if ((err = nwam_value_create_string(NWAM_ENM_CONF_FILE,
+ &enmval)) != NWAM_SUCCESS ||
+ (err = nwam_value_create_string(NWAM_LOC_CONF_FILE,
+ &locval)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value(objlist, NWAM_ENM_OBJECT_STRING,
+ enmval)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value(objlist, NWAM_LOC_OBJECT_STRING,
+ locval)) != NWAM_SUCCESS)
+ goto done_with_containers;
+
+ /*
+ * Construct NCP list by finding all files in NWAM directory
+ * that match the NCP filename format.
+ */
+ if ((err = create_ncp_file_list(&ncpfiles, &num_files))
+ != NWAM_SUCCESS)
+ goto done_with_containers;
+
+ if ((err = nwam_value_create_string_array(ncpfiles, num_files,
+ &ncpval)) == NWAM_SUCCESS) {
+ err = nwam_set_prop_value(objlist,
+ NWAM_NCP_OBJECT_STRING, ncpval);
+ }
+
+done_with_containers:
+ nwam_value_free(enmval);
+ nwam_value_free(locval);
+ nwam_value_free(ncpval);
+ if (ncpfiles != NULL) {
+ for (j = 0; j < num_files; j++)
+ free(ncpfiles[j]);
+ free(ncpfiles);
+ }
+ if (err != NWAM_SUCCESS)
+ nwam_free_object_list(objlist);
+ else
+ *((char **)obj) = objlist;
+ return (err);
+ }
+
+ if (objname == NULL) {
+ /* Allocate string array to store object names */
+ if ((objnames = calloc(NWAM_MAX_NUM_OBJECTS, sizeof (char *)))
+ == NULL)
+ return (NWAM_NO_MEMORY);
+ }
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ if (errno != ENOENT) {
+ if (objname == NULL)
+ free(objnames);
+ return (NWAM_ERROR_INTERNAL);
+ }
+
+ /*
+ * Check NCP file list in case filename passed in was derived
+ * from a case-insensitive NCP name.
+ */
+ if ((err = create_ncp_file_list(&ncpfiles, &num_files))
+ == NWAM_SUCCESS) {
+ for (j = 0; j < num_files; j++) {
+ if (strcasecmp(ncpfiles[j], filename) == 0) {
+ fp = fopen(ncpfiles[j], "r");
+ if (fp != NULL) {
+ /* Copy real filename back */
+ (void) strlcpy(filename,
+ ncpfiles[j],
+ strlen(filename) + 1);
+ break;
+ }
+ }
+ }
+ for (j = 0; j < num_files; j++)
+ free(ncpfiles[j]);
+ free(ncpfiles);
+ }
+ /* Return NOT_FOUND if file not found */
+ if (fp == NULL) {
+ if (objname == NULL)
+ free(objnames);
+ return (NWAM_ENTITY_NOT_FOUND);
+ }
+ }
+
+ while (fgets(line, sizeof (line), fp) != NULL) {
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ cp = line;
+
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '#' || *cp == '\0')
+ continue;
+
+ if ((err = nwam_line_to_object(cp, &foundobjname, &proplist))
+ != NWAM_SUCCESS)
+ goto done;
+
+ if (objname != NULL) {
+ /*
+ * Is this the specified object? If so set objname and
+ * obj and bail.
+ */
+ if (strcasecmp(objname, foundobjname) == 0) {
+ *((char **)obj) = proplist;
+ (void) strlcpy(objname, foundobjname,
+ NWAM_MAX_NAME_LEN);
+ break;
+ } else {
+ nwam_free_object_list(proplist);
+ }
+ } else {
+ objnames[i] = strdup(foundobjname);
+ nwam_free_object_list(proplist);
+ if (objnames[i] == NULL) {
+ err = NWAM_NO_MEMORY;
+ goto done;
+ }
+ i++;
+ }
+
+ }
+ if (objname == NULL) {
+ /*
+ * Allocate object list with one value named
+ * NWAM_OBJECT_NAMES_STRING - it's values are the names of
+ * the objects found.
+ */
+ if ((err = nwam_alloc_object_list(&objlist)) == NWAM_SUCCESS &&
+ (err = nwam_value_create_string_array(objnames, i,
+ &objnamesval)) == NWAM_SUCCESS) {
+ err = nwam_set_prop_value(objlist,
+ NWAM_OBJECT_NAMES_STRING, objnamesval);
+ }
+ }
+
+done:
+ if (fp != NULL)
+ (void) fclose(fp);
+
+ /*
+ * We're done, either we have success, and return our object list
+ * containing object names, or we have failure and we need to free
+ * the object list.
+ */
+ if (objname == NULL) {
+ for (j = 0; j < i; j++)
+ free(objnames[j]);
+ free(objnames);
+ nwam_value_free(objnamesval);
+ if (err == NWAM_SUCCESS) {
+ *((char **)obj) = objlist;
+ } else {
+ *((char **)obj) = NULL;
+ nwam_free_object_list(objlist);
+ }
+ } else {
+ /* Check if to-be-read object was not found */
+ if (*((char **)obj) == NULL && err == NWAM_SUCCESS)
+ return (NWAM_ENTITY_NOT_FOUND);
+ }
+
+ return (err);
+}
+
+nwam_error_t
+nwam_object_to_line(FILE *fp, const char *objname, void *proplist)
+{
+ char *propname, *lastpropname = NULL;
+ boolean_t *valbool;
+ int64_t *valint;
+ uint64_t *valuint;
+ char **valstr;
+ uint_t nelem, i;
+ nwam_value_t val;
+ nwam_value_type_t type;
+
+ (void) fprintf(fp, "%s\t", objname);
+
+ while (nwam_next_object_prop(proplist, lastpropname, &propname, &val)
+ == NWAM_SUCCESS) {
+
+ (void) fprintf(fp, "%s%c", propname, NWAM_FILE_PROP_ASSIGN);
+
+ if (nwam_value_get_type(val, &type) != NWAM_SUCCESS)
+ return (NWAM_INVALID_ARG);
+
+ switch (type) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ (void) fprintf(fp, "%s",
+ nwam_value_type_to_string(NWAM_VALUE_TYPE_BOOLEAN));
+ if (nwam_value_get_boolean_array(val, &valbool, &nelem)
+ != NWAM_SUCCESS) {
+ nwam_value_free(val);
+ return (NWAM_INVALID_ARG);
+ }
+ for (i = 0; i < nelem; i++) {
+ (void) fprintf(fp, "%c",
+ NWAM_FILE_VALUE_DELIMITER);
+ if (valbool[i]) {
+ (void) fprintf(fp,
+ NWAM_FILE_BOOLEAN_TRUE);
+ } else {
+ (void) fprintf(fp,
+ NWAM_FILE_BOOLEAN_FALSE);
+ }
+ }
+ break;
+
+ case NWAM_VALUE_TYPE_INT64:
+ (void) fprintf(fp, "%s",
+ nwam_value_type_to_string(NWAM_VALUE_TYPE_INT64));
+ if (nwam_value_get_int64_array(val, &valint, &nelem)
+ != NWAM_SUCCESS) {
+ nwam_value_free(val);
+ return (NWAM_INVALID_ARG);
+ }
+ for (i = 0; i < nelem; i++) {
+ (void) fprintf(fp, "%c%lld",
+ NWAM_FILE_VALUE_DELIMITER, valint[i]);
+ }
+ break;
+
+ case NWAM_VALUE_TYPE_UINT64:
+ (void) fprintf(fp, "%s",
+ nwam_value_type_to_string(NWAM_VALUE_TYPE_UINT64));
+ if (nwam_value_get_uint64_array(val, &valuint, &nelem)
+ != NWAM_SUCCESS) {
+ nwam_value_free(val);
+ return (NWAM_INVALID_ARG);
+ }
+ for (i = 0; i < nelem; i++) {
+ (void) fprintf(fp, "%c%lld",
+ NWAM_FILE_VALUE_DELIMITER, valuint[i]);
+ }
+ break;
+
+ case NWAM_VALUE_TYPE_STRING:
+ (void) fprintf(fp, "%s",
+ nwam_value_type_to_string(NWAM_VALUE_TYPE_STRING));
+ if (nwam_value_get_string_array(val, &valstr, &nelem)
+ != NWAM_SUCCESS) {
+ nwam_value_free(val);
+ return (NWAM_INVALID_ARG);
+ }
+ for (i = 0; i < nelem; i++) {
+ char evalstr[NWAM_MAX_VALUE_LEN];
+ /* Add escape chars as necessary */
+ value_add_escapes(valstr[i], evalstr);
+ (void) fprintf(fp, "%c%s",
+ NWAM_FILE_VALUE_DELIMITER, evalstr);
+ }
+ break;
+ default:
+ nwam_value_free(val);
+ return (NWAM_INVALID_ARG);
+ }
+ nwam_value_free(val);
+ (void) fprintf(fp, "%c", NWAM_FILE_PROP_DELIMITER);
+
+ lastpropname = propname;
+
+ }
+ (void) fprintf(fp, "\n");
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Write object specified by objname to file. If objname is NULL, objlist
+ * must be a list of lists, where each list corresponds to an
+ * object to write to the file. Otherwise objlist should point to a list of
+ * properties for the object specified by objname. The write operation is
+ * first done to filename.new, and if this succeeds, the file is renamed to
+ * filename. Since rename(2) is atomic, this approach guarantees a complete
+ * configuration will end up in filename as a result of an aborted operation.
+ */
+nwam_error_t
+nwam_write_object_to_files_backend(const char *filename, const char *objname,
+ uint64_t flags, void *objlist)
+{
+ void *proplist;
+ char *currobjname, *lastobjname = NULL;
+ int fd, cmd;
+ nwam_error_t err = NWAM_SUCCESS;
+ char *dir;
+ char tmpfilename[MAXPATHLEN], filename_copy[MAXPATHLEN];
+ FILE *fp;
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0};
+ struct flock fu = { F_UNLCK, SEEK_SET, 0, 0, 0};
+
+ assert(filename != NULL);
+
+ /* Create the directory in case it does not exist. */
+ (void) strlcpy(filename_copy, filename, MAXPATHLEN);
+ if ((dir = dirname(filename_copy)) == NULL)
+ return (nwam_errno_to_nwam_error(errno));
+ if (mkdir(dir, dirmode) != 0) {
+ if (errno != EEXIST)
+ return (nwam_errno_to_nwam_error(errno));
+ }
+
+ (void) snprintf(tmpfilename, MAXPATHLEN, "%s.new", filename);
+
+ if ((fd = open(tmpfilename, O_RDWR | O_CREAT | O_TRUNC, mode)) < 0)
+ return (nwam_errno_to_nwam_error(errno));
+
+ if ((fp = fdopen(fd, "w")) == NULL) {
+ err = nwam_errno_to_nwam_error(errno);
+ goto done;
+ }
+ /*
+ * Need to lock filename.new to prevent multiple commits colliding
+ * at this point.
+ */
+ if (flags & NWAM_FLAG_BLOCKING)
+ cmd = F_SETLKW;
+ else
+ cmd = F_SETLK;
+ if (fcntl(fd, cmd, &fl) != 0) {
+ if (errno == EAGAIN)
+ return (NWAM_ENTITY_IN_USE);
+ else
+ return (NWAM_ERROR_INTERNAL);
+ }
+
+ if (objname != NULL) {
+ /* Only one object to write */
+ err = nwam_object_to_line(fp, objname, objlist);
+ } else {
+ if (objlist == NULL) {
+ err = NWAM_SUCCESS;
+ goto done;
+ }
+ /* Otherwise, write each object in turn. */
+ while ((err = nwam_next_object_list(objlist, lastobjname,
+ &currobjname, &proplist)) == NWAM_SUCCESS) {
+ if ((err = nwam_object_to_line(fp, currobjname,
+ proplist)) != NWAM_SUCCESS)
+ break;
+ lastobjname = currobjname;
+ }
+ if (err == NWAM_LIST_END)
+ err = NWAM_SUCCESS;
+ }
+done:
+ if (err == NWAM_SUCCESS) {
+ if (rename(tmpfilename, filename) == 0) {
+ (void) fcntl(fd, F_SETLKW, &fu);
+ (void) fclose(fp);
+ return (NWAM_SUCCESS);
+ } else {
+ err = nwam_errno_to_nwam_error(errno);
+ }
+ }
+ (void) fcntl(fd, F_SETLKW, &fu);
+ (void) fclose(fp);
+ (void) unlink(tmpfilename);
+
+ return (err);
+}
+
+/*
+ * Read in all objects from file and update object corresponding to objname
+ * with properties recorded in proplist, and then write results to filename.
+ * If objname is empty, no object needs to be updated. If proplist is NULL,
+ * object is to be removed (this is done by simply not adding it to the list
+ * of objects).
+ */
+nwam_error_t
+nwam_update_object_in_files_backend(char *filename, char *objname,
+ uint64_t flags, void *proplist)
+{
+ nwam_error_t err;
+ void *objlist, *objnamelist;
+ char **object_names;
+ nwam_value_t value = NULL;
+ uint_t i, num_objects;
+
+ assert(filename != NULL);
+
+ /* If we find existing object, fail if creation was specified */
+ if (flags & NWAM_FLAG_CREATE) {
+ char discard_objname[NWAM_MAX_NAME_LEN];
+ void *discard_objlist;
+
+ (void) strlcpy(discard_objname, objname,
+ sizeof (discard_objname));
+ if ((err = nwam_read_object_from_files_backend(filename,
+ discard_objname, 0, &discard_objlist)) == NWAM_SUCCESS) {
+ nwam_free_object_list(discard_objlist);
+ return (NWAM_ENTITY_EXISTS);
+ }
+ }
+
+ /* Get existing list of object names (if any) */
+ err = nwam_read_object_from_files_backend(filename, NULL, flags,
+ &objnamelist);
+ switch (err) {
+ case NWAM_SUCCESS:
+ /*
+ * For each object name on list other than the one to be
+ * updated, add an object list consisting of its properties.
+ * The object to be updated (if any) will be added below.
+ */
+ if ((err = nwam_alloc_object_list(&objlist)) != NWAM_SUCCESS) {
+ nwam_free_object_list(objnamelist);
+ return (err);
+ }
+ if ((err = nwam_get_prop_value(objnamelist,
+ NWAM_OBJECT_NAMES_STRING, &value)) != NWAM_SUCCESS ||
+ (err = nwam_value_get_string_array(value, &object_names,
+ &num_objects)) != NWAM_SUCCESS) {
+ nwam_value_free(value);
+ nwam_free_object_list(objnamelist);
+ nwam_free_object_list(objlist);
+ return (err);
+ }
+ nwam_free_object_list(objnamelist);
+
+ for (i = 0; i < num_objects; i++) {
+ void *oproplist = NULL;
+
+ if (objname != NULL &&
+ strcmp(objname, object_names[i]) == 0)
+ continue;
+
+ if ((err = nwam_read_object_from_files_backend(filename,
+ object_names[i], flags, &oproplist))
+ != NWAM_SUCCESS ||
+ (err = nwam_object_list_add_object_list(objlist,
+ object_names[i], oproplist)) != NWAM_SUCCESS) {
+ nwam_free_object_list(oproplist);
+ nwam_free_object_list(objlist);
+ nwam_value_free(value);
+ return (err);
+ }
+ nwam_free_object_list(oproplist);
+ }
+ nwam_value_free(value);
+ break;
+
+ case NWAM_ENTITY_NOT_FOUND:
+ /*
+ * Just need to write/remove this single object.
+ */
+ return (nwam_write_object_to_files_backend(filename, objname,
+ flags, proplist));
+
+ default:
+ return (err);
+ }
+
+ /*
+ * Add the object to be updated to our list of objects if the
+ * property list is non-NULL (NULL signifies remove the object).
+ */
+ if (objname != NULL && proplist != NULL) {
+ if ((err = nwam_object_list_add_object_list(objlist,
+ (char *)objname, proplist)) != NWAM_SUCCESS) {
+ nwam_free_object_list(objlist);
+ return (err);
+ }
+ }
+
+ err = nwam_write_object_to_files_backend(filename, NULL, flags,
+ objlist);
+
+ nwam_free_object_list(objlist);
+
+ return (err);
+}
+
+/*
+ * Remove specified object from file by reading in the list of objects,
+ * removing objname and writing the remainder.
+ */
+nwam_error_t
+nwam_remove_object_from_files_backend(char *filename, char *objname,
+ uint64_t flags)
+{
+ int uerr;
+
+ assert(filename != NULL);
+
+ if (objname == NULL) {
+ /*
+ * NULL objname signifies remove file.
+ */
+ uerr = unlink(filename);
+ if (uerr != 0)
+ return (nwam_errno_to_nwam_error(errno));
+ return (NWAM_SUCCESS);
+ }
+
+ return (nwam_update_object_in_files_backend(filename, objname, flags,
+ NULL));
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_impl.h b/usr/src/lib/libnwam/common/libnwam_impl.h
new file mode 100644
index 0000000000..32854bb878
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_impl.h
@@ -0,0 +1,326 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains hidden implementation structures and APIs of libnwam,
+ * and is not installed in the proto area. Implementation is MT safe.
+ */
+
+
+#ifndef _LIBNWAM_IMPL_H
+#define _LIBNWAM_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * We separate global flags (which are applicable to all object types) from
+ * local flags (which only apply to specific object types). These definitions
+ * mask off the global vs. local portions of the flags value, with the former
+ * being the low-order 32 bits and the latter the high-order 32 bits.
+ */
+#define NWAM_FLAG_GLOBAL_MASK 0xFFFFFFFF
+#define NWAM_FLAG_LOCAL_MASK 0xFFFFFFFFULL << 32
+#define NWAM_WALK_FILTER_MASK NWAM_FLAG_LOCAL_MASK
+
+/*
+ * Maximum object size is the size of a maximally-sized name/values property
+ * multiplied by the maximum number of properties. The maximum object size
+ * and the maximum number of objects are used to determine how much space
+ * needs to be allocated for door calls to retrieve objects from the
+ * backend.
+ */
+#define NWAM_MAX_OBJECT_LEN \
+ ((NWAM_MAX_NAME_LEN + \
+ (NWAM_MAX_VALUE_LEN * NWAM_MAX_NUM_VALUES)) * \
+ NWAM_MAX_NUM_PROPERTIES)
+
+#define NWAM_MAX_NUM_OBJECTS 4192
+
+#define NWAM_MAX_OBJECT_LIST_LEN \
+ (NWAM_MAX_NUM_OBJECTS * NWAM_MAX_NAME_LEN)
+
+#define NWAM_BACKEND_DOOR_ARG_SIZE \
+ (sizeof (nwam_backend_door_arg_t) + \
+ (NWAM_MAX_OBJECT_LEN > NWAM_MAX_OBJECT_LIST_LEN ? \
+ NWAM_MAX_OBJECT_LEN : NWAM_MAX_OBJECT_LIST_LEN))
+
+
+#define NWAMD_DOOR_ARG_SIZE \
+ (sizeof (nwamd_door_arg_t) + \
+ (NWAMD_MAX_NUM_WLANS * sizeof (nwam_wlan_t));
+
+#define NWAM_CONF_DIR "/etc/nwam/"
+
+#define NWAM_LOC_OBJECT_STRING "loc"
+#define NWAM_LOC_CONF_FILE NWAM_CONF_DIR "loc.conf"
+
+struct nwam_handle {
+ nwam_object_type_t nwh_object_type;
+ char nwh_name[NWAM_MAX_NAME_LEN];
+ boolean_t nwh_committed;
+ void *nwh_data;
+};
+
+#define NWAM_OBJECT_NAMES_STRING "object-names"
+#define NWAM_NCP_OBJECT_STRING "ncp"
+#define NWAM_NCP_CONF_FILE_PRE "ncp-"
+#define NWAM_NCP_CONF_FILE_SUF ".conf"
+#define NWAM_NCU_LINK_NAME_PRE "link:"
+#define NWAM_NCU_INTERFACE_NAME_PRE "interface:"
+
+struct nwam_value {
+ nwam_value_type_t nwv_value_type;
+ uint_t nwv_value_numvalues;
+ union {
+ boolean_t *nwv_boolean;
+ int64_t *nwv_int64;
+ uint64_t *nwv_uint64;
+ char **nwv_string;
+ } nwv_values;
+};
+
+/* Used in property table retrieval of property attributes */
+#define NWAM_TYPE_ANY 1
+#define NWAM_CLASS_ANY 1
+
+typedef nwam_error_t (*nwam_prop_validate_func_t)(nwam_value_t);
+
+/* Used to hold validation/description data for properties */
+struct nwam_prop_table_entry {
+ const char *prop_name;
+ nwam_value_type_t prop_type;
+ boolean_t prop_is_readonly;
+ uint_t prop_min_numvalues;
+ uint_t prop_max_numvalues;
+ nwam_prop_validate_func_t prop_validate;
+ const char *prop_description;
+ uint64_t prop_type_membership;
+ uint64_t prop_class_membership;
+};
+
+struct nwam_prop_table {
+ uint_t num_entries;
+ struct nwam_prop_table_entry *entries;
+};
+
+#define NWAM_ENM_OBJECT_STRING "enm"
+#define NWAM_ENM_CONF_FILE NWAM_CONF_DIR "enm.conf"
+
+#define NWAM_KNOWN_WLAN_OBJECT_STRING "known-wlan"
+#define NWAM_KNOWN_WLAN_CONF_FILE NWAM_CONF_DIR "known-wlan.conf"
+
+/* Definitions that are used to map uint64 property values to strings */
+#define NWAM_ACTIVATION_MODE_MANUAL_STRING "manual"
+#define NWAM_ACTIVATION_MODE_SYSTEM_STRING "system"
+#define NWAM_ACTIVATION_MODE_PRIORITIZED_STRING "prioritized"
+#define NWAM_ACTIVATION_MODE_CONDITIONAL_ANY_STRING "conditional-any"
+#define NWAM_ACTIVATION_MODE_CONDITIONAL_ALL_STRING "conditional-all"
+
+#define NWAM_CONDITION_IS_STRING "is"
+#define NWAM_CONDITION_IS_NOT_STRING "is-not"
+#define NWAM_CONDITION_IS_IN_RANGE_STRING "is-in-range"
+#define NWAM_CONDITION_IS_NOT_IN_RANGE_STRING "is-not-in-range"
+#define NWAM_CONDITION_CONTAINS_STRING "contains"
+#define NWAM_CONDITION_DOES_NOT_CONTAIN_STRING "does-not-contain"
+
+#define NWAM_CONDITION_OBJECT_TYPE_NCP_STRING "ncp"
+#define NWAM_CONDITION_OBJECT_TYPE_NCU_STRING "ncu"
+#define NWAM_CONDITION_OBJECT_TYPE_ENM_STRING "enm"
+#define NWAM_CONDITION_OBJECT_TYPE_LOC_STRING "loc"
+#define NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS_STRING "ip-address"
+#define NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN_STRING "advertised-domain"
+#define NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN_STRING "system-domain"
+#define NWAM_CONDITION_OBJECT_TYPE_ESSID_STRING "essid"
+#define NWAM_CONDITION_OBJECT_TYPE_BSSID_STRING "bssid"
+
+#define NWAM_CONDITION_ACTIVE_STRING "active"
+
+#define NWAM_NAMESERVICES_DNS_STRING "dns"
+#define NWAM_NAMESERVICES_FILES_STRING "files"
+#define NWAM_NAMESERVICES_NIS_STRING "nis"
+#define NWAM_NAMESERVICES_LDAP_STRING "ldap"
+
+#define NWAM_CONFIGSRC_MANUAL_STRING "manual"
+#define NWAM_CONFIGSRC_DHCP_STRING "dhcp"
+
+#define NWAM_NCU_TYPE_LINK_STRING "link"
+#define NWAM_NCU_TYPE_INTERFACE_STRING "interface"
+
+#define NWAM_NCU_CLASS_PHYS_STRING "phys"
+#define NWAM_NCU_CLASS_IP_STRING "ip"
+
+#define NWAM_IP_VERSION_IPV4_STRING "ipv4"
+#define NWAM_IP_VERSION_IPV6_STRING "ipv6"
+
+#define NWAM_ADDRSRC_DHCP_STRING "dhcp"
+#define NWAM_ADDRSRC_AUTOCONF_STRING "autoconf"
+#define NWAM_ADDRSRC_STATIC_STRING "static"
+
+#define NWAM_PRIORITY_MODE_EXCLUSIVE_STRING "exclusive"
+#define NWAM_PRIORITY_MODE_SHARED_STRING "shared"
+#define NWAM_PRIORITY_MODE_ALL_STRING "all"
+
+/*
+ * Functions that interact with nwamd's door server to request
+ * object actions, states or to register for receipt of events from nwamd.
+ * See libnwam_door.c.
+ */
+extern nwam_error_t nwam_request_register_unregister(nwam_request_type_t,
+ const char *);
+extern nwam_error_t nwam_request_action(nwam_object_type_t, const char *,
+ const char *, nwam_action_t);
+extern nwam_error_t nwam_request_state(nwam_object_type_t, const char *,
+ const char *, nwam_state_t *, nwam_aux_state_t *);
+extern nwam_error_t nwam_request_wlan(nwam_request_type_t, const char *,
+ const char *, const char *, uint32_t, uint_t, const char *, boolean_t);
+extern nwam_error_t nwam_request_wlan_scan_results(const char *name,
+ uint_t *, nwam_wlan_t **);
+extern nwam_error_t nwam_request_active_priority_group(int64_t *);
+
+/*
+ * Functions that access and manipulate backend representation of data -
+ * see libnwam_backend.c.
+ */
+extern nwam_error_t nwam_read_object_from_backend(char *, char *,
+ uint64_t, void *);
+extern nwam_error_t nwam_update_object_in_backend(char *, char *,
+ uint64_t, void *);
+extern nwam_error_t nwam_remove_object_from_backend(char *, char *,
+ uint64_t);
+
+/*
+ * Functions that handle files-specific backend persistent representation
+ * of data - see libnwam_files.c.
+ */
+extern nwam_error_t nwam_read_object_from_files_backend(char *,
+ char *, uint64_t, void *);
+extern nwam_error_t nwam_update_object_in_files_backend(char *,
+ char *, uint64_t, void *);
+extern nwam_error_t nwam_remove_object_from_files_backend(char *,
+ char *, uint64_t);
+
+/*
+ * Utility functions for nwam data (values and lists of values) associated
+ * with objects - see libnwam_values.c.
+ */
+nwam_error_t nwam_alloc_object_list(void *);
+void nwam_free_object_list(void *);
+nwam_error_t nwam_object_list_add_object_list(void *, char *, void *);
+nwam_error_t nwam_object_list_remove_object_list(void *, char *);
+nwam_error_t nwam_dup_object_list(void *, void *);
+nwam_error_t nwam_next_object_list(void *, char *, char **, void *);
+nwam_error_t nwam_next_object_prop(void *, char *, char **, nwam_value_t *);
+extern nwam_error_t nwam_pack_object_list(void *, char **, size_t *);
+extern nwam_error_t nwam_unpack_object_list(char *, size_t, void *);
+
+extern const char *nwam_value_type_to_string(nwam_value_type_t);
+extern nwam_value_type_t nwam_string_to_value_type(const char *);
+extern nwam_error_t nwam_delete_prop(void *, const char *);
+extern nwam_error_t nwam_set_prop_value(void *, const char *, nwam_value_t);
+extern nwam_error_t nwam_get_prop_value(void *, const char *, nwam_value_t *);
+
+/*
+ * Utility functions for nwam objects (NCUs, ENMs, locations and known WLANs).
+ * See libnwam_object.c.
+ */
+nwam_error_t nwam_handle_create(nwam_object_type_t, const char *,
+ struct nwam_handle **);
+nwam_error_t nwam_read(nwam_object_type_t, const char *, const char *,
+ uint64_t, struct nwam_handle **);
+nwam_error_t nwam_create(nwam_object_type_t, const char *, const char *,
+ struct nwam_handle **);
+nwam_error_t nwam_get_name(struct nwam_handle *, char **);
+nwam_error_t nwam_set_name(struct nwam_handle *, const char *);
+nwam_error_t nwam_walk(nwam_object_type_t, const char *,
+ int(*)(struct nwam_handle *, void *), void *, uint64_t, int *,
+ int(*)(struct nwam_handle *, uint64_t, void *));
+void nwam_free(struct nwam_handle *);
+nwam_error_t nwam_copy(const char *, struct nwam_handle *, const char *,
+ struct nwam_handle **);
+nwam_error_t nwam_walk_props(struct nwam_handle *,
+ int(*)(const char *, nwam_value_t, void *), void *, uint64_t, int *);
+nwam_error_t nwam_commit(const char *, struct nwam_handle *, uint64_t);
+nwam_error_t nwam_destroy(const char *, struct nwam_handle *, uint64_t);
+nwam_error_t nwam_enable(const char *, struct nwam_handle *);
+nwam_error_t nwam_disable(const char *, struct nwam_handle *);
+struct nwam_prop_table_entry *nwam_get_prop_table_entry(struct nwam_prop_table,
+ const char *);
+nwam_error_t nwam_get_prop_description(struct nwam_prop_table, const char *,
+ const char **);
+nwam_error_t nwam_get_prop_type(struct nwam_prop_table, const char *,
+ nwam_value_type_t *);
+nwam_error_t nwam_prop_multivalued(struct nwam_prop_table, const char *,
+ boolean_t *);
+nwam_error_t nwam_prop_read_only(struct nwam_prop_table, const char *,
+ boolean_t *);
+nwam_error_t nwam_validate_prop(struct nwam_prop_table, struct nwam_handle *,
+ const char *, nwam_value_t);
+nwam_error_t nwam_validate(struct nwam_prop_table, struct nwam_handle *,
+ const char **);
+nwam_error_t nwam_get_default_proplist(struct nwam_prop_table, uint64_t,
+ uint64_t, const char ***, uint_t *);
+nwam_error_t nwam_get_state(const char *, struct nwam_handle *, nwam_state_t *,
+ nwam_aux_state_t *);
+
+/*
+ * Generic validation functions - see libnwam_util.c.
+ */
+extern nwam_error_t nwam_valid_flags(uint64_t, uint64_t);
+extern nwam_error_t nwam_valid_condition(nwam_value_t);
+extern nwam_error_t nwam_valid_boolean(nwam_value_t);
+extern nwam_error_t nwam_valid_uint64(nwam_value_t);
+extern nwam_error_t nwam_valid_domain(nwam_value_t);
+extern nwam_error_t nwam_valid_host_any(nwam_value_t);
+extern nwam_error_t nwam_valid_host_v4(nwam_value_t);
+extern nwam_error_t nwam_valid_route_v4(nwam_value_t);
+extern nwam_error_t nwam_valid_host_v6(nwam_value_t);
+extern nwam_error_t nwam_valid_route_v6(nwam_value_t);
+extern nwam_error_t nwam_valid_host_or_domain(nwam_value_t);
+extern nwam_error_t nwam_valid_file(nwam_value_t);
+extern nwam_error_t nwam_valid_fmri(nwam_value_t);
+extern nwam_error_t nwam_valid_mac_addr(nwam_value_t);
+
+/* Misc. util functions */
+extern boolean_t nwam_uid_is_netadm(void);
+extern nwam_error_t nwam_set_smf_string_property(const char *, const char *,
+ const char *, const char *);
+extern nwam_error_t nwam_get_smf_string_property(const char *, const char *,
+ const char *, char **);
+extern int nwam_make_door_call(const char *, int *, void *, size_t);
+extern nwam_error_t nwam_errno_to_nwam_error(int);
+
+/* Needed in libnwam_files.c to check if NCP filename is valid */
+extern nwam_error_t nwam_ncp_file_to_name(const char *path, char **name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBNWAM_IMPL_H */
diff --git a/usr/src/lib/libnwam/common/libnwam_known_wlan.c b/usr/src/lib/libnwam/common/libnwam_known_wlan.c
new file mode 100644
index 0000000000..d05ee00334
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_known_wlan.c
@@ -0,0 +1,855 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libdllink.h>
+#include <libdlwlan.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Functions to support creating, modifying and destroying
+ * known WLAN objects. These represent the WiFi connection history,
+ * and are used by nwamd to identify and connect to known WLANs in
+ * scan results.
+ */
+
+static nwam_error_t valid_keyname(nwam_value_t);
+static nwam_error_t valid_keyslot(nwam_value_t);
+static nwam_error_t valid_secmode(nwam_value_t);
+
+struct nwam_prop_table_entry known_wlan_prop_table_entries[] = {
+ {NWAM_KNOWN_WLAN_PROP_PRIORITY, NWAM_VALUE_TYPE_UINT64, B_FALSE,
+ 1, 1, nwam_valid_uint64,
+ "specifies priority of known WLAN - lower values are prioritized",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_KNOWN_WLAN_PROP_BSSIDS, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, NWAM_MAX_NUM_VALUES, nwam_valid_mac_addr,
+ "specifies BSSID(s) (of the form aa:bb:cc:dd:ee:ff) associated "
+ "with known WLAN",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_KNOWN_WLAN_PROP_KEYNAME, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, 1, valid_keyname,
+ "specifies security key name used with known WLAN",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_KNOWN_WLAN_PROP_KEYSLOT, NWAM_VALUE_TYPE_UINT64, B_FALSE,
+ 0, 1, valid_keyslot,
+ "specifies key slot [1-4] for security key used with known WLAN",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_KNOWN_WLAN_PROP_SECURITY_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE,
+ 0, 1, valid_secmode,
+ "specifies security mode used for known WLAN",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY}
+};
+
+#define NWAM_NUM_KNOWN_WLAN_PROPS \
+ (sizeof (known_wlan_prop_table_entries) / \
+ sizeof (*known_wlan_prop_table_entries))
+
+struct nwam_prop_table known_wlan_prop_table =
+ { NWAM_NUM_KNOWN_WLAN_PROPS, known_wlan_prop_table_entries };
+
+nwam_error_t
+nwam_known_wlan_read(const char *name, uint64_t flags,
+ nwam_known_wlan_handle_t *kwhp)
+{
+ return (nwam_read(NWAM_OBJECT_TYPE_KNOWN_WLAN,
+ NWAM_KNOWN_WLAN_CONF_FILE, name, flags, kwhp));
+}
+
+nwam_error_t
+nwam_known_wlan_create(const char *name, nwam_known_wlan_handle_t *kwhp)
+{
+ nwam_error_t err;
+ nwam_value_t priorityval = NULL;
+
+ assert(kwhp != NULL && name != NULL);
+
+ if ((err = nwam_create(NWAM_OBJECT_TYPE_KNOWN_WLAN,
+ NWAM_KNOWN_WLAN_CONF_FILE, name, kwhp)) != NWAM_SUCCESS)
+ return (err);
+
+ /*
+ * Create new object list for known WLAN. The initial priority is
+ * also set.
+ */
+ if ((err = nwam_alloc_object_list(&((*kwhp)->nwh_data)))
+ != NWAM_SUCCESS)
+ goto finish;
+ if ((err = nwam_value_create_uint64(0, &priorityval)) != NWAM_SUCCESS)
+ goto finish;
+ err = nwam_set_prop_value((*kwhp)->nwh_data,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
+
+finish:
+ nwam_value_free(priorityval);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(*kwhp);
+ *kwhp = NULL;
+ }
+ return (err);
+}
+
+nwam_error_t
+nwam_known_wlan_get_name(nwam_known_wlan_handle_t kwh, char **namep)
+{
+ return (nwam_get_name(kwh, namep));
+}
+
+nwam_error_t
+nwam_known_wlan_set_name(nwam_known_wlan_handle_t kwh, const char *name)
+{
+ return (nwam_set_name(kwh, name));
+}
+
+boolean_t
+nwam_known_wlan_can_set_name(nwam_known_wlan_handle_t kwh)
+{
+ return (!kwh->nwh_committed);
+}
+
+/*
+ * Used to store wlan names/priorities for prioritized walk.
+ */
+struct nwam_wlan_info {
+ char *wlan_name;
+ uint64_t wlan_priority;
+ boolean_t wlan_walked;
+};
+
+struct nwam_wlan_info_list {
+ struct nwam_wlan_info **list;
+ uint_t num_wlans;
+};
+
+/*
+ * Used to read in each known WLAN name/priority.
+ */
+static int
+get_wlans_cb(nwam_known_wlan_handle_t kwh, void *data)
+{
+ struct nwam_wlan_info_list *wil = data;
+ struct nwam_wlan_info **list = wil->list;
+ struct nwam_wlan_info **newlist = NULL;
+ nwam_error_t err;
+ nwam_value_t priorityval = NULL;
+ uint_t num_wlans = wil->num_wlans;
+
+ /* Reallocate WLAN list and allocate new info list element. */
+ if ((newlist = realloc(list,
+ sizeof (struct nwam_wlan_info *) * ++num_wlans)) == NULL ||
+ (newlist[num_wlans - 1] = calloc(1,
+ sizeof (struct nwam_wlan_info))) == NULL) {
+ if (newlist != NULL)
+ free(newlist);
+ return (NWAM_NO_MEMORY);
+ }
+
+ /* Update list since realloc() may have relocated it */
+ wil->list = newlist;
+
+ /* Retrieve name/priority */
+ if ((err = nwam_known_wlan_get_name(kwh,
+ &((newlist[num_wlans - 1])->wlan_name))) != NWAM_SUCCESS ||
+ (err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY, &priorityval)) != NWAM_SUCCESS ||
+ (err = nwam_value_get_uint64(priorityval,
+ &((newlist[num_wlans - 1])->wlan_priority))) != NWAM_SUCCESS) {
+ free(newlist[num_wlans - 1]->wlan_name);
+ nwam_value_free(priorityval);
+ free(newlist[num_wlans - 1]);
+ return (err);
+ }
+ nwam_value_free(priorityval);
+
+ (newlist[num_wlans - 1])->wlan_walked = B_FALSE;
+
+ wil->num_wlans = num_wlans;
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Some recursion is required here, since if _WALK_PRIORITY_ORDER is specified,
+ * we need to first walk the list of known WLANs to retrieve names
+ * and priorities, then utilize that list to carry out an in-order walk.
+ */
+nwam_error_t
+nwam_walk_known_wlans(int(*cb)(nwam_known_wlan_handle_t, void *), void *data,
+ uint64_t flags, int *retp)
+{
+ nwam_known_wlan_handle_t kwh;
+ nwam_error_t err;
+ int ret = 0;
+
+ assert(cb != NULL);
+
+ if ((err = nwam_valid_flags(flags,
+ NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER | NWAM_FLAG_BLOCKING))
+ != NWAM_SUCCESS)
+ return (err);
+
+ if ((flags & NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER) != 0) {
+ struct nwam_wlan_info_list wil = { NULL, 0};
+ uint64_t iflags = flags &~
+ NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER;
+ uint64_t minpriority;
+ int errval, i, j, minindex;
+
+ if (nwam_walk_known_wlans(get_wlans_cb, &wil, iflags, &errval)
+ != NWAM_SUCCESS) {
+ err = (nwam_error_t)errval;
+ goto done;
+ }
+
+ err = NWAM_SUCCESS;
+
+ for (i = 0; i < wil.num_wlans; i++) {
+ /* Find lowest priority value not walked so far. */
+ minpriority = (uint64_t)-1;
+ for (j = 0; j < wil.num_wlans; j++) {
+ if (wil.list[j]->wlan_priority < minpriority &&
+ !(wil.list[j]->wlan_walked)) {
+ minpriority =
+ wil.list[j]->wlan_priority;
+ minindex = j;
+ }
+ }
+ wil.list[minindex]->wlan_walked = B_TRUE;
+ if ((err = nwam_known_wlan_read
+ (wil.list[minindex]->wlan_name,
+ iflags, &kwh)) != NWAM_SUCCESS) {
+ goto done;
+ }
+ ret = cb(kwh, data);
+ if (ret != 0) {
+ nwam_known_wlan_free(kwh);
+ err = NWAM_WALK_HALTED;
+ goto done;
+ }
+ nwam_known_wlan_free(kwh);
+ }
+done:
+ if (wil.list != NULL) {
+ for (j = 0; j < wil.num_wlans; j++) {
+ free(wil.list[j]->wlan_name);
+ free(wil.list[j]);
+ }
+ free(wil.list);
+ }
+ if (retp != NULL)
+ *retp = ret;
+ return (err);
+ }
+
+ return (nwam_walk(NWAM_OBJECT_TYPE_KNOWN_WLAN,
+ NWAM_KNOWN_WLAN_CONF_FILE, cb, data, flags, retp, NULL));
+}
+
+void
+nwam_known_wlan_free(nwam_known_wlan_handle_t kwh)
+{
+ nwam_free(kwh);
+}
+
+nwam_error_t
+nwam_known_wlan_copy(nwam_known_wlan_handle_t oldkwh, const char *newname,
+ nwam_known_wlan_handle_t *newkwhp)
+{
+ return (nwam_copy(NWAM_KNOWN_WLAN_CONF_FILE, oldkwh, newname, newkwhp));
+}
+
+nwam_error_t
+nwam_known_wlan_delete_prop(nwam_known_wlan_handle_t kwh, const char *propname)
+{
+ nwam_error_t err;
+ void *olddata;
+
+ assert(kwh != NULL && propname != NULL);
+
+ /*
+ * Duplicate data, remove property and validate. If validation
+ * fails, revert to data duplicated prior to remove.
+ */
+ if ((err = nwam_dup_object_list(kwh->nwh_data, &olddata))
+ != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_delete_prop(kwh->nwh_data, propname)) != NWAM_SUCCESS) {
+ nwam_free_object_list(kwh->nwh_data);
+ kwh->nwh_data = olddata;
+ return (err);
+ }
+ if ((err = nwam_known_wlan_validate(kwh, NULL)) != NWAM_SUCCESS) {
+ nwam_free_object_list(kwh->nwh_data);
+ kwh->nwh_data = olddata;
+ return (err);
+ }
+ nwam_free_object_list(olddata);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_known_wlan_set_prop_value(nwam_known_wlan_handle_t kwh,
+ const char *propname, nwam_value_t value)
+{
+ nwam_error_t err;
+
+ assert(kwh != NULL && propname != NULL && value != NULL);
+
+ if ((err = nwam_known_wlan_validate_prop(kwh, propname, value))
+ != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_set_prop_value(kwh->nwh_data, propname, value));
+}
+
+nwam_error_t
+nwam_known_wlan_get_prop_value(nwam_known_wlan_handle_t kwh,
+ const char *propname, nwam_value_t *valuep)
+{
+ return (nwam_get_prop_value(kwh->nwh_data, propname, valuep));
+}
+
+nwam_error_t
+nwam_known_wlan_walk_props(nwam_known_wlan_handle_t kwh,
+ int (*cb)(const char *, nwam_value_t, void *),
+ void *data, uint64_t flags, int *retp)
+{
+ return (nwam_walk_props(kwh, cb, data, flags, retp));
+}
+
+struct priority_collision_data {
+ char *wlan_name;
+ uint64_t priority;
+};
+
+static int
+avoid_priority_collisions_cb(nwam_known_wlan_handle_t kwh, void *data)
+{
+ nwam_value_t priorityval;
+ nwam_error_t err;
+ struct priority_collision_data *pcd = data;
+ char *name;
+ uint64_t priority;
+
+ err = nwam_known_wlan_get_name(kwh, &name);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if (strcmp(name, pcd->wlan_name) == 0) {
+ /* skip to-be-updated wlan */
+ free(name);
+ return (NWAM_SUCCESS);
+ }
+ free(name);
+
+ err = nwam_known_wlan_get_prop_value(kwh, NWAM_KNOWN_WLAN_PROP_PRIORITY,
+ &priorityval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(priorityval, &priority);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ nwam_value_free(priorityval);
+
+ if (priority < pcd->priority)
+ return (NWAM_SUCCESS);
+
+ if (priority == pcd->priority) {
+ /* Two priority values collide. Move this one up. */
+ err = nwam_value_create_uint64(priority + 1, &priorityval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
+ nwam_value_free(priorityval);
+ if (err != NWAM_SUCCESS) {
+ return (err);
+ }
+ /*
+ * We are doing a walk, and will continue shifting until
+ * we find a gap in the priority numbers; thus no need to
+ * do collision checking here.
+ */
+ err = nwam_known_wlan_commit(kwh,
+ NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ (pcd->priority)++;
+ return (NWAM_SUCCESS);
+ }
+
+ /*
+ * Only possiblity left at this point is that we're looking
+ * at a priority greater than the last one we wrote, so we've
+ * found a gap. We can halt the walk now.
+ */
+ return (NWAM_WALK_HALTED);
+}
+
+nwam_error_t
+nwam_known_wlan_commit(nwam_known_wlan_handle_t kwh, uint64_t flags)
+{
+ nwam_error_t err;
+ nwam_value_t priorityval;
+ int ret = 0;
+ struct priority_collision_data pcd;
+
+ assert(kwh != NULL && kwh->nwh_data != NULL);
+
+ if ((err = nwam_known_wlan_validate(kwh, NULL)) != NWAM_SUCCESS)
+ return (err);
+
+ /*
+ * If the NO_COLLISION_CHECK flag is set, no need to check for
+ * collision.
+ */
+ if (flags & NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK)
+ return (nwam_commit(NWAM_KNOWN_WLAN_CONF_FILE, kwh,
+ (flags & NWAM_FLAG_GLOBAL_MASK) |
+ NWAM_FLAG_ENTITY_KNOWN_WLAN));
+
+ /*
+ * We need to do priority checking. Walk the list, looking
+ * for the first entry with priority greater than or equal
+ * to the entry we're adding. Commit the new one (without
+ * doing additional checking), and then increment other
+ * entries as needed.
+ */
+ err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY, &priorityval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(priorityval, &(pcd.priority));
+ nwam_value_free(priorityval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ err = nwam_known_wlan_get_name(kwh, &(pcd.wlan_name));
+ if (err != NWAM_SUCCESS)
+ return (err);
+ err = nwam_walk_known_wlans(avoid_priority_collisions_cb, &pcd,
+ NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, &ret);
+ free(pcd.wlan_name);
+ /*
+ * a halted walk is okay, it just means we didn't have
+ * to walk the entire list to resolve priorities
+ */
+ if (ret != NWAM_SUCCESS && ret != NWAM_WALK_HALTED)
+ return (ret);
+
+ return (nwam_known_wlan_commit(kwh,
+ flags | NWAM_FLAG_KNOWN_WLAN_NO_COLLISION_CHECK));
+}
+
+nwam_error_t
+nwam_known_wlan_destroy(nwam_known_wlan_handle_t kwh, uint64_t flags)
+{
+ return (nwam_destroy(NWAM_KNOWN_WLAN_CONF_FILE, kwh,
+ flags | NWAM_FLAG_ENTITY_KNOWN_WLAN));
+}
+
+nwam_error_t
+nwam_known_wlan_get_prop_description(const char *propname,
+ const char **descriptionp)
+{
+ return (nwam_get_prop_description(known_wlan_prop_table, propname,
+ descriptionp));
+}
+
+/* Property-specific value validation functions should go here. */
+
+static nwam_error_t
+valid_keyname(nwam_value_t value)
+{
+ char *keyname;
+
+ if (nwam_value_get_string(value, &keyname) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ if (!dladm_valid_secobj_name(keyname))
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_keyslot(nwam_value_t value)
+{
+ uint64_t keyslot;
+
+ if (nwam_value_get_uint64(value, &keyslot) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ if (keyslot < 1 || keyslot > 4)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_secmode(nwam_value_t value)
+{
+ uint64_t secmode;
+
+ if (nwam_value_get_uint64(value, &secmode) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ if (secmode != DLADM_WLAN_SECMODE_NONE &&
+ secmode != DLADM_WLAN_SECMODE_WEP &&
+ secmode != DLADM_WLAN_SECMODE_WPA)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_known_wlan_validate(nwam_known_wlan_handle_t kwh, const char **errpropp)
+{
+ return (nwam_validate(known_wlan_prop_table, kwh, errpropp));
+}
+
+nwam_error_t
+nwam_known_wlan_validate_prop(nwam_known_wlan_handle_t kwh,
+ const char *propname, nwam_value_t value)
+{
+ return (nwam_validate_prop(known_wlan_prop_table, kwh, propname,
+ value));
+}
+
+/*
+ * Given a property, return expected property data type
+ */
+nwam_error_t
+nwam_known_wlan_get_prop_type(const char *propname, nwam_value_type_t *typep)
+{
+ return (nwam_get_prop_type(known_wlan_prop_table, propname, typep));
+}
+
+nwam_error_t
+nwam_known_wlan_prop_multivalued(const char *propname, boolean_t *multip)
+{
+ return (nwam_prop_multivalued(known_wlan_prop_table, propname, multip));
+}
+
+nwam_error_t
+nwam_known_wlan_get_default_proplist(const char ***prop_list,
+ uint_t *numvaluesp)
+{
+ return (nwam_get_default_proplist(known_wlan_prop_table,
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY, prop_list, numvaluesp));
+}
+
+/*
+ * Add the given ESSID, BSSID, secmode, keyslot and key name to known WLANs.
+ * BSSID and keyname can be NULL.
+ */
+nwam_error_t
+nwam_known_wlan_add_to_known_wlans(const char *essid, const char *bssid,
+ uint32_t secmode, uint_t keyslot, const char *keyname)
+{
+ nwam_known_wlan_handle_t kwh;
+ nwam_value_t keynameval = NULL, keyslotval = NULL, bssidsval = NULL;
+ nwam_value_t secmodeval = NULL, priorityval = NULL;
+ char **old_bssids = NULL, **new_bssids;
+ uint_t nelem = 0;
+ nwam_error_t err;
+ int i, j;
+
+ /*
+ * Check if the given ESSID already exists as known WLAN. If so,
+ * add the BSSID to the bssids property. If not, create one with
+ * the given ESSID and add BSSID if given.
+ */
+ err = nwam_known_wlan_read(essid, 0, &kwh);
+
+ switch (err) {
+ case NWAM_ENTITY_NOT_FOUND:
+ if ((err = nwam_known_wlan_create(essid, &kwh)) != NWAM_SUCCESS)
+ return (err);
+ /* New known WLAN - set priority to 0 */
+ if ((err = nwam_value_create_uint64(0, &priorityval))
+ != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_PRIORITY, priorityval);
+ nwam_value_free(priorityval);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ /* If BSSID is NULL, nothing more to do here. */
+ if (bssid == NULL)
+ break;
+ if ((err = nwam_value_create_string((char *)bssid, &bssidsval))
+ != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ /* Set the bssids property */
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, bssidsval);
+ nwam_value_free(bssidsval);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ break;
+ case NWAM_SUCCESS:
+ /* If no bssid is specified, nothing to do */
+ if (bssid == NULL)
+ break;
+
+ /* known WLAN exists, retrieve the existing bssids property */
+ err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval);
+ if (err != NWAM_SUCCESS && err != NWAM_ENTITY_NOT_FOUND) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ if (err == NWAM_SUCCESS) {
+ if ((err = nwam_value_get_string_array(bssidsval,
+ &old_bssids, &nelem)) != NWAM_SUCCESS) {
+ nwam_value_free(bssidsval);
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ }
+ /* Create a new array to append given BSSID */
+ new_bssids = calloc(nelem + 1, sizeof (char *));
+ if (new_bssids == NULL) {
+ nwam_value_free(bssidsval);
+ nwam_known_wlan_free(kwh);
+ return (NWAM_NO_MEMORY);
+ }
+
+ /*
+ * Copy over existing BSSIDs to the new array. Also, check
+ * to make sure that the given BSSID doesn't already exist
+ * in the known WLAN. If so, do abort copying and return
+ * NWAM_SUCCESS.
+ */
+ for (i = 0; i < nelem; i++) {
+ if (strcmp(old_bssids[i], bssid) == 0) {
+ /* nothing to do, so free up everything */
+ for (j = 0; j < i; j++)
+ free(new_bssids[j]);
+ free(new_bssids);
+ nwam_value_free(bssidsval);
+ goto set_key_info;
+ }
+ new_bssids[i] = strdup(old_bssids[i]);
+ }
+ new_bssids[nelem] = strdup(bssid);
+ nwam_value_free(bssidsval);
+
+ err = nwam_value_create_string_array(new_bssids, nelem + 1,
+ &bssidsval);
+ for (i = 0; i < nelem + 1; i++)
+ free(new_bssids[i]);
+ free(new_bssids);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ /* Set the bssids property */
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, bssidsval);
+ nwam_value_free(bssidsval);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ break;
+ default:
+ return (err);
+ }
+
+set_key_info:
+ /* Set the security mode property */
+ if ((err = nwam_value_create_uint64(secmode, &secmodeval))
+ != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_SECURITY_MODE, secmodeval);
+ nwam_value_free(secmodeval);
+
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+
+ if (keyname != NULL) {
+ if ((err = nwam_value_create_string((char *)keyname,
+ &keynameval)) != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYNAME, keynameval);
+ nwam_value_free(keynameval);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ if ((err = nwam_value_create_uint64(keyslot,
+ &keyslotval)) != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ err = nwam_known_wlan_set_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYSLOT, keyslotval);
+ nwam_value_free(keyslotval);
+ }
+
+ err = nwam_known_wlan_commit(kwh, 0);
+ nwam_known_wlan_free(kwh);
+
+ return (err);
+}
+
+/*
+ * Remove the given BSSID/keyname from the bssids/keyname property for the
+ * given ESSID.
+ */
+nwam_error_t
+nwam_known_wlan_remove_from_known_wlans(const char *essid, const char *bssid,
+ const char *keyname)
+{
+ nwam_known_wlan_handle_t kwh;
+ nwam_value_t bssidsval;
+ char **old_bssids, **new_bssids;
+ uint_t nelem;
+ nwam_error_t err;
+ int i, found = -1;
+
+ /* Retrieve the existing bssids */
+ if ((err = nwam_known_wlan_read(essid, 0, &kwh)) != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_known_wlan_get_prop_value(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS, &bssidsval)) != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ if ((err = nwam_value_get_string_array(bssidsval, &old_bssids, &nelem))
+ != NWAM_SUCCESS) {
+ nwam_value_free(bssidsval);
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+
+ /* Cycle through the BSSIDs array to find the BSSID to remove */
+ for (i = 0; i < nelem; i++) {
+ if (strcmp(old_bssids[i], bssid) == 0) {
+ found = i;
+ break;
+ }
+ }
+
+ /* Given BSSID was not found in the array */
+ if (found == -1) {
+ nwam_value_free(bssidsval);
+ nwam_known_wlan_free(kwh);
+ return (NWAM_INVALID_ARG);
+ }
+
+ /* If removing the only BSSID entry, remove the bssids property */
+ if (nelem == 1) {
+ nwam_value_free(bssidsval);
+ if ((err = nwam_known_wlan_delete_prop(kwh,
+ NWAM_KNOWN_WLAN_PROP_BSSIDS)) != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ err = nwam_known_wlan_commit(kwh, 0);
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+
+ new_bssids = calloc(nelem - 1, sizeof (char *));
+ if (new_bssids == NULL) {
+ nwam_value_free(bssidsval);
+ nwam_known_wlan_free(kwh);
+ return (NWAM_NO_MEMORY);
+ }
+
+ /* Copy over other BSSIDs */
+ for (i = 0; i < found; i++)
+ new_bssids[i] = strdup(old_bssids[i]);
+ for (i = found + 1; i < nelem; i++)
+ new_bssids[i-1] = strdup(old_bssids[i]);
+ nwam_value_free(bssidsval);
+
+ err = nwam_value_create_string_array(new_bssids, nelem - 1, &bssidsval);
+ for (i = 0; i < nelem - 1; i++)
+ free(new_bssids[i]);
+ free(new_bssids);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+
+ /* Set the bssids property */
+ err = nwam_known_wlan_set_prop_value(kwh, NWAM_KNOWN_WLAN_PROP_BSSIDS,
+ bssidsval);
+ nwam_value_free(bssidsval);
+ if (err != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+
+ if (keyname != NULL) {
+ if ((err = nwam_known_wlan_delete_prop(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYNAME)) != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ if ((err = nwam_known_wlan_delete_prop(kwh,
+ NWAM_KNOWN_WLAN_PROP_KEYSLOT)) != NWAM_SUCCESS) {
+ nwam_known_wlan_free(kwh);
+ return (err);
+ }
+ }
+
+ err = nwam_known_wlan_commit(kwh, 0);
+ nwam_known_wlan_free(kwh);
+
+ return (err);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_loc.c b/usr/src/lib/libnwam/common/libnwam_loc.c
new file mode 100644
index 0000000000..5f3bcacbd1
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_loc.c
@@ -0,0 +1,1182 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libscf.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Functions to support creating, modifying, destroying, querying the
+ * state of and changing the state of location objects. Locations
+ * represent the configuration to be applied once basic network configuration
+ * has been established - name services, IPsec config, etc, and can be enabled
+ * either manually or conditionally for a combination of the set of
+ * available conditions (an IP address is present, an ENM is active etc).
+ */
+
+#define NSSWITCH_PREFIX "/etc/nsswitch."
+
+typedef nwam_error_t (*nwam_loc_prop_validate_func_t)(nwam_value_t);
+
+static nwam_error_t valid_loc_activation_mode(nwam_value_t);
+static nwam_error_t valid_loc_condition(nwam_value_t);
+static nwam_error_t valid_nameservices(nwam_value_t);
+static nwam_error_t valid_configsrc(nwam_value_t);
+
+struct nwam_prop_table_entry loc_prop_table_entries[] = {
+ {NWAM_LOC_PROP_ACTIVATION_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
+ valid_loc_activation_mode,
+ "specifies the location activation mode - valid values are:\n"
+ "\'manual\', \'conditional-any\' and \'conditional-all\'",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_CONDITIONS, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, valid_loc_condition,
+ "specifies the activation condition. Conditions are of the form:\n"
+ "ncp|ncu|enm name is|is-not active\n"
+ "ip-address is|is-not|is-in-range|is-not-in-range| 1.2.3.4[/24]\n"
+ "advertised-domain is|is-not|contains|does-not-contain string\n"
+ "system-domain is|is-not|contains|does-not-contain string\n"
+ "essid is|is-not|contains|does-not-contain string\n"
+ "bssid is|is-not string",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_ENABLED, NWAM_VALUE_TYPE_BOOLEAN, B_TRUE, 1, 1,
+ nwam_valid_boolean,
+ "specifies if location is to be enabled",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_NAMESERVICES, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1,
+ NWAM_MAX_NUM_VALUES, valid_nameservices,
+ "specifies name service(s) to be used - valid values are:\n"
+ "\'files\', \'dns\', \'nis\', and \'ldap\'",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE, NWAM_VALUE_TYPE_STRING,
+ B_FALSE, 0, 1, nwam_valid_file,
+ "specifies path to configuration file for name services switch "
+ "for this location - see nsswitch.conf(4)",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC, NWAM_VALUE_TYPE_UINT64,
+ B_FALSE, 0, NWAM_MAX_NUM_VALUES, valid_configsrc,
+ "specifies sources of DNS configuration parameters - valid values "
+ "are:\n\'dhcp\', or \'manual\'",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, 1, nwam_valid_domain,
+ "specifies DNS domain name to be set for this location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, NWAM_MAX_NUM_VALUES, nwam_valid_host_any,
+ "specifies DNS server host address(es)",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, NWAM_MAX_NUM_VALUES, nwam_valid_domain,
+ "specifies DNS search list for host name lookup",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC, NWAM_VALUE_TYPE_UINT64,
+ B_FALSE, 0, NWAM_MAX_NUM_VALUES, valid_configsrc,
+ "specifies sources of NIS configuration parameters - valid values "
+ "are:\n\'dhcp\', or \'manual\'",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, NWAM_MAX_NUM_VALUES, nwam_valid_host_any,
+ "specifies NIS server host address(es)",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC, NWAM_VALUE_TYPE_UINT64,
+ B_FALSE, 0, NWAM_MAX_NUM_VALUES, valid_configsrc,
+ "specifies sources of NIS configuration parameters - currently, "
+ "the only valid value is \'manual\'",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS, NWAM_VALUE_TYPE_STRING,
+ B_FALSE, 0, NWAM_MAX_NUM_VALUES, nwam_valid_host_or_domain,
+ "specifies LDAP server host address(es)",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_DEFAULT_DOMAIN, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_domain,
+ "specifies the domainname(1M) to be set for this location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_NFSV4_DOMAIN, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_domain,
+ "specifies an NFSv4 domain for this location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_IPFILTER_CONFIG_FILE, NWAM_VALUE_TYPE_STRING, B_FALSE,
+ 0, 1, nwam_valid_file,
+ "specifies an absolute path to an ipf.conf(4) file for this "
+ "location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE, NWAM_VALUE_TYPE_STRING,
+ B_FALSE, 0, 1, nwam_valid_file,
+ "specifies an absolute path to an ipf6.conf file for this "
+ "location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_IPNAT_CONFIG_FILE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ 1, nwam_valid_file,
+ "specifies an absolute path to an ipnat.conf(4) file for this "
+ "location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_IPPOOL_CONFIG_FILE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ 1, nwam_valid_file,
+ "specifies an absolute path to an ippool.conf(4) file for this "
+ "location",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_IKE_CONFIG_FILE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_file,
+ "specifies an absolute path to an ike config file "
+ "(see ike.config(4))",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+ {NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE, NWAM_VALUE_TYPE_STRING,
+ B_FALSE, 0, 1, nwam_valid_file,
+ "specifies an absolute path to an IPsec policy configuration file "
+ "(see ipsecconf(1M)",
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY},
+};
+
+#define NWAM_NUM_LOC_PROPS (sizeof (loc_prop_table_entries) / \
+ sizeof (*loc_prop_table_entries))
+
+struct nwam_prop_table loc_prop_table =
+ { NWAM_NUM_LOC_PROPS, loc_prop_table_entries };
+
+static uint64_t
+nwam_loc_activation_to_flag(nwam_activation_mode_t activation)
+{
+ switch (activation) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ return (NWAM_FLAG_ACTIVATION_MODE_MANUAL);
+ case NWAM_ACTIVATION_MODE_SYSTEM:
+ return (NWAM_FLAG_ACTIVATION_MODE_SYSTEM);
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
+ return (NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ANY);
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
+ return (NWAM_FLAG_ACTIVATION_MODE_CONDITIONAL_ALL);
+ default:
+ return (0);
+ }
+}
+
+nwam_error_t
+nwam_loc_read(const char *name, uint64_t flags, nwam_loc_handle_t *lochp)
+{
+ return (nwam_read(NWAM_OBJECT_TYPE_LOC, NWAM_LOC_CONF_FILE, name,
+ flags, lochp));
+}
+
+nwam_error_t
+nwam_loc_create(const char *name, nwam_loc_handle_t *lochp)
+{
+ nwam_error_t err;
+ nwam_value_t val = NULL;
+ char *nsswitch = NULL;
+
+ assert(lochp != NULL && name != NULL);
+
+ if ((err = nwam_create(NWAM_OBJECT_TYPE_LOC, NWAM_LOC_CONF_FILE, name,
+ lochp)) != NWAM_SUCCESS)
+ return (err);
+
+ /* Create new object list for loc */
+ if ((err = nwam_alloc_object_list(&((*lochp)->nwh_data)))
+ != NWAM_SUCCESS)
+ goto finish;
+
+ /* NWAM_LOC_PROP_ACTIVATION_MODE is mandatory */
+ if ((err = nwam_value_create_uint64(NWAM_ACTIVATION_MODE_MANUAL, &val))
+ != NWAM_SUCCESS) {
+ goto finish;
+ }
+ if ((err = nwam_set_prop_value((*lochp)->nwh_data,
+ NWAM_LOC_PROP_ACTIVATION_MODE, val)) != NWAM_SUCCESS) {
+ goto finish;
+ }
+ nwam_value_free(val);
+ val = NULL;
+
+ /*
+ * NWAM_LOC_PROP_ENABLED defaults to false.
+ */
+ if ((err = nwam_value_create_boolean(B_FALSE, &val)) != NWAM_SUCCESS)
+ goto finish;
+ if ((err = nwam_set_prop_value((*lochp)->nwh_data,
+ NWAM_LOC_PROP_ENABLED, val)) != NWAM_SUCCESS)
+ goto finish;
+ nwam_value_free(val);
+ val = NULL;
+
+ /*
+ * Initialize name service properties: use DNS, configured
+ * via DHCP, with default nsswitch (/etc/nsswitch.dns).
+ */
+ if ((err = nwam_value_create_uint64(NWAM_NAMESERVICES_DNS, &val)) !=
+ NWAM_SUCCESS)
+ goto finish;
+ if ((err = nwam_set_prop_value((*lochp)->nwh_data,
+ NWAM_LOC_PROP_NAMESERVICES, val)) != NWAM_SUCCESS)
+ goto finish;
+ nwam_value_free(val);
+ val = NULL;
+
+ if ((err = nwam_value_create_uint64(NWAM_CONFIGSRC_DHCP, &val)) !=
+ NWAM_SUCCESS)
+ goto finish;
+ if ((err = nwam_set_prop_value((*lochp)->nwh_data,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC, val)) != NWAM_SUCCESS)
+ goto finish;
+ nwam_value_free(val);
+ val = NULL;
+
+ /* concatenate these two strings */
+ nsswitch = strdup(NSSWITCH_PREFIX NWAM_NAMESERVICES_DNS_STRING);
+ if (nsswitch == NULL) {
+ err = NWAM_NO_MEMORY;
+ goto finish;
+ }
+ if ((err = nwam_value_create_string(nsswitch, &val)) != NWAM_SUCCESS)
+ goto finish;
+ err = nwam_set_prop_value((*lochp)->nwh_data,
+ NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE, val);
+
+finish:
+ if (nsswitch != NULL)
+ free(nsswitch);
+ if (val != NULL)
+ nwam_value_free(val);
+ if (err != NWAM_SUCCESS) {
+ nwam_loc_free(*lochp);
+ *lochp = NULL;
+ }
+ return (err);
+}
+
+nwam_error_t
+nwam_loc_get_name(nwam_loc_handle_t loch, char **namep)
+{
+ return (nwam_get_name(loch, namep));
+}
+
+nwam_error_t
+nwam_loc_set_name(nwam_loc_handle_t loch, const char *name)
+{
+ return (nwam_set_name(loch, name));
+}
+
+boolean_t
+nwam_loc_can_set_name(nwam_loc_handle_t loch)
+{
+ return (!loch->nwh_committed);
+}
+
+/* ARGSUSED2 */
+static int
+loc_selectcb(struct nwam_handle *hp, uint64_t flags, void *data)
+{
+ nwam_loc_handle_t loch = hp;
+ char *locname;
+ uint64_t activation, actflag, walkfilter;
+ nwam_value_t activationval;
+
+ /* Skip the Legacy location in all cases */
+ if (nwam_loc_get_name(loch, &locname) != NWAM_SUCCESS)
+ return (NWAM_INVALID_ARG);
+ if (strcmp(locname, NWAM_LOC_NAME_LEGACY) == 0) {
+ free(locname);
+ return (NWAM_INVALID_ARG);
+ }
+ free(locname);
+
+ /*
+ * Get a bitmapped flag value corresponding to this loc's
+ * activation.
+ */
+ if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
+ &activationval) != NWAM_SUCCESS) {
+ return (NWAM_INVALID_ARG);
+ }
+ if (nwam_value_get_uint64(activationval, &activation) != NWAM_SUCCESS) {
+ nwam_value_free(activationval);
+ return (NWAM_INVALID_ARG);
+ }
+
+ actflag = nwam_loc_activation_to_flag(activation);
+ nwam_value_free(activationval);
+ if ((walkfilter = (flags & NWAM_WALK_FILTER_MASK)) == 0)
+ walkfilter = NWAM_FLAG_ACTIVATION_MODE_ALL;
+ if (actflag & walkfilter)
+ return (NWAM_SUCCESS);
+ return (NWAM_INVALID_ARG);
+}
+
+nwam_error_t
+nwam_walk_locs(int(*cb)(nwam_loc_handle_t, void *), void *data, uint64_t flags,
+ int *retp)
+{
+ nwam_error_t err = nwam_valid_flags(flags,
+ NWAM_FLAG_ACTIVATION_MODE_ALL | NWAM_FLAG_BLOCKING);
+
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_walk(NWAM_OBJECT_TYPE_LOC, NWAM_LOC_CONF_FILE,
+ cb, data, flags, retp, loc_selectcb));
+}
+
+void
+nwam_loc_free(nwam_loc_handle_t loch)
+{
+ nwam_free(loch);
+}
+
+nwam_error_t
+nwam_loc_delete_prop(nwam_loc_handle_t loch, const char *propname)
+{
+ nwam_error_t err;
+ boolean_t ro;
+ void *olddata;
+
+ assert(loch != NULL && propname != NULL);
+
+ if ((err = nwam_loc_prop_read_only(propname, &ro)) != NWAM_SUCCESS)
+ return (err);
+ if (ro)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ /*
+ * Duplicate data, remove property and validate. If validation
+ * fails, revert to data duplicated prior to remove.
+ */
+ if ((err = nwam_dup_object_list(loch->nwh_data, &olddata))
+ != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_delete_prop(loch->nwh_data, propname))
+ != NWAM_SUCCESS) {
+ nwam_free_object_list(loch->nwh_data);
+ loch->nwh_data = olddata;
+ return (err);
+ }
+ if ((err = nwam_loc_validate(loch, NULL)) != NWAM_SUCCESS) {
+ nwam_free_object_list(loch->nwh_data);
+ loch->nwh_data = olddata;
+ return (err);
+ }
+ nwam_free_object_list(olddata);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_loc_set_prop_value(nwam_loc_handle_t loch, const char *propname,
+ nwam_value_t value)
+{
+ nwam_error_t err;
+ boolean_t ro;
+
+ assert(loch != NULL && propname != NULL && value != NULL);
+
+ if ((err = nwam_loc_validate_prop(loch, propname, value))
+ != NWAM_SUCCESS ||
+ (err = nwam_loc_prop_read_only(propname, &ro)) != NWAM_SUCCESS)
+ return (err);
+ if (ro)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ return (nwam_set_prop_value(loch->nwh_data, propname, value));
+}
+
+nwam_error_t
+nwam_loc_get_prop_value(nwam_loc_handle_t loch, const char *propname,
+ nwam_value_t *valuep)
+{
+ return (nwam_get_prop_value(loch->nwh_data, propname, valuep));
+}
+
+nwam_error_t
+nwam_loc_walk_props(nwam_loc_handle_t loch,
+ int (*cb)(const char *, nwam_value_t, void *),
+ void *data, uint64_t flags, int *retp)
+{
+ return (nwam_walk_props(loch, cb, data, flags, retp));
+}
+
+nwam_error_t
+nwam_loc_commit(nwam_loc_handle_t loch, uint64_t flags)
+{
+ nwam_error_t err;
+
+ assert(loch != NULL && loch->nwh_data != NULL);
+
+ if ((err = nwam_loc_validate(loch, NULL)) != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_commit(NWAM_LOC_CONF_FILE, loch, flags));
+}
+
+nwam_error_t
+nwam_loc_destroy(nwam_loc_handle_t loch, uint64_t flags)
+{
+ nwam_error_t err;
+ nwam_value_t actval;
+ uint64_t activation;
+
+ /*
+ * Automatic and NoNet are not destroyable and Legacy is
+ * destroyable by netadm only. These have system activation-mode.
+ */
+ if ((err = nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
+ &actval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(actval, &activation);
+ nwam_value_free(actval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ if (activation == NWAM_ACTIVATION_MODE_SYSTEM) {
+ if (strcmp(loch->nwh_name, NWAM_LOC_NAME_LEGACY) == 0) {
+ if (!nwam_uid_is_netadm())
+ return (NWAM_ENTITY_NOT_DESTROYABLE);
+ } else {
+ return (NWAM_ENTITY_NOT_DESTROYABLE);
+ }
+ }
+
+ return (nwam_destroy(NWAM_LOC_CONF_FILE, loch, flags));
+}
+
+nwam_error_t
+nwam_loc_get_prop_description(const char *propname, const char **descriptionp)
+{
+ return (nwam_get_prop_description(loc_prop_table, propname,
+ descriptionp));
+}
+
+nwam_error_t
+nwam_loc_prop_read_only(const char *propname, boolean_t *readp)
+{
+ return (nwam_prop_read_only(loc_prop_table, propname, readp));
+}
+
+static nwam_error_t
+valid_loc_activation_mode(nwam_value_t value)
+{
+ uint64_t activation_mode;
+
+ if (nwam_value_get_uint64(value, &activation_mode) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ switch (activation_mode) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ case NWAM_ACTIVATION_MODE_SYSTEM:
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
+ case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
+ return (NWAM_SUCCESS);
+ }
+ return (NWAM_ENTITY_INVALID_VALUE);
+}
+
+/*
+ * Identical to nwam_valid_condition(), except locations cannot specify other
+ * location's activation as a condition, e.g. loc2 cannot specify
+ * "loc1 is active" since only one location is active at a time, and
+ * as a consequence the condition is unsatisfiable.
+ */
+nwam_error_t
+valid_loc_condition(nwam_value_t value)
+{
+ char **conditions;
+ uint_t i, numvalues;
+ nwam_condition_object_type_t object_type;
+ nwam_condition_t condition;
+
+ if (nwam_value_get_string_array(value, &conditions, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ char *object_name = NULL;
+
+ if (nwam_condition_string_to_condition(conditions[i],
+ &object_type, &condition, &object_name) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ if (object_type == NWAM_CONDITION_OBJECT_TYPE_LOC &&
+ condition == NWAM_CONDITION_IS) {
+ free(object_name);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ if (object_name != NULL)
+ free(object_name);
+ }
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_nameservices(nwam_value_t value)
+{
+ uint64_t *nameservices;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_uint64_array(value, &nameservices, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (nameservices[i] > NWAM_NAMESERVICES_LDAP)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_configsrc(nwam_value_t value)
+{
+ uint64_t *configsrcs;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_uint64_array(value, &configsrcs, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (configsrcs[i] > NWAM_CONFIGSRC_DHCP)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Validates that the activation-mode is system for Automatic and NoNet
+ * locations, and not system for all other locations.
+ */
+static nwam_error_t
+nwam_loc_validate_activation_mode(nwam_loc_handle_t loch, nwam_value_t actval)
+{
+ nwam_error_t err;
+ uint64_t activation;
+
+ if ((err = nwam_value_get_uint64(actval, &activation)) != NWAM_SUCCESS)
+ return (err);
+
+ if (NWAM_LOC_NAME_PRE_DEFINED(loch->nwh_name)) {
+ if (activation != NWAM_ACTIVATION_MODE_SYSTEM)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ } else {
+ if (activation == NWAM_ACTIVATION_MODE_SYSTEM)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Helper function to validate one nameservice, used by
+ * nwam_loc_validate_all_nameservices().
+ *
+ * requiredprop denotes the property that is mandatory when the
+ * configsrcprop is manual. errpropp is used to return the invalid
+ * property.
+ */
+static nwam_error_t
+nwam_loc_validate_one_nameservice(nwam_loc_handle_t loch,
+ const char *configsrcprop, const char *requiredprop, const char **errpropp)
+{
+ nwam_value_t configsrcval, requiredval;
+ uint64_t *configsrcs;
+ uint_t i, numvalues;
+
+ if (nwam_loc_get_prop_value(loch, configsrcprop, &configsrcval)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = configsrcprop;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+
+ if (nwam_value_get_uint64_array(configsrcval, &configsrcs, &numvalues)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = configsrcprop;
+ nwam_value_free(configsrcval);
+ return (NWAM_ENTITY_NO_VALUE);
+ }
+
+ /* If -configsrc is manual, requiredprop is required */
+ for (i = 0; i < numvalues; i++) {
+ if (configsrcs[i] == NWAM_CONFIGSRC_MANUAL) {
+ if (nwam_loc_get_prop_value(loch, requiredprop,
+ &requiredval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = requiredprop;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ nwam_value_free(requiredval);
+ }
+ }
+ nwam_value_free(configsrcval);
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Helper function to validate LDAP nameservice, used by
+ * nwam_loc_validate_all_nameservices(). Separated because LDAP must be
+ * configured manually only and both default-domain and -servers are required.
+ */
+static nwam_error_t
+nwam_loc_validate_ldap_nameservice(nwam_loc_handle_t loch,
+ const char **errpropp)
+{
+ nwam_value_t val;
+ uint64_t *configsrcs;
+ uint_t i, numvalues;
+
+ if (nwam_loc_get_prop_value(loch,
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC, &val) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ /* -configsrc is defined as an array */
+ if (nwam_value_get_uint64_array(val, &configsrcs, &numvalues)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC;
+ nwam_value_free(val);
+ return (NWAM_ENTITY_NO_VALUE);
+ }
+
+ /* -configsrc must be manual */
+ for (i = 0; i < numvalues; i++) {
+ if (configsrcs[i] != NWAM_CONFIGSRC_MANUAL) {
+ if (errpropp != NULL)
+ *errpropp =
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC;
+ nwam_value_free(val);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ }
+ nwam_value_free(val);
+
+ /* both default-domain and -servers are required */
+ if (nwam_loc_get_prop_value(loch,
+ NWAM_LOC_PROP_DEFAULT_DOMAIN, &val) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_DEFAULT_DOMAIN;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ nwam_value_free(val);
+
+ if (nwam_loc_get_prop_value(loch,
+ NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS, &val) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ nwam_value_free(val);
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Validates the different nameservices properties.
+ *
+ * If "nameservices" property has more than one nameservice to configure,
+ * "nameservices-config-file" must be specified. If only one nameservice
+ * is configured and "nameservices-config-file" is missing, set the
+ * property with the appropriately suffixed nsswitch file.
+ *
+ * For any nameservice being configured, the respective -configsrc property
+ * must be specified. For DNS, -servers is required if -configsrc is
+ * manual. For NIS and LDAP, default-domain is required if -configsrc is
+ * manual. For LDAP, -configsrc must be manual and -servers is required.
+ */
+static nwam_error_t
+nwam_loc_validate_all_nameservices(nwam_loc_handle_t loch,
+ nwam_value_t nameservicesval, const char **errpropp)
+{
+ nwam_error_t err;
+ nwam_value_t val;
+ uint64_t *nameservices;
+ uint_t i, numvalues;
+
+ if ((err = nwam_value_get_uint64_array(nameservicesval, &nameservices,
+ &numvalues)) != NWAM_SUCCESS)
+ return (err);
+
+ /*
+ * nameservices-config-file is required if nameservices has more
+ * than one value.
+ */
+ if (numvalues > 1) {
+ if (nwam_loc_get_prop_value(loch,
+ NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE, &val)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp =
+ NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ nwam_value_free(val);
+ } else if (numvalues == 1) {
+ /*
+ * If only one nameservice is being configured and
+ * nameservices-config-file doesn't exist, create it to
+ * point to the respective nsswitch file.
+ */
+ err = nwam_loc_get_prop_value(loch,
+ NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE, &val);
+ if (err == NWAM_INVALID_ARG || err == NWAM_ENTITY_NOT_FOUND) {
+ char *nsswitch;
+ const char *nsswitch_suffix;
+
+ /* get the single nameservice being configured */
+ if ((err = nwam_uint64_get_value_string(
+ NWAM_LOC_PROP_NAMESERVICES, nameservices[0],
+ &nsswitch_suffix)) != NWAM_SUCCESS)
+ goto config_file_fail;
+ if ((nsswitch = malloc(MAXPATHLEN)) == NULL) {
+ err = NWAM_NO_MEMORY;
+ goto config_file_fail;
+ }
+
+ /* create appropriately suffixed nsswitch name */
+ (void) snprintf(nsswitch, MAXPATHLEN, "%s%s",
+ NSSWITCH_PREFIX, nsswitch_suffix);
+ if ((err = nwam_value_create_string(nsswitch, &val))
+ != NWAM_SUCCESS) {
+ free(nsswitch);
+ goto config_file_fail;
+ }
+
+ err = nwam_set_prop_value(loch->nwh_data,
+ NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE, val);
+ free(nsswitch);
+ nwam_value_free(val);
+ if (err != NWAM_SUCCESS) {
+ nwam_value_free(val);
+ goto config_file_fail;
+ }
+ } else if (err != NWAM_SUCCESS) {
+ goto config_file_fail;
+ } else {
+ nwam_value_free(val);
+ }
+ }
+
+ /*
+ * validate the -configsrc property and the required default-domain
+ * and/or -servers property for each nameservice being configured.
+ */
+ for (i = 0; i < numvalues; i++) {
+ switch (nameservices[i]) {
+ case NWAM_NAMESERVICES_DNS:
+ if ((err = nwam_loc_validate_one_nameservice(loch,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
+ NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS, errpropp))
+ != NWAM_SUCCESS)
+ return (err);
+ break;
+ case NWAM_NAMESERVICES_NIS:
+ if ((err = nwam_loc_validate_one_nameservice(loch,
+ NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
+ NWAM_LOC_PROP_DEFAULT_DOMAIN, errpropp))
+ != NWAM_SUCCESS)
+ return (err);
+ break;
+ case NWAM_NAMESERVICES_LDAP:
+ if ((err = nwam_loc_validate_ldap_nameservice(loch,
+ errpropp)) != NWAM_SUCCESS)
+ return (err);
+ break;
+ case NWAM_NAMESERVICES_FILES:
+ break;
+ default:
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ }
+ return (NWAM_SUCCESS);
+
+config_file_fail:
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE;
+ return (err);
+}
+
+nwam_error_t
+nwam_loc_validate(nwam_loc_handle_t loch, const char **errpropp)
+{
+ nwam_error_t err;
+ nwam_value_t activationval, conditionval, nameservicesval;
+ uint64_t activation;
+ char **conditions, *name;
+ uint_t i, numvalues;
+ nwam_condition_object_type_t object_type;
+ nwam_condition_t condition;
+
+ assert(loch != NULL);
+
+ /*
+ * Make sure loc is internally consistent: if activation type is
+ * conditional, the condition string must be specified.
+ */
+ if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
+ &activationval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_ACTIVATION_MODE;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+
+ if (nwam_value_get_uint64(activationval, &activation)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_ACTIVATION_MODE;
+ nwam_value_free(activationval);
+ return (NWAM_ENTITY_NO_VALUE);
+ }
+
+ /* validate activation against the location first */
+ if ((err = nwam_loc_validate_activation_mode(loch, activationval))
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_ACTIVATION_MODE;
+ nwam_value_free(activationval);
+ return (err);
+ }
+ nwam_value_free(activationval);
+
+ if (activation == NWAM_ACTIVATION_MODE_CONDITIONAL_ANY ||
+ activation == NWAM_ACTIVATION_MODE_CONDITIONAL_ALL) {
+ if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_CONDITIONS,
+ &conditionval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_CONDITIONS;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ /*
+ * Are conditions self-referential? In other words, do any
+ * of the activation conditions refer to this location?
+ */
+ if (nwam_value_get_string_array(conditionval, &conditions,
+ &numvalues) != NWAM_SUCCESS) {
+ nwam_value_free(conditionval);
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_CONDITIONS;
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ if (nwam_loc_get_name(loch, &name) != NWAM_SUCCESS) {
+ nwam_value_free(conditionval);
+ return (NWAM_INVALID_ARG);
+ }
+ for (i = 0; i < numvalues; i++) {
+ char *object_name = NULL;
+
+ if (nwam_condition_string_to_condition(conditions[i],
+ &object_type, &condition, &object_name)
+ != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_CONDITIONS;
+ free(name);
+ nwam_value_free(conditionval);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ if (object_name != NULL &&
+ object_type == NWAM_CONDITION_OBJECT_TYPE_LOC &&
+ strcmp(object_name, name) == 0) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_CONDITIONS;
+ free(name);
+ free(object_name);
+ nwam_value_free(conditionval);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ free(object_name);
+ }
+ free(name);
+ nwam_value_free(conditionval);
+ }
+
+ /* validate namerservices */
+ if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_NAMESERVICES,
+ &nameservicesval) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = NWAM_LOC_PROP_NAMESERVICES;
+ return (NWAM_ENTITY_MISSING_MEMBER);
+ }
+ err = nwam_loc_validate_all_nameservices(loch, nameservicesval,
+ errpropp);
+ nwam_value_free(nameservicesval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_validate(loc_prop_table, loch, errpropp));
+}
+
+nwam_error_t
+nwam_loc_validate_prop(nwam_loc_handle_t loch, const char *propname,
+ nwam_value_t value)
+{
+ nwam_error_t err;
+
+ assert(loch != NULL);
+
+ if (strcmp(propname, NWAM_LOC_PROP_ACTIVATION_MODE) == 0) {
+ if ((err = nwam_loc_validate_activation_mode(loch, value))
+ != NWAM_SUCCESS)
+ return (err);
+ }
+
+ return (nwam_validate_prop(loc_prop_table, loch, propname, value));
+}
+
+nwam_error_t
+nwam_loc_copy(nwam_loc_handle_t oldloch, const char *newname,
+ nwam_loc_handle_t *newlochp)
+{
+ nwam_error_t err;
+ nwam_value_t val;
+
+ if ((err = nwam_copy(NWAM_LOC_CONF_FILE, oldloch, newname, newlochp))
+ != NWAM_SUCCESS)
+ return (err);
+
+ /* If the activation-mode is system, change it to manual */
+ if ((err = nwam_loc_get_prop_value(*newlochp,
+ NWAM_LOC_PROP_ACTIVATION_MODE, &val)) != NWAM_SUCCESS)
+ goto finish;
+ err = nwam_loc_validate_activation_mode(*newlochp, val);
+ nwam_value_free(val);
+ if (err != NWAM_SUCCESS) {
+ if ((err = nwam_value_create_uint64(NWAM_ACTIVATION_MODE_MANUAL,
+ &val)) != NWAM_SUCCESS)
+ goto finish;
+ err = nwam_set_prop_value((*newlochp)->nwh_data,
+ NWAM_LOC_PROP_ACTIVATION_MODE, val);
+ nwam_value_free(val);
+ if (err != NWAM_SUCCESS)
+ goto finish;
+
+ if ((err = nwam_value_create_boolean(B_FALSE, &val))
+ != NWAM_SUCCESS)
+ goto finish;
+ err = nwam_set_prop_value((*newlochp)->nwh_data,
+ NWAM_LOC_PROP_ENABLED, val);
+ nwam_value_free(val);
+ if (err != NWAM_SUCCESS)
+ goto finish;
+ }
+
+ return (NWAM_SUCCESS);
+
+finish:
+ nwam_loc_free(*newlochp);
+ *newlochp = NULL;
+ return (err);
+}
+
+/*
+ * Given a property, return expected property data type
+ */
+nwam_error_t
+nwam_loc_get_prop_type(const char *propname, nwam_value_type_t *typep)
+{
+ return (nwam_get_prop_type(loc_prop_table, propname, typep));
+}
+
+nwam_error_t
+nwam_loc_prop_multivalued(const char *propname, boolean_t *multip)
+{
+ return (nwam_prop_multivalued(loc_prop_table, propname, multip));
+}
+
+/*
+ * Determine if the location has manual activation-mode or not.
+ */
+nwam_error_t
+nwam_loc_is_manual(nwam_loc_handle_t loch, boolean_t *manualp)
+{
+ nwam_error_t err;
+ nwam_value_t actval;
+ uint64_t activation;
+
+ assert(loch != NULL);
+
+ if ((err = nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
+ &actval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(actval, &activation);
+ nwam_value_free(actval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ if (activation == NWAM_ACTIVATION_MODE_MANUAL)
+ *manualp = B_TRUE;
+ else
+ *manualp = B_FALSE;
+ return (NWAM_SUCCESS);
+}
+
+/* Determine if location is enabled or not */
+static nwam_error_t
+nwam_loc_is_enabled(nwam_loc_handle_t loch, boolean_t *enabledp)
+{
+ nwam_error_t err;
+ nwam_value_t enabledval;
+
+ assert(loch != NULL);
+
+ if ((err = nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ENABLED,
+ &enabledval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_boolean(enabledval, enabledp);
+ nwam_value_free(enabledval);
+ return (err);
+}
+
+/*
+ * Callback to disable all locations other than one to enable, the handle
+ * of which we pass in as an argument. If the argument is NULL, we disable
+ * all locations.
+ */
+static int
+loc_set_enabled(nwam_loc_handle_t loch, void *data)
+{
+ nwam_value_t enabledval;
+ boolean_t enabled = B_FALSE;
+ nwam_loc_handle_t testloch = data;
+ nwam_error_t err = NWAM_SUCCESS;
+
+ if (testloch != NULL) {
+ char *name, *testname;
+
+ if (nwam_loc_get_name(loch, &name) == NWAM_SUCCESS &&
+ nwam_loc_get_name(testloch, &testname) == NWAM_SUCCESS &&
+ strcmp(name, testname) == 0) {
+ /* We enable this location. */
+ enabled = B_TRUE;
+ }
+ }
+ if (nwam_value_create_boolean(enabled, &enabledval) != NWAM_SUCCESS)
+ return (0);
+ if (nwam_set_prop_value(loch->nwh_data, NWAM_LOC_PROP_ENABLED,
+ enabledval) == NWAM_SUCCESS)
+ err = nwam_loc_commit(loch, NWAM_FLAG_ENTITY_ENABLE);
+
+ nwam_value_free(enabledval);
+ return (err);
+}
+
+/*
+ * Update the enabled property for this location (and for all others
+ * if necessary.
+ */
+static int
+nwam_loc_update_enabled(nwam_loc_handle_t loch, boolean_t enabled)
+{
+ nwam_error_t err;
+ int cb_ret;
+
+ if (enabled) {
+ /*
+ * Disable all other locations that are manually enabled
+ * and enable this one - a maximum of 1 location can be
+ * enabled at once.
+ */
+ err = nwam_walk_locs(loc_set_enabled, loch, 0, &cb_ret);
+ if (err != NWAM_SUCCESS && err != NWAM_WALK_HALTED)
+ cb_ret = err;
+ } else {
+ cb_ret = loc_set_enabled(loch, NULL);
+ }
+ return (cb_ret);
+}
+
+nwam_error_t
+nwam_loc_enable(nwam_loc_handle_t loch)
+{
+ nwam_error_t err;
+ boolean_t enabled;
+
+ assert(loch != NULL);
+
+ /* Make sure location is not enabled */
+ if ((err = nwam_loc_is_enabled(loch, &enabled)) != NWAM_SUCCESS)
+ return (err);
+ if (enabled)
+ return (NWAM_SUCCESS);
+
+ if ((err = nwam_loc_update_enabled(loch, B_TRUE)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_enable(NULL, loch);
+
+ /* nwamd may not be running, that's okay. */
+ if (err == NWAM_ERROR_BIND)
+ return (NWAM_SUCCESS);
+ else
+ return (err);
+}
+
+nwam_error_t
+nwam_loc_disable(nwam_loc_handle_t loch)
+{
+ nwam_error_t err;
+ boolean_t enabled;
+
+ assert(loch != NULL);
+
+ /* Make sure location is enabled */
+ if ((err = nwam_loc_is_enabled(loch, &enabled)) != NWAM_SUCCESS)
+ return (err);
+ if (!enabled)
+ return (NWAM_SUCCESS);
+
+ if ((err = nwam_loc_update_enabled(loch, B_FALSE)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_disable(NULL, loch);
+
+ /* nwamd may not be running, that's okay. */
+ if (err == NWAM_ERROR_BIND)
+ return (NWAM_SUCCESS);
+ else
+ return (err);
+}
+
+nwam_error_t
+nwam_loc_get_default_proplist(const char ***prop_list, uint_t *numvaluesp)
+{
+ return (nwam_get_default_proplist(loc_prop_table,
+ NWAM_TYPE_ANY, NWAM_CLASS_ANY, prop_list, numvaluesp));
+}
+
+nwam_error_t
+nwam_loc_get_state(nwam_loc_handle_t loch, nwam_state_t *statep,
+ nwam_aux_state_t *auxp)
+{
+ return (nwam_get_state(NULL, loch, statep, auxp));
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_ncp.c b/usr/src/lib/libnwam/common/libnwam_ncp.c
new file mode 100644
index 0000000000..b2857a641a
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_ncp.c
@@ -0,0 +1,1719 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libdladm.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Functions to support creating, modifying, destroying, querying the
+ * state of and changing the state of NCP (Network Configuration Profiles)
+ * and the NCUs (Network Configuration Units) that are contained in those
+ * NCP objects. An NCP is simply a container for a set of NCUs which represent
+ * the datalink and interface configuration preferences for the system.
+ * An NCP can consist a set of prioritized link NCUs, e.g. wired links preferred
+ * over wireless, a set of manually enabled/diasbled NCUs, or a combination
+ * of both. Interface NCUs inherit activation from their underlying links,
+ * so if wired is preferred over wireless and a cable is plugged in,
+ * the wired link NCU will be active, as will the IP interface NCU above it.
+ */
+
+/*
+ * The NCU property table is used to mapping property types to property name
+ * strings, their associated value types etc. The table is used for validation
+ * purposes, and for commit()ing and read()ing NCUs.
+ */
+
+static nwam_error_t valid_type(nwam_value_t);
+static nwam_error_t valid_class(nwam_value_t);
+static nwam_error_t valid_ncp(nwam_value_t);
+static nwam_error_t valid_priority_mode(nwam_value_t);
+static nwam_error_t valid_ncu_activation_mode(nwam_value_t);
+static nwam_error_t valid_link_autopush(nwam_value_t);
+static nwam_error_t valid_link_mtu(nwam_value_t);
+static nwam_error_t valid_ip_version(nwam_value_t);
+static nwam_error_t valid_addrsrc_v4(nwam_value_t);
+static nwam_error_t valid_addrsrc_v6(nwam_value_t);
+
+struct nwam_prop_table_entry ncu_prop_table_entries[] = {
+ {NWAM_NCU_PROP_TYPE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1, valid_type,
+ "specifies the NCU type - valid values are \'datalink\' and \'ip\'",
+ NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
+ {NWAM_NCU_PROP_CLASS, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
+ valid_class,
+ "specifies the NCU class - valid values are "
+ "\'phys\' and \'ip\'",
+ NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
+ {NWAM_NCU_PROP_PARENT_NCP, NWAM_VALUE_TYPE_STRING, B_FALSE, 1, 1,
+ valid_ncp,
+ "specifies the parent NCP name",
+ NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
+ {NWAM_NCU_PROP_ACTIVATION_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 1, 1,
+ valid_ncu_activation_mode,
+ "specifies the NCU activation mode - valid values are:\n"
+ "\'prioritized\' and \'manual\'",
+ NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
+ {NWAM_NCU_PROP_ENABLED, NWAM_VALUE_TYPE_BOOLEAN, B_TRUE, 0, 1,
+ nwam_valid_boolean,
+ "specifies if manual NCU is to be enabled",
+ NWAM_FLAG_NCU_TYPE_ALL, NWAM_FLAG_NCU_CLASS_ALL},
+ {NWAM_NCU_PROP_PRIORITY_GROUP, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
+ nwam_valid_uint64,
+ "specifies the priority grouping of NCUs - lower values are "
+ "prioritized, negative values are invalid",
+ NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
+ {NWAM_NCU_PROP_PRIORITY_MODE, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
+ valid_priority_mode,
+ "specifies the mode of prioritization - valid values are:\n"
+ "\'exclusive\', \'shared\' and \'all\'",
+ NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
+ {NWAM_NCU_PROP_LINK_MAC_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0, 1,
+ nwam_valid_mac_addr,
+ "specifies MAC address of form aa:bb:cc:dd:ee:ff for the link",
+ NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
+ {NWAM_NCU_PROP_LINK_AUTOPUSH, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, valid_link_autopush,
+ "specifies modules to autopush on link",
+ NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
+ {NWAM_NCU_PROP_LINK_MTU, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0, 1,
+ valid_link_mtu,
+ "specifies MTU for link",
+ NWAM_FLAG_NCU_TYPE_LINK, NWAM_FLAG_NCU_CLASS_ALL_LINK},
+ {NWAM_NCU_PROP_IP_VERSION, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, valid_ip_version,
+ "specifies IP versions for IP NCU - valid values are:\n"
+ "\'ipv4\' and \'ipv6\'",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+ {NWAM_NCU_PROP_IPV4_ADDRSRC, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, valid_addrsrc_v4,
+ "specifies IPv4 address source(s) - valid values are:\n"
+ "\'dhcp\' and \'static\'",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+ {NWAM_NCU_PROP_IPV4_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, nwam_valid_host_v4,
+ "specifies static IPv4 host address(es)",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+ {NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ 1, nwam_valid_route_v4,
+ "specifies per-interface default IPv4 route",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+ {NWAM_NCU_PROP_IPV6_ADDRSRC, NWAM_VALUE_TYPE_UINT64, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, valid_addrsrc_v6,
+ "specifies IPv6 address source(s) - valid values are:\n"
+ "\'dhcp\', \'autoconf\' and \'static\'.\n"
+ "\'dhcp\' and \'autoconf\' are mandatory values.",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+ {NWAM_NCU_PROP_IPV6_ADDR, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ NWAM_MAX_NUM_VALUES, nwam_valid_host_v6,
+ "specifies static IPv6 host address(es)",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE},
+ {NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_VALUE_TYPE_STRING, B_FALSE, 0,
+ 1, nwam_valid_route_v6,
+ "specifies per-interface default IPv6 route",
+ NWAM_FLAG_NCU_TYPE_INTERFACE, NWAM_FLAG_NCU_CLASS_ALL_INTERFACE}
+};
+
+#define NWAM_NUM_NCU_PROPS (sizeof (ncu_prop_table_entries) / \
+ sizeof (*ncu_prop_table_entries))
+
+struct nwam_prop_table ncu_prop_table =
+ { NWAM_NUM_NCU_PROPS, ncu_prop_table_entries };
+
+nwam_error_t
+nwam_ncp_get_name(nwam_ncp_handle_t ncph, char **namep)
+{
+ return (nwam_get_name(ncph, namep));
+}
+
+static nwam_error_t
+nwam_ncp_name_to_file(const char *name, char **filename)
+{
+ assert(name != NULL && filename != NULL);
+
+ if ((*filename = malloc(MAXPATHLEN)) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ (void) snprintf(*filename, MAXPATHLEN, "%s%s%s%s", NWAM_CONF_DIR,
+ NWAM_NCP_CONF_FILE_PRE, name, NWAM_NCP_CONF_FILE_SUF);
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED1 */
+nwam_error_t
+nwam_ncp_create(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
+{
+ nwam_error_t err;
+ char *ncpfile;
+
+ if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, name, ncphp))
+ != NWAM_SUCCESS)
+ return (err);
+
+ /* Create empty container for NCUs */
+ if ((err = nwam_ncp_name_to_file(name, &ncpfile))
+ != NWAM_SUCCESS) {
+ nwam_free(*ncphp);
+ *ncphp = NULL;
+ return (err);
+ }
+
+ if ((err = nwam_commit(ncpfile, *ncphp, flags)) != NWAM_SUCCESS) {
+ nwam_free(*ncphp);
+ *ncphp = NULL;
+ }
+
+ free(ncpfile);
+
+ return (err);
+}
+
+/* Used by libnwam_files.c */
+nwam_error_t
+nwam_ncp_file_to_name(const char *path, char **name)
+{
+ char path_copy[MAXPATHLEN];
+ char *filename, *suffix;
+
+ assert(path != NULL && name != NULL);
+
+ /* Make a copy as basename(3c) may modify string */
+ (void) strlcpy(path_copy, path, MAXPATHLEN);
+
+ if ((*name = malloc(NWAM_MAX_NAME_LEN)) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ if ((filename = basename(path_copy)) == NULL) {
+ free(*name);
+ return (NWAM_ENTITY_INVALID);
+ }
+
+ /* Ensure filename begins/ends with right prefix/suffix */
+ if (sscanf(filename, NWAM_NCP_CONF_FILE_PRE "%256[^\n]s", *name) < 1) {
+ free(*name);
+ return (NWAM_ENTITY_INVALID);
+ }
+ suffix = *name + strlen(*name) - strlen(NWAM_NCP_CONF_FILE_SUF);
+ if (strstr(*name, NWAM_NCP_CONF_FILE_SUF) != suffix) {
+ free(*name);
+ return (NWAM_ENTITY_INVALID);
+ }
+ suffix[0] = '\0';
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED1 */
+nwam_error_t
+nwam_ncp_read(const char *name, uint64_t flags, nwam_ncp_handle_t *ncphp)
+{
+ char *filename;
+ nwam_error_t err;
+
+ assert(name != NULL && ncphp != NULL);
+
+ /* try to read the associated ncp configuration */
+ if ((err = nwam_ncp_name_to_file(name, &filename)) != NWAM_SUCCESS) {
+ *ncphp = NULL;
+ return (err);
+ }
+
+ err = nwam_read(NWAM_OBJECT_TYPE_NCP, filename, name, flags, ncphp);
+ free(filename);
+ return (err);
+}
+
+static nwam_error_t
+nwam_ncu_get_parent_ncp_name(nwam_ncu_handle_t ncuh, char **parentnamep)
+{
+ nwam_value_t parentval = NULL;
+ char *parentname;
+ nwam_error_t err;
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_PARENT_NCP,
+ &parentval)) != NWAM_SUCCESS ||
+ (err = nwam_value_get_string(parentval, &parentname))
+ != NWAM_SUCCESS ||
+ (*parentnamep = strdup(parentname)) == NULL) {
+ if (parentval != NULL)
+ nwam_value_free(parentval);
+ *parentnamep = NULL;
+ return (err);
+ }
+ nwam_value_free(parentval);
+
+ return (NWAM_SUCCESS);
+}
+
+static int
+nwam_ncp_copy_callback(nwam_ncu_handle_t oldncuh, void *arg)
+{
+ nwam_error_t err;
+ nwam_ncu_handle_t newncuh = NULL;
+ char *oldparent;
+ char *oldfilename = NULL, *newfilename = NULL;
+ nwam_ncp_handle_t newncph = (nwam_ncp_handle_t)arg;
+ nwam_value_t newparentval;
+
+ /* Get filenames for the new and old NCU's */
+ if ((err = nwam_ncu_get_parent_ncp_name(oldncuh, &oldparent))
+ != NWAM_SUCCESS)
+ return (err);
+ err = nwam_ncp_name_to_file(oldparent, &oldfilename);
+ free(oldparent);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_ncp_name_to_file(newncph->nwh_name, &newfilename))
+ != NWAM_SUCCESS)
+ goto fail;
+
+ /* new NCU name (and typedname) is the same as the old name */
+ if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, oldncuh->nwh_name,
+ &newncuh)) != NWAM_SUCCESS)
+ goto fail;
+ /* Duplicate the old NCU's data */
+ if ((err = nwam_dup_object_list(oldncuh->nwh_data,
+ &(newncuh->nwh_data))) != NWAM_SUCCESS)
+ goto fail;
+
+ /* Update the parent property for the new NCU */
+ if ((err = nwam_value_create_string(newncph->nwh_name, &newparentval))
+ != NWAM_SUCCESS)
+ goto fail;
+ err = nwam_set_prop_value(newncuh->nwh_data, NWAM_NCU_PROP_PARENT_NCP,
+ newparentval);
+ nwam_value_free(newparentval);
+ if (err != NWAM_SUCCESS)
+ goto fail;
+
+ /* Save the new NCU */
+ err = nwam_commit(newfilename, newncuh, 0);
+
+fail:
+ free(oldfilename);
+ free(newfilename);
+ nwam_ncu_free(newncuh);
+ return (err);
+}
+
+nwam_error_t
+nwam_ncp_copy(nwam_ncp_handle_t oldncph, const char *newname,
+ nwam_ncp_handle_t *newncphp)
+{
+ nwam_ncp_handle_t ncph;
+ nwam_error_t err;
+ int cb_ret;
+
+ assert(oldncph != NULL && newname != NULL && newncphp != NULL);
+
+ /* check if newname NCP already exists */
+ if (nwam_ncp_read(newname, 0, &ncph) == NWAM_SUCCESS) {
+ nwam_ncp_free(ncph);
+ *newncphp = NULL;
+ return (NWAM_ENTITY_EXISTS);
+ }
+
+ /* create new handle */
+ if ((err = nwam_ncp_create(newname, 0, newncphp)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_ncp_walk_ncus(oldncph, nwam_ncp_copy_callback, *newncphp,
+ NWAM_FLAG_NCU_TYPE_CLASS_ALL, &cb_ret);
+ if (err != NWAM_SUCCESS) {
+ /* remove the NCP even if any NCU's had already been copied */
+ (void) nwam_ncp_destroy(*newncphp, 0);
+ *newncphp = NULL;
+ if (err == NWAM_WALK_HALTED)
+ return (cb_ret);
+ else
+ return (err);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Convert type to flag
+ */
+static uint64_t
+nwam_ncu_type_to_flag(nwam_ncu_type_t type)
+{
+ switch (type) {
+ case NWAM_NCU_TYPE_LINK:
+ return (NWAM_FLAG_NCU_TYPE_LINK);
+ case NWAM_NCU_TYPE_INTERFACE:
+ return (NWAM_FLAG_NCU_TYPE_INTERFACE);
+ case NWAM_NCU_TYPE_ANY:
+ return (NWAM_FLAG_NCU_TYPE_ALL);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Convert class to flag
+ */
+uint64_t
+nwam_ncu_class_to_flag(nwam_ncu_class_t class)
+{
+ switch (class) {
+ case NWAM_NCU_CLASS_PHYS:
+ return (NWAM_FLAG_NCU_CLASS_PHYS);
+ case NWAM_NCU_CLASS_IP:
+ return (NWAM_FLAG_NCU_CLASS_IP);
+ case NWAM_NCU_CLASS_ANY:
+ return (NWAM_FLAG_NCU_CLASS_ALL);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Infer NCU type from NCU class
+ */
+nwam_ncu_type_t
+nwam_ncu_class_to_type(nwam_ncu_class_t class)
+{
+ switch (class) {
+ case NWAM_NCU_CLASS_PHYS:
+ return (NWAM_NCU_TYPE_LINK);
+ case NWAM_NCU_CLASS_IP:
+ return (NWAM_NCU_TYPE_INTERFACE);
+ case NWAM_NCU_CLASS_ANY:
+ return (NWAM_NCU_TYPE_ANY);
+ default:
+ return (NWAM_NCU_TYPE_UNKNOWN);
+ }
+}
+
+/*
+ * Make ncp active, deactivating any other active ncp.
+ */
+nwam_error_t
+nwam_ncp_enable(nwam_ncp_handle_t ncph)
+{
+ nwam_error_t err;
+ char *name;
+
+ assert(ncph != NULL);
+
+ err = nwam_enable(NULL, ncph);
+
+ if (err == NWAM_ERROR_BIND) {
+ /*
+ * nwamd is not running, set active_ncp property so when
+ * nwamd is next started, this NCP will be used.
+ */
+ if ((err = nwam_ncp_get_name(ncph, &name)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_set_smf_string_property(NWAM_FMRI, NWAM_PG,
+ NWAM_PROP_ACTIVE_NCP, name);
+ free(name);
+ }
+
+ return (err);
+}
+
+/* Compare NCP names c1 and c2 using strcasecmp() */
+static int
+ncpname_cmp(const void *c1, const void *c2)
+{
+ return (strcasecmp(*(const char **)c1, *(const char **)c2));
+}
+
+/* ARGSUSED1 */
+nwam_error_t
+nwam_walk_ncps(int (*cb)(nwam_ncp_handle_t, void *), void *data,
+ uint64_t flags, int *retp)
+{
+ char *ncpname, **ncpfiles;
+ nwam_ncp_handle_t ncph;
+ nwam_error_t err;
+ nwam_value_t value;
+ void *objlist;
+ uint_t i, num_ncpfiles;
+ int ret = 0;
+
+ assert(cb != NULL);
+
+ if ((err = nwam_valid_flags(flags, NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
+ return (err);
+ /*
+ * To get list of NCP files, call nwam_read_object_from_backend()
+ * with "parent" argument set to NULL. We get back an object list
+ * consisting of string arrays for each object type - NCP, ENM
+ * and location. We retrieve the NCP list, which corresponds to
+ * the set of NCP backend parent objects (these are files at present).
+ */
+ if ((err = nwam_read_object_from_backend(NULL, NULL, flags,
+ &objlist)) != NWAM_SUCCESS)
+ return (err);
+
+ if ((err = nwam_get_prop_value(objlist, NWAM_NCP_OBJECT_STRING, &value))
+ != NWAM_SUCCESS) {
+ nwam_free_object_list(objlist);
+ return (err);
+ }
+ if ((err = nwam_value_get_string_array(value, &ncpfiles,
+ &num_ncpfiles)) != NWAM_SUCCESS) {
+ nwam_value_free(value);
+ nwam_free_object_list(objlist);
+ return (err);
+ }
+
+ /* sort the NCP names alphabetically */
+ qsort(ncpfiles, num_ncpfiles, sizeof (char *), ncpname_cmp);
+
+ for (i = 0; i < num_ncpfiles; i++) {
+ if (nwam_ncp_file_to_name(ncpfiles[i], &ncpname)
+ != NWAM_SUCCESS)
+ continue;
+ if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, ncpname,
+ &ncph)) != NWAM_SUCCESS) {
+ free(ncpname);
+ break;
+ }
+ ret = cb(ncph, data);
+ free(ncph);
+ free(ncpname);
+ if (ret != 0) {
+ err = NWAM_WALK_HALTED;
+ break;
+ }
+ }
+ nwam_value_free(value);
+ nwam_free_object_list(objlist);
+
+ if (retp != NULL)
+ *retp = ret;
+ return (err);
+}
+
+/*
+ * Checks if NCP is read-only. Only NWAM_NCP_NAME_AUTOMATIC is read-only
+ * for all but the netadm user (which nwamd runs as).
+ */
+nwam_error_t
+nwam_ncp_get_read_only(nwam_ncp_handle_t ncph, boolean_t *readp)
+{
+ nwam_error_t err;
+ char *name;
+
+ assert(ncph != NULL && readp != NULL);
+
+ if ((err = nwam_ncp_get_name(ncph, &name)) != NWAM_SUCCESS)
+ return (err);
+
+ if (NWAM_NCP_AUTOMATIC(name))
+ *readp = !nwam_uid_is_netadm();
+ else
+ *readp = B_FALSE;
+
+ free(name);
+ return (NWAM_SUCCESS);
+}
+
+/* Checks if NCU is writable depending on its parent */
+nwam_error_t
+nwam_ncu_get_read_only(nwam_ncu_handle_t ncuh, boolean_t *readp)
+{
+ nwam_error_t err;
+ nwam_ncp_handle_t ncph;
+
+ assert(ncuh != NULL && readp != NULL);
+
+ if ((err = nwam_ncu_get_ncp(ncuh, &ncph)) != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_ncp_get_read_only(ncph, readp);
+ nwam_ncp_free(ncph);
+ return (err);
+}
+
+/* Returns true if the NCP is active */
+static boolean_t
+nwam_ncp_is_active(nwam_ncp_handle_t ncph)
+{
+ char *active_ncp, *name;
+ boolean_t ret;
+
+ assert(ncph != NULL);
+
+ /*
+ * Determine which NCP is active via the nwamd/active_ncp property
+ * value. This allows us to determine which NCP is active even
+ * if nwamd is not running.
+ */
+ if (nwam_ncp_get_name(ncph, &name) != NWAM_SUCCESS ||
+ nwam_get_smf_string_property(NWAM_FMRI, NWAM_PG,
+ NWAM_PROP_ACTIVE_NCP, &active_ncp) != NWAM_SUCCESS)
+ return (B_FALSE);
+
+ ret = (strcmp(name, active_ncp) == 0);
+
+ free(active_ncp);
+ free(name);
+
+ return (ret);
+}
+
+nwam_error_t
+nwam_ncp_destroy(nwam_ncp_handle_t ncph, uint64_t flags)
+{
+ char *filename;
+ nwam_error_t err;
+ boolean_t read_only;
+
+ assert(ncph != NULL);
+
+ if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
+ return (err);
+ if (read_only)
+ return (NWAM_ENTITY_NOT_DESTROYABLE);
+
+ if (nwam_ncp_is_active(ncph))
+ return (NWAM_ENTITY_IN_USE);
+
+ if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &filename))
+ != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_destroy(filename, ncph, flags);
+ free(filename);
+
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+nwam_ncu_internal_name_to_name(const char *internalname,
+ nwam_ncu_type_t *typep, char **namep)
+{
+ char *prefixstr;
+
+ assert(internalname != NULL && namep != NULL);
+
+ if (strncasecmp(internalname, NWAM_NCU_LINK_NAME_PRE,
+ strlen(NWAM_NCU_LINK_NAME_PRE)) == 0) {
+ prefixstr = NWAM_NCU_LINK_NAME_PRE;
+ *typep = NWAM_NCU_TYPE_LINK;
+ } else if (strncasecmp(internalname, NWAM_NCU_INTERFACE_NAME_PRE,
+ strlen(NWAM_NCU_INTERFACE_NAME_PRE)) == 0) {
+ prefixstr = NWAM_NCU_INTERFACE_NAME_PRE;
+ *typep = NWAM_NCU_TYPE_INTERFACE;
+ } else {
+ return (NWAM_INVALID_ARG);
+ }
+
+ *namep = strdup(internalname + strlen(prefixstr));
+ if (*namep == NULL)
+ return (NWAM_NO_MEMORY);
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED2 */
+static int
+ncu_selectcb(struct nwam_handle *hp, uint64_t flags, void *data)
+{
+ nwam_ncu_handle_t ncuh = hp;
+ nwam_value_t typeval = NULL, classval = NULL;
+ uint64_t type, class, matchflags, walkfilter;
+
+ if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval)
+ != NWAM_SUCCESS ||
+ nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &classval)
+ != NWAM_SUCCESS) {
+ if (typeval != NULL)
+ nwam_value_free(typeval);
+ return (NWAM_INVALID_ARG);
+ }
+ if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS ||
+ nwam_value_get_uint64(classval, &class) != NWAM_SUCCESS) {
+ nwam_value_free(typeval);
+ nwam_value_free(classval);
+ return (NWAM_INVALID_ARG);
+ }
+
+ matchflags = nwam_ncu_type_to_flag(type) |
+ nwam_ncu_class_to_flag(class);
+ nwam_value_free(typeval);
+ nwam_value_free(classval);
+
+ if ((walkfilter = (flags & NWAM_WALK_FILTER_MASK)) == 0)
+ walkfilter = NWAM_FLAG_NCU_TYPE_CLASS_ALL;
+
+ if (matchflags & walkfilter)
+ return (NWAM_SUCCESS);
+ return (NWAM_INVALID_ARG);
+}
+
+nwam_error_t
+nwam_ncp_walk_ncus(nwam_ncp_handle_t ncph,
+ int(*cb)(nwam_ncu_handle_t, void *), void *data, uint64_t flags, int *retp)
+{
+ char *ncpfile;
+ nwam_error_t err;
+
+ assert(ncph != NULL && cb != NULL);
+
+ if ((err = nwam_valid_flags(flags,
+ NWAM_FLAG_NCU_TYPE_CLASS_ALL | NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
+ return (err);
+
+ if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &ncpfile))
+ != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_walk(NWAM_OBJECT_TYPE_NCU, ncpfile, cb, data, flags,
+ retp, ncu_selectcb);
+ free(ncpfile);
+
+ return (err);
+}
+
+void
+nwam_ncp_free(nwam_ncp_handle_t ncph)
+{
+ nwam_free(ncph);
+}
+
+/*
+ * Are ncu type and class compatible?
+ */
+static boolean_t
+nwam_ncu_type_class_compatible(nwam_ncu_type_t type, nwam_ncu_class_t class)
+{
+ switch (type) {
+ case NWAM_NCU_TYPE_LINK:
+ return (class == NWAM_NCU_CLASS_PHYS);
+ case NWAM_NCU_TYPE_INTERFACE:
+ return (class == NWAM_NCU_CLASS_IP);
+ default:
+ return (B_FALSE);
+ }
+}
+
+/* Name to validate may be internal name. If so, convert it before validating */
+static boolean_t
+valid_ncu_name(const char *name)
+{
+ char *n;
+ boolean_t ret;
+ nwam_ncu_type_t type;
+
+ if (nwam_ncu_internal_name_to_name(name, &type, &n) == NWAM_SUCCESS) {
+
+ ret = dladm_valid_linkname(n);
+ free(n);
+ } else {
+ ret = dladm_valid_linkname(name);
+ }
+
+ return (ret);
+}
+
+nwam_error_t
+nwam_ncu_create(nwam_ncp_handle_t ncph, const char *name,
+ nwam_ncu_type_t type, nwam_ncu_class_t class, nwam_ncu_handle_t *ncuhp)
+{
+ nwam_ncu_handle_t ncuh;
+ nwam_value_t typeval = NULL, classval = NULL, parentval = NULL;
+ nwam_value_t enabledval = NULL;
+ nwam_error_t err;
+ boolean_t read_only;
+ char *typedname;
+
+ assert(ncph != NULL && name != NULL && ncuhp != NULL);
+
+ if (!valid_ncu_name(name))
+ return (NWAM_INVALID_ARG);
+
+ if ((err = nwam_ncp_get_read_only(ncph, &read_only)) != NWAM_SUCCESS)
+ return (err);
+ if (read_only)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ if (nwam_ncu_read(ncph, name, type, 0, &ncuh) == NWAM_SUCCESS) {
+ nwam_ncu_free(ncuh);
+ return (NWAM_ENTITY_EXISTS);
+ }
+
+ if (!valid_ncu_name(name) ||
+ !nwam_ncu_type_class_compatible(type, class))
+ return (NWAM_INVALID_ARG);
+
+ if ((err = nwam_ncu_name_to_typed_name(name, type, &typedname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ /* Create handle */
+ if ((err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, typedname, ncuhp))
+ != NWAM_SUCCESS)
+ return (err);
+ free(typedname);
+
+ /*
+ * Create new object list for NCU. The new NCU is initialized with
+ * the appropriate type and class.
+ */
+ if ((err = nwam_alloc_object_list(&(*ncuhp)->nwh_data)) != NWAM_SUCCESS)
+ goto finish;
+
+ if ((err = nwam_value_create_uint64(type, &typeval))
+ != NWAM_SUCCESS ||
+ (err = nwam_value_create_uint64(class, &classval))
+ != NWAM_SUCCESS ||
+ (err = nwam_value_create_string(ncph->nwh_name, &parentval))
+ != NWAM_SUCCESS ||
+ (err = nwam_value_create_boolean(B_TRUE, &enabledval))
+ != NWAM_SUCCESS) {
+ goto finish;
+ }
+ if ((err = nwam_set_prop_value((*ncuhp)->nwh_data, NWAM_NCU_PROP_TYPE,
+ typeval)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value((*ncuhp)->nwh_data, NWAM_NCU_PROP_CLASS,
+ classval)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value((*ncuhp)->nwh_data,
+ NWAM_NCU_PROP_PARENT_NCP, parentval)) != NWAM_SUCCESS ||
+ (err = nwam_set_prop_value((*ncuhp)->nwh_data,
+ NWAM_NCU_PROP_ENABLED, enabledval)) != NWAM_SUCCESS) {
+ goto finish;
+ }
+
+ /* Set default IP, datalink properties */
+ if (type == NWAM_NCU_TYPE_INTERFACE && class == NWAM_NCU_CLASS_IP) {
+
+ uint64_t ver[] = { IPV4_VERSION, IPV6_VERSION };
+ uint64_t v6src[] = { NWAM_ADDRSRC_DHCP, NWAM_ADDRSRC_AUTOCONF };
+ uint_t vercnt = 2, v6srccnt = 2;
+ nwam_value_t ipver = NULL, v4addrsrc = NULL, v6addrsrc = NULL;
+
+ if ((err = nwam_value_create_uint64_array(ver, vercnt, &ipver))
+ != NWAM_SUCCESS ||
+ (err = nwam_value_create_uint64(NWAM_ADDRSRC_DHCP,
+ &v4addrsrc)) != NWAM_SUCCESS ||
+ (err = nwam_value_create_uint64_array(v6src, v6srccnt,
+ &v6addrsrc)) != NWAM_SUCCESS) {
+ nwam_value_free(ipver);
+ nwam_value_free(v4addrsrc);
+ goto finish;
+ }
+ if ((err = nwam_set_prop_value((*ncuhp)->nwh_data,
+ NWAM_NCU_PROP_IP_VERSION, ipver)) == NWAM_SUCCESS &&
+ (err = nwam_set_prop_value((*ncuhp)->nwh_data,
+ NWAM_NCU_PROP_IPV4_ADDRSRC, v4addrsrc)) == NWAM_SUCCESS) {
+ err = nwam_set_prop_value((*ncuhp)->nwh_data,
+ NWAM_NCU_PROP_IPV6_ADDRSRC, v6addrsrc);
+ }
+ nwam_value_free(ipver);
+ nwam_value_free(v4addrsrc);
+ nwam_value_free(v6addrsrc);
+ } else {
+ nwam_value_t actval = NULL;
+ if ((err = nwam_value_create_uint64(NWAM_ACTIVATION_MODE_MANUAL,
+ &actval)) != NWAM_SUCCESS)
+ goto finish;
+ err = nwam_set_prop_value((*ncuhp)->nwh_data,
+ NWAM_NCU_PROP_ACTIVATION_MODE, actval);
+ nwam_value_free(actval);
+ }
+
+finish:
+ nwam_value_free(typeval);
+ nwam_value_free(classval);
+ nwam_value_free(parentval);
+ nwam_value_free(enabledval);
+ if (err != NWAM_SUCCESS) {
+ nwam_ncu_free(*ncuhp);
+ *ncuhp = NULL;
+ }
+ return (err);
+}
+
+nwam_error_t
+nwam_ncu_read(nwam_ncp_handle_t ncph, const char *name,
+ nwam_ncu_type_t type, uint64_t flags, nwam_ncu_handle_t *ncuhp)
+{
+ char *ncpfile, *typedname;
+ nwam_error_t err, err_ip, err_link;
+ nwam_ncu_handle_t ncuh_ip, ncuh_link;
+
+ assert(ncph != NULL && name != NULL && ncuhp != NULL);
+
+ if ((err = nwam_ncp_name_to_file(ncph->nwh_name, &ncpfile))
+ != NWAM_SUCCESS)
+ return (err);
+
+ if (type == NWAM_NCU_TYPE_ANY) {
+
+ free(ncpfile);
+
+ /*
+ * If we get to this point, we have discovered that no
+ * NCU type is discernable from name or type arguments.
+ * Either exactly one NCU called name must exist of either
+ * type, or the operation should fail.
+ */
+ err_ip = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_INTERFACE,
+ flags, &ncuh_ip);
+ err_link = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK,
+ flags, &ncuh_link);
+
+ *ncuhp = NULL;
+
+ if (err_ip == NWAM_SUCCESS && err_link == NWAM_SUCCESS) {
+ nwam_ncu_free(ncuh_ip);
+ nwam_ncu_free(ncuh_link);
+ err = NWAM_ENTITY_MULTIPLE_VALUES;
+ } else if (err_ip != NWAM_SUCCESS && err_link != NWAM_SUCCESS) {
+ err = NWAM_ENTITY_NOT_FOUND;
+ } else {
+ if (err_ip == NWAM_SUCCESS) {
+ *ncuhp = ncuh_ip;
+ } else {
+ *ncuhp = ncuh_link;
+ }
+ err = NWAM_SUCCESS;
+ }
+
+ return (err);
+ }
+ if ((err = nwam_ncu_name_to_typed_name(name, type, &typedname)) !=
+ NWAM_SUCCESS) {
+ free(ncpfile);
+ return (err);
+ }
+ err = nwam_read(NWAM_OBJECT_TYPE_NCU, ncpfile, typedname, flags, ncuhp);
+
+ free(typedname);
+ free(ncpfile);
+
+ return (err);
+}
+
+nwam_error_t
+nwam_ncu_get_name(nwam_ncu_handle_t ncuh, char **namep)
+{
+ nwam_ncu_type_t type;
+
+ assert(ncuh != NULL && namep != NULL);
+
+ return (nwam_ncu_internal_name_to_name(ncuh->nwh_name, &type, namep));
+}
+
+nwam_error_t
+nwam_ncu_name_to_typed_name(const char *name, nwam_ncu_type_t type,
+ char **typednamep)
+{
+ char *prefixstr;
+ size_t typednamesz;
+
+ assert(name != NULL && typednamep != NULL);
+
+ switch (type) {
+ case NWAM_NCU_TYPE_INTERFACE:
+ prefixstr = NWAM_NCU_INTERFACE_NAME_PRE;
+ break;
+ case NWAM_NCU_TYPE_LINK:
+ prefixstr = NWAM_NCU_LINK_NAME_PRE;
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+ }
+ typednamesz = strlen(name) + strlen(prefixstr) + 1;
+ if ((*typednamep = malloc(typednamesz)) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ /* Name may be already qualified by type */
+ if (strncasecmp(prefixstr, name, strlen(prefixstr)) == 0) {
+ (void) snprintf(*typednamep, typednamesz, "%s", name);
+ } else {
+ (void) snprintf(*typednamep, typednamesz, "%s%s",
+ prefixstr, name);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_ncu_typed_name_to_name(const char *typed_name, nwam_ncu_type_t *typep,
+ char **name)
+{
+ return (nwam_ncu_internal_name_to_name(typed_name, typep, name));
+}
+
+void
+nwam_ncu_free(nwam_ncu_handle_t ncuh)
+{
+ nwam_free(ncuh);
+}
+
+nwam_error_t
+nwam_ncu_copy(nwam_ncu_handle_t oldncuh, const char *newname,
+ nwam_ncu_handle_t *newncuhp)
+{
+ nwam_ncp_handle_t ncph;
+ nwam_ncu_handle_t ncuh;
+ nwam_error_t err;
+ nwam_value_t typeval;
+ uint64_t type;
+ char *typednewname;
+
+ assert(oldncuh != NULL && newname != NULL && newncuhp != NULL);
+
+ if (nwam_ncu_get_prop_value(oldncuh, NWAM_NCU_PROP_TYPE,
+ &typeval) != NWAM_SUCCESS) {
+ return (NWAM_INVALID_ARG);
+ }
+ if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS) {
+ nwam_value_free(typeval);
+ return (NWAM_INVALID_ARG);
+ }
+ nwam_value_free(typeval);
+
+ /* check if newname NCU already exists */
+ if ((err = nwam_ncu_get_ncp(oldncuh, &ncph)) != NWAM_SUCCESS)
+ return (err);
+ if (nwam_ncu_read(ncph, newname, type, 0, &ncuh) == NWAM_SUCCESS) {
+ nwam_ncu_free(ncuh);
+ nwam_ncp_free(ncph);
+ return (NWAM_ENTITY_EXISTS);
+ }
+ nwam_ncp_free(ncph);
+
+ if ((err = nwam_ncu_name_to_typed_name(newname, type, &typednewname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_handle_create(NWAM_OBJECT_TYPE_NCU, typednewname, newncuhp);
+ free(typednewname);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_dup_object_list(oldncuh->nwh_data,
+ &((*newncuhp)->nwh_data))) != NWAM_SUCCESS) {
+ free(*newncuhp);
+ *newncuhp = NULL;
+ return (err);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_ncu_delete_prop(nwam_ncu_handle_t ncuh, const char *propname)
+{
+ boolean_t ro_ncu, ro_prop;
+ nwam_error_t err;
+ void *olddata;
+
+ assert(ncuh != NULL && propname != NULL);
+
+ if ((err = nwam_ncu_get_read_only(ncuh, &ro_ncu)) != NWAM_SUCCESS ||
+ (err = nwam_ncu_prop_read_only(propname, &ro_prop)) != NWAM_SUCCESS)
+ return (err);
+ if (ro_ncu || ro_prop)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ /*
+ * Duplicate data, remove property and validate. If validation
+ * fails, revert to data duplicated prior to remove.
+ */
+ if ((err = nwam_dup_object_list(ncuh->nwh_data, &olddata))
+ != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_delete_prop(ncuh->nwh_data, propname))
+ != NWAM_SUCCESS) {
+ nwam_free_object_list(ncuh->nwh_data);
+ ncuh->nwh_data = olddata;
+ return (err);
+ }
+ if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS) {
+ nwam_free_object_list(ncuh->nwh_data);
+ ncuh->nwh_data = olddata;
+ return (err);
+ }
+ nwam_free_object_list(olddata);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_ncu_set_prop_value(nwam_ncu_handle_t ncuh, const char *propname,
+ nwam_value_t value)
+{
+ boolean_t ro_ncu, ro_prop;
+ nwam_error_t err;
+ nwam_ncp_handle_t ncph;
+
+ assert(ncuh != NULL && propname != NULL && value != NULL);
+
+ if ((err = nwam_ncu_get_read_only(ncuh, &ro_ncu)) != NWAM_SUCCESS ||
+ (err = nwam_ncu_prop_read_only(propname, &ro_prop)) != NWAM_SUCCESS)
+ return (err);
+ if (ro_ncu || ro_prop)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ err = nwam_ncu_get_ncp(ncuh, &ncph);
+ if (err != NWAM_SUCCESS && err != NWAM_INVALID_ARG) {
+ /*
+ * If "parent" property doesn't exist, NWAM_INVALID_ARG
+ * is returned. Allow the setting to continue.
+ */
+ return (err);
+ }
+ nwam_ncp_free(ncph);
+
+ /* Need to ensure property, type and value are valid */
+ if ((err = nwam_ncu_validate_prop(ncuh, propname, value))
+ != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_set_prop_value(ncuh->nwh_data, propname, value));
+}
+
+nwam_error_t
+nwam_ncu_get_prop_value(nwam_ncu_handle_t ncuh, const char *propname,
+ nwam_value_t *valuep)
+{
+ assert(ncuh != NULL && propname != NULL && valuep != NULL);
+
+ return (nwam_get_prop_value(ncuh->nwh_data, propname, valuep));
+}
+
+nwam_error_t
+nwam_ncu_walk_props(nwam_ncu_handle_t ncuh,
+ int (*cb)(const char *, nwam_value_t, void *),
+ void *data, uint64_t flags, int *retp)
+{
+ return (nwam_walk_props(ncuh, cb, data, flags, retp));
+}
+
+nwam_error_t
+nwam_ncu_get_ncp(nwam_ncu_handle_t ncuh, nwam_ncp_handle_t *ncphp)
+{
+ nwam_error_t err;
+ char *parentname = NULL;
+
+ if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &parentname))
+ != NWAM_SUCCESS ||
+ (err = nwam_handle_create(NWAM_OBJECT_TYPE_NCP, parentname, ncphp))
+ != NWAM_SUCCESS) {
+ if (parentname != NULL)
+ free(parentname);
+ return (err);
+ }
+ free(parentname);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_ncu_commit(nwam_ncu_handle_t ncuh, uint64_t flags)
+{
+ nwam_error_t err;
+ boolean_t read_only;
+ char *ncpfile, *ncpname;
+
+ assert(ncuh != NULL && ncuh->nwh_data != NULL);
+
+ if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
+ return (err);
+ if (read_only)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ if ((err = nwam_ncu_validate(ncuh, NULL)) != NWAM_SUCCESS ||
+ (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ if ((err = nwam_ncp_name_to_file(ncpname, &ncpfile)) != NWAM_SUCCESS) {
+ free(ncpname);
+ return (err);
+ }
+
+ err = nwam_commit(ncpfile, ncuh, flags);
+
+ free(ncpname);
+ free(ncpfile);
+
+ return (err);
+}
+/* Get the NCU type */
+nwam_error_t
+nwam_ncu_get_ncu_type(nwam_ncu_handle_t ncuh, nwam_ncu_type_t *typep)
+{
+ nwam_error_t err;
+ nwam_value_t typeval;
+ uint64_t type;
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval))
+ != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(typeval, &type);
+ nwam_value_free(typeval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ *typep = type;
+ return (NWAM_SUCCESS);
+}
+
+/* Get the NCU class */
+nwam_error_t
+nwam_ncu_get_ncu_class(nwam_ncu_handle_t ncuh, nwam_ncu_class_t *classp)
+{
+ nwam_error_t err;
+ nwam_value_t classval;
+ uint64_t class;
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS,
+ &classval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(classval, &class);
+ nwam_value_free(classval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ *classp = class;
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Determine if the NCU has manual activation-mode or not.
+ */
+nwam_error_t
+nwam_ncu_is_manual(nwam_ncu_handle_t ncuh, boolean_t *manualp)
+{
+ nwam_error_t err;
+ nwam_value_t actval;
+ uint64_t activation;
+
+ assert(ncuh != NULL);
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ACTIVATION_MODE,
+ &actval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_uint64(actval, &activation);
+ nwam_value_free(actval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ if (activation == NWAM_ACTIVATION_MODE_MANUAL)
+ *manualp = B_TRUE;
+ else
+ *manualp = B_FALSE;
+ return (NWAM_SUCCESS);
+}
+
+/* Determine if NCU is enabled or not */
+static nwam_error_t
+nwam_ncu_is_enabled(nwam_ncu_handle_t ncuh, boolean_t *enabledp)
+{
+ nwam_error_t err;
+ nwam_value_t enabledval;
+
+ assert(ncuh != NULL);
+
+ if ((err = nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_ENABLED,
+ &enabledval)) != NWAM_SUCCESS)
+ return (err);
+ err = nwam_value_get_boolean(enabledval, enabledp);
+ nwam_value_free(enabledval);
+ return (err);
+}
+
+/* Update the enabled property */
+static nwam_error_t
+nwam_ncu_update_enabled(nwam_ncu_handle_t ncuh, boolean_t enabled)
+{
+ nwam_error_t err;
+ nwam_value_t enabledval;
+
+ if ((err = nwam_value_create_boolean(enabled, &enabledval))
+ != NWAM_SUCCESS)
+ return (err);
+ err = nwam_set_prop_value(ncuh->nwh_data, NWAM_NCU_PROP_ENABLED,
+ enabledval);
+ nwam_value_free(enabledval);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ return (nwam_ncu_commit(ncuh, NWAM_FLAG_ENTITY_ENABLE));
+}
+
+/*
+ * Make ncu active; fails if the NCU's parent NCP is not active.
+ */
+nwam_error_t
+nwam_ncu_enable(nwam_ncu_handle_t ncuh)
+{
+ char *ncpname = NULL;
+ nwam_error_t err;
+ nwam_ncu_type_t type;
+ boolean_t read_only, enabled, manual;
+
+ assert(ncuh != NULL);
+
+ /* Don't allow NCUs of Automatic NCP to be enabled */
+ if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
+ return (err);
+ if (read_only)
+ return (NWAM_ENTITY_NOT_MANUAL);
+
+ /* Link NCUs with manual activation-mode or IP NCUs can be enabled */
+ if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS)
+ return (err);
+
+ if (type == NWAM_NCU_TYPE_LINK) {
+ if ((err = nwam_ncu_is_manual(ncuh, &manual)) != NWAM_SUCCESS)
+ return (err);
+ if (!manual)
+ return (NWAM_ENTITY_NOT_MANUAL);
+ }
+
+ /* Make sure NCU is not enabled */
+ if ((err = nwam_ncu_is_enabled(ncuh, &enabled)) != NWAM_SUCCESS ||
+ (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ if (enabled) {
+ free(ncpname);
+ return (NWAM_SUCCESS);
+ }
+
+ if ((err = nwam_ncu_update_enabled(ncuh, B_TRUE)) != NWAM_SUCCESS) {
+ free(ncpname);
+ return (err);
+ }
+
+ err = nwam_enable(ncpname, ncuh);
+ free(ncpname);
+
+ /* nwamd may not be running, that's okay. */
+ if (err == NWAM_ERROR_BIND)
+ return (NWAM_SUCCESS);
+ else
+ return (err);
+}
+
+/*
+ * Disable ncu; fails if the NCU's parent NCP is not active, or if the
+ * NCU is not currently active.
+ */
+nwam_error_t
+nwam_ncu_disable(nwam_ncu_handle_t ncuh)
+{
+ char *ncpname = NULL;
+ nwam_error_t err;
+ nwam_ncu_type_t type;
+ boolean_t read_only, enabled, manual;
+
+ assert(ncuh != NULL);
+
+ /* Don't allow NCUs of Automatic NCP to be disabled */
+ if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
+ return (err);
+ if (read_only)
+ return (NWAM_ENTITY_NOT_MANUAL);
+
+ /* Link NCUs with manual activation-mode or IP NCUs can be disabled */
+ if ((err = nwam_ncu_get_ncu_type(ncuh, &type)) != NWAM_SUCCESS)
+ return (err);
+
+ if (type == NWAM_NCU_TYPE_LINK) {
+ if ((err = nwam_ncu_is_manual(ncuh, &manual)) != NWAM_SUCCESS)
+ return (err);
+ if (!manual)
+ return (NWAM_ENTITY_NOT_MANUAL);
+ }
+
+ /* Make sure NCU is enabled */
+ if ((err = nwam_ncu_is_enabled(ncuh, &enabled)) != NWAM_SUCCESS ||
+ (err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ if (!enabled) {
+ free(ncpname);
+ return (NWAM_SUCCESS);
+ }
+
+ if ((err = nwam_ncu_update_enabled(ncuh, B_FALSE)) != NWAM_SUCCESS) {
+ free(ncpname);
+ return (err);
+ }
+
+ err = nwam_disable(ncpname, ncuh);
+ free(ncpname);
+
+ /* nwamd may not be running, that's okay. */
+ if (err == NWAM_ERROR_BIND)
+ return (NWAM_SUCCESS);
+ else
+ return (err);
+}
+
+nwam_error_t
+nwam_ncu_destroy(nwam_ncu_handle_t ncuh, uint64_t flags)
+{
+ char *ncpname, *ncpfile;
+ boolean_t read_only;
+ nwam_error_t err;
+
+ assert(ncuh != NULL);
+
+ if ((err = nwam_ncu_get_read_only(ncuh, &read_only)) != NWAM_SUCCESS)
+ return (err);
+ if (read_only)
+ return (NWAM_ENTITY_NOT_DESTROYABLE);
+
+ if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
+ != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_ncp_name_to_file(ncpname, &ncpfile))
+ != NWAM_SUCCESS) {
+ free(ncpname);
+ return (err);
+ }
+
+ err = nwam_destroy(ncpfile, ncuh, flags);
+
+ free(ncpname);
+ free(ncpfile);
+
+ return (err);
+}
+
+nwam_error_t
+nwam_ncu_get_prop_description(const char *propname, const char **descriptionp)
+{
+ return (nwam_get_prop_description(ncu_prop_table, propname,
+ descriptionp));
+}
+
+/* Get expected property data type */
+nwam_error_t
+nwam_ncu_get_prop_type(const char *propname, nwam_value_type_t *typep)
+{
+ return (nwam_get_prop_type(ncu_prop_table, propname, typep));
+}
+
+nwam_error_t
+nwam_ncu_prop_read_only(const char *propname, boolean_t *readp)
+{
+ if ((*readp = NWAM_NCU_PROP_SETONCE(propname)) == B_TRUE)
+ return (NWAM_SUCCESS);
+
+ return (nwam_prop_read_only(ncu_prop_table, propname, readp));
+}
+
+nwam_error_t
+nwam_ncu_prop_multivalued(const char *propname, boolean_t *multip)
+{
+ return (nwam_prop_multivalued(ncu_prop_table, propname, multip));
+}
+
+/*
+ * Ensure that the properties in the ncu, determined by that ncu's
+ * type and class, belong there.
+ */
+static nwam_error_t
+nwam_ncu_validate_prop_membership(nwam_ncu_handle_t ncuh, const char *propname)
+{
+ struct nwam_prop_table_entry *pte;
+ nwam_value_t typeval, classval;
+ uint64_t type, class;
+ uint64_t typeflags = 0, classflags = 0;
+
+ /* Get type/class from ncu */
+ if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_TYPE, &typeval)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID);
+ if (nwam_value_get_uint64(typeval, &type) != NWAM_SUCCESS) {
+ nwam_value_free(typeval);
+ return (NWAM_ENTITY_INVALID);
+ }
+ typeflags = nwam_ncu_type_to_flag((nwam_ncu_type_t)type);
+ nwam_value_free(typeval);
+
+ if (nwam_ncu_get_prop_value(ncuh, NWAM_NCU_PROP_CLASS, &classval)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID);
+ if (nwam_value_get_uint64(classval, &class) != NWAM_SUCCESS) {
+ nwam_value_free(classval);
+ return (NWAM_ENTITY_INVALID);
+ }
+ classflags = nwam_ncu_class_to_flag((nwam_ncu_class_t)class);
+ nwam_value_free(classval);
+
+ if ((pte = nwam_get_prop_table_entry(ncu_prop_table, propname)) == NULL)
+ return (NWAM_INVALID_ARG);
+
+ if (typeflags & pte->prop_type_membership &&
+ classflags & pte->prop_class_membership) {
+ return (NWAM_SUCCESS);
+ } else {
+ return (NWAM_ENTITY_INVALID_MEMBER);
+ }
+}
+
+/* Validate property's ncu membership and type, number and range of values */
+nwam_error_t
+nwam_ncu_validate_prop(nwam_ncu_handle_t ncuh, const char *propname,
+ nwam_value_t value)
+{
+ nwam_error_t err;
+
+ assert(ncuh != NULL && propname != NULL);
+
+ /* First, determine if this property is valid for this ncu */
+ if ((err = nwam_ncu_validate_prop_membership(ncuh, propname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ return (nwam_validate_prop(ncu_prop_table, ncuh, propname, value));
+}
+
+/* Property-specific value validation functions follow */
+
+static nwam_error_t
+valid_type(nwam_value_t value)
+{
+ uint64_t type;
+
+ if (nwam_value_get_uint64(value, &type) != NWAM_SUCCESS ||
+ type > NWAM_NCU_TYPE_INTERFACE)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_class(nwam_value_t value)
+{
+ uint64_t class;
+
+ if (nwam_value_get_uint64(value, &class) != NWAM_SUCCESS ||
+ class > NWAM_NCU_CLASS_IP)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_ncp(nwam_value_t value)
+{
+ char *ncp;
+
+ if (nwam_value_get_string(value, &ncp) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_priority_mode(nwam_value_t value)
+{
+ uint64_t priority_mode;
+
+ if (nwam_value_get_uint64(value, &priority_mode) != NWAM_SUCCESS ||
+ priority_mode > NWAM_PRIORITY_MODE_ALL)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_ncu_activation_mode(nwam_value_t value)
+{
+ uint64_t activation_mode;
+
+ if (nwam_value_get_uint64(value, &activation_mode) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ switch (activation_mode) {
+ case NWAM_ACTIVATION_MODE_MANUAL:
+ case NWAM_ACTIVATION_MODE_PRIORITIZED:
+ return (NWAM_SUCCESS);
+ }
+ return (NWAM_ENTITY_INVALID_VALUE);
+}
+
+/* ARGSUSED0 */
+static nwam_error_t
+valid_link_autopush(nwam_value_t value)
+{
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_ip_version(nwam_value_t value)
+{
+ uint64_t *versions;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_uint64_array(value, &versions, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (versions[i] != IPV4_VERSION &&
+ versions[i] != IPV6_VERSION)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_addrsrc_v4(nwam_value_t value)
+{
+ uint64_t *addrsrc;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_uint64_array(value, &addrsrc, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (addrsrc[i] != NWAM_ADDRSRC_DHCP &&
+ addrsrc[i] != NWAM_ADDRSRC_STATIC)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+static nwam_error_t
+valid_addrsrc_v6(nwam_value_t value)
+{
+ uint64_t *addrsrc;
+ uint_t i, numvalues;
+ boolean_t dhcp_found = B_FALSE, autoconf_found = B_FALSE;
+
+ if (nwam_value_get_uint64_array(value, &addrsrc, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (addrsrc[i] != NWAM_ADDRSRC_DHCP &&
+ addrsrc[i] != NWAM_ADDRSRC_STATIC &&
+ addrsrc[i] != NWAM_ADDRSRC_AUTOCONF)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ if (addrsrc[i] == NWAM_ADDRSRC_DHCP)
+ dhcp_found = B_TRUE;
+ if (addrsrc[i] == NWAM_ADDRSRC_AUTOCONF)
+ autoconf_found = B_TRUE;
+ }
+ /*
+ * DHCP and AUTOCONF need to be specified as v6 address sources
+ * since there is no way to switch them off in NWAM at present.
+ */
+ if (dhcp_found && autoconf_found)
+ return (NWAM_SUCCESS);
+ else
+ return (NWAM_ENTITY_INVALID_VALUE);
+}
+
+/* ARGSUSED0 */
+static nwam_error_t
+valid_link_mtu(nwam_value_t value)
+{
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_ncu_validate(nwam_ncu_handle_t ncuh, const char **errpropp)
+{
+ return (nwam_validate(ncu_prop_table, ncuh, errpropp));
+}
+
+/*
+ * Given the ncu type and ncu class, return the list of properties that needs
+ * to be set. Note this list is a complete property list that includes both
+ * the required ones and the optional ones. Caller needs to free prop_list.
+ */
+nwam_error_t
+nwam_ncu_get_default_proplist(nwam_ncu_type_t type, nwam_ncu_class_t class,
+ const char ***prop_list, uint_t *numvalues)
+{
+ uint64_t typeflags = nwam_ncu_type_to_flag(type);
+ uint64_t classflags = nwam_ncu_class_to_flag(class);
+
+ return (nwam_get_default_proplist(ncu_prop_table, typeflags,
+ classflags, prop_list, numvalues));
+}
+
+nwam_error_t
+nwam_ncp_get_state(nwam_ncp_handle_t ncph, nwam_state_t *statep,
+ nwam_aux_state_t *auxp)
+{
+ return (nwam_get_state(ncph->nwh_name, ncph, statep, auxp));
+}
+
+nwam_error_t
+nwam_ncu_get_state(nwam_ncu_handle_t ncuh, nwam_state_t *statep,
+ nwam_aux_state_t *auxp)
+{
+ nwam_ncp_handle_t ncph;
+ char *ncpname;
+ nwam_error_t err;
+
+ assert(ncuh != NULL);
+
+ if ((err = nwam_ncu_get_ncp(ncuh, &ncph)) != NWAM_SUCCESS)
+ return (err);
+ if (!nwam_ncp_is_active(ncph)) {
+ nwam_ncp_free(ncph);
+ return (NWAM_ENTITY_INVALID);
+ }
+ nwam_ncp_free(ncph);
+
+ if ((err = nwam_ncu_get_parent_ncp_name(ncuh, &ncpname))
+ != NWAM_SUCCESS)
+ return (err);
+
+ err = nwam_request_state(NWAM_OBJECT_TYPE_NCU, ncuh->nwh_name, ncpname,
+ statep, auxp);
+ free(ncpname);
+ return (err);
+}
+
+nwam_error_t
+nwam_ncp_get_active_priority_group(int64_t *priorityp)
+{
+ return (nwam_request_active_priority_group(priorityp));
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_object.c b/usr/src/lib/libnwam/common/libnwam_object.c
new file mode 100644
index 0000000000..7fe6eae475
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_object.c
@@ -0,0 +1,744 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+
+#include "libnwam_impl.h"
+#include <libintl.h>
+#include <libnwam.h>
+
+/*
+ * Generic object manipulation functions. Given an object handle and
+ * other parameters, create/destroy objects, walk them, walk their
+ * properties, modify/retrieve/delete properties, enable/disable them,
+ * etc. All object handles are "struct nwam_handle *" objects, sharing
+ * the same description based on the object type, name, original name
+ * (used in renaming) and associated data representing properties.
+ */
+
+nwam_error_t
+nwam_handle_create(nwam_object_type_t type, const char *name,
+ struct nwam_handle **hpp)
+{
+
+ assert(name != NULL && hpp != NULL);
+
+ if (strnlen(name, NWAM_MAX_NAME_LEN) > NWAM_MAX_NAME_LEN) {
+ *hpp = NULL;
+ return (NWAM_INVALID_ARG);
+ }
+
+ if ((*hpp = calloc(1, sizeof (struct nwam_handle))) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ (*hpp)->nwh_object_type = type;
+ (void) strlcpy((*hpp)->nwh_name, name, strlen(name) + 1);
+ (*hpp)->nwh_committed = B_FALSE;
+ (*hpp)->nwh_data = NULL;
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Read object of specified type from dbname.
+ */
+nwam_error_t
+nwam_read(nwam_object_type_t type, const char *dbname, const char *name,
+ uint64_t flags, struct nwam_handle **hpp)
+{
+ nwam_error_t err;
+ char dbname_copy[MAXPATHLEN];
+
+ assert(name != NULL && hpp != NULL);
+
+ if (dbname != NULL)
+ (void) strlcpy(dbname_copy, dbname, sizeof (dbname_copy));
+
+ if ((err = nwam_valid_flags(flags, NWAM_FLAG_BLOCKING)) != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_handle_create(type, name, hpp)) != NWAM_SUCCESS)
+ return (err);
+
+ if ((err = nwam_read_object_from_backend
+ (dbname != NULL ? dbname_copy : NULL,
+ type == NWAM_OBJECT_TYPE_NCP ? NULL : (*hpp)->nwh_name, flags,
+ &(*hpp)->nwh_data)) != NWAM_SUCCESS) {
+ free(*hpp);
+ *hpp = NULL;
+ return (err);
+ }
+ if (type == NWAM_OBJECT_TYPE_NCP && dbname != NULL) {
+ char *ncpname;
+
+ /*
+ * dbname_copy may have been changed due to case-insensitive
+ * match against the actual NCP configuration file.
+ */
+ if (nwam_ncp_file_to_name(dbname_copy, &ncpname)
+ == NWAM_SUCCESS) {
+ (void) strlcpy((*hpp)->nwh_name, ncpname,
+ sizeof ((*hpp)->nwh_name));
+ free(ncpname);
+ }
+ }
+
+ (*hpp)->nwh_committed = B_TRUE;
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Create simply creates the handle - the object-specific function must
+ * then fill in property values.
+ */
+nwam_error_t
+nwam_create(nwam_object_type_t type, const char *dbname, const char *name,
+ struct nwam_handle **hpp)
+{
+ struct nwam_handle *hp;
+
+ assert(hpp != NULL && name != NULL);
+
+ if (nwam_read(type, dbname, name, 0, &hp) == NWAM_SUCCESS) {
+ nwam_free(hp);
+ return (NWAM_ENTITY_EXISTS);
+ }
+ /* Create handle */
+ return (nwam_handle_create(type, name, hpp));
+}
+
+nwam_error_t
+nwam_get_name(struct nwam_handle *hp, char **namep)
+{
+ assert(hp != NULL && namep != NULL);
+
+ if ((*namep = strdup(hp->nwh_name)) == NULL) {
+ *namep = NULL;
+ return (NWAM_NO_MEMORY);
+ }
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_set_name(struct nwam_handle *hp, const char *name)
+{
+ assert(hp != NULL && name != NULL);
+
+ if (hp->nwh_committed)
+ return (NWAM_ENTITY_READ_ONLY);
+
+ if (strlen(name) >= sizeof (hp->nwh_name))
+ return (NWAM_INVALID_ARG);
+
+ (void) strcpy(hp->nwh_name, name);
+
+ return (NWAM_SUCCESS);
+}
+
+/* Compare object names c1 and c2 using strcasecmp() */
+static int
+name_cmp(const void *c1, const void *c2)
+{
+ nwam_ncu_type_t t1, t2;
+ char *n1, *n2;
+
+ /* If c1 and c2 are typed NCU names, compare names without the types */
+ if (nwam_ncu_typed_name_to_name(*(const char **)c1, &t1, &n1)
+ == NWAM_SUCCESS &&
+ nwam_ncu_typed_name_to_name(*(const char **)c2, &t2, &n2)
+ == NWAM_SUCCESS) {
+ int ret = strcasecmp(n1, n2);
+ free(n1);
+ free(n2);
+
+ /* For NCUs with the same name, compare their types */
+ if (ret == 0) {
+ if (t1 < t2)
+ ret = -1;
+ else if (t1 > t2)
+ ret = 1;
+ }
+ return (ret);
+ }
+
+ return (strcasecmp(*(const char **)c1, *(const char **)c2));
+}
+
+/*
+ * Generic walk function takes the standard walk arguments, and in addition
+ * takes a selection callback that is object-specific. If this returns
+ * 0, the object is a valid selection for the walk and the callback is called.
+ * Otherwise, it is skipped.
+ */
+nwam_error_t
+nwam_walk(nwam_object_type_t type, const char *dbname,
+ int(*cb)(struct nwam_handle *, void *),
+ void *data, uint64_t flags, int *retp,
+ int(*selectcb)(struct nwam_handle *, uint64_t, void *))
+{
+ void *objlist;
+ nwam_value_t value;
+ char **object_names;
+ uint_t i, num_objects = 0;
+ struct nwam_handle *hp;
+ nwam_error_t err;
+ int ret = 0;
+
+ assert(cb != NULL);
+
+ /*
+ * To walk a set of objects, call nwam_read_object_from_backend()
+ * with a "dbname" argument set to the container db name and
+ * the object name set to NULL. This returns an nvlist with one
+ * member - the NWAM_OBJECT_NAMES_STRING - and the values it contains
+ * represent the names of the objects. Read each in turn, calling
+ * the callback function.
+ */
+ if ((err = nwam_read_object_from_backend((char *)dbname, NULL, flags,
+ &objlist)) != NWAM_SUCCESS) {
+ if (err == NWAM_ENTITY_NOT_FOUND) {
+ /*
+ * This indicates the dbname container is not present.
+ * Do not pass back an error in this case, since it is
+ * valid for a container not to exist.
+ */
+ return (NWAM_SUCCESS);
+ }
+ return (err);
+ }
+
+ if ((err = nwam_get_prop_value(objlist, NWAM_OBJECT_NAMES_STRING,
+ &value)) != NWAM_SUCCESS) {
+ nwam_free_object_list(objlist);
+ return (err);
+ }
+ err = nwam_value_get_string_array(value, &object_names, &num_objects);
+ nwam_free_object_list(objlist);
+ if (err != NWAM_SUCCESS) {
+ nwam_value_free(value);
+ return (err);
+ }
+
+ /* sort the object names alphabetically */
+ qsort(object_names, num_objects, sizeof (char *), name_cmp);
+
+ for (i = 0; i < num_objects; i++) {
+ err = nwam_read(type, dbname, object_names[i],
+ flags & NWAM_FLAG_GLOBAL_MASK, &hp);
+ /* An object may have disappeared. If so, skip it. */
+ if (err == NWAM_ENTITY_NOT_FOUND)
+ continue;
+ if (err != NWAM_SUCCESS) {
+ nwam_value_free(value);
+ return (err);
+ }
+ if ((selectcb == NULL) || (selectcb(hp, flags, data) == 0)) {
+ ret = cb(hp, data);
+ if (ret != 0) {
+ nwam_free(hp);
+ nwam_value_free(value);
+ if (retp != NULL)
+ *retp = ret;
+ return (NWAM_WALK_HALTED);
+ }
+ }
+ nwam_free(hp);
+ }
+ nwam_value_free(value);
+
+ if (retp != NULL)
+ *retp = ret;
+ return (err);
+}
+
+void
+nwam_free(struct nwam_handle *hp)
+{
+ if (hp != NULL) {
+ if (hp->nwh_data != NULL)
+ nwam_free_object_list(hp->nwh_data);
+ free(hp);
+ }
+}
+
+/*
+ * Copy object represented by oldhp to an object newname, all in container
+ * dbname.
+ */
+nwam_error_t
+nwam_copy(const char *dbname, struct nwam_handle *oldhp, const char *newname,
+ struct nwam_handle **newhpp)
+{
+ nwam_error_t err;
+ struct nwam_handle *hp;
+
+ assert(oldhp != NULL && newname != NULL && newhpp != NULL);
+
+ if (nwam_read(oldhp->nwh_object_type, dbname, newname, 0, &hp)
+ == NWAM_SUCCESS) {
+ nwam_free(hp);
+ return (NWAM_ENTITY_EXISTS);
+ }
+
+ if ((err = nwam_handle_create(oldhp->nwh_object_type, newname, newhpp))
+ != NWAM_SUCCESS)
+ return (err);
+ if ((err = nwam_dup_object_list(oldhp->nwh_data,
+ &((*newhpp)->nwh_data))) != NWAM_SUCCESS) {
+ nwam_free(*newhpp);
+ *newhpp = NULL;
+ return (err);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+/* ARGSUSED3 */
+nwam_error_t
+nwam_walk_props(struct nwam_handle *hp,
+ int (*cb)(const char *, nwam_value_t, void *),
+ void *data, uint64_t flags, int *retp)
+{
+ char *lastpropname = NULL, *propname;
+ nwam_value_t value;
+ nwam_error_t err;
+ int ret = 0;
+
+ assert(hp != NULL && hp->nwh_data != NULL && cb != NULL);
+
+ if ((err = nwam_valid_flags(flags, 0)) != NWAM_SUCCESS)
+ return (err);
+ while ((err = nwam_next_object_prop(hp->nwh_data, lastpropname,
+ &propname, &value)) == NWAM_SUCCESS) {
+
+ ret = cb(propname, value, data);
+ if (ret != 0)
+ err = NWAM_WALK_HALTED;
+
+ /* Free value */
+ nwam_value_free(value);
+
+ if (err != NWAM_SUCCESS)
+ break;
+
+ lastpropname = propname;
+ }
+
+ if (retp != NULL)
+ *retp = ret;
+ if (err == NWAM_SUCCESS || err == NWAM_LIST_END)
+ return (NWAM_SUCCESS);
+ return (err);
+}
+
+/*
+ * Note that prior to calling the generic commit function, object-specific
+ * validation should be carried out.
+ */
+nwam_error_t
+nwam_commit(const char *dbname, struct nwam_handle *hp, uint64_t flags)
+{
+ nwam_error_t err;
+ uint64_t iflags = flags;
+ boolean_t is_ncu;
+ struct nwam_handle *testhp;
+ nwam_action_t action;
+
+ assert(hp != NULL);
+
+ /*
+ * NWAM_FLAG_ENTITY_KNOWN_WLAN is only used for Known WLANs and
+ * NWAM_FLAG_ENTITY_ENABLE is used for other objects (during enable
+ * and disable).
+ */
+ if ((err = nwam_valid_flags(flags,
+ NWAM_FLAG_BLOCKING | NWAM_FLAG_CREATE |
+ (hp->nwh_object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN ?
+ NWAM_FLAG_ENTITY_KNOWN_WLAN : NWAM_FLAG_ENTITY_ENABLE)))
+ != NWAM_SUCCESS)
+ return (err);
+
+ is_ncu = (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU);
+
+ /*
+ * Does object already exist? If not, action is ADD, otherwise REFRESH.
+ */
+ switch (nwam_read(hp->nwh_object_type, (char *)dbname, hp->nwh_name, 0,
+ &testhp)) {
+ case NWAM_ENTITY_NOT_FOUND:
+ action = NWAM_ACTION_ADD;
+ break;
+ case NWAM_SUCCESS:
+ nwam_free(testhp);
+ if (hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP)
+ return (NWAM_ENTITY_EXISTS);
+ /* FALLTHRU */
+ default:
+ action = NWAM_ACTION_REFRESH;
+ break;
+ }
+
+ err = nwam_update_object_in_backend((char *)dbname,
+ hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP ? NULL : hp->nwh_name,
+ iflags, hp->nwh_data);
+ if (err != NWAM_SUCCESS)
+ return (err);
+
+ hp->nwh_committed = B_TRUE;
+
+ /*
+ * Tell nwamd to reread this object. For NCUs, we need to convert
+ * the dbname to the NCP name in order to pass it to nwamd.
+ */
+ if (is_ncu) {
+ char *ncpname;
+
+ if (nwam_ncp_file_to_name(dbname, &ncpname) == NWAM_SUCCESS) {
+ (void) nwam_request_action(hp->nwh_object_type,
+ hp->nwh_name, ncpname, action);
+ free(ncpname);
+ }
+ } else {
+ (void) nwam_request_action(hp->nwh_object_type, hp->nwh_name,
+ NULL, action);
+ }
+ return (NWAM_SUCCESS);
+}
+
+static boolean_t
+nwam_is_active(struct nwam_handle *hp)
+{
+ nwam_state_t state;
+ nwam_aux_state_t aux;
+
+ return ((nwam_get_state(NULL, hp, &state, &aux) == NWAM_SUCCESS &&
+ state == NWAM_STATE_ONLINE));
+}
+
+nwam_error_t
+nwam_destroy(const char *dbname, struct nwam_handle *hp, uint64_t flags)
+{
+ nwam_error_t err;
+ char *name;
+ boolean_t is_ncp, is_ncu;
+
+ assert(hp != NULL);
+
+ /* NWAM_FLAG_ENTITY_KNOWN_WLAN is only used for Known WLANs */
+ if ((err = nwam_valid_flags(flags,
+ NWAM_FLAG_BLOCKING | NWAM_FLAG_DO_NOT_FREE |
+ (hp->nwh_object_type == NWAM_OBJECT_TYPE_KNOWN_WLAN ?
+ NWAM_FLAG_ENTITY_KNOWN_WLAN : 0))) != NWAM_SUCCESS)
+ return (err);
+
+ is_ncp = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCP;
+ is_ncu = hp->nwh_object_type == NWAM_OBJECT_TYPE_NCU;
+ name = hp->nwh_name;
+
+ /* Check if object is active */
+ if (!is_ncp && !is_ncu && nwam_is_active(hp))
+ return (NWAM_ENTITY_IN_USE);
+
+ /* For NCPs, just remove the dbname file, otherwise remove the object */
+ err = nwam_remove_object_from_backend((char *)dbname,
+ is_ncp ? NULL : name, flags);
+
+ /*
+ * Tell nwamd to remove this object. For NCUs, we need to convert the
+ * dbname filename to the NCP name to pass it to nwamd.
+ */
+ if (is_ncu) {
+ char *ncpname;
+
+ if (nwam_ncp_file_to_name(dbname, &ncpname) == NWAM_SUCCESS) {
+ (void) nwam_request_action(hp->nwh_object_type, name,
+ ncpname, NWAM_ACTION_DESTROY);
+ free(ncpname);
+ }
+ } else {
+ (void) nwam_request_action(hp->nwh_object_type, name, NULL,
+ NWAM_ACTION_DESTROY);
+ }
+
+ if ((err == NWAM_SUCCESS) && !(flags & NWAM_FLAG_DO_NOT_FREE))
+ nwam_free(hp);
+
+ return (err);
+}
+
+/*
+ * Enable/disable functions assume prior checking of activation mode
+ * to ensure an enable/disable action is valid for the object. "parent" in these
+ * functions specifies the NCP for NCUs.
+ */
+nwam_error_t
+nwam_enable(const char *parent, struct nwam_handle *hp)
+{
+ return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
+ parent, NWAM_ACTION_ENABLE));
+}
+
+nwam_error_t
+nwam_disable(const char *parent, struct nwam_handle *hp)
+{
+ return (nwam_request_action(hp->nwh_object_type, hp->nwh_name,
+ parent, NWAM_ACTION_DISABLE));
+}
+
+nwam_error_t
+nwam_get_state(const char *parent, struct nwam_handle *hp, nwam_state_t *statep,
+ nwam_aux_state_t *auxp)
+{
+ return (nwam_request_state(hp->nwh_object_type, hp->nwh_name, parent,
+ statep, auxp));
+}
+
+struct nwam_prop_table_entry *
+nwam_get_prop_table_entry(struct nwam_prop_table table, const char *propname)
+{
+ struct nwam_prop_table_entry *cur = table.entries;
+ struct nwam_prop_table_entry *end = cur + table.num_entries;
+
+ assert(propname != NULL);
+
+ for (; cur < end; cur++) {
+ if (strcmp(propname, cur->prop_name) == 0)
+ return (cur);
+ }
+ return (NULL);
+}
+
+nwam_error_t
+nwam_get_prop_description(struct nwam_prop_table table, const char *propname,
+ const char **descriptionp)
+{
+ struct nwam_prop_table_entry *pte;
+
+ assert(propname != NULL && descriptionp != NULL);
+
+ if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL) {
+ *descriptionp = NULL;
+ return (NWAM_INVALID_ARG);
+ }
+
+ *descriptionp = dgettext(TEXT_DOMAIN, pte->prop_description);
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_get_prop_type(struct nwam_prop_table table, const char *propname,
+ nwam_value_type_t *typep)
+{
+ struct nwam_prop_table_entry *pte;
+
+ assert(propname != NULL && typep != NULL);
+
+ if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
+ return (NWAM_INVALID_ARG);
+
+ *typep = pte->prop_type;
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_prop_multivalued(struct nwam_prop_table table, const char *propname,
+ boolean_t *multip)
+{
+ struct nwam_prop_table_entry *pte;
+
+ assert(propname != NULL && multip != NULL);
+
+ if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
+ return (NWAM_INVALID_ARG);
+
+ if (pte->prop_max_numvalues > 1)
+ *multip = B_TRUE;
+ else
+ *multip = B_FALSE;
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_prop_read_only(struct nwam_prop_table table, const char *propname,
+ boolean_t *readp)
+{
+ struct nwam_prop_table_entry *pte;
+
+ assert(propname != NULL && readp != NULL);
+
+ if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
+ return (NWAM_INVALID_ARG);
+
+ *readp = (pte->prop_is_readonly && !nwam_uid_is_netadm());
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Structure used to pass in prop table and errprop string pointer to internal
+ * validate function.
+ */
+struct validate_internal_arg {
+ struct nwam_prop_table table;
+ const char **errpropp;
+};
+
+/*
+ * Callback used by nwam_walk_props() in nwam_validate(), and
+ * by nwam_validate_prop() to determine that the number, type and
+ * range of values are correct, and that validation function (if present)
+ * succeeds.
+ */
+static int
+nwam_validate_prop_internal(const char *propname, nwam_value_t value,
+ void *arg)
+{
+ struct validate_internal_arg *via = arg;
+ struct nwam_prop_table table = via->table;
+ const char **errpropp = via->errpropp;
+ struct nwam_prop_table_entry *pte;
+ nwam_error_t err;
+ nwam_value_type_t type;
+ uint_t numvalues;
+ int i;
+
+ if ((err = nwam_value_get_numvalues(value, &numvalues))
+ != NWAM_SUCCESS ||
+ (err = nwam_value_get_type(value, &type)) != NWAM_SUCCESS) {
+ if (errpropp != NULL)
+ *errpropp = propname;
+ return (err);
+ }
+ if ((pte = nwam_get_prop_table_entry(table, propname)) == NULL)
+ return (NWAM_INVALID_ARG);
+
+ /* have we get expected number of values? */
+ if (numvalues < pte->prop_min_numvalues ||
+ numvalues > pte->prop_max_numvalues) {
+ if (errpropp != NULL)
+ *errpropp = propname;
+ if (numvalues < 1)
+ return (NWAM_ENTITY_NO_VALUE);
+ else
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ /* Ensure type matches */
+ if (numvalues > 0) {
+ for (i = 0; i < numvalues; i++) {
+ if (pte->prop_type != type) {
+ if (errpropp != NULL)
+ *errpropp = propname;
+ return (NWAM_ENTITY_TYPE_MISMATCH);
+
+ }
+ }
+ }
+ /* Call property-specific validation function */
+ if (pte->prop_validate != NULL) {
+ err = pte->prop_validate(value);
+ if (err != NWAM_SUCCESS && errpropp != NULL)
+ *errpropp = propname;
+ return (err);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_validate_prop(struct nwam_prop_table table, struct nwam_handle *hp,
+ const char *propname, nwam_value_t value)
+{
+ struct validate_internal_arg via;
+
+ assert(hp != NULL && propname != NULL);
+
+ via.table = table;
+ via.errpropp = NULL;
+
+ return ((nwam_error_t)nwam_validate_prop_internal(propname,
+ value, &via));
+}
+
+nwam_error_t
+nwam_validate(struct nwam_prop_table table, struct nwam_handle *hp,
+ const char **errpropp)
+{
+ struct validate_internal_arg via;
+ nwam_error_t err1, err2;
+
+ assert(hp != NULL);
+
+ via.table = table;
+ via.errpropp = errpropp;
+
+ err1 = nwam_walk_props(hp, nwam_validate_prop_internal, &via,
+ 0, (int *)&err2);
+ if (err1 != NWAM_SUCCESS)
+ return (err2);
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Given the type and class flag representations, return the list of properties
+ * that can be set for that type/class combination. Note this list is a complete
+ * property list that includes both the required and the optional properties.
+ * The type and class flags are only used for NCU objects at present.
+ *
+ * Caller needs to free prop_list.
+ */
+nwam_error_t
+nwam_get_default_proplist(struct nwam_prop_table table,
+ uint64_t type, uint64_t class, const char ***prop_list, uint_t *numvalues)
+{
+ struct nwam_prop_table_entry *cur = table.entries;
+ struct nwam_prop_table_entry *end = cur + table.num_entries;
+ int i = 0;
+ const char **list = NULL;
+
+ assert(prop_list != NULL && numvalues != NULL);
+
+ /* Construct a list of all properties for required type/class */
+ list = calloc(table.num_entries, sizeof (char *));
+ if (list == NULL) {
+ *prop_list = NULL;
+ *numvalues = 0;
+ return (NWAM_NO_MEMORY);
+ }
+ for (; cur < end; cur++) {
+ if (((type & cur->prop_type_membership) == 0) ||
+ ((class & cur->prop_class_membership) == 0))
+ continue;
+ list[i++] = cur->prop_name;
+ }
+ *numvalues = i;
+ *prop_list = list;
+ return (NWAM_SUCCESS);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_priv.h b/usr/src/lib/libnwam/common/libnwam_priv.h
new file mode 100644
index 0000000000..14b92b2c3a
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_priv.h
@@ -0,0 +1,167 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains private data structures and APIs of libnwam. Currently
+ * these are used by nwamd (nwam_event_*() and nwam_record_audit_event()) and
+ * netcfgd (nwam_backend_*()) only, supporting the event messaging, audit
+ * and backend configuration access that nwamd and netcfgd supply.
+ *
+ * Implementation is MT safe.
+ */
+#ifndef _LIBNWAM_PRIV_H
+#define _LIBNWAM_PRIV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libnwam.h>
+
+/* Name of directory containing the doors */
+#define NWAM_DOOR_DIR "/etc/svc/volatile/nwam"
+
+/* Name of door used to communicate with libnwam backend (in netcfgd) */
+#define NWAM_BACKEND_DOOR_FILE NWAM_DOOR_DIR "/nwam_backend_door"
+
+/* Name of door used to communicate with nwamd */
+#define NWAM_DOOR NWAM_DOOR_DIR "/nwam_door"
+
+/* Requests to nwamd door */
+typedef enum {
+ NWAM_REQUEST_TYPE_NOOP,
+ NWAM_REQUEST_TYPE_EVENT_REGISTER,
+ NWAM_REQUEST_TYPE_EVENT_UNREGISTER,
+ NWAM_REQUEST_TYPE_ACTION,
+ NWAM_REQUEST_TYPE_STATE,
+ NWAM_REQUEST_TYPE_PRIORITY_GROUP,
+ NWAM_REQUEST_TYPE_WLAN_SCAN,
+ NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS,
+ NWAM_REQUEST_TYPE_WLAN_SELECT,
+ NWAM_REQUEST_TYPE_WLAN_SET_KEY
+} nwam_request_type_t;
+
+/* Status returned by nwamd door */
+typedef enum {
+ NWAM_REQUEST_STATUS_OK,
+ NWAM_REQUEST_STATUS_FAILED,
+ NWAM_REQUEST_STATUS_UNKNOWN,
+ NWAM_REQUEST_STATUS_ALREADY
+} nwam_request_status_t;
+
+#define NWAMD_MAX_NUM_WLANS 64
+
+typedef union {
+ /* Used for EVENT_[UN]REGISTER requests */
+ struct {
+ char nwdad_name[MAXPATHLEN];
+ } nwdad_register_info;
+
+ /* Used for ACTION requests */
+ struct {
+ nwam_object_type_t nwdad_object_type;
+ char nwdad_name[NWAM_MAX_NAME_LEN];
+ char nwdad_parent[NWAM_MAX_NAME_LEN];
+ nwam_action_t nwdad_action;
+ } nwdad_object_action;
+
+ /* Used for STATE requests */
+ struct {
+ nwam_object_type_t nwdad_object_type;
+ char nwdad_name[NWAM_MAX_NAME_LEN];
+ char nwdad_parent[NWAM_MAX_NAME_LEN];
+ nwam_state_t nwdad_state;
+ nwam_aux_state_t nwdad_aux_state;
+ } nwdad_object_state;
+
+ /* Used for PRIORITY_GROUP requests */
+ struct {
+ int64_t nwdad_priority;
+ } nwdad_priority_group_info;
+
+ /* Used for WLAN request/responses */
+ struct {
+ char nwdad_name[NWAM_MAX_NAME_LEN];
+ char nwdad_essid[NWAM_MAX_NAME_LEN];
+ char nwdad_bssid[NWAM_MAX_NAME_LEN];
+ uint32_t nwdad_security_mode;
+ char nwdad_key[NWAM_MAX_NAME_LEN];
+ uint_t nwdad_keyslot;
+ boolean_t nwdad_add_to_known_wlans;
+ uint_t nwdad_num_wlans;
+ nwam_wlan_t nwdad_wlans[NWAMD_MAX_NUM_WLANS];
+ } nwdad_wlan_info;
+
+} nwamd_door_arg_data_t;
+
+typedef struct {
+ nwam_request_type_t nwda_type;
+ nwam_request_status_t nwda_status;
+ nwam_error_t nwda_error;
+ nwamd_door_arg_data_t nwda_data;
+} nwamd_door_arg_t;
+
+typedef enum {
+ NWAM_BACKEND_DOOR_CMD_READ_REQ,
+ NWAM_BACKEND_DOOR_CMD_UPDATE_REQ,
+ NWAM_BACKEND_DOOR_CMD_REMOVE_REQ
+} nwam_backend_door_cmd_t;
+
+typedef struct nwam_backend_door_arg {
+ nwam_backend_door_cmd_t nwbda_cmd;
+ char nwbda_dbname[MAXPATHLEN]; /* config filename */
+ char nwbda_object[NWAM_MAX_NAME_LEN]; /* config object */
+ size_t nwbda_datalen; /* data follows arg */
+ nwam_error_t nwbda_result; /* return code */
+ uint64_t nwbda_flags;
+} nwam_backend_door_arg_t;
+
+/*
+ * Functions needed to initialize/stop processing of libnwam backend data
+ * (used in netcfgd).
+ */
+extern nwam_error_t nwam_backend_init(void);
+extern void nwam_backend_fini(void);
+
+/*
+ * create audit session, report event, end session. Used by nwamd.
+ */
+extern void nwam_record_audit_event(const ucred_t *, au_event_t, char *, char *,
+ int, int);
+
+/*
+ * NWAM daemon functions, used to send, stop sending, initialize or finish
+ * event IPC. Used by nwamd.
+ */
+extern nwam_error_t nwam_event_send(nwam_event_t);
+extern void nwam_event_send_fini(void);
+extern nwam_error_t nwam_event_queue_init(const char *);
+extern void nwam_event_queue_fini(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBNWAM_PRIV_H */
diff --git a/usr/src/lib/libnwam/common/libnwam_util.c b/usr/src/lib/libnwam/common/libnwam_util.c
new file mode 100644
index 0000000000..d900f249f3
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_util.c
@@ -0,0 +1,991 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <atomic.h>
+#include <ctype.h>
+#include <errno.h>
+#include <inet/ip.h>
+#include <libintl.h>
+#include <libproc.h>
+#include <libscf.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Utility functions for door access, common validation functions etc.
+ */
+
+pthread_mutex_t door_mutex = PTHREAD_MUTEX_INITIALIZER;
+int nwam_door_fd = -1;
+
+static int
+open_door(const char *door_name, int *door_fdp)
+{
+ struct door_info dinfo;
+ int err = 0;
+
+ (void) pthread_mutex_lock(&door_mutex);
+
+ if (*door_fdp != -1) {
+ /* Check door fd is not old (from previous nwamd). */
+ if (door_info(*door_fdp, &dinfo) != 0 ||
+ (dinfo.di_attributes & DOOR_REVOKED) != 0) {
+ (void) close(*door_fdp);
+ *door_fdp = -1;
+ }
+ }
+ if (*door_fdp == -1) {
+ *door_fdp = open(door_name, 0);
+ if (*door_fdp == -1)
+ err = errno;
+ }
+
+ (void) pthread_mutex_unlock(&door_mutex);
+
+ return (err);
+}
+
+int
+nwam_make_door_call(const char *door_name, int *door_fdp,
+ void *request, size_t request_size)
+{
+ int err;
+ door_arg_t door_args;
+
+ door_args.data_ptr = (void *)request;
+ door_args.data_size = request_size;
+ door_args.desc_ptr = NULL;
+ door_args.desc_num = 0;
+ door_args.rbuf = (void *)request;
+ door_args.rsize = request_size;
+
+ if ((err = open_door(door_name, door_fdp)) != 0)
+ return (err);
+
+ if (door_call(*door_fdp, &door_args) == -1)
+ return (errno);
+
+ return (0);
+}
+
+static nwam_error_t
+send_msg_to_nwam(nwamd_door_arg_t *request)
+{
+ int err;
+
+ if ((err = nwam_make_door_call(NWAM_DOOR, &nwam_door_fd,
+ request, sizeof (nwamd_door_arg_t))) != 0) {
+ if (err == ENOENT)
+ return (NWAM_ERROR_BIND);
+ return (nwam_errno_to_nwam_error(err));
+ }
+
+ switch (request->nwda_status) {
+ case NWAM_REQUEST_STATUS_OK:
+ return (NWAM_SUCCESS);
+ case NWAM_REQUEST_STATUS_UNKNOWN:
+ return (NWAM_INVALID_ARG);
+ case NWAM_REQUEST_STATUS_ALREADY:
+ return (NWAM_ENTITY_IN_USE);
+ case NWAM_REQUEST_STATUS_FAILED:
+ return (request->nwda_error);
+ default:
+ return (NWAM_ERROR_INTERNAL);
+ }
+}
+
+nwam_error_t
+nwam_request_register_unregister(nwam_request_type_t type,
+ const char *event_msg_file)
+{
+ nwamd_door_arg_t req;
+
+ req.nwda_type = type;
+
+ (void) strlcpy(req.nwda_data.nwdad_register_info.nwdad_name,
+ event_msg_file,
+ sizeof (req.nwda_data.nwdad_register_info.nwdad_name));
+
+ return (send_msg_to_nwam(&req));
+}
+
+nwam_error_t
+nwam_request_action(nwam_object_type_t object_type,
+ const char *name, const char *parent, nwam_action_t action)
+{
+ nwamd_door_arg_t req;
+
+ assert(name != NULL);
+
+ req.nwda_type = NWAM_REQUEST_TYPE_ACTION;
+ req.nwda_data.nwdad_object_action.nwdad_object_type = object_type;
+ req.nwda_data.nwdad_object_action.nwdad_action = action;
+ (void) strlcpy(req.nwda_data.nwdad_object_action.nwdad_name, name,
+ sizeof (req.nwda_data.nwdad_object_action.nwdad_name));
+ if (parent != NULL) {
+ (void) strlcpy(req.nwda_data.nwdad_object_action.nwdad_parent,
+ parent,
+ sizeof (req.nwda_data.nwdad_object_action.nwdad_parent));
+ } else {
+ req.nwda_data.nwdad_object_action.nwdad_parent[0] = '\0';
+ }
+
+ return (send_msg_to_nwam(&req));
+}
+
+nwam_error_t
+nwam_request_state(nwam_object_type_t object_type, const char *name,
+ const char *parent, nwam_state_t *statep, nwam_aux_state_t *auxp)
+{
+ nwamd_door_arg_t req;
+ nwam_error_t err;
+
+ assert(name != NULL && statep != NULL && auxp != NULL);
+
+ req.nwda_type = NWAM_REQUEST_TYPE_STATE;
+
+ req.nwda_data.nwdad_object_state.nwdad_object_type = object_type;
+
+ (void) strlcpy(req.nwda_data.nwdad_object_state.nwdad_name, name,
+ sizeof (req.nwda_data.nwdad_object_state.nwdad_name));
+ if (parent != NULL) {
+ (void) strlcpy(req.nwda_data.nwdad_object_state.nwdad_parent,
+ parent,
+ sizeof (req.nwda_data.nwdad_object_state.nwdad_parent));
+ }
+
+ err = send_msg_to_nwam(&req);
+
+ if (err == NWAM_SUCCESS) {
+ *statep = req.nwda_data.nwdad_object_state.nwdad_state;
+ *auxp = req.nwda_data.nwdad_object_state.nwdad_aux_state;
+ }
+
+ return (err);
+}
+
+nwam_error_t
+nwam_request_wlan(nwam_request_type_t type, const char *name,
+ const char *essid, const char *bssid, uint32_t security_mode,
+ uint_t keyslot, const char *key, boolean_t add_to_known_wlans)
+{
+ nwamd_door_arg_t req;
+
+ assert(name != NULL);
+
+ req.nwda_type = type;
+
+ (void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_name, name,
+ sizeof (req.nwda_data.nwdad_wlan_info));
+ if (essid != NULL) {
+ (void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_essid, essid,
+ sizeof (req.nwda_data.nwdad_wlan_info.nwdad_essid));
+ } else {
+ req.nwda_data.nwdad_wlan_info.nwdad_essid[0] = '\0';
+ }
+ if (bssid != NULL) {
+ (void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_bssid, bssid,
+ sizeof (req.nwda_data.nwdad_wlan_info.nwdad_bssid));
+ } else {
+ req.nwda_data.nwdad_wlan_info.nwdad_bssid[0] = '\0';
+ }
+ if (key != NULL) {
+ (void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_key, key,
+ sizeof (req.nwda_data.nwdad_wlan_info.nwdad_key));
+ req.nwda_data.nwdad_wlan_info.nwdad_keyslot = keyslot;
+ } else {
+ req.nwda_data.nwdad_wlan_info.nwdad_key[0] = '\0';
+ }
+
+ req.nwda_data.nwdad_wlan_info.nwdad_security_mode = security_mode;
+ req.nwda_data.nwdad_wlan_info.nwdad_add_to_known_wlans =
+ add_to_known_wlans;
+
+ return (send_msg_to_nwam(&req));
+}
+
+nwam_error_t
+nwam_request_wlan_scan_results(const char *name, uint_t *num_wlansp,
+ nwam_wlan_t **wlansp)
+{
+ nwamd_door_arg_t req;
+ nwam_error_t err;
+
+ assert(name != NULL && num_wlansp != NULL && wlansp != NULL);
+
+ req.nwda_type = NWAM_REQUEST_TYPE_WLAN_SCAN_RESULTS;
+
+ (void) strlcpy(req.nwda_data.nwdad_wlan_info.nwdad_name, name,
+ sizeof (req.nwda_data.nwdad_wlan_info.nwdad_name));
+
+ if ((err = send_msg_to_nwam(&req)) != NWAM_SUCCESS)
+ return (err);
+
+ *num_wlansp = req.nwda_data.nwdad_wlan_info.nwdad_num_wlans;
+
+ *wlansp = calloc(*num_wlansp, sizeof (nwam_wlan_t));
+ if (*wlansp == NULL)
+ return (NWAM_NO_MEMORY);
+
+ (void) memcpy(*wlansp, req.nwda_data.nwdad_wlan_info.nwdad_wlans,
+ *num_wlansp * sizeof (nwam_wlan_t));
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_request_active_priority_group(int64_t *priorityp)
+{
+ nwamd_door_arg_t req;
+ nwam_error_t err;
+
+ assert(priorityp != NULL);
+
+ req.nwda_type = NWAM_REQUEST_TYPE_PRIORITY_GROUP;
+ err = send_msg_to_nwam(&req);
+
+ if (err == NWAM_SUCCESS)
+ *priorityp =
+ req.nwda_data.nwdad_priority_group_info.nwdad_priority;
+
+ return (err);
+}
+
+/* String conversion functions */
+
+const char *
+nwam_value_type_to_string(nwam_value_type_t type)
+{
+ switch (type) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ return ("boolean");
+ case NWAM_VALUE_TYPE_INT64:
+ return ("int64");
+ case NWAM_VALUE_TYPE_UINT64:
+ return ("uint64");
+ case NWAM_VALUE_TYPE_STRING:
+ return ("string");
+ default:
+ return ("unknown");
+ }
+}
+
+nwam_value_type_t
+nwam_string_to_value_type(const char *typestr)
+{
+ if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_BOOLEAN),
+ strlen(typestr)) == 0)
+ return (NWAM_VALUE_TYPE_BOOLEAN);
+ if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_INT64),
+ strlen(typestr)) == 0)
+ return (NWAM_VALUE_TYPE_INT64);
+ if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_UINT64),
+ strlen(typestr)) == 0)
+ return (NWAM_VALUE_TYPE_UINT64);
+ if (strncmp(typestr, nwam_value_type_to_string(NWAM_VALUE_TYPE_STRING),
+ strlen(typestr)) == 0)
+ return (NWAM_VALUE_TYPE_STRING);
+ return (NWAM_VALUE_TYPE_UNKNOWN);
+}
+
+const char *
+nwam_action_to_string(nwam_action_t action)
+{
+ switch (action) {
+ case NWAM_ACTION_ADD:
+ return ("add");
+ case NWAM_ACTION_REMOVE:
+ return ("remove");
+ case NWAM_ACTION_REFRESH:
+ return ("refresh");
+ case NWAM_ACTION_ENABLE:
+ return ("enable");
+ case NWAM_ACTION_DISABLE:
+ return ("disable");
+ case NWAM_ACTION_DESTROY:
+ return ("destroy");
+ default:
+ return ("unknown");
+ }
+}
+
+const char *
+nwam_event_type_to_string(int event_type)
+{
+ switch (event_type) {
+ case NWAM_EVENT_TYPE_NOOP:
+ return ("NOOP");
+ case NWAM_EVENT_TYPE_INIT:
+ return ("INIT");
+ case NWAM_EVENT_TYPE_SHUTDOWN:
+ return ("SHUTDOWN");
+ case NWAM_EVENT_TYPE_OBJECT_ACTION:
+ return ("OBJECT_ACTION");
+ case NWAM_EVENT_TYPE_OBJECT_STATE:
+ return ("OBJECT_STATE");
+ case NWAM_EVENT_TYPE_PRIORITY_GROUP:
+ return ("PRIORITY_GROUP");
+ case NWAM_EVENT_TYPE_INFO:
+ return ("INFO");
+ case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
+ return ("WLAN_SCAN_REPORT");
+ case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
+ return ("WLAN_NEED_CHOICE");
+ case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
+ return ("WLAN_NEED_KEY");
+ case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
+ return ("WLAN_CONNECTION_REPORT");
+ case NWAM_EVENT_TYPE_IF_ACTION:
+ return ("IF_ACTION");
+ case NWAM_EVENT_TYPE_IF_STATE:
+ return ("IF_STATE");
+ case NWAM_EVENT_TYPE_LINK_ACTION:
+ return ("LINK_ACTION");
+ case NWAM_EVENT_TYPE_LINK_STATE:
+ return ("LINK_STATE");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+const char *
+nwam_state_to_string(nwam_state_t state)
+{
+ switch (state) {
+ case NWAM_STATE_UNINITIALIZED:
+ return ("uninitialized");
+ case NWAM_STATE_INITIALIZED:
+ return ("initialized");
+ case NWAM_STATE_OFFLINE:
+ return ("offline");
+ case NWAM_STATE_OFFLINE_TO_ONLINE:
+ return ("offline*");
+ case NWAM_STATE_ONLINE_TO_OFFLINE:
+ return ("online*");
+ case NWAM_STATE_ONLINE:
+ return ("online");
+ case NWAM_STATE_MAINTENANCE:
+ return ("maintenance");
+ case NWAM_STATE_DEGRADED:
+ return ("degraded");
+ case NWAM_STATE_DISABLED:
+ return ("disabled");
+ default:
+ return ("unknown");
+ }
+}
+
+const char *
+nwam_aux_state_to_string(nwam_aux_state_t aux_state)
+{
+ switch (aux_state) {
+ case NWAM_AUX_STATE_UNINITIALIZED:
+ return ("uninitialized");
+ case NWAM_AUX_STATE_INITIALIZED:
+ return ("(re)initialized but not configured");
+ case NWAM_AUX_STATE_CONDITIONS_NOT_MET:
+ return ("conditions for activation are unmet");
+ case NWAM_AUX_STATE_MANUAL_DISABLE:
+ return ("disabled by administrator");
+ case NWAM_AUX_STATE_METHOD_FAILED:
+ return ("method/service failed");
+ case NWAM_AUX_STATE_METHOD_MISSING:
+ return ("method or FMRI not specified");
+ case NWAM_AUX_STATE_INVALID_CONFIG:
+ return ("invalid configuration values");
+ case NWAM_AUX_STATE_METHOD_RUNNING:
+ return ("method/service executing");
+ case NWAM_AUX_STATE_ACTIVE:
+ return ("active");
+ case NWAM_AUX_STATE_LINK_WIFI_SCANNING:
+ return ("scanning for WiFi networks");
+ case NWAM_AUX_STATE_LINK_WIFI_NEED_SELECTION:
+ return ("need WiFi network selection");
+ case NWAM_AUX_STATE_LINK_WIFI_NEED_KEY:
+ return ("need WiFi security key");
+ case NWAM_AUX_STATE_LINK_WIFI_CONNECTING:
+ return ("connecting to WiFi network");
+ case NWAM_AUX_STATE_IF_WAITING_FOR_ADDR:
+ return ("waiting for IP address to be set");
+ case NWAM_AUX_STATE_IF_DHCP_TIMED_OUT:
+ return ("DHCP wait timeout, still trying...");
+ case NWAM_AUX_STATE_IF_DUPLICATE_ADDR:
+ return ("duplicate address detected");
+ case NWAM_AUX_STATE_UP:
+ return ("interface/link is up");
+ case NWAM_AUX_STATE_DOWN:
+ return ("interface/link is down");
+ case NWAM_AUX_STATE_NOT_FOUND:
+ return ("interface/link not found");
+ default:
+ return ("unknown");
+ }
+}
+
+const char *
+nwam_object_type_to_string(nwam_object_type_t type)
+{
+ switch (type) {
+ case NWAM_OBJECT_TYPE_NCP:
+ return ("ncp");
+ case NWAM_OBJECT_TYPE_NCU:
+ return ("ncu");
+ case NWAM_OBJECT_TYPE_LOC:
+ return ("loc");
+ case NWAM_OBJECT_TYPE_ENM:
+ return ("enm");
+ case NWAM_OBJECT_TYPE_KNOWN_WLAN:
+ return ("known wlan");
+ default:
+ return ("unknown");
+ }
+}
+
+nwam_object_type_t
+nwam_string_to_object_type(const char *typestr)
+{
+ if (strcasecmp(typestr,
+ nwam_object_type_to_string(NWAM_OBJECT_TYPE_NCP)) == 0)
+ return (NWAM_OBJECT_TYPE_NCP);
+ if (strcasecmp(typestr,
+ nwam_object_type_to_string(NWAM_OBJECT_TYPE_NCU)) == 0)
+ return (NWAM_OBJECT_TYPE_NCU);
+ if (strcasecmp(typestr,
+ nwam_object_type_to_string(NWAM_OBJECT_TYPE_LOC)) == 0)
+ return (NWAM_OBJECT_TYPE_LOC);
+ if (strcasecmp(typestr,
+ nwam_object_type_to_string(NWAM_OBJECT_TYPE_ENM)) == 0)
+ return (NWAM_OBJECT_TYPE_ENM);
+ if (strcasecmp(typestr,
+ nwam_object_type_to_string(NWAM_OBJECT_TYPE_KNOWN_WLAN)) == 0)
+ return (NWAM_OBJECT_TYPE_KNOWN_WLAN);
+ return (NWAM_OBJECT_TYPE_UNKNOWN);
+}
+
+nwam_error_t
+nwam_errno_to_nwam_error(int errnum)
+{
+ switch (errnum) {
+ case 0:
+ return (NWAM_SUCCESS);
+ case EBADF:
+ return (NWAM_ERROR_BIND);
+ case EPERM:
+ case EACCES:
+ return (NWAM_PERMISSION_DENIED);
+ case ENOENT:
+ return (NWAM_ENTITY_NOT_FOUND);
+ case EIDRM:
+ return (NWAM_ENTITY_INVALID);
+ case EEXIST:
+ return (NWAM_ENTITY_EXISTS);
+ case EAGAIN:
+ case EBUSY:
+ return (NWAM_ENTITY_IN_USE);
+ case ENOMEM:
+ case ENOSPC:
+ return (NWAM_NO_MEMORY);
+ case EINVAL:
+ case E2BIG:
+ return (NWAM_INVALID_ARG);
+ default:
+ return (NWAM_ERROR_INTERNAL);
+ }
+}
+
+/* Common validation functions */
+
+/*
+ * Do the flags represent a subset of valid_flags?
+ */
+nwam_error_t
+nwam_valid_flags(uint64_t flags, uint64_t valid_flags)
+{
+
+ if ((flags | valid_flags) != valid_flags)
+ return (NWAM_INVALID_ARG);
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_valid_condition(nwam_value_t value)
+{
+ char **conditions;
+ uint_t i, numvalues;
+ nwam_condition_object_type_t object_type;
+ nwam_condition_t condition;
+
+ if (nwam_value_get_string_array(value, &conditions, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ char *object_name = NULL;
+
+ if (nwam_condition_string_to_condition(conditions[i],
+ &object_type, &condition, &object_name) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ if (object_name != NULL)
+ free(object_name);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* check if boolean values are correct, generalize for array of booleans */
+nwam_error_t
+nwam_valid_boolean(nwam_value_t value)
+{
+ boolean_t *val;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_boolean_array(value, &val, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (val[i] != B_TRUE && val[i] != B_FALSE)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* check if uint64 values are correct, generalize for array of ints */
+nwam_error_t
+nwam_valid_uint64(nwam_value_t value)
+{
+ int64_t *val;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_int64_array(value, &val, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ if (val[i] < 0)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* check if domain names are correct, generalize for array of domains */
+nwam_error_t
+nwam_valid_domain(nwam_value_t value)
+{
+ char **domainvalues, *domain;
+ uint_t i, numvalues;
+ int len, j;
+
+ if (nwam_value_get_string_array(value, &domainvalues, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ /*
+ * First and last character must be alphanumeric.
+ * Only '.' and '-' are allowed.
+ */
+ domain = domainvalues[i];
+ len = strlen(domain);
+ if (!isalnum(domain[0]) || !isalnum(domain[len-1]))
+ return (NWAM_ENTITY_INVALID_VALUE);
+ for (j = 0; j < len; j++) {
+ if (!isalnum(domain[j]) &&
+ domain[j] != '.' && domain[j] != '-')
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* check if address prefix is valid */
+static nwam_error_t
+nwam_valid_prefix(char *addr, int max_plen)
+{
+ char *prefix, *end;
+ int prefixlen;
+
+ if ((prefix = strchr(addr, '/')) != NULL) {
+ prefix++;
+ prefixlen = strtol(prefix, &end, 10);
+ if (prefix == end || prefixlen < 0 || prefixlen > max_plen)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* check if IPv4 addresses are correct, generalize for array of addresses */
+nwam_error_t
+nwam_valid_host_v4(nwam_value_t value)
+{
+ char **addrvalues, *addr;
+ uint_t i, numvalues;
+ struct sockaddr_in sa;
+
+ if (nwam_value_get_string_array(value, &addrvalues, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ addr = strdup(addrvalues[i]);
+ if (nwam_valid_prefix(addr, IP_ABITS) != NWAM_SUCCESS) {
+ free(addr);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ /* replace '/' with '\0' */
+ addr = strsep(&addr, "/");
+ if (inet_pton(AF_INET, addr, &(sa.sin_addr)) != 1) {
+ free(addr);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ free(addr);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* Check if IPv4 address for default route is valid */
+nwam_error_t
+nwam_valid_route_v4(nwam_value_t value)
+{
+ char *addrvalue;
+ struct sockaddr_in sa;
+
+ if (nwam_value_get_string(value, &addrvalue) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ if (inet_pton(AF_INET, addrvalue, &(sa.sin_addr)) != 1)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ return (NWAM_SUCCESS);
+}
+
+/* check if IPv6 addresses are correct, generalize for array of addresses */
+nwam_error_t
+nwam_valid_host_v6(nwam_value_t value)
+{
+ char **addrvalues, *addr;
+ uint_t i, numvalues;
+ struct sockaddr_in6 sa;
+
+ if (nwam_value_get_string_array(value, &addrvalues, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ addr = strdup(addrvalues[i]);
+ if (nwam_valid_prefix(addr, IPV6_ABITS) != NWAM_SUCCESS) {
+ free(addr);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ /* replace '/' with '\0' */
+ addr = strsep(&addr, "/");
+ if (inet_pton(AF_INET6, addr, &(sa.sin6_addr)) != 1) {
+ free(addr);
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ free(addr);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/* Check if IPv4 address for default route is valid */
+nwam_error_t
+nwam_valid_route_v6(nwam_value_t value)
+{
+ char *addrvalue;
+ struct sockaddr_in6 sa;
+
+ if (nwam_value_get_string(value, &addrvalue) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ if (inet_pton(AF_INET6, addrvalue, &(sa.sin6_addr)) != 1)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_valid_host_any(nwam_value_t value)
+{
+ if (nwam_valid_host_v4(value) != NWAM_SUCCESS &&
+ nwam_valid_host_v6(value) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_valid_host_or_domain(nwam_value_t value)
+{
+ if (nwam_valid_host_any(value) != NWAM_SUCCESS &&
+ nwam_valid_domain(value) != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ return (NWAM_SUCCESS);
+}
+
+/* We do not validate file existence, merely that it is an absolute path. */
+nwam_error_t
+nwam_valid_file(nwam_value_t value)
+{
+ char **files;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_string_array(value, &files, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ int j = 0;
+ while (isspace(files[i][j]))
+ j++;
+ if (files[i][j] != '/')
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * We do not validate existence of the object pointed to by the FMRI
+ * but merely ensure that it is a valid FMRI. We do this by
+ * using scf_handle_decode_fmri(), but ignore all errors bar
+ * SCF_ERROR_INVALID_ARGUMENT (which indicates the FMRI is invalid).
+ */
+nwam_error_t
+nwam_valid_fmri(nwam_value_t value)
+{
+ char **valstr;
+ scf_handle_t *h = NULL;
+ scf_service_t *svc = NULL;
+ uint_t i, numvalues;
+ nwam_error_t err = NWAM_SUCCESS;
+
+ if ((err = nwam_value_get_string_array(value, &valstr, &numvalues))
+ != NWAM_SUCCESS)
+ return (err);
+
+ h = scf_handle_create(SCF_VERSION);
+ if (h == NULL)
+ return (NWAM_ERROR_INTERNAL);
+
+ if (scf_handle_bind(h) != 0) {
+ err = NWAM_ERROR_INTERNAL;
+ goto out;
+ }
+
+ if ((svc = scf_service_create(h)) == NULL) {
+ err = NWAM_ERROR_INTERNAL;
+ goto out;
+ }
+
+
+ for (i = 0; i < numvalues; i++) {
+ if (scf_handle_decode_fmri(h, valstr[i], NULL, svc,
+ NULL, NULL, NULL, SCF_DECODE_FMRI_TRUNCATE) == 0 ||
+ scf_error() != SCF_ERROR_INVALID_ARGUMENT) {
+ err = NWAM_SUCCESS;
+ continue;
+ }
+ err = NWAM_ENTITY_INVALID_VALUE;
+ break;
+ }
+out:
+ scf_service_destroy(svc);
+ scf_handle_destroy(h);
+ return (err);
+}
+
+/* verifies mac-address and bssids */
+nwam_error_t
+nwam_valid_mac_addr(nwam_value_t value)
+{
+ char **mac_addrs, *addr;
+ uchar_t *hwaddr;
+ int hwaddrlen, j;
+ uint_t i, numvalues;
+
+ if (nwam_value_get_string_array(value, &mac_addrs, &numvalues)
+ != NWAM_SUCCESS)
+ return (NWAM_ENTITY_INVALID_VALUE);
+
+ for (i = 0; i < numvalues; i++) {
+ addr = mac_addrs[i];
+ j = 0;
+
+ /* validate that a-fA-F0-9 and ':' only */
+ while (addr[j] != 0) {
+ if (!isxdigit(addr[j]) && addr[j] != ':')
+ return (NWAM_ENTITY_INVALID_VALUE);
+ j++;
+ }
+
+ if ((hwaddr = _link_aton(addr, &hwaddrlen)) == NULL)
+ return (NWAM_ENTITY_INVALID_VALUE);
+ free(hwaddr);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+boolean_t
+nwam_uid_is_netadm(void)
+{
+ return (getuid() == UID_NETADM);
+}
+
+nwam_error_t
+nwam_get_smf_string_property(const char *fmri, const char *pgname,
+ const char *propname, char **valuep)
+{
+ scf_handle_t *h = NULL;
+ scf_snapshot_t *snap = NULL;
+ scf_instance_t *inst = NULL;
+ scf_propertygroup_t *pg = NULL;
+ scf_property_t *prop = NULL;
+ scf_value_t *val = NULL;
+ nwam_error_t err = NWAM_SUCCESS;
+
+ if ((*valuep = malloc(NWAM_MAX_NAME_LEN)) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
+ scf_handle_bind(h) != 0 ||
+ (inst = scf_instance_create(h)) == NULL ||
+ (snap = scf_snapshot_create(h)) == NULL ||
+ (pg = scf_pg_create(h)) == NULL ||
+ (prop = scf_property_create(h)) == NULL ||
+ (val = scf_value_create(h)) == NULL) {
+ err = NWAM_ERROR_INTERNAL;
+ goto out;
+ }
+ if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst,
+ NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
+ err = NWAM_ENTITY_NOT_FOUND;
+ goto out;
+ }
+ /* Retrieve value from running snapshot (if present) */
+ if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
+ scf_snapshot_destroy(snap);
+ snap = NULL;
+ }
+ if (scf_instance_get_pg_composed(inst, snap, pgname, pg) != 0 ||
+ scf_pg_get_property(pg, propname, prop) != 0 ||
+ scf_property_get_value(prop, val) != 0 ||
+ scf_value_get_astring(val, *valuep, NWAM_MAX_NAME_LEN) == -1) {
+ err = NWAM_ENTITY_NOT_FOUND;
+ }
+out:
+ if (err != NWAM_SUCCESS)
+ free(*valuep);
+
+ scf_value_destroy(val);
+ scf_property_destroy(prop);
+ scf_pg_destroy(pg);
+ if (snap != NULL)
+ scf_snapshot_destroy(snap);
+ scf_instance_destroy(inst);
+ scf_handle_destroy(h);
+
+ return (err);
+}
+
+nwam_error_t
+nwam_set_smf_string_property(const char *fmri, const char *pgname,
+ const char *propname, const char *propval)
+{
+ scf_handle_t *h = NULL;
+ scf_instance_t *inst = NULL;
+ scf_propertygroup_t *pg = NULL;
+ scf_property_t *prop = NULL;
+ scf_value_t *val = NULL;
+ scf_transaction_t *tx = NULL;
+ scf_transaction_entry_t *ent = NULL;
+ nwam_error_t err = NWAM_SUCCESS;
+ int result;
+
+ if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
+ scf_handle_bind(h) != 0 ||
+ (inst = scf_instance_create(h)) == NULL ||
+ (pg = scf_pg_create(h)) == NULL ||
+ (prop = scf_property_create(h)) == NULL ||
+ (val = scf_value_create(h)) == NULL ||
+ scf_value_set_astring(val, propval) != 0 ||
+ (tx = scf_transaction_create(h)) == NULL ||
+ (ent = scf_entry_create(h)) == NULL) {
+ err = NWAM_ERROR_INTERNAL;
+ goto out;
+ }
+ if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst,
+ NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0 ||
+ scf_instance_get_pg_composed(inst, NULL, pgname, pg) != 0) {
+ err = NWAM_ENTITY_NOT_FOUND;
+ goto out;
+ }
+
+retry:
+ if (scf_transaction_start(tx, pg) == -1 ||
+ scf_transaction_property_change(tx, ent, propname, SCF_TYPE_ASTRING)
+ == -1 || scf_entry_add_value(ent, val) != 0) {
+ err = NWAM_ERROR_INTERNAL;
+ goto out;
+ }
+
+ result = scf_transaction_commit(tx);
+ switch (result) {
+ case 1:
+ (void) smf_refresh_instance(fmri);
+ break;
+ case 0:
+ scf_transaction_reset(tx);
+ if (scf_pg_update(pg) == -1) {
+ err = NWAM_ERROR_INTERNAL;
+ goto out;
+ }
+ goto retry;
+ default:
+ err = NWAM_ERROR_INTERNAL;
+ break;
+ }
+out:
+ scf_value_destroy(val);
+ scf_property_destroy(prop);
+ scf_pg_destroy(pg);
+ scf_instance_destroy(inst);
+ scf_handle_destroy(h);
+
+ return (err);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_values.c b/usr/src/lib/libnwam/common/libnwam_values.c
new file mode 100644
index 0000000000..1f4b8bef84
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_values.c
@@ -0,0 +1,1147 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <libdlwlan.h>
+#include <libnvpair.h>
+
+#include "libnwam_impl.h"
+#include <libnwam_priv.h>
+#include <libnwam.h>
+
+/*
+ * Internal implementation of libnwam in-memory objects and values. Objects
+ * are nvlists.
+ */
+
+void
+nwam_value_free(nwam_value_t value)
+{
+ uint_t i;
+
+ if (value == NULL)
+ return;
+
+ switch (value->nwv_value_type) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ free(value->nwv_values.nwv_boolean);
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ free(value->nwv_values.nwv_int64);
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ free(value->nwv_values.nwv_uint64);
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ for (i = 0; i < value->nwv_value_numvalues; i++)
+ free(value->nwv_values.nwv_string[i]);
+ free(value->nwv_values.nwv_string);
+ break;
+ }
+ free(value);
+}
+
+nwam_error_t
+nwam_value_create(nwam_value_type_t value_type, void *values, uint_t numvalues,
+ nwam_value_t *valuep)
+{
+ nwam_value_t newvalue;
+ boolean_t *values_boolean;
+ int64_t *values_int64;
+ uint64_t *values_uint64;
+ char **values_string;
+ int i, j;
+ nwam_error_t err = NWAM_SUCCESS;
+
+ *valuep = NULL;
+
+ if (numvalues > NWAM_MAX_NUM_VALUES)
+ return (NWAM_INVALID_ARG);
+
+ if ((newvalue = calloc(1, sizeof (struct nwam_value))) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ newvalue->nwv_value_type = value_type;
+ newvalue->nwv_value_numvalues = numvalues;
+
+ switch (value_type) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ values_boolean = values;
+ if ((newvalue->nwv_values.nwv_boolean =
+ calloc(numvalues, sizeof (boolean_t))) == NULL) {
+ free(newvalue);
+ return (NWAM_NO_MEMORY);
+ }
+ for (i = 0; i < numvalues; i++)
+ newvalue->nwv_values.nwv_boolean[i] = values_boolean[i];
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ values_int64 = values;
+ if ((newvalue->nwv_values.nwv_int64 =
+ calloc(numvalues, sizeof (int64_t))) == NULL) {
+ free(newvalue);
+ return (NWAM_NO_MEMORY);
+ }
+ for (i = 0; i < numvalues; i++)
+ newvalue->nwv_values.nwv_int64[i] = values_int64[i];
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ values_uint64 = values;
+ if ((newvalue->nwv_values.nwv_uint64 =
+ calloc(numvalues, sizeof (uint64_t))) == NULL) {
+ free(newvalue);
+ return (NWAM_NO_MEMORY);
+ }
+ for (i = 0; i < numvalues; i++)
+ newvalue->nwv_values.nwv_uint64[i] = values_uint64[i];
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ values_string = values;
+ if ((newvalue->nwv_values.nwv_string =
+ calloc(numvalues, sizeof (char *))) == NULL) {
+ free(newvalue);
+ return (NWAM_NO_MEMORY);
+ }
+ for (i = 0; i < numvalues; i++) {
+ if (strnlen(values_string[i], NWAM_MAX_VALUE_LEN) ==
+ NWAM_MAX_VALUE_LEN) {
+ err = NWAM_ENTITY_INVALID_VALUE;
+ } else if ((newvalue->nwv_values.nwv_string[i] =
+ strdup(values_string[i])) == NULL) {
+ err = NWAM_NO_MEMORY;
+ }
+ if (err != NWAM_SUCCESS) {
+ for (j = 0; j < i; j++)
+ free(
+ newvalue->nwv_values.nwv_string[i]);
+ free(newvalue->nwv_values.nwv_string);
+ free(newvalue);
+ return (err);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ *valuep = newvalue;
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_copy(nwam_value_t old, nwam_value_t *newp)
+{
+ void *values;
+
+ assert(old != NULL && newp != NULL);
+
+ switch (old->nwv_value_type) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ values = old->nwv_values.nwv_boolean;
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ values = old->nwv_values.nwv_int64;
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ values = old->nwv_values.nwv_uint64;
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ values = old->nwv_values.nwv_string;
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+ }
+ return (nwam_value_create(old->nwv_value_type, values,
+ old->nwv_value_numvalues, newp));
+}
+nwam_error_t
+nwam_value_create_boolean_array(boolean_t *values, uint_t numvalues,
+ nwam_value_t *valuep)
+{
+ return (nwam_value_create(NWAM_VALUE_TYPE_BOOLEAN, values, numvalues,
+ valuep));
+}
+
+nwam_error_t
+nwam_value_create_boolean(boolean_t value, nwam_value_t *valuep)
+{
+ return (nwam_value_create_boolean_array(&value, 1, valuep));
+}
+
+nwam_error_t
+nwam_value_create_int64_array(int64_t *values, uint_t numvalues,
+ nwam_value_t *valuep)
+{
+ return (nwam_value_create(NWAM_VALUE_TYPE_INT64, values, numvalues,
+ valuep));
+}
+
+nwam_error_t
+nwam_value_create_int64(int64_t value, nwam_value_t *valuep)
+{
+ return (nwam_value_create_int64_array(&value, 1, valuep));
+}
+
+nwam_error_t
+nwam_value_create_uint64_array(uint64_t *values, uint_t numvalues,
+ nwam_value_t *valuep)
+{
+ return (nwam_value_create(NWAM_VALUE_TYPE_UINT64, values, numvalues,
+ valuep));
+}
+
+nwam_error_t
+nwam_value_create_uint64(uint64_t value, nwam_value_t *valuep)
+{
+ return (nwam_value_create_uint64_array(&value, 1, valuep));
+}
+
+nwam_error_t
+nwam_value_create_string_array(char **values, uint_t numvalues,
+ nwam_value_t *valuep)
+{
+ return (nwam_value_create(NWAM_VALUE_TYPE_STRING, values, numvalues,
+ valuep));
+}
+
+nwam_error_t
+nwam_value_create_string(char *value, nwam_value_t *valuep)
+{
+ return (nwam_value_create_string_array(&value, 1, valuep));
+}
+
+nwam_error_t
+nwam_value_get_boolean_array(nwam_value_t value, boolean_t **valuesp,
+ uint_t *numvaluesp)
+{
+ assert(value != NULL && numvaluesp != NULL && valuesp != NULL);
+
+ *numvaluesp = value->nwv_value_numvalues;
+ *valuesp = value->nwv_values.nwv_boolean;
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_boolean(nwam_value_t value, boolean_t *valuep)
+{
+ uint_t numvalues;
+ boolean_t *myvaluesp;
+ nwam_error_t err;
+
+ err = nwam_value_get_boolean_array(value, &myvaluesp, &numvalues);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if (numvalues != 1)
+ return (NWAM_ENTITY_MULTIPLE_VALUES);
+
+ *valuep = myvaluesp[0];
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_int64_array(nwam_value_t value, int64_t **valuesp,
+ uint_t *numvaluesp)
+{
+ assert(value != NULL && numvaluesp != NULL && valuesp != NULL);
+
+ *numvaluesp = value->nwv_value_numvalues;
+ *valuesp = value->nwv_values.nwv_int64;
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_int64(nwam_value_t value, int64_t *valuep)
+{
+ uint_t numvalues;
+ int64_t *myvaluesp;
+ nwam_error_t err;
+
+ err = nwam_value_get_int64_array(value, &myvaluesp, &numvalues);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if (numvalues != 1)
+ return (NWAM_ENTITY_MULTIPLE_VALUES);
+
+ *valuep = myvaluesp[0];
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_uint64_array(nwam_value_t value, uint64_t **valuesp,
+ uint_t *numvaluesp)
+{
+ assert(value != NULL && numvaluesp != NULL && valuesp != NULL);
+
+ *numvaluesp = value->nwv_value_numvalues;
+ *valuesp = value->nwv_values.nwv_uint64;
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_uint64(nwam_value_t value, uint64_t *valuep)
+{
+ uint_t numvalues;
+ uint64_t *myvaluesp;
+ nwam_error_t err;
+
+ err = nwam_value_get_uint64_array(value, &myvaluesp, &numvalues);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if (numvalues != 1)
+ return (NWAM_ENTITY_MULTIPLE_VALUES);
+
+ *valuep = myvaluesp[0];
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_string_array(nwam_value_t value, char ***valuesp,
+ uint_t *numvaluesp)
+{
+ assert(value != NULL && numvaluesp != NULL && valuesp != NULL);
+
+ *numvaluesp = value->nwv_value_numvalues;
+ *valuesp = value->nwv_values.nwv_string;
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_string(nwam_value_t value, char **valuep)
+{
+ uint_t numvalues;
+ char **myvaluesp;
+ nwam_error_t err;
+
+ err = nwam_value_get_string_array(value, &myvaluesp, &numvalues);
+ if (err != NWAM_SUCCESS)
+ return (err);
+ if (numvalues != 1)
+ return (NWAM_ENTITY_MULTIPLE_VALUES);
+
+ *valuep = myvaluesp[0];
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_type(nwam_value_t value, nwam_value_type_t *typep)
+{
+ *typep = value->nwv_value_type;
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_value_get_numvalues(nwam_value_t value, uint_t *numvaluesp)
+{
+ *numvaluesp = value->nwv_value_numvalues;
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Generic object data functions. We hide nvlist implementation
+ * from NCP, ENM and location implementations.
+ */
+nwam_error_t
+nwam_alloc_object_list(void *list)
+{
+ int nverr;
+
+ assert(list != NULL);
+
+ if ((nverr = nvlist_alloc((nvlist_t **)list, NV_UNIQUE_NAME, 0)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+
+ return (NWAM_SUCCESS);
+}
+
+void
+nwam_free_object_list(void *list)
+{
+ if (list != NULL)
+ nvlist_free(list);
+}
+
+nwam_error_t
+nwam_dup_object_list(void *oldlist, void *newlist)
+{
+ int nverr;
+
+ assert(oldlist != NULL && newlist != NULL);
+
+ if ((nverr = nvlist_dup(oldlist, newlist, 0)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+
+ return (NWAM_SUCCESS);
+}
+
+/* Add child object list to parent object list using property name childname */
+nwam_error_t
+nwam_object_list_add_object_list(void *parentlist, char *childname,
+ void *childlist)
+{
+ return (nwam_errno_to_nwam_error(nvlist_add_nvlist(parentlist,
+ childname, childlist)));
+}
+
+/* Remove object list from parent object list */
+nwam_error_t
+nwam_object_list_remove_object_list(void *parentlist, char *childname)
+{
+ return (nwam_errno_to_nwam_error(nvlist_remove_all(parentlist,
+ childname)));
+}
+
+/*
+ * Get next object list (nvlist) after lastname. Used to walk NCUs, ENMs and
+ * locations, each of which is internally represented as an nvlist.
+ */
+nwam_error_t
+nwam_next_object_list(void *parentlist, char *lastname, char **childnamep,
+ void *childlistp)
+{
+ nvpair_t *last = NULL, *next;
+ int nverr;
+
+ if (lastname != NULL) {
+ if ((nverr = nvlist_lookup_nvpair(parentlist, lastname, &last))
+ != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ }
+ if ((next = nvlist_next_nvpair(parentlist, last)) == NULL)
+ return (NWAM_LIST_END);
+
+ *childnamep = nvpair_name(next);
+
+ if (nvpair_type(next) != DATA_TYPE_NVLIST)
+ return (NWAM_ERROR_INTERNAL);
+
+ if ((nverr = nvpair_value_nvlist(next, childlistp)) != NWAM_SUCCESS)
+ return (nwam_errno_to_nwam_error(nverr));
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Pack nvlist into contiguous memory. If packed_listp is NULL, we just
+ * return the size of the memory needed to do so.
+ */
+nwam_error_t
+nwam_pack_object_list(void *list, char **packed_listp, size_t *packed_sizep)
+{
+ int nverr;
+
+ assert(list != NULL && packed_sizep != NULL);
+
+ if (packed_listp == NULL) {
+ nverr = nvlist_size(list, packed_sizep, NV_ENCODE_XDR);
+ } else {
+ nverr = nvlist_pack(list, packed_listp, packed_sizep,
+ NV_ENCODE_XDR, 0);
+ }
+
+ if (nverr != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_unpack_object_list(char *packed_list, size_t packed_size,
+ void *list)
+{
+ int nverr;
+
+ assert(packed_list != NULL && list != NULL);
+
+ *((nvlist_t **)list) = NULL;
+
+ nverr = nvlist_unpack(packed_list, packed_size, (nvlist_t **)list, 0);
+
+ if (nverr != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+
+ return (NWAM_SUCCESS);
+}
+
+/*
+ * Functions to walk, set and get properties in nvlist, translating
+ * between nwam_value_t and nvlist/nvpair representations.
+ */
+nwam_error_t
+nwam_next_object_prop(void *list, char *lastname, char **namep,
+ nwam_value_t *valuep)
+{
+ nvpair_t *last = NULL, *next;
+ int nverr;
+
+ if (lastname != NULL) {
+ if ((nverr = nvlist_lookup_nvpair(list, lastname, &last)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ }
+ if ((next = nvlist_next_nvpair(list, last)) == NULL)
+ return (NWAM_LIST_END);
+
+ *namep = nvpair_name(next);
+
+ return (nwam_get_prop_value(list, (const char *)*namep, valuep));
+}
+
+nwam_error_t
+nwam_get_prop_value(void *list, const char *name, nwam_value_t *valuep)
+{
+ nvpair_t *prop;
+ nwam_error_t err;
+ int nverr;
+ boolean_t *valbool;
+ int64_t *valint64;
+ uint64_t *valuint64;
+ char **valstr;
+ uint_t numvalues;
+
+ assert(valuep != NULL);
+
+ *valuep = NULL;
+
+ if ((nverr = nvlist_lookup_nvpair(list, name, &prop)) != 0) {
+ /* convert EINVAL to NOT_FOUND */
+ if (nverr == EINVAL)
+ return (NWAM_ENTITY_NOT_FOUND);
+ return (nwam_errno_to_nwam_error(nverr));
+ }
+
+ switch (nvpair_type(prop)) {
+ case DATA_TYPE_BOOLEAN_ARRAY:
+ if ((nverr = nvpair_value_boolean_array(prop,
+ &valbool, &numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ if ((err = nwam_value_create_boolean_array(valbool, numvalues,
+ valuep)) != NWAM_SUCCESS)
+ return (err);
+ break;
+ case DATA_TYPE_INT64_ARRAY:
+ if ((nverr = nvpair_value_int64_array(prop,
+ &valint64, &numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ if ((err = nwam_value_create_int64_array(valint64, numvalues,
+ valuep)) != NWAM_SUCCESS)
+ return (err);
+ break;
+ case DATA_TYPE_UINT64_ARRAY:
+ if ((nverr = nvpair_value_uint64_array(prop,
+ &valuint64, &numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ if ((err = nwam_value_create_uint64_array(valuint64, numvalues,
+ valuep)) != NWAM_SUCCESS)
+ return (err);
+ break;
+ case DATA_TYPE_STRING_ARRAY:
+ if ((nverr = nvpair_value_string_array(prop,
+ &valstr, &numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ if ((err = nwam_value_create_string_array(valstr, numvalues,
+ valuep)) != NWAM_SUCCESS)
+ return (err);
+ break;
+ default:
+ /* Should not happen */
+ return (NWAM_ERROR_INTERNAL);
+ }
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_delete_prop(void *list, const char *name)
+{
+ int nverr;
+
+ if ((nverr = nvlist_remove_all(list, name)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_set_prop_value(void *list, const char *propname, nwam_value_t value)
+{
+ int nverr;
+ nwam_error_t err;
+ nwam_value_type_t type;
+ uint_t numvalues;
+ boolean_t *valbool;
+ int64_t *valint64;
+ uint64_t *valuint64;
+ char **valstr;
+
+ assert(list != NULL && value != NULL);
+
+ if ((err = nwam_value_get_type(value, &type)) != NWAM_SUCCESS)
+ return (err);
+
+ switch (type) {
+ case NWAM_VALUE_TYPE_BOOLEAN:
+ if ((err = nwam_value_get_boolean_array(value, &valbool,
+ &numvalues)) != NWAM_SUCCESS)
+ return (err);
+ if ((nverr = nvlist_add_boolean_array(list, propname,
+ valbool, numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ break;
+ case NWAM_VALUE_TYPE_INT64:
+ if ((err = nwam_value_get_int64_array(value, &valint64,
+ &numvalues)) != NWAM_SUCCESS)
+ return (err);
+ if ((nverr = nvlist_add_int64_array(list, propname,
+ valint64, numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ break;
+ case NWAM_VALUE_TYPE_UINT64:
+ if ((err = nwam_value_get_uint64_array(value, &valuint64,
+ &numvalues)) != NWAM_SUCCESS)
+ return (err);
+ if ((nverr = nvlist_add_uint64_array(list, propname,
+ valuint64, numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ break;
+ case NWAM_VALUE_TYPE_STRING:
+ if ((err = nwam_value_get_string_array(value, &valstr,
+ &numvalues)) != NWAM_SUCCESS)
+ return (err);
+ if ((nverr = nvlist_add_string_array(list, propname,
+ valstr, numvalues)) != 0)
+ return (nwam_errno_to_nwam_error(nverr));
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+ }
+
+ return (NWAM_SUCCESS);
+}
+
+/* Map uint64 values to their string counterparts */
+
+struct nwam_value_entry {
+ const char *value_string;
+ uint64_t value;
+};
+
+struct nwam_value_entry prop_activation_mode_value_entries[] =
+{
+ { NWAM_ACTIVATION_MODE_MANUAL_STRING, NWAM_ACTIVATION_MODE_MANUAL },
+ { NWAM_ACTIVATION_MODE_SYSTEM_STRING, NWAM_ACTIVATION_MODE_SYSTEM },
+ { NWAM_ACTIVATION_MODE_CONDITIONAL_ANY_STRING,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ANY },
+ { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL_STRING,
+ NWAM_ACTIVATION_MODE_CONDITIONAL_ALL },
+ { NWAM_ACTIVATION_MODE_PRIORITIZED_STRING,
+ NWAM_ACTIVATION_MODE_PRIORITIZED },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry ncu_prop_type_entries[] =
+{
+ { NWAM_NCU_TYPE_LINK_STRING, NWAM_NCU_TYPE_LINK },
+ { NWAM_NCU_TYPE_INTERFACE_STRING, NWAM_NCU_TYPE_INTERFACE },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry ncu_prop_class_entries[] =
+{
+ { NWAM_NCU_CLASS_PHYS_STRING, NWAM_NCU_CLASS_PHYS },
+ { NWAM_NCU_CLASS_IP_STRING, NWAM_NCU_CLASS_IP },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry ncu_prop_ip_version_entries[] =
+{
+ { NWAM_IP_VERSION_IPV4_STRING, IPV4_VERSION },
+ { NWAM_IP_VERSION_IPV6_STRING, IPV6_VERSION },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry ncu_prop_ipv4_addrsrc_entries[] =
+{
+ { NWAM_ADDRSRC_DHCP_STRING, NWAM_ADDRSRC_DHCP },
+ { NWAM_ADDRSRC_STATIC_STRING, NWAM_ADDRSRC_STATIC },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry ncu_prop_ipv6_addrsrc_entries[] =
+{
+ { NWAM_ADDRSRC_DHCP_STRING, NWAM_ADDRSRC_DHCP },
+ { NWAM_ADDRSRC_STATIC_STRING, NWAM_ADDRSRC_STATIC },
+ { NWAM_ADDRSRC_AUTOCONF_STRING, NWAM_ADDRSRC_AUTOCONF },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry ncu_prop_priority_mode_entries[] =
+{
+ { NWAM_PRIORITY_MODE_EXCLUSIVE_STRING, NWAM_PRIORITY_MODE_EXCLUSIVE },
+ { NWAM_PRIORITY_MODE_SHARED_STRING, NWAM_PRIORITY_MODE_SHARED },
+ { NWAM_PRIORITY_MODE_ALL_STRING, NWAM_PRIORITY_MODE_ALL },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry loc_prop_nameservices_entries[] =
+{
+ { NWAM_NAMESERVICES_DNS_STRING, NWAM_NAMESERVICES_DNS },
+ { NWAM_NAMESERVICES_FILES_STRING, NWAM_NAMESERVICES_FILES },
+ { NWAM_NAMESERVICES_NIS_STRING, NWAM_NAMESERVICES_NIS },
+ { NWAM_NAMESERVICES_LDAP_STRING, NWAM_NAMESERVICES_LDAP },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry loc_prop_nameservice_configsrc_entries[] =
+{
+ { NWAM_CONFIGSRC_MANUAL_STRING, NWAM_CONFIGSRC_MANUAL },
+ { NWAM_CONFIGSRC_DHCP_STRING, NWAM_CONFIGSRC_DHCP },
+ { NULL, 0 }
+};
+
+struct nwam_value_entry known_wlan_prop_security_mode_entries[] =
+{
+ { "none", DLADM_WLAN_SECMODE_NONE },
+ { "wep", DLADM_WLAN_SECMODE_WEP },
+ { "wpa", DLADM_WLAN_SECMODE_WPA },
+ { NULL, 0 }
+};
+
+struct nwam_prop_value_entry {
+ const char *prop_name;
+ struct nwam_value_entry *value_entries;
+} prop_value_entry_table[] =
+{
+ { NWAM_NCU_PROP_ACTIVATION_MODE, prop_activation_mode_value_entries },
+ { NWAM_NCU_PROP_TYPE, ncu_prop_type_entries },
+ { NWAM_NCU_PROP_CLASS, ncu_prop_class_entries },
+ { NWAM_NCU_PROP_IP_VERSION, ncu_prop_ip_version_entries },
+ { NWAM_NCU_PROP_IPV4_ADDRSRC, ncu_prop_ipv4_addrsrc_entries },
+ { NWAM_NCU_PROP_IPV6_ADDRSRC, ncu_prop_ipv6_addrsrc_entries },
+ { NWAM_NCU_PROP_PRIORITY_MODE, ncu_prop_priority_mode_entries },
+ { NWAM_ENM_PROP_ACTIVATION_MODE, prop_activation_mode_value_entries },
+ { NWAM_LOC_PROP_ACTIVATION_MODE, prop_activation_mode_value_entries },
+ { NWAM_LOC_PROP_NAMESERVICES, loc_prop_nameservices_entries },
+ { NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
+ loc_prop_nameservice_configsrc_entries },
+ { NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
+ loc_prop_nameservice_configsrc_entries },
+ { NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
+ loc_prop_nameservice_configsrc_entries },
+ { NWAM_KNOWN_WLAN_PROP_SECURITY_MODE,
+ known_wlan_prop_security_mode_entries },
+ { NULL, NULL }
+};
+
+/*
+ * Convert uint64 values for property propname into a string representing
+ * that value. Used by enum values.
+ */
+nwam_error_t
+nwam_uint64_get_value_string(const char *propname, uint64_t val,
+ const char **valstrp)
+{
+ int i, j;
+ int max = 0; /* largest enum value seen so far */
+ struct nwam_value_entry *value_entries;
+
+ assert(propname != NULL && valstrp != NULL);
+
+ for (i = 0; prop_value_entry_table[i].prop_name != NULL; i++) {
+ if (strcmp(prop_value_entry_table[i].prop_name, propname) != 0)
+ continue;
+
+ value_entries = prop_value_entry_table[i].value_entries;
+
+ for (j = 0; value_entries[j].value_string != NULL; j++) {
+ if (value_entries[j].value == val) {
+ *valstrp = value_entries[j].value_string;
+ return (NWAM_SUCCESS);
+ }
+ max = value_entries[j].value > max ?
+ value_entries[j].value : max;
+ }
+ /*
+ * If trying to get the string for an enum value that doesn't
+ * exist, return NWAM_LIST_END. Otherwise, the input enum
+ * value doesn't exist for the given property.
+ */
+ if (val > max)
+ return (NWAM_LIST_END);
+ else
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_INVALID_ARG);
+}
+
+/*
+ * Convert string to appropriate uint64 value.
+ */
+nwam_error_t
+nwam_value_string_get_uint64(const char *propname, const char *valstr,
+ uint64_t *valp)
+{
+ int i, j;
+ struct nwam_value_entry *value_entries;
+
+ assert(propname != NULL && valstr != NULL && valp != NULL);
+
+ for (i = 0; prop_value_entry_table[i].prop_name != NULL; i++) {
+ if (strcmp(prop_value_entry_table[i].prop_name, propname) != 0)
+ continue;
+
+ value_entries = prop_value_entry_table[i].value_entries;
+
+ for (j = 0; value_entries[j].value_string != NULL; j++) {
+ if (strcasecmp(value_entries[j].value_string, valstr)
+ == 0) {
+ *valp = value_entries[j].value;
+ return (NWAM_SUCCESS);
+ }
+ }
+ return (NWAM_ENTITY_INVALID_VALUE);
+ }
+ return (NWAM_INVALID_ARG);
+}
+
+/* Conditional activation functions */
+
+nwam_error_t
+nwam_condition_to_condition_string(nwam_condition_object_type_t object_type,
+ nwam_condition_t condition, const char *object_name, char **stringp)
+{
+ char *object_type_string, *condition_string;
+ char *string;
+
+ assert(stringp != NULL);
+
+ *stringp = NULL;
+
+ switch (object_type) {
+ case NWAM_CONDITION_OBJECT_TYPE_NCP:
+ object_type_string = NWAM_CONDITION_OBJECT_TYPE_NCP_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_NCU:
+ object_type_string = NWAM_CONDITION_OBJECT_TYPE_NCU_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_ENM:
+ object_type_string = NWAM_CONDITION_OBJECT_TYPE_ENM_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_LOC:
+ object_type_string = NWAM_CONDITION_OBJECT_TYPE_LOC_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS:
+ object_type_string =
+ NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN:
+ object_type_string =
+ NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN:
+ object_type_string =
+ NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_ESSID:
+ object_type_string = NWAM_CONDITION_OBJECT_TYPE_ESSID_STRING;
+ break;
+ case NWAM_CONDITION_OBJECT_TYPE_BSSID:
+ object_type_string = NWAM_CONDITION_OBJECT_TYPE_BSSID_STRING;
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+
+ }
+ switch (condition) {
+ case NWAM_CONDITION_IS:
+ condition_string = NWAM_CONDITION_IS_STRING;
+ break;
+ case NWAM_CONDITION_IS_NOT:
+ condition_string = NWAM_CONDITION_IS_NOT_STRING;
+ break;
+ case NWAM_CONDITION_CONTAINS:
+ if (object_type != NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN &&
+ object_type != NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN &&
+ object_type != NWAM_CONDITION_OBJECT_TYPE_ESSID)
+ return (NWAM_INVALID_ARG);
+ condition_string = NWAM_CONDITION_CONTAINS_STRING;
+ break;
+ case NWAM_CONDITION_DOES_NOT_CONTAIN:
+ if (object_type != NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN &&
+ object_type != NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN &&
+ object_type != NWAM_CONDITION_OBJECT_TYPE_ESSID)
+ return (NWAM_INVALID_ARG);
+
+ condition_string = NWAM_CONDITION_DOES_NOT_CONTAIN_STRING;
+ break;
+ case NWAM_CONDITION_IS_IN_RANGE:
+ if (object_type != NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS)
+ return (NWAM_INVALID_ARG);
+ condition_string = NWAM_CONDITION_IS_IN_RANGE_STRING;
+ break;
+ case NWAM_CONDITION_IS_NOT_IN_RANGE:
+ if (object_type != NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS)
+ return (NWAM_INVALID_ARG);
+ condition_string = NWAM_CONDITION_IS_NOT_IN_RANGE_STRING;
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+ break;
+ }
+ if ((string = malloc(NWAM_MAX_VALUE_LEN)) == NULL)
+ return (NWAM_NO_MEMORY);
+ switch (object_type) {
+ case NWAM_CONDITION_OBJECT_TYPE_NCP:
+ case NWAM_CONDITION_OBJECT_TYPE_NCU:
+ case NWAM_CONDITION_OBJECT_TYPE_ENM:
+ case NWAM_CONDITION_OBJECT_TYPE_LOC:
+ (void) snprintf(string, NWAM_MAX_VALUE_LEN,
+ "%s %s %s active", object_type_string,
+ object_name, condition_string);
+ *stringp = string;
+ break;
+
+ case NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS:
+ case NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN:
+ case NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN:
+ case NWAM_CONDITION_OBJECT_TYPE_ESSID:
+ case NWAM_CONDITION_OBJECT_TYPE_BSSID:
+ (void) snprintf(string, NWAM_MAX_VALUE_LEN,
+ "%s %s %s", object_type_string,
+ condition_string, object_name);
+ *stringp = string;
+ break;
+
+ default:
+ free(string);
+ return (NWAM_INVALID_ARG);
+
+ }
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_condition_string_to_condition(const char *string,
+ nwam_condition_object_type_t *object_typep,
+ nwam_condition_t *conditionp, char **object_namep)
+{
+ char *copy, *lasts;
+ char *object_type_string, *object_name;
+ char *condition_string, *active_string;
+
+ assert(string != NULL && object_typep != NULL && conditionp != NULL &&
+ object_namep != NULL);
+
+ if ((copy = strdup(string)) == NULL)
+ return (NWAM_NO_MEMORY);
+
+ if ((object_type_string = strtok_r(copy, " \t", &lasts)) == NULL) {
+ free(copy);
+ return (NWAM_INVALID_ARG);
+ }
+
+ if (strcmp(object_type_string, NWAM_CONDITION_OBJECT_TYPE_NCP_STRING)
+ == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_NCP;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_NCU_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_NCU;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_ENM_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_ENM;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_LOC_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_LOC;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_ESSID_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_ESSID;
+ else if (strcmp(object_type_string,
+ NWAM_CONDITION_OBJECT_TYPE_BSSID_STRING) == 0)
+ *object_typep = NWAM_CONDITION_OBJECT_TYPE_BSSID;
+ else {
+ free(copy);
+ return (NWAM_INVALID_ARG);
+ }
+
+ if (*object_typep == NWAM_CONDITION_OBJECT_TYPE_NCP ||
+ *object_typep == NWAM_CONDITION_OBJECT_TYPE_NCU ||
+ *object_typep == NWAM_CONDITION_OBJECT_TYPE_ENM ||
+ *object_typep == NWAM_CONDITION_OBJECT_TYPE_LOC) {
+ if ((object_name = strtok_r(NULL, " \t", &lasts)) == NULL) {
+ free(copy);
+ return (NWAM_INVALID_ARG);
+ }
+ if ((*object_namep = strdup(object_name)) == NULL) {
+ free(copy);
+ return (NWAM_NO_MEMORY);
+ }
+
+ }
+
+ if ((condition_string = strtok_r(NULL, " \t", &lasts)) == NULL) {
+ free(copy);
+ if (*object_namep != NULL)
+ free(*object_namep);
+ return (NWAM_INVALID_ARG);
+ }
+ if (strcmp(condition_string, NWAM_CONDITION_IS_STRING) == 0)
+ *conditionp = NWAM_CONDITION_IS;
+ else if (strcmp(condition_string, NWAM_CONDITION_IS_NOT_STRING) == 0)
+ *conditionp = NWAM_CONDITION_IS_NOT;
+ else if (strcmp(condition_string, NWAM_CONDITION_CONTAINS_STRING) == 0)
+ *conditionp = NWAM_CONDITION_CONTAINS;
+ else if (strcmp(condition_string,
+ NWAM_CONDITION_DOES_NOT_CONTAIN_STRING) == 0)
+ *conditionp = NWAM_CONDITION_DOES_NOT_CONTAIN;
+ else if (strcmp(condition_string,
+ NWAM_CONDITION_IS_IN_RANGE_STRING) == 0)
+ *conditionp = NWAM_CONDITION_IS_IN_RANGE;
+ else if (strcmp(condition_string,
+ NWAM_CONDITION_IS_NOT_IN_RANGE_STRING) == 0)
+ *conditionp = NWAM_CONDITION_IS_NOT_IN_RANGE;
+ else {
+ free(copy);
+ if (*object_namep != NULL)
+ free(*object_namep);
+ return (NWAM_INVALID_ARG);
+ }
+
+ if (*object_typep == NWAM_CONDITION_OBJECT_TYPE_NCP ||
+ *object_typep == NWAM_CONDITION_OBJECT_TYPE_NCU ||
+ *object_typep == NWAM_CONDITION_OBJECT_TYPE_ENM ||
+ *object_typep == NWAM_CONDITION_OBJECT_TYPE_LOC) {
+ if ((*conditionp != NWAM_CONDITION_IS &&
+ *conditionp != NWAM_CONDITION_IS_NOT) ||
+ (active_string = strtok_r(NULL, " \t", &lasts)) == NULL ||
+ strcmp(active_string, NWAM_CONDITION_ACTIVE_STRING) != 0) {
+ free(copy);
+ free(*object_namep);
+ return (NWAM_INVALID_ARG);
+ }
+ } else {
+ switch (*conditionp) {
+ case NWAM_CONDITION_CONTAINS:
+ case NWAM_CONDITION_DOES_NOT_CONTAIN:
+ if (*object_typep !=
+ NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN &&
+ *object_typep !=
+ NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN &&
+ *object_typep != NWAM_CONDITION_OBJECT_TYPE_ESSID) {
+ free(copy);
+ free(*object_namep);
+ return (NWAM_INVALID_ARG);
+ }
+ break;
+ case NWAM_CONDITION_IS_IN_RANGE:
+ case NWAM_CONDITION_IS_NOT_IN_RANGE:
+ if (*object_typep !=
+ NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS) {
+ free(copy);
+ free(*object_namep);
+ return (NWAM_INVALID_ARG);
+ }
+ break;
+ }
+
+ if ((object_name = strtok_r(NULL, " \t", &lasts)) == NULL) {
+ free(copy);
+ free(*object_namep);
+ return (NWAM_INVALID_ARG);
+ }
+ if ((*object_namep = strdup(object_name)) == NULL) {
+ free(copy);
+ free(*object_namep);
+ return (NWAM_NO_MEMORY);
+ }
+ }
+
+ free(copy);
+ return (NWAM_SUCCESS);
+}
+
+nwam_error_t
+nwam_condition_rate(nwam_condition_object_type_t object_type,
+ nwam_condition_t condition, uint64_t *ratep)
+{
+ assert(ratep != NULL);
+
+ *ratep = 0;
+
+ switch (object_type) {
+ case NWAM_CONDITION_OBJECT_TYPE_NCP:
+ case NWAM_CONDITION_OBJECT_TYPE_NCU:
+ case NWAM_CONDITION_OBJECT_TYPE_ENM:
+ case NWAM_CONDITION_OBJECT_TYPE_LOC:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_OBJECT_TYPE_SYS_DOMAIN:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_OBJECT_TYPE_ADV_DOMAIN:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_OBJECT_TYPE_IP_ADDRESS:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_OBJECT_TYPE_BSSID:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_OBJECT_TYPE_ESSID:
+ (*ratep)++;
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+ }
+
+ switch (condition) {
+ case NWAM_CONDITION_IS:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_CONTAINS:
+ case NWAM_CONDITION_IS_IN_RANGE:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_DOES_NOT_CONTAIN:
+ case NWAM_CONDITION_IS_NOT_IN_RANGE:
+ (*ratep)++;
+ /* FALLTHRU */
+ case NWAM_CONDITION_IS_NOT:
+ (*ratep)++;
+ break;
+ default:
+ return (NWAM_INVALID_ARG);
+ }
+ return (NWAM_SUCCESS);
+}
diff --git a/usr/src/lib/libnwam/common/libnwam_wlan.c b/usr/src/lib/libnwam/common/libnwam_wlan.c
new file mode 100644
index 0000000000..6853614d16
--- /dev/null
+++ b/usr/src/lib/libnwam/common/libnwam_wlan.c
@@ -0,0 +1,76 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include "libnwam_impl.h"
+#include <libnwam.h>
+
+/*
+ * Functions that request WLAN-specific actions (scan, WLAN selection
+ * and key setting).
+ */
+
+/*
+ * Launch scan on specified link linkname.
+ */
+nwam_error_t
+nwam_wlan_scan(const char *linkname)
+{
+ return (nwam_request_wlan(NWAM_REQUEST_TYPE_WLAN_SCAN, linkname,
+ NULL, NULL, 0, 0, NULL, B_FALSE));
+}
+
+/*
+ * Get most-recently-cached scan results for link linkname.
+ */
+nwam_error_t
+nwam_wlan_get_scan_results(const char *linkname, uint_t *num_wlansp,
+ nwam_wlan_t **wlansp)
+{
+ return (nwam_request_wlan_scan_results(linkname, num_wlansp,
+ wlansp));
+}
+
+/*
+ * Select specified WLAN <essid, bssid> for link linkname.
+ */
+nwam_error_t
+nwam_wlan_select(const char *linkname, const char *essid, const char *bssid,
+ uint32_t secmode, boolean_t add_to_known_wlans)
+{
+ return (nwam_request_wlan(NWAM_REQUEST_TYPE_WLAN_SELECT,
+ linkname, essid, bssid, secmode, 0, NULL, add_to_known_wlans));
+}
+
+/*
+ * Create/update security key for WLAN <essid, bssid>.
+ */
+nwam_error_t
+nwam_wlan_set_key(const char *linkname, const char *essid, const char *bssid,
+ uint32_t secmode, uint_t keyslot, const char *key)
+{
+ return (nwam_request_wlan(NWAM_REQUEST_TYPE_WLAN_SET_KEY,
+ linkname, essid, bssid, secmode, keyslot, key, B_FALSE));
+}
diff --git a/usr/src/lib/libnwam/common/llib-lnwam b/usr/src/lib/libnwam/common/llib-lnwam
index 396526a369..96cc76df3e 100644
--- a/usr/src/lib/libnwam/common/llib-lnwam
+++ b/usr/src/lib/libnwam/common/llib-lnwam
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -27,3 +27,4 @@
/* PROTOLIB1 */
#include <libnwam.h>
+#include <libnwam_priv.h>
diff --git a/usr/src/lib/libnwam/common/mapfile-vers b/usr/src/lib/libnwam/common/mapfile-vers
index 97abf7c5fe..02f9e11448 100644
--- a/usr/src/lib/libnwam/common/mapfile-vers
+++ b/usr/src/lib/libnwam/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -39,24 +39,169 @@
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_wlan_key_secmode;
- libnwam_start_rescan;
- libnwam_fini;
- libnwam_init;
+ nwam_strerror;
+ nwam_value_create_boolean;
+ nwam_value_create_boolean_array;
+ nwam_value_create_int64_array;
+ nwam_value_create_int64;
+ nwam_value_create_uint64_array;
+ nwam_value_create_uint64;
+ nwam_value_create_string;
+ nwam_value_create_string_array;
+ nwam_value_get_boolean;
+ nwam_value_get_boolean_array;
+ nwam_value_get_int64;
+ nwam_value_get_int64_array;
+ nwam_value_get_uint64;
+ nwam_value_get_uint64_array;
+ nwam_value_get_string;
+ nwam_value_get_string_array;
+ nwam_value_get_type;
+ nwam_value_get_numvalues;
+ nwam_value_free;
+ nwam_value_copy;
+ nwam_uint64_get_value_string;
+ nwam_value_string_get_uint64;
+ nwam_condition_to_condition_string;
+ nwam_condition_string_to_condition;
+ nwam_condition_rate;
+ nwam_loc_create;
+ nwam_loc_copy;
+ nwam_loc_read;
+ nwam_loc_commit;
+ nwam_loc_destroy;
+ nwam_loc_free;
+ nwam_loc_validate;
+ nwam_walk_locs;
+ nwam_loc_get_name;
+ nwam_loc_set_name;
+ nwam_loc_can_set_name;
+ nwam_loc_enable;
+ nwam_loc_disable;
+ nwam_loc_walk_props;
+ nwam_loc_delete_prop;
+ nwam_loc_get_prop_value;
+ nwam_loc_set_prop_value;
+ nwam_loc_validate_prop;
+ nwam_loc_get_prop_type;
+ nwam_loc_prop_multivalued;
+ nwam_loc_get_prop_description;
+ nwam_loc_get_default_proplist;
+ nwam_loc_get_state;
+ nwam_loc_prop_read_only;
+ nwam_loc_is_manual;
+ nwam_ncp_create;
+ nwam_ncp_read;
+ nwam_ncp_copy;
+ nwam_walk_ncps;
+ nwam_ncp_get_name;
+ nwam_ncp_get_read_only;
+ nwam_ncp_destroy;
+ nwam_ncp_walk_ncus;
+ nwam_ncp_enable;
+ nwam_ncp_free;
+ nwam_ncp_get_state;
+ nwam_ncp_get_active_priority_group;
+ nwam_ncu_create;
+ nwam_ncu_read;
+ nwam_ncu_commit;
+ nwam_ncu_destroy;
+ nwam_ncu_free;
+ nwam_ncu_validate;
+ nwam_ncu_get_name;
+ nwam_ncu_name_to_typed_name;
+ nwam_ncu_typed_name_to_name;
+ nwam_ncu_get_ncp;
+ nwam_ncu_enable;
+ nwam_ncu_disable;
+ nwam_ncu_get_read_only;
+ nwam_ncu_get_default_proplist;
+ nwam_ncu_delete_prop;
+ nwam_ncu_get_prop_value;
+ nwam_ncu_set_prop_value;
+ nwam_ncu_validate_prop;
+ nwam_ncu_get_default_proplist;
+ nwam_ncu_walk_props;
+ nwam_ncu_get_prop_type;
+ nwam_ncu_get_prop_description;
+ nwam_ncu_prop_read_only;
+ nwam_ncu_prop_multivalued;
+ nwam_ncu_copy;
+ nwam_ncu_get_state;
+ nwam_ncu_get_ncu_type;
+ nwam_ncu_get_ncu_class;
+ nwam_ncu_is_manual;
+ nwam_ncu_class_to_flag;
+ nwam_ncu_class_to_type;
+ nwam_enm_create;
+ nwam_enm_read;
+ nwam_walk_enms;
+ nwam_enm_commit;
+ nwam_enm_destroy;
+ nwam_enm_free;
+ nwam_enm_validate;
+ nwam_enm_validate_prop;
+ nwam_enm_get_prop_type;
+ nwam_enm_prop_multivalued;
+ nwam_enm_get_prop_description;
+ nwam_enm_delete_prop;
+ nwam_enm_get_prop_value;
+ nwam_enm_set_prop_value;
+ nwam_enm_prop_read_only;
+ nwam_enm_walk_props;
+ nwam_enm_get_name;
+ nwam_enm_set_name;
+ nwam_enm_can_set_name;
+ nwam_enm_get_default_proplist;
+ nwam_enm_enable;
+ nwam_enm_disable;
+ nwam_enm_copy;
+ nwam_enm_get_state;
+ nwam_enm_is_manual;
+ nwam_known_wlan_create;
+ nwam_known_wlan_read;
+ nwam_known_wlan_destroy;
+ nwam_known_wlan_free;
+ nwam_known_wlan_copy;
+ nwam_known_wlan_commit;
+ nwam_known_wlan_validate;
+ nwam_walk_known_wlans;
+ nwam_known_wlan_get_name;
+ nwam_known_wlan_set_name;
+ nwam_known_wlan_can_set_name;
+ nwam_known_wlan_walk_props;
+ nwam_known_wlan_delete_prop;
+ nwam_known_wlan_get_prop_value;
+ nwam_known_wlan_set_prop_value;
+ nwam_known_wlan_validate_prop;
+ nwam_known_wlan_get_prop_type;
+ nwam_known_wlan_prop_multivalued;
+ nwam_known_wlan_get_prop_description;
+ nwam_known_wlan_get_default_proplist;
+ nwam_known_wlan_add_to_known_wlans;
+ nwam_known_wlan_remove_from_known_wlans;
+ nwam_wlan_scan;
+ nwam_wlan_get_scan_results;
+ nwam_wlan_select;
+ nwam_wlan_set_key;
+ nwam_events_init;
+ nwam_events_fini;
+ nwam_event_wait;
+ nwam_event_free;
+ nwam_action_to_string;
+ nwam_event_type_to_string;
+ nwam_state_to_string;
+ nwam_aux_state_to_string;
+ nwam_object_type_to_string;
+ nwam_string_to_object_type;
+ nwam_backend_init;
+ nwam_backend_fini;
+ nwam_record_audit_event;
+ nwam_event_send;
+ nwam_event_send_fini;
+ nwam_event_queue_init;
+ nwam_event_queue_fini;
+ nwam_tokenize_by_unescaped_delim;
local:
*;
};
diff --git a/usr/src/lib/libnwam/i386/Makefile b/usr/src/lib/libnwam/i386/Makefile
index 1d2aa5ae17..8069eadad1 100644
--- a/usr/src/lib/libnwam/i386/Makefile
+++ b/usr/src/lib/libnwam/i386/Makefile
@@ -19,10 +19,10 @@
# CDDL HEADER END
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# lib/libnwam/i386/Makefile
+#
include ../Makefile.com
diff --git a/usr/src/lib/libnwam/libnwam.xcl b/usr/src/lib/libnwam/libnwam.xcl
new file mode 100644
index 0000000000..29d094efa8
--- /dev/null
+++ b/usr/src/lib/libnwam/libnwam.xcl
@@ -0,0 +1,200 @@
+#
+# 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 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+msgid "%c"
+msgid "%c%lld"
+msgid "%c%s"
+msgid " %s"
+msgid "%s %s %s active"
+msgid "%s %s %s"
+msgid "%s"
+msgid "%s%c"
+msgid "%s%s"
+msgid "%s%s%s%s"
+msgid "%s.%d"
+msgid "%s.new"
+msgid "%s/%s"
+msgid "%s\t"
+msgid "\n"
+msgid "DNS"
+msgid "ENM"
+msgid "FMRI"
+msgid "IF_ACTION"
+msgid "IF_STATE"
+msgid "INFO"
+msgid "INIT"
+msgid "IP"
+msgid "IPsec"
+msgid "IPv4"
+msgid "IPv6"
+msgid "LDAP"
+msgid "LINK_ACTION"
+msgid "LINK_STATE"
+msgid "LOC"
+msgid "MAC"
+msgid "MTU"
+msgid "NCP"
+msgid "NCU"
+msgid "NFSv4"
+msgid "NIS"
+msgid "NOOP"
+msgid "OBJECT_ACTION"
+msgid "OBJECT_STATE"
+msgid "PRIORITY_GROUP"
+msgid "SHUTDOWN"
+msgid "SMF"
+msgid "UNKNOWN"
+msgid "WLAN_CONNECTION_REPORT"
+msgid "WLAN_NEED_CHOICE"
+msgid "WLAN_NEED_KEY"
+msgid "WLAN_SCAN_REPORT"
+msgid "Automatic"
+msgid "Legacy"
+msgid "NoNet"
+msgid "User"
+msgid "aa:bb:cc:dd:ee:ff"
+msgid "activation-mode"
+msgid "active"
+msgid "add"
+msgid "advertised-domain"
+msgid "all"
+msgid "autoconf"
+msgid "autopush"
+msgid "boolean"
+msgid "bssid"
+msgid "bssids"
+msgid "class"
+msgid "conditional-all"
+msgid "conditional-any"
+msgid "conditions"
+msgid "contains"
+msgid "default-domain"
+msgid "degraded"
+msgid "destroy"
+msgid "dhcp"
+msgid "disable"
+msgid "disabled"
+msgid "dns"
+msgid "dns-nameservice-configsrc"
+msgid "dns-nameservice-domain"
+msgid "dns-nameservice-search"
+msgid "dns-nameservice-servers"
+msgid "does-not-contain"
+msgid "domainname"
+msgid "enable"
+msgid "enabled"
+msgid "enm"
+msgid "essid"
+msgid "exclusive"
+msgid "false"
+msgid "files"
+msgid "fmri"
+msgid "ike"
+msgid "ike-config-file"
+msgid "ike.config"
+msgid "initialized"
+msgid "int64"
+msgid "interface"
+msgid "interface:"
+msgid "ip"
+msgid "ip-address"
+msgid "ip-version"
+msgid "ipf.conf"
+msgid "ipf6.conf"
+msgid "ipfilter-config-file"
+msgid "ipfilter-v6-config-file"
+msgid "ipnat-config-file"
+msgid "ipnat.conf"
+msgid "ipool.conf"
+msgid "ippool-config-file"
+msgid "ipsecconf"
+msgid "ipsecpolicy-config-file"
+msgid "ipv4"
+msgid "ipv4-addr"
+msgid "ipv4-addrsrc"
+msgid "ipv4-default-route"
+msgid "ipv6"
+msgid "ipv6-addr"
+msgid "ipv6-addrsrc"
+msgid "ipv6-default-route"
+msgid "is"
+msgid "is-in-range"
+msgid "is-not"
+msgid "is-not-in-range"
+msgid "keyname"
+msgid "keyslot"
+msgid "known WLAN"
+msgid "known wlan"
+msgid "ldap"
+msgid "ldap-nameservice-configsrc"
+msgid "ldap-nameservice-servers"
+msgid "link"
+msgid "link-autopush"
+msgid "link-mac-addr"
+msgid "link-mtu"
+msgid "link:"
+msgid "loc"
+msgid "maintenance"
+msgid "manual"
+msgid "nameservices"
+msgid "nameservices-config-file"
+msgid "ncp"
+msgid "ncu"
+msgid "nfsv4-domain"
+msgid "nis"
+msgid "nis-nameservice-configsrc"
+msgid "nis-nameservice-servers"
+msgid "none"
+msgid "nsswitch.conf"
+msgid "nwam_event_send: ftok: %s"
+msgid "nwam_event_send: msgctl: %s"
+msgid "nwam_event_send: msgget: %s"
+msgid "nwam_event_send: msgsnd: %s, "
+msgid "offline"
+msgid "offline*"
+msgid "online"
+msgid "online*"
+msgid "parent"
+msgid "phys"
+msgid "prioritized"
+msgid "priority"
+msgid "priority-group"
+msgid "priority-mode"
+msgid "refresh"
+msgid "remove"
+msgid "security-mode"
+msgid "shared"
+msgid "start"
+msgid "static"
+msgid "stop"
+msgid "string"
+msgid "system"
+msgid "system-domain"
+msgid "true"
+msgid "type"
+msgid "uint64"
+msgid "uninitialized"
+msgid "unknown"
+msgid "wep"
+msgid "wpa"
diff --git a/usr/src/lib/libnwam/sparc/Makefile b/usr/src/lib/libnwam/sparc/Makefile
index 93ab0970d2..743c659a64 100644
--- a/usr/src/lib/libnwam/sparc/Makefile
+++ b/usr/src/lib/libnwam/sparc/Makefile
@@ -19,10 +19,9 @@
# CDDL HEADER END
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# lib/libnwam/sparc/Makefile
include ../Makefile.com
diff --git a/usr/src/pkgdefs/SUNWnwamintu/Makefile b/usr/src/lib/libnwam/sparcv9/Makefile
index 0ff58fdf86..820bab63df 100644
--- a/usr/src/pkgdefs/SUNWnwamintu/Makefile
+++ b/usr/src/lib/libnwam/sparcv9/Makefile
@@ -18,18 +18,12 @@
#
# CDDL HEADER END
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
include ../Makefile.com
+include ../../Makefile.lib.64
-DATAFILES += depend
-
-.KEEP_STATE:
-
-all: $(FILES)
-
-install: all pkg
-
-include ../Makefile.targ
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index 0b2d1b5ff4..84dee31da3 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -91,7 +91,10 @@ 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.autoconf.read:::View Network Auto-Magic Config::help=NetworkAutoconfRead.html
+solaris.network.autoconf.select:::Enable/Disable Network Auto-Magic Config::help=NetworkAutoconfSelect.html
+solaris.network.autoconf.wlan:::Create Network Auto-Magic Config for Known WLANs::help=NetworkAutoconfWlan.html
+solaris.network.autoconf.write:::Create Network Auto-Magic Config::help=NetworkAutoconfWrite.html
solaris.network.ilb.config:::Network ILB Configuration::help=NetworkILBconf.html
solaris.network.ilb.enable:::Network ILB Enable Configuration::help=NetworkILBenable.html
solaris.network.link.security:::Link Security::help=LinkSecurity.html
@@ -141,6 +144,7 @@ solaris.smf.manage.ilb:::Manage Integrated Load Balancer Service States::help=Sm
solaris.smf.manage.inetd:::Manage inetd and inetd managed services States::help=SmfIntedStates.html
solaris.smf.manage.ipsec:::Manage IPsec Service States::help=SmfIPsecStates.html
solaris.smf.manage.labels:::Manage label server::help=LabelServer.html
+solaris.smf.manage.location:::Manage Network Location Service States::help=SmfLocationStates.html
solaris.smf.manage.mdns:::Manage Multicast DNS Service States::help=SmfMDNSStates.html
solaris.smf.manage.name-service-cache:::Manage Name Service Cache Daemon Service States::help=SmfNscdStates.html
solaris.smf.manage.nwam:::Manage Network Auto-Magic Service States::help=SmfNWAMStates.html
@@ -194,6 +198,8 @@ solaris.system.power.suspend.disk:::Suspend to Disk::help=SysPowerMgmtSuspendtoD
solaris.system.power.suspend.ram:::Suspend to RAM::help=SysPowerMgmtSuspendToRAM.html
solaris.system.power.brightness:::Control LCD Brightness::help=SysPowerMgmtBrightness.html
solaris.system.power.cpu:::Manage CPU related power::help=SysCpuPowerMgmt.html
+solaris.system.sysevent.read:::Retrieve Sysevents::help=SysSyseventRead.html
+solaris.system.sysevent.write:::Publish Sysevents::help=SysSyseventWrite.html
#
solaris.smf.manage.iscsitgt:::Manage ISCSI Target Service States::help=SmfValueIscsitgt.html
solaris.smf.read.iscsitgt:::Read ISCSI Target secrets::help=SmfValueIscsitgt.html
diff --git a/usr/src/lib/libsecdb/exec_attr.txt b/usr/src/lib/libsecdb/exec_attr.txt
index 465887d3d1..c3f3bd513e 100644
--- a/usr/src/lib/libsecdb/exec_attr.txt
+++ b/usr/src/lib/libsecdb/exec_attr.txt
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# /etc/security/exec_attr
@@ -170,9 +170,9 @@ Network Management:solaris:cmd:::/sbin/ifconfig:uid=0
Network Management:solaris:cmd:::/sbin/route:privs=sys_ip_config
Network Management:solaris:cmd:::/sbin/routeadm:euid=0;\
privs=proc_chroot,proc_owner,sys_ip_config
-Network Management:solaris:cmd:::/sbin/dladm:euid=dladm;egid=sys;\
+Network Management:solaris:cmd:::/sbin/dladm:euid=dladm;egid=netadm;\
privs=sys_dl_config,net_rawaccess,proc_audit
-Network Management:solaris:cmd:::/sbin/flowadm:euid=dladm;egid=sys;\
+Network Management:solaris:cmd:::/sbin/flowadm:euid=dladm;egid=netadm;\
privs=sys_dl_config,net_rawaccess,proc_audit
Network Management:suser:cmd:::/usr/bin/netstat:uid=0
Network Management:suser:cmd:::/usr/bin/rup:euid=0
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index 200740501d..b56ddefdd6 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -77,6 +77,7 @@ HTMLENTS = \
SmfILBStates.html \
SmfInetdStates.html \
SmfIPsecStates.html \
+ SmfLocationStates.html \
SmfManageHeader.html \
SmfManageHotplug.html \
SmfMDNSStates.html \
@@ -121,7 +122,10 @@ HTMLENTS = \
SmfValueVt.html \
SmfVRRPStates.html \
SmfWpaStates.html \
- NetworkAutoconf.html \
+ NetworkAutoconfRead.html \
+ NetworkAutoconfSelect.html \
+ NetworkAutoconfWlan.html \
+ NetworkAutoconfWrite.html \
NetworkILBconf.html \
NetworkILBenable.html \
NetworkHeader.html \
@@ -160,6 +164,8 @@ HTMLENTS = \
SysPowerMgmtSuspendtoRAM.html \
SysPowerMgmtBrightness.html \
SysCpuPowerMgmt.html \
+ SysSyseventRead.html \
+ SysSyseventWrite.html \
SmfManageZFSSnap.html \
MMSHeader.html \
AuthMMSDeviceLog.html \
diff --git a/usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfRead.html
index afdd492da9..c734350c34 100644
--- a/usr/src/lib/libsecdb/help/auths/NetworkAutoconf.html
+++ b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfRead.html
@@ -1,7 +1,7 @@
<html>
<!--
- Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
CDDL HEADER START
@@ -30,9 +30,8 @@ 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.
+When View Network Auto-Magic Config is in the Authorizations Included
+column, it grants permission to view the configuration repository used
+by the nwamd(1M) daemon to manage network configuration.
</body>
</html>
diff --git a/usr/src/lib/libsecdb/help/auths/NetworkAutoconfSelect.html b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfSelect.html
new file mode 100644
index 0000000000..966530c655
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfSelect.html
@@ -0,0 +1,37 @@
+<html>
+
+<!--
+ Copyright 2010 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 Enable/Disable Network Auto-Magic Config is in the Authorizations Included
+column, it grants permission to enable and disable profiles by nwamd(1M) via
+the nwam-manager GUI or the nwamadm(1M) command.
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/help/auths/NetworkAutoconfWlan.html b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfWlan.html
new file mode 100644
index 0000000000..ee9827885d
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfWlan.html
@@ -0,0 +1,38 @@
+<html>
+
+<!--
+ Copyright 2010 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 Create Network Auto-Magic Config for Known WLANs is in the Authorizations
+Included column, it grants permission to create or modify Known WLAN data in
+the configuration repository used by the nwamd(1M) daemon to manage network
+configuration.
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/help/auths/NetworkAutoconfWrite.html b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfWrite.html
new file mode 100644
index 0000000000..ae9b0fbde5
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/NetworkAutoconfWrite.html
@@ -0,0 +1,37 @@
+<html>
+
+<!--
+ Copyright 2010 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 Create Network Auto-Magic Config is in the Authorizations Included
+column, it grants permission to create or modify data in the configuration
+repository used by the nwamd(1M) daemon to manage network configuration.
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/help/auths/SmfLocationStates.html b/usr/src/lib/libsecdb/help/auths/SmfLocationStates.html
new file mode 100644
index 0000000000..c23288e488
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfLocationStates.html
@@ -0,0 +1,36 @@
+<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 2010 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
+-->
+<!--
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Manage Network Location Service States is in the Authorizations Included
+column, it grants the authorization to enable, disable, or restart
+Network Location (NWAM) services.
+<P>
+If Manage Network Location Service States is grayed, then you are not entitled
+to Add or Remove this authorization.
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/SysSyseventRead.html b/usr/src/lib/libsecdb/help/auths/SysSyseventRead.html
new file mode 100644
index 0000000000..b04f285f31
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SysSyseventRead.html
@@ -0,0 +1,39 @@
+<HTML>
+<!--
+ Copyright 2010 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"
+-->
+<!--
+META NAME="GENERATOR" CONTENT="Mozilla/4.02 [en] (X11; U; SunOS 5.6 sun4u) [Netscape]"
+-->
+</HEAD>
+<BODY>
+When Retrieve Sysevents is in the Authorizations Included column, it grants the authorization to get sysevents via libsysevent(3LIB).
+<p>
+If Retrieve Sysevents is grayed, then you are not entitled to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/SysSyseventWrite.html b/usr/src/lib/libsecdb/help/auths/SysSyseventWrite.html
new file mode 100644
index 0000000000..87cc24df58
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SysSyseventWrite.html
@@ -0,0 +1,39 @@
+<HTML>
+<!--
+ Copyright 2010 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"
+-->
+<!--
+META NAME="GENERATOR" CONTENT="Mozilla/4.02 [en] (X11; U; SunOS 5.6 sun4u) [Netscape]"
+-->
+</HEAD>
+<BODY>
+When Publish Sysevents is in the Authorizations Included column, it grants the authorization to post sysevents via libsysevent(3LIB).
+<p>
+If Publish Sysevents is grayed, then you are not entitled to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/profiles/Makefile b/usr/src/lib/libsecdb/help/profiles/Makefile
index 1b71e02237..72c109af90 100644
--- a/usr/src/lib/libsecdb/help/profiles/Makefile
+++ b/usr/src/lib/libsecdb/help/profiles/Makefile
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# lib/libsecdb/help/profiles/Makefile
@@ -59,7 +59,8 @@ HTMLENTS = \
RtNDMPMngmnt.html \
RtNameServiceAdmin.html \
RtNameServiceSecure.html \
- RtNetAutoconf.html \
+ RtNetAutoconfAdmin.html \
+ RtNetAutoconfUser.html \
RtNetILB.html \
RtNetIPsec.html \
RtNetMngmnt.html \
diff --git a/usr/src/lib/libsecdb/help/profiles/RtNetAutoconfAdmin.html b/usr/src/lib/libsecdb/help/profiles/RtNetAutoconfAdmin.html
new file mode 100644
index 0000000000..5825f2d74d
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/profiles/RtNetAutoconfAdmin.html
@@ -0,0 +1,38 @@
+<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 2010 Sun Microsystems, Inc. All rights reserved.
+-- Use is subject to license terms.
+-->
+<head>
+<title></title>
+</head>
+<body>
+When Network Autoconf Admin is in the Rights Included column, it grants the
+right to create and edit the data in the configuration repository (using the
+nwamcfg(1M) command or the nwam-manager GUI) and to apply the changes made to
+the configuration repository to the system by nwamd(1M) daemon (using the
+nwamadm(1M) command or the nwam-manager GUI).
+<p>
+If Network Autoconf Admin is grayed, then you are not entitled to Add or
+Remove this right.
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html b/usr/src/lib/libsecdb/help/profiles/RtNetAutoconfUser.html
index 28f5de6ccd..d8ad5cd1b3 100644
--- a/usr/src/lib/libsecdb/help/profiles/RtNetAutoconf.html
+++ b/usr/src/lib/libsecdb/help/profiles/RtNetAutoconfUser.html
@@ -19,19 +19,18 @@
CDDL HEADER END
--- Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+-- Copyright 2010 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.
+When Network Autoconf User is in the Rights Included column, it grants the
+right to enable and disable different profiles and select WiFi networks using
+the nwamadm(1M) command or the nwam-manager GUI.
<p>
-If Network Autoconf is grayed, then you are not entitled to Add or
+If Network Autoconf User 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 970f8d5cd4..b102f2bc40 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -32,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,Network Autoconf;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 User;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
@@ -61,10 +61,11 @@ 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.*;help=RtMMSOper.html
MMS User:::MMS Tape User:auths=solaris.mms.io.*;help=RtMMSUser.html
NDMP Management:::Manage the NDMP service:auths=solaris.smf.manage.ndmp,solaris.smf.value.ndmp,solaris.smf.read.ndmp;help=RtNdmpMngmnt.html
-Network Autoconf:::Manage network auto-magic configuration via nwamd:auths=solaris.network.autoconf;help=RtNetAutoconf.html
+Network Autoconf Admin:::Manage Network Auto-Magic configuration via nwamd:profiles=Network Autoconf User;auths=solaris.network.autoconf.write,solaris.smf.manage.location,solaris.smf.modify.application;help=RtNetAutoconfAdmin.html
+Network Autoconf User:::Network Auto-Magic User:auths=solaris.network.autoconf.read,solaris.network.autoconf.select,solaris.network.autoconf.wlan;help=RtNetAutoconfUser.html
Network ILB:::Manage ILB configuration via ilbadm:auths=solaris.network.ilb.config,solaris.network.ilb.enable;help=RtNetILB.html
Network VRRP:::Manage VRRP instances:auths=solaris.network.vrrp,solaris.smf.manage.vrrp;help=RtNetVRRP.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,solaris.smf.manage.ilb;profiles=Network Wifi Management,Inetd Management,Network Autoconf,Network VRRP,Network Observability;help=RtNetMngmnt.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns,solaris.smf.manage.ilb;profiles=Network Wifi Management,Inetd Management,Network VRRP,Network Observability;help=RtNetMngmnt.html
Network Observability:::Allow access to observability devices:privs=net_observability;help=RtNetObservability.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
diff --git a/usr/src/lib/libsecdb/user_attr.txt b/usr/src/lib/libsecdb/user_attr.txt
index f48558b72c..ac804537c9 100644
--- a/usr/src/lib/libsecdb/user_attr.txt
+++ b/usr/src/lib/libsecdb/user_attr.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# CDDL HEADER START
@@ -31,4 +31,6 @@ lp::::profiles=Printer Management
adm::::profiles=Log Management
dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify
daemon::::auths=solaris.smf.manage.ilb,solaris.smf.modify.application
+netadm::::type=role;project=default;profiles=Network Autoconf Admin,Network Management,Service Management
+netcfg::::type=role;project=default;profiles=Network Autoconf User;auths=solaris.network.autoconf.write
zfssnap::::type=role;auths=solaris.smf.manage.zfs-auto-snapshot;profiles=ZFS File System Management
diff --git a/usr/src/lib/libsysevent/libsysevent.c b/usr/src/lib/libsysevent/libsysevent.c
index 23cecfd7ad..7ad71b5f68 100644
--- a/usr/src/lib/libsysevent/libsysevent.c
+++ b/usr/src/lib/libsysevent/libsysevent.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -1835,7 +1835,6 @@ sysevent_open_channel(const char *channel)
while (getextmntent(fp, &m, sizeof (struct extmnttab)) == 0) {
if (strcmp(m.mnt_mountp, "/var/run") == 0 &&
strcmp(m.mnt_fstype, "tmpfs") == 0) {
- (void) fclose(fp);
var_run_mounted = 1;
break;
}
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index 78a73d5270..9593cca5fe 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -370,8 +370,6 @@ COMMON_SUBDIRS= \
SUNWnfscu \
SUNWnisr \
SUNWnisu \
- SUNWnwamintr \
- SUNWnwamintu \
SUNWonfmes \
SUNWonzfsr \
SUNWonzfs \
diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com
index 14e5556ebe..f149026b7a 100644
--- a/usr/src/pkgdefs/SUNW0on/prototype_com
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com
@@ -256,6 +256,7 @@ f none usr/lib/help/auths/locale/SmfModifyFramework.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyHeader.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyMethod.html 444 root bin
f none usr/lib/help/auths/locale/SmfIPsecStates.html 444 root bin
+f none usr/lib/help/auths/locale/SmfLocationStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfNscdStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfNADDStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfNWAMStates.html 444 root bin
@@ -291,7 +292,10 @@ 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/SmfValueVt.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/NetworkAutoconfRead.html 444 root bin
+f none usr/lib/help/auths/locale/NetworkAutoconfSelect.html 444 root bin
+f none usr/lib/help/auths/locale/NetworkAutoconfWlan.html 444 root bin
+f none usr/lib/help/auths/locale/NetworkAutoconfWrite.html 444 root bin
f none usr/lib/help/auths/locale/NetworkHeader.html 444 root bin
f none usr/lib/help/auths/locale/NetworkILBconf.html 444 root bin
f none usr/lib/help/auths/locale/NetworkILBenable.html 444 root bin
@@ -335,6 +339,8 @@ f none usr/lib/help/auths/locale/SysPowerMgmtSuspendtoDisk.html 444 root bin
f none usr/lib/help/auths/locale/SysPowerMgmtSuspendtoRAM.html 444 root bin
f none usr/lib/help/auths/locale/SysPowerMgmtBrightness.html 444 root bin
f none usr/lib/help/auths/locale/SysCpuPowerMgmt.html 444 root bin
+f none usr/lib/help/auths/locale/SysSyseventRead.html 444 root bin
+f none usr/lib/help/auths/locale/SysSyseventWrite.html 444 root bin
f none usr/lib/help/auths/locale/SmfManageZFSSnap.html 444 root bin
f none usr/lib/help/auths/locale/MMSHeader.html 0444 root bin
f none usr/lib/help/auths/locale/AuthMMSMedia.html 0444 root bin
@@ -378,7 +384,8 @@ f none usr/lib/help/profiles/locale/RtMediaCtlg.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/RtNetAutoconfAdmin.html 444 root bin
+f none usr/lib/help/profiles/locale/RtNetAutoconfUser.html 444 root bin
f none usr/lib/help/profiles/locale/RtNetILB.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
diff --git a/usr/src/pkgdefs/SUNWarcr/prototype_com b/usr/src/pkgdefs/SUNWarcr/prototype_com
index 114064c808..eb44cc2e2b 100644
--- a/usr/src/pkgdefs/SUNWarcr/prototype_com
+++ b/usr/src/pkgdefs/SUNWarcr/prototype_com
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -96,6 +96,8 @@ f none lib/llib-lnsl 644 root bin
f none lib/llib-lnsl.ln 644 root bin
f none lib/llib-lnvpair 644 root bin
f none lib/llib-lnvpair.ln 644 root bin
+f none lib/llib-lnwam 644 root bin
+f none lib/llib-lnwam.ln 644 root bin
f none lib/llib-lpam 644 root bin
f none lib/llib-lpam.ln 644 root bin
s none lib/llib-lposix4=./llib-lrt
diff --git a/usr/src/pkgdefs/SUNWcnetr/postinstall b/usr/src/pkgdefs/SUNWcnetr/postinstall
index 3d8ce9735e..d836853f5f 100644
--- a/usr/src/pkgdefs/SUNWcnetr/postinstall
+++ b/usr/src/pkgdefs/SUNWcnetr/postinstall
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -191,4 +191,15 @@ for file in `ls ${PKG_INSTALL_ROOT}/etc/inet/ike/crls/* \
chmod 644 $file
fi
done
+
+#
+# Change group and permissions of /etc/dladm/*.conf config files.
+# secobj.conf changes permissions and group. The rest only change the group.
+#
+DLADM_PATH="${PKG_INSTALL_ROOT}/etc/dladm"
+DLADM_FILES="${DLADM_PATH}/datalink.conf ${DLADM_PATH}/flowadm.conf \
+ ${DLADM_PATH}/flowprop.conf ${DLADM_PATH}/secobj.conf"
+chgrp netadm ${DLADM_FILES}
+chmod 660 ${DLADM_PATH}/secobj.conf
+
exit 0
diff --git a/usr/src/pkgdefs/SUNWcnetr/prototype_com b/usr/src/pkgdefs/SUNWcnetr/prototype_com
index 97037ca11e..14e6a04567 100644
--- a/usr/src/pkgdefs/SUNWcnetr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcnetr/prototype_com
@@ -19,10 +19,9 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 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.
@@ -48,11 +47,11 @@ i i.dhcpagent
# SUNWcnetr
#
d none etc 755 root sys
-d none etc/dladm 755 dladm sys
-e preserve etc/dladm/secobj.conf 600 dladm sys
-e preserve etc/dladm/datalink.conf 644 dladm sys
-e preserve etc/dladm/flowadm.conf 644 dladm sys
-e preserve etc/dladm/flowprop.conf 644 dladm sys
+d none etc/dladm 755 dladm netadm
+e preserve etc/dladm/secobj.conf 660 dladm netadm
+e preserve etc/dladm/datalink.conf 644 dladm netadm
+e preserve etc/dladm/flowadm.conf 644 dladm netadm
+e preserve etc/dladm/flowprop.conf 644 dladm netadm
d none etc/default 755 root sys
e dhcpagent etc/default/dhcpagent 644 root sys
e preserve etc/default/inetinit 644 root sys
@@ -71,6 +70,13 @@ e preserve etc/inet/secret/ike.preshared 600 root sys
d none etc/inet/secret/ike.privatekeys 700 root sys
f none etc/inet/secret/ipseckeys.sample 600 root sys
e sock2path etc/inet/sock2path 644 root sys
+d none etc/nwam 755 netadm netadm
+d none etc/nwam/loc 755 netadm netadm
+e preserve etc/nwam/loc/create_loc_auto 644 netadm netadm
+e preserve etc/nwam/loc/create_loc_nonet 644 netadm netadm
+d none etc/nwam/loc/NoNet 755 netadm netadm
+e preserve etc/nwam/loc/NoNet/ipf.conf.dfl 644 netadm netadm
+e preserve etc/nwam/loc/NoNet/ipf6.conf.dfl 644 netadm netadm
s none etc/sock2path=./inet/sock2path
d none sbin 755 root sys
f none sbin/dladm 555 root bin
diff --git a/usr/src/pkgdefs/SUNWcslr/prototype_com b/usr/src/pkgdefs/SUNWcslr/prototype_com
index 269e743733..a5978ffe2f 100644
--- a/usr/src/pkgdefs/SUNWcslr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcslr/prototype_com
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# This required package information file contains a list of package contents.
@@ -117,6 +117,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
+s none lib/libnwam.so=libnwam.so.1
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
diff --git a/usr/src/pkgdefs/SUNWcsr/prototype_com b/usr/src/pkgdefs/SUNWcsr/prototype_com
index cc4f2606d2..e47cbc013b 100644
--- a/usr/src/pkgdefs/SUNWcsr/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsr/prototype_com
@@ -18,7 +18,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# This required package information file contains a list of package contents.
@@ -331,6 +331,7 @@ d none lib/crypto 755 root bin
f none lib/crypto/kcfd 555 root bin
d none lib/inet 755 root bin
f none lib/inet/in.mpathd 555 root bin
+f none lib/inet/netcfgd 555 root bin
f none lib/inet/nwamd 555 root bin
d none lib/svc 0755 root bin
d none lib/svc/bin 0755 root bin
@@ -361,8 +362,11 @@ f none lib/svc/method/ldap-client 0555 root bin
f none lib/svc/method/manifest-import 0555 root bin
f none lib/svc/method/mpxio-upgrade 0555 root bin
f none lib/svc/method/net-init 0555 root bin
+f none lib/svc/method/net-ipqos 0555 root bin
f none lib/svc/method/net-iptun 0555 root bin
+f none lib/svc/method/net-loc 0555 root bin
f none lib/svc/method/net-loopback 0555 root bin
+f none lib/svc/method/net-netmask 0555 root bin
f none lib/svc/method/net-nwam 0555 root bin
f none lib/svc/method/net-physical 0555 root bin
f none lib/svc/method/net-routing-setup 0555 root bin
@@ -508,8 +512,12 @@ f manifest var/svc/manifest/network/inetd.xml 0444 root sys
f manifest var/svc/manifest/network/inetd-upgrade.xml 0444 root sys
f seedmanifest var/svc/manifest/network/dlmgmt.xml 0444 root sys
f manifest var/svc/manifest/network/network-initial.xml 0444 root sys
+f manifest var/svc/manifest/network/network-ipqos.xml 0444 root sys
f manifest var/svc/manifest/network/network-iptun.xml 0444 root sys
+f manifest var/svc/manifest/network/network-location.xml 0444 root sys
f manifest var/svc/manifest/network/network-loopback.xml 0444 root sys
+f manifest var/svc/manifest/network/network-netcfg.xml 0444 root sys
+f manifest var/svc/manifest/network/network-netmask.xml 0444 root sys
f manifest var/svc/manifest/network/network-physical.xml 0444 root sys
f manifest var/svc/manifest/network/network-routing-setup.xml 0444 root sys
f manifest var/svc/manifest/network/network-service.xml 0444 root sys
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index ac0c0e9038..1df0d63ce0 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -485,7 +485,10 @@ 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/NetworkAutoconfRead.html 444 root bin
+f none usr/lib/help/auths/locale/C/NetworkAutoconfSelect.html 444 root bin
+f none usr/lib/help/auths/locale/C/NetworkAutoconfWlan.html 444 root bin
+f none usr/lib/help/auths/locale/C/NetworkAutoconfWrite.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/NetworkILBconf.html 444 root bin
f none usr/lib/help/auths/locale/C/NetworkILBenable.html 444 root bin
@@ -512,6 +515,7 @@ f none usr/lib/help/auths/locale/C/SmfModifyMethod.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfILBStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfInetdStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfIPsecStates.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfLocationStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfNscdStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfNADDStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfNWAMStates.html 444 root bin
@@ -564,6 +568,8 @@ f none usr/lib/help/auths/locale/C/SysPowerMgmtSuspendtoDisk.html 0444 root bin
f none usr/lib/help/auths/locale/C/SysPowerMgmtSuspendtoRAM.html 0444 root bin
f none usr/lib/help/auths/locale/C/SysPowerMgmtBrightness.html 0444 root bin
f none usr/lib/help/auths/locale/C/SysCpuPowerMgmt.html 0444 root bin
+f none usr/lib/help/auths/locale/C/SysSyseventRead.html 0444 root bin
+f none usr/lib/help/auths/locale/C/SysSyseventWrite.html 0444 root bin
f none usr/lib/help/auths/locale/C/SmfManageZFSSnap.html 0444 root bin
f none usr/lib/help/auths/locale/C/MMSHeader.html 0444 root bin
f none usr/lib/help/auths/locale/C/AuthMMSMedia.html 0444 root bin
@@ -611,7 +617,8 @@ f none usr/lib/help/profiles/locale/C/RtMediaCtlg.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/RtNetAutoconfAdmin.html 444 root bin
+f none usr/lib/help/profiles/locale/C/RtNetAutoconfUser.html 444 root bin
f none usr/lib/help/profiles/locale/C/RtNetILB.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
@@ -924,6 +931,8 @@ f none usr/sbin/netservices 0555 root sys
s none usr/sbin/newfs=../lib/fs/ufs/newfs
f none usr/sbin/nlsadmin 755 root adm
f none usr/sbin/nscd 555 root bin
+f none usr/sbin/nwamadm 555 root bin
+f none usr/sbin/nwamcfg 555 root bin
f none usr/sbin/passmgmt 555 root sys
l none usr/sbin/pbind=../../usr/lib/isaexec
f none usr/sbin/pmadm 555 root sys
diff --git a/usr/src/pkgdefs/SUNWhea/prototype_com b/usr/src/pkgdefs/SUNWhea/prototype_com
index ff5fb54178..9030f1bf51 100644
--- a/usr/src/pkgdefs/SUNWhea/prototype_com
+++ b/usr/src/pkgdefs/SUNWhea/prototype_com
@@ -312,6 +312,7 @@ f none usr/include/libgen.h 644 root bin
f none usr/include/libgrubmgmt.h 644 root bin
f none usr/include/libintl.h 644 root bin
f none usr/include/libipmi.h 644 root bin
+f none usr/include/libnwam.h 644 root bin
f none usr/include/libnvpair.h 644 root bin
f none usr/include/libipp.h 644 root bin
d none usr/include/libpolkit 755 root bin
diff --git a/usr/src/pkgdefs/SUNWnwamintr/prototype_com b/usr/src/pkgdefs/SUNWnwamintr/prototype_com
deleted file mode 100644
index 835cbb48cd..0000000000
--- a/usr/src/pkgdefs/SUNWnwamintr/prototype_com
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 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
deleted file mode 100644
index 6f85cc6c1b..0000000000
--- a/usr/src/pkgdefs/SUNWnwamintr/prototype_i386
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 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
deleted file mode 100644
index 91200ec6a1..0000000000
--- a/usr/src/pkgdefs/SUNWnwamintr/prototype_sparc
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 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/prototype_com b/usr/src/pkgdefs/SUNWnwamintu/prototype_com
deleted file mode 100644
index 051cd0f76e..0000000000
--- a/usr/src/pkgdefs/SUNWnwamintu/prototype_com
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 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
deleted file mode 100644
index d3632da695..0000000000
--- a/usr/src/pkgdefs/SUNWnwamintu/prototype_i386
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 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
deleted file mode 100644
index 7f3f9c31b7..0000000000
--- a/usr/src/pkgdefs/SUNWnwamintu/prototype_sparc
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 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
-#
diff --git a/usr/src/pkgdefs/common_files/i.ftpusers b/usr/src/pkgdefs/common_files/i.ftpusers
index c164b77657..5787c03fc6 100644
--- a/usr/src/pkgdefs/common_files/i.ftpusers
+++ b/usr/src/pkgdefs/common_files/i.ftpusers
@@ -19,7 +19,7 @@
#
# CDDL HEADER END
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# This is similar to i.preserve, except we also check if there is a file with
@@ -59,7 +59,7 @@ while read src dest; do
fi
fi
fi
- for user in dladm smmsp gdm webservd mysql openldap xvm
+ for user in dladm netadm netcfg smmsp gdm webservd mysql openldap xvm
do
egrep "^$user$|^#[ ]*$user$" $dest >/dev/null 2>&1 || \
echo $user >> $dest
diff --git a/usr/src/pkgdefs/common_files/i.group b/usr/src/pkgdefs/common_files/i.group
index ac4e709ce4..3c82ec67b6 100644
--- a/usr/src/pkgdefs/common_files/i.group
+++ b/usr/src/pkgdefs/common_files/i.group
@@ -313,6 +313,23 @@ do
'"$UNKNOWNGROUP_LINE"'' $dest > $TEMPF
mv -f $TEMPF $dest
fi
+ # Add the 'netadm' group if it doesn't already exist.
+ #
+ NETADMGROUP_LINE="netadm::65:"
+ cur_name=`awk -F: '$3 == 65 {print $1}' $dest`
+ cur_id=`awk -F: '$1 == "netadm" {print $3}' $dest`
+ if [ ! -z "$cur_name" -a "$cur_name" != "netadm" ]; then
+ echo "ERROR: Reserved GID 65 already assigned" \
+ "to '$cur_name'" >> /tmp/CLEANUP
+ elif [ ! -z "$cur_id" -a "$cur_id" != "65" ]; then
+ echo "NOTE: netadm group already assigned" \
+ "to id '$cur_id'" >> /tmp/CLEANUP
+ elif grep "$NETADMGROUP_LINE" $dest 2>&1 >/dev/null; then
+ :
+ else
+ printf '/^xvm::60:\na\n%s\n.\nw\nq\n' \
+ "$NETADMGROUP_LINE" | ed -s $dest > /dev/null
+ fi
fi
done
exit 0
diff --git a/usr/src/pkgdefs/common_files/i.passwd b/usr/src/pkgdefs/common_files/i.passwd
index e0a021a5d0..42a0a18204 100644
--- a/usr/src/pkgdefs/common_files/i.passwd
+++ b/usr/src/pkgdefs/common_files/i.passwd
@@ -180,13 +180,17 @@ do
#
# Add the 'dladm' user if it doesn't exist.
#
- DLADM_LIN="dladm:x:15:3:Datalink Admin:/:"
+ DLADM_LIN="dladm:x:15:65:Datalink Admin:/:"
+ OLD_DLADM_LIN="dladm:x:15:3:Datalink Admin:/:"
cur_name=`awk -F: '$3 == 15 { print $1 }' $dest`
if [ ! -z "$cur_name" -a "$cur_name" != "dladm" ]; then
echo "ERROR: Reserved UID 15 already assigned" \
"to '$cur_name'" >> /tmp/CLEANUP
elif grep "$DLADM_LIN" $dest > /dev/null 2>&1; then
:
+ elif grep "$OLD_DLADM_LIN" $dest > /dev/null 2>&1; then
+ sed '/^dladm:/s/:3:/:65:/' $dest > $TEMPF
+ mv -f $TEMPF $dest
else
sed '/^nuucp:x/ a\
'"$DLADM_LIN"'' $dest > $TEMPF
@@ -291,6 +295,34 @@ do
'"$UNKNOWN_LIN"'' $dest > $TEMPF
mv -f $TEMPF $dest
fi
+ # Add the 'netadm' user if it doesn't exist.
+ #
+ NETADM_LIN="netadm:x:16:65:Network Admin:/:"
+ cur_name=`awk -F: '$3 == 16 { print $1 }' $dest`
+ if [ ! -z "$cur_name" -a "$cur_name" != "netadm" ]; then
+ echo "ERROR: Reserved UID 16 already assigned" \
+ "to '$cur_name'" >> /tmp/CLEANUP
+ elif grep "$NETADM_LIN" $dest 2>&1 >/dev/null; then
+ :
+ else
+ printf '/^dladm:x\na\n%s\n.\nw\nq\n' \
+ "$NETADM_LIN" | ed -s $dest > /dev/null
+ fi
+
+ #
+ # Add the 'netcfg' user if it doesn't exist.
+ #
+ NETCFG_LIN="netcfg:x:17:65:Network Configuration Admin:/:"
+ cur_name=`awk -F: '$3 == 17 { print $1 }' $dest`
+ if [ ! -z "$cur_name" -a "$cur_name" != "netcfg" ]; then
+ echo "ERROR: Reserved UID 17 already assigned" \
+ "to '$cur_name'" >> /tmp/CLEANUP
+ elif grep "$NETCFG_LIN" $dest 2>&1 >/dev/null; then
+ :
+ else
+ printf '/^netadm:x\na\n%s\n.\nw\nq\n' \
+ "$NETCFG_LIN" | ed -s $dest > /dev/null
+ fi
fi
done
diff --git a/usr/src/pkgdefs/common_files/i.shadow b/usr/src/pkgdefs/common_files/i.shadow
index 2823880cef..c36dc803bf 100644
--- a/usr/src/pkgdefs/common_files/i.shadow
+++ b/usr/src/pkgdefs/common_files/i.shadow
@@ -211,6 +211,28 @@ do
fi
#
+ # Add the 'netadm' reserved user if it doesn't exist.
+ #
+ NETADM_LINE="netadm:*LK*:::::::"
+ if grep "^netadm:" $dest 2>&1 >/dev/null; then
+ :
+ else
+ printf '/^dladm:\*LK\*\na\n%s\n.\nw\nq\n' \
+ "$NETADM_LINE" | ed -s $dest > /dev/null
+ fi
+
+ #
+ # Add the 'netcfg' reserved user if it doesn't exist.
+ #
+ NETCFG_LINE="netcfg:*LK*:::::::"
+ if grep "^netcfg:" $dest 2>&1 >/dev/null; then
+ :
+ else
+ printf '/^netadm:\*LK\*\na\n%s\n.\nw\nq\n' \
+ "$NETCFG_LINE" | ed -s $dest > /dev/null
+ fi
+
+ #
# Warn the user if an empty password is found and
# PASSREQ is set to yes.
#
@@ -220,7 +242,6 @@ do
warn_nopass=0;
fi
fi
-
fi
done
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index a3e73359a3..ae0b9bc832 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -1271,3 +1271,6 @@ usr/lib/llib-lvrrpadm.ln i386
usr/lib/amd64/libvrrpadm.so i386
usr/lib/amd64/llib-lvrrpadm.ln i386
usr/lib/llib-lvrrpadm i386
+
+# Private/Internal NWAM header file. Do not ship.
+usr/include/libnwam_priv.h i386
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 034eb8701a..e796a604a0 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -1361,3 +1361,6 @@ usr/lib/llib-lvrrpadm.ln sparc
usr/lib/sparcv9/libvrrpadm.so sparc
usr/lib/sparcv9/llib-lvrrpadm.ln sparc
usr/lib/llib-lvrrpadm sparc
+
+# Private/Internal NWAM header file. Do not ship.
+usr/include/libnwam_priv.h sparc
diff --git a/usr/src/tools/pmodes/exceptions.h b/usr/src/tools/pmodes/exceptions.h
index 2ddc3a5de9..da3296fa95 100644
--- a/usr/src/tools/pmodes/exceptions.h
+++ b/usr/src/tools/pmodes/exceptions.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* $Id: exceptions.h,v 1.11 2000/01/13 14:12:58 casper Exp $
@@ -29,8 +29,6 @@
*
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
"/etc/dumpdates",
"/etc/lp",
"/var/mail/:saved",
@@ -59,6 +57,12 @@
/* CUPS */
"/var/cache/cups",
+ /*
+ * NWAM Phase 1 -- nwamd running as netadm:netadm must be able to
+ * modify secobj.conf, which has ownership dladm:netadm.
+ */
+ "/etc/dladm/secobj.conf",
+
/* another strange logfile */
"/usr/oasys/tmp/TERRLOG",
diff --git a/usr/src/tools/protocmp/stdusers.c b/usr/src/tools/protocmp/stdusers.c
index cd1ccbd4a2..5564062f1b 100644
--- a/usr/src/tools/protocmp/stdusers.c
+++ b/usr/src/tools/protocmp/stdusers.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -37,6 +37,8 @@ const struct stdlist usernames[] = {
{ "uucp", 5 },
{ "nuucp", 9 },
{ "dladm", 15 },
+ { "netadm", 16 },
+ { "netcfg", 17 },
{ "smmsp", 25 },
{ "listen", 37 },
{ "gdm", 50 },
@@ -68,6 +70,7 @@ const struct stdlist groupnames[] = {
{ "games", 20 },
{ "smmsp", 25 },
{ "gdm", 50 },
+ { "netadm", 65 },
{ "mysql", 70 },
{ "openldap", 75 },
{ "webservd", 80 },
diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh
index d888812679..66cbf3ddfc 100644
--- a/usr/src/tools/scripts/bfu.sh
+++ b/usr/src/tools/scripts/bfu.sh
@@ -1724,6 +1724,24 @@ smf_cleanup_dlmgmtd() {
)
}
+smf_cleanup_nwam() {
+(
+ #
+ # Delete NWAM phase 1-specific components, which comprise the
+ # netcfg, location and ipqos services and associated scripts/binaries.
+ #
+ smf_delete_manifest "var/svc/manifest/network/network-netcfg.xml"
+ smf_delete_manifest "var/svc/manifest/network/network-location.xml"
+ smf_delete_manifest "var/svc/manifest/network/network-ipqos.xml"
+ cd $root
+ rm -f lib/svc/method/net-loc
+ rm -f lib/inet/netcfgd
+ rm -f lib/svc/method/net-ipqos
+ rm -f usr/sbin/nwamcfg
+ rm -f usr/sbin/nwamadm
+)
+}
+
smf_cleanup_vt() {
(
smf_delete_manifest var/src/manifest/system/vtdaemon.xml
@@ -2071,6 +2089,9 @@ smf_apply_conf () {
smf_cleanup_dlmgmtd
fi
+ if [[ $nwam_status = cleanup ]]; then
+ smf_cleanup_nwam
+ fi
#
# When doing backwards BFU, if the target does not contain
# vtdaemon manifest, delete it and delete all the additional
@@ -2346,10 +2367,29 @@ EOFA
smf_import_service system/name-service-cache.xml
#
- # Import the datalink-management service.
+ # Import the datalink-management service. If datalink-managment
+ # service exists, there is no need to ensure that the service is
+ # enabled only after reboot. This enabling after reboot is
+ # achieved by passing the service FMRI as the second argument to
+ # smf_import_service().
+ #
+ /usr/bin/svcs svc:/network/datalink-management:default \
+ >/dev/null 2>/dev/null
+ if [ $? -eq 0 ]; then
+ smf_import_service network/dlmgmt.xml
+ else
+ smf_import_service network/dlmgmt.xml \
+ svc:/network/datalink-management:default
+ fi
+
+ #
+ # Import nwam service if backbfu'ing past NWAM phase 1. This
+ # is needed prior to reboot to ensure that the nwam service (if active)
+ # does not end up in maintenance state.
#
- smf_import_service network/dlmgmt.xml \
- svc:/network/datalink-management:default
+ if [[ $nwam_status = cleanup ]]; then
+ smf_import_service network/network-physical.xml
+ fi
#
# Import the ldap/client service. This is to get the service
@@ -2936,6 +2976,22 @@ if [[ -f $datalink_file ]]; then
fi
#
+# Check whether the archives have a netcfgd service; this is later used to
+# determine whether we need to remove the netcfg and location services as
+# part of a backbfu using a pre-NWAM phase 1 archive.
+#
+if archive_file_exists generic.lib "lib/inet/netcfgd"; then
+ netcfgd_exists=true
+else
+ netcfgd_exists=false
+fi
+
+nwam_status=none
+if [[ -f $root/lib/inet/netcfgd ]] && ! $netcfgd_exists ; then
+ nwam_status=cleanup
+fi
+
+#
# Check whether the build is boot-archive or ufsboot sparc
# boot based on the existence of a generic.boot archive
#
diff --git a/usr/src/uts/common/sys/param.h b/usr/src/uts/common/sys/param.h
index 82b971c808..0d541568ac 100644
--- a/usr/src/uts/common/sys/param.h
+++ b/usr/src/uts/common/sys/param.h
@@ -81,8 +81,9 @@ extern "C" {
#define GID_NOBODY UID_NOBODY
#define UID_UNKNOWN 96
#define GID_UNKNOWN UID_UNKNOWN
-#define GID_SYS 3
#define UID_DLADM 15
+#define UID_NETADM 16
+#define GID_NETADM 65
#define UID_NOACCESS 60002 /* user ID no access */
#ifdef _KERNEL