xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/loc.c (revision 861a91627796c35220e75654dac61e5707536dcd)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <arpa/inet.h>
28 #include <errno.h>
29 #include <inet/ip.h>
30 #include <inetcfg.h>
31 #include <libdladm.h>
32 #include <libdllink.h>
33 #include <libdlwlan.h>
34 #include <libscf.h>
35 #include <limits.h>
36 #include <netdb.h>
37 #include <netinet/in.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/socket.h>
42 #include <sys/types.h>
43 
44 #include <libnwam.h>
45 #include "conditions.h"
46 #include "events.h"
47 #include "objects.h"
48 #include "util.h"
49 
50 /*
51  * loc.c - contains routines which handle location abstraction.
52  */
53 
54 pthread_mutex_t active_loc_mutex = PTHREAD_MUTEX_INITIALIZER;
55 char active_loc[NWAM_MAX_NAME_LEN];
56 
57 static int
58 loc_create_init_fini_event(nwam_loc_handle_t loch, void *data)
59 {
60 	boolean_t *init = data;
61 	char *name;
62 	nwamd_event_t event;
63 
64 	if (nwam_loc_get_name(loch, &name) != NWAM_SUCCESS) {
65 		nlog(LOG_ERR, "loc_init_fini: could not get loc name");
66 		return (0);
67 	}
68 
69 	event = nwamd_event_init(*init ?
70 	    NWAM_EVENT_TYPE_OBJECT_INIT : NWAM_EVENT_TYPE_OBJECT_FINI,
71 	    NWAM_OBJECT_TYPE_LOC, 0, name);
72 	if (event != NULL)
73 		nwamd_event_enqueue(event);
74 	free(name);
75 
76 	return (0);
77 }
78 
79 /*
80  * Walk all locs, creating init events for each.
81  */
82 void
83 nwamd_init_locs(void)
84 {
85 	boolean_t init = B_TRUE;
86 
87 	/* Unset active location */
88 	(void) pthread_mutex_lock(&active_loc_mutex);
89 	active_loc[0] = '\0';
90 	(void) pthread_mutex_unlock(&active_loc_mutex);
91 	(void) nwam_walk_locs(loc_create_init_fini_event, &init, 0, NULL);
92 }
93 
94 /*
95  * Walk all locs, creating fini events for each.
96  */
97 void
98 nwamd_fini_locs(void)
99 {
100 	boolean_t init = B_FALSE;
101 
102 	(void) nwam_walk_locs(loc_create_init_fini_event, &init, 0, NULL);
103 }
104 
105 static boolean_t
106 loc_is_enabled(nwam_loc_handle_t loch)
107 {
108 	nwam_value_t enabledval;
109 	boolean_t enabled = B_FALSE;
110 
111 	if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ENABLED,
112 	    &enabledval) != NWAM_SUCCESS) {
113 		nlog(LOG_ERR, "loc_is_enabled: could not retrieve "
114 		    "enabled value");
115 		return (B_FALSE);
116 	}
117 	if (nwam_value_get_boolean(enabledval, &enabled)
118 	    != NWAM_SUCCESS) {
119 		nlog(LOG_ERR, "loc_is_enabled: could not retrieve "
120 		    "enabled value");
121 		nwam_value_free(enabledval);
122 		return (B_FALSE);
123 	}
124 	nwam_value_free(enabledval);
125 	return (enabled);
126 }
127 
128 static int64_t
129 loc_get_activation_mode(nwam_loc_handle_t loch)
130 {
131 	nwam_error_t err;
132 	uint64_t activation;
133 	nwam_value_t activationval;
134 
135 	if (nwam_loc_get_prop_value(loch, NWAM_LOC_PROP_ACTIVATION_MODE,
136 	    &activationval)  != NWAM_SUCCESS) {
137 		nlog(LOG_ERR, "loc_get_activation_mode: could not retrieve "
138 		    "activation mode value");
139 		return (-1);
140 	}
141 	err = nwam_value_get_uint64(activationval, &activation);
142 	nwam_value_free(activationval);
143 	if (err != NWAM_SUCCESS) {
144 		nlog(LOG_ERR, "loc_get_activation_mode: could not retrieve "
145 		    "activation mode value");
146 		return (-1);
147 	}
148 
149 	return ((int64_t)activation);
150 }
151 
152 /* Enables the location. */
153 static void
154 nwamd_loc_activate(const char *object_name)
155 {
156 	char *enabled;
157 
158 	nlog(LOG_DEBUG, "nwamd_loc_activate: activating loc %s",
159 	    object_name);
160 
161 	/*
162 	 * Find currently enabled location and change its state to disabled
163 	 * if it is a manual location, or offline (if it is not).
164 	 * Only manual locations reach disabled, since conditional and
165 	 * system locations which are manually disabled simply revert to
166 	 * their conditions for activation.
167 	 */
168 	if ((enabled = malloc(NWAM_MAX_NAME_LEN)) != NULL &&
169 	    nwamd_lookup_string_property(NET_LOC_FMRI, NET_LOC_PG,
170 	    NET_LOC_SELECTED_PROP, enabled, NWAM_MAX_NAME_LEN) == 0) {
171 		/* Only change state if current != new */
172 		if (strcmp(enabled, object_name) != 0) {
173 			boolean_t do_disable = B_FALSE;
174 			nwamd_object_t eobj = nwamd_object_find
175 			    (NWAM_OBJECT_TYPE_LOC, enabled);
176 			if (eobj == NULL) {
177 				nlog(LOG_ERR, "nwamd_loc_activate: cannot "
178 				    "find old location %s", enabled);
179 				free(enabled);
180 				return;
181 			}
182 			/*
183 			 * Disable if the old location was manual, since the
184 			 * only way a manual location can deactivate is if
185 			 * it is disabled.
186 			 */
187 			do_disable =
188 			    (loc_get_activation_mode(eobj->nwamd_object_handle)
189 			    == (int64_t)NWAM_ACTIVATION_MODE_MANUAL);
190 			nwamd_object_release(eobj);
191 
192 			if (do_disable) {
193 				nlog(LOG_DEBUG, "nwamd_loc_activate: "
194 				    "disable needed for old location %s",
195 				    enabled);
196 				nwamd_object_set_state
197 				    (NWAM_OBJECT_TYPE_LOC, enabled,
198 				    NWAM_STATE_DISABLED,
199 				    NWAM_AUX_STATE_MANUAL_DISABLE);
200 			} else {
201 				nlog(LOG_DEBUG, "nwamd_loc_activate: "
202 				    "offline needed for old location %s",
203 				    enabled);
204 				nwamd_object_set_state
205 				    (NWAM_OBJECT_TYPE_LOC, enabled,
206 				    NWAM_STATE_OFFLINE,
207 				    NWAM_AUX_STATE_CONDITIONS_NOT_MET);
208 			}
209 		}
210 	}
211 	free(enabled);
212 
213 	if (nwamd_set_string_property(NET_LOC_FMRI, NET_LOC_PG,
214 	    NET_LOC_SELECTED_PROP, object_name) == 0) {
215 		char *state = smf_get_state(NET_LOC_FMRI);
216 		nlog(LOG_INFO, "nwam_loc_activate: set %s/%s to %s; "
217 		    "service is in %s state", NET_LOC_PG, NET_LOC_SELECTED_PROP,
218 		    object_name, state == NULL ? "unknown" : state);
219 		free(state);
220 		(void) smf_restore_instance(NET_LOC_FMRI);
221 		if (smf_refresh_instance(NET_LOC_FMRI) == 0) {
222 			(void) pthread_mutex_lock(&active_loc_mutex);
223 			(void) strlcpy(active_loc, object_name,
224 			    sizeof (active_loc));
225 			(void) pthread_mutex_unlock(&active_loc_mutex);
226 			nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
227 			    object_name,
228 			    NWAM_STATE_ONLINE, NWAM_AUX_STATE_ACTIVE);
229 		} else {
230 			nlog(LOG_ERR, "nwamd_loc_activate: "
231 			    "%s could not be refreshed", NET_LOC_FMRI);
232 			nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
233 			    object_name,
234 			    NWAM_STATE_MAINTENANCE,
235 			    NWAM_AUX_STATE_METHOD_FAILED);
236 		}
237 	}
238 }
239 
240 struct nwamd_loc_check_walk_arg {
241 	nwamd_object_t winning_object;
242 	uint64_t winning_rating;
243 };
244 
245 /*
246  * Determine which location should be activated.
247  */
248 static int
249 nwamd_loc_check(nwamd_object_t object, void *data)
250 {
251 	struct nwamd_loc_check_walk_arg *wa = data;
252 	nwam_loc_handle_t loch = object->nwamd_object_handle;
253 	nwam_value_t conditionval;
254 	int64_t lactivation;
255 	uint64_t rating, activation;
256 	boolean_t satisfied;
257 	char **conditions;
258 	uint_t nelem;
259 
260 	lactivation = loc_get_activation_mode(object->nwamd_object_handle);
261 
262 	if (lactivation == -1)
263 		return (0);
264 
265 	activation = (uint64_t)lactivation;
266 	switch (activation) {
267 	case NWAM_ACTIVATION_MODE_MANUAL:
268 		if (loc_is_enabled(loch)) {
269 			/* Manually enabled locations should always win out. */
270 			nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
271 			    object->nwamd_object_name);
272 			wa->winning_object = object;
273 			wa->winning_rating = UINT64_MAX;
274 		} else {
275 			nlog(LOG_DEBUG, "nwamd_loc_check: %s is disabled",
276 			    object->nwamd_object_name);
277 			if (object->nwamd_object_state != NWAM_STATE_DISABLED) {
278 				nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC,
279 				    object->nwamd_object_name,
280 				    NWAM_STATE_DISABLED,
281 				    NWAM_AUX_STATE_MANUAL_DISABLE);
282 			}
283 		}
284 
285 		return (0);
286 
287 	case NWAM_ACTIVATION_MODE_CONDITIONAL_ANY:
288 	case NWAM_ACTIVATION_MODE_CONDITIONAL_ALL:
289 		if (loc_is_enabled(loch)) {
290 			/* Manually enabled locations should always win out. */
291 			nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
292 			    object->nwamd_object_name);
293 			wa->winning_object = object;
294 			wa->winning_rating = UINT64_MAX;
295 		}
296 
297 		if (nwam_loc_get_prop_value(loch,
298 		    NWAM_LOC_PROP_CONDITIONS, &conditionval) != NWAM_SUCCESS) {
299 			nlog(LOG_ERR, "nwamd_loc_check: could not retrieve "
300 			    "condition value");
301 			return (0);
302 		}
303 		if (nwam_value_get_string_array(conditionval,
304 		    &conditions, &nelem) != NWAM_SUCCESS) {
305 			nlog(LOG_ERR, "nwamd_loc_check: could not retrieve "
306 			    "condition value");
307 			nwam_value_free(conditionval);
308 			return (0);
309 		}
310 		satisfied = nwamd_check_conditions(activation, conditions,
311 		    nelem);
312 
313 		if (satisfied) {
314 			rating = nwamd_rate_conditions(activation,
315 			    conditions, nelem);
316 			if (rating > wa->winning_rating) {
317 				wa->winning_object = object;
318 				wa->winning_rating = rating;
319 			}
320 		}
321 		nwam_value_free(conditionval);
322 		return (0);
323 
324 	case NWAM_ACTIVATION_MODE_SYSTEM:
325 		if (loc_is_enabled(loch)) {
326 			/* Manually enabled locations should always win out. */
327 			nlog(LOG_DEBUG, "nwamd_loc_check: %s is enabled",
328 			    object->nwamd_object_name);
329 			wa->winning_object = object;
330 			wa->winning_rating = UINT64_MAX;
331 		}
332 
333 		/* Either NoNet, Automatic or Legacy location, so skip. */
334 
335 		return (0);
336 	default:
337 		return (0);
338 	}
339 	/*NOTREACHED*/
340 	return (0);
341 }
342 
343 static int
344 nwamd_ncu_online_check(nwamd_object_t object, void *data)
345 {
346 	boolean_t *online = data;
347 	nwamd_ncu_t *ncu_data = object->nwamd_object_data;
348 
349 	if (ncu_data->ncu_type != NWAM_NCU_TYPE_INTERFACE)
350 		return (0);
351 
352 	if (object->nwamd_object_state == NWAM_STATE_ONLINE) {
353 		/* An online IP NCU found, stop walk */
354 		*online = B_TRUE;
355 		return (1);
356 	}
357 	return (0);
358 }
359 
360 void
361 nwamd_loc_check_conditions(void)
362 {
363 	struct nwamd_loc_check_walk_arg wa = { NULL, 0 };
364 	const char *winning_loc;
365 	boolean_t ncu_online = B_FALSE;
366 	boolean_t is_active;
367 
368 	/*
369 	 * Walk the NCUs to find out if at least one IP NCU is online.  If so,
370 	 * check the activation-mode and conditions.  If not, enable the NoNet
371 	 * location.
372 	 */
373 	(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_NCU, nwamd_ncu_online_check,
374 	    &ncu_online);
375 
376 	if (!ncu_online) {
377 		winning_loc = NWAM_LOC_NAME_NO_NET;
378 	} else {
379 		(void) nwamd_walk_objects(NWAM_OBJECT_TYPE_LOC, nwamd_loc_check,
380 		    &wa);
381 		if (wa.winning_object != NULL)
382 			winning_loc = wa.winning_object->nwamd_object_name;
383 		else
384 			winning_loc = NWAM_LOC_NAME_AUTOMATIC;
385 	}
386 	nlog(LOG_INFO, "nwamd_loc_check_conditions: winning loc is %s",
387 	    winning_loc);
388 
389 	/* If the winning location is already active, do nothing */
390 	(void) pthread_mutex_lock(&active_loc_mutex);
391 	is_active = (strcmp(active_loc, winning_loc) == 0);
392 	(void) pthread_mutex_unlock(&active_loc_mutex);
393 	if (is_active)
394 		return;
395 
396 	nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC, winning_loc,
397 	    NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_METHOD_RUNNING);
398 }
399 
400 int
401 nwamd_loc_action(const char *loc, nwam_action_t action)
402 {
403 	nwamd_event_t event = nwamd_event_init_object_action
404 	    (NWAM_OBJECT_TYPE_LOC, loc, NULL, action);
405 	if (event == NULL)
406 		return (1);
407 	nwamd_event_enqueue(event);
408 	return (0);
409 }
410 
411 /*
412  * Event handling functions.
413  */
414 
415 /* Handle loc initialization/refresh event */
416 void
417 nwamd_loc_handle_init_event(nwamd_event_t event)
418 {
419 	nwamd_object_t object;
420 	nwam_loc_handle_t loch;
421 	nwam_error_t err;
422 	boolean_t manual_disabled = B_FALSE;
423 	nwam_state_t state;
424 
425 	if ((err = nwam_loc_read(event->event_object, 0, &loch))
426 	    != NWAM_SUCCESS) {
427 		nlog(LOG_ERR, "nwamd_loc_handle_init_event: could not "
428 		    "read object '%s': %s", event->event_object,
429 		    nwam_strerror(err));
430 		nwamd_event_do_not_send(event);
431 		return;
432 	}
433 	if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC,
434 	    event->event_object)) != NULL) {
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 	manual_disabled = (loc_get_activation_mode(loch) ==
445 	    NWAM_ACTIVATION_MODE_MANUAL && !loc_is_enabled(loch));
446 	state = object->nwamd_object_state;
447 	nwamd_object_release(object);
448 
449 	/*
450 	 * If this location is ONLINE, and not manual and disabled (since in
451 	 * that case it was online but we've just set enabled = false as part
452 	 * of a disable action), then it is still active but refreshing.
453 	 * Change states to re-activate itself.
454 	 */
455 	if (!manual_disabled && state == NWAM_STATE_ONLINE) {
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
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_ERR, "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
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
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_ERR, "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.
596 		 */
597 		nwamd_loc_check_conditions();
598 		break;
599 	case NWAM_STATE_DISABLED:
600 	case NWAM_STATE_OFFLINE:
601 	case NWAM_STATE_UNINITIALIZED:
602 	case NWAM_STATE_MAINTENANCE:
603 	case NWAM_STATE_DEGRADED:
604 	default:
605 		/* do nothing */
606 		break;
607 	}
608 }
609