3 The contents of this file are subject to the terms of the
4 Common Development and Distribution License (the "License").
5 You may not use this file except in compliance with the License.
7 You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
8 or http://www.opensolaris.org/os/licensing.
9 See the License for the specific language governing permissions
10 and limitations under the License.
12 When distributing Covered Code, include this CDDL HEADER in each
13 file and include the License file at usr/src/OPENSOLARIS.LICENSE.
14 If applicable, add the following below this CDDL HEADER, with the
15 fields enclosed by brackets "[]" replaced with your own identifying
16 information: Portions Copyright [yyyy] [name of copyright owner]
20 Copyright 2010 Sun Microsystems, Inc. All rights reserved.
21 Use is subject to license terms.
23 Implementation Overview for the NetWork AutoMagic daemon
24 John Beck, Renee Danson, Michael Hunter, Alan Maguire, Kacheong Poon,
25 Garima Tripathi, Jan Xie, Anurag Maskey
26 [Structure and some content shamelessly stolen from Peter Memishian's
27 dhcpagent architecture overview.]
32 Details about the NWAM requirements, architecture, and design are
33 available via the NWAM opensolaris project at
34 http://opensolaris.org/os/project/nwam. The point of this document is
35 to place details relevant to somebody attempting to understand the
36 implementation close to the source code.
41 SOURCE FILE ORGANIZATION
42 =======================
48 object-specific event handlers:
75 Here we discuss the essential objects and subtle aspects of the NWAM
76 daemon implementation. Note that there is of course much more that is
77 not discussed here, but after this overview you should be able to fend
78 for yourself in the source code.
83 Events come to NWAM from a variety of different sources asyncronously.
90 Routing sockets and dlpi (DL_NOTE_LINK_UP|DOWN events) are handled by
91 dedicated threads. Sysevents and doors are both seen as callbacks into
92 the process proper and will often post their results to the main event
93 queue. All event sources post events onto the main event queue. In
94 addition state changes of objects and door requests (requesting current
95 state or a change of state, specification of a WiFi key etc) can
96 lead to additional events. We have daemon-internal events (object
97 initialization, periodic state checks) which are simply enqueued
98 on the event queue, and external events which are both enqueued on
99 the event queue and sent to registered listeners (via nwam_event_send()).
101 So the structure of the daemon is a set of threads that drive event
102 generation. Events are posted either directly onto the event queue
103 or are delayed by posting onto the pending event queue. SIGALARMs
104 are set for the event delay, and when the SIGALARM is received
105 pending events that have expired are moved onto the event queue
106 proper. Delayed enqueueing is useful for periodic checks.
108 Decisions to change conditions based upon object state changes are
109 delayed until after bursts of events. This is achieved by marking a
110 flag when it is deemed checking is necessary and then the next time the
111 queue is empty performing those checks. A typical event profile will
112 be one event (e.g. a link down) causing a flurry of other events (e.g.
113 related interface down). By waiting until all the consequences of the
114 initial event have been carried out to make higher level decisions we
115 implicitly debounce those higher level decisions.
117 At the moment queue quiet actually means that the queue has been quiet
118 for some short period of time (.1s). Typically the flurry of events we
119 want to work through are internally generated and are back to back in
120 the queue. We wait a bit longer in case there are reprucussions from
121 what we do that cause external events to be posted on us. We are not
122 interested in waiting for longer term things to happen but merely to
123 catch immediate changes.
125 When running, the daemon will consist of a number of threads:
127 o the event handling thread: a thread blocking until events appear on the
128 event queue, processing each event in order. Events that require
129 time-consuming processing are spawned in worker threads (e.g. WiFi
130 connect, DHCP requests etc).
131 o door request threads: the door infrastructure manages server threads
132 which process synchronous NWAM client requests (e.g. get state of an
133 object, connect to a specific WLAN, initiate a scan on a link etc).
134 o various wifi/IP threads: threads which do asynchronous work such as
135 DHCP requests, WLAN scans etc that cannot hold up event processing in
136 the main event handling thread.
137 o routing socket threads: process routing socket messages of interest
138 (address additons/deletions) and package them as NWAM messages.
139 o dlpi threads: used to monitor for DL_NOTE_LINK messages on links
141 The daemon is structured around a set of objects representing NCPs[1],
142 NCUs[2], ENMs[3] and known WLANs and a set of state machines which
143 consume events which act on those objects. Object lists are maintained
144 for each object type, and these contain both a libnwam handle (to allow
145 reading the object directly) and an optional object data pointer which
146 can point to state information used to configure the object.
148 Events can be associated with specific objects (e.g. link up), or associated
149 with no object in particular (e.g. shutdown).
151 Each object type registers a set of event handler functions with the event
152 framework such that when an event occurs, the appropriate handler for the
153 object type is used. The event handlers are usually called
154 nwamd_handle_*_event().
156 [1] NCP Network Configuration Profile; the set of link- and IP-layer
157 configuration units which collectively specify how a system should be
158 connected to the network
160 [2] NCU Network Configuration Unit; the individual components of an NCP
162 [3] ENM External Network Modifiers; user executable scripts often used
165 Doors and External Events
166 =========================
168 The command interface to nwamd is thread a door at NWAM_DOOR
169 (/etc/svc/volatile/nwam/nwam_door). This door allows external program to send
170 messages to nwamd. The way doors work is to provide a mechanism for
171 another process to execute code in your process space. This looks like
172 a CSPish send/receive/reply in that the receiving process provide a
173 syncronization point (via door_create(3C)), the calling process uses
174 that syncronization point to rendezvous with and provide arguments (via
175 door_call(3C), and then the receive process reply (via
176 door_return(3C))) passing back data as required. The OS makes it such
177 that the memory used to pass data via door_call(3C) is mapped into the
178 receiving process which can write back into it and then transparently
179 have it mapped back to the calling process.
181 As well as handling internal events of interest, the daemon also needs
182 to send events of interest (link up/down, WLAN scan/connect results etc)
183 to (possibly) multiple NWAM client listeners. This is done via
184 System V message queues. On registering for events via a libnwam door
185 request into the daemon (nwam_events_register()), a per-client
186 (identified by pid) message queue file is created. The
187 daemon sends messages to all listeners by examining the list of
188 message queue files (allowing registration to be robust across
189 daemon restarts) and sending events to each listener. This is done
190 via the libnwam function nwam_event_send() which hides the IPC
191 mechanism from the daemon.
195 Four object lists are maintained within the daemon - one each for
196 the configuration objects libnwam manages. i.e.:
201 o NCUs of the current active NCP
203 Objects have an associated libnwam handle and an optional data
204 field (which is used for NCUs only).
206 Locking is straightforward - nwamd_object_init() will initialize
207 an object of a particular type in the appropriate object list,
208 returning it with the object lock held. When it is no longer needed,
209 nwamd_object_unlock() should be called on the object.
211 To retrieve an existing object, nwamd_object_find() should be
212 called - again this returns the object in a locked state.
214 nwamd_object_lock() is deliberately not exposed outside of objects.c,
215 since object locking is implicit in the above creation/retrieval
218 An object is removed from the object list (with handle destroyed)
219 via nwamd_object_fini() - the object data (if any) is returned
220 from this call to allow deallocation.
224 nwamd deals with 3 broad types of object that need to maintain
225 internal state: NCUs, ENMs and locations (known WLANs are configuration
226 objects but don't have a state beyond simply being present).
227 NWAM objects all share a basic set of states:
231 uninitialized object representation not present on system or in nwamd
232 initialized object representation present in system and in nwamd
233 disabled disabled manually
234 offline external conditions are not satisfied
235 offline* external conditions are satisfied, trying to move online
236 online* external conditions no longer satisfied, trying to move offline
237 online conditions satisfied and configured
238 maintenance error occurred in applying configuration
240 These deliberately mimic SMF states.
242 The states of interest are offline, offline* and online.
244 An object (link/interface NCU, ENM or location) should only move online
245 when its conditions are satisfied _and_ its configuration has been successfully
246 applied. This occurs when an ENM method has run or a link is up, or an
247 interface has at least one address assigned.
249 To understand the distinction between offline and offline*, consider the case
250 where a link is of prioritized activation, and either is a lower priority
251 group - and hence inactive (due to cable being unplugged or inability to
252 connect to wifi) - or a higher priority group - and hence active. In general,
253 we want to distinguish between two cases:
255 1) when we are actively configuring the link with a view to moving online
256 (offline*), as would be the case when the link's priority group is
258 2) when external policy-based conditions prevent a link from being active.
259 offline should be used for such cases. Links in priority groups above and
260 below the currently-active group will be offline, since policy precludes them
261 from activating (as less-prioritized links).
263 So we see that offline and offline* can thus be used to distinguish between
264 cases that have the potentiality to move online (offline*) from a policy
265 perspective - i.e. conditions on the location allow it, or link prioritization
266 allows it - and cases where external conditions dictate that it should not
269 Once an object reaches offline*, its configuration processes should kick in.
270 This is where auxiliary state is useful, as it allows us to distinguish between
271 various states in that configuration process. For example, a link can be
272 waiting for WLAN selection or key data, or an interface can be waiting for
273 DHCP response. This auxiliary state can then also be used diagnostically by
274 libnwam consumers to determine the current status of a link, interface, ENM
277 WiFi links present a problem however. On the one hand, we want them
278 to be inactive when they are not part of the current priority grouping,
279 while on the other we want to watch out for new WLANs appearing in
280 scan data if the WiFi link is of a higher priority than the currently-selected
281 group. The reason we watch out for these is they represent the potential
282 to change priority grouping to a more preferred group. To accommodate this,
283 WiFi links of the same or lower (more preferred) priority group will always
284 be trying to connect (and thus be offline* if they cannot).
286 It might appear unnecessary to have a separate state value/machine for
287 auxiliary state - why can't we simply add the auxiliary state machine to the
288 global object state machine? Part of the answer is that there are times we
289 need to run through the same configuration state machine when the global
290 object state is different - in paticular either offline* or online. Consider
291 WiFi - we want to do periodic scans to find a "better" WLAN - we can easily
292 do this by running back through the link state machine of auxiliary
293 states, but we want to stay online while we do it, since we are still
294 connected (if the WLAN disconnects of course we go to LINK_DOWN and offline).
296 Another reason we wish to separate the more general states (offline, online
297 etc) from the more specific ones (WIFI_NEED_SELECTION etc) is to ensure
298 that the representation of configuration objects closely matches the way
301 For an NCU physical link, the following link-specific auxiliary states are
304 Auxiliary state Description
305 =============== ===========
307 LINK_WIFI_SCANNING Scan in progress
308 LINK_WIFI_NEED_SELECTION Need user to specify WLAN
309 LINK_WIFI_NEED_KEY Need user to specify a WLAN key for selection
310 LINK_WIFI_CONNECTING Connecting to current selection
312 A WiFI link differs from a wired one in that it always has the
313 potential to be available - it just depends if visited WLANs are in range.
314 So such links - if they are higher in the priority grouping than the
315 currently-active priority group - should always be able to scan, as they
316 are always "trying" to be activated.
318 Wired links that do not support DL_NOTE_LINK_UP/DOWN are problematic,
319 since we have to simply assume a cable is plugged in. If an IP NCU
320 is activated above such a link, and that NCU uses DHCP, a timeout
321 will be triggered eventually (user-configurable via the nwamd/ncu_wait_time
322 SMF property of the network/physical:nwam instance) which will cause
323 us to give up on the link.
325 For an IP interface NCU, the following auxiliary states are suggested.
327 Auxiliary state Description
328 =============== ===========
330 NWAM_AUX_STATE_IF_WAITING_FOR_ADDR Waiting for an address to be assigned
331 NWAM_AUX_STATE_IF_DHCP_TIMED_OUT DHCP timed out on interface
333 A link can have multiple logical interfaces plumbed on it consisting
334 of a mix of static and DHCP-acquired addresses. This means that
335 we need to decide how to aggregate the state of these logical
336 interfaces into the NCU state. The concept of "up" we use here
337 does not correspond to IFF_UP or IFF_RUNNING, but rather
338 when we get (via getting RTM_NEWADDR events with non-zero
339 addresses) at least one address assigned to the link.
341 We use this concept of up as it represents the potential for
342 network communication - e.g. after assigning a static
343 address, if the location specifies nameserver etc, it
344 is possible to communicate over the network. One important
345 edge case here is that when DHCP information comes
346 in, we need to reassess location activation conditions and
347 possibly change or reapply the current location. The problem
348 is that if we have a static/DHCP mix, and if we rely on
349 the IP interface's notion of "up" to trigger location activation,
350 we will likely first apply the location when the static address
351 has been assigned and before the DHCP information has
352 been returned (which may include nameserver info). So
353 the solution is that on getting an RTM_NEWADDR, we
354 check if the (logical) interface associated is DHCP, and
355 even if the interface NCU is already up, we reassess
356 location activation. This will lead to a reapplication of
357 the current location or possibly a location switch.
359 In order to move through the various states, a generic
363 nwamd_object_set_state(nwamd_object_t obj, nwamd_state_t state,
364 nwamd_aux_state_t aux_state);
366 This function creates an OBJECT_STATE event containing
367 the new state/aux_state and enqueues it in the event
368 queue. Each object registers its own handler for this
369 event, and in response to the current state/aux state and
370 desired aux state it responds appropriately in the event
371 handling thread, spawning other threads to carry out
372 actions as appropriate. The object state event is
373 then sent to any registered listeners.
375 So for NCUs, we define a handle_object_state() function
376 to run the state machine for the NCU object.
378 Link state and NCP policy
379 =========================
383 o prioritized: where the constituent link NCUs specify priority group
384 numbers (where lower are more favoured) and grouping types. These
385 are used to allow link NCUs to be either grouped separately (exclusive)
386 or together (shared or all).
387 o manual: their activation is governed by the value of their enabled
389 o a combination of the above.
391 IP interface NCUs interit their activation from the links below them,
392 so an IP interface NCU will be active if its underlying link is (assuming
393 it hasn't been disabled).
395 At startup, and at regular intervals (often triggered by NWAM
396 events), the NCP policy needs to be reassessed. There
397 are a number of causes for NCP policy to be reassessed -
399 o a periodic check of link state that occurs every N seconds
400 o a link goes from offline(*) to online (cable plug/wifi connect)
401 o a link goes from online to offline (cable unplug/wifi disconnect).
403 Any of these should cause the link selecton algorithm to rerun.
405 The link selection algorithm works as follows:
407 Starting from the lowest priority grouping value, assess all links
408 in that priority group.
410 The current priority-group is considered failed if:
412 o "exclusive" NCUs exist and none are offline*/online,
413 o "shared" NCUs exist and none are offline*/online,
414 o "all" NCUs exist and all are not offline*/online,
415 o no NCUs are offline*/online.
417 We do not invalidate a link that is offline* since its configuration
418 is in progress. This has the unfortunate side-effect that
419 wired links that do not do DL_NOTE_LINK_UP/DOWN will never
420 fail. If such links wish to be skipped, their priority group value
421 should be increased (prioritizing wireless links).
423 One a priority group has been selected, all links in groups above
424 _and_ below it need to be moved offline.
428 A basic set of system-supplied locations are supplied - NoNet and
429 Automatic. nwamd will apply the NoNet location until such a time
430 as an interface NCU is online, at which point it will switch
431 to the Automatic location. If a user-supplied location is supplied,
432 and it is either manually enabled or its conditions are satisfied, it
433 will be preferred and activated instead. Only one location can be
434 active at once since each location has its own specification of nameservices
439 ENMs are either manual or conditional in activation and will be
440 activated if they are enabled (manual) or if the conditions
441 are met (conditional). Multiple ENMs can be active at once.