1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <arpa/inet.h>
27 #include <assert.h>
28 #include <dhcpagent_ipc.h>
29 #include <dhcp_inittab.h>
30 #include <dhcp_symbol.h>
31 #include <dhcpagent_util.h>
32 #include <errno.h>
33 #include <execinfo.h>
34 #include <libnwam.h>
35 #include <stdlib.h>
36 #include <strings.h>
37 #include <ucontext.h>
38 #include <unistd.h>
39 #include <libscf.h>
40
41 #include "conditions.h"
42 #include "events.h"
43 #include "ncp.h"
44 #include "ncu.h"
45 #include "objects.h"
46 #include "util.h"
47
48 /*
49 * ncu_ip.c - contains routines that are IP interface-specific for NCUs.
50 */
51
52 #define STATELESS_RUNNING (IFF_RUNNING | IFF_UP | IFF_ADDRCONF)
53 #define DHCP_RUNNING (IFF_RUNNING | IFF_UP | IFF_DHCPRUNNING)
54
55 static void nwamd_dhcp(const char *, ipadm_addrobj_t, dhcp_ipc_type_t);
56 static void nwamd_down_interface(const char *, ipadm_addr_type_t, const char *);
57 static boolean_t stateless_running(const nwamd_ncu_t *);
58
59 /*
60 * Given a sockaddr representation of an IPv4 or IPv6 address returns the
61 * string representation. Note that 'sockaddr' should point at the correct
62 * sockaddr structure for the address family (sockaddr_in for AF_INET or
63 * sockaddr_in6 for AF_INET6) or alternatively at a sockaddr_storage
64 * structure.
65 */
66 static const char *
nwamd_sockaddr2str(const struct sockaddr * addr,char * str,size_t len)67 nwamd_sockaddr2str(const struct sockaddr *addr, char *str, size_t len)
68 {
69 struct sockaddr_in *sin;
70 struct sockaddr_in6 *sin6;
71 const char *straddr;
72
73 if (addr == NULL)
74 return (NULL);
75
76 if (addr->sa_family == AF_INET) {
77 /* LINTED E_BAD_PTR_CAST_ALIGN */
78 sin = (struct sockaddr_in *)addr;
79 straddr = inet_ntop(AF_INET, (void *)&sin->sin_addr, str, len);
80 } else if (addr->sa_family == AF_INET6) {
81 /* LINTED E_BAD_PTR_CAST_ALIGN */
82 sin6 = (struct sockaddr_in6 *)addr;
83 straddr = inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, str,
84 len);
85 } else {
86 errno = EINVAL;
87 return (NULL);
88 }
89 return (straddr != NULL ? str : NULL);
90 }
91
92 void
nwamd_propogate_link_up_down_to_ip(const char * linkname,boolean_t up)93 nwamd_propogate_link_up_down_to_ip(const char *linkname, boolean_t up)
94 {
95 nwamd_object_t ip_ncu = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE,
96 linkname);
97 nwamd_ncu_t *ncu;
98
99 if (ip_ncu == NULL) {
100 nlog(LOG_DEBUG, "nwamd_propogate_link_up_down_to_ip: no IP NCU "
101 "for link %s, cannot propogate %s event", linkname,
102 up ? "up" : "down");
103 return;
104 }
105 ncu = ip_ncu->nwamd_object_data;
106
107 if (ncu->ncu_enabled) {
108 if (ip_ncu->nwamd_object_aux_state ==
109 NWAM_AUX_STATE_UNINITIALIZED) {
110 nlog(LOG_DEBUG,
111 "nwamd_propogate_link_up_down_to_ip: will not "
112 "propogate link %s event as IP NCU %s is being "
113 "removed", up ? "up" : "down", linkname);
114 } else {
115 nlog(LOG_DEBUG,
116 "nwamd_propogate_link_up_down_to_ip: propogating "
117 "link %s event to interface %s",
118 up ? "up" : "down", linkname);
119 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
120 ip_ncu->nwamd_object_name,
121 up ?
122 NWAM_STATE_OFFLINE_TO_ONLINE :
123 NWAM_STATE_ONLINE_TO_OFFLINE,
124 up ? NWAM_AUX_STATE_INITIALIZED :
125 NWAM_AUX_STATE_CONDITIONS_NOT_MET);
126 }
127 } else {
128 nlog(LOG_DEBUG,
129 "nwamd_propogate_link_up_down_to_ip: not propogating "
130 "link %s event to interface %s, IP NCU is disabled",
131 up ? "up" : "down", linkname);
132 }
133 nwamd_object_release(ip_ncu);
134 }
135
136 /*
137 * Returns the value associated with the given symbol for the given
138 * interface. The interface may be NULL, in which case the primary
139 * interface is used.
140 * This function substitutes the need to call dhcpinfo(1), thus it is
141 * very similar to the implementation of dhcpinfo(1).
142 * When multiple values need to be returned (e.g., nameservers), they
143 * are separated by a space ' '.
144 */
145 char *
nwamd_get_dhcpinfo_data(const char * sym_name,char * ifname)146 nwamd_get_dhcpinfo_data(const char *sym_name, char *ifname)
147 {
148 dhcp_symbol_t *entry;
149 dhcp_optnum_t optnum;
150 dhcp_ipc_request_t *request = NULL;
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(5) */
163 entry = inittab_getbyname(ITAB_CAT_SITE | ITAB_CAT_STANDARD |
164 ITAB_CAT_VENDOR | ITAB_CAT_FIELD, ITAB_CONS_INFO, sym_name);
165
166 if (entry == NULL) {
167 (void) snprintf(errmsg, LINE_MAX, "unknown identifier: %s",
168 sym_name);
169 goto fail;
170 }
171
172 /* allocate request */
173 optnum.code = entry->ds_code;
174 optnum.category = entry->ds_category;
175 optnum.size = entry->ds_max * inittab_type_to_size(entry);
176 request = dhcp_ipc_alloc_request(DHCP_GET_TAG, ifname, &optnum,
177 sizeof (dhcp_optnum_t), DHCP_TYPE_OPTNUM);
178 if (request == NULL) {
179 (void) snprintf(errmsg, LINE_MAX, "failed dhcp alloc request");
180 goto fail;
181 }
182
183 /* make the request */
184 err = dhcp_ipc_make_request(request, &reply, DHCP_IPC_WAIT_DEFAULT);
185 if (err != 0 || reply->return_code != 0) {
186 (void) snprintf(errmsg, LINE_MAX, "%s",
187 dhcp_ipc_strerror(err == 0 ? reply->return_code : err));
188 }
189
190 /* get data from the reply */
191 opt = dhcp_ipc_get_data(reply, &opt_len, NULL);
192 if (opt_len == 0) {
193 (void) snprintf(errmsg, LINE_MAX, "invalid data");
194 goto fail;
195 }
196
197 /* check protocol error */
198 if (opt_len < 2 || (opt_len -2 != opt->len)) {
199 (void) snprintf(errmsg, LINE_MAX, "data length mismatch");
200 goto fail;
201 }
202 opt_len -= 2;
203
204 /* decode the data into ascii */
205 value = inittab_decode(entry, opt->value, opt_len, B_TRUE);
206 if (value == NULL) {
207 (void) snprintf(errmsg, LINE_MAX, "cannot decode reply");
208 goto fail;
209 }
210
211 free(request);
212 free(reply);
213 return (value);
214
215 fail:
216 nlog(LOG_DEBUG, "get_dhcpinfo_data() failed: %s", errmsg);
217 free(request);
218 free(reply);
219 return (NULL);
220 }
221
222 void
nwamd_add_default_routes(nwamd_ncu_t * ncu)223 nwamd_add_default_routes(nwamd_ncu_t *ncu)
224 {
225 nwamd_if_t *nif = &ncu->ncu_if;
226 char str[INET6_ADDRSTRLEN];
227
228 if (nif->nwamd_if_ipv4 && nif->nwamd_if_ipv4_default_route_set) {
229 struct sockaddr_in v4dest, v4mask;
230
231 v4dest.sin_addr.s_addr = htonl(INADDR_ANY);
232 v4dest.sin_family = AF_INET;
233
234 v4mask.sin_addr.s_addr = 0;
235 v4mask.sin_family = AF_INET;
236
237 nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default "
238 "route %s", nwamd_sockaddr2str((struct sockaddr *)
239 &nif->nwamd_if_ipv4_default_route, str,
240 sizeof (str)));
241 nwamd_add_route((struct sockaddr *)&v4dest,
242 (struct sockaddr *)&v4mask,
243 (struct sockaddr *)&nif->nwamd_if_ipv4_default_route,
244 ncu->ncu_name);
245 }
246
247 if (nif->nwamd_if_ipv6 && nif->nwamd_if_ipv6_default_route_set) {
248 struct sockaddr_in6 v6dest, v6mask;
249
250 (void) bzero(&v6dest, sizeof (struct sockaddr_in6));
251 v6dest.sin6_family = AF_INET6;
252
253 (void) bzero(&v6mask, sizeof (struct sockaddr_in6));
254 v6mask.sin6_family = AF_INET6;
255
256 nlog(LOG_DEBUG, "nwamd_add_default_routes: adding default "
257 "route %s", nwamd_sockaddr2str((struct sockaddr *)
258 &nif->nwamd_if_ipv6_default_route, str,
259 sizeof (str)));
260 nwamd_add_route((struct sockaddr *)&v6dest,
261 (struct sockaddr *)&v6mask,
262 (struct sockaddr *)&nif->nwamd_if_ipv6_default_route,
263 ncu->ncu_name);
264 }
265 }
266
267 /*
268 * Returns the nwamd_if_address structure for the given static address,
269 * NULL if not found.
270 */
271 static struct nwamd_if_address *
find_static_address(const struct sockaddr_storage * addr,const nwamd_ncu_t * ncu)272 find_static_address(const struct sockaddr_storage *addr, const nwamd_ncu_t *ncu)
273 {
274 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list;
275 struct sockaddr_storage saddr;
276 char str[INET6_ADDRSTRLEN];
277
278 nlog(LOG_DEBUG, "find_static_address: %s",
279 nwamd_sockaddr2str((struct sockaddr *)addr, str, sizeof (str)));
280 for (nifap = nifa; nifap != NULL; nifap = nifap->next) {
281 if (nifap->ipaddr_atype != IPADM_ADDR_STATIC ||
282 ipadm_get_addr(nifap->ipaddr, &saddr) != IPADM_SUCCESS)
283 continue;
284
285 if (sockaddrcmp(addr, &saddr))
286 return (nifap);
287 }
288 return (NULL);
289 }
290
291 /*
292 * Returns the nwamd_if_address structure representing the non-static address
293 * in the NCU. For IPv6, both stateless and stateful (DHCPv6) share the same
294 * nwamd_if_address. Will only return the nwamd_if_address if the relevant
295 * address is configured (v4 DHCP, v6 either stateless or stateless) for the
296 * NCU. Returns NULL if the structure is not found.
297 */
298 static struct nwamd_if_address *
find_nonstatic_address(const nwamd_ncu_t * ncu,sa_family_t family)299 find_nonstatic_address(const nwamd_ncu_t *ncu, sa_family_t family)
300 {
301 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list;
302 const nwamd_if_t *u_if = &ncu->ncu_if;
303
304 nlog(LOG_DEBUG, "find_nonstatic_address for %s %s",
305 (family == AF_INET ? "IPv4" : "IPv6"), ncu->ncu_name);
306 for (nifap = nifa; nifap != NULL; nifap = nifap->next) {
307 if (nifap->ipaddr_atype == IPADM_ADDR_STATIC)
308 continue;
309
310 if (family == AF_INET) {
311 if (nifap->ipaddr_atype == IPADM_ADDR_DHCP &&
312 u_if->nwamd_if_dhcp_requested)
313 return (nifap);
314 } else if (family == AF_INET6) {
315 if (nifap->ipaddr_atype == IPADM_ADDR_IPV6_ADDRCONF &&
316 (u_if->nwamd_if_stateful_requested ||
317 u_if->nwamd_if_stateless_requested))
318 return (nifap);
319 }
320 }
321 return (NULL);
322 }
323
324 /*
325 * Returns the nwamd_if_address structure that configured the given address,
326 * NULL if not found.
327 */
328 static struct nwamd_if_address *
find_configured_address(const struct sockaddr_storage * addr,const nwamd_ncu_t * ncu)329 find_configured_address(const struct sockaddr_storage *addr,
330 const nwamd_ncu_t *ncu)
331 {
332 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list;
333 char str[INET6_ADDRSTRLEN];
334
335 nlog(LOG_DEBUG, "find_configured_address: %s",
336 nwamd_sockaddr2str((struct sockaddr *)addr, str, sizeof (str)));
337 for (nifap = nifa; nifap != NULL; nifap = nifap->next) {
338 if (sockaddrcmp(addr, &nifap->conf_addr) ||
339 sockaddrcmp(addr, &nifap->conf_stateless_addr))
340 return (nifap);
341 }
342 return (NULL);
343 }
344
345 /*
346 * Are one or more static addresses configured?
347 */
348 boolean_t
nwamd_static_addresses_configured(nwamd_ncu_t * ncu,sa_family_t family)349 nwamd_static_addresses_configured(nwamd_ncu_t *ncu, sa_family_t family)
350 {
351 struct nwamd_if_address *n;
352
353 for (n = ncu->ncu_if.nwamd_if_list; n != NULL; n = n->next) {
354 if (n->ipaddr_atype != IPADM_ADDR_STATIC)
355 continue;
356 if ((family == AF_UNSPEC || family == n->family) &&
357 n->configured)
358 return (B_TRUE);
359 }
360 nlog(LOG_DEBUG, "no static addresses configured for %s", ncu->ncu_name);
361 return (B_FALSE);
362 }
363
364 /*
365 * Is DHCP probably managing an address on this index. We decide that it is
366 * probably managing an address if there is an interface with IFF_DHCP set
367 * that isn't in our set of static addresses. Note that IFF_DHCP gets set
368 * on static addresses when we do a dhcp inform and if that list has changed
369 * recently then the result of this function could be erronous.
370 */
371 boolean_t
nwamd_dhcp_managing(int protocol,nwamd_ncu_t * ncu)372 nwamd_dhcp_managing(int protocol, nwamd_ncu_t *ncu)
373 {
374 struct sockaddr_storage addr;
375 uint64_t flags;
376 boolean_t rv = B_FALSE;
377 ipadm_addr_info_t *addrinfo, *a;
378 ipadm_status_t ipstatus;
379
380 if ((ipstatus = ipadm_addr_info(ipadm_handle, ncu->ncu_name, &addrinfo,
381 0, 0)) != IPADM_SUCCESS) {
382 nlog(LOG_ERR, "nwamd_dhcp_managing: "
383 "ipadm_addr_info failed for %s: %s",
384 ncu->ncu_name, ipadm_status2str(ipstatus));
385 return (B_FALSE);
386 }
387
388 for (a = addrinfo; a != NULL; a = IA_NEXT(a)) {
389 /*
390 * WARNING: This memcpy() assumes knowledge of the
391 * implementation of getifaddrs() and that it always
392 * uses sockaddr_storage as the backing store for
393 * address information, thus making it possible to
394 * copy the entire structure rather than do it on
395 * the size of the sockaddr according to family.
396 * This assumption is made elsewhere in this file.
397 */
398 (void) memcpy(&addr, a->ia_ifa.ifa_addr, sizeof (addr));
399
400 /* is this address an expected static one? */
401 if (find_static_address(&addr, ncu) != NULL)
402 continue;
403
404 /*
405 * For IPv4, DHCPRUNNING flag is set when dhcpagent is in
406 * the process of getting an address, but doesn't have one
407 * yet (interface has 0.0.0.0). For IPv6, DHCPRUNNING flag
408 * is set on the link-local address if trying to get a
409 * stateful address. In both cases, consider the interface
410 * as not being managed by DHCP and skip checking of flags.
411 */
412 if ((protocol == AF_INET &&
413 ((struct sockaddr_in *)&addr)->sin_addr.s_addr ==
414 INADDR_ANY) ||
415 (protocol == AF_INET6 &&
416 IN6_IS_ADDR_LINKLOCAL(
417 &((struct sockaddr_in6 *)&addr)->sin6_addr))) {
418 continue;
419 }
420
421 flags = a->ia_ifa.ifa_flags;
422 if (flags & IFF_DHCPRUNNING) {
423 /*
424 * If we get here we have an address that has the
425 * DHCP flag set and isn't an expected static address.
426 */
427 rv = B_TRUE;
428 break;
429 }
430 }
431
432 ipadm_free_addr_info(addrinfo);
433 return (rv);
434 }
435
436 /*
437 * Return B_TRUE if IPv4 is requested in the given NCU.
438 */
439 static boolean_t
nwamd_v4_requested(nwamd_ncu_t * ncu)440 nwamd_v4_requested(nwamd_ncu_t *ncu)
441 {
442 boolean_t anyv4_requested;
443 nwamd_if_t *u_if;
444
445 anyv4_requested = B_FALSE;
446 u_if = &ncu->ncu_if;
447 if (u_if->nwamd_if_dhcp_requested) {
448 anyv4_requested = B_TRUE;
449 } else {
450 struct nwamd_if_address *n;
451
452 for (n = u_if->nwamd_if_list; n != NULL; n = n->next) {
453 if (n->family == AF_INET &&
454 n->ipaddr_atype == IPADM_ADDR_STATIC)
455 break;
456 }
457 if (n != NULL)
458 anyv4_requested = B_TRUE;
459 }
460
461 return (anyv4_requested);
462 }
463
464 /*
465 * Returns B_TRUE if IPv6 is requested in the given NCU.
466 */
467 static boolean_t
nwamd_v6_requested(nwamd_ncu_t * ncu)468 nwamd_v6_requested(nwamd_ncu_t *ncu)
469 {
470 boolean_t anyv6_requested;
471 nwamd_if_t *u_if;
472
473 anyv6_requested = B_FALSE;
474 u_if = &ncu->ncu_if;
475 if (u_if->nwamd_if_stateful_requested ||
476 u_if->nwamd_if_stateless_requested) {
477 anyv6_requested = B_TRUE;
478 } else {
479 struct nwamd_if_address *n;
480
481 for (n = u_if->nwamd_if_list; n != NULL; n = n->next) {
482 if (n->family == AF_INET6 &&
483 n->ipaddr_atype == IPADM_ADDR_STATIC)
484 break;
485 }
486 if (n != NULL)
487 anyv6_requested = B_TRUE;
488 }
489
490 return (anyv6_requested);
491 }
492
493 /*
494 * Bring up the ncu if we have the right combination of requested configuration
495 * and actual configuration and up is true, or bring down the ncu if no
496 * addresses are configured, and up is false.
497 */
498 static void
interface_ncu_up_down(nwamd_ncu_t * ncu,boolean_t up)499 interface_ncu_up_down(nwamd_ncu_t *ncu, boolean_t up)
500 {
501 boolean_t ncu_online;
502 char *name;
503
504 assert(ncu->ncu_type == NWAM_NCU_TYPE_INTERFACE);
505
506 /*
507 * If V4 with or without V6 is configured then one of its interfaces
508 * needs to be up for the ncu to come online. If only V6 is requested
509 * then one of its interfaces needs to be up for the ncu to come online.
510 */
511 ncu_online = B_FALSE;
512 if (nwamd_v4_requested(ncu)) {
513 if (nwamd_dhcp_managing(AF_INET, ncu) ||
514 nwamd_static_addresses_configured(ncu, AF_INET))
515 ncu_online = B_TRUE;
516 } else if (nwamd_v6_requested(ncu)) {
517 if ((nwamd_dhcp_managing(AF_INET6, ncu) ||
518 stateless_running(ncu) ||
519 nwamd_static_addresses_configured(ncu, AF_INET6)))
520 ncu_online = B_TRUE;
521 }
522
523 if (nwam_ncu_name_to_typed_name(ncu->ncu_name, ncu->ncu_type, &name) !=
524 NWAM_SUCCESS) {
525 nlog(LOG_DEBUG, "interface_ncu_up_down: "
526 "nwam_ncu_name_to_typed_name failed");
527 return;
528 }
529 if (ncu_online && up) {
530 nlog(LOG_DEBUG, "interface_ncu_up_down: "
531 "bringing %s up", name);
532 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name,
533 NWAM_STATE_OFFLINE_TO_ONLINE, NWAM_AUX_STATE_UP);
534 } else if (!ncu_online && !up) {
535 nlog(LOG_DEBUG, "interface_ncu_up_down: "
536 "bringing %s down", name);
537 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU, name,
538 NWAM_STATE_ONLINE_TO_OFFLINE,
539 NWAM_AUX_STATE_DOWN);
540 }
541
542 free(name);
543 }
544
545 static void
interface_ncu_up(nwamd_ncu_t * ncu)546 interface_ncu_up(nwamd_ncu_t *ncu)
547 {
548 interface_ncu_up_down(ncu, B_TRUE);
549 }
550
551 static void
interface_ncu_down(nwamd_ncu_t * ncu)552 interface_ncu_down(nwamd_ncu_t *ncu)
553 {
554 interface_ncu_up_down(ncu, B_FALSE);
555 }
556
557 static boolean_t
stateless_running(const nwamd_ncu_t * ncu)558 stateless_running(const nwamd_ncu_t *ncu)
559 {
560 ipadm_addr_info_t *ainfo, *ainfop;
561 ipadm_status_t ipstatus;
562 boolean_t rv = B_FALSE;
563 uint64_t flags;
564
565 if ((ipstatus = ipadm_addr_info(ipadm_handle, ncu->ncu_name, &ainfo,
566 0, 0)) != IPADM_SUCCESS) {
567 nlog(LOG_ERR, "stateless_running: "
568 "ipadm_addr_info failed for %s: %s",
569 ncu->ncu_name, ipadm_status2str(ipstatus));
570 return (B_FALSE);
571 }
572
573 for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) {
574 if (ainfop->ia_ifa.ifa_addr->sa_family != AF_INET6)
575 continue;
576 flags = ainfop->ia_ifa.ifa_flags;
577 if (flags & STATELESS_RUNNING) {
578 rv = B_TRUE;
579 break;
580 }
581 }
582 ipadm_free_addr_info(ainfo);
583 return (rv);
584 }
585
586 /*
587 * Returns the addrinfo associated with the given address. There is always
588 * only one addrinfo for each address.
589 */
590 static boolean_t
addrinfo_for_addr(const struct sockaddr_storage * caddr,const char * ifname,ipadm_addr_info_t ** ainfo)591 addrinfo_for_addr(const struct sockaddr_storage *caddr, const char *ifname,
592 ipadm_addr_info_t **ainfo)
593 {
594 ipadm_addr_info_t *addrinfo, *ainfop, *last = NULL;
595 ipadm_status_t ipstatus;
596
597 ipstatus = ipadm_addr_info(ipadm_handle, ifname, &addrinfo, 0, 0);
598 if (ipstatus != IPADM_SUCCESS) {
599 nlog(LOG_INFO, "addrinfo_for_addr: "
600 "ipadm_addr_info failed for %s: %s",
601 ifname, ipadm_status2str(ipstatus));
602 return (B_FALSE);
603 }
604
605 *ainfo = NULL;
606 for (ainfop = addrinfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) {
607 struct sockaddr_storage addr;
608
609 (void) memcpy(&addr, ainfop->ia_ifa.ifa_addr, sizeof (addr));
610 /*
611 * If addresses match, rearrange pointers so that addrinfo
612 * does not contain a, and return a.
613 */
614 if (sockaddrcmp(&addr, caddr)) {
615 if (last != NULL)
616 last->ia_ifa.ifa_next = ainfop->ia_ifa.ifa_next;
617 else
618 addrinfo = IA_NEXT(ainfop);
619
620 ainfop->ia_ifa.ifa_next = NULL;
621 *ainfo = ainfop;
622 break;
623 }
624 last = ainfop;
625 }
626 ipadm_free_addr_info(addrinfo);
627 return (*ainfo == NULL ? B_FALSE : B_TRUE);
628 }
629
630 /*
631 * Returns B_TRUE if the addrinfo associated with the given ipaddr using its
632 * aobjname is found. An addrinfo list is created and returned in ainfo.
633 * Stateless and stateful IPv6 addrinfo have the same aobjname, thus the need
634 * to create a list of addrinfo.
635 */
636 static boolean_t
addrinfo_for_ipaddr(ipadm_addrobj_t ipaddr,const char * ifname,ipadm_addr_info_t ** ainfo)637 addrinfo_for_ipaddr(ipadm_addrobj_t ipaddr, const char *ifname,
638 ipadm_addr_info_t **ainfo)
639 {
640 char aobjname[IPADM_AOBJSIZ];
641 ipadm_addr_info_t *addrinfo, *ainfop;
642 ipadm_addr_info_t *last = NULL;
643 ipadm_status_t ipstatus;
644
645 ipstatus = ipadm_get_aobjname(ipaddr, aobjname, sizeof (aobjname));
646 if (ipstatus != IPADM_SUCCESS)
647 return (B_FALSE);
648
649 ipstatus = ipadm_addr_info(ipadm_handle, ifname, &addrinfo, 0, 0);
650 if (ipstatus != IPADM_SUCCESS) {
651 nlog(LOG_INFO, "addrinfo_for_ipaddr: "
652 "ipadm_addr_info failed for %s: %s",
653 ifname, ipadm_status2str(ipstatus));
654 return (B_FALSE);
655 }
656
657 *ainfo = NULL;
658 ainfop = addrinfo;
659 while (ainfop != NULL) {
660 /* If aobjnames match, rearrange pointers to create new list */
661 if (strcmp(ainfop->ia_aobjname, aobjname) == 0) {
662 ipadm_addr_info_t *match = ainfop;
663
664 ainfop = IA_NEXT(ainfop); /* move iterator */
665 if (last != NULL)
666 last->ia_ifa.ifa_next = match->ia_ifa.ifa_next;
667 else
668 addrinfo = ainfop;
669 if (*ainfo == NULL)
670 match->ia_ifa.ifa_next = NULL;
671 else
672 match->ia_ifa.ifa_next = &(*ainfo)->ia_ifa;
673 *ainfo = match;
674 } else {
675 last = ainfop;
676 ainfop = IA_NEXT(ainfop);
677 }
678 }
679 ipadm_free_addr_info(addrinfo);
680 return (*ainfo == NULL ? B_FALSE : B_TRUE);
681 }
682
683 /*
684 * Add the address provided in the nwamd_if_address. If DHCP is required,
685 * start DHCP. If a static address is configured, create the address; then do
686 * a DHCP_INFORM (in a separate thread) to get other networking configuration
687 * parameters. RTM_NEWADDRs - translated into IF_STATE events - will then
688 * finish the job of bringing the NCU online.
689 */
690 static boolean_t
add_ip_address(const char * ifname,const struct nwamd_if_address * nifa,boolean_t * do_inform)691 add_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 */
780 void
nwamd_configure_interface_addresses(nwamd_ncu_t * ncu)781 nwamd_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 */
805 void
nwamd_ncu_handle_if_state_event(nwamd_event_t event)806 nwamd_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 = B_FALSE, 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 v4dhcp_running = B_FALSE;
883 v6dhcp_running = B_FALSE;
884 stateless_running = B_FALSE;
885
886 nlog(LOG_DEBUG,
887 "nwamd_ncu_handle_if_state_event: addr %s %s",
888 nwamd_sockaddr2str((struct sockaddr *)addr, addrstr,
889 sizeof (addrstr)), addr_added ? "added" : "removed");
890
891 /*
892 * Need to get flags for this interface. Get the addrinfo for
893 * the address that generated this IF_STATE event.
894 */
895 if (addr_added) {
896 /*
897 * Address was added. Find the addrinfo for this
898 * address and the nwamd_if_address corresponding to
899 * this address.
900 */
901 if (!addrinfo_for_addr(addr, ncu->ncu_name, &ai)) {
902 nlog(LOG_ERR,
903 "nwamd_ncu_handle_if_state_event: "
904 "addrinfo doesn't exist for %s", addrstr);
905 nwamd_event_do_not_send(event);
906 goto valid_done;
907 }
908 addrinfo = ai;
909 flags = addrinfo->ia_ifa.ifa_flags;
910 (void) memcpy(&ai_addr, addrinfo->ia_ifa.ifa_addr,
911 sizeof (ai_addr));
912 aip = &ai_addr;
913
914 if (addrinfo->ia_atype == IPADM_ADDR_IPV6_ADDRCONF ||
915 addrinfo->ia_atype == IPADM_ADDR_DHCP)
916 nifa = find_nonstatic_address(ncu, family);
917 else if (addrinfo->ia_atype == IPADM_ADDR_STATIC)
918 nifa = find_static_address(addr, ncu);
919
920 /*
921 * If nwamd_if_address is not found, then this address
922 * isn't one that nwamd created. Remove it.
923 */
924 if (nifa == NULL) {
925 nlog(LOG_ERR,
926 "nwamd_ncu_handle_if_state_event: "
927 "address %s not managed by nwam added, "
928 "removing it", addrstr);
929 nwamd_down_interface(addrinfo->ia_aobjname,
930 addrinfo->ia_atype, ncu->ncu_name);
931 nwamd_event_do_not_send(event);
932 goto valid_done;
933 }
934
935 /* check flags to determine how intf is configured */
936 stateless_running = (family == AF_INET6) &&
937 ((flags & STATELESS_RUNNING) == STATELESS_RUNNING);
938 v4dhcp_running = (family == AF_INET) &&
939 ((flags & DHCP_RUNNING) == DHCP_RUNNING);
940 v6dhcp_running = (family == AF_INET6) &&
941 ((flags & DHCP_RUNNING) == DHCP_RUNNING);
942 static_addr = (addrinfo->ia_atype == IPADM_ADDR_STATIC);
943
944 /* copy the configured address into nwamd_if_address */
945 if (stateless_running) {
946 (void) memcpy(&nifa->conf_stateless_addr,
947 addrinfo->ia_ifa.ifa_addr,
948 sizeof (struct sockaddr_storage));
949 } else {
950 (void) memcpy(&nifa->conf_addr,
951 addrinfo->ia_ifa.ifa_addr,
952 sizeof (struct sockaddr_storage));
953 }
954
955 } else {
956 /*
957 * Address was removed. Find the nwamd_if_address
958 * that configured this address.
959 */
960 nifa = find_configured_address(addr, ncu);
961 if (nifa == NULL) {
962 nlog(LOG_ERR,
963 "nwamd_ncu_handle_if_state_event: "
964 "address %s not managed by nwam removed, "
965 "nothing to do", addrstr);
966 nwamd_event_do_not_send(event);
967 goto valid_done;
968 }
969
970 if (addrinfo_for_ipaddr(nifa->ipaddr, ncu->ncu_name,
971 &ai)) {
972 ipadm_addr_info_t *a;
973 for (a = ai; a != NULL; a = IA_NEXT(a)) {
974 struct sockaddr_storage stor;
975
976 (void) memcpy(&stor, a->ia_ifa.ifa_addr,
977 sizeof (stor));
978 /*
979 * Since multiple addrinfo can have
980 * the same ipaddr, find the one for
981 * the address that generated this
982 * state event.
983 */
984 if (sockaddrcmp(addr, &stor)) {
985 flags = a->ia_ifa.ifa_flags;
986 (void) memcpy(&ai_addr,
987 a->ia_ifa.ifa_addr,
988 sizeof (ai_addr));
989 aip = &ai_addr;
990 addrinfo = a;
991 }
992 /*
993 * Stateful and stateless IPv6
994 * addrinfo have the same aobjname.
995 * Use the flags to determine which
996 * address is present in the system.
997 */
998 if (family == AF_INET6) {
999 stateless_ai_found =
1000 (a->ia_ifa.ifa_flags &
1001 STATELESS_RUNNING);
1002 stateful_ai_found =
1003 (a->ia_ifa.ifa_flags &
1004 DHCP_RUNNING);
1005 }
1006 }
1007 }
1008 }
1009
1010 /* Set the flags in the event for listeners */
1011 evm->nwe_data.nwe_if_state.nwe_flags = flags;
1012
1013 if (family == AF_INET && !addr_added) {
1014 /*
1015 * Check for failure due to CR 6745448: if we get a
1016 * report that an address has been deleted, then check
1017 * for interface up, datalink down, and actual address
1018 * non-zero. If that combination is seen, then this is
1019 * a DHCP cached lease, and we need to remove it from
1020 * the system, or it'll louse up the kernel routes
1021 * (which aren't smart enough to avoid dead
1022 * interfaces).
1023 */
1024 if (((struct sockaddr_in *)addr)->sin_addr.s_addr
1025 == INADDR_ANY && aip != 0) {
1026 struct sockaddr_in *a;
1027 char astr[INET6_ADDRSTRLEN];
1028 a = (struct sockaddr_in *)aip;
1029
1030 if ((flags & IFF_UP) &&
1031 !(flags & IFF_RUNNING) &&
1032 a->sin_addr.s_addr != INADDR_ANY) {
1033 nlog(LOG_DEBUG,
1034 "nwamd_ncu_handle_if_state_event: "
1035 "bug workaround: clear out addr "
1036 "%s on %s", nwamd_sockaddr2str
1037 ((struct sockaddr *)a, astr,
1038 sizeof (astr)),
1039 ncu->ncu_name);
1040 nwamd_down_interface(
1041 addrinfo->ia_aobjname,
1042 IPADM_ADDR_DHCP, ncu->ncu_name);
1043 }
1044 goto valid_done;
1045 }
1046 }
1047
1048 /*
1049 * If we received an RTM_NEWADDR and the IFF_UP flags has not
1050 * been set, ignore this IF_STATE event. Once the IFF_UP flag
1051 * is set, we'll get another RTM_NEWADDR message.
1052 */
1053 if (addr_added & !(flags & IFF_UP)) {
1054 nlog(LOG_INFO, "nwamd_ncu_handle_if_state_event: "
1055 "address %s added on %s without IFF_UP flag (%x), "
1056 "ignoring IF_STATE event",
1057 addrstr, ncu->ncu_name, flags);
1058 nwamd_event_do_not_send(event);
1059 goto valid_done;
1060 }
1061
1062 /*
1063 * Has the address really been removed? Sometimes spurious
1064 * RTM_DELADDRs are generated, so we need to ensure that
1065 * the address is really gone. If IFF_DUPLICATE is set,
1066 * we're getting the RTM_DELADDR due to DAD, so don't test
1067 * in that case.
1068 */
1069 if (!addr_added && !(flags & IFF_DUPLICATE)) {
1070 if (aip != 0 && sockaddrcmp(addr, aip)) {
1071 nlog(LOG_INFO,
1072 "nwamd_ncu_handle_if_state_event: "
1073 "address %s is not really gone from %s, "
1074 "ignoring IF_STATE event",
1075 addrstr, ncu->ncu_name);
1076 nwamd_event_do_not_send(event);
1077 goto valid_done;
1078 }
1079 }
1080
1081 if (addr_added) {
1082 /*
1083 * Address has been added.
1084 *
1085 * We need to make sure that we really want to keep
1086 * this address. There is a race where we requested an
1087 * address but by the time we got here we don't really
1088 * want it and need to remove it.
1089 *
1090 * Once we decide we want the address adjust the ncu
1091 * state accordingly. For example if this address is
1092 * enough move online.
1093 */
1094 if (u_if->nwamd_if_dhcp_requested && v4dhcp_running) {
1095 u_if->nwamd_if_dhcp_configured = B_TRUE;
1096 } else if (u_if->nwamd_if_stateful_requested &&
1097 v6dhcp_running) {
1098 u_if->nwamd_if_stateful_configured = B_TRUE;
1099 } else if (u_if->nwamd_if_stateless_requested &&
1100 stateless_running) {
1101 u_if->nwamd_if_stateless_configured = B_TRUE;
1102 } else if (!static_addr) {
1103 /*
1104 * This is something we didn't expect. Remove
1105 * the address.
1106 */
1107 nwamd_down_interface(addrinfo->ia_aobjname,
1108 addrinfo->ia_atype, ncu->ncu_name);
1109 nifa->configured = B_FALSE;
1110 goto valid_done;
1111 }
1112
1113 /*
1114 * The address looks valid so mark configured and
1115 * move online if we either have a v4 address if
1116 * v4 is configured or a v6 address if only v6 is
1117 * configured.
1118 */
1119 nifa->configured = B_TRUE;
1120 if (state != NWAM_STATE_ONLINE)
1121 interface_ncu_up(ncu);
1122
1123 /*
1124 * Refresh network/location since we may also have other
1125 * DHCP information. We might have to restore it first
1126 * in case it is in maintenance.
1127 */
1128 nlog(LOG_DEBUG, "nwamd_handle_if_state_event: "
1129 "refreshing %s as we may have other "
1130 "DHCP information", NET_LOC_FMRI);
1131 (void) smf_restore_instance(NET_LOC_FMRI);
1132 if (smf_refresh_instance(NET_LOC_FMRI) != 0) {
1133 nlog(LOG_ERR,
1134 "nwamd_ncu_handle_if_state_"
1135 "event: refresh of %s "
1136 "failed", NET_LOC_FMRI);
1137 }
1138
1139 } else if (state == NWAM_STATE_ONLINE ||
1140 state == NWAM_STATE_OFFLINE_TO_ONLINE) {
1141 /*
1142 * Address has been removed. Only pay attention to
1143 * disappearing addresses if we are online or coming
1144 * online.
1145 *
1146 * Undo whatever configuration is necessary. Note
1147 * that this may or may not cause the NCU to go down.
1148 * We can get RTM_DELADDRs for duplicate addresses
1149 * so deal with this seperately.
1150 */
1151 nifa->configured = B_FALSE;
1152
1153 if (!static_addr && family == AF_INET) {
1154 u_if->nwamd_if_dhcp_configured = B_FALSE;
1155 } else if (!static_addr && family == AF_INET6) {
1156 /*
1157 * The address is already gone. When looking
1158 * for the addrinfo (using aobjname in
1159 * ipaddr), we found addrinfo for either one
1160 * or both stateless and stateful. Using the
1161 * flags we determined whether each was
1162 * configured or not. Update the flags here
1163 * accordingly.
1164 */
1165 u_if->nwamd_if_stateful_configured =
1166 stateless_ai_found;
1167 u_if->nwamd_if_stateless_configured =
1168 stateful_ai_found;
1169 }
1170
1171 if (flags & IFF_DUPLICATE) {
1172 nlog(LOG_INFO,
1173 "nwamd_ncu_handle_if_state_event: "
1174 "duplicate address detected on %s",
1175 ncu->ncu_name);
1176 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1177 event->event_object,
1178 NWAM_STATE_MAINTENANCE,
1179 NWAM_AUX_STATE_IF_DUPLICATE_ADDR);
1180 } else {
1181 interface_ncu_down(ncu);
1182 }
1183 }
1184 valid_done:
1185 ipadm_free_addr_info(ai);
1186 }
1187 nwamd_object_release(ncu_obj);
1188 }
1189
1190 void
nwamd_ncu_handle_if_action_event(nwamd_event_t event)1191 nwamd_ncu_handle_if_action_event(nwamd_event_t event)
1192 {
1193 nwamd_object_t ncu_obj;
1194
1195 nlog(LOG_DEBUG, "if action event %s",
1196 event->event_object[0] == '\0' ? "n/a" : event->event_object);
1197
1198 ncu_obj = nwamd_object_find(NWAM_OBJECT_TYPE_NCU, event->event_object);
1199 if (ncu_obj == NULL) {
1200 nlog(LOG_ERR, "nwamd_ncu_handle_if_action_event: no object");
1201 nwamd_event_do_not_send(event);
1202 return;
1203 }
1204 nwamd_object_release(ncu_obj);
1205 }
1206
1207 /*
1208 * Remove the address in the given aobjname. IPADM_OPT_RELEASE is specified
1209 * for a DHCP address and specifies that the DHCP lease should also be released.
1210 * ifname is only used for nlog().
1211 */
1212 static void
nwamd_down_interface(const char * aobjname,ipadm_addr_type_t atype,const char * ifname)1213 nwamd_down_interface(const char *aobjname, ipadm_addr_type_t atype,
1214 const char *ifname)
1215 {
1216 ipadm_status_t ipstatus;
1217 uint32_t rflags = (atype == IPADM_ADDR_DHCP ? IPADM_OPT_RELEASE : 0);
1218
1219 nlog(LOG_DEBUG, "nwamd_down_interface: %s [aobjname = %s]",
1220 ifname, aobjname);
1221 if ((ipstatus = ipadm_delete_addr(ipadm_handle, aobjname,
1222 IPADM_OPT_ACTIVE | rflags)) != IPADM_SUCCESS) {
1223 nlog(LOG_ERR, "nwamd_down_interface: "
1224 "ipadm_delete_addr failed on %s: %s",
1225 ifname, ipadm_status2str(ipstatus));
1226 }
1227 }
1228
1229 static void
unconfigure_addresses(nwamd_ncu_t * ncu,sa_family_t af)1230 unconfigure_addresses(nwamd_ncu_t *ncu, sa_family_t af)
1231 {
1232 struct nwamd_if_address *nifap, *nifa = ncu->ncu_if.nwamd_if_list;
1233
1234 for (nifap = nifa; nifap != NULL; nifap = nifap->next)
1235 if (af == AF_UNSPEC || nifap->family == af)
1236 nifap->configured = B_FALSE;
1237 }
1238
1239 static void
dhcp_release(const char * ifname)1240 dhcp_release(const char *ifname)
1241 {
1242 ipadm_addr_info_t *ainfo, *ainfop;
1243
1244 if (ipadm_addr_info(ipadm_handle, ifname, &ainfo, 0, 0)
1245 != IPADM_SUCCESS)
1246 return;
1247
1248 for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) {
1249 if (ainfop->ia_atype == IPADM_ADDR_DHCP)
1250 nwamd_down_interface(ainfop->ia_aobjname,
1251 ainfop->ia_atype, ifname);
1252 }
1253 ipadm_free_addr_info(ainfo);
1254 }
1255
1256 static void
nwamd_plumb_unplumb_interface(nwamd_ncu_t * ncu,sa_family_t af,boolean_t plumb)1257 nwamd_plumb_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af, boolean_t plumb)
1258 {
1259 char *ifname = ncu->ncu_name;
1260 nwamd_if_t *u_if = &ncu->ncu_if;
1261 ipadm_status_t ipstatus;
1262
1263 nlog(LOG_DEBUG, "nwamd_plumb_unplumb_interface: %s %s %s",
1264 (plumb ? "plumb" : "unplumb"), (af == AF_INET ? "IPv4" : "IPv6"),
1265 ifname);
1266
1267 if (plumb) {
1268 ipstatus = ipadm_create_if(ipadm_handle, ifname, af,
1269 IPADM_OPT_ACTIVE);
1270 } else {
1271 /* release DHCP address, if any */
1272 if (af == AF_INET)
1273 dhcp_release(ifname);
1274 ipstatus = ipadm_delete_if(ipadm_handle, ifname, af,
1275 IPADM_OPT_ACTIVE);
1276 }
1277
1278 if (ipstatus != IPADM_SUCCESS) {
1279 if ((plumb && ipstatus != IPADM_IF_EXISTS) ||
1280 (!plumb && ipstatus != IPADM_ENXIO)) {
1281 nlog(LOG_ERR, "nwamd_plumb_unplumb_interface: "
1282 "%s %s failed for %s: %s",
1283 (plumb ? "plumb" : "unplumb"),
1284 (af == AF_INET ? "IPv4" : "IPv6"),
1285 ifname, ipadm_status2str(ipstatus));
1286 }
1287 }
1288
1289 /* Unset flags */
1290 if (!plumb) {
1291 unconfigure_addresses(ncu, af);
1292 switch (af) {
1293 case AF_INET:
1294 u_if->nwamd_if_dhcp_configured = B_FALSE;
1295 break;
1296 case AF_INET6:
1297 u_if->nwamd_if_stateful_configured = B_FALSE;
1298 u_if->nwamd_if_stateless_configured = B_FALSE;
1299 break;
1300 }
1301 }
1302 }
1303
1304 void
nwamd_plumb_interface(nwamd_ncu_t * ncu,sa_family_t af)1305 nwamd_plumb_interface(nwamd_ncu_t *ncu, sa_family_t af)
1306 {
1307 /*
1308 * We get all posssible privs by calling nwamd_deescalate(). During
1309 * startup opening /dev/dld (data link management) needs all privs
1310 * because we don't have access to /etc/security/device_policy yet.
1311 */
1312 nwamd_escalate();
1313 nwamd_plumb_unplumb_interface(ncu, af, B_TRUE);
1314 nwamd_deescalate();
1315 }
1316
1317 void
nwamd_unplumb_interface(nwamd_ncu_t * ncu,sa_family_t af)1318 nwamd_unplumb_interface(nwamd_ncu_t *ncu, sa_family_t af)
1319 {
1320 nwamd_plumb_unplumb_interface(ncu, af, B_FALSE);
1321 }
1322
1323 static void *
start_dhcp_thread(void * arg)1324 start_dhcp_thread(void *arg)
1325 {
1326 struct nwamd_dhcp_thread_arg *thread_arg = arg;
1327 nwamd_object_t ncu_obj;
1328 dhcp_ipc_type_t type;
1329 char *name;
1330 ipadm_addrobj_t ipaddr;
1331 ipadm_status_t ipstatus;
1332 int retries = 0;
1333
1334 name = thread_arg->name;
1335 type = thread_arg->type;
1336 ipaddr = thread_arg->ipaddr;
1337
1338 retry:
1339 /* Make sure the NCU is in appropriate state for DHCP command */
1340 ncu_obj = nwamd_ncu_object_find(NWAM_NCU_TYPE_INTERFACE, name);
1341 if (ncu_obj == NULL) {
1342 nlog(LOG_ERR, "start_dhcp: no IP object %s", name);
1343 return (NULL);
1344 }
1345
1346 if (ncu_obj->nwamd_object_state != NWAM_STATE_OFFLINE_TO_ONLINE &&
1347 ncu_obj->nwamd_object_state != NWAM_STATE_ONLINE) {
1348 nlog(LOG_INFO, "start_dhcp: IP NCU %s is in invalid state "
1349 "for DHCP command", ncu_obj->nwamd_object_name);
1350 nwamd_object_release(ncu_obj);
1351 return (NULL);
1352 }
1353 nwamd_object_release(ncu_obj);
1354
1355 switch (type) {
1356 case DHCP_INFORM:
1357 {
1358 char aobjname[IPADM_AOBJSIZ];
1359
1360 if ((ipstatus = ipadm_get_aobjname(ipaddr, aobjname,
1361 sizeof (aobjname))) != IPADM_SUCCESS) {
1362 nlog(LOG_ERR, "start_dhcp: "
1363 "ipadm_get_aobjname failed for %s: %s",
1364 name, ipadm_status2str(ipstatus));
1365 goto done;
1366 }
1367 ipstatus = ipadm_refresh_addr(ipadm_handle, aobjname,
1368 IPADM_OPT_ACTIVE | IPADM_OPT_INFORM);
1369 break;
1370 }
1371 case DHCP_START:
1372 ipstatus = ipadm_create_addr(ipadm_handle, ipaddr,
1373 IPADM_OPT_ACTIVE);
1374 break;
1375 default:
1376 nlog(LOG_ERR, "start_dhcp: invalid dhcp_ipc_type_t: %d", type);
1377 goto done;
1378 }
1379
1380 if (ipstatus == IPADM_DHCP_IPC_TIMEOUT) {
1381 /*
1382 * DHCP timed out: for DHCP_START requests, change state for
1383 * this NCU and euqueue event to check NCU priority-groups;
1384 * for DHCP_INFORM requests, nothing to do.
1385 */
1386 if (type == DHCP_START) {
1387 char *object_name;
1388
1389 nlog(LOG_INFO,
1390 "start_dhcp: DHCP_START timed out for %s", name);
1391
1392 if (nwam_ncu_name_to_typed_name(name,
1393 NWAM_NCU_TYPE_INTERFACE, &object_name)
1394 != NWAM_SUCCESS) {
1395 nlog(LOG_ERR, "start_dhcp: "
1396 "nwam_ncu_name_to_typed_name failed for %s",
1397 name);
1398 goto done;
1399 }
1400 nwamd_object_set_state(NWAM_OBJECT_TYPE_NCU,
1401 object_name, NWAM_STATE_OFFLINE_TO_ONLINE,
1402 NWAM_AUX_STATE_IF_DHCP_TIMED_OUT);
1403 nwamd_create_ncu_check_event(0);
1404 free(object_name);
1405 } else {
1406 nlog(LOG_INFO,
1407 "start_dhcp: DHCP_INFORM timed out for %s", name);
1408 }
1409
1410 } else if ((ipstatus == IPADM_DHCP_IPC_ERROR ||
1411 ipstatus == IPADM_IPC_ERROR) && retries++ < NWAMD_DHCP_RETRIES) {
1412 /*
1413 * Retry DHCP request as we may have been unplumbing as part
1414 * of the configuration phase.
1415 */
1416 nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr on %s returned: %s, "
1417 "retrying in %d sec",
1418 (type == DHCP_START ? "create" : "refresh"), name,
1419 ipadm_status2str(ipstatus), NWAMD_DHCP_RETRY_WAIT_TIME);
1420 (void) sleep(NWAMD_DHCP_RETRY_WAIT_TIME);
1421 goto retry;
1422
1423 } else if (ipstatus != IPADM_SUCCESS) {
1424 nlog(LOG_ERR, "start_dhcp: ipadm_%s_addr failed for %s: %s",
1425 (type == DHCP_START ? "create" : "refresh"), name,
1426 ipadm_status2str(ipstatus));
1427 }
1428
1429 done:
1430 free(name);
1431 free(arg);
1432 return (NULL);
1433 }
1434
1435 static void
nwamd_dhcp(const char * ifname,ipadm_addrobj_t ipaddr,dhcp_ipc_type_t cmd)1436 nwamd_dhcp(const char *ifname, ipadm_addrobj_t ipaddr, dhcp_ipc_type_t cmd)
1437 {
1438 struct nwamd_dhcp_thread_arg *arg;
1439 pthread_attr_t attr;
1440
1441 nlog(LOG_DEBUG, "nwamd_dhcp: starting DHCP %s thread for %s",
1442 dhcp_ipc_type_to_string(cmd), ifname);
1443
1444 arg = malloc(sizeof (*arg));
1445 if (arg == NULL) {
1446 nlog(LOG_ERR, "nwamd_dhcp: error allocating memory for "
1447 "dhcp request");
1448 return;
1449 }
1450
1451 arg->name = strdup(ifname);
1452 arg->type = cmd;
1453 arg->ipaddr = ipaddr;
1454
1455 (void) pthread_attr_init(&attr);
1456 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1457 if (pthread_create(NULL, &attr, start_dhcp_thread, arg) == -1) {
1458 nlog(LOG_ERR, "nwamd_dhcp: cannot start dhcp thread");
1459 free(arg->name);
1460 free(arg);
1461 (void) pthread_attr_destroy(&attr);
1462 return;
1463 }
1464 (void) pthread_attr_destroy(&attr);
1465 }
1466