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