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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * This file contains the routines that manipulate Link Layer Profiles 29 * (aka LLPs) and various support functions. This includes parsing and 30 * updating of the /etc/nwam/llp file. 31 * 32 * The daemon maintains a list of llp_t structures that represent the 33 * provided configuration information for a link. After the llp file 34 * is read, entries are added to the LLP list for any known links 35 * (identified by checking the interface list, which is based on the 36 * v4 interfaces present after 'ifconfig -a plumb') which were not 37 * represented in the llp file. These entries contain the default 38 * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the 39 * v4 interface, and accept router- and DHCPv6-assigned addresses on 40 * the v6 interface. The default entries created by the daemon are 41 * also added to the llp file. 42 * 43 * LLP priority is assigned based on two factors: the order within 44 * the llp file, with earlier entries having higher priority; and 45 * a preference for wired interfaces before wireless. Entries that 46 * are added to the file by the daemon are added *after* any existing 47 * entries; within the added block, wired entries are added before 48 * wireless. Thus if the llp file is never modified externally, wired 49 * will generally be ordered before wireless. However, if the 50 * administrator creates the file with wireless entries before wired, 51 * that priority order will be respected. 52 * 53 * The llp list is protected by the global llp_lock, which must be 54 * pthread_mutex_lock()'d before reading or writing the list. Only the main 55 * thread can write to the list; this allows the main thread to deal with read 56 * access to structure pointers without holding locks and without the 57 * complexity of reference counts. All other threads must hold llp_lock for 58 * the duration of any read access to the data, and must not deal directly in 59 * structure pointers. (A thread may also hold machine_lock to block the main 60 * thread entirely in order to manipulate the data; such use is isolated to the 61 * door interface.) 62 * 63 * Functions in this file have comments noting where the main thread alone is 64 * the caller. These functions do not need to acquire the lock. 65 * 66 * If you hold both ifs_lock and llp_lock, you must take ifs_lock first. 67 */ 68 69 #include <stdio.h> 70 #include <stdlib.h> 71 #include <unistd.h> 72 #include <limits.h> 73 #include <strings.h> 74 #include <string.h> 75 #include <ctype.h> 76 #include <errno.h> 77 #include <assert.h> 78 #include <sys/types.h> 79 #include <sys/stat.h> 80 #include <syslog.h> 81 #include <netinet/in.h> 82 #include <arpa/inet.h> 83 #include <atomic.h> 84 #include <pthread.h> 85 #include <signal.h> 86 87 #include "defines.h" 88 #include "structures.h" 89 #include "functions.h" 90 #include "variables.h" 91 92 /* Lock to protect the llp list. */ 93 static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER; 94 95 /* Accessed only from main thread or with llp_lock held */ 96 llp_t *link_layer_profile; 97 98 static struct qelem llp_list; 99 static llp_t *locked_llp; 100 101 /* 102 * Global variable to hold the highest priority. Need to use the atomic 103 * integer arithmetic functions to update it. 104 */ 105 static uint32_t llp_highest_pri; 106 107 static void print_llp_list(void); 108 109 void 110 initialize_llp(void) 111 { 112 llp_list.q_forw = llp_list.q_back = &llp_list; 113 } 114 115 char * 116 llp_prnm(llp_t *llp) 117 { 118 if (llp == NULL) 119 return ("null_llp"); 120 else if (llp->llp_lname == NULL) 121 return ("null_lname"); 122 else 123 return (llp->llp_lname); 124 } 125 126 /* 127 * This function removes a given LLP from the global list and discards it. 128 * Called only from the main thread. 129 */ 130 void 131 llp_delete(llp_t *llp) 132 { 133 if (pthread_mutex_lock(&llp_lock) == 0) { 134 if (llp == locked_llp) 135 locked_llp = NULL; 136 assert(llp != link_layer_profile); 137 remque(&llp->llp_links); 138 (void) pthread_mutex_unlock(&llp_lock); 139 free(llp->llp_ipv6addrstr); 140 free(llp->llp_ipv4addrstr); 141 free(llp); 142 } 143 } 144 145 static void 146 llp_list_free(void) 147 { 148 int retv; 149 llp_t *llp; 150 151 locked_llp = NULL; 152 if ((retv = pthread_mutex_lock(&llp_lock)) != 0) { 153 /* Something very serious is wrong... */ 154 syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %s", 155 strerror(retv)); 156 return; 157 } 158 while (llp_list.q_forw != &llp_list) { 159 llp = (llp_t *)llp_list.q_forw; 160 remque(&llp->llp_links); 161 free(llp->llp_ipv6addrstr); 162 free(llp->llp_ipv4addrstr); 163 free(llp); 164 } 165 (void) pthread_mutex_unlock(&llp_lock); 166 } 167 168 /* 169 * Called either from main thread or with llp_lock held. 170 */ 171 llp_t * 172 llp_lookup(const char *link) 173 { 174 llp_t *llp; 175 176 if (link == NULL) 177 return (NULL); 178 179 for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; 180 llp = (llp_t *)llp->llp_links.q_forw) { 181 if (strcmp(link, llp->llp_lname) == 0) 182 break; 183 } 184 if (llp == (llp_t *)&llp_list) 185 llp = NULL; 186 return (llp); 187 } 188 189 /* 190 * Choose the higher priority llp of the two passed in. If one is 191 * NULL, the other will be higher priority. If both are NULL, NULL 192 * is returned. 193 * 194 * Assumes that both are available (i.e. doesn't check IFF_RUNNING 195 * or IF_DHCPFAILED flag values). 196 */ 197 llp_t * 198 llp_high_pri(llp_t *a, llp_t *b) 199 { 200 if (a == NULL || a->llp_links.q_forw == NULL) 201 return (b); 202 else if (b == NULL || b->llp_links.q_forw == NULL) 203 return (a); 204 205 /* Check for locked LLP selection for user interface */ 206 if (a == locked_llp) 207 return (a); 208 else if (b == locked_llp) 209 return (b); 210 211 if (a->llp_failed && !b->llp_failed) 212 return (b); 213 if (!a->llp_failed && b->llp_failed) 214 return (a); 215 216 /* 217 * Higher priority is represented by a lower number. This seems a 218 * bit backwards, but for now it makes assigning priorities very easy. 219 */ 220 return ((a->llp_pri <= b->llp_pri) ? a : b); 221 } 222 223 /* 224 * Chooses the highest priority link that corresponds to an available 225 * interface. Called only in the main thread. 226 */ 227 llp_t * 228 llp_best_avail(void) 229 { 230 llp_t *llp, *rtnllp; 231 232 if ((rtnllp = locked_llp) == NULL) { 233 for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; 234 llp = (llp_t *)llp->llp_links.q_forw) { 235 if (is_interface_ok(llp->llp_lname)) 236 rtnllp = llp_high_pri(llp, rtnllp); 237 } 238 } 239 240 return (rtnllp); 241 } 242 243 /* 244 * Called only by the main thread. Note that this leaves link_layer_profile 245 * set to NULL only in the case of abject failure, and then leaves llp_failed 246 * set. 247 */ 248 static void 249 llp_activate(llp_t *llp) 250 { 251 char *host; 252 /* 253 * Choosing "dhcp" as a hostname is unsupported right now. 254 * We use hostname="dhcp" as a keyword telling bringupinterface() 255 * to use dhcp on the interface. 256 */ 257 char *dhcpstr = "dhcp"; 258 259 assert(link_layer_profile == NULL); 260 261 host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr : 262 llp->llp_ipv4addrstr; 263 264 report_llp_selected(llp->llp_lname); 265 switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr, 266 llp->llp_ipv6onlink)) { 267 case SUCCESS: 268 llp->llp_failed = B_FALSE; 269 llp->llp_waiting = B_FALSE; 270 link_layer_profile = llp; 271 dprintf("llp_activate: activated llp for %s", llp_prnm(llp)); 272 break; 273 case FAILURE: 274 llp->llp_failed = B_TRUE; 275 llp->llp_waiting = B_FALSE; 276 dprintf("llp_activate: failed to bring up %s", llp_prnm(llp)); 277 report_llp_unselected(llp->llp_lname, dcFailed); 278 link_layer_profile = NULL; 279 break; 280 case WAITING: 281 llp->llp_failed = B_FALSE; 282 llp->llp_waiting = B_TRUE; 283 link_layer_profile = llp; 284 dprintf("llp_activate: waiting for %s", llp_prnm(llp)); 285 } 286 } 287 288 /* 289 * Replace the currently active link layer profile with the one 290 * specified. And since we're changing the lower layer stuff, 291 * we need to first deactivate the current upper layer profile. 292 * An upper layer profile will be reactivated later, when we get 293 * confirmation that the new llp is fully up (has an address 294 * assigned). 295 * 296 * If the new llp is the same as the currently active one, don't 297 * do anything. 298 * 299 * Called only by the main thread. 300 */ 301 void 302 llp_swap(llp_t *newllp, libnwam_diag_cause_t cause) 303 { 304 int minpri; 305 306 if (newllp == link_layer_profile) 307 return; 308 309 deactivate_upper_layer_profile(); 310 311 if (link_layer_profile != NULL) { 312 dprintf("taking down current link layer profile (%s)", 313 llp_prnm(link_layer_profile)); 314 report_llp_unselected(link_layer_profile->llp_lname, cause); 315 link_layer_profile->llp_waiting = B_FALSE; 316 link_layer_profile = NULL; 317 } 318 319 /* 320 * Establish the new link layer profile. If we have trouble setting 321 * it, then try to get another. Note that llp_activate sets llp_failed 322 * on failure, so this loop is guaranteed to terminate. 323 */ 324 while (newllp != NULL) { 325 dprintf("bringing up new link layer profile (%s)", 326 llp_prnm(newllp)); 327 llp_activate(newllp); 328 newllp = NULL; 329 if (link_layer_profile == NULL && 330 (newllp = llp_best_avail()) != NULL && 331 newllp->llp_failed) 332 newllp = NULL; 333 } 334 335 /* 336 * Knock down all interfaces that are at a lower (higher-numbered) 337 * priority than the new one. If there isn't a new one, then leave 338 * everything as it is. 339 */ 340 if (link_layer_profile == NULL) { 341 minpri = -1; 342 if (locked_llp != NULL) 343 dprintf("taking down all but %s", llp_prnm(locked_llp)); 344 } else { 345 minpri = link_layer_profile->llp_pri; 346 dprintf("taking down remaining interfaces below priority %d", 347 minpri); 348 } 349 for (newllp = (llp_t *)llp_list.q_forw; newllp != (llp_t *)&llp_list; 350 newllp = (llp_t *)newllp->llp_links.q_forw) { 351 if (newllp == link_layer_profile) 352 continue; 353 if ((link_layer_profile != NULL && newllp->llp_pri > minpri) || 354 (locked_llp != NULL && newllp != locked_llp)) 355 takedowninterface(newllp->llp_lname, cause); 356 else 357 clear_cached_address(newllp->llp_lname); 358 } 359 } 360 361 /* 362 * Create the named LLP with default settings. Called only in main thread. 363 */ 364 llp_t * 365 llp_add(const char *name) 366 { 367 int retv; 368 llp_t *llp; 369 370 if ((llp = calloc(1, sizeof (llp_t))) == NULL) { 371 syslog(LOG_ERR, "cannot allocate LLP: %m"); 372 return (NULL); 373 } 374 375 if (strlcpy(llp->llp_lname, name, sizeof (llp->llp_lname)) >= 376 sizeof (llp->llp_lname)) { 377 syslog(LOG_ERR, "llp: link name '%s' too long; ignoring entry", 378 name); 379 free(llp); 380 return (NULL); 381 } 382 383 llp->llp_fileorder = llp->llp_pri = 384 atomic_add_32_nv(&llp_highest_pri, 1); 385 llp->llp_ipv4src = IPV4SRC_DHCP; 386 llp->llp_type = find_if_type(llp->llp_lname); 387 llp->llp_ipv6onlink = B_TRUE; 388 389 /* 390 * should be a no-op, but for now, make sure we only 391 * create llps for wired and wireless interfaces. 392 */ 393 if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) { 394 syslog(LOG_ERR, "llp: wrong type of interface for %s", name); 395 free(llp); 396 return (NULL); 397 } 398 399 if ((retv = pthread_mutex_lock(&llp_lock)) != 0) { 400 /* Something very serious is wrong... */ 401 syslog(LOG_ERR, "llp: cannot lock mutex: %s", strerror(retv)); 402 free(llp); 403 return (NULL); 404 } 405 406 insque(&llp->llp_links, llp_list.q_back); 407 408 (void) pthread_mutex_unlock(&llp_lock); 409 410 dprintf("created llp for link %s, priority %d", llp->llp_lname, 411 llp->llp_pri); 412 return (llp); 413 } 414 415 /* 416 * Walker function to pass to walk_interface() to find out if 417 * an interface description is missing from LLPFILE. If it is, 418 * add it. 419 * 420 * Currently, IF_TUN type interfaces are special-cased: they are 421 * only handled as user-enabled, layered links (which may be created 422 * as part of a higher-layer profile, for example). Thus, they 423 * shouldn't be considered when looking at the llp list, so don't 424 * add them here. 425 * 426 * ifs_lock is held when this function is called. Called only in main thread. 427 */ 428 static void 429 find_and_add_llp(struct interface *ifp, void *arg) 430 { 431 FILE *fp = arg; 432 433 if (ifp->if_type != IF_TUN && llp_lookup(ifp->if_name) == NULL) { 434 switch (ifp->if_family) { 435 case AF_INET: 436 (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name); 437 break; 438 439 case AF_INET6: 440 (void) fprintf(fp, "%s\tipv6\n", ifp->if_name); 441 break; 442 443 default: 444 syslog(LOG_ERR, "interface %s family %d?!", 445 ifp->if_name, ifp->if_family); 446 return; 447 } 448 dprintf("Added %s to %s", ifp->if_name, LLPFILE); 449 /* If we run out of memory, ignore this interface for now. */ 450 (void) llp_add(ifp->if_name); 451 } 452 } 453 454 static void 455 print_llp_list(void) 456 { 457 llp_t *wp; 458 459 dprintf("Walking llp list"); 460 for (wp = (llp_t *)llp_list.q_forw; wp != (llp_t *)&llp_list; 461 wp = (llp_t *)wp->llp_links.q_forw) 462 dprintf("==> %s", wp->llp_lname); 463 } 464 465 /* 466 * This function parses /etc/nwam/llp which describes the phase 0 link layer 467 * profile. The file is line oriented with each line containing tab or space 468 * delimited fields. Each address family (IPv4, IPv6) is described on a 469 * separate line. 470 * The first field is a link name. 471 * The second field can be either static, dhcp, ipv6, noipv6, or priority. 472 * If the second field is static then the next field is an ipv4 address which 473 * can contain a prefix. Previous versions of this file could contain a 474 * hostname in this field which is no longer supported. 475 * If the second field is dhcp then dhcp will be used on the interface. 476 * If the second field is ipv6 then an ipv6 interface is plumbed up. The 477 * outcome of this is that if offered by the network in.ndpd and dhcp 478 * will conspire to put addresses on additional ipv6 logical interfaces. 479 * If the next field is non-null then it is taken to be an IPv6 address 480 * and possible prefix which are applied to the interface. 481 * If the second field is noipv6 then no ipv6 interfaces will be put on that 482 * link. 483 * If the second field is priority, then the next field is an integer 484 * specifying the link priority. 485 * 486 * Called only in main thread. 487 */ 488 void 489 llp_parse_config(void) 490 { 491 static const char STATICSTR[] = "static"; 492 static const char DHCP[] = "dhcp"; 493 static const char IPV6[] = "ipv6"; 494 static const char NOIPV6[] = "noipv6"; 495 static const char PRIORITY[] = "priority"; 496 FILE *fp; 497 char line[LINE_MAX]; 498 char *cp, *lasts, *lstr, *srcstr, *addrstr; 499 int lnum; 500 llp_t *llp; 501 502 /* Create the NWAM directory in case it does not exist. */ 503 if (mkdir(LLPDIRNAME, LLPDIRMODE) != 0 && 504 errno != EEXIST) { 505 syslog(LOG_ERR, "could not create %s: %m", LLPDIRNAME); 506 return; 507 } 508 509 fp = fopen(LLPFILE, "r+"); 510 if (fp == NULL) { 511 if (errno != ENOENT) { 512 syslog(LOG_ERR, "open LLP config file: %m"); 513 return; 514 } 515 if ((fp = fopen(LLPFILE, "w+")) == NULL) { 516 syslog(LOG_ERR, "create LLP config file: %m"); 517 return; 518 } 519 } 520 521 llp_list_free(); 522 523 for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) { 524 if (line[strlen(line) - 1] == '\n') 525 line[strlen(line) - 1] = '\0'; 526 527 cp = line; 528 while (isspace(*cp)) 529 cp++; 530 531 if (*cp == '#' || *cp == '\0') 532 continue; 533 534 dprintf("parsing llp conf file line %d...", lnum); 535 536 if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) || 537 ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) { 538 syslog(LOG_ERR, "llp:%d: not enough tokens; " 539 "ignoring entry", lnum); 540 continue; 541 } 542 543 if ((llp = llp_lookup(lstr)) == NULL && 544 (llp = llp_add(lstr)) == NULL) { 545 syslog(LOG_ERR, "llp:%d: cannot add entry", lnum); 546 continue; 547 } 548 549 if (strcasecmp(srcstr, STATICSTR) == 0) { 550 if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL || 551 atoi(addrstr) == 0) { /* crude check for number */ 552 syslog(LOG_ERR, "llp:%d: missing ipaddr " 553 "for static config", lnum); 554 } else if ((addrstr = strdup(addrstr)) == NULL) { 555 syslog(LOG_ERR, "llp:%d: cannot save address", 556 lnum); 557 } else { 558 free(llp->llp_ipv4addrstr); 559 llp->llp_ipv4src = IPV4SRC_STATIC; 560 llp->llp_ipv4addrstr = addrstr; 561 } 562 563 } else if (strcasecmp(srcstr, DHCP) == 0) { 564 llp->llp_ipv4src = IPV4SRC_DHCP; 565 566 } else if (strcasecmp(srcstr, IPV6) == 0) { 567 llp->llp_ipv6onlink = B_TRUE; 568 if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) { 569 (void) 0; 570 } else if ((addrstr = strdup(addrstr)) == NULL) { 571 syslog(LOG_ERR, "llp:%d: cannot save address", 572 lnum); 573 } else { 574 free(llp->llp_ipv6addrstr); 575 llp->llp_ipv6addrstr = addrstr; 576 } 577 578 } else if (strcasecmp(srcstr, NOIPV6) == 0) { 579 llp->llp_ipv6onlink = B_FALSE; 580 581 } else if (strcasecmp(srcstr, PRIORITY) == 0) { 582 if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL) { 583 syslog(LOG_ERR, 584 "llp:%d: missing priority value", lnum); 585 } else { 586 llp->llp_pri = atoi(addrstr); 587 } 588 589 } else { 590 syslog(LOG_ERR, "llp:%d: unrecognized field '%s'", lnum, 591 srcstr); 592 } 593 } 594 595 /* 596 * So we have read in the llp file, is there an interface which 597 * it does not describe? If yes, we'd better add it to the 598 * file for future reference. 599 */ 600 walk_interface(find_and_add_llp, fp); 601 602 (void) fclose(fp); 603 604 print_llp_list(); 605 } 606 607 /* 608 * Called only from the main thread. 609 */ 610 void 611 llp_add_file(const llp_t *llp) 612 { 613 FILE *fp; 614 615 if ((fp = fopen(LLPFILE, "a")) == NULL) 616 return; 617 (void) fprintf(fp, "%s\tdhcp\n", llp->llp_lname); 618 (void) fclose(fp); 619 } 620 621 /* 622 * This function rewrites the LLP configuration file entry for a given 623 * interface and keyword. If the keyword is present, then it is updated if 624 * removeonly is B_FALSE, otherwise it's removed. If the keyword is not 625 * present, then it is added immediately after the last entry for that 626 * interface if removeonly is B_FALSE, otherwise no action is taken. User 627 * comments are preserved. 628 * 629 * To preserve file integrity, this is called only from the main thread. 630 */ 631 static void 632 llp_update_config(const char *ifname, const char *keyword, const char *optval, 633 boolean_t removeonly) 634 { 635 FILE *fpin, *fpout; 636 char line[LINE_MAX]; 637 char *cp, *lstr, *keystr, *valstr, *lasts; 638 boolean_t matched_if, copying; 639 long match_pos; 640 641 if ((fpin = fopen(LLPFILE, "r")) == NULL) 642 return; 643 if ((fpout = fopen(LLPFILETMP, "w")) == NULL) { 644 syslog(LOG_ERR, "create LLP temporary config file: %m"); 645 (void) fclose(fpin); 646 return; 647 } 648 matched_if = copying = B_FALSE; 649 restart: 650 while (fgets(line, sizeof (line), fpin) != NULL) { 651 cp = line + strlen(line) - 1; 652 if (cp >= line && *cp == '\n') 653 *cp = '\0'; 654 655 cp = line; 656 while (isspace(*cp)) 657 cp++; 658 659 lstr = NULL; 660 if (copying || *cp == '#' || 661 (lstr = strtok_r(cp, " \t", &lasts)) == NULL || 662 strcmp(lstr, ifname) != 0) { 663 if (!matched_if || copying) { 664 /* 665 * It's ugly to write through the pointer 666 * returned as the third argument of strtok_r, 667 * but doing so saves a data copy. 668 */ 669 if (lstr != NULL && lasts != NULL) 670 lasts[-1] = '\t'; 671 (void) fprintf(fpout, "%s\n", line); 672 } 673 continue; 674 } 675 676 if (lasts != NULL) 677 lasts[-1] = '\t'; 678 679 /* 680 * If we've found the keyword, then process removal or update 681 * of the value. 682 */ 683 if ((keystr = strtok_r(NULL, " \t", &lasts)) != NULL && 684 strcmp(keystr, keyword) == 0) { 685 matched_if = copying = B_TRUE; 686 if (removeonly) 687 continue; 688 valstr = strtok_r(NULL, " \t", &lasts); 689 if ((valstr == NULL && optval == NULL) || 690 (valstr != NULL && optval != NULL && 691 strcmp(valstr, optval) == 0)) { 692 /* Value identical; abort update */ 693 goto no_change; 694 } 695 if (optval == NULL) { 696 (void) fprintf(fpout, "%s\t%s\n", ifname, 697 keyword); 698 } else { 699 (void) fprintf(fpout, "%s\t%s %s\n", ifname, 700 keyword, optval); 701 } 702 continue; 703 } 704 705 /* Otherwise, record the last possible insertion point */ 706 matched_if = B_TRUE; 707 match_pos = ftell(fpin); 708 if (lasts != NULL) 709 lasts[-1] = '\t'; 710 (void) fprintf(fpout, "%s\n", line); 711 } 712 if (!copying) { 713 /* keyword not encountered; we're done if deleting */ 714 if (removeonly) 715 goto no_change; 716 /* need to add keyword and value */ 717 if (optval == NULL) { 718 (void) fprintf(fpout, "%s\t%s\n", ifname, keyword); 719 } else { 720 (void) fprintf(fpout, "%s\t%s %s\n", ifname, keyword, 721 optval); 722 } 723 /* copy the rest of the file */ 724 (void) fseek(fpin, match_pos, SEEK_SET); 725 copying = B_TRUE; 726 goto restart; 727 } 728 (void) fclose(fpin); 729 (void) fclose(fpout); 730 if (rename(LLPFILETMP, LLPFILE) != 0) { 731 syslog(LOG_ERR, "rename LLP temporary config file: %m"); 732 (void) unlink(LLPFILETMP); 733 } 734 return; 735 736 no_change: 737 (void) fclose(fpin); 738 (void) fclose(fpout); 739 (void) unlink(LLPFILETMP); 740 } 741 742 /* 743 * This is called back from the main thread by the state machine. 744 */ 745 void 746 llp_write_changed_priority(llp_t *llp) 747 { 748 if (llp->llp_pri == llp->llp_fileorder) { 749 llp_update_config(llp->llp_lname, "priority", NULL, B_TRUE); 750 } else { 751 char prival[32]; 752 753 (void) snprintf(prival, sizeof (prival), "%d", llp->llp_pri); 754 llp_update_config(llp->llp_lname, "priority", prival, B_FALSE); 755 } 756 } 757 758 /* 759 * Called by the door interface: set LLP priority and schedule an LLP update if 760 * this interface has changed. 761 */ 762 int 763 set_llp_priority(const char *ifname, int prio) 764 { 765 llp_t *llp; 766 int retv; 767 768 if (prio < 0) 769 return (EINVAL); 770 771 if ((retv = pthread_mutex_lock(&llp_lock)) != 0) 772 return (retv); 773 if ((llp = llp_lookup(ifname)) != NULL) { 774 llp->llp_failed = B_FALSE; 775 if (llp->llp_pri != prio) { 776 llp->llp_pri = prio; 777 (void) np_queue_add_event(EV_USER, ifname); 778 } 779 retv = 0; 780 } else { 781 retv = ENXIO; 782 } 783 (void) pthread_mutex_unlock(&llp_lock); 784 return (retv); 785 } 786 787 /* 788 * Called by the door interface: set a locked LLP and schedule an LLP update if 789 * the locked LLP has changed. 790 */ 791 int 792 set_locked_llp(const char *ifname) 793 { 794 llp_t *llp; 795 int retv; 796 797 if ((retv = pthread_mutex_lock(&llp_lock)) != 0) 798 return (retv); 799 if (ifname[0] == '\0') { 800 if (locked_llp != NULL) { 801 ifname = locked_llp->llp_lname; 802 locked_llp = NULL; 803 (void) np_queue_add_event(EV_USER, ifname); 804 } 805 } else if ((llp = llp_lookup(ifname)) != NULL) { 806 locked_llp = llp; 807 if (llp != link_layer_profile) 808 (void) np_queue_add_event(EV_USER, ifname); 809 } else { 810 retv = ENXIO; 811 } 812 (void) pthread_mutex_unlock(&llp_lock); 813 return (retv); 814 } 815 816 /* Copy string to pre-allocated buffer. */ 817 static void 818 strinsert(char **dest, const char *src, char **buf) 819 { 820 if (*dest != NULL) { 821 *dest = strcpy(*buf, src); 822 *buf += strlen(src) + 1; 823 } 824 } 825 826 /* 827 * Sample the list of LLPs and copy to a single buffer for return through the 828 * door interface. 829 */ 830 llp_t * 831 get_llp_list(size_t *lsize, uint_t *countp, char *selected, char *locked) 832 { 833 llp_t *llplist, *llpl, *llp; 834 char *strptr; 835 uint_t nllp; 836 size_t strspace; 837 int retv; 838 839 *lsize = 0; 840 if ((retv = pthread_mutex_lock(&llp_lock)) != 0) { 841 errno = retv; 842 return (NULL); 843 } 844 (void) strlcpy(selected, link_layer_profile == NULL ? "" : 845 link_layer_profile->llp_lname, LIFNAMSIZ); 846 (void) strlcpy(locked, locked_llp == NULL ? "" : 847 locked_llp->llp_lname, LIFNAMSIZ); 848 nllp = 0; 849 strspace = 0; 850 for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; 851 llp = (llp_t *)llp->llp_links.q_forw) { 852 nllp++; 853 if (llp->llp_ipv4addrstr != NULL) 854 strspace += strlen(llp->llp_ipv4addrstr) + 1; 855 if (llp->llp_ipv6addrstr != NULL) 856 strspace += strlen(llp->llp_ipv6addrstr) + 1; 857 } 858 *countp = nllp; 859 /* Note that malloc doesn't guarantee a NULL return for zero count */ 860 llplist = nllp == 0 ? NULL : 861 malloc(sizeof (*llplist) * nllp + strspace); 862 if (llplist != NULL) { 863 *lsize = sizeof (*llplist) * nllp + strspace; 864 llpl = llplist; 865 strptr = (char *)(llplist + nllp); 866 for (llp = (llp_t *)llp_list.q_forw; llp != (llp_t *)&llp_list; 867 llp = (llp_t *)llp->llp_links.q_forw) { 868 *llpl = *llp; 869 strinsert(&llpl->llp_ipv4addrstr, llp->llp_ipv4addrstr, 870 &strptr); 871 strinsert(&llpl->llp_ipv6addrstr, llp->llp_ipv6addrstr, 872 &strptr); 873 llpl++; 874 } 875 } 876 (void) pthread_mutex_unlock(&llp_lock); 877 878 /* Add in the special door-only state flags */ 879 llpl = llplist; 880 while (nllp-- > 0) { 881 get_interface_state(llpl->llp_lname, &llpl->llp_dhcp_failed, 882 &llpl->llp_link_up); 883 if (llpl->llp_type == IF_WIRELESS) { 884 get_wireless_state(llpl->llp_lname, 885 &llpl->llp_need_wlan, &llpl->llp_need_key); 886 } 887 llpl++; 888 } 889 return (llplist); 890 } 891 892 /* 893 * This is called for the special case when there are outstanding requests sent 894 * to the user interface, and the user interface disappears. We handle this 895 * case by re-running bringupinterface() without deselecting. That function 896 * will call the wireless and DHCP-related parts again, and they should proceed 897 * in automatic mode, because the UI is now gone. 898 * 899 * Called only by the main thread or by a thread holding machine_lock. 900 */ 901 void 902 llp_reselect(void) 903 { 904 llp_t *llp; 905 const char *host; 906 907 /* 908 * If there's no active profile, or if the active profile isn't waiting 909 * on the UI, then just return; nothing to do. 910 */ 911 if ((llp = link_layer_profile) == NULL || !llp->llp_waiting) 912 return; 913 914 host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? "dhcp" : 915 llp->llp_ipv4addrstr; 916 917 dprintf("llp_reselect: bringing up %s", llp_prnm(llp)); 918 switch (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr, 919 llp->llp_ipv6onlink)) { 920 case SUCCESS: 921 llp->llp_failed = B_FALSE; 922 llp->llp_waiting = B_FALSE; 923 dprintf("llp_reselect: activated llp for %s", llp_prnm(llp)); 924 break; 925 case FAILURE: 926 llp->llp_failed = B_TRUE; 927 llp->llp_waiting = B_FALSE; 928 dprintf("llp_reselect: failed to bring up %s", llp_prnm(llp)); 929 report_llp_unselected(llp->llp_lname, dcFailed); 930 link_layer_profile = NULL; 931 break; 932 case WAITING: 933 llp->llp_failed = B_FALSE; 934 dprintf("llp_reselect: waiting for %s", llp_prnm(llp)); 935 } 936 } 937 938 /* 939 * This is used by the wireless module to check on the selected LLP. We don't 940 * do periodic rescans if a wireless interface is current and if its connection 941 * state is good. 942 */ 943 void 944 llp_get_name_and_type(char *ifname, size_t ifnlen, 945 libnwam_interface_type_t *iftype) 946 { 947 *ifname = '\0'; 948 *iftype = IF_UNKNOWN; 949 950 if (pthread_mutex_lock(&llp_lock) == 0) { 951 if (link_layer_profile != NULL) { 952 (void) strlcpy(ifname, link_layer_profile->llp_lname, 953 ifnlen); 954 *iftype = link_layer_profile->llp_type; 955 } 956 (void) pthread_mutex_unlock(&llp_lock); 957 } 958 } 959 960 /* 961 * This is called by the interface.c module to check if an interface needs to 962 * run DHCP. It's intentionally called without ifs_lock held. 963 */ 964 libnwam_ipv4src_t 965 llp_get_ipv4src(const char *ifname) 966 { 967 libnwam_ipv4src_t src = IPV4SRC_DHCP; 968 llp_t *llp; 969 970 if (pthread_mutex_lock(&llp_lock) == 0) { 971 if ((llp = llp_lookup(ifname)) != NULL) 972 src = llp->llp_ipv4src; 973 (void) pthread_mutex_unlock(&llp_lock); 974 } 975 return (src); 976 } 977 978 /* 979 * Dump out the LLP state via debug messages. 980 */ 981 void 982 print_llp_status(void) 983 { 984 llp_t *llp; 985 986 if (pthread_mutex_lock(&llp_lock) == 0) { 987 if (link_layer_profile == NULL) 988 dprintf("no LLP selected"); 989 else 990 dprintf("LLP %s selected", 991 link_layer_profile->llp_lname); 992 if (locked_llp == NULL) 993 dprintf("no LLP locked"); 994 else 995 dprintf("LLP %s locked", locked_llp->llp_lname); 996 for (llp = (llp_t *)llp_list.q_forw; 997 llp != (llp_t *)&llp_list; 998 llp = (llp_t *)llp->llp_links.q_forw) { 999 dprintf("LLP %s pri %d file order %d type %d " 1000 "%sfailed %swaiting src %d v4addr %s v6addr %s " 1001 "v6 %son-link", 1002 llp->llp_lname, llp->llp_pri, llp->llp_fileorder, 1003 (int)llp->llp_type, llp->llp_failed ? "" : "not ", 1004 llp->llp_waiting ? "" : "not ", 1005 (int)llp->llp_ipv4src, 1006 STRING(llp->llp_ipv4addrstr), 1007 STRING(llp->llp_ipv6addrstr), 1008 llp->llp_ipv6onlink ? "not " : ""); 1009 } 1010 (void) pthread_mutex_unlock(&llp_lock); 1011 } 1012 } 1013