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, B_TRUE, 248 link_layer_profile->llp_ipv6onlink); 249 250 dprintf("llp_deactivate: setting link_layer_profile(%p) to NULL", 251 (void *)link_layer_profile); 252 link_layer_profile = NULL; 253 } 254 255 /* 256 * Replace the currently active link layer profile with the one 257 * specified. And since we're changing the lower layer stuff, 258 * we need to first deactivate the current upper layer profile. 259 * An upper layer profile will be reactivated later, when we get 260 * confirmation that the new llp is fully up (has an address 261 * assigned). 262 * 263 * If the new llp is the same as the currently active one, don't 264 * do anything. 265 * 266 * If the new llp is NULL, just take down the currently active one. 267 */ 268 void 269 llp_swap(llp_t *newllp) 270 { 271 char *upifname; 272 273 if (newllp == link_layer_profile) 274 return; 275 276 deactivate_upper_layer_profile(); 277 278 if (link_layer_profile == NULL) { 279 /* 280 * there shouldn't be anything else running; 281 * make sure that's the case! 282 */ 283 upifname = (newllp == NULL) ? NULL : newllp->llp_lname; 284 take_down_all_ifs(upifname); 285 } else { 286 dprintf("taking down current link layer profile (%s)", 287 llp_prnm(link_layer_profile)); 288 llp_deactivate(); 289 } 290 if (newllp != NULL) { 291 dprintf("bringing up new link layer profile (%s)", 292 llp_prnm(newllp)); 293 (void) llp_activate(newllp); 294 } 295 } 296 297 /* 298 * 299 * ifp->if_family == AF_INET, addr_src == DHCP ==> addr == NULL 300 * ifp->if_family == AF_INET, addr_src == STATIC ==> addr non null sockaddr_in 301 * ifp->if_family == AF_INET6, ipv6onlink == FALSE ==> addr == NULL 302 * ifp->if_family == AF_INET6, ipv6onlink == TRUE, 303 * if addr non NULL then it is the textual representation of the address 304 * and prefix. 305 * 306 * The above set of conditions describe what the inputs to this fuction are 307 * expected to be. Given input which meets those conditions this functions 308 * then outputs a line of configuration describing the inputs. 309 * 310 * Note that it is assumed only one thread can call this function at 311 * any time. So there is no lock to protect the file writing. This 312 * is true as the only caller of this function should originate from 313 * llp_parse_config(), which is done at program initialization time. 314 */ 315 static void 316 add_if_file(FILE *fp, struct interface *ifp, ipv4src_t addr_src, 317 boolean_t ipv6onlink, void *addr) 318 { 319 char addr_buf[INET6_ADDRSTRLEN]; 320 321 switch (ifp->if_family) { 322 case AF_INET: 323 switch (addr_src) { 324 case IPV4SRC_STATIC: 325 /* This is not supposed to happen... */ 326 if (addr == NULL) { 327 (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name); 328 break; 329 } 330 (void) inet_ntop(AF_INET, addr, addr_buf, 331 INET6_ADDRSTRLEN); 332 (void) fprintf(fp, "%s\tstatic\t%s\n", ifp->if_name, 333 addr_buf); 334 break; 335 case IPV4SRC_DHCP: 336 /* Default is DHCP for now. */ 337 default: 338 (void) fprintf(fp, "%s\tdhcp\n", ifp->if_name); 339 break; 340 } 341 break; 342 343 case AF_INET6: 344 if (ipv6onlink) 345 (void) fprintf(fp, "%s\tipv6\n", ifp->if_name); 346 break; 347 348 default: 349 syslog(LOG_ERR, "interface %s of type %d?!", ifp->if_name, 350 ifp->if_family); 351 break; 352 } 353 } 354 355 /* 356 * Walker function to pass to walk_interface() to add a default 357 * interface description to the LLPFILE. 358 * 359 * Regarding IF_TUN interfaces: see comments before find_and_add_llp() 360 * for an explanation of why we skip them. 361 */ 362 static void 363 add_if_default(struct interface *ifp, void *arg) 364 { 365 FILE *fp = (FILE *)arg; 366 367 if (ifp->if_type != IF_TUN) 368 add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL); 369 } 370 371 /* Create the LLPFILE using info from the interface list. */ 372 static void 373 create_llp_file(void) 374 { 375 FILE *fp; 376 int dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; 377 378 /* Create the NWAM directory in case it does not exist. */ 379 if (mkdir(LLPDIR, dirmode) != 0) { 380 if (errno != EEXIST) { 381 syslog(LOG_ERR, "create NWAM directory: %m"); 382 return; 383 } 384 } 385 if ((fp = fopen(LLPFILE, "w")) == NULL) { 386 syslog(LOG_ERR, "create LLP config file: %m"); 387 return; 388 } 389 syslog(LOG_INFO, "Creating %s", LLPFILE); 390 walk_interface(add_if_default, fp); 391 (void) fclose(fp); 392 } 393 394 /* 395 * Append an llp struct to the end of the llp list. 396 */ 397 static void 398 llp_list_append(llp_t *llp) 399 { 400 llp_t **wpp = &llp_head; 401 402 /* 403 * should be a no-op, but for now, make sure we only 404 * create llps for wired and wireless interfaces. 405 */ 406 if (llp->llp_type != IF_WIRED && llp->llp_type != IF_WIRELESS) 407 return; 408 409 if (pthread_mutex_lock(&llp_lock) != 0) { 410 /* Something very serious is wrong... */ 411 syslog(LOG_ERR, "llp_list_append: cannot lock mutex: %m"); 412 return; 413 } 414 415 while (*wpp != NULL) 416 wpp = &(*wpp)->llp_next; 417 *wpp = llp; 418 llp->llp_next = NULL; 419 420 (void) pthread_mutex_unlock(&llp_lock); 421 } 422 423 /* 424 * Create a llp given the parameters and add it to the global list. 425 */ 426 static void 427 create_and_add_llp(const char *name, ipv4src_t ipv4src, const char *addrstr, 428 boolean_t ipv6onlink, const char *ipv6addrstr) 429 { 430 llp_t *newllp; 431 int lnamelen; 432 433 if ((newllp = llp_lookup(name)) != NULL) { 434 if (ipv6addrstr != NULL) { 435 newllp->llp_ipv6addrstr = strdup(ipv6addrstr); 436 if (newllp->llp_ipv6addrstr == NULL) { 437 syslog(LOG_ERR, "could not save ipv6 static " 438 "address for %s", name); 439 } 440 } 441 newllp->llp_ipv6onlink = ipv6onlink; 442 return; 443 } else if ((newllp = calloc(1, sizeof (llp_t))) == NULL) { 444 syslog(LOG_ERR, "calloc llp: %m"); 445 return; 446 } 447 448 lnamelen = sizeof (newllp->llp_lname); 449 if (strlcpy(newllp->llp_lname, name, lnamelen) >= lnamelen) { 450 syslog(LOG_ERR, "llp: link name too long; ignoring entry"); 451 free(newllp); 452 return; 453 } 454 if (ipv4src == IPV4SRC_STATIC) { 455 if ((newllp->llp_ipv4addrstr = strdup(addrstr)) == NULL) { 456 syslog(LOG_ERR, "malloc ipaddrstr: %m"); 457 free(newllp); 458 return; 459 } 460 } else { 461 newllp->llp_ipv4addrstr = NULL; 462 } 463 newllp->llp_next = NULL; 464 newllp->llp_pri = atomic_add_32_nv(&llp_highest_pri, 1); 465 newllp->llp_ipv4src = ipv4src; 466 newllp->llp_type = find_if_type(newllp->llp_lname); 467 newllp->llp_ipv6onlink = ipv6onlink; 468 469 if (ipv6onlink && ipv6addrstr != NULL) { 470 newllp->llp_ipv6addrstr = strdup(ipv6addrstr); 471 if (newllp->llp_ipv6addrstr == NULL) 472 syslog(LOG_WARNING, "could not store static address %s" 473 "on interface %s", ipv6addrstr, newllp->llp_lname); 474 } else { 475 newllp->llp_ipv6addrstr = NULL; 476 } 477 478 llp_list_append(newllp); 479 480 dprintf("created llp for link %s, pri %d", newllp->llp_lname, 481 newllp->llp_pri); 482 } 483 484 /* 485 * Walker function to pass to walk_interface() to find out if 486 * an interface description is missing from LLPFILE. If it is, 487 * add it. 488 * 489 * Currently, IF_TUN type interfaces are special-cased: they are 490 * only handled as user-enabled, layered links (which may be created 491 * as part of a higher-layer profile, for example). Thus, they 492 * shouldn't be considered when looking at the llp list, so don't 493 * add them here. 494 */ 495 static void 496 find_and_add_llp(struct interface *ifp, void *arg) 497 { 498 FILE *fp = (FILE *)arg; 499 500 if (ifp->if_type != IF_TUN && (llp_lookup(ifp->if_name) == NULL)) { 501 dprintf("Adding %s to %s", ifp->if_name, LLPFILE); 502 add_if_file(fp, ifp, IPV4SRC_DHCP, B_TRUE, NULL); 503 /* If we run out of memory, ignore this interface for now. */ 504 create_and_add_llp(ifp->if_name, IPV4SRC_DHCP, NULL, 505 B_TRUE, NULL); 506 } 507 } 508 509 /* 510 * This is a very "slow" function. It uses walk_interface() to find 511 * out if any of the interface is missing from the LLPFILE. For the 512 * missing ones, add them to the LLPFILE. 513 */ 514 static void 515 add_missing_if_llp(FILE *fp) 516 { 517 walk_interface(find_and_add_llp, fp); 518 } 519 520 static void 521 print_llp_list(void) 522 { 523 llp_t *wp; 524 525 dprintf("Walking llp list"); 526 for (wp = llp_head; wp != NULL; wp = wp->llp_next) 527 dprintf("==> %s", wp->llp_lname); 528 } 529 530 /* 531 * This function parses /etc/nwam/llp which describes the phase 0 link layer 532 * profile. The file is line oriented with each line containing tab or space 533 * delimited fields. Each address family (IPv4, IPv6) is described on a 534 * separate line. 535 * The first field is a link name. 536 * The second field can be either static, dhcp, ipv6, or noipv6. 537 * If the second field is static then the next field is an ipv4 address which 538 * can contain a prefix. Previous versions of this file could contain a 539 * hostname in this field which is no longer supported. 540 * If the second field is dhcp then dhcp will be used on the interface. 541 * If the second field is ipv6 then an ipv6 interface is plumbed up. The 542 * outcome of this is that if offered by the network in.ndpd and dhcp 543 * will conspire to put addresses on additional ipv6 logical interfaces. 544 * If the next field is non-null then it is taken to be an IPv6 address 545 * and possible prefix which are applied to the interface. 546 * If the second field is noipv6 then no ipv6 interfaces will be put on that 547 * link. 548 */ 549 void 550 llp_parse_config(void) 551 { 552 static const char STATICSTR[] = "static"; 553 static const char DHCP[] = "dhcp"; 554 static const char IPV6[] = "ipv6"; 555 static const char NOIPV6[] = "noipv6"; 556 FILE *fp; 557 char line[LINE_MAX]; 558 char *cp, *lasts, *lstr, *srcstr, *addrstr, *v6addrstr; 559 int lnum; 560 ipv4src_t ipv4src; 561 boolean_t ipv6onlink; 562 563 fp = fopen(LLPFILE, "r+"); 564 if (fp == NULL) { 565 if (errno != ENOENT) { 566 /* 567 * XXX See comment before create_llp_file() re 568 * better error handling. 569 */ 570 syslog(LOG_ERR, "open LLP config file: %m"); 571 return; 572 } 573 574 /* 575 * If there is none, we should create one instead. 576 * For now, we will use the order of the interface list 577 * for the priority. We should have a priority field 578 * in the llp file eventually... 579 */ 580 create_llp_file(); 581 582 /* Now we can try to reopen the file for processing. */ 583 fp = fopen(LLPFILE, "r+"); 584 if (fp == NULL) { 585 syslog(LOG_ERR, "2nd open LLP config file: %m"); 586 return; 587 } 588 } 589 590 if (llp_head != NULL) 591 llp_list_free(llp_head); 592 llp_head = NULL; 593 594 for (lnum = 1; fgets(line, sizeof (line), fp) != NULL; lnum++) { 595 ipv4src = IPV4SRC_DHCP; 596 ipv6onlink = B_FALSE; 597 addrstr = NULL; 598 v6addrstr = NULL; 599 600 if (line[strlen(line) - 1] == '\n') 601 line[strlen(line) - 1] = '\0'; 602 603 cp = line; 604 while (isspace(*cp)) 605 cp++; 606 607 if (*cp == '#' || *cp == '\0') 608 continue; 609 610 dprintf("parsing llp conf file line %d...", lnum); 611 612 if (((lstr = strtok_r(cp, " \t", &lasts)) == NULL) || 613 ((srcstr = strtok_r(NULL, " \t", &lasts)) == NULL)) { 614 syslog(LOG_ERR, "llp:%d: not enough tokens; " 615 "ignoring entry", lnum); 616 continue; 617 } 618 if (strcasecmp(srcstr, STATICSTR) == 0) { 619 if ((addrstr = strtok_r(NULL, " \t", &lasts)) == NULL || 620 atoi(addrstr) == 0) { /* crude check for number */ 621 syslog(LOG_ERR, "llp:%d: missing ipaddr " 622 "for static config; ignoring entry", 623 lnum); 624 continue; 625 } 626 ipv4src = IPV4SRC_STATIC; 627 } else if (strcasecmp(srcstr, DHCP) == 0) { 628 ipv4src = IPV4SRC_DHCP; 629 } else if (strcasecmp(srcstr, IPV6) == 0) { 630 ipv6onlink = B_TRUE; 631 if ((addrstr = strtok_r(NULL, " \t", &lasts)) != NULL) { 632 v6addrstr = strdup(addrstr); 633 if (v6addrstr == NULL) { 634 syslog(LOG_ERR, "could not store v6 " 635 "static address %s for %s", 636 v6addrstr, lstr); 637 } 638 } else { 639 v6addrstr = NULL; 640 } 641 } else if (strcasecmp(srcstr, NOIPV6) == 0) { 642 ipv6onlink = B_FALSE; 643 } else { 644 syslog(LOG_ERR, "llp:%d: unrecognized " 645 "field; ignoring entry", lnum); 646 continue; 647 } 648 649 create_and_add_llp(lstr, ipv4src, addrstr, ipv6onlink, 650 v6addrstr); 651 } 652 653 /* 654 * So we have read in the llp file, is there an interface which 655 * it does not describe? If yes, we'd better add it to the 656 * file for future reference. Again, since we don't have a 657 * priority field yet, we will add the interface in the order 658 * in the interface list. 659 */ 660 add_missing_if_llp(fp); 661 662 (void) fclose(fp); 663 664 print_llp_list(); 665 } 666