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