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