summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
authorGirish Moodalbail <Girish.Moodalbail@Sun.COM>2010-03-26 17:53:11 -0400
committerGirish Moodalbail <Girish.Moodalbail@Sun.COM>2010-03-26 17:53:11 -0400
commit6e91bba0d6c6bdabbba62cefae583715a4a58e2a (patch)
treee10bc428e6a27ac87b541b72769d8095d64e894f /usr/src/lib
parent00a57bdfe7eeb62d10d0c0b3aab64d24a4d89287 (diff)
downloadillumos-gate-6e91bba0d6c6bdabbba62cefae583715a4a58e2a.tar.gz
PSARC 2009/306 Brussels II - ipadm and libipadm
PSARC 2010/080 Brussels II addendum 6827318 Brussels Phase II aka ipadm(1m) 6731945 need BSD getifaddrs() API 6909065 explicitly disallow non-contiguous netmasks in the next minor release 6853922 ifconfig dumps core when ether address is non-hexadecimal. 6815806 ipReasmTimeout value should be variable 6567083 nd_getset has some dead and confusing code. 6884466 remove unused tcp/sctp ndd tunables 6928813 Comments at odds with default value of tcp_time_wait_interval 6236982 ifconfig usesrc lets adapter use itself as source address 6936855 modifying the ip6_strict_src_multihoming to non-zero value will unbind V4 IREs
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile14
-rw-r--r--usr/src/lib/brand/solaris10/zone/s10_boot.ksh9
-rw-r--r--usr/src/lib/libbc/inc/include/net/if.h24
-rw-r--r--usr/src/lib/libinetcfg/common/inetcfg.c52
-rw-r--r--usr/src/lib/libinetutil/common/inetutil.c102
-rw-r--r--usr/src/lib/libinetutil/common/libinetutil.h14
-rw-r--r--usr/src/lib/libinetutil/common/mapfile-vers7
-rw-r--r--usr/src/lib/libipadm/Makefile61
-rw-r--r--usr/src/lib/libipadm/Makefile.com53
-rw-r--r--usr/src/lib/libipadm/common/ipadm_addr.c3466
-rw-r--r--usr/src/lib/libipadm/common/ipadm_if.c1534
-rw-r--r--usr/src/lib/libipadm/common/ipadm_ipmgmt.h272
-rw-r--r--usr/src/lib/libipadm/common/ipadm_ndpd.c366
-rw-r--r--usr/src/lib/libipadm/common/ipadm_ndpd.h72
-rw-r--r--usr/src/lib/libipadm/common/ipadm_persist.c828
-rw-r--r--usr/src/lib/libipadm/common/ipadm_prop.c1699
-rw-r--r--usr/src/lib/libipadm/common/libipadm.c922
-rw-r--r--usr/src/lib/libipadm/common/libipadm.h346
-rw-r--r--usr/src/lib/libipadm/common/libipadm_impl.h227
-rw-r--r--usr/src/lib/libipadm/common/llib-lipadm35
-rw-r--r--usr/src/lib/libipadm/common/mapfile-vers93
-rw-r--r--usr/src/lib/libipadm/i386/Makefile28
-rw-r--r--usr/src/lib/libipadm/libipadm.xcl89
-rw-r--r--usr/src/lib/libipadm/sparc/Makefile29
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt1
-rw-r--r--usr/src/lib/libsecdb/exec_attr.txt2
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile1
-rw-r--r--usr/src/lib/libsecdb/help/auths/NetworkInterfaceConfig.html42
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt2
-rw-r--r--usr/src/lib/libsocket/Makefile12
-rw-r--r--usr/src/lib/libsocket/Makefile.com5
-rw-r--r--usr/src/lib/libsocket/common/libsocket_priv.h43
-rw-r--r--usr/src/lib/libsocket/common/llib-lsocket11
-rw-r--r--usr/src/lib/libsocket/common/mapfile-vers10
-rw-r--r--usr/src/lib/libsocket/inet/getifaddrs.c269
35 files changed, 10652 insertions, 88 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 4086c4210c..2ed5c76543 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -112,6 +112,7 @@ SUBDIRS += \
libcryptoutil \
libinetcfg \
libinetutil \
+ libipadm \
libipmp \
libiscsit \
libkmf \
@@ -334,6 +335,7 @@ MSGSUBDIRS= \
libilb \
libinetutil \
libinstzones \
+ libipadm \
libnsl \
libnwam \
libpam \
@@ -386,6 +388,7 @@ HDRSUBDIRS= \
libc \
libcmd \
libcmdutils \
+ libcommputil \
libcontract \
libcpc \
libctf \
@@ -411,8 +414,7 @@ HDRSUBDIRS= \
libfru \
libfstyp \
libgen \
- libwanboot \
- libwanbootutil \
+ libipadm \
libipsecutil \
libinetcfg \
libinetsvc \
@@ -445,7 +447,6 @@ HDRSUBDIRS= \
librdc \
libscf \
libsip \
- libcommputil \
libsmbios \
librestart \
librpcsvc \
@@ -456,6 +457,7 @@ HDRSUBDIRS= \
libshell \
libslp \
libsmedia \
+ libsocket \
libsqlite \
libfcoe \
libstmf \
@@ -473,6 +475,8 @@ HDRSUBDIRS= \
libumem \
libunistat \
libuutil \
+ libwanboot \
+ libwanbootutil \
libwrap \
libxcurses2 \
libzfs \
@@ -592,7 +596,9 @@ libefi: libuuid
libfstyp: libnvpair
libelfsign: libcryptoutil libkmf
libidmap: libadutils libldap5 libavl libsldap
-libinetcfg: libnsl libsocket libdlpi
+libinetcfg: libnsl libsocket libdlpi libinetutil
+libipadm: libnsl libinetutil libsocket libdlpi libnvpair libdhcpagent \
+ libdladm libsecdb
libiscsit: libc libnvpair libstmf libuuid libnsl
libkmf: libcryptoutil pkcs11
libnsl: libmd5 libscf
diff --git a/usr/src/lib/brand/solaris10/zone/s10_boot.ksh b/usr/src/lib/brand/solaris10/zone/s10_boot.ksh
index 09c4f39a59..5a57696eef 100644
--- a/usr/src/lib/brand/solaris10/zone/s10_boot.ksh
+++ b/usr/src/lib/brand/solaris10/zone/s10_boot.ksh
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# s10 boot script.
@@ -144,6 +144,13 @@ safe_dir /sbin
replace_with_native /sbin/ifconfig 0555 root:bin
#
+# PSARC 2009/306 removed the ND_SET/ND_GET ioctl's for modifying
+# IP/TCP/UDP/SCTP/ICMP tunables. If S10 ndd(1M) is used within an
+# S10 container, the kernel will return EINVAL. So we need this.
+#
+replace_with_native /usr/sbin/ndd 0555 root:bin
+
+#
# Replace automount and automountd with native wrappers.
#
if [ ! -h $ZONEROOT/usr/lib/fs/autofs -a -d $ZONEROOT/usr/lib/fs/autofs ]; then
diff --git a/usr/src/lib/libbc/inc/include/net/if.h b/usr/src/lib/libbc/inc/include/net/if.h
index 389bb290b6..513fa0d2bf 100644
--- a/usr/src/lib/libbc/inc/include/net/if.h
+++ b/usr/src/lib/libbc/inc/include/net/if.h
@@ -1,5 +1,5 @@
/*
- * Copyright 1990 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -12,8 +12,6 @@
#ifndef _net_if_h
#define _net_if_h
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Structures defining a network interface, providing a packet
* transport mechanism (ala level 0 of the PUP protocols).
@@ -94,7 +92,7 @@ struct ifnet {
#define IFF_NOARP 0x80 /* no address resolution protocol */
#define IFF_PROMISC 0x100 /* receive all packets */
#define IFF_ALLMULTI 0x200 /* receive all multicast packets */
-#define IFF_PRIVATE 0x8000 /* do not advertise */
+#define IFF_PRIVATE 0x8000 /* do not advertise */
/* flags set internally only: */
#define IFF_CANTCHANGE \
@@ -131,8 +129,8 @@ struct ifnet {
* IF_ADJ should be used otherwise to adjust for its presence.
*/
#define IF_ADJ(m) { \
- (m)->m_off += sizeof(struct ifnet *); \
- (m)->m_len -= sizeof(struct ifnet *); \
+ (m)->m_off += sizeof (struct ifnet *); \
+ (m)->m_len -= sizeof (struct ifnet *); \
if ((m)->m_len == 0) { \
struct mbuf *n; \
MFREE((m), n); \
@@ -175,8 +173,12 @@ struct ifaddr {
struct sockaddr ifu_broadaddr;
struct sockaddr ifu_dstaddr;
} ifa_ifu;
+#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_ifu.ifu_broadaddr /* broadcast address */
+#endif
+#ifndef ifa_dstaddr
#define ifa_dstaddr ifa_ifu.ifu_dstaddr /* other end of p-to-p link */
+#endif
struct ifnet *ifa_ifp; /* back-pointer to interface */
struct ifaddr *ifa_next; /* next address for interface */
};
@@ -223,7 +225,7 @@ struct ifreq {
struct ifr_fddi_gen_struct {
int ifru_fddi_gioctl; /* field for gen ioctl */
- caddr_t ifru_fddi_gaddr ; /* Generic ptr to a field */
+ caddr_t ifru_fddi_gaddr; /* Generic ptr to a field */
} ifru_fddi_gstruct;
} ifr_ifru;
@@ -237,10 +239,10 @@ struct ifreq {
#define ifr_data ifr_ifru.ifru_data /* for use by interface */
/* FDDI specific */
-#define ifr_dnld_req ifr_ifru.ifru_dnld_req
-#define ifr_fddi_stat ifr_ifru.ifru_fddi_stat
-#define ifr_fddi_netmap ifr_ifru.ifru_netmapent /* FDDI network map entries */
-#define ifr_fddi_gstruct ifr_ifru.ifru_fddi_gstruct
+#define ifr_dnld_req ifr_ifru.ifru_dnld_req
+#define ifr_fddi_stat ifr_ifru.ifru_fddi_stat
+#define ifr_fddi_netmap ifr_ifru.ifru_netmapent /* FDDI network map entries */
+#define ifr_fddi_gstruct ifr_ifru.ifru_fddi_gstruct
};
diff --git a/usr/src/lib/libinetcfg/common/inetcfg.c b/usr/src/lib/libinetcfg/common/inetcfg.c
index cdcbf00329..f61df0e287 100644
--- a/usr/src/lib/libinetcfg/common/inetcfg.c
+++ b/usr/src/lib/libinetcfg/common/inetcfg.c
@@ -128,32 +128,6 @@ dlpi_error_to_icfg_error(int err)
}
/*
- * Convert a prefix length to a netmask. Note that the mask array
- * should zero'ed by the caller.
- *
- * Returns: ICFG_SUCCESS or ICFG_FAILURE.
- */
-static int
-prefixlen_to_mask(int prefixlen, int maxlen, uchar_t *mask)
-{
- if ((prefixlen < 0) || (prefixlen > maxlen)) {
- errno = EINVAL;
- return (ICFG_FAILURE);
- }
-
- while (prefixlen > 0) {
- if (prefixlen >= 8) {
- *mask++ = 0xFF;
- prefixlen -= 8;
- continue;
- }
- *mask |= 1 << (8 - prefixlen);
- prefixlen--;
- }
- return (ICFG_SUCCESS);
-}
-
-/*
* Copies an an IPv4 or IPv6 address from a sockaddr_storage
* structure into the appropriate sockaddr structure for the
* address family (sockaddr_in for AF_INET or sockaddr_in6 for
@@ -573,34 +547,16 @@ icfg_set_prefixlen(icfg_handle_t handle, int prefixlen)
{
struct lifreq lifr;
- (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
(void) strlcpy(lifr.lifr_name, icfg_if_name(handle),
sizeof (lifr.lifr_name));
- lifr.lifr_addr.ss_family = icfg_if_protocol(handle);
-
- if (icfg_if_protocol(handle) == AF_INET6) {
- struct sockaddr_in6 *sin6;
- int ret;
-
- sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
- if ((ret = prefixlen_to_mask(prefixlen, IPV6_ABITS,
- (uchar_t *)&sin6->sin6_addr)) != ICFG_SUCCESS) {
- return (ret);
- }
- } else {
- struct sockaddr_in *sin;
- int ret;
- sin = (struct sockaddr_in *)&lifr.lifr_addr;
- if ((ret = prefixlen_to_mask(prefixlen, IP_ABITS,
- (uchar_t *)&sin->sin_addr)) != ICFG_SUCCESS) {
- return (ret);
- }
+ if (plen2mask(prefixlen, icfg_if_protocol(handle),
+ &lifr.lifr_addr) != 0) {
+ return (ICFG_FAILURE);
}
- if (ioctl(handle->ifh_sock, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) {
+ if (ioctl(handle->ifh_sock, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
return (ICFG_FAILURE);
- }
return (ICFG_SUCCESS);
}
diff --git a/usr/src/lib/libinetutil/common/inetutil.c b/usr/src/lib/libinetutil/common/inetutil.c
index 195d080b79..be92c0e77d 100644
--- a/usr/src/lib/libinetutil/common/inetutil.c
+++ b/usr/src/lib/libinetutil/common/inetutil.c
@@ -20,15 +20,17 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <unistd.h>
#include <netinet/in.h>
#include <libinetutil.h>
-
-extern int getnetmaskbyaddr(const struct in_addr, struct in_addr *);
+#include <inet/ip.h>
+#include <strings.h>
+#include <errno.h>
+#include <libsocket_priv.h>
/*
* Internet utility functions.
@@ -95,3 +97,97 @@ sockaddrcmp(const struct sockaddr_storage *ssp1,
}
return (B_FALSE);
}
+
+/*
+ * Stores the netmask in `mask' for the given prefixlen `plen' and also sets
+ * `ss_family' in `mask'.
+ */
+int
+plen2mask(uint_t prefixlen, sa_family_t af, struct sockaddr_storage *mask)
+{
+ uint8_t *addr;
+
+ bzero(mask, sizeof (*mask));
+ mask->ss_family = af;
+ if (af == AF_INET) {
+ if (prefixlen > IP_ABITS)
+ return (EINVAL);
+ addr = (uint8_t *)&((struct sockaddr_in *)mask)->
+ sin_addr.s_addr;
+ } else {
+ if (prefixlen > IPV6_ABITS)
+ return (EINVAL);
+ addr = (uint8_t *)&((struct sockaddr_in6 *)mask)->
+ sin6_addr.s6_addr;
+ }
+
+ while (prefixlen > 0) {
+ if (prefixlen >= 8) {
+ *addr++ = 0xFF;
+ prefixlen -= 8;
+ continue;
+ }
+ *addr |= 1 << (8 - prefixlen);
+ prefixlen--;
+ }
+ return (0);
+}
+
+/*
+ * Convert a mask to a prefix length.
+ * Returns prefix length on success, -1 otherwise.
+ */
+int
+mask2plen(const struct sockaddr_storage *mask)
+{
+ int rc = 0;
+ uint8_t last;
+ uint8_t *addr;
+ int limit;
+
+ if (mask->ss_family == AF_INET) {
+ limit = IP_ABITS;
+ addr = (uint8_t *)&((struct sockaddr_in *)mask)->
+ sin_addr.s_addr;
+ } else {
+ limit = IPV6_ABITS;
+ addr = (uint8_t *)&((struct sockaddr_in6 *)mask)->
+ sin6_addr.s6_addr;
+ }
+
+ while (*addr == 0xff) {
+ rc += 8;
+ if (rc == limit)
+ return (limit);
+ addr++;
+ }
+
+ last = *addr;
+ while (last != 0) {
+ rc++;
+ last = (last << 1) & 0xff;
+ }
+
+ return (rc);
+}
+
+/*
+ * Returns B_TRUE if the address in `ss' is INADDR_ANY for IPv4 or
+ * :: for IPv6. Otherwise, returns B_FALSE.
+ */
+boolean_t
+sockaddrunspec(const struct sockaddr_storage *ss)
+{
+ struct sockaddr_storage zeroaddr = {0};
+
+ switch (ss->ss_family) {
+ case AF_INET:
+ return (((struct sockaddr_in *)ss)->sin_addr.s_addr ==
+ INADDR_ANY);
+ case AF_INET6:
+ return (IN6_IS_ADDR_UNSPECIFIED(
+ &((struct sockaddr_in6 *)ss)->sin6_addr));
+ }
+
+ return (bcmp(&zeroaddr, ss, sizeof (zeroaddr)) == 0);
+}
diff --git a/usr/src/lib/libinetutil/common/libinetutil.h b/usr/src/lib/libinetutil/common/libinetutil.h
index a285103af6..d294e6b50c 100644
--- a/usr/src/lib/libinetutil/common/libinetutil.h
+++ b/usr/src/lib/libinetutil/common/libinetutil.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -50,10 +50,14 @@ typedef struct {
char ifsp_devnm[LIFNAMSIZ]; /* only the device name */
} ifspec_t;
-extern boolean_t ifparse_ifspec(const char *, ifspec_t *);
-extern void get_netmask4(const struct in_addr *, struct in_addr *);
-extern boolean_t sockaddrcmp(const struct sockaddr_storage *,
- const struct sockaddr_storage *);
+extern boolean_t ifparse_ifspec(const char *, ifspec_t *);
+extern void get_netmask4(const struct in_addr *, struct in_addr *);
+extern boolean_t sockaddrcmp(const struct sockaddr_storage *,
+ const struct sockaddr_storage *);
+extern int plen2mask(uint_t, sa_family_t,
+ struct sockaddr_storage *);
+extern int mask2plen(const struct sockaddr_storage *);
+extern boolean_t sockaddrunspec(const struct sockaddr_storage *);
/*
* Extended version of the classic BSD ifaddrlist() interface:
diff --git a/usr/src/lib/libinetutil/common/mapfile-vers b/usr/src/lib/libinetutil/common/mapfile-vers
index 8bc79dd4cb..8dc7fb2c3a 100644
--- a/usr/src/lib/libinetutil/common/mapfile-vers
+++ b/usr/src/lib/libinetutil/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -62,12 +62,15 @@ SUNWprivate_1.1 {
iu_tq_destroy;
iu_unregister_event;
octet_to_hexascii;
- sockaddrcmp;
ofmt_open;
ofmt_close;
ofmt_print;
ofmt_update_winsize;
ofmt_strerror;
+ mask2plen;
+ plen2mask;
+ sockaddrcmp;
+ sockaddrunspec;
local:
*;
};
diff --git a/usr/src/lib/libipadm/Makefile b/usr/src/lib/libipadm/Makefile
new file mode 100644
index 0000000000..4e407d7fc4
--- /dev/null
+++ b/usr/src/lib/libipadm/Makefile
@@ -0,0 +1,61 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include $(SRC)/lib/Makefile.lib
+
+HDRS = libipadm.h ipadm_ndpd.h ipadm_ipmgmt.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+POFILE = libipadm.po
+MSGFILES = common/libipadm.c common/ipadm_prop.c common/ipadm_persist.c \
+ common/ipadm_addr.c common/ipadm_if.c common/ipadm_ndpd.c
+XGETFLAGS = -a -x libipadm.xcl
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(POFILE): pofile_MSGFILES
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libipadm/Makefile.com b/usr/src/lib/libipadm/Makefile.com
new file mode 100644
index 0000000000..60f677736e
--- /dev/null
+++ b/usr/src/lib/libipadm/Makefile.com
@@ -0,0 +1,53 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+LIBRARY = libipadm.a
+VERS = .1
+OBJECTS = libipadm.o ipadm_prop.o ipadm_persist.o ipadm_addr.o ipadm_if.o \
+ ipadm_ndpd.o
+
+include ../../Makefile.lib
+
+# install this library in the root filesystem
+include ../../Makefile.rootfs
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lnsl -linetutil -lsocket -ldlpi -lnvpair -ldhcpagent \
+ -ldladm -lsecdb
+
+SRCDIR = ../common
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRCDIR) -D_REENTRANT
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libipadm/common/ipadm_addr.c b/usr/src/lib/libipadm/common/ipadm_addr.c
new file mode 100644
index 0000000000..fb39cb1a24
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_addr.c
@@ -0,0 +1,3466 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains functions for address management such as creating
+ * an address, deleting an address, enabling an address, disabling an
+ * address, bringing an address down or up, setting/getting properties
+ * on an address object and listing address information
+ * for all addresses in active as well as persistent configuration.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <inet/ip.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/sockio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <zone.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <dhcpagent_util.h>
+#include <dhcpagent_ipc.h>
+#include <ipadm_ndpd.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdliptun.h>
+#include <ifaddrs.h>
+#include "libipadm_impl.h"
+
+#define SIN6(a) ((struct sockaddr_in6 *)a)
+#define SIN(a) ((struct sockaddr_in *)a)
+
+static ipadm_status_t i_ipadm_create_addr(ipadm_handle_t, ipadm_addrobj_t,
+ uint32_t);
+static ipadm_status_t i_ipadm_create_dhcp(ipadm_handle_t, ipadm_addrobj_t,
+ uint32_t);
+static ipadm_status_t i_ipadm_delete_dhcp(ipadm_handle_t, ipadm_addrobj_t,
+ boolean_t);
+static ipadm_status_t i_ipadm_get_db_addr(ipadm_handle_t, const char *,
+ const char *, nvlist_t **);
+static ipadm_status_t i_ipadm_op_dhcp(ipadm_addrobj_t, dhcp_ipc_type_t,
+ int *);
+static ipadm_status_t i_ipadm_validate_create_addr(ipadm_handle_t,
+ ipadm_addrobj_t, uint32_t);
+static ipadm_status_t i_ipadm_addr_persist_nvl(ipadm_handle_t, nvlist_t *,
+ uint32_t);
+static ipadm_status_t i_ipadm_get_default_prefixlen(struct sockaddr_storage *,
+ uint32_t *);
+static ipadm_status_t i_ipadm_get_static_addr_db(ipadm_handle_t,
+ ipadm_addrobj_t);
+static boolean_t i_ipadm_is_user_aobjname_valid(const char *);
+
+/*
+ * Callback functions to retrieve property values from the kernel. These
+ * functions, when required, translate the values from the kernel to a format
+ * suitable for printing. They also retrieve DEFAULT, PERM and POSSIBLE values
+ * for a given property.
+ */
+static ipadm_pd_getf_t i_ipadm_get_prefixlen, i_ipadm_get_addr_flag,
+ i_ipadm_get_zone, i_ipadm_get_broadcast;
+
+/*
+ * Callback functions to set property values. These functions translate the
+ * values to a format suitable for kernel consumption, allocate the necessary
+ * ioctl buffers and then invoke ioctl().
+ */
+static ipadm_pd_setf_t i_ipadm_set_prefixlen, i_ipadm_set_addr_flag,
+ i_ipadm_set_zone;
+
+/* address properties description table */
+ipadm_prop_desc_t ipadm_addrprop_table[] = {
+ { "broadcast", IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE,
+ NULL, NULL, i_ipadm_get_broadcast },
+
+ { "deprecated", IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE,
+ i_ipadm_set_addr_flag, i_ipadm_get_onoff,
+ i_ipadm_get_addr_flag },
+
+ { "prefixlen", IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE,
+ i_ipadm_set_prefixlen, i_ipadm_get_prefixlen,
+ i_ipadm_get_prefixlen },
+
+ { "private", IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE,
+ i_ipadm_set_addr_flag, i_ipadm_get_onoff, i_ipadm_get_addr_flag },
+
+ { "transmit", IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE,
+ i_ipadm_set_addr_flag, i_ipadm_get_onoff, i_ipadm_get_addr_flag },
+
+ { "zone", IPADMPROP_CLASS_ADDR, MOD_PROTO_NONE,
+ i_ipadm_set_zone, NULL, i_ipadm_get_zone },
+
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+static ipadm_prop_desc_t up_addrprop = { "up", IPADMPROP_CLASS_ADDR,
+ MOD_PROTO_NONE, NULL, NULL, NULL };
+
+/*
+ * Helper function that initializes the `ipadm_ifname', `ipadm_aobjname', and
+ * `ipadm_atype' fields of the given `ipaddr'.
+ */
+void
+i_ipadm_init_addr(ipadm_addrobj_t ipaddr, const char *ifname,
+ const char *aobjname, ipadm_addr_type_t atype)
+{
+ bzero(ipaddr, sizeof (struct ipadm_addrobj_s));
+ (void) strlcpy(ipaddr->ipadm_ifname, ifname,
+ sizeof (ipaddr->ipadm_ifname));
+ (void) strlcpy(ipaddr->ipadm_aobjname, aobjname,
+ sizeof (ipaddr->ipadm_aobjname));
+ ipaddr->ipadm_atype = atype;
+}
+
+/*
+ * Determine the permission of the property depending on whether it has a
+ * set() and/or get() callback functions.
+ */
+static ipadm_status_t
+i_ipadm_pd2permstr(ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize)
+{
+ uint_t perm;
+ size_t nbytes;
+
+ perm = 0;
+ if (pdp->ipd_set != NULL)
+ perm |= MOD_PROP_PERM_WRITE;
+ if (pdp->ipd_get != NULL)
+ perm |= MOD_PROP_PERM_READ;
+
+ nbytes = snprintf(buf, *bufsize, "%c%c",
+ ((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-',
+ ((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-');
+
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Given an addrobj with `ipadm_aobjname' filled in, i_ipadm_get_addrobj()
+ * retrieves the information necessary for any operation on the object,
+ * such as delete-addr, enable-addr, disable-addr, up-addr, down-addr,
+ * refresh-addr, get-addrprop or set-addrprop. The information include
+ * the logical interface number, address type, address family,
+ * the interface id (if the address type is IPADM_ADDR_IPV6_ADDRCONF) and
+ * the ipadm_flags that indicate if the address is present in
+ * active configuration or persistent configuration or both. If the address
+ * is not found, IPADM_NOTSUP is returned.
+ */
+ipadm_status_t
+i_ipadm_get_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
+{
+ ipmgmt_aobjop_arg_t larg;
+ ipmgmt_aobjop_rval_t rval, *rvalp;
+ int err = 0;
+
+ /* populate the door_call argument structure */
+ larg.ia_cmd = IPMGMT_CMD_AOBJNAME2ADDROBJ;
+ (void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname,
+ sizeof (larg.ia_aobjname));
+
+ rvalp = &rval;
+ err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
+ sizeof (rval), B_FALSE);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ (void) strlcpy(ipaddr->ipadm_ifname, rval.ir_ifname,
+ sizeof (ipaddr->ipadm_ifname));
+ ipaddr->ipadm_lifnum = rval.ir_lnum;
+ ipaddr->ipadm_atype = rval.ir_atype;
+ ipaddr->ipadm_af = rval.ir_family;
+ ipaddr->ipadm_flags = rval.ir_flags;
+ if (rval.ir_atype == IPADM_ADDR_IPV6_ADDRCONF) {
+ (void) memcpy(&ipaddr->ipadm_intfid, &rval.ir_ifid,
+ sizeof (ipaddr->ipadm_intfid));
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Retrieves the static address (IPv4 or IPv6) for the given address object
+ * in `ipaddr' from persistent DB.
+ */
+static ipadm_status_t
+i_ipadm_get_static_addr_db(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
+{
+ ipadm_status_t status;
+ nvlist_t *onvl;
+ nvlist_t *anvl = NULL;
+ nvlist_t *nvladdr;
+ nvpair_t *nvp;
+ char *name;
+ char *aobjname = ipaddr->ipadm_aobjname;
+ char *sname;
+ sa_family_t af = AF_UNSPEC;
+
+ /*
+ * Get the address line in the nvlist `onvl' from ipmgmtd daemon.
+ */
+ status = i_ipadm_get_db_addr(iph, NULL, aobjname, &onvl);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ /*
+ * Walk through the nvlist `onvl' to extract the IPADM_NVP_IPV4ADDR
+ * or the IPADM_NVP_IPV6ADDR name-value pair.
+ */
+ for (nvp = nvlist_next_nvpair(onvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(onvl, NULL)) {
+ if (nvpair_value_nvlist(nvp, &anvl) != 0)
+ continue;
+ if (nvlist_exists(anvl, IPADM_NVP_IPV4ADDR) ||
+ nvlist_exists(anvl, IPADM_NVP_IPV6ADDR))
+ break;
+ }
+ if (nvp == NULL)
+ goto fail;
+ for (nvp = nvlist_next_nvpair(anvl, NULL);
+ nvp != NULL; nvp = nvlist_next_nvpair(anvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IPV4ADDR) == 0) {
+ af = AF_INET;
+ break;
+ } else if (strcmp(name, IPADM_NVP_IPV6ADDR) == 0) {
+ af = AF_INET6;
+ break;
+ }
+ }
+ assert(af != AF_UNSPEC);
+ if (nvpair_value_nvlist(nvp, &nvladdr) != 0 ||
+ nvlist_lookup_string(nvladdr, IPADM_NVP_IPADDRHNAME, &sname) != 0 ||
+ ipadm_set_addr(ipaddr, sname, af) != IPADM_SUCCESS) {
+ goto fail;
+ }
+ nvlist_free(onvl);
+ return (IPADM_SUCCESS);
+fail:
+ nvlist_free(onvl);
+ return (IPADM_NOTFOUND);
+}
+
+/*
+ * For the given `addrobj->ipadm_lifnum' and `addrobj->ipadm_af', this function
+ * fills in the address objname, the address type and the ipadm_flags.
+ */
+ipadm_status_t
+i_ipadm_get_lif2addrobj(ipadm_handle_t iph, ipadm_addrobj_t addrobj)
+{
+ ipmgmt_aobjop_arg_t larg;
+ ipmgmt_aobjop_rval_t rval, *rvalp;
+ int err;
+
+ larg.ia_cmd = IPMGMT_CMD_LIF2ADDROBJ;
+ (void) strlcpy(larg.ia_ifname, addrobj->ipadm_ifname,
+ sizeof (larg.ia_ifname));
+ larg.ia_lnum = addrobj->ipadm_lifnum;
+ larg.ia_family = addrobj->ipadm_af;
+
+ rvalp = &rval;
+ err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
+ sizeof (rval), B_FALSE);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ (void) strlcpy(addrobj->ipadm_aobjname, rval.ir_aobjname,
+ sizeof (addrobj->ipadm_aobjname));
+ addrobj->ipadm_atype = rval.ir_atype;
+ addrobj->ipadm_flags = rval.ir_flags;
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Adds an addrobj to ipmgmtd daemon's aobjmap (active configuration).
+ * with the given name and logical interface number.
+ * This API is called by in.ndpd to add addrobjs when new prefixes or
+ * dhcpv6 addresses are configured.
+ */
+ipadm_status_t
+ipadm_add_aobjname(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ const char *aobjname, ipadm_addr_type_t atype, int lnum)
+{
+ ipmgmt_aobjop_arg_t larg;
+ int err;
+
+ larg.ia_cmd = IPMGMT_CMD_ADDROBJ_ADD;
+ (void) strlcpy(larg.ia_ifname, ifname, sizeof (larg.ia_ifname));
+ (void) strlcpy(larg.ia_aobjname, aobjname, sizeof (larg.ia_aobjname));
+ larg.ia_atype = atype;
+ larg.ia_lnum = lnum;
+ larg.ia_family = af;
+ err = ipadm_door_call(iph, &larg, sizeof (larg), NULL, 0, B_FALSE);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Deletes an address object with given name and logical number from ipmgmtd
+ * daemon's aobjmap (active configuration). This API is called by in.ndpd to
+ * remove addrobjs when auto-configured prefixes or dhcpv6 addresses are
+ * removed.
+ */
+ipadm_status_t
+ipadm_delete_aobjname(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ const char *aobjname, ipadm_addr_type_t atype, int lnum)
+{
+ struct ipadm_addrobj_s aobj;
+
+ i_ipadm_init_addr(&aobj, ifname, aobjname, atype);
+ aobj.ipadm_af = af;
+ aobj.ipadm_lifnum = lnum;
+ return (i_ipadm_delete_addrobj(iph, &aobj, IPADM_OPT_ACTIVE));
+}
+
+/*
+ * Gets all the addresses from active configuration and populates the
+ * address information in `addrinfo'.
+ */
+static ipadm_status_t
+i_ipadm_active_addr_info(ipadm_handle_t iph, const char *ifname,
+ ipadm_addr_info_t **addrinfo, uint32_t ipadm_flags, int64_t lifc_flags)
+{
+ ipadm_status_t status;
+ struct ifaddrs *ifap, *ifa;
+ ipadm_addr_info_t *curr, *prev = NULL;
+ struct ifaddrs *cifaddr;
+ struct lifreq lifr;
+ int sock;
+ uint64_t flags;
+ char cifname[LIFNAMSIZ];
+ struct sockaddr_in6 *sin6;
+ struct ipadm_addrobj_s ipaddr;
+ char *sep;
+ int lnum;
+
+retry:
+ *addrinfo = NULL;
+
+ /* Get all the configured addresses */
+ if (getallifaddrs(AF_UNSPEC, &ifa, lifc_flags) < 0)
+ return (ipadm_errno2status(errno));
+ /* Return if there is nothing to process. */
+ if (ifa == NULL)
+ return (IPADM_SUCCESS);
+ bzero(&lifr, sizeof (lifr));
+ for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
+ (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
+ lnum = 0;
+ if ((sep = strrchr(cifname, ':')) != NULL) {
+ *sep++ = '\0';
+ lnum = atoi(sep);
+ }
+ if (ifname != NULL && strcmp(cifname, ifname) != 0)
+ continue;
+ if (!(ipadm_flags & IPADM_OPT_ZEROADDR)) {
+ /*
+ * Do not list it if it is zero, unless
+ * it is under DHCP or has a non-zero
+ * destination address.
+ */
+ if (sockaddrunspec(ifap->ifa_addr) &&
+ (!(ifap->ifa_flags & IFF_DHCPRUNNING) &&
+ (!(ifap->ifa_flags & IFF_POINTOPOINT) ||
+ sockaddrunspec(ifap->ifa_dstaddr)))) {
+ continue;
+ }
+ }
+
+ /* Allocate and populate the current node in the list. */
+ if ((curr = calloc(1, sizeof (ipadm_addr_info_t))) == NULL)
+ goto fail;
+
+ /* Link to the list in `addrinfo'. */
+ if (prev != NULL)
+ prev->ia_ifa.ifa_next = &curr->ia_ifa;
+ else
+ *addrinfo = curr;
+ prev = curr;
+
+ cifaddr = &curr->ia_ifa;
+ if ((cifaddr->ifa_name = strdup(ifap->ifa_name)) == NULL)
+ goto fail;
+ cifaddr->ifa_flags = ifap->ifa_flags;
+ cifaddr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
+ if (cifaddr->ifa_addr == NULL)
+ goto fail;
+ *cifaddr->ifa_addr = *ifap->ifa_addr;
+ cifaddr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
+ if (cifaddr->ifa_netmask == NULL)
+ goto fail;
+ *cifaddr->ifa_netmask = *ifap->ifa_netmask;
+ if (ifap->ifa_flags & IFF_POINTOPOINT) {
+ cifaddr->ifa_dstaddr = malloc(
+ sizeof (struct sockaddr_storage));
+ if (cifaddr->ifa_dstaddr == NULL)
+ goto fail;
+ *cifaddr->ifa_dstaddr = *ifap->ifa_dstaddr;
+ } else if (ifap->ifa_flags & IFF_BROADCAST) {
+ cifaddr->ifa_broadaddr = malloc(
+ sizeof (struct sockaddr_storage));
+ if (cifaddr->ifa_broadaddr == NULL)
+ goto fail;
+ *cifaddr->ifa_broadaddr = *ifap->ifa_broadaddr;
+ }
+ /* Get the addrobj name stored for this logical interface. */
+ ipaddr.ipadm_aobjname[0] = '\0';
+ (void) strlcpy(ipaddr.ipadm_ifname, cifname,
+ sizeof (ipaddr.ipadm_ifname));
+ ipaddr.ipadm_lifnum = lnum;
+ ipaddr.ipadm_af = ifap->ifa_addr->ss_family;
+ status = i_ipadm_get_lif2addrobj(iph, &ipaddr);
+
+ /*
+ * Find address type from ifa_flags, if we could not get it
+ * from daemon.
+ */
+ sin6 = SIN6(ifap->ifa_addr);
+ flags = ifap->ifa_flags;
+ if (status == IPADM_SUCCESS) {
+ (void) strlcpy(curr->ia_aobjname, ipaddr.ipadm_aobjname,
+ sizeof (curr->ia_aobjname));
+ curr->ia_atype = ipaddr.ipadm_atype;
+ } else if ((flags & IFF_DHCPRUNNING) && (!(flags & IFF_IPV6) ||
+ !IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
+ curr->ia_atype = IPADM_ADDR_DHCP;
+ } else if (flags & IFF_ADDRCONF) {
+ curr->ia_atype = IPADM_ADDR_IPV6_ADDRCONF;
+ } else {
+ curr->ia_atype = IPADM_ADDR_STATIC;
+ }
+ /*
+ * Populate the flags for the active configuration from the
+ * `ifa_flags'.
+ */
+ if (!(flags & IFF_UP)) {
+ if (flags & IFF_DUPLICATE)
+ curr->ia_state = IFA_DUPLICATE;
+ else
+ curr->ia_state = IFA_DOWN;
+ } else {
+ curr->ia_cflags |= IA_UP;
+ if (flags & IFF_RUNNING) {
+ (void) strlcpy(lifr.lifr_name, ifap->ifa_name,
+ sizeof (lifr.lifr_name));
+ sock = (ifap->ifa_addr->ss_family == AF_INET) ?
+ iph->iph_sock : iph->iph_sock6;
+ if (ioctl(sock, SIOCGLIFDADSTATE,
+ (caddr_t)&lifr) < 0) {
+ if (errno == ENXIO) {
+ freeifaddrs(ifa);
+ ipadm_free_addr_info(*addrinfo);
+ goto retry;
+ }
+ goto fail;
+ }
+ if (lifr.lifr_dadstate == DAD_IN_PROGRESS)
+ curr->ia_state = IFA_TENTATIVE;
+ else
+ curr->ia_state = IFA_OK;
+ } else {
+ curr->ia_state = IFA_INACCESSIBLE;
+ }
+ }
+ if (flags & IFF_UNNUMBERED)
+ curr->ia_cflags |= IA_UNNUMBERED;
+ if (flags & IFF_PRIVATE)
+ curr->ia_cflags |= IA_PRIVATE;
+ if (flags & IFF_TEMPORARY)
+ curr->ia_cflags |= IA_TEMPORARY;
+ if (flags & IFF_DEPRECATED)
+ curr->ia_cflags |= IA_DEPRECATED;
+
+ }
+
+ freeifaddrs(ifa);
+ return (IPADM_SUCCESS);
+
+fail:
+ /* On error, cleanup everything and return. */
+ ipadm_free_addr_info(*addrinfo);
+ *addrinfo = NULL;
+ freeifaddrs(ifa);
+ return (ipadm_errno2status(errno));
+}
+
+/*
+ * From the given `name', i_ipadm_name2atype() deduces the address type
+ * and address family. If the `name' implies an address, it returns B_TRUE.
+ * Else, returns B_FALSE and leaves the output parameters unchanged.
+ */
+boolean_t
+i_ipadm_name2atype(const char *name, sa_family_t *af, ipadm_addr_type_t *type)
+{
+ boolean_t is_addr = B_TRUE;
+
+ if (strcmp(name, IPADM_NVP_IPV4ADDR) == 0) {
+ *af = AF_INET;
+ *type = IPADM_ADDR_STATIC;
+ } else if (strcmp(name, IPADM_NVP_IPV6ADDR) == 0) {
+ *af = AF_INET6;
+ *type = IPADM_ADDR_STATIC;
+ } else if (strcmp(name, IPADM_NVP_DHCP) == 0) {
+ *af = AF_INET;
+ *type = IPADM_ADDR_DHCP;
+ } else if (strcmp(name, IPADM_NVP_INTFID) == 0) {
+ *af = AF_INET6;
+ *type = IPADM_ADDR_IPV6_ADDRCONF;
+ } else {
+ is_addr = B_FALSE;
+ }
+
+ return (is_addr);
+}
+
+/*
+ * Parses the given nvlist `nvl' for an address or an address property.
+ * The input nvlist must contain either an address or an address property.
+ * `ainfo' is an input as well as output parameter. When an address or an
+ * address property is found, `ainfo' is updated with the information found.
+ * Some of the fields may be already filled in by the calling function.
+ *
+ * The fields that will be filled/updated by this function are `ia_pflags',
+ * `ia_sname' and `ia_dname'. Values for `ia_pflags' are obtained if the `nvl'
+ * contains an address property. `ia_sname', `ia_dname', and `ia_pflags' are
+ * obtained if `nvl' contains an address.
+ */
+static ipadm_status_t
+i_ipadm_nvl2ainfo_common(nvlist_t *nvl, ipadm_addr_info_t *ainfo)
+{
+ nvlist_t *nvladdr;
+ char *name;
+ char *propstr = NULL;
+ char *sname, *dname;
+ nvpair_t *nvp;
+ sa_family_t af;
+ ipadm_addr_type_t atype;
+ boolean_t is_addr = B_FALSE;
+ int err;
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (i_ipadm_name2atype(name, &af, &atype)) {
+ err = nvpair_value_nvlist(nvp, &nvladdr);
+ is_addr = B_TRUE;
+ } else if (IPADM_PRIV_NVP(name)) {
+ continue;
+ } else {
+ err = nvpair_value_string(nvp, &propstr);
+ }
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+
+ if (is_addr) {
+ /*
+ * We got an address from the nvlist `nvl'.
+ * Parse `nvladdr' and populate relevant information
+ * in `ainfo'.
+ */
+ switch (atype) {
+ case IPADM_ADDR_STATIC:
+ if (strcmp(name, "up") == 0 &&
+ strcmp(propstr, "yes") == 0) {
+ ainfo->ia_pflags |= IA_UP;
+ }
+ /*
+ * For static addresses, we need to get the hostnames.
+ */
+ err = nvlist_lookup_string(nvladdr,
+ IPADM_NVP_IPADDRHNAME, &sname);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ (void) strlcpy(ainfo->ia_sname, sname,
+ sizeof (ainfo->ia_sname));
+ err = nvlist_lookup_string(nvladdr,
+ IPADM_NVP_IPDADDRHNAME, &dname);
+ if (err == 0) {
+ (void) strlcpy(ainfo->ia_dname, dname,
+ sizeof (ainfo->ia_dname));
+ }
+ break;
+ case IPADM_ADDR_DHCP:
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ /*
+ * dhcp and addrconf address objects are always
+ * marked up when re-enabled.
+ */
+ ainfo->ia_pflags |= IA_UP;
+ break;
+ default:
+ return (IPADM_FAILURE);
+ }
+ } else {
+ /*
+ * We got an address property from `nvl'. Parse the
+ * name and the property value. Update the `ainfo->ia_pflags'
+ * for the flags.
+ */
+ if (strcmp(name, "deprecated") == 0) {
+ if (strcmp(propstr, IPADM_ONSTR) == 0)
+ ainfo->ia_pflags |= IA_DEPRECATED;
+ } else if (strcmp(name, "private") == 0) {
+ if (strcmp(propstr, IPADM_ONSTR) == 0)
+ ainfo->ia_pflags |= IA_PRIVATE;
+ }
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Parses the given nvlist `nvl' for an address or an address property.
+ * The input nvlist must contain either an address or an address property.
+ * `ainfo' is an input as well as output parameter. When an address or an
+ * address property is found, `ainfo' is updated with the information found.
+ * Some of the fields may be already filled in by the calling function,
+ * because of previous calls to i_ipadm_nvl2ainfo_active().
+ *
+ * Since the address object in `nvl' is also in the active configuration, the
+ * fields that will be filled/updated by this function are `ia_pflags',
+ * `ia_sname' and `ia_dname'.
+ *
+ * If this function returns an error, the calling function will take
+ * care of freeing the fields in `ainfo'.
+ */
+static ipadm_status_t
+i_ipadm_nvl2ainfo_active(nvlist_t *nvl, ipadm_addr_info_t *ainfo)
+{
+ return (i_ipadm_nvl2ainfo_common(nvl, ainfo));
+}
+
+/*
+ * Parses the given nvlist `nvl' for an address or an address property.
+ * The input nvlist must contain either an address or an address property.
+ * `ainfo' is an input as well as output parameter. When an address or an
+ * address property is found, `ainfo' is updated with the information found.
+ * Some of the fields may be already filled in by the calling function,
+ * because of previous calls to i_ipadm_nvl2ainfo_persist().
+ *
+ * All the relevant fields in `ainfo' will be filled by this function based
+ * on what we find in `nvl'.
+ *
+ * If this function returns an error, the calling function will take
+ * care of freeing the fields in `ainfo'.
+ */
+static ipadm_status_t
+i_ipadm_nvl2ainfo_persist(nvlist_t *nvl, ipadm_addr_info_t *ainfo)
+{
+ nvlist_t *nvladdr;
+ struct ifaddrs *ifa;
+ char *name;
+ char *ifname = NULL;
+ char *aobjname = NULL;
+ char *propstr = NULL;
+ nvpair_t *nvp;
+ sa_family_t af;
+ ipadm_addr_type_t atype;
+ boolean_t is_addr = B_FALSE;
+ size_t size = sizeof (struct sockaddr_storage);
+ struct sockaddr_in6 *sin6;
+ uint32_t plen = 0;
+ int err;
+ ipadm_status_t status;
+
+ status = i_ipadm_nvl2ainfo_common(nvl, ainfo);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IFNAME) == 0) {
+ err = nvpair_value_string(nvp, &ifname);
+ } else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0) {
+ err = nvpair_value_string(nvp, &aobjname);
+ } else if (i_ipadm_name2atype(name, &af, &atype)) {
+ err = nvpair_value_nvlist(nvp, &nvladdr);
+ is_addr = B_TRUE;
+ } else {
+ err = nvpair_value_string(nvp, &propstr);
+ }
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+
+ ifa = &ainfo->ia_ifa;
+ (void) strlcpy(ainfo->ia_aobjname, aobjname,
+ sizeof (ainfo->ia_aobjname));
+ if (ifa->ifa_name == NULL && (ifa->ifa_name = strdup(ifname)) == NULL)
+ return (IPADM_NO_MEMORY);
+ if (is_addr) {
+ /*
+ * We got an address from the nvlist `nvl'.
+ * Parse `nvladdr' and populate `ifa->ifa_addr'.
+ */
+ ainfo->ia_atype = atype;
+ if ((ifa->ifa_addr = calloc(1, size)) == NULL)
+ return (IPADM_NO_MEMORY);
+ switch (atype) {
+ case IPADM_ADDR_STATIC:
+ ifa->ifa_addr->ss_family = af;
+ break;
+ case IPADM_ADDR_DHCP:
+ ifa->ifa_addr->ss_family = AF_INET;
+ break;
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ sin6 = SIN6(ifa->ifa_addr);
+ sin6->sin6_family = AF_INET6;
+ if (i_ipadm_nvl2in6_addr(nvladdr, IPADM_NVP_IPNUMADDR,
+ &sin6->sin6_addr) != IPADM_SUCCESS)
+ return (IPADM_NO_MEMORY);
+ err = nvlist_lookup_uint32(nvladdr, IPADM_NVP_PREFIXLEN,
+ &plen);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ if ((ifa->ifa_netmask = malloc(size)) == NULL)
+ return (IPADM_NO_MEMORY);
+ if ((err = plen2mask(plen, af, ifa->ifa_netmask)) != 0)
+ return (ipadm_errno2status(err));
+ break;
+ default:
+ return (IPADM_FAILURE);
+ }
+ } else {
+ if (strcmp(name, "prefixlen") == 0) {
+ /*
+ * If a prefixlen was found, update the
+ * `ainfo->ia_ifa.ifa_netmask'.
+ */
+
+ if ((ifa->ifa_netmask = malloc(size)) == NULL)
+ return (IPADM_NO_MEMORY);
+ /*
+ * Address property lines always follow the address
+ * line itself in the persistent db. We must have
+ * found a valid `ainfo->ia_ifa.ifa_addr' by now.
+ */
+ assert(ifa->ifa_addr != NULL);
+ err = plen2mask(atoi(propstr), ifa->ifa_addr->ss_family,
+ ifa->ifa_netmask);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Retrieves all addresses from active config and appends to it the
+ * addresses that are found only in persistent config. In addition,
+ * it updates the persistent fields for each address from information
+ * found in persistent config. The output parameter `addrinfo' contains
+ * complete information regarding all addresses in active as well as
+ * persistent config.
+ */
+static ipadm_status_t
+i_ipadm_get_all_addr_info(ipadm_handle_t iph, const char *ifname,
+ ipadm_addr_info_t **addrinfo, uint32_t ipadm_flags, int64_t lifc_flags)
+{
+ nvlist_t *nvladdr = NULL;
+ nvlist_t *onvl = NULL;
+ nvpair_t *nvp;
+ ipadm_status_t status;
+ ipadm_addr_info_t *ainfo = NULL;
+ ipadm_addr_info_t *curr;
+ ipadm_addr_info_t *last = NULL;
+ char *aobjname;
+
+ /* Get all addresses from active config. */
+ status = i_ipadm_active_addr_info(iph, ifname, &ainfo, ipadm_flags,
+ lifc_flags);
+ if (status != IPADM_SUCCESS)
+ goto fail;
+
+ /* Get all addresses from persistent config. */
+ status = i_ipadm_get_db_addr(iph, ifname, NULL, &onvl);
+ /*
+ * If no address was found in persistent config, just
+ * return what we found in active config.
+ */
+ if (status == IPADM_NOTFOUND) {
+ /*
+ * If nothing was found neither active nor persistent
+ * config, this means that the interface does not exist,
+ * if one was provided in `ifname'.
+ */
+ if (ainfo == NULL && ifname != NULL)
+ return (IPADM_ENXIO);
+ *addrinfo = ainfo;
+ return (IPADM_SUCCESS);
+ }
+ /* In case of any other error, cleanup and return. */
+ if (status != IPADM_SUCCESS)
+ goto fail;
+ /* we append to make sure, loopback addresses are first */
+ if (ainfo != NULL) {
+ for (curr = ainfo; IA_NEXT(curr) != NULL; curr = IA_NEXT(curr))
+ ;
+ last = curr;
+ }
+
+ /*
+ * `onvl' will contain all the address lines from the db. Each line
+ * could contain the address itself or an address property. Addresses
+ * and address properties are found in separate lines.
+ *
+ * If an address A was found in active, we will already have `ainfo',
+ * and it is present in persistent configuration as well, we need to
+ * update `ainfo' with persistent information (`ia_pflags).
+ * For each address B found only in persistent configuration,
+ * append the address to the list with the address info for B from
+ * `onvl'.
+ */
+ for (nvp = nvlist_next_nvpair(onvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(onvl, nvp)) {
+ if (nvpair_value_nvlist(nvp, &nvladdr) != 0)
+ continue;
+ if (nvlist_lookup_string(nvladdr, IPADM_NVP_AOBJNAME,
+ &aobjname) != 0)
+ continue;
+ for (curr = ainfo; curr != NULL; curr = IA_NEXT(curr)) {
+ if (strcmp(curr->ia_aobjname, aobjname) == 0)
+ break;
+ }
+ if (curr == NULL) {
+ /*
+ * We did not find this address object in `ainfo'.
+ * This means that the address object exists only
+ * in the persistent configuration. Get its
+ * details and append to `ainfo'.
+ */
+ curr = calloc(1, sizeof (ipadm_addr_info_t));
+ if (curr == NULL)
+ goto fail;
+ curr->ia_state = IFA_DISABLED;
+ if (last != NULL)
+ last->ia_ifa.ifa_next = &curr->ia_ifa;
+ else
+ ainfo = curr;
+ last = curr;
+ }
+ /*
+ * Fill relevant fields of `curr' from the persistent info
+ * in `nvladdr'. Call the appropriate function based on the
+ * `ia_state' value.
+ */
+ if (curr->ia_state == IFA_DISABLED)
+ status = i_ipadm_nvl2ainfo_persist(nvladdr, curr);
+ else
+ status = i_ipadm_nvl2ainfo_active(nvladdr, curr);
+ if (status != IPADM_SUCCESS)
+ goto fail;
+ }
+ *addrinfo = ainfo;
+ nvlist_free(onvl);
+ return (status);
+fail:
+ /* On error, cleanup and return. */
+ nvlist_free(onvl);
+ ipadm_free_addr_info(ainfo);
+ *addrinfo = NULL;
+ return (status);
+}
+
+/*
+ * Callback function that sets the property `prefixlen' on the address
+ * object in `arg' to the value in `pval'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_prefixlen(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
+{
+ struct sockaddr_storage netmask;
+ struct lifreq lifr;
+ int err, s;
+ unsigned long prefixlen, abits;
+ char *end;
+ ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
+
+ if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP)
+ return (IPADM_NOTSUP);
+
+ errno = 0;
+ prefixlen = strtoul(pval, &end, 10);
+ if (errno != 0 || *end != '\0')
+ return (IPADM_INVALID_ARG);
+
+ abits = (af == AF_INET ? IP_ABITS : IPV6_ABITS);
+ if (prefixlen == 0 || prefixlen == (abits - 1))
+ return (IPADM_INVALID_ARG);
+
+ if ((err = plen2mask(prefixlen, af, &netmask)) != 0)
+ return (ipadm_errno2status(err));
+
+ s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+
+ bzero(&lifr, sizeof (lifr));
+ i_ipadm_addrobj2lifname(ipaddr, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ (void) memcpy(&lifr.lifr_addr, &netmask, sizeof (netmask));
+ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ /* now, change the broadcast address to reflect the prefixlen */
+ if (af == AF_INET) {
+ /*
+ * get the interface address and set it, this should reset
+ * the broadcast address.
+ */
+ (void) ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr);
+ (void) ioctl(s, SIOCSLIFADDR, (caddr_t)&lifr);
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+
+/*
+ * Callback function that sets the given value `pval' to one of the
+ * properties among `deprecated', `private', and `transmit' as defined in
+ * `pdp', on the address object in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_addr_flag(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
+{
+ char lifname[LIFNAMSIZ];
+ uint64_t on_flags = 0, off_flags = 0;
+ boolean_t on;
+ ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
+
+ if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP &&
+ strcmp(pdp->ipd_name, "deprecated") == 0)
+ return (IPADM_NOTSUP);
+
+ if (strcmp(pval, IPADM_ONSTR) == 0)
+ on = B_TRUE;
+ else if (strcmp(pval, IPADM_OFFSTR) == 0)
+ on = B_FALSE;
+ else
+ return (IPADM_INVALID_ARG);
+
+ if (strcmp(pdp->ipd_name, "private") == 0) {
+ if (on)
+ on_flags = IFF_PRIVATE;
+ else
+ off_flags = IFF_PRIVATE;
+ } else if (strcmp(pdp->ipd_name, "transmit") == 0) {
+ if (on)
+ off_flags = IFF_NOXMIT;
+ else
+ on_flags = IFF_NOXMIT;
+ } else if (strcmp(pdp->ipd_name, "deprecated") == 0) {
+ if (on)
+ on_flags = IFF_DEPRECATED;
+ else
+ off_flags = IFF_DEPRECATED;
+ } else {
+ return (IPADM_PROP_UNKNOWN);
+ }
+
+ i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
+ return (i_ipadm_set_flags(iph, lifname, af, on_flags, off_flags));
+}
+
+/*
+ * Callback function that sets the property `zone' on the address
+ * object in `arg' to the value in `pval'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_zone(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t af, uint_t flags)
+{
+ struct lifreq lifr;
+ zoneid_t zoneid;
+ int s;
+
+ /*
+ * To modify the zone assignment such that it persists across
+ * reboots, zonecfg(1M) must be used.
+ */
+ if (flags & IPADM_OPT_PERSIST) {
+ return (IPADM_NOTSUP);
+ } else if (flags & IPADM_OPT_ACTIVE) {
+ /* put logical interface into all zones */
+ if (strcmp(pval, "all-zones") == 0) {
+ zoneid = ALL_ZONES;
+ } else {
+ /* zone must be ready or running */
+ if ((zoneid = getzoneidbyname(pval)) == -1)
+ return (ipadm_errno2status(errno));
+ }
+ } else {
+ return (IPADM_INVALID_ARG);
+ }
+
+ s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+ bzero(&lifr, sizeof (lifr));
+ i_ipadm_addrobj2lifname((ipadm_addrobj_t)arg, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_zoneid = zoneid;
+ if (ioctl(s, SIOCSLIFZONE, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Callback function that gets the property `broadcast' for the address
+ * object in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_broadcast(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
+ uint_t valtype)
+{
+ struct sockaddr_in *sin;
+ struct lifreq lifr;
+ char lifname[LIFNAMSIZ];
+ ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
+ ipadm_status_t status;
+ size_t nbytes = 0;
+ uint64_t ifflags = 0;
+
+ i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
+ if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
+ status = i_ipadm_get_flags(iph, lifname, af, &ifflags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (!(ifflags & IFF_BROADCAST)) {
+ buf[0] = '\0';
+ return (IPADM_SUCCESS);
+ }
+ }
+
+ switch (valtype) {
+ case MOD_PROP_DEFAULT: {
+ struct sockaddr_storage mask;
+ struct in_addr broadaddr;
+ uint_t plen;
+ in_addr_t addr, maddr;
+ char val[MAXPROPVALLEN];
+ uint_t valsz = MAXPROPVALLEN;
+ ipadm_status_t status;
+ int err;
+ struct sockaddr_in *sin;
+
+ if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE)) {
+ /*
+ * Since the address is unknown we cannot
+ * obtain default prefixlen
+ */
+ if (ipaddr->ipadm_atype == IPADM_ADDR_DHCP ||
+ ipaddr->ipadm_af == AF_INET6) {
+ buf[0] = '\0';
+ return (IPADM_SUCCESS);
+ }
+ /*
+ * For the static address, we get the address from the
+ * persistent db.
+ */
+ status = i_ipadm_get_static_addr_db(iph, ipaddr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ sin = SIN(&ipaddr->ipadm_static_addr);
+ addr = sin->sin_addr.s_addr;
+ } else {
+ /*
+ * If the address object is active, we retrieve the
+ * address from kernel.
+ */
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, lifname,
+ sizeof (lifr.lifr_name));
+ if (ioctl(iph->iph_sock, SIOCGLIFADDR,
+ (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ addr = (SIN(&lifr.lifr_addr))->sin_addr.s_addr;
+ }
+ /*
+ * For default broadcast address, get the address and the
+ * default prefixlen for that address and then compute the
+ * broadcast address.
+ */
+ status = i_ipadm_get_prefixlen(iph, arg, NULL, val, &valsz, af,
+ MOD_PROP_DEFAULT);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ plen = atoi(val);
+ if ((err = plen2mask(plen, AF_INET, &mask)) != 0)
+ return (ipadm_errno2status(err));
+ maddr = (SIN(&mask))->sin_addr.s_addr;
+ broadaddr.s_addr = (addr & maddr) | ~maddr;
+ nbytes = snprintf(buf, *bufsize, "%s", inet_ntoa(broadaddr));
+ break;
+ }
+ case MOD_PROP_ACTIVE:
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, lifname,
+ sizeof (lifr.lifr_name));
+ if (ioctl(iph->iph_sock, SIOCGLIFBRDADDR,
+ (caddr_t)&lifr) < 0) {
+ return (ipadm_errno2status(errno));
+ } else {
+ sin = SIN(&lifr.lifr_addr);
+ nbytes = snprintf(buf, *bufsize, "%s",
+ inet_ntoa(sin->sin_addr));
+ }
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Callback function that retrieves the value of the property `prefixlen'
+ * for the address object in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_prefixlen(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
+ uint_t valtype)
+{
+ struct lifreq lifr;
+ ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
+ char lifname[LIFNAMSIZ];
+ int s;
+ uint32_t prefixlen;
+ size_t nbytes;
+ ipadm_status_t status;
+ uint64_t lifflags;
+
+ i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
+ if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
+ status = i_ipadm_get_flags(iph, lifname, af, &lifflags);
+ if (status != IPADM_SUCCESS) {
+ return (status);
+ } else if (lifflags & IFF_POINTOPOINT) {
+ buf[0] = '\0';
+ return (status);
+ }
+ }
+
+ s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name));
+ switch (valtype) {
+ case MOD_PROP_POSSIBLE:
+ if (af == AF_INET)
+ nbytes = snprintf(buf, *bufsize, "1-30,32");
+ else
+ nbytes = snprintf(buf, *bufsize, "1-126,128");
+ break;
+ case MOD_PROP_DEFAULT:
+ if (ipaddr->ipadm_flags & IPMGMT_ACTIVE) {
+ /*
+ * For static addresses, we retrieve the address
+ * from kernel if it is active.
+ */
+ if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ status = i_ipadm_get_default_prefixlen(
+ &lifr.lifr_addr, &prefixlen);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ } else if ((ipaddr->ipadm_flags & IPMGMT_PERSIST) &&
+ ipaddr->ipadm_atype == IPADM_ADDR_DHCP) {
+ /*
+ * Since the address is unknown we cannot
+ * obtain default prefixlen
+ */
+ buf[0] = '\0';
+ return (IPADM_SUCCESS);
+ } else {
+ /*
+ * If not in active config, we use the address
+ * from persistent store.
+ */
+ status = i_ipadm_get_static_addr_db(iph, ipaddr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ status = i_ipadm_get_default_prefixlen(
+ &ipaddr->ipadm_static_addr, &prefixlen);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ }
+ nbytes = snprintf(buf, *bufsize, "%u", prefixlen);
+ break;
+ case MOD_PROP_ACTIVE:
+ if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ prefixlen = lifr.lifr_addrlen;
+ nbytes = snprintf(buf, *bufsize, "%u", prefixlen);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Callback function that retrieves the value of one of the properties
+ * among `deprecated', `private', and `transmit' for the address object
+ * in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_addr_flag(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
+ uint_t valtype)
+{
+ boolean_t on = B_FALSE;
+ char lifname[LIFNAMSIZ];
+ ipadm_status_t status = IPADM_SUCCESS;
+ uint64_t ifflags;
+ size_t nbytes;
+ ipadm_addrobj_t ipaddr = (ipadm_addrobj_t)arg;
+
+ switch (valtype) {
+ case MOD_PROP_DEFAULT:
+ if (strcmp(pdp->ipd_name, "private") == 0 ||
+ strcmp(pdp->ipd_name, "deprecated") == 0) {
+ on = B_FALSE;
+ } else if (strcmp(pdp->ipd_name, "transmit") == 0) {
+ on = B_TRUE;
+ } else {
+ return (IPADM_PROP_UNKNOWN);
+ }
+ break;
+ case MOD_PROP_ACTIVE:
+ /*
+ * If the address is present in active configuration, we
+ * retrieve it from kernel to get the property value.
+ * Else, there is no value to return.
+ */
+ i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
+ status = i_ipadm_get_flags(iph, lifname, af, &ifflags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (strcmp(pdp->ipd_name, "private") == 0)
+ on = (ifflags & IFF_PRIVATE);
+ else if (strcmp(pdp->ipd_name, "transmit") == 0)
+ on = !(ifflags & IFF_NOXMIT);
+ else if (strcmp(pdp->ipd_name, "deprecated") == 0)
+ on = (ifflags & IFF_DEPRECATED);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ nbytes = snprintf(buf, *bufsize, "%s",
+ (on ? IPADM_ONSTR : IPADM_OFFSTR));
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ status = IPADM_NO_BUFS;
+ }
+
+ return (status);
+}
+
+/*
+ * Callback function that retrieves the value of the property `zone'
+ * for the address object in `arg'.
+ */
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_zone(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t af,
+ uint_t valtype)
+{
+ struct lifreq lifr;
+ char zone_name[ZONENAME_MAX];
+ int s;
+ size_t nbytes = 0;
+
+ if (getzoneid() != GLOBAL_ZONEID) {
+ buf[0] = '\0';
+ return (IPADM_SUCCESS);
+ }
+
+ /*
+ * we are in global zone. See if the lifname is assigned to shared-ip
+ * zone or global zone.
+ */
+ switch (valtype) {
+ case MOD_PROP_DEFAULT:
+ if (getzonenamebyid(GLOBAL_ZONEID, zone_name,
+ sizeof (zone_name)) > 0)
+ nbytes = snprintf(buf, *bufsize, "%s", zone_name);
+ else
+ return (ipadm_errno2status(errno));
+ break;
+ case MOD_PROP_ACTIVE:
+ bzero(&lifr, sizeof (lifr));
+ i_ipadm_addrobj2lifname((ipadm_addrobj_t)arg, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ s = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+
+ if (ioctl(s, SIOCGLIFZONE, (caddr_t)&lifr) == -1)
+ return (ipadm_errno2status(errno));
+
+ if (lifr.lifr_zoneid == ALL_ZONES) {
+ nbytes = snprintf(buf, *bufsize, "%s", "all-zones");
+ } else if (getzonenamebyid(lifr.lifr_zoneid, zone_name,
+ sizeof (zone_name)) < 0) {
+ return (ipadm_errno2status(errno));
+ } else {
+ nbytes = snprintf(buf, *bufsize, "%s", zone_name);
+ }
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+static ipadm_prop_desc_t *
+i_ipadm_getpropdesc(const char *pname)
+{
+ int i;
+
+ for (i = 0; ipadm_addrprop_table[i].ipd_name != NULL; i++) {
+ if (strcmp(pname, ipadm_addrprop_table[i].ipd_name) == 0)
+ return (&ipadm_addrprop_table[i]);
+ }
+ return (NULL);
+}
+
+/*
+ * Gets the value of the given address property `pname' for the address
+ * object with name `aobjname'.
+ */
+ipadm_status_t
+ipadm_get_addrprop(ipadm_handle_t iph, const char *pname, char *buf,
+ uint_t *bufsize, const char *aobjname, uint_t valtype)
+{
+ struct ipadm_addrobj_s ipaddr;
+ ipadm_status_t status = IPADM_SUCCESS;
+ sa_family_t af;
+ ipadm_prop_desc_t *pdp = NULL;
+
+ if (iph == NULL || pname == NULL || buf == NULL ||
+ bufsize == NULL || *bufsize == 0 || aobjname == NULL) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* find the property in the property description table */
+ if ((pdp = i_ipadm_getpropdesc(pname)) == NULL)
+ return (IPADM_PROP_UNKNOWN);
+
+ /*
+ * For the given aobjname, get the addrobj it represents and
+ * retrieve the property value for that object.
+ */
+ i_ipadm_init_addr(&ipaddr, "", aobjname, IPADM_ADDR_NONE);
+ if ((status = i_ipadm_get_addrobj(iph, &ipaddr)) != IPADM_SUCCESS)
+ return (status);
+
+ if (ipaddr.ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF)
+ return (IPADM_NOTSUP);
+ af = ipaddr.ipadm_af;
+
+ /*
+ * Call the appropriate callback function to based on the field
+ * that was asked for.
+ */
+ switch (valtype) {
+ case IPADM_OPT_PERM:
+ status = i_ipadm_pd2permstr(pdp, buf, bufsize);
+ break;
+ case IPADM_OPT_ACTIVE:
+ if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE)) {
+ buf[0] = '\0';
+ } else {
+ status = pdp->ipd_get(iph, &ipaddr, pdp, buf, bufsize,
+ af, MOD_PROP_ACTIVE);
+ }
+ break;
+ case IPADM_OPT_DEFAULT:
+ status = pdp->ipd_get(iph, &ipaddr, pdp, buf, bufsize,
+ af, MOD_PROP_DEFAULT);
+ break;
+ case IPADM_OPT_POSSIBLE:
+ if (pdp->ipd_get_range != NULL) {
+ status = pdp->ipd_get_range(iph, &ipaddr, pdp, buf,
+ bufsize, af, MOD_PROP_POSSIBLE);
+ break;
+ }
+ buf[0] = '\0';
+ break;
+ case IPADM_OPT_PERSIST:
+ status = i_ipadm_get_persist_propval(iph, pdp, buf, bufsize,
+ &ipaddr);
+ break;
+ default:
+ status = IPADM_INVALID_ARG;
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * Sets the value of the given address property `pname' to `pval' for the
+ * address object with name `aobjname'.
+ */
+ipadm_status_t
+ipadm_set_addrprop(ipadm_handle_t iph, const char *pname,
+ const char *pval, const char *aobjname, uint_t pflags)
+{
+ struct ipadm_addrobj_s ipaddr;
+ sa_family_t af;
+ ipadm_prop_desc_t *pdp = NULL;
+ char defbuf[MAXPROPVALLEN];
+ uint_t defbufsize = MAXPROPVALLEN;
+ boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
+ ipadm_status_t status = IPADM_SUCCESS;
+
+ /* Check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ if (iph == NULL || pname == NULL || aobjname == NULL || pflags == 0 ||
+ pflags == IPADM_OPT_PERSIST ||
+ (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT)) ||
+ (!reset && pval == NULL)) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* find the property in the property description table */
+ if ((pdp = i_ipadm_getpropdesc(pname)) == NULL)
+ return (IPADM_PROP_UNKNOWN);
+
+ if (pdp->ipd_set == NULL || (reset && pdp->ipd_get == NULL))
+ return (IPADM_NOTSUP);
+
+ /*
+ * For the given aobjname, get the addrobj it represents and
+ * set the property value for that object.
+ */
+ i_ipadm_init_addr(&ipaddr, "", aobjname, IPADM_ADDR_NONE);
+ if ((status = i_ipadm_get_addrobj(iph, &ipaddr)) != IPADM_SUCCESS)
+ return (status);
+
+ if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE))
+ return (IPADM_OP_DISABLE_OBJ);
+
+ /* Persistent operation not allowed on a temporary object. */
+ if ((pflags & IPADM_OPT_PERSIST) &&
+ !(ipaddr.ipadm_flags & IPMGMT_PERSIST))
+ return (IPADM_TEMPORARY_OBJ);
+
+ /*
+ * Currently, setting an address property on an address object of type
+ * IPADM_ADDR_IPV6_ADDRCONF is not supported. Supporting it involves
+ * in.ndpd retrieving the address properties from ipmgmtd for given
+ * address object and then setting them on auto-configured addresses,
+ * whenever in.ndpd gets a new prefix. This will be supported in
+ * future releases.
+ */
+ if (ipaddr.ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF)
+ return (IPADM_NOTSUP);
+
+ /*
+ * Setting an address property on an address object that is
+ * not present in active configuration is not supported.
+ */
+ if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE))
+ return (IPADM_NOTSUP);
+
+ af = ipaddr.ipadm_af;
+ if (reset) {
+ /*
+ * If we were asked to reset the value, we need to fetch
+ * the default value and set the default value.
+ */
+ status = pdp->ipd_get(iph, &ipaddr, pdp, defbuf, &defbufsize,
+ af, MOD_PROP_DEFAULT);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ pval = defbuf;
+ }
+ /* set the user provided or default property value */
+ status = pdp->ipd_set(iph, &ipaddr, pdp, pval, af, pflags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ /*
+ * If IPADM_OPT_PERSIST was set in `flags', we need to store
+ * property and its value in persistent DB.
+ */
+ if (pflags & IPADM_OPT_PERSIST) {
+ status = i_ipadm_persist_propval(iph, pdp, pval, &ipaddr,
+ pflags);
+ }
+
+ return (status);
+}
+
+/*
+ * Remove the address specified by the address object in `addr'
+ * from kernel. If the address is on a non-zero logical interface, we do a
+ * SIOCLIFREMOVEIF, otherwise we set the address to INADDR_ANY for IPv4 or
+ * :: for IPv6.
+ */
+ipadm_status_t
+i_ipadm_delete_addr(ipadm_handle_t iph, ipadm_addrobj_t addr)
+{
+ struct lifreq lifr;
+ int sock;
+ ipadm_status_t status;
+
+ bzero(&lifr, sizeof (lifr));
+ i_ipadm_addrobj2lifname(addr, lifr.lifr_name, sizeof (lifr.lifr_name));
+ sock = (addr->ipadm_af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+ if (addr->ipadm_lifnum == 0) {
+ /*
+ * Fake the deletion of the 0'th address by
+ * clearing IFF_UP and setting it to as 0.0.0.0 or ::.
+ */
+ status = i_ipadm_set_flags(iph, addr->ipadm_ifname,
+ addr->ipadm_af, 0, IFF_UP);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
+ lifr.lifr_addr.ss_family = addr->ipadm_af;
+ if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ if (ioctl(sock, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ } else if (ioctl(sock, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
+ return (ipadm_errno2status(errno));
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Extracts the IPv6 address from the nvlist in `nvl'.
+ */
+ipadm_status_t
+i_ipadm_nvl2in6_addr(nvlist_t *nvl, char *addr_type, in6_addr_t *in6_addr)
+{
+ uint8_t *addr6;
+ uint_t n;
+
+ if (nvlist_lookup_uint8_array(nvl, addr_type, &addr6, &n) != 0)
+ return (IPADM_NOTFOUND);
+ assert(n == 16);
+ bcopy(addr6, in6_addr->s6_addr, n);
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Used to validate the given addrobj name string. Length of `aobjname'
+ * cannot exceed IPADM_AOBJ_USTRSIZ. `aobjname' should start with an
+ * alphabetic character and it can only contain alphanumeric characters.
+ */
+static boolean_t
+i_ipadm_is_user_aobjname_valid(const char *aobjname)
+{
+ const char *cp;
+
+ if (aobjname == NULL || strlen(aobjname) >= IPADM_AOBJ_USTRSIZ ||
+ !isalpha(*aobjname)) {
+ return (B_FALSE);
+ }
+ for (cp = aobjname + 1; *cp && isalnum(*cp); cp++)
+ ;
+ return (*cp == '\0');
+}
+
+/*
+ * Computes the prefixlen for the given `addr' based on the netmask found using
+ * the order specified in /etc/nsswitch.conf. If not found, then the
+ * prefixlen is computed using the Classful subnetting semantics defined
+ * in RFC 791 for IPv4 and RFC 4291 for IPv6.
+ */
+static ipadm_status_t
+i_ipadm_get_default_prefixlen(struct sockaddr_storage *addr, uint32_t *plen)
+{
+ sa_family_t af = addr->ss_family;
+ struct sockaddr_storage mask;
+ struct sockaddr_in *m = (struct sockaddr_in *)&mask;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+ struct in_addr ia;
+ uint32_t prefixlen = 0;
+
+ switch (af) {
+ case AF_INET:
+ sin = SIN(addr);
+ ia.s_addr = ntohl(sin->sin_addr.s_addr);
+ get_netmask4(&ia, &m->sin_addr);
+ m->sin_addr.s_addr = htonl(m->sin_addr.s_addr);
+ m->sin_family = AF_INET;
+ prefixlen = mask2plen(&mask);
+ break;
+ case AF_INET6:
+ sin6 = SIN6(addr);
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ prefixlen = 10;
+ else
+ prefixlen = 64;
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ *plen = prefixlen;
+ return (IPADM_SUCCESS);
+}
+
+static ipadm_status_t
+i_ipadm_resolve_addr(const char *name, sa_family_t af,
+ struct sockaddr_storage *ss)
+{
+ struct addrinfo hints, *ai;
+ int rc;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin;
+ boolean_t is_mapped;
+
+ (void) memset(&hints, 0, sizeof (hints));
+ hints.ai_family = af;
+ hints.ai_flags = (AI_ALL | AI_V4MAPPED);
+ rc = getaddrinfo(name, NULL, &hints, &ai);
+ if (rc != 0) {
+ if (rc == EAI_NONAME)
+ return (IPADM_BAD_ADDR);
+ else
+ return (IPADM_FAILURE);
+ }
+ if (ai->ai_next != NULL) {
+ /* maps to more than one hostname */
+ freeaddrinfo(ai);
+ return (IPADM_BAD_HOSTNAME);
+ }
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ is_mapped = IN6_IS_ADDR_V4MAPPED(&(SIN6(ai->ai_addr))->sin6_addr);
+ if (is_mapped) {
+ sin = SIN(ss);
+ sin->sin_family = AF_INET;
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ IN6_V4MAPPED_TO_INADDR(&(SIN6(ai->ai_addr))->sin6_addr,
+ &sin->sin_addr);
+ } else {
+ sin6 = SIN6(ss);
+ sin6->sin6_family = AF_INET6;
+ bcopy(ai->ai_addr, sin6, sizeof (*sin6));
+ }
+ freeaddrinfo(ai);
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * This takes a static address string <addr>[/<mask>] or a hostname
+ * and maps it to a single numeric IP address, consulting DNS if
+ * hostname was provided. If a specific address family was requested,
+ * an error is returned if the given hostname does not map to an address
+ * of the given family. Note that this function returns failure
+ * if the name maps to more than one IP address.
+ */
+ipadm_status_t
+ipadm_set_addr(ipadm_addrobj_t ipaddr, const char *astr, sa_family_t af)
+{
+ char *prefixlenstr;
+ uint32_t prefixlen = 0;
+ char *endp;
+ /*
+ * We use (NI_MAXHOST + 5) because the longest possible
+ * astr will have (NI_MAXHOST + '/' + {a maximum of 32 for IPv4
+ * or a maximum of 128 for IPv6 + '\0') chars
+ */
+ char addrstr[NI_MAXHOST + 5];
+ ipadm_status_t status;
+
+ (void) snprintf(addrstr, sizeof (addrstr), "%s", astr);
+ if ((prefixlenstr = strchr(addrstr, '/')) != NULL) {
+ *prefixlenstr++ = '\0';
+ errno = 0;
+ prefixlen = strtoul(prefixlenstr, &endp, 10);
+ if (errno != 0 || *endp != '\0')
+ return (IPADM_INVALID_ARG);
+ if ((af == AF_INET && prefixlen > IP_ABITS) ||
+ (af == AF_INET6 && prefixlen > IPV6_ABITS))
+ return (IPADM_INVALID_ARG);
+ }
+
+ status = i_ipadm_resolve_addr(addrstr, af, &ipaddr->ipadm_static_addr);
+ if (status == IPADM_SUCCESS) {
+ (void) strlcpy(ipaddr->ipadm_static_aname, addrstr,
+ sizeof (ipaddr->ipadm_static_aname));
+ ipaddr->ipadm_af = ipaddr->ipadm_static_addr.ss_family;
+ ipaddr->ipadm_static_prefixlen = prefixlen;
+ }
+ return (status);
+}
+
+/*
+ * Set up tunnel destination address in ipaddr by contacting DNS.
+ * The function works similar to ipadm_set_addr().
+ * The dst_addr must resolve to exactly one address. IPADM_BAD_ADDR is returned
+ * if dst_addr resolves to more than one address. The caller has to verify
+ * that ipadm_static_addr and ipadm_static_dst_addr have the same ss_family
+ */
+ipadm_status_t
+ipadm_set_dst_addr(ipadm_addrobj_t ipaddr, const char *daddrstr, sa_family_t af)
+{
+ ipadm_status_t status;
+
+ /* mask lengths are not meaningful for point-to-point interfaces. */
+ if (strchr(daddrstr, '/') != NULL)
+ return (IPADM_BAD_ADDR);
+
+ status = i_ipadm_resolve_addr(daddrstr, af,
+ &ipaddr->ipadm_static_dst_addr);
+ if (status == IPADM_SUCCESS) {
+ (void) strlcpy(ipaddr->ipadm_static_dname, daddrstr,
+ sizeof (ipaddr->ipadm_static_dname));
+ }
+ return (status);
+}
+
+/*
+ * Sets the interface ID in the address object `ipaddr' with the address
+ * in the string `interface_id'. This interface ID will be used when
+ * ipadm_create_addr() is called with `ipaddr' with address type
+ * set to IPADM_ADDR_IPV6_ADDRCONF.
+ */
+ipadm_status_t
+ipadm_set_interface_id(ipadm_addrobj_t ipaddr, const char *interface_id)
+{
+ struct sockaddr_in6 *sin6;
+ char *end;
+ char *cp;
+ uint32_t prefixlen;
+ char addrstr[INET6_ADDRSTRLEN + 1];
+
+ if (ipaddr == NULL || interface_id == NULL ||
+ ipaddr->ipadm_atype != IPADM_ADDR_IPV6_ADDRCONF)
+ return (IPADM_INVALID_ARG);
+
+ (void) strlcpy(addrstr, interface_id, sizeof (addrstr));
+ if ((cp = strchr(addrstr, '/')) == NULL)
+ return (IPADM_INVALID_ARG);
+ *cp++ = '\0';
+ sin6 = &ipaddr->ipadm_intfid;
+ if (inet_pton(AF_INET6, addrstr, &sin6->sin6_addr) == 1) {
+ errno = 0;
+ prefixlen = strtoul(cp, &end, 10);
+ if (errno != 0 || *end != '\0' || prefixlen > IPV6_ABITS)
+ return (IPADM_INVALID_ARG);
+ sin6->sin6_family = AF_INET6;
+ ipaddr->ipadm_intfidlen = prefixlen;
+ return (IPADM_SUCCESS);
+ }
+ return (IPADM_INVALID_ARG);
+}
+
+/*
+ * Sets the value for the field `ipadm_stateless' in address object `ipaddr'.
+ */
+ipadm_status_t
+ipadm_set_stateless(ipadm_addrobj_t ipaddr, boolean_t stateless)
+{
+ if (ipaddr == NULL ||
+ ipaddr->ipadm_atype != IPADM_ADDR_IPV6_ADDRCONF)
+ return (IPADM_INVALID_ARG);
+ ipaddr->ipadm_stateless = stateless;
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Sets the value for the field `ipadm_stateful' in address object `ipaddr'.
+ */
+ipadm_status_t
+ipadm_set_stateful(ipadm_addrobj_t ipaddr, boolean_t stateful)
+{
+ if (ipaddr == NULL ||
+ ipaddr->ipadm_atype != IPADM_ADDR_IPV6_ADDRCONF)
+ return (IPADM_INVALID_ARG);
+ ipaddr->ipadm_stateful = stateful;
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Sets the dhcp parameter `ipadm_primary' in the address object `ipaddr'.
+ * The field is used during the address creation with address
+ * type IPADM_ADDR_DHCP. It specifies if the interface should be set
+ * as a primary interface for getting dhcp global options from the DHCP server.
+ */
+ipadm_status_t
+ipadm_set_primary(ipadm_addrobj_t ipaddr, boolean_t primary)
+{
+ if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
+ return (IPADM_INVALID_ARG);
+ ipaddr->ipadm_primary = primary;
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Sets the dhcp parameter `ipadm_wait' in the address object `ipaddr'.
+ * This field is used during the address creation with address type
+ * IPADM_ADDR_DHCP. It specifies how long the API ipadm_create_addr()
+ * should wait before returning while the dhcp address is being acquired
+ * by the dhcpagent.
+ * Possible values:
+ * - IPADM_DHCP_WAIT_FOREVER : Do not return until dhcpagent returns.
+ * - IPADM_DHCP_WAIT_DEFAULT : Wait a default amount of time before returning.
+ * - <integer> : Wait the specified number of seconds before returning.
+ */
+ipadm_status_t
+ipadm_set_wait_time(ipadm_addrobj_t ipaddr, int32_t wait)
+{
+ if (ipaddr == NULL || ipaddr->ipadm_atype != IPADM_ADDR_DHCP)
+ return (IPADM_INVALID_ARG);
+ ipaddr->ipadm_wait = wait;
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Creates a placeholder for the `ipadm_aobjname' in the ipmgmtd `aobjmap'.
+ * If the `aobjname' already exists in the daemon's `aobjmap' then
+ * IPADM_ADDROBJ_EXISTS will be returned.
+ *
+ * If the libipadm consumer set `ipaddr.ipadm_aobjname[0]' to `\0', then the
+ * daemon will generate an `aobjname' for the given `ipaddr'.
+ */
+ipadm_status_t
+i_ipadm_lookupadd_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
+{
+ ipmgmt_aobjop_arg_t larg;
+ ipmgmt_aobjop_rval_t rval, *rvalp;
+ int err;
+
+ bzero(&larg, sizeof (larg));
+ larg.ia_cmd = IPMGMT_CMD_ADDROBJ_LOOKUPADD;
+ (void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname,
+ sizeof (larg.ia_aobjname));
+ (void) strlcpy(larg.ia_ifname, ipaddr->ipadm_ifname,
+ sizeof (larg.ia_ifname));
+ larg.ia_family = ipaddr->ipadm_af;
+
+ rvalp = &rval;
+ err = ipadm_door_call(iph, &larg, sizeof (larg), (void **)&rvalp,
+ sizeof (rval), B_FALSE);
+ if (err == 0 && ipaddr->ipadm_aobjname[0] == '\0') {
+ /* copy the daemon generated `aobjname' into `ipadddr' */
+ (void) strlcpy(ipaddr->ipadm_aobjname, rval.ir_aobjname,
+ sizeof (ipaddr->ipadm_aobjname));
+ }
+ if (err == EEXIST)
+ return (IPADM_ADDROBJ_EXISTS);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Creates the IPv4 or IPv6 address in the nvlist `nvl' on the interface
+ * `ifname'. If a hostname is present, it is resolved before the address
+ * is created.
+ */
+ipadm_status_t
+i_ipadm_enable_static(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl,
+ sa_family_t af)
+{
+ char *prefixlenstr = NULL;
+ char *upstr = NULL;
+ char *sname = NULL, *dname = NULL;
+ struct ipadm_addrobj_s ipaddr;
+ char *aobjname = NULL;
+ nvlist_t *nvaddr = NULL;
+ nvpair_t *nvp;
+ char *cidraddr;
+ char *name;
+ ipadm_status_t status;
+ int err = 0;
+ uint32_t flags = IPADM_OPT_ACTIVE;
+
+ /* retrieve the address information */
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IPV4ADDR) == 0 ||
+ strcmp(name, IPADM_NVP_IPV6ADDR) == 0) {
+ err = nvpair_value_nvlist(nvp, &nvaddr);
+ } else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0) {
+ err = nvpair_value_string(nvp, &aobjname);
+ } else if (strcmp(name, IPADM_NVP_PREFIXLEN) == 0) {
+ err = nvpair_value_string(nvp, &prefixlenstr);
+ } else if (strcmp(name, "up") == 0) {
+ err = nvpair_value_string(nvp, &upstr);
+ }
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+ for (nvp = nvlist_next_nvpair(nvaddr, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvaddr, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IPADDRHNAME) == 0)
+ err = nvpair_value_string(nvp, &sname);
+ else if (strcmp(name, IPADM_NVP_IPDADDRHNAME) == 0)
+ err = nvpair_value_string(nvp, &dname);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+
+ if (strcmp(upstr, "yes") == 0)
+ flags |= IPADM_OPT_UP;
+
+ /* build the address object from the above information */
+ i_ipadm_init_addr(&ipaddr, ifname, aobjname, IPADM_ADDR_STATIC);
+ if (prefixlenstr != NULL && atoi(prefixlenstr) > 0) {
+ if (asprintf(&cidraddr, "%s/%s", sname, prefixlenstr) == -1)
+ return (IPADM_NO_MEMORY);
+ status = ipadm_set_addr(&ipaddr, cidraddr, af);
+ free(cidraddr);
+ } else {
+ status = ipadm_set_addr(&ipaddr, sname, af);
+ }
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ if (dname != NULL) {
+ status = ipadm_set_dst_addr(&ipaddr, dname, af);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ }
+ return (i_ipadm_create_addr(iph, &ipaddr, flags));
+}
+
+/*
+ * Creates a dhcp address on the interface `ifname' based on the
+ * IPADM_ADDR_DHCP address object parameters from the nvlist `nvl'.
+ */
+ipadm_status_t
+i_ipadm_enable_dhcp(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl)
+{
+ int32_t wait;
+ boolean_t primary;
+ nvlist_t *nvdhcp;
+ nvpair_t *nvp;
+ char *name;
+ struct ipadm_addrobj_s ipaddr;
+ char *aobjname;
+ int err = 0;
+
+ /* Extract the dhcp parameters */
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_DHCP) == 0)
+ err = nvpair_value_nvlist(nvp, &nvdhcp);
+ else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0)
+ err = nvpair_value_string(nvp, &aobjname);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+ for (nvp = nvlist_next_nvpair(nvdhcp, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvdhcp, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_WAIT) == 0)
+ err = nvpair_value_int32(nvp, &wait);
+ else if (strcmp(name, IPADM_NVP_PRIMARY) == 0)
+ err = nvpair_value_boolean_value(nvp, &primary);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+
+ /* Build the address object */
+ i_ipadm_init_addr(&ipaddr, ifname, aobjname, IPADM_ADDR_DHCP);
+ ipaddr.ipadm_primary = primary;
+ if (iph->iph_flags & IPH_INIT)
+ ipaddr.ipadm_wait = 0;
+ else
+ ipaddr.ipadm_wait = wait;
+ return (i_ipadm_create_dhcp(iph, &ipaddr, IPADM_OPT_ACTIVE));
+}
+
+/*
+ * Creates auto-configured addresses on the interface `ifname' based on
+ * the IPADM_ADDR_IPV6_ADDRCONF address object parameters from the nvlist `nvl'.
+ */
+ipadm_status_t
+i_ipadm_enable_addrconf(ipadm_handle_t iph, const char *ifname, nvlist_t *nvl)
+{
+ struct ipadm_addrobj_s ipaddr;
+ char *stateful = NULL, *stateless = NULL;
+ uint_t n;
+ uint8_t *addr6 = NULL;
+ uint32_t intfidlen = 0;
+ char *aobjname;
+ nvlist_t *nvaddr;
+ nvpair_t *nvp;
+ char *name;
+ int err = 0;
+
+ /* Extract the parameters */
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_INTFID) == 0)
+ err = nvpair_value_nvlist(nvp, &nvaddr);
+ else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0)
+ err = nvpair_value_string(nvp, &aobjname);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+ for (nvp = nvlist_next_nvpair(nvaddr, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvaddr, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IPNUMADDR) == 0)
+ err = nvpair_value_uint8_array(nvp, &addr6, &n);
+ if (strcmp(name, IPADM_NVP_PREFIXLEN) == 0)
+ err = nvpair_value_uint32(nvp, &intfidlen);
+ else if (strcmp(name, IPADM_NVP_STATELESS) == 0)
+ err = nvpair_value_string(nvp, &stateless);
+ else if (strcmp(name, IPADM_NVP_STATEFUL) == 0)
+ err = nvpair_value_string(nvp, &stateful);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ }
+ /* Build the address object. */
+ i_ipadm_init_addr(&ipaddr, ifname, aobjname, IPADM_ADDR_IPV6_ADDRCONF);
+ if (intfidlen > 0) {
+ ipaddr.ipadm_intfidlen = intfidlen;
+ bcopy(addr6, &ipaddr.ipadm_intfid.sin6_addr.s6_addr, n);
+ }
+ ipaddr.ipadm_stateless = (strcmp(stateless, "yes") == 0);
+ ipaddr.ipadm_stateful = (strcmp(stateful, "yes") == 0);
+ return (i_ipadm_create_ipv6addrs(iph, &ipaddr, IPADM_OPT_ACTIVE));
+}
+
+/*
+ * Allocates `ipadm_addrobj_t' and populates the relevant member fields based on
+ * the provided `type'. `aobjname' represents the address object name, which
+ * is of the form `<ifname>/<addressname>'.
+ *
+ * The caller has to minimally provide <ifname>. If <addressname> is not
+ * provided, then a default one will be generated by the API.
+ */
+ipadm_status_t
+ipadm_create_addrobj(ipadm_addr_type_t type, const char *aobjname,
+ ipadm_addrobj_t *ipaddr)
+{
+ ipadm_addrobj_t newaddr;
+ ipadm_status_t status;
+ char *aname, *cp;
+ char ifname[IPADM_AOBJSIZ];
+ ifspec_t ifsp;
+
+ if (ipaddr == NULL)
+ return (IPADM_INVALID_ARG);
+ *ipaddr = NULL;
+
+ if (aobjname == NULL || aobjname[0] == '\0')
+ return (IPADM_INVALID_ARG);
+
+ if (strlcpy(ifname, aobjname, IPADM_AOBJSIZ) >= IPADM_AOBJSIZ)
+ return (IPADM_INVALID_ARG);
+
+ if ((aname = strchr(ifname, '/')) != NULL)
+ *aname++ = '\0';
+
+ /* Check if the interface name is valid. */
+ if (!ifparse_ifspec(ifname, &ifsp))
+ return (IPADM_INVALID_ARG);
+
+ /* Check if the given addrobj name is valid. */
+ if (aname != NULL && !i_ipadm_is_user_aobjname_valid(aname))
+ return (IPADM_INVALID_ARG);
+
+ if ((newaddr = calloc(1, sizeof (struct ipadm_addrobj_s))) == NULL)
+ return (IPADM_NO_MEMORY);
+
+ /*
+ * If the ifname has logical interface number, extract it and assign
+ * it to `ipadm_lifnum'. Only applications with IPH_LEGACY set will do
+ * this today. We will check for the validity later in
+ * i_ipadm_validate_create_addr().
+ */
+ if (ifsp.ifsp_lunvalid) {
+ newaddr->ipadm_lifnum = ifsp.ifsp_lun;
+ cp = strchr(ifname, IPADM_LOGICAL_SEP);
+ *cp = '\0';
+ }
+ (void) strlcpy(newaddr->ipadm_ifname, ifname,
+ sizeof (newaddr->ipadm_ifname));
+
+ if (aname != NULL) {
+ (void) snprintf(newaddr->ipadm_aobjname,
+ sizeof (newaddr->ipadm_aobjname), "%s/%s", ifname, aname);
+ }
+
+ switch (type) {
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ newaddr->ipadm_intfidlen = 0;
+ newaddr->ipadm_stateful = B_TRUE;
+ newaddr->ipadm_stateless = B_TRUE;
+ newaddr->ipadm_af = AF_INET6;
+ break;
+
+ case IPADM_ADDR_DHCP:
+ newaddr->ipadm_primary = B_FALSE;
+ newaddr->ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
+ newaddr->ipadm_af = AF_INET;
+ break;
+
+ case IPADM_ADDR_STATIC:
+ newaddr->ipadm_af = AF_UNSPEC;
+ newaddr->ipadm_static_prefixlen = 0;
+ break;
+ default:
+ status = IPADM_INVALID_ARG;
+ goto fail;
+ }
+ newaddr->ipadm_atype = type;
+ *ipaddr = newaddr;
+ return (IPADM_SUCCESS);
+fail:
+ free(newaddr);
+ return (status);
+}
+
+/*
+ * Frees the address object in `ipaddr'.
+ */
+void
+ipadm_destroy_addrobj(ipadm_addrobj_t ipaddr)
+{
+ free(ipaddr);
+}
+
+/*
+ * Retrieves the logical interface name from `ipaddr' and stores the
+ * string in `lifname'.
+ */
+void
+i_ipadm_addrobj2lifname(ipadm_addrobj_t ipaddr, char *lifname, int lifnamesize)
+{
+ if (ipaddr->ipadm_lifnum != 0) {
+ (void) snprintf(lifname, lifnamesize, "%s:%d",
+ ipaddr->ipadm_ifname, ipaddr->ipadm_lifnum);
+ } else {
+ (void) snprintf(lifname, lifnamesize, "%s",
+ ipaddr->ipadm_ifname);
+ }
+}
+
+/*
+ * Checks if a non-zero static address is present on the 0th logical interface
+ * of the given IPv4 or IPv6 physical interface. For an IPv4 interface, it
+ * also checks if the interface is under DHCP control. If the condition is true,
+ * the output argument `exists' will be set to B_TRUE. Otherwise, `exists'
+ * is set to B_FALSE.
+ */
+static ipadm_status_t
+i_ipadm_addr_exists_on_if(ipadm_handle_t iph, const char *ifname,
+ sa_family_t af, boolean_t *exists)
+{
+ struct lifreq lifr;
+ int sock;
+
+ /* For IPH_LEGACY, a new logical interface will never be added. */
+ if (iph->iph_flags & IPH_LEGACY) {
+ *exists = B_FALSE;
+ return (IPADM_SUCCESS);
+ }
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (af == AF_INET) {
+ sock = iph->iph_sock;
+ if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ if (lifr.lifr_flags & IFF_DHCPRUNNING) {
+ *exists = B_TRUE;
+ return (IPADM_SUCCESS);
+ }
+ } else {
+ sock = iph->iph_sock6;
+ }
+ if (ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ *exists = !sockaddrunspec(&lifr.lifr_addr);
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Reads all the address lines from the persistent DB into the nvlist `onvl',
+ * when both `ifname' and `aobjname' are NULL. If an `ifname' is provided,
+ * it returns all the addresses for the given interface `ifname'.
+ * If an `aobjname' is specified, then the address line corresponding to
+ * that name will be returned.
+ */
+static ipadm_status_t
+i_ipadm_get_db_addr(ipadm_handle_t iph, const char *ifname,
+ const char *aobjname, nvlist_t **onvl)
+{
+ ipmgmt_getaddr_arg_t garg;
+ ipmgmt_get_rval_t *rvalp;
+ int err;
+ size_t nvlsize;
+ char *nvlbuf;
+
+ /* Populate the door_call argument structure */
+ bzero(&garg, sizeof (garg));
+ garg.ia_cmd = IPMGMT_CMD_GETADDR;
+ if (aobjname != NULL)
+ (void) strlcpy(garg.ia_aobjname, aobjname,
+ sizeof (garg.ia_aobjname));
+ if (ifname != NULL)
+ (void) strlcpy(garg.ia_ifname, ifname, sizeof (garg.ia_ifname));
+
+ rvalp = malloc(sizeof (ipmgmt_get_rval_t));
+ err = ipadm_door_call(iph, &garg, sizeof (garg), (void **)&rvalp,
+ sizeof (*rvalp), B_TRUE);
+ if (err == 0) {
+ nvlsize = rvalp->ir_nvlsize;
+ nvlbuf = (char *)rvalp + sizeof (ipmgmt_get_rval_t);
+ err = nvlist_unpack(nvlbuf, nvlsize, onvl, NV_ENCODE_NATIVE);
+ }
+ free(rvalp);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Adds the IP address contained in the 'ipaddr' argument to the physical
+ * interface represented by 'ifname' after doing the required validation.
+ * If the interface does not exist, it is created before the address is
+ * added.
+ *
+ * If IPH_LEGACY is set in iph_flags, flags has to be IPADM_OPT_ACTIVE
+ * and a default addrobj name will be generated. Input `addr->ipadm_aobjname',
+ * if provided, will be ignored and replaced with the newly generated name.
+ * The interface name provided has to be a logical interface name that
+ * already exists. No new logical interface will be added in this function.
+ */
+ipadm_status_t
+ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags)
+{
+ ipadm_status_t status;
+ sa_family_t af;
+ sa_family_t daf;
+ sa_family_t other_af;
+ boolean_t created_af = B_FALSE;
+ boolean_t created_other_af = B_FALSE;
+ ipadm_addr_type_t type;
+ char *ifname = addr->ipadm_ifname;
+ boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
+ boolean_t aobjfound;
+ boolean_t is_6to4;
+ struct lifreq lifr;
+ uint64_t ifflags;
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* Validate the addrobj. This also fills in addr->ipadm_ifname. */
+ status = i_ipadm_validate_create_addr(iph, addr, flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ /*
+ * For Legacy case, check if an addrobj already exists for the
+ * given logical interface name. If one does not exist,
+ * a default name will be generated and added to the daemon's
+ * aobjmap.
+ */
+ if (legacy) {
+ struct ipadm_addrobj_s ipaddr;
+
+ ipaddr = *addr;
+ status = i_ipadm_get_lif2addrobj(iph, &ipaddr);
+ if (status == IPADM_SUCCESS) {
+ aobjfound = B_TRUE;
+ /*
+ * With IPH_LEGACY, modifying an address that is not
+ * a static address will return with an error.
+ */
+ if (ipaddr.ipadm_atype != IPADM_ADDR_STATIC)
+ return (IPADM_NOTSUP);
+ /*
+ * we found the addrobj in daemon, copy over the
+ * aobjname to `addr'.
+ */
+ (void) strlcpy(addr->ipadm_aobjname,
+ ipaddr.ipadm_aobjname, IPADM_AOBJSIZ);
+ } else if (status == IPADM_NOTFOUND) {
+ aobjfound = B_FALSE;
+ } else {
+ return (status);
+ }
+ }
+
+ af = addr->ipadm_af;
+ /*
+ * Create a placeholder for this address object in the daemon.
+ * Skip this step for IPH_LEGACY case if the addrobj already
+ * exists.
+ */
+ if (!legacy || !aobjfound) {
+ status = i_ipadm_lookupadd_addrobj(iph, addr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ }
+
+ is_6to4 = i_ipadm_is_6to4(iph, ifname);
+ /* Plumb the IP interfaces if necessary */
+ status = i_ipadm_create_if(iph, ifname, af, flags);
+ if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS) {
+ (void) i_ipadm_delete_addrobj(iph, addr, IPADM_OPT_ACTIVE);
+ return (status);
+ }
+ if (status == IPADM_SUCCESS)
+ created_af = B_TRUE;
+ if (!is_6to4 && !legacy) {
+ other_af = (af == AF_INET ? AF_INET6 : AF_INET);
+ status = i_ipadm_create_if(iph, ifname, other_af, flags);
+ if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS) {
+ (void) i_ipadm_delete_if(iph, ifname, af, flags);
+ return (status);
+ }
+ if (status == IPADM_SUCCESS)
+ created_other_af = B_TRUE;
+ }
+
+ /* Validate static addresses for IFF_POINTOPOINT interfaces. */
+ if (addr->ipadm_atype == IPADM_ADDR_STATIC) {
+ status = i_ipadm_get_flags(iph, ifname, af, &ifflags);
+ if (status != IPADM_SUCCESS)
+ goto fail;
+ daf = addr->ipadm_static_dst_addr.ss_family;
+ if (ifflags & IFF_POINTOPOINT) {
+ if (is_6to4) {
+ if (af != AF_INET6 || daf != AF_UNSPEC) {
+ status = IPADM_INVALID_ARG;
+ goto fail;
+ }
+ } else {
+ if (daf != af) {
+ status = IPADM_INVALID_ARG;
+ goto fail;
+ }
+ /* Check for a valid dst address. */
+ if (!legacy && sockaddrunspec(
+ &addr->ipadm_static_dst_addr)) {
+ status = IPADM_BAD_ADDR;
+ goto fail;
+ }
+ }
+ } else {
+ /*
+ * Disallow setting of dstaddr when the link is not
+ * a point-to-point link.
+ */
+ if (daf != AF_UNSPEC)
+ return (IPADM_INVALID_ARG);
+ }
+ }
+
+ /*
+ * For 6to4 interfaces, kernel configures a default link-local
+ * address. We need to replace it, if the caller has provided
+ * an address that is different from the default link-local.
+ */
+ if (status == IPADM_SUCCESS && is_6to4) {
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, addr->ipadm_ifname,
+ sizeof (lifr.lifr_name));
+ if (ioctl(iph->iph_sock6, SIOCGLIFADDR, &lifr) < 0) {
+ status = ipadm_errno2status(errno);
+ goto fail;
+ }
+ if (sockaddrcmp(&lifr.lifr_addr, &addr->ipadm_static_addr))
+ return (IPADM_SUCCESS);
+ }
+
+ /* Create the address. */
+ type = addr->ipadm_atype;
+ switch (type) {
+ case IPADM_ADDR_STATIC:
+ status = i_ipadm_create_addr(iph, addr, flags);
+ break;
+ case IPADM_ADDR_DHCP:
+ status = i_ipadm_create_dhcp(iph, addr, flags);
+ break;
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ status = i_ipadm_create_ipv6addrs(iph, addr, flags);
+ break;
+ default:
+ status = IPADM_INVALID_ARG;
+ break;
+ }
+
+ /*
+ * If address was not created successfully, unplumb the interface
+ * if it was plumbed implicitly in this function and remove the
+ * addrobj created by the ipmgmtd daemon as a placeholder.
+ * If IPH_LEGACY is set, then remove the addrobj only if it was
+ * created in this function.
+ */
+fail:
+ if (status != IPADM_DHCP_IPC_TIMEOUT &&
+ status != IPADM_SUCCESS) {
+ if (!legacy) {
+ if (created_af || created_other_af) {
+ if (created_af) {
+ (void) i_ipadm_delete_if(iph, ifname,
+ af, flags);
+ }
+ if (created_other_af) {
+ (void) i_ipadm_delete_if(iph, ifname,
+ other_af, flags);
+ }
+ } else {
+ (void) i_ipadm_delete_addrobj(iph, addr, flags);
+ }
+ } else if (!aobjfound) {
+ (void) i_ipadm_delete_addrobj(iph, addr, flags);
+ }
+ }
+
+ return (status);
+}
+
+/*
+ * Creates the static address in `ipaddr' in kernel. After successfully
+ * creating it, it updates the ipmgmtd daemon's aobjmap with the logical
+ * interface information.
+ */
+static ipadm_status_t
+i_ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t ipaddr, uint32_t flags)
+{
+ struct lifreq lifr;
+ ipadm_status_t status = IPADM_SUCCESS;
+ int sock;
+ struct sockaddr_storage m, *mask = &m;
+ const struct sockaddr_storage *addr = &ipaddr->ipadm_static_addr;
+ const struct sockaddr_storage *daddr = &ipaddr->ipadm_static_dst_addr;
+ sa_family_t af;
+ boolean_t addif;
+ boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
+ struct ipadm_addrobj_s legacy_addr;
+ boolean_t default_prefixlen = B_FALSE;
+
+ af = ipaddr->ipadm_af;
+ sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+
+ /* If prefixlen was not provided, get default prefixlen */
+ if (ipaddr->ipadm_static_prefixlen == 0) {
+ /* prefixlen was not provided, get default prefixlen */
+ status = i_ipadm_get_default_prefixlen(
+ &ipaddr->ipadm_static_addr,
+ &ipaddr->ipadm_static_prefixlen);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ default_prefixlen = B_TRUE;
+ }
+ (void) plen2mask(ipaddr->ipadm_static_prefixlen, af, mask);
+
+ /*
+ * Check if the interface already has a non-zero address on 0th lif.
+ * It it does, we create a new logical interface and add the address
+ * on the new logical interface. If not, we replace the zero address
+ * on 0th logical interface with the given address.
+ */
+ status = i_ipadm_addr_exists_on_if(iph, ipaddr->ipadm_ifname, af,
+ &addif);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ /*
+ * This is a "hack" to get around the problem of SIOCLIFADDIF. The
+ * problem is that this ioctl does not include the netmask when adding
+ * a logical interface.
+ *
+ * To get around this problem, we first add the logical interface with
+ * a 0 address. After that, we set the netmask if provided. Finally
+ * we set the interface address.
+ */
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ipaddr->ipadm_ifname,
+ sizeof (lifr.lifr_name));
+ if (addif) {
+ if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ ipaddr->ipadm_lifnum = i_ipadm_get_lnum(lifr.lifr_name);
+ } else if (!legacy) {
+ /*
+ * If IPH_LEGACY is specified, the input logical interface
+ * number is already available in ipadm_lifnum.
+ */
+ ipaddr->ipadm_lifnum = 0;
+ }
+ i_ipadm_addrobj2lifname(ipaddr, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ lifr.lifr_addr = *mask;
+ if (ioctl(sock, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) {
+ status = ipadm_errno2status(errno);
+ goto ret;
+ }
+ lifr.lifr_addr = *addr;
+ if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0) {
+ status = ipadm_errno2status(errno);
+ goto ret;
+ }
+ /* Set the destination address, if one is given. */
+ if (daddr->ss_family != AF_UNSPEC) {
+ lifr.lifr_addr = *daddr;
+ if (ioctl(sock, SIOCSLIFDSTADDR, (caddr_t)&lifr) < 0) {
+ status = ipadm_errno2status(errno);
+ goto ret;
+ }
+ }
+
+ if (flags & IPADM_OPT_UP) {
+ status = i_ipadm_set_flags(iph, lifr.lifr_name, af, IFF_UP, 0);
+
+ /*
+ * IPADM_DAD_FOUND is a soft-error for create-addr.
+ * No need to tear down the address.
+ */
+ if (status == IPADM_DAD_FOUND)
+ status = IPADM_SUCCESS;
+ }
+
+ if (status == IPADM_SUCCESS) {
+ /*
+ * For IPH_LEGACY, we might be modifying the address on
+ * an address object that already exists e.g. by doing
+ * "ifconfig bge0:1 <addr>; ifconfig bge0:1 <newaddr>"
+ * So, we need to store the object only if it does not
+ * already exist in ipmgmtd.
+ */
+ if (legacy) {
+ bzero(&legacy_addr, sizeof (legacy_addr));
+ (void) strlcpy(legacy_addr.ipadm_aobjname,
+ ipaddr->ipadm_aobjname,
+ sizeof (legacy_addr.ipadm_aobjname));
+ status = i_ipadm_get_addrobj(iph, &legacy_addr);
+ if (status == IPADM_SUCCESS &&
+ legacy_addr.ipadm_lifnum >= 0) {
+ return (status);
+ }
+ }
+ status = i_ipadm_addr_persist(iph, ipaddr, default_prefixlen,
+ flags);
+ }
+ret:
+ if (status != IPADM_SUCCESS && !legacy)
+ (void) i_ipadm_delete_addr(iph, ipaddr);
+ return (status);
+}
+
+/*
+ * Removes the address object identified by `aobjname' from both active and
+ * persistent configuration. The address object will be removed from only
+ * active configuration if IPH_LEGACY is set in `iph->iph_flags'.
+ *
+ * If the address type is IPADM_ADDR_STATIC or IPADM_ADDR_DHCP, the address
+ * in the address object will be removed from the physical interface.
+ * If the address type is IPADM_ADDR_DHCP, the flag IPADM_OPT_RELEASE specifies
+ * whether the lease should be released. If IPADM_OPT_RELEASE is not
+ * specified, the lease will be dropped. This option is ignored
+ * for other address types.
+ *
+ * If the address type is IPADM_ADDR_IPV6_ADDRCONF, the link-local address and
+ * all the autoconfigured addresses will be removed.
+ * Finally, the address object is also removed from ipmgmtd's aobjmap and from
+ * the persistent DB.
+ */
+ipadm_status_t
+ipadm_delete_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags)
+{
+ ipadm_status_t status;
+ struct ipadm_addrobj_s ipaddr;
+ boolean_t release = ((flags & IPADM_OPT_RELEASE) != 0);
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* validate input */
+ if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
+ !(flags & IPADM_OPT_ACTIVE)) ||
+ (flags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_RELEASE))) {
+ return (IPADM_INVALID_ARG);
+ }
+ bzero(&ipaddr, sizeof (ipaddr));
+ if (aobjname == NULL || strlcpy(ipaddr.ipadm_aobjname, aobjname,
+ IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* Retrieve the address object information from ipmgmtd. */
+ status = i_ipadm_get_addrobj(iph, &ipaddr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ if (release && ipaddr.ipadm_atype != IPADM_ADDR_DHCP)
+ return (IPADM_NOTSUP);
+ /*
+ * If requested to delete just from active config but the address
+ * is not in active config, return error.
+ */
+ if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE) &&
+ (flags & IPADM_OPT_ACTIVE) && !(flags & IPADM_OPT_PERSIST)) {
+ return (IPADM_NOTFOUND);
+ }
+
+ /*
+ * If address is present in active config, remove it from
+ * kernel.
+ */
+ if (ipaddr.ipadm_flags & IPMGMT_ACTIVE) {
+ switch (ipaddr.ipadm_atype) {
+ case IPADM_ADDR_STATIC:
+ status = i_ipadm_delete_addr(iph, &ipaddr);
+ break;
+ case IPADM_ADDR_DHCP:
+ status = i_ipadm_delete_dhcp(iph, &ipaddr, release);
+ break;
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ status = i_ipadm_delete_ipv6addrs(iph, &ipaddr);
+ break;
+ default:
+ /*
+ * This is the case of address object name residing in
+ * daemon's aobjmap (added by ADDROBJ_LOOKUPADD). Fall
+ * through and delete that address object.
+ */
+ break;
+ }
+
+ /*
+ * If the address was previously deleted from the active
+ * config, we will get a IPADM_ENXIO from kernel.
+ * We will still proceed and purge the address information
+ * in the DB.
+ */
+ if (status == IPADM_ENXIO)
+ status = IPADM_SUCCESS;
+ else if (status != IPADM_SUCCESS)
+ return (status);
+ }
+
+ if (!(ipaddr.ipadm_flags & IPMGMT_PERSIST) &&
+ (flags & IPADM_OPT_PERSIST)) {
+ flags &= ~IPADM_OPT_PERSIST;
+ }
+ status = i_ipadm_delete_addrobj(iph, &ipaddr, flags);
+ if (status == IPADM_NOTFOUND)
+ return (status);
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Starts the dhcpagent and sends it the message DHCP_START to start
+ * configuring a dhcp address on the given interface in `addr'.
+ * After making the dhcpagent request, it also updates the
+ * address object information in ipmgmtd's aobjmap and creates an
+ * entry in persistent DB if IPADM_OPT_PERSIST is set in `flags'.
+ */
+static ipadm_status_t
+i_ipadm_create_dhcp(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags)
+{
+ struct lifreq lifr;
+ int sock = iph->iph_sock;
+ ipadm_status_t status;
+ ipadm_status_t dh_status;
+ boolean_t addif;
+
+ if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1)
+ return (IPADM_DHCP_START_ERROR);
+ /*
+ * Check if a new logical interface has to be created.
+ */
+ addr->ipadm_lifnum = 0;
+ status = i_ipadm_addr_exists_on_if(iph, addr->ipadm_ifname, AF_INET,
+ &addif);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (addif) {
+ /*
+ * If there is an address on 0th logical interface,
+ * add a new logical interface.
+ */
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, addr->ipadm_ifname,
+ sizeof (lifr.lifr_name));
+ if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ addr->ipadm_lifnum = i_ipadm_get_lnum(lifr.lifr_name);
+ }
+ /* Send DHCP_START to the dhcpagent. */
+ status = i_ipadm_op_dhcp(addr, DHCP_START, NULL);
+ /*
+ * We do not undo the create-addr operation for IPADM_DHCP_IPC_TIMEOUT
+ * since it is only a soft error to indicate the caller that the lease
+ * might be required after the function returns.
+ */
+ if (status != IPADM_SUCCESS && status != IPADM_DHCP_IPC_TIMEOUT)
+ goto fail;
+ dh_status = status;
+
+ /* Persist the address object information in ipmgmtd. */
+ status = i_ipadm_addr_persist(iph, addr, B_FALSE, flags);
+ if (status != IPADM_SUCCESS)
+ goto fail;
+
+ return (dh_status);
+fail:
+ /* In case of error, delete the dhcp address */
+ (void) i_ipadm_delete_dhcp(iph, addr, B_TRUE);
+ return (status);
+}
+
+/*
+ * Releases/drops the dhcp lease on the logical interface in the address
+ * object `addr'. If `release' is set to B_FALSE, the lease will be dropped.
+ */
+static ipadm_status_t
+i_ipadm_delete_dhcp(ipadm_handle_t iph, ipadm_addrobj_t addr, boolean_t release)
+{
+ ipadm_status_t status;
+ int dherr;
+
+ /* Send DHCP_RELEASE or DHCP_DROP to the dhcpagent */
+ if (release) {
+ status = i_ipadm_op_dhcp(addr, DHCP_RELEASE, &dherr);
+ /*
+ * If no lease was obtained on the object, we should
+ * drop the dhcp control on the interface.
+ */
+ if (status != IPADM_SUCCESS && dherr == DHCP_IPC_E_OUTSTATE)
+ status = i_ipadm_op_dhcp(addr, DHCP_DROP, NULL);
+ } else {
+ status = i_ipadm_op_dhcp(addr, DHCP_DROP, NULL);
+ }
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ /* Delete the logical interface */
+ if (addr->ipadm_lifnum != 0) {
+ struct lifreq lifr;
+
+ bzero(&lifr, sizeof (lifr));
+ i_ipadm_addrobj2lifname(addr, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ if (ioctl(iph->iph_sock, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Communicates with the dhcpagent to send a dhcp message of type `type'.
+ * It returns the dhcp error in `dhcperror' if a non-null pointer is provided
+ * in `dhcperror'.
+ */
+static ipadm_status_t
+i_ipadm_op_dhcp(ipadm_addrobj_t addr, dhcp_ipc_type_t type, int *dhcperror)
+{
+ dhcp_ipc_request_t *request;
+ dhcp_ipc_reply_t *reply = NULL;
+ char ifname[LIFNAMSIZ];
+ int error;
+ int dhcp_timeout;
+
+ /* Construct a message to the dhcpagent. */
+ bzero(&ifname, sizeof (ifname));
+ i_ipadm_addrobj2lifname(addr, ifname, sizeof (ifname));
+ if (addr->ipadm_primary)
+ type |= DHCP_PRIMARY;
+ request = dhcp_ipc_alloc_request(type, ifname, NULL, 0, DHCP_TYPE_NONE);
+ if (request == NULL)
+ return (IPADM_NO_MEMORY);
+
+ if (addr->ipadm_wait == IPADM_DHCP_WAIT_FOREVER)
+ dhcp_timeout = DHCP_IPC_WAIT_FOREVER;
+ else if (addr->ipadm_wait == IPADM_DHCP_WAIT_DEFAULT)
+ dhcp_timeout = DHCP_IPC_WAIT_DEFAULT;
+ else
+ dhcp_timeout = addr->ipadm_wait;
+ /* Send the message to dhcpagent. */
+ error = dhcp_ipc_make_request(request, &reply, dhcp_timeout);
+ free(request);
+ if (error == 0) {
+ error = reply->return_code;
+ free(reply);
+ }
+ if (error != 0) {
+ if (dhcperror != NULL)
+ *dhcperror = error;
+ if (error != DHCP_IPC_E_TIMEOUT)
+ return (IPADM_DHCP_IPC_ERROR);
+ else if (dhcp_timeout != 0)
+ return (IPADM_DHCP_IPC_TIMEOUT);
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Returns the IP addresses of the specified interface in both the
+ * active and the persistent configuration. If no
+ * interface is specified, it returns all non-zero IP addresses
+ * configured on all interfaces in active and persistent
+ * configurations.
+ * `addrinfo' will contain addresses that are
+ * (1) in both active and persistent configuration (created persistently)
+ * (2) only in active configuration (created temporarily)
+ * (3) only in persistent configuration (disabled addresses)
+ *
+ * Address list that is returned by this function must be freed
+ * using the ipadm_freeaddr_info() function.
+ */
+ipadm_status_t
+ipadm_addr_info(ipadm_handle_t iph, const char *ifname,
+ ipadm_addr_info_t **addrinfo, uint32_t flags, int64_t lifc_flags)
+{
+ ifspec_t ifsp;
+
+ if (addrinfo == NULL || iph == NULL)
+ return (IPADM_INVALID_ARG);
+ if (ifname != NULL &&
+ (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
+ return (IPADM_INVALID_ARG);
+ }
+ return (i_ipadm_get_all_addr_info(iph, ifname, addrinfo,
+ flags, lifc_flags));
+}
+
+/*
+ * Frees the structure allocated by ipadm_addr_info().
+ */
+void
+ipadm_free_addr_info(ipadm_addr_info_t *ainfo)
+{
+ freeifaddrs((struct ifaddrs *)ainfo);
+}
+
+/*
+ * Makes a door call to ipmgmtd to update its `aobjmap' with the address
+ * object in `ipaddr'. This door call also updates the persistent DB to
+ * remember address object to be recreated on next reboot or on an
+ * ipadm_enable_addr()/ipadm_enable_if() call.
+ */
+ipadm_status_t
+i_ipadm_addr_persist(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr,
+ boolean_t default_prefixlen, uint32_t flags)
+{
+ char *aname = ipaddr->ipadm_aobjname;
+ nvlist_t *nvl;
+ int err = 0;
+ ipadm_status_t status;
+ char pval[MAXPROPVALLEN];
+ uint_t pflags = 0;
+ ipadm_prop_desc_t *pdp = NULL;
+
+ /*
+ * Construct the nvl to send to the door.
+ */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ return (IPADM_NO_MEMORY);
+ if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME,
+ ipaddr->ipadm_ifname)) != 0 ||
+ (err = nvlist_add_string(nvl, IPADM_NVP_AOBJNAME, aname)) != 0 ||
+ (err = nvlist_add_int32(nvl, IPADM_NVP_LIFNUM,
+ ipaddr->ipadm_lifnum)) != 0) {
+ status = ipadm_errno2status(err);
+ goto ret;
+ }
+ switch (ipaddr->ipadm_atype) {
+ case IPADM_ADDR_STATIC:
+ status = i_ipadm_add_ipaddr2nvl(nvl, ipaddr);
+ if (status != IPADM_SUCCESS)
+ goto ret;
+ (void) snprintf(pval, sizeof (pval), "%d",
+ ipaddr->ipadm_static_prefixlen);
+ if (flags & IPADM_OPT_UP)
+ err = nvlist_add_string(nvl, "up", "yes");
+ else
+ err = nvlist_add_string(nvl, "up", "no");
+ status = ipadm_errno2status(err);
+ break;
+ case IPADM_ADDR_DHCP:
+ status = i_ipadm_add_dhcp2nvl(nvl, ipaddr->ipadm_primary,
+ ipaddr->ipadm_wait);
+ break;
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ status = i_ipadm_add_intfid2nvl(nvl, ipaddr);
+ break;
+ }
+ if (status != IPADM_SUCCESS)
+ goto ret;
+
+ if (iph->iph_flags & IPH_INIT) {
+ /*
+ * IPMGMT_INIT tells the ipmgmtd to set both IPMGMT_ACTIVE and
+ * IPMGMT_PERSIST on the address object in its `aobjmap'.
+ * For the callers ipadm_enable_if() and ipadm_enable_addr(),
+ * IPADM_OPT_PERSIST is not set in their flags. They send
+ * IPH_INIT in iph_flags, so that the address object will be
+ * set as both IPMGMT_ACTIVE and IPMGMT_PERSIST.
+ */
+ pflags |= IPMGMT_INIT;
+ } else {
+ if (flags & IPADM_OPT_ACTIVE)
+ pflags |= IPMGMT_ACTIVE;
+ if (flags & IPADM_OPT_PERSIST)
+ pflags |= IPMGMT_PERSIST;
+ }
+ status = i_ipadm_addr_persist_nvl(iph, nvl, pflags);
+ /*
+ * prefixlen is stored in a separate line in the DB and not along
+ * with the address itself, since it is also an address property and
+ * all address properties are stored in separate lines. We need to
+ * persist the prefixlen by calling the function that persists
+ * address properties.
+ */
+ if (status == IPADM_SUCCESS && !default_prefixlen &&
+ ipaddr->ipadm_atype == IPADM_ADDR_STATIC &&
+ (flags & IPADM_OPT_PERSIST)) {
+ for (pdp = ipadm_addrprop_table; pdp->ipd_name != NULL; pdp++) {
+ if (strcmp("prefixlen", pdp->ipd_name) == 0)
+ break;
+ }
+ assert(pdp != NULL);
+ status = i_ipadm_persist_propval(iph, pdp, pval, ipaddr, flags);
+ }
+ret:
+ nvlist_free(nvl);
+ return (status);
+}
+
+/*
+ * Makes the door call to ipmgmtd to store the address object in the
+ * nvlist `nvl'.
+ */
+static ipadm_status_t
+i_ipadm_addr_persist_nvl(ipadm_handle_t iph, nvlist_t *nvl, uint32_t flags)
+{
+ char *buf = NULL, *nvlbuf = NULL;
+ size_t nvlsize, bufsize;
+ ipmgmt_setaddr_arg_t *sargp;
+ int err;
+
+ err = nvlist_pack(nvl, &nvlbuf, &nvlsize, NV_ENCODE_NATIVE, 0);
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ bufsize = sizeof (*sargp) + nvlsize;
+ buf = calloc(1, bufsize);
+ sargp = (void *)buf;
+ sargp->ia_cmd = IPMGMT_CMD_SETADDR;
+ sargp->ia_flags = flags;
+ sargp->ia_nvlsize = nvlsize;
+ (void) bcopy(nvlbuf, buf + sizeof (*sargp), nvlsize);
+ err = ipadm_door_call(iph, buf, bufsize, NULL, 0, B_FALSE);
+ free(buf);
+ free(nvlbuf);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Makes a door call to ipmgmtd to remove the address object in `ipaddr'
+ * from its `aobjmap'. This door call also removes the address object and all
+ * its properties from the persistent DB if IPADM_OPT_PERSIST is set in
+ * `flags', so that the object will not be recreated on next reboot or on an
+ * ipadm_enable_addr()/ipadm_enable_if() call.
+ */
+ipadm_status_t
+i_ipadm_delete_addrobj(ipadm_handle_t iph, const ipadm_addrobj_t ipaddr,
+ uint32_t flags)
+{
+ ipmgmt_addr_arg_t arg;
+ int err;
+
+ arg.ia_cmd = IPMGMT_CMD_RESETADDR;
+ arg.ia_flags = 0;
+ if (flags & IPADM_OPT_ACTIVE)
+ arg.ia_flags |= IPMGMT_ACTIVE;
+ if (flags & IPADM_OPT_PERSIST)
+ arg.ia_flags |= IPMGMT_PERSIST;
+ (void) strlcpy(arg.ia_aobjname, ipaddr->ipadm_aobjname,
+ sizeof (arg.ia_aobjname));
+ arg.ia_lnum = ipaddr->ipadm_lifnum;
+ err = ipadm_door_call(iph, &arg, sizeof (arg), NULL, 0, B_FALSE);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Checks if the caller is authorized for the up/down operation.
+ * Retrieves the address object corresponding to `aobjname' from ipmgmtd
+ * and retrieves the address flags for that object from kernel.
+ * The arguments `ipaddr' and `ifflags' must be allocated by the caller.
+ */
+static ipadm_status_t
+i_ipadm_updown_common(ipadm_handle_t iph, const char *aobjname,
+ ipadm_addrobj_t ipaddr, uint32_t ipadm_flags, uint64_t *ifflags)
+{
+ ipadm_status_t status;
+ char lifname[LIFNAMSIZ];
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* validate input */
+ if (aobjname == NULL || strlcpy(ipaddr->ipadm_aobjname, aobjname,
+ IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* Retrieve the address object information. */
+ status = i_ipadm_get_addrobj(iph, ipaddr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ if (!(ipaddr->ipadm_flags & IPMGMT_ACTIVE))
+ return (IPADM_OP_DISABLE_OBJ);
+ if ((ipadm_flags & IPADM_OPT_PERSIST) &&
+ !(ipaddr->ipadm_flags & IPMGMT_PERSIST))
+ return (IPADM_TEMPORARY_OBJ);
+ if (ipaddr->ipadm_atype == IPADM_ADDR_IPV6_ADDRCONF ||
+ (ipaddr->ipadm_atype == IPADM_ADDR_DHCP &&
+ (ipadm_flags & IPADM_OPT_PERSIST)))
+ return (IPADM_NOTSUP);
+
+ i_ipadm_addrobj2lifname(ipaddr, lifname, sizeof (lifname));
+ return (i_ipadm_get_flags(iph, lifname, ipaddr->ipadm_af, ifflags));
+}
+
+/*
+ * Marks the address in the address object `aobjname' up. This operation is
+ * not supported for an address object of type IPADM_ADDR_IPV6_ADDRCONF.
+ * For an address object of type IPADM_ADDR_DHCP, this operation can
+ * only be temporary and no updates will be made to the persistent DB.
+ */
+ipadm_status_t
+ipadm_up_addr(ipadm_handle_t iph, const char *aobjname, uint32_t ipadm_flags)
+{
+ struct ipadm_addrobj_s ipaddr;
+ ipadm_status_t status;
+ uint64_t flags;
+ char lifname[LIFNAMSIZ];
+
+ status = i_ipadm_updown_common(iph, aobjname, &ipaddr, ipadm_flags,
+ &flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (flags & IFF_UP)
+ goto persist;
+ /*
+ * If the address is already a duplicate, then refresh-addr
+ * should be used to mark it up.
+ */
+ if (flags & IFF_DUPLICATE)
+ return (IPADM_DAD_FOUND);
+
+ i_ipadm_addrobj2lifname(&ipaddr, lifname, sizeof (lifname));
+ status = i_ipadm_set_flags(iph, lifname, ipaddr.ipadm_af, IFF_UP, 0);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+persist:
+ /* Update persistent DB. */
+ if (ipadm_flags & IPADM_OPT_PERSIST) {
+ status = i_ipadm_persist_propval(iph, &up_addrprop,
+ "yes", &ipaddr, 0);
+ }
+
+ return (status);
+}
+
+/*
+ * Marks the address in the address object `aobjname' down. This operation is
+ * not supported for an address object of type IPADM_ADDR_IPV6_ADDRCONF.
+ * For an address object of type IPADM_ADDR_DHCP, this operation can
+ * only be temporary and no updates will be made to the persistent DB.
+ */
+ipadm_status_t
+ipadm_down_addr(ipadm_handle_t iph, const char *aobjname, uint32_t ipadm_flags)
+{
+ struct ipadm_addrobj_s ipaddr;
+ ipadm_status_t status;
+ struct lifreq lifr;
+ uint64_t flags;
+
+ status = i_ipadm_updown_common(iph, aobjname, &ipaddr, ipadm_flags,
+ &flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ i_ipadm_addrobj2lifname(&ipaddr, lifr.lifr_name,
+ sizeof (lifr.lifr_name));
+ if (flags & IFF_UP) {
+ status = i_ipadm_set_flags(iph, lifr.lifr_name,
+ ipaddr.ipadm_af, 0, IFF_UP);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ } else if (flags & IFF_DUPLICATE) {
+ /*
+ * Clear the IFF_DUPLICATE flag.
+ */
+ if (ioctl(iph->iph_sock, SIOCGLIFADDR, &lifr) < 0)
+ return (ipadm_errno2status(errno));
+ if (ioctl(iph->iph_sock, SIOCSLIFADDR, &lifr) < 0)
+ return (ipadm_errno2status(errno));
+ }
+
+ /* Update persistent DB */
+ if (ipadm_flags & IPADM_OPT_PERSIST) {
+ status = i_ipadm_persist_propval(iph, &up_addrprop,
+ "no", &ipaddr, 0);
+ }
+
+ return (status);
+}
+
+/*
+ * Refreshes the address in the address object `aobjname'. If the address object
+ * is of type IPADM_ADDR_STATIC, DAD is re-initiated on the address. If
+ * `ipadm_flags' has IPADM_OPT_INFORM set, a DHCP_INFORM message is sent to the
+ * dhcpagent for this static address. If the address object is of type
+ * IPADM_ADDR_DHCP, a DHCP_EXTEND message is sent to the dhcpagent.
+ * If a dhcp address has not yet been acquired, a DHCP_START is sent to the
+ * dhcpagent. This operation is not supported for an address object of
+ * type IPADM_ADDR_IPV6_ADDRCONF.
+ */
+ipadm_status_t
+ipadm_refresh_addr(ipadm_handle_t iph, const char *aobjname,
+ uint32_t ipadm_flags)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ uint64_t flags;
+ struct ipadm_addrobj_s ipaddr;
+ sa_family_t af;
+ char lifname[LIFNAMSIZ];
+ boolean_t inform =
+ ((ipadm_flags & IPADM_OPT_INFORM) != 0);
+ int dherr;
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* validate input */
+ if (aobjname == NULL || strlcpy(ipaddr.ipadm_aobjname, aobjname,
+ IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* Retrieve the address object information. */
+ status = i_ipadm_get_addrobj(iph, &ipaddr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ if (!(ipaddr.ipadm_flags & IPMGMT_ACTIVE))
+ return (IPADM_OP_DISABLE_OBJ);
+
+ if (i_ipadm_is_vni(ipaddr.ipadm_ifname))
+ return (IPADM_NOTSUP);
+ if (inform && ipaddr.ipadm_atype != IPADM_ADDR_STATIC)
+ return (IPADM_INVALID_ARG);
+ af = ipaddr.ipadm_af;
+ if (ipaddr.ipadm_atype == IPADM_ADDR_STATIC) {
+ i_ipadm_addrobj2lifname(&ipaddr, lifname, sizeof (lifname));
+ status = i_ipadm_get_flags(iph, lifname, af, &flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (inform) {
+ ipaddr.ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
+ return (i_ipadm_op_dhcp(&ipaddr, DHCP_INFORM, NULL));
+ }
+ if (!(flags & IFF_DUPLICATE))
+ return (IPADM_SUCCESS);
+ status = i_ipadm_set_flags(iph, lifname, af, IFF_UP, 0);
+ } else if (ipaddr.ipadm_atype == IPADM_ADDR_DHCP) {
+ status = i_ipadm_op_dhcp(&ipaddr, DHCP_EXTEND, &dherr);
+ /*
+ * Restart the dhcp address negotiation with server if no
+ * address has been acquired yet.
+ */
+ if (status != IPADM_SUCCESS && dherr == DHCP_IPC_E_OUTSTATE) {
+ ipaddr.ipadm_wait = IPADM_DHCP_WAIT_DEFAULT;
+ status = i_ipadm_op_dhcp(&ipaddr, DHCP_START, NULL);
+ }
+ } else {
+ status = IPADM_NOTSUP;
+ }
+ return (status);
+}
+
+/*
+ * This is called from ipadm_create_addr() to validate the address parameters.
+ * It does the following steps:
+ * 1. Validates the interface name.
+ * 2. Verifies that the interface is not an IPMP meta-interface or an
+ * underlying interface.
+ * 3. In case of a persistent operation, verifies that the interface
+ * is persistent. Returns error if interface is not enabled but
+ * is in persistent config.
+ * 4. Verifies that the destination address is not set or the address type is
+ * not DHCP or ADDRCONF when the interface is a loopback interface.
+ * 5. Verifies that the address type is not DHCP or ADDRCONF when the interface
+ * has IFF_VRRP interface flag set.
+ */
+static ipadm_status_t
+i_ipadm_validate_create_addr(ipadm_handle_t iph, ipadm_addrobj_t ipaddr,
+ uint32_t flags)
+{
+ sa_family_t af;
+ sa_family_t other_af;
+ char *ifname;
+ ipadm_status_t status;
+ boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
+ boolean_t islo, isvni;
+ uint64_t ifflags = 0;
+ boolean_t p_exists;
+ boolean_t af_exists, other_af_exists, a_exists;
+
+ if (ipaddr == NULL || flags == 0 || flags == IPADM_OPT_PERSIST ||
+ (flags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_UP))) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ if (ipaddr->ipadm_af == AF_UNSPEC)
+ return (IPADM_BAD_ADDR);
+
+ if (!legacy && ipaddr->ipadm_lifnum != 0)
+ return (IPADM_INVALID_ARG);
+
+ if (legacy && ipaddr->ipadm_atype != IPADM_ADDR_STATIC)
+ return (IPADM_NOTSUP);
+
+ ifname = ipaddr->ipadm_ifname;
+
+ if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
+ return (IPADM_NOTSUP);
+
+ af = ipaddr->ipadm_af;
+ af_exists = ipadm_if_enabled(iph, ifname, af);
+ /*
+ * For legacy case, interfaces are not implicitly plumbed. We need to
+ * check if the interface exists in the active configuration.
+ */
+ if (legacy && !af_exists)
+ return (IPADM_ENXIO);
+
+ other_af = (af == AF_INET ? AF_INET6 : AF_INET);
+ other_af_exists = ipadm_if_enabled(iph, ifname, other_af);
+ /*
+ * Check if one of the v4 or the v6 interfaces exists in the
+ * active configuration. An interface is considered disabled only
+ * if both v4 and v6 are not active.
+ */
+ a_exists = (af_exists || other_af_exists);
+
+ /* Check if interface exists in the persistent configuration. */
+ status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (!a_exists && p_exists)
+ return (IPADM_OP_DISABLE_OBJ);
+ if ((flags & IPADM_OPT_PERSIST) && a_exists && !p_exists) {
+ /*
+ * If address has to be created persistently,
+ * and the interface does not exist in the persistent
+ * store but in active config, fail.
+ */
+ return (IPADM_TEMPORARY_OBJ);
+ }
+ if (af_exists) {
+ status = i_ipadm_get_flags(iph, ifname, af, &ifflags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ }
+
+ /* Perform validation steps (4) and (5) */
+ islo = i_ipadm_is_loopback(ifname);
+ isvni = i_ipadm_is_vni(ifname);
+ switch (ipaddr->ipadm_atype) {
+ case IPADM_ADDR_STATIC:
+ if ((islo || isvni) && ipaddr->ipadm_static_dname[0] != '\0')
+ return (IPADM_INVALID_ARG);
+ /* Check for a valid src address */
+ if (!legacy && sockaddrunspec(&ipaddr->ipadm_static_addr))
+ return (IPADM_BAD_ADDR);
+ break;
+ case IPADM_ADDR_DHCP:
+ if (islo || (ifflags & IFF_VRRP))
+ return (IPADM_NOTSUP);
+ break;
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ if (islo || (ifflags & IFF_VRRP) ||
+ i_ipadm_is_6to4(iph, ifname)) {
+ return (IPADM_NOTSUP);
+ }
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+ipadm_status_t
+i_ipadm_merge_prefixlen_from_nvl(nvlist_t *invl, nvlist_t *onvl,
+ const char *aobjname)
+{
+ nvpair_t *nvp, *prefixnvp;
+ nvlist_t *tnvl;
+ char *aname;
+ int err;
+
+ for (nvp = nvlist_next_nvpair(invl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(invl, nvp)) {
+ if (nvpair_value_nvlist(nvp, &tnvl) == 0 &&
+ nvlist_exists(tnvl, IPADM_NVP_PREFIXLEN) &&
+ nvlist_lookup_string(tnvl, IPADM_NVP_AOBJNAME,
+ &aname) == 0 && strcmp(aname, aobjname) == 0) {
+ /* prefixlen exists for given address object */
+ (void) nvlist_lookup_nvpair(tnvl, IPADM_NVP_PREFIXLEN,
+ &prefixnvp);
+ err = nvlist_add_nvpair(onvl, prefixnvp);
+ if (err == 0) {
+ err = nvlist_remove(invl, nvpair_name(nvp),
+ nvpair_type(nvp));
+ }
+ return (ipadm_errno2status(err));
+ }
+ }
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Re-enables the address object `aobjname' based on the saved
+ * configuration for `aobjname'.
+ */
+ipadm_status_t
+ipadm_enable_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags)
+{
+ nvlist_t *addrnvl, *nvl;
+ nvpair_t *nvp;
+ ipadm_status_t status;
+ struct ipadm_addrobj_s ipaddr;
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* validate input */
+ if (flags & IPADM_OPT_PERSIST)
+ return (IPADM_NOTSUP);
+ if (aobjname == NULL || strlcpy(ipaddr.ipadm_aobjname, aobjname,
+ IPADM_AOBJSIZ) >= IPADM_AOBJSIZ) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* Retrieve the address object information. */
+ status = i_ipadm_get_addrobj(iph, &ipaddr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (ipaddr.ipadm_flags & IPMGMT_ACTIVE)
+ return (IPADM_ADDROBJ_EXISTS);
+
+ status = i_ipadm_get_db_addr(iph, NULL, aobjname, &addrnvl);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ assert(addrnvl != NULL);
+
+ for (nvp = nvlist_next_nvpair(addrnvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(addrnvl, nvp)) {
+ if (nvpair_value_nvlist(nvp, &nvl) != 0)
+ continue;
+
+ if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR) ||
+ nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
+ status = i_ipadm_merge_prefixlen_from_nvl(addrnvl, nvl,
+ aobjname);
+ if (status != IPADM_SUCCESS)
+ continue;
+ }
+ iph->iph_flags |= IPH_INIT;
+ status = i_ipadm_init_addrobj(iph, nvl);
+ iph->iph_flags &= ~IPH_INIT;
+ if (status != IPADM_SUCCESS)
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * Disables the address object in `aobjname' from the active configuration.
+ * Error code return values follow the model in ipadm_delete_addr().
+ */
+ipadm_status_t
+ipadm_disable_addr(ipadm_handle_t iph, const char *aobjname, uint32_t flags)
+{
+ /* validate input */
+ if (flags & IPADM_OPT_PERSIST)
+ return (IPADM_NOTSUP);
+
+ return (ipadm_delete_addr(iph, aobjname, IPADM_OPT_ACTIVE));
+}
diff --git a/usr/src/lib/libipadm/common/ipadm_if.c b/usr/src/lib/libipadm/common/ipadm_if.c
new file mode 100644
index 0000000000..d4a24d49a4
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_if.c
@@ -0,0 +1,1534 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <errno.h>
+#include <sys/sockio.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <strings.h>
+#include <libdlpi.h>
+#include <libdllink.h>
+#include <libinetutil.h>
+#include <inet/ip.h>
+#include <limits.h>
+#include <zone.h>
+#include <ipadm_ndpd.h>
+#include "libipadm_impl.h"
+
+static ipadm_status_t i_ipadm_slifname_arp(char *, uint64_t, int);
+static ipadm_status_t i_ipadm_slifname(ipadm_handle_t, char *, char *,
+ uint64_t, int, uint32_t);
+static ipadm_status_t i_ipadm_create_ipmp_peer(ipadm_handle_t, char *,
+ sa_family_t);
+static ipadm_status_t i_ipadm_persist_if(ipadm_handle_t, const char *,
+ sa_family_t);
+
+/*
+ * Returns B_FALSE if the interface in `ifname' has at least one address that is
+ * IFF_UP in the addresses in `ifa'.
+ */
+static boolean_t
+i_ipadm_is_if_down(char *ifname, struct ifaddrs *ifa)
+{
+ struct ifaddrs *ifap;
+ char cifname[LIFNAMSIZ];
+ char *sep;
+
+ for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
+ (void) strlcpy(cifname, ifap->ifa_name, sizeof (cifname));
+ if ((sep = strrchr(cifname, IPADM_LOGICAL_SEP)) != NULL)
+ *sep = '\0';
+ /*
+ * If this condition is true, there is at least one
+ * address that is IFF_UP. So, we need to return B_FALSE.
+ */
+ if (strcmp(cifname, ifname) == 0 &&
+ (ifap->ifa_flags & IFF_UP)) {
+ return (B_FALSE);
+ }
+ }
+ /* We did not find any IFF_UP addresses. */
+ return (B_TRUE);
+}
+
+/*
+ * Retrieves the information for the interface `ifname' from active
+ * config if `ifname' is specified and returns the result in the list `if_info'.
+ * Otherwise, it retrieves the information for all the interfaces in
+ * the active config and returns the result in the list `if_info'.
+ */
+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)
+{
+ struct lifreq *buf;
+ struct lifreq *lifrp;
+ struct lifreq lifrl;
+ ipadm_if_info_t *last = NULL;
+ ipadm_if_info_t *ifp;
+ int s;
+ int n;
+ int numifs;
+ ipadm_status_t status;
+
+ *if_info = NULL;
+ /*
+ * Get information for all interfaces.
+ */
+ if (getallifs(iph->iph_sock, 0, &buf, &numifs, lifc_flags) != 0)
+ return (ipadm_errno2status(errno));
+
+ lifrp = buf;
+ for (n = 0; n < numifs; n++, lifrp++) {
+ /* Skip interfaces with logical num != 0 */
+ if (i_ipadm_get_lnum(lifrp->lifr_name) != 0)
+ continue;
+ /*
+ * Skip the current interface if a specific `ifname' has
+ * been requested and current interface does not match
+ * `ifname'.
+ */
+ if (ifname != NULL && strcmp(lifrp->lifr_name, ifname) != 0)
+ continue;
+ /*
+ * Check if the interface already exists in our list.
+ * If it already exists, we need to update its flags.
+ */
+ for (ifp = *if_info; ifp != NULL; ifp = ifp->ifi_next) {
+ if (strcmp(lifrp->lifr_name, ifp->ifi_name) == 0)
+ break;
+ }
+ if (ifp == NULL) {
+ ifp = calloc(1, sizeof (ipadm_if_info_t));
+ if (ifp == NULL) {
+ status = ipadm_errno2status(errno);
+ goto fail;
+ }
+ (void) strlcpy(ifp->ifi_name, lifrp->lifr_name,
+ sizeof (ifp->ifi_name));
+ /* Update the `ifi_next' pointer for this new node */
+ if (*if_info == NULL)
+ *if_info = ifp;
+ else
+ last->ifi_next = ifp;
+ last = ifp;
+ }
+
+ /*
+ * Retrieve the flags for the interface by doing a
+ * SIOCGLIFFLAGS to populate the `ifi_cflags' field.
+ */
+ (void) strlcpy(lifrl.lifr_name,
+ lifrp->lifr_name, sizeof (lifrl.lifr_name));
+ s = (lifrp->lifr_addr.ss_family == AF_INET) ?
+ iph->iph_sock : iph->iph_sock6;
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
+ continue;
+ if (lifrl.lifr_flags & IFF_BROADCAST)
+ ifp->ifi_cflags |= IFIF_BROADCAST;
+ if (lifrl.lifr_flags & IFF_MULTICAST)
+ ifp->ifi_cflags |= IFIF_MULTICAST;
+ if (lifrl.lifr_flags & IFF_POINTOPOINT)
+ ifp->ifi_cflags |= IFIF_POINTOPOINT;
+ if (lifrl.lifr_flags & IFF_VIRTUAL)
+ ifp->ifi_cflags |= IFIF_VIRTUAL;
+ if (lifrl.lifr_flags & IFF_IPMP)
+ ifp->ifi_cflags |= IFIF_IPMP;
+ if (lifrl.lifr_flags & IFF_STANDBY)
+ ifp->ifi_cflags |= IFIF_STANDBY;
+ if (lifrl.lifr_flags & IFF_INACTIVE)
+ ifp->ifi_cflags |= IFIF_INACTIVE;
+ if (lifrl.lifr_flags & IFF_VRRP)
+ ifp->ifi_cflags |= IFIF_VRRP;
+ if (lifrl.lifr_flags & IFF_NOACCEPT)
+ ifp->ifi_cflags |= IFIF_NOACCEPT;
+ if (lifrl.lifr_flags & IFF_IPV4)
+ ifp->ifi_cflags |= IFIF_IPV4;
+ if (lifrl.lifr_flags & IFF_IPV6)
+ ifp->ifi_cflags |= IFIF_IPV6;
+ }
+ free(buf);
+ return (IPADM_SUCCESS);
+fail:
+ free(buf);
+ ipadm_free_if_info(*if_info);
+ *if_info = NULL;
+ return (status);
+}
+
+/*
+ * Returns the interface information for `ifname' in `if_info' from persistent
+ * config if `ifname' is non-null. Otherwise, it returns all the interfaces
+ * from persistent config in `if_info'.
+ */
+static ipadm_status_t
+i_ipadm_persist_if_info(ipadm_handle_t iph, const char *ifname,
+ ipadm_if_info_t **if_info)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ ipmgmt_getif_arg_t getif;
+ ipmgmt_getif_rval_t *rvalp;
+ ipadm_if_info_t *ifp, *curr, *prev = NULL;
+ int i = 0, err = 0;
+
+ bzero(&getif, sizeof (getif));
+ if (ifname != NULL)
+ (void) strlcpy(getif.ia_ifname, ifname, LIFNAMSIZ);
+ getif.ia_cmd = IPMGMT_CMD_GETIF;
+
+ *if_info = NULL;
+
+ if ((rvalp = malloc(sizeof (ipmgmt_getif_rval_t))) == NULL)
+ return (ipadm_errno2status(errno));
+ err = ipadm_door_call(iph, &getif, sizeof (getif), (void **)&rvalp,
+ sizeof (*rvalp), B_TRUE);
+ if (err == ENOENT) {
+ free(rvalp);
+ if (ifname != NULL)
+ return (ipadm_errno2status(err));
+ return (IPADM_SUCCESS);
+ } else if (err != 0) {
+ free(rvalp);
+ return (ipadm_errno2status(err));
+ }
+
+ ifp = rvalp->ir_ifinfo;
+ for (i = 0; i < rvalp->ir_ifcnt; i++) {
+ ifp = rvalp->ir_ifinfo + i;
+ if ((curr = malloc(sizeof (*curr))) == NULL) {
+ status = ipadm_errno2status(errno);
+ ipadm_free_if_info(prev);
+ break;
+ }
+ (void) bcopy(ifp, curr, sizeof (*curr));
+ curr->ifi_next = prev;
+ prev = curr;
+ }
+ *if_info = curr;
+ free(rvalp);
+ return (status);
+}
+
+/*
+ * Collects information for `ifname' if one is specified from both
+ * active and persistent config in `if_info'. If no `ifname' is specified,
+ * this returns all the interfaces in active and persistent config in
+ * `if_info'.
+ */
+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)
+{
+ ipadm_status_t status;
+ ipadm_if_info_t *aifinfo = NULL;
+ ipadm_if_info_t *pifinfo = NULL;
+ ipadm_if_info_t *aifp;
+ ipadm_if_info_t *pifp;
+ ipadm_if_info_t *last = NULL;
+ struct ifaddrs *ifa;
+ struct ifaddrs *ifap;
+
+ /*
+ * Retrive the information for the requested `ifname' or all
+ * interfaces from active configuration.
+ */
+retry:
+ status = i_ipadm_active_if_info(iph, ifname, &aifinfo, lifc_flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ /* Get the interface state for each interface in `aifinfo'. */
+ if (aifinfo != NULL) {
+ /* We need all addresses to get the interface state */
+ if (getallifaddrs(AF_UNSPEC, &ifa, (LIFC_NOXMIT|LIFC_TEMPORARY|
+ LIFC_ALLZONES|LIFC_UNDER_IPMP)) != 0) {
+ status = ipadm_errno2status(errno);
+ goto fail;
+ }
+ for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
+ /*
+ * Find the `ifaddrs' structure from `ifa'
+ * for this interface. We need the IFF_* flags
+ * to find the interface state.
+ */
+ for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) {
+ if (strcmp(ifap->ifa_name, aifp->ifi_name) == 0)
+ break;
+ }
+ if (ifap == NULL) {
+ /*
+ * The interface might have been removed
+ * from kernel. Retry getting all the active
+ * interfaces.
+ */
+ freeifaddrs(ifa);
+ ipadm_free_if_info(aifinfo);
+ aifinfo = NULL;
+ goto retry;
+ }
+ if (!(ifap->ifa_flags & IFF_RUNNING) ||
+ (ifap->ifa_flags & IFF_FAILED))
+ aifp->ifi_state = IFIS_FAILED;
+ else if (ifap->ifa_flags & IFF_OFFLINE)
+ aifp->ifi_state = IFIS_OFFLINE;
+ else if (i_ipadm_is_if_down(aifp->ifi_name, ifa))
+ aifp->ifi_state = IFIS_DOWN;
+ else
+ aifp->ifi_state = IFIS_OK;
+ if (aifp->ifi_next == NULL)
+ last = aifp;
+ }
+ freeifaddrs(ifa);
+ }
+ /*
+ * Get the persistent interface information in `pifinfo'.
+ */
+ status = i_ipadm_persist_if_info(iph, ifname, &pifinfo);
+ if (status == IPADM_NOTFOUND) {
+ *if_info = aifinfo;
+ return (IPADM_SUCCESS);
+ }
+ if (status != IPADM_SUCCESS)
+ goto fail;
+ /*
+ * If a persistent interface is also found in `aifinfo', update
+ * its entry in `aifinfo' with the persistent information from
+ * `pifinfo'. If an interface is found in `pifinfo', but not in
+ * `aifinfo', it means that this interface was disabled. We should
+ * add this interface to `aifinfo' and set it state to IFIF_DISABLED.
+ */
+ for (pifp = pifinfo; pifp != NULL; pifp = pifp->ifi_next) {
+ for (aifp = aifinfo; aifp != NULL; aifp = aifp->ifi_next) {
+ if (strcmp(aifp->ifi_name, pifp->ifi_name) == 0) {
+ aifp->ifi_pflags = pifp->ifi_pflags;
+ break;
+ }
+ }
+ if (aifp == NULL) {
+ aifp = malloc(sizeof (ipadm_if_info_t));
+ if (aifp == NULL) {
+ status = ipadm_errno2status(errno);
+ goto fail;
+ }
+ *aifp = *pifp;
+ aifp->ifi_next = NULL;
+ aifp->ifi_state = IFIS_DISABLED;
+ if (last != NULL)
+ last->ifi_next = aifp;
+ else
+ aifinfo = aifp;
+ last = aifp;
+ }
+ }
+ *if_info = aifinfo;
+ ipadm_free_if_info(pifinfo);
+ return (IPADM_SUCCESS);
+fail:
+ *if_info = NULL;
+ ipadm_free_if_info(aifinfo);
+ ipadm_free_if_info(pifinfo);
+ return (status);
+}
+
+int
+i_ipadm_get_lnum(const char *ifname)
+{
+ char *num = strrchr(ifname, IPADM_LOGICAL_SEP);
+
+ if (num == NULL)
+ return (0);
+
+ return (atoi(++num));
+}
+
+/*
+ * Sets the output argument `exists' to true or false based on whether
+ * any persistent configuration is available for `ifname' and returns
+ * IPADM_SUCCESS as status. If the persistent information cannot be retrieved,
+ * `exists' is unmodified and an error status is returned.
+ */
+ipadm_status_t
+i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ boolean_t *exists)
+{
+ ipadm_if_info_t *ifinfo;
+ ipadm_status_t status;
+
+ status = i_ipadm_persist_if_info(iph, ifname, &ifinfo);
+ if (status == IPADM_SUCCESS) {
+ *exists = ((af == AF_INET &&
+ (ifinfo->ifi_pflags & IFIF_IPV4)) ||
+ (af == AF_INET6 &&
+ (ifinfo->ifi_pflags & IFIF_IPV6)));
+ free(ifinfo);
+ } else if (status == IPADM_NOTFOUND) {
+ status = IPADM_SUCCESS;
+ *exists = B_FALSE;
+ }
+ return (status);
+}
+
+/*
+ * Open "/dev/udp{,6}" for use as a multiplexor to PLINK the interface stream
+ * under. We use "/dev/udp" instead of "/dev/ip" since STREAMS will not let
+ * you PLINK a driver under itself, and "/dev/ip" is typically the driver at
+ * the bottom of the stream for tunneling interfaces.
+ */
+ipadm_status_t
+ipadm_open_arp_on_udp(const char *udp_dev_name, int *fd)
+{
+ int err;
+
+ if ((*fd = open(udp_dev_name, O_RDWR)) == -1)
+ return (ipadm_errno2status(errno));
+
+ /*
+ * Pop off all undesired modules (note that the user may have
+ * configured autopush to add modules above udp), and push the
+ * arp module onto the resulting stream. This is used to make
+ * IP+ARP be able to atomically track the muxid for the I_PLINKed
+ * STREAMS, thus it isn't related to ARP running the ARP protocol.
+ */
+ while (ioctl(*fd, I_POP, 0) != -1)
+ ;
+ if (errno == EINVAL && ioctl(*fd, I_PUSH, ARP_MOD_NAME) != -1)
+ return (IPADM_SUCCESS);
+ err = errno;
+ (void) close(*fd);
+
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * i_ipadm_create_ipmp() is called from i_ipadm_create_ipmp_peer() when an
+ * underlying interface in an ipmp group G is plumbed for an address family,
+ * but the meta-interface for the other address family `af' does not exist
+ * yet for the group G. If `af' is IPv6, we need to bring up the
+ * link-local address.
+ */
+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)
+{
+ ipadm_status_t status;
+ struct lifreq lifr;
+ int sock;
+ int err;
+
+ assert(ipadm_flags & IPADM_OPT_IPMP);
+
+ /* Create the ipmp underlying interface */
+ status = i_ipadm_create_if(iph, ifname, af, ipadm_flags);
+ if (status != IPADM_SUCCESS && status != IPADM_IF_EXISTS)
+ return (status);
+
+ /*
+ * To preserve backward-compatibility, always bring up the link-local
+ * address for implicitly-created IPv6 IPMP interfaces.
+ */
+ if (af == AF_INET6)
+ (void) i_ipadm_set_flags(iph, ifname, AF_INET6, IFF_UP, 0);
+
+ sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+ /*
+ * If the caller requested a different group name, issue a
+ * SIOCSLIFGROUPNAME on the new IPMP interface.
+ */
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (strcmp(lifr.lifr_name, grname) != 0) {
+ (void) strlcpy(lifr.lifr_groupname, grname, LIFGRNAMSIZ);
+ if (ioctl(sock, SIOCSLIFGROUPNAME, &lifr) == -1) {
+ err = errno;
+ /* Remove the interface we created. */
+ if (status == IPADM_SUCCESS) {
+ (void) i_ipadm_delete_if(iph, ifname, af,
+ ipadm_flags);
+ }
+ return (ipadm_errno2status(err));
+ }
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Checks if `ifname' is plumbed and in an IPMP group on its "other" address
+ * family. If so, create a matching IPMP group for address family `af'.
+ */
+static ipadm_status_t
+i_ipadm_create_ipmp_peer(ipadm_handle_t iph, char *ifname, sa_family_t af)
+{
+ lifgroupinfo_t lifgr;
+ ipadm_status_t status = IPADM_SUCCESS;
+ struct lifreq lifr;
+ int other_af_sock;
+
+ assert(af == AF_INET || af == AF_INET6);
+
+ other_af_sock = (af == AF_INET ? iph->iph_sock6 : iph->iph_sock);
+
+ /*
+ * iph is the handle for the interface that we are trying to plumb.
+ * other_af_sock is the socket for the "other" address family.
+ */
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(other_af_sock, SIOCGLIFGROUPNAME, &lifr) != 0)
+ return (IPADM_SUCCESS);
+
+ (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ);
+ if (ioctl(other_af_sock, SIOCGLIFGROUPINFO, &lifgr) != 0)
+ return (IPADM_SUCCESS);
+
+ /*
+ * If `ifname' *is* the IPMP group interface, or if the relevant
+ * address family is already configured, then there's nothing to do.
+ */
+ if (strcmp(lifgr.gi_grifname, ifname) == 0 ||
+ (af == AF_INET && lifgr.gi_v4) || (af == AF_INET6 && lifgr.gi_v6)) {
+ return (IPADM_SUCCESS);
+ }
+
+ status = i_ipadm_create_ipmp(iph, lifgr.gi_grifname, af,
+ lifgr.gi_grname, IPADM_OPT_ACTIVE|IPADM_OPT_IPMP);
+ return (status);
+}
+
+/*
+ * Issues the ioctl SIOCSLIFNAME to kernel on the given ARP stream fd.
+ */
+static ipadm_status_t
+i_ipadm_slifname_arp(char *ifname, uint64_t flags, int fd)
+{
+ struct lifreq lifr;
+ ifspec_t ifsp;
+
+ bzero(&lifr, sizeof (lifr));
+ (void) ifparse_ifspec(ifname, &ifsp);
+ lifr.lifr_ppa = ifsp.ifsp_ppa;
+ lifr.lifr_flags = flags;
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ /*
+ * Tell ARP the name and unit number for this interface.
+ * Note that arp has no support for transparent ioctls.
+ */
+ if (i_ipadm_strioctl(fd, SIOCSLIFNAME, (char *)&lifr,
+ sizeof (lifr)) == -1) {
+ return (ipadm_errno2status(errno));
+ }
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Issues the ioctl SIOCSLIFNAME to kernel. If IPADM_OPT_GENPPA is set in
+ * `ipadm_flags', then a ppa will be generated. `newif' will be updated
+ * with the generated ppa.
+ */
+static ipadm_status_t
+i_ipadm_slifname(ipadm_handle_t iph, char *ifname, char *newif, uint64_t flags,
+ int fd, uint32_t ipadm_flags)
+{
+ struct lifreq lifr;
+ ipadm_status_t status = IPADM_SUCCESS;
+ int err = 0;
+ sa_family_t af;
+ int ppa;
+ ifspec_t ifsp;
+ boolean_t valid_if;
+
+ bzero(&lifr, sizeof (lifr));
+ if (ipadm_flags & IPADM_OPT_GENPPA) {
+ /*
+ * We'd like to just set lifr_ppa to UINT_MAX and have the
+ * kernel pick a PPA. Unfortunately, that would mishandle
+ * two cases:
+ *
+ * 1. If the PPA is available but the groupname is taken
+ * (e.g., the "ipmp2" IP interface name is available
+ * but the "ipmp2" groupname is taken) then the
+ * auto-assignment by the kernel will fail.
+ *
+ * 2. If we're creating (e.g.) an IPv6-only IPMP
+ * interface, and there's already an IPv4-only IPMP
+ * interface, the kernel will allow us to accidentally
+ * reuse the IPv6 IPMP interface name (since
+ * SIOCSLIFNAME uniqueness is per-interface-type).
+ * This will cause administrative confusion.
+ *
+ * Thus, we instead take a brute-force approach of checking
+ * whether the IPv4 or IPv6 name is already in-use before
+ * attempting the SIOCSLIFNAME. As per (1) above, the
+ * SIOCSLIFNAME may still fail, in which case we just proceed
+ * to the next one. If this approach becomes too slow, we
+ * can add a new SIOC* to handle this case in the kernel.
+ */
+ for (ppa = 0; ppa < UINT_MAX; ppa++) {
+ (void) snprintf(lifr.lifr_name, LIFNAMSIZ, "%s%d",
+ ifname, ppa);
+
+ if (ioctl(iph->iph_sock, SIOCGLIFFLAGS, &lifr) != -1 ||
+ errno != ENXIO)
+ continue;
+
+ if (ioctl(iph->iph_sock6, SIOCGLIFFLAGS, &lifr) != -1 ||
+ errno != ENXIO)
+ continue;
+
+ lifr.lifr_ppa = ppa;
+ lifr.lifr_flags = flags;
+
+ err = ioctl(fd, SIOCSLIFNAME, &lifr);
+ if (err != -1 || errno != EEXIST)
+ break;
+ }
+ if (err == -1) {
+ status = ipadm_errno2status(errno);
+ } else {
+ /*
+ * PPA has been successfully established.
+ * Update `newif' with the ppa.
+ */
+ assert(newif != NULL);
+ if (snprintf(newif, LIFNAMSIZ, "%s%d", ifname,
+ ppa) >= LIFNAMSIZ)
+ return (IPADM_INVALID_ARG);
+ }
+ } else {
+ /* We should have already validated the interface name. */
+ valid_if = ifparse_ifspec(ifname, &ifsp);
+ assert(valid_if);
+
+ /*
+ * Before we call SIOCSLIFNAME, ensure that the IPMP group
+ * interface for this address family exists. Otherwise, the
+ * kernel will kick the interface out of the group when we do
+ * the SIOCSLIFNAME.
+ *
+ * Example: suppose bge0 is plumbed for IPv4 and in group "a".
+ * If we're now plumbing bge0 for IPv6, but the IPMP group
+ * interface for "a" is not plumbed for IPv6, the SIOCSLIFNAME
+ * will kick bge0 out of group "a", which is undesired.
+ */
+ if (flags & IFF_IPV4)
+ af = AF_INET;
+ else
+ af = AF_INET6;
+ status = i_ipadm_create_ipmp_peer(iph, ifname, af);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ lifr.lifr_ppa = ifsp.ifsp_ppa;
+ lifr.lifr_flags = flags;
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)
+ status = ipadm_errno2status(errno);
+ }
+
+ return (status);
+}
+
+/*
+ * Plumbs the interface `ifname' for the address family `af'. It also persists
+ * the interface for `af' if IPADM_OPT_PERSIST is set in `ipadm_flags'.
+ */
+ipadm_status_t
+i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
+ uint32_t ipadm_flags)
+{
+ int ip_muxid;
+ int mux_fd = -1, ip_fd, arp_fd;
+ char *udp_dev_name;
+ dlpi_handle_t dh_arp = NULL, dh_ip;
+ uint64_t ifflags;
+ struct lifreq lifr;
+ uint_t dlpi_flags;
+ ipadm_status_t status = IPADM_SUCCESS;
+ char *linkname;
+ boolean_t legacy = (iph->iph_flags & IPH_LEGACY);
+ zoneid_t zoneid;
+ char newif[LIFNAMSIZ];
+ char lifname[LIFNAMSIZ];
+ datalink_id_t linkid;
+ int sock;
+ boolean_t islo;
+ boolean_t is_persistent =
+ ((ipadm_flags & IPADM_OPT_PERSIST) != 0);
+ uint32_t dlflags;
+ dladm_status_t dlstatus;
+
+ if (iph->iph_dlh != NULL) {
+ dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid,
+ &dlflags, NULL, NULL);
+ }
+ /*
+ * If we're in the global zone and we're plumbing a datalink, make
+ * sure that the datalink is not assigned to a non-global zone. Note
+ * that the non-global zones don't need this check, because zoneadm
+ * has taken care of this when the zones boot.
+ */
+ if (getzoneid() == GLOBAL_ZONEID && dlstatus == DLADM_STATUS_OK) {
+ zoneid = ALL_ZONES;
+ if (zone_check_datalink(&zoneid, linkid) == 0) {
+ /* interface is in use by a non-global zone. */
+ return (IPADM_IF_INUSE);
+ }
+ }
+
+ /* loopback interfaces are just added as logical interface */
+ bzero(&lifr, sizeof (lifr));
+ islo = i_ipadm_is_loopback(ifname);
+ if (islo || i_ipadm_get_lnum(ifname) != 0) {
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (af == AF_INET)
+ sock = iph->iph_sock;
+ else
+ sock = iph->iph_sock6;
+ if (islo && ioctl(sock, SIOCGLIFADDR, (caddr_t)&lifr) >= 0)
+ return (IPADM_IF_EXISTS);
+ if (ioctl(sock, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ /*
+ * By default, kernel configures 127.0.0.1 on the loopback
+ * interface. Replace this with 0.0.0.0 to be consistent
+ * with interface creation on other physical interfaces.
+ */
+ if (islo && !legacy) {
+ bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
+ lifr.lifr_addr.ss_family = af;
+ if (ioctl(sock, SIOCSLIFADDR, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ if (is_persistent) {
+ status = i_ipadm_persist_if(iph, ifname, af);
+ if (status != IPADM_SUCCESS) {
+ (void) i_ipadm_delete_if(iph, ifname,
+ af, IPADM_OPT_ACTIVE);
+ }
+ }
+ }
+ return (status);
+ }
+
+ dlpi_flags = DLPI_NOATTACH;
+
+ /*
+ * If IPADM_OPT_IPMP is specified, then this is a request
+ * to create an IPMP interface atop /dev/ipmpstub0. (We can't simply
+ * pass "ipmpstub0" as devname since an admin *could* have a normal
+ * vanity-named link named "ipmpstub0" that they'd like to plumb.)
+ */
+ if (ipadm_flags & IPADM_OPT_IPMP) {
+ dlpi_flags |= DLPI_DEVONLY;
+ linkname = "ipmpstub0";
+ } else {
+ /*
+ * Verify that the user is not creating a persistent
+ * IP interface on a non-persistent data-link.
+ */
+ if (!i_ipadm_is_vni(ifname) && dlstatus == DLADM_STATUS_OK &&
+ is_persistent && !(dlflags & DLADM_OPT_PERSIST)) {
+ return (IPADM_TEMPORARY_OBJ);
+ }
+ linkname = ifname;
+ }
+
+ /*
+ * We use DLPI_NOATTACH because the ip module will do the attach
+ * itself for DLPI style-2 devices.
+ */
+ if (dlpi_open(linkname, &dh_ip, dlpi_flags) != DLPI_SUCCESS)
+ return (IPADM_DLPI_FAILURE);
+ ip_fd = dlpi_fd(dh_ip);
+ if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) {
+ status = ipadm_errno2status(errno);
+ goto done;
+ }
+
+ /*
+ * Set IFF_IPV4/IFF_IPV6 flags. The kernel only allows modifications
+ * to IFF_IPv4, IFF_IPV6, IFF_BROADCAST, IFF_XRESOLV, IFF_NOLINKLOCAL.
+ */
+ ifflags = 0;
+
+ /* Set the name string and the IFF_IPV* flag */
+ if (af == AF_INET) {
+ ifflags = IFF_IPV4;
+ } else {
+ ifflags = IFF_IPV6;
+ /*
+ * With the legacy method, the link-local address should be
+ * configured as part of the interface plumb, using the default
+ * token. If IPH_LEGACY is not specified, we want to set :: as
+ * the address and require the admin to explicitly call
+ * ipadm_create_addr() with the address object type set to
+ * IPADM_ADDR_IPV6_ADDRCONF to create the link-local address
+ * as well as the autoconfigured addresses.
+ */
+ if (!legacy && !i_ipadm_is_6to4(iph, ifname))
+ ifflags |= IFF_NOLINKLOCAL;
+ }
+ (void) strlcpy(newif, ifname, sizeof (newif));
+ status = i_ipadm_slifname(iph, ifname, newif, ifflags, ip_fd,
+ ipadm_flags);
+ if (status != IPADM_SUCCESS)
+ goto done;
+
+ /* Get the full set of existing flags for this stream */
+ status = i_ipadm_get_flags(iph, newif, af, &ifflags);
+ if (status != IPADM_SUCCESS)
+ goto done;
+
+ udp_dev_name = (af == AF_INET6 ? UDP6_DEV_NAME : UDP_DEV_NAME);
+ status = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
+ if (status != IPADM_SUCCESS)
+ goto done;
+
+ /* Check if arp is not needed */
+ if (ifflags & (IFF_NOARP|IFF_IPV6)) {
+ /*
+ * PLINK the interface stream so that the application can exit
+ * without tearing down the stream.
+ */
+ if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1)
+ status = ipadm_errno2status(errno);
+ goto done;
+ }
+
+ /*
+ * This interface does use ARP, so set up a separate stream
+ * from the interface to ARP.
+ *
+ * We use DLPI_NOATTACH because the arp module will do the attach
+ * itself for DLPI style-2 devices.
+ */
+ if (dlpi_open(linkname, &dh_arp, dlpi_flags) != DLPI_SUCCESS) {
+ status = IPADM_DLPI_FAILURE;
+ goto done;
+ }
+
+ arp_fd = dlpi_fd(dh_arp);
+ if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) {
+ status = ipadm_errno2status(errno);
+ goto done;
+ }
+
+ status = i_ipadm_slifname_arp(newif, ifflags, arp_fd);
+ if (status != IPADM_SUCCESS)
+ goto done;
+ /*
+ * PLINK the IP and ARP streams so that ifconfig can exit
+ * without tearing down the stream.
+ */
+ if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) {
+ status = ipadm_errno2status(errno);
+ goto done;
+ }
+
+ if (ioctl(mux_fd, I_PLINK, arp_fd) < 0) {
+ status = ipadm_errno2status(errno);
+ (void) ioctl(mux_fd, I_PUNLINK, ip_muxid);
+ }
+
+done:
+ dlpi_close(dh_ip);
+ if (dh_arp != NULL)
+ dlpi_close(dh_arp);
+
+ if (mux_fd != -1)
+ (void) close(mux_fd);
+
+ if (status == IPADM_SUCCESS) {
+ /* copy back new ifname */
+ (void) strlcpy(ifname, newif, LIFNAMSIZ);
+ /*
+ * If it is a 6to4 tunnel, create a default
+ * addrobj name for the default address on the 0'th
+ * logical interface and set IFF_UP in the interface flags.
+ */
+ if (i_ipadm_is_6to4(iph, ifname)) {
+ struct ipadm_addrobj_s addr;
+
+ i_ipadm_init_addr(&addr, ifname, "", IPADM_ADDR_STATIC);
+ addr.ipadm_af = af;
+ status = i_ipadm_lookupadd_addrobj(iph, &addr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ status = ipadm_add_aobjname(iph, ifname,
+ af, addr.ipadm_aobjname, IPADM_ADDR_STATIC, 0);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ addr.ipadm_lifnum = 0;
+ i_ipadm_addrobj2lifname(&addr, lifname,
+ sizeof (lifname));
+ status = i_ipadm_set_flags(iph, lifname, af,
+ IFF_UP, 0);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ } else {
+ /*
+ * Prevent static IPv6 addresses from triggering
+ * autoconf. This does not have to be done for
+ * 6to4 tunnel interfaces, since in.ndpd will
+ * not autoconfigure those interfaces.
+ */
+ if (af == AF_INET6 && !legacy)
+ (void) i_ipadm_disable_autoconf(newif);
+ }
+
+ /*
+ * If IPADM_OPT_PERSIST was set in flags, store the
+ * interface in persistent DB.
+ */
+ if (is_persistent) {
+ status = i_ipadm_persist_if(iph, newif, af);
+ if (status != IPADM_SUCCESS) {
+ (void) i_ipadm_delete_if(iph, newif, af,
+ IPADM_OPT_ACTIVE);
+ }
+ }
+ }
+ if (status == IPADM_EXISTS)
+ status = IPADM_IF_EXISTS;
+ return (status);
+}
+
+/*
+ * Unplumbs the interface in `ifname' of family `af'.
+ */
+ipadm_status_t
+i_ipadm_unplumb_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
+{
+ int ip_muxid, arp_muxid;
+ int mux_fd = -1;
+ int muxid_fd = -1;
+ char *udp_dev_name;
+ uint64_t flags;
+ boolean_t changed_arp_muxid = B_FALSE;
+ int save_errno;
+ struct lifreq lifr;
+ ipadm_status_t ret = IPADM_SUCCESS;
+ int sock;
+ lifgroupinfo_t lifgr;
+ ifaddrlistx_t *ifaddrs, *ifaddrp;
+ boolean_t v6 = (af == AF_INET6);
+
+ /* Just do SIOCLIFREMOVEIF on loopback interfaces */
+ bzero(&lifr, sizeof (lifr));
+ if (i_ipadm_is_loopback(ifname) ||
+ (i_ipadm_get_lnum(ifname) != 0 && (iph->iph_flags & IPH_LEGACY))) {
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl((af == AF_INET) ? iph->iph_sock : iph->iph_sock6,
+ SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) {
+ return (ipadm_errno2status(errno));
+ }
+ return (IPADM_SUCCESS);
+ }
+
+ /*
+ * We used /dev/udp or udp6 to set up the mux. So we have to use
+ * the same now for PUNLINK also.
+ */
+ if (v6) {
+ udp_dev_name = UDP6_DEV_NAME;
+ sock = iph->iph_sock6;
+ } else {
+ udp_dev_name = UDP_DEV_NAME;
+ sock = iph->iph_sock;
+ }
+ if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) {
+ ret = ipadm_errno2status(errno);
+ goto done;
+ }
+ ret = ipadm_open_arp_on_udp(udp_dev_name, &mux_fd);
+ if (ret != IPADM_SUCCESS)
+ goto done;
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ ret = ipadm_errno2status(errno);
+ goto done;
+ }
+ flags = lifr.lifr_flags;
+again:
+ if (flags & IFF_IPMP) {
+ /*
+ * There are two reasons the I_PUNLINK can fail with EBUSY:
+ * (1) if IP interfaces are in the group, or (2) if IPMP data
+ * addresses are administratively up. For case (1), we fail
+ * here with a specific error message. For case (2), we bring
+ * down the addresses prior to doing the I_PUNLINK. If the
+ * I_PUNLINK still fails with EBUSY then the configuration
+ * must have changed after our checks, in which case we branch
+ * back up to `again' and rerun this logic. The net effect is
+ * that unplumbing an IPMP interface will only fail with EBUSY
+ * if IP interfaces are in the group.
+ */
+ if (ioctl(sock, SIOCGLIFGROUPNAME, &lifr) == -1) {
+ ret = ipadm_errno2status(errno);
+ goto done;
+ }
+ (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
+ LIFGRNAMSIZ);
+ if (ioctl(sock, SIOCGLIFGROUPINFO, &lifgr) == -1) {
+ ret = ipadm_errno2status(errno);
+ goto done;
+ }
+ if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) {
+ ret = IPADM_GRP_NOTEMPTY;
+ goto done;
+ }
+
+ /*
+ * The kernel will fail the I_PUNLINK if the IPMP interface
+ * has administratively up addresses; bring them down.
+ */
+ if (ifaddrlistx(ifname, IFF_UP|IFF_DUPLICATE,
+ 0, &ifaddrs) == -1) {
+ ret = ipadm_errno2status(errno);
+ goto done;
+ }
+ ifaddrp = ifaddrs;
+ for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) {
+ int sock = (ifaddrp->ia_flags & IFF_IPV4) ?
+ iph->iph_sock : iph->iph_sock6;
+ struct lifreq lifrl;
+
+ if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) ||
+ (!(ifaddrp->ia_flags & IFF_IPV6) && v6))
+ continue;
+
+ bzero(&lifrl, sizeof (lifrl));
+ (void) strlcpy(lifrl.lifr_name, ifaddrp->ia_name,
+ sizeof (lifrl.lifr_name));
+ if (ioctl(sock, SIOCGLIFFLAGS, &lifrl) < 0) {
+ ret = ipadm_errno2status(errno);
+ ifaddrlistx_free(ifaddrs);
+ goto done;
+ }
+ if (lifrl.lifr_flags & IFF_UP) {
+ ret = i_ipadm_set_flags(iph, lifrl.lifr_name,
+ ((lifrl.lifr_flags & IFF_IPV4) ? AF_INET :
+ AF_INET6), 0, IFF_UP);
+ if (ret != IPADM_SUCCESS) {
+ ifaddrlistx_free(ifaddrs);
+ goto done;
+ }
+ } else if (lifrl.lifr_flags & IFF_DUPLICATE) {
+ if (ioctl(sock, SIOCGLIFADDR, &lifrl) < 0 ||
+ ioctl(sock, SIOCSLIFADDR, &lifrl) < 0) {
+ ret = ipadm_errno2status(errno);
+ ifaddrlistx_free(ifaddrs);
+ goto done;
+ }
+ }
+ }
+ ifaddrlistx_free(ifaddrs);
+ }
+
+ if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) {
+ ret = ipadm_errno2status(errno);
+ goto done;
+ }
+ arp_muxid = lifr.lifr_arp_muxid;
+ ip_muxid = lifr.lifr_ip_muxid;
+
+ /*
+ * We don't have a good way of knowing whether the arp stream is
+ * plumbed. We can't rely on IFF_NOARP because someone could
+ * have turned it off later using "ifconfig xxx -arp".
+ */
+ if (arp_muxid != 0) {
+ if (ioctl(mux_fd, I_PUNLINK, arp_muxid) < 0) {
+ /*
+ * See the comment before the SIOCGLIFGROUPNAME call.
+ */
+ if (errno == EBUSY && (flags & IFF_IPMP))
+ goto again;
+
+ if ((errno == EINVAL) &&
+ (flags & (IFF_NOARP | IFF_IPV6))) {
+ /*
+ * Some plumbing utilities set the muxid to
+ * -1 or some invalid value to signify that
+ * there is no arp stream. Set the muxid to 0
+ * before trying to unplumb the IP stream.
+ * IP does not allow the IP stream to be
+ * unplumbed if it sees a non-null arp muxid,
+ * for consistency of IP-ARP streams.
+ */
+ lifr.lifr_arp_muxid = 0;
+ (void) ioctl(muxid_fd, SIOCSLIFMUXID,
+ (caddr_t)&lifr);
+ changed_arp_muxid = B_TRUE;
+ }
+ /*
+ * In case of any other error, we continue with
+ * the unplumb.
+ */
+ }
+ }
+
+ if (ioctl(mux_fd, I_PUNLINK, ip_muxid) < 0) {
+ if (changed_arp_muxid) {
+ /*
+ * Some error occurred, and we need to restore
+ * everything back to what it was.
+ */
+ save_errno = errno;
+ lifr.lifr_arp_muxid = arp_muxid;
+ lifr.lifr_ip_muxid = ip_muxid;
+ (void) ioctl(muxid_fd, SIOCSLIFMUXID, (caddr_t)&lifr);
+ errno = save_errno;
+ }
+ /*
+ * See the comment before the SIOCGLIFGROUPNAME call.
+ */
+ if (errno == EBUSY && (flags & IFF_IPMP))
+ goto again;
+
+ ret = ipadm_errno2status(errno);
+ }
+done:
+ if (muxid_fd != -1)
+ (void) close(muxid_fd);
+ if (mux_fd != -1)
+ (void) close(mux_fd);
+
+ if (af == AF_INET6 && ret == IPADM_SUCCESS) {
+ /*
+ * in.ndpd maintains the phyints in its memory even after
+ * the interface is plumbed, so that it can be reused when
+ * the interface gets plumbed again. The default behavior
+ * of in.ndpd is to start autoconfiguration for an interface
+ * that gets plumbed. We need to send the
+ * message IPADM_ENABLE_AUTOCONF to in.ndpd to restore this
+ * default behavior on replumb.
+ */
+ (void) i_ipadm_enable_autoconf(ifname);
+ }
+ return (ret);
+}
+
+/*
+ * Saves the given interface name `ifname' with address family `af' in
+ * persistent DB.
+ */
+static ipadm_status_t
+i_ipadm_persist_if(ipadm_handle_t iph, const char *ifname, sa_family_t af)
+{
+ ipmgmt_if_arg_t ifarg;
+ int err;
+
+ (void) strlcpy(ifarg.ia_ifname, ifname, sizeof (ifarg.ia_ifname));
+ ifarg.ia_family = af;
+ ifarg.ia_cmd = IPMGMT_CMD_SETIF;
+ ifarg.ia_flags = IPMGMT_PERSIST;
+ err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Remove the IP interface from active configuration. If IPADM_OPT_PERSIST
+ * is set in `ipadm_flags', it is also removed from persistent configuration.
+ */
+ipadm_status_t
+i_ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ uint32_t ipadm_flags)
+{
+ ipadm_status_t ret = IPADM_SUCCESS;
+ ipadm_status_t db_status;
+ char tmp_ifname[LIFNAMSIZ];
+ char *cp;
+ struct ipadm_addrobj_s ipaddr;
+ boolean_t is_persistent =
+ (ipadm_flags & IPADM_OPT_PERSIST);
+
+ ret = i_ipadm_unplumb_if(iph, ifname, af);
+ if (ret != IPADM_SUCCESS)
+ goto done;
+
+ cp = strrchr(ifname, IPADM_LOGICAL_SEP);
+ if (cp != NULL) {
+ assert(iph->iph_flags & IPH_LEGACY);
+ /*
+ * This is a non-zero logical interface.
+ * Find the addrobj and remove it from the daemon's memory.
+ */
+ (void) strlcpy(tmp_ifname, ifname, sizeof (tmp_ifname));
+ tmp_ifname[cp - ifname] = '\0';
+ *cp++ = '\0';
+ ipaddr.ipadm_lifnum = atoi(cp);
+ (void) strlcpy(ipaddr.ipadm_ifname, tmp_ifname,
+ sizeof (ipaddr.ipadm_ifname));
+ ipaddr.ipadm_af = af;
+ ret = i_ipadm_get_lif2addrobj(iph, &ipaddr);
+ if (ret == IPADM_SUCCESS) {
+ ret = i_ipadm_delete_addrobj(iph, &ipaddr,
+ IPADM_OPT_ACTIVE);
+ } else if (ret == IPADM_NOTFOUND) {
+ ret = IPADM_SUCCESS;
+ }
+ return (ret);
+ }
+done:
+ /*
+ * Even if interface does not exist, remove all its addresses and
+ * properties from the persistent store. If interface does not
+ * exist both in kernel and the persistent store, return IPADM_ENXIO.
+ */
+ if ((ret == IPADM_ENXIO && is_persistent) || ret == IPADM_SUCCESS) {
+ db_status = i_ipadm_delete_ifobj(iph, ifname, af,
+ is_persistent);
+ if (db_status == IPADM_SUCCESS)
+ ret = IPADM_SUCCESS;
+ }
+
+ return (ret);
+}
+
+/*
+ * Resets all addresses on interface `ifname' with address family `af'
+ * from ipmgmtd daemon. If is_persistent = B_TRUE, all interface properties
+ * and address objects of `ifname' for `af' are also removed from the
+ * persistent DB.
+ */
+ipadm_status_t
+i_ipadm_delete_ifobj(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ boolean_t is_persistent)
+{
+ ipmgmt_if_arg_t ifarg;
+ int err;
+
+ ifarg.ia_cmd = IPMGMT_CMD_RESETIF;
+ ifarg.ia_flags = IPMGMT_ACTIVE;
+ if (is_persistent)
+ ifarg.ia_flags |= IPMGMT_PERSIST;
+ ifarg.ia_family = af;
+ (void) strlcpy(ifarg.ia_ifname, ifname, LIFNAMSIZ);
+
+ err = ipadm_door_call(iph, &ifarg, sizeof (ifarg), NULL, 0, B_FALSE);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Create the interface by plumbing it for IP.
+ * This function will check if there is saved configuration information
+ * for `ifname' and return IPADM_OP_DISABLE_OBJ if the name-space
+ * for `ifname' is taken.
+ */
+ipadm_status_t
+i_ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
+ uint32_t ipadm_flags)
+{
+ ipadm_status_t status;
+ boolean_t p_exists;
+ sa_family_t other_af;
+
+ /*
+ * Return error, if the interface already exists in either the active
+ * or the persistent configuration.
+ */
+ if (ipadm_if_enabled(iph, ifname, af))
+ return (IPADM_IF_EXISTS);
+
+ status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ other_af = (af == AF_INET ? AF_INET6 : AF_INET);
+ if (p_exists) {
+ if (!ipadm_if_enabled(iph, ifname, other_af))
+ return (IPADM_OP_DISABLE_OBJ);
+ else
+ ipadm_flags &= ~IPADM_OPT_PERSIST;
+ }
+
+ return (i_ipadm_plumb_if(iph, ifname, af, ipadm_flags));
+}
+
+/*
+ * Plumbs an interface. Creates both IPv4 and IPv6 interfaces by
+ * default, unless a value in `af' is specified. The interface may be plumbed
+ * only if there is no previously saved persistent configuration information
+ * for the interface (in which case the ipadm_enable_if() function must
+ * be used to enable the interface).
+ *
+ * Returns: IPADM_SUCCESS, IPADM_FAILURE, IPADM_IF_EXISTS,
+ * IPADM_IF_PERSIST_EXISTS, IPADM_DLPI_FAILURE,
+ * or appropriate ipadm_status_t corresponding to the errno.
+ *
+ * `ifname' must point to memory that can hold upto LIFNAMSIZ chars. It may
+ * be over-written with the actual interface name when a PPA has to be
+ * internally generated by the library.
+ */
+ipadm_status_t
+ipadm_create_if(ipadm_handle_t iph, char *ifname, sa_family_t af,
+ uint32_t flags)
+{
+ ipadm_status_t status;
+ boolean_t created_v4 = B_FALSE;
+ char newifname[LIFNAMSIZ];
+
+ /* Check for the required authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ if (flags == 0 || ((flags & IPADM_OPT_PERSIST) &&
+ !(flags & IPADM_OPT_ACTIVE)) ||
+ (flags & ~(IPADM_COMMON_OPT_MASK | IPADM_OPT_IPMP |
+ IPADM_OPT_GENPPA))) {
+ return (IPADM_INVALID_ARG);
+ }
+ if (flags & IPADM_OPT_GENPPA) {
+ if (snprintf(newifname, LIFNAMSIZ, "%s0", ifname) >=
+ LIFNAMSIZ)
+ return (IPADM_INVALID_ARG);
+ } else {
+ if (strlcpy(newifname, ifname, LIFNAMSIZ) >= LIFNAMSIZ)
+ return (IPADM_INVALID_ARG);
+ }
+
+ if (!i_ipadm_validate_ifname(iph, newifname))
+ return (IPADM_INVALID_ARG);
+
+ if ((af == AF_INET || af == AF_UNSPEC) &&
+ !i_ipadm_is_6to4(iph, ifname)) {
+ status = i_ipadm_create_if(iph, ifname, AF_INET, flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ created_v4 = B_TRUE;
+ }
+ if (af == AF_INET6 || af == AF_UNSPEC) {
+ status = i_ipadm_create_if(iph, ifname, AF_INET6, flags);
+ if (status != IPADM_SUCCESS) {
+ if (created_v4) {
+ (void) i_ipadm_delete_if(iph, ifname, AF_INET,
+ IPADM_OPT_ACTIVE);
+ }
+ return (status);
+ }
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Deletes the interface in `ifname'. Removes both IPv4 and IPv6 interfaces
+ * when `af' = AF_UNSPEC.
+ */
+ipadm_status_t
+ipadm_delete_if(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ uint32_t flags)
+{
+ ipadm_status_t status1 = IPADM_SUCCESS;
+ ipadm_status_t status2 = IPADM_SUCCESS;
+ ipadm_status_t other;
+
+ /* Check for the required authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* Validate the `ifname' for any logical interface. */
+ if (flags == 0 || (flags & ~(IPADM_COMMON_OPT_MASK)) ||
+ !i_ipadm_validate_ifname(iph, ifname))
+ return (IPADM_INVALID_ARG);
+
+ if (af == AF_INET || af == AF_UNSPEC)
+ status1 = i_ipadm_delete_if(iph, ifname, AF_INET, flags);
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ status2 = i_ipadm_delete_if(iph, ifname, AF_INET6, flags);
+ /*
+ * If the family has been uniquely identified, we return the
+ * associated status, even if that is ENXIO. Calls from ifconfig
+ * which can only unplumb one of IPv4/IPv6 at any time fall under
+ * this category.
+ */
+ if (af == AF_INET)
+ return (status1);
+ else if (af == AF_INET6)
+ return (status2);
+ else if (af != AF_UNSPEC)
+ return (IPADM_INVALID_ARG);
+
+ /*
+ * If af is AF_UNSPEC, then we return the following:
+ * status1, if status1 == status2
+ * IPADM_SUCCESS, if either of status1 or status2 is SUCCESS
+ * and the other status is ENXIO
+ * IPADM_ENXIO, if both status1 and status2 are ENXIO
+ * IPADM_FAILURE otherwise.
+ */
+ if (status1 == status2) {
+ /* covers the case when both status1 and status2 are ENXIO */
+ return (status1);
+ } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
+ if (status1 == IPADM_SUCCESS)
+ other = status2;
+ else
+ other = status1;
+ return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
+ } else {
+ return (IPADM_FAILURE);
+ }
+}
+
+/*
+ * Returns information about all interfaces in both active and persistent
+ * configuration. If `ifname' is not NULL, it returns only the interface
+ * identified by `ifname'.
+ *
+ * Return values:
+ * On success: IPADM_SUCCESS.
+ * On error : IPADM_INVALID_ARG, IPADM_ENXIO or IPADM_FAILURE.
+ */
+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)
+{
+ ipadm_status_t status;
+ ifspec_t ifsp;
+
+ if (if_info == NULL || iph == NULL || flags != 0)
+ return (IPADM_INVALID_ARG);
+
+ if (ifname != NULL &&
+ (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ status = i_ipadm_get_all_if_info(iph, ifname, if_info, lifc_flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ if (ifname != NULL && *if_info == NULL)
+ return (IPADM_ENXIO);
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Frees the linked list allocated by ipadm_if_info().
+ */
+void
+ipadm_free_if_info(ipadm_if_info_t *ifinfo)
+{
+ ipadm_if_info_t *ifinfo_next;
+
+ for (; ifinfo != NULL; ifinfo = ifinfo_next) {
+ ifinfo_next = ifinfo->ifi_next;
+ free(ifinfo);
+ }
+}
+
+/*
+ * Re-enable the interface `ifname' based on the saved configuration
+ * for `ifname'.
+ */
+ipadm_status_t
+ipadm_enable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
+{
+ nvlist_t *ifnvl;
+ ipadm_status_t status;
+ ifspec_t ifsp;
+
+ /* Check for the required authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* Check for logical interfaces. */
+ if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
+ return (IPADM_INVALID_ARG);
+
+ /* Enabling an interface persistently is not supported. */
+ if (flags & IPADM_OPT_PERSIST)
+ return (IPADM_NOTSUP);
+
+ /*
+ * Return early by checking if the interface is already enabled.
+ */
+ if (ipadm_if_enabled(iph, ifname, AF_INET) &&
+ ipadm_if_enabled(iph, ifname, AF_INET6)) {
+ return (IPADM_IF_EXISTS);
+ }
+ /*
+ * Enable the interface and restore all its interface properties
+ * and address objects.
+ */
+ status = i_ipadm_init_ifs(iph, ifname, &ifnvl);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ assert(ifnvl != NULL);
+ /*
+ * ipadm_enable_if() does exactly what ipadm_init_ifs() does,
+ * but only for one interface. We need to set IPH_INIT because
+ * ipmgmtd daemon does not have to write the interface to persistent
+ * db. The interface is already available in persistent db
+ * and we are here to re-enable the persistent configuration.
+ */
+ iph->iph_flags |= IPH_INIT;
+ status = i_ipadm_init_ifobj(iph, ifname, ifnvl);
+ iph->iph_flags &= ~IPH_INIT;
+ return (status);
+}
+
+/*
+ * Disable the interface `ifname' by removing it from the active configuration.
+ * Error code return values follow the model in ipadm_delete_if()
+ */
+ipadm_status_t
+ipadm_disable_if(ipadm_handle_t iph, const char *ifname, uint32_t flags)
+{
+ ipadm_status_t status1, status2, other;
+ ifspec_t ifsp;
+
+ /* Check for the required authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ /* Check for logical interfaces. */
+ if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid)
+ return (IPADM_INVALID_ARG);
+
+ /* Disabling an interface persistently is not supported. */
+ if (flags & IPADM_OPT_PERSIST)
+ return (IPADM_NOTSUP);
+
+ status1 = i_ipadm_unplumb_if(iph, ifname, AF_INET6);
+ if (status1 == IPADM_SUCCESS)
+ status1 = i_ipadm_delete_ifobj(iph, ifname, AF_INET6, B_FALSE);
+ status2 = i_ipadm_unplumb_if(iph, ifname, AF_INET);
+ if (status2 == IPADM_SUCCESS)
+ status2 = i_ipadm_delete_ifobj(iph, ifname, AF_INET, B_FALSE);
+ if (status1 == status2) {
+ return (status2);
+ } else if (status1 == IPADM_SUCCESS || status2 == IPADM_SUCCESS) {
+ if (status1 == IPADM_SUCCESS)
+ other = status2;
+ else
+ other = status1;
+ return (other == IPADM_ENXIO ? IPADM_SUCCESS : IPADM_FAILURE);
+ } else {
+ return (IPADM_FAILURE);
+ }
+}
diff --git a/usr/src/lib/libipadm/common/ipadm_ipmgmt.h b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h
new file mode 100644
index 0000000000..7bd0d19f23
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h
@@ -0,0 +1,272 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _IPADM_IPMGMT_H
+#define _IPADM_IPMGMT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <door.h>
+#include <libipadm.h>
+#include <inet/tunables.h>
+
+/*
+ * Function declarations and data structures shared by libipadm.so and
+ * the IP management daemon.
+ */
+
+/* Authorization required to configure network interfaces */
+#define NETWORK_INTERFACE_CONFIG_AUTH "solaris.network.interface.config"
+
+/*
+ * Data store read/write utilities related declarations.
+ */
+/* Permanent data store for ipadm */
+#define IPADM_DB_FILE "/etc/ipadm/ipadm.conf"
+#define IPADM_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+#define IPADM_TMPFS_DIR "/etc/svc/volatile/ipadm"
+
+/*
+ * For more information on these definitions please refer to the top of
+ * ipadm_persist.c. These are the name of the nvpairs which hold the
+ * respective values. All nvpairs private to ipadm have names that begin
+ * with "_". Note below that 'prefixlen' is an address property and therefore
+ * not a private nvpair name.
+ */
+#define IPADM_NVP_PROTONAME "_protocol" /* protocol name */
+#define IPADM_NVP_IFNAME "_ifname" /* interface name */
+#define IPADM_NVP_AOBJNAME "_aobjname" /* addrobj name */
+#define IPADM_NVP_FAMILY "_family" /* address family */
+#define IPADM_NVP_IPV4ADDR "_ipv4addr" /* name of IPv4 addr nvlist */
+#define IPADM_NVP_IPNUMADDR "_addr" /* local address */
+#define IPADM_NVP_IPADDRHNAME "_aname" /* local hostname */
+#define IPADM_NVP_IPDADDRHNAME "_dname" /* remote hostname */
+#define IPADM_NVP_PREFIXLEN "prefixlen" /* prefixlen */
+#define IPADM_NVP_IPV6ADDR "_ipv6addr" /* name of IPv6 addr nvlist */
+#define IPADM_NVP_DHCP "_dhcp" /* name of DHCP nvlist */
+#define IPADM_NVP_WAIT "_wait" /* DHCP timeout value */
+#define IPADM_NVP_PRIMARY "_primary" /* DHCP primary interface */
+#define IPADM_NVP_LIFNUM "_lifnum" /* logical interface number */
+#define IPADM_NVP_INTFID "_intfid" /* name of IPv6 intfid nvlist */
+#define IPADM_NVP_STATELESS "_stateless" /* IPv6 autoconf stateless */
+#define IPADM_NVP_STATEFUL "_stateful" /* IPv6 autoconf dhcpv6 */
+
+#define IPADM_PRIV_NVP(s) ((s)[0] == '_')
+
+/* data-store operations */
+typedef enum {
+ IPADM_DB_WRITE = 0, /* Writes to DB */
+ IPADM_DB_DELETE, /* Deletes an entry from DB */
+ IPADM_DB_READ /* Read from DB */
+} ipadm_db_op_t;
+
+/*
+ * callback arg used by db_wfunc_t that writes to DB. The contents to be
+ * written to DB are captured in `dbw_nvl'.
+ */
+typedef struct ipadm_dbwrite_cbarg_s {
+ nvlist_t *dbw_nvl;
+ uint_t dbw_flags;
+} ipadm_dbwrite_cbarg_t;
+
+/*
+ * door related function declarations and data structures.
+ */
+
+/* The door file for the ipmgmt (ip-interface management) daemon */
+#define IPMGMT_DOOR "/etc/svc/volatile/ipadm/ipmgmt_door"
+#define MAXPROTONAMELEN 32
+
+/* door call command type */
+typedef enum {
+ IPMGMT_CMD_SETPROP = 1, /* persist property */
+ IPMGMT_CMD_SETIF, /* persist interface */
+ IPMGMT_CMD_SETADDR, /* persist address */
+ IPMGMT_CMD_GETPROP, /* retrieve persisted property value */
+ IPMGMT_CMD_GETIF, /* retrieve persisted interface conf. */
+ IPMGMT_CMD_GETADDR, /* retrieve persisted addresses */
+ IPMGMT_CMD_RESETIF, /* purge interface configuration */
+ IPMGMT_CMD_RESETADDR, /* purge address configuration */
+ IPMGMT_CMD_RESETPROP, /* purge property configuration */
+ IPMGMT_CMD_INITIF, /* retrieve interfaces to initialize */
+ IPMGMT_CMD_ADDROBJ_LOOKUPADD, /* addr. object lookup & add */
+ IPMGMT_CMD_ADDROBJ_ADD, /* add addr. object to addrobj map */
+ IPMGMT_CMD_LIF2ADDROBJ, /* lifname to addrobj mapping */
+ IPMGMT_CMD_AOBJNAME2ADDROBJ /* aobjname to addrobj mapping */
+} ipmgmt_door_cmd_type_t;
+
+/*
+ * Note: We need to keep the size of the structure the same on amd64 and i386
+ * for all door_call arguments and door_return structures.
+ */
+/* door_call argument */
+typedef struct ipmgmt_arg {
+ ipmgmt_door_cmd_type_t ia_cmd;
+} ipmgmt_arg_t;
+
+/* IPMGMT_CMD_{SETPROP|GETPROP|RESETPROP} door_call argument */
+typedef struct ipmgmt_prop_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ char ia_ifname[LIFNAMSIZ];
+ char ia_aobjname[IPADM_AOBJSIZ];
+ char ia_module[MAXPROTONAMELEN];
+ char ia_pname[MAXPROPNAMELEN];
+ char ia_pval[MAXPROPVALLEN];
+} ipmgmt_prop_arg_t;
+/*
+ * ia_flags used in ipmgmt_prop_arg_t.
+ * - APPEND updates the multi-valued property entry with a new value
+ * - REDUCE updates the multi-valued property entry by removing a value
+ */
+#define IPMGMT_APPEND 0x00000001
+#define IPMGMT_REMOVE 0x00000002
+
+/* IPMGMT_CMD_GETIF door_call argument structure */
+typedef struct ipmgmt_getif_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ char ia_ifname[LIFNAMSIZ];
+} ipmgmt_getif_arg_t;
+
+/* IPMGMT_CMD_RESETIF, IPMGMT_CMD_SETIF door_call argument structure */
+typedef struct ipmgmt_if_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ char ia_ifname[LIFNAMSIZ];
+ sa_family_t ia_family;
+} ipmgmt_if_arg_t;
+
+/* IPMGMT_CMD_INITIF door_call argument structure */
+typedef struct ipmgmt_initif_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ sa_family_t ia_family;
+ size_t ia_nvlsize;
+ /* packed nvl follows */
+} ipmgmt_initif_arg_t;
+
+/* IPMGMT_CMD_SETADDR door_call argument */
+typedef struct ipmgmt_setaddr_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ size_t ia_nvlsize;
+ /* packed nvl follows */
+} ipmgmt_setaddr_arg_t;
+
+/* IPMGMT_CMD_GETADDR door_call argument */
+typedef struct ipmgmt_getaddr_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ char ia_ifname[LIFNAMSIZ];
+ sa_family_t ia_family;
+ char ia_aobjname[IPADM_AOBJSIZ];
+} ipmgmt_getaddr_arg_t;
+
+/* IPMGMT_CMD_RESETADDR door_call argument */
+typedef struct ipmgmt_addr_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ char ia_aobjname[IPADM_AOBJSIZ];
+ int32_t ia_lnum;
+} ipmgmt_addr_arg_t;
+
+/*
+ * IPMGMT_CMD_{ADDROBJ_ADD|ADDROBJ_LOOKUPADD|LIFNUM2ADDROBJ|
+ * ADDROBJ2LIFNUM} door_call argument.
+ */
+typedef struct ipmgmt_aobjop_arg_s {
+ ipmgmt_door_cmd_type_t ia_cmd;
+ uint32_t ia_flags;
+ char ia_aobjname[IPADM_AOBJSIZ];
+ char ia_ifname[LIFNAMSIZ];
+ int32_t ia_lnum;
+ sa_family_t ia_family;
+ ipadm_addr_type_t ia_atype;
+} ipmgmt_aobjop_arg_t;
+
+/*
+ * ia_flags used inside the arguments for interface/address commands
+ * - ACTIVE updates the running configuration
+ * - PERSIST updates the permanent data store
+ * - INIT indicates that operation being performed is under init
+ * context
+ */
+#define IPMGMT_ACTIVE 0x00000001
+#define IPMGMT_PERSIST 0x00000002
+#define IPMGMT_INIT 0x00000004
+
+/* door call return value */
+typedef struct ipmgmt_retval_s {
+ int32_t ir_err;
+} ipmgmt_retval_t;
+
+/* IPMGMT_CMD_GETADDR door_return value */
+typedef struct ipmgmt_get_rval_s {
+ int32_t ir_err;
+ size_t ir_nvlsize;
+ /* packed nvl follows */
+} ipmgmt_get_rval_t;
+
+/* IPMGMT_CMD_GETPROP door_return value */
+typedef struct ipmgmt_getprop_rval_s {
+ int32_t ir_err;
+ char ir_pval[MAXPROPVALLEN];
+} ipmgmt_getprop_rval_t;
+
+/* IPMGMT_CMD_GETIF door_return value */
+typedef struct ipmgmt_getif_rval_s {
+ int32_t ir_err;
+ uint32_t ir_ifcnt;
+ ipadm_if_info_t ir_ifinfo[1];
+} ipmgmt_getif_rval_t;
+
+/* IPMGMT_CMD_{LOOKUPADD|LIFNUM2ADDROBJ|ADDROBJ2LIFNUM} door_return value */
+typedef struct ipmgmt_aobjop_rval_s {
+ int32_t ir_err;
+ char ir_aobjname[IPADM_AOBJSIZ];
+ char ir_ifname[LIFNAMSIZ];
+ int32_t ir_lnum;
+ sa_family_t ir_family;
+ uint32_t ir_flags;
+ ipadm_addr_type_t ir_atype;
+ struct sockaddr_storage ir_ifid;
+} ipmgmt_aobjop_rval_t;
+
+/* DB walk callback functions */
+typedef boolean_t db_wfunc_t(void *, nvlist_t *, char *, size_t, int *);
+extern int ipadm_rw_db(db_wfunc_t *, void *, const char *, mode_t,
+ ipadm_db_op_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IPADM_IPMGMT_H */
diff --git a/usr/src/lib/libipadm/common/ipadm_ndpd.c b/usr/src/lib/libipadm/common/ipadm_ndpd.c
new file mode 100644
index 0000000000..2030295e24
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_ndpd.c
@@ -0,0 +1,366 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains the functions that are required for communicating
+ * with in.ndpd while creating autoconfigured addresses.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <inet/ip.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <poll.h>
+#include <ipadm_ndpd.h>
+#include "libipadm_impl.h"
+
+#define NDPDTIMEOUT 5000
+#define PREFIXLEN_LINKLOCAL 10
+
+static ipadm_status_t i_ipadm_create_linklocal(ipadm_handle_t,
+ ipadm_addrobj_t);
+static void i_ipadm_make_linklocal(struct sockaddr_in6 *,
+ const struct in6_addr *);
+static ipadm_status_t i_ipadm_send_ndpd_cmd(const char *,
+ const struct ipadm_addrobj_s *, int);
+
+/*
+ * Sends message to in.ndpd asking not to do autoconf for the given interface,
+ * until IPADM_CREATE_ADDRS or IPADM_ENABLE_AUTOCONF is sent.
+ */
+ipadm_status_t
+i_ipadm_disable_autoconf(const char *ifname)
+{
+ return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_DISABLE_AUTOCONF));
+}
+
+/*
+ * Sends message to in.ndpd to enable autoconf for the given interface,
+ * until another IPADM_DISABLE_AUTOCONF is sent.
+ */
+ipadm_status_t
+i_ipadm_enable_autoconf(const char *ifname)
+{
+ return (i_ipadm_send_ndpd_cmd(ifname, NULL, IPADM_ENABLE_AUTOCONF));
+}
+
+ipadm_status_t
+i_ipadm_create_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t addr,
+ uint32_t i_flags)
+{
+ ipadm_status_t status;
+
+ /*
+ * Create the link local based on the given token. If the same intfid
+ * was already used with a different address object, this step will
+ * fail.
+ */
+ status = i_ipadm_create_linklocal(iph, addr);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ /*
+ * Request in.ndpd to start the autoconfiguration.
+ * If autoconfiguration was already started by another means (e.g.
+ * "ifconfig" ), in.ndpd will return EEXIST.
+ */
+ if (addr->ipadm_stateless || addr->ipadm_stateful) {
+ status = i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr,
+ IPADM_CREATE_ADDRS);
+ if (status != IPADM_SUCCESS &&
+ status != IPADM_NDPD_NOT_RUNNING) {
+ (void) i_ipadm_delete_addr(iph, addr);
+ return (status);
+ }
+ }
+
+ /* Persist the intfid. */
+ status = i_ipadm_addr_persist(iph, addr, B_FALSE, i_flags);
+ if (status != IPADM_SUCCESS) {
+ (void) i_ipadm_delete_addr(iph, addr);
+ (void) i_ipadm_send_ndpd_cmd(addr->ipadm_ifname, addr,
+ IPADM_DELETE_ADDRS);
+ }
+
+ return (status);
+}
+
+ipadm_status_t
+i_ipadm_delete_ipv6addrs(ipadm_handle_t iph, ipadm_addrobj_t ipaddr)
+{
+ ipadm_status_t status;
+
+ /*
+ * Send a msg to in.ndpd to remove the autoconfigured addresses,
+ * and delete the link local that was created.
+ */
+ status = i_ipadm_send_ndpd_cmd(ipaddr->ipadm_ifname, ipaddr,
+ IPADM_DELETE_ADDRS);
+ if (status == IPADM_NDPD_NOT_RUNNING)
+ status = IPADM_SUCCESS;
+ if (status == IPADM_SUCCESS)
+ status = i_ipadm_delete_addr(iph, ipaddr);
+
+ return (status);
+}
+
+static ipadm_status_t
+i_ipadm_create_linklocal(ipadm_handle_t iph, ipadm_addrobj_t addr)
+{
+ boolean_t addif = B_FALSE;
+ struct sockaddr_in6 *sin6;
+ struct lifreq lifr;
+ int err;
+ ipadm_status_t status;
+ in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, addr->ipadm_ifname, LIFNAMSIZ);
+
+ if ((err = ioctl(iph->iph_sock6, SIOCGLIFADDR, (caddr_t)&lifr)) < 0)
+ return (ipadm_errno2status(errno));
+
+ /*
+ * If no address exists on 0th logical interface,
+ * create link-local address on it. Else, create a new
+ * logical interface.
+ */
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ if (!IN6_IS_ADDR_UNSPECIFIED((&sin6->sin6_addr))) {
+ if (ioctl(iph->iph_sock6, SIOCLIFADDIF, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ addr->ipadm_lifnum = i_ipadm_get_lnum(lifr.lifr_name);
+ addif = B_TRUE;
+ }
+ /* Create the link-local address */
+ bzero(&lifr.lifr_addr, sizeof (lifr.lifr_addr));
+ (void) plen2mask(PREFIXLEN_LINKLOCAL, AF_INET6, &lifr.lifr_addr);
+ if ((err = ioctl(iph->iph_sock6, SIOCSLIFNETMASK, (caddr_t)&lifr)) < 0)
+ goto fail;
+ if (addr->ipadm_intfidlen == 0) {
+ /*
+ * If we have to use the default interface id,
+ * we just need to set the prefix to the link-local prefix.
+ * SIOCSLIFPREFIX sets the address with the given prefix
+ * and the default interface id.
+ */
+ sin6->sin6_addr = ll_template;
+ err = ioctl(iph->iph_sock6, SIOCSLIFPREFIX, (caddr_t)&lifr);
+ if (err < 0)
+ goto fail;
+ } else {
+ /* Make a linklocal address in sin6 and set it */
+ i_ipadm_make_linklocal(sin6, &addr->ipadm_intfid.sin6_addr);
+ err = ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr);
+ if (err < 0)
+ goto fail;
+ }
+ if ((err = ioctl(iph->iph_sock6, SIOCGLIFFLAGS, (char *)&lifr)) < 0)
+ goto fail;
+ lifr.lifr_flags |= IFF_UP;
+ if ((err = ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (char *)&lifr)) < 0)
+ goto fail;
+ return (IPADM_SUCCESS);
+
+fail:
+ if (errno == EEXIST)
+ status = IPADM_ADDRCONF_EXISTS;
+ else
+ status = ipadm_errno2status(errno);
+ /* Remove the linklocal that was created. */
+ if (addif) {
+ (void) ioctl(iph->iph_sock6, SIOCLIFREMOVEIF, (caddr_t)&lifr);
+ } else {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
+ lifr.lifr_flags &= ~IFF_UP;
+ (void) ioctl(iph->iph_sock6, SIOCSLIFFLAGS, (caddr_t)&lifr);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = in6addr_any;
+ (void) ioctl(iph->iph_sock6, SIOCSLIFADDR, (caddr_t)&lifr);
+ }
+ return (status);
+}
+
+/*
+ * Make a linklocal address based on the given intfid and copy it into
+ * the output parameter `sin6'.
+ */
+static void
+i_ipadm_make_linklocal(struct sockaddr_in6 *sin6, const struct in6_addr *intfid)
+{
+ int i;
+ in6_addr_t ll_template = {0xfe, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = *intfid;
+ for (i = 0; i < 4; i++) {
+ sin6->sin6_addr.s6_addr[i] =
+ sin6->sin6_addr.s6_addr[i] | ll_template.s6_addr[i];
+ }
+}
+
+/*
+ * Function that forms an ndpd msg and sends it to the in.ndpd daemon's loopback
+ * listener socket.
+ */
+static ipadm_status_t
+i_ipadm_send_ndpd_cmd(const char *ifname, const struct ipadm_addrobj_s *addr,
+ int cmd)
+{
+ int fd;
+ struct sockaddr_un servaddr;
+ int flags;
+ ipadm_ndpd_msg_t msg;
+ int retval;
+
+ if (addr == NULL &&
+ (cmd == IPADM_CREATE_ADDRS || cmd == IPADM_DELETE_ADDRS)) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1)
+ return (IPADM_FAILURE);
+
+ /* Put the socket in non-blocking mode */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags != -1)
+ (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ /* Connect to in.ndpd */
+ bzero(&servaddr, sizeof (servaddr));
+ servaddr.sun_family = AF_UNIX;
+ (void) strlcpy(servaddr.sun_path, IPADM_UDS_PATH,
+ sizeof (servaddr.sun_path));
+ if (connect(fd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1)
+ goto fail;
+
+ bzero(&msg, sizeof (msg));
+ msg.inm_cmd = cmd;
+ (void) strlcpy(msg.inm_ifname, ifname, sizeof (msg.inm_ifname));
+ if (addr != NULL) {
+ msg.inm_intfid = addr->ipadm_intfid;
+ msg.inm_intfidlen = addr->ipadm_intfidlen;
+ msg.inm_stateless = addr->ipadm_stateless;
+ msg.inm_stateful = addr->ipadm_stateful;
+ if (cmd == IPADM_CREATE_ADDRS) {
+ (void) strlcpy(msg.inm_aobjname, addr->ipadm_aobjname,
+ sizeof (msg.inm_aobjname));
+ }
+ }
+ if (ipadm_ndpd_write(fd, &msg, sizeof (msg)) < 0)
+ goto fail;
+ if (ipadm_ndpd_read(fd, &retval, sizeof (retval)) < 0)
+ goto fail;
+ (void) close(fd);
+ if (cmd == IPADM_CREATE_ADDRS && retval == EEXIST)
+ return (IPADM_ADDRCONF_EXISTS);
+ return (ipadm_errno2status(retval));
+fail:
+ (void) close(fd);
+ return (IPADM_NDPD_NOT_RUNNING);
+}
+
+/*
+ * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed
+ * to by `buf'.
+ */
+int
+ipadm_ndpd_read(int fd, void *buffer, size_t buflen)
+{
+ int retval;
+ ssize_t nbytes = 0; /* total bytes processed */
+ ssize_t prbytes; /* per-round bytes processed */
+ struct pollfd pfd;
+
+ while (nbytes < buflen) {
+
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+
+ /*
+ * Wait for data to come in or for the timeout to fire.
+ */
+ retval = poll(&pfd, 1, NDPDTIMEOUT);
+ if (retval <= 0) {
+ if (retval == 0)
+ errno = ETIME;
+ break;
+ }
+
+ /*
+ * Descriptor is ready; have at it.
+ */
+ prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes);
+ if (prbytes <= 0) {
+ if (prbytes == -1 && errno == EINTR)
+ continue;
+ break;
+ }
+ nbytes += prbytes;
+ }
+
+ return (nbytes == buflen ? 0 : -1);
+}
+
+/*
+ * Write `buflen' bytes from `buffer' to open file `fd'. Returns 0
+ * if all requested bytes were written, or an error code if not.
+ */
+int
+ipadm_ndpd_write(int fd, const void *buffer, size_t buflen)
+{
+ size_t nwritten;
+ ssize_t nbytes;
+ const char *buf = buffer;
+
+ for (nwritten = 0; nwritten < buflen; nwritten += nbytes) {
+ nbytes = write(fd, &buf[nwritten], buflen - nwritten);
+ if (nbytes == -1)
+ return (-1);
+ if (nbytes == 0) {
+ errno = EIO;
+ return (-1);
+ }
+ }
+
+ assert(nwritten == buflen);
+ return (0);
+}
diff --git a/usr/src/lib/libipadm/common/ipadm_ndpd.h b/usr/src/lib/libipadm/common/ipadm_ndpd.h
new file mode 100644
index 0000000000..f35f8d9b7a
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_ndpd.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef _IPADM_NDPD_H
+#define _IPADM_NDPD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <libipadm.h>
+
+/* File used for the AF_UNIX socket used in communicating with in.ndpd */
+#define IPADM_UDS_PATH "/var/run/in.ndpd_ipadm"
+
+/* Types of messages sent to in.ndpd */
+enum {
+ IPADM_DISABLE_AUTOCONF,
+ IPADM_ENABLE_AUTOCONF,
+ IPADM_CREATE_ADDRS,
+ IPADM_DELETE_ADDRS
+};
+
+/* Message format sent to in.ndpd */
+typedef struct ipadm_ndpd_msg_s {
+ uint32_t inm_cmd;
+ char inm_ifname[LIFNAMSIZ];
+ struct sockaddr_in6 inm_intfid;
+ int inm_intfidlen;
+ boolean_t inm_stateless;
+ boolean_t inm_stateful;
+ char inm_aobjname[MAXNAMELEN];
+} ipadm_ndpd_msg_t;
+
+/* Functions to send to and receive from in.ndpd */
+extern int ipadm_ndpd_write(int, const void *, size_t);
+extern int ipadm_ndpd_read(int, void *, size_t);
+
+/*
+ * Functions used by in.ndpd to add and delete address objects while
+ * adding/deleting each stateless/stateful autoconfigured address.
+ */
+extern ipadm_status_t ipadm_add_aobjname(ipadm_handle_t, const char *,
+ sa_family_t, const char *, ipadm_addr_type_t, int);
+extern ipadm_status_t ipadm_delete_aobjname(ipadm_handle_t, const char *,
+ sa_family_t, const char *, ipadm_addr_type_t, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IPADM_NDPD_H */
diff --git a/usr/src/lib/libipadm/common/ipadm_persist.c b/usr/src/lib/libipadm/common/ipadm_persist.c
new file mode 100644
index 0000000000..698d4af359
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_persist.c
@@ -0,0 +1,828 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains routines to read/write formatted entries from/to
+ * libipadm data store /etc/ipadm/ipadm.conf. Each entry in the DB is a
+ * series of IPADM_NVPAIR_SEP separated (name, value) pairs, as shown
+ * below:
+ * name=value[;...]
+ *
+ * The 'name' determines how to interpret 'value'. The supported names are:
+ *
+ * IPADM_NVP_IPV6ADDR - value holds local and remote IPv6 addresses and when
+ * converted to nvlist, will contain nvpairs for local and remote
+ * addresses. These nvpairs are of type DATA_TYPE_STRING
+ *
+ * IPADM_NVP_IPV4ADDR - value holds local and remote IPv4 addresses and when
+ * converted to nvlist, will contain nvpairs for local and remote
+ * addresses. These nvpairs are of type DATA_TYPE_STRING
+ *
+ * IPADM_NVP_INTFID - value holds token, prefixlen, stateless and stateful
+ * info and when converted to nvlist, will contain following nvpairs
+ * interface_id: DATA_TYPE_UINT8_ARRAY
+ * prefixlen: DATA_TYPE_UINT32
+ * stateless: DATA_TYPE_STRING
+ * stateful: DATA_TYPE_STRING
+ *
+ * IPADM_NVP_DHCP - value holds wait time and primary info and when converted
+ * to nvlist, will contain following nvpairs
+ * wait: DATA_TYPE_INT32
+ * primary: DATA_TYPE_BOOLEAN
+ *
+ * default - value is a single entity and when converted to nvlist, will
+ * contain nvpair of type DATA_TYPE_STRING. nvpairs private to
+ * ipadm are of this type. Further the property name and property
+ * values are stored as nvpairs of this type.
+ *
+ * The syntax for each line is described above the respective functions below.
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dld.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/sockio.h>
+#include "libipadm_impl.h"
+
+#define MAXLINELEN 1024
+#define IPADM_NVPAIR_SEP ";"
+#define IPADM_NAME_SEP ","
+
+static char ipadm_rootdir[MAXPATHLEN] = "/";
+
+static int ipadm_process_db_line(db_wfunc_t *, void *, FILE *fp, FILE *nfp,
+ ipadm_db_op_t);
+
+/*
+ * convert nvpair to a "name=value" string for writing to the DB.
+ */
+typedef size_t ipadm_wfunc_t(nvpair_t *, char *, size_t);
+
+/*
+ * ipadm_rfunc_t takes (`name', `value') and adds the appropriately typed
+ * nvpair to the nvlist.
+ */
+typedef void ipadm_rfunc_t(nvlist_t *, char *name, char *value);
+
+static ipadm_rfunc_t i_ipadm_str_dbline2nvl, i_ipadm_ip4_dbline2nvl,
+ i_ipadm_ip6_dbline2nvl, i_ipadm_intfid_dbline2nvl,
+ i_ipadm_dhcp_dbline2nvl;
+
+static ipadm_wfunc_t i_ipadm_str_nvp2dbline, i_ipadm_ip4_nvp2dbline,
+ i_ipadm_ip6_nvp2dbline, i_ipadm_intfid_nvp2dbline,
+ i_ipadm_dhcp_nvp2dbline;
+
+/*
+ * table of function pointers to read/write formatted entries from/to
+ * ipadm.conf.
+ */
+typedef struct ipadm_conf_ent_s {
+ const char *ipent_type_name;
+ ipadm_wfunc_t *ipent_wfunc;
+ ipadm_rfunc_t *ipent_rfunc;
+} ipadm_conf_ent_t;
+
+static ipadm_conf_ent_t ipadm_conf_ent[] = {
+ { IPADM_NVP_IPV6ADDR, i_ipadm_ip6_nvp2dbline, i_ipadm_ip6_dbline2nvl },
+ { IPADM_NVP_IPV4ADDR, i_ipadm_ip4_nvp2dbline, i_ipadm_ip4_dbline2nvl },
+ { IPADM_NVP_INTFID, i_ipadm_intfid_nvp2dbline,
+ i_ipadm_intfid_dbline2nvl },
+ { IPADM_NVP_DHCP, i_ipadm_dhcp_nvp2dbline, i_ipadm_dhcp_dbline2nvl },
+ { NULL, i_ipadm_str_nvp2dbline, i_ipadm_str_dbline2nvl }
+};
+
+static ipadm_conf_ent_t *
+i_ipadm_find_conf_type(const char *type)
+{
+ int i;
+
+ for (i = 0; ipadm_conf_ent[i].ipent_type_name != NULL; i++)
+ if (strcmp(type, ipadm_conf_ent[i].ipent_type_name) == 0)
+ break;
+ return (&ipadm_conf_ent[i]);
+}
+
+/*
+ * Extracts the hostnames IPADM_NVP_IPADDRHNAME and IPADM_NVP_IPDADDRHNAME from
+ * the given nvlist `nvl' and adds the strings to `buf'.
+ */
+size_t
+i_ipadm_ip_addhostname2dbline(nvlist_t *nvl, char *buf, size_t buflen)
+{
+ char *cp;
+ char tmpbuf[IPADM_STRSIZE];
+
+ /* Add the local hostname */
+ if (nvlist_lookup_string(nvl, IPADM_NVP_IPADDRHNAME, &cp) != 0)
+ return (0);
+ (void) strlcat(buf, cp, buflen); /* local hostname */
+
+ /* Add the dst hostname */
+ if (nvlist_lookup_string(nvl, IPADM_NVP_IPDADDRHNAME, &cp) != 0) {
+ /* no dst addr. just add a NULL character */
+ (void) snprintf(tmpbuf, sizeof (tmpbuf), ",");
+ } else {
+ (void) snprintf(tmpbuf, sizeof (tmpbuf), ",%s", cp);
+ }
+ return (strlcat(buf, tmpbuf, buflen));
+}
+
+/*
+ * Converts IPADM_NVP_IPV4ADDR nvpair to a string representation for writing to
+ * the DB. The converted string format:
+ * ipv4addr=<local numeric IP string or hostname,remote numeric IP
+ * string or hostname>
+ */
+static size_t
+i_ipadm_ip4_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+ nvlist_t *v;
+ int nbytes;
+
+ assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
+ strcmp(nvpair_name(nvp), IPADM_NVP_IPV4ADDR) == 0);
+
+ (void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV4ADDR);
+ if (nvpair_value_nvlist(nvp, &v) != 0)
+ goto fail;
+ nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen);
+ if (nbytes != 0)
+ return (nbytes);
+fail:
+ buf[0] = '\0';
+ return (0);
+}
+
+/*
+ * Converts IPADM_NVP_IPV6ADDR nvpair to a string representation for writing to
+ * the DB. The converted string format:
+ * ipv6addr=<local numeric IP string or hostname,remote numeric IP
+ * string or hostname>
+ */
+static size_t
+i_ipadm_ip6_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+ nvlist_t *v;
+ int nbytes;
+
+ assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
+ strcmp(nvpair_name(nvp), IPADM_NVP_IPV6ADDR) == 0);
+
+ (void) snprintf(buf, buflen, "%s=", IPADM_NVP_IPV6ADDR);
+ if (nvpair_value_nvlist(nvp, &v) != 0)
+ goto fail;
+ nbytes = i_ipadm_ip_addhostname2dbline(v, buf, buflen);
+ if (nbytes != 0)
+ return (nbytes);
+fail:
+ buf[0] = '\0';
+ return (0);
+}
+
+/*
+ * Converts IPADM_NVP_INTFID nvpair to a string representation for writing to
+ * the DB. The converted string format:
+ * IPADM_NVP_INTFID=<intfid/prefixlen>,{yes|no},{yes|no}
+ */
+static size_t
+i_ipadm_intfid_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+ char addrbuf[IPADM_STRSIZE];
+ nvlist_t *v;
+ uint32_t prefixlen;
+ struct in6_addr in6addr;
+ char *stateless;
+ char *stateful;
+
+ assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
+ strcmp(nvpair_name(nvp), IPADM_NVP_INTFID) == 0);
+
+ (void) snprintf(buf, buflen, "%s=", IPADM_NVP_INTFID);
+ if (nvpair_value_nvlist(nvp, &v) != 0)
+ goto fail;
+ if (i_ipadm_nvl2in6_addr(v, IPADM_NVP_IPNUMADDR, &in6addr) !=
+ IPADM_SUCCESS)
+ goto fail;
+ (void) inet_ntop(AF_INET6, &in6addr, addrbuf,
+ sizeof (addrbuf));
+ (void) strlcat(buf, addrbuf, buflen);
+ if (nvlist_lookup_uint32(v, IPADM_NVP_PREFIXLEN, &prefixlen) != 0 ||
+ nvlist_lookup_string(v, IPADM_NVP_STATELESS, &stateless) != 0 ||
+ nvlist_lookup_string(v, IPADM_NVP_STATEFUL, &stateful) != 0)
+ goto fail;
+ (void) snprintf(addrbuf, sizeof (addrbuf), "/%d,%s,%s",
+ prefixlen, stateless, stateful);
+ return (strlcat(buf, addrbuf, buflen));
+fail:
+ buf[0] = '\0';
+ return (0);
+}
+
+/*
+ * Converts IPADM_NVP_DHCP nvpair to a string representation for writing to the
+ * DB. The converted string format:
+ * IPADM_NVP_DHCP=<wait_time>,{yes|no}
+ */
+static size_t
+i_ipadm_dhcp_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+ char addrbuf[IPADM_STRSIZE];
+ int32_t wait;
+ boolean_t primary;
+ nvlist_t *v;
+
+ assert(nvpair_type(nvp) == DATA_TYPE_NVLIST &&
+ strcmp(nvpair_name(nvp), IPADM_NVP_DHCP) == 0);
+
+ if (nvpair_value_nvlist(nvp, &v) != 0 ||
+ nvlist_lookup_int32(v, IPADM_NVP_WAIT, &wait) != 0 ||
+ nvlist_lookup_boolean_value(v, IPADM_NVP_PRIMARY, &primary) != 0) {
+ return (0);
+ }
+ (void) snprintf(buf, buflen, "%s=", IPADM_NVP_DHCP);
+ (void) snprintf(addrbuf, sizeof (addrbuf), "%d,%s", wait,
+ (primary ? "yes" : "no"));
+ return (strlcat(buf, addrbuf, buflen));
+}
+
+/*
+ * Constructs a "<name>=<value>" string from the nvpair, whose type must
+ * be STRING.
+ */
+static size_t
+i_ipadm_str_nvp2dbline(nvpair_t *nvp, char *buf, size_t buflen)
+{
+ char *str = NULL;
+
+ assert(nvpair_type(nvp) == DATA_TYPE_STRING);
+ if (nvpair_value_string(nvp, &str) != 0)
+ return (0);
+ return (snprintf(buf, buflen, "%s=%s", nvpair_name(nvp), str));
+}
+
+/*
+ * Converts a nvlist to string of the form:
+ * <prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
+ */
+size_t
+ipadm_nvlist2str(nvlist_t *nvl, char *buf, size_t buflen)
+{
+ nvpair_t *nvp = NULL;
+ uint_t nbytes = 0, tbytes = 0;
+ ipadm_conf_ent_t *ipent;
+ size_t bufsize = buflen;
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ ipent = i_ipadm_find_conf_type(nvpair_name(nvp));
+ nbytes = (*ipent->ipent_wfunc)(nvp, buf, buflen);
+ /* add nvpair separator */
+ nbytes += snprintf(buf + nbytes, buflen - nbytes, "%s",
+ IPADM_NVPAIR_SEP);
+ buflen -= nbytes;
+ buf += nbytes;
+ tbytes += nbytes;
+ if (tbytes >= bufsize) /* buffer overflow */
+ return (0);
+ }
+ nbytes = snprintf(buf, buflen, "%c%c", '\n', '\0');
+ tbytes += nbytes;
+ if (tbytes >= bufsize)
+ return (0);
+ return (tbytes);
+}
+
+/*
+ * Adds a nvpair, using the `name' and `value', to the nvlist in `nvl'.
+ * The value will be interpreted as explained at the top of this file.
+ */
+static void
+i_ipadm_add_nvpair(nvlist_t *nvl, char *name, char *value)
+{
+ ipadm_conf_ent_t *ipent;
+
+ ipent = i_ipadm_find_conf_type(name);
+ (*ipent->ipent_rfunc)(nvl, name, value);
+}
+
+/*
+ * Adds an nvpair for IPv4 addr to the nvlist. The "name" is the string in
+ * IPADM_NVP_IPV4ADDR. The "value" for IPADM_NVP_IPV4ADDR is another nvlist.
+ * Allocate the value nvlist for IPADM_NVP_IPV4ADDR if necessary, and add
+ * the address and hostnames from the address object `ipaddr' to it.
+ * Then add the allocated nvlist to `nvl'.
+ */
+ipadm_status_t
+i_ipadm_add_ipaddr2nvl(nvlist_t *nvl, ipadm_addrobj_t ipaddr)
+{
+ nvlist_t *nvl_addr = NULL;
+ int err;
+ char *name;
+ sa_family_t af = ipaddr->ipadm_af;
+
+ if (af == AF_INET) {
+ name = IPADM_NVP_IPV4ADDR;
+ } else {
+ assert(af == AF_INET6);
+ name = IPADM_NVP_IPV6ADDR;
+ }
+
+ if (!nvlist_exists(nvl, name)) {
+ if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0)
+ return (ipadm_errno2status(err));
+ if ((err = nvlist_add_nvlist(nvl, name, nvl_addr)) != 0) {
+ nvlist_free(nvl_addr);
+ return (ipadm_errno2status(err));
+ }
+ nvlist_free(nvl_addr);
+ }
+ if ((err = nvlist_lookup_nvlist(nvl, name, &nvl_addr)) != 0 ||
+ (err = nvlist_add_string(nvl_addr, IPADM_NVP_IPADDRHNAME,
+ ipaddr->ipadm_static_aname)) != 0)
+ return (ipadm_errno2status(err));
+ if (!sockaddrunspec(&ipaddr->ipadm_static_dst_addr)) {
+ if ((err = nvlist_add_string(nvl_addr, IPADM_NVP_IPDADDRHNAME,
+ ipaddr->ipadm_static_dname)) != 0)
+ return (ipadm_errno2status(err));
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Adds an nvpair for IPv6 interface id to the nvlist. The "name" is
+ * the string in IPADM_NVP_INTFID. The "value" for IPADM_NVP_INTFID is another
+ * nvlist. Allocate the value nvlist for IPADM_NVP_INTFID if necessary, and add
+ * the interface id and its prefixlen from the address object `ipaddr' to it.
+ * Then add the allocated nvlist to `nvl'.
+ */
+ipadm_status_t
+i_ipadm_add_intfid2nvl(nvlist_t *nvl, ipadm_addrobj_t addr)
+{
+ nvlist_t *nvl_addr = NULL;
+ struct in6_addr addr6;
+ int err;
+
+ if (!nvlist_exists(nvl, IPADM_NVP_INTFID)) {
+ if ((err = nvlist_alloc(&nvl_addr, NV_UNIQUE_NAME, 0)) != 0)
+ return (ipadm_errno2status(err));
+ if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_INTFID,
+ nvl_addr)) != 0) {
+ nvlist_free(nvl_addr);
+ return (ipadm_errno2status(err));
+ }
+ nvlist_free(nvl_addr);
+ }
+ if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID,
+ &nvl_addr)) != 0 || (err = nvlist_add_uint32(nvl_addr,
+ IPADM_NVP_PREFIXLEN, addr->ipadm_intfidlen)) != 0) {
+ return (ipadm_errno2status(err));
+ }
+ addr6 = addr->ipadm_intfid.sin6_addr;
+ if ((err = nvlist_add_uint8_array(nvl_addr, IPADM_NVP_IPNUMADDR,
+ addr6.s6_addr, 16)) != 0) {
+ return (ipadm_errno2status(err));
+ }
+ if (addr->ipadm_stateless)
+ err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "yes");
+ else
+ err = nvlist_add_string(nvl_addr, IPADM_NVP_STATELESS, "no");
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ if (addr->ipadm_stateful)
+ err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "yes");
+ else
+ err = nvlist_add_string(nvl_addr, IPADM_NVP_STATEFUL, "no");
+ if (err != 0)
+ return (ipadm_errno2status(err));
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Adds an nvpair for a dhcp address object to the nvlist. The "name" is
+ * the string in IPADM_NVP_DHCP. The "value" for IPADM_NVP_DHCP is another
+ * nvlist. Allocate the value nvlist for IPADM_NVP_DHCP if necessary, and add
+ * the parameters from the arguments `primary' and `wait'.
+ * Then add the allocated nvlist to `nvl'.
+ */
+ipadm_status_t
+i_ipadm_add_dhcp2nvl(nvlist_t *nvl, boolean_t primary, int32_t wait)
+{
+ nvlist_t *nvl_dhcp = NULL;
+ int err;
+
+ if (!nvlist_exists(nvl, IPADM_NVP_DHCP)) {
+ if ((err = nvlist_alloc(&nvl_dhcp, NV_UNIQUE_NAME, 0)) != 0)
+ return (ipadm_errno2status(err));
+ if ((err = nvlist_add_nvlist(nvl, IPADM_NVP_DHCP,
+ nvl_dhcp)) != 0) {
+ nvlist_free(nvl_dhcp);
+ return (ipadm_errno2status(err));
+ }
+ nvlist_free(nvl_dhcp);
+ }
+ if ((err = nvlist_lookup_nvlist(nvl, IPADM_NVP_DHCP, &nvl_dhcp)) != 0 ||
+ (err = nvlist_add_int32(nvl_dhcp, IPADM_NVP_WAIT, wait)) != 0 ||
+ (err = nvlist_add_boolean_value(nvl_dhcp, IPADM_NVP_PRIMARY,
+ primary)) != 0) {
+ return (ipadm_errno2status(err));
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Add (name, value) as an nvpair of type DATA_TYPE_STRING to nvlist.
+ */
+static void
+i_ipadm_str_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+ /* if value is NULL create an empty node */
+ if (value == NULL)
+ (void) nvlist_add_string(nvl, name, "");
+ else
+ (void) nvlist_add_string(nvl, name, value);
+}
+
+/*
+ * `name' = IPADM_NVP_IPV4ADDR and
+ * `value' = <local numeric IP string or hostname,remote numeric IP string or
+ * hostname>
+ * This function will add an nvlist with the hostname information in
+ * nvpairs to the nvlist in `nvl'.
+ */
+static void
+i_ipadm_ip4_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+ char *cp, *hname;
+ struct ipadm_addrobj_s ipaddr;
+
+ assert(strcmp(name, IPADM_NVP_IPV4ADDR) == 0 && value != NULL);
+
+ bzero(&ipaddr, sizeof (ipaddr));
+ ipaddr.ipadm_af = AF_INET;
+
+ hname = value; /* local hostname */
+ cp = strchr(hname, ',');
+ assert(cp != NULL);
+ *cp++ = '\0';
+ (void) strlcpy(ipaddr.ipadm_static_aname, hname,
+ sizeof (ipaddr.ipadm_static_aname));
+
+ if (*cp != '\0') {
+ /* we have a dst hostname */
+ (void) strlcpy(ipaddr.ipadm_static_dname, cp,
+ sizeof (ipaddr.ipadm_static_dname));
+ }
+ (void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr);
+}
+
+/*
+ * `name' = IPADM_NVP_IPV6ADDR and
+ * `value' = <local numeric IP string or hostname,remote numeric IP string or
+ * hostname>
+ * This function will add an nvlist with the hostname information in
+ * nvpairs to the nvlist in `nvl'.
+ */
+static void
+i_ipadm_ip6_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+ char *cp, *hname;
+ struct ipadm_addrobj_s ipaddr;
+
+ assert(strcmp(name, IPADM_NVP_IPV6ADDR) == 0 && value != NULL);
+
+ bzero(&ipaddr, sizeof (ipaddr));
+ ipaddr.ipadm_af = AF_INET6;
+
+ hname = value; /* local hostname */
+ cp = strchr(hname, ',');
+ assert(cp != NULL);
+ *cp++ = '\0';
+ (void) strlcpy(ipaddr.ipadm_static_aname, hname,
+ sizeof (ipaddr.ipadm_static_aname));
+
+ if (*cp != '\0') {
+ /* we have a dst hostname */
+ (void) strlcpy(ipaddr.ipadm_static_dname, cp,
+ sizeof (ipaddr.ipadm_static_dname));
+ }
+ (void) i_ipadm_add_ipaddr2nvl(nvl, &ipaddr);
+}
+
+/*
+ * `name' = IPADM_NVP_INTFID and `value' = <intfid/prefixlen>,{yes,no},{yes|no}
+ * This function will add an nvlist with the address object information in
+ * nvpairs to the nvlist in `nvl'.
+ */
+static void
+i_ipadm_intfid_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+ char *cp;
+ struct ipadm_addrobj_s ipaddr;
+ char *endp;
+ char *prefixlen;
+ char *stateless;
+ char *stateful;
+
+ assert(strcmp(name, IPADM_NVP_INTFID) == 0 && value != NULL);
+
+ bzero(&ipaddr, sizeof (ipaddr));
+
+ cp = strchr(value, '/');
+ assert(cp != NULL);
+
+ *cp++ = '\0';
+ ipaddr.ipadm_intfid.sin6_family = AF_INET6;
+ (void) inet_pton(AF_INET6, value, &ipaddr.ipadm_intfid.sin6_addr);
+
+ prefixlen = cp;
+ cp = strchr(cp, ',');
+ assert(cp != NULL);
+ *cp++ = '\0';
+
+ errno = 0;
+ ipaddr.ipadm_intfidlen = (uint32_t)strtoul(prefixlen, &endp, 10);
+ if (*endp != '\0' || errno != 0)
+ return;
+
+ stateless = cp;
+ stateful = strchr(stateless, ',');
+ assert(stateful != NULL);
+ *stateful++ = '\0';
+ ipaddr.ipadm_stateless = (strcmp(stateless, "yes") == 0);
+ ipaddr.ipadm_stateful = (strcmp(stateful, "yes") == 0);
+
+ /* Add all of it to the given nvlist */
+ (void) i_ipadm_add_intfid2nvl(nvl, &ipaddr);
+}
+
+/*
+ * `name' = IPADM_NVP_DHCP and `value' = <wait_time>,{yes|no}
+ * This function will add an nvlist with the dhcp address object information in
+ * nvpairs to the nvlist in `nvl'.
+ */
+static void
+i_ipadm_dhcp_dbline2nvl(nvlist_t *nvl, char *name, char *value)
+{
+ char *cp;
+ char *endp;
+ long wait_time;
+ boolean_t primary;
+
+ assert(strcmp(name, IPADM_NVP_DHCP) == 0 && value != NULL);
+ cp = strchr(value, ',');
+ assert(cp != NULL);
+ *cp++ = '\0';
+ errno = 0;
+ wait_time = strtol(value, &endp, 10);
+ if (*endp != '\0' || errno != 0)
+ return;
+ primary = (strcmp(cp, "yes") == 0);
+ (void) i_ipadm_add_dhcp2nvl(nvl, primary, (int32_t)wait_time);
+}
+
+/*
+ * Parses the buffer, for name-value pairs and creates nvlist. The value
+ * is always considered to be a string.
+ */
+int
+ipadm_str2nvlist(const char *inbuf, nvlist_t **ipnvl, uint_t flags)
+{
+ char *nv, *name, *val, *buf, *cp, *sep;
+ int err;
+
+ if (inbuf == NULL || inbuf[0] == '\0' || ipnvl == NULL)
+ return (EINVAL);
+ *ipnvl = NULL;
+
+ /*
+ * If IPADM_NORVAL is set, then `inbuf' should be comma delimited values
+ */
+ if ((flags & IPADM_NORVAL) && strchr(inbuf, '=') != NULL)
+ return (EINVAL);
+
+ if ((cp = buf = strdup(inbuf)) == NULL)
+ return (errno);
+
+ while (isspace(*buf))
+ buf++;
+
+ if (*buf == '\0') {
+ err = EINVAL;
+ goto fail;
+ }
+
+ nv = buf;
+ /*
+ * work on one nvpair at a time and extract the name and value
+ */
+ sep = ((flags & IPADM_NORVAL) ? IPADM_NAME_SEP : IPADM_NVPAIR_SEP);
+ while ((nv = strsep(&buf, sep)) != NULL) {
+ if (*nv == '\n')
+ continue;
+ name = nv;
+ if ((val = strchr(nv, '=')) != NULL)
+ *val++ = '\0';
+ if (*ipnvl == NULL &&
+ (err = nvlist_alloc(ipnvl, NV_UNIQUE_NAME, 0)) != 0)
+ goto fail;
+ if (nvlist_exists(*ipnvl, name)) {
+ err = EEXIST;
+ goto fail;
+ }
+ /* Add the extracted nvpair to the nvlist `ipnvl'. */
+ (void) i_ipadm_add_nvpair(*ipnvl, name, val);
+ }
+ free(cp);
+ return (0);
+fail:
+ free(cp);
+ nvlist_free(*ipnvl);
+ *ipnvl = NULL;
+ return (err);
+}
+
+/*
+ * Opens the data store for read/write operation. For write operation we open
+ * another file and scribble the changes to it and copy the new file back to
+ * old file.
+ */
+int
+ipadm_rw_db(db_wfunc_t *db_walk_func, void *arg, const char *db_file,
+ mode_t db_perms, ipadm_db_op_t db_op)
+{
+ FILE *fp, *nfp = NULL;
+ char file[MAXPATHLEN];
+ char newfile[MAXPATHLEN];
+ int nfd;
+ boolean_t writeop;
+ int err = 0;
+
+ writeop = (db_op != IPADM_DB_READ);
+
+ (void) snprintf(file, MAXPATHLEN, "%s/%s", ipadm_rootdir, db_file);
+
+ /* open the data store */
+ if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL)
+ return (errno);
+
+ if (writeop) {
+ (void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
+ ipadm_rootdir, db_file);
+ if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
+ db_perms)) < 0) {
+ err = errno;
+ (void) fclose(fp);
+ return (err);
+ }
+
+ if ((nfp = fdopen(nfd, "w")) == NULL) {
+ err = errno;
+ (void) close(nfd);
+ (void) fclose(fp);
+ (void) unlink(newfile);
+ return (err);
+ }
+ }
+ err = ipadm_process_db_line(db_walk_func, arg, fp, nfp, db_op);
+ if (!writeop)
+ goto done;
+ if (err != 0 && err != ENOENT)
+ goto done;
+
+ if (fflush(nfp) == EOF) {
+ err = errno;
+ goto done;
+ }
+ (void) fclose(fp);
+ (void) fclose(nfp);
+
+ if (rename(newfile, file) < 0) {
+ err = errno;
+ (void) unlink(newfile);
+ }
+ return (err);
+done:
+ if (nfp != NULL) {
+ (void) fclose(nfp);
+ if (err != 0)
+ (void) unlink(newfile);
+ }
+ (void) fclose(fp);
+ return (err);
+}
+
+/*
+ * Processes each line of the configuration file, skipping lines with
+ * leading spaces, blank lines and comments. The line form the DB
+ * is converted to nvlist and the callback function is called to process
+ * the list. The buf could be modified by the callback function and
+ * if this is a write operation and buf is not truncated, buf will
+ * be written to disk.
+ *
+ * Further if cont is set to B_FALSE, the remainder of the file will
+ * continue to be read (however callback function will not be called) and,
+ * if necessary, written to disk as well.
+ */
+static int
+ipadm_process_db_line(db_wfunc_t *db_walk_func, void *arg, FILE *fp, FILE *nfp,
+ ipadm_db_op_t db_op)
+{
+ int err = 0;
+ char buf[MAXLINELEN];
+ boolean_t cont = B_TRUE;
+ int i, len;
+ nvlist_t *db_nvl = NULL;
+ boolean_t line_deleted = B_FALSE;
+
+ while (fgets(buf, MAXLINELEN, fp) != NULL) {
+ /*
+ * Skip leading spaces, blank lines, and comments.
+ */
+ len = strnlen(buf, MAXLINELEN);
+ for (i = 0; i < len; i++) {
+ if (!isspace(buf[i]))
+ break;
+ }
+
+ if (i != len && buf[i] != '#' && cont) {
+ if (ipadm_str2nvlist(buf, &db_nvl, 0) == 0) {
+ cont = db_walk_func(arg, db_nvl, buf,
+ MAXLINELEN, &err);
+ } else {
+ /* Delete corrupted line. */
+ buf[0] = '\0';
+ }
+ nvlist_free(db_nvl);
+ db_nvl = NULL;
+ }
+ if (err != 0)
+ break;
+ if (nfp != NULL && buf[0] == '\0')
+ line_deleted = B_TRUE;
+ if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
+ err = errno;
+ break;
+ }
+ }
+
+ if (err != 0 || !cont)
+ return (err);
+
+ if (db_op == IPADM_DB_WRITE) {
+ ipadm_dbwrite_cbarg_t *cb = arg;
+ nvlist_t *nvl = cb->dbw_nvl;
+
+ /*
+ * If the specified entry is not found above, we add
+ * the entry to the configuration file, here.
+ */
+ (void) memset(buf, 0, MAXLINELEN);
+ if (ipadm_nvlist2str(nvl, buf, MAXLINELEN) == 0)
+ err = ENOBUFS;
+ else if (fputs(buf, nfp) == EOF)
+ err = errno;
+ return (err);
+ }
+
+ if (db_op == IPADM_DB_DELETE && line_deleted)
+ return (0);
+
+ /* if we have come this far, then we didn't find any match */
+ return (ENOENT);
+}
diff --git a/usr/src/lib/libipadm/common/ipadm_prop.c b/usr/src/lib/libipadm/common/ipadm_prop.c
new file mode 100644
index 0000000000..56aba6bb2d
--- /dev/null
+++ b/usr/src/lib/libipadm/common/ipadm_prop.c
@@ -0,0 +1,1699 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file contains routines that are used to modify/retrieve protocol or
+ * interface property values. It also holds all the supported properties for
+ * both IP interface and protocols in `ipadm_prop_desc_t'. Following protocols
+ * are supported: IP, IPv4, IPv6, TCP, SCTP, UDP and ICMP.
+ *
+ * This file also contains walkers, which walks through the property table and
+ * calls the callback function, of the form `ipadm_prop_wfunc_t' , for every
+ * property in the table.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/sockio.h>
+#include <assert.h>
+#include <libdllink.h>
+#include <zone.h>
+#include "libipadm_impl.h"
+
+#define IPADM_NONESTR "none"
+#define DEF_METRIC_VAL 0 /* default metric value */
+
+#define A_CNT(arr) (sizeof (arr) / sizeof (arr[0]))
+
+static ipadm_status_t i_ipadm_validate_if(ipadm_handle_t, const char *,
+ uint_t, uint_t);
+
+/*
+ * Callback functions to retrieve property values from the kernel. These
+ * functions, when required, translate the values from the kernel to a format
+ * suitable for printing. For example: boolean values will be translated
+ * to on/off. They also retrieve DEFAULT, PERM and POSSIBLE values for
+ * a given property.
+ */
+static ipadm_pd_getf_t i_ipadm_get_prop, i_ipadm_get_ifprop_flags,
+ i_ipadm_get_mtu, i_ipadm_get_metric,
+ i_ipadm_get_usesrc, i_ipadm_get_forwarding,
+ i_ipadm_get_ecnsack;
+
+/*
+ * Callback function to set property values. These functions translate the
+ * values to a format suitable for kernel consumption, allocates the necessary
+ * ioctl buffers and then invokes ioctl().
+ */
+static ipadm_pd_setf_t i_ipadm_set_prop, i_ipadm_set_mtu,
+ i_ipadm_set_ifprop_flags,
+ i_ipadm_set_metric, i_ipadm_set_usesrc,
+ i_ipadm_set_forwarding, i_ipadm_set_eprivport,
+ i_ipadm_set_ecnsack;
+
+/* array of protocols we support */
+static int protocols[] = { MOD_PROTO_IP, MOD_PROTO_RAWIP,
+ MOD_PROTO_TCP, MOD_PROTO_UDP,
+ MOD_PROTO_SCTP };
+
+/*
+ * Supported IP protocol properties.
+ */
+static ipadm_prop_desc_t ipadm_ip_prop_table[] = {
+ { "arp", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4,
+ i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
+ i_ipadm_get_ifprop_flags },
+
+ { "forwarding", IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV4,
+ i_ipadm_set_forwarding, i_ipadm_get_onoff,
+ i_ipadm_get_forwarding },
+
+ { "metric", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4,
+ i_ipadm_set_metric, NULL, i_ipadm_get_metric },
+
+ { "mtu", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4,
+ i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu },
+
+ { "exchange_routes", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4,
+ i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
+ i_ipadm_get_ifprop_flags },
+
+ { "usesrc", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4,
+ i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc },
+
+ { "ttl", IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "forwarding", IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV6,
+ i_ipadm_set_forwarding, i_ipadm_get_onoff,
+ i_ipadm_get_forwarding },
+
+ { "hoplimit", IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "metric", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6,
+ i_ipadm_set_metric, NULL, i_ipadm_get_metric },
+
+ { "mtu", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6,
+ i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu },
+
+ { "nud", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6,
+ i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
+ i_ipadm_get_ifprop_flags },
+
+ { "exchange_routes", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6,
+ i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
+ i_ipadm_get_ifprop_flags },
+
+ { "usesrc", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6,
+ i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc },
+
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+/* possible values for TCP properties `ecn' and `sack' */
+static const char *ecn_sack_vals[] = {"never", "passive", "active", NULL};
+
+/* Supported TCP protocol properties */
+static ipadm_prop_desc_t ipadm_tcp_prop_table[] = {
+ { "ecn", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
+
+ { "extra_priv_ports", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_eprivport, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "largest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "sack", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
+
+ { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "smallest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "smallest_nonpriv_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+/* Supported UDP protocol properties */
+static ipadm_prop_desc_t ipadm_udp_prop_table[] = {
+ { "extra_priv_ports", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
+ i_ipadm_set_eprivport, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "largest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "smallest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "smallest_nonpriv_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+/* Supported SCTP protocol properties */
+static ipadm_prop_desc_t ipadm_sctp_prop_table[] = {
+ { "extra_priv_ports", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
+ i_ipadm_set_eprivport, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "largest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "smallest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "smallest_nonpriv_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+/* Supported ICMP protocol properties */
+static ipadm_prop_desc_t ipadm_icmp_prop_table[] = {
+ { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
+
+ { NULL, 0, 0, NULL, NULL, NULL }
+};
+
+/*
+ * A dummy private property structure, used while handling private
+ * protocol properties (properties not yet supported by libipadm).
+ */
+static ipadm_prop_desc_t ipadm_privprop =\
+ { NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_NONE,
+ i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop };
+
+/*
+ * Returns the property description table, for the given protocol
+ */
+static ipadm_prop_desc_t *
+i_ipadm_get_propdesc_table(uint_t proto)
+{
+ switch (proto) {
+ case MOD_PROTO_IP:
+ case MOD_PROTO_IPV4:
+ case MOD_PROTO_IPV6:
+ return (ipadm_ip_prop_table);
+ case MOD_PROTO_RAWIP:
+ return (ipadm_icmp_prop_table);
+ case MOD_PROTO_TCP:
+ return (ipadm_tcp_prop_table);
+ case MOD_PROTO_UDP:
+ return (ipadm_udp_prop_table);
+ case MOD_PROTO_SCTP:
+ return (ipadm_sctp_prop_table);
+ }
+
+ return (NULL);
+}
+
+char *
+ipadm_proto2str(uint_t proto)
+{
+ switch (proto) {
+ case MOD_PROTO_IP:
+ return ("ip");
+ case MOD_PROTO_IPV4:
+ return ("ipv4");
+ case MOD_PROTO_IPV6:
+ return ("ipv6");
+ case MOD_PROTO_RAWIP:
+ return ("icmp");
+ case MOD_PROTO_TCP:
+ return ("tcp");
+ case MOD_PROTO_UDP:
+ return ("udp");
+ case MOD_PROTO_SCTP:
+ return ("sctp");
+ }
+
+ return (NULL);
+}
+
+uint_t
+ipadm_str2proto(const char *protostr)
+{
+ if (protostr == NULL)
+ return (MOD_PROTO_NONE);
+ if (strcmp(protostr, "tcp") == 0)
+ return (MOD_PROTO_TCP);
+ else if (strcmp(protostr, "udp") == 0)
+ return (MOD_PROTO_UDP);
+ else if (strcmp(protostr, "ip") == 0)
+ return (MOD_PROTO_IP);
+ else if (strcmp(protostr, "ipv4") == 0)
+ return (MOD_PROTO_IPV4);
+ else if (strcmp(protostr, "ipv6") == 0)
+ return (MOD_PROTO_IPV6);
+ else if (strcmp(protostr, "icmp") == 0)
+ return (MOD_PROTO_RAWIP);
+ else if (strcmp(protostr, "sctp") == 0)
+ return (MOD_PROTO_SCTP);
+ else if (strcmp(protostr, "arp") == 0)
+ return (MOD_PROTO_IP);
+
+ return (MOD_PROTO_NONE);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_mtu(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ struct lifreq lifr;
+ char *endp;
+ uint_t mtu;
+ int s;
+ const char *ifname = arg;
+ char val[MAXPROPVALLEN];
+
+ /* to reset MTU first retrieve the default MTU and then set it */
+ if (flags & IPADM_OPT_DEFAULT) {
+ ipadm_status_t status;
+ uint_t size = MAXPROPVALLEN;
+
+ status = i_ipadm_get_prop(iph, arg, pdp, val, &size,
+ proto, MOD_PROP_DEFAULT);
+ if (status != IPADM_SUCCESS)
+ return (status);
+ pval = val;
+ }
+
+ errno = 0;
+ mtu = (uint_t)strtol(pval, &endp, 10);
+ if (errno != 0 || *endp != '\0')
+ return (IPADM_INVALID_ARG);
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ lifr.lifr_mtu = mtu;
+
+ s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
+ if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ return (IPADM_SUCCESS);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_metric(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ struct lifreq lifr;
+ char *endp;
+ int metric;
+ const char *ifname = arg;
+ int s;
+
+ /* if we are resetting, set the value to its default value */
+ if (flags & IPADM_OPT_DEFAULT) {
+ metric = DEF_METRIC_VAL;
+ } else {
+ errno = 0;
+ metric = (uint_t)strtol(pval, &endp, 10);
+ if (errno != 0 || *endp != '\0')
+ return (IPADM_INVALID_ARG);
+ }
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ lifr.lifr_metric = metric;
+
+ s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
+
+ if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ return (IPADM_SUCCESS);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_usesrc(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ struct lifreq lifr;
+ const char *ifname = arg;
+ int s;
+ uint_t ifindex = 0;
+
+ /* if we are resetting, set the value to its default value */
+ if (flags & IPADM_OPT_DEFAULT)
+ pval = IPADM_NONESTR;
+
+ /*
+ * cannot specify logical interface name. We can also filter out other
+ * bogus interface names here itself through i_ipadm_validate_ifname().
+ */
+ if (strcmp(pval, IPADM_NONESTR) != 0 &&
+ !i_ipadm_validate_ifname(iph, pval))
+ return (IPADM_INVALID_ARG);
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+
+ s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
+
+ if (strcmp(pval, IPADM_NONESTR) != 0) {
+ if ((ifindex = if_nametoindex(pval)) == 0)
+ return (ipadm_errno2status(errno));
+ lifr.lifr_index = ifindex;
+ } else {
+ if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ lifr.lifr_index = 0;
+ }
+ if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+
+ return (IPADM_SUCCESS);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_ifprop_flags(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ const char *ifname = arg;
+ uint64_t on_flags = 0, off_flags = 0;
+ boolean_t on = B_FALSE;
+ sa_family_t af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
+
+ /* if we are resetting, set the value to its default value */
+ if (flags & IPADM_OPT_DEFAULT) {
+ if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
+ strcmp(pdp->ipd_name, "arp") == 0 ||
+ strcmp(pdp->ipd_name, "nud") == 0) {
+ pval = IPADM_ONSTR;
+ } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
+ pval = IPADM_OFFSTR;
+ } else {
+ return (IPADM_PROP_UNKNOWN);
+ }
+ }
+
+ if (strcmp(pval, IPADM_ONSTR) == 0)
+ on = B_TRUE;
+ else if (strcmp(pval, IPADM_OFFSTR) == 0)
+ on = B_FALSE;
+ else
+ return (IPADM_INVALID_ARG);
+
+ if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
+ if (on)
+ off_flags = IFF_NORTEXCH;
+ else
+ on_flags = IFF_NORTEXCH;
+ } else if (strcmp(pdp->ipd_name, "arp") == 0) {
+ if (on)
+ off_flags = IFF_NOARP;
+ else
+ on_flags = IFF_NOARP;
+ } else if (strcmp(pdp->ipd_name, "nud") == 0) {
+ if (on)
+ off_flags = IFF_NONUD;
+ else
+ on_flags = IFF_NONUD;
+ } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
+ if (on)
+ on_flags = IFF_ROUTER;
+ else
+ off_flags = IFF_ROUTER;
+ }
+
+ if (on_flags || off_flags) {
+ status = i_ipadm_set_flags(iph, ifname, af, on_flags,
+ off_flags);
+ }
+ return (status);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_eprivport(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ nvlist_t *portsnvl = NULL;
+ nvpair_t *nvp;
+ ipadm_status_t status = IPADM_SUCCESS;
+ int err;
+ char *port;
+ uint_t count = 0;
+
+ if (flags & IPADM_OPT_DEFAULT) {
+ assert(pval == NULL);
+ return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
+ }
+
+ if ((err = ipadm_str2nvlist(pval, &portsnvl, IPADM_NORVAL)) != 0)
+ return (ipadm_errno2status(err));
+
+ /* count the number of ports */
+ for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(portsnvl, nvp)) {
+ ++count;
+ }
+
+ /* We allow only one port to be added or removed, at a time */
+ if (count > 1 && (flags & (IPADM_OPT_APPEND|IPADM_OPT_REMOVE)))
+ return (IPADM_INVALID_ARG);
+
+ /*
+ * However on reboot, while initializing protocol properties,
+ * extra_priv_ports might have multiple values. Only in that case
+ * we allow setting multiple properties.
+ */
+ if (count > 1 && !(iph->iph_flags & IPH_INIT))
+ return (IPADM_INVALID_ARG);
+
+ count = 0;
+ for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(portsnvl, nvp)) {
+ port = nvpair_name(nvp);
+ if (count == 0) {
+ status = i_ipadm_set_prop(iph, arg, pdp, port, proto,
+ flags);
+ } else {
+ assert(iph->iph_flags & IPH_INIT);
+ status = i_ipadm_set_prop(iph, arg, pdp, port, proto,
+ IPADM_OPT_APPEND);
+ }
+ ++count;
+ if (status != IPADM_SUCCESS)
+ break;
+ }
+ret:
+ nvlist_free(portsnvl);
+ return (status);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_forwarding(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ const char *ifname = arg;
+ ipadm_status_t status;
+
+ /*
+ * if interface name is provided, then set forwarding using the
+ * IFF_ROUTER flag
+ */
+ if (ifname != NULL) {
+ status = i_ipadm_set_ifprop_flags(iph, ifname, pdp, pval,
+ proto, flags);
+ } else {
+ char *val = NULL;
+
+ /*
+ * if the caller is IPH_LEGACY, `pval' already contains
+ * numeric values.
+ */
+ if (!(flags & IPADM_OPT_DEFAULT) &&
+ !(iph->iph_flags & IPH_LEGACY)) {
+
+ if (strcmp(pval, IPADM_ONSTR) == 0)
+ val = "1";
+ else if (strcmp(pval, IPADM_OFFSTR) == 0)
+ val = "0";
+ else
+ return (IPADM_INVALID_ARG);
+ pval = val;
+ }
+
+ status = i_ipadm_set_prop(iph, ifname, pdp, pval, proto, flags);
+ }
+
+ return (status);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_set_ecnsack(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ uint_t i;
+ char val[MAXPROPVALLEN];
+
+ /* if IPH_LEGACY is set, `pval' already contains numeric values */
+ if (!(flags & IPADM_OPT_DEFAULT) && !(iph->iph_flags & IPH_LEGACY)) {
+ for (i = 0; ecn_sack_vals[i] != NULL; i++) {
+ if (strcmp(pval, ecn_sack_vals[i]) == 0)
+ break;
+ }
+ if (ecn_sack_vals[i] == NULL)
+ return (IPADM_INVALID_ARG);
+ (void) snprintf(val, MAXPROPVALLEN, "%d", i);
+ pval = val;
+ }
+
+ return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
+}
+
+/* ARGSUSED */
+ipadm_status_t
+i_ipadm_get_ecnsack(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ uint_t i, nbytes = 0;
+
+ switch (valtype) {
+ case MOD_PROP_POSSIBLE:
+ for (i = 0; ecn_sack_vals[i] != NULL; i++) {
+ if (i == 0)
+ nbytes += snprintf(buf + nbytes,
+ *bufsize - nbytes, "%s", ecn_sack_vals[i]);
+ else
+ nbytes += snprintf(buf + nbytes,
+ *bufsize - nbytes, ",%s", ecn_sack_vals[i]);
+ if (nbytes >= *bufsize)
+ break;
+ }
+ break;
+ case MOD_PROP_PERM:
+ case MOD_PROP_DEFAULT:
+ case MOD_PROP_ACTIVE:
+ status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto,
+ valtype);
+
+ /*
+ * If IPH_LEGACY is set, do not convert the value returned
+ * from kernel,
+ */
+ if (iph->iph_flags & IPH_LEGACY)
+ break;
+
+ /*
+ * For current and default value, convert the value returned
+ * from kernel to more discrete representation.
+ */
+ if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
+ valtype == MOD_PROP_DEFAULT)) {
+ i = atoi(buf);
+ assert(i < 3);
+ nbytes = snprintf(buf, *bufsize, "%s",
+ ecn_sack_vals[i]);
+ }
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+
+ return (status);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_forwarding(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ const char *ifname = arg;
+ ipadm_status_t status = IPADM_SUCCESS;
+
+ /*
+ * if interface name is provided, then get forwarding status using
+ * SIOCGLIFFLAGS
+ */
+ if (ifname != NULL) {
+ status = i_ipadm_get_ifprop_flags(iph, ifname, pdp,
+ buf, bufsize, pdp->ipd_proto, valtype);
+ } else {
+ status = i_ipadm_get_prop(iph, ifname, pdp, buf,
+ bufsize, proto, valtype);
+ /*
+ * If IPH_LEGACY is set, do not convert the value returned
+ * from kernel,
+ */
+ if (iph->iph_flags & IPH_LEGACY)
+ goto ret;
+ if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
+ valtype == MOD_PROP_DEFAULT)) {
+ uint_t val = atoi(buf);
+
+ (void) snprintf(buf, *bufsize,
+ (val == 1 ? IPADM_ONSTR : IPADM_OFFSTR));
+ }
+ }
+
+ret:
+ return (status);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_mtu(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ struct lifreq lifr;
+ const char *ifname = arg;
+ size_t nbytes;
+ int s;
+
+ switch (valtype) {
+ case MOD_PROP_PERM:
+ nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
+ break;
+ case MOD_PROP_DEFAULT:
+ case MOD_PROP_POSSIBLE:
+ return (i_ipadm_get_prop(iph, arg, pdp, buf, bufsize,
+ proto, valtype));
+ case MOD_PROP_ACTIVE:
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
+
+ if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ nbytes = snprintf(buf, *bufsize, "%u", lifr.lifr_mtu);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+ return (IPADM_SUCCESS);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_metric(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ struct lifreq lifr;
+ const char *ifname = arg;
+ size_t nbytes;
+ int s, val;
+
+ switch (valtype) {
+ case MOD_PROP_PERM:
+ val = MOD_PROP_PERM_RW;
+ break;
+ case MOD_PROP_DEFAULT:
+ val = DEF_METRIC_VAL;
+ break;
+ case MOD_PROP_ACTIVE:
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+
+ s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
+ if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ val = lifr.lifr_metric;
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ nbytes = snprintf(buf, *bufsize, "%d", val);
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+
+ return (IPADM_SUCCESS);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_usesrc(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *ipd, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ struct lifreq lifr;
+ const char *ifname = arg;
+ int s;
+ char if_name[IF_NAMESIZE];
+ size_t nbytes;
+
+ switch (valtype) {
+ case MOD_PROP_PERM:
+ nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
+ break;
+ case MOD_PROP_DEFAULT:
+ nbytes = snprintf(buf, *bufsize, "%s", IPADM_NONESTR);
+ break;
+ case MOD_PROP_ACTIVE:
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+
+ s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
+ if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ if (lifr.lifr_index == 0) {
+ /* no src address was set, so print 'none' */
+ (void) strlcpy(if_name, IPADM_NONESTR,
+ sizeof (if_name));
+ } else if (if_indextoname(lifr.lifr_index, if_name) == NULL) {
+ return (ipadm_errno2status(errno));
+ }
+ nbytes = snprintf(buf, *bufsize, "%s", if_name);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ return (IPADM_NO_BUFS);
+ }
+ return (IPADM_SUCCESS);
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_ifprop_flags(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ uint64_t intf_flags;
+ char *val;
+ size_t nbytes;
+ const char *ifname = arg;
+ sa_family_t af;
+ ipadm_status_t status = IPADM_SUCCESS;
+
+ switch (valtype) {
+ case MOD_PROP_PERM:
+ nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
+ break;
+ case MOD_PROP_DEFAULT:
+ if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
+ strcmp(pdp->ipd_name, "arp") == 0 ||
+ strcmp(pdp->ipd_name, "nud") == 0) {
+ val = IPADM_ONSTR;
+ } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
+ val = IPADM_OFFSTR;
+ } else {
+ return (IPADM_PROP_UNKNOWN);
+ }
+ nbytes = snprintf(buf, *bufsize, "%s", val);
+ break;
+ case MOD_PROP_ACTIVE:
+ af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
+ status = i_ipadm_get_flags(iph, ifname, af, &intf_flags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ val = IPADM_OFFSTR;
+ if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
+ if (!(intf_flags & IFF_NORTEXCH))
+ val = IPADM_ONSTR;
+ } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
+ if (intf_flags & IFF_ROUTER)
+ val = IPADM_ONSTR;
+ } else if (strcmp(pdp->ipd_name, "arp") == 0) {
+ if (!(intf_flags & IFF_NOARP))
+ val = IPADM_ONSTR;
+ } else if (strcmp(pdp->ipd_name, "nud") == 0) {
+ if (!(intf_flags & IFF_NONUD))
+ val = IPADM_ONSTR;
+ }
+ nbytes = snprintf(buf, *bufsize, "%s", val);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+ if (nbytes >= *bufsize) {
+ /* insufficient buffer space */
+ *bufsize = nbytes + 1;
+ status = IPADM_NO_BUFS;
+ }
+
+ return (status);
+}
+
+static void
+i_ipadm_perm2str(char *buf, uint_t *bufsize)
+{
+ uint_t perm = atoi(buf);
+
+ (void) snprintf(buf, *bufsize, "%c%c",
+ ((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-',
+ ((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-');
+}
+
+/* ARGSUSED */
+static ipadm_status_t
+i_ipadm_get_prop(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ const char *ifname = arg;
+ mod_ioc_prop_t *mip;
+ char *pname = pdp->ipd_name;
+ uint_t iocsize;
+
+ /* allocate sufficient ioctl buffer to retrieve value */
+ iocsize = sizeof (mod_ioc_prop_t) + *bufsize - 1;
+ if ((mip = calloc(1, iocsize)) == NULL)
+ return (IPADM_NO_BUFS);
+
+ mip->mpr_version = MOD_PROP_VERSION;
+ mip->mpr_flags = valtype;
+ mip->mpr_proto = proto;
+ if (ifname != NULL) {
+ (void) strlcpy(mip->mpr_ifname, ifname,
+ sizeof (mip->mpr_ifname));
+ }
+ (void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
+ mip->mpr_valsize = *bufsize;
+
+ if (i_ipadm_strioctl(iph->iph_sock, SIOCGETPROP, (char *)mip,
+ iocsize) < 0) {
+ if (errno == ENOENT)
+ status = IPADM_PROP_UNKNOWN;
+ else
+ status = ipadm_errno2status(errno);
+ } else {
+ bcopy(mip->mpr_val, buf, *bufsize);
+ }
+
+ free(mip);
+ return (status);
+}
+
+/*
+ * populates the ipmgmt_prop_arg_t based on the class of property.
+ */
+static void
+i_ipadm_populate_proparg(ipmgmt_prop_arg_t *pargp, ipadm_prop_desc_t *pdp,
+ const char *pval, const void *object)
+{
+ const struct ipadm_addrobj_s *ipaddr;
+ uint_t class = pdp->ipd_class;
+ uint_t proto = pdp->ipd_proto;
+
+ (void) strlcpy(pargp->ia_pname, pdp->ipd_name,
+ sizeof (pargp->ia_pname));
+ if (pval != NULL)
+ (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
+
+ switch (class) {
+ case IPADMPROP_CLASS_MODULE:
+ (void) strlcpy(pargp->ia_module, object,
+ sizeof (pargp->ia_module));
+ break;
+ case IPADMPROP_CLASS_MODIF:
+ /* check if object is protostr or an ifname */
+ if (ipadm_str2proto(object) != MOD_PROTO_NONE) {
+ (void) strlcpy(pargp->ia_module, object,
+ sizeof (pargp->ia_module));
+ break;
+ }
+ /* it's an interface property, fall through */
+ /* FALLTHRU */
+ case IPADMPROP_CLASS_IF:
+ (void) strlcpy(pargp->ia_ifname, object,
+ sizeof (pargp->ia_ifname));
+ (void) strlcpy(pargp->ia_module, ipadm_proto2str(proto),
+ sizeof (pargp->ia_module));
+ break;
+ case IPADMPROP_CLASS_ADDR:
+ ipaddr = object;
+ (void) strlcpy(pargp->ia_ifname, ipaddr->ipadm_ifname,
+ sizeof (pargp->ia_ifname));
+ (void) strlcpy(pargp->ia_aobjname, ipaddr->ipadm_aobjname,
+ sizeof (pargp->ia_aobjname));
+ break;
+ }
+}
+
+/*
+ * Common function to retrieve property value for a given interface `ifname' or
+ * for a given protocol `proto'. The property name is in `pname'.
+ *
+ * `valtype' determines the type of value that will be retrieved.
+ * IPADM_OPT_ACTIVE - current value of the property (active config)
+ * IPADM_OPT_PERSIST - value of the property from persistent store
+ * IPADM_OPT_DEFAULT - default hard coded value (boot-time value)
+ * IPADM_OPT_PERM - read/write permissions for the value
+ * IPADM_OPT_POSSIBLE - range of values
+ */
+static ipadm_status_t
+i_ipadm_getprop_common(ipadm_handle_t iph, const char *ifname,
+ const char *pname, char *buf, uint_t *bufsize, uint_t proto,
+ uint_t valtype)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ ipadm_prop_desc_t *pdp, *pdtbl;
+ char priv_propname[MAXPROPNAMELEN];
+ boolean_t matched_name = B_FALSE;
+ boolean_t is_if = (ifname != NULL);
+
+ pdtbl = i_ipadm_get_propdesc_table(proto);
+
+ /*
+ * We already checked for supported protocol,
+ * pdtbl better not be NULL.
+ */
+ assert(pdtbl != NULL);
+
+ for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
+ if (strcmp(pname, pdp->ipd_name) == 0) {
+ matched_name = B_TRUE;
+ if (proto == pdp->ipd_proto)
+ break;
+ }
+ }
+
+ if (pdp->ipd_name != NULL) {
+ /*
+ * check whether the property can be
+ * applied on an interface
+ */
+ if (is_if && !(pdp->ipd_class & IPADMPROP_CLASS_IF))
+ return (IPADM_INVALID_ARG);
+ /*
+ * check whether the property can be
+ * applied on a module
+ */
+ if (!is_if && !(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
+ return (IPADM_INVALID_ARG);
+
+ } else {
+ /*
+ * if we matched name, but failed protocol check,
+ * then return error
+ */
+ if (matched_name)
+ return (IPADM_INVALID_ARG);
+
+ /* there are no private interface properties */
+ if (is_if)
+ return (IPADM_PROP_UNKNOWN);
+
+ /* private protocol properties, pass it to kernel directly */
+ pdp = &ipadm_privprop;
+ (void) strlcpy(priv_propname, pname, sizeof (priv_propname));
+ pdp->ipd_name = priv_propname;
+ }
+
+ switch (valtype) {
+ case IPADM_OPT_PERM:
+ status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
+ MOD_PROP_PERM);
+ if (status == IPADM_SUCCESS)
+ i_ipadm_perm2str(buf, bufsize);
+ break;
+ case IPADM_OPT_ACTIVE:
+ status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
+ MOD_PROP_ACTIVE);
+ break;
+ case IPADM_OPT_DEFAULT:
+ status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
+ MOD_PROP_DEFAULT);
+ break;
+ case IPADM_OPT_POSSIBLE:
+ if (pdp->ipd_get_range != NULL) {
+ status = pdp->ipd_get_range(iph, ifname, pdp, buf,
+ bufsize, proto, MOD_PROP_POSSIBLE);
+ break;
+ }
+ buf[0] = '\0';
+ break;
+ case IPADM_OPT_PERSIST:
+ /* retrieve from database */
+ if (is_if)
+ status = i_ipadm_get_persist_propval(iph, pdp, buf,
+ bufsize, ifname);
+ else
+ status = i_ipadm_get_persist_propval(iph, pdp, buf,
+ bufsize, ipadm_proto2str(proto));
+ break;
+ default:
+ status = IPADM_INVALID_ARG;
+ break;
+ }
+ return (status);
+}
+
+/*
+ * Get protocol property of the specified protocol.
+ */
+ipadm_status_t
+ipadm_get_prop(ipadm_handle_t iph, const char *pname, char *buf,
+ uint_t *bufsize, uint_t proto, uint_t valtype)
+{
+ /*
+ * validate the arguments of the function.
+ */
+ if (iph == NULL || pname == NULL || buf == NULL ||
+ bufsize == NULL || *bufsize == 0) {
+ return (IPADM_INVALID_ARG);
+ }
+ /*
+ * Do we support this proto, if not return error.
+ */
+ if (ipadm_proto2str(proto) == NULL)
+ return (IPADM_NOTSUP);
+
+ return (i_ipadm_getprop_common(iph, NULL, pname, buf, bufsize,
+ proto, valtype));
+}
+
+/*
+ * Get interface property of the specified interface.
+ */
+ipadm_status_t
+ipadm_get_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
+ char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
+{
+ /* validate the arguments of the function. */
+ if (iph == NULL || pname == NULL || buf == NULL ||
+ bufsize == NULL || *bufsize == 0) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /* Do we support this proto, if not return error. */
+ if (ipadm_proto2str(proto) == NULL)
+ return (IPADM_NOTSUP);
+
+ /*
+ * check if interface name is provided for interface property and
+ * is valid.
+ */
+ if (!i_ipadm_validate_ifname(iph, ifname))
+ return (IPADM_INVALID_ARG);
+
+ return (i_ipadm_getprop_common(iph, ifname, pname, buf, bufsize,
+ proto, valtype));
+}
+
+/*
+ * Allocates sufficient ioctl buffers and copies property name and the
+ * value, among other things. If the flag IPADM_OPT_DEFAULT is set, then
+ * `pval' will be NULL and it instructs the kernel to reset the current
+ * value to property's default value.
+ */
+static ipadm_status_t
+i_ipadm_set_prop(ipadm_handle_t iph, const void *arg,
+ ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ const char *ifname = arg;
+ mod_ioc_prop_t *mip;
+ char *pname = pdp->ipd_name;
+ uint_t valsize, iocsize;
+ uint_t iocflags = 0;
+
+ if (flags & IPADM_OPT_DEFAULT) {
+ iocflags |= MOD_PROP_DEFAULT;
+ } else if (flags & IPADM_OPT_ACTIVE) {
+ iocflags |= MOD_PROP_ACTIVE;
+ if (flags & IPADM_OPT_APPEND)
+ iocflags |= MOD_PROP_APPEND;
+ else if (flags & IPADM_OPT_REMOVE)
+ iocflags |= MOD_PROP_REMOVE;
+ }
+
+ if (pval != NULL) {
+ valsize = strlen(pval);
+ iocsize = sizeof (mod_ioc_prop_t) + valsize - 1;
+ } else {
+ valsize = 0;
+ iocsize = sizeof (mod_ioc_prop_t);
+ }
+
+ if ((mip = calloc(1, iocsize)) == NULL)
+ return (IPADM_NO_BUFS);
+
+ mip->mpr_version = MOD_PROP_VERSION;
+ mip->mpr_flags = iocflags;
+ mip->mpr_proto = proto;
+ if (ifname != NULL) {
+ (void) strlcpy(mip->mpr_ifname, ifname,
+ sizeof (mip->mpr_ifname));
+ }
+
+ (void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
+ mip->mpr_valsize = valsize;
+ if (pval != NULL)
+ bcopy(pval, mip->mpr_val, valsize);
+
+ if (i_ipadm_strioctl(iph->iph_sock, SIOCSETPROP, (char *)mip,
+ iocsize) < 0) {
+ if (errno == ENOENT)
+ status = IPADM_PROP_UNKNOWN;
+ else
+ status = ipadm_errno2status(errno);
+ }
+ free(mip);
+ return (status);
+}
+
+/*
+ * Common function for modifying both protocol/interface property.
+ *
+ * If:
+ * IPADM_OPT_PERSIST is set then the value is persisted.
+ * IPADM_OPT_DEFAULT is set then the default value for the property will
+ * be applied.
+ */
+static ipadm_status_t
+i_ipadm_setprop_common(ipadm_handle_t iph, const char *ifname,
+ const char *pname, const char *buf, uint_t proto, uint_t pflags)
+{
+ ipadm_status_t status = IPADM_SUCCESS;
+ boolean_t persist = (pflags & IPADM_OPT_PERSIST);
+ boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
+ ipadm_prop_desc_t *pdp, *pdtbl;
+ boolean_t is_if = (ifname != NULL);
+ char priv_propname[MAXPROPNAMELEN];
+ boolean_t matched_name = B_FALSE;
+
+ /* Check that property value is within the allowed size */
+ if (!reset && strnlen(buf, MAXPROPVALLEN) >= MAXPROPVALLEN)
+ return (IPADM_INVALID_ARG);
+
+ pdtbl = i_ipadm_get_propdesc_table(proto);
+ /*
+ * We already checked for supported protocol,
+ * pdtbl better not be NULL.
+ */
+ assert(pdtbl != NULL);
+
+ /* Walk through the property table to match the given property name */
+ for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
+ /*
+ * we find the entry which matches <pname, proto> tuple
+ */
+ if (strcmp(pname, pdp->ipd_name) == 0) {
+ matched_name = B_TRUE;
+ if (pdp->ipd_proto == proto)
+ break;
+ }
+ }
+
+ if (pdp->ipd_name != NULL) {
+ /* do some sanity checks */
+ if (is_if) {
+ if (!(pdp->ipd_class & IPADMPROP_CLASS_IF))
+ return (IPADM_INVALID_ARG);
+ } else {
+ if (!(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
+ return (IPADM_INVALID_ARG);
+ }
+ } else {
+ /*
+ * if we matched name, but failed protocol check,
+ * then return error.
+ */
+ if (matched_name)
+ return (IPADM_BAD_PROTOCOL);
+
+ /* Possibly a private property, pass it to kernel directly */
+
+ /* there are no private interface properties */
+ if (is_if)
+ return (IPADM_PROP_UNKNOWN);
+
+ pdp = &ipadm_privprop;
+ (void) strlcpy(priv_propname, pname, sizeof (priv_propname));
+ pdp->ipd_name = priv_propname;
+ }
+
+ status = pdp->ipd_set(iph, ifname, pdp, buf, proto, pflags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ if (persist) {
+ if (is_if)
+ status = i_ipadm_persist_propval(iph, pdp, buf, ifname,
+ pflags);
+ else
+ status = i_ipadm_persist_propval(iph, pdp, buf,
+ ipadm_proto2str(proto), pflags);
+ }
+ return (status);
+}
+
+/*
+ * Sets the property value of the specified interface
+ */
+ipadm_status_t
+ipadm_set_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
+ const char *buf, uint_t proto, uint_t pflags)
+{
+ boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
+ ipadm_status_t status;
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+ /*
+ * validate the arguments of the function.
+ */
+ if (iph == NULL || pname == NULL || (!reset && buf == NULL) ||
+ pflags == 0 || pflags == IPADM_OPT_PERSIST ||
+ (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT))) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /*
+ * Do we support this protocol, if not return error.
+ */
+ if (ipadm_proto2str(proto) == NULL)
+ return (IPADM_NOTSUP);
+
+ /*
+ * Validate the interface and check if a persistent
+ * operation is performed on a temporary object.
+ */
+ status = i_ipadm_validate_if(iph, ifname, proto, pflags);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ return (i_ipadm_setprop_common(iph, ifname, pname, buf, proto,
+ pflags));
+}
+
+/*
+ * Sets the property value of the specified protocol.
+ */
+ipadm_status_t
+ipadm_set_prop(ipadm_handle_t iph, const char *pname, const char *buf,
+ uint_t proto, uint_t pflags)
+{
+ boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+ /*
+ * validate the arguments of the function.
+ */
+ if (iph == NULL || pname == NULL ||(!reset && buf == NULL) ||
+ pflags == 0 || pflags == IPADM_OPT_PERSIST ||
+ (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT|
+ IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
+ return (IPADM_INVALID_ARG);
+ }
+
+ /*
+ * Do we support this proto, if not return error.
+ */
+ if (ipadm_proto2str(proto) == NULL)
+ return (IPADM_NOTSUP);
+
+ return (i_ipadm_setprop_common(iph, NULL, pname, buf, proto,
+ pflags));
+}
+
+/* helper function for ipadm_walk_proptbl */
+static void
+i_ipadm_walk_proptbl(ipadm_prop_desc_t *pdtbl, uint_t proto, uint_t class,
+ ipadm_prop_wfunc_t *func, void *arg)
+{
+ ipadm_prop_desc_t *pdp;
+
+ for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
+ if (!(pdp->ipd_class & class))
+ continue;
+
+ if (proto != MOD_PROTO_NONE && !(pdp->ipd_proto & proto))
+ continue;
+
+ /*
+ * we found a class specific match, call the
+ * user callback function.
+ */
+ if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
+ break;
+ }
+}
+
+/*
+ * Walks through all the properties, for a given protocol and property class
+ * (protocol or interface).
+ *
+ * Further if proto == MOD_PROTO_NONE, then it walks through all the supported
+ * protocol property tables.
+ */
+ipadm_status_t
+ipadm_walk_proptbl(uint_t proto, uint_t class, ipadm_prop_wfunc_t *func,
+ void *arg)
+{
+ ipadm_prop_desc_t *pdtbl;
+ ipadm_status_t status = IPADM_SUCCESS;
+ int i;
+ int count = A_CNT(protocols);
+
+ if (func == NULL)
+ return (IPADM_INVALID_ARG);
+
+ switch (class) {
+ case IPADMPROP_CLASS_ADDR:
+ pdtbl = ipadm_addrprop_table;
+ break;
+ case IPADMPROP_CLASS_IF:
+ case IPADMPROP_CLASS_MODULE:
+ pdtbl = i_ipadm_get_propdesc_table(proto);
+ if (pdtbl == NULL && proto != MOD_PROTO_NONE)
+ return (IPADM_INVALID_ARG);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+
+ if (pdtbl != NULL) {
+ /*
+ * proto will be MOD_PROTO_NONE in the case of
+ * IPADMPROP_CLASS_ADDR.
+ */
+ i_ipadm_walk_proptbl(pdtbl, proto, class, func, arg);
+ } else {
+ /* Walk thru all the protocol tables, we support */
+ for (i = 0; i < count; i++) {
+ pdtbl = i_ipadm_get_propdesc_table(protocols[i]);
+ i_ipadm_walk_proptbl(pdtbl, protocols[i], class, func,
+ arg);
+ }
+ }
+ return (status);
+}
+
+/*
+ * Given a property name, walks through all the instances of a property name.
+ * Some properties have two instances one for v4 interfaces and another for v6
+ * interfaces. For example: MTU. MTU can have different values for v4 and v6.
+ * Therefore there are two properties for 'MTU'.
+ *
+ * This function invokes `func' for every instance of property `pname'
+ */
+ipadm_status_t
+ipadm_walk_prop(const char *pname, uint_t proto, uint_t class,
+ ipadm_prop_wfunc_t *func, void *arg)
+{
+ ipadm_prop_desc_t *pdtbl, *pdp;
+ ipadm_status_t status = IPADM_SUCCESS;
+ boolean_t matched = B_FALSE;
+
+ if (pname == NULL || func == NULL)
+ return (IPADM_INVALID_ARG);
+
+ switch (class) {
+ case IPADMPROP_CLASS_ADDR:
+ pdtbl = ipadm_addrprop_table;
+ break;
+ case IPADMPROP_CLASS_IF:
+ case IPADMPROP_CLASS_MODULE:
+ pdtbl = i_ipadm_get_propdesc_table(proto);
+ break;
+ default:
+ return (IPADM_INVALID_ARG);
+ }
+
+ if (pdtbl == NULL)
+ return (IPADM_INVALID_ARG);
+
+ for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
+ if (strcmp(pname, pdp->ipd_name) != 0)
+ continue;
+ if (!(pdp->ipd_proto & proto))
+ continue;
+ matched = B_TRUE;
+ /* we found a match, call the callback function */
+ if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
+ break;
+ }
+ if (!matched)
+ status = IPADM_PROP_UNKNOWN;
+ return (status);
+}
+
+/* ARGSUSED */
+ipadm_status_t
+i_ipadm_get_onoff(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *dp,
+ char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
+{
+ (void) snprintf(buf, *bufsize, "%s,%s", IPADM_ONSTR, IPADM_OFFSTR);
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Makes a door call to ipmgmtd to retrieve the persisted property value
+ */
+ipadm_status_t
+i_ipadm_get_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
+ char *gbuf, uint_t *gbufsize, const void *object)
+{
+ ipmgmt_prop_arg_t parg;
+ ipmgmt_getprop_rval_t rval, *rvalp;
+ size_t nbytes;
+ int err = 0;
+
+ bzero(&parg, sizeof (parg));
+ parg.ia_cmd = IPMGMT_CMD_GETPROP;
+ i_ipadm_populate_proparg(&parg, pdp, NULL, object);
+
+ rvalp = &rval;
+ err = ipadm_door_call(iph, &parg, sizeof (parg), (void **)&rvalp,
+ sizeof (rval), B_FALSE);
+ if (err == 0) {
+ /* assert that rvalp was not reallocated */
+ assert(rvalp == &rval);
+
+ /* `ir_pval' contains the property value */
+ nbytes = snprintf(gbuf, *gbufsize, "%s", rvalp->ir_pval);
+ if (nbytes >= *gbufsize) {
+ /* insufficient buffer space */
+ *gbufsize = nbytes + 1;
+ err = ENOBUFS;
+ }
+ }
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Persists the property value for a given property in the data store
+ */
+ipadm_status_t
+i_ipadm_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
+ const char *pval, const void *object, uint_t flags)
+{
+ ipmgmt_prop_arg_t parg;
+ int err = 0;
+
+ bzero(&parg, sizeof (parg));
+ i_ipadm_populate_proparg(&parg, pdp, pval, object);
+
+ /*
+ * Check if value to be persisted need to be appended or removed. This
+ * is required for multi-valued property.
+ */
+ if (flags & IPADM_OPT_APPEND)
+ parg.ia_flags |= IPMGMT_APPEND;
+ if (flags & IPADM_OPT_REMOVE)
+ parg.ia_flags |= IPMGMT_REMOVE;
+
+ if (flags & (IPADM_OPT_DEFAULT|IPADM_OPT_REMOVE))
+ parg.ia_cmd = IPMGMT_CMD_RESETPROP;
+ else
+ parg.ia_cmd = IPMGMT_CMD_SETPROP;
+
+ err = ipadm_door_call(iph, &parg, sizeof (parg), NULL, 0, B_FALSE);
+
+ /*
+ * its fine if there were no entry in the DB to delete. The user
+ * might be changing property value, which was not changed
+ * persistently.
+ */
+ if (err == ENOENT)
+ err = 0;
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * Called during boot.
+ *
+ * Walk through the DB and apply all the global module properties. We plow
+ * through the DB even if we fail to apply property.
+ */
+/* ARGSUSED */
+boolean_t
+ipadm_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
+ int *errp)
+{
+ ipadm_handle_t iph = cbarg;
+ nvpair_t *nvp, *pnvp;
+ char *strval = NULL, *name, *mod = NULL;
+ uint_t proto;
+
+ /*
+ * We could have used nvl_exists() directly, however we need several
+ * calls to it and each call traverses the list. Since this codepath
+ * is exercised during boot, let's traverse the list ourselves and do
+ * the necessary checks.
+ */
+ for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(db_nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (IPADM_PRIV_NVP(name)) {
+ if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
+ strcmp(name, IPADM_NVP_AOBJNAME) == 0)
+ return (B_TRUE);
+ else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
+ nvpair_value_string(nvp, &mod) != 0)
+ return (B_TRUE);
+ } else {
+ /* possible a property */
+ pnvp = nvp;
+ }
+ }
+
+ /* if we are here than we found a global property */
+ assert(mod != NULL);
+ assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
+
+ proto = ipadm_str2proto(mod);
+ if (nvpair_value_string(pnvp, &strval) == 0) {
+ (void) ipadm_set_prop(iph, name, strval, proto,
+ IPADM_OPT_ACTIVE);
+ }
+
+ return (B_TRUE);
+}
+
+/* initialize global module properties */
+ipadm_status_t
+ipadm_init_prop()
+{
+ ipadm_handle_t iph = NULL;
+ ipadm_status_t status;
+ int err;
+
+ /* check for solaris.network.interface.config authorization */
+ if (!ipadm_check_auth())
+ return (IPADM_EAUTH);
+
+ if ((status = ipadm_open(&iph, IPH_INIT)) != IPADM_SUCCESS)
+ return (status);
+
+ err = ipadm_rw_db(ipadm_db_init, iph, IPADM_DB_FILE, IPADM_FILE_MODE,
+ IPADM_DB_READ);
+
+ ipadm_close(iph);
+ return (ipadm_errno2status(err));
+}
+
+/*
+ * This is called from ipadm_set_ifprop() to validate the set operation.
+ * It does the following steps:
+ * 1. Validates the interface name.
+ * 2. Fails if it is an IPMP meta-interface or an underlying interface.
+ * 3. In case of a persistent operation, verifies that the
+ * interface is persistent.
+ */
+static ipadm_status_t
+i_ipadm_validate_if(ipadm_handle_t iph, const char *ifname,
+ uint_t proto, uint_t flags)
+{
+ sa_family_t af, other_af;
+ ipadm_status_t status;
+ boolean_t p_exists;
+ boolean_t af_exists, other_af_exists, a_exists;
+
+ /* Check if the interface name is valid. */
+ if (!i_ipadm_validate_ifname(iph, ifname))
+ return (IPADM_INVALID_ARG);
+
+ af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
+ /*
+ * Setting properties on an IPMP meta-interface or underlying
+ * interface is not supported.
+ */
+ if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
+ return (IPADM_NOTSUP);
+
+ /* Check if interface exists in the persistent configuration. */
+ status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
+ if (status != IPADM_SUCCESS)
+ return (status);
+
+ /* Check if interface exists in the active configuration. */
+ af_exists = ipadm_if_enabled(iph, ifname, af);
+ other_af = (af == AF_INET ? AF_INET6 : AF_INET);
+ other_af_exists = ipadm_if_enabled(iph, ifname, other_af);
+ a_exists = (af_exists || other_af_exists);
+ if (!a_exists && p_exists)
+ return (IPADM_OP_DISABLE_OBJ);
+ if (!af_exists)
+ return (IPADM_ENXIO);
+
+ /*
+ * If a persistent operation is requested, check if the underlying
+ * IP interface is persistent.
+ */
+ if ((flags & IPADM_OPT_PERSIST) && !p_exists)
+ return (IPADM_TEMPORARY_OBJ);
+ return (IPADM_SUCCESS);
+}
diff --git a/usr/src/lib/libipadm/common/libipadm.c b/usr/src/lib/libipadm/common/libipadm.c
new file mode 100644
index 0000000000..2c18331a7b
--- /dev/null
+++ b/usr/src/lib/libipadm/common/libipadm.c
@@ -0,0 +1,922 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <inet/ip.h>
+#include <arpa/inet.h>
+#include <libintl.h>
+#include <libdlpi.h>
+#include <libinetutil.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdliptun.h>
+#include <strings.h>
+#include <zone.h>
+#include <ctype.h>
+#include <limits.h>
+#include <assert.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <secdb.h>
+#include <nss_dbdefs.h>
+#include "libipadm_impl.h"
+
+/* error codes and text description */
+static struct ipadm_error_info {
+ ipadm_status_t error_code;
+ const char *error_desc;
+} ipadm_errors[] = {
+ { IPADM_SUCCESS, "Operation succeeded" },
+ { IPADM_FAILURE, "Operation failed" },
+ { IPADM_EAUTH, "Insufficient user authorizations" },
+ { IPADM_EPERM, "Permission denied" },
+ { IPADM_NO_BUFS, "No buffer space available" },
+ { IPADM_NO_MEMORY, "Insufficient memory" },
+ { IPADM_BAD_ADDR, "Invalid address" },
+ { IPADM_BAD_PROTOCOL, "Incorrect protocol family for operation" },
+ { IPADM_DAD_FOUND, "Duplicate address detected" },
+ { IPADM_EXISTS, "Already exists" },
+ { IPADM_IF_EXISTS, "Interface already exists" },
+ { IPADM_ADDROBJ_EXISTS, "Address object already exists" },
+ { IPADM_ADDRCONF_EXISTS, "Addrconf already in progress" },
+ { IPADM_ENXIO, "Interface does not exist" },
+ { IPADM_GRP_NOTEMPTY, "IPMP group is not empty" },
+ { IPADM_INVALID_ARG, "Invalid argument provided" },
+ { IPADM_INVALID_NAME, "Invalid name" },
+ { IPADM_DLPI_FAILURE, "Could not open DLPI link" },
+ { IPADM_DLADM_FAILURE, "Datalink does not exist" },
+ { IPADM_PROP_UNKNOWN, "Unknown property" },
+ { IPADM_ERANGE, "Value is outside the allowed range" },
+ { IPADM_ESRCH, "Value does not exist" },
+ { IPADM_EOVERFLOW, "Number of values exceeds the allowed limit" },
+ { IPADM_NOTFOUND, "Object not found" },
+ { IPADM_IF_INUSE, "Interface already in use" },
+ { IPADM_ADDR_INUSE, "Address already in use" },
+ { IPADM_BAD_HOSTNAME, "Hostname maps to multiple IP addresses" },
+ { IPADM_ADDR_NOTAVAIL, "Can't assign requested address" },
+ { IPADM_ALL_ADDRS_NOT_ENABLED, "All addresses could not be enabled" },
+ { IPADM_NDPD_NOT_RUNNING, "IPv6 autoconf daemon in.ndpd not running" },
+ { IPADM_DHCP_START_ERROR, "Could not start dhcpagent" },
+ { IPADM_DHCP_IPC_ERROR, "Could not communicate with dhcpagent" },
+ { IPADM_DHCP_IPC_TIMEOUT, "Communication with dhcpagent timed out" },
+ { IPADM_TEMPORARY_OBJ, "Persistent operation on temporary object" },
+ { IPADM_IPC_ERROR, "Could not communicate with ipmgmtd" },
+ { IPADM_NOTSUP, "Operation not supported" },
+ { IPADM_OP_DISABLE_OBJ, "Operation not supported on disabled object" },
+ { IPADM_EBADE, "Invalid data exchange with daemon" }
+};
+
+#define IPADM_NUM_ERRORS (sizeof (ipadm_errors) / sizeof (*ipadm_errors))
+
+ipadm_status_t
+ipadm_errno2status(int error)
+{
+ switch (error) {
+ case 0:
+ return (IPADM_SUCCESS);
+ case ENXIO:
+ return (IPADM_ENXIO);
+ case ENOMEM:
+ return (IPADM_NO_MEMORY);
+ case ENOBUFS:
+ return (IPADM_NO_BUFS);
+ case EINVAL:
+ return (IPADM_INVALID_ARG);
+ case EBUSY:
+ return (IPADM_IF_INUSE);
+ case EEXIST:
+ return (IPADM_EXISTS);
+ case EADDRNOTAVAIL:
+ return (IPADM_ADDR_NOTAVAIL);
+ case EADDRINUSE:
+ return (IPADM_ADDR_INUSE);
+ case ENOENT:
+ return (IPADM_NOTFOUND);
+ case ERANGE:
+ return (IPADM_ERANGE);
+ case EPERM:
+ return (IPADM_EPERM);
+ case ENOTSUP:
+ case EOPNOTSUPP:
+ return (IPADM_NOTSUP);
+ case EBADF:
+ return (IPADM_IPC_ERROR);
+ case EBADE:
+ return (IPADM_EBADE);
+ case ESRCH:
+ return (IPADM_ESRCH);
+ case EOVERFLOW:
+ return (IPADM_EOVERFLOW);
+ default:
+ return (IPADM_FAILURE);
+ }
+}
+
+/*
+ * Returns a message string for the given libipadm error status.
+ */
+const char *
+ipadm_status2str(ipadm_status_t status)
+{
+ int i;
+
+ for (i = 0; i < IPADM_NUM_ERRORS; i++) {
+ if (status == ipadm_errors[i].error_code)
+ return (dgettext(TEXT_DOMAIN,
+ ipadm_errors[i].error_desc));
+ }
+
+ return (dgettext(TEXT_DOMAIN, "<unknown error>"));
+}
+
+/*
+ * Opens a handle to libipadm.
+ * Possible values for flags:
+ * IPH_VRRP: Used by VRRP daemon to set the socket option SO_VRRP.
+ * IPH_LEGACY: This is used whenever an application needs to provide a
+ * logical interface name while creating or deleting
+ * interfaces and static addresses.
+ * IPH_INIT: Used by ipadm_init_prop(), to initialize protocol properties
+ * on reboot.
+ */
+ipadm_status_t
+ipadm_open(ipadm_handle_t *handle, uint32_t flags)
+{
+ ipadm_handle_t iph;
+ ipadm_status_t status = IPADM_SUCCESS;
+ zoneid_t zoneid;
+ ushort_t zflags;
+ int on = B_TRUE;
+
+ if (handle == NULL)
+ return (IPADM_INVALID_ARG);
+ *handle = NULL;
+
+ if (flags & ~(IPH_VRRP|IPH_LEGACY|IPH_INIT))
+ return (IPADM_INVALID_ARG);
+
+ if ((iph = calloc(1, sizeof (struct ipadm_handle))) == NULL)
+ return (IPADM_NO_MEMORY);
+ iph->iph_sock = -1;
+ iph->iph_sock6 = -1;
+ iph->iph_door_fd = -1;
+ iph->iph_flags = flags;
+ (void) pthread_mutex_init(&iph->iph_lock, NULL);
+
+ if ((iph->iph_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
+ (iph->iph_sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ goto errnofail;
+ }
+
+ /*
+ * We open a handle to libdladm here, to facilitate some daemons (like
+ * nwamd) which opens handle to libipadm before devfsadmd installs the
+ * right device permissions into the kernel and requires "all"
+ * privileges to open DLD_CONTROL_DEV.
+ *
+ * In a non-global shared-ip zone there will be no DLD_CONTROL_DEV node
+ * and dladm_open() will fail. So, we avoid this by not calling
+ * dladm_open() for such zones.
+ */
+ zoneid = getzoneid();
+ if (zoneid != GLOBAL_ZONEID) {
+ if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags,
+ sizeof (zflags)) < 0) {
+ goto errnofail;
+ }
+ }
+ if ((zoneid == GLOBAL_ZONEID) || (zflags & ZF_NET_EXCL)) {
+ if (dladm_open(&iph->iph_dlh) != DLADM_STATUS_OK) {
+ ipadm_close(iph);
+ return (IPADM_DLADM_FAILURE);
+ }
+ } else {
+ assert(zoneid != GLOBAL_ZONEID);
+ iph->iph_dlh = NULL;
+ }
+ if (flags & IPH_VRRP) {
+ if (setsockopt(iph->iph_sock6, SOL_SOCKET, SO_VRRP, &on,
+ sizeof (on)) < 0 || setsockopt(iph->iph_sock, SOL_SOCKET,
+ SO_VRRP, &on, sizeof (on)) < 0) {
+ goto errnofail;
+ }
+ }
+ *handle = iph;
+ return (status);
+
+errnofail:
+ status = ipadm_errno2status(errno);
+ ipadm_close(iph);
+ return (status);
+}
+
+/*
+ * Closes and frees the libipadm handle.
+ */
+void
+ipadm_close(ipadm_handle_t iph)
+{
+ if (iph == NULL)
+ return;
+ if (iph->iph_sock != -1)
+ (void) close(iph->iph_sock);
+ if (iph->iph_sock6 != -1)
+ (void) close(iph->iph_sock6);
+ if (iph->iph_door_fd != -1)
+ (void) close(iph->iph_door_fd);
+ dladm_close(iph->iph_dlh);
+ (void) pthread_mutex_destroy(&iph->iph_lock);
+ free(iph);
+}
+
+/*
+ * Checks if the caller has the authorization to configure network
+ * interfaces.
+ */
+boolean_t
+ipadm_check_auth(void)
+{
+ struct passwd pwd;
+ char buf[NSS_BUFLEN_PASSWD];
+
+ /* get the password entry for the given user ID */
+ if (getpwuid_r(getuid(), &pwd, buf, sizeof (buf)) == NULL)
+ return (B_FALSE);
+
+ /* check for presence of given authorization */
+ return (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, pwd.pw_name) != 0);
+}
+
+/*
+ * Stores the index value of the interface in `ifname' for the address
+ * family `af' into the buffer pointed to by `index'.
+ */
+static ipadm_status_t
+i_ipadm_get_index(ipadm_handle_t iph, const char *ifname, sa_family_t af,
+ int *index)
+{
+ struct lifreq lifr;
+ int sock;
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (af == AF_INET)
+ sock = iph->iph_sock;
+ else
+ sock = iph->iph_sock6;
+
+ if (ioctl(sock, SIOCGLIFINDEX, (caddr_t)&lifr) < 0)
+ return (ipadm_errno2status(errno));
+ *index = lifr.lifr_index;
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Maximum amount of time (in milliseconds) to wait for Duplicate Address
+ * Detection to complete in the kernel.
+ */
+#define DAD_WAIT_TIME 1000
+
+/*
+ * Any time that flags are changed on an interface where either the new or the
+ * existing flags have IFF_UP set, we'll get a RTM_NEWADDR message to
+ * announce the new address added and its flag status.
+ * We wait here for that message and look for IFF_UP.
+ * If something's amiss with the kernel, though, we don't wait forever.
+ * (Note that IFF_DUPLICATE is a high-order bit, and we cannot see
+ * it in the routing socket messages.)
+ */
+static ipadm_status_t
+i_ipadm_dad_wait(ipadm_handle_t handle, const char *lifname, sa_family_t af,
+ int rtsock)
+{
+ struct pollfd fds[1];
+ union {
+ struct if_msghdr ifm;
+ char buf[1024];
+ } msg;
+ int index;
+ ipadm_status_t retv;
+ uint64_t flags;
+ hrtime_t starttime, now;
+
+ fds[0].fd = rtsock;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+
+ retv = i_ipadm_get_index(handle, lifname, af, &index);
+ if (retv != IPADM_SUCCESS)
+ return (retv);
+
+ starttime = gethrtime();
+ for (;;) {
+ now = gethrtime();
+ now = (now - starttime) / 1000000;
+ if (now >= DAD_WAIT_TIME)
+ break;
+ if (poll(fds, 1, DAD_WAIT_TIME - (int)now) <= 0)
+ break;
+ if (read(rtsock, &msg, sizeof (msg)) <= 0)
+ break;
+ if (msg.ifm.ifm_type != RTM_NEWADDR)
+ continue;
+ /* Note that ifm_index is just 16 bits */
+ if (index == msg.ifm.ifm_index && (msg.ifm.ifm_flags & IFF_UP))
+ return (IPADM_SUCCESS);
+ }
+
+ retv = i_ipadm_get_flags(handle, lifname, af, &flags);
+ if (retv != IPADM_SUCCESS)
+ return (retv);
+ if (flags & IFF_DUPLICATE)
+ return (IPADM_DAD_FOUND);
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Sets the flags `on_flags' and resets the flags `off_flags' for the logical
+ * interface in `lifname'.
+ *
+ * If the new flags value will transition the interface from "down" to "up"
+ * then duplicate address detection is performed by the kernel. This routine
+ * waits to get the outcome of that test.
+ */
+ipadm_status_t
+i_ipadm_set_flags(ipadm_handle_t iph, const char *lifname, sa_family_t af,
+ uint64_t on_flags, uint64_t off_flags)
+{
+ struct lifreq lifr;
+ uint64_t oflags;
+ ipadm_status_t ret;
+ int rtsock = -1;
+ int sock, err;
+
+ ret = i_ipadm_get_flags(iph, lifname, af, &oflags);
+ if (ret != IPADM_SUCCESS)
+ return (ret);
+
+ sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6);
+
+ /*
+ * Any time flags are changed on an interface that has IFF_UP set,
+ * we get a routing socket message. We care about the status,
+ * though, only when the new flags are marked "up."
+ */
+ if (!(oflags & IFF_UP) && (on_flags & IFF_UP))
+ rtsock = socket(PF_ROUTE, SOCK_RAW, af);
+
+ oflags |= on_flags;
+ oflags &= ~off_flags;
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name));
+ lifr.lifr_flags = oflags;
+ if (ioctl(sock, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ err = errno;
+ if (rtsock != -1)
+ (void) close(rtsock);
+ return (ipadm_errno2status(err));
+ }
+ if (rtsock == -1) {
+ return (IPADM_SUCCESS);
+ } else {
+ /* Wait for DAD to complete. */
+ ret = i_ipadm_dad_wait(iph, lifname, af, rtsock);
+ (void) close(rtsock);
+ return (ret);
+ }
+}
+
+/*
+ * Returns the flags value for the logical interface in `lifname'
+ * in the buffer pointed to by `flags'.
+ */
+ipadm_status_t
+i_ipadm_get_flags(ipadm_handle_t iph, const char *lifname, sa_family_t af,
+ uint64_t *flags)
+{
+ struct lifreq lifr;
+ int sock;
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, lifname, sizeof (lifr.lifr_name));
+ if (af == AF_INET)
+ sock = iph->iph_sock;
+ else
+ sock = iph->iph_sock6;
+
+ if (ioctl(sock, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ return (ipadm_errno2status(errno));
+ }
+ *flags = lifr.lifr_flags;
+
+ return (IPADM_SUCCESS);
+}
+
+/*
+ * Determines whether or not an interface name represents a loopback
+ * interface, before the interface has been plumbed.
+ * It is assumed that the interface name in `ifname' is of correct format
+ * as verified by ifparse_ifspec().
+ *
+ * Returns: B_TRUE if loopback, B_FALSE if not.
+ */
+boolean_t
+i_ipadm_is_loopback(const char *ifname)
+{
+ int len = strlen(LOOPBACK_IF);
+
+ return (strncmp(ifname, LOOPBACK_IF, len) == 0 &&
+ (ifname[len] == '\0' || ifname[len] == IPADM_LOGICAL_SEP));
+}
+
+/*
+ * Determines whether or not an interface name represents a vni
+ * interface, before the interface has been plumbed.
+ * It is assumed that the interface name in `ifname' is of correct format
+ * as verified by ifparse_ifspec().
+ *
+ * Returns: B_TRUE if vni, B_FALSE if not.
+ */
+boolean_t
+i_ipadm_is_vni(const char *ifname)
+{
+ ifspec_t ifsp;
+
+ return (ifparse_ifspec(ifname, &ifsp) &&
+ strcmp(ifsp.ifsp_devnm, "vni") == 0);
+}
+
+/*
+ * Returns B_TRUE if `ifname' is an IP interface on a 6to4 tunnel.
+ */
+boolean_t
+i_ipadm_is_6to4(ipadm_handle_t iph, char *ifname)
+{
+ dladm_status_t dlstatus;
+ datalink_class_t class;
+ iptun_params_t params;
+ datalink_id_t linkid;
+
+ if (iph->iph_dlh == NULL) {
+ assert(getzoneid() != GLOBAL_ZONEID);
+ return (B_FALSE);
+ }
+ dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid, NULL,
+ &class, NULL);
+ if (dlstatus == DLADM_STATUS_OK && class == DATALINK_CLASS_IPTUN) {
+ params.iptun_param_linkid = linkid;
+ dlstatus = dladm_iptun_getparams(iph->iph_dlh, &params,
+ DLADM_OPT_ACTIVE);
+ if (dlstatus == DLADM_STATUS_OK &&
+ params.iptun_param_type == IPTUN_TYPE_6TO4) {
+ return (B_TRUE);
+ }
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Returns B_TRUE if `ifname' represents an IPMP underlying interface.
+ */
+boolean_t
+i_ipadm_is_under_ipmp(ipadm_handle_t iph, const char *ifname)
+{
+ struct lifreq lifr;
+
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ if (ioctl(iph->iph_sock, SIOCGLIFGROUPNAME, (caddr_t)&lifr) < 0) {
+ if (ioctl(iph->iph_sock6, SIOCGLIFGROUPNAME,
+ (caddr_t)&lifr) < 0) {
+ return (B_FALSE);
+ }
+ }
+ return (lifr.lifr_groupname[0] != '\0');
+}
+
+/*
+ * Returns B_TRUE if `ifname' represents an IPMP meta-interface.
+ */
+boolean_t
+i_ipadm_is_ipmp(ipadm_handle_t iph, const char *ifname)
+{
+ uint64_t flags;
+
+ if (i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS &&
+ i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS)
+ return (B_FALSE);
+
+ return ((flags & IFF_IPMP) != 0);
+}
+
+/*
+ * For a given interface name, ipadm_if_enabled() checks if v4
+ * or v6 or both IP interfaces exist in the active configuration.
+ */
+boolean_t
+ipadm_if_enabled(ipadm_handle_t iph, const char *ifname, sa_family_t af)
+{
+ struct lifreq lifr;
+ int s4 = iph->iph_sock;
+ int s6 = iph->iph_sock6;
+
+ bzero(&lifr, sizeof (lifr));
+ (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
+ switch (af) {
+ case AF_INET:
+ if (ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0)
+ return (B_TRUE);
+ break;
+ case AF_INET6:
+ if (ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0)
+ return (B_TRUE);
+ break;
+ case AF_UNSPEC:
+ if (ioctl(s4, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0 ||
+ ioctl(s6, SIOCGLIFFLAGS, (caddr_t)&lifr) == 0) {
+ return (B_TRUE);
+ }
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Apply the interface property by retrieving information from nvl.
+ */
+static ipadm_status_t
+i_ipadm_init_ifprop(ipadm_handle_t iph, nvlist_t *nvl)
+{
+ nvpair_t *nvp;
+ char *name, *pname = NULL;
+ char *protostr = NULL, *ifname = NULL, *pval = NULL;
+ uint_t proto;
+ int err = 0;
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IFNAME) == 0) {
+ if ((err = nvpair_value_string(nvp, &ifname)) != 0)
+ break;
+ } else if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
+ if ((err = nvpair_value_string(nvp, &protostr)) != 0)
+ break;
+ } else {
+ assert(!IPADM_PRIV_NVP(name));
+ pname = name;
+ if ((err = nvpair_value_string(nvp, &pval)) != 0)
+ break;
+ }
+ }
+ if (err != 0)
+ return (ipadm_errno2status(err));
+ proto = ipadm_str2proto(protostr);
+ return (ipadm_set_ifprop(iph, ifname, pname, pval, proto,
+ IPADM_OPT_ACTIVE));
+}
+
+/*
+ * Instantiate the address object or set the address object property by
+ * retrieving the configuration from the nvlist `nvl'.
+ */
+ipadm_status_t
+i_ipadm_init_addrobj(ipadm_handle_t iph, nvlist_t *nvl)
+{
+ nvpair_t *nvp;
+ char *name;
+ char *aobjname = NULL, *pval = NULL, *ifname = NULL;
+ sa_family_t af = AF_UNSPEC;
+ ipadm_addr_type_t atype = IPADM_ADDR_NONE;
+ int err = 0;
+ ipadm_status_t status = IPADM_SUCCESS;
+
+ for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(nvl, nvp)) {
+ name = nvpair_name(nvp);
+ if (strcmp(name, IPADM_NVP_IFNAME) == 0) {
+ if ((err = nvpair_value_string(nvp, &ifname)) != 0)
+ break;
+ } else if (strcmp(name, IPADM_NVP_AOBJNAME) == 0) {
+ if ((err = nvpair_value_string(nvp, &aobjname)) != 0)
+ break;
+ } else if (i_ipadm_name2atype(name, &af, &atype)) {
+ break;
+ } else {
+ assert(!IPADM_PRIV_NVP(name));
+ err = nvpair_value_string(nvp, &pval);
+ break;
+ }
+ }
+ if (err != 0)
+ return (ipadm_errno2status(err));
+
+ switch (atype) {
+ case IPADM_ADDR_STATIC:
+ status = i_ipadm_enable_static(iph, ifname, nvl, af);
+ break;
+ case IPADM_ADDR_DHCP:
+ status = i_ipadm_enable_dhcp(iph, ifname, nvl);
+ if (status == IPADM_DHCP_IPC_TIMEOUT)
+ status = IPADM_SUCCESS;
+ break;
+ case IPADM_ADDR_IPV6_ADDRCONF:
+ status = i_ipadm_enable_addrconf(iph, ifname, nvl);
+ break;
+ case IPADM_ADDR_NONE:
+ status = ipadm_set_addrprop(iph, name, pval, aobjname,
+ IPADM_OPT_ACTIVE);
+ break;
+ }
+
+ return (status);
+}
+
+/*
+ * Instantiate the interface object by retrieving the configuration from
+ * `ifnvl'. The nvlist `ifnvl' contains all the persistent configuration
+ * (interface properties and address objects on that interface) for the
+ * given `ifname'.
+ */
+ipadm_status_t
+i_ipadm_init_ifobj(ipadm_handle_t iph, const char *ifname, nvlist_t *ifnvl)
+{
+ nvlist_t *nvl = NULL;
+ nvpair_t *nvp;
+ char *afstr;
+ ipadm_status_t status;
+ ipadm_status_t ret_status = IPADM_SUCCESS;
+ char newifname[LIFNAMSIZ];
+ char *aobjstr;
+
+ (void) strlcpy(newifname, ifname, sizeof (newifname));
+ /*
+ * First plumb the given interface and then apply all the persistent
+ * interface properties and then instantiate any persistent addresses
+ * objects on that interface.
+ */
+ for (nvp = nvlist_next_nvpair(ifnvl, NULL); nvp != NULL;
+ nvp = nvlist_next_nvpair(ifnvl, nvp)) {
+ if (nvpair_value_nvlist(nvp, &nvl) != 0)
+ continue;
+
+ if (nvlist_lookup_string(nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
+ status = i_ipadm_plumb_if(iph, newifname, atoi(afstr),
+ IPADM_OPT_ACTIVE);
+ /*
+ * If the interface is already plumbed, we should
+ * ignore this error because there might be address
+ * address objects on that interface that needs to
+ * be enabled again.
+ */
+ if (status == IPADM_IF_EXISTS)
+ status = IPADM_SUCCESS;
+ } else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME,
+ &aobjstr) == 0) {
+ /*
+ * For a static address, we need to search for
+ * the prefixlen in the nvlist `ifnvl'.
+ */
+ if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR) ||
+ nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) {
+ status = i_ipadm_merge_prefixlen_from_nvl(ifnvl,
+ nvl, aobjstr);
+ if (status != IPADM_SUCCESS)
+ continue;
+ }
+ status = i_ipadm_init_addrobj(iph, nvl);
+ /*
+ * If this address is in use on some other interface,
+ * we want to record an error to be returned as
+ * a soft error and continue processing the rest of
+ * the addresses.
+ */
+ if (status == IPADM_ADDR_NOTAVAIL) {
+ ret_status = IPADM_ALL_ADDRS_NOT_ENABLED;
+ status = IPADM_SUCCESS;
+ }
+ } else {
+ assert(nvlist_exists(nvl, IPADM_NVP_PROTONAME));
+ status = i_ipadm_init_ifprop(iph, nvl);
+ }
+ if (status != IPADM_SUCCESS)
+ return (status);
+ }
+ return (ret_status);
+}
+
+/*
+ * Retrieves the persistent configuration for the given interface(s) in `ifs'
+ * by contacting the daemon and dumps the information in `allifs'.
+ */
+ipadm_status_t
+i_ipadm_init_ifs(ipadm_handle_t iph, const char *ifs, nvlist_t **allifs)
+{
+ nvlist_t *nvl = NULL;
+ size_t nvlsize, bufsize;
+ ipmgmt_initif_arg_t *iargp;
+ char *buf = NULL, *nvlbuf = NULL;
+ ipmgmt_get_rval_t *rvalp = NULL;
+ int err;
+ ipadm_status_t status = IPADM_SUCCESS;
+
+ if ((err = ipadm_str2nvlist(ifs, &nvl, IPADM_NORVAL)) != 0)
+ return (ipadm_errno2status(err));
+
+ err = nvlist_pack(nvl, &nvlbuf, &nvlsize, NV_ENCODE_NATIVE, 0);
+ if (err != 0) {
+ status = ipadm_errno2status(err);
+ goto done;
+ }
+ bufsize = sizeof (*iargp) + nvlsize;
+ if ((buf = malloc(bufsize)) == NULL) {
+ status = ipadm_errno2status(errno);
+ goto done;
+ }
+
+ /* populate the door_call argument structure */
+ iargp = (void *)buf;
+ iargp->ia_cmd = IPMGMT_CMD_INITIF;
+ iargp->ia_flags = 0;
+ iargp->ia_family = AF_UNSPEC;
+ iargp->ia_nvlsize = nvlsize;
+ (void) bcopy(nvlbuf, buf + sizeof (*iargp), nvlsize);
+
+ if ((rvalp = malloc(sizeof (ipmgmt_get_rval_t))) == NULL) {
+ status = ipadm_errno2status(errno);
+ goto done;
+ }
+ if ((err = ipadm_door_call(iph, iargp, bufsize, (void **)&rvalp,
+ sizeof (*rvalp), B_TRUE)) != 0) {
+ status = ipadm_errno2status(err);
+ goto done;
+ }
+ nvlsize = rvalp->ir_nvlsize;
+ nvlbuf = (char *)rvalp + sizeof (ipmgmt_get_rval_t);
+
+ /*
+ * nvlbuf contains a list of nvlists, each of which represents
+ * configuration information for the given interface(s)
+ */
+ err = nvlist_unpack(nvlbuf, nvlsize, allifs, NV_ENCODE_NATIVE);
+ if (err != 0)
+ status = ipadm_errno2status(err);
+done:
+ nvlist_free(nvl);
+ free(buf);
+ free(nvlbuf);
+ free(rvalp);
+ return (status);
+}
+
+/*
+ * Returns B_FALSE if
+ * (1) `ifname' is NULL or has no string or has a string of invalid length
+ * (2) ifname is a logical interface and IPH_LEGACY is not set, or
+ */
+boolean_t
+i_ipadm_validate_ifname(ipadm_handle_t iph, const char *ifname)
+{
+ ifspec_t ifsp;
+
+ if (ifname == NULL || ifname[0] == '\0' ||
+ !ifparse_ifspec(ifname, &ifsp))
+ return (B_FALSE);
+ if (ifsp.ifsp_lunvalid)
+ return (ifsp.ifsp_lun > 0 && (iph->iph_flags & IPH_LEGACY));
+ return (B_TRUE);
+}
+
+/*
+ * Wrapper for sending a non-transparent I_STR ioctl().
+ * Returns: Result from ioctl().
+ */
+int
+i_ipadm_strioctl(int s, int cmd, char *buf, int buflen)
+{
+ struct strioctl ioc;
+
+ (void) memset(&ioc, 0, sizeof (ioc));
+ ioc.ic_cmd = cmd;
+ ioc.ic_timout = 0;
+ ioc.ic_len = buflen;
+ ioc.ic_dp = buf;
+
+ return (ioctl(s, I_STR, (char *)&ioc));
+}
+
+/*
+ * Make a door call to the server and checks if the door call succeeded or not.
+ * `is_varsize' specifies that the data returned by ipmgmtd daemon is of
+ * variable size and door will allocate buffer using mmap(). In such cases
+ * we re-allocate the required memory,n assign it to `rbufp', copy the data to
+ * `rbufp' and then call munmap() (see below).
+ *
+ * It also checks to see if the server side procedure ran successfully by
+ * checking for ir_err. Therefore, for some callers who just care about the
+ * return status can set `rbufp' to NULL and set `rsize' to 0.
+ */
+int
+ipadm_door_call(ipadm_handle_t iph, void *arg, size_t asize, void **rbufp,
+ size_t rsize, boolean_t is_varsize)
+{
+ door_arg_t darg;
+ int err;
+ ipmgmt_retval_t rval, *rvalp;
+
+ if (rbufp == NULL) {
+ rvalp = &rval;
+ rbufp = (void **)&rvalp;
+ rsize = sizeof (rval);
+ }
+
+ darg.data_ptr = arg;
+ darg.data_size = asize;
+ darg.desc_ptr = NULL;
+ darg.desc_num = 0;
+ darg.rbuf = *rbufp;
+ darg.rsize = rsize;
+
+ (void) pthread_mutex_lock(&iph->iph_lock);
+ /* The door descriptor is opened if it isn't already */
+ if (iph->iph_door_fd == -1) {
+ if ((iph->iph_door_fd = open(IPMGMT_DOOR, O_RDONLY)) < 0) {
+ err = errno;
+ (void) pthread_mutex_unlock(&iph->iph_lock);
+ return (err);
+ }
+ }
+ (void) pthread_mutex_unlock(&iph->iph_lock);
+
+ if (door_call(iph->iph_door_fd, &darg) == -1)
+ return (errno);
+ err = ((ipmgmt_retval_t *)(void *)(darg.rbuf))->ir_err;
+ if (darg.rbuf != *rbufp) {
+ /*
+ * if the caller is expecting the result to fit in specified
+ * buffer then return failure.
+ */
+ if (!is_varsize)
+ err = EBADE;
+ /*
+ * The size of the buffer `*rbufp' was not big enough
+ * and the door itself allocated buffer, for us. We will
+ * hit this, on several occasion as for some cases
+ * we cannot predict the size of the return structure.
+ * Reallocate the buffer `*rbufp' and memcpy() the contents
+ * to new buffer.
+ */
+ if (err == 0) {
+ void *newp;
+
+ /* allocated memory will be freed by the caller */
+ if ((newp = realloc(*rbufp, darg.rsize)) == NULL) {
+ err = ENOMEM;
+ } else {
+ *rbufp = newp;
+ (void) memcpy(*rbufp, darg.rbuf, darg.rsize);
+ }
+ }
+ /* munmap() the door buffer */
+ (void) munmap(darg.rbuf, darg.rsize);
+ } else {
+ if (darg.rsize != rsize)
+ err = EBADE;
+ }
+ return (err);
+}
diff --git a/usr/src/lib/libipadm/common/libipadm.h b/usr/src/lib/libipadm/common/libipadm.h
new file mode 100644
index 0000000000..621c06b4e4
--- /dev/null
+++ b/usr/src/lib/libipadm/common/libipadm.h
@@ -0,0 +1,346 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef _LIBIPADM_H
+#define _LIBIPADM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+#include <libnvpair.h>
+#include <netinet/tcp.h>
+#include <sys/stropts.h>
+
+#define IPADM_AOBJ_USTRSIZ 32
+#define IPADM_AOBJSIZ (LIFNAMSIZ + IPADM_AOBJ_USTRSIZ)
+#define MAXPROPVALLEN 512
+#define LOOPBACK_IF "lo0"
+
+/* special timeout values for dhcp operations */
+#define IPADM_DHCP_WAIT_DEFAULT (-1)
+#define IPADM_DHCP_WAIT_FOREVER (-2)
+
+/*
+ * Specifies that the string passed to ipadm_str2nvlist() is a string of comma
+ * separated names and that each name does not have values associated with it.
+ */
+#define IPADM_NORVAL 0x00000001
+
+/* error codes */
+typedef enum {
+ IPADM_SUCCESS, /* No error occurred */
+ IPADM_FAILURE, /* Generic failure */
+ IPADM_EAUTH, /* Insufficient user authorizations */
+ IPADM_EPERM, /* Permission denied */
+ IPADM_NO_BUFS, /* No Buffer space available */
+ IPADM_NO_MEMORY, /* Insufficient memory */
+ IPADM_BAD_ADDR, /* Invalid address */
+ IPADM_BAD_PROTOCOL, /* Wrong protocol family for operation */
+ IPADM_DAD_FOUND, /* Duplicate address detected */
+ IPADM_EXISTS, /* Already exists */
+ IPADM_IF_EXISTS, /* Interface already exists */
+ IPADM_ADDROBJ_EXISTS, /* Address object already exists */
+ IPADM_ADDRCONF_EXISTS, /* Addrconf already in progress */
+ IPADM_ENXIO, /* Interface does not exist */
+ IPADM_GRP_NOTEMPTY, /* IPMP Group non-empty on unplumb */
+ IPADM_INVALID_ARG, /* Invalid argument */
+ IPADM_INVALID_NAME, /* Invalid name */
+ IPADM_DLPI_FAILURE, /* Could not open DLPI link */
+ IPADM_DLADM_FAILURE, /* DLADM error encountered */
+ IPADM_PROP_UNKNOWN, /* Unknown property */
+ IPADM_ERANGE, /* Value is outside the allowed range */
+ IPADM_ESRCH, /* Value does not exist */
+ IPADM_EOVERFLOW, /* Number of values exceed the allowed limit */
+ IPADM_NOTFOUND, /* Object not found */
+ IPADM_IF_INUSE, /* Interface already in use */
+ IPADM_ADDR_INUSE, /* Address alrelady in use */
+ IPADM_BAD_HOSTNAME, /* hostname maps to multiple IP addresses */
+ IPADM_ADDR_NOTAVAIL, /* Can't assign requested address */
+ IPADM_ALL_ADDRS_NOT_ENABLED, /* All addresses could not be enabled */
+ IPADM_NDPD_NOT_RUNNING, /* in.ndpd not running */
+ IPADM_DHCP_START_ERROR, /* Cannot start dhcpagent */
+ IPADM_DHCP_IPC_ERROR, /* Cannot communicate with dhcpagent */
+ IPADM_DHCP_IPC_TIMEOUT, /* Communication with dhcpagent timed out */
+ IPADM_TEMPORARY_OBJ, /* Permanent operation on temporary object */
+ IPADM_IPC_ERROR, /* Cannot communicate with ipmgmtd */
+ IPADM_OP_DISABLE_OBJ, /* Operation on disable object */
+ IPADM_NOTSUP, /* Operation not supported */
+ IPADM_EBADE /* Invalid data exchange with ipmgmtd */
+} ipadm_status_t;
+
+/*
+ * option flags taken by the libipadm functions
+ *
+ * - IPADM_OPT_PERSIST:
+ * For all the create/delete/up/down/set/get functions,
+ * requests to persist the configuration so that it can be
+ * re-enabled or reapplied on boot.
+ *
+ * - IPADM_OPT_ACTIVE:
+ * Requests to apply configuration without persisting it and
+ * used by show-* subcommands to retrieve current values.
+ *
+ * - IPADM_OPT_DEFAULT:
+ * retrieves the default value for a given property
+ *
+ * - IPADM_OPT_PERM
+ * retrieves the permission for a given property
+ *
+ * - IPADM_OPT_POSSIBLE
+ * retrieves the range of values for a given property
+ *
+ * - IPADM_OPT_APPEND
+ * for multi-valued properties, appends a new value.
+ *
+ * - IPADM_OPT_REMOVE
+ * for multi-valued properties, removes the specified value
+ *
+ * - IPADM_OPT_IPMP
+ * Used in ipadm_create_if() to plumb ipmp interfaces.
+ *
+ * - IPADM_OPT_GENPPA
+ * Used in ipadm_create_if() to generate a ppa for the given interface.
+ *
+ * - IPADM_OPT_ZEROADDR
+ * return :: or INADDR_ANY
+ *
+ * - IPADM_OPT_RELEASE
+ * Used to release the lease on a dhcp address object
+ *
+ * - IPADM_OPT_INFORM
+ * Used to perform DHCP_INFORM on a specified static address object
+ *
+ * - IPADM_OPT_UP
+ * Used to bring up a static address on creation
+ */
+#define IPADM_OPT_PERSIST 0x00000001
+#define IPADM_OPT_ACTIVE 0x00000002
+#define IPADM_OPT_DEFAULT 0x00000004
+#define IPADM_OPT_PERM 0x00000008
+#define IPADM_OPT_POSSIBLE 0x00000010
+#define IPADM_OPT_APPEND 0x00000020
+#define IPADM_OPT_REMOVE 0x00000040
+#define IPADM_OPT_IPMP 0x00000080
+#define IPADM_OPT_GENPPA 0x00000100
+#define IPADM_OPT_ZEROADDR 0x00000200
+#define IPADM_OPT_RELEASE 0x00000400
+#define IPADM_OPT_INFORM 0x00000800
+#define IPADM_OPT_UP 0x00001000
+
+/* IPADM property class */
+#define IPADMPROP_CLASS_MODULE 0x00000001 /* on 'protocol' only */
+#define IPADMPROP_CLASS_IF 0x00000002 /* on 'IP interface' only */
+#define IPADMPROP_CLASS_ADDR 0x00000004 /* on 'IP address' only */
+/* protocol property that can be applied on interface too */
+#define IPADMPROP_CLASS_MODIF (IPADMPROP_CLASS_MODULE | IPADMPROP_CLASS_IF)
+
+/* opaque ipadm handle to libipadm functions */
+struct ipadm_handle;
+typedef struct ipadm_handle *ipadm_handle_t;
+
+/* ipadm_handle flags */
+#define IPH_VRRP 0x00000001 /* Caller is VRRP */
+#define IPH_LEGACY 0x00000002 /* Caller is legacy app */
+
+/* opaque address object structure */
+typedef struct ipadm_addrobj_s *ipadm_addrobj_t;
+
+/* ipadm_if_info_t states */
+typedef enum {
+ IFIS_OK, /* Interface is usable */
+ IFIS_DOWN, /* Interface has no UP addresses */
+ IFIS_FAILED, /* Interface has failed. */
+ IFIS_OFFLINE, /* Interface has been offlined */
+ IFIS_DISABLED /* Interface has been disabled. */
+} ipadm_if_state_t;
+
+typedef struct ipadm_if_info_s {
+ struct ipadm_if_info_s *ifi_next;
+ char ifi_name[LIFNAMSIZ]; /* interface name */
+ ipadm_if_state_t ifi_state; /* see above */
+ uint_t ifi_cflags; /* current flags */
+ uint_t ifi_pflags; /* persistent flags */
+} ipadm_if_info_t;
+
+/* ipadm_if_info_t flags */
+#define IFIF_BROADCAST 0x00000001
+#define IFIF_MULTICAST 0x00000002
+#define IFIF_POINTOPOINT 0x00000004
+#define IFIF_VIRTUAL 0x00000008
+#define IFIF_IPMP 0x00000010
+#define IFIF_STANDBY 0x00000020
+#define IFIF_INACTIVE 0x00000040
+#define IFIF_VRRP 0x00000080
+#define IFIF_NOACCEPT 0x00000100
+#define IFIF_IPV4 0x00000200
+#define IFIF_IPV6 0x00000400
+
+/* ipadm_addr_info_t state */
+typedef enum {
+ IFA_DISABLED, /* Address not in active configuration. */
+ IFA_DUPLICATE, /* DAD failed. */
+ IFA_DOWN, /* Address is not IFF_UP */
+ IFA_TENTATIVE, /* DAD verification initiated */
+ IFA_OK, /* Address is usable */
+ IFA_INACCESSIBLE /* Interface has failed */
+} ipadm_addr_state_t;
+
+/* possible address types */
+typedef enum {
+ IPADM_ADDR_NONE,
+ IPADM_ADDR_STATIC,
+ IPADM_ADDR_IPV6_ADDRCONF,
+ IPADM_ADDR_DHCP
+} ipadm_addr_type_t;
+
+typedef struct ipadm_addr_info_s {
+ struct ifaddrs ia_ifa; /* list of addresses */
+ char ia_sname[NI_MAXHOST]; /* local hostname */
+ char ia_dname[NI_MAXHOST]; /* remote hostname */
+ char ia_aobjname[IPADM_AOBJSIZ];
+ uint_t ia_cflags; /* active flags */
+ uint_t ia_pflags; /* persistent flags */
+ ipadm_addr_type_t ia_atype; /* see above */
+ ipadm_addr_state_t ia_state; /* see above */
+} ipadm_addr_info_t;
+#define IA_NEXT(ia) ((ipadm_addr_info_t *)(ia->ia_ifa.ifa_next))
+
+/* ipadm_addr_info_t flags */
+#define IA_UP 0x00000001
+#define IA_UNNUMBERED 0x00000002
+#define IA_PRIVATE 0x00000004
+#define IA_TEMPORARY 0x00000008
+#define IA_DEPRECATED 0x00000010
+
+/* open/close libipadm handle */
+extern ipadm_status_t ipadm_open(ipadm_handle_t *, uint32_t);
+extern void ipadm_close(ipadm_handle_t);
+
+/* Check authorization for network configuration */
+extern boolean_t ipadm_check_auth(void);
+/*
+ * Interface mangement functions
+ */
+extern ipadm_status_t ipadm_create_if(ipadm_handle_t, char *, sa_family_t,
+ uint32_t);
+extern ipadm_status_t ipadm_disable_if(ipadm_handle_t, const char *,
+ uint32_t);
+extern ipadm_status_t ipadm_enable_if(ipadm_handle_t, const char *, uint32_t);
+extern ipadm_status_t ipadm_if_info(ipadm_handle_t, const char *,
+ ipadm_if_info_t **, uint32_t, int64_t);
+extern void ipadm_free_if_info(ipadm_if_info_t *);
+extern ipadm_status_t ipadm_delete_if(ipadm_handle_t, const char *,
+ sa_family_t, uint32_t);
+
+/*
+ * Address management functions
+ */
+extern ipadm_status_t ipadm_create_addr(ipadm_handle_t, ipadm_addrobj_t,
+ uint32_t);
+extern ipadm_status_t ipadm_disable_addr(ipadm_handle_t, const char *,
+ uint32_t);
+extern ipadm_status_t ipadm_enable_addr(ipadm_handle_t, const char *,
+ uint32_t);
+extern ipadm_status_t ipadm_addr_info(ipadm_handle_t, const char *,
+ ipadm_addr_info_t **, uint32_t, int64_t);
+extern void ipadm_free_addr_info(ipadm_addr_info_t *);
+extern ipadm_status_t ipadm_up_addr(ipadm_handle_t, const char *,
+ uint32_t);
+extern ipadm_status_t ipadm_down_addr(ipadm_handle_t, const char *,
+ uint32_t);
+extern ipadm_status_t ipadm_refresh_addr(ipadm_handle_t, const char *,
+ uint32_t);
+extern ipadm_status_t ipadm_delete_addr(ipadm_handle_t, const char *,
+ uint32_t);
+
+/* Functions related to creating/deleting/modifying opaque address object */
+extern ipadm_status_t ipadm_create_addrobj(ipadm_addr_type_t, const char *,
+ ipadm_addrobj_t *);
+extern void ipadm_destroy_addrobj(ipadm_addrobj_t);
+
+/* Functions to set fields in addrobj for static addresses */
+extern ipadm_status_t ipadm_set_addr(ipadm_addrobj_t, const char *,
+ sa_family_t);
+extern ipadm_status_t ipadm_set_dst_addr(ipadm_addrobj_t, const char *,
+ sa_family_t);
+
+/* Functions to set fields in addrobj for IPv6 addrconf */
+extern ipadm_status_t ipadm_set_interface_id(ipadm_addrobj_t, const char *);
+extern ipadm_status_t ipadm_set_stateless(ipadm_addrobj_t, boolean_t);
+extern ipadm_status_t ipadm_set_stateful(ipadm_addrobj_t, boolean_t);
+
+/* Functions to set fields in addrobj for DHCP */
+extern ipadm_status_t ipadm_set_primary(ipadm_addrobj_t, boolean_t);
+extern ipadm_status_t ipadm_set_wait_time(ipadm_addrobj_t, int32_t);
+
+/*
+ * Property management functions
+ */
+/* call back function for the property walker */
+typedef boolean_t ipadm_prop_wfunc_t(void *, const char *, uint_t);
+extern ipadm_status_t ipadm_walk_proptbl(uint_t, uint_t, ipadm_prop_wfunc_t *,
+ void *);
+extern ipadm_status_t ipadm_walk_prop(const char *, uint_t, uint_t,
+ ipadm_prop_wfunc_t *, void *);
+
+/* Interface property management - set, reset and get */
+extern ipadm_status_t ipadm_set_ifprop(ipadm_handle_t, const char *,
+ const char *, const char *, uint_t, uint_t);
+extern ipadm_status_t ipadm_get_ifprop(ipadm_handle_t, const char *,
+ const char *, char *, uint_t *, uint_t, uint_t);
+
+/* Address property management - set, reset and get */
+extern ipadm_status_t ipadm_set_addrprop(ipadm_handle_t, const char *,
+ const char *, const char *, uint_t);
+extern ipadm_status_t ipadm_get_addrprop(ipadm_handle_t, const char *, char *,
+ uint_t *, const char *, uint_t);
+
+/* Protoocl property management - set, reset and get */
+extern ipadm_status_t ipadm_set_prop(ipadm_handle_t, const char *,
+ const char *, uint_t, uint_t);
+extern ipadm_status_t ipadm_get_prop(ipadm_handle_t, const char *, char *,
+ uint_t *, uint_t, uint_t);
+extern ipadm_status_t ipadm_init_prop(void);
+
+/*
+ * miscellaneous helper functions.
+ */
+extern const char *ipadm_status2str(ipadm_status_t);
+extern int ipadm_str2nvlist(const char *, nvlist_t **, uint_t);
+extern size_t ipadm_nvlist2str(nvlist_t *, char *, size_t);
+extern char *ipadm_proto2str(uint_t);
+extern uint_t ipadm_str2proto(const char *);
+extern ipadm_status_t ipadm_open_arp_on_udp(const char *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBIPADM_H */
diff --git a/usr/src/lib/libipadm/common/libipadm_impl.h b/usr/src/lib/libipadm/common/libipadm_impl.h
new file mode 100644
index 0000000000..6572bc64b7
--- /dev/null
+++ b/usr/src/lib/libipadm/common/libipadm_impl.h
@@ -0,0 +1,227 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBIPADM_IMPL_H
+#define _LIBIPADM_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <libipadm.h>
+#include <libdladm.h>
+#include <ipadm_ipmgmt.h>
+#include <inet/tunables.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <libinetutil.h>
+#include <libsocket_priv.h>
+
+#define IPADM_STRSIZE 256
+#define IPADM_ONSTR "on"
+#define IPADM_OFFSTR "off"
+#define ARP_MOD_NAME "arp"
+#define IPADM_LOGICAL_SEP ':'
+#define IPV6_MIN_MTU 1280 /* rfc2460 */
+
+/* mask for flags accepted by libipadm functions */
+#define IPADM_COMMON_OPT_MASK (IPADM_OPT_ACTIVE | IPADM_OPT_PERSIST)
+
+/* Opaque library handle */
+struct ipadm_handle {
+ int iph_sock; /* socket to interface */
+ int iph_sock6; /* socket to interface */
+ int iph_door_fd; /* door descriptor to ipmgmtd */
+ dladm_handle_t iph_dlh; /* handle to libdladm library */
+ uint32_t iph_flags; /* internal flags */
+ pthread_mutex_t iph_lock; /* lock to set door_fd */
+};
+
+/*
+ * Indicates that the operation being invoked is in 'init' context. This is
+ * a library private flag.
+ */
+#define IPH_INIT 0x10000000
+
+struct ipadm_addrobj_s {
+ char ipadm_ifname[LIFNAMSIZ];
+ int32_t ipadm_lifnum;
+ char ipadm_aobjname[IPADM_AOBJSIZ];
+ ipadm_addr_type_t ipadm_atype;
+ uint32_t ipadm_flags;
+ sa_family_t ipadm_af;
+ union {
+ struct {
+ char ipadm_ahname[MAXNAMELEN];
+ struct sockaddr_storage ipadm_addr;
+ uint32_t ipadm_prefixlen;
+ char ipadm_dhname[MAXNAMELEN];
+ struct sockaddr_storage ipadm_dstaddr;
+ } ipadm_static_addr_s;
+ struct {
+ struct sockaddr_in6 ipadm_intfid;
+ uint32_t ipadm_intfidlen;
+ boolean_t ipadm_stateless;
+ boolean_t ipadm_stateful;
+ } ipadm_ipv6_intfid_s;
+ struct {
+ boolean_t ipadm_primary;
+ int32_t ipadm_wait;
+ } ipadm_dhcp_s;
+ } ipadm_addr_u;
+};
+
+#define ipadm_static_addr ipadm_addr_u.ipadm_static_addr_s.ipadm_addr
+#define ipadm_static_aname ipadm_addr_u.ipadm_static_addr_s.ipadm_ahname
+#define ipadm_static_prefixlen ipadm_addr_u.ipadm_static_addr_s.ipadm_prefixlen
+#define ipadm_static_dst_addr ipadm_addr_u.ipadm_static_addr_s.ipadm_dstaddr
+#define ipadm_static_dname ipadm_addr_u.ipadm_static_addr_s.ipadm_dhname
+#define ipadm_intfid ipadm_addr_u.ipadm_ipv6_intfid_s.ipadm_intfid
+#define ipadm_intfidlen ipadm_addr_u.ipadm_ipv6_intfid_s.ipadm_intfidlen
+#define ipadm_stateless ipadm_addr_u.ipadm_ipv6_intfid_s.ipadm_stateless
+#define ipadm_stateful ipadm_addr_u.ipadm_ipv6_intfid_s.ipadm_stateful
+#define ipadm_primary ipadm_addr_u.ipadm_dhcp_s.ipadm_primary
+#define ipadm_wait ipadm_addr_u.ipadm_dhcp_s.ipadm_wait
+
+/*
+ * Data structures and callback functions related to property management
+ */
+struct ipadm_prop_desc;
+typedef struct ipadm_prop_desc ipadm_prop_desc_t;
+
+/* property set() callback */
+typedef ipadm_status_t ipadm_pd_setf_t(ipadm_handle_t, const void *,
+ ipadm_prop_desc_t *, const void *, uint_t, uint_t);
+
+/* property get() callback */
+typedef ipadm_status_t ipadm_pd_getf_t(ipadm_handle_t, const void *,
+ ipadm_prop_desc_t *, char *, uint_t *, uint_t, uint_t);
+
+struct ipadm_prop_desc {
+ char *ipd_name; /* property name */
+ uint_t ipd_class; /* prop. class - global/perif/both */
+ uint_t ipd_proto; /* protocol to which property belongs */
+ ipadm_pd_setf_t *ipd_set; /* set callback function */
+ ipadm_pd_getf_t *ipd_get_range; /* get range callback function */
+ ipadm_pd_getf_t *ipd_get; /* get value callback function */
+};
+
+extern ipadm_prop_desc_t ipadm_addrprop_table[];
+extern ipadm_pd_getf_t i_ipadm_get_onoff;
+
+/* libipadm.c */
+extern ipadm_status_t i_ipadm_get_flags(ipadm_handle_t, const char *,
+ sa_family_t, uint64_t *);
+extern ipadm_status_t i_ipadm_set_flags(ipadm_handle_t, const char *,
+ sa_family_t, uint64_t, uint64_t);
+extern ipadm_status_t i_ipadm_init_ifs(ipadm_handle_t, const char *,
+ nvlist_t **);
+extern ipadm_status_t i_ipadm_init_ifobj(ipadm_handle_t, const char *,
+ nvlist_t *);
+extern ipadm_status_t i_ipadm_init_addrobj(ipadm_handle_t, nvlist_t *);
+extern ipadm_status_t i_ipadm_addr_persist(ipadm_handle_t,
+ const ipadm_addrobj_t, boolean_t, uint32_t);
+extern ipadm_status_t i_ipadm_delete_addr(ipadm_handle_t, ipadm_addrobj_t);
+extern int i_ipadm_strioctl(int, int, char *, int);
+extern boolean_t i_ipadm_is_loopback(const char *);
+extern boolean_t i_ipadm_is_vni(const char *);
+extern boolean_t i_ipadm_is_ipmp(ipadm_handle_t, const char *);
+extern boolean_t i_ipadm_is_under_ipmp(ipadm_handle_t, const char *);
+extern boolean_t i_ipadm_is_6to4(ipadm_handle_t, char *);
+extern boolean_t i_ipadm_validate_ifname(ipadm_handle_t, const char *);
+extern ipadm_status_t ipadm_errno2status(int);
+extern int ipadm_door_call(ipadm_handle_t, void *, size_t, void **,
+ size_t, boolean_t);
+extern boolean_t ipadm_if_enabled(ipadm_handle_t, const char *,
+ sa_family_t);
+
+/* ipadm_ndpd.c */
+extern ipadm_status_t i_ipadm_create_ipv6addrs(ipadm_handle_t,
+ ipadm_addrobj_t, uint32_t);
+extern ipadm_status_t i_ipadm_delete_ipv6addrs(ipadm_handle_t,
+ ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_disable_autoconf(const char *);
+extern ipadm_status_t i_ipadm_enable_autoconf(const char *);
+
+/* ipadm_persist.c */
+extern ipadm_status_t i_ipadm_add_ipaddr2nvl(nvlist_t *, ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_add_ip6addr2nvl(nvlist_t *, ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_add_intfid2nvl(nvlist_t *, ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_add_dhcp2nvl(nvlist_t *, boolean_t, int32_t);
+
+/* ipadm_prop.c */
+extern ipadm_status_t i_ipadm_persist_propval(ipadm_handle_t,
+ ipadm_prop_desc_t *, const char *, const void *,
+ uint_t);
+extern ipadm_status_t i_ipadm_get_persist_propval(ipadm_handle_t,
+ ipadm_prop_desc_t *, char *, uint_t *,
+ const void *);
+
+/* ipadm_addr.c */
+extern void i_ipadm_init_addr(ipadm_addrobj_t, const char *,
+ const char *, ipadm_addr_type_t);
+extern ipadm_status_t i_ipadm_merge_prefixlen_from_nvl(nvlist_t *, nvlist_t *,
+ const char *);
+extern ipadm_status_t i_ipadm_get_addrobj(ipadm_handle_t, ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_enable_static(ipadm_handle_t, const char *,
+ nvlist_t *, sa_family_t);
+extern ipadm_status_t i_ipadm_enable_dhcp(ipadm_handle_t, const char *,
+ nvlist_t *);
+extern ipadm_status_t i_ipadm_enable_addrconf(ipadm_handle_t, const char *,
+ nvlist_t *);
+extern void i_ipadm_addrobj2lifname(ipadm_addrobj_t, char *, int);
+extern ipadm_status_t i_ipadm_nvl2in6_addr(nvlist_t *, char *,
+ in6_addr_t *);
+extern ipadm_status_t i_ipadm_get_lif2addrobj(ipadm_handle_t,
+ ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_lookupadd_addrobj(ipadm_handle_t,
+ ipadm_addrobj_t);
+extern ipadm_status_t i_ipadm_delete_addrobj(ipadm_handle_t,
+ const ipadm_addrobj_t, uint32_t);
+extern boolean_t i_ipadm_name2atype(const char *, sa_family_t *,
+ ipadm_addr_type_t *);
+
+/* ipadm_if.c */
+extern ipadm_status_t i_ipadm_create_if(ipadm_handle_t, char *, sa_family_t,
+ uint32_t);
+extern ipadm_status_t i_ipadm_delete_if(ipadm_handle_t, const char *,
+ sa_family_t, uint32_t);
+extern ipadm_status_t i_ipadm_plumb_if(ipadm_handle_t, char *, sa_family_t,
+ uint32_t);
+extern ipadm_status_t i_ipadm_unplumb_if(ipadm_handle_t, const char *,
+ sa_family_t);
+extern ipadm_status_t i_ipadm_if_pexists(ipadm_handle_t, const char *,
+ sa_family_t, boolean_t *);
+extern ipadm_status_t i_ipadm_delete_ifobj(ipadm_handle_t, const char *,
+ sa_family_t, boolean_t);
+extern int i_ipadm_get_lnum(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBIPADM_IMPL_H */
diff --git a/usr/src/lib/libipadm/common/llib-lipadm b/usr/src/lib/libipadm/common/llib-lipadm
new file mode 100644
index 0000000000..4553567250
--- /dev/null
+++ b/usr/src/lib/libipadm/common/llib-lipadm
@@ -0,0 +1,35 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libipadm.h>
+/*
+ * functions private to libipadm and ipmgmtd/in.ndpd are prototyped
+ * in the files below
+ */
+#include <ipadm_ipmgmt.h>
+#include <ipadm_ndpd.h>
diff --git a/usr/src/lib/libipadm/common/mapfile-vers b/usr/src/lib/libipadm/common/mapfile-vers
new file mode 100644
index 0000000000..9a48e57547
--- /dev/null
+++ b/usr/src/lib/libipadm/common/mapfile-vers
@@ -0,0 +1,93 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate_1.1 {
+ global:
+ ipadm_add_aobjname;
+ ipadm_addr_info;
+ ipadm_check_auth;
+ ipadm_close;
+ ipadm_create_addr;
+ ipadm_create_addrobj;
+ ipadm_create_if;
+ ipadm_delete_addr;
+ ipadm_delete_aobjname;
+ ipadm_delete_if;
+ ipadm_destroy_addrobj;
+ ipadm_disable_addr;
+ ipadm_disable_if;
+ ipadm_down_addr;
+ ipadm_enable_addr;
+ ipadm_enable_if;
+ ipadm_free_addr_info;
+ ipadm_free_if_info;
+ ipadm_get_addrprop;
+ ipadm_get_ifprop;
+ ipadm_get_prop;
+ ipadm_if_enabled;
+ ipadm_if_info;
+ ipadm_init_prop;
+ ipadm_ndpd_read;
+ ipadm_ndpd_write;
+ ipadm_nvlist2str;
+ ipadm_open;
+ ipadm_open_arp_on_udp;
+ ipadm_proto2str;
+ ipadm_refresh_addr;
+ ipadm_rw_db;
+ ipadm_set_addr;
+ ipadm_set_addrprop;
+ ipadm_set_dst_addr;
+ ipadm_set_ifprop;
+ ipadm_set_interface_id;
+ ipadm_set_primary;
+ ipadm_set_prop;
+ ipadm_set_stateful;
+ ipadm_set_stateless;
+ ipadm_set_wait_time;
+ ipadm_status2str;
+ ipadm_str2nvlist;
+ ipadm_str2proto;
+ ipadm_up_addr;
+ ipadm_walk_prop;
+ ipadm_walk_proptbl;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libipadm/i386/Makefile b/usr/src/lib/libipadm/i386/Makefile
new file mode 100644
index 0000000000..4d9d4abd5b
--- /dev/null
+++ b/usr/src/lib/libipadm/i386/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libipadm/libipadm.xcl b/usr/src/lib/libipadm/libipadm.xcl
new file mode 100644
index 0000000000..d8877dd4ca
--- /dev/null
+++ b/usr/src/lib/libipadm/libipadm.xcl
@@ -0,0 +1,89 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+msgid ""
+msgid "%c%c"
+msgid "%d"
+msgid "%d,%s"
+msgid "%s"
+msgid "%s%d"
+msgid "%s,%s"
+msgid "%s/%s"
+msgid "%s/%s.new"
+msgid "%s0"
+msgid "%s:%d"
+msgid "%s="
+msgid "%s=%s"
+msgid "%u"
+msgid ","
+msgid ",%s"
+msgid "/"
+msgid "/%d,%s,%s"
+msgid "0"
+msgid "1"
+msgid "1-126,128"
+msgid "1-30,32"
+msgid "active"
+msgid "all-zones"
+msgid "arp"
+msgid "broadcast"
+msgid "deprecated"
+msgid "ecn"
+msgid "exchange_routes"
+msgid "extra_priv_ports"
+msgid "forwarding"
+msgid "hoplimit"
+msgid "icmp"
+msgid "ip"
+msgid "ipmpstub0"
+msgid "ipv4"
+msgid "ipv6"
+msgid "largest_anon_port"
+msgid "metric"
+msgid "mtu"
+msgid "never"
+msgid "no"
+msgid "nud"
+msgid "passive"
+msgid "prefixlen"
+msgid "private"
+msgid "r"
+msgid "r+"
+msgid "recv_maxbuf"
+msgid "sack"
+msgid "sctp"
+msgid "send_maxbuf"
+msgid "smallest_anon_port"
+msgid "smallest_nonpriv_port"
+msgid "tcp"
+msgid "transmit"
+msgid "ttl"
+msgid "udp"
+msgid "up"
+msgid "usesrc"
+msgid "vni"
+msgid "w"
+msgid "yes"
+msgid "zone"
diff --git a/usr/src/lib/libipadm/sparc/Makefile b/usr/src/lib/libipadm/sparc/Makefile
new file mode 100644
index 0000000000..8069eadad1
--- /dev/null
+++ b/usr/src/lib/libipadm/sparc/Makefile
@@ -0,0 +1,29 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index 0a10c10a11..7d5ef346c6 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -97,6 +97,7 @@ solaris.network.autoconf.wlan:::Create Network Auto-Magic Config for Known WLANs
solaris.network.autoconf.write:::Create Network Auto-Magic Config::help=NetworkAutoconfWrite.html
solaris.network.ilb.config:::Network ILB Configuration::help=NetworkILBconf.html
solaris.network.ilb.enable:::Network ILB Enable Configuration::help=NetworkILBenable.html
+solaris.network.interface.config:::Network Interface Configuration::help=NetworkInterfaceConfig.html
solaris.network.link.security:::Link Security::help=LinkSecurity.html
solaris.network.wifi.config:::Wifi Config::help=WifiConfig.html
solaris.network.wifi.wep:::Wifi Wep::help=WifiWep.html
diff --git a/usr/src/lib/libsecdb/exec_attr.txt b/usr/src/lib/libsecdb/exec_attr.txt
index db47b15bd0..b7cfde648e 100644
--- a/usr/src/lib/libsecdb/exec_attr.txt
+++ b/usr/src/lib/libsecdb/exec_attr.txt
@@ -175,6 +175,8 @@ Network Management:solaris:cmd:::/sbin/dlstat:euid=dladm;egid=sys;
Network Management:solaris:cmd:::/sbin/flowadm:euid=dladm;egid=sys;\
privs=sys_dl_config,net_rawaccess,proc_audit
Network Management:solaris:cmd:::/sbin/flowstat:euid=dladm;egid=sys;
+Network Management:solaris:cmd:::/sbin/ipadm:euid=netadm;egid=netadm;\
+ privs=sys_ip_config,net_rawaccess
Network Management:suser:cmd:::/usr/bin/netstat:uid=0
Network Management:suser:cmd:::/usr/bin/rup:euid=0
Network Management:suser:cmd:::/usr/bin/ruptime:euid=0
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index cb9361c94e..6b1bb7737b 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -129,6 +129,7 @@ HTMLENTS = \
NetworkILBenable.html \
NetworkHeader.html \
NetworkVRRP.html \
+ NetworkInterfaceConfig.html \
WifiConfig.html \
WifiWep.html \
LinkSecurity.html \
diff --git a/usr/src/lib/libsecdb/help/auths/NetworkInterfaceConfig.html b/usr/src/lib/libsecdb/help/auths/NetworkInterfaceConfig.html
new file mode 100644
index 0000000000..afc2f5ebd1
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/NetworkInterfaceConfig.html
@@ -0,0 +1,42 @@
+<html>
+
+<!--
+ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+-->
+
+<head>
+<!--
+meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
+-->
+</head>
+<body>
+When Network Interface Configuration is in the Authorizations
+Included column, it grants permission to create/delete/show network IP
+interfaces on data-links. It also grants permission to set/reset/get tunables on
+protocol modules.
+<p>
+Note that querying configuration information on Network IP interfaces or the
+protocol module properties doesn't require the Network Interface Configuration
+authorization.
+</body>
+</html>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index a8d5fc56b6..a1b5961d09 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -65,7 +65,7 @@ Network Autoconf Admin:::Manage Network Auto-Magic configuration via nwamd:profi
Network Autoconf User:::Network Auto-Magic User:auths=solaris.network.autoconf.read,solaris.network.autoconf.select,solaris.network.autoconf.wlan;help=RtNetAutoconfUser.html
Network ILB:::Manage ILB configuration via ilbadm:auths=solaris.network.ilb.config,solaris.network.ilb.enable;help=RtNetILB.html
Network VRRP:::Manage VRRP instances:auths=solaris.network.vrrp,solaris.smf.manage.vrrp;help=RtNetVRRP.html
-Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns,solaris.smf.manage.ilb;profiles=Network Wifi Management,Inetd Management,Network VRRP,Network Observability;help=RtNetMngmnt.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns,solaris.smf.manage.ilb,solaris.network.interface.config;profiles=Network Wifi Management,Inetd Management,Network VRRP,Network Observability;help=RtNetMngmnt.html
Network Observability:::Allow access to observability devices:privs=net_observability;help=RtNetObservability.html
Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh,solaris.smf.value.tnd,solaris.network.*;profiles=Network Wifi Security,Network Link Security,Network IPsec Management;help=RtNetSecure.html
Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html
diff --git a/usr/src/lib/libsocket/Makefile b/usr/src/lib/libsocket/Makefile
index 18d1f13290..763363b7fd 100644
--- a/usr/src/lib/libsocket/Makefile
+++ b/usr/src/lib/libsocket/Makefile
@@ -18,15 +18,14 @@
#
# CDDL HEADER END
#
-#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
include ../Makefile.lib
+HDRS = libsocket_priv.h
+HDRDIR = common
SUBDIRS = $(MACH)
$(BUILD64)SUBDIRS += $(MACH64)
@@ -45,6 +44,10 @@ TEXT_DOMAIN = SUNW_OST_NETRPC
all clean clobber install lint: $(SUBDIRS)
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
_msg: $(MSGDOMAINPOFILE)
$(POFILE): pofile_MSGFILES
@@ -54,4 +57,5 @@ $(SUBDIRS): FRC
FRC:
+include $(SRC)/lib/Makefile.targ
include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/lib/libsocket/Makefile.com b/usr/src/lib/libsocket/Makefile.com
index cdb99117c5..f1e7a0af99 100644
--- a/usr/src/lib/libsocket/Makefile.com
+++ b/usr/src/lib/libsocket/Makefile.com
@@ -20,7 +20,7 @@
#
#
-# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -32,7 +32,8 @@ INETOBJS = bindresvport.o bootparams_getbyname.o ether_addr.o \
getprotoent.o getprotoent_r.o getservbyname_r.o getservent.o \
getservent_r.o inet_lnaof.o inet_mkaddr.o inet_network.o \
inet6_opt.o inet6_rthdr.o interface_id.o link_addr.o \
- netmasks.o rcmd.o rexec.o ruserpass.o sourcefilter.o
+ netmasks.o rcmd.o rexec.o ruserpass.o sourcefilter.o \
+ getifaddrs.o
SOCKOBJS = _soutil.o sockatmark.o socket.o socketpair.o weaks.o
OBJECTS = $(INETOBJS) $(SOCKOBJS)
diff --git a/usr/src/lib/libsocket/common/libsocket_priv.h b/usr/src/lib/libsocket/common/libsocket_priv.h
new file mode 100644
index 0000000000..8d84ffb5da
--- /dev/null
+++ b/usr/src/lib/libsocket/common/libsocket_priv.h
@@ -0,0 +1,43 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+#ifndef _LIBSOCKET_PRIV_H
+#define _LIBSOCKET_PRIV_H
+
+#include <net/if.h>
+#include <ifaddrs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int getallifaddrs(sa_family_t, struct ifaddrs **, int64_t);
+extern int getallifs(int, sa_family_t, struct lifreq **, int *, int64_t);
+extern int getnetmaskbyaddr(const struct in_addr, struct in_addr *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSOCKET_PRIV_H */
diff --git a/usr/src/lib/libsocket/common/llib-lsocket b/usr/src/lib/libsocket/common/llib-lsocket
index f74c8cea92..104dc78235 100644
--- a/usr/src/lib/libsocket/common/llib-lsocket
+++ b/usr/src/lib/libsocket/common/llib-lsocket
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,10 +19,9 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
/* LINTLIBRARY */
/* PROTOLIB1 */
@@ -38,6 +36,8 @@
#include <sys/ethernet.h>
#include <netdb.h>
#include <net/if_dl.h>
+#include <ifaddrs.h>
+#include <libsocket_priv.h>
/*
* usr/src/lib/libsocket/inet routines not prototyped in the above
@@ -56,7 +56,6 @@ struct in6_addr *__inet6_rthdr_getaddr(void *, int);
/* netmasks.c */
int getnetmaskbynet(const struct in_addr net, struct in_addr *mask);
-int getnetmaskbyaddr(const struct in_addr addr, struct in_addr *mask);
/* ruserpass.c */
void _ruserpass(const char *host, char **aname, char **apass);
diff --git a/usr/src/lib/libsocket/common/mapfile-vers b/usr/src/lib/libsocket/common/mapfile-vers
index 6839f8bb04..b71b64968d 100644
--- a/usr/src/lib/libsocket/common/mapfile-vers
+++ b/usr/src/lib/libsocket/common/mapfile-vers
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -37,6 +37,12 @@
# MAPFILE HEADER END
#
+SUNW_1.7 {
+ global:
+ freeifaddrs;
+ getifaddrs;
+} SUNW_1.6;
+
SUNW_1.6 {
global:
getipv4sourcefilter;
@@ -157,6 +163,8 @@ SUNWprivate_1.3 {
_nss_initf_netmasks;
_nss_initf_proto;
_nss_initf_services;
+ getallifaddrs;
+ getallifs;
str2ether;
str2addr;
str2netent;
diff --git a/usr/src/lib/libsocket/inet/getifaddrs.c b/usr/src/lib/libsocket/inet/getifaddrs.c
new file mode 100644
index 0000000000..893e83f730
--- /dev/null
+++ b/usr/src/lib/libsocket/inet/getifaddrs.c
@@ -0,0 +1,269 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <netdb.h>
+#include <nss_dbdefs.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <libsocket_priv.h>
+
+/*
+ * Create a linked list of `struct ifaddrs' structures, one for each
+ * address that is UP. If successful, store the list in *ifap and
+ * return 0. On errors, return -1 and set `errno'.
+ *
+ * The storage returned in *ifap is allocated dynamically and can
+ * only be properly freed by passing it to `freeifaddrs'.
+ */
+int
+getifaddrs(struct ifaddrs **ifap)
+{
+ int err;
+ char *cp;
+ struct ifaddrs *curr;
+
+ if (ifap == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ *ifap = NULL;
+ err = getallifaddrs(AF_UNSPEC, ifap, LIFC_ENABLED);
+ if (err == 0) {
+ for (curr = *ifap; curr != NULL; curr = curr->ifa_next) {
+ if ((cp = strchr(curr->ifa_name, ':')) != NULL)
+ *cp = '\0';
+ }
+ }
+ return (err);
+}
+
+void
+freeifaddrs(struct ifaddrs *ifa)
+{
+ struct ifaddrs *curr;
+
+ while (ifa != NULL) {
+ curr = ifa;
+ ifa = ifa->ifa_next;
+ free(curr->ifa_name);
+ free(curr->ifa_addr);
+ free(curr->ifa_netmask);
+ free(curr->ifa_dstaddr);
+ free(curr);
+ }
+}
+
+/*
+ * Returns all addresses configured on the system. If flags contain
+ * LIFC_ENABLED, only the addresses that are UP are returned.
+ * Address list that is returned by this function must be freed
+ * using freeifaddrs().
+ */
+int
+getallifaddrs(sa_family_t af, struct ifaddrs **ifap, int64_t flags)
+{
+ struct lifreq *buf = NULL;
+ struct lifreq *lifrp;
+ struct lifreq lifrl;
+ int ret;
+ int s, n, numifs;
+ struct ifaddrs *curr, *prev;
+ sa_family_t lifr_af;
+ int sock4;
+ int sock6;
+ int err;
+
+ if ((sock4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return (-1);
+ if ((sock6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err = errno;
+ close(sock4);
+ errno = err;
+ return (-1);
+ }
+
+retry:
+ /* Get all interfaces from SIOCGLIFCONF */
+ ret = getallifs(sock4, af, &buf, &numifs, (flags & ~LIFC_ENABLED));
+ if (ret != 0)
+ goto fail;
+
+ /*
+ * Loop through the interfaces obtained from SIOCGLIFCOMF
+ * and retrieve the addresses, netmask and flags.
+ */
+ prev = NULL;
+ lifrp = buf;
+ *ifap = NULL;
+ for (n = 0; n < numifs; n++, lifrp++) {
+
+ /* Prepare for the ioctl call */
+ (void) strncpy(lifrl.lifr_name, lifrp->lifr_name,
+ sizeof (lifrl.lifr_name));
+ lifr_af = lifrp->lifr_addr.ss_family;
+ if (af != AF_UNSPEC && lifr_af != af)
+ continue;
+
+ s = (lifr_af == AF_INET ? sock4 : sock6);
+
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0)
+ goto fail;
+ if ((flags & LIFC_ENABLED) && !(lifrl.lifr_flags & IFF_UP))
+ continue;
+
+ /*
+ * Allocate the current list node. Each node contains data
+ * for one ifaddrs structure.
+ */
+ curr = calloc(1, sizeof (struct ifaddrs));
+ if (curr == NULL)
+ goto fail;
+
+ if (prev != NULL) {
+ prev->ifa_next = curr;
+ } else {
+ /* First node in the linked list */
+ *ifap = curr;
+ }
+ prev = curr;
+
+ curr->ifa_flags = lifrl.lifr_flags;
+ if ((curr->ifa_name = strdup(lifrp->lifr_name)) == NULL)
+ goto fail;
+
+ curr->ifa_addr = malloc(sizeof (struct sockaddr_storage));
+ if (curr->ifa_addr == NULL)
+ goto fail;
+ *curr->ifa_addr = lifrp->lifr_addr;
+
+ /* Get the netmask */
+ if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifrl) < 0)
+ goto fail;
+ curr->ifa_netmask = malloc(sizeof (struct sockaddr_storage));
+ if (curr->ifa_netmask == NULL)
+ goto fail;
+ *curr->ifa_netmask = lifrl.lifr_addr;
+
+ /* Get the destination for a pt-pt interface */
+ if (curr->ifa_flags & IFF_POINTOPOINT) {
+ if (ioctl(s, SIOCGLIFDSTADDR, (caddr_t)&lifrl) < 0)
+ goto fail;
+ curr->ifa_dstaddr = malloc(
+ sizeof (struct sockaddr_storage));
+ if (curr->ifa_dstaddr == NULL)
+ goto fail;
+ *curr->ifa_dstaddr = lifrl.lifr_addr;
+ } else if (curr->ifa_flags & IFF_BROADCAST) {
+ if (ioctl(s, SIOCGLIFBRDADDR, (caddr_t)&lifrl) < 0)
+ goto fail;
+ curr->ifa_broadaddr = malloc(
+ sizeof (struct sockaddr_storage));
+ if (curr->ifa_broadaddr == NULL)
+ goto fail;
+ *curr->ifa_broadaddr = lifrl.lifr_addr;
+ }
+
+ }
+ free(buf);
+ close(sock4);
+ close(sock6);
+ return (0);
+fail:
+ err = errno;
+ free(buf);
+ freeifaddrs(*ifap);
+ *ifap = NULL;
+ if (err == ENXIO)
+ goto retry;
+ close(sock4);
+ close(sock6);
+ errno = err;
+ return (-1);
+}
+
+/*
+ * Do a SIOCGLIFCONF and store all the interfaces in `buf'.
+ */
+int
+getallifs(int s, sa_family_t af, struct lifreq **lifr, int *numifs,
+ int64_t lifc_flags)
+{
+ struct lifnum lifn;
+ struct lifconf lifc;
+ size_t bufsize;
+ char *tmp;
+ caddr_t *buf = (caddr_t *)lifr;
+
+ lifn.lifn_family = af;
+ lifn.lifn_flags = lifc_flags;
+
+ *buf = NULL;
+retry:
+ if (ioctl(s, SIOCGLIFNUM, &lifn) < 0)
+ goto fail;
+
+ /*
+ * When calculating the buffer size needed, add a small number
+ * of interfaces to those we counted. We do this to capture
+ * the interface status of potential interfaces which may have
+ * been plumbed between the SIOCGLIFNUM and the SIOCGLIFCONF.
+ */
+ bufsize = (lifn.lifn_count + 4) * sizeof (struct lifreq);
+
+ if ((tmp = realloc(*buf, bufsize)) == NULL)
+ goto fail;
+
+ *buf = tmp;
+ lifc.lifc_family = af;
+ lifc.lifc_flags = lifc_flags;
+ lifc.lifc_len = bufsize;
+ lifc.lifc_buf = *buf;
+ if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
+ goto fail;
+
+ *numifs = lifc.lifc_len / sizeof (struct lifreq);
+ if (*numifs >= (lifn.lifn_count + 4)) {
+ /*
+ * If every entry was filled, there are probably
+ * more interfaces than (lifn.lifn_count + 4).
+ * Redo the ioctls SIOCGLIFNUM and SIOCGLIFCONF to
+ * get all the interfaces.
+ */
+ goto retry;
+ }
+ return (0);
+fail:
+ free(*buf);
+ *buf = NULL;
+ return (-1);
+}