xref: /titanic_44/usr/src/cmd/cmd-inet/lib/nwamd/ncu_ip.c (revision 0dc2366f7b9f9f36e10909b1e95edbf2a261c2ac)
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 	nwamd_plumb_unplumb_interface(ncu, lifnum, af, B_TRUE);
1459 }
1460 
1461 void
1462 nwamd_unplumb_interface(nwamd_ncu_t *ncu, uint_t lifnum, int af)
1463 {
1464 	nwamd_plumb_unplumb_interface(ncu, lifnum, af, B_FALSE);
1465 }
1466 
1467 static void *
1468 start_dhcp_thread(void *arg)
1469 {
1470 	struct nwamd_dhcp_thread_arg *thread_arg;
1471 	dhcp_ipc_reply_t *reply = NULL;
1472 	dhcp_ipc_request_t *request;
1473 	dhcp_ipc_type_t type;
1474 	int timeout;
1475 	char *name;
1476 	int rc, retries = 0;
1477 
1478 	thread_arg = (struct nwamd_dhcp_thread_arg *)arg;
1479 	timeout = thread_arg->timeout;
1480 	name = thread_arg->name;
1481 	type = thread_arg->type;
1482 
1483 	/* Try starting agent, though it may already be there */
1484 	nwamd_to_root();
1485 	rc = dhcp_start_agent(DHCP_IPC_MAX_WAIT);
1486 	nwamd_from_root();
1487 	if (rc == -1) {
1488 		nlog(LOG_DEBUG, "Unable to start %s", DHCP_AGENT_PATH);
1489 		goto failed;
1490 	}
1491 retry:
1492 	/* Now allocate and send the request */
1493 	request = dhcp_ipc_alloc_request(type, name, NULL, 0,
1494 	    DHCP_TYPE_NONE);
1495 	if (request == NULL) {
1496 		nlog(LOG_DEBUG, "start_dhcp: dhcp_ipc_alloc_request : %s",
1497 		    strerror(errno));
1498 		goto failed;
1499 	}
1500 
1501 	rc = dhcp_ipc_make_request(request, &reply, timeout);
1502 	free(request);
1503 	if (rc != 0) {
1504 		nlog(LOG_DEBUG, "start_dhcp %s: %s", name,
1505 		    dhcp_ipc_strerror(rc));
1506 		goto failed;
1507 	}
1508 
1509 	rc = reply->return_code;
1510 	if (rc != 0) {
1511 		if (rc == DHCP_IPC_E_TIMEOUT && timeout == 0) {
1512 			goto failed;
1513 		}
1514 
1515 		/*
1516 		 * DHCP timed out: change state for this NCU and enqueue
1517 		 * event to check NCU priority-groups.  Only care for
1518 		 * DHCP requests (not informs).
1519 		 */
1520 		if (rc == DHCP_IPC_E_TIMEOUT && type != DHCP_INFORM) {
1521 			char *object_name;
1522 
1523 			nlog(LOG_INFO, "start_dhcp: DHCP timed out for %s",
1524 			    name);
1525 			if (nwam_ncu_name_to_typed_name(name,
1526 			    NWAM_NCU_TYPE_INTERFACE, &object_name)
1527 			    != NWAM_SUCCESS) {
1528 				nlog(LOG_ERR, "start_dhcp: "
1529 				    "nwam_ncu_name_to_typed_name failed");
1530 				goto failed;
1531 			}
1532 			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1533 			    object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
1534 			    NWAM_AUX_STATE_IF_DHCP_TIMED_OUT);
1535 			nwamd_create_ncu_check_event(0);
1536 
1537 			free(object_name);
1538 			goto failed;
1539 
1540 		} else if (rc == DHCP_IPC_E_RUNNING) {
1541 			/*
1542 			 * DHCP is already running.  Check if IP address is
1543 			 * already configured on the interface.
1544 			 */
1545 
1546 			icfg_handle_t h;
1547 			icfg_if_t intf;
1548 			struct sockaddr_in sin;
1549 			socklen_t alen = sizeof (struct sockaddr_in);
1550 			int plen, index;
1551 			uint64_t flags;
1552 			nwamd_event_t ip_event;
1553 
1554 			nlog(LOG_ERR, "start_dhcp: DHCP already running on %s",
1555 			    name);
1556 
1557 			(void) strlcpy(intf.if_name, name,
1558 			    sizeof (intf.if_name));
1559 			intf.if_protocol = AF_INET;
1560 
1561 			if (icfg_open(&h, &intf) != ICFG_SUCCESS) {
1562 				nlog(LOG_ERR, "start_dhcp: "
1563 				    "icfg_open failed on %s", name);
1564 				goto failed;
1565 			}
1566 
1567 			/* Get address */
1568 			if (icfg_get_addr(h, (struct sockaddr *)&sin, &alen,
1569 			    &plen, B_FALSE) != ICFG_SUCCESS) {
1570 				nlog(LOG_ERR, "start_dhcp: "
1571 				    "icfg_get_addr failed on %s: %s",
1572 				    name, strerror(errno));
1573 				goto bail;
1574 			}
1575 			/* Check if 0.0.0.0 */
1576 			if (sin.sin_addr.s_addr == INADDR_ANY) {
1577 				nlog(LOG_ERR, "start_dhcp: empty address on %s",
1578 				    name);
1579 				goto bail;
1580 			}
1581 
1582 			/* valid address exists, get the flags, index of intf */
1583 			if (icfg_get_flags(h, &flags) != ICFG_SUCCESS) {
1584 				nlog(LOG_ERR, "start_dhcp: "
1585 				    "icfg_get_flags failed on %s", name);
1586 				goto bail;
1587 			}
1588 			if (icfg_get_index(h, &index) != ICFG_SUCCESS) {
1589 				nlog(LOG_ERR, "start_dhcp: "
1590 				    "icfg_get_index failed on %s", name);
1591 				goto bail;
1592 			}
1593 
1594 			/* synthesize an IF_STATE event with the intf's flags */
1595 			ip_event = nwamd_event_init_if_state(name, flags,
1596 			    B_TRUE, index, (struct sockaddr *)&sin);
1597 			if (ip_event != NULL)
1598 				nwamd_event_enqueue(ip_event);
1599 bail:
1600 			icfg_close(h);
1601 			goto failed;
1602 
1603 		} else if ((rc == DHCP_IPC_E_SOCKET ||
1604 		    rc == DHCP_IPC_E_INVIF) && retries++ < NWAMD_DHCP_RETRIES) {
1605 			/*
1606 			 * Retry DHCP request as we may have been unplumbing
1607 			 * as part of the configuration phase.
1608 			 */
1609 			nlog(LOG_ERR, "start_dhcp %s: %s; will retry in %d sec",
1610 			    name, dhcp_ipc_strerror(rc),
1611 			    rc == DHCP_IPC_E_INVIF ?
1612 			    NWAMD_DHCP_RETRY_WAIT_TIME : 0);
1613 			if (rc == DHCP_IPC_E_INVIF)
1614 				(void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME);
1615 			goto retry;
1616 		} else {
1617 			nlog(LOG_ERR, "start_dhcp %s: %s", name,
1618 			    dhcp_ipc_strerror(rc));
1619 			goto failed;
1620 		}
1621 	}
1622 
1623 	/* If status was the command, then output the results */
1624 	if (DHCP_IPC_CMD(type) == DHCP_STATUS) {
1625 		nlog(LOG_DEBUG, "%s", dhcp_status_hdr_string());
1626 		nlog(LOG_DEBUG, "%s", dhcp_status_reply_to_string(reply));
1627 	}
1628 
1629 failed:
1630 	free(reply);
1631 	if (arg != NULL) {
1632 		free(name);
1633 		free(arg);
1634 	}
1635 	return (NULL);
1636 }
1637 
1638 void
1639 nwamd_start_dhcp(nwamd_ncu_t *ncu)
1640 {
1641 	struct nwamd_dhcp_thread_arg *arg;
1642 	char *name = NULL;
1643 	pthread_attr_t attr;
1644 
1645 	nlog(LOG_DEBUG, "nwamd_start_dhcp: starting DHCP for %s %d",
1646 	    ncu->ncu_name, ncu->ncu_type);
1647 
1648 	arg = malloc(sizeof (*arg));
1649 	if (arg == NULL) {
1650 		nlog(LOG_ERR, "nwamd_start_dhcp: error allocating memory "
1651 		    "for dhcp request");
1652 		free(name);
1653 		return;
1654 	}
1655 
1656 	arg->name = strdup(ncu->ncu_name);
1657 	arg->type = DHCP_START;
1658 	arg->timeout = ncu_wait_time;
1659 
1660 	(void) pthread_attr_init(&attr);
1661 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1662 	if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) {
1663 		nlog(LOG_ERR, "nwamd_start_dhcp: cannot start dhcp thread");
1664 		free(name);
1665 		free(arg);
1666 		(void) pthread_attr_destroy(&attr);
1667 		return;
1668 	}
1669 	(void) pthread_attr_destroy(&attr);
1670 }
1671