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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <arpa/inet.h> 28 #include <assert.h> 29 #include <dhcpagent_ipc.h> 30 #include <dhcp_inittab.h> 31 #include <dhcp_symbol.h> 32 #include <dhcpagent_util.h> 33 #include <errno.h> 34 #include <execinfo.h> 35 #include <inetcfg.h> 36 #include <libnwam.h> 37 #include <netinet/in.h> 38 #include <stdlib.h> 39 #include <strings.h> 40 #include <sys/socket.h> 41 #include <sys/types.h> 42 #include <ucontext.h> 43 #include <unistd.h> 44 #include <libscf.h> 45 46 #include "conditions.h" 47 #include "events.h" 48 #include "ncp.h" 49 #include "ncu.h" 50 #include "objects.h" 51 #include "util.h" 52 53 /* 54 * ncu_ip.c - contains routines that are IP interface-specific for NCUs. 55 */ 56 57 #define STATELESS_RUNNING (IFF_RUNNING | IFF_UP | IFF_ADDRCONF) 58 #define DHCP_RUNNING (IFF_RUNNING | IFF_UP | IFF_DHCPRUNNING) 59 60 static void *start_dhcp_thread(void *); 61 static void nwamd_down_interface(const char *, uint_t, int); 62 static boolean_t stateless_running(const nwamd_ncu_t *); 63 64 char * 65 nwamd_sockaddr_to_str(const struct sockaddr *sockaddr, char *str, size_t len) 66 { 67 if (icfg_sockaddr_to_str(sockaddr->sa_family, sockaddr, str, len) != 68 ICFG_SUCCESS) { 69 return (NULL); 70 } else { 71 return (str); 72 } 73 } 74 75 static void 76 nwamd_log_if_address(int severity, struct nwamd_if_address *nifa) 77 { 78 char str[INET6_ADDRSTRLEN]; 79 80 nlog(severity, "%s address %s is %s", 81 nifa->address.sa_family == AF_INET ? "IPv4" : "IPv6", 82 nwamd_sockaddr_to_str(&nifa->address, str, sizeof (str)), 83 nifa->configured ? "configured" : "not configured"); 84 } 85 86 void 87 nwamd_propogate_link_up_down_to_ip(const char *linkname, boolean_t up) 88 { 89 nwamd_object_t ip_ncu = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE, 90 linkname); 91 nwamd_ncu_t *ncu; 92 93 if (ip_ncu == NULL) { 94 nlog(LOG_DEBUG, "nwamd_propogate_link_up_down_to_ip: no IP NCU " 95 "for link %s, cannot propogate %s event", linkname, 96 up ? "up" : "down"); 97 return; 98 } 99 ncu = ip_ncu->nwamd_object_data; 100 101 if (ncu->ncu_enabled) { 102 if (ip_ncu->nwamd_object_aux_state == 103 NWAM_AUX_STATE_UNINITIALIZED) { 104 nlog(LOG_DEBUG, 105 "nwamd_propogate_link_up_down_to_ip: will not " 106 "propogate link %s event as IP NCU %s is being " 107 "removed", up ? "up" : "down", linkname); 108 } else { 109 nlog(LOG_DEBUG, 110 "nwamd_propogate_link_up_down_to_ip: propogating " 111 "link %s event to interface %s", 112 up ? "up" : "down", linkname); 113 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 114 ip_ncu->nwamd_object_name, 115 up ? 116 NWAM_STATE_OFFLINE_TO_ONLINE : 117 NWAM_STATE_ONLINE_TO_OFFLINE, 118 up ? NWAM_AUX_STATE_INITIALIZED : 119 NWAM_AUX_STATE_CONDITIONS_NOT_MET); 120 } 121 } else { 122 nlog(LOG_DEBUG, 123 "nwamd_propogate_link_up_down_to_ip: not propogating " 124 "link %s event to interface %s, IP NCU is disabled", 125 up ? "up" : "down", linkname); 126 } 127 nwamd_object_release(ip_ncu); 128 } 129 130 /* 131 * Returns the value associated with the given symbol for the given 132 * interface. The interface may be NULL, in which case the primary 133 * interface is used. 134 * This function substitutes the need to call dhcpinfo(1), thus it is 135 * very similar to the implementation of dhcpinfo(1). 136 * When multiple values need to be returned (e.g., nameservers), they 137 * are separated by a space ' '. 138 */ 139 char * 140 nwamd_get_dhcpinfo_data(const char *sym_name, char *ifname) 141 { 142 dhcp_symbol_t *entry; 143 dhcp_optnum_t optnum; 144 dhcp_ipc_request_t *request; 145 dhcp_ipc_reply_t *reply; 146 DHCP_OPT *opt; 147 size_t opt_len; 148 char *value; /* return value */ 149 int err; 150 char errmsg[LINE_MAX]; 151 152 /* if interface is not given, change it to empty string */ 153 if (ifname == NULL) 154 ifname = ""; 155 156 /* find code and category in dhcp_inittab(4) */ 157 entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD | 158 ITAB_CAT_VENDOR | ITAB_CAT_FIELD, ITAB_CONS_INFO, sym_name); 159 160 if (entry == NULL) { 161 (void) snprintf(errmsg, LINE_MAX, "unknown identifier: %s", 162 sym_name); 163 goto fail; 164 } 165 166 /* allocate request */ 167 optnum.code = entry->ds_code; 168 optnum.category = entry->ds_category; 169 optnum.size = entry->ds_max * inittab_type_to_size(entry); 170 request = dhcp_ipc_alloc_request(DHCP_GET_TAG, ifname, &optnum, 171 sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM); 172 if (request == NULL) { 173 (void) snprintf(errmsg, LINE_MAX, "failed dhcp alloc request"); 174 goto fail; 175 } 176 177 /* make the request */ 178 err = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT); 179 if (err != 0 || reply->return_code != 0) { 180 (void) snprintf(errmsg, LINE_MAX, "%s", 181 dhcp_ipc_strerror(err == 0 ? reply->return_code : err)); 182 } 183 184 /* get data from the reply */ 185 opt = dhcp_ipc_get_data(reply, &opt_len, NULL); 186 if (opt_len == 0) { 187 (void) snprintf(errmsg, LINE_MAX, "invalid data"); 188 goto fail; 189 } 190 191 /* check protocol error */ 192 if (opt_len < 2 || (opt_len -2 != opt->len)) { 193 (void) snprintf(errmsg, LINE_MAX, "data length mismatch"); 194 goto fail; 195 } 196 opt_len -= 2; 197 198 /* decode the data into ascii */ 199 value = inittab_decode(entry, opt->value, opt_len, B_TRUE); 200 if (value == NULL) { 201 (void) snprintf(errmsg, LINE_MAX, "cannot decode reply"); 202 goto fail; 203 } 204 205 free(request); 206 free(reply); 207 return (value); 208 209 fail: 210 nlog(LOG_DEBUG, "get_dhcpinfo_data() failed: %s", errmsg); 211 free(request); 212 free(reply); 213 return (NULL); 214 } 215 216 void 217 nwamd_dhcp_release(const char *ifname) 218 { 219 dhcp_ipc_reply_t *reply = NULL; 220 dhcp_ipc_request_t *request; 221 int rc; 222 223 /* Now allocate and send the request */ 224 request = dhcp_ipc_alloc_request(DHCP_RELEASE, ifname, NULL, 0, 225 DHCP_TYPE_NONE); 226 if (request == NULL) { 227 nlog(LOG_DEBUG, "nwamd_dhcp_release: dhcp_ipc_alloc_request : " 228 "%s", strerror(errno)); 229 return; 230 } 231 rc = dhcp_ipc_make_request(request, &reply, 1); 232 free(request); 233 free(reply); 234 reply = NULL; 235 if (rc != 0) { 236 /* Fall back to drop request */ 237 request = dhcp_ipc_alloc_request(DHCP_DROP, ifname, NULL, 0, 238 DHCP_TYPE_NONE); 239 if (request == NULL) { 240 nlog(LOG_DEBUG, "nwamd_dhcp_release: " 241 "dhcp_ipc_alloc_request : %s", strerror(errno)); 242 return; 243 } 244 (void) dhcp_ipc_make_request(request, &reply, 1); 245 free(request); 246 free(reply); 247 } 248 } 249 250 static boolean_t 251 add_ip_address(const char *ifname, struct nwamd_if_address *nifa, 252 boolean_t logical_if) 253 { 254 icfg_handle_t h, newh; 255 icfg_if_t intf; 256 uint64_t flags; 257 int rc; 258 struct sockaddr_in bcastaddr; 259 char str[INET6_ADDRSTRLEN]; 260 261 (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name)); 262 intf.if_protocol = nifa->address.sa_family; 263 264 nlog(LOG_DEBUG, "add_ip_address: %s address %s for link %s", 265 logical_if ? "adding" : "setting", 266 nwamd_sockaddr_to_str(&nifa->address, str, sizeof (str)), 267 intf.if_name); 268 269 if (icfg_open(&h, &intf) != ICFG_SUCCESS) { 270 nlog(LOG_ERR, "add_ip_address: icfg_open failed on %s", ifname); 271 return (B_FALSE); 272 } 273 /* 274 * When working with the physical interface, we need to be careful 275 * to set the prefixlen and broadcast addresses before setting the 276 * IP address, otherwise RTM_DELADDRs for the old broadcast/netmask 277 * will confuse us into thinking we've lost the address we've just 278 * assigned. 279 */ 280 if (logical_if) { 281 rc = icfg_add_addr(h, &newh, 282 (const struct sockaddr *)&nifa->address, 283 intf.if_protocol == AF_INET ? 284 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)); 285 } else { 286 newh = h; 287 288 /* Make sure DHCP is no longer running */ 289 if (icfg_get_flags(newh, &flags) == ICFG_SUCCESS) { 290 if (flags & IFF_DHCPRUNNING) { 291 nlog(LOG_DEBUG, "add_ip_address: " 292 "turning off DHCP for %s", ifname); 293 nwamd_dhcp_release(ifname); 294 } 295 } 296 /* 297 * Set interface IFF_UP if not already. Do this and 298 * setting of prefixlen/broadcast addresses as otherwise 299 * these can trigger an RTM_DELADDR that makes it appear 300 * that the address has gone away. 301 */ 302 rc = icfg_set_addr(newh, 303 (const struct sockaddr *)&nifa->address, 304 intf.if_protocol == AF_INET ? 305 sizeof (struct sockaddr_in) : 306 sizeof (struct sockaddr_in6)); 307 } 308 if (rc != ICFG_SUCCESS) { 309 nlog(LOG_DEBUG, "add_ip_address: add of ipaddr failed " 310 "for %s: %d", ifname, rc); 311 goto out; 312 } 313 314 if (nifa->prefix != 0) { 315 if ((rc = icfg_set_prefixlen(newh, nifa->prefix)) 316 != ICFG_SUCCESS) { 317 nlog(LOG_ERR, "add_ip_address: icfg_set_prefix %d " 318 "failed on %s: %s", nifa->prefix, ifname, 319 icfg_errmsg(rc)); 320 } else if (intf.if_protocol == AF_INET) { 321 /* Set broadcast address based on address, prefixlen */ 322 bcastaddr.sin_addr.s_addr = 323 /*LINTED*/ 324 ((struct sockaddr_in *)&nifa->address) 325 ->sin_addr.s_addr | 326 htonl(0xffffffff >> nifa->prefix); 327 328 if ((rc = icfg_set_broadcast(newh, &bcastaddr)) 329 != ICFG_SUCCESS) { 330 nlog(LOG_ERR, "add_ip_address: " 331 "icfg_set_broadcast(%s) failed on %s: %s", 332 inet_ntoa(bcastaddr.sin_addr), ifname, 333 icfg_errmsg(rc)); 334 } 335 } 336 } 337 if (rc == ICFG_SUCCESS) { 338 if (icfg_get_flags(newh, &flags) == ICFG_SUCCESS) { 339 if ((flags & IFF_UP) == 0) 340 rc = icfg_set_flags(newh, flags | IFF_UP); 341 } else { 342 nlog(LOG_DEBUG, "add_ip_address: couldn't bring up %s", 343 ifname); 344 } 345 } 346 347 out: 348 /* Check if address was a duplicate */ 349 if (rc == ICFG_DAD_FOUND || (flags & IFF_DUPLICATE) != 0) { 350 char *object_name; 351 nwam_error_t err; 352 353 nlog(LOG_INFO, "add_ip_address: " 354 "duplicate address detected on %s", ifname); 355 if ((err = nwam_ncu_name_to_typed_name(ifname, 356 NWAM_NCU_TYPE_INTERFACE, &object_name)) == NWAM_SUCCESS) { 357 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 358 object_name, NWAM_STATE_MAINTENANCE, 359 NWAM_AUX_STATE_IF_DUPLICATE_ADDR); 360 free(object_name); 361 } else { 362 nlog(LOG_ERR, "add_ip_address: could not " 363 "create state event for %s: %s", ifname, 364 nwam_strerror(err)); 365 } 366 rc = ICFG_DAD_FOUND; 367 } 368 369 if (h != newh) 370 icfg_close(newh); 371 icfg_close(h); 372 373 return (rc == ICFG_SUCCESS); 374 } 375 376 void 377 nwamd_add_default_routes(nwamd_ncu_t *ncu) 378 { 379 nwamd_if_t *nif = &ncu->ncu_node.u_if; 380 char str[INET6_ADDRSTRLEN]; 381 382 if (nif->nwamd_if_ipv4 && nif->nwamd_if_ipv4_default_route_set) { 383 struct sockaddr_in v4dest, v4mask; 384 385 v4dest.sin_addr.s_addr = htonl(INADDR_ANY); 386 v4dest.sin_family = AF_INET; 387 388 v4mask.sin_addr.s_addr = 0; 389 v4mask.sin_family = AF_INET; 390 391 nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default " 392 "route %s", nwamd_sockaddr_to_str 393 ((struct sockaddr *)&nif->nwamd_if_ipv4_default_route, str, 394 sizeof (str))); 395 nwamd_add_route((struct sockaddr *)&v4dest, 396 (struct sockaddr *)&v4mask, 397 (struct sockaddr *)&nif->nwamd_if_ipv4_default_route, 398 ncu->ncu_name); 399 } 400 401 if (nif->nwamd_if_ipv6 && nif->nwamd_if_ipv6_default_route_set) { 402 struct sockaddr_in6 v6dest, v6mask; 403 404 (void) bzero(&v6dest, sizeof (struct sockaddr_in6)); 405 v6dest.sin6_family = AF_INET6; 406 407 (void) bzero(&v6mask, sizeof (struct sockaddr_in6)); 408 v6mask.sin6_family = AF_INET6; 409 410 nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default " 411 "route %s", nwamd_sockaddr_to_str 412 ((struct sockaddr *)&nif->nwamd_if_ipv6_default_route, str, 413 sizeof (str))); 414 nwamd_add_route((struct sockaddr *)&v6dest, 415 (struct sockaddr *)&v6mask, 416 (struct sockaddr *)&nif->nwamd_if_ipv6_default_route, 417 ncu->ncu_name); 418 } 419 } 420 421 void 422 nwamd_dhcp_inform(nwamd_ncu_t *ncu) 423 { 424 struct nwamd_dhcp_thread_arg *arg; 425 char *name = NULL; 426 pthread_attr_t attr; 427 428 arg = malloc(sizeof (*arg)); 429 if (arg == NULL) { 430 nlog(LOG_ERR, "nwamd_dhcp_inform: error allocating memory " 431 "for dhcp request"); 432 free(name); 433 return; 434 } 435 436 arg->name = strdup(ncu->ncu_name); 437 arg->type = DHCP_INFORM; 438 arg->timeout = DHCP_IPC_WAIT_DEFAULT; 439 440 (void) pthread_attr_init(&attr); 441 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 442 if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) { 443 nlog(LOG_ERR, "Cannot start dhcp thread"); 444 free(name); 445 free(arg); 446 (void) pthread_attr_destroy(&attr); 447 return; 448 } 449 (void) pthread_attr_destroy(&attr); 450 } 451 452 static boolean_t 453 addresses_match(const struct sockaddr *addr1, const struct sockaddr *addr2) 454 { 455 if (addr1->sa_family != addr2->sa_family) 456 return (B_FALSE); 457 458 switch (addr1->sa_family) { 459 case AF_INET: 460 /*LINTED*/ 461 return (memcmp(&((struct sockaddr_in *)addr1)->sin_addr, 462 /*LINTED*/ 463 &((struct sockaddr_in *)addr2)->sin_addr, 464 sizeof (struct in_addr)) == 0); 465 case AF_INET6: 466 /*LINTED*/ 467 return (memcmp(&((struct sockaddr_in6 *)addr1)->sin6_addr, 468 /*LINTED*/ 469 &((struct sockaddr_in6 *)addr2)->sin6_addr, 470 sizeof (struct in6_addr)) == 0); 471 default: 472 return (B_FALSE); 473 } 474 } 475 476 /* 477 * Returns the nwamd_if_address structure for the given static address, 478 * NULL if not found. 479 */ 480 static struct nwamd_if_address * 481 find_static_address(const struct sockaddr *addr, const nwamd_ncu_t *ncu) 482 { 483 struct nwamd_if_address *n, *nifa = ncu->ncu_node.u_if.nwamd_if_list; 484 char str[INET6_ADDRSTRLEN]; 485 486 nlog(LOG_DEBUG, "find_static_address %s", 487 nwamd_sockaddr_to_str(addr, str, sizeof (str))); 488 for (n = nifa; n != NULL; n = n->next) { 489 if (addresses_match(addr, &n->address)) 490 return (n); 491 } 492 return (NULL); 493 } 494 495 /* 496 * Returns the nwamd_if_address structure representing the non-static address 497 * in the NCU. dhcp is used to detemrine if the DHCP (stateful for v6) 498 * structure is needed or the stateless/autoconf structure for the given 499 * family. dhcp should be B_TRUE if looking for v4. Will only return the 500 * nwamd_if_address if the relevant address is configured (v4 DHCP, v6 501 * stateless/stateful) for the NCU. 502 * 503 * Returns NULL if structure is not found. 504 */ 505 static struct nwamd_if_address * 506 find_nonstatic_address(const nwamd_ncu_t *ncu, ushort_t family, boolean_t dhcp) 507 { 508 struct nwamd_if_address *n, *nifa = ncu->ncu_node.u_if.nwamd_if_list; 509 const nwamd_if_t *u_if = &ncu->ncu_node.u_if; 510 511 nlog(LOG_DEBUG, "find_nonstatic_address: %s", 512 dhcp ? "dhcp" : "stateless"); 513 for (n = nifa; n != NULL; n = n->next) { 514 if (family == AF_INET) { 515 if (!dhcp) 516 return (NULL); 517 if (n->address.sa_family == family && n->dhcp_if && 518 u_if->nwamd_if_dhcp_configured) 519 return (n); 520 } else if (family == AF_INET6) { 521 if (n->address.sa_family == family) { 522 if (dhcp && n->dhcp_if && 523 u_if->nwamd_if_stateful_configured) 524 return (n); 525 else if (!dhcp && n->stateless_if && 526 u_if->nwamd_if_stateless_configured) 527 return (n); 528 } 529 } 530 } 531 return (NULL); 532 } 533 534 /* 535 * Sets "configured" nwam_if_address value for corresponding address. 536 * Used when we process IF_STATE events to handle RTM_NEWADDR/DELADDRs. 537 */ 538 static boolean_t 539 update_address_configured_value(const struct sockaddr *configured_addr, 540 nwamd_ncu_t *ncu, boolean_t configured) 541 { 542 struct nwamd_if_address *n; 543 char str[INET6_ADDRSTRLEN]; 544 545 nlog(LOG_DEBUG, "update_address_configured_value(%s, %s, %s)", 546 nwamd_sockaddr_to_str(configured_addr, str, sizeof (str)), 547 ncu->ncu_name, configured ? "configure" : "unconfigure"); 548 n = find_static_address(configured_addr, ncu); 549 if (n) { 550 n->configured = configured; 551 nlog(LOG_DEBUG, "update_address_configured_value: marking " 552 "address %s", 553 nwamd_sockaddr_to_str(&n->address, str, sizeof (str))); 554 return (B_TRUE); 555 } 556 return (B_FALSE); 557 } 558 559 void 560 nwamd_update_addresses_unconfigured(nwamd_ncu_t *ncu, sa_family_t af) 561 { 562 struct nwamd_if_address *n, *nifa = ncu->ncu_node.u_if.nwamd_if_list; 563 564 for (n = nifa; n != NULL; n = n->next) 565 if (af == AF_UNSPEC || n->address.sa_family == af) { 566 n->configured = B_FALSE; 567 nwamd_log_if_address(LOG_DEBUG, n); 568 } 569 } 570 571 /* 572 * Are one or more static addresses configured? 573 */ 574 boolean_t 575 nwamd_static_addresses_configured(nwamd_ncu_t *ncu, sa_family_t family) 576 { 577 struct nwamd_if_address *n; 578 579 for (n = ncu->ncu_node.u_if.nwamd_if_list; n != NULL; n = n->next) { 580 if ((family == AF_UNSPEC || family == n->address.sa_family) && 581 n->configured && !n->dhcp_if && !n->stateless_if) 582 return (B_TRUE); 583 } 584 nlog(LOG_DEBUG, "no static addresses configured for %s", ncu->ncu_name); 585 return (B_FALSE); 586 } 587 588 /* 589 * Is DHCP probably managing an address on this index. We decide that it is 590 * probably managing an address if there is an interface with IFF_DHCP set 591 * that isn't in our set of static addresses. Note that IFF_DHCP gets set 592 * on static addresses when we do a dhcp inform and if that list has changed 593 * recently then the result of this function could be erronous. 594 */ 595 boolean_t 596 nwamd_dhcp_managing(int protocol, nwamd_ncu_t *ncu) 597 { 598 icfg_if_t *iflist; 599 icfg_handle_t ifh; 600 int numif, i; 601 struct sockaddr_storage addr; 602 socklen_t len; 603 int prefixlen; 604 uint64_t flags; 605 boolean_t rv = B_FALSE; 606 607 if (icfg_get_if_list(&iflist, &numif, protocol, ICFG_PLUMBED) != 608 ICFG_SUCCESS) { 609 return (B_TRUE); 610 } 611 for (i = 0; i < numif; i++) { 612 if (strncmp(iflist[i].if_name, ncu->ncu_name, 613 strlen(ncu->ncu_name)) != 0) 614 continue; 615 616 if (icfg_open(&ifh, &iflist[i]) != ICFG_SUCCESS) 617 continue; 618 619 /* is this address an expected static one? */ 620 len = sizeof (addr); 621 if (icfg_get_addr(ifh, (struct sockaddr *)&addr, &len, 622 &prefixlen, B_FALSE) != ICFG_SUCCESS || 623 find_static_address((struct sockaddr *)&addr, ncu) 624 != NULL) { 625 icfg_close(ifh); 626 continue; 627 } 628 629 /* 630 * For IPv4, DHCPRUNNING flag is set when dhcpagent is in 631 * the process of getting an address, but doesn't have one 632 * yet (interface has 0.0.0.0). For IPv6, DHCPRUNNING flag 633 * is set on the link-local address if trying to get a 634 * stateful address. In both cases, consider the interface 635 * as not being managed by DHCP and skip checking of flags. 636 */ 637 if ((protocol == AF_INET && 638 ((struct sockaddr_in *)&addr)->sin_addr.s_addr == 639 INADDR_ANY) || 640 (protocol == AF_INET6 && 641 IN6_IS_ADDR_LINKLOCAL( 642 &((struct sockaddr_in6 *)&addr)->sin6_addr))) { 643 icfg_close(ifh); 644 continue; 645 } 646 647 if (icfg_get_flags(ifh, &flags) == ICFG_SUCCESS && 648 (flags & IFF_DHCPRUNNING)) { 649 /* 650 * If we get here we have an address that has the 651 * DHCP flag set and isn't an expected static address. 652 */ 653 icfg_close(ifh); 654 rv = B_TRUE; 655 break; 656 } 657 } 658 659 icfg_free_if_list(iflist); 660 return (rv); 661 } 662 663 static boolean_t 664 nwamd_v4_requested(nwamd_ncu_t *ncu) 665 { 666 boolean_t anyv4_requested; 667 nwamd_if_t *u_if; 668 669 anyv4_requested = B_FALSE; 670 u_if = &ncu->ncu_node.u_if; 671 if (u_if->nwamd_if_dhcp_requested) { 672 anyv4_requested = B_TRUE; 673 } else { 674 struct nwamd_if_address *a; 675 for (a = u_if->nwamd_if_list; 676 a != NULL && a->address.sa_family != AF_INET; 677 a = a->next) 678 /* Empty loop body */; 679 if (a != NULL) 680 anyv4_requested = B_TRUE; 681 } 682 683 return (anyv4_requested); 684 } 685 686 static boolean_t 687 nwamd_v6_requested(nwamd_ncu_t *ncu) 688 { 689 boolean_t anyv6_requested; 690 nwamd_if_t *u_if; 691 692 anyv6_requested = B_FALSE; 693 u_if = &ncu->ncu_node.u_if; 694 if (u_if->nwamd_if_stateful_requested || 695 u_if->nwamd_if_stateless_requested) { 696 anyv6_requested = B_TRUE; 697 } else { 698 struct nwamd_if_address *a; 699 for (a = u_if->nwamd_if_list; 700 a != NULL && a->address.sa_family != AF_INET6; 701 a = a->next) 702 /* Empty loop body */; 703 if (a != NULL) 704 anyv6_requested = B_TRUE; 705 } 706 707 return (anyv6_requested); 708 } 709 710 /* 711 * Bring up the ncu if we have the right combination of requested configuration 712 * and actual configuration and up is true, or bring down the ncu if no 713 * addresses are configured, and up is false. 714 */ 715 static void 716 interface_ncu_up_down(nwamd_ncu_t *ncu, boolean_t up) 717 { 718 boolean_t ncu_online; 719 char *name; 720 721 assert(ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE); 722 723 /* 724 * If V4 with or without V6 is configured then one of its interfaces 725 * needs to be up for the ncu to come online. If only V6 is requested 726 * then one of its interfaces needs to be up for the ncu to come online. 727 */ 728 ncu_online = B_FALSE; 729 if (nwamd_v4_requested(ncu)) { 730 if (nwamd_dhcp_managing(AF_INET, ncu) || 731 nwamd_static_addresses_configured(ncu, AF_INET)) 732 ncu_online = B_TRUE; 733 } else if (nwamd_v6_requested(ncu)) { 734 if ((nwamd_dhcp_managing(AF_INET6, ncu) || 735 stateless_running(ncu) || 736 nwamd_static_addresses_configured(ncu, AF_INET6))) 737 ncu_online = B_TRUE; 738 } 739 740 if (nwam_ncu_name_to_typed_name(ncu->ncu_name, ncu->ncu_type, &name) != 741 NWAM_SUCCESS) { 742 nlog(LOG_DEBUG, "interface_ncu_up_down: " 743 "nwam_ncu_name_to_typed_name failed"); 744 return; 745 } 746 if (ncu_online && up) { 747 nlog(LOG_DEBUG, "interface_ncu_up_down: " 748 "bringing %s up", name); 749 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name, 750 NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_UP); 751 } else if (!ncu_online && !up) { 752 nlog(LOG_DEBUG, "interface_ncu_up_down: " 753 "bringing %s down", name); 754 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name, 755 NWAM_STATE_ONLINE_TO_OFFLINE, 756 NWAM_AUX_STATE_DOWN); 757 } 758 759 free(name); 760 } 761 762 static void 763 interface_ncu_up(nwamd_ncu_t *ncu) 764 { 765 interface_ncu_up_down(ncu, B_TRUE); 766 } 767 768 static void 769 interface_ncu_down(nwamd_ncu_t *ncu) 770 { 771 interface_ncu_up_down(ncu, B_FALSE); 772 } 773 774 /* Callback to find if DHCP is running on the interface index */ 775 static int 776 flags_set_for_ifindex_cb(icfg_if_t *intf, void *arg, uint64_t flags_wanted) 777 { 778 int *indexp = arg; 779 icfg_handle_t h; 780 int index; 781 uint64_t flags = 0; 782 783 if (icfg_open(&h, intf) != ICFG_SUCCESS) { 784 nlog(LOG_ERR, "flags_set_for_ifindex_cb: icfg_open failed"); 785 return (0); 786 } 787 if (icfg_get_index(h, &index) != ICFG_SUCCESS) { 788 nlog(LOG_ERR, 789 "flags_set_for_ifindex_cb: icfg_get_index failed"); 790 icfg_close(h); 791 return (0); 792 } 793 if (index != *indexp) { 794 icfg_close(h); 795 return (0); 796 } 797 798 if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) { 799 nlog(LOG_ERR, 800 "flags_set_for_ifindex_cb: icfg_get_flags failed"); 801 } 802 icfg_close(h); 803 return ((flags & flags_wanted) == flags_wanted); 804 } 805 806 static int 807 stateless_running_for_ifindex_cb(icfg_if_t *intf, void *arg) 808 { 809 return (flags_set_for_ifindex_cb(intf, arg, 810 IFF_RUNNING | IFF_ADDRCONF | IFF_UP)); 811 } 812 813 /* 814 * Is autoconf running on the interface with specified ifindex? 815 */ 816 static boolean_t 817 stateless_running_for_ifindex(int ifindex) 818 { 819 return (icfg_iterate_if(AF_INET6, ICFG_PLUMBED, &ifindex, 820 stateless_running_for_ifindex_cb) != 0); 821 } 822 823 static boolean_t 824 stateless_running(const nwamd_ncu_t *ncu) 825 { 826 int index; 827 icfg_if_t intf; 828 icfg_handle_t ifh; 829 830 intf.if_protocol = AF_INET6; 831 (void) strlcpy(intf.if_name, ncu->ncu_name, sizeof (intf.if_name)); 832 if (icfg_open(&ifh, &intf) != ICFG_SUCCESS) { 833 nlog(LOG_ERR, "stateless_running: icfg_open(%s) failed", 834 ncu->ncu_name); 835 return (B_FALSE); 836 } 837 838 if (icfg_get_index(ifh, &index) != ICFG_SUCCESS) { 839 nlog(LOG_ERR, "stateless_running: icfg_get_index(%s) failed", 840 ncu->ncu_name); 841 return (B_FALSE); 842 } 843 844 icfg_close(ifh); 845 846 return (stateless_running_for_ifindex(index)); 847 } 848 849 void 850 nwamd_configure_interface_addresses(nwamd_ncu_t *ncu) 851 { 852 struct nwamd_if_address *nifa = ncu->ncu_node.u_if.nwamd_if_list; 853 struct nwamd_if_address *n; 854 int num_configured_v4 = 0; 855 boolean_t add_logical_if; 856 857 nlog(LOG_DEBUG, "nwamd_configure_interface_addresses(%s)", 858 ncu->ncu_name); 859 860 /* 861 * Add static addresses. For IPv4, we only use the physical interface 862 * (i.e. not a logical interface) if DHCP has not been requested and 863 * this is the first address to be configured. 864 */ 865 for (n = nifa; n != NULL; n = n->next) { 866 if (n->configured || n->dhcp_if || n->stateless_if) 867 continue; 868 switch (n->address.sa_family) { 869 case AF_INET: 870 add_logical_if = (num_configured_v4 > 0 || 871 ncu->ncu_node.u_if.nwamd_if_dhcp_requested); 872 num_configured_v4++; 873 break; 874 case AF_INET6: 875 add_logical_if = B_TRUE; 876 break; 877 } 878 n->configured = add_ip_address(ncu->ncu_name, n, 879 add_logical_if); 880 } 881 } 882 883 static int 884 lifnum_from_ifname(const char *ifname) 885 { 886 char *lifstr = strchr(ifname, ':'); 887 888 if (lifstr != NULL) { 889 lifstr++; 890 return (atoi(lifstr)); 891 } 892 return (0); 893 } 894 895 /* 896 * Copies the ifname (with lifnum) associated with the given address. 897 * Returns B_TRUE if a match is found, B_FASLE otherwise. 898 */ 899 static boolean_t 900 ifname_for_addr(const struct sockaddr *caddr, char *ifname, int len) 901 { 902 struct sockaddr_in6 addr; 903 int numif, i, prefixlen; 904 icfg_if_t *iflist; 905 icfg_handle_t ifh; 906 socklen_t slen; 907 908 if (icfg_get_if_list(&iflist, &numif, caddr->sa_family, ICFG_PLUMBED) 909 != ICFG_SUCCESS) { 910 nlog(LOG_DEBUG, "ifname_for_addr: icfg_get_if_list failed"); 911 return (B_FALSE); 912 } 913 914 for (i = 0; i < numif; i++) { 915 if (icfg_open(&ifh, &iflist[i]) != ICFG_SUCCESS) { 916 nlog(LOG_ERR, "ifname_for_addr: icfg_open %s failed", 917 iflist[i].if_name); 918 continue; 919 } 920 921 slen = sizeof (addr); 922 if (icfg_get_addr(ifh, (struct sockaddr *)&addr, 923 &slen, &prefixlen, B_FALSE) != ICFG_SUCCESS) { 924 nlog(LOG_ERR, "ifname_for_addr: " 925 "icfg_get_addr %s failed", iflist[i].if_name); 926 } else { 927 /* Compare addresses */ 928 if (addresses_match((struct sockaddr *)&addr, caddr)) { 929 (void) strlcpy(ifname, iflist[i].if_name, len); 930 icfg_close(ifh); 931 icfg_free_if_list(iflist); 932 return (B_TRUE); 933 } 934 } 935 icfg_close(ifh); 936 } 937 icfg_free_if_list(iflist); 938 return (B_FALSE); 939 } 940 941 /* 942 * This event tells us that an interface address has appeared or disappeared, 943 * or that the interface flags on an interface have changed. 944 */ 945 void 946 nwamd_ncu_handle_if_state_event(nwamd_event_t event) 947 { 948 nwam_event_t evm; 949 nwamd_object_t ncu_obj; 950 nwamd_ncu_t *ncu; 951 nwam_state_t state; 952 nwam_aux_state_t aux_state; 953 954 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, 955 event->event_object); 956 if (ncu_obj == NULL) { 957 nlog(LOG_ERR, "nwamd_ncu_handle_if_state_event: no object %s", 958 event->event_object); 959 nwamd_event_do_not_send(event); 960 return; 961 } 962 ncu = ncu_obj->nwamd_object_data; 963 evm = event->event_msg; 964 state = ncu_obj->nwamd_object_state; 965 aux_state = ncu_obj->nwamd_object_aux_state; 966 967 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 968 "if %s, state (%s, %s)", event->event_object, 969 nwam_state_to_string(state), nwam_aux_state_to_string(aux_state)); 970 971 /* Ensure object is in correct state to handle IF state events */ 972 switch (state) { 973 case NWAM_STATE_OFFLINE_TO_ONLINE: 974 if (aux_state != NWAM_AUX_STATE_IF_WAITING_FOR_ADDR && 975 aux_state != NWAM_AUX_STATE_IF_DHCP_TIMED_OUT) { 976 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 977 "if %s is in invalid aux state %s for IF_STATE " 978 "events", event->event_object, 979 nwam_aux_state_to_string(aux_state)); 980 nwamd_event_do_not_send(event); 981 nwamd_object_release(ncu_obj); 982 return; 983 } 984 break; 985 case NWAM_STATE_ONLINE: 986 /* 987 * We can get addresses from DHCP after we've taken the interface down. 988 * We deal with those below. 989 */ 990 case NWAM_STATE_ONLINE_TO_OFFLINE: 991 case NWAM_STATE_OFFLINE: 992 break; 993 default: 994 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 995 "if %s is in invalid state %s for IF_STATE events", 996 event->event_object, nwam_state_to_string(state)); 997 nwamd_event_do_not_send(event); 998 nwamd_object_release(ncu_obj); 999 return; 1000 } 1001 1002 if (evm->nwe_data.nwe_if_state.nwe_addr_valid) { 1003 struct nwam_event_if_state *if_state; 1004 boolean_t stateless_running; 1005 char addrstr[INET6_ADDRSTRLEN], ifname[LIFNAMSIZ]; 1006 boolean_t v4dhcp_running; 1007 boolean_t v6dhcp_running; 1008 struct nwamd_if_address *nifa; 1009 struct sockaddr *addr; 1010 boolean_t static_addr; 1011 icfg_if_t intf; 1012 icfg_handle_t ifh; 1013 nwamd_if_t *u_if; 1014 ushort_t family; 1015 uint64_t flags = 0; 1016 int lifnum; 1017 1018 if_state = &evm->nwe_data.nwe_if_state; 1019 u_if = &ncu->ncu_node.u_if; 1020 family = if_state->nwe_addr.ss_family; 1021 addr = (struct sockaddr *)&if_state->nwe_addr; 1022 1023 nlog(LOG_DEBUG, 1024 "nwamd_ncu_handle_if_state_event: addr %s %s", 1025 nwamd_sockaddr_to_str(addr, addrstr, sizeof (addrstr)), 1026 evm->nwe_data.nwe_if_state.nwe_addr_added ? 1027 "added" : "removed"); 1028 1029 /* determine the interface name with lifnum */ 1030 if (if_state->nwe_addr_added) { 1031 /* figure out the ifname for the address */ 1032 if (!ifname_for_addr(addr, ifname, sizeof (ifname))) { 1033 nlog(LOG_ERR, 1034 "nwamd_ncu_handle_if_state_event:" 1035 "could not find ifname for %s", addrstr); 1036 nwamd_event_do_not_send(event); 1037 goto exit; 1038 } 1039 } else { 1040 /* 1041 * Figure out the ifname that had the address that was 1042 * removed. The address is already gone from the 1043 * interface, so cannot walk the interface list. 1044 */ 1045 struct nwamd_if_address *n; 1046 1047 if ((n = find_static_address(addr, ncu)) == NULL && 1048 (n = find_nonstatic_address(ncu, family, B_TRUE)) 1049 == NULL && 1050 (n = find_nonstatic_address(ncu, family, B_FALSE)) 1051 == NULL) { 1052 nlog(LOG_ERR, 1053 "nwamd_ncu_handle_if_state_event: " 1054 "could not find nwamd_if_address for %s", 1055 addrstr); 1056 nwamd_event_do_not_send(event); 1057 goto exit; 1058 } 1059 (void) strlcpy(ifname, n->ifname, sizeof (ifname)); 1060 } 1061 1062 nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: " 1063 "ifname for %s is %s", addrstr, ifname); 1064 1065 /* 1066 * Get interface flags using nwe_ifname as it is logical 1067 * interface name. 1068 */ 1069 intf.if_protocol = family; 1070 (void) strlcpy(intf.if_name, ifname, sizeof (intf.if_name)); 1071 lifnum = lifnum_from_ifname(intf.if_name); 1072 1073 if (icfg_open(&ifh, &intf) != ICFG_SUCCESS) { 1074 nlog(LOG_ERR, "nwamd_ncu_handle_if_state_event: can't " 1075 "find if %s", intf.if_name); 1076 nwamd_event_do_not_send(event); 1077 goto exit; 1078 } 1079 if (icfg_get_flags(ifh, &flags) != ICFG_SUCCESS) { 1080 nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: can't " 1081 "get flags for %s", icfg_if_name(ifh)); 1082 /* 1083 * If the interface is unplumbed, icfg_get_flags() 1084 * will fail. Don't exit, continue with empty flags. 1085 */ 1086 if (if_state->nwe_addr_added) { 1087 icfg_close(ifh); 1088 goto exit; 1089 } 1090 } 1091 1092 if (family == AF_INET && !if_state->nwe_addr_added) { 1093 /* 1094 * Check for failure due to CR 6745448: if we get a 1095 * report that an address has been deleted, then check 1096 * for interface up, datalink down, and actual address 1097 * non-zero. If that combination is seen, then this is 1098 * a DHCP cached lease, and we need to remove it from 1099 * the system, or it'll louse up the kernel routes 1100 * (which aren't smart enough to avoid dead 1101 * interfaces). 1102 */ 1103 /*LINTED*/ 1104 if (((struct sockaddr_in *)addr)->sin_addr.s_addr 1105 == INADDR_ANY) { 1106 socklen_t slen; 1107 struct sockaddr_in s; 1108 int pfxlen; 1109 1110 if ((flags & IFF_UP) && 1111 !(flags & IFF_RUNNING) && 1112 icfg_get_addr(ifh, (struct sockaddr *)&s, 1113 &slen, &pfxlen, B_FALSE) == ICFG_SUCCESS && 1114 s.sin_addr.s_addr != INADDR_ANY) { 1115 nlog(LOG_DEBUG, "bug workaround: " 1116 "clear out addr %s on %s", 1117 inet_ntoa(s.sin_addr), ifname); 1118 s.sin_addr.s_addr = INADDR_ANY; 1119 (void) icfg_set_addr(ifh, 1120 (const struct sockaddr *)&s, slen); 1121 } 1122 icfg_close(ifh); 1123 goto exit; 1124 } 1125 } 1126 1127 /* 1128 * Has address really been removed? Sometimes spurious 1129 * RTM_DELADDRs are generated, so we need to ensure that 1130 * the address is really gone. If IFF_DUPLICATE is set, 1131 * we're getting the RTM_DELADDR due to DAD, so don't test 1132 * in that case. 1133 */ 1134 if (!if_state->nwe_addr_added && !(flags & IFF_DUPLICATE)) { 1135 struct sockaddr_storage ifaddr; 1136 socklen_t len; 1137 int plen; 1138 1139 len = family == AF_INET ? sizeof (struct sockaddr_in) : 1140 sizeof (struct sockaddr_in6); 1141 if (icfg_get_addr(ifh, (struct sockaddr *)&ifaddr, &len, 1142 &plen, B_FALSE) == ICFG_SUCCESS && 1143 addresses_match(addr, (struct sockaddr *)&ifaddr)) { 1144 nlog(LOG_DEBUG, 1145 "nwamd_ncu_handle_if_state_event: " 1146 "address %s is not really gone from %s, " 1147 "ignoring IF_STATE event", 1148 addrstr, intf.if_name); 1149 icfg_close(ifh); 1150 nwamd_event_do_not_send(event); 1151 goto exit; 1152 } 1153 } 1154 icfg_close(ifh); 1155 1156 stateless_running = (family == AF_INET6) && 1157 ((flags & STATELESS_RUNNING) == STATELESS_RUNNING); 1158 v4dhcp_running = (family == AF_INET) && 1159 ((flags & DHCP_RUNNING) == DHCP_RUNNING); 1160 v6dhcp_running = (family == AF_INET6) && 1161 ((flags & DHCP_RUNNING) == DHCP_RUNNING); 1162 static_addr = (find_static_address(addr, ncu) != NULL); 1163 1164 if (if_state->nwe_addr_added) { 1165 /* 1166 * Address has been added. 1167 * 1168 * We need to make sure that we really want to keep 1169 * this address. There is a race where we requested an 1170 * address but by the time we got here we don't really 1171 * want it and need to remove it. 1172 * 1173 * [Note that since we use DHCP inform on interfaces 1174 * with static addresses that they will also have the 1175 * DHCP flag set on the interface.] 1176 * 1177 * Once we decide we want the address adjust the ncu 1178 * state accordingly. For example if this address is 1179 * enough move online. 1180 */ 1181 1182 /* Figure out if we want to keep this address. */ 1183 if (static_addr) { 1184 nifa = find_static_address(addr, ncu); 1185 assert(nifa != NULL); 1186 nifa->configured = B_TRUE; 1187 (void) strlcpy(nifa->ifname, ifname, 1188 sizeof (nifa->ifname)); 1189 } else if (u_if->nwamd_if_dhcp_requested && 1190 v4dhcp_running) { 1191 u_if->nwamd_if_dhcp_configured = B_TRUE; 1192 nifa = find_nonstatic_address(ncu, family, 1193 B_TRUE); 1194 assert(nifa != NULL); 1195 (void) strlcpy(nifa->ifname, ifname, 1196 sizeof (nifa->ifname)); 1197 } else if (u_if->nwamd_if_stateful_requested && 1198 v6dhcp_running) { 1199 u_if->nwamd_if_stateful_configured = B_TRUE; 1200 nifa = find_nonstatic_address(ncu, family, 1201 B_TRUE); 1202 assert(nifa != NULL); 1203 (void) strlcpy(nifa->ifname, ifname, 1204 sizeof (nifa->ifname)); 1205 } else if (u_if->nwamd_if_stateless_requested && 1206 stateless_running) { 1207 u_if->nwamd_if_stateless_configured = B_TRUE; 1208 nifa = find_nonstatic_address(ncu, family, 1209 B_FALSE); 1210 assert(nifa != NULL); 1211 (void) strlcpy(nifa->ifname, ifname, 1212 sizeof (nifa->ifname)); 1213 } else { 1214 /* 1215 * This is something we didn't expect. Remove 1216 * it by unplumbing the logical interface. 1217 */ 1218 if (u_if->nwamd_if_dhcp_requested && 1219 v4dhcp_running) 1220 nwamd_dhcp_release(ncu->ncu_name); 1221 if (lifnum == 0) { 1222 nwamd_down_interface(ncu->ncu_name, 1223 lifnum, family); 1224 interface_ncu_down(ncu); 1225 } else { 1226 nwamd_unplumb_interface(ncu, lifnum, 1227 family); 1228 } 1229 goto exit; 1230 } 1231 1232 /* 1233 * The address looks valid so mark configured and 1234 * move online if we either have a v4 address if 1235 * v4 is configured or a v6 address if only v6 is 1236 * configured. 1237 */ 1238 (void) update_address_configured_value(addr, ncu, 1239 B_TRUE); 1240 if (state != NWAM_STATE_ONLINE) 1241 interface_ncu_up(ncu); 1242 1243 /* 1244 * Refresh network/location since we may also have other 1245 * DHCP information. We might have to restore it first 1246 * in case it is in maintenance. 1247 */ 1248 nlog(LOG_DEBUG, "nwamd_handle_if_state_event: " 1249 "refreshing %s as we may have other " 1250 "DHCP information", NET_LOC_FMRI); 1251 (void) smf_restore_instance(NET_LOC_FMRI); 1252 if (smf_refresh_instance(NET_LOC_FMRI) != 0) { 1253 nlog(LOG_ERR, 1254 "nwamd_ncu_handle_if_state_" 1255 "event: refresh of %s " 1256 "failed", NET_LOC_FMRI); 1257 } 1258 } else if (state == NWAM_STATE_ONLINE || 1259 state == NWAM_STATE_OFFLINE_TO_ONLINE) { 1260 /* 1261 * Address has been removed. Only pay attention to 1262 * disappearing addresses if we are online or coming 1263 * online. 1264 * 1265 * Undo whatever configuration is necessary. Note 1266 * that this may or may not cause the NCU to go down. 1267 * We can get RTM_DELADDRs for duplicate addresses 1268 * so deal with this seperately. 1269 */ 1270 if (static_addr) { 1271 (void) update_address_configured_value(addr, 1272 ncu, B_FALSE); 1273 } else if (family == AF_INET) { 1274 u_if->nwamd_if_dhcp_configured = B_FALSE; 1275 } else if (family == AF_INET6) { 1276 /* 1277 * The address is already gone. I'm not sure 1278 * how we figure out if this address is 1279 * stateful (DHCP) or stateless. When we 1280 * are managing IPv6 more explicitly this will 1281 * have to be done more carefully. 1282 */ 1283 u_if->nwamd_if_stateful_configured = B_FALSE; 1284 u_if->nwamd_if_stateless_configured = B_FALSE; 1285 } 1286 1287 if (flags & IFF_DUPLICATE) { 1288 nlog(LOG_INFO, 1289 "nwamd_ncu_handle_if_state_event: " 1290 "duplicate address detected on %s", 1291 ncu->ncu_name); 1292 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1293 event->event_object, 1294 NWAM_STATE_MAINTENANCE, 1295 NWAM_AUX_STATE_IF_DUPLICATE_ADDR); 1296 } else { 1297 interface_ncu_down(ncu); 1298 } 1299 } 1300 } 1301 exit: 1302 nwamd_object_release(ncu_obj); 1303 } 1304 1305 void 1306 nwamd_ncu_handle_if_action_event(nwamd_event_t event) 1307 { 1308 nwamd_object_t ncu_obj; 1309 1310 nlog(LOG_DEBUG, "if action event %s", 1311 event->event_object[0] == '\0' ? "n/a" : event->event_object); 1312 1313 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object); 1314 if (ncu_obj == NULL) { 1315 nlog(LOG_ERR, "nwamd_ncu_handle_if_action_event: no object"); 1316 nwamd_event_do_not_send(event); 1317 return; 1318 } 1319 nwamd_object_release(ncu_obj); 1320 } 1321 1322 /* 1323 * This function downs any logical interface and just zeros the address off of 1324 * the physical interface (logical interface 0). If you want to unplumb 0 then 1325 * you need to call nwamd_unplumb_interface() directly. 1326 */ 1327 static void 1328 nwamd_down_interface(const char *linkname, uint_t lifnum, int family) 1329 { 1330 uint64_t flags; 1331 icfg_if_t intf; 1332 icfg_handle_t h; 1333 icfg_error_t rc; 1334 1335 if (linkname == NULL) { 1336 nlog(LOG_ERR, "nwamd_down_interface: linkname null"); 1337 return; 1338 } 1339 1340 (void) nwamd_link_to_ifname(linkname, lifnum, intf.if_name, 1341 sizeof (intf.if_name)); 1342 intf.if_protocol = family; 1343 1344 rc = icfg_open(&h, &intf); 1345 if (rc != ICFG_SUCCESS) { 1346 nlog(LOG_ERR, "nwamd_down_interface: icfg_open failed for %s: " 1347 "%s", intf.if_name, icfg_errmsg(rc)); 1348 return; 1349 } 1350 1351 if (lifnum == 0) { 1352 struct sockaddr_in6 addr; 1353 1354 (void) memset(&addr, 0, sizeof (addr)); 1355 addr.sin6_family = family; 1356 if (icfg_set_addr(h, (struct sockaddr *)&addr, 1357 family == AF_INET ? sizeof (struct sockaddr_in) : 1358 sizeof (struct sockaddr_in6)) != ICFG_SUCCESS) 1359 nlog(LOG_ERR, "nwamd_down_interface couldn't zero " 1360 "address on %s", h->ifh_interface.if_name); 1361 } else { 1362 if (icfg_get_flags(h, &flags) == ICFG_SUCCESS) { 1363 if (icfg_set_flags(h, flags & ~IFF_UP) != ICFG_SUCCESS) 1364 nlog(LOG_ERR, "nwamd_down_interface: couldn't " 1365 "bring %s down", h->ifh_interface.if_name); 1366 } else { 1367 nlog(LOG_ERR, "nwamd_down_interface: icfg_get_flags " 1368 "failed on %s", h->ifh_interface.if_name); 1369 } 1370 } 1371 1372 icfg_close(h); 1373 } 1374 1375 static void 1376 nwamd_plumb_unplumb_interface(nwamd_ncu_t *ncu, uint_t lifnum, 1377 int af, boolean_t plumb) 1378 { 1379 uint64_t flags; 1380 icfg_if_t intf; 1381 icfg_handle_t h; 1382 icfg_error_t rc; 1383 nwamd_if_t *u_if; 1384 const char *linkname = ncu->ncu_name; 1385 1386 if (linkname == NULL) { 1387 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: linkname null"); 1388 return; 1389 } 1390 1391 (void) nwamd_link_to_ifname(linkname, lifnum, intf.if_name, 1392 sizeof (intf.if_name)); 1393 intf.if_protocol = af; 1394 1395 nlog(LOG_DEBUG, "nwamd_plumb_unplumb_interface: %s %s on link %s", 1396 plumb ? "plumbing" : "unplumbing", 1397 af == AF_INET ? "IPv4" : "IPv6", linkname); 1398 1399 /* 1400 * Before unplumbing, do a DHCP release if lifnum is 0. Otherwise 1401 * dhcpagent can get confused. 1402 */ 1403 if (!plumb && af == AF_INET && lifnum == 0) 1404 nwamd_dhcp_release(ncu->ncu_name); 1405 1406 rc = icfg_open(&h, &intf); 1407 if (rc != ICFG_SUCCESS) { 1408 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: " 1409 "icfg_open failed for %s: %s", intf.if_name, 1410 icfg_errmsg(rc)); 1411 return; 1412 } 1413 rc = plumb ? icfg_plumb(h) : icfg_unplumb(h); 1414 1415 if (rc != ICFG_SUCCESS) { 1416 if ((plumb && rc != ICFG_EXISTS) || 1417 (!plumb && rc != ICFG_NO_EXIST)) { 1418 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: " 1419 "%s %s failed for %s: %s", 1420 plumb ? "plumb" : "unplumb", 1421 af == AF_INET ? "IPv4" : "IPv6", 1422 intf.if_name, icfg_errmsg(rc)); 1423 } 1424 } else if (plumb) { 1425 if (icfg_get_flags(h, &flags) == ICFG_SUCCESS && 1426 (flags & IFF_UP) == 0) { 1427 if (icfg_set_flags(h, flags | IFF_UP) != ICFG_SUCCESS) 1428 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: " 1429 "couldn't bring %s up", 1430 h->ifh_interface.if_name); 1431 } else { 1432 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: " 1433 "icfg_get_flags failed on %s", 1434 h->ifh_interface.if_name); 1435 } 1436 } 1437 1438 u_if = &ncu->ncu_node.u_if; 1439 if (!plumb) { 1440 nwamd_update_addresses_unconfigured(ncu, af); 1441 switch (af) { 1442 case AF_INET: 1443 u_if->nwamd_if_dhcp_configured = B_FALSE; 1444 break; 1445 case AF_INET6: 1446 u_if->nwamd_if_stateful_configured = B_FALSE; 1447 u_if->nwamd_if_stateless_configured = B_FALSE; 1448 break; 1449 } 1450 } 1451 1452 icfg_close(h); 1453 } 1454 1455 void 1456 nwamd_plumb_interface(nwamd_ncu_t *ncu, uint_t lifnum, int af) 1457 { 1458 /* 1459 * We get all posssible privs by calling nwamd_deescalate(). During 1460 * startup opening /dev/dld (data link management) needs all privs 1461 * because we don't have access to /etc/security/device_policy yet. 1462 */ 1463 nwamd_escalate(); 1464 nwamd_plumb_unplumb_interface(ncu, lifnum, af, B_TRUE); 1465 nwamd_deescalate(); 1466 } 1467 1468 void 1469 nwamd_unplumb_interface(nwamd_ncu_t *ncu, uint_t lifnum, int af) 1470 { 1471 nwamd_plumb_unplumb_interface(ncu, lifnum, af, B_FALSE); 1472 } 1473 1474 static void * 1475 start_dhcp_thread(void *arg) 1476 { 1477 struct nwamd_dhcp_thread_arg *thread_arg; 1478 dhcp_ipc_reply_t *reply = NULL; 1479 dhcp_ipc_request_t *request; 1480 dhcp_ipc_type_t type; 1481 int timeout; 1482 char *name; 1483 int rc, retries = 0; 1484 1485 thread_arg = (struct nwamd_dhcp_thread_arg *)arg; 1486 timeout = thread_arg->timeout; 1487 name = thread_arg->name; 1488 type = thread_arg->type; 1489 1490 /* Try starting agent, though it may already be there */ 1491 nwamd_escalate(); 1492 rc = dhcp_start_agent(DHCP_IPC_MAX_WAIT); 1493 nwamd_deescalate(); 1494 if (rc == -1) { 1495 nlog(LOG_DEBUG, "Unable to start %s", DHCP_AGENT_PATH); 1496 goto failed; 1497 } 1498 retry: 1499 /* Now allocate and send the request */ 1500 request = dhcp_ipc_alloc_request(type, name, NULL, 0, 1501 DHCP_TYPE_NONE); 1502 if (request == NULL) { 1503 nlog(LOG_DEBUG, "start_dhcp: dhcp_ipc_alloc_request : %s", 1504 strerror(errno)); 1505 goto failed; 1506 } 1507 1508 rc = dhcp_ipc_make_request(request, &reply, timeout); 1509 free(request); 1510 if (rc != 0) { 1511 nlog(LOG_DEBUG, "start_dhcp %s: %s", name, 1512 dhcp_ipc_strerror(rc)); 1513 goto failed; 1514 } 1515 1516 rc = reply->return_code; 1517 if (rc != 0) { 1518 if (rc == DHCP_IPC_E_TIMEOUT && timeout == 0) { 1519 goto failed; 1520 } 1521 1522 /* 1523 * DHCP timed out: change state for this NCU and enqueue 1524 * event to check NCU priority-groups. Only care for 1525 * DHCP requests (not informs). 1526 */ 1527 if (rc == DHCP_IPC_E_TIMEOUT && type != DHCP_INFORM) { 1528 char *object_name; 1529 1530 nlog(LOG_INFO, "start_dhcp: DHCP timed out for %s", 1531 name); 1532 if (nwam_ncu_name_to_typed_name(name, 1533 NWAM_NCU_TYPE_INTERFACE, &object_name) 1534 != NWAM_SUCCESS) { 1535 nlog(LOG_ERR, "start_dhcp: " 1536 "nwam_ncu_name_to_typed_name failed"); 1537 goto failed; 1538 } 1539 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, 1540 object_name, NWAM_STATE_OFFLINE_TO_ONLINE, 1541 NWAM_AUX_STATE_IF_DHCP_TIMED_OUT); 1542 nwamd_create_ncu_check_event(0); 1543 1544 free(object_name); 1545 goto failed; 1546 1547 } else if (rc == DHCP_IPC_E_RUNNING) { 1548 /* 1549 * DHCP is already running. Check if IP address is 1550 * already configured on the interface. 1551 */ 1552 1553 icfg_handle_t h; 1554 icfg_if_t intf; 1555 struct sockaddr_in sin; 1556 socklen_t alen = sizeof (struct sockaddr_in); 1557 int plen, index; 1558 uint64_t flags; 1559 nwamd_event_t ip_event; 1560 1561 nlog(LOG_ERR, "start_dhcp: DHCP already running on %s", 1562 name); 1563 1564 (void) strlcpy(intf.if_name, name, 1565 sizeof (intf.if_name)); 1566 intf.if_protocol = AF_INET; 1567 1568 if (icfg_open(&h, &intf) != ICFG_SUCCESS) { 1569 nlog(LOG_ERR, "start_dhcp: " 1570 "icfg_open failed on %s", name); 1571 goto failed; 1572 } 1573 1574 /* Get address */ 1575 if (icfg_get_addr(h, (struct sockaddr *)&sin, &alen, 1576 &plen, B_FALSE) != ICFG_SUCCESS) { 1577 nlog(LOG_ERR, "start_dhcp: " 1578 "icfg_get_addr failed on %s: %s", 1579 name, strerror(errno)); 1580 goto bail; 1581 } 1582 /* Check if 0.0.0.0 */ 1583 if (sin.sin_addr.s_addr == INADDR_ANY) { 1584 nlog(LOG_ERR, "start_dhcp: empty address on %s", 1585 name); 1586 goto bail; 1587 } 1588 1589 /* valid address exists, get the flags, index of intf */ 1590 if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) { 1591 nlog(LOG_ERR, "start_dhcp: " 1592 "icfg_get_flags failed on %s", name); 1593 goto bail; 1594 } 1595 if (icfg_get_index(h, &index) != ICFG_SUCCESS) { 1596 nlog(LOG_ERR, "start_dhcp: " 1597 "icfg_get_index failed on %s", name); 1598 goto bail; 1599 } 1600 1601 /* synthesize an IF_STATE event with the intf's flags */ 1602 ip_event = nwamd_event_init_if_state(name, flags, 1603 B_TRUE, index, (struct sockaddr *)&sin); 1604 if (ip_event != NULL) 1605 nwamd_event_enqueue(ip_event); 1606 bail: 1607 icfg_close(h); 1608 goto failed; 1609 1610 } else if ((rc == DHCP_IPC_E_SOCKET || 1611 rc == DHCP_IPC_E_INVIF) && retries++ < NWAMD_DHCP_RETRIES) { 1612 /* 1613 * Retry DHCP request as we may have been unplumbing 1614 * as part of the configuration phase. 1615 */ 1616 nlog(LOG_ERR, "start_dhcp %s: %s; will retry in %d sec", 1617 name, dhcp_ipc_strerror(rc), 1618 rc == DHCP_IPC_E_INVIF ? 1619 NWAMD_DHCP_RETRY_WAIT_TIME : 0); 1620 if (rc == DHCP_IPC_E_INVIF) 1621 (void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME); 1622 goto retry; 1623 } else { 1624 nlog(LOG_ERR, "start_dhcp %s: %s", name, 1625 dhcp_ipc_strerror(rc)); 1626 goto failed; 1627 } 1628 } 1629 1630 /* If status was the command, then output the results */ 1631 if (DHCP_IPC_CMD(type) == DHCP_STATUS) { 1632 nlog(LOG_DEBUG, "%s", dhcp_status_hdr_string()); 1633 nlog(LOG_DEBUG, "%s", dhcp_status_reply_to_string(reply)); 1634 } 1635 1636 failed: 1637 free(reply); 1638 if (arg != NULL) { 1639 free(name); 1640 free(arg); 1641 } 1642 return (NULL); 1643 } 1644 1645 void 1646 nwamd_start_dhcp(nwamd_ncu_t *ncu) 1647 { 1648 struct nwamd_dhcp_thread_arg *arg; 1649 char *name = NULL; 1650 pthread_attr_t attr; 1651 1652 nlog(LOG_DEBUG, "nwamd_start_dhcp: starting DHCP for %s %d", 1653 ncu->ncu_name, ncu->ncu_type); 1654 1655 arg = malloc(sizeof (*arg)); 1656 if (arg == NULL) { 1657 nlog(LOG_ERR, "nwamd_start_dhcp: error allocating memory " 1658 "for dhcp request"); 1659 free(name); 1660 return; 1661 } 1662 1663 arg->name = strdup(ncu->ncu_name); 1664 arg->type = DHCP_START; 1665 arg->timeout = ncu_wait_time; 1666 1667 (void) pthread_attr_init(&attr); 1668 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1669 if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) { 1670 nlog(LOG_ERR, "nwamd_start_dhcp: cannot start dhcp thread"); 1671 free(name); 1672 free(arg); 1673 (void) pthread_attr_destroy(&attr); 1674 return; 1675 } 1676 (void) pthread_attr_destroy(&attr); 1677 } 1678