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