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 * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
25 */
26
27 #include <errno.h>
28 #include <sys/sockio.h>
29 #include <sys/list.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <unistd.h>
33 #include <stropts.h>
34 #include <strings.h>
35 #include <libdlpi.h>
36 #include <libdllink.h>
37 #include <libinetutil.h>
38 #include <inet/ip.h>
39 #include <limits.h>
40 #include <zone.h>
41 #include <ipadm_ndpd.h>
42 #include <ipmp_query.h>
43 #include "libipadm_impl.h"
44
45 static ipadm_status_t i_ipadm_slifname_arp(char *, uint64_t, int);
46 static ipadm_status_t i_ipadm_slifname(ipadm_handle_t, char *, char *,
47 uint64_t, int, uint32_t);
48 static ipadm_status_t i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
49 sa_family_t);
50 static ipadm_status_t i_ipadm_persist_if(ipadm_handle_t, const char *,
51 sa_family_t, uint32_t);
52 static ipadm_status_t i_ipadm_allocate_ifinfo(ipadm_if_info_t **);
53 static ipadm_status_t i_ipadm_get_db_if(ipadm_handle_t, const char *,
54 nvlist_t **);
55 static ipadm_status_t i_ipadm_nvl2ifinfo(nvlist_t *, ipadm_if_info_t **);
56 static ipadm_status_t i_ipadm_fill_cmembers(char *, ipadm_ipmp_members_t *);
57 static ipadm_status_t i_ipadm_fill_pmembers(nvlist_t *, ipadm_ipmp_members_t *);
58 static ipadm_status_t i_ipadm_add_persistent_if_info(ipadm_if_info_t *,
59 ipadm_if_info_t *);
60 static void i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *);
61 static ipadm_status_t i_ipadm_persist_update_ipmp(ipadm_handle_t, const char *,
62 const char *,
63 ipadm_ipmp_op_t);
64 static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
65 const char *, uint32_t,
66 ipadm_ipmp_op_t);
67
68 /*
69 * Returns B_FALSE if the interface in `ifname' has at least one address that is
70 * IFF_UP in the addresses in `ifa'.
71 */
72 static boolean_t
i_ipadm_is_if_down(char * ifname,struct ifaddrs * ifa)73 i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
74 {
75 struct ifaddrs *ifap;
76 char cifname[LIFNAMSIZ];
77 char *sep;
78
79 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
80 (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
81 if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
82 *sep = '\0';
83 /*
84 * If this condition is true, there is at least one
85 * address that is IFF_UP. So, we need to return B_FALSE.
86 */
87 if (strcmp(cifname, ifname) == 0 &&
88 (ifap->ifa_flags & IFF_UP)) {
89 return (B_FALSE);
90 }
91 }
92 /* We did not find any IFF_UP addresses. */
93 return (B_TRUE);
94 }
95
96 /*
97 * Retrieves the information for the interface `ifname' from active
98 * config if `ifname' is specified and returns the result in the list `if_info'.
99 * Otherwise, it retrieves the information for all the interfaces in
100 * the active config and returns the result in the list `if_info'.
101 */
102 static ipadm_status_t
i_ipadm_active_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info,int64_t lifc_flags)103 i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname,
104 ipadm_if_info_t **if_info, int64_t lifc_flags)
105 {
106 struct lifreq *buf;
107 struct lifreq *lifrp;
108 struct lifreq lifrl;
109 ipadm_if_info_t *last = NULL;
110 ipadm_if_info_t *ifp;
111 int s;
112 int n;
113 int numifs;
114 ipadm_status_t status = IPADM_SUCCESS;
115
116 *if_info = NULL;
117 /*
118 * Get information for all interfaces.
119 */
120 if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
121 return (ipadm_errno2status(errno));
122
123 lifrp = buf;
124 for (n = 0; n < numifs; n++, lifrp++) {
125 /* Skip interfaces with logical num != 0 */
126 if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
127 continue;
128 /*
129 * Skip the current interface if a specific `ifname' has
130 * been requested and current interface does not match
131 * `ifname'.
132 */
133 if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
134 continue;
135 /*
136 * Check if the interface already exists in our list.
137 * If it already exists, we need to update its flags.
138 */
139 for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
140 if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
141 break;
142 }
143 if (ifp == NULL) {
144 if ((status =
145 i_ipadm_allocate_ifinfo(&ifp)) != IPADM_SUCCESS)
146 break;
147
148 (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
149 sizeof (ifp->ifi_name));
150 /* Update the `ifi_next' pointer for this new node */
151 if (*if_info == NULL)
152 *if_info = ifp;
153 else
154 last->ifi_next = ifp;
155 last = ifp;
156 }
157
158 /*
159 * Retrieve the flags for the interface by doing a
160 * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
161 */
162 (void) strlcpy(lifrl.lifr_name,
163 lifrp->lifr_name, sizeof (lifrl.lifr_name));
164 s = (lifrp->lifr_addr.ss_family == AF_INET) ?
165 iph->iph_sock : iph->iph_sock6;
166 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
167 continue;
168
169 /* a regular interface by default */
170 ifp->ifi_class = IPADM_IF_CLASS_REGULAR;
171
172 if (lifrl.lifr_flags & IFF_BROADCAST)
173 ifp->ifi_cflags |= IFIF_BROADCAST;
174 if (lifrl.lifr_flags & IFF_MULTICAST)
175 ifp->ifi_cflags |= IFIF_MULTICAST;
176 if (lifrl.lifr_flags & IFF_POINTOPOINT)
177 ifp->ifi_cflags |= IFIF_POINTOPOINT;
178 if (lifrl.lifr_flags & IFF_VIRTUAL) {
179 ifp->ifi_cflags |= IFIF_VIRTUAL;
180 ifp->ifi_class = IPADM_IF_CLASS_VIRTUAL;
181 }
182 if (lifrl.lifr_flags & IFF_IPMP) {
183 ifp->ifi_cflags |= IFIF_IPMP;
184 ifp->ifi_class = IPADM_IF_CLASS_IPMP;
185 }
186 if (lifrl.lifr_flags & IFF_STANDBY)
187 ifp->ifi_cflags |= IFIF_STANDBY;
188 if (lifrl.lifr_flags & IFF_INACTIVE)
189 ifp->ifi_cflags |= IFIF_INACTIVE;
190 if (lifrl.lifr_flags & IFF_VRRP)
191 ifp->ifi_cflags |= IFIF_VRRP;
192 if (lifrl.lifr_flags & IFF_NOACCEPT)
193 ifp->ifi_cflags |= IFIF_NOACCEPT;
194 if (lifrl.lifr_flags & IFF_IPV4)
195 ifp->ifi_cflags |= IFIF_IPV4;
196 if (lifrl.lifr_flags & IFF_IPV6)
197 ifp->ifi_cflags |= IFIF_IPV6;
198 if (lifrl.lifr_flags & IFF_L3PROTECT)
199 ifp->ifi_cflags |= IFIF_L3PROTECT;
200
201 /*
202 * Retrieve active IPMP members. This may fail in in.mpathd if
203 * the IPMP interface has just been created with no members.
204 * Hence, ignore errors, cmembers will just be empty.
205 */
206 if (ifp->ifi_class == IPADM_IF_CLASS_IPMP) {
207 if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifrl) == 0) {
208 (void) i_ipadm_fill_cmembers(
209 lifrl.lifr_groupname,
210 &ifp->ifi_ipmp_cmembers);
211 }
212 }
213 }
214 free(buf);
215 if (status != IPADM_SUCCESS) {
216 ipadm_free_if_info(*if_info);
217 *if_info = NULL;
218 }
219 return (status);
220 }
221
222 /*
223 * Returns the interface information for `ifname' in `if_info' from persistent
224 * config if `ifname' is non-null. Otherwise, it returns all the interfaces
225 * from persistent config in `if_info'.
226 */
227 static ipadm_status_t
i_ipadm_persist_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info)228 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
229 ipadm_if_info_t **if_info)
230 {
231 ipadm_status_t status = IPADM_SUCCESS;
232 nvlist_t *ifs_info_nvl;
233
234 *if_info = NULL;
235
236 if ((status = i_ipadm_get_db_if(iph,
237 ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
238 return (status);
239
240 assert(ifs_info_nvl != NULL);
241
242 return (i_ipadm_nvl2ifinfo(ifs_info_nvl, if_info));
243 }
244
245 static ipadm_status_t
i_ipadm_nvl2ifinfo(nvlist_t * ifs_info_nvl,ipadm_if_info_t ** if_info)246 i_ipadm_nvl2ifinfo(nvlist_t *ifs_info_nvl, ipadm_if_info_t **if_info)
247 {
248 ipadm_if_info_t *ific = NULL, *ifil = NULL;
249 nvlist_t *if_info_nvl;
250 nvpair_t *nvp;
251 char *strval;
252 ipadm_status_t status = IPADM_SUCCESS;
253 uint16_t *families;
254 uint_t nelem = 0;
255
256 for (nvp = nvlist_next_nvpair(ifs_info_nvl, NULL); nvp != NULL;
257 nvp = nvlist_next_nvpair(ifs_info_nvl, nvp)) {
258 if (nvpair_value_nvlist(nvp, &if_info_nvl) != 0)
259 continue;
260
261 status = i_ipadm_allocate_ifinfo(&ific);
262 if (status != IPADM_SUCCESS) {
263 ipadm_free_if_info(*if_info);
264 break;
265 }
266 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_IFNAME,
267 &strval) != 0) {
268 ipadm_free_if_info(ific);
269 ific = NULL;
270 continue;
271 }
272 (void) strlcpy(ific->ifi_name, strval,
273 sizeof (ific->ifi_name));
274
275 if (nvlist_lookup_uint16_array(if_info_nvl,
276 IPADM_NVP_FAMILIES, &families, &nelem) == 0) {
277 while (nelem-- > 0) {
278 if (families[nelem] == AF_INET)
279 ific->ifi_pflags |= IFIF_IPV4;
280 else if (families[nelem] == AF_INET6)
281 ific->ifi_pflags |= IFIF_IPV6;
282 }
283 }
284
285 if (nvlist_lookup_string(if_info_nvl,
286 IPADM_NVP_IFCLASS, &strval) == 0)
287 ific->ifi_class = atoi(strval);
288 else
289 ific->ifi_class = IPADM_IF_CLASS_REGULAR;
290
291 if (ific->ifi_class == IPADM_IF_CLASS_IPMP)
292 /* do not expect any failures there */
293 (void) i_ipadm_fill_pmembers(if_info_nvl,
294 &ific->ifi_ipmp_pmembers);
295
296 if (*if_info == NULL)
297 *if_info = ific;
298 else
299 ifil->ifi_next = ific;
300 ifil = ific;
301 }
302
303 nvlist_free(ifs_info_nvl);
304 return (status);
305 }
306
307 /*
308 * Fill the ipadm_if_info_t->ifi_ipmp_pmembers by info from
309 * ipadm DB
310 */
311 static ipadm_status_t
i_ipadm_fill_pmembers(nvlist_t * if_info_nvl,ipadm_ipmp_members_t * pmembers)312 i_ipadm_fill_pmembers(nvlist_t *if_info_nvl, ipadm_ipmp_members_t *pmembers)
313 {
314 uint_t nelem = 0;
315 char **members;
316 ipadm_ipmp_member_t *ipmp_member;
317
318 if (nvlist_lookup_string_array(if_info_nvl, IPADM_NVP_MIFNAMES,
319 &members, &nelem) != 0)
320 return (IPADM_SUCCESS);
321
322 while (nelem-- > 0) {
323 if ((ipmp_member = calloc(1,
324 sizeof (ipadm_ipmp_member_t))) == NULL)
325 return (ipadm_errno2status(errno));
326
327 (void) strlcpy(ipmp_member->if_name, members[nelem],
328 sizeof (ipmp_member->if_name));
329 list_insert_tail(pmembers, ipmp_member);
330 }
331 return (IPADM_SUCCESS);
332 }
333
334 /*
335 * Fill the ipadm_if_info_t->ifi_ipmp_cmembers by info from
336 * kernel (libipmp is used to retrieve the required info)
337 */
338 static ipadm_status_t
i_ipadm_fill_cmembers(char * grname,ipadm_ipmp_members_t * cmembers)339 i_ipadm_fill_cmembers(char *grname, ipadm_ipmp_members_t *cmembers)
340 {
341 ipmp_handle_t ipmp_handle;
342 ipmp_groupinfo_t *grinfo;
343 ipmp_iflist_t *iflistp;
344 ipadm_ipmp_member_t *ipmp_member;
345 ipadm_status_t ipadm_status = IPADM_SUCCESS;
346 int i;
347
348 if (ipmp_open(&ipmp_handle) != IPMP_SUCCESS)
349 return (IPADM_FAILURE);
350
351 if (ipmp_getgroupinfo(ipmp_handle, grname, &grinfo) != IPMP_SUCCESS) {
352 ipadm_status = IPADM_FAILURE;
353 goto fail2;
354 }
355
356 iflistp = grinfo->gr_iflistp;
357 for (i = 0; i < iflistp->il_nif; i++) {
358 if ((ipmp_member = calloc(1,
359 sizeof (ipadm_ipmp_member_t))) == NULL) {
360 ipadm_status = ipadm_errno2status(errno);
361 goto fail1;
362 }
363 (void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
364 sizeof (ipmp_member->if_name));
365 list_insert_tail(cmembers, ipmp_member);
366 }
367
368 fail1:
369 ipmp_freegroupinfo(grinfo);
370 fail2:
371 ipmp_close(ipmp_handle);
372 return (ipadm_status);
373 }
374
375 /*
376 * Collects information for `ifname' if one is specified from both
377 * active and persistent config in `if_info'. If no `ifname' is specified,
378 * this returns all the interfaces in active and persistent config in
379 * `if_info'.
380 */
381 ipadm_status_t
i_ipadm_get_all_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info,int64_t lifc_flags)382 i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
383 ipadm_if_info_t **if_info, int64_t lifc_flags)
384 {
385 ipadm_status_t status;
386 ipadm_if_info_t *aifinfo = NULL;
387 ipadm_if_info_t *pifinfo = NULL;
388 ipadm_if_info_t *aifp;
389 ipadm_if_info_t *pifp;
390 ipadm_if_info_t *last = NULL;
391 struct ifaddrs *ifa;
392 struct ifaddrs *ifap;
393
394 /*
395 * Retrive the information for the requested `ifname' or all
396 * interfaces from active configuration.
397 */
398 retry:
399 status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
400 if (status != IPADM_SUCCESS)
401 return (status);
402 /* Get the interface state for each interface in `aifinfo'. */
403 if (aifinfo != NULL) {
404 /* We need all addresses to get the interface state */
405 if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
406 LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
407 status = ipadm_errno2status(errno);
408 goto fail;
409 }
410 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
411 /*
412 * Find the `ifaddrs' structure from `ifa'
413 * for this interface. We need the IFF_* flags
414 * to find the interface state.
415 */
416 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
417 if (ifap->ifa_addr->sa_family == AF_LINK)
418 continue;
419 if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
420 break;
421 }
422 if (ifap == NULL) {
423 /*
424 * The interface might have been removed
425 * from kernel. Retry getting all the active
426 * interfaces.
427 */
428 freeifaddrs(ifa);
429 ipadm_free_if_info(aifinfo);
430 aifinfo = NULL;
431 goto retry;
432 }
433 if (!(ifap->ifa_flags & IFF_RUNNING) ||
434 (ifap->ifa_flags & IFF_FAILED))
435 aifp->ifi_state = IFIS_FAILED;
436 else if (ifap->ifa_flags & IFF_OFFLINE)
437 aifp->ifi_state = IFIS_OFFLINE;
438 else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
439 aifp->ifi_state = IFIS_DOWN;
440 else
441 aifp->ifi_state = IFIS_OK;
442 if (aifp->ifi_next == NULL)
443 last = aifp;
444 }
445 freeifaddrs(ifa);
446 }
447 /*
448 * Get the persistent interface information in `pifinfo'.
449 */
450 status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
451 if (status == IPADM_NOTFOUND) {
452 *if_info = aifinfo;
453 return (IPADM_SUCCESS);
454 }
455 if (status != IPADM_SUCCESS)
456 goto fail;
457
458 /*
459 * Process the persistent interface information.
460 *
461 * First try to get the persistent "standby" property, as that isn't
462 * retrieved by i_ipadm_persist_if_info().
463 *
464 * Next, if a persistent interface is also found in `aifinfo', update
465 * its entry in `aifinfo' with the persistent information from
466 * `pifinfo'. If an interface is found in `pifinfo', but not in
467 * `aifinfo', it means that this interface was disabled. We should
468 * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
469 */
470 for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
471 char buf[10] = "";
472 uint_t bufsize = sizeof (buf);
473
474 status = ipadm_get_ifprop(iph, pifp->ifi_name, "standby", buf,
475 &bufsize, MOD_PROTO_IP, IPADM_OPT_PERSIST);
476
477 if (status == IPADM_SUCCESS && strcmp(buf, "on") == 0)
478 pifp->ifi_pflags |= IFIF_STANDBY;
479
480 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
481 if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
482 break;
483 }
484 }
485
486 if (aifp == NULL) {
487 if ((status =
488 i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
489 goto fail;
490
491 (void) strlcpy(aifp->ifi_name, pifp->ifi_name,
492 sizeof (aifp->ifi_name));
493
494 aifp->ifi_next = NULL;
495 aifp->ifi_state = IFIS_DISABLED;
496 if (last != NULL)
497 last->ifi_next = aifp;
498 else
499 aifinfo = aifp;
500 last = aifp;
501 }
502
503 if ((status = i_ipadm_add_persistent_if_info(aifp,
504 pifp)) != IPADM_SUCCESS)
505 goto fail;
506 }
507 *if_info = aifinfo;
508 ipadm_free_if_info(pifinfo);
509 return (IPADM_SUCCESS);
510 fail:
511 *if_info = NULL;
512 ipadm_free_if_info(aifinfo);
513 ipadm_free_if_info(pifinfo);
514 return (status);
515 }
516
517 /*
518 * Updates active if_info by data from persistent if_info
519 */
520 static ipadm_status_t
i_ipadm_add_persistent_if_info(ipadm_if_info_t * aifp,ipadm_if_info_t * pifp)521 i_ipadm_add_persistent_if_info(ipadm_if_info_t *aifp, ipadm_if_info_t *pifp)
522 {
523 ipadm_ipmp_member_t *pp_ipmp_member, *ap_ipmp_member;
524
525 ipadm_ipmp_members_t *apmembers = &aifp->ifi_ipmp_pmembers;
526 ipadm_ipmp_members_t *ppmembers = &pifp->ifi_ipmp_pmembers;
527
528 aifp->ifi_pflags = pifp->ifi_pflags;
529 aifp->ifi_class = pifp->ifi_class;
530
531 for (pp_ipmp_member = list_head(ppmembers); pp_ipmp_member;
532 pp_ipmp_member = list_next(ppmembers, pp_ipmp_member)) {
533 if ((ap_ipmp_member = calloc(1,
534 sizeof (ipadm_ipmp_member_t))) == NULL)
535 return (ipadm_errno2status(errno));
536
537 (void) strlcpy(ap_ipmp_member->if_name,
538 pp_ipmp_member->if_name,
539 sizeof (ap_ipmp_member->if_name));
540
541 list_insert_tail(apmembers, ap_ipmp_member);
542 }
543 return (IPADM_SUCCESS);
544 }
545
546 static ipadm_status_t
i_ipadm_allocate_ifinfo(ipadm_if_info_t ** if_info)547 i_ipadm_allocate_ifinfo(ipadm_if_info_t **if_info)
548 {
549 *if_info = calloc(1, sizeof (ipadm_if_info_t));
550 if (*if_info == NULL)
551 return (ipadm_errno2status(errno));
552
553 /* List of active (current) members */
554 list_create(&((*if_info)->ifi_ipmp_cmembers),
555 sizeof (ipadm_ipmp_member_t),
556 offsetof(ipadm_ipmp_member_t, node));
557
558 /* List of persistent members */
559 list_create(&((*if_info)->ifi_ipmp_pmembers),
560 sizeof (ipadm_ipmp_member_t),
561 offsetof(ipadm_ipmp_member_t, node));
562
563 return (IPADM_SUCCESS);
564 }
565
566 /*
567 * Reads all the interface lines from the persistent DB into the nvlist `onvl',
568 * when `ifname' is NULL.
569 * If an `ifname' is specified, then the interface line corresponding to
570 * that name will be returned.
571 */
572 static ipadm_status_t
i_ipadm_get_db_if(ipadm_handle_t iph,const char * ifname,nvlist_t ** onvl)573 i_ipadm_get_db_if(ipadm_handle_t iph, const char *ifname, nvlist_t **onvl)
574 {
575 ipmgmt_getif_arg_t garg;
576
577 /* Populate the door_call argument structure */
578 bzero(&garg, sizeof (garg));
579 garg.ia_cmd = IPMGMT_CMD_GETIF;
580 if (ifname != NULL)
581 (void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
582
583 return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
584 }
585
586 int
i_ipadm_get_lnum(const char * ifname)587 i_ipadm_get_lnum(const char *ifname)
588 {
589 char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
590
591 if (num == NULL)
592 return (0);
593
594 return (atoi(++num));
595 }
596
597 /*
598 * Sets the output argument `exists' to true or false based on whether
599 * any persistent configuration is available for `ifname' and returns
600 * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
601 * `exists' is unmodified and an error status is returned.
602 */
603 ipadm_status_t
i_ipadm_if_pexists(ipadm_handle_t iph,const char * ifname,sa_family_t af,boolean_t * exists)604 i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
605 boolean_t *exists)
606 {
607 ipadm_if_info_t *ifinfo;
608 ipadm_status_t status;
609
610 /*
611 * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
612 * knows about persistent configuration in the first place, so we
613 * just return success.
614 */
615 if (iph->iph_flags & IPH_IPMGMTD) {
616 *exists = B_FALSE;
617 return (IPADM_SUCCESS);
618 }
619 status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
620 if (status == IPADM_SUCCESS) {
621 *exists = ((af == AF_INET &&
622 (ifinfo->ifi_pflags & IFIF_IPV4)) ||
623 (af == AF_INET6 &&
624 (ifinfo->ifi_pflags & IFIF_IPV6)));
625 ipadm_free_if_info(ifinfo);
626 } else if (status == IPADM_NOTFOUND) {
627 status = IPADM_SUCCESS;
628 *exists = B_FALSE;
629 }
630 return (status);
631 }
632
633 /*
634 * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
635 * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
636 * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
637 * the bottom of the stream for tunneling interfaces.
638 */
639 ipadm_status_t
ipadm_open_arp_on_udp(const char * udp_dev_name,int * fd)640 ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
641 {
642 int err;
643
644 if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
645 return (ipadm_errno2status(errno));
646
647 /*
648 * Pop off all undesired modules (note that the user may have
649 * configured autopush to add modules above udp), and push the
650 * arp module onto the resulting stream. This is used to make
651 * IP+ARP be able to atomically track the muxid for the I_PLINKed
652 * STREAMS, thus it isn't related to ARP running the ARP protocol.
653 */
654 while (ioctl(*fd, I_POP, 0) != -1)
655 ;
656 if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
657 return (IPADM_SUCCESS);
658 err = errno;
659 (void) close(*fd);
660
661 return (ipadm_errno2status(err));
662 }
663
664 /*
665 * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
666 * underlying interface in an ipmp group G is plumbed for an address family,
667 * but the meta-interface for the other address family `af' does not exist
668 * yet for the group G. If `af' is IPv6, we need to bring up the
669 * link-local address.
670 */
671 static ipadm_status_t
i_ipadm_create_ipmp(ipadm_handle_t iph,char * ifname,sa_family_t af,const char * grname,uint32_t ipadm_flags)672 i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af,
673 const char *grname, uint32_t ipadm_flags)
674 {
675 ipadm_status_t status;
676 struct lifreq lifr;
677 int sock;
678 int err;
679
680 assert(ipadm_flags & IPADM_OPT_IPMP);
681
682 /* Create the ipmp underlying interface */
683 status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
684 if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
685 return (status);
686
687 /*
688 * To preserve backward-compatibility, always bring up the link-local
689 * address for implicitly-created IPv6 IPMP interfaces.
690 */
691 if (af == AF_INET6)
692 (void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
693
694 sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
695 /*
696 * If the caller requested a different group name, issue a
697 * SIOCSLIFGROUPNAME on the new IPMP interface.
698 */
699 bzero(&lifr, sizeof (lifr));
700 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
701 if (strcmp(lifr.lifr_name, grname) != 0) {
702 (void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
703 if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
704 err = errno;
705 /* Remove the interface we created. */
706 if (status == IPADM_SUCCESS) {
707 (void) i_ipadm_delete_if(iph, ifname, af,
708 ipadm_flags);
709 }
710 return (ipadm_errno2status(err));
711 }
712 }
713
714 return (IPADM_SUCCESS);
715 }
716
717 /*
718 * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
719 * family. If so, create a matching IPMP group for address family `af'.
720 */
721 static ipadm_status_t
i_ipadm_create_ipmp_peer(ipadm_handle_t iph,char * ifname,sa_family_t af)722 i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
723 {
724 lifgroupinfo_t lifgr;
725 ipadm_status_t status = IPADM_SUCCESS;
726 struct lifreq lifr;
727 int other_af_sock;
728
729 assert(af == AF_INET || af == AF_INET6);
730
731 other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
732
733 /*
734 * iph is the handle for the interface that we are trying to plumb.
735 * other_af_sock is the socket for the "other" address family.
736 */
737 bzero(&lifr, sizeof (lifr));
738 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
739 if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
740 return (IPADM_SUCCESS);
741
742 (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
743 if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
744 return (IPADM_SUCCESS);
745
746 /*
747 * If `ifname' *is* the IPMP group interface, or if the relevant
748 * address family is already configured, then there's nothing to do.
749 */
750 if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
751 (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
752 return (IPADM_SUCCESS);
753 }
754
755 status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
756 lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
757 return (status);
758 }
759
760 /*
761 * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
762 */
763 static ipadm_status_t
i_ipadm_slifname_arp(char * ifname,uint64_t flags,int fd)764 i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
765 {
766 struct lifreq lifr;
767 ifspec_t ifsp;
768
769 bzero(&lifr, sizeof (lifr));
770 (void) ifparse_ifspec(ifname, &ifsp);
771 lifr.lifr_ppa = ifsp.ifsp_ppa;
772 lifr.lifr_flags = flags;
773 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
774 /*
775 * Tell ARP the name and unit number for this interface.
776 * Note that arp has no support for transparent ioctls.
777 */
778 if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
779 sizeof (lifr)) == -1) {
780 return (ipadm_errno2status(errno));
781 }
782 return (IPADM_SUCCESS);
783 }
784
785 /*
786 * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
787 * `ipadm_flags', then a ppa will be generated. `newif' will be updated
788 * with the generated ppa.
789 */
790 static ipadm_status_t
i_ipadm_slifname(ipadm_handle_t iph,char * ifname,char * newif,uint64_t flags,int fd,uint32_t ipadm_flags)791 i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
792 int fd, uint32_t ipadm_flags)
793 {
794 struct lifreq lifr;
795 ipadm_status_t status = IPADM_SUCCESS;
796 int err = 0;
797 sa_family_t af;
798 int ppa;
799 ifspec_t ifsp;
800 boolean_t valid_if;
801
802 bzero(&lifr, sizeof (lifr));
803 if (ipadm_flags & IPADM_OPT_GENPPA) {
804 /*
805 * We'd like to just set lifr_ppa to UINT_MAX and have the
806 * kernel pick a PPA. Unfortunately, that would mishandle
807 * two cases:
808 *
809 * 1. If the PPA is available but the groupname is taken
810 * (e.g., the "ipmp2" IP interface name is available
811 * but the "ipmp2" groupname is taken) then the
812 * auto-assignment by the kernel will fail.
813 *
814 * 2. If we're creating (e.g.) an IPv6-only IPMP
815 * interface, and there's already an IPv4-only IPMP
816 * interface, the kernel will allow us to accidentally
817 * reuse the IPv6 IPMP interface name (since
818 * SIOCSLIFNAME uniqueness is per-interface-type).
819 * This will cause administrative confusion.
820 *
821 * Thus, we instead take a brute-force approach of checking
822 * whether the IPv4 or IPv6 name is already in-use before
823 * attempting the SIOCSLIFNAME. As per (1) above, the
824 * SIOCSLIFNAME may still fail, in which case we just proceed
825 * to the next one. If this approach becomes too slow, we
826 * can add a new SIOC* to handle this case in the kernel.
827 */
828 for (ppa = 0; ppa < UINT_MAX; ppa++) {
829 (void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
830 ifname, ppa);
831
832 if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
833 errno != ENXIO)
834 continue;
835
836 if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
837 errno != ENXIO)
838 continue;
839
840 lifr.lifr_ppa = ppa;
841 lifr.lifr_flags = flags;
842
843 err = ioctl(fd, SIOCSLIFNAME, &lifr);
844 if (err != -1 || errno != EEXIST)
845 break;
846 }
847 if (err == -1) {
848 status = ipadm_errno2status(errno);
849 } else {
850 /*
851 * PPA has been successfully established.
852 * Update `newif' with the ppa.
853 */
854 assert(newif != NULL);
855 if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
856 ppa) >= LIFNAMSIZ)
857 return (IPADM_INVALID_ARG);
858 }
859 } else {
860 /* We should have already validated the interface name. */
861 valid_if = ifparse_ifspec(ifname, &ifsp);
862 assert(valid_if);
863
864 /*
865 * Before we call SIOCSLIFNAME, ensure that the IPMP group
866 * interface for this address family exists. Otherwise, the
867 * kernel will kick the interface out of the group when we do
868 * the SIOCSLIFNAME.
869 *
870 * Example: suppose bge0 is plumbed for IPv4 and in group "a".
871 * If we're now plumbing bge0 for IPv6, but the IPMP group
872 * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
873 * will kick bge0 out of group "a", which is undesired.
874 */
875 if (flags & IFF_IPV4)
876 af = AF_INET;
877 else
878 af = AF_INET6;
879 status = i_ipadm_create_ipmp_peer(iph, ifname, af);
880 if (status != IPADM_SUCCESS)
881 return (status);
882 lifr.lifr_ppa = ifsp.ifsp_ppa;
883 lifr.lifr_flags = flags;
884 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
885 if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
886 status = ipadm_errno2status(errno);
887 }
888
889 return (status);
890 }
891
892 /*
893 * Plumbs the interface `ifname' for the address family `af'. It also persists
894 * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
895 */
896 ipadm_status_t
i_ipadm_plumb_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t ipadm_flags)897 i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
898 uint32_t ipadm_flags)
899 {
900 int ip_muxid;
901 int mux_fd = -1, ip_fd, arp_fd;
902 char *udp_dev_name;
903 dlpi_handle_t dh_arp = NULL, dh_ip;
904 uint64_t ifflags;
905 struct lifreq lifr;
906 uint_t dlpi_flags;
907 ipadm_status_t status = IPADM_SUCCESS;
908 char *linkname;
909 boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
910 zoneid_t zoneid;
911 char newif[LIFNAMSIZ];
912 char lifname[LIFNAMSIZ];
913 datalink_id_t linkid;
914 int sock;
915 boolean_t islo;
916 boolean_t is_persistent =
917 ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
918 uint32_t dlflags;
919 dladm_status_t dlstatus;
920
921 if (iph->iph_dlh != NULL) {
922 dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
923 &dlflags, NULL, NULL);
924 }
925 /*
926 * If we're in the global zone and we're plumbing a datalink, make
927 * sure that the datalink is not assigned to a non-global zone. Note
928 * that the non-global zones don't need this check, because zoneadm
929 * has taken care of this when the zones boot.
930 */
931 if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
932 zoneid = ALL_ZONES;
933 if (zone_check_datalink(&zoneid, linkid) == 0) {
934 /* interface is in use by a non-global zone. */
935 return (IPADM_IF_INUSE);
936 }
937 }
938
939 /* loopback interfaces are just added as logical interface */
940 bzero(&lifr, sizeof (lifr));
941 islo = i_ipadm_is_loopback(ifname);
942 if (islo || i_ipadm_get_lnum(ifname) != 0) {
943 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
944 if (af == AF_INET)
945 sock = iph->iph_sock;
946 else
947 sock = iph->iph_sock6;
948 if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
949 return (IPADM_IF_EXISTS);
950 if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
951 return (ipadm_errno2status(errno));
952
953 /*
954 * By default, kernel configures 127.0.0.1 on the loopback
955 * interface. Replace this with 0.0.0.0 to be consistent
956 * with interface creation on other physical interfaces.
957 */
958 if (islo && !legacy) {
959 bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
960 lifr.lifr_addr.ss_family = af;
961 if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
962 return (ipadm_errno2status(errno));
963 if (is_persistent) {
964 status = i_ipadm_persist_if(iph,
965 ifname, af, ipadm_flags);
966 if (status != IPADM_SUCCESS) {
967 (void) i_ipadm_delete_if(iph, ifname,
968 af, IPADM_OPT_ACTIVE);
969 }
970 }
971 }
972 return (status);
973 }
974
975 dlpi_flags = DLPI_NOATTACH;
976
977 /*
978 * If IPADM_OPT_IPMP is specified, then this is a request
979 * to create an IPMP interface atop /dev/ipmpstub0. (We can't simply
980 * pass "ipmpstub0" as devname since an admin *could* have a normal
981 * vanity-named link named "ipmpstub0" that they'd like to plumb.)
982 */
983 if (ipadm_flags & IPADM_OPT_IPMP) {
984 dlpi_flags |= DLPI_DEVONLY;
985 linkname = "ipmpstub0";
986 } else {
987 /*
988 * Verify that the user is not creating a persistent
989 * IP interface on a non-persistent data-link.
990 */
991 if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
992 is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
993 return (IPADM_TEMPORARY_OBJ);
994 }
995 linkname = ifname;
996 }
997
998 /*
999 * We use DLPI_NOATTACH because the ip module will do the attach
1000 * itself for DLPI style-2 devices.
1001 */
1002 if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
1003 return (IPADM_DLPI_FAILURE);
1004 ip_fd = dlpi_fd(dh_ip);
1005 if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
1006 status = ipadm_errno2status(errno);
1007 goto done;
1008 }
1009
1010 /*
1011 * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
1012 * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
1013 */
1014 ifflags = 0;
1015
1016 /* Set the name string and the IFF_IPV* flag */
1017 if (af == AF_INET) {
1018 ifflags = IFF_IPV4;
1019 } else {
1020 ifflags = IFF_IPV6;
1021 /*
1022 * With the legacy method, the link-local address should be
1023 * configured as part of the interface plumb, using the default
1024 * token. If IPH_LEGACY is not specified, we want to set :: as
1025 * the address and require the admin to explicitly call
1026 * ipadm_create_addr() with the address object type set to
1027 * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
1028 * as well as the autoconfigured addresses.
1029 */
1030 if (!legacy && !i_ipadm_is_6to4(iph, ifname))
1031 ifflags |= IFF_NOLINKLOCAL;
1032 }
1033 (void) strlcpy(newif, ifname, sizeof (newif));
1034 status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
1035 ipadm_flags);
1036 if (status != IPADM_SUCCESS)
1037 goto done;
1038
1039 /* Get the full set of existing flags for this stream */
1040 status = i_ipadm_get_flags(iph, newif, af, &ifflags);
1041 if (status != IPADM_SUCCESS)
1042 goto done;
1043
1044 udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
1045 status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
1046 if (status != IPADM_SUCCESS)
1047 goto done;
1048
1049 /* Check if arp is not needed */
1050 if (ifflags & (IFF_NOARP|IFF_IPV6)) {
1051 /*
1052 * PLINK the interface stream so that the application can exit
1053 * without tearing down the stream.
1054 */
1055 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
1056 status = ipadm_errno2status(errno);
1057 goto done;
1058 }
1059
1060 /*
1061 * This interface does use ARP, so set up a separate stream
1062 * from the interface to ARP.
1063 *
1064 * We use DLPI_NOATTACH because the arp module will do the attach
1065 * itself for DLPI style-2 devices.
1066 */
1067 if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
1068 status = IPADM_DLPI_FAILURE;
1069 goto done;
1070 }
1071
1072 arp_fd = dlpi_fd(dh_arp);
1073 if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
1074 status = ipadm_errno2status(errno);
1075 goto done;
1076 }
1077
1078 status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
1079 if (status != IPADM_SUCCESS)
1080 goto done;
1081 /*
1082 * PLINK the IP and ARP streams so that ifconfig can exit
1083 * without tearing down the stream.
1084 */
1085 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
1086 status = ipadm_errno2status(errno);
1087 goto done;
1088 }
1089
1090 if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
1091 status = ipadm_errno2status(errno);
1092 (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
1093 }
1094
1095 done:
1096 dlpi_close(dh_ip);
1097 if (dh_arp != NULL)
1098 dlpi_close(dh_arp);
1099
1100 if (mux_fd != -1)
1101 (void) close(mux_fd);
1102
1103 if (status == IPADM_SUCCESS) {
1104 /* copy back new ifname */
1105 (void) strlcpy(ifname, newif, LIFNAMSIZ);
1106 /*
1107 * If it is a 6to4 tunnel, create a default
1108 * addrobj name for the default address on the 0'th
1109 * logical interface and set IFF_UP in the interface flags.
1110 */
1111 if (i_ipadm_is_6to4(iph, ifname)) {
1112 struct ipadm_addrobj_s addr;
1113
1114 i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
1115 addr.ipadm_af = af;
1116 status = i_ipadm_lookupadd_addrobj(iph, &addr);
1117 if (status != IPADM_SUCCESS)
1118 return (status);
1119 status = ipadm_add_aobjname(iph, ifname,
1120 af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
1121 if (status != IPADM_SUCCESS)
1122 return (status);
1123 addr.ipadm_lifnum = 0;
1124 i_ipadm_addrobj2lifname(&addr, lifname,
1125 sizeof (lifname));
1126 status = i_ipadm_set_flags(iph, lifname, af,
1127 IFF_UP, 0);
1128 if (status != IPADM_SUCCESS)
1129 return (status);
1130 } else {
1131 /*
1132 * Prevent static IPv6 addresses from triggering
1133 * autoconf. This does not have to be done for
1134 * 6to4 tunnel interfaces, since in.ndpd will
1135 * not autoconfigure those interfaces.
1136 */
1137 if (af == AF_INET6 && !legacy)
1138 (void) i_ipadm_disable_autoconf(newif);
1139 }
1140
1141 /*
1142 * If IPADM_OPT_PERSIST was set in flags, store the
1143 * interface in persistent DB.
1144 */
1145 if (is_persistent) {
1146 status = i_ipadm_persist_if(iph,
1147 newif, af, ipadm_flags);
1148 if (status != IPADM_SUCCESS) {
1149 (void) i_ipadm_delete_if(iph, newif, af,
1150 IPADM_OPT_ACTIVE);
1151 }
1152 }
1153 }
1154 if (status == IPADM_EXISTS)
1155 status = IPADM_IF_EXISTS;
1156 return (status);
1157 }
1158
1159 /*
1160 * Unplumbs the interface in `ifname' of family `af'.
1161 */
1162 ipadm_status_t
i_ipadm_unplumb_if(ipadm_handle_t iph,const char * ifname,sa_family_t af)1163 i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
1164 {
1165 int ip_muxid, arp_muxid;
1166 int mux_fd = -1;
1167 int muxid_fd = -1;
1168 char *udp_dev_name;
1169 uint64_t flags;
1170 boolean_t changed_arp_muxid = B_FALSE;
1171 int save_errno;
1172 struct lifreq lifr;
1173 ipadm_status_t ret = IPADM_SUCCESS;
1174 int sock;
1175 lifgroupinfo_t lifgr;
1176 ifaddrlistx_t *ifaddrs, *ifaddrp;
1177 boolean_t v6 = (af == AF_INET6);
1178
1179 /* Just do SIOCLIFREMOVEIF on loopback interfaces */
1180 bzero(&lifr, sizeof (lifr));
1181 if (i_ipadm_is_loopback(ifname) ||
1182 (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
1183 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1184 if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
1185 SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
1186 return (ipadm_errno2status(errno));
1187 }
1188 return (IPADM_SUCCESS);
1189 }
1190
1191 /*
1192 * We used /dev/udp or udp6 to set up the mux. So we have to use
1193 * the same now for PUNLINK also.
1194 */
1195 if (v6) {
1196 udp_dev_name = UDP6_DEV_NAME;
1197 sock = iph->iph_sock6;
1198 } else {
1199 udp_dev_name = UDP_DEV_NAME;
1200 sock = iph->iph_sock;
1201 }
1202 if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
1203 ret = ipadm_errno2status(errno);
1204 goto done;
1205 }
1206 ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
1207 if (ret != IPADM_SUCCESS)
1208 goto done;
1209 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1210 if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
1211 ret = ipadm_errno2status(errno);
1212 goto done;
1213 }
1214 flags = lifr.lifr_flags;
1215 again:
1216 if (flags & IFF_IPMP) {
1217 /*
1218 * There are two reasons the I_PUNLINK can fail with EBUSY:
1219 * (1) if IP interfaces are in the group, or (2) if IPMP data
1220 * addresses are administratively up. For case (1), we fail
1221 * here with a specific error message. For case (2), we bring
1222 * down the addresses prior to doing the I_PUNLINK. If the
1223 * I_PUNLINK still fails with EBUSY then the configuration
1224 * must have changed after our checks, in which case we branch
1225 * back up to `again' and rerun this logic. The net effect is
1226 * that unplumbing an IPMP interface will only fail with EBUSY
1227 * if IP interfaces are in the group.
1228 */
1229 if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
1230 ret = ipadm_errno2status(errno);
1231 goto done;
1232 }
1233 (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
1234 LIFGRNAMSIZ);
1235 if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
1236 ret = ipadm_errno2status(errno);
1237 goto done;
1238 }
1239 if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
1240 ret = IPADM_GRP_NOTEMPTY;
1241 goto done;
1242 }
1243
1244 /*
1245 * The kernel will fail the I_PUNLINK if the IPMP interface
1246 * has administratively up addresses; bring them down.
1247 */
1248 if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
1249 0, &ifaddrs) == -1) {
1250 ret = ipadm_errno2status(errno);
1251 goto done;
1252 }
1253 ifaddrp = ifaddrs;
1254 for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1255 int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
1256 iph->iph_sock : iph->iph_sock6;
1257 struct lifreq lifrl;
1258
1259 if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
1260 (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
1261 continue;
1262
1263 bzero(&lifrl, sizeof (lifrl));
1264 (void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
1265 sizeof (lifrl.lifr_name));
1266 if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
1267 ret = ipadm_errno2status(errno);
1268 ifaddrlistx_free(ifaddrs);
1269 goto done;
1270 }
1271 if (lifrl.lifr_flags & IFF_UP) {
1272 ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
1273 ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
1274 AF_INET6), 0, IFF_UP);
1275 if (ret != IPADM_SUCCESS) {
1276 ifaddrlistx_free(ifaddrs);
1277 goto done;
1278 }
1279 } else if (lifrl.lifr_flags & IFF_DUPLICATE) {
1280 if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
1281 ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
1282 ret = ipadm_errno2status(errno);
1283 ifaddrlistx_free(ifaddrs);
1284 goto done;
1285 }
1286 }
1287 }
1288 ifaddrlistx_free(ifaddrs);
1289 }
1290
1291 if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
1292 ret = ipadm_errno2status(errno);
1293 goto done;
1294 }
1295 arp_muxid = lifr.lifr_arp_muxid;
1296 ip_muxid = lifr.lifr_ip_muxid;
1297
1298 /*
1299 * We don't have a good way of knowing whether the arp stream is
1300 * plumbed. We can't rely on IFF_NOARP because someone could
1301 * have turned it off later using "ifconfig xxx -arp".
1302 */
1303 if (arp_muxid != 0) {
1304 if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
1305 /*
1306 * See the comment before the SIOCGLIFGROUPNAME call.
1307 */
1308 if (errno == EBUSY && (flags & IFF_IPMP))
1309 goto again;
1310
1311 if ((errno == EINVAL) &&
1312 (flags & (IFF_NOARP | IFF_IPV6))) {
1313 /*
1314 * Some plumbing utilities set the muxid to
1315 * -1 or some invalid value to signify that
1316 * there is no arp stream. Set the muxid to 0
1317 * before trying to unplumb the IP stream.
1318 * IP does not allow the IP stream to be
1319 * unplumbed if it sees a non-null arp muxid,
1320 * for consistency of IP-ARP streams.
1321 */
1322 lifr.lifr_arp_muxid = 0;
1323 (void) ioctl(muxid_fd, SIOCSLIFMUXID,
1324 (caddr_t)&lifr);
1325 changed_arp_muxid = B_TRUE;
1326 }
1327 /*
1328 * In case of any other error, we continue with
1329 * the unplumb.
1330 */
1331 }
1332 }
1333
1334 if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
1335 if (changed_arp_muxid) {
1336 /*
1337 * Some error occurred, and we need to restore
1338 * everything back to what it was.
1339 */
1340 save_errno = errno;
1341 lifr.lifr_arp_muxid = arp_muxid;
1342 lifr.lifr_ip_muxid = ip_muxid;
1343 (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
1344 errno = save_errno;
1345 }
1346 /*
1347 * See the comment before the SIOCGLIFGROUPNAME call.
1348 */
1349 if (errno == EBUSY && (flags & IFF_IPMP))
1350 goto again;
1351
1352 ret = ipadm_errno2status(errno);
1353 }
1354 done:
1355 if (muxid_fd != -1)
1356 (void) close(muxid_fd);
1357 if (mux_fd != -1)
1358 (void) close(mux_fd);
1359
1360 if (af == AF_INET6 && ret == IPADM_SUCCESS) {
1361 /*
1362 * in.ndpd maintains the phyints in its memory even after
1363 * the interface is plumbed, so that it can be reused when
1364 * the interface gets plumbed again. The default behavior
1365 * of in.ndpd is to start autoconfiguration for an interface
1366 * that gets plumbed. We need to send the
1367 * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
1368 * default behavior on replumb.
1369 */
1370 (void) i_ipadm_enable_autoconf(ifname);
1371 }
1372 return (ret);
1373 }
1374
1375 /*
1376 * Saves the given interface name `ifname' with address family `af' in
1377 * persistent DB.
1378 */
1379 static ipadm_status_t
i_ipadm_persist_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t ipadm_flags)1380 i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1381 uint32_t ipadm_flags)
1382 {
1383 ipmgmt_if_arg_t ifarg;
1384 int err;
1385
1386 (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
1387 ifarg.ia_family = af;
1388 if (ipadm_flags & IPADM_OPT_IPMP)
1389 ifarg.ia_ifclass = IPADM_IF_CLASS_IPMP;
1390 else
1391 ifarg.ia_ifclass = IPADM_IF_CLASS_REGULAR;
1392
1393 ifarg.ia_cmd = IPMGMT_CMD_SETIF;
1394 ifarg.ia_flags = IPMGMT_PERSIST;
1395 err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1396 return (ipadm_errno2status(err));
1397 }
1398
1399 /*
1400 * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
1401 * is set in `ipadm_flags', it is also removed from persistent configuration.
1402 */
1403 ipadm_status_t
i_ipadm_delete_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t ipadm_flags)1404 i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1405 uint32_t ipadm_flags)
1406 {
1407 ipadm_status_t ret = IPADM_SUCCESS;
1408 ipadm_status_t db_status;
1409 char tmp_ifname[LIFNAMSIZ];
1410 char *cp;
1411 struct ipadm_addrobj_s ipaddr;
1412 boolean_t is_persistent =
1413 (ipadm_flags & IPADM_OPT_PERSIST);
1414
1415 ret = i_ipadm_unplumb_if(iph, ifname, af);
1416 if (ret != IPADM_SUCCESS)
1417 goto done;
1418
1419 cp = strrchr(ifname, IPADM_LOGICAL_SEP);
1420 if (cp != NULL) {
1421 assert(iph->iph_flags & IPH_LEGACY);
1422 /*
1423 * This is a non-zero logical interface.
1424 * Find the addrobj and remove it from the daemon's memory.
1425 */
1426 (void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
1427 tmp_ifname[cp - ifname] = '\0';
1428 *cp++ = '\0';
1429 ipaddr.ipadm_lifnum = atoi(cp);
1430 (void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
1431 sizeof (ipaddr.ipadm_ifname));
1432 ipaddr.ipadm_af = af;
1433 ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
1434 if (ret == IPADM_SUCCESS) {
1435 ret = i_ipadm_delete_addrobj(iph, &ipaddr,
1436 IPADM_OPT_ACTIVE);
1437 } else if (ret == IPADM_NOTFOUND) {
1438 ret = IPADM_SUCCESS;
1439 }
1440 return (ret);
1441 }
1442 done:
1443 /*
1444 * Even if interface does not exist, remove all its addresses and
1445 * properties from the persistent store. If interface does not
1446 * exist both in kernel and the persistent store, return IPADM_ENXIO.
1447 */
1448 if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
1449 db_status = i_ipadm_delete_ifobj(iph, ifname, af,
1450 is_persistent);
1451 if (db_status == IPADM_SUCCESS)
1452 ret = IPADM_SUCCESS;
1453 }
1454
1455 return (ret);
1456 }
1457
1458 /*
1459 * Resets all addresses on interface `ifname' with address family `af'
1460 * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
1461 * and address objects of `ifname' for `af' are also removed from the
1462 * persistent DB.
1463 */
1464 ipadm_status_t
i_ipadm_delete_ifobj(ipadm_handle_t iph,const char * ifname,sa_family_t af,boolean_t is_persistent)1465 i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1466 boolean_t is_persistent)
1467 {
1468 ipmgmt_if_arg_t ifarg;
1469 int err;
1470
1471 ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
1472 ifarg.ia_flags = IPMGMT_ACTIVE;
1473 if (is_persistent)
1474 ifarg.ia_flags |= IPMGMT_PERSIST;
1475 ifarg.ia_family = af;
1476 (void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
1477
1478 err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1479 return (ipadm_errno2status(err));
1480 }
1481
1482 /*
1483 * Create the interface by plumbing it for IP.
1484 * This function will check if there is saved configuration information
1485 * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
1486 * for `ifname' is taken.
1487 */
1488 ipadm_status_t
i_ipadm_create_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t ipadm_flags)1489 i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1490 uint32_t ipadm_flags)
1491 {
1492 ipadm_status_t status;
1493 boolean_t p_exists;
1494 sa_family_t other_af;
1495
1496 /*
1497 * Return error, if the interface already exists in either the active
1498 * or the persistent configuration.
1499 */
1500 if (ipadm_if_enabled(iph, ifname, af))
1501 return (IPADM_IF_EXISTS);
1502
1503 if (!(iph->iph_flags & IPH_LEGACY)) {
1504 status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1505 if (status != IPADM_SUCCESS)
1506 return (status);
1507 other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1508 if (p_exists) {
1509 if (!ipadm_if_enabled(iph, ifname, other_af))
1510 return (IPADM_OP_DISABLE_OBJ);
1511 else
1512 ipadm_flags &= ~IPADM_OPT_PERSIST;
1513 }
1514 }
1515
1516 return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
1517 }
1518
1519 /*
1520 * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
1521 * default, unless a value in `af' is specified. The interface may be plumbed
1522 * only if there is no previously saved persistent configuration information
1523 * for the interface (in which case the ipadm_enable_if() function must
1524 * be used to enable the interface).
1525 *
1526 * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
1527 * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
1528 * or appropriate ipadm_status_t corresponding to the errno.
1529 *
1530 * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
1531 * be over-written with the actual interface name when a PPA has to be
1532 * internally generated by the library.
1533 */
1534 ipadm_status_t
ipadm_create_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t flags)1535 ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1536 uint32_t flags)
1537 {
1538 ipadm_status_t status;
1539 boolean_t created_v4 = B_FALSE;
1540 char newifname[LIFNAMSIZ];
1541
1542 /* Check for the required authorization */
1543 if (!ipadm_check_auth())
1544 return (IPADM_EAUTH);
1545
1546 if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
1547 !(flags & IPADM_OPT_ACTIVE)) ||
1548 (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
1549 IPADM_OPT_GENPPA))) {
1550 return (IPADM_INVALID_ARG);
1551 }
1552 if (flags & IPADM_OPT_GENPPA) {
1553 if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
1554 LIFNAMSIZ)
1555 return (IPADM_INVALID_ARG);
1556 } else {
1557 if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
1558 return (IPADM_INVALID_ARG);
1559 }
1560
1561 if (!i_ipadm_validate_ifname(iph, newifname))
1562 return (IPADM_INVALID_ARG);
1563
1564 if ((af == AF_INET || af == AF_UNSPEC) &&
1565 !i_ipadm_is_6to4(iph, ifname)) {
1566 status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
1567 if (status != IPADM_SUCCESS)
1568 return (status);
1569 created_v4 = B_TRUE;
1570 }
1571 if (af == AF_INET6 || af == AF_UNSPEC) {
1572 status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
1573 if (status != IPADM_SUCCESS) {
1574 if (created_v4) {
1575 (void) i_ipadm_delete_if(iph, ifname, AF_INET,
1576 IPADM_OPT_ACTIVE);
1577 }
1578 return (status);
1579 }
1580 }
1581
1582 return (IPADM_SUCCESS);
1583 }
1584
1585 ipadm_status_t
ipadm_add_ipmp_member(ipadm_handle_t iph,const char * gifname,const char * mifname,uint32_t ipadm_flags)1586 ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
1587 const char *mifname, uint32_t ipadm_flags)
1588 {
1589 return (i_ipadm_update_ipmp(iph, gifname, mifname,
1590 ipadm_flags, IPADM_ADD_IPMP));
1591 }
1592
1593 ipadm_status_t
ipadm_remove_ipmp_member(ipadm_handle_t iph,const char * gifname,const char * mifname,uint32_t ipadm_flags)1594 ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
1595 const char *mifname, uint32_t ipadm_flags)
1596 {
1597 return (i_ipadm_update_ipmp(iph, gifname, mifname,
1598 ipadm_flags, IPADM_REMOVE_IPMP));
1599 }
1600
1601 /*
1602 * Updates active IPMP configuration according to the specified
1603 * command. It also persists the configuration if IPADM_OPT_PERSIST
1604 * is set in `ipadm_flags'.
1605 */
1606 static ipadm_status_t
i_ipadm_update_ipmp(ipadm_handle_t iph,const char * gifname,const char * mifname,uint32_t ipadm_flags,ipadm_ipmp_op_t op)1607 i_ipadm_update_ipmp(ipadm_handle_t iph, const char *gifname,
1608 const char *mifname, uint32_t ipadm_flags, ipadm_ipmp_op_t op)
1609 {
1610 ipadm_status_t status;
1611 char groupname1[LIFGRNAMSIZ];
1612 char groupname2[LIFGRNAMSIZ];
1613
1614 /* Check for the required authorization */
1615 if (!ipadm_check_auth())
1616 return (IPADM_EAUTH);
1617
1618 if (!(ipadm_flags & IPADM_OPT_ACTIVE) ||
1619 gifname == NULL || mifname == NULL)
1620 return (IPADM_INVALID_ARG);
1621
1622 if (!ipadm_if_enabled(iph, gifname, AF_UNSPEC) ||
1623 !ipadm_if_enabled(iph, mifname, AF_UNSPEC))
1624 return (IPADM_OP_DISABLE_OBJ);
1625
1626 if (!i_ipadm_is_ipmp(iph, gifname))
1627 return (IPADM_INVALID_ARG);
1628
1629 if (op == IPADM_ADD_IPMP && i_ipadm_is_under_ipmp(iph, mifname))
1630 return (IPADM_IF_INUSE);
1631
1632 if ((status = i_ipadm_get_groupname_active(iph, gifname,
1633 groupname2, sizeof (groupname2))) != IPADM_SUCCESS)
1634 return (status);
1635
1636 if (op == IPADM_REMOVE_IPMP) {
1637 if ((status = i_ipadm_get_groupname_active(iph, mifname,
1638 groupname1, sizeof (groupname1))) != IPADM_SUCCESS)
1639 return (status);
1640
1641 if (groupname1[0] == '\0' ||
1642 strcmp(groupname1, groupname2) != 0)
1643 return (IPADM_INVALID_ARG);
1644
1645 groupname2[0] = '\0';
1646 }
1647
1648 if ((ipadm_flags & IPADM_OPT_PERSIST) &&
1649 (status = i_ipadm_persist_update_ipmp(iph, gifname,
1650 mifname, op)) != IPADM_SUCCESS)
1651 return (status);
1652
1653 return (i_ipadm_set_groupname_active(iph, mifname, groupname2));
1654 }
1655
1656 /*
1657 * Call the ipmgmtd to update the IPMP configuration in ipadm DB.
1658 * After this call the DB will know that mifname is under gifname and
1659 * gifname has a member, which name is mifname.
1660 */
1661 static ipadm_status_t
i_ipadm_persist_update_ipmp(ipadm_handle_t iph,const char * gifname,const char * mifname,ipadm_ipmp_op_t op)1662 i_ipadm_persist_update_ipmp(ipadm_handle_t iph, const char *gifname,
1663 const char *mifname, ipadm_ipmp_op_t op)
1664 {
1665 ipmgmt_ipmp_update_arg_t args;
1666 int err;
1667
1668 assert(op == IPADM_ADD_IPMP || op == IPADM_REMOVE_IPMP);
1669
1670 bzero(&args, sizeof (ipmgmt_ipmp_update_arg_t));
1671
1672 args.ia_cmd = IPMGMT_CMD_IPMP_UPDATE;
1673
1674 (void) strlcpy(args.ia_gifname, gifname, sizeof (args.ia_gifname));
1675 (void) strlcpy(args.ia_mifname, mifname, sizeof (args.ia_mifname));
1676
1677 if (op == IPADM_ADD_IPMP)
1678 args.ia_flags = IPMGMT_APPEND;
1679 else
1680 args.ia_flags = IPMGMT_REMOVE;
1681
1682 args.ia_flags |= IPMGMT_PERSIST;
1683
1684 err = ipadm_door_call(iph, &args, sizeof (args), NULL, 0, B_FALSE);
1685 return (ipadm_errno2status(err));
1686 }
1687
1688 /*
1689 * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
1690 * when `af' = AF_UNSPEC.
1691 */
1692 ipadm_status_t
ipadm_delete_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t flags)1693 ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1694 uint32_t flags)
1695 {
1696 ipadm_status_t status1 = IPADM_SUCCESS;
1697 ipadm_status_t status2 = IPADM_SUCCESS;
1698 ipadm_status_t other;
1699
1700 /* Check for the required authorization */
1701 if (!ipadm_check_auth())
1702 return (IPADM_EAUTH);
1703
1704 /* Validate the `ifname' for any logical interface. */
1705 if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
1706 !i_ipadm_validate_ifname(iph, ifname))
1707 return (IPADM_INVALID_ARG);
1708
1709 if (af == AF_INET || af == AF_UNSPEC)
1710 status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
1711 if (af == AF_INET6 || af == AF_UNSPEC)
1712 status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
1713 /*
1714 * If the family has been uniquely identified, we return the
1715 * associated status, even if that is ENXIO. Calls from ifconfig
1716 * which can only unplumb one of IPv4/IPv6 at any time fall under
1717 * this category.
1718 */
1719 if (af == AF_INET)
1720 return (status1);
1721 else if (af == AF_INET6)
1722 return (status2);
1723 else if (af != AF_UNSPEC)
1724 return (IPADM_INVALID_ARG);
1725
1726 /*
1727 * If af is AF_UNSPEC, then we return the following:
1728 * status1, if status1 == status2
1729 * IPADM_SUCCESS, if either of status1 or status2 is SUCCESS
1730 * and the other status is ENXIO
1731 * IPADM_ENXIO, if both status1 and status2 are ENXIO
1732 * IPADM_FAILURE otherwise.
1733 */
1734 if (status1 == status2) {
1735 /* covers the case when both status1 and status2 are ENXIO */
1736 return (status1);
1737 } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1738 if (status1 == IPADM_SUCCESS)
1739 other = status2;
1740 else
1741 other = status1;
1742 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1743 } else {
1744 return (IPADM_FAILURE);
1745 }
1746 }
1747
1748 /*
1749 * Returns information about all interfaces in both active and persistent
1750 * configuration. If `ifname' is not NULL, it returns only the interface
1751 * identified by `ifname'.
1752 *
1753 * Return values:
1754 * On success: IPADM_SUCCESS.
1755 * On error : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
1756 */
1757 ipadm_status_t
ipadm_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info,uint32_t flags,int64_t lifc_flags)1758 ipadm_if_info(ipadm_handle_t iph, const char *ifname,
1759 ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags)
1760 {
1761 ipadm_status_t status;
1762 ifspec_t ifsp;
1763
1764 if (if_info == NULL || iph == NULL || flags != 0)
1765 return (IPADM_INVALID_ARG);
1766
1767 if (ifname != NULL &&
1768 (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
1769 return (IPADM_INVALID_ARG);
1770 }
1771
1772 status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
1773 if (status != IPADM_SUCCESS)
1774 return (status);
1775 if (ifname != NULL && *if_info == NULL)
1776 return (IPADM_ENXIO);
1777
1778 return (IPADM_SUCCESS);
1779 }
1780
1781 /*
1782 * Frees the linked list allocated by ipadm_if_info().
1783 */
1784 void
ipadm_free_if_info(ipadm_if_info_t * ifinfo)1785 ipadm_free_if_info(ipadm_if_info_t *ifinfo)
1786 {
1787 ipadm_if_info_t *ifinfo_next;
1788
1789 for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1790 ifinfo_next = ifinfo->ifi_next;
1791 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_cmembers);
1792 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_pmembers);
1793 free(ifinfo);
1794 }
1795 }
1796
1797 static void
i_ipadm_free_ipmp_members(ipadm_ipmp_members_t * ipmp_members)1798 i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *ipmp_members)
1799 {
1800 ipadm_ipmp_member_t *ipmp_member;
1801
1802 while ((ipmp_member = list_remove_head(ipmp_members)) != NULL)
1803 free(ipmp_member);
1804
1805 list_destroy(ipmp_members);
1806 }
1807
1808 /*
1809 * Re-enable the interface `ifname' based on the saved configuration
1810 * for `ifname'.
1811 */
1812 ipadm_status_t
ipadm_enable_if(ipadm_handle_t iph,const char * ifname,uint32_t flags)1813 ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1814 {
1815 boolean_t set_init = B_FALSE;
1816 nvlist_t *ifnvl;
1817 ipadm_status_t status;
1818 ifspec_t ifsp;
1819
1820 /* Check for the required authorization */
1821 if (!ipadm_check_auth())
1822 return (IPADM_EAUTH);
1823
1824 /* Check for logical interfaces. */
1825 if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1826 return (IPADM_INVALID_ARG);
1827
1828 /* Enabling an interface persistently is not supported. */
1829 if (flags & IPADM_OPT_PERSIST)
1830 return (IPADM_NOTSUP);
1831
1832 /*
1833 * Return early by checking if the interface is already enabled.
1834 */
1835 if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1836 ipadm_if_enabled(iph, ifname, AF_INET6))
1837 return (IPADM_IF_EXISTS);
1838
1839 /*
1840 * Enable the interface and restore all its interface properties
1841 * and address objects.
1842 */
1843 status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
1844 if (status != IPADM_SUCCESS)
1845 return (status);
1846
1847 assert(ifnvl != NULL);
1848 /*
1849 * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
1850 * but only for one interface. We need to set IPH_INIT because
1851 * ipmgmtd daemon does not have to write the interface to the
1852 * persistent db. The interface is already available in the
1853 * persistent db and we are here to re-enable the persistent
1854 * configuration.
1855 *
1856 * But we need to make sure we're not accidentally clearing an
1857 * IPH_INIT flag that was already set when we were called.
1858 */
1859 if ((iph->iph_flags & IPH_INIT) == 0) {
1860 iph->iph_flags |= IPH_INIT;
1861 set_init = B_TRUE;
1862 }
1863
1864 status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1865
1866 if (set_init)
1867 iph->iph_flags &= ~IPH_INIT;
1868
1869 nvlist_free(ifnvl);
1870 return (status);
1871 }
1872
1873 /*
1874 * Disable the interface `ifname' by removing it from the active configuration.
1875 * Error code return values follow the model in ipadm_delete_if()
1876 */
1877 ipadm_status_t
ipadm_disable_if(ipadm_handle_t iph,const char * ifname,uint32_t flags)1878 ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1879 {
1880 ipadm_status_t status1, status2, other;
1881 ifspec_t ifsp;
1882
1883 /* Check for the required authorization */
1884 if (!ipadm_check_auth())
1885 return (IPADM_EAUTH);
1886
1887 /* Check for logical interfaces. */
1888 if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1889 return (IPADM_INVALID_ARG);
1890
1891 /* Disabling an interface persistently is not supported. */
1892 if (flags & IPADM_OPT_PERSIST)
1893 return (IPADM_NOTSUP);
1894
1895 status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1896 if (status1 == IPADM_SUCCESS)
1897 status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1898 status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1899 if (status2 == IPADM_SUCCESS)
1900 status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1901 if (status1 == status2) {
1902 return (status2);
1903 } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1904 if (status1 == IPADM_SUCCESS)
1905 other = status2;
1906 else
1907 other = status1;
1908 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1909 } else {
1910 return (IPADM_FAILURE);
1911 }
1912 }
1913
1914 /*
1915 * FIXME Remove this when ifconfig(8) is updated to use IPMP support
1916 * in libipadm.
1917 */
1918 /*
1919 * This workaround is required by ifconfig(8) whenever an
1920 * interface is moved into an IPMP group to update the daemon's
1921 * in-memory mapping of `aobjname' to 'lifnum'.
1922 *
1923 * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1924 * door_call(3C) fails. Also, there is no use in returning error because
1925 * `ifname' would have been successfuly moved into IPMP group, by this time.
1926 */
1927 void
ipadm_if_move(ipadm_handle_t iph,const char * ifname)1928 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1929 {
1930 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1931 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1932 }
1933
1934 ipadm_status_t
i_ipadm_set_groupname_active(ipadm_handle_t iph,const char * ifname,const char * groupname)1935 i_ipadm_set_groupname_active(ipadm_handle_t iph, const char *ifname,
1936 const char *groupname)
1937 {
1938 struct lifreq lifr;
1939 ipadm_addr_info_t *addrinfo, *ia;
1940 ipadm_status_t status = IPADM_SUCCESS;
1941
1942 (void) memset(&lifr, 0, sizeof (lifr));
1943
1944 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1945 (void) strlcpy(lifr.lifr_groupname, groupname,
1946 sizeof (lifr.lifr_groupname));
1947
1948 /* Disable all addresses on the interface */
1949 (void) i_ipadm_active_addr_info(iph, ifname, &addrinfo,
1950 IPADM_OPT_ACTIVE | IPADM_OPT_ZEROADDR, IFF_UP | IFF_DUPLICATE);
1951
1952 for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia)) {
1953 if (strlen(ia->ia_aobjname) > 0) {
1954 (void) ipadm_disable_addr(iph, ia->ia_aobjname, 0);
1955 } else {
1956 /*
1957 * There's an address on this interfaces with no
1958 * corresponding addrobj. Just clear IFF_UP.
1959 */
1960 (void) i_ipadm_set_flags(iph, ifname,
1961 addrinfo->ia_ifa.ifa_addr->sa_family, 0, IFF_UP);
1962 }
1963 }
1964
1965 if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1966 ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1)
1967 status = ipadm_errno2status(errno);
1968
1969 /* Enable all addresses on the interface */
1970 for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia)) {
1971 if (strlen(ia->ia_aobjname) > 0) {
1972 (void) ipadm_enable_addr(iph, ia->ia_aobjname, 0);
1973 } else {
1974 /*
1975 * There's an address on this interfaces with no
1976 * corresponding addrobj. Just set IFF_UP.
1977 */
1978 (void) i_ipadm_set_flags(iph, ifname,
1979 addrinfo->ia_ifa.ifa_addr->sa_family, IFF_UP, 0);
1980 }
1981 }
1982
1983 if (status == IPADM_SUCCESS) {
1984 if (groupname[0] == '\0') {
1985 /*
1986 * If interface was removed from IPMP group, unset the
1987 * DEPRECATED and NOFAILOVER flags.
1988 */
1989 (void) i_ipadm_set_flags(iph, ifname, AF_INET, 0,
1990 IFF_DEPRECATED | IFF_NOFAILOVER);
1991 (void) i_ipadm_set_flags(iph, ifname, AF_INET6, 0,
1992 IFF_DEPRECATED | IFF_NOFAILOVER);
1993 } else if (addrinfo == NULL) {
1994 /*
1995 * If interface was added to IPMP group and there are no
1996 * active addresses, explicitly bring it up to be used
1997 * for link-based IPMP configuration.
1998 */
1999 (void) i_ipadm_set_flags(iph, ifname, AF_INET,
2000 IFF_UP, 0);
2001 (void) i_ipadm_set_flags(iph, ifname, AF_INET6,
2002 IFF_UP, 0);
2003 }
2004 }
2005
2006 ipadm_free_addr_info(addrinfo);
2007
2008 return (status);
2009 }
2010
2011 ipadm_status_t
i_ipadm_get_groupname_active(ipadm_handle_t iph,const char * ifname,char * groupname,size_t size)2012 i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
2013 char *groupname, size_t size)
2014 {
2015 struct lifreq lifr;
2016
2017 (void) memset(&lifr, 0, sizeof (lifr));
2018
2019 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
2020
2021 if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
2022 ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1)
2023 return (ipadm_errno2status(errno));
2024
2025 (void) strlcpy(groupname, lifr.lifr_groupname, size);
2026
2027 return (IPADM_SUCCESS);
2028 }
2029
2030 /*
2031 * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
2032 */
2033 boolean_t
i_ipadm_is_under_ipmp(ipadm_handle_t iph,const char * ifname)2034 i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
2035 {
2036
2037 char groupname[LIFGRNAMSIZ];
2038
2039 if (i_ipadm_get_groupname_active(iph, ifname, groupname,
2040 sizeof (groupname)) != IPADM_SUCCESS ||
2041 groupname[0] == '\0' ||
2042 strcmp(ifname, groupname) == 0)
2043 return (B_FALSE);
2044
2045 return (B_TRUE);
2046 }
2047
2048 /*
2049 * Returns B_TRUE if `ifname' represents an IPMP group interface.
2050 */
2051 boolean_t
i_ipadm_is_ipmp(ipadm_handle_t iph,const char * ifname)2052 i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
2053 {
2054 uint64_t flags;
2055
2056 if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
2057 i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS)
2058 return (B_FALSE);
2059
2060 return ((flags & IFF_IPMP) != 0);
2061 }
2062