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