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
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 */
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
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
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;
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
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
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
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));
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";
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
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;
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
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
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 =
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
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
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
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
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
1531 status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1532 if (status1 == IPADM_SUCCESS)
1533 status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1534 status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1535 if (status2 == IPADM_SUCCESS)
1536 status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1537 if (status1 == status2) {
1538 return (status2);
1539 } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1540 if (status1 == IPADM_SUCCESS)
1541 other = status2;
1542 else
1543 other = status1;
1544 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1545 } else {
1546 return (IPADM_FAILURE);
1547 }
1548 }
1549
1550 /*
1551 * This workaround is until libipadm supports IPMP and is required whenever an
1552 * interface is moved into an IPMP group. Since libipadm doesn't support IPMP
1553 * yet, we will have to update the daemon's in-memory mapping of
1554 * `aobjname' to 'lifnum'.
1555 *
1556 * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1557 * door_call(3C) fails. Also, there is no use in returning error because
1558 * `ifname' would have been successfuly moved into IPMP group, by this time.
1559 */
1560 void
1561 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1562 {
1563 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1564 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1565 }
|
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016 Nexenta Systems, Inc.
25 */
26
27 #include <errno.h>
28 #include <sys/sockio.h>
29 #include <sys/list.h>
30 #include <string.h>
31 #include <assert.h>
32 #include <unistd.h>
33 #include <stropts.h>
34 #include <strings.h>
35 #include <libdlpi.h>
36 #include <libdllink.h>
37 #include <libinetutil.h>
38 #include <inet/ip.h>
39 #include <limits.h>
40 #include <zone.h>
41 #include <ipadm_ndpd.h>
42 #include <ipmp_query.h>
43 #include "libipadm_impl.h"
44
45 static ipadm_status_t i_ipadm_slifname_arp(char *, uint64_t, int);
46 static ipadm_status_t i_ipadm_slifname(ipadm_handle_t, char *, char *,
47 uint64_t, int, uint32_t);
48 static ipadm_status_t i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
49 sa_family_t);
50 static ipadm_status_t i_ipadm_persist_if(ipadm_handle_t, const char *,
51 sa_family_t, uint32_t);
52 static ipadm_status_t i_ipadm_allocate_ifinfo(ipadm_if_info_t **);
53 static ipadm_status_t i_ipadm_get_db_if(ipadm_handle_t, const char *,
54 nvlist_t **);
55 static ipadm_status_t i_ipadm_nvl2ifinfo(nvlist_t *, ipadm_if_info_t **);
56 static ipadm_status_t i_ipadm_fill_cmembers(char *, ipadm_ipmp_members_t *);
57 static ipadm_status_t i_ipadm_fill_pmembers(nvlist_t *, ipadm_ipmp_members_t *);
58 static ipadm_status_t i_ipadm_add_persistent_if_info(ipadm_if_info_t *,
59 ipadm_if_info_t *);
60 static void i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *);
61 static ipadm_status_t i_ipadm_persist_update_ipmp(ipadm_handle_t, const char *,
62 const char *,
63 ipadm_ipmp_op_t);
64 static ipadm_status_t i_ipadm_update_ipmp(ipadm_handle_t, const char *,
65 const char *, uint32_t,
66 ipadm_ipmp_op_t);
67
68 /*
69 * Returns B_FALSE if the interface in `ifname' has at least one address that is
70 * IFF_UP in the addresses in `ifa'.
71 */
72 static boolean_t
73 i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
74 {
75 struct ifaddrs *ifap;
76 char cifname[LIFNAMSIZ];
77 char *sep;
78
79 for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
80 (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
81 if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
82 *sep = '\0';
83 /*
84 * If this condition is true, there is at least one
85 * address that is IFF_UP. So, we need to return B_FALSE.
86 */
124 for (n = 0; n < numifs; n++, lifrp++) {
125 /* Skip interfaces with logical num != 0 */
126 if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
127 continue;
128 /*
129 * Skip the current interface if a specific `ifname' has
130 * been requested and current interface does not match
131 * `ifname'.
132 */
133 if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
134 continue;
135 /*
136 * Check if the interface already exists in our list.
137 * If it already exists, we need to update its flags.
138 */
139 for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
140 if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
141 break;
142 }
143 if (ifp == NULL) {
144 if ((status =
145 i_ipadm_allocate_ifinfo(&ifp)) != IPADM_SUCCESS)
146 break;
147
148 (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
149 sizeof (ifp->ifi_name));
150 /* Update the `ifi_next' pointer for this new node */
151 if (*if_info == NULL)
152 *if_info = ifp;
153 else
154 last->ifi_next = ifp;
155 last = ifp;
156 }
157
158 /*
159 * Retrieve the flags for the interface by doing a
160 * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
161 */
162 (void) strlcpy(lifrl.lifr_name,
163 lifrp->lifr_name, sizeof (lifrl.lifr_name));
164 s = (lifrp->lifr_addr.ss_family == AF_INET) ?
165 iph->iph_sock : iph->iph_sock6;
166 if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
167 continue;
168
169 /* a regular interface by default */
170 ifp->ifi_class = IPADM_IF_CLASS_REGULAR;
171
172 if (lifrl.lifr_flags & IFF_BROADCAST)
173 ifp->ifi_cflags |= IFIF_BROADCAST;
174 if (lifrl.lifr_flags & IFF_MULTICAST)
175 ifp->ifi_cflags |= IFIF_MULTICAST;
176 if (lifrl.lifr_flags & IFF_POINTOPOINT)
177 ifp->ifi_cflags |= IFIF_POINTOPOINT;
178 if (lifrl.lifr_flags & IFF_VIRTUAL) {
179 ifp->ifi_cflags |= IFIF_VIRTUAL;
180 ifp->ifi_class = IPADM_IF_CLASS_VIRTUAL;
181 }
182 if (lifrl.lifr_flags & IFF_IPMP) {
183 ifp->ifi_cflags |= IFIF_IPMP;
184 ifp->ifi_class = IPADM_IF_CLASS_IPMP;
185 }
186 if (lifrl.lifr_flags & IFF_STANDBY)
187 ifp->ifi_cflags |= IFIF_STANDBY;
188 if (lifrl.lifr_flags & IFF_INACTIVE)
189 ifp->ifi_cflags |= IFIF_INACTIVE;
190 if (lifrl.lifr_flags & IFF_VRRP)
191 ifp->ifi_cflags |= IFIF_VRRP;
192 if (lifrl.lifr_flags & IFF_NOACCEPT)
193 ifp->ifi_cflags |= IFIF_NOACCEPT;
194 if (lifrl.lifr_flags & IFF_IPV4)
195 ifp->ifi_cflags |= IFIF_IPV4;
196 if (lifrl.lifr_flags & IFF_IPV6)
197 ifp->ifi_cflags |= IFIF_IPV6;
198 if (lifrl.lifr_flags & IFF_L3PROTECT)
199 ifp->ifi_cflags |= IFIF_L3PROTECT;
200
201 /* Retrieve active IPMP members */
202 if (ifp->ifi_class == IPADM_IF_CLASS_IPMP) {
203 if (ioctl(s, SIOCGLIFGROUPNAME,
204 (caddr_t)&lifrl) < 0) {
205 status = ipadm_errno2status(errno);
206 break;
207 }
208
209 if ((status = i_ipadm_fill_cmembers(
210 lifrl.lifr_groupname,
211 &ifp->ifi_ipmp_cmembers)) != IPADM_SUCCESS)
212 break;
213 }
214 }
215 free(buf);
216 if (status != IPADM_SUCCESS) {
217 ipadm_free_if_info(*if_info);
218 *if_info = NULL;
219 }
220 return (status);
221 }
222
223 /*
224 * Returns the interface information for `ifname' in `if_info' from persistent
225 * config if `ifname' is non-null. Otherwise, it returns all the interfaces
226 * from persistent config in `if_info'.
227 */
228 static ipadm_status_t
229 i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
230 ipadm_if_info_t **if_info)
231 {
232 ipadm_status_t status = IPADM_SUCCESS;
233 nvlist_t *ifs_info_nvl;
234
235 *if_info = NULL;
236
237 if ((status = i_ipadm_get_db_if(iph,
238 ifname, &ifs_info_nvl)) != IPADM_SUCCESS)
239 return (status);
240
241 assert(ifs_info_nvl != NULL);
242
243 return (i_ipadm_nvl2ifinfo(ifs_info_nvl, if_info));
244 }
245
246 static ipadm_status_t
247 i_ipadm_nvl2ifinfo(nvlist_t *ifs_info_nvl, ipadm_if_info_t **if_info)
248 {
249 ipadm_if_info_t *ific = NULL, *ifil = NULL;
250 nvlist_t *if_info_nvl;
251 nvpair_t *nvp;
252 char *strval;
253 ipadm_status_t status = IPADM_SUCCESS;
254 uint16_t *families;
255 uint_t nelem = 0;
256
257 for (nvp = nvlist_next_nvpair(ifs_info_nvl, NULL); nvp != NULL;
258 nvp = nvlist_next_nvpair(ifs_info_nvl, nvp)) {
259 if (nvpair_value_nvlist(nvp, &if_info_nvl) != 0)
260 continue;
261
262 status = i_ipadm_allocate_ifinfo(&ific);
263 if (status != IPADM_SUCCESS) {
264 ipadm_free_if_info(*if_info);
265 break;
266 }
267 if (nvlist_lookup_string(if_info_nvl, IPADM_NVP_IFNAME,
268 &strval) != 0) {
269 ipadm_free_if_info(ific);
270 ific = NULL;
271 continue;
272 }
273 (void) strlcpy(ific->ifi_name, strval,
274 sizeof (ific->ifi_name));
275
276 if (nvlist_lookup_uint16_array(if_info_nvl,
277 IPADM_NVP_FAMILIES, &families, &nelem) == 0) {
278 while (nelem-- > 0) {
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 }
285
286 if (nvlist_lookup_string(if_info_nvl,
287 IPADM_NVP_IFCLASS, &strval) == 0)
288 ific->ifi_class = atoi(strval);
289 else
290 ific->ifi_class = IPADM_IF_CLASS_REGULAR;
291
292 if (ific->ifi_class == IPADM_IF_CLASS_IPMP)
293 /* do not expect any failures there */
294 (void) i_ipadm_fill_pmembers(if_info_nvl,
295 &ific->ifi_ipmp_pmembers);
296
297 if (*if_info == NULL)
298 *if_info = ific;
299 else
300 ifil->ifi_next = ific;
301 ifil = ific;
302 }
303
304 nvlist_free(ifs_info_nvl);
305 return (status);
306 }
307
308 /*
309 * Fill the ipadm_if_info_t->ifi_ipmp_pmembers by info from
310 * ipadm DB
311 */
312 static ipadm_status_t
313 i_ipadm_fill_pmembers(nvlist_t *if_info_nvl, ipadm_ipmp_members_t *pmembers)
314 {
315 uint_t nelem = 0;
316 char **members;
317 ipadm_ipmp_member_t *ipmp_member;
318
319 if (nvlist_lookup_string_array(if_info_nvl, IPADM_NVP_MIFNAMES,
320 &members, &nelem) != 0)
321 return (IPADM_SUCCESS);
322
323 while (nelem-- > 0) {
324 if ((ipmp_member = calloc(1,
325 sizeof (ipadm_ipmp_member_t))) == NULL)
326 return (ipadm_errno2status(errno));
327
328 (void) strlcpy(ipmp_member->if_name, members[nelem],
329 sizeof (ipmp_member->if_name));
330 list_insert_tail(pmembers, ipmp_member);
331 }
332 return (IPADM_SUCCESS);
333 }
334
335 /*
336 * Fill the ipadm_if_info_t->ifi_ipmp_cmembers by info from
337 * kernel (libipmp is used to retrieve the required info)
338 */
339 static ipadm_status_t
340 i_ipadm_fill_cmembers(char *grname, ipadm_ipmp_members_t *cmembers)
341 {
342 ipmp_handle_t ipmp_handle;
343 ipmp_groupinfo_t *grinfo;
344 ipmp_iflist_t *iflistp;
345 ipadm_ipmp_member_t *ipmp_member;
346 ipadm_status_t ipadm_status = IPADM_SUCCESS;
347 int i;
348
349 if (ipmp_open(&ipmp_handle) != IPMP_SUCCESS)
350 return (IPADM_FAILURE);
351
352 if (ipmp_getgroupinfo(ipmp_handle, grname, &grinfo) != IPMP_SUCCESS) {
353 ipadm_status = IPADM_FAILURE;
354 goto fail2;
355 }
356
357 iflistp = grinfo->gr_iflistp;
358 for (i = 0; i < iflistp->il_nif; i++) {
359 if ((ipmp_member = calloc(1,
360 sizeof (ipadm_ipmp_member_t))) == NULL) {
361 ipadm_status = ipadm_errno2status(errno);
362 goto fail1;
363 }
364 (void) strlcpy(ipmp_member->if_name, iflistp->il_ifs[i],
365 sizeof (ipmp_member->if_name));
366 list_insert_tail(cmembers, ipmp_member);
367 }
368
369 fail1:
370 ipmp_freegroupinfo(grinfo);
371 fail2:
372 ipmp_close(ipmp_handle);
373 return (ipadm_status);
374 }
375
376 /*
377 * Collects information for `ifname' if one is specified from both
378 * active and persistent config in `if_info'. If no `ifname' is specified,
379 * this returns all the interfaces in active and persistent config in
380 * `if_info'.
381 */
382 ipadm_status_t
383 i_ipadm_get_all_if_info(ipadm_handle_t iph, const char *ifname,
384 ipadm_if_info_t **if_info, int64_t lifc_flags)
385 {
386 ipadm_status_t status;
387 ipadm_if_info_t *aifinfo = NULL;
388 ipadm_if_info_t *pifinfo = NULL;
389 ipadm_if_info_t *aifp;
390 ipadm_if_info_t *pifp;
391 ipadm_if_info_t *last = NULL;
392 struct ifaddrs *ifa;
393 struct ifaddrs *ifap;
446 /*
447 * Get the persistent interface information in `pifinfo'.
448 */
449 status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
450 if (status == IPADM_NOTFOUND) {
451 *if_info = aifinfo;
452 return (IPADM_SUCCESS);
453 }
454 if (status != IPADM_SUCCESS)
455 goto fail;
456 /*
457 * If a persistent interface is also found in `aifinfo', update
458 * its entry in `aifinfo' with the persistent information from
459 * `pifinfo'. If an interface is found in `pifinfo', but not in
460 * `aifinfo', it means that this interface was disabled. We should
461 * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
462 */
463 for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
464 for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
465 if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
466 break;
467 }
468 }
469
470 if (aifp == NULL) {
471 if ((status =
472 i_ipadm_allocate_ifinfo(&aifp)) != IPADM_SUCCESS)
473 goto fail;
474
475 (void) strlcpy(aifp->ifi_name, pifp->ifi_name,
476 sizeof (aifp->ifi_name));
477
478 aifp->ifi_next = NULL;
479 aifp->ifi_state = IFIS_DISABLED;
480 if (last != NULL)
481 last->ifi_next = aifp;
482 else
483 aifinfo = aifp;
484 last = aifp;
485 }
486
487 if ((status = i_ipadm_add_persistent_if_info(aifp,
488 pifp)) != IPADM_SUCCESS)
489 goto fail;
490 }
491 *if_info = aifinfo;
492 ipadm_free_if_info(pifinfo);
493 return (IPADM_SUCCESS);
494 fail:
495 *if_info = NULL;
496 ipadm_free_if_info(aifinfo);
497 ipadm_free_if_info(pifinfo);
498 return (status);
499 }
500
501 /*
502 * Updates active if_info by data from persistent if_info
503 */
504 static ipadm_status_t
505 i_ipadm_add_persistent_if_info(ipadm_if_info_t *aifp, ipadm_if_info_t *pifp)
506 {
507 ipadm_ipmp_member_t *pp_ipmp_member, *ap_ipmp_member;
508
509 ipadm_ipmp_members_t *apmembers = &aifp->ifi_ipmp_pmembers;
510 ipadm_ipmp_members_t *ppmembers = &pifp->ifi_ipmp_pmembers;
511
512 aifp->ifi_pflags = pifp->ifi_pflags;
513 aifp->ifi_class = pifp->ifi_class;
514
515 for (pp_ipmp_member = list_head(ppmembers); pp_ipmp_member;
516 pp_ipmp_member = list_next(ppmembers, pp_ipmp_member)) {
517 if ((ap_ipmp_member = calloc(1,
518 sizeof (ipadm_ipmp_member_t))) == NULL)
519 return (ipadm_errno2status(errno));
520
521 (void) strlcpy(ap_ipmp_member->if_name,
522 pp_ipmp_member->if_name,
523 sizeof (ap_ipmp_member->if_name));
524
525 list_insert_tail(apmembers, ap_ipmp_member);
526 }
527 return (IPADM_SUCCESS);
528 }
529
530 static ipadm_status_t
531 i_ipadm_allocate_ifinfo(ipadm_if_info_t **if_info)
532 {
533 *if_info = calloc(1, sizeof (ipadm_if_info_t));
534 if (*if_info == NULL)
535 return (ipadm_errno2status(errno));
536
537 /* List of active (current) members */
538 list_create(&((*if_info)->ifi_ipmp_cmembers),
539 sizeof (ipadm_ipmp_member_t),
540 offsetof(ipadm_ipmp_member_t, node));
541
542 /* List of persistent members */
543 list_create(&((*if_info)->ifi_ipmp_pmembers),
544 sizeof (ipadm_ipmp_member_t),
545 offsetof(ipadm_ipmp_member_t, node));
546
547 return (IPADM_SUCCESS);
548 }
549
550 /*
551 * Reads all the interface lines from the persistent DB into the nvlist `onvl',
552 * when `ifname' is NULL.
553 * If an `ifname' is specified, then the interface line corresponding to
554 * that name will be returned.
555 */
556 static ipadm_status_t
557 i_ipadm_get_db_if(ipadm_handle_t iph, const char *ifname, nvlist_t **onvl)
558 {
559 ipmgmt_getif_arg_t garg;
560
561 /* Populate the door_call argument structure */
562 bzero(&garg, sizeof (garg));
563 garg.ia_cmd = IPMGMT_CMD_GETIF;
564 if (ifname != NULL)
565 (void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
566
567 return (i_ipadm_call_ipmgmtd(iph, (void *) &garg, sizeof (garg), onvl));
568 }
569
570 int
571 i_ipadm_get_lnum(const char *ifname)
572 {
573 char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
574
575 if (num == NULL)
576 return (0);
577
578 return (atoi(++num));
579 }
580
581 /*
582 * Sets the output argument `exists' to true or false based on whether
583 * any persistent configuration is available for `ifname' and returns
584 * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
585 * `exists' is unmodified and an error status is returned.
586 */
587 ipadm_status_t
588 i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
589 boolean_t *exists)
590 {
591 ipadm_if_info_t *ifinfo;
592 ipadm_status_t status;
593
594 /*
595 * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already
596 * knows about persistent configuration in the first place, so we
597 * just return success.
598 */
599 if (iph->iph_flags & IPH_IPMGMTD) {
600 *exists = B_FALSE;
601 return (IPADM_SUCCESS);
602 }
603 status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
604 if (status == IPADM_SUCCESS) {
605 *exists = ((af == AF_INET &&
606 (ifinfo->ifi_pflags & IFIF_IPV4)) ||
607 (af == AF_INET6 &&
608 (ifinfo->ifi_pflags & IFIF_IPV6)));
609 ipadm_free_if_info(ifinfo);
610 } else if (status == IPADM_NOTFOUND) {
611 status = IPADM_SUCCESS;
612 *exists = B_FALSE;
613 }
614 return (status);
615 }
616
617 /*
618 * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
619 * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
620 * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
621 * the bottom of the stream for tunneling interfaces.
622 */
623 ipadm_status_t
624 ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
625 {
626 int err;
627
628 if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
629 return (ipadm_errno2status(errno));
928 if (af == AF_INET)
929 sock = iph->iph_sock;
930 else
931 sock = iph->iph_sock6;
932 if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
933 return (IPADM_IF_EXISTS);
934 if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
935 return (ipadm_errno2status(errno));
936
937 /*
938 * By default, kernel configures 127.0.0.1 on the loopback
939 * interface. Replace this with 0.0.0.0 to be consistent
940 * with interface creation on other physical interfaces.
941 */
942 if (islo && !legacy) {
943 bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
944 lifr.lifr_addr.ss_family = af;
945 if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
946 return (ipadm_errno2status(errno));
947 if (is_persistent) {
948 status = i_ipadm_persist_if(iph,
949 ifname, af, ipadm_flags);
950 if (status != IPADM_SUCCESS) {
951 (void) i_ipadm_delete_if(iph, ifname,
952 af, IPADM_OPT_ACTIVE);
953 }
954 }
955 }
956 return (status);
957 }
958
959 dlpi_flags = DLPI_NOATTACH;
960
961 /*
962 * If IPADM_OPT_IPMP is specified, then this is a request
963 * to create an IPMP interface atop /dev/ipmpstub0. (We can't simply
964 * pass "ipmpstub0" as devname since an admin *could* have a normal
965 * vanity-named link named "ipmpstub0" that they'd like to plumb.)
966 */
967 if (ipadm_flags & IPADM_OPT_IPMP) {
968 dlpi_flags |= DLPI_DEVONLY;
969 linkname = "ipmpstub0";
1110 status = i_ipadm_set_flags(iph, lifname, af,
1111 IFF_UP, 0);
1112 if (status != IPADM_SUCCESS)
1113 return (status);
1114 } else {
1115 /*
1116 * Prevent static IPv6 addresses from triggering
1117 * autoconf. This does not have to be done for
1118 * 6to4 tunnel interfaces, since in.ndpd will
1119 * not autoconfigure those interfaces.
1120 */
1121 if (af == AF_INET6 && !legacy)
1122 (void) i_ipadm_disable_autoconf(newif);
1123 }
1124
1125 /*
1126 * If IPADM_OPT_PERSIST was set in flags, store the
1127 * interface in persistent DB.
1128 */
1129 if (is_persistent) {
1130 status = i_ipadm_persist_if(iph,
1131 newif, af, ipadm_flags);
1132 if (status != IPADM_SUCCESS) {
1133 (void) i_ipadm_delete_if(iph, newif, af,
1134 IPADM_OPT_ACTIVE);
1135 }
1136 }
1137 }
1138 if (status == IPADM_EXISTS)
1139 status = IPADM_IF_EXISTS;
1140 return (status);
1141 }
1142
1143 /*
1144 * Unplumbs the interface in `ifname' of family `af'.
1145 */
1146 ipadm_status_t
1147 i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
1148 {
1149 int ip_muxid, arp_muxid;
1150 int mux_fd = -1;
1151 int muxid_fd = -1;
1344 if (af == AF_INET6 && ret == IPADM_SUCCESS) {
1345 /*
1346 * in.ndpd maintains the phyints in its memory even after
1347 * the interface is plumbed, so that it can be reused when
1348 * the interface gets plumbed again. The default behavior
1349 * of in.ndpd is to start autoconfiguration for an interface
1350 * that gets plumbed. We need to send the
1351 * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
1352 * default behavior on replumb.
1353 */
1354 (void) i_ipadm_enable_autoconf(ifname);
1355 }
1356 return (ret);
1357 }
1358
1359 /*
1360 * Saves the given interface name `ifname' with address family `af' in
1361 * persistent DB.
1362 */
1363 static ipadm_status_t
1364 i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1365 uint32_t ipadm_flags)
1366 {
1367 ipmgmt_if_arg_t ifarg;
1368 int err;
1369
1370 (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
1371 ifarg.ia_family = af;
1372 if (ipadm_flags & IPADM_OPT_IPMP)
1373 ifarg.ia_ifclass = IPADM_IF_CLASS_IPMP;
1374 else
1375 ifarg.ia_ifclass = IPADM_IF_CLASS_REGULAR;
1376
1377 ifarg.ia_cmd = IPMGMT_CMD_SETIF;
1378 ifarg.ia_flags = IPMGMT_PERSIST;
1379 err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
1380 return (ipadm_errno2status(err));
1381 }
1382
1383 /*
1384 * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
1385 * is set in `ipadm_flags', it is also removed from persistent configuration.
1386 */
1387 ipadm_status_t
1388 i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1389 uint32_t ipadm_flags)
1390 {
1391 ipadm_status_t ret = IPADM_SUCCESS;
1392 ipadm_status_t db_status;
1393 char tmp_ifname[LIFNAMSIZ];
1394 char *cp;
1395 struct ipadm_addrobj_s ipaddr;
1396 boolean_t is_persistent =
1549 !i_ipadm_is_6to4(iph, ifname)) {
1550 status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
1551 if (status != IPADM_SUCCESS)
1552 return (status);
1553 created_v4 = B_TRUE;
1554 }
1555 if (af == AF_INET6 || af == AF_UNSPEC) {
1556 status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
1557 if (status != IPADM_SUCCESS) {
1558 if (created_v4) {
1559 (void) i_ipadm_delete_if(iph, ifname, AF_INET,
1560 IPADM_OPT_ACTIVE);
1561 }
1562 return (status);
1563 }
1564 }
1565
1566 return (IPADM_SUCCESS);
1567 }
1568
1569 ipadm_status_t
1570 ipadm_add_ipmp_member(ipadm_handle_t iph, const char *gifname,
1571 const char *mifname, uint32_t ipadm_flags)
1572 {
1573 return (i_ipadm_update_ipmp(iph, gifname, mifname,
1574 ipadm_flags, IPADM_ADD_IPMP));
1575 }
1576
1577 ipadm_status_t
1578 ipadm_remove_ipmp_member(ipadm_handle_t iph, const char *gifname,
1579 const char *mifname, uint32_t ipadm_flags)
1580 {
1581 return (i_ipadm_update_ipmp(iph, gifname, mifname,
1582 ipadm_flags, IPADM_REMOVE_IPMP));
1583 }
1584
1585 /*
1586 * Updates active IPMP configuration according to the specified
1587 * command. It also persists the configuration if IPADM_OPT_PERSIST
1588 * is set in `ipadm_flags'.
1589 */
1590 static ipadm_status_t
1591 i_ipadm_update_ipmp(ipadm_handle_t iph, const char *gifname,
1592 const char *mifname, uint32_t ipadm_flags, ipadm_ipmp_op_t op)
1593 {
1594 ipadm_status_t status;
1595 char groupname1[LIFGRNAMSIZ];
1596 char groupname2[LIFGRNAMSIZ];
1597
1598 /* Check for the required authorization */
1599 if (!ipadm_check_auth())
1600 return (IPADM_EAUTH);
1601
1602 if (!(ipadm_flags & IPADM_OPT_ACTIVE) ||
1603 gifname == NULL || mifname == NULL)
1604 return (IPADM_INVALID_ARG);
1605
1606 if (!ipadm_if_enabled(iph, gifname, AF_UNSPEC) ||
1607 !ipadm_if_enabled(iph, mifname, AF_UNSPEC))
1608 return (IPADM_OP_DISABLE_OBJ);
1609
1610 if (!i_ipadm_is_ipmp(iph, gifname))
1611 return (IPADM_INVALID_ARG);
1612
1613 if (op == IPADM_ADD_IPMP && i_ipadm_is_under_ipmp(iph, mifname))
1614 return (IPADM_IF_INUSE);
1615
1616 if ((status = i_ipadm_get_groupname_active(iph, gifname,
1617 groupname2, sizeof (groupname2))) != IPADM_SUCCESS)
1618 return (status);
1619
1620 if (op == IPADM_REMOVE_IPMP) {
1621 if ((status = i_ipadm_get_groupname_active(iph, mifname,
1622 groupname1, sizeof (groupname1))) != IPADM_SUCCESS)
1623 return (status);
1624
1625 if (groupname1[0] == '\0' ||
1626 strcmp(groupname1, groupname2) != 0)
1627 return (IPADM_INVALID_ARG);
1628
1629 groupname2[0] = '\0';
1630 }
1631
1632 if ((ipadm_flags & IPADM_OPT_PERSIST) &&
1633 (status = i_ipadm_persist_update_ipmp(iph, gifname,
1634 mifname, op)) != IPADM_SUCCESS)
1635 return (status);
1636
1637 return (i_ipadm_set_groupname_active(iph, mifname, groupname2));
1638 }
1639
1640 /*
1641 * Call the ipmgmtd to update the IPMP configuration in ipadm DB.
1642 * After this call the DB will know that mifname is under gifname and
1643 * gifname has a member, which name is mifname.
1644 */
1645 static ipadm_status_t
1646 i_ipadm_persist_update_ipmp(ipadm_handle_t iph, const char *gifname,
1647 const char *mifname, ipadm_ipmp_op_t op)
1648 {
1649 ipmgmt_ipmp_update_arg_t args;
1650 int err;
1651
1652 assert(op == IPADM_ADD_IPMP || op == IPADM_REMOVE_IPMP);
1653
1654 bzero(&args, sizeof (ipmgmt_ipmp_update_arg_t));
1655
1656 args.ia_cmd = IPMGMT_CMD_IPMP_UPDATE;
1657
1658 (void) strlcpy(args.ia_gifname, gifname, sizeof (args.ia_gifname));
1659 (void) strlcpy(args.ia_mifname, mifname, sizeof (args.ia_mifname));
1660
1661 if (op == IPADM_ADD_IPMP)
1662 args.ia_flags = IPMGMT_APPEND;
1663 else
1664 args.ia_flags = IPMGMT_REMOVE;
1665
1666 args.ia_flags |= IPMGMT_PERSIST;
1667
1668 err = ipadm_door_call(iph, &args, sizeof (args), NULL, 0, B_FALSE);
1669 return (ipadm_errno2status(err));
1670 }
1671
1672 /*
1673 * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
1674 * when `af' = AF_UNSPEC.
1675 */
1676 ipadm_status_t
1677 ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
1678 uint32_t flags)
1679 {
1680 ipadm_status_t status1 = IPADM_SUCCESS;
1681 ipadm_status_t status2 = IPADM_SUCCESS;
1682 ipadm_status_t other;
1683
1684 /* Check for the required authorization */
1685 if (!ipadm_check_auth())
1686 return (IPADM_EAUTH);
1687
1688 /* Validate the `ifname' for any logical interface. */
1689 if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
1690 !i_ipadm_validate_ifname(iph, ifname))
1691 return (IPADM_INVALID_ARG);
1692
1755
1756 status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
1757 if (status != IPADM_SUCCESS)
1758 return (status);
1759 if (ifname != NULL && *if_info == NULL)
1760 return (IPADM_ENXIO);
1761
1762 return (IPADM_SUCCESS);
1763 }
1764
1765 /*
1766 * Frees the linked list allocated by ipadm_if_info().
1767 */
1768 void
1769 ipadm_free_if_info(ipadm_if_info_t *ifinfo)
1770 {
1771 ipadm_if_info_t *ifinfo_next;
1772
1773 for (; ifinfo != NULL; ifinfo = ifinfo_next) {
1774 ifinfo_next = ifinfo->ifi_next;
1775 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_cmembers);
1776 i_ipadm_free_ipmp_members(&ifinfo->ifi_ipmp_pmembers);
1777 free(ifinfo);
1778 }
1779 }
1780
1781 static void
1782 i_ipadm_free_ipmp_members(ipadm_ipmp_members_t *ipmp_members)
1783 {
1784 ipadm_ipmp_member_t *ipmp_member;
1785
1786 while ((ipmp_member = list_remove_head(ipmp_members)) != NULL)
1787 free(ipmp_member);
1788
1789 list_destroy(ipmp_members);
1790 }
1791
1792 /*
1793 * Re-enable the interface `ifname' based on the saved configuration
1794 * for `ifname'.
1795 */
1796 ipadm_status_t
1797 ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
1798 {
1799 nvlist_t *ifnvl;
1800 ipadm_status_t status;
1801 ifspec_t ifsp;
1802
1803 /* Check for the required authorization */
1804 if (!ipadm_check_auth())
1805 return (IPADM_EAUTH);
1806
1807 /* Check for logical interfaces. */
1808 if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
1809 return (IPADM_INVALID_ARG);
1810
1811 /* Enabling an interface persistently is not supported. */
1812 if (flags & IPADM_OPT_PERSIST)
1813 return (IPADM_NOTSUP);
1814
1815 /*
1816 * Return early by checking if the interface is already enabled.
1817 */
1818 if (ipadm_if_enabled(iph, ifname, AF_INET) &&
1819 ipadm_if_enabled(iph, ifname, AF_INET6))
1820 return (IPADM_IF_EXISTS);
1821
1822 /*
1823 * Enable the interface and restore all its interface properties
1824 * and address objects.
1825 */
1826 status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
1827 if (status != IPADM_SUCCESS)
1828 return (status);
1829
1830 assert(ifnvl != NULL);
1831 /*
1832 * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
1833 * but only for one interface. We need to set IPH_INIT because
1834 * ipmgmtd daemon does not have to write the interface to persistent
1835 * db. The interface is already available in persistent db
1836 * and we are here to re-enable the persistent configuration.
1837 */
1838 iph->iph_flags |= IPH_INIT;
1839 status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
1840 iph->iph_flags &= ~IPH_INIT;
1841
1868 status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
1869 if (status1 == IPADM_SUCCESS)
1870 status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1871 status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
1872 if (status2 == IPADM_SUCCESS)
1873 status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1874 if (status1 == status2) {
1875 return (status2);
1876 } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
1877 if (status1 == IPADM_SUCCESS)
1878 other = status2;
1879 else
1880 other = status1;
1881 return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
1882 } else {
1883 return (IPADM_FAILURE);
1884 }
1885 }
1886
1887 /*
1888 * FIXME Remove this when ifconfig(1M) is updated to use IPMP support
1889 * in libipadm.
1890 */
1891 /*
1892 * This workaround is required by ifconfig(1M) whenever an
1893 * interface is moved into an IPMP group to update the daemon's
1894 * in-memory mapping of `aobjname' to 'lifnum'.
1895 *
1896 * For `IPMGMT_ACTIVE' case, i_ipadm_delete_ifobj() would only fail if
1897 * door_call(3C) fails. Also, there is no use in returning error because
1898 * `ifname' would have been successfuly moved into IPMP group, by this time.
1899 */
1900 void
1901 ipadm_if_move(ipadm_handle_t iph, const char *ifname)
1902 {
1903 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
1904 (void) i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
1905 }
1906
1907 ipadm_status_t
1908 i_ipadm_set_groupname_active(ipadm_handle_t iph, const char *ifname,
1909 const char *groupname)
1910 {
1911 struct lifreq lifr;
1912 ipadm_addr_info_t *addrinfo, *ia;
1913 ipadm_status_t status = IPADM_SUCCESS;
1914
1915 (void) memset(&lifr, 0, sizeof (lifr));
1916
1917 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1918 (void) strlcpy(lifr.lifr_groupname, groupname,
1919 sizeof (lifr.lifr_groupname));
1920
1921 /* Disable all addresses on the interface */
1922 (void) i_ipadm_active_addr_info(iph, ifname, &addrinfo,
1923 IPADM_OPT_ACTIVE, IFF_UP | IFF_DUPLICATE);
1924 for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia))
1925 (void) ipadm_disable_addr(iph, ia->ia_aobjname, 0);
1926
1927 if (ioctl(iph->iph_sock, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1928 ioctl(iph->iph_sock6, SIOCSLIFGROUPNAME, (caddr_t)&lifr) == -1)
1929 status = ipadm_errno2status(errno);
1930
1931 /* Enable all addresses on the interface */
1932 for (ia = addrinfo; ia != NULL; ia = IA_NEXT(ia))
1933 (void) ipadm_enable_addr(iph, ia->ia_aobjname, 0);
1934
1935 if (status == IPADM_SUCCESS) {
1936 if (groupname[0] == '\0') {
1937 /*
1938 * If interface was removed from IPMP group, unset the
1939 * DEPRECATED and NOFAILOVER flags.
1940 */
1941 (void) i_ipadm_set_flags(iph, ifname, AF_INET, 0,
1942 IFF_DEPRECATED | IFF_NOFAILOVER);
1943 (void) i_ipadm_set_flags(iph, ifname, AF_INET6, 0,
1944 IFF_DEPRECATED | IFF_NOFAILOVER);
1945 } else if (addrinfo == NULL) {
1946 /*
1947 * If interface was added to IPMP group and there are no
1948 * active addresses, explicitly bring it up to be used
1949 * for link-based IPMP configuration.
1950 */
1951 (void) i_ipadm_set_flags(iph, ifname, AF_INET,
1952 IFF_UP, 0);
1953 (void) i_ipadm_set_flags(iph, ifname, AF_INET6,
1954 IFF_UP, 0);
1955 }
1956 }
1957
1958 ipadm_free_addr_info(addrinfo);
1959
1960 return (status);
1961 }
1962
1963 ipadm_status_t
1964 i_ipadm_get_groupname_active(ipadm_handle_t iph, const char *ifname,
1965 char *groupname, size_t size)
1966 {
1967 struct lifreq lifr;
1968
1969 (void) memset(&lifr, 0, sizeof (lifr));
1970
1971 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
1972
1973 if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1 &&
1974 ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME, (caddr_t)&lifr) == -1)
1975 return (ipadm_errno2status(errno));
1976
1977 (void) strlcpy(groupname, lifr.lifr_groupname, size);
1978
1979 return (IPADM_SUCCESS);
1980 }
1981
1982 /*
1983 * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
1984 */
1985 boolean_t
1986 i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
1987 {
1988
1989 char groupname[LIFGRNAMSIZ];
1990
1991 if (i_ipadm_get_groupname_active(iph, ifname, groupname,
1992 sizeof (groupname)) != IPADM_SUCCESS ||
1993 groupname[0] == '\0' ||
1994 strcmp(ifname, groupname) == 0)
1995 return (B_FALSE);
1996
1997 return (B_TRUE);
1998 }
1999
2000 /*
2001 * Returns B_TRUE if `ifname' represents an IPMP group interface.
2002 */
2003 boolean_t
2004 i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
2005 {
2006 uint64_t flags, flags6;
2007
2008 if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
2009 i_ipadm_get_flags(iph, ifname, AF_INET6, &flags6) != IPADM_SUCCESS)
2010 return (B_FALSE);
2011
2012 return ((flags & IFF_IPMP) != 0 || (flags6 & IFF_IPMP) != 0);
2013 }
|