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; 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(4) */ 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 /* is this address an expected static one? */ 390 addr = a->ia_ifa.ifa_addr; 391 if (find_static_address(addr, ncu) != NULL) 392 continue; 393 394 /* 395 * For IPv4, DHCPRUNNING flag is set when dhcpagent is in 396 * the process of getting an address, but doesn't have one 397 * yet (interface has 0.0.0.0). For IPv6, DHCPRUNNING flag 398 * is set on the link-local address if trying to get a 399 * stateful address. In both cases, consider the interface 400 * as not being managed by DHCP and skip checking of flags. 401 */ 402 if ((protocol == AF_INET && 403 ((struct sockaddr_in *)addr)->sin_addr.s_addr == 404 INADDR_ANY) || 405 (protocol == AF_INET6 && 406 IN6_IS_ADDR_LINKLOCAL( 407 &((struct sockaddr_in6 *)addr)->sin6_addr))) { 408 continue; 409 } 410 411 flags = a->ia_ifa.ifa_flags; 412 if (flags & IFF_DHCPRUNNING) { 413 /* 414 * If we get here we have an address that has the 415 * DHCP flag set and isn't an expected static address. 416 */ 417 rv = B_TRUE; 418 break; 419 } 420 } 421 422 ipadm_free_addr_info(addrinfo); 423 return (rv); 424 } 425 426 /* 427 * Return B_TRUE if IPv4 is requested in the given NCU. 428 */ 429 static boolean_t 430 nwamd_v4_requested(nwamd_ncu_t *ncu) 431 { 432 boolean_t anyv4_requested; 433 nwamd_if_t *u_if; 434 435 anyv4_requested = B_FALSE; 436 u_if = &ncu->ncu_if; 437 if (u_if->nwamd_if_dhcp_requested) { 438 anyv4_requested = B_TRUE; 439 } else { 440 struct nwamd_if_address *n; 441 442 for (n = u_if->nwamd_if_list; n != NULL; n = n->next) { 443 if (n->family == AF_INET && 444 n->ipaddr_atype == IPADM_ADDR_STATIC) 445 break; 446 } 447 if (n != NULL) 448 anyv4_requested = B_TRUE; 449 } 450 451 return (anyv4_requested); 452 } 453 454 /* 455 * Returns B_TRUE if IPv6 is requested in the given NCU. 456 */ 457 static boolean_t 458 nwamd_v6_requested(nwamd_ncu_t *ncu) 459 { 460 boolean_t anyv6_requested; 461 nwamd_if_t *u_if; 462 463 anyv6_requested = B_FALSE; 464 u_if = &ncu->ncu_if; 465 if (u_if->nwamd_if_stateful_requested || 466 u_if->nwamd_if_stateless_requested) { 467 anyv6_requested = B_TRUE; 468 } else { 469 struct nwamd_if_address *n; 470 471 for (n = u_if->nwamd_if_list; n != NULL; n = n->next) { 472 if (n->family == AF_INET6 && 473 n->ipaddr_atype == IPADM_ADDR_STATIC) 474 break; 475 } 476 if (n != NULL) 477 anyv6_requested = B_TRUE; 478 } 479 480 return (anyv6_requested); 481 } 482 483 /* 484 * Bring up the ncu if we have the right combination of requested configuration 485 * and actual configuration and up is true, or bring down the ncu if no 486 * addresses are configured, and up is false. 487 */ 488 static void 489 interface_ncu_up_down(nwamd_ncu_t *ncu, boolean_t up) 490 { 491 boolean_t ncu_online; 492 char *name; 493 494 assert(ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE); 495 496 /* 497 * If V4 with or without V6 is configured then one of its interfaces 498 * needs to be up for the ncu to come online. If only V6 is requested 499 * then one of its interfaces needs to be up for the ncu to come online. 500 */ 501 ncu_online = B_FALSE; 502 if (nwamd_v4_requested(ncu)) { 503 if (nwamd_dhcp_managing(AF_INET, ncu) || 504 nwamd_static_addresses_configured(ncu, AF_INET)) 505 ncu_online = B_TRUE; 506 } else if (nwamd_v6_requested(ncu)) { 507 if ((nwamd_dhcp_managing(AF_INET6, ncu) || 508 stateless_running(ncu) || 509 nwamd_static_addresses_configured(ncu, AF_INET6))) 510 ncu_online = B_TRUE; 511 } 512 513 if (nwam_ncu_name_to_typed_name(ncu->ncu_name, ncu->ncu_type, &name) != 514 NWAM_SUCCESS) { 515 nlog(LOG_DEBUG, "interface_ncu_up_down: " 516 "nwam_ncu_name_to_typed_name failed"); 517 return; 518 } 519 if (ncu_online && up) { 520 nlog(LOG_DEBUG, "interface_ncu_up_down: " 521 "bringing %s up", name); 522 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name, 523 NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_UP); 524 } else if (!ncu_online && !up) { 525 nlog(LOG_DEBUG, "interface_ncu_up_down: " 526 "bringing %s down", name); 527 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name, 528 NWAM_STATE_ONLINE_TO_OFFLINE, 529 NWAM_AUX_STATE_DOWN); 530 } 531 532 free(name); 533 } 534 535 static void 536 interface_ncu_up(nwamd_ncu_t *ncu) 537 { 538 interface_ncu_up_down(ncu, B_TRUE); 539 } 540 541 static void 542 interface_ncu_down(nwamd_ncu_t *ncu) 543 { 544 interface_ncu_up_down(ncu, B_FALSE); 545 } 546 547 static boolean_t 548 stateless_running(const nwamd_ncu_t *ncu) 549 { 550 ipadm_addr_info_t *ainfo, *ainfop; 551 ipadm_status_t ipstatus; 552 boolean_t rv = B_FALSE; 553 uint64_t flags; 554 555 if ((ipstatus = ipadm_addr_info(ipadm_handle, ncu->ncu_name, &ainfo, 556 0, 0)) != IPADM_SUCCESS) { 557 nlog(LOG_ERR, "stateless_running: " 558 "ipadm_addr_info failed for %s: %s", 559 ncu->ncu_name, ipadm_status2str(ipstatus)); 560 return (B_FALSE); 561 } 562 563 for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { 564 if (ainfop->ia_ifa.ifa_addr->ss_family != AF_INET6) 565 continue; 566 flags = ainfop->ia_ifa.ifa_flags; 567 if (flags & STATELESS_RUNNING) { 568 rv = B_TRUE; 569 break; 570 } 571 } 572 ipadm_free_addr_info(ainfo); 573 return (rv); 574 } 575 576 /* 577 * Returns the addrinfo associated with the given address. There is always 578 * only one addrinfo for each address. 579 */ 580 static boolean_t 581 addrinfo_for_addr(const struct sockaddr_storage *caddr, const char *ifname, 582 ipadm_addr_info_t **ainfo) 583 { 584 ipadm_addr_info_t *addrinfo, *ainfop, *last = NULL; 585 ipadm_status_t ipstatus; 586 587 ipstatus = ipadm_addr_info(ipadm_handle, ifname, &addrinfo, 0, 0); 588 if (ipstatus != IPADM_SUCCESS) { 589 nlog(LOG_INFO, "addrinfo_for_addr: " 590 "ipadm_addr_info failed for %s: %s", 591 ifname, ipadm_status2str(ipstatus)); 592 return (B_FALSE); 593 } 594 595 *ainfo = NULL; 596 for (ainfop = addrinfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { 597 /* 598 * If addresses match, rearrange pointers so that addrinfo 599 * does not contain a, and return a. 600 */ 601 if (sockaddrcmp(ainfop->ia_ifa.ifa_addr, caddr)) { 602 if (last != NULL) 603 last->ia_ifa.ifa_next = ainfop->ia_ifa.ifa_next; 604 else 605 addrinfo = IA_NEXT(ainfop); 606 607 ainfop->ia_ifa.ifa_next = NULL; 608 *ainfo = ainfop; 609 break; 610 } 611 last = ainfop; 612 } 613 ipadm_free_addr_info(addrinfo); 614 return (*ainfo == NULL ? B_FALSE : B_TRUE); 615 } 616 617 /* 618 * Returns B_TRUE if the addrinfo associated with the given ipaddr using its 619 * aobjname is found. An addrinfo list is created and returned in ainfo. 620 * Stateless and stateful IPv6 addrinfo have the same aobjname, thus the need 621 * to create a list of addrinfo. 622 */ 623 static boolean_t 624 addrinfo_for_ipaddr(ipadm_addrobj_t ipaddr, const char *ifname, 625 ipadm_addr_info_t **ainfo) 626 { 627 char aobjname[IPADM_AOBJSIZ]; 628 ipadm_addr_info_t *addrinfo, *ainfop; 629 ipadm_addr_info_t *last = NULL; 630 ipadm_status_t ipstatus; 631 632 ipstatus = ipadm_get_aobjname(ipaddr, aobjname, sizeof (aobjname)); 633 if (ipstatus != IPADM_SUCCESS) 634 return (B_FALSE); 635 636 ipstatus = ipadm_addr_info(ipadm_handle, ifname, &addrinfo, 0, 0); 637 if (ipstatus != IPADM_SUCCESS) { 638 nlog(LOG_INFO, "addrinfo_for_ipaddr: " 639 "ipadm_addr_info failed for %s: %s", 640 ifname, ipadm_status2str(ipstatus)); 641 return (B_FALSE); 642 } 643 644 *ainfo = NULL; 645 ainfop = addrinfo; 646 while (ainfop != NULL) { 647 /* If aobjnames match, rearrange pointers to create new list */ 648 if (strcmp(ainfop->ia_aobjname, aobjname) == 0) { 649 ipadm_addr_info_t *match = ainfop; 650 651 ainfop = IA_NEXT(ainfop); /* move iterator */ 652 if (last != NULL) 653 last->ia_ifa.ifa_next = match->ia_ifa.ifa_next; 654 else 655 addrinfo = ainfop; 656 if (*ainfo == NULL) 657 match->ia_ifa.ifa_next = NULL; 658 else 659 match->ia_ifa.ifa_next = &(*ainfo)->ia_ifa; 660 *ainfo = match; 661 } else { 662 last = ainfop; 663 ainfop = IA_NEXT(ainfop); 664 } 665 } 666 ipadm_free_addr_info(addrinfo); 667 return (*ainfo == NULL ? B_FALSE : B_TRUE); 668 } 669 670 /* 671 * Add the address provided in the nwamd_if_address. If DHCP is required, 672 * start DHCP. If a static address is configured, create the address; then do 673 * a DHCP_INFORM (in a separate thread) to get other networking configuration 674 * parameters. RTM_NEWADDRs - translated into IF_STATE events - will then 675 * finish the job of bringing the NCU online. 676 */ 677 static boolean_t 678 add_ip_address(const char *ifname, const struct nwamd_if_address *nifa) 679 { 680 ipadm_status_t ipstatus; 681 ipadm_addr_info_t *addrinfo = NULL; 682 uint64_t flags; 683 684 if (nifa->ipaddr_atype == IPADM_ADDR_DHCP) { 685 /* 686 * To make getting a DHCP address asynchronous, call 687 * ipadm_create_addr() in a new thread. 688 */ 689 nlog(LOG_DEBUG, "add_ip_address: " 690 "adding IPv4 DHCP address on %s", ifname); 691 nwamd_dhcp(ifname, nifa->ipaddr, DHCP_START); 692 } else { 693 nlog(LOG_DEBUG, "add_ip_address: adding %s address on %s", 694 (nifa->ipaddr_atype == IPADM_ADDR_STATIC ? 695 "STATIC" : "IPv6 ADDRCONF"), ifname); 696 if ((ipstatus = ipadm_create_addr(ipadm_handle, nifa->ipaddr, 697 IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) { 698 nlog(LOG_ERR, "add_ip_address: " 699 "ipadm_create_addr failed on %s: %s", 700 ifname, ipadm_status2str(ipstatus)); 701 return (B_FALSE); 702 } 703 /* 704 * When creating a static address, ipadm_create_addr() returns 705 * SUCCESS even if duplicate address is detected. Retrieve 706 * the addrinfo to get the flags. 707 */ 708 if (nifa->ipaddr_atype == IPADM_ADDR_STATIC) { 709 /* 710 * Since we are configuring a static address, there 711 * will be just *ONE* addrinfo with the aobjname in 712 * nifa->ipaddr. 713 */ 714 if (!addrinfo_for_ipaddr(nifa->ipaddr, ifname, 715 &addrinfo)) { 716 nlog(LOG_ERR, "add_ip_address: " 717 "could not find addrinfo on %s", ifname); 718 return (B_FALSE); 719 } 720 721 flags = addrinfo->ia_ifa.ifa_flags; 722 ipadm_free_addr_info(addrinfo); 723 if (flags & IFF_DUPLICATE) { 724 char *object_name; 725 nwam_error_t err; 726 727 nlog(LOG_INFO, "add_ip_address: " 728 "duplicate address detected on %s", ifname); 729 if ((err = nwam_ncu_name_to_typed_name(ifname, 730 NWAM_NCU_TYPE_INTERFACE, &object_name)) 731 == NWAM_SUCCESS) { 732 nwamd_object_set_state( 733 NWAM_OBJECT_TYPE_NCU, 734 object_name, NWAM_STATE_MAINTENANCE, 735 NWAM_AUX_STATE_IF_DUPLICATE_ADDR); 736 free(object_name); 737 } else { 738 nlog(LOG_ERR, "add_ip_address: " 739 "could not create state event " 740 "for %s: %s", 741 ifname, nwam_strerror(err)); 742 } 743 return (B_FALSE); 744 } 745 /* Do DHCP_INFORM using async ipadm_refresh_addr() */ 746 nwamd_dhcp(ifname, nifa->ipaddr, DHCP_INFORM); 747 } 748 } 749 750 return (B_TRUE); 751 } 752 753 /* 754 * Adds addresses for the given NCU. 755 */ 756 void 757 nwamd_configure_interface_addresses(nwamd_ncu_t *ncu) 758 { 759 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 760 761 nlog(LOG_DEBUG, "nwamd_configure_interface_addresses(%s)", 762 ncu->ncu_name); 763 764 for (nifap = nifa; nifap != NULL; nifap = nifap->next) { 765 if (nifap->configured) 766 continue; 767 768 nifap->configured = add_ip_address(ncu->ncu_name, nifap); 769 } 770 } 771 772 /* 773 * This event tells us that an interface address has appeared or disappeared, 774 * or that the interface flags on an interface have changed. 775 */ 776 void 777 nwamd_ncu_handle_if_state_event(nwamd_event_t event) 778 { 779 nwam_event_t evm; 780 nwamd_object_t ncu_obj; 781 nwamd_ncu_t *ncu; 782 nwam_state_t state; 783 nwam_aux_state_t aux_state; 784 785 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, 786 event->event_object); 787 if (ncu_obj == NULL) { 788 nlog(LOG_ERR, "nwamd_ncu_handle_if_state_event: no object %s", 789 event->event_object); 790 nwamd_event_do_not_send(event); 791 return; 792 } 793 ncu = ncu_obj->nwamd_object_data; 794 evm = event->event_msg; 795 state = ncu_obj->nwamd_object_state; 796 aux_state = ncu_obj->nwamd_object_aux_state; 797 798 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 799 "if %s, state (%s, %s)", event->event_object, 800 nwam_state_to_string(state), nwam_aux_state_to_string(aux_state)); 801 802 /* Ensure object is in correct state to handle IF state events */ 803 switch (state) { 804 case NWAM_STATE_OFFLINE_TO_ONLINE: 805 if (aux_state != NWAM_AUX_STATE_IF_WAITING_FOR_ADDR && 806 aux_state != NWAM_AUX_STATE_IF_DHCP_TIMED_OUT) { 807 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 808 "if %s is in invalid aux state %s for IF_STATE " 809 "events", event->event_object, 810 nwam_aux_state_to_string(aux_state)); 811 nwamd_event_do_not_send(event); 812 nwamd_object_release(ncu_obj); 813 return; 814 } 815 break; 816 case NWAM_STATE_ONLINE: 817 /* 818 * We can get addresses from DHCP after we've taken the interface down. 819 * We deal with those below. 820 */ 821 case NWAM_STATE_ONLINE_TO_OFFLINE: 822 case NWAM_STATE_OFFLINE: 823 break; 824 default: 825 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 826 "if %s is in invalid state %s for IF_STATE events", 827 event->event_object, nwam_state_to_string(state)); 828 nwamd_event_do_not_send(event); 829 nwamd_object_release(ncu_obj); 830 return; 831 } 832 833 if (evm->nwe_data.nwe_if_state.nwe_addr_valid) { 834 struct nwam_event_if_state *if_state; 835 char addrstr[INET6_ADDRSTRLEN]; 836 boolean_t static_addr, addr_added; 837 boolean_t v4dhcp_running, v6dhcp_running, stateless_running; 838 ipadm_addr_info_t *ai = NULL, *addrinfo = NULL; 839 boolean_t stateless_ai_found = B_FALSE; 840 boolean_t stateful_ai_found = B_FALSE; 841 struct nwamd_if_address *nifa = NULL; 842 nwamd_if_t *u_if; 843 struct sockaddr_storage *addr, *ai_addr = 0; 844 ushort_t family; 845 uint64_t flags = 0; 846 847 if_state = &evm->nwe_data.nwe_if_state; 848 u_if = &ncu->ncu_if; 849 family = if_state->nwe_addr.ss_family; 850 addr = &if_state->nwe_addr; 851 addr_added = if_state->nwe_addr_added; 852 853 nlog(LOG_DEBUG, 854 "nwamd_ncu_handle_if_state_event: addr %s %s", 855 nwamd_sockaddr2str((struct sockaddr *)addr, addrstr, 856 sizeof (addrstr)), addr_added ? "added" : "removed"); 857 858 /* 859 * Need to get flags for this interface. Get the addrinfo for 860 * the address that generated this IF_STATE event. 861 */ 862 if (addr_added) { 863 /* 864 * Address was added. Find the addrinfo for this 865 * address and the nwamd_if_address corresponding to 866 * this address. 867 */ 868 if (!addrinfo_for_addr(addr, ncu->ncu_name, &ai)) { 869 nlog(LOG_ERR, 870 "nwamd_ncu_handle_if_state_event: " 871 "addrinfo doesn't exist for %s", addrstr); 872 nwamd_event_do_not_send(event); 873 goto valid_done; 874 } 875 addrinfo = ai; 876 flags = addrinfo->ia_ifa.ifa_flags; 877 ai_addr = addrinfo->ia_ifa.ifa_addr; 878 879 if (addrinfo->ia_atype == IPADM_ADDR_IPV6_ADDRCONF || 880 addrinfo->ia_atype == IPADM_ADDR_DHCP) 881 nifa = find_nonstatic_address(ncu, family); 882 else if (addrinfo->ia_atype == IPADM_ADDR_STATIC) 883 nifa = find_static_address(addr, ncu); 884 885 /* 886 * If nwamd_if_address is not found, then this address 887 * isn't one that nwamd created. Remove it. 888 */ 889 if (nifa == NULL) { 890 nlog(LOG_ERR, 891 "nwamd_ncu_handle_if_state_event: " 892 "address %s not managed by nwam added, " 893 "removing it", addrstr); 894 nwamd_down_interface(addrinfo->ia_aobjname, 895 addrinfo->ia_atype, ncu->ncu_name); 896 nwamd_event_do_not_send(event); 897 goto valid_done; 898 } 899 900 /* check flags to determine how intf is configured */ 901 stateless_running = (family == AF_INET6) && 902 ((flags & STATELESS_RUNNING) == STATELESS_RUNNING); 903 v4dhcp_running = (family == AF_INET) && 904 ((flags & DHCP_RUNNING) == DHCP_RUNNING); 905 v6dhcp_running = (family == AF_INET6) && 906 ((flags & DHCP_RUNNING) == DHCP_RUNNING); 907 static_addr = (addrinfo->ia_atype == IPADM_ADDR_STATIC); 908 909 /* copy the configured address into nwamd_if_address */ 910 if (stateless_running) { 911 (void) memcpy(&nifa->conf_stateless_addr, 912 addrinfo->ia_ifa.ifa_addr, 913 sizeof (struct sockaddr_storage)); 914 } else { 915 (void) memcpy(&nifa->conf_addr, 916 addrinfo->ia_ifa.ifa_addr, 917 sizeof (struct sockaddr_storage)); 918 } 919 920 } else { 921 /* 922 * Address was removed. Find the nwamd_if_address 923 * that configured this address. 924 */ 925 nifa = find_configured_address(addr, ncu); 926 if (nifa == NULL) { 927 nlog(LOG_ERR, 928 "nwamd_ncu_handle_if_state_event: " 929 "address %s not managed by nwam removed, " 930 "nothing to do", addrstr); 931 nwamd_event_do_not_send(event); 932 goto valid_done; 933 } 934 935 if (addrinfo_for_ipaddr(nifa->ipaddr, ncu->ncu_name, 936 &ai)) { 937 ipadm_addr_info_t *a; 938 for (a = ai; a != NULL; a = IA_NEXT(a)) { 939 /* 940 * Since multiple addrinfo can have 941 * the same ipaddr, find the one for 942 * the address that generated this 943 * state event. 944 */ 945 if (sockaddrcmp(addr, 946 a->ia_ifa.ifa_addr)) { 947 flags = a->ia_ifa.ifa_flags; 948 ai_addr = a->ia_ifa.ifa_addr; 949 addrinfo = a; 950 } 951 /* 952 * Stateful and stateless IPv6 953 * addrinfo have the same aobjname. 954 * Use the flags to determine which 955 * address is present in the system. 956 */ 957 if (family == AF_INET6) { 958 stateless_ai_found = 959 (a->ia_ifa.ifa_flags & 960 STATELESS_RUNNING); 961 stateful_ai_found = 962 (a->ia_ifa.ifa_flags & 963 DHCP_RUNNING); 964 } 965 } 966 } 967 } 968 969 /* Set the flags in the event for listeners */ 970 evm->nwe_data.nwe_if_state.nwe_flags = flags; 971 972 if (family == AF_INET && !addr_added) { 973 /* 974 * Check for failure due to CR 6745448: if we get a 975 * report that an address has been deleted, then check 976 * for interface up, datalink down, and actual address 977 * non-zero. If that combination is seen, then this is 978 * a DHCP cached lease, and we need to remove it from 979 * the system, or it'll louse up the kernel routes 980 * (which aren't smart enough to avoid dead 981 * interfaces). 982 */ 983 if (((struct sockaddr_in *)addr)->sin_addr.s_addr 984 == INADDR_ANY && ai_addr != 0) { 985 struct sockaddr_in *a; 986 char astr[INET6_ADDRSTRLEN]; 987 a = (struct sockaddr_in *)ai_addr; 988 989 if ((flags & IFF_UP) && 990 !(flags & IFF_RUNNING) && 991 a->sin_addr.s_addr != INADDR_ANY) { 992 nlog(LOG_DEBUG, 993 "nwamd_ncu_handle_if_state_event: " 994 "bug workaround: clear out addr " 995 "%s on %s", nwamd_sockaddr2str 996 ((struct sockaddr *)a, astr, 997 sizeof (astr)), 998 ncu->ncu_name); 999 nwamd_down_interface( 1000 addrinfo->ia_aobjname, 1001 IPADM_ADDR_DHCP, ncu->ncu_name); 1002 } 1003 goto valid_done; 1004 } 1005 } 1006 1007 /* 1008 * If we received an RTM_NEWADDR and the IFF_UP flags has not 1009 * been set, ignore this IF_STATE event. Once the IFF_UP flag 1010 * is set, we'll get another RTM_NEWADDR message. 1011 */ 1012 if (addr_added & !(flags & IFF_UP)) { 1013 nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: " 1014 "address %s added on %s without IFF_UP flag (%x), " 1015 "ignoring IF_STATE event", 1016 addrstr, ncu->ncu_name, flags); 1017 nwamd_event_do_not_send(event); 1018 goto valid_done; 1019 } 1020 1021 /* 1022 * Has the address really been removed? Sometimes spurious 1023 * RTM_DELADDRs are generated, so we need to ensure that 1024 * the address is really gone. If IFF_DUPLICATE is set, 1025 * we're getting the RTM_DELADDR due to DAD, so don't test 1026 * in that case. 1027 */ 1028 if (!addr_added && !(flags & IFF_DUPLICATE)) { 1029 if (ai_addr != 0 && sockaddrcmp(addr, ai_addr)) { 1030 nlog(LOG_INFO, 1031 "nwamd_ncu_handle_if_state_event: " 1032 "address %s is not really gone from %s, " 1033 "ignoring IF_STATE event", 1034 addrstr, ncu->ncu_name); 1035 nwamd_event_do_not_send(event); 1036 goto valid_done; 1037 } 1038 } 1039 1040 if (addr_added) { 1041 /* 1042 * Address has been added. 1043 * 1044 * We need to make sure that we really want to keep 1045 * this address. There is a race where we requested an 1046 * address but by the time we got here we don't really 1047 * want it and need to remove it. 1048 * 1049 * Once we decide we want the address adjust the ncu 1050 * state accordingly. For example if this address is 1051 * enough move online. 1052 */ 1053 if (u_if->nwamd_if_dhcp_requested && v4dhcp_running) { 1054 u_if->nwamd_if_dhcp_configured = B_TRUE; 1055 } else if (u_if->nwamd_if_stateful_requested && 1056 v6dhcp_running) { 1057 u_if->nwamd_if_stateful_configured = B_TRUE; 1058 } else if (u_if->nwamd_if_stateless_requested && 1059 stateless_running) { 1060 u_if->nwamd_if_stateless_configured = B_TRUE; 1061 } else if (!static_addr) { 1062 /* 1063 * This is something we didn't expect. Remove 1064 * the address. 1065 */ 1066 nwamd_down_interface(addrinfo->ia_aobjname, 1067 addrinfo->ia_atype, ncu->ncu_name); 1068 nifa->configured = B_FALSE; 1069 goto valid_done; 1070 } 1071 1072 /* 1073 * The address looks valid so mark configured and 1074 * move online if we either have a v4 address if 1075 * v4 is configured or a v6 address if only v6 is 1076 * configured. 1077 */ 1078 nifa->configured = B_TRUE; 1079 if (state != NWAM_STATE_ONLINE) 1080 interface_ncu_up(ncu); 1081 1082 /* 1083 * Refresh network/location since we may also have other 1084 * DHCP information. We might have to restore it first 1085 * in case it is in maintenance. 1086 */ 1087 nlog(LOG_DEBUG, "nwamd_handle_if_state_event: " 1088 "refreshing %s as we may have other " 1089 "DHCP information", NET_LOC_FMRI); 1090 (void) smf_restore_instance(NET_LOC_FMRI); 1091 if (smf_refresh_instance(NET_LOC_FMRI) != 0) { 1092 nlog(LOG_ERR, 1093 "nwamd_ncu_handle_if_state_" 1094 "event: refresh of %s " 1095 "failed", NET_LOC_FMRI); 1096 } 1097 1098 } else if (state == NWAM_STATE_ONLINE || 1099 state == NWAM_STATE_OFFLINE_TO_ONLINE) { 1100 /* 1101 * Address has been removed. Only pay attention to 1102 * disappearing addresses if we are online or coming 1103 * online. 1104 * 1105 * Undo whatever configuration is necessary. Note 1106 * that this may or may not cause the NCU to go down. 1107 * We can get RTM_DELADDRs for duplicate addresses 1108 * so deal with this seperately. 1109 */ 1110 nifa->configured = B_FALSE; 1111 1112 if (!static_addr && family == AF_INET) { 1113 u_if->nwamd_if_dhcp_configured = B_FALSE; 1114 } else if (!static_addr && family == AF_INET6) { 1115 /* 1116 * The address is already gone. When looking 1117 * for the addrinfo (using aobjname in 1118 * ipaddr), we found addrinfo for either one 1119 * or both stateless and stateful. Using the 1120 * flags we determined whether each was 1121 * configured or not. Update the flags here 1122 * accordingly. 1123 */ 1124 u_if->nwamd_if_stateful_configured = 1125 stateless_ai_found; 1126 u_if->nwamd_if_stateless_configured = 1127 stateful_ai_found; 1128 } 1129 1130 if (flags & IFF_DUPLICATE) { 1131 nlog(LOG_INFO, 1132 "nwamd_ncu_handle_if_state_event: " 1133 "duplicate address detected on %s", 1134 ncu->ncu_name); 1135 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1136 event->event_object, 1137 NWAM_STATE_MAINTENANCE, 1138 NWAM_AUX_STATE_IF_DUPLICATE_ADDR); 1139 } else { 1140 interface_ncu_down(ncu); 1141 } 1142 } 1143 valid_done: 1144 ipadm_free_addr_info(ai); 1145 } 1146 nwamd_object_release(ncu_obj); 1147 } 1148 1149 void 1150 nwamd_ncu_handle_if_action_event(nwamd_event_t event) 1151 { 1152 nwamd_object_t ncu_obj; 1153 1154 nlog(LOG_DEBUG, "if action event %s", 1155 event->event_object[0] == '\0' ? "n/a" : event->event_object); 1156 1157 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object); 1158 if (ncu_obj == NULL) { 1159 nlog(LOG_ERR, "nwamd_ncu_handle_if_action_event: no object"); 1160 nwamd_event_do_not_send(event); 1161 return; 1162 } 1163 nwamd_object_release(ncu_obj); 1164 } 1165 1166 /* 1167 * Remove the address in the given aobjname. IPADM_OPT_RELEASE is specified 1168 * for a DHCP address and specifies that the DHCP lease should also be released. 1169 * ifname is only used for nlog(). 1170 */ 1171 static void 1172 nwamd_down_interface(const char *aobjname, ipadm_addr_type_t atype, 1173 const char *ifname) 1174 { 1175 ipadm_status_t ipstatus; 1176 uint32_t rflags = (atype == IPADM_ADDR_DHCP ? IPADM_OPT_RELEASE : 0); 1177 1178 nlog(LOG_DEBUG, "nwamd_down_interface: %s [aobjname = %s]", 1179 ifname, aobjname); 1180 if ((ipstatus = ipadm_delete_addr(ipadm_handle, aobjname, 1181 IPADM_OPT_ACTIVE | rflags)) != IPADM_SUCCESS) { 1182 nlog(LOG_ERR, "nwamd_down_interface: " 1183 "ipadm_delete_addr failed on %s: %s", 1184 ifname, ipadm_status2str(ipstatus)); 1185 } 1186 } 1187 1188 static void 1189 unconfigure_addresses(nwamd_ncu_t *ncu, sa_family_t af) 1190 { 1191 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list; 1192 1193 for (nifap = nifa; nifap != NULL; nifap = nifap->next) 1194 if (af == AF_UNSPEC || nifap->family == af) 1195 nifap->configured = B_FALSE; 1196 } 1197 1198 static void 1199 dhcp_release(const char *ifname) 1200 { 1201 ipadm_addr_info_t *ainfo, *ainfop; 1202 1203 if (ipadm_addr_info(ipadm_handle, ifname, &ainfo, 0, 0) 1204 != IPADM_SUCCESS) 1205 return; 1206 1207 for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { 1208 if (ainfop->ia_atype == IPADM_ADDR_DHCP) 1209 nwamd_down_interface(ainfop->ia_aobjname, 1210 ainfop->ia_atype, ifname); 1211 } 1212 ipadm_free_addr_info(ainfo); 1213 } 1214 1215 static void 1216 nwamd_plumb_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af, boolean_t plumb) 1217 { 1218 char *ifname = ncu->ncu_name; 1219 nwamd_if_t *u_if = &ncu->ncu_if; 1220 ipadm_status_t ipstatus; 1221 1222 nlog(LOG_DEBUG, "nwamd_plumb_unplumb_interface: %s %s %s", 1223 (plumb ? "plumb" : "unplumb"), (af == AF_INET ? "IPv4" : "IPv6"), 1224 ifname); 1225 1226 if (plumb) { 1227 ipstatus = ipadm_create_if(ipadm_handle, ifname, af, 1228 IPADM_OPT_ACTIVE); 1229 } else { 1230 /* release DHCP address, if any */ 1231 if (af == AF_INET) 1232 dhcp_release(ifname); 1233 ipstatus = ipadm_delete_if(ipadm_handle, ifname, af, 1234 IPADM_OPT_ACTIVE); 1235 } 1236 1237 if (ipstatus != IPADM_SUCCESS) { 1238 if ((plumb && ipstatus != IPADM_IF_EXISTS) || 1239 (!plumb && ipstatus != IPADM_ENXIO)) { 1240 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: " 1241 "%s %s failed for %s: %s", 1242 (plumb ? "plumb" : "unplumb"), 1243 (af == AF_INET ? "IPv4" : "IPv6"), 1244 ifname, ipadm_status2str(ipstatus)); 1245 } 1246 } 1247 1248 /* Unset flags */ 1249 if (!plumb) { 1250 unconfigure_addresses(ncu, af); 1251 switch (af) { 1252 case AF_INET: 1253 u_if->nwamd_if_dhcp_configured = B_FALSE; 1254 break; 1255 case AF_INET6: 1256 u_if->nwamd_if_stateful_configured = B_FALSE; 1257 u_if->nwamd_if_stateless_configured = B_FALSE; 1258 break; 1259 } 1260 } 1261 } 1262 1263 void 1264 nwamd_plumb_interface(nwamd_ncu_t *ncu, sa_family_t af) 1265 { 1266 /* 1267 * We get all posssible privs by calling nwamd_deescalate(). During 1268 * startup opening /dev/dld (data link management) needs all privs 1269 * because we don't have access to /etc/security/device_policy yet. 1270 */ 1271 nwamd_escalate(); 1272 nwamd_plumb_unplumb_interface(ncu, af, B_TRUE); 1273 nwamd_deescalate(); 1274 } 1275 1276 void 1277 nwamd_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af) 1278 { 1279 nwamd_plumb_unplumb_interface(ncu, af, B_FALSE); 1280 } 1281 1282 static void * 1283 start_dhcp_thread(void *arg) 1284 { 1285 struct nwamd_dhcp_thread_arg *thread_arg = arg; 1286 nwamd_object_t ncu_obj; 1287 dhcp_ipc_type_t type; 1288 char *name; 1289 ipadm_addrobj_t ipaddr; 1290 ipadm_status_t ipstatus; 1291 int retries = 0; 1292 1293 name = thread_arg->name; 1294 type = thread_arg->type; 1295 ipaddr = thread_arg->ipaddr; 1296 1297 retry: 1298 /* Make sure the NCU is in appropriate state for DHCP command */ 1299 ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE, name); 1300 if (ncu_obj == NULL) { 1301 nlog(LOG_ERR, "start_dhcp: no IP object %s"); 1302 return (NULL); 1303 } 1304 1305 if (ncu_obj->nwamd_object_state != NWAM_STATE_OFFLINE_TO_ONLINE && 1306 ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) { 1307 nlog(LOG_INFO, "start_dhcp: IP NCU %s is in invalid state " 1308 "for DHCP command", ncu_obj->nwamd_object_name); 1309 nwamd_object_release(ncu_obj); 1310 return (NULL); 1311 } 1312 nwamd_object_release(ncu_obj); 1313 1314 switch (type) { 1315 case DHCP_INFORM: 1316 { 1317 char aobjname[IPADM_AOBJSIZ]; 1318 1319 if ((ipstatus = ipadm_get_aobjname(ipaddr, aobjname, 1320 sizeof (aobjname))) != IPADM_SUCCESS) { 1321 nlog(LOG_ERR, "start_dhcp: " 1322 "ipadm_get_aobjname failed for %s: %s", 1323 name, ipadm_status2str(ipstatus)); 1324 goto done; 1325 } 1326 ipstatus = ipadm_refresh_addr(ipadm_handle, aobjname, 1327 IPADM_OPT_ACTIVE | IPADM_OPT_INFORM); 1328 1329 break; 1330 } 1331 case DHCP_START: 1332 { 1333 ipstatus = ipadm_create_addr(ipadm_handle, ipaddr, 1334 IPADM_OPT_ACTIVE); 1335 1336 if (ipstatus == IPADM_DHCP_IPC_TIMEOUT) { 1337 /* 1338 * DHCP timed out: change state for this NCU and enqueue 1339 * event to check NCU priority-groups. Only care for 1340 * DHCP requests (not informs). 1341 */ 1342 char *object_name; 1343 1344 nlog(LOG_INFO, "start_dhcp: DHCP timed out for %s", 1345 name); 1346 1347 if (nwam_ncu_name_to_typed_name(name, 1348 NWAM_NCU_TYPE_INTERFACE, &object_name) 1349 != NWAM_SUCCESS) { 1350 nlog(LOG_ERR, "start_dhcp: " 1351 "nwam_ncu_name_to_typed_name failed " 1352 "for %s", name); 1353 goto done; 1354 } 1355 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1356 object_name, NWAM_STATE_OFFLINE_TO_ONLINE, 1357 NWAM_AUX_STATE_IF_DHCP_TIMED_OUT); 1358 nwamd_create_ncu_check_event(0); 1359 1360 free(object_name); 1361 goto done; 1362 1363 } else if (ipstatus == IPADM_DHCP_IPC_ERROR && 1364 retries++ < NWAMD_DHCP_RETRIES) { 1365 /* 1366 * Retry DHCP request as we may have been unplumbing 1367 * as part of the configuration phase. 1368 */ 1369 nlog(LOG_ERR, "start_dhcp: will retry on %s in %d sec", 1370 name, NWAMD_DHCP_RETRY_WAIT_TIME); 1371 (void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME); 1372 goto retry; 1373 } 1374 break; 1375 } 1376 default: 1377 nlog(LOG_ERR, "start_dhcp: invalid dhcp_ipc_type_t: %d", type); 1378 goto done; 1379 } 1380 1381 if (ipstatus != IPADM_SUCCESS) { 1382 nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr failed for %s: %s", 1383 (type == DHCP_START ? "create" : "refresh"), 1384 name, ipadm_status2str(ipstatus)); 1385 } 1386 1387 done: 1388 free(name); 1389 free(arg); 1390 return (NULL); 1391 } 1392 1393 static void 1394 nwamd_dhcp(const char *ifname, ipadm_addrobj_t ipaddr, dhcp_ipc_type_t cmd) 1395 { 1396 struct nwamd_dhcp_thread_arg *arg; 1397 pthread_attr_t attr; 1398 1399 nlog(LOG_DEBUG, "nwamd_dhcp: starting DHCP %s thread for %s", 1400 dhcp_ipc_type_to_string(cmd), ifname); 1401 1402 arg = malloc(sizeof (*arg)); 1403 if (arg == NULL) { 1404 nlog(LOG_ERR, "nwamd_dhcp: error allocating memory for " 1405 "dhcp request"); 1406 return; 1407 } 1408 1409 arg->name = strdup(ifname); 1410 arg->type = cmd; 1411 arg->ipaddr = ipaddr; 1412 1413 (void) pthread_attr_init(&attr); 1414 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1415 if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) { 1416 nlog(LOG_ERR, "nwamd_dhcp: cannot start dhcp thread"); 1417 free(arg->name); 1418 free(arg); 1419 (void) pthread_attr_destroy(&attr); 1420 return; 1421 } 1422 (void) pthread_attr_destroy(&attr); 1423 } 1424