1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <arpa/inet.h>
27 #include <errno.h>
28 #include <inet/ip.h>
29 #include <libdladm.h>
30 #include <libdllink.h>
31 #include <libdlwlan.h>
32 #include <libscf.h>
33 #include <limits.h>
34 #include <netdb.h>
35 #include <netinet/in.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
41
42 #include <libnwam.h>
43 #include "conditions.h"
44 #include "events.h"
45 #include "objects.h"
46 #include "util.h"
47
48 /*
49 * loc.c - contains routines which handle location abstraction.
50 */
51
52 pthread_mutex_t active_loc_mutex = PTHREAD_MUTEX_INITIALIZER;
53 char active_loc[NWAM_MAX_NAME_LEN];
54
55 static int
loc_create_init_fini_event(nwam_loc_handle_t loch,void * data)56 loc_create_init_fini_event(nwam_loc_handle_t loch, void *data)
57 {
58 boolean_t *init = data;
59 char *name;
60 nwamd_event_t event;
61
62 if (nwam_loc_get_name(loch, &name) != NWAM_SUCCESS) {
63 nlog(LOG_ERR, "loc_init_fini: could not get loc name");
64 return (0);
65 }
66
67 event = nwamd_event_init(*init ?
68 NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
69 NWAM_OBJECT_TYPE_LOC, 0, name);
70 if (event != NULL)
71 nwamd_event_enqueue(event);
72 free(name);
73
74 return (0);
75 }
76
77 /*
78 * Walk all locs, creating init events for each.
79 */
80 void
nwamd_init_locs(void)81 nwamd_init_locs(void)
82 {
83 boolean_t init = B_TRUE;
84
85 /* Unset active location */
86 (void) pthread_mutex_lock(&active_loc_mutex);
87 active_loc[0] = '\0';
88 (void) pthread_mutex_unlock(&active_loc_mutex);
89 (void) nwam_walk_locs(loc_create_init_fini_event, &init, 0, NULL);
90 }
91
92 /*
93 * Walk all locs, creating fini events for each.
94 */
95 void
nwamd_fini_locs(void)96 nwamd_fini_locs(void)
97 {
98 boolean_t init = B_FALSE;
99
100 (void) nwam_walk_locs(loc_create_init_fini_event, &init, 0, NULL);
101 }
102
103 static boolean_t
loc_is_enabled(nwam_loc_handle_t loch)104 loc_is_enabled(nwam_loc_handle_t loch)
105 {
106 nwam_value_t enabledval;
107 boolean_t enabled = B_FALSE;
108
109 if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ENABLED,
110 &enabledval) != NWAM_SUCCESS) {
111 nlog(LOG_ERR, "loc_is_enabled: could not retrieve "
112 "enabled value");
113 return (B_FALSE);
114 }
115 if (nwam_value_get_boolean(enabledval, &enabled)
116 != NWAM_SUCCESS) {
117 nlog(LOG_ERR, "loc_is_enabled: could not retrieve "
118 "enabled value");
119 nwam_value_free(enabledval);
120 return (B_FALSE);
121 }
122 nwam_value_free(enabledval);
123 return (enabled);
124 }
125
126 static int64_t
loc_get_activation_mode(nwam_loc_handle_t loch)127 loc_get_activation_mode(nwam_loc_handle_t loch)
128 {
129 nwam_error_t err;
130 uint64_t activation;
131 nwam_value_t activationval;
132
133 if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
134 &activationval) != NWAM_SUCCESS) {
135 nlog(LOG_ERR, "loc_get_activation_mode: could not retrieve "
136 "activation mode value");
137 return (-1);
138 }
139 err = nwam_value_get_uint64(activationval, &activation);
140 nwam_value_free(activationval);
141 if (err != NWAM_SUCCESS) {
142 nlog(LOG_ERR, "loc_get_activation_mode: could not retrieve "
143 "activation mode value");
144 return (-1);
145 }
146
147 return ((int64_t)activation);
148 }
149
150 /* Enables the location. */
151 static void
nwamd_loc_activate(const char * object_name)152 nwamd_loc_activate(const char *object_name)
153 {
154 char *enabled;
155
156 nlog(LOG_DEBUG, "nwamd_loc_activate: activating loc %s",
157 object_name);
158
159 /*
160 * Find currently enabled location and change its state to disabled
161 * if it is a manual location, or offline (if it is not).
162 * Only manual locations reach disabled, since conditional and
163 * system locations which are manually disabled simply revert to
164 * their conditions for activation.
165 */
166 if ((enabled = malloc(NWAM_MAX_NAME_LEN)) != NULL &&
167 nwamd_lookup_string_property(NET_LOC_FMRI, NET_LOC_PG,
168 NET_LOC_SELECTED_PROP, enabled, NWAM_MAX_NAME_LEN) == 0) {
169 /* Only change state if current != new */
170 if (strcmp(enabled, object_name) != 0) {
171 boolean_t do_disable = B_FALSE;
172 nwamd_object_t eobj = nwamd_object_find
173 (NWAM_OBJECT_TYPE_LOC, enabled);
174 if (eobj == NULL) {
175 nlog(LOG_INFO, "nwamd_loc_activate: could not "
176 "find old location %s", enabled);
177 goto skip_disable;
178 }
179 /*
180 * Disable if the old location was manual, since the
181 * only way a manual location can deactivate is if
182 * it is disabled.
183 */
184 do_disable =
185 (loc_get_activation_mode(eobj->nwamd_object_handle)
186 == (int64_t)NWAM_ACTIVATION_MODE_MANUAL);
187 nwamd_object_release(eobj);
188
189 if (do_disable) {
190 nlog(LOG_DEBUG, "nwamd_loc_activate: "
191 "disable needed for old location %s",
192 enabled);
193 nwamd_object_set_state
194 (NWAM_OBJECT_TYPE_LOC, enabled,
195 NWAM_STATE_DISABLED,
196 NWAM_AUX_STATE_MANUAL_DISABLE);
197 } else {
198 nlog(LOG_DEBUG, "nwamd_loc_activate: "
199 "offline needed for old location %s",
200 enabled);
201 nwamd_object_set_state
202 (NWAM_OBJECT_TYPE_LOC, enabled,
203 NWAM_STATE_OFFLINE,
204 NWAM_AUX_STATE_CONDITIONS_NOT_MET);
205 }
206 }
207 }
208 skip_disable:
209 free(enabled);
210
211 if (nwamd_set_string_property(NET_LOC_FMRI, NET_LOC_PG,
212 NET_LOC_SELECTED_PROP, object_name) == 0) {
213 char *state = smf_get_state(NET_LOC_FMRI);
214 nlog(LOG_INFO, "nwamd_loc_activate: set %s/%s to %s; "
215 "service is in %s state", NET_LOC_PG, NET_LOC_SELECTED_PROP,
216 object_name, state == NULL ? "unknown" : state);
217 free(state);
218 (void) smf_restore_instance(NET_LOC_FMRI);
219 if (smf_refresh_instance(NET_LOC_FMRI) == 0) {
220 (void) pthread_mutex_lock(&active_loc_mutex);
221 (void) strlcpy(active_loc, object_name,
222 sizeof (active_loc));
223 (void) pthread_mutex_unlock(&active_loc_mutex);
224 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
225 object_name,
226 NWAM_STATE_ONLINE, NWAM_AUX_STATE_ACTIVE);
227 } else {
228 nlog(LOG_ERR, "nwamd_loc_activate: "
229 "%s could not be refreshed", NET_LOC_FMRI);
230 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
231 object_name,
232 NWAM_STATE_MAINTENANCE,
233 NWAM_AUX_STATE_METHOD_FAILED);
234 }
235 }
236 }
237
238 struct nwamd_loc_check_walk_arg {
239 nwamd_object_t winning_object;
240 uint64_t winning_rating;
241 };
242
243 /*
244 * Determine which location should be activated.
245 */
246 static int
nwamd_loc_check(nwamd_object_t object,void * data)247 nwamd_loc_check(nwamd_object_t object, void *data)
248 {
249 struct nwamd_loc_check_walk_arg *wa = data;
250 nwam_loc_handle_t loch = object->nwamd_object_handle;
251 nwam_value_t conditionval;
252 int64_t lactivation;
253 uint64_t rating, activation;
254 boolean_t satisfied;
255 char **conditions;
256 uint_t nelem;
257
258 lactivation = loc_get_activation_mode(object->nwamd_object_handle);
259
260 if (lactivation == -1)
261 return (0);
262
263 activation = (uint64_t)lactivation;
264 switch (activation) {
265 case NWAM_ACTIVATION_MODE_MANUAL:
266 if (loc_is_enabled(loch)) {
267 /* Manually enabled locations should always win out. */
268 nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
269 object->nwamd_object_name);
270 wa->winning_object = object;
271 wa->winning_rating = UINT64_MAX;
272 } else {
273 nlog(LOG_DEBUG, "nwamd_loc_check: %s is disabled",
274 object->nwamd_object_name);
275 if (object->nwamd_object_state != NWAM_STATE_DISABLED) {
276 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
277 object->nwamd_object_name,
278 NWAM_STATE_DISABLED,
279 NWAM_AUX_STATE_MANUAL_DISABLE);
280 }
281 }
282
283 return (0);
284
285 case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
286 case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
287 if (loc_is_enabled(loch)) {
288 /* Manually enabled locations should always win out. */
289 nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
290 object->nwamd_object_name);
291 wa->winning_object = object;
292 wa->winning_rating = UINT64_MAX;
293 }
294
295 if (nwam_loc_get_prop_value(loch,
296 NWAM_LOC_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
297 nlog(LOG_ERR, "nwamd_loc_check: could not retrieve "
298 "condition value");
299 return (0);
300 }
301 if (nwam_value_get_string_array(conditionval,
302 &conditions, &nelem) != NWAM_SUCCESS) {
303 nlog(LOG_ERR, "nwamd_loc_check: could not retrieve "
304 "condition value");
305 nwam_value_free(conditionval);
306 return (0);
307 }
308 satisfied = nwamd_check_conditions(activation, conditions,
309 nelem);
310
311 if (satisfied) {
312 rating = nwamd_rate_conditions(activation,
313 conditions, nelem);
314 if (rating > wa->winning_rating) {
315 wa->winning_object = object;
316 wa->winning_rating = rating;
317 }
318 }
319 nwam_value_free(conditionval);
320 return (0);
321
322 case NWAM_ACTIVATION_MODE_SYSTEM:
323 if (loc_is_enabled(loch)) {
324 /* Manually enabled locations should always win out. */
325 nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
326 object->nwamd_object_name);
327 wa->winning_object = object;
328 wa->winning_rating = UINT64_MAX;
329 }
330
331 /* Either NoNet, Automatic or Legacy location, so skip. */
332
333 return (0);
334 default:
335 return (0);
336 }
337 /*NOTREACHED*/
338 return (0);
339 }
340
341 static int
nwamd_ncu_online_check(nwamd_object_t object,void * data)342 nwamd_ncu_online_check(nwamd_object_t object, void *data)
343 {
344 boolean_t *online = data;
345 nwamd_ncu_t *ncu_data = object->nwamd_object_data;
346
347 if (ncu_data->ncu_type != NWAM_NCU_TYPE_INTERFACE)
348 return (0);
349
350 if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
351 /* An online IP NCU found, stop walk */
352 *online = B_TRUE;
353 return (1);
354 }
355 return (0);
356 }
357
358 void
nwamd_loc_check_conditions(void)359 nwamd_loc_check_conditions(void)
360 {
361 struct nwamd_loc_check_walk_arg wa = { NULL, 0 };
362 const char *winning_loc;
363 boolean_t ncu_online = B_FALSE;
364 boolean_t is_active;
365
366 /*
367 * Walk the NCUs to find out if at least one IP NCU is online. If so,
368 * check the activation-mode and conditions. If not, enable the NoNet
369 * location.
370 */
371 (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, nwamd_ncu_online_check,
372 &ncu_online);
373
374 if (!ncu_online) {
375 winning_loc = NWAM_LOC_NAME_NO_NET;
376 } else {
377 (void) nwamd_walk_objects(NWAM_OBJECT_TYPE_LOC, nwamd_loc_check,
378 &wa);
379 if (wa.winning_object != NULL)
380 winning_loc = wa.winning_object->nwamd_object_name;
381 else
382 winning_loc = NWAM_LOC_NAME_AUTOMATIC;
383 }
384 nlog(LOG_DEBUG, "nwamd_loc_check_conditions: winning loc is %s",
385 winning_loc);
386
387 /* If the winning location is already active, do nothing */
388 (void) pthread_mutex_lock(&active_loc_mutex);
389 is_active = (strcmp(active_loc, winning_loc) == 0);
390 (void) pthread_mutex_unlock(&active_loc_mutex);
391 if (is_active)
392 return;
393
394 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC, winning_loc,
395 NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_METHOD_RUNNING);
396 }
397
398 int
nwamd_loc_action(const char * loc,nwam_action_t action)399 nwamd_loc_action(const char *loc, nwam_action_t action)
400 {
401 nwamd_event_t event = nwamd_event_init_object_action
402 (NWAM_OBJECT_TYPE_LOC, loc, NULL, action);
403 if (event == NULL)
404 return (1);
405 nwamd_event_enqueue(event);
406 return (0);
407 }
408
409 /*
410 * Event handling functions.
411 */
412
413 /* Handle loc initialization/refresh event */
414 void
nwamd_loc_handle_init_event(nwamd_event_t event)415 nwamd_loc_handle_init_event(nwamd_event_t event)
416 {
417 nwamd_object_t object;
418 nwam_loc_handle_t loch;
419 nwam_error_t err;
420 boolean_t new_enabled, old_enabled = B_FALSE;
421 nwam_state_t state;
422
423 if ((err = nwam_loc_read(event->event_object, 0, &loch))
424 != NWAM_SUCCESS) {
425 nlog(LOG_ERR, "nwamd_loc_handle_init_event: could not "
426 "read object '%s': %s", event->event_object,
427 nwam_strerror(err));
428 nwamd_event_do_not_send(event);
429 return;
430 }
431 if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
432 event->event_object)) != NULL) {
433 old_enabled = loc_is_enabled(object->nwamd_object_handle);
434 nwam_loc_free(object->nwamd_object_handle);
435 object->nwamd_object_handle = loch;
436 } else {
437 object = nwamd_object_init(NWAM_OBJECT_TYPE_LOC,
438 event->event_object, loch, NULL);
439 object->nwamd_object_state = NWAM_STATE_OFFLINE;
440 object->nwamd_object_aux_state =
441 NWAM_AUX_STATE_CONDITIONS_NOT_MET;
442 }
443 new_enabled = loc_is_enabled(loch);
444 state = object->nwamd_object_state;
445 nwamd_object_release(object);
446
447 /*
448 * If this location is ONLINE and the value of the "enabled" property
449 * has not changed, then this location is getting refreshed because it
450 * was committed with changes. Change states to re-activate itself.
451 * If the "enabled" property has changed, then this location is
452 * getting refreshed as part of a enable/disable action and there is
453 * no need to change states here.
454 */
455 if (state == NWAM_STATE_ONLINE && old_enabled == new_enabled) {
456 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
457 event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
458 NWAM_AUX_STATE_METHOD_RUNNING);
459 }
460 }
461
462 /* Handle loc finish event */
463 void
nwamd_loc_handle_fini_event(nwamd_event_t event)464 nwamd_loc_handle_fini_event(nwamd_event_t event)
465 {
466 nwamd_object_t object;
467
468 nlog(LOG_DEBUG, "nwamd_loc_handle_fini_event(%s)",
469 event->event_object);
470
471 /* Don't disable the location, as this can enable the Automatic loc */
472 if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
473 event->event_object)) == NULL) {
474 nlog(LOG_INFO, "nwamd_loc_handle_fini_event: "
475 "loc %s not found", event->event_object);
476 nwamd_event_do_not_send(event);
477 return;
478 }
479 nwamd_object_release_and_destroy(object);
480 }
481
482 void
nwamd_loc_handle_action_event(nwamd_event_t event)483 nwamd_loc_handle_action_event(nwamd_event_t event)
484 {
485 nwamd_object_t object;
486
487 switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
488 case NWAM_ACTION_ENABLE:
489 object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
490 event->event_object);
491 if (object == NULL) {
492 nlog(LOG_ERR, "nwamd_loc_handle_action_event: "
493 "could not find location %s", event->event_object);
494 nwamd_event_do_not_send(event);
495 return;
496 }
497 if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
498 nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: "
499 "location %s already online, nothing to do",
500 event->event_object);
501 nwamd_object_release(object);
502 return;
503 }
504 nwamd_object_release(object);
505
506 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
507 event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
508 NWAM_AUX_STATE_METHOD_RUNNING);
509 break;
510 case NWAM_ACTION_DISABLE:
511 object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
512 event->event_object);
513 if (object == NULL) {
514 nlog(LOG_ERR, "nwamd_loc_handle_action_event: "
515 "could not find location %s", event->event_object);
516 nwamd_event_do_not_send(event);
517 return;
518 }
519 if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
520 nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: "
521 "location %s already disabled, nothing to do",
522 event->event_object);
523 nwamd_object_release(object);
524 return;
525 }
526 nwamd_object_release(object);
527
528 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
529 event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
530 NWAM_AUX_STATE_MANUAL_DISABLE);
531 break;
532 case NWAM_ACTION_ADD:
533 case NWAM_ACTION_REFRESH:
534 nwamd_loc_handle_init_event(event);
535 break;
536 case NWAM_ACTION_DESTROY:
537 nwamd_loc_handle_fini_event(event);
538 break;
539 default:
540 nlog(LOG_INFO, "nwam_loc_handle_action_event: "
541 "unexpected action");
542 break;
543 }
544 }
545
546 void
nwamd_loc_handle_state_event(nwamd_event_t event)547 nwamd_loc_handle_state_event(nwamd_event_t event)
548 {
549 nwamd_object_t object;
550 nwam_state_t new_state;
551 nwam_aux_state_t new_aux_state;
552
553 if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
554 event->event_object)) == NULL) {
555 nlog(LOG_INFO, "nwamd_loc_handle_state_event: "
556 "state event for nonexistent loc %s", event->event_object);
557 nwamd_event_do_not_send(event);
558 return;
559 }
560 new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
561 new_aux_state =
562 event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
563
564 if (new_state == object->nwamd_object_state &&
565 new_aux_state == object->nwamd_object_aux_state) {
566 nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: "
567 "loc %s already in state (%s , %s)",
568 object->nwamd_object_name,
569 nwam_state_to_string(new_state),
570 nwam_aux_state_to_string(new_aux_state));
571 nwamd_object_release(object);
572 return;
573 }
574
575 object->nwamd_object_state = new_state;
576 object->nwamd_object_aux_state = new_aux_state;
577
578 nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: changing state for loc "
579 "%s to (%s , %s)", object->nwamd_object_name,
580 nwam_state_to_string(object->nwamd_object_state),
581 nwam_aux_state_to_string(object->nwamd_object_aux_state));
582
583 nwamd_object_release(object);
584
585 /*
586 * State machine for location.
587 */
588 switch (new_state) {
589 case NWAM_STATE_OFFLINE_TO_ONLINE:
590 nwamd_loc_activate(event->event_object);
591 break;
592 case NWAM_STATE_ONLINE_TO_OFFLINE:
593 /*
594 * Don't need to deactivate current location - condition check
595 * will activate another. If the currently active location is
596 * being deactivated, then it is being manually deactivated;
597 * so also clear active_loc so condition checking is not
598 * confused.
599 */
600 (void) pthread_mutex_lock(&active_loc_mutex);
601 if (strcmp(event->event_object, active_loc) == 0)
602 active_loc[0] = '\0';
603 (void) pthread_mutex_unlock(&active_loc_mutex);
604 nwamd_loc_check_conditions();
605 break;
606 case NWAM_STATE_DISABLED:
607 case NWAM_STATE_OFFLINE:
608 case NWAM_STATE_UNINITIALIZED:
609 case NWAM_STATE_MAINTENANCE:
610 case NWAM_STATE_DEGRADED:
611 default:
612 /* do nothing */
613 break;
614 }
615 }
616