xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/loc.c (revision ccd81fdd)
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 new_enabled, old_enabled = 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 		old_enabled = loc_is_enabled(object->nwamd_object_handle);
435 		nwam_loc_free(object->nwamd_object_handle);
436 		object->nwamd_object_handle = loch;
437 	} else {
438 		object = nwamd_object_init(NWAM_OBJECT_TYPE_LOC,
439 		    event->event_object, loch, NULL);
440 		object->nwamd_object_state = NWAM_STATE_OFFLINE;
441 		object->nwamd_object_aux_state =
442 		    NWAM_AUX_STATE_CONDITIONS_NOT_MET;
443 	}
444 	new_enabled = loc_is_enabled(loch);
445 	state = object->nwamd_object_state;
446 	nwamd_object_release(object);
447 
448 	/*
449 	 * If this location is ONLINE and the value of the "enabled" property
450 	 * has not changed, then this location is getting refreshed because it
451 	 * was committed with changes.  Change states to re-activate itself.
452 	 * If the "enabled" property has changed, then this location is
453 	 * getting refreshed as part of a enable/disable action and there is
454 	 * no need to change states here.
455 	 */
456 	if (state == NWAM_STATE_ONLINE && old_enabled == new_enabled) {
457 		nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
458 		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
459 		    NWAM_AUX_STATE_METHOD_RUNNING);
460 	}
461 }
462 
463 /* Handle loc finish event */
464 void
465 nwamd_loc_handle_fini_event(nwamd_event_t event)
466 {
467 	nwamd_object_t object;
468 
469 	nlog(LOG_DEBUG, "nwamd_loc_handle_fini_event(%s)",
470 	    event->event_object);
471 
472 	/* Don't disable the location, as this can enable the Automatic loc */
473 	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
474 	    event->event_object)) == NULL) {
475 		nlog(LOG_ERR, "nwamd_loc_handle_fini_event: "
476 		    "loc %s not found", event->event_object);
477 		nwamd_event_do_not_send(event);
478 		return;
479 	}
480 	nwamd_object_release_and_destroy(object);
481 }
482 
483 void
484 nwamd_loc_handle_action_event(nwamd_event_t event)
485 {
486 	nwamd_object_t object;
487 
488 	switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) {
489 	case NWAM_ACTION_ENABLE:
490 		object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
491 		    event->event_object);
492 		if (object == NULL) {
493 			nlog(LOG_ERR, "nwamd_loc_handle_action_event: "
494 			    "could not find location %s", event->event_object);
495 			nwamd_event_do_not_send(event);
496 			return;
497 		}
498 		if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
499 			nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: "
500 			    "location %s already online, nothing to do",
501 			    event->event_object);
502 			nwamd_object_release(object);
503 			return;
504 		}
505 		nwamd_object_release(object);
506 
507 		nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
508 		    event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE,
509 		    NWAM_AUX_STATE_METHOD_RUNNING);
510 		break;
511 	case NWAM_ACTION_DISABLE:
512 		object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
513 		    event->event_object);
514 		if (object == NULL) {
515 			nlog(LOG_ERR, "nwamd_loc_handle_action_event: "
516 			    "could not find location %s", event->event_object);
517 			nwamd_event_do_not_send(event);
518 			return;
519 		}
520 		if (object->nwamd_object_state == NWAM_STATE_DISABLED) {
521 			nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: "
522 			    "location %s already disabled, nothing to do",
523 			    event->event_object);
524 			nwamd_object_release(object);
525 			return;
526 		}
527 		nwamd_object_release(object);
528 
529 		nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
530 		    event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE,
531 		    NWAM_AUX_STATE_MANUAL_DISABLE);
532 		break;
533 	case NWAM_ACTION_ADD:
534 	case NWAM_ACTION_REFRESH:
535 		nwamd_loc_handle_init_event(event);
536 		break;
537 	case NWAM_ACTION_DESTROY:
538 		nwamd_loc_handle_fini_event(event);
539 		break;
540 	default:
541 		nlog(LOG_INFO, "nwam_loc_handle_action_event: "
542 		    "unexpected action");
543 		break;
544 	}
545 }
546 
547 void
548 nwamd_loc_handle_state_event(nwamd_event_t event)
549 {
550 	nwamd_object_t object;
551 	nwam_state_t new_state;
552 	nwam_aux_state_t new_aux_state;
553 
554 	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
555 	    event->event_object)) == NULL) {
556 		nlog(LOG_ERR, "nwamd_loc_handle_state_event: "
557 		    "state event for nonexistent loc %s", event->event_object);
558 		nwamd_event_do_not_send(event);
559 		return;
560 	}
561 	new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state;
562 	new_aux_state =
563 	    event->event_msg->nwe_data.nwe_object_state.nwe_aux_state;
564 
565 	if (new_state == object->nwamd_object_state &&
566 	    new_aux_state == object->nwamd_object_aux_state) {
567 		nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: "
568 		    "loc %s already in state (%s , %s)",
569 		    object->nwamd_object_name,
570 		    nwam_state_to_string(new_state),
571 		    nwam_aux_state_to_string(new_aux_state));
572 		nwamd_object_release(object);
573 		return;
574 	}
575 
576 	object->nwamd_object_state = new_state;
577 	object->nwamd_object_aux_state = new_aux_state;
578 
579 	nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: changing state for loc "
580 	    "%s to (%s , %s)", object->nwamd_object_name,
581 	    nwam_state_to_string(object->nwamd_object_state),
582 	    nwam_aux_state_to_string(object->nwamd_object_aux_state));
583 
584 	nwamd_object_release(object);
585 
586 	/*
587 	 * State machine for location.
588 	 */
589 	switch (new_state) {
590 	case NWAM_STATE_OFFLINE_TO_ONLINE:
591 		nwamd_loc_activate(event->event_object);
592 		break;
593 	case NWAM_STATE_ONLINE_TO_OFFLINE:
594 		/*
595 		 * Don't need to deactivate current location - condition check
596 		 * will activate another.  If the currently active location is
597 		 * being deactivated, then it is being manually deactivated;
598 		 * so also clear active_loc so condition checking is not
599 		 * confused.
600 		 */
601 		(void) pthread_mutex_lock(&active_loc_mutex);
602 		if (strcmp(event->event_object, active_loc) == 0)
603 			active_loc[0] = '\0';
604 		(void) pthread_mutex_unlock(&active_loc_mutex);
605 		nwamd_loc_check_conditions();
606 		break;
607 	case NWAM_STATE_DISABLED:
608 	case NWAM_STATE_OFFLINE:
609 	case NWAM_STATE_UNINITIALIZED:
610 	case NWAM_STATE_MAINTENANCE:
611 	case NWAM_STATE_DEGRADED:
612 	default:
613 		/* do nothing */
614 		break;
615 	}
616 }
617