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 new_enabled, old_enabled = 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 old_enabled = loc_is_enabled(object->nwamd_object_handle); 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 new_enabled = loc_is_enabled(loch); 445 state = object->nwamd_object_state; 446 nwamd_object_release(object); 447 448 /* 449 * If this location is ONLINE and the value of the "enabled" property 450 * has not changed, then this location is getting refreshed because it 451 * was committed with changes. Change states to re-activate itself. 452 * If the "enabled" property has changed, then this location is 453 * getting refreshed as part of a enable/disable action and there is 454 * no need to change states here. 455 */ 456 if (state == NWAM_STATE_ONLINE && old_enabled == new_enabled) { 457 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC, 458 event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE, 459 NWAM_AUX_STATE_METHOD_RUNNING); 460 } 461 } 462 463 /* Handle loc finish event */ 464 void 465 nwamd_loc_handle_fini_event(nwamd_event_t event) 466 { 467 nwamd_object_t object; 468 469 nlog(LOG_DEBUG, "nwamd_loc_handle_fini_event(%s)", 470 event->event_object); 471 472 /* Don't disable the location, as this can enable the Automatic loc */ 473 if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC, 474 event->event_object)) == NULL) { 475 nlog(LOG_ERR, "nwamd_loc_handle_fini_event: " 476 "loc %s not found", event->event_object); 477 nwamd_event_do_not_send(event); 478 return; 479 } 480 nwamd_object_release_and_destroy(object); 481 } 482 483 void 484 nwamd_loc_handle_action_event(nwamd_event_t event) 485 { 486 nwamd_object_t object; 487 488 switch (event->event_msg->nwe_data.nwe_object_action.nwe_action) { 489 case NWAM_ACTION_ENABLE: 490 object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC, 491 event->event_object); 492 if (object == NULL) { 493 nlog(LOG_ERR, "nwamd_loc_handle_action_event: " 494 "could not find location %s", event->event_object); 495 nwamd_event_do_not_send(event); 496 return; 497 } 498 if (object->nwamd_object_state == NWAM_STATE_ONLINE) { 499 nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: " 500 "location %s already online, nothing to do", 501 event->event_object); 502 nwamd_object_release(object); 503 return; 504 } 505 nwamd_object_release(object); 506 507 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC, 508 event->event_object, NWAM_STATE_OFFLINE_TO_ONLINE, 509 NWAM_AUX_STATE_METHOD_RUNNING); 510 break; 511 case NWAM_ACTION_DISABLE: 512 object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC, 513 event->event_object); 514 if (object == NULL) { 515 nlog(LOG_ERR, "nwamd_loc_handle_action_event: " 516 "could not find location %s", event->event_object); 517 nwamd_event_do_not_send(event); 518 return; 519 } 520 if (object->nwamd_object_state == NWAM_STATE_DISABLED) { 521 nlog(LOG_DEBUG, "nwamd_loc_handle_action_event: " 522 "location %s already disabled, nothing to do", 523 event->event_object); 524 nwamd_object_release(object); 525 return; 526 } 527 nwamd_object_release(object); 528 529 nwamd_object_set_state(NWAM_OBJECT_TYPE_LOC, 530 event->event_object, NWAM_STATE_ONLINE_TO_OFFLINE, 531 NWAM_AUX_STATE_MANUAL_DISABLE); 532 break; 533 case NWAM_ACTION_ADD: 534 case NWAM_ACTION_REFRESH: 535 nwamd_loc_handle_init_event(event); 536 break; 537 case NWAM_ACTION_DESTROY: 538 nwamd_loc_handle_fini_event(event); 539 break; 540 default: 541 nlog(LOG_INFO, "nwam_loc_handle_action_event: " 542 "unexpected action"); 543 break; 544 } 545 } 546 547 void 548 nwamd_loc_handle_state_event(nwamd_event_t event) 549 { 550 nwamd_object_t object; 551 nwam_state_t new_state; 552 nwam_aux_state_t new_aux_state; 553 554 if ((object = nwamd_object_find(NWAM_OBJECT_TYPE_LOC, 555 event->event_object)) == NULL) { 556 nlog(LOG_ERR, "nwamd_loc_handle_state_event: " 557 "state event for nonexistent loc %s", event->event_object); 558 nwamd_event_do_not_send(event); 559 return; 560 } 561 new_state = event->event_msg->nwe_data.nwe_object_state.nwe_state; 562 new_aux_state = 563 event->event_msg->nwe_data.nwe_object_state.nwe_aux_state; 564 565 if (new_state == object->nwamd_object_state && 566 new_aux_state == object->nwamd_object_aux_state) { 567 nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: " 568 "loc %s already in state (%s , %s)", 569 object->nwamd_object_name, 570 nwam_state_to_string(new_state), 571 nwam_aux_state_to_string(new_aux_state)); 572 nwamd_object_release(object); 573 return; 574 } 575 576 object->nwamd_object_state = new_state; 577 object->nwamd_object_aux_state = new_aux_state; 578 579 nlog(LOG_DEBUG, "nwamd_loc_handle_state_event: changing state for loc " 580 "%s to (%s , %s)", object->nwamd_object_name, 581 nwam_state_to_string(object->nwamd_object_state), 582 nwam_aux_state_to_string(object->nwamd_object_aux_state)); 583 584 nwamd_object_release(object); 585 586 /* 587 * State machine for location. 588 */ 589 switch (new_state) { 590 case NWAM_STATE_OFFLINE_TO_ONLINE: 591 nwamd_loc_activate(event->event_object); 592 break; 593 case NWAM_STATE_ONLINE_TO_OFFLINE: 594 /* 595 * Don't need to deactivate current location - condition check 596 * will activate another. If the currently active location is 597 * being deactivated, then it is being manually deactivated; 598 * so also clear active_loc so condition checking is not 599 * confused. 600 */ 601 (void) pthread_mutex_lock(&active_loc_mutex); 602 if (strcmp(event->event_object, active_loc) == 0) 603 active_loc[0] = '\0'; 604 (void) pthread_mutex_unlock(&active_loc_mutex); 605 nwamd_loc_check_conditions(); 606 break; 607 case NWAM_STATE_DISABLED: 608 case NWAM_STATE_OFFLINE: 609 case NWAM_STATE_UNINITIALIZED: 610 case NWAM_STATE_MAINTENANCE: 611 case NWAM_STATE_DEGRADED: 612 default: 613 /* do nothing */ 614 break; 615 } 616 } 617