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
55static void nwamd_dhcp(const char *, ipadm_addrobj_t, dhcp_ipc_type_t);
56static void nwamd_down_interface(const char *, ipadm_addr_type_t, const char *);
57static 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 */
66static const char *
67nwamd_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
92void
93nwamd_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 */
145char *
146nwamd_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
215fail:
216	nlog(LOG_DEBUG, "get_dhcpinfo_data() failed: %s", errmsg);
217	free(request);
218	free(reply);
219	return (NULL);
220}
221
222void
223nwamd_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 */
271static struct nwamd_if_address *
272find_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 */
298static struct nwamd_if_address *
299find_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 */
328static struct nwamd_if_address *
329find_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 */
348boolean_t
349nwamd_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 */
371boolean_t
372nwamd_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 */
439static boolean_t
440nwamd_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 */
467static boolean_t
468nwamd_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 */
498static void
499interface_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
545static void
546interface_ncu_up(nwamd_ncu_t *ncu)
547{
548	interface_ncu_up_down(ncu, B_TRUE);
549}
550
551static void
552interface_ncu_down(nwamd_ncu_t *ncu)
553{
554	interface_ncu_up_down(ncu, B_FALSE);
555}
556
557static boolean_t
558stateless_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 */
590static boolean_t
591addrinfo_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 */
636static boolean_t
637addrinfo_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 */
690static boolean_t
691add_ip_address(const char *ifname, const struct nwamd_if_address *nifa,
692    boolean_t *do_inform)
693{
694	ipadm_status_t ipstatus;
695	ipadm_addr_info_t *addrinfo = NULL;
696	uint64_t flags;
697
698	if (nifa->ipaddr_atype == IPADM_ADDR_DHCP) {
699		/*
700		 * To make getting a DHCP address asynchronous, call
701		 * ipadm_create_addr() in a new thread.
702		 */
703		nlog(LOG_DEBUG, "add_ip_address: "
704		    "adding IPv4 DHCP address on %s", ifname);
705		nwamd_dhcp(ifname, nifa->ipaddr, DHCP_START);
706	} else {
707		nlog(LOG_DEBUG, "add_ip_address: adding %s address on %s",
708		    (nifa->ipaddr_atype == IPADM_ADDR_STATIC ?
709		    "STATIC" : "IPv6 ADDRCONF"), ifname);
710		if ((ipstatus = ipadm_create_addr(ipadm_handle, nifa->ipaddr,
711		    IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) {
712			nlog(LOG_ERR, "add_ip_address: "
713			    "ipadm_create_addr failed on %s: %s",
714			    ifname, ipadm_status2str(ipstatus));
715			return (B_FALSE);
716		}
717		/*
718		 * When creating a static address, ipadm_create_addr() returns
719		 * SUCCESS even if duplicate address is detected.  Retrieve
720		 * the addrinfo to get the flags.
721		 */
722		if (nifa->ipaddr_atype == IPADM_ADDR_STATIC) {
723			/*
724			 * Since we are configuring a static address, there
725			 * will be just *ONE* addrinfo with the aobjname in
726			 * nifa->ipaddr.
727			 */
728			if (!addrinfo_for_ipaddr(nifa->ipaddr, ifname,
729			    &addrinfo)) {
730				nlog(LOG_ERR, "add_ip_address: "
731				    "could not find addrinfo on %s", ifname);
732				return (B_FALSE);
733			}
734
735			flags = addrinfo->ia_ifa.ifa_flags;
736			ipadm_free_addr_info(addrinfo);
737			if (flags & IFF_DUPLICATE) {
738				char *object_name;
739				nwam_error_t err;
740
741				nlog(LOG_INFO, "add_ip_address: "
742				    "duplicate address detected on %s", ifname);
743				if ((err = nwam_ncu_name_to_typed_name(ifname,
744				    NWAM_NCU_TYPE_INTERFACE, &object_name))
745				    == NWAM_SUCCESS) {
746					nwamd_object_set_state(
747					    NWAM_OBJECT_TYPE_NCU,
748					    object_name, NWAM_STATE_MAINTENANCE,
749					    NWAM_AUX_STATE_IF_DUPLICATE_ADDR);
750					free(object_name);
751				} else {
752					nlog(LOG_ERR, "add_ip_address: "
753					    "could not create state event "
754					    "for %s: %s",
755					    ifname, nwam_strerror(err));
756				}
757				return (B_FALSE);
758			}
759			/*
760			 * Do DHCP_INFORM using async ipadm_refresh_addr().
761			 * Only need to do this once per interface, and we
762			 * do *not* need to do it if we are also getting a
763			 * dhcp lease; so we only send the INFORM if the
764			 * passed-in flag says to, and we clear the flag
765			 * once we've initiated the INFORM transaction.
766			 */
767			if (*do_inform) {
768				nwamd_dhcp(ifname, nifa->ipaddr, DHCP_INFORM);
769				*do_inform = B_FALSE;
770			}
771		}
772	}
773
774	return (B_TRUE);
775}
776
777/*
778 * Adds addresses for the given NCU.
779 */
780void
781nwamd_configure_interface_addresses(nwamd_ncu_t *ncu)
782{
783	struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list;
784	boolean_t do_inform;
785
786	/* only need an inform if we're not also getting a dhcp lease */
787	do_inform = !ncu->ncu_if.nwamd_if_dhcp_requested;
788
789	nlog(LOG_DEBUG, "nwamd_configure_interface_addresses(%s)",
790	    ncu->ncu_name);
791
792	for (nifap = nifa; nifap != NULL; nifap = nifap->next) {
793		if (nifap->configured)
794			continue;
795
796		nifap->configured = add_ip_address(ncu->ncu_name, nifap,
797		    &do_inform);
798	}
799}
800
801/*
802 * This event tells us that an interface address has appeared or disappeared,
803 * or that the interface flags on an interface have changed.
804 */
805void
806nwamd_ncu_handle_if_state_event(nwamd_event_t event)
807{
808	nwam_event_t evm;
809	nwamd_object_t ncu_obj;
810	nwamd_ncu_t *ncu;
811	nwam_state_t state;
812	nwam_aux_state_t aux_state;
813
814	ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU,
815	    event->event_object);
816	if (ncu_obj == NULL) {
817		nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: no object %s",
818		    event->event_object);
819		nwamd_event_do_not_send(event);
820		return;
821	}
822	ncu = ncu_obj->nwamd_object_data;
823	evm = event->event_msg;
824	state = ncu_obj->nwamd_object_state;
825	aux_state = ncu_obj->nwamd_object_aux_state;
826
827	nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
828	    "if %s, state (%s, %s)", event->event_object,
829	    nwam_state_to_string(state), nwam_aux_state_to_string(aux_state));
830
831	/* Ensure object is in correct state to handle IF state events */
832	switch (state) {
833	case NWAM_STATE_OFFLINE_TO_ONLINE:
834		if (aux_state != NWAM_AUX_STATE_IF_WAITING_FOR_ADDR &&
835		    aux_state != NWAM_AUX_STATE_IF_DHCP_TIMED_OUT) {
836			nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
837			    "if %s is in invalid aux state %s for IF_STATE "
838			    "events", event->event_object,
839			    nwam_aux_state_to_string(aux_state));
840			nwamd_event_do_not_send(event);
841			nwamd_object_release(ncu_obj);
842			return;
843		}
844		break;
845	case NWAM_STATE_ONLINE:
846	/*
847	 * We can get addresses from DHCP after we've taken the interface down.
848	 * We deal with those below.
849	 */
850	case NWAM_STATE_ONLINE_TO_OFFLINE:
851	case NWAM_STATE_OFFLINE:
852		break;
853	default:
854		nlog(LOG_DEBUG, "nwamd_ncu_handle_if_state_event: "
855		    "if %s is in invalid state %s for IF_STATE events",
856		    event->event_object, nwam_state_to_string(state));
857		nwamd_event_do_not_send(event);
858		nwamd_object_release(ncu_obj);
859		return;
860	}
861
862	if (evm->nwe_data.nwe_if_state.nwe_addr_valid) {
863		struct nwam_event_if_state *if_state;
864		char addrstr[INET6_ADDRSTRLEN];
865		boolean_t static_addr, addr_added;
866		boolean_t v4dhcp_running, v6dhcp_running, stateless_running;
867		ipadm_addr_info_t *ai = NULL, *addrinfo = NULL;
868		boolean_t stateless_ai_found = B_FALSE;
869		boolean_t stateful_ai_found = B_FALSE;
870		struct nwamd_if_address *nifa = NULL;
871		nwamd_if_t *u_if;
872		struct sockaddr_storage *addr, ai_addr, *aip = NULL;
873		ushort_t family;
874		uint64_t flags = 0;
875
876		if_state = &evm->nwe_data.nwe_if_state;
877		u_if = &ncu->ncu_if;
878		family = if_state->nwe_addr.ss_family;
879		addr = &if_state->nwe_addr;
880		addr_added = if_state->nwe_addr_added;
881
882		nlog(LOG_DEBUG,
883		    "nwamd_ncu_handle_if_state_event: addr %s %s",
884		    nwamd_sockaddr2str((struct sockaddr *)addr, addrstr,
885		    sizeof (addrstr)), addr_added ? "added" : "removed");
886
887		/*
888		 * Need to get flags for this interface.  Get the addrinfo for
889		 * the address that generated this IF_STATE event.
890		 */
891		if (addr_added) {
892			/*
893			 * Address was added.  Find the addrinfo for this
894			 * address and the nwamd_if_address corresponding to
895			 * this address.
896			 */
897			if (!addrinfo_for_addr(addr, ncu->ncu_name, &ai)) {
898				nlog(LOG_ERR,
899				    "nwamd_ncu_handle_if_state_event: "
900				    "addrinfo doesn't exist for %s", addrstr);
901				nwamd_event_do_not_send(event);
902				goto valid_done;
903			}
904			addrinfo = ai;
905			flags = addrinfo->ia_ifa.ifa_flags;
906			(void) memcpy(&ai_addr, addrinfo->ia_ifa.ifa_addr,
907			    sizeof (ai_addr));
908			aip = &ai_addr;
909
910			if (addrinfo->ia_atype == IPADM_ADDR_IPV6_ADDRCONF ||
911			    addrinfo->ia_atype == IPADM_ADDR_DHCP)
912				nifa = find_nonstatic_address(ncu, family);
913			else if (addrinfo->ia_atype == IPADM_ADDR_STATIC)
914				nifa = find_static_address(addr, ncu);
915
916			/*
917			 * If nwamd_if_address is not found, then this address
918			 * isn't one that nwamd created.  Remove it.
919			 */
920			if (nifa == NULL) {
921				nlog(LOG_ERR,
922				    "nwamd_ncu_handle_if_state_event: "
923				    "address %s not managed by nwam added, "
924				    "removing it", addrstr);
925				nwamd_down_interface(addrinfo->ia_aobjname,
926				    addrinfo->ia_atype, ncu->ncu_name);
927				nwamd_event_do_not_send(event);
928				goto valid_done;
929			}
930
931			/* check flags to determine how intf is configured */
932			stateless_running = (family == AF_INET6) &&
933			    ((flags & STATELESS_RUNNING) == STATELESS_RUNNING);
934			v4dhcp_running = (family == AF_INET) &&
935			    ((flags & DHCP_RUNNING) == DHCP_RUNNING);
936			v6dhcp_running = (family == AF_INET6) &&
937			    ((flags & DHCP_RUNNING) == DHCP_RUNNING);
938			static_addr = (addrinfo->ia_atype == IPADM_ADDR_STATIC);
939
940			/* copy the configured address into nwamd_if_address */
941			if (stateless_running) {
942				(void) memcpy(&nifa->conf_stateless_addr,
943				    addrinfo->ia_ifa.ifa_addr,
944				    sizeof (struct sockaddr_storage));
945			} else {
946				(void) memcpy(&nifa->conf_addr,
947				    addrinfo->ia_ifa.ifa_addr,
948				    sizeof (struct sockaddr_storage));
949			}
950
951		} else {
952			/*
953			 * Address was removed.  Find the nwamd_if_address
954			 * that configured this address.
955			 */
956			nifa = find_configured_address(addr, ncu);
957			if (nifa == NULL) {
958				nlog(LOG_ERR,
959				    "nwamd_ncu_handle_if_state_event: "
960				    "address %s not managed by nwam removed, "
961				    "nothing to do", addrstr);
962				nwamd_event_do_not_send(event);
963				goto valid_done;
964			}
965
966			if (addrinfo_for_ipaddr(nifa->ipaddr, ncu->ncu_name,
967			    &ai)) {
968				ipadm_addr_info_t *a;
969				for (a = ai; a != NULL; a = IA_NEXT(a)) {
970					struct sockaddr_storage stor;
971
972					(void) memcpy(&stor, a->ia_ifa.ifa_addr,
973					    sizeof (stor));
974					/*
975					 * Since multiple addrinfo can have
976					 * the same ipaddr, find the one for
977					 * the address that generated this
978					 * state event.
979					 */
980					if (sockaddrcmp(addr, &stor)) {
981						flags = a->ia_ifa.ifa_flags;
982						(void) memcpy(&ai_addr,
983						    a->ia_ifa.ifa_addr,
984						    sizeof (ai_addr));
985						aip = &ai_addr;
986						addrinfo = a;
987					}
988					/*
989					 * Stateful and stateless IPv6
990					 * addrinfo have the same aobjname.
991					 * Use the flags to determine which
992					 * address is present in the system.
993					 */
994					if (family == AF_INET6) {
995						stateless_ai_found =
996						    (a->ia_ifa.ifa_flags &
997						    STATELESS_RUNNING);
998						stateful_ai_found =
999						    (a->ia_ifa.ifa_flags &
1000						    DHCP_RUNNING);
1001					}
1002				}
1003			}
1004		}
1005
1006		/* Set the flags in the event for listeners */
1007		evm->nwe_data.nwe_if_state.nwe_flags = flags;
1008
1009		if (family == AF_INET && !addr_added) {
1010			/*
1011			 * Check for failure due to CR 6745448: if we get a
1012			 * report that an address has been deleted, then check
1013			 * for interface up, datalink down, and actual address
1014			 * non-zero.  If that combination is seen, then this is
1015			 * a DHCP cached lease, and we need to remove it from
1016			 * the system, or it'll louse up the kernel routes
1017			 * (which aren't smart enough to avoid dead
1018			 * interfaces).
1019			 */
1020			if (((struct sockaddr_in *)addr)->sin_addr.s_addr
1021			    == INADDR_ANY && aip != 0) {
1022				struct sockaddr_in *a;
1023				char astr[INET6_ADDRSTRLEN];
1024				a = (struct sockaddr_in *)aip;
1025
1026				if ((flags & IFF_UP) &&
1027				    !(flags & IFF_RUNNING) &&
1028				    a->sin_addr.s_addr != INADDR_ANY) {
1029					nlog(LOG_DEBUG,
1030					    "nwamd_ncu_handle_if_state_event: "
1031					    "bug workaround: clear out addr "
1032					    "%s on %s", nwamd_sockaddr2str
1033					    ((struct sockaddr *)a, astr,
1034					    sizeof (astr)),
1035					    ncu->ncu_name);
1036					nwamd_down_interface(
1037					    addrinfo->ia_aobjname,
1038					    IPADM_ADDR_DHCP, ncu->ncu_name);
1039				}
1040				goto valid_done;
1041			}
1042		}
1043
1044		/*
1045		 * If we received an RTM_NEWADDR and the IFF_UP flags has not
1046		 * been set, ignore this IF_STATE event.  Once the IFF_UP flag
1047		 * is set, we'll get another RTM_NEWADDR message.
1048		 */
1049		if (addr_added & !(flags & IFF_UP)) {
1050			nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: "
1051			    "address %s added on %s without IFF_UP flag (%x), "
1052			    "ignoring IF_STATE event",
1053			    addrstr, ncu->ncu_name, flags);
1054			nwamd_event_do_not_send(event);
1055			goto valid_done;
1056		}
1057
1058		/*
1059		 * Has the address really been removed?  Sometimes spurious
1060		 * RTM_DELADDRs are generated, so we need to ensure that
1061		 * the address is really gone.  If IFF_DUPLICATE is set,
1062		 * we're getting the RTM_DELADDR due to DAD, so don't test
1063		 * in that case.
1064		 */
1065		if (!addr_added && !(flags & IFF_DUPLICATE)) {
1066			if (aip != 0 && sockaddrcmp(addr, aip)) {
1067				nlog(LOG_INFO,
1068				    "nwamd_ncu_handle_if_state_event: "
1069				    "address %s is not really gone from %s, "
1070				    "ignoring IF_STATE event",
1071				    addrstr, ncu->ncu_name);
1072				nwamd_event_do_not_send(event);
1073				goto valid_done;
1074			}
1075		}
1076
1077		if (addr_added) {
1078			/*
1079			 * Address has been added.
1080			 *
1081			 * We need to make sure that we really want to keep
1082			 * this address.  There is a race where we requested an
1083			 * address but by the time we got here we don't really
1084			 * want it and need to remove it.
1085			 *
1086			 * Once we decide we want the address adjust the ncu
1087			 * state accordingly.  For example if this address is
1088			 * enough move online.
1089			 */
1090			if (u_if->nwamd_if_dhcp_requested && v4dhcp_running) {
1091				u_if->nwamd_if_dhcp_configured = B_TRUE;
1092			} else if (u_if->nwamd_if_stateful_requested &&
1093			    v6dhcp_running) {
1094				u_if->nwamd_if_stateful_configured = B_TRUE;
1095			} else if (u_if->nwamd_if_stateless_requested &&
1096			    stateless_running) {
1097				u_if->nwamd_if_stateless_configured = B_TRUE;
1098			} else if (!static_addr) {
1099				/*
1100				 * This is something we didn't expect.  Remove
1101				 * the address.
1102				 */
1103				nwamd_down_interface(addrinfo->ia_aobjname,
1104				    addrinfo->ia_atype, ncu->ncu_name);
1105				nifa->configured = B_FALSE;
1106				goto valid_done;
1107			}
1108
1109			/*
1110			 * The address looks valid so mark configured and
1111			 * move online if we either have a v4 address if
1112			 * v4 is configured or a v6 address if only v6 is
1113			 * configured.
1114			 */
1115			nifa->configured = B_TRUE;
1116			if (state != NWAM_STATE_ONLINE)
1117				interface_ncu_up(ncu);
1118
1119			/*
1120			 * Refresh network/location since we may also have other
1121			 * DHCP information.  We might have to restore it first
1122			 * in case it is in maintenance.
1123			 */
1124			nlog(LOG_DEBUG, "nwamd_handle_if_state_event: "
1125			    "refreshing %s as we may have other "
1126			    "DHCP information", NET_LOC_FMRI);
1127			(void) smf_restore_instance(NET_LOC_FMRI);
1128			if (smf_refresh_instance(NET_LOC_FMRI) != 0) {
1129				nlog(LOG_ERR,
1130				    "nwamd_ncu_handle_if_state_"
1131				    "event: refresh of %s "
1132				    "failed", NET_LOC_FMRI);
1133			}
1134
1135		} else if (state == NWAM_STATE_ONLINE ||
1136		    state == NWAM_STATE_OFFLINE_TO_ONLINE) {
1137			/*
1138			 * Address has been removed.  Only pay attention to
1139			 * disappearing addresses if we are online or coming
1140			 * online.
1141			 *
1142			 * Undo whatever configuration is necessary.  Note
1143			 * that this may or may not cause the NCU to go down.
1144			 * We can get RTM_DELADDRs for duplicate addresses
1145			 * so deal with this seperately.
1146			 */
1147			nifa->configured = B_FALSE;
1148
1149			if (!static_addr && family == AF_INET) {
1150				u_if->nwamd_if_dhcp_configured = B_FALSE;
1151			} else if (!static_addr && family == AF_INET6) {
1152				/*
1153				 * The address is already gone.  When looking
1154				 * for the addrinfo (using aobjname in
1155				 * ipaddr), we found addrinfo for either one
1156				 * or both stateless and stateful.  Using the
1157				 * flags we determined whether each was
1158				 * configured or not.  Update the flags here
1159				 * accordingly.
1160				 */
1161				u_if->nwamd_if_stateful_configured =
1162				    stateless_ai_found;
1163				u_if->nwamd_if_stateless_configured =
1164				    stateful_ai_found;
1165			}
1166
1167			if (flags & IFF_DUPLICATE) {
1168				nlog(LOG_INFO,
1169				    "nwamd_ncu_handle_if_state_event: "
1170				    "duplicate address detected on %s",
1171				    ncu->ncu_name);
1172				nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1173				    event->event_object,
1174				    NWAM_STATE_MAINTENANCE,
1175				    NWAM_AUX_STATE_IF_DUPLICATE_ADDR);
1176			} else {
1177				interface_ncu_down(ncu);
1178			}
1179		}
1180valid_done:
1181		ipadm_free_addr_info(ai);
1182	}
1183	nwamd_object_release(ncu_obj);
1184}
1185
1186void
1187nwamd_ncu_handle_if_action_event(nwamd_event_t event)
1188{
1189	nwamd_object_t ncu_obj;
1190
1191	nlog(LOG_DEBUG, "if action event %s",
1192	    event->event_object[0] == '\0' ? "n/a" : event->event_object);
1193
1194	ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object);
1195	if (ncu_obj == NULL) {
1196		nlog(LOG_ERR, "nwamd_ncu_handle_if_action_event: no object");
1197		nwamd_event_do_not_send(event);
1198		return;
1199	}
1200	nwamd_object_release(ncu_obj);
1201}
1202
1203/*
1204 * Remove the address in the given aobjname.  IPADM_OPT_RELEASE is specified
1205 * for a DHCP address and specifies that the DHCP lease should also be released.
1206 * ifname is only used for nlog().
1207 */
1208static void
1209nwamd_down_interface(const char *aobjname, ipadm_addr_type_t atype,
1210    const char *ifname)
1211{
1212	ipadm_status_t ipstatus;
1213	uint32_t rflags = (atype == IPADM_ADDR_DHCP ? IPADM_OPT_RELEASE : 0);
1214
1215	nlog(LOG_DEBUG, "nwamd_down_interface: %s [aobjname = %s]",
1216	    ifname, aobjname);
1217	if ((ipstatus = ipadm_delete_addr(ipadm_handle, aobjname,
1218	    IPADM_OPT_ACTIVE | rflags)) != IPADM_SUCCESS) {
1219		nlog(LOG_ERR, "nwamd_down_interface: "
1220		    "ipadm_delete_addr failed on %s: %s",
1221		    ifname, ipadm_status2str(ipstatus));
1222	}
1223}
1224
1225static void
1226unconfigure_addresses(nwamd_ncu_t *ncu, sa_family_t af)
1227{
1228	struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list;
1229
1230	for (nifap = nifa; nifap != NULL; nifap = nifap->next)
1231		if (af == AF_UNSPEC || nifap->family == af)
1232			nifap->configured = B_FALSE;
1233}
1234
1235static void
1236dhcp_release(const char *ifname)
1237{
1238	ipadm_addr_info_t *ainfo, *ainfop;
1239
1240	if (ipadm_addr_info(ipadm_handle, ifname, &ainfo, 0, 0)
1241	    != IPADM_SUCCESS)
1242		return;
1243
1244	for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) {
1245		if (ainfop->ia_atype == IPADM_ADDR_DHCP)
1246			nwamd_down_interface(ainfop->ia_aobjname,
1247			    ainfop->ia_atype, ifname);
1248	}
1249	ipadm_free_addr_info(ainfo);
1250}
1251
1252static void
1253nwamd_plumb_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af, boolean_t plumb)
1254{
1255	char *ifname = ncu->ncu_name;
1256	nwamd_if_t *u_if = &ncu->ncu_if;
1257	ipadm_status_t ipstatus;
1258
1259	nlog(LOG_DEBUG, "nwamd_plumb_unplumb_interface: %s %s %s",
1260	    (plumb ? "plumb" : "unplumb"), (af == AF_INET ? "IPv4" : "IPv6"),
1261	    ifname);
1262
1263	if (plumb) {
1264		ipstatus = ipadm_create_if(ipadm_handle, ifname, af,
1265		    IPADM_OPT_ACTIVE);
1266	} else {
1267		/* release DHCP address, if any */
1268		if (af == AF_INET)
1269			dhcp_release(ifname);
1270		ipstatus = ipadm_delete_if(ipadm_handle, ifname, af,
1271		    IPADM_OPT_ACTIVE);
1272	}
1273
1274	if (ipstatus != IPADM_SUCCESS) {
1275		if ((plumb && ipstatus != IPADM_IF_EXISTS) ||
1276		    (!plumb && ipstatus != IPADM_ENXIO)) {
1277			nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: "
1278			    "%s %s failed for %s: %s",
1279			    (plumb ? "plumb" : "unplumb"),
1280			    (af == AF_INET ? "IPv4" : "IPv6"),
1281			    ifname, ipadm_status2str(ipstatus));
1282		}
1283	}
1284
1285	/* Unset flags */
1286	if (!plumb) {
1287		unconfigure_addresses(ncu, af);
1288		switch (af) {
1289		case AF_INET:
1290			u_if->nwamd_if_dhcp_configured = B_FALSE;
1291			break;
1292		case AF_INET6:
1293			u_if->nwamd_if_stateful_configured = B_FALSE;
1294			u_if->nwamd_if_stateless_configured = B_FALSE;
1295			break;
1296		}
1297	}
1298}
1299
1300void
1301nwamd_plumb_interface(nwamd_ncu_t *ncu, sa_family_t af)
1302{
1303	/*
1304	 * We get all posssible privs by calling nwamd_deescalate().  During
1305	 * startup opening /dev/dld (data link management) needs all privs
1306	 * because we don't have access to /etc/security/device_policy yet.
1307	 */
1308	nwamd_escalate();
1309	nwamd_plumb_unplumb_interface(ncu, af, B_TRUE);
1310	nwamd_deescalate();
1311}
1312
1313void
1314nwamd_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af)
1315{
1316	nwamd_plumb_unplumb_interface(ncu, af, B_FALSE);
1317}
1318
1319static void *
1320start_dhcp_thread(void *arg)
1321{
1322	struct nwamd_dhcp_thread_arg *thread_arg = arg;
1323	nwamd_object_t ncu_obj;
1324	dhcp_ipc_type_t type;
1325	char *name;
1326	ipadm_addrobj_t ipaddr;
1327	ipadm_status_t ipstatus;
1328	int retries = 0;
1329
1330	name = thread_arg->name;
1331	type = thread_arg->type;
1332	ipaddr = thread_arg->ipaddr;
1333
1334retry:
1335	/* Make sure the NCU is in appropriate state for DHCP command */
1336	ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE, name);
1337	if (ncu_obj == NULL) {
1338		nlog(LOG_ERR, "start_dhcp: no IP object %s", name);
1339		return (NULL);
1340	}
1341
1342	if (ncu_obj->nwamd_object_state != NWAM_STATE_OFFLINE_TO_ONLINE &&
1343	    ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
1344		nlog(LOG_INFO, "start_dhcp: IP NCU %s is in invalid state "
1345		    "for DHCP command", ncu_obj->nwamd_object_name);
1346		nwamd_object_release(ncu_obj);
1347		return (NULL);
1348	}
1349	nwamd_object_release(ncu_obj);
1350
1351	switch (type) {
1352	case DHCP_INFORM:
1353	{
1354		char aobjname[IPADM_AOBJSIZ];
1355
1356		if ((ipstatus = ipadm_get_aobjname(ipaddr, aobjname,
1357		    sizeof (aobjname))) != IPADM_SUCCESS) {
1358			nlog(LOG_ERR, "start_dhcp: "
1359			    "ipadm_get_aobjname failed for %s: %s",
1360			    name, ipadm_status2str(ipstatus));
1361			goto done;
1362		}
1363		ipstatus = ipadm_refresh_addr(ipadm_handle, aobjname,
1364		    IPADM_OPT_ACTIVE | IPADM_OPT_INFORM);
1365		break;
1366	}
1367	case DHCP_START:
1368		ipstatus = ipadm_create_addr(ipadm_handle, ipaddr,
1369		    IPADM_OPT_ACTIVE);
1370		break;
1371	default:
1372		nlog(LOG_ERR, "start_dhcp: invalid dhcp_ipc_type_t: %d", type);
1373		goto done;
1374	}
1375
1376	if (ipstatus == IPADM_DHCP_IPC_TIMEOUT) {
1377		/*
1378		 * DHCP timed out: for DHCP_START requests, change state for
1379		 * this NCU and euqueue event to check NCU priority-groups;
1380		 * for DHCP_INFORM requests, nothing to do.
1381		 */
1382		if (type == DHCP_START) {
1383			char *object_name;
1384
1385			nlog(LOG_INFO,
1386			    "start_dhcp: DHCP_START timed out for %s", name);
1387
1388			if (nwam_ncu_name_to_typed_name(name,
1389			    NWAM_NCU_TYPE_INTERFACE, &object_name)
1390			    != NWAM_SUCCESS) {
1391				nlog(LOG_ERR, "start_dhcp: "
1392				    "nwam_ncu_name_to_typed_name failed for %s",
1393				    name);
1394				goto done;
1395			}
1396			nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1397			    object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
1398			    NWAM_AUX_STATE_IF_DHCP_TIMED_OUT);
1399			nwamd_create_ncu_check_event(0);
1400			free(object_name);
1401		} else {
1402			nlog(LOG_INFO,
1403			    "start_dhcp: DHCP_INFORM timed out for %s", name);
1404		}
1405
1406	} else if ((ipstatus == IPADM_DHCP_IPC_ERROR ||
1407	    ipstatus == IPADM_IPC_ERROR) && retries++ < NWAMD_DHCP_RETRIES) {
1408		/*
1409		 * Retry DHCP request as we may have been unplumbing as part
1410		 * of the configuration phase.
1411		 */
1412		nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr on %s returned: %s, "
1413		    "retrying in %d sec",
1414		    (type == DHCP_START ? "create" : "refresh"), name,
1415		    ipadm_status2str(ipstatus), NWAMD_DHCP_RETRY_WAIT_TIME);
1416		(void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME);
1417		goto retry;
1418
1419	} else if (ipstatus != IPADM_SUCCESS) {
1420		nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr failed for %s: %s",
1421		    (type == DHCP_START ? "create" : "refresh"), name,
1422		    ipadm_status2str(ipstatus));
1423	}
1424
1425done:
1426	free(name);
1427	free(arg);
1428	return (NULL);
1429}
1430
1431static void
1432nwamd_dhcp(const char *ifname, ipadm_addrobj_t ipaddr, dhcp_ipc_type_t cmd)
1433{
1434	struct nwamd_dhcp_thread_arg *arg;
1435	pthread_attr_t attr;
1436
1437	nlog(LOG_DEBUG, "nwamd_dhcp: starting DHCP %s thread for %s",
1438	    dhcp_ipc_type_to_string(cmd), ifname);
1439
1440	arg = malloc(sizeof (*arg));
1441	if (arg == NULL) {
1442		nlog(LOG_ERR, "nwamd_dhcp: error allocating memory for "
1443		    "dhcp request");
1444		return;
1445	}
1446
1447	arg->name = strdup(ifname);
1448	arg->type = cmd;
1449	arg->ipaddr = ipaddr;
1450
1451	(void) pthread_attr_init(&attr);
1452	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1453	if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) {
1454		nlog(LOG_ERR, "nwamd_dhcp: cannot start dhcp thread");
1455		free(arg->name);
1456		free(arg);
1457		(void) pthread_attr_destroy(&attr);
1458		return;
1459	}
1460	(void) pthread_attr_destroy(&attr);
1461}
1462