diff options
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> +</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> +</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 |
