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