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 */
24
25 #include <errno.h>
26 #include <sys/sockio.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <stropts.h>
31 #include <strings.h>
32 #include <libdlpi.h>
33 #include <libdllink.h>
34 #include <libinetutil.h>
35 #include <inet/ip.h>
36 #include <limits.h>
37 #include <zone.h>
38 #include <ipadm_ndpd.h>
39 #include "libipadm_impl.h"
40
41 static ipadm_status_t i_ipadm_slifname_arp(char *, uint64_t, int);
42 static ipadm_status_t i_ipadm_slifname(ipadm_handle_t, char *, char *,
43 uint64_t, int, uint32_t);
44 static ipadm_status_t i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
45 sa_family_t);
46 static ipadm_status_t i_ipadm_persist_if(ipadm_handle_t, const char *,
47 sa_family_t);
48
49 /*
50 * Returns B_FALSE if the interface in `ifname' has at least one address that is
51 * IFF_UP in the addresses in `ifa'.
52 */
53 static boolean_t
i_ipadm_is_if_down(char * ifname,struct ifaddrs * ifa)54 i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
55 {
56 struct ifaddrs *ifap;
57 char cifname[LIFNAMSIZ];
58 char *sep;
59
60 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
61 (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
62 if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
63 *sep = '\0';
64 /*
65 * If this condition is true, there is at least one
66 * address that is IFF_UP. So, we need to return B_FALSE.
67 */
68 if (strcmp(cifname, ifname) == 0 &&
69 (ifap->ifa_flags & IFF_UP)) {
70 return (B_FALSE);
71 }
72 }
73 /* We did not find any IFF_UP addresses. */
74 return (B_TRUE);
75 }
76
77 /*
78 * Retrieves the information for the interface `ifname' from active
79 * config if `ifname' is specified and returns the result in the list `if_info'.
80 * Otherwise, it retrieves the information for all the interfaces in
81 * the active config and returns the result in the list `if_info'.
82 */
83 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)84 i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname,
85 ipadm_if_info_t **if_info, int64_t lifc_flags)
86 {
87 struct lifreq *buf;
88 struct lifreq *lifrp;
89 struct lifreq lifrl;
90 ipadm_if_info_t *last = NULL;
91 ipadm_if_info_t *ifp;
92 int s;
93 int n;
94 int numifs;
95 ipadm_status_t status;
96
97 *if_info = NULL;
98 /*
99 * Get information for all interfaces.
100 */
101 if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
102 return (ipadm_errno2status(errno));
103
104 lifrp = buf;
105 for (n = 0; n < numifs; n++, lifrp++) {
106 /* Skip interfaces with logical num != 0 */
107 if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
108 continue;
109 /*
110 * Skip the current interface if a specific `ifname' has
111 * been requested and current interface does not match
112 * `ifname'.
113 */
114 if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
115 continue;
116 /*
117 * Check if the interface already exists in our list.
118 * If it already exists, we need to update its flags.
119 */
120 for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
121 if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
122 break;
123 }
124 if (ifp == NULL) {
125 ifp = calloc(1, sizeof (ipadm_if_info_t));
126 if (ifp == NULL) {
127 status = ipadm_errno2status(errno);
128 goto fail;
129 }
130 (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
131 sizeof (ifp->ifi_name));
132 /* Update the `ifi_next' pointer for this new node */
133 if (*if_info == NULL)
134 *if_info = ifp;
135 else
136 last->ifi_next = ifp;
137 last = ifp;
138 }
139
140 /*
141 * Retrieve the flags for the interface by doing a
142 * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
143 */
144 (void) strlcpy(lifrl.lifr_name,
145 lifrp->lifr_name, sizeof (lifrl.lifr_name));
146 s = (lifrp->lifr_addr.ss_family == AF_INET) ?
147 iph->iph_sock : iph->iph_sock6;
148 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
149 continue;
150 if (lifrl.lifr_flags & IFF_BROADCAST)
151 ifp->ifi_cflags |= IFIF_BROADCAST;
152 if (lifrl.lifr_flags & IFF_MULTICAST)
153 ifp->ifi_cflags |= IFIF_MULTICAST;
154 if (lifrl.lifr_flags & IFF_POINTOPOINT)
155 ifp->ifi_cflags |= IFIF_POINTOPOINT;
156 if (lifrl.lifr_flags & IFF_VIRTUAL)
157 ifp->ifi_cflags |= IFIF_VIRTUAL;
158 if (lifrl.lifr_flags & IFF_IPMP)
159 ifp->ifi_cflags |= IFIF_IPMP;
160 if (lifrl.lifr_flags & IFF_STANDBY)
161 ifp->ifi_cflags |= IFIF_STANDBY;
162 if (lifrl.lifr_flags & IFF_INACTIVE)
163 ifp->ifi_cflags |= IFIF_INACTIVE;
164 if (lifrl.lifr_flags & IFF_VRRP)
165 ifp->ifi_cflags |= IFIF_VRRP;
166 if (lifrl.lifr_flags & IFF_NOACCEPT)
167 ifp->ifi_cflags |= IFIF_NOACCEPT;
168 if (lifrl.lifr_flags & IFF_IPV4)
169 ifp->ifi_cflags |= IFIF_IPV4;
170 if (lifrl.lifr_flags & IFF_IPV6)
171 ifp->ifi_cflags |= IFIF_IPV6;
172 if (lifrl.lifr_flags & IFF_L3PROTECT)
173 ifp->ifi_cflags |= IFIF_L3PROTECT;
174 }
175 free(buf);
176 return (IPADM_SUCCESS);
177 fail:
178 free(buf);
179 ipadm_free_if_info(*if_info);
180 *if_info = NULL;
181 return (status);
182 }
183
184 /*
185 * Returns the interface information for `ifname' in `if_info' from persistent
186 * config if `ifname' is non-null. Otherwise, it returns all the interfaces
187 * from persistent config in `if_info'.
188 */
189 static ipadm_status_t
i_ipadm_persist_if_info(ipadm_handle_t iph,const char * ifname,ipadm_if_info_t ** if_info)190 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
191 ipadm_if_info_t **if_info)
192 {
193 ipadm_status_t status = IPADM_SUCCESS;
194 ipmgmt_getif_arg_t getif;
195 ipmgmt_getif_rval_t *rvalp;
196 ipadm_if_info_t *ifp, *curr, *prev = NULL;
197 int i = 0, err = 0;
198
199 bzero(&getif, sizeof (getif));
200 if (ifname != NULL)
201 (void) strlcpy(getif.ia_ifname, ifname, LIFNAMSIZ);
202 getif.ia_cmd = IPMGMT_CMD_GETIF;
203
204 *if_info = NULL;
205
206 if ((rvalp = malloc(sizeof (ipmgmt_getif_rval_t))) == NULL)
207 return (ipadm_errno2status(errno));
208 err = ipadm_door_call(iph, &getif, sizeof (getif), (void **)&rvalp,
209 sizeof (*rvalp), B_TRUE);
210 if (err == ENOENT) {
211 free(rvalp);
212 if (ifname != NULL)
213 return (ipadm_errno2status(err));
214 return (IPADM_SUCCESS);
215 } else if (err != 0) {
216 free(rvalp);
217 return (ipadm_errno2status(err));
218 }
219
220 ifp = rvalp->ir_ifinfo;
221 for (i = 0; i < rvalp->ir_ifcnt; i++) {
222 ifp = rvalp->ir_ifinfo + i;
223 if ((curr = malloc(sizeof (*curr))) == NULL) {
224 status = ipadm_errno2status(errno);
225 ipadm_free_if_info(prev);
226 break;
227 }
228 (void) bcopy(ifp, curr, sizeof (*curr));
229 curr->ifi_next = prev;
230 prev = curr;
231 }
232 *if_info = curr;
233 free(rvalp);
234 return (status);
235 }
236
237 /*
238 * Collects information for `ifname' if one is specified from both
239 * active and persistent config in `if_info'. If no `ifname' is specified,
240 * this returns all the interfaces in active and persistent config in
241 * `if_info'.
242 */
243 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)244 i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
245 ipadm_if_info_t **if_info, int64_t lifc_flags)
246 {
247 ipadm_status_t status;
248 ipadm_if_info_t *aifinfo = NULL;
249 ipadm_if_info_t *pifinfo = NULL;
250 ipadm_if_info_t *aifp;
251 ipadm_if_info_t *pifp;
252 ipadm_if_info_t *last = NULL;
253 struct ifaddrs *ifa;
254 struct ifaddrs *ifap;
255
256 /*
257 * Retrive the information for the requested `ifname' or all
258 * interfaces from active configuration.
259 */
260 retry:
261 status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
262 if (status != IPADM_SUCCESS)
263 return (status);
264 /* Get the interface state for each interface in `aifinfo'. */
265 if (aifinfo != NULL) {
266 /* We need all addresses to get the interface state */
267 if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
268 LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
269 status = ipadm_errno2status(errno);
270 goto fail;
271 }
272 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
273 /*
274 * Find the `ifaddrs' structure from `ifa'
275 * for this interface. We need the IFF_* flags
276 * to find the interface state.
277 */
278 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
279 if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
280 break;
281 }
282 if (ifap == NULL) {
283 /*
284 * The interface might have been removed
285 * from kernel. Retry getting all the active
286 * interfaces.
287 */
288 freeifaddrs(ifa);
289 ipadm_free_if_info(aifinfo);
290 aifinfo = NULL;
291 goto retry;
292 }
293 if (!(ifap->ifa_flags & IFF_RUNNING) ||
294 (ifap->ifa_flags & IFF_FAILED))
295 aifp->ifi_state = IFIS_FAILED;
296 else if (ifap->ifa_flags & IFF_OFFLINE)
297 aifp->ifi_state = IFIS_OFFLINE;
298 else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
299 aifp->ifi_state = IFIS_DOWN;
300 else
301 aifp->ifi_state = IFIS_OK;
302 if (aifp->ifi_next == NULL)
303 last = aifp;
304 }
305 freeifaddrs(ifa);
306 }
307 /*
308 * Get the persistent interface information in `pifinfo'.
309 */
310 status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
311 if (status == IPADM_NOTFOUND) {
312 *if_info = aifinfo;
313 return (IPADM_SUCCESS);
314 }
315 if (status != IPADM_SUCCESS)
316 goto fail;
317 /*
318 * If a persistent interface is also found in `aifinfo', update
319 * its entry in `aifinfo' with the persistent information from
320 * `pifinfo'. If an interface is found in `pifinfo', but not in
321 * `aifinfo', it means that this interface was disabled. We should
322 * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
323 */
324 for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
325 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
326 if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
327 aifp->ifi_pflags = pifp->ifi_pflags;
328 break;
329 }
330 }
331 if (aifp == NULL) {
332 aifp = malloc(sizeof (ipadm_if_info_t));
333 if (aifp == NULL) {
334 status = ipadm_errno2status(errno);
335 goto fail;
336 }
337 *aifp = *pifp;
338 aifp->ifi_next = NULL;
339 aifp->ifi_state = IFIS_DISABLED;
340 if (last != NULL)
341 last->ifi_next = aifp;
342 else
343 aifinfo = aifp;
344 last = aifp;
345 }
346 }
347 *if_info = aifinfo;
348 ipadm_free_if_info(pifinfo);
349 return (IPADM_SUCCESS);
350 fail:
351 *if_info = NULL;
352 ipadm_free_if_info(aifinfo);
353 ipadm_free_if_info(pifinfo);
354 return (status);
355 }
356
357 int
i_ipadm_get_lnum(const char * ifname)358 i_ipadm_get_lnum(const char *ifname)
359 {
360 char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
361
362 if (num == NULL)
363 return (0);
364
365 return (atoi(++num));
366 }
367
368 /*
369 * Sets the output argument `exists' to true or false based on whether
370 * any persistent configuration is available for `ifname' and returns
371 * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
372 * `exists' is unmodified and an error status is returned.
373 */
374 ipadm_status_t
i_ipadm_if_pexists(ipadm_handle_t iph,const char * ifname,sa_family_t af,boolean_t * exists)375 i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
376 boolean_t *exists)
377 {
378 ipadm_if_info_t *ifinfo;
379 ipadm_status_t status;
380
381 /*
382 * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
383 * knows about persistent configuration in the first place, so we
384 * just return success.
385 */
386 if (iph->iph_flags & IPH_IPMGMTD) {
387 *exists = B_FALSE;
388 return (IPADM_SUCCESS);
389 }
390 status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
391 if (status == IPADM_SUCCESS) {
392 *exists = ((af == AF_INET &&
393 (ifinfo->ifi_pflags & IFIF_IPV4)) ||
394 (af == AF_INET6 &&
395 (ifinfo->ifi_pflags & IFIF_IPV6)));
396 free(ifinfo);
397 } else if (status == IPADM_NOTFOUND) {
398 status = IPADM_SUCCESS;
399 *exists = B_FALSE;
400 }
401 return (status);
402 }
403
404 /*
405 * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
406 * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
407 * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
408 * the bottom of the stream for tunneling interfaces.
409 */
410 ipadm_status_t
ipadm_open_arp_on_udp(const char * udp_dev_name,int * fd)411 ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
412 {
413 int err;
414
415 if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
416 return (ipadm_errno2status(errno));
417
418 /*
419 * Pop off all undesired modules (note that the user may have
420 * configured autopush to add modules above udp), and push the
421 * arp module onto the resulting stream. This is used to make
422 * IP+ARP be able to atomically track the muxid for the I_PLINKed
423 * STREAMS, thus it isn't related to ARP running the ARP protocol.
424 */
425 while (ioctl(*fd, I_POP, 0) != -1)
426 ;
427 if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
428 return (IPADM_SUCCESS);
429 err = errno;
430 (void) close(*fd);
431
432 return (ipadm_errno2status(err));
433 }
434
435 /*
436 * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
437 * underlying interface in an ipmp group G is plumbed for an address family,
438 * but the meta-interface for the other address family `af' does not exist
439 * yet for the group G. If `af' is IPv6, we need to bring up the
440 * link-local address.
441 */
442 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)443 i_ipadm_create_ipmp(ipadm_handle_t iph, char *ifname, sa_family_t af,
444 const char *grname, uint32_t ipadm_flags)
445 {
446 ipadm_status_t status;
447 struct lifreq lifr;
448 int sock;
449 int err;
450
451 assert(ipadm_flags & IPADM_OPT_IPMP);
452
453 /* Create the ipmp underlying interface */
454 status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
455 if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
456 return (status);
457
458 /*
459 * To preserve backward-compatibility, always bring up the link-local
460 * address for implicitly-created IPv6 IPMP interfaces.
461 */
462 if (af == AF_INET6)
463 (void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
464
465 sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
466 /*
467 * If the caller requested a different group name, issue a
468 * SIOCSLIFGROUPNAME on the new IPMP interface.
469 */
470 bzero(&lifr, sizeof (lifr));
471 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
472 if (strcmp(lifr.lifr_name, grname) != 0) {
473 (void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
474 if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
475 err = errno;
476 /* Remove the interface we created. */
477 if (status == IPADM_SUCCESS) {
478 (void) i_ipadm_delete_if(iph, ifname, af,
479 ipadm_flags);
480 }
481 return (ipadm_errno2status(err));
482 }
483 }
484
485 return (IPADM_SUCCESS);
486 }
487
488 /*
489 * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
490 * family. If so, create a matching IPMP group for address family `af'.
491 */
492 static ipadm_status_t
i_ipadm_create_ipmp_peer(ipadm_handle_t iph,char * ifname,sa_family_t af)493 i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
494 {
495 lifgroupinfo_t lifgr;
496 ipadm_status_t status = IPADM_SUCCESS;
497 struct lifreq lifr;
498 int other_af_sock;
499
500 assert(af == AF_INET || af == AF_INET6);
501
502 other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
503
504 /*
505 * iph is the handle for the interface that we are trying to plumb.
506 * other_af_sock is the socket for the "other" address family.
507 */
508 bzero(&lifr, sizeof (lifr));
509 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
510 if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
511 return (IPADM_SUCCESS);
512
513 (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
514 if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
515 return (IPADM_SUCCESS);
516
517 /*
518 * If `ifname' *is* the IPMP group interface, or if the relevant
519 * address family is already configured, then there's nothing to do.
520 */
521 if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
522 (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
523 return (IPADM_SUCCESS);
524 }
525
526 status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
527 lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
528 return (status);
529 }
530
531 /*
532 * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
533 */
534 static ipadm_status_t
i_ipadm_slifname_arp(char * ifname,uint64_t flags,int fd)535 i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
536 {
537 struct lifreq lifr;
538 ifspec_t ifsp;
539
540 bzero(&lifr, sizeof (lifr));
541 (void) ifparse_ifspec(ifname, &ifsp);
542 lifr.lifr_ppa = ifsp.ifsp_ppa;
543 lifr.lifr_flags = flags;
544 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
545 /*
546 * Tell ARP the name and unit number for this interface.
547 * Note that arp has no support for transparent ioctls.
548 */
549 if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
550 sizeof (lifr)) == -1) {
551 return (ipadm_errno2status(errno));
552 }
553 return (IPADM_SUCCESS);
554 }
555
556 /*
557 * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
558 * `ipadm_flags', then a ppa will be generated. `newif' will be updated
559 * with the generated ppa.
560 */
561 static ipadm_status_t
i_ipadm_slifname(ipadm_handle_t iph,char * ifname,char * newif,uint64_t flags,int fd,uint32_t ipadm_flags)562 i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
563 int fd, uint32_t ipadm_flags)
564 {
565 struct lifreq lifr;
566 ipadm_status_t status = IPADM_SUCCESS;
567 int err = 0;
568 sa_family_t af;
569 int ppa;
570 ifspec_t ifsp;
571 boolean_t valid_if;
572
573 bzero(&lifr, sizeof (lifr));
574 if (ipadm_flags & IPADM_OPT_GENPPA) {
575 /*
576 * We'd like to just set lifr_ppa to UINT_MAX and have the
577 * kernel pick a PPA. Unfortunately, that would mishandle
578 * two cases:
579 *
580 * 1. If the PPA is available but the groupname is taken
581 * (e.g., the "ipmp2" IP interface name is available
582 * but the "ipmp2" groupname is taken) then the
583 * auto-assignment by the kernel will fail.
584 *
585 * 2. If we're creating (e.g.) an IPv6-only IPMP
586 * interface, and there's already an IPv4-only IPMP
587 * interface, the kernel will allow us to accidentally
588 * reuse the IPv6 IPMP interface name (since
589 * SIOCSLIFNAME uniqueness is per-interface-type).
590 * This will cause administrative confusion.
591 *
592 * Thus, we instead take a brute-force approach of checking
593 * whether the IPv4 or IPv6 name is already in-use before
594 * attempting the SIOCSLIFNAME. As per (1) above, the
595 * SIOCSLIFNAME may still fail, in which case we just proceed
596 * to the next one. If this approach becomes too slow, we
597 * can add a new SIOC* to handle this case in the kernel.
598 */
599 for (ppa = 0; ppa < UINT_MAX; ppa++) {
600 (void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
601 ifname, ppa);
602
603 if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
604 errno != ENXIO)
605 continue;
606
607 if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
608 errno != ENXIO)
609 continue;
610
611 lifr.lifr_ppa = ppa;
612 lifr.lifr_flags = flags;
613
614 err = ioctl(fd, SIOCSLIFNAME, &lifr);
615 if (err != -1 || errno != EEXIST)
616 break;
617 }
618 if (err == -1) {
619 status = ipadm_errno2status(errno);
620 } else {
621 /*
622 * PPA has been successfully established.
623 * Update `newif' with the ppa.
624 */
625 assert(newif != NULL);
626 if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
627 ppa) >= LIFNAMSIZ)
628 return (IPADM_INVALID_ARG);
629 }
630 } else {
631 /* We should have already validated the interface name. */
632 valid_if = ifparse_ifspec(ifname, &ifsp);
633 assert(valid_if);
634
635 /*
636 * Before we call SIOCSLIFNAME, ensure that the IPMP group
637 * interface for this address family exists. Otherwise, the
638 * kernel will kick the interface out of the group when we do
639 * the SIOCSLIFNAME.
640 *
641 * Example: suppose bge0 is plumbed for IPv4 and in group "a".
642 * If we're now plumbing bge0 for IPv6, but the IPMP group
643 * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
644 * will kick bge0 out of group "a", which is undesired.
645 */
646 if (flags & IFF_IPV4)
647 af = AF_INET;
648 else
649 af = AF_INET6;
650 status = i_ipadm_create_ipmp_peer(iph, ifname, af);
651 if (status != IPADM_SUCCESS)
652 return (status);
653 lifr.lifr_ppa = ifsp.ifsp_ppa;
654 lifr.lifr_flags = flags;
655 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
656 if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
657 status = ipadm_errno2status(errno);
658 }
659
660 return (status);
661 }
662
663 /*
664 * Plumbs the interface `ifname' for the address family `af'. It also persists
665 * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
666 */
667 ipadm_status_t
i_ipadm_plumb_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t ipadm_flags)668 i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
669 uint32_t ipadm_flags)
670 {
671 int ip_muxid;
672 int mux_fd = -1, ip_fd, arp_fd;
673 char *udp_dev_name;
674 dlpi_handle_t dh_arp = NULL, dh_ip;
675 uint64_t ifflags;
676 struct lifreq lifr;
677 uint_t dlpi_flags;
678 ipadm_status_t status = IPADM_SUCCESS;
679 char *linkname;
680 boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
681 zoneid_t zoneid;
682 char newif[LIFNAMSIZ];
683 char lifname[LIFNAMSIZ];
684 datalink_id_t linkid;
685 int sock;
686 boolean_t islo;
687 boolean_t is_persistent =
688 ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
689 uint32_t dlflags;
690 dladm_status_t dlstatus;
691
692 if (iph->iph_dlh != NULL) {
693 dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
694 &dlflags, NULL, NULL);
695 }
696 /*
697 * If we're in the global zone and we're plumbing a datalink, make
698 * sure that the datalink is not assigned to a non-global zone. Note
699 * that the non-global zones don't need this check, because zoneadm
700 * has taken care of this when the zones boot.
701 */
702 if (iph->iph_zoneid == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
703 zoneid = ALL_ZONES;
704 if (zone_check_datalink(&zoneid, linkid) == 0) {
705 /* interface is in use by a non-global zone. */
706 return (IPADM_IF_INUSE);
707 }
708 }
709
710 /* loopback interfaces are just added as logical interface */
711 bzero(&lifr, sizeof (lifr));
712 islo = i_ipadm_is_loopback(ifname);
713 if (islo || i_ipadm_get_lnum(ifname) != 0) {
714 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
715 if (af == AF_INET)
716 sock = iph->iph_sock;
717 else
718 sock = iph->iph_sock6;
719 if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
720 return (IPADM_IF_EXISTS);
721 if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
722 return (ipadm_errno2status(errno));
723
724 /*
725 * By default, kernel configures 127.0.0.1 on the loopback
726 * interface. Replace this with 0.0.0.0 to be consistent
727 * with interface creation on other physical interfaces.
728 */
729 if (islo && !legacy) {
730 bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
731 lifr.lifr_addr.ss_family = af;
732 if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
733 return (ipadm_errno2status(errno));
734 if (is_persistent) {
735 status = i_ipadm_persist_if(iph, ifname, af);
736 if (status != IPADM_SUCCESS) {
737 (void) i_ipadm_delete_if(iph, ifname,
738 af, IPADM_OPT_ACTIVE);
739 }
740 }
741 }
742 return (status);
743 }
744
745 dlpi_flags = DLPI_NOATTACH;
746
747 /*
748 * If IPADM_OPT_IPMP is specified, then this is a request
749 * to create an IPMP interface atop /dev/ipmpstub0. (We can't simply
750 * pass "ipmpstub0" as devname since an admin *could* have a normal
751 * vanity-named link named "ipmpstub0" that they'd like to plumb.)
752 */
753 if (ipadm_flags & IPADM_OPT_IPMP) {
754 dlpi_flags |= DLPI_DEVONLY;
755 linkname = "ipmpstub0";
756 } else {
757 /*
758 * Verify that the user is not creating a persistent
759 * IP interface on a non-persistent data-link.
760 */
761 if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
762 is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
763 return (IPADM_TEMPORARY_OBJ);
764 }
765 linkname = ifname;
766 }
767
768 /*
769 * We use DLPI_NOATTACH because the ip module will do the attach
770 * itself for DLPI style-2 devices.
771 */
772 if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
773 return (IPADM_DLPI_FAILURE);
774 ip_fd = dlpi_fd(dh_ip);
775 if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
776 status = ipadm_errno2status(errno);
777 goto done;
778 }
779
780 /*
781 * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
782 * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
783 */
784 ifflags = 0;
785
786 /* Set the name string and the IFF_IPV* flag */
787 if (af == AF_INET) {
788 ifflags = IFF_IPV4;
789 } else {
790 ifflags = IFF_IPV6;
791 /*
792 * With the legacy method, the link-local address should be
793 * configured as part of the interface plumb, using the default
794 * token. If IPH_LEGACY is not specified, we want to set :: as
795 * the address and require the admin to explicitly call
796 * ipadm_create_addr() with the address object type set to
797 * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
798 * as well as the autoconfigured addresses.
799 */
800 if (!legacy && !i_ipadm_is_6to4(iph, ifname))
801 ifflags |= IFF_NOLINKLOCAL;
802 }
803 (void) strlcpy(newif, ifname, sizeof (newif));
804 status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
805 ipadm_flags);
806 if (status != IPADM_SUCCESS)
807 goto done;
808
809 /* Get the full set of existing flags for this stream */
810 status = i_ipadm_get_flags(iph, newif, af, &ifflags);
811 if (status != IPADM_SUCCESS)
812 goto done;
813
814 udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
815 status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
816 if (status != IPADM_SUCCESS)
817 goto done;
818
819 /* Check if arp is not needed */
820 if (ifflags & (IFF_NOARP|IFF_IPV6)) {
821 /*
822 * PLINK the interface stream so that the application can exit
823 * without tearing down the stream.
824 */
825 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
826 status = ipadm_errno2status(errno);
827 goto done;
828 }
829
830 /*
831 * This interface does use ARP, so set up a separate stream
832 * from the interface to ARP.
833 *
834 * We use DLPI_NOATTACH because the arp module will do the attach
835 * itself for DLPI style-2 devices.
836 */
837 if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
838 status = IPADM_DLPI_FAILURE;
839 goto done;
840 }
841
842 arp_fd = dlpi_fd(dh_arp);
843 if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
844 status = ipadm_errno2status(errno);
845 goto done;
846 }
847
848 status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
849 if (status != IPADM_SUCCESS)
850 goto done;
851 /*
852 * PLINK the IP and ARP streams so that ifconfig can exit
853 * without tearing down the stream.
854 */
855 if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
856 status = ipadm_errno2status(errno);
857 goto done;
858 }
859
860 if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
861 status = ipadm_errno2status(errno);
862 (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
863 }
864
865 done:
866 dlpi_close(dh_ip);
867 if (dh_arp != NULL)
868 dlpi_close(dh_arp);
869
870 if (mux_fd != -1)
871 (void) close(mux_fd);
872
873 if (status == IPADM_SUCCESS) {
874 /* copy back new ifname */
875 (void) strlcpy(ifname, newif, LIFNAMSIZ);
876 /*
877 * If it is a 6to4 tunnel, create a default
878 * addrobj name for the default address on the 0'th
879 * logical interface and set IFF_UP in the interface flags.
880 */
881 if (i_ipadm_is_6to4(iph, ifname)) {
882 struct ipadm_addrobj_s addr;
883
884 i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
885 addr.ipadm_af = af;
886 status = i_ipadm_lookupadd_addrobj(iph, &addr);
887 if (status != IPADM_SUCCESS)
888 return (status);
889 status = ipadm_add_aobjname(iph, ifname,
890 af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
891 if (status != IPADM_SUCCESS)
892 return (status);
893 addr.ipadm_lifnum = 0;
894 i_ipadm_addrobj2lifname(&addr, lifname,
895 sizeof (lifname));
896 status = i_ipadm_set_flags(iph, lifname, af,
897 IFF_UP, 0);
898 if (status != IPADM_SUCCESS)
899 return (status);
900 } else {
901 /*
902 * Prevent static IPv6 addresses from triggering
903 * autoconf. This does not have to be done for
904 * 6to4 tunnel interfaces, since in.ndpd will
905 * not autoconfigure those interfaces.
906 */
907 if (af == AF_INET6 && !legacy)
908 (void) i_ipadm_disable_autoconf(newif);
909 }
910
911 /*
912 * If IPADM_OPT_PERSIST was set in flags, store the
913 * interface in persistent DB.
914 */
915 if (is_persistent) {
916 status = i_ipadm_persist_if(iph, newif, af);
917 if (status != IPADM_SUCCESS) {
918 (void) i_ipadm_delete_if(iph, newif, af,
919 IPADM_OPT_ACTIVE);
920 }
921 }
922 }
923 if (status == IPADM_EXISTS)
924 status = IPADM_IF_EXISTS;
925 return (status);
926 }
927
928 /*
929 * Unplumbs the interface in `ifname' of family `af'.
930 */
931 ipadm_status_t
i_ipadm_unplumb_if(ipadm_handle_t iph,const char * ifname,sa_family_t af)932 i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
933 {
934 int ip_muxid, arp_muxid;
935 int mux_fd = -1;
936 int muxid_fd = -1;
937 char *udp_dev_name;
938 uint64_t flags;
939 boolean_t changed_arp_muxid = B_FALSE;
940 int save_errno;
941 struct lifreq lifr;
942 ipadm_status_t ret = IPADM_SUCCESS;
943 int sock;
944 lifgroupinfo_t lifgr;
945 ifaddrlistx_t *ifaddrs, *ifaddrp;
946 boolean_t v6 = (af == AF_INET6);
947
948 /* Just do SIOCLIFREMOVEIF on loopback interfaces */
949 bzero(&lifr, sizeof (lifr));
950 if (i_ipadm_is_loopback(ifname) ||
951 (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
952 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
953 if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
954 SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
955 return (ipadm_errno2status(errno));
956 }
957 return (IPADM_SUCCESS);
958 }
959
960 /*
961 * We used /dev/udp or udp6 to set up the mux. So we have to use
962 * the same now for PUNLINK also.
963 */
964 if (v6) {
965 udp_dev_name = UDP6_DEV_NAME;
966 sock = iph->iph_sock6;
967 } else {
968 udp_dev_name = UDP_DEV_NAME;
969 sock = iph->iph_sock;
970 }
971 if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
972 ret = ipadm_errno2status(errno);
973 goto done;
974 }
975 ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
976 if (ret != IPADM_SUCCESS)
977 goto done;
978 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
979 if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
980 ret = ipadm_errno2status(errno);
981 goto done;
982 }
983 flags = lifr.lifr_flags;
984 again:
985 if (flags & IFF_IPMP) {
986 /*
987 * There are two reasons the I_PUNLINK can fail with EBUSY:
988 * (1) if IP interfaces are in the group, or (2) if IPMP data
989 * addresses are administratively up. For case (1), we fail
990 * here with a specific error message. For case (2), we bring
991 * down the addresses prior to doing the I_PUNLINK. If the
992 * I_PUNLINK still fails with EBUSY then the configuration
993 * must have changed after our checks, in which case we branch
994 * back up to `again' and rerun this logic. The net effect is
995 * that unplumbing an IPMP interface will only fail with EBUSY
996 * if IP interfaces are in the group.
997 */
998 if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
999 ret = ipadm_errno2status(errno);
1000 goto done;
1001 }
1002 (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
1003 LIFGRNAMSIZ);
1004 if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
1005 ret = ipadm_errno2status(errno);
1006 goto done;
1007 }
1008 if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
1009 ret = IPADM_GRP_NOTEMPTY;
1010 goto done;
1011 }
1012
1013 /*
1014 * The kernel will fail the I_PUNLINK if the IPMP interface
1015 * has administratively up addresses; bring them down.
1016 */
1017 if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
1018 0, &ifaddrs) == -1) {
1019 ret = ipadm_errno2status(errno);
1020 goto done;
1021 }
1022 ifaddrp = ifaddrs;
1023 for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
1024 int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
1025 iph->iph_sock : iph->iph_sock6;
1026 struct lifreq lifrl;
1027
1028 if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
1029 (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
1030 continue;
1031
1032 bzero(&lifrl, sizeof (lifrl));
1033 (void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
1034 sizeof (lifrl.lifr_name));
1035 if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
1036 ret = ipadm_errno2status(errno);
1037 ifaddrlistx_free(ifaddrs);
1038 goto done;
1039 }
1040 if (lifrl.lifr_flags & IFF_UP) {
1041 ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
1042 ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
1043 AF_INET6), 0, IFF_UP);
1044 if (ret != IPADM_SUCCESS) {
1045 ifaddrlistx_free(ifaddrs);
1046 goto done;
1047 }
1048 } else if (lifrl.lifr_flags & IFF_DUPLICATE) {
1049 if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
1050 ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
1051 ret = ipadm_errno2status(errno);
1052 ifaddrlistx_free(ifaddrs);
1053 goto done;
1054 }
1055 }
1056 }
1057 ifaddrlistx_free(ifaddrs);
1058 }
1059
1060 if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
1061 ret = ipadm_errno2status(errno);
1062 goto done;
1063 }
1064 arp_muxid = lifr.lifr_arp_muxid;
1065 ip_muxid = lifr.lifr_ip_muxid;
1066
1067 /*
1068 * We don't have a good way of knowing whether the arp stream is
1069 * plumbed. We can't rely on IFF_NOARP because someone could
1070 * have turned it off later using "ifconfig xxx -arp".
1071 */
1072 if (arp_muxid != 0) {
1073 if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
1074 /*
1075 * See the comment before the SIOCGLIFGROUPNAME call.
1076 */
1077 if (errno == EBUSY && (flags & IFF_IPMP))
1078 goto again;
1079
1080 if ((errno == EINVAL) &&
1081 (flags & (IFF_NOARP | IFF_IPV6))) {
1082 /*
1083 * Some plumbing utilities set the muxid to
1084 * -1 or some invalid value to signify that
1085 * there is no arp stream. Set the muxid to 0
1086 * before trying to unplumb the IP stream.
1087 * IP does not allow the IP stream to be
1088 * unplumbed if it sees a non-null arp muxid,
1089 * for consistency of IP-ARP streams.
1090 */
1091 lifr.lifr_arp_muxid = 0;
1092 (void) ioctl(muxid_fd, SIOCSLIFMUXID,
1093 (caddr_t)&lifr);
1094 changed_arp_muxid = B_TRUE;
1095 }
1096 /*
1097 * In case of any other error, we continue with
1098 * the unplumb.
1099 */
1100 }
1101 }
1102
1103 if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
1104 if (changed_arp_muxid) {
1105 /*
1106 * Some error occurred, and we need to restore
1107 * everything back to what it was.
1108 */
1109 save_errno = errno;
1110 lifr.lifr_arp_muxid = arp_muxid;
1111 lifr.lifr_ip_muxid = ip_muxid;
1112 (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
1113 errno = save_errno;
1114 }
1115 /*
1116 * See the comment before the SIOCGLIFGROUPNAME call.
1117 */
1118 if (errno == EBUSY && (flags & IFF_IPMP))
1119 goto again;
1120
1121 ret = ipadm_errno2status(errno);
1122 }
1123 done:
1124 if (muxid_fd != -1)
1125 (void) close(muxid_fd);
1126 if (mux_fd != -1)
1127 (void) close(mux_fd);
1128
1129 if (af == AF_INET6 && ret == IPADM_SUCCESS) {
1130 /*
1131 * in.ndpd maintains the phyints in its memory even after
1132 * the interface is plumbed, so that it can be reused when
1133 * the interface gets plumbed again. The default behavior
1134 * of in.ndpd is to start autoconfiguration for an interface
1135 * that gets plumbed. We need to send the
1136 * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
1137 * default behavior on replumb.
1138 */
1139 (void) i_ipadm_enable_autoconf(ifname);
1140 }
1141 return (ret);
1142 }
1143
1144 /*
1145 * Saves the given interface name `ifname' with address family `af' in
1146 * persistent DB.
1147 */
1148 static ipadm_status_t
i_ipadm_persist_if(ipadm_handle_t iph,const char * ifname,sa_family_t af)1149 i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
1150 {
1151 ipmgmt_if_arg_t ifarg;
1152 int err;
1153
1154 (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
1155 ifarg.ia_family = af;
1156 ifarg.ia_cmd = IPMGMT_CMD_SETIF;
1157 ifarg.ia_flags = IPMGMT_PERSIST;
1158 err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1159 return (ipadm_errno2status(err));
1160 }
1161
1162 /*
1163 * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
1164 * is set in `ipadm_flags', it is also removed from persistent configuration.
1165 */
1166 ipadm_status_t
i_ipadm_delete_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t ipadm_flags)1167 i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1168 uint32_t ipadm_flags)
1169 {
1170 ipadm_status_t ret = IPADM_SUCCESS;
1171 ipadm_status_t db_status;
1172 char tmp_ifname[LIFNAMSIZ];
1173 char *cp;
1174 struct ipadm_addrobj_s ipaddr;
1175 boolean_t is_persistent =
1176 (ipadm_flags & IPADM_OPT_PERSIST);
1177
1178 ret = i_ipadm_unplumb_if(iph, ifname, af);
1179 if (ret != IPADM_SUCCESS)
1180 goto done;
1181
1182 cp = strrchr(ifname, IPADM_LOGICAL_SEP);
1183 if (cp != NULL) {
1184 assert(iph->iph_flags & IPH_LEGACY);
1185 /*
1186 * This is a non-zero logical interface.
1187 * Find the addrobj and remove it from the daemon's memory.
1188 */
1189 (void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
1190 tmp_ifname[cp - ifname] = '\0';
1191 *cp++ = '\0';
1192 ipaddr.ipadm_lifnum = atoi(cp);
1193 (void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
1194 sizeof (ipaddr.ipadm_ifname));
1195 ipaddr.ipadm_af = af;
1196 ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
1197 if (ret == IPADM_SUCCESS) {
1198 ret = i_ipadm_delete_addrobj(iph, &ipaddr,
1199 IPADM_OPT_ACTIVE);
1200 } else if (ret == IPADM_NOTFOUND) {
1201 ret = IPADM_SUCCESS;
1202 }
1203 return (ret);
1204 }
1205 done:
1206 /*
1207 * Even if interface does not exist, remove all its addresses and
1208 * properties from the persistent store. If interface does not
1209 * exist both in kernel and the persistent store, return IPADM_ENXIO.
1210 */
1211 if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
1212 db_status = i_ipadm_delete_ifobj(iph, ifname, af,
1213 is_persistent);
1214 if (db_status == IPADM_SUCCESS)
1215 ret = IPADM_SUCCESS;
1216 }
1217
1218 return (ret);
1219 }
1220
1221 /*
1222 * Resets all addresses on interface `ifname' with address family `af'
1223 * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
1224 * and address objects of `ifname' for `af' are also removed from the
1225 * persistent DB.
1226 */
1227 ipadm_status_t
i_ipadm_delete_ifobj(ipadm_handle_t iph,const char * ifname,sa_family_t af,boolean_t is_persistent)1228 i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1229 boolean_t is_persistent)
1230 {
1231 ipmgmt_if_arg_t ifarg;
1232 int err;
1233
1234 ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
1235 ifarg.ia_flags = IPMGMT_ACTIVE;
1236 if (is_persistent)
1237 ifarg.ia_flags |= IPMGMT_PERSIST;
1238 ifarg.ia_family = af;
1239 (void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
1240
1241 err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1242 return (ipadm_errno2status(err));
1243 }
1244
1245 /*
1246 * Create the interface by plumbing it for IP.
1247 * This function will check if there is saved configuration information
1248 * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
1249 * for `ifname' is taken.
1250 */
1251 ipadm_status_t
i_ipadm_create_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t ipadm_flags)1252 i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1253 uint32_t ipadm_flags)
1254 {
1255 ipadm_status_t status;
1256 boolean_t p_exists;
1257 sa_family_t other_af;
1258
1259 /*
1260 * Return error, if the interface already exists in either the active
1261 * or the persistent configuration.
1262 */
1263 if (ipadm_if_enabled(iph, ifname, af))
1264 return (IPADM_IF_EXISTS);
1265
1266 if (!(iph->iph_flags & IPH_LEGACY)) {
1267 status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1268 if (status != IPADM_SUCCESS)
1269 return (status);
1270 other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1271 if (p_exists) {
1272 if (!ipadm_if_enabled(iph, ifname, other_af))
1273 return (IPADM_OP_DISABLE_OBJ);
1274 else
1275 ipadm_flags &= ~IPADM_OPT_PERSIST;
1276 }
1277 }
1278
1279 return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
1280 }
1281
1282 /*
1283 * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
1284 * default, unless a value in `af' is specified. The interface may be plumbed
1285 * only if there is no previously saved persistent configuration information
1286 * for the interface (in which case the ipadm_enable_if() function must
1287 * be used to enable the interface).
1288 *
1289 * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
1290 * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
1291 * or appropriate ipadm_status_t corresponding to the errno.
1292 *
1293 * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
1294 * be over-written with the actual interface name when a PPA has to be
1295 * internally generated by the library.
1296 */
1297 ipadm_status_t
ipadm_create_if(ipadm_handle_t iph,char * ifname,sa_family_t af,uint32_t flags)1298 ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
1299 uint32_t flags)
1300 {
1301 ipadm_status_t status;
1302 boolean_t created_v4 = B_FALSE;
1303 char newifname[LIFNAMSIZ];
1304
1305 /* Check for the required authorization */
1306 if (!ipadm_check_auth())
1307 return (IPADM_EAUTH);
1308
1309 if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
1310 !(flags & IPADM_OPT_ACTIVE)) ||
1311 (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
1312 IPADM_OPT_GENPPA))) {
1313 return (IPADM_INVALID_ARG);
1314 }
1315 if (flags & IPADM_OPT_GENPPA) {
1316 if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
1317 LIFNAMSIZ)
1318 return (IPADM_INVALID_ARG);
1319 } else {
1320 if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
1321 return (IPADM_INVALID_ARG);
1322 }
1323
1324 if (!i_ipadm_validate_ifname(iph, newifname))
1325 return (IPADM_INVALID_ARG);
1326
1327 if ((af == AF_INET || af == AF_UNSPEC) &&
1328 !i_ipadm_is_6to4(iph, ifname)) {
1329 status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
1330 if (status != IPADM_SUCCESS)
1331 return (status);
1332 created_v4 = B_TRUE;
1333 }
1334 if (af == AF_INET6 || af == AF_UNSPEC) {
1335 status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
1336 if (status != IPADM_SUCCESS) {
1337 if (created_v4) {
1338 (void) i_ipadm_delete_if(iph, ifname, AF_INET,
1339 IPADM_OPT_ACTIVE);
1340 }
1341 return (status);
1342 }
1343 }
1344
1345 return (IPADM_SUCCESS);
1346 }
1347
1348 /*
1349 * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
1350 * when `af' = AF_UNSPEC.
1351 */
1352 ipadm_status_t
ipadm_delete_if(ipadm_handle_t iph,const char * ifname,sa_family_t af,uint32_t flags)1353 ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1354 uint32_t flags)
1355 {
1356 ipadm_status_t status1 = IPADM_SUCCESS;
1357 ipadm_status_t status2 = IPADM_SUCCESS;
1358 ipadm_status_t other;
1359
1360 /* Check for the required authorization */
1361 if (!ipadm_check_auth())
1362 return (IPADM_EAUTH);
1363
1364 /* Validate the `ifname' for any logical interface. */
1365 if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
1366 !i_ipadm_validate_ifname(iph, ifname))
1367 return (IPADM_INVALID_ARG);
1368
1369 if (af == AF_INET || af == AF_UNSPEC)
1370 status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
1371 if (af == AF_INET6 || af == AF_UNSPEC)
1372 status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
1373 /*
1374 * If the family has been uniquely identified, we return the
1375 * associated status, even if that is ENXIO. Calls from ifconfig
1376 * which can only unplumb one of IPv4/IPv6 at any time fall under
1377 * this category.
1378 */
1379 if (af == AF_INET)
1380 return (status1);
1381 else if (af == AF_INET6)
1382 return (status2);
1383 else if (af != AF_UNSPEC)
1384 return (IPADM_INVALID_ARG);
1385
1386 /*
1387 * If af is AF_UNSPEC, then we return the following:
1388 * status1, if status1 == status2
1389 * IPADM_SUCCESS, if either of status1 or status2 is SUCCESS
1390 * and the other status is ENXIO
1391 * IPADM_ENXIO, if both status1 and status2 are ENXIO
1392 * IPADM_FAILURE otherwise.
1393 */
1394 if (status1 == status2) {
1395 /* covers the case when both status1 and status2 are ENXIO */
1396 return (status1);
1397 } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1398 if (status1 == IPADM_SUCCESS)
1399 other = status2;
1400 else
1401 other = status1;
1402 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1403 } else {
1404 return (IPADM_FAILURE);
1405 }
1406 }
1407
1408 /*
1409 * Returns information about all interfaces in both active and persistent
1410 * configuration. If `ifname' is not NULL, it returns only the interface
1411 * identified by `ifname'.
1412 *
1413 * Return values:
1414 * On success: IPADM_SUCCESS.
1415 * On error : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
1416 */
1417 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)1418 ipadm_if_info(ipadm_handle_t iph, const char *ifname,
1419 ipadm_if_info_t **if_info, uint32_t flags, int64_t lifc_flags)
1420 {
1421 ipadm_status_t status;
1422 ifspec_t ifsp;
1423
1424 if (if_info == NULL || iph == NULL || flags != 0)
1425 return (IPADM_INVALID_ARG);
1426
1427 if (ifname != NULL &&
1428 (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
1429 return (IPADM_INVALID_ARG);
1430 }
1431
1432 status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
1433 if (status != IPADM_SUCCESS)
1434 return (status);
1435 if (ifname != NULL && *if_info == NULL)
1436 return (IPADM_ENXIO);
1437
1438 return (IPADM_SUCCESS);
1439 }
1440
1441 /*
1442 * Frees the linked list allocated by ipadm_if_info().
1443 */
1444 void
ipadm_free_if_info(ipadm_if_info_t * ifinfo)1445 ipadm_free_if_info(ipadm_if_info_t *ifinfo)
1446 {
1447 ipadm_if_info_t *ifinfo_next;
1448
1449 for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1450 ifinfo_next = ifinfo->ifi_next;
1451 free(ifinfo);
1452 }
1453 }
1454
1455 /*
1456 * Re-enable the interface `ifname' based on the saved configuration
1457 * for `ifname'.
1458 */
1459 ipadm_status_t
ipadm_enable_if(ipadm_handle_t iph,const char * ifname,uint32_t flags)1460 ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1461 {
1462 nvlist_t *ifnvl;
1463 ipadm_status_t status;
1464 ifspec_t ifsp;
1465
1466 /* Check for the required authorization */
1467 if (!ipadm_check_auth())
1468 return (IPADM_EAUTH);
1469
1470 /* Check for logical interfaces. */
1471 if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1472 return (IPADM_INVALID_ARG);
1473
1474 /* Enabling an interface persistently is not supported. */
1475 if (flags & IPADM_OPT_PERSIST)
1476 return (IPADM_NOTSUP);
1477
1478 /*
1479 * Return early by checking if the interface is already enabled.
1480 */
1481 if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1482 ipadm_if_enabled(iph, ifname, AF_INET6)) {
1483 return (IPADM_IF_EXISTS);
1484 }
1485 /*
1486 * Enable the interface and restore all its interface properties
1487 * and address objects.
1488 */
1489 status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
1490 if (status != IPADM_SUCCESS)
1491 return (status);
1492
1493 assert(ifnvl != NULL);
1494 /*
1495 * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
1496 * but only for one interface. We need to set IPH_INIT because
1497 * ipmgmtd daemon does not have to write the interface to persistent
1498 * db. The interface is already available in persistent db
1499 * and we are here to re-enable the persistent configuration.
1500 */
1501 iph->iph_flags |= IPH_INIT;
1502 status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1503 iph->iph_flags &= ~IPH_INIT;
1504 return (status);
1505 }
1506
1507 /*
1508 * Disable the interface `ifname' by removing it from the active configuration.
1509 * Error code return values follow the model in ipadm_delete_if()
1510 */
1511 ipadm_status_t
ipadm_disable_if(ipadm_handle_t iph,const char * ifname,uint32_t flags)1512 ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1513 {
1514 ipadm_status_t status1, status2, other;
1515 ifspec_t ifsp;
1516
1517 /* Check for the required authorization */
1518 if (!ipadm_check_auth())
1519 return (IPADM_EAUTH);
1520
1521 /* Check for logical interfaces. */
1522 if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1523 return (IPADM_INVALID_ARG);
1524
1525 /* Disabling an interface persistently is not supported. */
1526 if (flags & IPADM_OPT_PERSIST)
1527 return (IPADM_NOTSUP);
1528
1529 status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1530 if (status1 == IPADM_SUCCESS)
1531 status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1532 status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1533 if (status2 == IPADM_SUCCESS)
1534 status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1535 if (status1 == status2) {
1536 return (status2);
1537 } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1538 if (status1 == IPADM_SUCCESS)
1539 other = status2;
1540 else
1541 other = status1;
1542 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1543 } else {
1544 return (IPADM_FAILURE);
1545 }
1546 }
1547
1548 /*
1549 * This workaround is until libipadm supports IPMP and is required whenever an
1550 * interface is moved into an IPMP group. Since libipadm doesn't support IPMP
1551 * yet, we will have to update the daemon's in-memory mapping of
1552 * `aobjname' to 'lifnum'.
1553 *
1554 * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1555 * door_call(3C) fails. Also, there is no use in returning error because
1556 * `ifname' would have been successfuly moved into IPMP group, by this time.
1557 */
1558 void
ipadm_if_move(ipadm_handle_t iph,const char * ifname)1559 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1560 {
1561 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1562 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1563 }
1564