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 <assert.h> 28 #include <dhcpagent_ipc.h> 29 #include <dhcp_inittab.h> 30 #include <dhcp_symbol.h> 31 #include <dhcpagent_util.h> 32 #include <errno.h> 33 #include <execinfo.h> 34 #include <libnwam.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 #include <ucontext.h> 38 #include <unistd.h> 39 #include <libscf.h> 40 41 #include "conditions.h" 42 #include "events.h" 43 #include "ncp.h" 44 #include "ncu.h" 45 #include "objects.h" 46 #include "util.h" 47 48 /* 49 * ncu_ip.c - contains routines that are IP interface-specific for NCUs. 50 */ 51 52 #define STATELESS_RUNNING (IFF_RUNNING | IFF_UP | IFF_ADDRCONF) 53 #define DHCP_RUNNING (IFF_RUNNING | IFF_UP | IFF_DHCPRUNNING) 54 55 static void nwamd_dhcp(const char *, ipadm_addrobj_t, dhcp_ipc_type_t); 56 static void nwamd_down_interface(const char *, ipadm_addr_type_t, const char *); 57 static boolean_t stateless_running(const nwamd_ncu_t *); 58 59 /* 60 * Given a sockaddr representation of an IPv4 or IPv6 address returns the 61 * string representation. Note that 'sockaddr' should point at the correct 62 * sockaddr structure for the address family (sockaddr_in for AF_INET or 63 * sockaddr_in6 for AF_INET6) or alternatively at a sockaddr_storage 64 * structure. 65 */ 66 static const char * 67 nwamd_sockaddr2str(const struct sockaddr *addr, char *str, size_t len) 68 { 69 struct sockaddr_in *sin; 70 struct sockaddr_in6 *sin6; 71 const char *straddr; 72 73 if (addr == NULL) 74 return (NULL); 75 76 if (addr->sa_family == AF_INET) { 77 /* LINTED E_BAD_PTR_CAST_ALIGN */ 78 sin = (struct sockaddr_in *)addr; 79 straddr = inet_ntop(AF_INET, (void *)&sin->sin_addr, str, len); 80 } else if (addr->sa_family == AF_INET6) { 81 /* LINTED E_BAD_PTR_CAST_ALIGN */ 82 sin6 = (struct sockaddr_in6 *)addr; 83 straddr = inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, str, 84 len); 85 } else { 86 errno = EINVAL; 87 return (NULL); 88 } 89 return (straddr != NULL ? str : NULL); 90 } 91 92 void 93 nwamd_propogate_link_up_down_to_ip(const char *linkname, boolean_t up) 94 { 95 nwamd_object_t ip_ncu = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE, 96 linkname); 97 nwamd_ncu_t *ncu; 98 99 if (ip_ncu == NULL) { 100 nlog(LOG_DEBUG, "nwamd_propogate_link_up_down_to_ip: no IP NCU " 101 "for link %s, cannot propogate %s event", linkname, 102 up ? "up" : "down"); 103 return; 104 } 105 ncu = ip_ncu->nwamd_object_data; 106 107 if (ncu->ncu_enabled) { 108 if (ip_ncu->nwamd_object_aux_state == 109 NWAM_AUX_STATE_UNINITIALIZED) { 110 nlog(LOG_DEBUG, 111 "nwamd_propogate_link_up_down_to_ip: will not " 112 "propogate link %s event as IP NCU %s is being " 113 "removed", up ? "up" : "down", linkname); 114 } else { 115 nlog(LOG_DEBUG, 116 "nwamd_propogate_link_up_down_to_ip: propogating " 117 "link %s event to interface %s", 118 up ? "up" : "down", linkname); 119 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 120 ip_ncu->nwamd_object_name, 121 up ? 122 NWAM_STATE_OFFLINE_TO_ONLINE : 123 NWAM_STATE_ONLINE_TO_OFFLINE, 124 up ? NWAM_AUX_STATE_INITIALIZED : 125 NWAM_AUX_STATE_CONDITIONS_NOT_MET); 126 } 127 } else { 128 nlog(LOG_DEBUG, 129 "nwamd_propogate_link_up_down_to_ip: not propogating " 130 "link %s event to interface %s, IP NCU is disabled", 131 up ? "up" : "down", linkname); 132 } 133 nwamd_object_release(ip_ncu); 134 } 135 136 /* 137 * Returns the value associated with the given symbol for the given 138 * interface. The interface may be NULL, in which case the primary 139 * interface is used. 140 * This function substitutes the need to call dhcpinfo(1), thus it is 141 * very similar to the implementation of dhcpinfo(1). 142 * When multiple values need to be returned (e.g., nameservers), they 143 * are separated by a space ' '. 144 */ 145 char * 146 nwamd_get_dhcpinfo_data(const char *sym_name, char *ifname) 147 { 148 dhcp_symbol_t *entry; 149 dhcp_optnum_t optnum; 150 dhcp_ipc_request_t *request = NULL; 151 dhcp_ipc_reply_t *reply; 152 DHCP_OPT *opt; 153 size_t opt_len; 154 char *value; /* return value */ 155 int err; 156 char errmsg[LINE_MAX]; 157 158 /* if interface is not given, change it to empty string */ 159 if (ifname == NULL) 160 ifname = ""; 161 162 /* find code and category in dhcp_inittab(5) */ 163 entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD | 164 ITAB_CAT_VENDOR | ITAB_CAT_FIELD, ITAB_CONS_INFO, sym_name); 165 166 if (entry == NULL) { 167 (void) snprintf(errmsg, LINE_MAX, "unknown identifier: %s", 168 sym_name); 169 goto fail; 170 } 171 172 /* allocate request */ 173 optnum.code = entry->ds_code; 174 optnum.category = entry->ds_category; 175 optnum.size = entry->ds_max * inittab_type_to_size(entry); 176 request = dhcp_ipc_alloc_request(DHCP_GET_TAG, ifname, &optnum, 177 sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM); 178 if (request == NULL) { 179 (void) snprintf(errmsg, LINE_MAX, "failed dhcp alloc request"); 180 goto fail; 181 } 182 183 /* make the request */ 184 err = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT); 185 if (err != 0 || reply->return_code != 0) { 186 (void) snprintf(errmsg, LINE_MAX, "%s", 187 dhcp_ipc_strerror(err == 0 ? reply->return_code : err)); 188 } 189 190 /* get data from the reply */ 191 opt = dhcp_ipc_get_data(reply, &opt_len, NULL); 192 if (opt_len == 0) { 193 (void) snprintf(errmsg, LINE_MAX, "invalid data"); 194 goto fail; 195 } 196 197 /* check protocol error */ 198 if (opt_len < 2 || (opt_len -2 != opt->len)) { 199 (void) snprintf(errmsg, LINE_MAX, "data length mismatch"); 200 goto fail; 201 } 202 opt_len -= 2; 203 204 /* decode the data into ascii */ 205 value = inittab_decode(entry, opt->value, opt_len, B_TRUE); 206 if (value == NULL) { 207 (void) snprintf(errmsg, LINE_MAX, "cannot decode reply"); 208 goto fail; 209 } 210 211 free(request); 212 free(reply); 213 return (value); 214 215 fail: 216 nlog(LOG_DEBUG, "get_dhcpinfo_data() failed: %s", errmsg); 217 free(request); 218 free(reply); 219 return (NULL); 220 } 221 222 void 223 nwamd_add_default_routes(nwamd_ncu_t *ncu) 224 { 225 nwamd_if_t *nif = &ncu->ncu_if; 226 char str[INET6_ADDRSTRLEN]; 227 228 if (nif->nwamd_if_ipv4 && nif->nwamd_if_ipv4_default_route_set) { 229 struct sockaddr_in v4dest, v4mask; 230 231 v4dest.sin_addr.s_addr = htonl(INADDR_ANY); 232 v4dest.sin_family = AF_INET; 233 234 v4mask.sin_addr.s_addr = 0; 235 v4mask.sin_family = AF_INET; 236 237 nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default " 238 "route %s", nwamd_sockaddr2str((struct sockaddr *) 239 &nif->nwamd_if_ipv4_default_route, str, 240 sizeof (str))); 241 nwamd_add_route((struct sockaddr *)&v4dest, 242 (struct sockaddr *)&v4mask, 243 (struct sockaddr *)&nif->nwamd_if_ipv4_default_route, 244 ncu->ncu_name); 245 } 246 247 if (nif->nwamd_if_ipv6 && nif->nwamd_if_ipv6_default_route_set) { 248 struct sockaddr_in6 v6dest, v6mask; 249 250 (void) bzero(&v6dest, sizeof (struct sockaddr_in6)); 251 v6dest.sin6_family = AF_INET6; 252 253 (void) bzero(&v6mask, sizeof (struct sockaddr_in6)); 254 v6mask.sin6_family = AF_INET6; 255 256 nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default " 257 "route %s", nwamd_sockaddr2str((struct sockaddr *) 258 &nif->nwamd_if_ipv6_default_route, str, 259 sizeof (str))); 260 nwamd_add_route((struct sockaddr *)&v6dest, 261 (struct sockaddr *)&v6mask, 262 (struct sockaddr *)&nif->nwamd_if_ipv6_default_route, 263 ncu->ncu_name); 264 } 265 } 266 267 /* 268 * Returns the nwamd_if_address structure for the given static address, 269 * NULL if not found. 270 */ 271 static struct nwamd_if_address * 272 find_static_address(const struct sockaddr_storage *addr, const nwamd_ncu_t *ncu) 273 { 274 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 275 struct sockaddr_storage saddr; 276 char str[INET6_ADDRSTRLEN]; 277 278 nlog(LOG_DEBUG, "find_static_address: %s", 279 nwamd_sockaddr2str((struct sockaddr *)addr, str, sizeof (str))); 280 for (nifap = nifa; nifap != NULL; nifap = nifap->next) { 281 if (nifap->ipaddr_atype != IPADM_ADDR_STATIC || 282 ipadm_get_addr(nifap->ipaddr, &saddr) != IPADM_SUCCESS) 283 continue; 284 285 if (sockaddrcmp(addr, &saddr)) 286 return (nifap); 287 } 288 return (NULL); 289 } 290 291 /* 292 * Returns the nwamd_if_address structure representing the non-static address 293 * in the NCU. For IPv6, both stateless and stateful (DHCPv6) share the same 294 * nwamd_if_address. Will only return the nwamd_if_address if the relevant 295 * address is configured (v4 DHCP, v6 either stateless or stateless) for the 296 * NCU. Returns NULL if the structure is not found. 297 */ 298 static struct nwamd_if_address * 299 find_nonstatic_address(const nwamd_ncu_t *ncu, sa_family_t family) 300 { 301 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 302 const nwamd_if_t *u_if = &ncu->ncu_if; 303 304 nlog(LOG_DEBUG, "find_nonstatic_address for %s %s", 305 (family == AF_INET ? "IPv4" : "IPv6"), ncu->ncu_name); 306 for (nifap = nifa; nifap != NULL; nifap = nifap->next) { 307 if (nifap->ipaddr_atype == IPADM_ADDR_STATIC) 308 continue; 309 310 if (family == AF_INET) { 311 if (nifap->ipaddr_atype == IPADM_ADDR_DHCP && 312 u_if->nwamd_if_dhcp_requested) 313 return (nifap); 314 } else if (family == AF_INET6) { 315 if (nifap->ipaddr_atype == IPADM_ADDR_IPV6_ADDRCONF && 316 (u_if->nwamd_if_stateful_requested || 317 u_if->nwamd_if_stateless_requested)) 318 return (nifap); 319 } 320 } 321 return (NULL); 322 } 323 324 /* 325 * Returns the nwamd_if_address structure that configured the given address, 326 * NULL if not found. 327 */ 328 static struct nwamd_if_address * 329 find_configured_address(const struct sockaddr_storage *addr, 330 const nwamd_ncu_t *ncu) 331 { 332 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 333 char str[INET6_ADDRSTRLEN]; 334 335 nlog(LOG_DEBUG, "find_configured_address: %s", 336 nwamd_sockaddr2str((struct sockaddr *)addr, str, sizeof (str))); 337 for (nifap = nifa; nifap != NULL; nifap = nifap->next) { 338 if (sockaddrcmp(addr, &nifap->conf_addr) || 339 sockaddrcmp(addr, &nifap->conf_stateless_addr)) 340 return (nifap); 341 } 342 return (NULL); 343 } 344 345 /* 346 * Are one or more static addresses configured? 347 */ 348 boolean_t 349 nwamd_static_addresses_configured(nwamd_ncu_t *ncu, sa_family_t family) 350 { 351 struct nwamd_if_address *n; 352 353 for (n = ncu->ncu_if.nwamd_if_list; n != NULL; n = n->next) { 354 if (n->ipaddr_atype != IPADM_ADDR_STATIC) 355 continue; 356 if ((family == AF_UNSPEC || family == n->family) && 357 n->configured) 358 return (B_TRUE); 359 } 360 nlog(LOG_DEBUG, "no static addresses configured for %s", ncu->ncu_name); 361 return (B_FALSE); 362 } 363 364 /* 365 * Is DHCP probably managing an address on this index. We decide that it is 366 * probably managing an address if there is an interface with IFF_DHCP set 367 * that isn't in our set of static addresses. Note that IFF_DHCP gets set 368 * on static addresses when we do a dhcp inform and if that list has changed 369 * recently then the result of this function could be erronous. 370 */ 371 boolean_t 372 nwamd_dhcp_managing(int protocol, nwamd_ncu_t *ncu) 373 { 374 struct sockaddr_storage addr; 375 uint64_t flags; 376 boolean_t rv = B_FALSE; 377 ipadm_addr_info_t *addrinfo, *a; 378 ipadm_status_t ipstatus; 379 380 if ((ipstatus = ipadm_addr_info(ipadm_handle, ncu->ncu_name, &addrinfo, 381 0, 0)) != IPADM_SUCCESS) { 382 nlog(LOG_ERR, "nwamd_dhcp_managing: " 383 "ipadm_addr_info failed for %s: %s", 384 ncu->ncu_name, ipadm_status2str(ipstatus)); 385 return (B_FALSE); 386 } 387 388 for (a = addrinfo; a != NULL; a = IA_NEXT(a)) { 389 /* 390 * WARNING: This memcpy() assumes knowledge of the 391 * implementation of getifaddrs() and that it always 392 * uses sockaddr_storage as the backing store for 393 * address information, thus making it possible to 394 * copy the entire structure rather than do it on 395 * the size of the sockaddr according to family. 396 * This assumption is made elsewhere in this file. 397 */ 398 (void) memcpy(&addr, a->ia_ifa.ifa_addr, sizeof (addr)); 399 400 /* is this address an expected static one? */ 401 if (find_static_address(&addr, ncu) != NULL) 402 continue; 403 404 /* 405 * For IPv4, DHCPRUNNING flag is set when dhcpagent is in 406 * the process of getting an address, but doesn't have one 407 * yet (interface has 0.0.0.0). For IPv6, DHCPRUNNING flag 408 * is set on the link-local address if trying to get a 409 * stateful address. In both cases, consider the interface 410 * as not being managed by DHCP and skip checking of flags. 411 */ 412 if ((protocol == AF_INET && 413 ((struct sockaddr_in *)&addr)->sin_addr.s_addr == 414 INADDR_ANY) || 415 (protocol == AF_INET6 && 416 IN6_IS_ADDR_LINKLOCAL( 417 &((struct sockaddr_in6 *)&addr)->sin6_addr))) { 418 continue; 419 } 420 421 flags = a->ia_ifa.ifa_flags; 422 if (flags & IFF_DHCPRUNNING) { 423 /* 424 * If we get here we have an address that has the 425 * DHCP flag set and isn't an expected static address. 426 */ 427 rv = B_TRUE; 428 break; 429 } 430 } 431 432 ipadm_free_addr_info(addrinfo); 433 return (rv); 434 } 435 436 /* 437 * Return B_TRUE if IPv4 is requested in the given NCU. 438 */ 439 static boolean_t 440 nwamd_v4_requested(nwamd_ncu_t *ncu) 441 { 442 boolean_t anyv4_requested; 443 nwamd_if_t *u_if; 444 445 anyv4_requested = B_FALSE; 446 u_if = &ncu->ncu_if; 447 if (u_if->nwamd_if_dhcp_requested) { 448 anyv4_requested = B_TRUE; 449 } else { 450 struct nwamd_if_address *n; 451 452 for (n = u_if->nwamd_if_list; n != NULL; n = n->next) { 453 if (n->family == AF_INET && 454 n->ipaddr_atype == IPADM_ADDR_STATIC) 455 break; 456 } 457 if (n != NULL) 458 anyv4_requested = B_TRUE; 459 } 460 461 return (anyv4_requested); 462 } 463 464 /* 465 * Returns B_TRUE if IPv6 is requested in the given NCU. 466 */ 467 static boolean_t 468 nwamd_v6_requested(nwamd_ncu_t *ncu) 469 { 470 boolean_t anyv6_requested; 471 nwamd_if_t *u_if; 472 473 anyv6_requested = B_FALSE; 474 u_if = &ncu->ncu_if; 475 if (u_if->nwamd_if_stateful_requested || 476 u_if->nwamd_if_stateless_requested) { 477 anyv6_requested = B_TRUE; 478 } else { 479 struct nwamd_if_address *n; 480 481 for (n = u_if->nwamd_if_list; n != NULL; n = n->next) { 482 if (n->family == AF_INET6 && 483 n->ipaddr_atype == IPADM_ADDR_STATIC) 484 break; 485 } 486 if (n != NULL) 487 anyv6_requested = B_TRUE; 488 } 489 490 return (anyv6_requested); 491 } 492 493 /* 494 * Bring up the ncu if we have the right combination of requested configuration 495 * and actual configuration and up is true, or bring down the ncu if no 496 * addresses are configured, and up is false. 497 */ 498 static void 499 interface_ncu_up_down(nwamd_ncu_t *ncu, boolean_t up) 500 { 501 boolean_t ncu_online; 502 char *name; 503 504 assert(ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE); 505 506 /* 507 * If V4 with or without V6 is configured then one of its interfaces 508 * needs to be up for the ncu to come online. If only V6 is requested 509 * then one of its interfaces needs to be up for the ncu to come online. 510 */ 511 ncu_online = B_FALSE; 512 if (nwamd_v4_requested(ncu)) { 513 if (nwamd_dhcp_managing(AF_INET, ncu) || 514 nwamd_static_addresses_configured(ncu, AF_INET)) 515 ncu_online = B_TRUE; 516 } else if (nwamd_v6_requested(ncu)) { 517 if ((nwamd_dhcp_managing(AF_INET6, ncu) || 518 stateless_running(ncu) || 519 nwamd_static_addresses_configured(ncu, AF_INET6))) 520 ncu_online = B_TRUE; 521 } 522 523 if (nwam_ncu_name_to_typed_name(ncu->ncu_name, ncu->ncu_type, &name) != 524 NWAM_SUCCESS) { 525 nlog(LOG_DEBUG, "interface_ncu_up_down: " 526 "nwam_ncu_name_to_typed_name failed"); 527 return; 528 } 529 if (ncu_online && up) { 530 nlog(LOG_DEBUG, "interface_ncu_up_down: " 531 "bringing %s up", name); 532 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name, 533 NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_UP); 534 } else if (!ncu_online && !up) { 535 nlog(LOG_DEBUG, "interface_ncu_up_down: " 536 "bringing %s down", name); 537 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name, 538 NWAM_STATE_ONLINE_TO_OFFLINE, 539 NWAM_AUX_STATE_DOWN); 540 } 541 542 free(name); 543 } 544 545 static void 546 interface_ncu_up(nwamd_ncu_t *ncu) 547 { 548 interface_ncu_up_down(ncu, B_TRUE); 549 } 550 551 static void 552 interface_ncu_down(nwamd_ncu_t *ncu) 553 { 554 interface_ncu_up_down(ncu, B_FALSE); 555 } 556 557 static boolean_t 558 stateless_running(const nwamd_ncu_t *ncu) 559 { 560 ipadm_addr_info_t *ainfo, *ainfop; 561 ipadm_status_t ipstatus; 562 boolean_t rv = B_FALSE; 563 uint64_t flags; 564 565 if ((ipstatus = ipadm_addr_info(ipadm_handle, ncu->ncu_name, &ainfo, 566 0, 0)) != IPADM_SUCCESS) { 567 nlog(LOG_ERR, "stateless_running: " 568 "ipadm_addr_info failed for %s: %s", 569 ncu->ncu_name, ipadm_status2str(ipstatus)); 570 return (B_FALSE); 571 } 572 573 for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { 574 if (ainfop->ia_ifa.ifa_addr->sa_family != AF_INET6) 575 continue; 576 flags = ainfop->ia_ifa.ifa_flags; 577 if (flags & STATELESS_RUNNING) { 578 rv = B_TRUE; 579 break; 580 } 581 } 582 ipadm_free_addr_info(ainfo); 583 return (rv); 584 } 585 586 /* 587 * Returns the addrinfo associated with the given address. There is always 588 * only one addrinfo for each address. 589 */ 590 static boolean_t 591 addrinfo_for_addr(const struct sockaddr_storage *caddr, const char *ifname, 592 ipadm_addr_info_t **ainfo) 593 { 594 ipadm_addr_info_t *addrinfo, *ainfop, *last = NULL; 595 ipadm_status_t ipstatus; 596 597 ipstatus = ipadm_addr_info(ipadm_handle, ifname, &addrinfo, 0, 0); 598 if (ipstatus != IPADM_SUCCESS) { 599 nlog(LOG_INFO, "addrinfo_for_addr: " 600 "ipadm_addr_info failed for %s: %s", 601 ifname, ipadm_status2str(ipstatus)); 602 return (B_FALSE); 603 } 604 605 *ainfo = NULL; 606 for (ainfop = addrinfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { 607 struct sockaddr_storage addr; 608 609 (void) memcpy(&addr, ainfop->ia_ifa.ifa_addr, sizeof (addr)); 610 /* 611 * If addresses match, rearrange pointers so that addrinfo 612 * does not contain a, and return a. 613 */ 614 if (sockaddrcmp(&addr, caddr)) { 615 if (last != NULL) 616 last->ia_ifa.ifa_next = ainfop->ia_ifa.ifa_next; 617 else 618 addrinfo = IA_NEXT(ainfop); 619 620 ainfop->ia_ifa.ifa_next = NULL; 621 *ainfo = ainfop; 622 break; 623 } 624 last = ainfop; 625 } 626 ipadm_free_addr_info(addrinfo); 627 return (*ainfo == NULL ? B_FALSE : B_TRUE); 628 } 629 630 /* 631 * Returns B_TRUE if the addrinfo associated with the given ipaddr using its 632 * aobjname is found. An addrinfo list is created and returned in ainfo. 633 * Stateless and stateful IPv6 addrinfo have the same aobjname, thus the need 634 * to create a list of addrinfo. 635 */ 636 static boolean_t 637 addrinfo_for_ipaddr(ipadm_addrobj_t ipaddr, const char *ifname, 638 ipadm_addr_info_t **ainfo) 639 { 640 char aobjname[IPADM_AOBJSIZ]; 641 ipadm_addr_info_t *addrinfo, *ainfop; 642 ipadm_addr_info_t *last = NULL; 643 ipadm_status_t ipstatus; 644 645 ipstatus = ipadm_get_aobjname(ipaddr, aobjname, sizeof (aobjname)); 646 if (ipstatus != IPADM_SUCCESS) 647 return (B_FALSE); 648 649 ipstatus = ipadm_addr_info(ipadm_handle, ifname, &addrinfo, 0, 0); 650 if (ipstatus != IPADM_SUCCESS) { 651 nlog(LOG_INFO, "addrinfo_for_ipaddr: " 652 "ipadm_addr_info failed for %s: %s", 653 ifname, ipadm_status2str(ipstatus)); 654 return (B_FALSE); 655 } 656 657 *ainfo = NULL; 658 ainfop = addrinfo; 659 while (ainfop != NULL) { 660 /* If aobjnames match, rearrange pointers to create new list */ 661 if (strcmp(ainfop->ia_aobjname, aobjname) == 0) { 662 ipadm_addr_info_t *match = ainfop; 663 664 ainfop = IA_NEXT(ainfop); /* move iterator */ 665 if (last != NULL) 666 last->ia_ifa.ifa_next = match->ia_ifa.ifa_next; 667 else 668 addrinfo = ainfop; 669 if (*ainfo == NULL) 670 match->ia_ifa.ifa_next = NULL; 671 else 672 match->ia_ifa.ifa_next = &(*ainfo)->ia_ifa; 673 *ainfo = match; 674 } else { 675 last = ainfop; 676 ainfop = IA_NEXT(ainfop); 677 } 678 } 679 ipadm_free_addr_info(addrinfo); 680 return (*ainfo == NULL ? B_FALSE : B_TRUE); 681 } 682 683 /* 684 * Add the address provided in the nwamd_if_address. If DHCP is required, 685 * start DHCP. If a static address is configured, create the address; then do 686 * a DHCP_INFORM (in a separate thread) to get other networking configuration 687 * parameters. RTM_NEWADDRs - translated into IF_STATE events - will then 688 * finish the job of bringing the NCU online. 689 */ 690 static boolean_t 691 add_ip_address(const char *ifname, const struct nwamd_if_address *nifa, 692 boolean_t *do_inform) 693 { 694 ipadm_status_t ipstatus; 695 ipadm_addr_info_t *addrinfo = NULL; 696 uint64_t flags; 697 698 if (nifa->ipaddr_atype == IPADM_ADDR_DHCP) { 699 /* 700 * To make getting a DHCP address asynchronous, call 701 * ipadm_create_addr() in a new thread. 702 */ 703 nlog(LOG_DEBUG, "add_ip_address: " 704 "adding IPv4 DHCP address on %s", ifname); 705 nwamd_dhcp(ifname, nifa->ipaddr, DHCP_START); 706 } else { 707 nlog(LOG_DEBUG, "add_ip_address: adding %s address on %s", 708 (nifa->ipaddr_atype == IPADM_ADDR_STATIC ? 709 "STATIC" : "IPv6 ADDRCONF"), ifname); 710 if ((ipstatus = ipadm_create_addr(ipadm_handle, nifa->ipaddr, 711 IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) { 712 nlog(LOG_ERR, "add_ip_address: " 713 "ipadm_create_addr failed on %s: %s", 714 ifname, ipadm_status2str(ipstatus)); 715 return (B_FALSE); 716 } 717 /* 718 * When creating a static address, ipadm_create_addr() returns 719 * SUCCESS even if duplicate address is detected. Retrieve 720 * the addrinfo to get the flags. 721 */ 722 if (nifa->ipaddr_atype == IPADM_ADDR_STATIC) { 723 /* 724 * Since we are configuring a static address, there 725 * will be just *ONE* addrinfo with the aobjname in 726 * nifa->ipaddr. 727 */ 728 if (!addrinfo_for_ipaddr(nifa->ipaddr, ifname, 729 &addrinfo)) { 730 nlog(LOG_ERR, "add_ip_address: " 731 "could not find addrinfo on %s", ifname); 732 return (B_FALSE); 733 } 734 735 flags = addrinfo->ia_ifa.ifa_flags; 736 ipadm_free_addr_info(addrinfo); 737 if (flags & IFF_DUPLICATE) { 738 char *object_name; 739 nwam_error_t err; 740 741 nlog(LOG_INFO, "add_ip_address: " 742 "duplicate address detected on %s", ifname); 743 if ((err = nwam_ncu_name_to_typed_name(ifname, 744 NWAM_NCU_TYPE_INTERFACE, &object_name)) 745 == NWAM_SUCCESS) { 746 nwamd_object_set_state( 747 NWAM_OBJECT_TYPE_NCU, 748 object_name, NWAM_STATE_MAINTENANCE, 749 NWAM_AUX_STATE_IF_DUPLICATE_ADDR); 750 free(object_name); 751 } else { 752 nlog(LOG_ERR, "add_ip_address: " 753 "could not create state event " 754 "for %s: %s", 755 ifname, nwam_strerror(err)); 756 } 757 return (B_FALSE); 758 } 759 /* 760 * Do DHCP_INFORM using async ipadm_refresh_addr(). 761 * Only need to do this once per interface, and we 762 * do *not* need to do it if we are also getting a 763 * dhcp lease; so we only send the INFORM if the 764 * passed-in flag says to, and we clear the flag 765 * once we've initiated the INFORM transaction. 766 */ 767 if (*do_inform) { 768 nwamd_dhcp(ifname, nifa->ipaddr, DHCP_INFORM); 769 *do_inform = B_FALSE; 770 } 771 } 772 } 773 774 return (B_TRUE); 775 } 776 777 /* 778 * Adds addresses for the given NCU. 779 */ 780 void 781 nwamd_configure_interface_addresses(nwamd_ncu_t *ncu) 782 { 783 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 784 boolean_t do_inform; 785 786 /* only need an inform if we're not also getting a dhcp lease */ 787 do_inform = !ncu->ncu_if.nwamd_if_dhcp_requested; 788 789 nlog(LOG_DEBUG, "nwamd_configure_interface_addresses(%s)", 790 ncu->ncu_name); 791 792 for (nifap = nifa; nifap != NULL; nifap = nifap->next) { 793 if (nifap->configured) 794 continue; 795 796 nifap->configured = add_ip_address(ncu->ncu_name, nifap, 797 &do_inform); 798 } 799 } 800 801 /* 802 * This event tells us that an interface address has appeared or disappeared, 803 * or that the interface flags on an interface have changed. 804 */ 805 void 806 nwamd_ncu_handle_if_state_event(nwamd_event_t event) 807 { 808 nwam_event_t evm; 809 nwamd_object_t ncu_obj; 810 nwamd_ncu_t *ncu; 811 nwam_state_t state; 812 nwam_aux_state_t aux_state; 813 814 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, 815 event->event_object); 816 if (ncu_obj == NULL) { 817 nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: no object %s", 818 event->event_object); 819 nwamd_event_do_not_send(event); 820 return; 821 } 822 ncu = ncu_obj->nwamd_object_data; 823 evm = event->event_msg; 824 state = ncu_obj->nwamd_object_state; 825 aux_state = ncu_obj->nwamd_object_aux_state; 826 827 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 828 "if %s, state (%s, %s)", event->event_object, 829 nwam_state_to_string(state), nwam_aux_state_to_string(aux_state)); 830 831 /* Ensure object is in correct state to handle IF state events */ 832 switch (state) { 833 case NWAM_STATE_OFFLINE_TO_ONLINE: 834 if (aux_state != NWAM_AUX_STATE_IF_WAITING_FOR_ADDR && 835 aux_state != NWAM_AUX_STATE_IF_DHCP_TIMED_OUT) { 836 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 837 "if %s is in invalid aux state %s for IF_STATE " 838 "events", event->event_object, 839 nwam_aux_state_to_string(aux_state)); 840 nwamd_event_do_not_send(event); 841 nwamd_object_release(ncu_obj); 842 return; 843 } 844 break; 845 case NWAM_STATE_ONLINE: 846 /* 847 * We can get addresses from DHCP after we've taken the interface down. 848 * We deal with those below. 849 */ 850 case NWAM_STATE_ONLINE_TO_OFFLINE: 851 case NWAM_STATE_OFFLINE: 852 break; 853 default: 854 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 855 "if %s is in invalid state %s for IF_STATE events", 856 event->event_object, nwam_state_to_string(state)); 857 nwamd_event_do_not_send(event); 858 nwamd_object_release(ncu_obj); 859 return; 860 } 861 862 if (evm->nwe_data.nwe_if_state.nwe_addr_valid) { 863 struct nwam_event_if_state *if_state; 864 char addrstr[INET6_ADDRSTRLEN]; 865 boolean_t static_addr = B_FALSE, addr_added; 866 boolean_t v4dhcp_running, v6dhcp_running, stateless_running; 867 ipadm_addr_info_t *ai = NULL, *addrinfo = NULL; 868 boolean_t stateless_ai_found = B_FALSE; 869 boolean_t stateful_ai_found = B_FALSE; 870 struct nwamd_if_address *nifa = NULL; 871 nwamd_if_t *u_if; 872 struct sockaddr_storage *addr, ai_addr, *aip = NULL; 873 ushort_t family; 874 uint64_t flags = 0; 875 876 if_state = &evm->nwe_data.nwe_if_state; 877 u_if = &ncu->ncu_if; 878 family = if_state->nwe_addr.ss_family; 879 addr = &if_state->nwe_addr; 880 addr_added = if_state->nwe_addr_added; 881 882 v4dhcp_running = B_FALSE; 883 v6dhcp_running = B_FALSE; 884 stateless_running = B_FALSE; 885 886 nlog(LOG_DEBUG, 887 "nwamd_ncu_handle_if_state_event: addr %s %s", 888 nwamd_sockaddr2str((struct sockaddr *)addr, addrstr, 889 sizeof (addrstr)), addr_added ? "added" : "removed"); 890 891 /* 892 * Need to get flags for this interface. Get the addrinfo for 893 * the address that generated this IF_STATE event. 894 */ 895 if (addr_added) { 896 /* 897 * Address was added. Find the addrinfo for this 898 * address and the nwamd_if_address corresponding to 899 * this address. 900 */ 901 if (!addrinfo_for_addr(addr, ncu->ncu_name, &ai)) { 902 nlog(LOG_ERR, 903 "nwamd_ncu_handle_if_state_event: " 904 "addrinfo doesn't exist for %s", addrstr); 905 nwamd_event_do_not_send(event); 906 goto valid_done; 907 } 908 addrinfo = ai; 909 flags = addrinfo->ia_ifa.ifa_flags; 910 (void) memcpy(&ai_addr, addrinfo->ia_ifa.ifa_addr, 911 sizeof (ai_addr)); 912 aip = &ai_addr; 913 914 if (addrinfo->ia_atype == IPADM_ADDR_IPV6_ADDRCONF || 915 addrinfo->ia_atype == IPADM_ADDR_DHCP) 916 nifa = find_nonstatic_address(ncu, family); 917 else if (addrinfo->ia_atype == IPADM_ADDR_STATIC) 918 nifa = find_static_address(addr, ncu); 919 920 /* 921 * If nwamd_if_address is not found, then this address 922 * isn't one that nwamd created. Remove it. 923 */ 924 if (nifa == NULL) { 925 nlog(LOG_ERR, 926 "nwamd_ncu_handle_if_state_event: " 927 "address %s not managed by nwam added, " 928 "removing it", addrstr); 929 nwamd_down_interface(addrinfo->ia_aobjname, 930 addrinfo->ia_atype, ncu->ncu_name); 931 nwamd_event_do_not_send(event); 932 goto valid_done; 933 } 934 935 /* check flags to determine how intf is configured */ 936 stateless_running = (family == AF_INET6) && 937 ((flags & STATELESS_RUNNING) == STATELESS_RUNNING); 938 v4dhcp_running = (family == AF_INET) && 939 ((flags & DHCP_RUNNING) == DHCP_RUNNING); 940 v6dhcp_running = (family == AF_INET6) && 941 ((flags & DHCP_RUNNING) == DHCP_RUNNING); 942 static_addr = (addrinfo->ia_atype == IPADM_ADDR_STATIC); 943 944 /* copy the configured address into nwamd_if_address */ 945 if (stateless_running) { 946 (void) memcpy(&nifa->conf_stateless_addr, 947 addrinfo->ia_ifa.ifa_addr, 948 sizeof (struct sockaddr_storage)); 949 } else { 950 (void) memcpy(&nifa->conf_addr, 951 addrinfo->ia_ifa.ifa_addr, 952 sizeof (struct sockaddr_storage)); 953 } 954 955 } else { 956 /* 957 * Address was removed. Find the nwamd_if_address 958 * that configured this address. 959 */ 960 nifa = find_configured_address(addr, ncu); 961 if (nifa == NULL) { 962 nlog(LOG_ERR, 963 "nwamd_ncu_handle_if_state_event: " 964 "address %s not managed by nwam removed, " 965 "nothing to do", addrstr); 966 nwamd_event_do_not_send(event); 967 goto valid_done; 968 } 969 970 if (addrinfo_for_ipaddr(nifa->ipaddr, ncu->ncu_name, 971 &ai)) { 972 ipadm_addr_info_t *a; 973 for (a = ai; a != NULL; a = IA_NEXT(a)) { 974 struct sockaddr_storage stor; 975 976 (void) memcpy(&stor, a->ia_ifa.ifa_addr, 977 sizeof (stor)); 978 /* 979 * Since multiple addrinfo can have 980 * the same ipaddr, find the one for 981 * the address that generated this 982 * state event. 983 */ 984 if (sockaddrcmp(addr, &stor)) { 985 flags = a->ia_ifa.ifa_flags; 986 (void) memcpy(&ai_addr, 987 a->ia_ifa.ifa_addr, 988 sizeof (ai_addr)); 989 aip = &ai_addr; 990 addrinfo = a; 991 } 992 /* 993 * Stateful and stateless IPv6 994 * addrinfo have the same aobjname. 995 * Use the flags to determine which 996 * address is present in the system. 997 */ 998 if (family == AF_INET6) { 999 stateless_ai_found = 1000 (a->ia_ifa.ifa_flags & 1001 STATELESS_RUNNING); 1002 stateful_ai_found = 1003 (a->ia_ifa.ifa_flags & 1004 DHCP_RUNNING); 1005 } 1006 } 1007 } 1008 } 1009 1010 /* Set the flags in the event for listeners */ 1011 evm->nwe_data.nwe_if_state.nwe_flags = flags; 1012 1013 if (family == AF_INET && !addr_added) { 1014 /* 1015 * Check for failure due to CR 6745448: if we get a 1016 * report that an address has been deleted, then check 1017 * for interface up, datalink down, and actual address 1018 * non-zero. If that combination is seen, then this is 1019 * a DHCP cached lease, and we need to remove it from 1020 * the system, or it'll louse up the kernel routes 1021 * (which aren't smart enough to avoid dead 1022 * interfaces). 1023 */ 1024 if (((struct sockaddr_in *)addr)->sin_addr.s_addr 1025 == INADDR_ANY && aip != 0) { 1026 struct sockaddr_in *a; 1027 char astr[INET6_ADDRSTRLEN]; 1028 a = (struct sockaddr_in *)aip; 1029 1030 if ((flags & IFF_UP) && 1031 !(flags & IFF_RUNNING) && 1032 a->sin_addr.s_addr != INADDR_ANY) { 1033 nlog(LOG_DEBUG, 1034 "nwamd_ncu_handle_if_state_event: " 1035 "bug workaround: clear out addr " 1036 "%s on %s", nwamd_sockaddr2str 1037 ((struct sockaddr *)a, astr, 1038 sizeof (astr)), 1039 ncu->ncu_name); 1040 nwamd_down_interface( 1041 addrinfo->ia_aobjname, 1042 IPADM_ADDR_DHCP, ncu->ncu_name); 1043 } 1044 goto valid_done; 1045 } 1046 } 1047 1048 /* 1049 * If we received an RTM_NEWADDR and the IFF_UP flags has not 1050 * been set, ignore this IF_STATE event. Once the IFF_UP flag 1051 * is set, we'll get another RTM_NEWADDR message. 1052 */ 1053 if (addr_added & !(flags & IFF_UP)) { 1054 nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: " 1055 "address %s added on %s without IFF_UP flag (%x), " 1056 "ignoring IF_STATE event", 1057 addrstr, ncu->ncu_name, flags); 1058 nwamd_event_do_not_send(event); 1059 goto valid_done; 1060 } 1061 1062 /* 1063 * Has the address really been removed? Sometimes spurious 1064 * RTM_DELADDRs are generated, so we need to ensure that 1065 * the address is really gone. If IFF_DUPLICATE is set, 1066 * we're getting the RTM_DELADDR due to DAD, so don't test 1067 * in that case. 1068 */ 1069 if (!addr_added && !(flags & IFF_DUPLICATE)) { 1070 if (aip != 0 && sockaddrcmp(addr, aip)) { 1071 nlog(LOG_INFO, 1072 "nwamd_ncu_handle_if_state_event: " 1073 "address %s is not really gone from %s, " 1074 "ignoring IF_STATE event", 1075 addrstr, ncu->ncu_name); 1076 nwamd_event_do_not_send(event); 1077 goto valid_done; 1078 } 1079 } 1080 1081 if (addr_added) { 1082 /* 1083 * Address has been added. 1084 * 1085 * We need to make sure that we really want to keep 1086 * this address. There is a race where we requested an 1087 * address but by the time we got here we don't really 1088 * want it and need to remove it. 1089 * 1090 * Once we decide we want the address adjust the ncu 1091 * state accordingly. For example if this address is 1092 * enough move online. 1093 */ 1094 if (u_if->nwamd_if_dhcp_requested && v4dhcp_running) { 1095 u_if->nwamd_if_dhcp_configured = B_TRUE; 1096 } else if (u_if->nwamd_if_stateful_requested && 1097 v6dhcp_running) { 1098 u_if->nwamd_if_stateful_configured = B_TRUE; 1099 } else if (u_if->nwamd_if_stateless_requested && 1100 stateless_running) { 1101 u_if->nwamd_if_stateless_configured = B_TRUE; 1102 } else if (!static_addr) { 1103 /* 1104 * This is something we didn't expect. Remove 1105 * the address. 1106 */ 1107 nwamd_down_interface(addrinfo->ia_aobjname, 1108 addrinfo->ia_atype, ncu->ncu_name); 1109 nifa->configured = B_FALSE; 1110 goto valid_done; 1111 } 1112 1113 /* 1114 * The address looks valid so mark configured and 1115 * move online if we either have a v4 address if 1116 * v4 is configured or a v6 address if only v6 is 1117 * configured. 1118 */ 1119 nifa->configured = B_TRUE; 1120 if (state != NWAM_STATE_ONLINE) 1121 interface_ncu_up(ncu); 1122 1123 /* 1124 * Refresh network/location since we may also have other 1125 * DHCP information. We might have to restore it first 1126 * in case it is in maintenance. 1127 */ 1128 nlog(LOG_DEBUG, "nwamd_handle_if_state_event: " 1129 "refreshing %s as we may have other " 1130 "DHCP information", NET_LOC_FMRI); 1131 (void) smf_restore_instance(NET_LOC_FMRI); 1132 if (smf_refresh_instance(NET_LOC_FMRI) != 0) { 1133 nlog(LOG_ERR, 1134 "nwamd_ncu_handle_if_state_" 1135 "event: refresh of %s " 1136 "failed", NET_LOC_FMRI); 1137 } 1138 1139 } else if (state == NWAM_STATE_ONLINE || 1140 state == NWAM_STATE_OFFLINE_TO_ONLINE) { 1141 /* 1142 * Address has been removed. Only pay attention to 1143 * disappearing addresses if we are online or coming 1144 * online. 1145 * 1146 * Undo whatever configuration is necessary. Note 1147 * that this may or may not cause the NCU to go down. 1148 * We can get RTM_DELADDRs for duplicate addresses 1149 * so deal with this seperately. 1150 */ 1151 nifa->configured = B_FALSE; 1152 1153 if (!static_addr && family == AF_INET) { 1154 u_if->nwamd_if_dhcp_configured = B_FALSE; 1155 } else if (!static_addr && family == AF_INET6) { 1156 /* 1157 * The address is already gone. When looking 1158 * for the addrinfo (using aobjname in 1159 * ipaddr), we found addrinfo for either one 1160 * or both stateless and stateful. Using the 1161 * flags we determined whether each was 1162 * configured or not. Update the flags here 1163 * accordingly. 1164 */ 1165 u_if->nwamd_if_stateful_configured = 1166 stateless_ai_found; 1167 u_if->nwamd_if_stateless_configured = 1168 stateful_ai_found; 1169 } 1170 1171 if (flags & IFF_DUPLICATE) { 1172 nlog(LOG_INFO, 1173 "nwamd_ncu_handle_if_state_event: " 1174 "duplicate address detected on %s", 1175 ncu->ncu_name); 1176 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1177 event->event_object, 1178 NWAM_STATE_MAINTENANCE, 1179 NWAM_AUX_STATE_IF_DUPLICATE_ADDR); 1180 } else { 1181 interface_ncu_down(ncu); 1182 } 1183 } 1184 valid_done: 1185 ipadm_free_addr_info(ai); 1186 } 1187 nwamd_object_release(ncu_obj); 1188 } 1189 1190 void 1191 nwamd_ncu_handle_if_action_event(nwamd_event_t event) 1192 { 1193 nwamd_object_t ncu_obj; 1194 1195 nlog(LOG_DEBUG, "if action event %s", 1196 event->event_object[0] == '\0' ? "n/a" : event->event_object); 1197 1198 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object); 1199 if (ncu_obj == NULL) { 1200 nlog(LOG_ERR, "nwamd_ncu_handle_if_action_event: no object"); 1201 nwamd_event_do_not_send(event); 1202 return; 1203 } 1204 nwamd_object_release(ncu_obj); 1205 } 1206 1207 /* 1208 * Remove the address in the given aobjname. IPADM_OPT_RELEASE is specified 1209 * for a DHCP address and specifies that the DHCP lease should also be released. 1210 * ifname is only used for nlog(). 1211 */ 1212 static void 1213 nwamd_down_interface(const char *aobjname, ipadm_addr_type_t atype, 1214 const char *ifname) 1215 { 1216 ipadm_status_t ipstatus; 1217 uint32_t rflags = (atype == IPADM_ADDR_DHCP ? IPADM_OPT_RELEASE : 0); 1218 1219 nlog(LOG_DEBUG, "nwamd_down_interface: %s [aobjname = %s]", 1220 ifname, aobjname); 1221 if ((ipstatus = ipadm_delete_addr(ipadm_handle, aobjname, 1222 IPADM_OPT_ACTIVE | rflags)) != IPADM_SUCCESS) { 1223 nlog(LOG_ERR, "nwamd_down_interface: " 1224 "ipadm_delete_addr failed on %s: %s", 1225 ifname, ipadm_status2str(ipstatus)); 1226 } 1227 } 1228 1229 static void 1230 unconfigure_addresses(nwamd_ncu_t *ncu, sa_family_t af) 1231 { 1232 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 1233 1234 for (nifap = nifa; nifap != NULL; nifap = nifap->next) 1235 if (af == AF_UNSPEC || nifap->family == af) 1236 nifap->configured = B_FALSE; 1237 } 1238 1239 static void 1240 dhcp_release(const char *ifname) 1241 { 1242 ipadm_addr_info_t *ainfo, *ainfop; 1243 1244 if (ipadm_addr_info(ipadm_handle, ifname, &ainfo, 0, 0) 1245 != IPADM_SUCCESS) 1246 return; 1247 1248 for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { 1249 if (ainfop->ia_atype == IPADM_ADDR_DHCP) 1250 nwamd_down_interface(ainfop->ia_aobjname, 1251 ainfop->ia_atype, ifname); 1252 } 1253 ipadm_free_addr_info(ainfo); 1254 } 1255 1256 static void 1257 nwamd_plumb_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af, boolean_t plumb) 1258 { 1259 char *ifname = ncu->ncu_name; 1260 nwamd_if_t *u_if = &ncu->ncu_if; 1261 ipadm_status_t ipstatus; 1262 1263 nlog(LOG_DEBUG, "nwamd_plumb_unplumb_interface: %s %s %s", 1264 (plumb ? "plumb" : "unplumb"), (af == AF_INET ? "IPv4" : "IPv6"), 1265 ifname); 1266 1267 if (plumb) { 1268 ipstatus = ipadm_create_if(ipadm_handle, ifname, af, 1269 IPADM_OPT_ACTIVE); 1270 } else { 1271 /* release DHCP address, if any */ 1272 if (af == AF_INET) 1273 dhcp_release(ifname); 1274 ipstatus = ipadm_delete_if(ipadm_handle, ifname, af, 1275 IPADM_OPT_ACTIVE); 1276 } 1277 1278 if (ipstatus != IPADM_SUCCESS) { 1279 if ((plumb && ipstatus != IPADM_IF_EXISTS) || 1280 (!plumb && ipstatus != IPADM_ENXIO)) { 1281 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: " 1282 "%s %s failed for %s: %s", 1283 (plumb ? "plumb" : "unplumb"), 1284 (af == AF_INET ? "IPv4" : "IPv6"), 1285 ifname, ipadm_status2str(ipstatus)); 1286 } 1287 } 1288 1289 /* Unset flags */ 1290 if (!plumb) { 1291 unconfigure_addresses(ncu, af); 1292 switch (af) { 1293 case AF_INET: 1294 u_if->nwamd_if_dhcp_configured = B_FALSE; 1295 break; 1296 case AF_INET6: 1297 u_if->nwamd_if_stateful_configured = B_FALSE; 1298 u_if->nwamd_if_stateless_configured = B_FALSE; 1299 break; 1300 } 1301 } 1302 } 1303 1304 void 1305 nwamd_plumb_interface(nwamd_ncu_t *ncu, sa_family_t af) 1306 { 1307 /* 1308 * We get all posssible privs by calling nwamd_deescalate(). During 1309 * startup opening /dev/dld (data link management) needs all privs 1310 * because we don't have access to /etc/security/device_policy yet. 1311 */ 1312 nwamd_escalate(); 1313 nwamd_plumb_unplumb_interface(ncu, af, B_TRUE); 1314 nwamd_deescalate(); 1315 } 1316 1317 void 1318 nwamd_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af) 1319 { 1320 nwamd_plumb_unplumb_interface(ncu, af, B_FALSE); 1321 } 1322 1323 static void * 1324 start_dhcp_thread(void *arg) 1325 { 1326 struct nwamd_dhcp_thread_arg *thread_arg = arg; 1327 nwamd_object_t ncu_obj; 1328 dhcp_ipc_type_t type; 1329 char *name; 1330 ipadm_addrobj_t ipaddr; 1331 ipadm_status_t ipstatus; 1332 int retries = 0; 1333 1334 name = thread_arg->name; 1335 type = thread_arg->type; 1336 ipaddr = thread_arg->ipaddr; 1337 1338 retry: 1339 /* Make sure the NCU is in appropriate state for DHCP command */ 1340 ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE, name); 1341 if (ncu_obj == NULL) { 1342 nlog(LOG_ERR, "start_dhcp: no IP object %s", name); 1343 return (NULL); 1344 } 1345 1346 if (ncu_obj->nwamd_object_state != NWAM_STATE_OFFLINE_TO_ONLINE && 1347 ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) { 1348 nlog(LOG_INFO, "start_dhcp: IP NCU %s is in invalid state " 1349 "for DHCP command", ncu_obj->nwamd_object_name); 1350 nwamd_object_release(ncu_obj); 1351 return (NULL); 1352 } 1353 nwamd_object_release(ncu_obj); 1354 1355 switch (type) { 1356 case DHCP_INFORM: 1357 { 1358 char aobjname[IPADM_AOBJSIZ]; 1359 1360 if ((ipstatus = ipadm_get_aobjname(ipaddr, aobjname, 1361 sizeof (aobjname))) != IPADM_SUCCESS) { 1362 nlog(LOG_ERR, "start_dhcp: " 1363 "ipadm_get_aobjname failed for %s: %s", 1364 name, ipadm_status2str(ipstatus)); 1365 goto done; 1366 } 1367 ipstatus = ipadm_refresh_addr(ipadm_handle, aobjname, 1368 IPADM_OPT_ACTIVE | IPADM_OPT_INFORM); 1369 break; 1370 } 1371 case DHCP_START: 1372 ipstatus = ipadm_create_addr(ipadm_handle, ipaddr, 1373 IPADM_OPT_ACTIVE); 1374 break; 1375 default: 1376 nlog(LOG_ERR, "start_dhcp: invalid dhcp_ipc_type_t: %d", type); 1377 goto done; 1378 } 1379 1380 if (ipstatus == IPADM_DHCP_IPC_TIMEOUT) { 1381 /* 1382 * DHCP timed out: for DHCP_START requests, change state for 1383 * this NCU and euqueue event to check NCU priority-groups; 1384 * for DHCP_INFORM requests, nothing to do. 1385 */ 1386 if (type == DHCP_START) { 1387 char *object_name; 1388 1389 nlog(LOG_INFO, 1390 "start_dhcp: DHCP_START timed out for %s", name); 1391 1392 if (nwam_ncu_name_to_typed_name(name, 1393 NWAM_NCU_TYPE_INTERFACE, &object_name) 1394 != NWAM_SUCCESS) { 1395 nlog(LOG_ERR, "start_dhcp: " 1396 "nwam_ncu_name_to_typed_name failed for %s", 1397 name); 1398 goto done; 1399 } 1400 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1401 object_name, NWAM_STATE_OFFLINE_TO_ONLINE, 1402 NWAM_AUX_STATE_IF_DHCP_TIMED_OUT); 1403 nwamd_create_ncu_check_event(0); 1404 free(object_name); 1405 } else { 1406 nlog(LOG_INFO, 1407 "start_dhcp: DHCP_INFORM timed out for %s", name); 1408 } 1409 1410 } else if ((ipstatus == IPADM_DHCP_IPC_ERROR || 1411 ipstatus == IPADM_IPC_ERROR) && retries++ < NWAMD_DHCP_RETRIES) { 1412 /* 1413 * Retry DHCP request as we may have been unplumbing as part 1414 * of the configuration phase. 1415 */ 1416 nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr on %s returned: %s, " 1417 "retrying in %d sec", 1418 (type == DHCP_START ? "create" : "refresh"), name, 1419 ipadm_status2str(ipstatus), NWAMD_DHCP_RETRY_WAIT_TIME); 1420 (void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME); 1421 goto retry; 1422 1423 } else if (ipstatus != IPADM_SUCCESS) { 1424 nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr failed for %s: %s", 1425 (type == DHCP_START ? "create" : "refresh"), name, 1426 ipadm_status2str(ipstatus)); 1427 } 1428 1429 done: 1430 free(name); 1431 free(arg); 1432 return (NULL); 1433 } 1434 1435 static void 1436 nwamd_dhcp(const char *ifname, ipadm_addrobj_t ipaddr, dhcp_ipc_type_t cmd) 1437 { 1438 struct nwamd_dhcp_thread_arg *arg; 1439 pthread_attr_t attr; 1440 1441 nlog(LOG_DEBUG, "nwamd_dhcp: starting DHCP %s thread for %s", 1442 dhcp_ipc_type_to_string(cmd), ifname); 1443 1444 arg = malloc(sizeof (*arg)); 1445 if (arg == NULL) { 1446 nlog(LOG_ERR, "nwamd_dhcp: error allocating memory for " 1447 "dhcp request"); 1448 return; 1449 } 1450 1451 arg->name = strdup(ifname); 1452 arg->type = cmd; 1453 arg->ipaddr = ipaddr; 1454 1455 (void) pthread_attr_init(&attr); 1456 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1457 if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) { 1458 nlog(LOG_ERR, "nwamd_dhcp: cannot start dhcp thread"); 1459 free(arg->name); 1460 free(arg); 1461 (void) pthread_attr_destroy(&attr); 1462 return; 1463 } 1464 (void) pthread_attr_destroy(&attr); 1465 } 1466