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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This file contains the routines that manipulate Link Layer Profiles 31 * (aka LLPs) and various support functions. This includes parsing and 32 * updating of the /etc/nwam/llp file. 33 * 34 * The daemon maintains a list of llp_t structures that represent the 35 * provided configuration information for a link. After the llp file 36 * is read, entries are added to the LLP list for any known links 37 * (identified by checking the interface list, which is based on the 38 * v4 interfaces present after 'ifconfig -a plumb') which were not 39 * represented in the llp file. These entries contain the default 40 * "automatic" settings: plumb both IPv4 and IPv6, use DHCP on the 41 * v4 interface, and accept router- and DHCPv6-assigned addresses on 42 * the v6 interface. The default entries created by the daemon are 43 * also added to the llp file. 44 * 45 * LLP priority is assigned based on two factors: the order within 46 * the llp file, with earlier entries having higher priority; and 47 * a preference for wired interfaces before wireless. Entries that 48 * are added to the file by the daemon are added *after* any existing 49 * entries; within the added block, wired entries are added before 50 * wireless. Thus if the llp file is never modified externally, wired 51 * will generally be ordered before wireless. However, if the 52 * administrator creates the file with wireless entries before wired, 53 * that priority order will be respected. 54 * 55 * The llp list (pointed to by the global llp_head) is protected by 56 * the global llp_lock, which should be pthread_mutex_lock()'d before 57 * reading or writing the list. 58 */ 59 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <limits.h> 63 #include <strings.h> 64 #include <string.h> 65 #include <ctype.h> 66 #include <errno.h> 67 #include <assert.h> 68 #include <sys/types.h> 69 #include <sys/stat.h> 70 #include <syslog.h> 71 #include <netinet/in.h> 72 #include <arpa/inet.h> 73 #include <atomic.h> 74 #include <pthread.h> 75 #include <signal.h> 76 77 #include "defines.h" 78 #include "structures.h" 79 #include "functions.h" 80 #include "variables.h" 81 82 /* Lock to protect the llp list. */ 83 static pthread_mutex_t llp_lock = PTHREAD_MUTEX_INITIALIZER; 84 85 llp_t *llp_head = NULL; 86 llp_t *link_layer_profile = NULL; 87 88 /* 89 * Global variable to hold the highest priority. Need to use the atomic 90 * integer arithmetic functions to update it. 91 */ 92 static uint32_t llp_highest_pri = 0; 93 94 static void print_llp_list(void); 95 96 char * 97 llp_prnm(llp_t *llp) 98 { 99 if (llp == NULL) 100 return ("null_llp"); 101 else if (llp->llp_lname == NULL) 102 return ("null_lname"); 103 else 104 return (llp->llp_lname); 105 } 106 107 static void 108 llp_list_free(llp_t *head) 109 { 110 llp_t **llpp; 111 llp_t *llpfree; 112 113 if (pthread_mutex_lock(&llp_lock) != 0) { 114 /* Something very serious is wrong... */ 115 syslog(LOG_ERR, "llp_list_free: cannot lock mutex: %m"); 116 return; 117 } 118 llpp = &head; 119 while (*llpp != NULL) { 120 llpfree = *llpp; 121 *llpp = llpfree->llp_next; 122 free(llpfree->llp_ipv4addrstr); 123 free(llpfree); 124 } 125 (void) pthread_mutex_unlock(&llp_lock); 126 } 127 128 llp_t * 129 llp_lookup(const char *link) 130 { 131 llp_t *llp; 132 133 if (link == NULL) 134 return (NULL); 135 136 /* The name may change. Better hold the lock. */ 137 if (pthread_mutex_lock(&llp_lock) != 0) { 138 /* Something very serious is wrong... */ 139 syslog(LOG_ERR, "llp_lookup: cannot lock mutex: %m"); 140 return (NULL); 141 } 142 for (llp = llp_head; llp != NULL; llp = llp->llp_next) { 143 if (strcmp(link, llp->llp_lname) == 0) 144 break; 145 } 146 (void) pthread_mutex_unlock(&llp_lock); 147 return (llp); 148 } 149 150 /* 151 * Choose the higher priority llp of the two passed in. If one is 152 * NULL, the other will be higher priority. If both are NULL, NULL 153 * is returned. 154 * 155 * Assumes that both are available (i.e. doesn't check IFF_RUNNING 156 * or IF_DHCPFAILED flag values). 157 */ 158 llp_t * 159 llp_high_pri(llp_t *a, llp_t *b) 160 { 161 if (a == NULL) 162 return (b); 163 else if (b == NULL) 164 return (a); 165 166 /* 167 * Higher priority is represented by a lower number. This seems a 168 * bit backwards, but for now it makes assigning priorities very easy. 169 * 170 * We shouldn't have ties right now, but just in case, tie goes to a. 171 */ 172 return ((a->llp_pri <= b->llp_pri) ? a : b); 173 } 174 175 /* 176 * Chooses the highest priority link that corresponds to an 177 * available interface. 178 */ 179 llp_t * 180 llp_best_avail(void) 181 { 182 llp_t *p, *rtnllp = NULL; 183 struct interface *ifp; 184 185 /* The priority may change. Better hold the lock. */ 186 if (pthread_mutex_lock(&llp_lock) != 0) { 187 /* Something very serious is wrong... */ 188 syslog(LOG_ERR, "llp_best_avail: cannot lock mutex: %m"); 189 return (NULL); 190 } 191 for (p = llp_head; p != NULL; p = p->llp_next) { 192 ifp = get_interface(p->llp_lname); 193 if (ifp == NULL || !is_plugged_in(ifp) || 194 (ifp->if_lflags & IF_DHCPFAILED) != 0) 195 continue; 196 rtnllp = llp_high_pri(p, rtnllp); 197 } 198 (void) pthread_mutex_unlock(&llp_lock); 199 200 return (rtnllp); 201 } 202 203 /* 204 * Returns B_TRUE if llp is successfully activated; 205 * B_FALSE if activation fails. 206 */ 207 boolean_t 208 llp_activate(llp_t *llp) 209 { 210 boolean_t rtn; 211 char *host; 212 /* 213 * Choosing "dhcp" as a hostname is unsupported right now. 214 * We use hostname="dhcp" as a keyword telling bringupinterface() 215 * to use dhcp on the interface. 216 */ 217 char *dhcpstr = "dhcp"; 218 219 llp_deactivate(); 220 221 host = (llp->llp_ipv4src == IPV4SRC_DHCP) ? dhcpstr : 222 llp->llp_ipv4addrstr; 223 224 if (bringupinterface(llp->llp_lname, host, llp->llp_ipv6addrstr, 225 llp->llp_ipv6onlink)) { 226 link_layer_profile = llp; 227 dprintf("llp_activate: activated llp for %s", llp_prnm(llp)); 228 rtn = B_TRUE; 229 } else { 230 dprintf("llp_activate: failed to bringup %s", llp_prnm(llp)); 231 link_layer_profile = NULL; 232 rtn = B_FALSE; 233 } 234 235 return (rtn); 236 } 237 238 /* 239 * Deactivate the current active llp (link_layer_profile) 240 */ 241 void 242 llp_deactivate(void) 243 { 244 if (link_layer_profile == NULL) 245 return; 246 247 takedowninterface(link_layer_profile->llp_lname, 248 link_layer_profile->llp_ipv4src == IPV4SRC_DHCP, B_TRUE, 249 link_layer_profile->llp_ipv6onlink); 250 251 dprintf("llp_deactivate: setting link_layer_profile(%p) to NULL", 252 (void *)link_layer_profile); 253 link_layer_profile = NULL; 254 } 255 256 /* 257 * Replace the currently active link layer profile with the one 258 * specified. And since we're changing the lower layer stuff, 259 * we need to first deactivate the current upper layer profile. 260 * An upper layer profile will be reactivated later, when we get 261 * confirmation that the new llp is fully up (has an address 262 * assigned). 263 * 264 * If the new llp is the same as the currently active one, don't 265 * do anything. 266 * 267 * If the new llp is NULL, just take down the currently active one. 268 */ 269 void 270 llp_swap(llp_t *newllp) 271 { 272 char *upifname; 273 274 if (newllp == link_layer_profile) 275 return; 276 277 deactivate_upper_layer_profile(); 278 279 if (link_layer_profile == NULL) { 280 /* 281 * there shouldn't be anything else running; 282 * make sure that's the case! 283 */ 284 upifname = (newllp == NULL) ? NULL : newllp->llp_lname; 285 take_down_all_ifs(upifname); 286 } else { 287 dprintf("taking down current link layer profile (%s)", 288 llp_prnm(link_layer_profile)); 289 llp_deactivate(); 290 } 291 if (newllp != NULL) { 292 dprintf("bringing up new link layer profile (%s)", 293 llp_prnm(newllp)); 294 (void) llp_activate(newllp); 295 } 296 } 297 298 /* 299 * 300 * ifp->if_family == AF_INET, addr_src == DHCP ==> addr == NULL 301 * ifp->if_family == AF_INET, addr_src == STATIC ==> addr non null sockaddr_in 302 * ifp->if_family == AF_INET6, ipv6onlink == FALSE ==> addr == NULL 303 * ifp->if_family == AF_INET6, ipv6onlink == TRUE, 304 * if addr non NULL then it is the textual representation of the address 305 * and prefix. 306 * 307 * The above set of conditions describe what the inputs to this fuction are 308 * expected to be. Given input which meets those conditions this functions 309 * then outputs a line of configuration describing the inputs. 310 * 311 * Note that it is assumed only one thread can call this function at 312 * any time. So there is no lock to protect the file writing. This 313 * is true as the only caller of this function should originate from 314 * llp_parse_config(), which is done at program initialization time. 315 */ 316 static void 317 add_if_file(FILE *fp, struct interface *ifp, ipv4src_t addr_src, 318 boolean_t ipv6onlink, void *addr) 319 { 320 char addr_buf[INET6_ADDRSTRLEN]; 321 322 switch (ifp->if_family) { 323 case AF_INET: 324 switch (addr_src) { 325 case IPV4SRC_STATIC: 326 /* This is not supposed to happen... */ 327 if (addr == NULL) { 328 (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name); 329 break; 330 } 331 (void) inet_ntop(AF_INET, addr, addr_buf, 332 INET6_ADDRSTRLEN); 333 (void) fprintf(fp, "%s\tstatic\t%s\n", ifp->if_name, 334 addr_buf); 335 break; 336 case IPV4SRC_DHCP: 337 /* Default is DHCP for now. */ 338 default: 339 (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name); 340 break; 341 } 342 break; 343 344 case AF_INET6: 345 if (ipv6onlink) 346 (void) fprintf(fp, "%s\tipv6\n", ifp->if_name); 347 break; 348 349 default: 350 syslog(LOG_ERR, "interface %s of type %d?!", ifp->if_name, 351 ifp->if_family); 352 break; 353 } 354 } 355 356 /* 357 * Walker function to pass to walk_interface() to add a default 358 * interface description to the LLPFILE. 359 * 360 * Regarding IF_TUN interfaces: see comments before find_and_add_llp() 361 * for an explanation of why we skip them. 362 */ 363 static void 364 add_if_default(struct interface *ifp, void *arg) 365 { 366 FILE *fp = (FILE *)arg; 367 368 if (ifp->if_type != IF_TUN) 369 add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL); 370 } 371 372 /* Create the LLPFILE using info from the interface list. */ 373 static void 374 create_llp_file(void) 375 { 376 FILE *fp; 377 int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 378 379 /* Create the NWAM directory in case it does not exist. */ 380 if (mkdir(LLPDIR, dirmode) != 0) { 381 if (errno != EEXIST) { 382 syslog(LOG_ERR, "create NWAM directory: %m"); 383 return; 384 } 385 } 386 if ((fp = fopen(LLPFILE, "w")) == NULL) { 387 syslog(LOG_ERR, "create LLP config file: %m"); 388 return; 389 } 390 syslog(LOG_INFO, "Creating %s", LLPFILE); 391 walk_interface(add_if_default, fp); 392 (void) fclose(fp); 393 } 394 395 /* 396 * Append an llp struct to the end of the llp list. 397 */ 398 static void 399 llp_list_append(llp_t *llp) 400 { 401 llp_t **wpp = &llp_head; 402 403 /* 404 * should be a no-op, but for now, make sure we only 405 * create llps for wired and wireless interfaces. 406 */ 407 if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) 408 return; 409 410 if (pthread_mutex_lock(&llp_lock) != 0) { 411 /* Something very serious is wrong... */ 412 syslog(LOG_ERR, "llp_list_append: cannot lock mutex: %m"); 413 return; 414 } 415 416 while (*wpp != NULL) 417 wpp = &(*wpp)->llp_next; 418 *wpp = llp; 419 llp->llp_next = NULL; 420 421 (void) pthread_mutex_unlock(&llp_lock); 422 } 423 424 /* 425 * Create a llp given the parameters and add it to the global list. 426 */ 427 static void 428 create_and_add_llp(const char *name, ipv4src_t ipv4src, const char *addrstr, 429 boolean_t ipv6onlink, const char *ipv6addrstr) 430 { 431 llp_t *newllp; 432 int lnamelen; 433 434 if ((newllp = llp_lookup(name)) != NULL) { 435 if (ipv6addrstr != NULL) { 436 newllp->llp_ipv6addrstr = strdup(ipv6addrstr); 437 if (newllp->llp_ipv6addrstr == NULL) { 438 syslog(LOG_ERR, "could not save ipv6 static " 439 "address for %s", name); 440 } 441 } 442 newllp->llp_ipv6onlink = ipv6onlink; 443 return; 444 } else if ((newllp = calloc(1, sizeof (llp_t))) == NULL) { 445 syslog(LOG_ERR, "calloc llp: %m"); 446 return; 447 } 448 449 lnamelen = sizeof (newllp->llp_lname); 450 if (strlcpy(newllp->llp_lname, name, lnamelen) >= lnamelen) { 451 syslog(LOG_ERR, "llp: link name too long; ignoring entry"); 452 free(newllp); 453 return; 454 } 455 if (ipv4src == IPV4SRC_STATIC) { 456 if ((newllp->llp_ipv4addrstr = strdup(addrstr)) == NULL) { 457 syslog(LOG_ERR, "malloc ipaddrstr: %m"); 458 free(newllp); 459 return; 460 } 461 } else { 462 newllp->llp_ipv4addrstr = NULL; 463 } 464 newllp->llp_next = NULL; 465 newllp->llp_pri = atomic_add_32_nv(&llp_highest_pri, 1); 466 newllp->llp_ipv4src = ipv4src; 467 newllp->llp_type = find_if_type(newllp->llp_lname); 468 newllp->llp_ipv6onlink = ipv6onlink; 469 470 if (ipv6onlink && ipv6addrstr != NULL) { 471 newllp->llp_ipv6addrstr = strdup(ipv6addrstr); 472 if (newllp->llp_ipv6addrstr == NULL) 473 syslog(LOG_WARNING, "could not store static address %s" 474 "on interface %s", ipv6addrstr, newllp->llp_lname); 475 } else { 476 newllp->llp_ipv6addrstr = NULL; 477 } 478 479 llp_list_append(newllp); 480 481 dprintf("created llp for link %s, pri %d", newllp->llp_lname, 482 newllp->llp_pri); 483 } 484 485 /* 486 * Walker function to pass to walk_interface() to find out if 487 * an interface description is missing from LLPFILE. If it is, 488 * add it. 489 * 490 * Currently, IF_TUN type interfaces are special-cased: they are 491 * only handled as user-enabled, layered links (which may be created 492 * as part of a higher-layer profile, for example). Thus, they 493 * shouldn't be considered when looking at the llp list, so don't 494 * add them here. 495 */ 496 static void 497 find_and_add_llp(struct interface *ifp, void *arg) 498 { 499 FILE *fp = (FILE *)arg; 500 501 if (ifp->if_type != IF_TUN && (llp_lookup(ifp->if_name) == NULL)) { 502 dprintf("Adding %s to %s", ifp->if_name, LLPFILE); 503 add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL); 504 /* If we run out of memory, ignore this interface for now. */ 505 create_and_add_llp(ifp->if_name, IPV4SRC_DHCP, NULL, 506 B_TRUE, NULL); 507 } 508 } 509 510 /* 511 * This is a very "slow" function. It uses walk_interface() to find 512 * out if any of the interface is missing from the LLPFILE. For the 513 * missing ones, add them to the LLPFILE. 514 */ 515 static void 516 add_missing_if_llp(FILE *fp) 517 { 518 walk_interface(find_and_add_llp, fp); 519 } 520 521 static void 522 print_llp_list(void) 523 { 524 llp_t *wp; 525 526 dprintf("Walking llp list"); 527 for (wp = llp_head; wp != NULL; wp = wp->llp_next) 528 dprintf("==> %s", wp->llp_lname); 529 } 530 531 /* 532 * This function parses /etc/nwam/llp which describes the phase 0 link layer 533 * profile. The file is line oriented with each line containing tab or space 534 * delimited fields. Each address family (IPv4, IPv6) is described on a 535 * separate line. 536 * The first field is a link name. 537 * The second field can be either static, dhcp, ipv6, or noipv6. 538 * If the second field is static then the next field is an ipv4 address which 539 * can contain a prefix. Previous versions of this file could contain a 540 * hostname in this field which is no longer supported. 541 * If the second field is dhcp then dhcp will be used on the interface. 542 * If the second field is ipv6 then an ipv6 interface is plumbed up. The 543 * outcome of this is that if offered by the network in.ndpd and dhcp 544 * will conspire to put addresses on additional ipv6 logical interfaces. 545 * If the next field is non-null then it is taken to be an IPv6 address 546 * and possible prefix which are applied to the interface. 547 * If the second field is noipv6 then no ipv6 interfaces will be put on that 548 * link. 549 */ 550 void 551 llp_parse_config(void) 552 { 553 static const char STATICSTR[] = "static"; 554 static const char DHCP[] = "dhcp"; 555 static const char IPV6[] = "ipv6"; 556 static const char NOIPV6[] = "noipv6"; 557 FILE *fp; 558 char line[LINE_MAX]; 559 char *cp, *lasts, *lstr, *srcstr, *addrstr, *v6addrstr; 560 int lnum; 561 ipv4src_t ipv4src; 562 boolean_t ipv6onlink; 563 564 fp = fopen(LLPFILE, "r+"); 565 if (fp == NULL) { 566 if (errno != ENOENT) { 567 /* 568 * XXX See comment before create_llp_file() re 569 * better error handling. 570 */ 571 syslog(LOG_ERR, "open LLP config file: %m"); 572 return; 573 } 574 575 /* 576 * If there is none, we should create one instead. 577 * For now, we will use the order of the interface list 578 * for the priority. We should have a priority field 579 * in the llp file eventually... 580 */ 581 create_llp_file(); 582 583 /* Now we can try to reopen the file for processing. */ 584 fp = fopen(LLPFILE, "r+"); 585 if (fp == NULL) { 586 syslog(LOG_ERR, "2nd open LLP config file: %m"); 587 return; 588 } 589 } 590 591 if (llp_head != NULL) 592 llp_list_free(llp_head); 593 llp_head = NULL; 594 595 for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) { 596 ipv4src = IPV4SRC_DHCP; 597 ipv6onlink = B_FALSE; 598 addrstr = NULL; 599 v6addrstr = NULL; 600 601 if (line[strlen(line) - 1] == '\n') 602 line[strlen(line) - 1] = '\0'; 603 604 cp = line; 605 while (isspace(*cp)) 606 cp++; 607 608 if (*cp == '#' || *cp == '\0') 609 continue; 610 611 dprintf("parsing llp conf file line %d...", lnum); 612 613 if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) || 614 ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) { 615 syslog(LOG_ERR, "llp:%d: not enough tokens; " 616 "ignoring entry", lnum); 617 continue; 618 } 619 if (strcasecmp(srcstr, STATICSTR) == 0) { 620 if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL || 621 atoi(addrstr) == 0) { /* crude check for number */ 622 syslog(LOG_ERR, "llp:%d: missing ipaddr " 623 "for static config; ignoring entry", 624 lnum); 625 continue; 626 } 627 ipv4src = IPV4SRC_STATIC; 628 } else if (strcasecmp(srcstr, DHCP) == 0) { 629 ipv4src = IPV4SRC_DHCP; 630 } else if (strcasecmp(srcstr, IPV6) == 0) { 631 ipv6onlink = B_TRUE; 632 if ((addrstr = strtok_r(NULL, " \t", &lasts)) != NULL) { 633 v6addrstr = strdup(addrstr); 634 if (v6addrstr == NULL) { 635 syslog(LOG_ERR, "could not store v6 " 636 "static address %s for %s", 637 v6addrstr, lstr); 638 } 639 } else { 640 v6addrstr = NULL; 641 } 642 } else if (strcasecmp(srcstr, NOIPV6) == 0) { 643 ipv6onlink = B_FALSE; 644 } else { 645 syslog(LOG_ERR, "llp:%d: unrecognized " 646 "field; ignoring entry", lnum); 647 continue; 648 } 649 650 create_and_add_llp(lstr, ipv4src, addrstr, ipv6onlink, 651 v6addrstr); 652 } 653 654 /* 655 * So we have read in the llp file, is there an interface which 656 * it does not describe? If yes, we'd better add it to the 657 * file for future reference. Again, since we don't have a 658 * priority field yet, we will add the interface in the order 659 * in the interface list. 660 */ 661 add_missing_if_llp(fp); 662 663 (void) fclose(fp); 664 665 print_llp_list(); 666 } 667