xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/loc.c (revision 7ab4e62e3b5c454f248a38bec0d489e8f5543324)
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
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
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
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
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
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
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
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
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
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
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
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
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
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_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