diff options
author | Girish Moodalbail <Girish.Moodalbail@Sun.COM> | 2010-03-26 17:53:11 -0400 |
---|---|---|
committer | Girish Moodalbail <Girish.Moodalbail@Sun.COM> | 2010-03-26 17:53:11 -0400 |
commit | 6e91bba0d6c6bdabbba62cefae583715a4a58e2a (patch) | |
tree | e10bc428e6a27ac87b541b72769d8095d64e894f /usr/src | |
parent | 00a57bdfe7eeb62d10d0c0b3aab64d24a4d89287 (diff) | |
download | illumos-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')
127 files changed, 20717 insertions, 4110 deletions
diff --git a/usr/src/Makefile.lint b/usr/src/Makefile.lint index 2182a61ff9..baaf43922c 100644 --- a/usr/src/Makefile.lint +++ b/usr/src/Makefile.lint @@ -376,6 +376,7 @@ COMMON_SUBDIRS = \ lib/libinetsvc \ lib/libinetutil \ lib/libinstzones \ + lib/libipadm \ lib/libipmi \ lib/libipmp \ lib/libipp \ diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs index ce20527857..a4131e292e 100644 --- a/usr/src/Targetdirs +++ b/usr/src/Targetdirs @@ -118,6 +118,7 @@ DIRS= \ /etc/hal/fdi/preprobe/10osvendor \ /etc/hal/fdi/preprobe/20thirdparty \ /etc/hal/fdi/preprobe/30user \ + /etc/ipadm \ /etc/iscsi \ /etc/rpcsec \ /etc/security \ diff --git a/usr/src/cmd/cmd-inet/lib/Makefile b/usr/src/cmd/cmd-inet/lib/Makefile index f497fc41b5..0c527437e0 100644 --- a/usr/src/cmd/cmd-inet/lib/Makefile +++ b/usr/src/cmd/cmd-inet/lib/Makefile @@ -23,12 +23,12 @@ # Use is subject to license terms. # -SUBDIRS= nwamd netcfgd -MSGSUBDIRS= nwamd +SUBDIRS= ipmgmtd nwamd netcfgd +MSGSUBDIRS= ipmgmtd nwamd -include ../../Makefile.cmd +include ../../Makefile.cmd -POFILES= $(MSGSUBDIRS:%=%/%.po) +POFILES= $(MSGSUBDIRS:%=%/%.po) POFILE= lib.po all:= TARGET= all @@ -46,4 +46,4 @@ $(SUBDIRS): FRC FRC: -include ../Makefile.msg +include ../Makefile.msg diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile new file mode 100644 index 0000000000..344fb9e6fc --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile @@ -0,0 +1,90 @@ +# +# 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. +# + +# Needed for ROOTFS_LIBDIR definition +include ../../../../lib/Makefile.lib + +PROG= ipmgmtd +OBJS= ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o +SRCS= $(OBJS:.o=.c) +SVCMETHOD= net-ipmgmt +MANIFEST= network-ipmgmt.xml +CFGFILES= ipadm.conf + +# Needed for ROOTETC definition +include ../../../Makefile.cmd + +POFILE= $(PROG).po +POFILES= ipmgmt_main.po ipmgmt_door.po + +ROOTCFGDIR= $(ROOTETC)/ipadm +ROOTCFGFILES= $(CFGFILES:%=$(ROOTCFGDIR)/%) +ROOTMANIFESTDIR= $(ROOTSVCNETWORK) + +$(ROOTCFGFILES) := OWNER= ipadm +$(ROOTCFGFILES) := GROUP= sys +$(ROOTCFGFILES) := FILEMODE= 644 + +ROOTCMDDIR= $(ROOTFS_LIBDIR)/inet + +LDLIBS += -lipadm -lnvpair -lsecdb -lnsl -lumem + +# +# Instrument ipmgmtd with CTF data to ease debugging. +# +CTFCONVERT_HOOK = && $(CTFCONVERT_O) +CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS) +$(OBJS) := CFLAGS += $(CTF_FLAGS) + +.KEEP_STATE: + +.PARALLEL: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(CTFMERGE_HOOK) + $(POST_PROCESS) + +install: $(ROOTCMD) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTCFGDIR) \ + $(ROOTCFGFILES) + +check: $(SRCS) $(HEADERS) $(CHKMANIFEST) + $(CSTYLE) -cpP $(SRCS) $(HEADERS) + +$(ROOTCMD): $(PROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +$(ROOTCFGDIR): + $(INS.dir) + +$(ROOTCFGDIR)/%: $(ROOTCFGDIR) % + $(INS.file) + +include ../../../Makefile.targ +include ../../Makefile.msg diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipadm.conf b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipadm.conf new file mode 100644 index 0000000000..eeaee090b6 --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipadm.conf @@ -0,0 +1,26 @@ +# +# 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. +# +# DO NOT EDIT OR PARSE THIS FILE! +# +# Use the ipadm(1m) command to change the contents of this file. diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c new file mode 100644 index 0000000000..d2996faf53 --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c @@ -0,0 +1,845 @@ +/* + * 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. + */ + +/* + * Main door handler functions used by ipmgmtd to process the different door + * call requests, issued by the library libipadm.so. + */ + +#include <alloca.h> +#include <pwd.h> +#include <auth_attr.h> +#include <secdb.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> +#include <libnvpair.h> +#include "ipmgmt_impl.h" + +/* Handler declaration for each door command */ +typedef void ipmgmt_door_handler_t(void *argp); + +static ipmgmt_door_handler_t ipmgmt_getaddr_handler, + ipmgmt_getprop_handler, + ipmgmt_getif_handler, + ipmgmt_initif_handler, + ipmgmt_aobjop_handler, + ipmgmt_resetaddr_handler, + ipmgmt_setif_handler, + ipmgmt_resetif_handler, + ipmgmt_resetprop_handler, + ipmgmt_setaddr_handler, + ipmgmt_setprop_handler; + +typedef struct ipmgmt_door_info_s { + uint_t idi_cmd; + boolean_t idi_set; + ipmgmt_door_handler_t *idi_handler; +} ipmgmt_door_info_t; + +/* maps door commands to door handler functions */ +static ipmgmt_door_info_t i_ipmgmt_door_info_tbl[] = { + { IPMGMT_CMD_SETPROP, B_TRUE, ipmgmt_setprop_handler }, + { IPMGMT_CMD_SETIF, B_TRUE, ipmgmt_setif_handler }, + { IPMGMT_CMD_SETADDR, B_TRUE, ipmgmt_setaddr_handler }, + { IPMGMT_CMD_GETPROP, B_FALSE, ipmgmt_getprop_handler }, + { IPMGMT_CMD_GETIF, B_FALSE, ipmgmt_getif_handler }, + { IPMGMT_CMD_GETADDR, B_FALSE, ipmgmt_getaddr_handler }, + { IPMGMT_CMD_RESETIF, B_TRUE, ipmgmt_resetif_handler }, + { IPMGMT_CMD_RESETADDR, B_TRUE, ipmgmt_resetaddr_handler }, + { IPMGMT_CMD_RESETPROP, B_TRUE, ipmgmt_resetprop_handler }, + { IPMGMT_CMD_INITIF, B_TRUE, ipmgmt_initif_handler }, + { IPMGMT_CMD_ADDROBJ_LOOKUPADD, B_TRUE, ipmgmt_aobjop_handler }, + { IPMGMT_CMD_ADDROBJ_ADD, B_TRUE, ipmgmt_aobjop_handler }, + { IPMGMT_CMD_AOBJNAME2ADDROBJ, B_FALSE, ipmgmt_aobjop_handler }, + { IPMGMT_CMD_LIF2ADDROBJ, B_FALSE, ipmgmt_aobjop_handler }, + { 0, 0, NULL }, +}; + +/* + * The main server procedure function that gets invoked for any of the incoming + * door commands. Inside this function we identify the incoming command and + * invoke the right door handler function. + */ +/* ARGSUSED */ +void +ipmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, + uint_t n_desc) +{ + ipmgmt_door_info_t *infop = NULL; + ipmgmt_retval_t retval; + int i; + uint_t err; + ucred_t *cred = NULL; + + for (i = 0; i_ipmgmt_door_info_tbl[i].idi_cmd != 0; i++) { + if (i_ipmgmt_door_info_tbl[i].idi_cmd == + ((ipmgmt_arg_t *)(void *)argp)->ia_cmd) { + infop = &i_ipmgmt_door_info_tbl[i]; + break; + } + } + + if (infop == NULL) { + ipmgmt_log(LOG_ERR, "Invalid door command specified"); + err = EINVAL; + goto fail; + } + + /* check for solaris.network.interface.config authorization */ + if (infop->idi_set) { + uid_t uid; + struct passwd pwd; + char buf[1024]; + + if (door_ucred(&cred) != 0) { + err = errno; + ipmgmt_log(LOG_ERR, "Could not get user credentials."); + goto fail; + } + uid = ucred_getruid(cred); + if ((int)uid < 0) { + err = errno; + ipmgmt_log(LOG_ERR, "Could not get user id."); + goto fail; + } + if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == + NULL) { + err = errno; + ipmgmt_log(LOG_ERR, "Could not get password entry."); + goto fail; + } + if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, + pwd.pw_name) != 1) { + err = EPERM; + ipmgmt_log(LOG_ERR, "Not authorized for operation."); + goto fail; + } + ucred_free(cred); + } + + /* individual handlers take care of calling door_return */ + infop->idi_handler((void *)argp); + return; +fail: + ucred_free(cred); + retval.ir_err = err; + (void) door_return((char *)&retval, sizeof (retval), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_GETPROP. It retrieves the persisted + * property value for the given property. + */ +static void +ipmgmt_getprop_handler(void *argp) +{ + ipmgmt_prop_arg_t *pargp = argp; + ipmgmt_getprop_rval_t rval, *rvalp = &rval; + + assert(pargp->ia_cmd == IPMGMT_CMD_GETPROP); + + rvalp->ir_err = ipmgmt_db_walk(ipmgmt_db_getprop, pargp, IPADM_DB_READ); + if (rvalp->ir_err == 0) + (void) strlcpy(rvalp->ir_pval, pargp->ia_pval, + sizeof (rvalp->ir_pval)); + (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_SETPROP. It persists the property value + * for the given property in the DB. + */ +static void +ipmgmt_setprop_handler(void *argp) +{ + ipmgmt_prop_arg_t *pargp = argp; + ipmgmt_retval_t rval; + ipadm_dbwrite_cbarg_t cb; + nvlist_t *nvl = NULL; + int err; + + assert(pargp->ia_cmd == IPMGMT_CMD_SETPROP); + + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto fail; + if (pargp->ia_module[0] != '\0' && + (err = nvlist_add_string(nvl, IPADM_NVP_PROTONAME, + pargp->ia_module)) != 0) { + goto fail; + } + if (pargp->ia_ifname[0] != '\0' && + (err = nvlist_add_string(nvl, IPADM_NVP_IFNAME, + pargp->ia_ifname)) != 0) + goto fail; + if (pargp->ia_aobjname[0] != '\0' && + (err = nvlist_add_string(nvl, IPADM_NVP_AOBJNAME, + pargp->ia_aobjname)) != 0) + goto fail; + if ((err = nvlist_add_string(nvl, pargp->ia_pname, + pargp->ia_pval)) != 0) + goto fail; + + cb.dbw_nvl = nvl; + cb.dbw_flags = pargp->ia_flags; + err = ipmgmt_db_walk(ipmgmt_db_update, &cb, IPADM_DB_WRITE); +fail: + nvlist_free(nvl); + rval.ir_err = err; + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} + +/* + * Helper function for ipmgmt_setaddr_handler(). + * It converts the nvlist_t, `nvl', to aobjmap node `nodep'. + */ +static int +i_ipmgmt_nvl2aobjnode(nvlist_t *nvl, ipmgmt_aobjmap_t *nodep) +{ + char *aobjname = NULL, *ifname = NULL; + int32_t lnum; + nvlist_t *nvladdr; + struct sockaddr_storage addr; + uint_t n; + sa_family_t af = AF_UNSPEC; + ipadm_addr_type_t addrtype = IPADM_ADDR_NONE; + int err = 0; + + /* + * Retrieve all the information needed to build '*nodep' from + * nvlist_t nvl. + */ + if ((err = nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME, + &aobjname)) != 0 || + (err = nvlist_lookup_string(nvl, IPADM_NVP_IFNAME, &ifname)) != 0 || + (err = nvlist_lookup_int32(nvl, IPADM_NVP_LIFNUM, &lnum)) != 0) { + return (err); + } + if (nvlist_exists(nvl, IPADM_NVP_IPV4ADDR)) { + af = AF_INET; + addrtype = IPADM_ADDR_STATIC; + } else if (nvlist_exists(nvl, IPADM_NVP_DHCP)) { + af = AF_INET; + addrtype = IPADM_ADDR_DHCP; + } else if (nvlist_exists(nvl, IPADM_NVP_IPV6ADDR)) { + af = AF_INET6; + addrtype = IPADM_ADDR_STATIC; + } else if (nvlist_lookup_nvlist(nvl, IPADM_NVP_INTFID, &nvladdr) == 0) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + uint8_t *addr6; + uint32_t plen; + + af = AF_INET6; + addrtype = IPADM_ADDR_IPV6_ADDRCONF; + if (nvlist_lookup_uint32(nvladdr, IPADM_NVP_PREFIXLEN, + &plen) != 0) + return (EINVAL); + if (plen != 0) { + if (nvlist_lookup_uint8_array(nvladdr, + IPADM_NVP_IPNUMADDR, &addr6, &n) != 0) + return (EINVAL); + bcopy(addr6, &sin6->sin6_addr, n); + } else { + bzero(&sin6->sin6_addr, sizeof (sin6->sin6_addr)); + } + } + + /* + * populate the `*nodep' with retrieved values. + */ + (void) strlcpy(nodep->am_ifname, ifname, sizeof (nodep->am_ifname)); + (void) strlcpy(nodep->am_aobjname, aobjname, + sizeof (nodep->am_aobjname)); + nodep->am_lnum = lnum; + nodep->am_family = af; + nodep->am_atype = addrtype; + if (addrtype == IPADM_ADDR_IPV6_ADDRCONF) { + nodep->am_linklocal = B_TRUE; + nodep->am_ifid = addr; + } + nodep->am_next = NULL; + + /* + * Do not store logical interface number in persistent store as it + * takes different value on reboot. So remove it from `nvl'. + */ + if (nvlist_exists(nvl, IPADM_NVP_LIFNUM)) + (void) nvlist_remove(nvl, IPADM_NVP_LIFNUM, DATA_TYPE_INT32); + + return (0); +} + +/* + * Handles the door command IPMGMT_CMD_SETADDR. It adds a new address object + * node to the list `aobjmap' and then persists the address information in the + * DB. + */ +static void +ipmgmt_setaddr_handler(void *argp) +{ + ipmgmt_setaddr_arg_t *sargp = argp; + ipmgmt_retval_t rval; + ipmgmt_aobjmap_t node; + nvlist_t *nvl = NULL; + char *nvlbuf; + size_t nvlsize = sargp->ia_nvlsize; + uint32_t flags = sargp->ia_flags; + int err = 0; + + nvlbuf = (char *)argp + sizeof (ipmgmt_setaddr_arg_t); + if ((err = nvlist_unpack(nvlbuf, nvlsize, &nvl, NV_ENCODE_NATIVE)) != 0) + goto ret; + if (flags & (IPMGMT_ACTIVE|IPMGMT_INIT)) { + if ((err = i_ipmgmt_nvl2aobjnode(nvl, &node)) != 0) + goto ret; + if (flags & IPMGMT_INIT) + node.am_flags = (IPMGMT_ACTIVE|IPMGMT_PERSIST); + else + node.am_flags = flags; + if ((err = ipmgmt_aobjmap_op(&node, ADDROBJ_ADD)) != 0) + goto ret; + } + if (flags & IPMGMT_PERSIST) { + ipadm_dbwrite_cbarg_t cb; + + cb.dbw_nvl = nvl; + cb.dbw_flags = 0; + err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE); + } +ret: + nvlist_free(nvl); + rval.ir_err = err; + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} + +/* + * Handles the door commands that modify the `aobjmap' structure. + * + * IPMGMT_CMD_ADDROBJ_LOOKUPADD - places a stub address object in `aobjmap' + * after ensuring that the namespace is not taken. If required, also + * generates an `aobjname' for address object for the library to use. + * IPMGMT_CMD_ADDROBJ_ADD - add/update address object in `aobjmap' + * IPMGMT_CMD_LIF2ADDROBJ - given a logical interface, return address object + * associated with that logical interface. + * IPMGMT_CMD_AOBJNAME2ADDROBJ - given an address object name return logical + * interface associated with that address object. + */ +static void +ipmgmt_aobjop_handler(void *argp) +{ + ipmgmt_aobjop_arg_t *largp = argp; + ipmgmt_retval_t rval; + ipmgmt_aobjop_rval_t aobjrval; + void *rvalp; + size_t rsize; + ipmgmt_aobjmap_t node; + int err = 0; + char *ifname = largp->ia_ifname; + char *aobjname = largp->ia_aobjname; + int32_t lnum = largp->ia_lnum; + sa_family_t af = largp->ia_family; + ipadm_addr_type_t atype = largp->ia_atype; + ipmgmt_aobjmap_t *head; + + switch (largp->ia_cmd) { + case IPMGMT_CMD_ADDROBJ_LOOKUPADD: + rsize = sizeof (ipmgmt_aobjop_rval_t); + rvalp = &aobjrval; + bzero(&node, sizeof (node)); + (void) strlcpy(node.am_aobjname, aobjname, + sizeof (node.am_aobjname)); + (void) strlcpy(node.am_ifname, ifname, + sizeof (node.am_ifname)); + node.am_family = af; + /* no logical number is associated with this addrobj yet */ + node.am_lnum = -1; + /* The address object is not persisted yet. */ + node.am_flags = IPMGMT_ACTIVE; + err = ipmgmt_aobjmap_op(&node, ADDROBJ_LOOKUPADD); + if (err == 0) { + (void) strlcpy(aobjrval.ir_aobjname, node.am_aobjname, + sizeof (aobjrval.ir_aobjname)); + } + break; + case IPMGMT_CMD_ADDROBJ_ADD: + rsize = sizeof (ipmgmt_retval_t); + rvalp = &rval; + if (aobjname[0] == '\0' || ifname[0] == '\0' || lnum == -1 || + af == AF_UNSPEC) { + err = EINVAL; + break; + } + bzero(&node, sizeof (node)); + (void) strlcpy(node.am_aobjname, aobjname, + sizeof (node.am_aobjname)); + (void) strlcpy(node.am_ifname, ifname, + sizeof (node.am_ifname)); + node.am_atype = atype; + node.am_lnum = lnum; + node.am_family = af; + /* The address object is not persisted. */ + node.am_flags = IPMGMT_ACTIVE; + err = ipmgmt_aobjmap_op(&node, ADDROBJ_ADD); + break; + case IPMGMT_CMD_AOBJNAME2ADDROBJ: + rsize = sizeof (ipmgmt_aobjop_rval_t); + rvalp = &aobjrval; + bzero(&aobjrval, sizeof (aobjrval)); + if (aobjname[0] == '\0') { + err = EINVAL; + break; + } + (void) pthread_rwlock_rdlock(&aobjmap.aobjmap_rwlock); + head = aobjmap.aobjmap_head; + for (; head; head = head->am_next) { + if (strcmp(head->am_aobjname, aobjname) != 0) + continue; + /* + * For an auto-configured interface, return + * the lifnum that has the link-local on it. + * Other logical interfaces were created for + * prefixes and dhcpv6 addresses and do not + * have am_ifid set. + */ + if (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF || + head->am_linklocal) { + break; + } + } + if (head == NULL) { + err = ENOENT; + (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); + break; + } + (void) strlcpy(aobjrval.ir_ifname, head->am_ifname, + sizeof (aobjrval.ir_ifname)); + aobjrval.ir_lnum = head->am_lnum; + aobjrval.ir_family = head->am_family; + aobjrval.ir_flags = head->am_flags; + aobjrval.ir_atype = head->am_atype; + if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF && + head->am_linklocal) + aobjrval.ir_ifid = head->am_ifid; + (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); + break; + case IPMGMT_CMD_LIF2ADDROBJ: + rsize = sizeof (ipmgmt_aobjop_rval_t); + rvalp = &aobjrval; + bzero(&aobjrval, sizeof (aobjrval)); + if (ifname[0] == '\0') { + err = EINVAL; + break; + } + (void) pthread_rwlock_rdlock(&aobjmap.aobjmap_rwlock); + head = aobjmap.aobjmap_head; + for (; head; head = head->am_next) { + if (strcmp(head->am_ifname, ifname) == 0 && + head->am_lnum == lnum && + head->am_family == af) { + break; + } + } + if (head == NULL) { + err = ENOENT; + (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); + break; + } + (void) strlcpy(aobjrval.ir_aobjname, head->am_aobjname, + sizeof (aobjrval.ir_aobjname)); + aobjrval.ir_atype = head->am_atype; + aobjrval.ir_flags = head->am_flags; + (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); + break; + default: + rsize = sizeof (ipmgmt_retval_t); + rvalp = &rval; + err = EINVAL; + } + ((ipmgmt_retval_t *)rvalp)->ir_err = err; + (void) door_return((char *)rvalp, rsize, NULL, 0); +} + +/* + * Given an interface name and family, deletes all the address objects + * associated with it. + */ +void +i_ipmgmt_delif_aobjs(char *ifname, sa_family_t af, uint32_t flags) +{ + ipmgmt_aobjmap_t *head, *next, *prev; + ipadm_db_op_t db_op; + + prev = NULL; + + (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); + head = aobjmap.aobjmap_head; + for (; head; head = next) { + next = head->am_next; + if (strcmp(head->am_ifname, ifname) != 0 || + head->am_family != af) { + prev = head; + continue; + } + + if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) && + flags == IPMGMT_ACTIVE) { + /* + * If the addres is present in both active and + * persistent store, and if we are performing + * a temporary delete, we update the node to + * indicate that the address is only present in + * persistent store and we proceed. Otherwise + * we always delete the node from aobjmap. + */ + head->am_flags &= ~IPMGMT_ACTIVE; + head->am_lnum = -1; + db_op = IPADM_DB_WRITE; + } else { + db_op = IPADM_DB_DELETE; + if (prev == NULL) + aobjmap.aobjmap_head = next; + else + prev->am_next = next; + } + (void) ipmgmt_persist_aobjmap(head, db_op); + if (db_op == IPADM_DB_DELETE) + free(head); + } + (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); +} + +/* + * Handles the door command IPMGMT_CMD_SETIF. It persists the interface + * information in the DB. + */ +static void +ipmgmt_setif_handler(void *argp) +{ + ipmgmt_if_arg_t *sargp = argp; + ipmgmt_retval_t rval; + ipadm_dbwrite_cbarg_t cb; + uint32_t flags = sargp->ia_flags; + nvlist_t *nvl = NULL; + int err = 0; + char strval[IPMGMT_STRSIZE]; + + if (!(flags & IPMGMT_PERSIST) || sargp->ia_family == AF_UNSPEC || + sargp->ia_ifname[0] == '\0') { + err = EINVAL; + goto ret; + } + if ((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) + goto ret; + if ((err = nvlist_add_string(nvl, IPADM_NVP_IFNAME, + sargp->ia_ifname)) != 0) + goto ret; + (void) snprintf(strval, IPMGMT_STRSIZE, "%d", sargp->ia_family); + if ((err = nvlist_add_string(nvl, IPADM_NVP_FAMILY, strval)) != 0) + goto ret; + cb.dbw_nvl = nvl; + cb.dbw_flags = 0; + err = ipmgmt_db_walk(ipmgmt_db_add, &cb, IPADM_DB_WRITE); +ret: + rval.ir_err = err; + nvlist_free(nvl); + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_RESETIF. For the given interface, + * deletes all the persisted interface configuration. It also deletes, from + * `aobjmap', all the address objects configured on the given interface. + */ +static void +ipmgmt_resetif_handler(void *argp) +{ + ipmgmt_if_arg_t *rargp = argp; + ipmgmt_retval_t rval; + ipmgmt_if_cbarg_t cbarg; + uint32_t flags = rargp->ia_flags; + int err = 0; + + cbarg.cb_family = rargp->ia_family; + cbarg.cb_ifname = rargp->ia_ifname; + if (flags & IPMGMT_PERSIST) + err = ipmgmt_db_walk(ipmgmt_db_resetif, &cbarg, + IPADM_DB_DELETE); + + if (flags & IPMGMT_ACTIVE) + i_ipmgmt_delif_aobjs(rargp->ia_ifname, rargp->ia_family, + flags); + + rval.ir_err = err; + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_RESETADDR. For the given addrobj + * deletes all the persisted addrobj configuration. It also deletes the + * corresponding node, from `aobjmap'. + */ +static void +ipmgmt_resetaddr_handler(void *argp) +{ + ipmgmt_addr_arg_t *rargp = argp; + ipmgmt_retval_t rval; + ipmgmt_aobjmap_t node; + uint32_t flags = rargp->ia_flags; + int err = 0; + ipmgmt_resetaddr_cbarg_t cbarg; + + cbarg.cb_aobjname = rargp->ia_aobjname; + + if (flags & IPMGMT_PERSIST) + err = ipmgmt_db_walk(ipmgmt_db_resetaddr, &cbarg, + IPADM_DB_DELETE); + + if (flags & IPMGMT_ACTIVE) { + bzero(&node, sizeof (node)); + (void) strlcpy(node.am_aobjname, rargp->ia_aobjname, + sizeof (node.am_aobjname)); + + /* + * am_lnum is used only for IPv6 autoconf case, since there + * can be multiple nodes with the same aobjname. + */ + node.am_lnum = rargp->ia_lnum; + node.am_flags = flags; + (void) ipmgmt_aobjmap_op(&node, ADDROBJ_DELETE); + } + + rval.ir_err = err; + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_GETADDR. It retrieves the persisted + * address for a given `gargp->ia_aobjname'. If it is not defined then it + * retrieves all the addresses configured on `gargp->ia_ifname'. The + * "ipadm show-addr addrobj" or "ipadm show-addr <ifname>/\*" will call this + * handler through library. + */ +static void +ipmgmt_getaddr_handler(void *argp) +{ + size_t buflen, onvlsize; + char *buf, *onvlbuf; + ipmgmt_getaddr_arg_t *gargp = argp; + ipmgmt_getaddr_cbarg_t cbarg; + ipmgmt_get_rval_t rval, *rvalp = &rval; + int err = 0; + + cbarg.cb_ifname = gargp->ia_ifname; + cbarg.cb_aobjname = gargp->ia_aobjname; + cbarg.cb_ocnt = 0; + if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0) + goto fail; + err = ipmgmt_db_walk(ipmgmt_db_getaddr, &cbarg, IPADM_DB_READ); + if (err == ENOENT && cbarg.cb_ocnt > 0) { + /* + * If there is atleast one entry in the nvlist, + * do not return error. + */ + err = 0; + } + if (err != 0) + goto fail; + + if ((err = nvlist_size(cbarg.cb_onvl, &onvlsize, + NV_ENCODE_NATIVE)) != 0) { + goto fail; + } + buflen = onvlsize + sizeof (ipmgmt_get_rval_t); + /* + * We cannot use malloc() here because door_return never returns, and + * memory allocated by malloc() would get leaked. Use alloca() instead. + */ + buf = alloca(buflen); + onvlbuf = buf + sizeof (ipmgmt_get_rval_t); + if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &onvlsize, + NV_ENCODE_NATIVE, 0)) != 0) { + goto fail; + } + nvlist_free(cbarg.cb_onvl); + rvalp = (ipmgmt_get_rval_t *)(void *)buf; + rvalp->ir_err = 0; + rvalp->ir_nvlsize = onvlsize; + + (void) door_return(buf, buflen, NULL, 0); + return; +fail: + nvlist_free(cbarg.cb_onvl); + rvalp->ir_err = err; + (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_RESETPROP. It deletes the property line + * from the DB. + */ +static void +ipmgmt_resetprop_handler(void *argp) +{ + ipmgmt_prop_arg_t *pargp = argp; + ipmgmt_retval_t rval; + + assert(pargp->ia_cmd == IPMGMT_CMD_RESETPROP); + + rval.ir_err = ipmgmt_db_walk(ipmgmt_db_resetprop, pargp, + IPADM_DB_DELETE); + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_GETIF. It retrieves the name of all the + * persisted interfaces and the IP protocols (IPv4 or IPv6) they support. + */ +static void +ipmgmt_getif_handler(void *argp) +{ + ipmgmt_getif_arg_t *getif = argp; + ipmgmt_getif_rval_t *rvalp; + ipmgmt_retval_t rval; + ipmgmt_getif_cbarg_t cbarg; + ipadm_if_info_t *ifp, *rifp, *curifp; + int i, err = 0, count = 0; + size_t rbufsize; + + assert(getif->ia_cmd == IPMGMT_CMD_GETIF); + + bzero(&cbarg, sizeof (cbarg)); + cbarg.cb_ifname = getif->ia_ifname; + err = ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); + if (err == ENOENT && cbarg.cb_ifinfo) { + /* + * If there is atleast one entry in the nvlist, + * do not return error. + */ + err = 0; + } + if (err != 0) { + rval.ir_err = err; + (void) door_return((char *)&rval, sizeof (rval), NULL, 0); + return; + } + + /* allocate sufficient buffer to return the interface info */ + for (ifp = cbarg.cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) + ++count; + rbufsize = sizeof (*rvalp) + count * sizeof (*ifp); + rvalp = alloca(rbufsize); + bzero(rvalp, rbufsize); + + rvalp->ir_ifcnt = count; + rifp = rvalp->ir_ifinfo; + ifp = cbarg.cb_ifinfo; + + /* + * copy the interface info to buffer allocated on stack. The reason + * we do this is to avoid memory leak, as door_return() would never + * return + */ + for (i = 0; i < count; i++) { + rifp = rvalp->ir_ifinfo + i; + (void) bcopy(ifp, rifp, sizeof (*rifp)); + rifp->ifi_next = NULL; + curifp = ifp->ifi_next; + free(ifp); + ifp = curifp; + } + rvalp->ir_err = err; + (void) door_return((char *)rvalp, rbufsize, NULL, 0); +} + +/* + * Handles the door command IPMGMT_CMD_INITIF. It retrieves all the persisted + * interface configuration (interface properties and addresses), for all those + * interfaces that need to be initialized. + */ +static void +ipmgmt_initif_handler(void *argp) +{ + ipmgmt_initif_arg_t *initif = argp; + size_t buflen, nvlsize; + char *buf = NULL, *onvlbuf, *invlbuf; + ipmgmt_get_rval_t rval, *rvalp = &rval; + ipmgmt_initif_cbarg_t cbarg; + int err; + + assert(initif->ia_cmd == IPMGMT_CMD_INITIF); + + bzero(&cbarg, sizeof (cbarg)); + invlbuf = (char *)argp + sizeof (ipmgmt_initif_arg_t); + nvlsize = initif->ia_nvlsize; + err = nvlist_unpack(invlbuf, nvlsize, &cbarg.cb_invl, NV_ENCODE_NATIVE); + if (err != 0) + goto fail; + + cbarg.cb_family = initif->ia_family; + if (nvlist_alloc(&cbarg.cb_onvl, NV_UNIQUE_NAME, 0) != 0) + goto fail; + + err = ipmgmt_db_walk(ipmgmt_db_initif, &cbarg, IPADM_DB_READ); + if (err == ENOENT && cbarg.cb_ocnt > 0) { + /* + * If there is atleast one entry in the nvlist, + * do not return error. + */ + err = 0; + } + if (err != 0) + goto fail; + + if ((err = nvlist_size(cbarg.cb_onvl, &nvlsize, NV_ENCODE_NATIVE)) != 0) + goto fail; + buflen = nvlsize + sizeof (ipmgmt_get_rval_t); + /* + * We cannot use malloc() here because door_return never returns, and + * memory allocated by malloc() would get leaked. Use alloca() instead. + */ + buf = alloca(buflen); + onvlbuf = buf + sizeof (ipmgmt_get_rval_t); + if ((err = nvlist_pack(cbarg.cb_onvl, &onvlbuf, &nvlsize, + NV_ENCODE_NATIVE, 0)) != 0) { + goto fail; + } + nvlist_free(cbarg.cb_invl); + nvlist_free(cbarg.cb_onvl); + rvalp = (ipmgmt_get_rval_t *)(void *)buf; + rvalp->ir_err = 0; + rvalp->ir_nvlsize = nvlsize; + + (void) door_return(buf, buflen, NULL, 0); + return; +fail: + nvlist_free(cbarg.cb_invl); + nvlist_free(cbarg.cb_onvl); + rvalp->ir_err = err; + (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); +} diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h new file mode 100644 index 0000000000..7ed45205a3 --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h @@ -0,0 +1,152 @@ +/* + * 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 _IPMGMT_IMPL_H +#define _IPMGMT_IMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <net/if.h> +#include <libnvpair.h> +#include <libipadm.h> +#include <ipadm_ipmgmt.h> +#include <syslog.h> +#include <pthread.h> + +#define IPMGMT_STRSIZE 256 + +/* ipmgmt_door.c */ +extern void ipmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t); + +/* ipmgmt_util.c */ +extern void ipmgmt_log(int, const char *, ...); + +/* ipmgmt_persist.c */ + +/* + * following are the list of DB walker callback functions and the callback + * arguments for each of the callback functions used by the daemon + */ +/* following functions take 'ipmgmt_prop_arg_t' as the callback argument */ +extern db_wfunc_t ipmgmt_db_getprop, ipmgmt_db_resetprop; + +/* following functions take ipadm_dbwrite_cbarg_t as callback argument */ +extern db_wfunc_t ipmgmt_db_add, ipmgmt_db_update; + +typedef struct { + char *cb_ifname; + ipadm_if_info_t *cb_ifinfo; +} ipmgmt_getif_cbarg_t; +extern db_wfunc_t ipmgmt_db_getif; + +typedef struct { + char *cb_aobjname; + char *cb_ifname; + nvlist_t *cb_onvl; + int cb_ocnt; +} ipmgmt_getaddr_cbarg_t; +extern db_wfunc_t ipmgmt_db_getaddr; + +typedef struct { + sa_family_t cb_family; + char *cb_ifname; +} ipmgmt_if_cbarg_t; +extern db_wfunc_t ipmgmt_db_setif, ipmgmt_db_resetif; + +typedef struct { + char *cb_aobjname; +} ipmgmt_resetaddr_cbarg_t; +extern db_wfunc_t ipmgmt_db_resetaddr; + +typedef struct { + sa_family_t cb_family; + nvlist_t *cb_invl; + nvlist_t *cb_onvl; + int cb_ocnt; +} ipmgmt_initif_cbarg_t; +extern db_wfunc_t ipmgmt_db_initif; + +/* + * A linked list of address object nodes. Each node in the list tracks + * following information for the address object identified by `am_aobjname'. + * - interface on which the address is created + * - logical interface number on which the address is created + * - address family + * - `am_nextnum' identifies the next number to use to generate user part + * of `aobjname'. + * - address type (static, dhcp or addrconf) + * - `am_flags' indicates if this addrobj in active and/or persist config + * - if `am_atype' is IPADM_ADDR_IPV6_ADDRCONF then `am_ifid' holds the + * interface-id used to configure auto-configured addresses + */ +typedef struct ipmgmt_aobjmap_s { + struct ipmgmt_aobjmap_s *am_next; + char am_aobjname[IPADM_AOBJSIZ]; + char am_ifname[LIFNAMSIZ]; + int32_t am_lnum; + sa_family_t am_family; + ipadm_addr_type_t am_atype; + uint32_t am_nextnum; + uint32_t am_flags; + boolean_t am_linklocal; + struct sockaddr_storage am_ifid; +} ipmgmt_aobjmap_t; + +/* linked list of `aobjmap' nodes, protected by RW lock */ +typedef struct ipmgmt_aobjmap_list_s { + ipmgmt_aobjmap_t *aobjmap_head; + pthread_rwlock_t aobjmap_rwlock; +} ipmgmt_aobjmap_list_t; + +/* global `aobjmap' defined in ipmgmt_main.c */ +extern ipmgmt_aobjmap_list_t aobjmap; + +/* operations on the `aobjmap' linked list */ +#define ADDROBJ_ADD 0x00000001 +#define ADDROBJ_DELETE 0x00000002 +#define ADDROBJ_LOOKUPADD 0x00000004 + +/* + * A temporary file created in SMF volatile filesystem. This file captures the + * in-memory copy of list `aobjmap' on disk. This is done to recover from + * daemon reboot (using svcadm) or crashes. + */ +#define ADDROBJ_MAPPING_DB_FILE IPADM_TMPFS_DIR"/aobjmap.conf" + +extern int ipmgmt_db_walk(db_wfunc_t *, void *, ipadm_db_op_t); +extern int ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *, uint32_t); +extern boolean_t ipmgmt_aobjmap_init(void *, nvlist_t *, char *, + size_t, int *); +extern int ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *, + ipadm_db_op_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _IPMGMT_IMPL_H */ diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c new file mode 100644 index 0000000000..011126d010 --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c @@ -0,0 +1,382 @@ +/* + * 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. + */ + +/* + * The ipmgmtd daemon is started by ip-interface-management SMF service. This + * daemon is used to manage, mapping of 'address object' to 'interface name' and + * 'logical interface number', on which the address is created. It also provides + * a means to update the ipadm persistent data-store. + * + * The daemon tracks the <addrobj, lifname> mapping in-memory using a linked + * list `aobjmap'. Access to this list is synchronized using a readers-writers + * lock. The active <addrobj, lifname> mapping is kept in + * /etc/svc/volatile/ipadm/aobjmap.conf cache file, so that the mapping can be + * recovered when ipmgmtd exits for some reason (e.g., when ipmgmtd is restarted + * using svcadm or accidentally killed). + * + * Today, the persistent configuration of interfaces, addresses and protocol + * properties is kept in /etc/ipadm/ipadm.conf. The access to the persistent + * data store is synchronized using reader-writers lock `ipmgmt_dbconf_lock'. + * + * The communication between the library, libipadm.so and the daemon, is through + * doors RPC. The library interacts with the daemon using the commands defined + * by `ipmgmt_door_cmd_type_t'. Further any 'write' operation would require + * the `NETWORK_INTERFACE_CONFIG_AUTH' authorization. + * + * On reboot, the aforementioned SMF service starts the daemon before any other + * networking service that configures network IP interfaces is started. + * Afterwards, the network/physical SMF script instantiates the persisted + * network interfaces, interface properties and addresses. + */ + +#include <errno.h> +#include <fcntl.h> +#include <priv_utils.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <unistd.h> +#include "ipmgmt_impl.h" + +const char *progname; + +/* readers-writers lock for reading/writing daemon data store */ +pthread_rwlock_t ipmgmt_dbconf_lock; + +/* tracks address object to {ifname|logical number|interface id} mapping */ +ipmgmt_aobjmap_list_t aobjmap; + +/* used to communicate failure to parent process, which spawned the daemon */ +static int pfds[2]; + +/* file descriptor to IPMGMT_DOOR */ +static int ipmgmt_door_fd = -1; + +static void ipmgmt_exit(int); +static int ipmgmt_init(); +static int ipmgmt_init_privileges(); + +static int +ipmgmt_db_init() +{ + int fd, err; + + /* creates the address object data store, if it doesn't exist */ + if ((fd = open(ADDROBJ_MAPPING_DB_FILE, O_CREAT|O_RDONLY, + IPADM_FILE_MODE)) == -1) { + err = errno; + ipmgmt_log(LOG_ERR, "could not open %s: %s", + ADDROBJ_MAPPING_DB_FILE, strerror(err)); + return (err); + } + (void) close(fd); + + aobjmap.aobjmap_head = NULL; + (void) pthread_rwlock_init(&aobjmap.aobjmap_rwlock, NULL); + + /* + * If the daemon is recovering from a crash or restart, read the + * address object to logical interface mapping and build an in-memory + * representation of the mapping. That is, build `aobjmap' structure + * from address object data store. + */ + if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, + ADDROBJ_MAPPING_DB_FILE, 0, IPADM_DB_READ)) != 0) { + /* if there was nothing to initialize, it's fine */ + if (err != ENOENT) + return (err); + err = 0; + } + + (void) pthread_rwlock_init(&ipmgmt_dbconf_lock, NULL); + return (err); +} + +static int +ipmgmt_door_init() +{ + int fd; + int err; + + /* create the door file for ipmgmtd */ + if ((fd = open(IPMGMT_DOOR, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) { + err = errno; + ipmgmt_log(LOG_ERR, "could not open %s: %s", + IPMGMT_DOOR, strerror(err)); + return (err); + } + (void) close(fd); + + if ((ipmgmt_door_fd = door_create(ipmgmt_handler, NULL, + DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { + err = errno; + ipmgmt_log(LOG_ERR, "failed to create door: %s", strerror(err)); + return (err); + } + /* + * fdetach first in case a previous daemon instance exited + * ungracefully. + */ + (void) fdetach(IPMGMT_DOOR); + if (fattach(ipmgmt_door_fd, IPMGMT_DOOR) != 0) { + err = errno; + ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", + IPMGMT_DOOR, strerror(err)); + goto fail; + } + return (0); +fail: + (void) door_revoke(ipmgmt_door_fd); + ipmgmt_door_fd = -1; + return (err); +} + +static void +ipmgmt_door_fini() +{ + if (ipmgmt_door_fd == -1) + return; + + (void) fdetach(IPMGMT_DOOR); + if (door_revoke(ipmgmt_door_fd) == -1) { + ipmgmt_log(LOG_ERR, "failed to revoke access to door %s: %s", + IPMGMT_DOOR, strerror(errno)); + } +} + +static int +ipmgmt_init() +{ + int err; + + if (signal(SIGTERM, ipmgmt_exit) == SIG_ERR || + signal(SIGINT, ipmgmt_exit) == SIG_ERR) { + err = errno; + ipmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s", + strerror(err)); + return (err); + } + if ((err = ipmgmt_db_init()) != 0 || (err = ipmgmt_door_init()) != 0) + return (err); + return (0); +} + +/* + * This is called by the child process to inform the parent process to + * exit with the given return value. + */ +static void +ipmgmt_inform_parent_exit(int rv) +{ + if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) { + ipmgmt_log(LOG_WARNING, + "failed to inform parent process of status: %s", + strerror(errno)); + (void) close(pfds[1]); + exit(EXIT_FAILURE); + } + (void) close(pfds[1]); +} + +/*ARGSUSED*/ +static void +ipmgmt_exit(int signo) +{ + (void) close(pfds[1]); + ipmgmt_door_fini(); + exit(EXIT_FAILURE); +} + +/* + * Set the uid of this daemon to the "ipadm" user. Finish the following + * operations before setuid() because they need root privileges: + * + * - create the /etc/svc/volatile/ipadm directory; + * - change its uid/gid to "ipadm"/"sys"; + */ +static int +ipmgmt_init_privileges() +{ + struct stat statbuf; + int err; + + /* create the IPADM_TMPFS_DIR directory */ + if (stat(IPADM_TMPFS_DIR, &statbuf) < 0) { + if (mkdir(IPADM_TMPFS_DIR, (mode_t)0755) < 0) { + err = errno; + goto fail; + } + } else { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + err = ENOTDIR; + goto fail; + } + } + + if ((chmod(IPADM_TMPFS_DIR, 0755) < 0) || + (chown(IPADM_TMPFS_DIR, UID_NETADM, GID_NETADM) < 0)) { + err = errno; + goto fail; + } + + /* + * limit the privileges of this daemon and set the uid of this + * daemon to UID_NETADM + */ + if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, UID_NETADM, + GID_NETADM, NULL) == -1) { + err = EPERM; + goto fail; + } + + return (0); +fail: + (void) ipmgmt_log(LOG_ERR, "failed to initialize the daemon: %s", + strerror(err)); + return (err); +} + +/* + * Keep the pfds fd open, close other fds. + */ +/*ARGSUSED*/ +static int +closefunc(void *arg, int fd) +{ + if (fd != pfds[1]) + (void) close(fd); + return (0); +} + +/* + * We cannot use libc's daemon() because the door we create is associated with + * the process ID. If we create the door before the call to daemon(), it will + * be associated with the parent and it's incorrect. On the other hand if we + * create the door later, after the call to daemon(), parent process exits + * early and gives a false notion to SMF that 'ipmgmtd' is up and running, + * which is incorrect. So, we have our own daemon() equivalent. + */ +static boolean_t +ipmgmt_daemonize(void) +{ + pid_t pid; + int rv; + + if (pipe(pfds) < 0) { + (void) fprintf(stderr, "%s: pipe() failed: %s\n", + progname, strerror(errno)); + exit(EXIT_FAILURE); + } + + if ((pid = fork()) == -1) { + (void) fprintf(stderr, "%s: fork() failed: %s\n", + progname, strerror(errno)); + exit(EXIT_FAILURE); + } else if (pid > 0) { /* Parent */ + (void) close(pfds[1]); + + /* + * Parent should not exit early, it should wait for the child + * to return Success/Failure. If the parent exits early, then + * SMF will think 'ipmgmtd' is up and would start all the + * depended services. + * + * If the child process exits unexpectedly, read() returns -1. + */ + if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) { + (void) kill(pid, SIGKILL); + rv = EXIT_FAILURE; + } + + (void) close(pfds[0]); + exit(rv); + } + + /* Child */ + (void) close(pfds[0]); + (void) setsid(); + + /* close all files except pfds[1] */ + (void) fdwalk(closefunc, NULL); + (void) chdir("/"); + openlog(progname, LOG_PID, LOG_DAEMON); + return (B_TRUE); +} + +int +main(int argc, char *argv[]) +{ + int opt; + boolean_t fg = B_FALSE; + + progname = strrchr(argv[0], '/'); + if (progname != NULL) + progname++; + else + progname = argv[0]; + + /* Process options */ + while ((opt = getopt(argc, argv, "f")) != EOF) { + switch (opt) { + case 'f': + fg = B_TRUE; + break; + default: + (void) fprintf(stderr, "Usage: %s [-f]\n", progname); + return (EXIT_FAILURE); + } + } + + if (!fg && getenv("SMF_FMRI") == NULL) { + (void) fprintf(stderr, + "ipmgmtd is a smf(5) managed service and cannot be run " + "from the command line.\n"); + return (EINVAL); + } + + if (!fg && !ipmgmt_daemonize()) + return (EXIT_FAILURE); + + if (ipmgmt_init_privileges() != 0) + goto child_out; + + if (ipmgmt_init() != 0) + goto child_out; + + /* Inform the parent process that it can successfully exit */ + ipmgmt_inform_parent_exit(EXIT_SUCCESS); + + for (;;) + (void) pause(); + +child_out: + /* return from main() forcibly exits an MT process */ + ipmgmt_inform_parent_exit(EXIT_FAILURE); + return (EXIT_FAILURE); +} diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c new file mode 100644 index 0000000000..deb077036d --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c @@ -0,0 +1,1136 @@ +/* + * 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. + */ + +/* + * Contains DB walker functions, which are of type `db_wfunc_t'; + * + * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf, + * size_t bufsize, int *errp); + * + * ipadm_rw_db() walks through the data store, one line at a time and calls + * these call back functions with: + * `cbarg' - callback argument + * `db_nvl' - representing a line from DB in nvlist_t form + * `buf' - character buffer to hold modified line + * `bufsize'- size of the buffer + * `errp' - captures any error inside the walker function. + * + * All the 'write' callback functions modify `db_nvl' based on `cbarg' and + * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'. + * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(), + * the modified `buf' is written back into DB. + * + * All the 'read' callback functions, retrieve the information from the DB, by + * reading `db_nvl' and then populate the `cbarg'. + */ + +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include "ipmgmt_impl.h" + +#define ATYPE "_atype" /* name of the address type nvpair */ +#define FLAGS "_flags" /* name of the flags nvpair */ + +/* + * flag used by ipmgmt_persist_aobjmap() to indicate address type is + * IPADM_ADDR_IPV6_ADDRCONF. + */ +#define IPMGMT_ATYPE_V6ACONF 0x1 + +extern pthread_rwlock_t ipmgmt_dbconf_lock; + +/* + * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed + * in private nvpairs `proto', `ifname' & `aobjname'. + */ +static boolean_t +ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname, + const char *aobjname) +{ + char *db_proto = NULL, *db_ifname = NULL; + char *db_aobjname = NULL; + nvpair_t *nvp; + char *name; + + /* walk through db_nvl and retrieve all its private nvpairs */ + for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(db_nvl, nvp)) { + name = nvpair_name(nvp); + if (strcmp(IPADM_NVP_PROTONAME, name) == 0) + (void) nvpair_value_string(nvp, &db_proto); + else if (strcmp(IPADM_NVP_IFNAME, name) == 0) + (void) nvpair_value_string(nvp, &db_ifname); + else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) + (void) nvpair_value_string(nvp, &db_aobjname); + } + + if (proto != NULL && proto[0] == '\0') + proto = NULL; + if (ifname != NULL && ifname[0] == '\0') + ifname = NULL; + if (aobjname != NULL && aobjname[0] == '\0') + aobjname = NULL; + + if ((proto == NULL && db_proto != NULL) || + (proto != NULL && db_proto == NULL) || + strcmp(proto, db_proto) != 0) { + /* no intersection - different protocols. */ + return (B_FALSE); + } + if ((ifname == NULL && db_ifname != NULL) || + (ifname != NULL && db_ifname == NULL) || + strcmp(ifname, db_ifname) != 0) { + /* no intersection - different interfaces. */ + return (B_FALSE); + } + if ((aobjname == NULL && db_aobjname != NULL) || + (aobjname != NULL && db_aobjname == NULL) || + strcmp(aobjname, db_aobjname) != 0) { + /* no intersection - different address objects */ + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects. + */ +static boolean_t +ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl) +{ + nvpair_t *nvp; + char *name; + char *proto = NULL, *ifname = NULL, *aobjname = NULL; + + /* walk through in_nvl and retrieve all its private nvpairs */ + for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(in_nvl, nvp)) { + name = nvpair_name(nvp); + if (strcmp(IPADM_NVP_PROTONAME, name) == 0) + (void) nvpair_value_string(nvp, &proto); + else if (strcmp(IPADM_NVP_IFNAME, name) == 0) + (void) nvpair_value_string(nvp, &ifname); + else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) + (void) nvpair_value_string(nvp, &aobjname); + } + + return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname)); +} + +/* + * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed + * in private nvpairs `proto', `ifname' & `aobjname'. + */ +static boolean_t +ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto, + const char *ifname, char *aobjname) +{ + char *db_ifname = NULL, *db_proto = NULL; + char *db_aobjname = NULL; + nvpair_t *nvp; + char *name; + + /* walk through db_nvl and retrieve all private nvpairs */ + for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(db_nvl, nvp)) { + name = nvpair_name(nvp); + if (strcmp(IPADM_NVP_PROTONAME, name) == 0) + (void) nvpair_value_string(nvp, &db_proto); + else if (strcmp(IPADM_NVP_IFNAME, name) == 0) + (void) nvpair_value_string(nvp, &db_ifname); + else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) + (void) nvpair_value_string(nvp, &db_aobjname); + } + + if (proto != NULL && proto[0] != '\0') { + if ((db_proto == NULL || strcmp(proto, db_proto) != 0)) + return (B_FALSE); + } + if (ifname != NULL && ifname[0] != '\0') { + if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0)) + return (B_FALSE); + } + if (aobjname != NULL && aobjname[0] != '\0') { + if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0)) + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * Retrieves the property value from the DB. The property whose value is to be + * retrieved is in `pargp->ia_pname'. + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_prop_arg_t *pargp = arg; + boolean_t cont = B_TRUE; + char *pval; + int err = 0; + + *errp = 0; + + if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, + pargp->ia_ifname, pargp->ia_aobjname)) + return (B_TRUE); + + if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname, + &pval)) == 0) { + (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval)); + /* + * We have retrieved what we are looking for. + * Stop the walker. + */ + cont = B_FALSE; + } else { + if (err == ENOENT) + err = 0; + *errp = err; + } + + return (cont); +} + +/* + * Removes the property value from the DB. The property whose value is to be + * removed is in `pargp->ia_pname'. + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_prop_arg_t *pargp = arg; + + *errp = 0; + if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module, + pargp->ia_ifname, pargp->ia_aobjname)) + return (B_TRUE); + + if (!nvlist_exists(db_nvl, pargp->ia_pname)) + return (B_TRUE); + + /* + * We found the property in the DB. If IPMGMT_REMOVE is not set then + * delete the entry from the db. If it is set, then the property is a + * multi-valued property so just remove the specified values from DB. + */ + if (pargp->ia_flags & IPMGMT_REMOVE) { + char *dbpval = NULL; + char *inpval = pargp->ia_pval; + char pval[MAXPROPVALLEN]; + char *val, *lasts; + + *errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval); + if (*errp != 0) + return (B_FALSE); + + /* + * multi-valued properties are represented as comma separated + * values. Use string tokenizer functions to split them and + * search for the value to be removed. + */ + bzero(pval, sizeof (pval)); + if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) { + if (strcmp(val, inpval) != 0) + (void) strlcat(pval, val, MAXPROPVALLEN); + while ((val = strtok_r(NULL, ",", &lasts)) != NULL) { + if (strcmp(val, inpval) != 0) { + if (pval[0] != '\0') + (void) strlcat(pval, ",", + MAXPROPVALLEN); + (void) strlcat(pval, val, + MAXPROPVALLEN); + } + } + } else { + if (strcmp(dbpval, inpval) != 0) + *errp = ENOENT; + else + buf[0] = '\0'; + return (B_FALSE); + } + *errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval); + if (*errp != 0) + return (B_FALSE); + + (void) memset(buf, 0, buflen); + if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { + /* buffer overflow */ + *errp = ENOBUFS; + } + } else { + buf[0] = '\0'; + } + + /* stop the search */ + return (B_FALSE); +} + +/* + * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is + * found, when one of the following occurs first. + * - the input aobjname matches the db aobjname. Return the db address. + * - the input interface matches the db interface. Return all the + * matching db lines with addresses. + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_getaddr_cbarg_t *cbarg = arg; + char *db_aobjname = NULL; + char *db_ifname = NULL; + nvlist_t *db_addr = NULL; + char name[IPMGMT_STRSIZE]; + nvpair_t *nvp; + boolean_t add_nvl = B_FALSE; + + /* Parse db nvlist */ + for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(db_nvl, nvp)) { + if (nvpair_type(nvp) == DATA_TYPE_NVLIST) + (void) nvpair_value_nvlist(nvp, &db_addr); + else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0) + (void) nvpair_value_string(nvp, &db_ifname); + else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0) + (void) nvpair_value_string(nvp, &db_aobjname); + } + + if (db_aobjname == NULL) /* Not an address */ + return (B_TRUE); + + /* Check for a match between the aobjnames or the interface name */ + if (cbarg->cb_aobjname[0] != '\0') { + if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0) + add_nvl = B_TRUE; + } else if (cbarg->cb_ifname[0] != '\0') { + if (strcmp(cbarg->cb_ifname, db_ifname) == 0) + add_nvl = B_TRUE; + } else { + add_nvl = B_TRUE; + } + + if (add_nvl) { + (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, + cbarg->cb_ocnt); + *errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl); + if (*errp == 0) + cbarg->cb_ocnt++; + } + return (B_TRUE); +} + +/* + * This function takes the appropriate lock, read or write, based on the + * `db_op' and then calls DB walker ipadm_rw_db(). + */ +extern int +ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) +{ + int err; + boolean_t writeop; + mode_t mode; + + writeop = (db_op != IPADM_DB_READ); + + if (writeop) { + (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); + mode = IPADM_FILE_MODE; + } else { + (void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock); + mode = 0; + } + + err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, mode, db_op); + (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); + return (err); +} + +/* + * Used to add an entry towards the end of DB. It just returns B_TRUE for + * every line of the DB. When we reach the end, ipadm_rw_db() adds the + * line at the end. + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp) +{ + return (B_TRUE); +} + +/* + * This function is used to update or create an entry in DB. The nvlist_t, + * `in_nvl', represents the line we are looking for. Once we ensure the right + * line from DB, we update that entry. + */ +boolean_t +ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipadm_dbwrite_cbarg_t *cb = arg; + uint_t flags = cb->dbw_flags; + nvlist_t *in_nvl = cb->dbw_nvl; + nvpair_t *nvp; + char *name, *instrval = NULL, *dbstrval = NULL; + char pval[MAXPROPVALLEN]; + + if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) + return (B_TRUE); + + for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(in_nvl, nvp)) { + name = nvpair_name(nvp); + if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name)) + break; + } + + if (nvp == NULL) + return (B_TRUE); + + assert(nvpair_type(nvp) == DATA_TYPE_STRING); + + if ((*errp = nvpair_value_string(nvp, &instrval)) != 0) + return (B_FALSE); + + /* + * If IPMGMT_APPEND is set then we are dealing with multi-valued + * properties. We append to the entry from the db, with the new value. + */ + if (flags & IPMGMT_APPEND) { + if ((*errp = nvlist_lookup_string(db_nvl, name, + &dbstrval)) != 0) + return (B_FALSE); + (void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval, + instrval); + if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0) + return (B_FALSE); + } else { + /* case of in-line update of a db entry */ + if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0) + return (B_FALSE); + } + + (void) memset(buf, 0, buflen); + if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) { + /* buffer overflow */ + *errp = ENOBUFS; + } + *errp = 0; + + /* we updated the DB entry, so do not continue */ + return (B_FALSE); +} + +/* + * For the given `cbarg->cb_ifname' interface, retrieves any persistent + * interface information (used in 'ipadm show-if') + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_getif_cbarg_t *cbarg = arg; + char *ifname = cbarg->cb_ifname; + char *intf = NULL; + ipadm_if_info_t *ifp = NULL; + sa_family_t af; + char *afstr; + + *errp = 0; + if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 || + nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 || + (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) { + return (B_TRUE); + } + af = atoi(afstr); + for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) { + if (strcmp(ifp->ifi_name, intf) == 0) + break; + } + if (ifp == NULL) { + ipadm_if_info_t *new; + + if ((new = calloc(1, sizeof (*new))) == NULL) { + *errp = ENOMEM; + return (B_FALSE); /* don't continue the walk */ + } + new->ifi_next = cbarg->cb_ifinfo; + cbarg->cb_ifinfo = new; + ifp = new; + (void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name)); + } + + if (af == AF_INET) { + ifp->ifi_pflags |= IFIF_IPV4; + } else { + assert(af == AF_INET6); + ifp->ifi_pflags |= IFIF_IPV6; + } + + /* Terminate the walk if we found both v4 and v6 interfaces. */ + if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) && + (ifp->ifi_pflags & IFIF_IPV6)) + return (B_FALSE); + + return (B_TRUE); +} + +/* + * Deletes those entries from the database for which interface name + * matches with the given `cbarg->cb_ifname' + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_if_cbarg_t *cbarg = arg; + boolean_t isv6 = (cbarg->cb_family == AF_INET6); + char *ifname = cbarg->cb_ifname; + char *modstr = NULL; + char *afstr; + char *aobjname; + uint_t proto; + ipmgmt_aobjmap_t *head; + boolean_t aobjfound = B_FALSE; + + *errp = 0; + + if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL)) + return (B_TRUE); + + if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) { + if (atoi(afstr) == cbarg->cb_family) + goto delete; + return (B_TRUE); + } + + /* Reset all the interface configurations for 'ifname' */ + if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) || + nvlist_exists(db_nvl, IPADM_NVP_INTFID))) { + goto delete; + } + if (!isv6 && + (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || + nvlist_exists(db_nvl, IPADM_NVP_DHCP))) { + goto delete; + } + + if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) { + /* + * This must be an address property. Delete this + * line if there is a match in the address family. + */ + head = aobjmap.aobjmap_head; + while (head != NULL) { + if (strcmp(head->am_aobjname, aobjname) == 0) { + aobjfound = B_TRUE; + if (head->am_family == cbarg->cb_family) + goto delete; + } + head = head->am_next; + } + /* + * If aobjfound = B_FALSE, then this address is not + * available in active configuration. We should go ahead + * and delete it. + */ + if (!aobjfound) + goto delete; + } + + /* + * If we are removing both v4 and v6 interface, then we get rid of + * all the properties for that interface. On the other hand, if we + * are deleting only v4 instance of an interface, then we delete v4 + * properties only. + */ + if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) { + proto = ipadm_str2proto(modstr); + switch (proto) { + case MOD_PROTO_IPV6: + if (isv6) + goto delete; + break; + case MOD_PROTO_IPV4: + if (!isv6) + goto delete; + break; + case MOD_PROTO_IP: + /* this should never be the case, today */ + assert(0); + break; + } + } + /* Not found a match yet. Continue processing the db */ + return (B_TRUE); +delete: + /* delete the line from the db */ + buf[0] = '\0'; + return (B_TRUE); +} + +/* + * Deletes those entries from the database for which address object name + * matches with the given `cbarg->cb_aobjname' + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_resetaddr_cbarg_t *cbarg = arg; + char *aobjname = cbarg->cb_aobjname; + + *errp = 0; + if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname)) + return (B_TRUE); + + /* delete the line from the db */ + buf[0] = '\0'; + return (B_TRUE); +} + +/* + * Retrieves all interface props, including addresses, for given interface(s). + * `invl' contains the list of interfaces, for which information need to be + * retrieved. + */ +/* ARGSUSED */ +boolean_t +ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + ipmgmt_initif_cbarg_t *cbarg = arg; + nvlist_t *onvl = cbarg->cb_onvl; + nvlist_t *invl = cbarg->cb_invl; + sa_family_t in_af = cbarg->cb_family; + char *db_ifname; + + *errp = 0; + if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 && + nvlist_exists(invl, db_ifname)) { + char name[IPMGMT_STRSIZE]; + sa_family_t db_af = in_af; + uint_t proto; + char *pstr; + + if (in_af != AF_UNSPEC) { + if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, + &pstr) == 0) { + proto = ipadm_str2proto(pstr); + if (proto == MOD_PROTO_IPV4) + db_af = AF_INET; + else if (proto == MOD_PROTO_IPV6) + db_af = AF_INET6; + else + db_af = in_af; + } else { + if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) || + nvlist_exists(db_nvl, IPADM_NVP_DHCP)) + db_af = AF_INET; + else + db_af = AF_INET6; + } + } + if (in_af == db_af) { + (void) snprintf(name, sizeof (name), "%s_%d", db_ifname, + cbarg->cb_ocnt); + *errp = nvlist_add_nvlist(onvl, name, db_nvl); + if (*errp == 0) + cbarg->cb_ocnt++; + } + } + return (B_TRUE); +} + +/* + * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep' + * into `aobjmap' structure. + */ +static int +i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep) +{ + ipmgmt_aobjmap_t *new, *head; + + head = aobjmap.aobjmap_head; + if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL) + return (ENOMEM); + *new = *nodep; + new->am_next = NULL; + + /* Add the node at the beginning of the list */ + if (head == NULL) { + aobjmap.aobjmap_head = new; + } else { + new->am_next = aobjmap.aobjmap_head; + aobjmap.aobjmap_head = new; + } + return (0); +} + +/* + * A recursive function to generate alphabetized number given a decimal number. + * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa', + * 'ab', 'ac', et al. + */ +static void +i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp) +{ + if (num >= 26) + i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp); + if (*cp != endp) { + *cp[0] = 'a' + (num % 26); + (*cp)++; + } +} + +/* + * This function generates an `aobjname', when required, and then does + * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks + * through the `aobjmap' to check if an address object with the same + * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate + * `aobjname's are not allowed. + * + * If `nodep->am_aobjname' is an empty string then the daemon generates an + * `aobjname' using the `am_nextnum', which contains the next number to be + * used to generate `aobjname'. `am_nextnum' is converted to base26 using + * `a-z' alphabets in i_ipmgmt_num2priv_aobjname(). + * + * `am_nextnum' will be 0 to begin with. Every time an address object that + * needs `aobjname' is added it's incremented by 1. So for the first address + * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1. + * For the second address object on that interface `am_aobjname' will be net0/_b + * and `am_nextnum' will incremented to 2. + */ +static int +i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep) +{ + ipmgmt_aobjmap_t *head; + uint32_t nextnum; + + for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next) + if (strcmp(head->am_ifname, nodep->am_ifname) == 0) + break; + nextnum = (head == NULL ? 0 : head->am_nextnum); + + /* + * if `aobjname' is empty, then the daemon has to generate the + * next `aobjname' for the given interface and family. + */ + if (nodep->am_aobjname[0] == '\0') { + char tmpstr[IPADM_AOBJ_USTRSIZ - 1]; /* 1 for leading '_' */ + char *cp = tmpstr; + char *endp = tmpstr + sizeof (tmpstr); + + i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp); + + if (cp == endp) + return (EINVAL); + cp[0] = '\0'; + + if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s", + nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) { + return (EINVAL); + } + nodep->am_nextnum = ++nextnum; + } else { + for (head = aobjmap.aobjmap_head; head != NULL; + head = head->am_next) { + if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0) + return (EEXIST); + } + nodep->am_nextnum = nextnum; + } + return (i_ipmgmt_add_amnode(nodep)); +} + +/* + * Performs following operations on the global `aobjmap' linked list. + * (a) ADDROBJ_ADD: add or update address object in `aobjmap' + * (b) ADDROBJ_DELETE: delete address object from `aobjmap' + * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap' + */ +int +ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op) +{ + ipmgmt_aobjmap_t *head, *prev; + boolean_t update = B_TRUE; + int err = 0; + ipadm_db_op_t db_op; + + (void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock); + + head = aobjmap.aobjmap_head; + switch (op) { + case ADDROBJ_ADD: + /* + * check for stub nodes (added by ADDROBJ_LOOKUPADD) and + * update, else add the new node. + */ + for (; head != NULL; head = head->am_next) { + if (strcmp(head->am_aobjname, + nodep->am_aobjname) == 0 && head->am_lnum == -1) + break; + } + + if (head != NULL) { + /* update the node */ + (void) strlcpy(head->am_ifname, nodep->am_ifname, + sizeof (head->am_ifname)); + head->am_lnum = nodep->am_lnum; + head->am_family = nodep->am_family; + head->am_flags = nodep->am_flags; + head->am_atype = nodep->am_atype; + if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { + head->am_ifid = nodep->am_ifid; + head->am_linklocal = nodep->am_linklocal; + } + } else { + for (head = aobjmap.aobjmap_head; head != NULL; + head = head->am_next) { + if (strcmp(head->am_ifname, + nodep->am_ifname) == 0) + break; + } + nodep->am_nextnum = (head == NULL ? 0 : + head->am_nextnum); + err = i_ipmgmt_add_amnode(nodep); + } + db_op = IPADM_DB_WRITE; + break; + case ADDROBJ_DELETE: + prev = head; + while (head != NULL) { + if (strcmp(head->am_aobjname, + nodep->am_aobjname) == 0) { + nodep->am_atype = head->am_atype; + /* + * There could be multiple IPV6_ADDRCONF nodes, + * with same address object name, so check for + * logical number also. + */ + if (head->am_atype != + IPADM_ADDR_IPV6_ADDRCONF || + nodep->am_lnum == head->am_lnum) + break; + } + prev = head; + head = head->am_next; + } + if (head != NULL) { + /* + * If the address object is in both active and + * persistent configuration and the user is deleting it + * only from active configuration then mark this node + * for deletion by reseting IPMGMT_ACTIVE bit. + * With this the same address object name cannot + * be reused until it is permanently removed. + */ + if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) && + nodep->am_flags == IPMGMT_ACTIVE) { + /* Update flags in the in-memory map. */ + head->am_flags &= ~IPMGMT_ACTIVE; + head->am_lnum = -1; + + /* Update info in file. */ + db_op = IPADM_DB_WRITE; + *nodep = *head; + } else { + (void) strlcpy(nodep->am_ifname, + head->am_ifname, + sizeof (nodep->am_ifname)); + /* otherwise delete the node */ + if (head == aobjmap.aobjmap_head) + aobjmap.aobjmap_head = head->am_next; + else + prev->am_next = head->am_next; + free(head); + db_op = IPADM_DB_DELETE; + } + } else { + err = ENOENT; + } + break; + case ADDROBJ_LOOKUPADD: + err = i_ipmgmt_lookupadd_amnode(nodep); + update = B_FALSE; + break; + default: + assert(0); + } + + if (err == 0 && update) + err = ipmgmt_persist_aobjmap(nodep, db_op); + + (void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock); + + return (err); +} + +/* + * Given a node in `aobjmap', this function converts it into nvlist_t structure. + * The content to be written to DB must be represented as nvlist_t. + */ +static int +i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np) +{ + int err; + char strval[IPMGMT_STRSIZE]; + + *nvl = NULL; + if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0) + goto fail; + + if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME, + np->am_aobjname)) != 0) + goto fail; + + if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME, + np->am_ifname)) != 0) + goto fail; + + (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum); + if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0) + goto fail; + + (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family); + if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0) + goto fail; + + (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags); + if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0) + goto fail; + + (void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype); + if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0) + goto fail; + + if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { + struct sockaddr_in6 *in6; + + in6 = (struct sockaddr_in6 *)&np->am_ifid; + if (np->am_linklocal && + IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) { + if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, + "default")) != 0) + goto fail; + } else { + if (inet_ntop(AF_INET6, &in6->sin6_addr, strval, + IPMGMT_STRSIZE) == NULL) { + err = errno; + goto fail; + } + if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, + strval)) != 0) + goto fail; + } + } else { + if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR, + "")) != 0) + goto fail; + } + return (err); +fail: + nvlist_free(*nvl); + return (err); +} + +/* + * Read the aobjmap data store and build the in-memory representation + * of the aobjmap. We don't need to hold any locks while building this as + * we do this in very early stage of daemon coming up, even before the door + * is opened. + */ +/* ARGSUSED */ +extern boolean_t +ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, + int *errp) +{ + nvpair_t *nvp = NULL; + char *name, *strval = NULL; + ipmgmt_aobjmap_t node; + struct sockaddr_in6 *in6; + + *errp = 0; + node.am_next = NULL; + for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL; + nvp = nvlist_next_nvpair(db_nvl, nvp)) { + name = nvpair_name(nvp); + + if ((*errp = nvpair_value_string(nvp, &strval)) != 0) + return (B_TRUE); + if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) { + (void) strlcpy(node.am_aobjname, strval, + sizeof (node.am_aobjname)); + } else if (strcmp(IPADM_NVP_IFNAME, name) == 0) { + (void) strlcpy(node.am_ifname, strval, + sizeof (node.am_ifname)); + } else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) { + node.am_lnum = atoi(strval); + } else if (strcmp(IPADM_NVP_FAMILY, name) == 0) { + node.am_family = (sa_family_t)atoi(strval); + } else if (strcmp(FLAGS, name) == 0) { + node.am_flags = atoi(strval); + } else if (strcmp(ATYPE, name) == 0) { + node.am_atype = (ipadm_addr_type_t)atoi(strval); + } else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) { + if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) { + in6 = (struct sockaddr_in6 *)&node.am_ifid; + if (strcmp(strval, "default") == 0) { + bzero(in6, sizeof (node.am_ifid)); + node.am_linklocal = B_TRUE; + } else { + (void) inet_pton(AF_INET6, strval, + &in6->sin6_addr); + if (IN6_IS_ADDR_UNSPECIFIED( + &in6->sin6_addr)) + node.am_linklocal = B_TRUE; + } + } + } + } + + /* we have all the information we need, add the node */ + *errp = i_ipmgmt_add_amnode(&node); + + return (B_TRUE); +} + +/* + * Updates an entry from the temporary cache file, which matches the given + * address object name. + */ +/* ARGSUSED */ +static boolean_t +ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, + size_t buflen, int *errp) +{ + ipadm_dbwrite_cbarg_t *cb = arg; + nvlist_t *in_nvl = cb->dbw_nvl; + uint32_t flags = cb->dbw_flags; + char *db_lifnumstr = NULL, *in_lifnumstr = NULL; + + *errp = 0; + if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl)) + return (B_TRUE); + + if (flags & IPMGMT_ATYPE_V6ACONF) { + if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, + &db_lifnumstr) != 0 || + nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM, + &in_lifnumstr) != 0 || + (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 && + strcmp(db_lifnumstr, in_lifnumstr) != 0)) + return (B_TRUE); + } + + /* we found the match */ + (void) memset(buf, 0, buflen); + if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) { + /* buffer overflow */ + *errp = ENOBUFS; + } + + /* stop the walker */ + return (B_FALSE); +} + +/* + * Deletes an entry from the temporary cache file, which matches the given + * address object name. + */ +/* ARGSUSED */ +static boolean_t +ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf, + size_t buflen, int *errp) +{ + ipmgmt_aobjmap_t *nodep = arg; + char *db_lifnumstr = NULL; + + *errp = 0; + if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname, + nodep->am_aobjname)) + return (B_TRUE); + + if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) { + if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM, + &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum) + return (B_TRUE); + } + + /* we found the match, delete the line from the db */ + buf[0] = '\0'; + + /* stop the walker */ + return (B_FALSE); +} + +/* + * Adds or deletes aobjmap node information into a temporary cache file. + */ +extern int +ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) +{ + int err; + ipadm_dbwrite_cbarg_t cb; + nvlist_t *nvl = NULL; + + if (op == IPADM_DB_WRITE) { + if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0) + return (err); + cb.dbw_nvl = nvl; + if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) + cb.dbw_flags = IPMGMT_ATYPE_V6ACONF; + else + cb.dbw_flags = 0; + + err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, + ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE); + nvlist_free(nvl); + } else { + assert(op == IPADM_DB_DELETE); + + err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, + ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE); + } + return (err); +} diff --git a/usr/src/uts/common/inet/sctp/sctpddi.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_util.c index 16b8551712..8ef9a09e3d 100644 --- a/usr/src/uts/common/inet/sctp/sctpddi.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_util.c @@ -18,45 +18,29 @@ * * CDDL HEADER END */ + /* - * Copyright 2007 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" - -#include <sys/types.h> -#include <sys/conf.h> -#include <sys/modctl.h> -#include <inet/common.h> -#include <inet/ip.h> - -#define INET_NAME "sctp" -#define INET_DEVDESC "SCTP device" -#define INET_DEVSTRTAB sctpinfo -#define INET_DEVMINOR 0 -#define INET_DEVMTFLAGS D_MP - -#include "../inetddi.c" +/* + * Utility functions used by the ipmgmtd daemon. + */ -int -_init(void) -{ - /* - * device initialization happens when the actual code containing - * module (/kernel/drv/ip) is loaded, and driven from ip_ddi_init() - */ - return (mod_install(&modlinkage)); -} +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <syslog.h> +#include <stdarg.h> +#include "ipmgmt_impl.h" -int -_fini(void) +void +ipmgmt_log(int pri, const char *fmt, ...) { - return (mod_remove(&modlinkage)); -} + va_list alist; -int -_info(struct modinfo *modinfop) -{ - return (mod_info(&modlinkage, modinfop)); + va_start(alist, fmt); + vsyslog(pri, fmt, alist); + va_end(alist); } diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt b/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt new file mode 100644 index 0000000000..e88c9a5352 --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/net-ipmgmt @@ -0,0 +1,62 @@ +#!/sbin/sh +# +# 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 daemon stores address object to logical interface number mappings +# (among other things) and reads/writes from/to ipmgmtd data store. +# + +. /lib/svc/share/smf_include.sh + +if [ -z "$SMF_FMRI" ]; then + echo "this script can only be invoked by smf(5)" + exit $SMF_EXIT_ERR_NOSMF +fi + +# +# network/ip-interface-management:default service is always enabled by default. +# When the non-global shared-IP stack zone boots, it tries to bring up this +# service as well. If we don't start a background process and simply exit the +# service, the service will go into maintenance mode and so will all it's +# dependents. +# +if smf_is_nonglobalzone; then + if [ `/sbin/zonename -t` = shared ]; then + (while true ; do sleep 3600 ; done) & + exit $SMF_EXIT_OK + fi +fi + +# Apply any persistent protocol (IP/TCP/SCTP/UDP/ICMP) properties +/sbin/ipadm init-prop + +# +# We must be now in a global zone or non-global zone with exclusive-IP stack. +# Start the ipmgmtd daemon. +# +if /lib/inet/ipmgmtd ; then + exit $SMF_EXIT_OK +else + exit $SMF_EXIT_ERR_FATAL +fi diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/network-ipmgmt.xml b/usr/src/cmd/cmd-inet/lib/ipmgmtd/network-ipmgmt.xml new file mode 100644 index 0000000000..7f2c7f53be --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/network-ipmgmt.xml @@ -0,0 +1,95 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + 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 + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='SUNWcsr:ipmgmtd'> + +<service + name='network/ip-interface-management' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance /> + + <dependent name='ipmgmt-loopback' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/loopback' /> + </dependent> + + <dependent name='ipmgmt-physical' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/physical' /> + </dependent> + + <dependent name='ipmgmt-iptun' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/iptun' /> + </dependent> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/net-ipmgmt' + timeout_seconds='60' /> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='5' /> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + IP interface management + </loctext> + </common_name> + <description> + <loctext xml:lang='C'> + Create IP interfaces, apply + TCP/IP properties and manage + address object mappings. + </loctext> + </description> + <documentation> + <manpage title='ipadm' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile index 3aca7d0fd9..542c79feb0 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/Makefile @@ -18,11 +18,9 @@ # # CDDL HEADER END # -# Copyright 2007 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" -# # cmd/cmd-inet/usr.lib/in.ndpd/Makefile # @@ -41,12 +39,12 @@ ROOTMANIFESTDIR= $(ROOTSVCNETWORKROUTING) # be accessed by -lxnet. In addition -lsocket and -lnsl are used to # capture new not-yet-standard interfaces. Someday -lxnet alone should be enough # when IPv6 inspired new interfaces are part of standards. -LDLIBS += -ldhcpagent -lxnet -lsocket -lnsl +LDLIBS += -ldhcpagent -lxnet -lsocket -lnsl -lipadm # these #defines are required to use UNIX 98 interfaces _D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ -$(OBJS) := CPPFLAGS += $(_D_UNIX98_EXTN) +$(OBJS) := CPPFLAGS += $(_D_UNIX98_EXTN) LINTFLAGS += $(_D_UNIX98_EXTN) @@ -58,6 +56,10 @@ LINTFLAGS += -erroff=E_INCONS_ARG_DECL2 -erroff=E_INCONS_VAL_TYPE_DECL2 # perfect would require a bigger rewrite. LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN +CTFCONVERT_HOOK = && $(CTFCONVERT_O) +CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS) +$(OBJS) := CFLAGS += $(CTF_FLAGS) + .KEEP_STATE: .PARALLEL: $(OBJS) @@ -65,7 +67,7 @@ LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN all: $(PROG) $(PROG): $(OBJS) - $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(CTFMERGE_HOOK) $(POST_PROCESS) include ../Makefile.lib diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h index 54c6fb6e35..169cdb6014 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h +++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/defs.h @@ -19,15 +19,13 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NDPD_DEFS_H #define _NDPD_DEFS_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <strings.h> @@ -62,6 +60,8 @@ #include <netinet/ip6.h> #include <netinet/icmp6.h> #include <net/route.h> +#include <libipadm.h> +#include <ipadm_ndpd.h> #include "tables.h" diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c index df8c79478e..62ba4df299 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c +++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/main.c @@ -18,7 +18,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. */ @@ -91,7 +91,22 @@ int rtsock = -1; /* Routing socket */ struct rt_msghdr *rt_msg; /* Routing socket message */ struct sockaddr_in6 *rta_gateway; /* RTA_GATEWAY sockaddr */ struct sockaddr_dl *rta_ifp; /* RTA_IFP sockaddr */ -int mibsock = -1; /* mib request socket */ + +/* + * These sockets are used internally in this file. + */ +static int mibsock = -1; /* mib request socket */ +static int cmdsock = -1; /* command socket */ + +static int ndpd_setup_cmd_listener(void); +static void ndpd_cmd_handler(int); +static int ndpd_process_cmd(int, ipadm_ndpd_msg_t *); +static int ndpd_send_error(int, int); +static int ndpd_set_autoconf(const char *, boolean_t); +static int ndpd_create_addrs(const char *, struct sockaddr_in6, int, + boolean_t, boolean_t, char *); +static int ndpd_delete_addrs(const char *); +static int phyint_check_ipadm_intfid(struct phyint *); /* * Return the current time in milliseconds truncated to @@ -361,7 +376,7 @@ poll_add(int fd) new_num = pollfd_num + 32; newfds = realloc(pollfds, new_num * sizeof (struct pollfd)); if (newfds == NULL) { - logperror("poll_add: realloc"); + logperror("realloc"); return (-1); } @@ -449,20 +464,27 @@ if_process(int s, char *ifname, boolean_t first) pi = phyint_lookup(phyintname); if (pi == NULL) { - /* - * Do not add anything for new interfaces until they are UP. - * For existing interfaces we track the up flag. - */ - if (!(lifr.lifr_flags & IFF_UP)) - return; - pi = phyint_create(phyintname); if (pi == NULL) { logmsg(LOG_ERR, "if_process: out of memory\n"); return; } + /* + * if in.ndpd is restarted, check with ipmgmtd if there is any + * interface id to be configured for this interface. + */ + if (first) { + if (phyint_check_ipadm_intfid(pi) == -1) + logmsg(LOG_ERR, "Could not get ipadm info\n"); + } + } else { + /* + * if the phyint already exists, synchronize it with + * the kernel state. For a newly created phyint, phyint_create + * calls phyint_init_from_k(). + */ + (void) phyint_init_from_k(pi); } - (void) phyint_init_from_k(pi); if (pi->pi_sock == -1 && !(pi->pi_kernel_state & PI_PRESENT)) { /* Interface is not yet present */ if (debug & D_PHYINT) { @@ -1088,7 +1110,7 @@ solicit_event(struct phyint *pi, enum solicit_events event, uint_t elapsed) "found on %s; assuming default flags\n", pi->pi_name); } - if (pi->pi_StatefulAddrConf) { + if (pi->pi_autoconf && pi->pi_StatefulAddrConf) { pi->pi_ra_flags |= ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER; start_dhcp(pi); @@ -1376,6 +1398,12 @@ in_signal(int fd) if (pi->pi_AdvSendAdvertisements) check_to_advertise(pi, START_FINAL_ADV); + /* + * Remove all the configured addresses. + * Remove the addrobj names created with ipmgmtd. + * Release the dhcpv6 addresses if any. + * Cleanup the phyints. + */ phyint_delete(pi); } @@ -1411,7 +1439,7 @@ in_signal(int fd) /* NOTREACHED */ case 255: /* - * Special "signal" from looback_ra_enqueue. + * Special "signal" from loopback_ra_enqueue. * Handle any queued loopback router advertisements. */ loopback_ra_dequeue(); @@ -1838,6 +1866,8 @@ check_if_removed(struct phyint *pi) if (!pr->pr_in_use) { /* Clear everything except PR_STATIC */ pr->pr_kernel_state &= PR_STATIC; + if (pr->pr_state & PR_STATIC) + prefix_update_ipadm_addrobj(pr, _B_FALSE); pr->pr_name[0] = '\0'; if (pr->pr_state & PR_STATIC) { prefix_delete(pr); @@ -1870,6 +1900,7 @@ check_if_removed(struct phyint *pi) for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) { next_pr = pr->pr_next; if (pr->pr_state & PR_AUTO) + prefix_update_ipadm_addrobj(pr, _B_FALSE); prefix_delete(pr); } @@ -2033,10 +2064,10 @@ main(int argc, char *argv[]) if (show_ifs) phyint_print_all(); - if (debug == 0) { + if (debug == 0) initlog(); - } + cmdsock = ndpd_setup_cmd_listener(); setup_eventpipe(); rtsock = setup_rtsock(); mibsock = setup_mibsock(); @@ -2067,6 +2098,10 @@ main(int argc, char *argv[]) process_mibsock(mibsock); break; } + if (pollfds[i].fd == cmdsock) { + ndpd_cmd_handler(cmdsock); + break; + } /* * Run timer routine to advance clock if more than * half a second since the clock was advanced. @@ -2167,3 +2202,417 @@ logperror_pr(const struct prefix *pr, const char *str) strerror(errno)); } } + +static int +ndpd_setup_cmd_listener(void) +{ + int sock; + int ret; + struct sockaddr_un servaddr; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + logperror("socket"); + exit(1); + } + + bzero(&servaddr, sizeof (servaddr)); + servaddr.sun_family = AF_UNIX; + (void) strlcpy(servaddr.sun_path, IPADM_UDS_PATH, + sizeof (servaddr.sun_path)); + (void) unlink(servaddr.sun_path); + ret = bind(sock, (struct sockaddr *)&servaddr, sizeof (servaddr)); + if (ret < 0) { + logperror("bind"); + exit(1); + } + if (listen(sock, 30) < 0) { + logperror("listen"); + exit(1); + } + if (poll_add(sock) == -1) { + logmsg(LOG_ERR, "command socket could not be added to the " + "polling set\n"); + exit(1); + } + + return (sock); +} + +/* + * Commands received over the command socket come here + */ +static void +ndpd_cmd_handler(int sock) +{ + int newfd; + struct sockaddr_storage peer; + socklen_t peerlen; + ipadm_ndpd_msg_t ndpd_msg; + int retval; + + peerlen = sizeof (peer); + newfd = accept(sock, (struct sockaddr *)&peer, &peerlen); + if (newfd < 0) { + logperror("accept"); + return; + } + + retval = ipadm_ndpd_read(newfd, &ndpd_msg, sizeof (ndpd_msg)); + if (retval != 0) + logperror("Could not read ndpd command"); + + retval = ndpd_process_cmd(newfd, &ndpd_msg); + if (retval != 0) { + logmsg(LOG_ERR, "ndpd command on interface %s failed with " + "error %s\n", ndpd_msg.inm_ifname, strerror(retval)); + } + (void) close(newfd); +} + +/* + * Process the commands received from the cmd listener socket. + */ +static int +ndpd_process_cmd(int newfd, ipadm_ndpd_msg_t *msg) +{ + int err; + + if (!ipadm_check_auth()) { + logmsg(LOG_ERR, "User not authorized to send the command\n"); + (void) ndpd_send_error(newfd, EPERM); + return (EPERM); + } + switch (msg->inm_cmd) { + case IPADM_DISABLE_AUTOCONF: + err = ndpd_set_autoconf(msg->inm_ifname, _B_FALSE); + break; + + case IPADM_ENABLE_AUTOCONF: + err = ndpd_set_autoconf(msg->inm_ifname, _B_TRUE); + break; + + case IPADM_CREATE_ADDRS: + err = ndpd_create_addrs(msg->inm_ifname, msg->inm_intfid, + msg->inm_intfidlen, msg->inm_stateless, + msg->inm_stateful, msg->inm_aobjname); + break; + + case IPADM_DELETE_ADDRS: + err = ndpd_delete_addrs(msg->inm_ifname); + break; + + default: + err = EINVAL; + break; + } + + (void) ndpd_send_error(newfd, err); + + return (err); +} + +static int +ndpd_send_error(int fd, int error) +{ + return (ipadm_ndpd_write(fd, &error, sizeof (error))); +} + +/* + * Disables/Enables autoconfiguration of addresses on the + * given physical interface. + * This is provided to support the legacy method of configuring IPv6 + * addresses. i.e. `ifconfig bge0 inet6 plumb` will plumb the interface + * and start stateless and stateful autoconfiguration. If this function is + * not called with enable=_B_FALSE, no autoconfiguration will be done until + * ndpd_create_addrs() is called with an Interface ID. + */ +static int +ndpd_set_autoconf(const char *ifname, boolean_t enable) +{ + struct phyint *pi; + + pi = phyint_lookup((char *)ifname); + if (pi == NULL) { + /* + * If the physical interface was plumbed but no + * addresses were configured yet, phyint will not exist. + */ + pi = phyint_create((char *)ifname); + if (pi == NULL) { + logmsg(LOG_ERR, "could not create phyint for " + "interface %s", ifname); + return (ENOMEM); + } + } + pi->pi_autoconf = enable; + + if (debug & D_PHYINT) { + logmsg(LOG_DEBUG, "ndpd_set_autoconf: %s autoconf for " + "interface %s\n", (enable ? "enabled" : "disabled"), + pi->pi_name); + } + return (0); +} + +/* + * Create auto-configured addresses on the given interface using + * the given token as the interface id during the next Router Advertisement. + * Currently, only one token per interface is supported. + */ +static int +ndpd_create_addrs(const char *ifname, struct sockaddr_in6 intfid, int intfidlen, + boolean_t stateless, boolean_t stateful, char *addrobj) +{ + struct phyint *pi; + struct lifreq lifr; + struct sockaddr_in6 *sin6; + int err; + + pi = phyint_lookup((char *)ifname); + if (pi == NULL) { + /* + * If the physical interface was plumbed but no + * addresses were configured yet, phyint will not exist. + */ + pi = phyint_create((char *)ifname); + if (pi == NULL) { + if (debug & D_PHYINT) + logmsg(LOG_ERR, "could not create phyint " + "for interface %s", ifname); + return (ENOMEM); + } + } else if (pi->pi_autoconf) { + logmsg(LOG_ERR, "autoconfiguration already in progress\n"); + return (EEXIST); + } + check_autoconf_var_consistency(pi, stateless, stateful); + + if (intfidlen == 0) { + pi->pi_default_token = _B_TRUE; + if (ifsock < 0) { + ifsock = socket(AF_INET6, SOCK_DGRAM, 0); + if (ifsock < 0) { + err = errno; + logperror("ndpd_create_addrs: socket"); + return (err); + } + } + (void) strncpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); + sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; + if (ioctl(ifsock, SIOCGLIFTOKEN, (char *)&lifr) < 0) { + err = errno; + logperror("SIOCGLIFTOKEN"); + return (err); + } + pi->pi_token = sin6->sin6_addr; + pi->pi_token_length = lifr.lifr_addrlen; + } else { + pi->pi_default_token = _B_FALSE; + pi->pi_token = intfid.sin6_addr; + pi->pi_token_length = intfidlen; + } + pi->pi_stateless = stateless; + pi->pi_stateful = stateful; + (void) strlcpy(pi->pi_ipadm_aobjname, addrobj, + sizeof (pi->pi_ipadm_aobjname)); + + /* We can allow autoconfiguration now. */ + pi->pi_autoconf = _B_TRUE; + + /* Restart the solicitations. */ + if (pi->pi_sol_state == DONE_SOLICIT) + pi->pi_sol_state = NO_SOLICIT; + if (pi->pi_sol_state == NO_SOLICIT) + check_to_solicit(pi, START_INIT_SOLICIT); + if (debug & D_PHYINT) + logmsg(LOG_DEBUG, "ndpd_create_addrs: " + "added token to interface %s\n", pi->pi_name); + return (0); +} + +/* + * This function deletes all addresses on the given interface + * with the given Interface ID. + */ +static int +ndpd_delete_addrs(const char *ifname) +{ + struct phyint *pi; + struct prefix *pr, *next_pr; + struct lifreq lifr; + int err; + + pi = phyint_lookup((char *)ifname); + if (pi == NULL) { + logmsg(LOG_ERR, "no phyint found for %s", ifname); + return (ENXIO); + } + if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_token)) { + logmsg(LOG_ERR, "token does not exist for %s", ifname); + return (EINVAL); + } + + if (ifsock < 0) { + ifsock = socket(AF_INET6, SOCK_DGRAM, 0); + if (ifsock < 0) { + err = errno; + logperror("ndpd_create_addrs: socket"); + return (err); + } + } + /* Remove the prefixes for this phyint if they exist */ + for (pr = pi->pi_prefix_list; pr != NULL; pr = next_pr) { + next_pr = pr->pr_next; + if (pr->pr_name[0] == '\0') { + prefix_delete(pr); + continue; + } + /* + * Delete all the prefixes for the auto-configured + * addresses as well as the DHCPv6 addresses. + */ + (void) strncpy(lifr.lifr_name, pr->pr_name, + sizeof (lifr.lifr_name)); + if (ioctl(ifsock, SIOCGLIFFLAGS, (char *)&lifr) < 0) { + err = errno; + logperror("SIOCGLIFFLAGS"); + return (err); + } + if ((lifr.lifr_flags & IFF_ADDRCONF) || + (lifr.lifr_flags & IFF_DHCPRUNNING)) { + prefix_update_ipadm_addrobj(pr, _B_FALSE); + } + prefix_delete(pr); + } + + /* + * If we had started dhcpagent, we need to release the leases + * if any are required. + */ + if (pi->pi_stateful) { + (void) strncpy(lifr.lifr_name, pi->pi_name, + sizeof (lifr.lifr_name)); + if (ioctl(ifsock, SIOCGLIFFLAGS, (char *)&lifr) < 0) { + err = errno; + logperror("SIOCGLIFFLAGS"); + return (err); + } + if (lifr.lifr_flags & IFF_DHCPRUNNING) + release_dhcp(pi); + } + + /* + * Reset the Interface ID on this phyint and stop autoconfigurations + * until a new interface ID is provided. + */ + pi->pi_token = in6addr_any; + pi->pi_token_length = 0; + pi->pi_autoconf = _B_FALSE; + pi->pi_ipadm_aobjname[0] = '\0'; + + /* Reset the stateless and stateful settings to default. */ + pi->pi_stateless = pi->pi_StatelessAddrConf; + pi->pi_stateful = pi->pi_StatefulAddrConf; + + if (debug & D_PHYINT) { + logmsg(LOG_DEBUG, "ndpd_delete_addrs: " + "removed token from interface %s\n", pi->pi_name); + } + return (0); +} + +void +check_autoconf_var_consistency(struct phyint *pi, boolean_t stateless, + boolean_t stateful) +{ + /* + * If StatelessAddrConf and StatelessAddrConf are set in + * /etc/inet/ndpd.conf, check if the new values override those + * settings. If so, log a warning. + */ + if ((pi->pi_StatelessAddrConf != + ifdefaults[I_StatelessAddrConf].cf_value && + stateless != pi->pi_StatelessAddrConf) || + (pi->pi_StatefulAddrConf != + ifdefaults[I_StatefulAddrConf].cf_value && + stateful != pi->pi_StatefulAddrConf)) { + logmsg(LOG_ERR, "check_autoconf_var_consistency: " + "Overriding the StatelessAddrConf or StatefulAddrConf " + "settings in ndpd.conf with the new values for " + "interface %s\n", pi->pi_name); + } +} + +/* + * If ipadm was used to start autoconfiguration and in.ndpd was restarted + * for some reason, in.ndpd has to resume autoconfiguration when it comes up. + * In this function, it scans the ipadm_addr_info() output to find a link-local + * on this interface with address type "addrconf" and extracts the interface id. + * It also stores the addrobj name to be used later when new addresses are + * created for the prefixes advertised by the router. + * If autoconfiguration was never started on this interface before in.ndpd + * was killed, then in.ndpd should refrain from configuring prefixes, even if + * there is a valid link-local on this interface, created by ipadm (identified + * if there is a valid addrobj name). + */ +static int +phyint_check_ipadm_intfid(struct phyint *pi) +{ + ipadm_status_t status; + ipadm_addr_info_t *addrinfo; + struct ifaddrs *ifap; + ipadm_addr_info_t *ainfop; + struct sockaddr_in6 *sin6; + ipadm_handle_t iph; + + if (ipadm_open(&iph, 0) != IPADM_SUCCESS) { + logmsg(LOG_ERR, "could not open handle to libipadm\n"); + return (-1); + } + + status = ipadm_addr_info(iph, pi->pi_name, &addrinfo, + IPADM_OPT_ZEROADDR, LIFC_NOXMIT|LIFC_TEMPORARY); + if (status != IPADM_SUCCESS) { + ipadm_close(iph); + return (-1); + } + pi->pi_autoconf = _B_TRUE; + for (ainfop = addrinfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { + ifap = &ainfop->ia_ifa; + if (ifap->ifa_addr->ss_family != AF_INET6 || + ainfop->ia_state == IFA_DISABLED) + continue; + sin6 = (struct sockaddr_in6 *)ifap->ifa_addr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + if (ainfop->ia_atype == IPADM_ADDR_IPV6_ADDRCONF) { + pi->pi_token = sin6->sin6_addr; + pi->pi_token._S6_un._S6_u32[0] = 0; + pi->pi_token._S6_un._S6_u32[1] = 0; + pi->pi_autoconf = _B_TRUE; + (void) strlcpy(pi->pi_ipadm_aobjname, + ainfop->ia_aobjname, + sizeof (pi->pi_ipadm_aobjname)); + break; + } + /* + * If IFF_NOLINKLOCAL is set, then the link-local + * was created using ipadm. Do not autoconfigure until + * ipadm is explicitly used for autoconfiguration. + */ + if (ifap->ifa_flags & IFF_NOLINKLOCAL) + pi->pi_autoconf = _B_FALSE; + } else if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) && + strrchr(ifap->ifa_name, ':') == NULL) { + /* The interface was created using ipadm. */ + pi->pi_autoconf = _B_FALSE; + } + } + ipadm_free_addr_info(addrinfo); + if (!pi->pi_autoconf) { + pi->pi_token = in6addr_any; + pi->pi_token_length = 0; + } + ipadm_close(iph); + return (0); +} diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c index 0a9e1e6a13..5f1acb2301 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c +++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c @@ -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. */ @@ -307,57 +307,99 @@ incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len, } /* - * Start up DHCPv6 on a given physical interface. Does not wait for a message - * to be returned from the daemon. + * Function that sends commands to dhcpagent daemon. */ -void -start_dhcp(struct phyint *pi) +int +dhcp_op(struct phyint *pi, int type) { dhcp_ipc_request_t *request; dhcp_ipc_reply_t *reply = NULL; int error; - int type; - - if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) { - logmsg(LOG_ERR, "start_dhcp: unable to start %s\n", - DHCP_AGENT_PATH); - /* make sure we try again next time there's a chance */ - pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER; - return; - } - - type = (pi->pi_ra_flags & ND_RA_FLAG_MANAGED) ? DHCP_START : - DHCP_INFORM; request = dhcp_ipc_alloc_request(type | DHCP_V6, pi->pi_name, NULL, 0, DHCP_TYPE_NONE); if (request == NULL) { - logmsg(LOG_ERR, "start_dhcp: out of memory\n"); + logmsg(LOG_ERR, "dhcp_op: out of memory\n"); /* make sure we try again next time there's a chance */ - pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER; - return; + if (type != DHCP_RELEASE) { + pi->pi_ra_flags &= + ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER; + } + return (DHCP_IPC_E_MEMORY); } error = dhcp_ipc_make_request(request, &reply, 0); free(request); if (error != 0) { - logmsg(LOG_ERR, "start_dhcp: err: %s: %s\n", pi->pi_name, - dhcp_ipc_strerror(error)); - return; + logmsg(LOG_ERR, "could not send request to dhcpagent: " + "%s: %s\n", pi->pi_name, dhcp_ipc_strerror(error)); + return (error); } error = reply->return_code; free(reply); + return (error); +} + +/* + * Start up DHCPv6 on a given physical interface. Does not wait for + * a message to be returned from the daemon. + */ +void +start_dhcp(struct phyint *pi) +{ + int error; + int type; + + if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) { + logmsg(LOG_ERR, "unable to start %s\n", DHCP_AGENT_PATH); + /* make sure we try again next time there's a chance */ + pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER; + return; + } + + else if (pi->pi_ra_flags & ND_RA_FLAG_MANAGED) + type = DHCP_START; + else + type = DHCP_INFORM; + + error = dhcp_op(pi, type); /* * Timeout is considered to be "success" because we don't wait for DHCP * to do its exchange. */ if (error != DHCP_IPC_SUCCESS && error != DHCP_IPC_E_RUNNING && error != DHCP_IPC_E_TIMEOUT) { - logmsg(LOG_ERR, "start_dhcp: ret: %s: %s\n", pi->pi_name, - dhcp_ipc_strerror(error)); - return; + logmsg(LOG_ERR, "Error in dhcpagent: %s: %s\n", + pi->pi_name, dhcp_ipc_strerror(error)); + } +} + +/* + * Release the acquired DHCPv6 lease on a given physical interface. + * Does not wait for a message to be returned from the daemon. + */ +void +release_dhcp(struct phyint *pi) +{ + int error; + int type; + + type = DHCP_RELEASE; +retry: + error = dhcp_op(pi, type); + if (error != DHCP_IPC_SUCCESS && error != DHCP_IPC_E_RUNNING && + error != DHCP_IPC_E_TIMEOUT) { + if (type == DHCP_RELEASE && error == DHCP_IPC_E_OUTSTATE) { + /* + * Drop the dhcp control if we cannot release it. + */ + type = DHCP_DROP; + goto retry; + } + logmsg(LOG_ERR, "Error in dhcpagent: %s: %s\n", + pi->pi_name, dhcp_ipc_strerror(error)); } } @@ -436,7 +478,7 @@ incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len, * get addresses via DHCP; only "other" data. If "managed" is set, * then we must always get both addresses and "other" data. */ - if (pi->pi_StatefulAddrConf && + if (pi->pi_autoconf && pi->pi_stateful && (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) { if (debug & D_DHCP) { @@ -540,14 +582,14 @@ incoming_prefix_opt(struct phyint *pi, uchar_t *opt, return; } if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && - pi->pi_StatelessAddrConf) { + pi->pi_stateless && pi->pi_autoconf) { good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback); } if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && good_prefix) { incoming_prefix_onlink(pi, opt); } - if (pi->pi_StatefulAddrConf) + if (pi->pi_stateful && pi->pi_autoconf) incoming_prefix_stateful(pi, opt); } @@ -924,8 +966,10 @@ incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr, "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n", pbuf, plen, abuf, pi->pi_name, preftime, pi->pi_TmpRegenAdvance); - if (new_prefix) + if (new_prefix) { + prefix_update_ipadm_addrobj(pr, _B_FALSE); prefix_delete(pr); + } return (_B_TRUE); } } @@ -939,7 +983,7 @@ incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr, /* * Form a new local address if the lengths match. */ - if (pr->pr_flags && IFF_TEMPORARY) { + if (pr->pr_flags & IFF_TEMPORARY) { if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) { if (!tmptoken_create(pi)) { prefix_delete(pr); @@ -990,6 +1034,7 @@ incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr, "Prefix already exists in interface %s\n", other_pr->pr_physical->pi_name); if (new_prefix) { + prefix_update_ipadm_addrobj(pr, _B_FALSE); prefix_delete(pr); return (_B_FALSE); } diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c index 09e6137965..72c006b63a 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c +++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.c @@ -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. */ @@ -131,6 +131,10 @@ phyint_create(char *name) pi->pi_TmpRegenCountdown = TIMER_INFINITY; pi->pi_sock = -1; + pi->pi_stateless = pi->pi_StatelessAddrConf; + pi->pi_stateful = pi->pi_StatefulAddrConf; + pi->pi_autoconf = _B_TRUE; + pi->pi_default_token = _B_TRUE; if (phyint_init_from_k(pi) == -1) { free(pi); return (NULL); @@ -237,6 +241,7 @@ start_over: (void) close(pi->pi_sock); pi->pi_sock = -1; } + if (debug & D_PHYINT) { logmsg(LOG_DEBUG, "phyint_init_from_k(%s): " "IFF_NOLOCAL or not IFF_UP\n", pi->pi_name); @@ -258,19 +263,21 @@ start_over: sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr; pi->pi_ifaddr = sin6->sin6_addr; - if (ioctl(fd, SIOCGLIFTOKEN, (char *)&lifr) < 0) { - logperror_pi(pi, "phyint_init_from_k: SIOCGLIFTOKEN"); - goto error; - } - /* Ignore interface if the token is all zeros */ - sin6 = (struct sockaddr_in6 *)&lifr.lifr_token; - if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { - logmsg(LOG_ERR, "ignoring interface %s: zero token\n", - pi->pi_name); - goto error; + if (pi->pi_autoconf && pi->pi_default_token) { + if (ioctl(fd, SIOCGLIFTOKEN, (char *)&lifr) < 0) { + logperror_pi(pi, "phyint_init_from_k: SIOCGLIFTOKEN"); + goto error; + } + /* Ignore interface if the token is all zeros */ + sin6 = (struct sockaddr_in6 *)&lifr.lifr_token; + if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { + logmsg(LOG_ERR, "ignoring interface %s: zero token\n", + pi->pi_name); + goto error; + } + pi->pi_token = sin6->sin6_addr; + pi->pi_token_length = lifr.lifr_addrlen; } - pi->pi_token = sin6->sin6_addr; - pi->pi_token_length = lifr.lifr_addrlen; /* * Guess a remote token for POINTOPOINT by looking at @@ -472,8 +479,10 @@ phyint_delete(struct phyint *pi) while (pi->pi_router_list) router_delete(pi->pi_router_list); - while (pi->pi_prefix_list) + while (pi->pi_prefix_list) { + prefix_update_ipadm_addrobj(pi->pi_prefix_list, _B_FALSE); prefix_delete(pi->pi_prefix_list); + } while (pi->pi_adv_prefix_list) adv_prefix_delete(pi->pi_adv_prefix_list); @@ -1262,6 +1271,12 @@ prefix_init_from_k(struct prefix *pr) pr2->pr_name, pr->pr_name); prefix_update_dhcp(pr); } + /* + * If this interface was created using ipadm, store the + * addrobj for the DHCPv6 interface in ipmgmtd daemon's + * in-memory aobjmap. + */ + prefix_update_ipadm_addrobj(pr, _B_TRUE); } else { if (ioctl(sock, SIOCGLIFSUBNET, (char *)&lifr) < 0) { logperror_pr(pr, @@ -1344,6 +1359,7 @@ error: * was added by in.ndpd (i.e. PR_STATIC is not set). * Handles delete of things that have not yet been inserted in the list * i.e. pr_physical is NULL. + * Removes the ipadm addrobj created for the prefix. */ void prefix_delete(struct prefix *pr) @@ -1357,9 +1373,10 @@ prefix_delete(struct prefix *pr) inet_ntop(AF_INET6, (void *)&pr->pr_prefix, abuf, sizeof (abuf)), pr->pr_prefix_len); } + pi = pr->pr_physical; + /* Remove non-static prefixes from the kernel. */ pr->pr_state &= PR_STATIC; - pi = pr->pr_physical; if (pr->pr_kernel_state != pr->pr_state) prefix_update_k(pr); @@ -1372,6 +1389,7 @@ prefix_delete(struct prefix *pr) if (pr->pr_next != NULL) pr->pr_next->pr_prev = pr->pr_prev; pr->pr_next = pr->pr_prev = NULL; + free(pr); } @@ -1595,6 +1613,11 @@ prefix_update_k(struct prefix *pr) logperror_pr(pr, "prefix_update_k: SIOCSLIFADDR"); return; } + /* + * If this interface was created using ipadm, store the + * addrobj for the prefix in ipmgmtd daemon's aobjmap. + */ + prefix_update_ipadm_addrobj(pr, _B_TRUE); if (pr->pr_state & PR_ONLINK) { sin6->sin6_addr = pr->pr_prefix; lifr.lifr_addrlen = pr->pr_prefix_len; @@ -2315,4 +2338,55 @@ phyint_cleanup(struct phyint *pi) (void) poll_remove(pi->pi_sock); (void) close(pi->pi_sock); pi->pi_sock = -1; + pi->pi_stateless = pi->pi_StatelessAddrConf; + pi->pi_stateful = pi->pi_StatefulAddrConf; + pi->pi_ipadm_aobjname[0] = '\0'; +} + +/* + * Sets/removes the ipadm address object name for the given prefix. + */ +void +prefix_update_ipadm_addrobj(struct prefix *pr, boolean_t add) +{ + struct phyint *pi = pr->pr_physical; + int lnum = 0; + char *cp; + ipadm_handle_t iph; + ipadm_status_t status; + + /* + * If ipadm was used to autoconfigure this interface, + * pi_ipadm_aobjname will contain the address object name + * that is used to identify the addresses. Use the same + * address object name for this prefix. + */ + if (pi->pi_ipadm_aobjname[0] == '\0' || + pr->pr_name[0] == '\0' || IN6_IS_ADDR_LINKLOCAL(&pr->pr_address) || + (!(pr->pr_flags & IFF_ADDRCONF) && + !(pr->pr_flags & IFF_DHCPRUNNING))) { + return; + } + if ((status = ipadm_open(&iph, 0)) != IPADM_SUCCESS) { + logmsg(LOG_ERR, "Could not open handle to libipadm: %s\n", + ipadm_status2str(status)); + return; + } + cp = strrchr(pr->pr_name, ':'); + if (cp != NULL) + lnum = atoi(++cp); + if (add) { + status = ipadm_add_aobjname(iph, pi->pi_name, AF_INET6, + pi->pi_ipadm_aobjname, IPADM_ADDR_IPV6_ADDRCONF, lnum); + } else { + status = ipadm_delete_aobjname(iph, pi->pi_name, AF_INET6, + pi->pi_ipadm_aobjname, IPADM_ADDR_IPV6_ADDRCONF, lnum); + } + /* Ignore the error if the ipmgmtd daemon is not running */ + if (status != IPADM_SUCCESS && status != IPADM_IPC_ERROR) { + logmsg(LOG_ERR, "ipadm error in %s '%s' : %s\n", + (add ? "adding" : "deleting"), pi->pi_ipadm_aobjname, + ipadm_status2str(status)); + } + ipadm_close(iph); } diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h index dfc5414d5d..5024c3d57b 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h +++ b/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/tables.h @@ -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. */ @@ -31,6 +31,7 @@ extern "C" { #endif #include <ndpd.h> +#include <libipadm.h> enum adv_states { NO_ADV = 0, REG_ADV, INIT_ADV, SOLICIT_ADV, FINAL_ADV }; enum adv_events { ADV_OFF, START_INIT_ADV, START_FINAL_ADV, RECEIVED_SOLICIT, @@ -60,6 +61,8 @@ struct phyint { uint_t pi_mtu; /* From SIOCGLIFMTU */ struct in6_addr pi_token; uint_t pi_token_length; + boolean_t pi_stateless; + boolean_t pi_stateful; struct in6_addr pi_tmp_token; /* For RFC3041 addrs */ struct in6_addr pi_dst_token; /* For POINTOPOINT */ @@ -119,9 +122,12 @@ struct phyint { * even if no Router Advertisements are received. * Tracked using pi_each_time_since_random. */ - uint_t pi_RetransTimer; /* In milliseconds */ + uint_t pi_RetransTimer; /* In milliseconds */ - uint_t pi_ra_flags; /* Detect when to start DHCP */ + uint_t pi_ra_flags; /* Detect when to start DHCP */ + boolean_t pi_autoconf; /* Enable/Disable autoconfiguration */ + boolean_t pi_default_token; /* Use default token */ + char pi_ipadm_aobjname[IPADM_AOBJSIZ]; }; /* @@ -311,6 +317,7 @@ extern void print_prefixlist(struct confvar *confvar); extern void in_data(struct phyint *pi); extern void start_dhcp(struct phyint *pi); +extern void release_dhcp(struct phyint *pi); extern void incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len, struct sockaddr_in6 *from, boolean_t loopback); @@ -323,6 +330,9 @@ extern boolean_t incoming_prefix_addrconf_process(struct phyint *pi, extern void incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt); +extern void check_autoconf_var_consistency(struct phyint *, boolean_t, + boolean_t); +extern void prefix_update_ipadm_addrobj(struct prefix *pr, boolean_t add); #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/cmd-inet/usr.sbin/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/Makefile index ce1ffcf27e..f7ae749660 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile @@ -54,7 +54,7 @@ NSLPROG= 6to4relay arp gettable hostconfig in.comsat in.rarpd \ CMDPROG= in.telnetd K5PROGS= in.telnetd in.rlogind in.rshd TSNETPROG= route -DLADMPROG= 6to4relay ndd +DLADMPROG= 6to4relay DEFAULTFILES= telnetd.dfl PROGSRCS= $(PROG:%=%.c) @@ -66,12 +66,12 @@ K5TELNETOBJS= in.telnetd.o SRCS= $(PROGSRCS) $(OTHERSRC) SUBDIRS= bootconfchk htable ifconfig ilbadm in.ftpd in.rdisc in.routed \ - in.talkd inetadm inetconv ipmpstat ipqosconf ipsecutils \ + in.talkd inetadm inetconv ipadm ipmpstat ipqosconf ipsecutils \ kssl/kssladm kssl/ksslcfg nwamadm nwamcfg ping routeadm \ snoop sppptun traceroute wificonfig MSGSUBDIRS= bootconfchk htable ifconfig ilbadm in.ftpd in.routed in.talkd \ - inetadm inetconv ipmpstat ipqosconf ipsecutils kssl/ksslcfg \ + inetadm inetconv ipadm ipmpstat ipqosconf ipsecutils kssl/ksslcfg \ nwamadm nwamcfg routeadm sppptun snoop wificonfig # As programs get lint-clean, add them here and to the 'lint' target. @@ -151,6 +151,7 @@ in.rarpd := LDLIBS += -linetutil -ldlpi if_mpadm := LDLIBS += -linetutil -lipmp if_mpadm.po := XGETFLAGS += -a route := CPPFLAGS += -DNDEBUG +ndd := LDLIBS += -ldladm -lipadm gettable in.comsat := LDFLAGS += $(MAPFILE.NGB:%=-M%) .KEEP_STATE: diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile index 64fe8e8ae7..9144fb1439 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/Makefile @@ -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,7 +37,7 @@ COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c) SRCS= $(LOCALSRCS) $(COMMONSRCS) CPPFLAGS += -I$(CMDINETCOMMONDIR) -I$(SRC)/common/net/dhcp -LDLIBS += -ldhcpagent -ldlpi -linetutil -lipmp -ldladm +LDLIBS += -ldhcpagent -ldlpi -linetutil -lipmp -ldladm -lipadm LINTFLAGS += -m ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%) diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c index 85e95c5fa4..ba798ccfcd 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c @@ -1,5 +1,5 @@ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -18,11 +18,15 @@ #include <libdllink.h> #include <inet/ip.h> #include <inet/ipsec_impl.h> +#include <libipadm.h> +#include <ifaddrs.h> +#include <libsocket_priv.h> #define LOOPBACK_IF "lo0" #define NONE_STR "none" #define ARP_MOD_NAME "arp" -#define IPMPSTUB (void *)-1 +#define LIFC_DEFAULT (LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\ + LIFC_UNDER_IPMP) typedef struct if_flags { uint64_t iff_value; @@ -93,8 +97,11 @@ static char name[LIFNAMSIZ]; /* foreach interface saved name */ static char origname[LIFNAMSIZ]; static int setaddr; +static boolean_t setaddr_done = _B_FALSE; static boolean_t ipsec_policy_set; static boolean_t ipsec_auth_covered; +static ipadm_handle_t iph; +static ipadm_addrobj_t ipaddr; /* * Make sure the algorithm variables hold more than the sizeof an algorithm @@ -149,7 +156,7 @@ static int setifgroupname(char *arg, int64_t param); static int configinfo(char *arg, int64_t param); static void print_config_flags(int af, uint64_t flags); static void print_flags(uint64_t flags); -static void print_ifether(char *ifname); +static void print_ifether(const char *ifname); static int set_tun_encap_limit(char *arg, int64_t param); static int clr_tun_encap_limit(char *arg, int64_t param); static int set_tun_hop_limit(char *arg, int64_t param); @@ -157,6 +164,7 @@ static int setzone(char *arg, int64_t param); static int setallzones(char *arg, int64_t param); static int setifsrc(char *arg, int64_t param); static int lifnum(const char *ifname); +static void plumball(int, char **, int64_t, int64_t, int64_t); /* * Address family specific function prototypes. @@ -172,36 +180,37 @@ static void in6_configinfo(int force, uint64_t flags); * Misc support functions */ static boolean_t ni_entry(const char *, void *); -static void foreachinterface(void (*func)(), int argc, char *argv[], - int af, int64_t onflags, int64_t offflags, - int64_t lifc_flags); -static void ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp); +static void foreachinterface(int argc, char *argv[], + int af, int64_t onflags, int64_t offflags, + int64_t lifc_flags); +static void ifconfig(int argc, char *argv[], int af, + struct ifaddrs *ifa); static boolean_t in_getmask(struct sockaddr_in *saddr, boolean_t addr_set); -static int in_getprefixlen(char *addr, boolean_t slash, int plen); +static int in_getprefixlen(char *addr, boolean_t slash, int plen); static boolean_t in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask); -static void status(void); -static void ifstatus(const char *); -static void tun_status(datalink_id_t); -static void usage(void); -static int strioctl(int s, int cmd, void *buf, int buflen); -static int setifdhcp(const char *caller, const char *ifname, - int argc, char *argv[]); -static int ip_domux2fd(int *, int *, int *, int *, int *); -static int ip_plink(int, int, int, int, int); -static int modop(char *arg, char op); -static int find_all_interfaces(struct lifconf *lifcp, char **buf, - int64_t lifc_flags); -static int create_ipmp(const char *grname, int af, const char *ifname, - boolean_t implicit); -static int create_ipmp_peer(int af, const char *ifname); -static void start_ipmp_daemon(void); -static boolean_t ifaddr_up(ifaddrlistx_t *ifaddrp); -static boolean_t ifaddr_down(ifaddrlistx_t *ifaddrp); +static void status(void); +static void ifstatus(const char *ifname); +static void tun_status(datalink_id_t); +static void usage(void); +static int setifdhcp(const char *caller, const char *ifname, + int argc, char *argv[]); +static int ip_domux2fd(int *, int *, int *, int *, int *); +static int ip_plink(int, int, int, int, int); +static int modop(char *arg, char op); +static int find_all_interfaces(struct lifconf *lifcp, char **buf, + int64_t lifc_flags); +static int create_ipmp(const char *grname, int af, + const char *ifname, boolean_t implicit); +static void start_ipmp_daemon(void); +static boolean_t ifaddr_up(ifaddrlistx_t *ifaddrp); +static boolean_t ifaddr_down(ifaddrlistx_t *ifaddrp); static dladm_status_t ifconfig_dladm_open(const char *, datalink_class_t, - datalink_id_t *); -static void dladmerr_exit(dladm_status_t status, const char *str); + datalink_id_t *); +static void dladmerr_exit(dladm_status_t status, const char *str); +static void ipadmerr_exit(ipadm_status_t status, const char *str); +static boolean_t ifconfig_use_libipadm(int, const char *); #define max(a, b) ((a) < (b) ? (b) : (a)) @@ -366,8 +375,9 @@ main(int argc, char *argv[]) { int64_t lifc_flags; char *default_ip_str; + ipadm_status_t istatus; - lifc_flags = LIFC_NOXMIT|LIFC_TEMPORARY|LIFC_ALLZONES|LIFC_UNDER_IPMP; + lifc_flags = LIFC_DEFAULT; if (argc < 2) { usage(); @@ -412,6 +422,13 @@ main(int argc, char *argv[]) s6 = socket(AF_INET6, SOCK_DGRAM, 0); if (s == -1 || s4 == -1 || s6 == -1) Perror0_exit("socket"); + /* + * Open the global libipadm handle. The flag IPH_LEGACY has to + * be specified to indicate that logical interface names will + * be used during interface creation and address creation. + */ + if ((istatus = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) + ipadmerr_exit(istatus, "unable to open handle to libipadm"); /* * Special interface names is any combination of these flags. @@ -484,157 +501,89 @@ main(int argc, char *argv[]) "ifconfig: %s: no such interface\n", name); exit(1); } - foreachinterface(ifconfig, argc, argv, af, onflags, offflags, + foreachinterface(argc, argv, af, onflags, offflags, lifc_flags); } else { - ifconfig(argc, argv, af, (struct lifreq *)NULL); + ifconfig(argc, argv, af, NULL); } + ipadm_close(iph); return (0); } /* - * For each interface, call (*func)(argc, argv, af, lifrp). + * For each interface, call ifconfig(argc, argv, af, ifa). * Only call function if onflags and offflags are set or clear, respectively, * in the interfaces flags field. */ static void -foreachinterface(void (*func)(), int argc, char *argv[], int af, +foreachinterface(int argc, char *argv[], int af, int64_t onflags, int64_t offflags, int64_t lifc_flags) { - int n; - char *buf; - struct lifnum lifn; - struct lifconf lifc; - struct lifreq *lifrp; - struct lifreq lifrl; /* Local lifreq struct */ - int numifs; - unsigned bufsize; - int plumball = 0; - int save_af = af; + ipadm_addr_info_t *ainfo, *ainfop; + struct ifaddrs *ifa; + ipadm_status_t istatus; - buf = NULL; /* * Special case: * ifconfig -a plumb should find all network interfaces in the current * zone. */ if (argc > 0 && (strcmp(*argv, "plumb") == 0)) { - if (find_all_interfaces(&lifc, &buf, lifc_flags) != 0 || - lifc.lifc_len == 0) - return; - plumball = 1; - } else { - lifn.lifn_family = AF_UNSPEC; - lifn.lifn_flags = lifc_flags; - if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) { - Perror0_exit("Could not determine number" - " of interfaces"); - } - numifs = lifn.lifn_count; - if (debug) - (void) printf("ifconfig: %d interfaces\n", numifs); - - bufsize = numifs * sizeof (struct lifreq); - if ((buf = malloc(bufsize)) == NULL) { - Perror0("out of memory\n"); - (void) close(s); - return; - } - - lifc.lifc_family = AF_UNSPEC; - lifc.lifc_flags = lifc_flags; - lifc.lifc_len = bufsize; - lifc.lifc_buf = buf; - - if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) { - Perror0("SIOCGLIFCONF"); - (void) close(s); - free(buf); - return; - } + plumball(argc, argv, onflags, offflags, lifc_flags); + return; } + /* Get all addresses in kernel including addresses that are zero. */ + istatus = ipadm_addr_info(iph, NULL, &ainfo, IPADM_OPT_ZEROADDR, + lifc_flags); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "could not get addresses from kernel"); - lifrp = lifc.lifc_req; - for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { - - if (!plumball) { - /* - * We must close and recreate the socket each time - * since we don't know what type of socket it is now - * (each status function may change it). - */ - - (void) close(s); - - af = lifrp->lifr_addr.ss_family; - s = socket(SOCKET_AF(af), SOCK_DGRAM, 0); - if (s == -1) { - /* - * Perror0() assumes the name to be in the - * globally defined lifreq structure. - */ - (void) strncpy(lifr.lifr_name, - lifrp->lifr_name, sizeof (lifr.lifr_name)); - Perror0_exit("socket"); - } - } - - /* - * Only service interfaces that match the on and off - * flags masks. - */ + /* + * For each logical interface, call ifconfig() with the + * given arguments. + */ + for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) { + if (ainfop->ia_state == IFA_DISABLED) + continue; + ifa = &ainfop->ia_ifa; if (onflags || offflags) { - (void) memset(&lifrl, 0, sizeof (lifrl)); - (void) strncpy(lifrl.lifr_name, lifrp->lifr_name, - sizeof (lifrl.lifr_name)); - if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifrl) < 0) { - /* - * Perror0() assumes the name to be in the - * globally defined lifreq structure. - */ - (void) strncpy(lifr.lifr_name, - lifrp->lifr_name, sizeof (lifr.lifr_name)); - Perror0_exit("foreachinterface: SIOCGLIFFLAGS"); - } - if ((lifrl.lifr_flags & onflags) != onflags) + if ((ifa->ifa_flags & onflags) != onflags) continue; - if ((~lifrl.lifr_flags & offflags) != offflags) + if ((~ifa->ifa_flags & offflags) != offflags) continue; } + s = (ifa->ifa_addr->ss_family == AF_INET ? s4 : s6); + (void) strncpy(name, ifa->ifa_name, sizeof (name)); + (void) strncpy(origname, name, sizeof (origname)); + ifconfig(argc, argv, af, ifa); + } + ipadm_free_addr_info(ainfo); +} - if (!plumball) { - (void) strncpy(lifrl.lifr_name, lifrp->lifr_name, - sizeof (lifrl.lifr_name)); - if (ioctl(s, SIOCGLIFADDR, (caddr_t)&lifrl) < 0) { - /* - * Perror0() assumes the name to be in the - * globally defined lifreq structure. - */ - (void) strncpy(lifr.lifr_name, - lifrp->lifr_name, sizeof (lifr.lifr_name)); - Perror0("foreachinterface: SIOCGLIFADDR"); - continue; - } - if (lifrl.lifr_addr.ss_family != af) { - /* Switch address family */ - af = lifrl.lifr_addr.ss_family; - (void) close(s); +/* + * Used for `ifconfig -a plumb'. Finds all datalinks and plumbs the interface. + */ +static void +plumball(int argc, char *argv[], int64_t onflags, int64_t offflags, + int64_t lifc_flags) +{ + int n; + struct lifreq *lifrp; + struct lifconf lifc; + char *buf; - s = socket(SOCKET_AF(af), SOCK_DGRAM, 0); - if (s == -1) { - /* - * Perror0() assumes the name to be in - * the globally defined lifreq - * structure. - */ - (void) strncpy(lifr.lifr_name, - lifrp->lifr_name, - sizeof (lifr.lifr_name)); - Perror0_exit("socket"); - } - } - } + if (onflags != 0 || offflags != 0) { + (void) fprintf(stderr, "ifconfig: invalid syntax used to " + "plumb all interfaces.\n"); + exit(1); + } + + if (find_all_interfaces(&lifc, &buf, lifc_flags) != 0 || + lifc.lifc_len == 0) + return; + lifrp = lifc.lifc_req; + for (n = lifc.lifc_len / sizeof (struct lifreq); n > 0; n--, lifrp++) { /* * Reset global state * setaddr: Used by parser to tear apart source and dest @@ -644,24 +593,23 @@ foreachinterface(void (*func)(), int argc, char *argv[], int af, setaddr = 0; (void) strncpy(name, lifrp->lifr_name, sizeof (name)); (void) strncpy(origname, name, sizeof (origname)); - - (*func)(argc, argv, save_af, lifrp); - /* the func could have overwritten origname, so restore */ - (void) strncpy(name, origname, sizeof (name)); + ifconfig(argc, argv, af, NULL); } - if (buf != NULL) - free(buf); } /* - * for the specified interface call (*func)(argc, argv, af, lifrp). + * Parses the interface name and the command in argv[]. Calls the + * appropriate callback function for the given command from `cmds[]' + * table. + * If there is no command specified, it prints all addresses. */ - static void -ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp) +ifconfig(int argc, char *argv[], int af, struct ifaddrs *ifa) { static boolean_t scan_netmask = _B_FALSE; int ret; + ipadm_status_t istatus; + struct lifreq lifr; if (argc == 0) { status(); @@ -782,8 +730,52 @@ ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp) * else (no keyword found), we assume it's an address * of some sort */ - if (p->c_name == 0 && setaddr) - p++; /* got src, do dst */ + if (setaddr && ipaddr != NULL) { + /* + * We must have already filled in a source address in + * `ipaddr' and we now got a destination address. + * Fill it in `ipaddr' and call libipadm to create + * the static address. + */ + if (p->c_name == 0) { + istatus = ipadm_set_dst_addr(ipaddr, *argv, + (p->c_af == AF_ANY ? AF_UNSPEC : af)); + if (istatus != IPADM_SUCCESS) { + ipadmerr_exit(istatus, "could not " + "set destination address"); + } + /* + * finished processing dstaddr, so reset setaddr + */ + setaddr = 0; + } + /* + * Both source and destination address are in `ipaddr'. + * Add the address by calling libipadm. + */ + istatus = ipadm_create_addr(iph, ipaddr, + IPADM_OPT_ACTIVE); + if (istatus != IPADM_SUCCESS) + goto createfailed; + ipadm_destroy_addrobj(ipaddr); + ipaddr = NULL; + setaddr_done = _B_TRUE; + if (p->c_name == 0) { + /* move parser along */ + argc--, argv++; + continue; + } + } + if (p->c_name == 0 && setaddr_done) { + /* + * catch odd commands like + * "ifconfig <intf> addr1 addr2 addr3 addr4 up" + */ + (void) fprintf(stderr, "%s", + "ifconfig: cannot configure more than two " + "addresses in one command\n"); + exit(1); + } if (p->c_func) { if (p->c_af == AF_INET6) { v4compat = 0; @@ -812,8 +804,8 @@ ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp) * the address families match */ if ((p->c_af == AF_ANY) || - (lifrp == (struct lifreq *)NULL) || - (lifrp->lifr_addr.ss_family == p->c_af)) { + (ifa == NULL) || + (ifa->ifa_addr->ss_family == p->c_af)) { ret = (*p->c_func)(*argv, p->c_parameter); /* * If c_func failed and we should @@ -830,11 +822,36 @@ ifconfig(int argc, char *argv[], int af, struct lifreq *lifrp) argc--, argv++; } + if (setaddr && ipaddr != NULL) { + /* + * Only the source address was provided, which was already + * set in `ipaddr'. Add the address by calling libipadm. + */ + istatus = ipadm_create_addr(iph, ipaddr, IPADM_OPT_ACTIVE); + if (istatus != IPADM_SUCCESS) + goto createfailed; + ipadm_destroy_addrobj(ipaddr); + ipaddr = NULL; + setaddr_done = _B_TRUE; + } + /* Check to see if there's a security hole in the tunnel setup. */ if (ipsec_policy_set && !ipsec_auth_covered) { (void) fprintf(stderr, "ifconfig: WARNING: tunnel with only " "ESP and no authentication.\n"); } + return; + +createfailed: + (void) fprintf(stderr, "ifconfig: could not create address:% s\n", + ipadm_status2str(istatus)); + /* Remove the newly created logical interface. */ + if (strcmp(name, origname) != 0) { + assert(strchr(name, ':') != NULL); + (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); + (void) ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr); + } + exit(1); } /* ARGSUSED */ @@ -902,12 +919,15 @@ set_mask_lifreq(struct lifreq *lifr, struct sockaddr_storage *addr, static int setifaddr(char *addr, int64_t param) { + ipadm_status_t istatus; int prefixlen = 0; + struct lifreq lifr1; struct sockaddr_storage laddr; struct sockaddr_storage netmask; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; struct sockaddr_storage sav_netmask; + char cidraddr[BUFSIZ]; if (addr[0] == '/') return (setifprefixlen(addr, 0)); @@ -951,13 +971,53 @@ setifaddr(char *addr, int64_t param) g_netmask_set = G_NETMASK_NIL; break; } + + /* + * Check and see if any "netmask" command is used and perform the + * necessary operation. + */ + set_mask_lifreq(&lifr, &laddr, &netmask); + + /* This check is temporary until libipadm supports IPMP interfaces. */ + if (ifconfig_use_libipadm(s, name)) { + istatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, name, + &ipaddr); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "setifaddr"); + + if (strchr(addr, '/') == NULL) { + /* + * lifr.lifr_addr, which is updated by set_mask_lifreq() + * will contain the right mask to use. + */ + prefixlen = mask2plen(&lifr.lifr_addr); + (void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d", + addr, prefixlen); + addr = cidraddr; + } + istatus = ipadm_set_addr(ipaddr, addr, af); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "could not set address"); + /* + * let parser know we got a source. + * Next address, if given, should be dest + */ + setaddr++; + + /* + * address will be set by the parser after nextarg has + * been scanned + */ + return (0); + } + /* Tell parser that an address was set */ setaddr++; /* save copy of netmask to restore in case of error */ - (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); - if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr) < 0) + (void) strncpy(lifr1.lifr_name, name, sizeof (lifr1.lifr_name)); + if (ioctl(s, SIOCGLIFNETMASK, (caddr_t)&lifr1) < 0) Perror0_exit("SIOCGLIFNETMASK"); - sav_netmask = lifr.lifr_addr; + sav_netmask = lifr1.lifr_addr; /* * If setting the address and not the mask, clear any existing mask @@ -966,7 +1026,8 @@ setifaddr(char *addr, int64_t param) * using the netmask command), set the mask first, so the address will * be interpreted correctly. */ - set_mask_lifreq(&lifr, &laddr, &netmask); + (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); + /* lifr.lifr_addr already contains netmask from set_mask_lifreq() */ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) Perror0_exit("SIOCSLIFNETMASK"); @@ -1567,7 +1628,7 @@ setifether(char *addr, int64_t param) if ((hwaddr = _link_aton(addr, &hwaddrlen)) == NULL) { if (hwaddrlen == -1) (void) fprintf(stderr, - "ifconfig: %s: bad address\n", hwaddr); + "ifconfig: bad ethernet address\n"); else (void) fprintf(stderr, "ifconfig: malloc() failed\n"); exit(1); @@ -1631,7 +1692,7 @@ setifether(char *addr, int64_t param) * Print an interface's Ethernet address, if it has one. */ static void -print_ifether(char *ifname) +print_ifether(const char *ifname) { int fd; @@ -1739,6 +1800,8 @@ addif(char *str, int64_t param) int prefixlen = 0; struct sockaddr_storage laddr; struct sockaddr_storage mask; + ipadm_status_t istatus; + char cidraddr[BUFSIZ]; (void) strncpy(name, origname, sizeof (name)); @@ -1818,11 +1881,54 @@ addif(char *str, int64_t param) * necessary operation. */ set_mask_lifreq(&lifr, &laddr, &mask); + + /* This check is temporary until libipadm supports IPMP interfaces. */ + if (ifconfig_use_libipadm(s, name)) { + /* + * We added the logical interface above before calling + * ipadm_create_addr(), because, with IPH_LEGACY, we need + * to do an addif for `ifconfig ce0 addif <addr>' but not for + * `ifconfig ce0 <addr>'. libipadm does not have a flag to + * to differentiate between these two cases. To keep it simple, + * we always create the logical interface and pass it to + * libipadm instead of requiring libipadm to addif for some + * cases and not do addif for other cases. + */ + istatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, name, + &ipaddr); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "addif"); + + if (strchr(str, '/') == NULL) { + /* + * lifr.lifr_addr, which is updated by set_mask_lifreq() + * will contain the right mask to use. + */ + prefixlen = mask2plen(&lifr.lifr_addr); + (void) snprintf(cidraddr, sizeof (cidraddr), "%s/%d", + str, prefixlen); + str = cidraddr; + } + istatus = ipadm_set_addr(ipaddr, str, af); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "could not set address"); + setaddr++; + /* + * address will be set by the parser after nextarg + * has been scanned + */ + return (0); + } + /* * Only set the netmask if "netmask" command is used or a prefix is * provided. */ if (g_netmask_set == G_NETMASK_SET || prefixlen >= 0) { + /* + * lifr.lifr_addr already contains netmask from + * set_mask_lifreq(). + */ if (ioctl(s, SIOCSLIFNETMASK, (caddr_t)&lifr) < 0) Perror0_exit("addif: SIOCSLIFNETMASK"); } @@ -1850,6 +1956,8 @@ static int removeif(char *str, int64_t param) { struct sockaddr_storage laddr; + ipadm_status_t istatus; + ipadm_addr_info_t *ainfo, *ainfop; if (strchr(name, ':') != NULL) { (void) fprintf(stderr, @@ -1859,8 +1967,43 @@ removeif(char *str, int64_t param) } (*afp->af_getaddr)(str, &laddr, NULL); - lifr.lifr_addr = laddr; + /* + * Following check is temporary until libipadm supports + * IPMP interfaces. + */ + if (!ifconfig_use_libipadm(s, name)) + goto delete; + + /* + * Get all addresses and search this address among the active + * addresses. If an address object was found, delete using + * ipadm_delete_addr(). + */ + istatus = ipadm_addr_info(iph, name, &ainfo, 0, LIFC_DEFAULT); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "removeif"); + + for (ainfop = ainfo; ainfop != NULL; ainfop = IA_NEXT(ainfop)) + if (sockaddrcmp(ainfop->ia_ifa.ifa_addr, &laddr)) + break; + + if (ainfop != NULL && ainfop->ia_aobjname[0] != '\0') { + istatus = ipadm_delete_addr(iph, ainfop->ia_aobjname, + IPADM_OPT_ACTIVE); + if (istatus != IPADM_SUCCESS) + ipadmerr_exit(istatus, "could not delete address"); + ipadm_free_addr_info(ainfo); + return (0); + } + ipadm_free_addr_info(ainfo); + +delete: + /* + * An address object for this address was not found in ipadm. + * Delete with SIOCLIFREMOVEIF. + */ + lifr.lifr_addr = laddr; (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) { if (errno == EBUSY) { @@ -2202,37 +2345,6 @@ modremove(char *arg, int64_t param) } /* - * Open a stream on /dev/udp{,6}, 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. - */ -static int -open_arp_on_udp(char *udp_dev_name) -{ - int fd; - - if ((fd = open(udp_dev_name, O_RDWR)) == -1) { - Perror2("open", udp_dev_name); - return (-1); - } - errno = 0; - while (ioctl(fd, I_POP, 0) != -1) - ; - if (errno != EINVAL) { - Perror2("pop", udp_dev_name); - } else if (ioctl(fd, I_PUSH, ARP_MOD_NAME) == -1) { - Perror2("arp PUSH", udp_dev_name); - } else { - return (fd); - } - (void) close(fd); - return (-1); -} - -/* * Helper function for mod*() functions. It gets a fd to the lower IP * stream and I_PUNLINK's the lower stream. It also initializes the * global variable lifr. @@ -2286,7 +2398,7 @@ ip_domux2fd(int *muxfd, int *muxid_fd, int *ipfd_lowstr, int *arpfd_lowstr, /* * Use /dev/udp{,6} as the mux to avoid linkcycles. */ - if ((*muxfd = open_arp_on_udp(udp_dev_name)) == -1) + if (ipadm_open_arp_on_udp(udp_dev_name, muxfd) != IPADM_SUCCESS) return (-1); if (lifr.lifr_arp_muxid != 0) { @@ -2636,11 +2748,9 @@ setifsrc(char *arg, int64_t param) * that any previous selection is cleared. */ - rval = strcmp(arg, name); - if (rval == 0) { + if (strchr(arg, ':') != NULL) { (void) fprintf(stderr, - "ifconfig: Cannot specify same interface for usesrc" - " group\n"); + "ifconfig: Cannot specify logical interface for usesrc \n"); exit(1); } @@ -2805,7 +2915,6 @@ ifstatus(const char *ifname) (void) putchar('\n'); } - /* * Print the status of the interface. If an address family was * specified, show it and it only; otherwise, show them all. @@ -2850,13 +2959,8 @@ status(void) (*p->af_status)(1, flags); } else { for (p = afs; p->af_name; p++) { - (void) close(s); - s = socket(SOCKET_AF(p->af_af), SOCK_DGRAM, 0); /* set global af for use in p->af_status */ af = p->af_af; - if (s == -1) { - Perror0_exit("socket"); - } (*p->af_status)(0, flags); } @@ -3471,277 +3575,6 @@ in6_configinfo(int force, uint64_t flags) } /* - * We need to plink both the arp-device stream and the arp-ip-device stream. - * However the muxid is stored only in IP. Plumbing 2 streams individually - * is not atomic, and if ifconfig is killed, the resulting plumbing can - * be inconsistent. For eg. if only the arp stream is plumbed, we have lost - * the muxid, and the half-baked plumbing can neither be unplumbed nor - * replumbed, thus requiring a reboot. To avoid the above the following - * scheme is used. - * - * Ifconfig asks IP to enforce atomicity of plumbing the arp and IP streams. - * This is done by pushing arp on to the mux (/dev/udp). ARP adds some - * extra information in the I_PLINK and I_PUNLINK ioctls to let IP know - * that the plumbing/unplumbing has to be done atomically. Ifconfig plumbs - * the IP stream first, and unplumbs it last. The kernel (IP) does not - * allow IP stream to be unplumbed without unplumbing arp stream. Similarly - * it does not allow arp stream to be plumbed before IP stream is plumbed. - * There is no need to use SIOCSLIFMUXID, since the whole operation is atomic, - * and IP uses the info in the I_PLINK message to get the muxid. - * - * a. STREAMS does not allow us to use /dev/ip itself as the mux. So we use - * /dev/udp{,6}. - * b. SIOCGLIFMUXID returns the muxid corresponding to the V4 or V6 stream - * depending on the open i.e. V4 vs V6 open. So we need to use /dev/udp - * or /dev/udp6 for SIOCGLIFMUXID and SIOCSLIFMUXID. - * c. We need to push ARP in order to get the required kernel support for - * atomic plumbings. The actual work done by ARP is explained in arp.c - * Without pushing ARP, we will still be able to plumb/unplumb. But - * it is not atomic, and is supported by the kernel for backward - * compatibility for other utilities like atmifconfig etc. In this case - * the utility must use SIOCSLIFMUXID. - */ -static int -ifplumb(const char *linkname, const char *ifname, boolean_t genppa, int af) -{ - int arp_muxid = -1, ip_muxid; - int mux_fd, ip_fd, arp_fd; - int retval; - char *udp_dev_name; - uint64_t flags; - uint_t dlpi_flags; - dlpi_handle_t dh_arp, dh_ip; - - /* - * Always dlpi_open() with DLPI_NOATTACH because the IP and ARP module - * will do the attach themselves for DLPI style-2 links. - */ - dlpi_flags = DLPI_NOATTACH; - - /* - * If `linkname' is the special token IPMPSTUB, then this is a request - * to create an IPMP interface atop /dev/ipmpstub0. (We can't simply - * pass "ipmpstub0" as `linkname' since an admin *could* have a normal - * vanity-named link named "ipmpstub0" that they'd like to plumb.) - */ - if (linkname == IPMPSTUB) { - linkname = "ipmpstub0"; - dlpi_flags |= DLPI_DEVONLY; - } - - retval = dlpi_open(linkname, &dh_ip, dlpi_flags); - if (retval != DLPI_SUCCESS) - Perrdlpi_exit("cannot open link", linkname, retval); - - if (debug) { - (void) printf("ifconfig: ifplumb: link %s, ifname %s, " - "genppa %u\n", linkname, ifname, genppa); - } - - ip_fd = dlpi_fd(dh_ip); - if (ioctl(ip_fd, I_PUSH, IP_MOD_NAME) == -1) - Perror2_exit("I_PUSH", IP_MOD_NAME); - - /* - * Prepare to set IFF_IPV4/IFF_IPV6 flags as part of SIOCSLIFNAME. - * (At this point in time the kernel also allows an override of the - * IFF_CANTCHANGE flags.) - */ - lifr.lifr_name[0] = '\0'; - if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1) - Perror0_exit("ifplumb: SIOCGLIFFLAGS"); - - if (af == AF_INET6) { - flags = lifr.lifr_flags | IFF_IPV6; - flags &= ~(IFF_BROADCAST | IFF_IPV4); - } else { - flags = lifr.lifr_flags | IFF_IPV4; - flags &= ~IFF_IPV6; - } - - /* - * Set the interface name. If we've been asked to generate the PPA, - * then find the lowest available PPA (only currently used for IPMP - * interfaces). Otherwise, use the interface name as-is. - */ - if (genppa) { - int ppa; - - /* - * 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(s4, SIOCGLIFFLAGS, &lifr) != -1 || - errno != ENXIO) - continue; - - if (ioctl(s6, SIOCGLIFFLAGS, &lifr) != -1 || - errno != ENXIO) - continue; - - lifr.lifr_ppa = ppa; - lifr.lifr_flags = flags; - retval = ioctl(ip_fd, SIOCSLIFNAME, &lifr); - if (retval != -1 || errno != EEXIST) - break; - } - } else { - ifspec_t ifsp; - - /* - * The interface name could have come from the command-line; - * check it. - */ - if (!ifparse_ifspec(ifname, &ifsp) || ifsp.ifsp_lunvalid) - Perror2_exit("invalid IP interface name", ifname); - - /* - * 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 (create_ipmp_peer(af, ifname) == -1) { - (void) fprintf(stderr, "ifconfig: warning: cannot " - "create %s IPMP group; %s will be removed from " - "group\n", af == AF_INET ? "IPv4" : "IPv6", ifname); - } - - lifr.lifr_ppa = ifsp.ifsp_ppa; - lifr.lifr_flags = flags; - (void) strlcpy(lifr.lifr_name, ifname, LIFNAMSIZ); - retval = ioctl(ip_fd, SIOCSLIFNAME, &lifr); - } - - if (retval == -1) { - if (errno != EEXIST) - Perror0_exit("SIOCSLIFNAME for ip"); - /* - * This difference between the way we behave for EEXIST - * and that with other errors exists to preserve legacy - * behaviour. Earlier when foreachinterface() and matchif() - * were doing the duplicate interface name checks, for - * already existing interfaces, inetplumb() returned "0". - * To preserve this behaviour, Perror0() and return are - * called for EEXIST. - */ - Perror0("SIOCSLIFNAME for ip"); - return (-1); - } - - /* Get the full set of existing flags for this stream */ - if (ioctl(ip_fd, SIOCGLIFFLAGS, (char *)&lifr) == -1) - Perror0_exit("ifplumb: SIOCGLIFFLAGS"); - - if (debug) { - (void) printf("ifconfig: ifplumb: %s got flags:\n", - lifr.lifr_name); - print_flags(lifr.lifr_flags); - (void) putchar('\n'); - } - - /* - * Open "/dev/udp" 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. - */ - if (af == AF_INET6) - udp_dev_name = UDP6_DEV_NAME; - else - udp_dev_name = UDP_DEV_NAME; - if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1) - exit(EXIT_FAILURE); - - /* Check if arp is not needed */ - if (lifr.lifr_flags & (IFF_NOARP|IFF_IPV6)) { - /* - * PLINK the interface stream so that ifconfig can exit - * without tearing down the stream. - */ - if ((ip_muxid = ioctl(mux_fd, I_PLINK, ip_fd)) == -1) - Perror0_exit("I_PLINK for ip"); - (void) close(mux_fd); - return (lifr.lifr_ppa); - } - - /* - * This interface does use ARP, so set up a separate stream - * from the interface to ARP. - */ - if (debug) - (void) printf("ifconfig: ifplumb: interface %s", ifname); - - retval = dlpi_open(linkname, &dh_arp, dlpi_flags); - if (retval != DLPI_SUCCESS) - Perrdlpi_exit("cannot open link", linkname, retval); - - arp_fd = dlpi_fd(dh_arp); - if (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1) - Perror2_exit("I_PUSH", ARP_MOD_NAME); - - /* - * Tell ARP the name and unit number for this interface. - * Note that arp has no support for transparent ioctls. - */ - if (strioctl(arp_fd, SIOCSLIFNAME, &lifr, sizeof (lifr)) == -1) { - if (errno != EEXIST) - Perror0_exit("SIOCSLIFNAME for arp"); - Perror0("SIOCSLIFNAME for arp"); - goto out; - } - - /* - * 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) - Perror0_exit("I_PLINK for ip"); - if ((arp_muxid = ioctl(mux_fd, I_PLINK, arp_fd)) == -1) { - (void) ioctl(mux_fd, I_PUNLINK, ip_muxid); - Perror0_exit("I_PLINK for arp"); - } - - if (debug) - (void) printf("arp muxid = %d\n", arp_muxid); -out: - dlpi_close(dh_ip); - dlpi_close(dh_arp); - (void) close(mux_fd); - return (lifr.lifr_ppa); -} - -/* * If this is a physical interface then remove it. * If it is a logical interface name use SIOCLIFREMOVEIF to * remove it. In both cases fail if it doesn't exist. @@ -3750,245 +3583,36 @@ out: static int inetunplumb(char *arg, int64_t param) { - int ip_muxid, arp_muxid; - int mux_fd; - int muxid_fd; - char *udp_dev_name; - char *strptr; - uint64_t flags; - boolean_t changed_arp_muxid = _B_FALSE; - int save_errno; - boolean_t v6 = (afp->af_af == AF_INET6); - - strptr = strchr(name, ':'); - if (strptr != NULL || strcmp(name, LOOPBACK_IF) == 0) { - /* Can't unplumb logical interface zero */ - if (strptr != NULL && strcmp(strptr, ":0") == 0) { - (void) fprintf(stderr, "ifconfig: unplumb:" - " Cannot unplumb %s: Invalid interface\n", name); - exit(1); - } - (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); - (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr)); - - if (ioctl(s, SIOCLIFREMOVEIF, (caddr_t)&lifr) < 0) - Perror0_exit("unplumb: SIOCLIFREMOVEIF"); - return (0); - } - - /* - * 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; - else - udp_dev_name = UDP_DEV_NAME; - - if ((muxid_fd = open(udp_dev_name, O_RDWR)) == -1) - exit(EXIT_FAILURE); - - if ((mux_fd = open_arp_on_udp(udp_dev_name)) == -1) - exit(EXIT_FAILURE); - - (void) strncpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); - if (ioctl(muxid_fd, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) { - Perror0_exit("unplumb: SIOCGLIFFLAGS"); - } - flags = lifr.lifr_flags; -again: - if (flags & IFF_IPMP) { - lifgroupinfo_t lifgr; - ifaddrlistx_t *ifaddrs, *ifaddrp; - - /* - * 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(s, SIOCGLIFGROUPNAME, &lifr) == -1) - Perror0_exit("unplumb: SIOCGLIFGROUPNAME"); - - (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, - LIFGRNAMSIZ); - if (ioctl(s, SIOCGLIFGROUPINFO, &lifgr) == -1) - Perror0_exit("unplumb: SIOCGLIFGROUPINFO"); - - if ((v6 && lifgr.gi_nv6 != 0) || (!v6 && lifgr.gi_nv4 != 0)) { - (void) fprintf(stderr, "ifconfig: %s: cannot unplumb:" - " IPMP group is not empty\n", name); - exit(1); - } - - /* - * The kernel will fail the I_PUNLINK if the IPMP interface - * has administratively up addresses; bring 'em down. - */ - if (ifaddrlistx(name, IFF_UP|IFF_DUPLICATE, 0, &ifaddrs) == -1) - Perror2_exit(name, "cannot get address list"); - - ifaddrp = ifaddrs; - for (; ifaddrp != NULL; ifaddrp = ifaddrp->ia_next) { - if (((ifaddrp->ia_flags & IFF_IPV6) && !v6) || - (!(ifaddrp->ia_flags & IFF_IPV6) && v6)) - continue; - - if (!ifaddr_down(ifaddrp)) { - Perror2_exit(ifaddrp->ia_name, - "cannot bring down"); - } - } - ifaddrlistx_free(ifaddrs); - } + ipadm_status_t istatus; - if (ioctl(muxid_fd, SIOCGLIFMUXID, (caddr_t)&lifr) < 0) { - Perror0_exit("unplumb: SIOCGLIFMUXID"); - } - 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 (debug) - (void) printf("arp_muxid %d\n", arp_muxid); - 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; - } else { - Perror0("I_PUNLINK for arp"); - } - } + istatus = ipadm_delete_if(iph, name, afp->af_af, IPADM_OPT_ACTIVE); + if (istatus != IPADM_SUCCESS) { + (void) fprintf(stderr, "ifconfig: cannot unplumb %s: %s\n", + name, ipadm_status2str(istatus)); + exit(1); } - if (debug) - (void) printf("ip_muxid %d\n", ip_muxid); - 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; - - Perror0_exit("I_PUNLINK for ip"); - } - (void) close(mux_fd); - (void) close(muxid_fd); return (0); } /* - * If this is a physical interface then create it unless it is already - * present. If it is a logical interface name use SIOCLIFADDIF to - * create and (and fail it if already exists.) - * As a special case send SIOCLIFADDIF for the loopback interface. This - * is needed since there is no other notion of plumbing the loopback - * interface. + * Create the interface in `name', using ipadm_create_if(). If `name' is a + * logical interface or loopback interface, ipadm_create_if() uses + * SIOCLIFADDIF to create it. */ /* ARGSUSED */ static int inetplumb(char *arg, int64_t param) { - char *strptr; - boolean_t islo; - zoneid_t zoneid; - datalink_id_t linkid; + ipadm_status_t istatus; - strptr = strchr(name, ':'); - islo = (strcmp(name, LOOPBACK_IF) == 0); - - if (strptr != NULL || islo) { - (void) memset(&lifr, 0, sizeof (lifr)); - (void) strlcpy(lifr.lifr_name, name, sizeof (lifr.lifr_name)); - if (islo && ioctl(s, SIOCGLIFADDR, (caddr_t)&lifr) >= 0) { - if (debug) { - (void) fprintf(stderr, - "ifconfig: %s already exists\n", name); - } - return (0); - } - if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) { - if (errno == EEXIST) { - if (debug) { - (void) fprintf(stderr, - "ifconfig: %s already exists\n", - name); - } - } else { - Perror2_exit("plumb: SIOCLIFADDIF", name); - } - } - return (0); - } - - /* - * 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. - */ - zoneid = getzoneid(); - if (zoneid == GLOBAL_ZONEID && - ifconfig_dladm_open(name, DATALINK_CLASS_ALL, &linkid) == - DLADM_STATUS_OK) { - int ret; - - zoneid = ALL_ZONES; - ret = zone_check_datalink(&zoneid, linkid); - if (ret == 0) { - char zonename[ZONENAME_MAX]; - - (void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX); - (void) fprintf(stderr, "%s is used by non-global" - "zone: %s\n", name, zonename); - return (1); - } + istatus = ipadm_create_if(iph, name, afp->af_af, IPADM_OPT_ACTIVE); + if (istatus != IPADM_SUCCESS) { + (void) fprintf(stderr, "ifconfig: cannot plumb %s: %s\n", + name, ipadm_status2str(istatus)); + if (istatus != IPADM_IF_EXISTS) + exit(1); } - - if (debug) - (void) printf("inetplumb: %s af %d\n", name, afp->af_af); - - (void) ifplumb(name, name, _B_FALSE, afp->af_af); return (0); } @@ -4026,29 +3650,30 @@ inetipmp(char *arg, int64_t param) static int create_ipmp(const char *grname, int af, const char *ifname, boolean_t implicit) { - int ppa; static int ipmp_daemon_started; + uint32_t flags = IPADM_OPT_IPMP|IPADM_OPT_ACTIVE; + ipadm_status_t istatus; if (debug) { (void) printf("create_ipmp: ifname %s grname %s af %d\n", ifname != NULL ? ifname : "NULL", grname, af); } - if (ifname != NULL) - ppa = ifplumb(IPMPSTUB, ifname, _B_FALSE, af); - else - ppa = ifplumb(IPMPSTUB, "ipmp", _B_TRUE, af); - - if (ppa == -1) { - Perror2(grname, "cannot create IPMP interface"); + /* + * ipadm_create_if() creates the IPMP interface and fills in the + * ppa in lifr.lifr_name, if `ifname'="ipmp". + */ + (void) strlcpy(lifr.lifr_name, (ifname ? ifname : "ipmp"), + sizeof (lifr.lifr_name)); + if (ifname == NULL) + flags |= IPADM_OPT_GENPPA; + istatus = ipadm_create_if(iph, lifr.lifr_name, af, flags); + if (istatus != IPADM_SUCCESS) { + (void) fprintf(stderr, "ifconfig: cannot create IPMP interface " + "%s: %s\n", grname, ipadm_status2str(istatus)); return (-1); } - if (ifname != NULL) - (void) strlcpy(lifr.lifr_name, ifname, LIFNAMSIZ); - else - (void) snprintf(lifr.lifr_name, LIFNAMSIZ, "ipmp%d", ppa); - /* * To preserve backward-compatibility, always bring up the link-local * address for implicitly-created IPv6 IPMP interfaces. @@ -4082,42 +3707,6 @@ create_ipmp(const char *grname, int af, const char *ifname, boolean_t implicit) } /* - * Check 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 int -create_ipmp_peer(int af, const char *ifname) -{ - int fd; - lifgroupinfo_t lifgr; - - assert(af == AF_INET || af == AF_INET6); - - /* - * Get the socket for the "other" address family. - */ - fd = (af == AF_INET) ? s6 : s4; - - (void) strlcpy(lifr.lifr_name, ifname, LIFNAMSIZ); - if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) != 0) - return (0); - - (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname, LIFGRNAMSIZ); - if (ioctl(fd, SIOCGLIFGROUPINFO, &lifgr) != 0) - return (0); - - /* - * 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 (0); - - return (create_ipmp(lifgr.gi_grname, af, lifgr.gi_grifname, _B_TRUE)); -} - -/* * Start in.mpathd if it's not already running. */ static void @@ -4244,7 +3833,35 @@ ifconfig_dladm_open(const char *name, datalink_class_t reqclass, return (status); } -void +/* + * This function checks if we can use libipadm API's. We will only + * call libipadm functions for non-IPMP interfaces. This check is + * temporary until libipadm supports IPMP interfaces. + */ +static boolean_t +ifconfig_use_libipadm(int s, const char *lifname) +{ + struct lifreq lifr1; + + (void) strlcpy(lifr1.lifr_name, lifname, sizeof (lifr1.lifr_name)); + if (ioctl(s, SIOCGLIFGROUPNAME, (caddr_t)&lifr1) < 0) { + (void) strncpy(lifr.lifr_name, lifname, + sizeof (lifr.lifr_name)); + Perror0_exit("error"); + } + + return (lifr1.lifr_groupname[0] == '\0'); +} + +static void +ipadmerr_exit(ipadm_status_t status, const char *str) +{ + (void) fprintf(stderr, "ifconfig: %s: %s\n", str, + ipadm_status2str(status)); + exit(1); +} + +static void dladmerr_exit(dladm_status_t status, const char *str) { char errstr[DLADM_STRSIZE]; @@ -4589,19 +4206,6 @@ lifnum(const char *ifname) return (atoi(cp + 1)); } -static int -strioctl(int s, int cmd, void *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)); -} - static void add_ni(const char *name) { diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h index f11f4d0a94..85f9ec0b17 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.h @@ -1,5 +1,5 @@ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -23,9 +23,6 @@ extern "C" { #define BAD_ADDR -1 /* prefix is invalid */ #define NO_PREFIX -2 /* no prefix was found */ -/* No suitable header file defines this, though it's in libsocket */ -extern int getnetmaskbyaddr(struct in_addr, struct in_addr *); - extern int debug; extern void Perror0(const char *); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile new file mode 100644 index 0000000000..94b41cfaaa --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/Makefile @@ -0,0 +1,94 @@ +# +# 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. +# +# + +PROG = ipadm +ROOTFS_PROG = $(PROG) +LOCALOBJS= ipadm.o +COMMONOBJS= +OBJS= $(LOCALOBJS) $(COMMONOBJS) + +include ../../../Makefile.cmd +include ../../Makefile.cmd-inet + +XGETFLAGS += -a -x $(PROG).xcl +LOCALSRCS= $(LOCALOBJS:%.o=%.c) +COMMONSRCS= $(CMDINETCOMMONDIR)/$(COMMONOBJS:%.o=%.c) +SRCS= $(LOCALSRCS) $(COMMONSRCS) + +CPPFLAGS += -I$(CMDINETCOMMONDIR) +LDLIBS += -linetutil -lipadm -lnvpair +LINTFLAGS += -m + +ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%) + +# ipadm uses the ancillary data feature which is available only through +# UNIX 98 standards version of Socket interface. This interface is supposed to +# be accessed by -lxnet. In addition -lsocket is used to capture new +# not-yet-standard interfaces. Someday -lxnet alone should be enough when IPv6 +# inspired new interfaces are part of standards. +LDLIBS += -lxnet -lsocket + +# these #defines are required to use UNIX 98 interfaces +_D_UNIX98_EXTN= -D_XOPEN_SOURCE=500 -D__EXTENSIONS__ + +$(OBJS) := CPPFLAGS += $(_D_UNIX98_EXTN) + +LINTFLAGS += $(_D_UNIX98_EXTN) + +$(ROOTCFGDIR)/ipadm.conf := FILEMODE= 644 + +# +# Instrument ipadm with CTF data to ease debugging. +# +CTFCONVERT_HOOK = && $(CTFCONVERT_O) +CTFMERGE_HOOK = && $(CTFMERGE) -L VERSION -o $@ $(OBJS) +$(OBJS) := CFLAGS += $(CTF_FLAGS) + +.KEEP_STATE: + +all: $(ROOTFS_PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(CTFMERGE_HOOK) + $(POST_PROCESS) + +install: all $(ROOTSBINPROG) $(ROOTCFGDIR) $(ROOTCFGFILES) $(ROOTUSRSBINLINKS) + +$(ROOTUSRSBINLINKS): + -$(RM) $@; $(SYMLINK) ../../sbin/$(@F) $@ + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +$(ROOTCFGDIR): + $(INS.dir) + +$(ROOTCFGDIR)/%: $(ROOTCFGDIR) % + $(INS.file) + +include ../../../Makefile.targ diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c new file mode 100644 index 0000000000..75833cf5b5 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c @@ -0,0 +1,2198 @@ +/* + * 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 <arpa/inet.h> +#include <errno.h> +#include <getopt.h> +#include <inet/ip.h> +#include <inet/iptun.h> +#include <inet/tunables.h> +#include <libdladm.h> +#include <libdliptun.h> +#include <libdllink.h> +#include <libinetutil.h> +#include <libipadm.h> +#include <locale.h> +#include <netdb.h> +#include <netinet/in.h> +#include <ofmt.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <zone.h> + +#define STR_UNKNOWN_VAL "?" +#define LIFC_DEFAULT (LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\ + LIFC_UNDER_IPMP) + +typedef void cmdfunc_t(int, char **, const char *); +static cmdfunc_t do_create_if, do_delete_if, do_enable_if, do_disable_if; +static cmdfunc_t do_show_if; +static cmdfunc_t do_set_prop, do_show_prop, do_init_prop, do_set_ifprop; +static cmdfunc_t do_show_ifprop, do_reset_ifprop, do_reset_prop; +static cmdfunc_t do_show_addrprop, do_set_addrprop, do_reset_addrprop; +static cmdfunc_t do_create_addr, do_delete_addr, do_show_addr; +static cmdfunc_t do_enable_addr, do_disable_addr; +static cmdfunc_t do_up_addr, do_down_addr, do_refresh_addr; + +typedef struct cmd { + char *c_name; + cmdfunc_t *c_fn; + const char *c_usage; +} cmd_t; + +static cmd_t cmds[] = { + /* interface management related sub-commands */ + { "create-if", do_create_if, "\tcreate-if\t[-t] <interface>" }, + { "disable-if", do_disable_if, "\tdisable-if\t-t <interface>" }, + { "enable-if", do_enable_if, "\tenable-if\t-t <interface>" }, + { "delete-if", do_delete_if, "\tdelete-if\t<interface>" }, + { "show-if", do_show_if, + "\tshow-if\t\t[[-p] -o <field>,...] [<interface>]\n" }, + { "set-ifprop", do_set_ifprop, + "\tset-ifprop\t[-t] -p <prop>=<value[,...]> -m <protocol> " + "<interface>" }, + { "reset-ifprop", do_reset_ifprop, + "\treset-ifprop\t[-t] -p <prop> -m <protocol> <interface>" }, + { "show-ifprop", do_show_ifprop, + "\tshow-ifprop\t[[-c] -o <field>,...] [-p <prop>,...]\n" + "\t\t\t[-m <protocol>] [interface]\n" }, + + /* address management related sub-commands */ + { "create-addr", do_create_addr, + "\tcreate-addr\t[-t] {-T static <static_args> |" + " -T dhcp <dhcp_args> |\n" + "\t\t\t-T addrconf <addrconf_args>} <addrobj>\n" + "\t\t\tstatic_args = <[-d] -a {local|remote}=addr[/prefixlen]>\n" + "\t\t\tdhcp_args = <[-w <seconds> | forever]>\n" + "\t\t\taddrconf_args = <[-i interface-id]\n" + "\t\t\t\t\t[-p {stateful|stateless}={yes|no}]>" }, + { "down-addr", do_down_addr, "\tdown-addr\t[-t] <addrobj>" }, + { "up-addr", do_up_addr, "\tup-addr\t\t[-t] <addrobj>" }, + { "disable-addr", do_disable_addr, "\tdisable-addr\t-t <addrobj>" }, + { "enable-addr", do_enable_addr, "\tenable-addr\t-t <addrobj>" }, + { "refresh-addr", do_refresh_addr, "\trefresh-addr\t[-i] <addrobj>" }, + { "delete-addr", do_delete_addr, "\tdelete-addr\t[-r] <addrobj>" }, + { "show-addr", do_show_addr, + "\tshow-addr\t[[-p] -o <field>,...] [<addrobj>]\n" }, + { "set-addrprop", do_set_addrprop, + "\tset-addrprop\t[-t] -p <prop>=<value[,...]> <addrobj>" }, + { "reset-addrprop", do_reset_addrprop, + "\treset-addrprop\t[-t] -p <prop> <addrobj>" }, + { "show-addrprop", do_show_addrprop, + "\tshow-addrprop\t[[-c] -o <field>,...] [-p <prop>,...] " + "<addrobj>\n" }, + + /* protocol properties related sub-commands */ + { "set-prop", do_set_prop, + "\tset-prop\t[-t] -p <prop>[+|-]=<value[,...]> <protocol>" }, + { "reset-prop", do_reset_prop, + "\treset-prop\t[-t] -p <prop> <protocol>" }, + { "show-prop", do_show_prop, + "\tshow-prop\t[[-c] -o <field>,...] [-p <prop>,...]" + " [protocol]" }, + + /* private sub-commands */ + { "init-prop", do_init_prop, "\tinit-prop\n" } +}; + +static const struct option if_longopts[] = { + {"temporary", no_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static const struct option show_prop_longopts[] = { + {"parsable", no_argument, 0, 'c' }, + {"prop", required_argument, 0, 'p' }, + {"output", required_argument, 0, 'o' }, + { 0, 0, 0, 0 } +}; + +static const struct option show_ifprop_longopts[] = { + {"module", required_argument, 0, 'm' }, + {"parsable", no_argument, 0, 'c' }, + {"prop", required_argument, 0, 'p' }, + {"output", required_argument, 0, 'o' }, + { 0, 0, 0, 0 } +}; + +static const struct option set_prop_longopts[] = { + {"prop", required_argument, 0, 'p' }, + {"temporary", no_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static const struct option set_ifprop_longopts[] = { + {"module", required_argument, 0, 'm' }, + {"prop", required_argument, 0, 'p' }, + {"temporary", no_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static const struct option addr_misc_longopts[] = { + {"inform", no_argument, 0, 'i' }, + {"release", no_argument, 0, 'r' }, + {"temporary", no_argument, 0, 't' }, + { 0, 0, 0, 0 } +}; + +static const struct option addr_longopts[] = { + {"address", required_argument, 0, 'a' }, + {"down", no_argument, 0, 'd' }, + {"interface-id", required_argument, 0, 'i' }, + {"prop", required_argument, 0, 'p' }, + {"temporary", no_argument, 0, 't' }, + {"type", required_argument, 0, 'T' }, + {"wait", required_argument, 0, 'w' }, + { 0, 0, 0, 0 } +}; + +static const struct option show_addr_longopts[] = { + {"parsable", no_argument, 0, 'p' }, + {"output", required_argument, 0, 'o' }, + { 0, 0, 0, 0 } +}; + +static const struct option show_if_longopts[] = { + {"parsable", no_argument, 0, 'p' }, + {"output", required_argument, 0, 'o' }, + { 0, 0, 0, 0 } +}; + +/* callback functions to print show-* subcommands output */ +static ofmt_cb_t print_prop_cb; +static ofmt_cb_t print_sa_cb; +static ofmt_cb_t print_si_cb; + +/* structures for 'ipadm show-*' subcommands */ +typedef enum { + IPADM_PROPFIELD_IFNAME, + IPADM_PROPFIELD_PROTO, + IPADM_PROPFIELD_ADDROBJ, + IPADM_PROPFIELD_PROPERTY, + IPADM_PROPFIELD_PERM, + IPADM_PROPFIELD_CURRENT, + IPADM_PROPFIELD_PERSISTENT, + IPADM_PROPFIELD_DEFAULT, + IPADM_PROPFIELD_POSSIBLE +} ipadm_propfield_index_t; + +static ofmt_field_t intfprop_fields[] = { +/* name, field width, index, callback */ +{ "IFNAME", 12, IPADM_PROPFIELD_IFNAME, print_prop_cb}, +{ "PROPERTY", 16, IPADM_PROPFIELD_PROPERTY, print_prop_cb}, +{ "PROTO", 6, IPADM_PROPFIELD_PROTO, print_prop_cb}, +{ "PERM", 5, IPADM_PROPFIELD_PERM, print_prop_cb}, +{ "CURRENT", 11, IPADM_PROPFIELD_CURRENT, print_prop_cb}, +{ "PERSISTENT", 11, IPADM_PROPFIELD_PERSISTENT, print_prop_cb}, +{ "DEFAULT", 11, IPADM_PROPFIELD_DEFAULT, print_prop_cb}, +{ "POSSIBLE", 16, IPADM_PROPFIELD_POSSIBLE, print_prop_cb}, +{ NULL, 0, 0, NULL} +}; + + +static ofmt_field_t modprop_fields[] = { +/* name, field width, index, callback */ +{ "PROTO", 6, IPADM_PROPFIELD_PROTO, print_prop_cb}, +{ "PROPERTY", 22, IPADM_PROPFIELD_PROPERTY, print_prop_cb}, +{ "PERM", 5, IPADM_PROPFIELD_PERM, print_prop_cb}, +{ "CURRENT", 13, IPADM_PROPFIELD_CURRENT, print_prop_cb}, +{ "PERSISTENT", 13, IPADM_PROPFIELD_PERSISTENT, print_prop_cb}, +{ "DEFAULT", 13, IPADM_PROPFIELD_DEFAULT, print_prop_cb}, +{ "POSSIBLE", 15, IPADM_PROPFIELD_POSSIBLE, print_prop_cb}, +{ NULL, 0, 0, NULL} +}; + +static ofmt_field_t addrprop_fields[] = { +/* name, field width, index, callback */ +{ "ADDROBJ", 18, IPADM_PROPFIELD_ADDROBJ, print_prop_cb}, +{ "PROPERTY", 11, IPADM_PROPFIELD_PROPERTY, print_prop_cb}, +{ "PERM", 5, IPADM_PROPFIELD_PERM, print_prop_cb}, +{ "CURRENT", 16, IPADM_PROPFIELD_CURRENT, print_prop_cb}, +{ "PERSISTENT", 16, IPADM_PROPFIELD_PERSISTENT, print_prop_cb}, +{ "DEFAULT", 16, IPADM_PROPFIELD_DEFAULT, print_prop_cb}, +{ "POSSIBLE", 15, IPADM_PROPFIELD_POSSIBLE, print_prop_cb}, +{ NULL, 0, 0, NULL} +}; + +typedef struct show_prop_state { + char sps_ifname[LIFNAMSIZ]; + char sps_aobjname[IPADM_AOBJSIZ]; + const char *sps_pname; + uint_t sps_proto; + char *sps_propval; + nvlist_t *sps_proplist; + boolean_t sps_parsable; + boolean_t sps_addrprop; + boolean_t sps_ifprop; + boolean_t sps_modprop; + ipadm_status_t sps_status; + ipadm_status_t sps_retstatus; + ofmt_handle_t sps_ofmt; +} show_prop_state_t; + +typedef struct show_addr_state { + boolean_t sa_parsable; + boolean_t sa_persist; + ofmt_handle_t sa_ofmt; +} show_addr_state_t; + +typedef struct show_if_state { + boolean_t si_parsable; + ofmt_handle_t si_ofmt; +} show_if_state_t; + +typedef struct show_addr_args_s { + show_addr_state_t *sa_state; + ipadm_addr_info_t *sa_info; +} show_addr_args_t; + +typedef struct show_if_args_s { + show_if_state_t *si_state; + ipadm_if_info_t *si_info; +} show_if_args_t; + +typedef enum { + SA_ADDROBJ, + SA_TYPE, + SA_STATE, + SA_CURRENT, + SA_PERSISTENT, + SA_ADDR +} sa_field_index_t; + +typedef enum { + SI_IFNAME, + SI_STATE, + SI_CURRENT, + SI_PERSISTENT +} si_field_index_t; + +static ofmt_field_t show_addr_fields[] = { +/* name, field width, id, callback */ +{ "ADDROBJ", 18, SA_ADDROBJ, print_sa_cb}, +{ "TYPE", 9, SA_TYPE, print_sa_cb}, +{ "STATE", 13, SA_STATE, print_sa_cb}, +{ "CURRENT", 8, SA_CURRENT, print_sa_cb}, +{ "PERSISTENT", 11, SA_PERSISTENT, print_sa_cb}, +{ "ADDR", 46, SA_ADDR, print_sa_cb}, +{ NULL, 0, 0, NULL} +}; + +static ofmt_field_t show_if_fields[] = { +/* name, field width, id, callback */ +{ "IFNAME", 11, SI_IFNAME, print_si_cb}, +{ "STATE", 9, SI_STATE, print_si_cb}, +{ "CURRENT", 12, SI_CURRENT, print_si_cb}, +{ "PERSISTENT", 11, SI_PERSISTENT, print_si_cb}, +{ NULL, 0, 0, NULL} +}; + +#define IPADM_ALL_BITS ((uint_t)-1) +typedef struct intf_mask { + char *name; + uint64_t bits; + uint64_t mask; +} fmask_t; + +/* + * Handle to libipadm. Opened in main() before the sub-command specific + * function is called and is closed before the program exits. + */ +ipadm_handle_t iph = NULL; + +/* + * Opaque ipadm address object. Used by all the address management subcommands. + */ +ipadm_addrobj_t ipaddr = NULL; + +static char *progname; + +static void die(const char *, ...); +static void die_opterr(int, int, const char *); +static void warn_ipadmerr(ipadm_status_t, const char *, ...); +static void ipadm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t); +static void ipadm_check_propstr(const char *, boolean_t, const char *); +static void process_misc_addrargs(int, char **, const char *, int *, + uint32_t *); + +static void +usage(void) +{ + int i; + cmd_t *cmdp; + + (void) fprintf(stderr, + gettext("usage: ipadm <subcommand> <args> ...\n")); + for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { + cmdp = &cmds[i]; + if (strcmp(cmdp->c_name, "init-prop") == 0) + continue; + if (cmdp->c_usage != NULL) + (void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage)); + } + + ipadm_destroy_addrobj(ipaddr); + ipadm_close(iph); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int i; + cmd_t *cmdp; + ipadm_status_t status; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + progname++; + + if (argc < 2) + usage(); + + status = ipadm_open(&iph, 0); + if (status != IPADM_SUCCESS) { + die("Could not open handle to library - %s", + ipadm_status2str(status)); + } + + for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { + cmdp = &cmds[i]; + if (strcmp(argv[1], cmdp->c_name) == 0) { + cmdp->c_fn(argc - 1, &argv[1], gettext(cmdp->c_usage)); + ipadm_destroy_addrobj(ipaddr); + ipadm_close(iph); + exit(0); + } + } + + (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), + progname, argv[1]); + usage(); + + return (0); +} + +/* + * Create an IP interface for which no saved configuration exists in the + * persistent store. + */ +static void +do_create_if(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int option; + uint32_t flags = IPADM_OPT_PERSIST|IPADM_OPT_ACTIVE; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":t", if_longopts, + NULL)) != -1) { + switch (option) { + case 't': + /* + * "ifconfig" mode - plumb interface, but do not + * restore settings that may exist in db. + */ + flags &= ~IPADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option, use); + } + } + if (optind != (argc - 1)) + die("Usage: %s", use); + status = ipadm_create_if(iph, argv[optind], AF_UNSPEC, flags); + if (status != IPADM_SUCCESS) { + die("Could not create %s : %s", + argv[optind], ipadm_status2str(status)); + } +} + +/* + * Enable an IP interface based on the persistent configuration for + * that interface. + */ +static void +do_enable_if(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int index; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + process_misc_addrargs(argc, argv, use, &index, &flags); + if (flags & IPADM_OPT_PERSIST) + die("persistent operation not supported for enable-if"); + status = ipadm_enable_if(iph, argv[index], flags); + if (status == IPADM_ALL_ADDRS_NOT_ENABLED) { + warn_ipadmerr(status, ""); + } else if (status != IPADM_SUCCESS) { + die("Could not enable %s : %s", + argv[optind], ipadm_status2str(status)); + } +} + +/* + * Remove an IP interface from both active and persistent configuration. + */ +static void +do_delete_if(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + if (argc != 2) + die("Usage: %s", use); + + status = ipadm_delete_if(iph, argv[1], AF_UNSPEC, flags); + if (status != IPADM_SUCCESS) { + die("Could not delete %s: %s", + argv[optind], ipadm_status2str(status)); + } +} + +/* + * Disable an IP interface by removing it from active configuration. + */ +static void +do_disable_if(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int index; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + process_misc_addrargs(argc, argv, use, &index, &flags); + if (flags & IPADM_OPT_PERSIST) + die("persistent operation not supported for disable-if"); + status = ipadm_disable_if(iph, argv[index], flags); + if (status != IPADM_SUCCESS) { + die("Could not disable %s: %s", + argv[optind], ipadm_status2str(status)); + } +} + +/* + * called in from print_prop_cb() and does the job of printing each + * individual column in the 'ipadm show-*prop' output. + */ +static void +print_prop(show_prop_state_t *statep, uint_t flags, char *buf, size_t bufsize) +{ + const char *prop_name = statep->sps_pname; + char *ifname = statep->sps_ifname; + char *propval = statep->sps_propval; + uint_t proto = statep->sps_proto; + size_t propsize = MAXPROPVALLEN; + char *object; + ipadm_status_t status; + + if (statep->sps_ifprop) { + status = ipadm_get_ifprop(iph, ifname, prop_name, propval, + &propsize, proto, flags); + object = ifname; + } else if (statep->sps_modprop) { + status = ipadm_get_prop(iph, prop_name, propval, &propsize, + proto, flags); + object = ipadm_proto2str(proto); + } else { + status = ipadm_get_addrprop(iph, prop_name, propval, &propsize, + statep->sps_aobjname, flags); + object = statep->sps_aobjname; + } + + if (status != IPADM_SUCCESS) { + if (status == IPADM_PROP_UNKNOWN || + status == IPADM_INVALID_ARG) { + warn_ipadmerr(status, "cannot get property '%s' for " + "'%s'", prop_name, object); + } else if (status == IPADM_NOTSUP) { + warn_ipadmerr(status, "'%s'", object); + } else if (status == IPADM_NOTFOUND) { + if (flags & IPADM_OPT_PERSIST) { + propval[0] = '\0'; + goto cont; + } else { + warn_ipadmerr(status, "no such object '%s'", + object); + } + } else if (status == IPADM_ENXIO) { + /* the interface is probably disabled */ + propval[0] = '\0'; + goto cont; + } + statep->sps_status = status; + statep->sps_retstatus = status; + return; + } +cont: + statep->sps_status = IPADM_SUCCESS; + (void) snprintf(buf, bufsize, "%s", propval); +} + +/* + * callback function which displays output for set-prop, set-ifprop and + * set-addrprop subcommands. + */ +static boolean_t +print_prop_cb(ofmt_arg_t *ofarg, char *buf, size_t bufsize) +{ + show_prop_state_t *statep = ofarg->ofmt_cbarg; + const char *propname = statep->sps_pname; + uint_t proto = statep->sps_proto; + boolean_t cont = _B_TRUE; + + /* + * Fail retrieving remaining fields, if you fail + * to retrieve a field. + */ + if (statep->sps_status != IPADM_SUCCESS) + return (_B_FALSE); + + switch (ofarg->ofmt_id) { + case IPADM_PROPFIELD_IFNAME: + (void) snprintf(buf, bufsize, "%s", statep->sps_ifname); + break; + case IPADM_PROPFIELD_PROTO: + (void) snprintf(buf, bufsize, "%s", ipadm_proto2str(proto)); + break; + case IPADM_PROPFIELD_ADDROBJ: + (void) snprintf(buf, bufsize, "%s", statep->sps_aobjname); + break; + case IPADM_PROPFIELD_PROPERTY: + (void) snprintf(buf, bufsize, "%s", propname); + break; + case IPADM_PROPFIELD_PERM: + print_prop(statep, IPADM_OPT_PERM, buf, bufsize); + break; + case IPADM_PROPFIELD_CURRENT: + print_prop(statep, IPADM_OPT_ACTIVE, buf, bufsize); + break; + case IPADM_PROPFIELD_PERSISTENT: + print_prop(statep, IPADM_OPT_PERSIST, buf, bufsize); + break; + case IPADM_PROPFIELD_DEFAULT: + print_prop(statep, IPADM_OPT_DEFAULT, buf, bufsize); + break; + case IPADM_PROPFIELD_POSSIBLE: + print_prop(statep, IPADM_OPT_POSSIBLE, buf, bufsize); + break; + } + if (statep->sps_status != IPADM_SUCCESS) + cont = _B_FALSE; + return (cont); +} + +/* + * Callback function called by the property walker (ipadm_walk_prop() or + * ipadm_walk_proptbl()), for every matched property. This function in turn + * calls ofmt_print() to print property information. + */ +boolean_t +show_property(void *arg, const char *pname, uint_t proto) +{ + show_prop_state_t *statep = arg; + + statep->sps_pname = pname; + statep->sps_proto = proto; + statep->sps_status = IPADM_SUCCESS; + ofmt_print(statep->sps_ofmt, arg); + + /* + * if an object is not found or operation is not supported then + * stop the walker. + */ + if (statep->sps_status == IPADM_NOTFOUND || + statep->sps_status == IPADM_NOTSUP) + return (_B_FALSE); + return (_B_TRUE); +} + +/* + * Properties to be displayed is in `statep->sps_proplist'. If it is NULL, + * for all the properties for the specified object, relavant information, will + * be displayed. Otherwise, for the selected property set, display relevant + * information + */ +static void +show_properties(void *arg, int prop_class) +{ + show_prop_state_t *statep = arg; + nvlist_t *nvl = statep->sps_proplist; + uint_t proto = statep->sps_proto; + nvpair_t *curr_nvp; + char *buf, *name; + ipadm_status_t status; + + /* allocate sufficient buffer to hold a property value */ + if ((buf = malloc(MAXPROPVALLEN)) == NULL) + die("insufficient memory"); + statep->sps_propval = buf; + + /* if no properties were specified, display all the properties */ + if (nvl == NULL) { + (void) ipadm_walk_proptbl(proto, prop_class, show_property, + statep); + } else { + for (curr_nvp = nvlist_next_nvpair(nvl, NULL); curr_nvp; + curr_nvp = nvlist_next_nvpair(nvl, curr_nvp)) { + name = nvpair_name(curr_nvp); + status = ipadm_walk_prop(name, proto, prop_class, + show_property, statep); + if (status == IPADM_PROP_UNKNOWN) + (void) show_property(statep, name, proto); + } + } + + free(buf); +} + +/* + * Display information for all or specific interface properties, either for a + * given interface or for all the interfaces in the system. + */ +static void +do_show_ifprop(int argc, char **argv, const char *use) +{ + int option; + nvlist_t *proplist = NULL; + char *fields_str = NULL; + char *ifname; + ofmt_handle_t ofmt; + ofmt_status_t oferr; + uint_t ofmtflags = 0; + uint_t proto; + boolean_t m_arg = _B_FALSE; + char *protostr; + ipadm_if_info_t *ifinfo, *ifp; + ipadm_status_t status; + show_prop_state_t state; + + opterr = 0; + bzero(&state, sizeof (state)); + state.sps_propval = NULL; + state.sps_parsable = _B_FALSE; + state.sps_ifprop = _B_TRUE; + state.sps_status = state.sps_retstatus = IPADM_SUCCESS; + while ((option = getopt_long(argc, argv, ":p:m:co:", + show_ifprop_longopts, NULL)) != -1) { + switch (option) { + case 'p': + if (ipadm_str2nvlist(optarg, &proplist, + IPADM_NORVAL) != 0) + die("invalid interface properties specified"); + break; + case 'c': + state.sps_parsable = _B_TRUE; + break; + case 'o': + fields_str = optarg; + break; + case 'm': + if (m_arg) + die("cannot specify more than one -m"); + m_arg = _B_TRUE; + protostr = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + + if (optind == argc - 1) + ifname = argv[optind]; + else if (optind != argc) + die("Usage: %s", use); + else + ifname = NULL; + + if (!m_arg) + protostr = "ip"; + if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) + die("invalid protocol '%s' specified", protostr); + + state.sps_proto = proto; + state.sps_proplist = proplist; + + if (state.sps_parsable) + ofmtflags |= OFMT_PARSABLE; + oferr = ofmt_open(fields_str, intfprop_fields, ofmtflags, 0, &ofmt); + ipadm_ofmt_check(oferr, state.sps_parsable, ofmt); + state.sps_ofmt = ofmt; + + /* retrieve interface(s) and print the properties */ + status = ipadm_if_info(iph, ifname, &ifinfo, 0, LIFC_DEFAULT); + if (ifname != NULL && status == IPADM_ENXIO) + die("no such object '%s': %s", ifname, + ipadm_status2str(status)); + if (status != IPADM_SUCCESS) + die("Error retrieving interface(s): %s", + ipadm_status2str(status)); + for (ifp = ifinfo; ifp; ifp = ifp->ifi_next) { + (void) strlcpy(state.sps_ifname, ifp->ifi_name, LIFNAMSIZ); + state.sps_proto = proto; + show_properties(&state, IPADMPROP_CLASS_IF); + } + if (ifinfo) + ipadm_free_if_info(ifinfo); + + nvlist_free(proplist); + ofmt_close(ofmt); + + if (state.sps_retstatus != IPADM_SUCCESS) { + ipadm_close(iph); + exit(EXIT_FAILURE); + } +} + +/* + * set/reset the interface property for a given interface. + */ +static void +set_ifprop(int argc, char **argv, boolean_t reset, const char *use) +{ + int option; + ipadm_status_t status = IPADM_SUCCESS; + boolean_t p_arg = _B_FALSE; + boolean_t m_arg = _B_FALSE; + char *ifname, *nv, *protostr; + char *prop_name, *prop_val; + uint_t flags = IPADM_OPT_PERSIST; + uint_t proto; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":m:p:t", + set_ifprop_longopts, NULL)) != -1) { + switch (option) { + case 'p': + if (p_arg) + die("-p must be specified once only"); + p_arg = _B_TRUE; + + ipadm_check_propstr(optarg, reset, use); + nv = optarg; + break; + case 'm': + if (m_arg) + die("-m must be specified once only"); + m_arg = _B_TRUE; + protostr = optarg; + break; + case 't': + flags &= ~IPADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option, use); + } + } + + if (!m_arg || !p_arg || optind != argc - 1) + die("Usage: %s", use); + + ifname = argv[optind]; + + prop_name = nv; + prop_val = strchr(nv, '='); + if (prop_val != NULL) + *prop_val++ = '\0'; + + if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) + die("invalid protocol '%s' specified", protostr); + + if (reset) + flags |= IPADM_OPT_DEFAULT; + else + flags |= IPADM_OPT_ACTIVE; + status = ipadm_set_ifprop(iph, ifname, prop_name, prop_val, proto, + flags); + +done: + if (status != IPADM_SUCCESS) { + if (reset) + die("reset-ifprop: %s: %s", + prop_name, ipadm_status2str(status)); + else + die("set-ifprop: %s: %s", + prop_name, ipadm_status2str(status)); + } +} + +static void +do_set_ifprop(int argc, char **argv, const char *use) +{ + set_ifprop(argc, argv, _B_FALSE, use); +} + +static void +do_reset_ifprop(int argc, char **argv, const char *use) +{ + set_ifprop(argc, argv, _B_TRUE, use); +} + +/* + * Display information for all or specific protocol properties, either for a + * given protocol or for supported protocols (IP/IPv4/IPv6/TCP/UDP/SCTP) + */ +static void +do_show_prop(int argc, char **argv, const char *use) +{ + char option; + nvlist_t *proplist = NULL; + char *fields_str = NULL; + char *protostr; + show_prop_state_t state; + ofmt_handle_t ofmt; + ofmt_status_t oferr; + uint_t ofmtflags = 0; + uint_t proto; + boolean_t p_arg = _B_FALSE; + + opterr = 0; + bzero(&state, sizeof (state)); + state.sps_propval = NULL; + state.sps_parsable = _B_FALSE; + state.sps_modprop = _B_TRUE; + state.sps_status = state.sps_retstatus = IPADM_SUCCESS; + while ((option = getopt_long(argc, argv, ":p:co:", show_prop_longopts, + NULL)) != -1) { + switch (option) { + case 'p': + if (p_arg) + die("-p must be specified once only"); + p_arg = _B_TRUE; + if (ipadm_str2nvlist(optarg, &proplist, + IPADM_NORVAL) != 0) + die("invalid protocol properties specified"); + break; + case 'c': + state.sps_parsable = _B_TRUE; + break; + case 'o': + fields_str = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + if (optind == argc - 1) { + protostr = argv[optind]; + if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) + die("invalid protocol '%s' specified", protostr); + state.sps_proto = proto; + } else if (optind != argc) { + die("Usage: %s", use); + } else { + if (p_arg) + die("protocol must be specified when " + "property name is used"); + state.sps_proto = MOD_PROTO_NONE; + } + + state.sps_proplist = proplist; + + if (state.sps_parsable) + ofmtflags |= OFMT_PARSABLE; + else + ofmtflags |= OFMT_WRAP; + oferr = ofmt_open(fields_str, modprop_fields, ofmtflags, 0, &ofmt); + ipadm_ofmt_check(oferr, state.sps_parsable, ofmt); + state.sps_ofmt = ofmt; + + /* handles all the errors */ + show_properties(&state, IPADMPROP_CLASS_MODULE); + + nvlist_free(proplist); + ofmt_close(ofmt); + + if (state.sps_retstatus != IPADM_SUCCESS) { + ipadm_close(iph); + exit(EXIT_FAILURE); + } +} + +/* + * Checks to see if there are any modifiers, + or -. If there are modifiers + * then sets IPADM_OPT_APPEND or IPADM_OPT_REMOVE, accordingly. + */ +static void +parse_modifiers(char *pstr, uint_t *flags, const char *use) +{ + char *p; + + p = strpbrk(pstr, "+-"); + if (p == NULL) + return; /* Nothing to parse, return */ + + if (p[1] != '=') + die("badly used modifier.\n%s", use); + + if (p[0] == '+') + *flags |= IPADM_OPT_APPEND; + else + *flags |= IPADM_OPT_REMOVE; +} + +/* + * set/reset the protocol property for a given protocol. + */ +static void +set_prop(int argc, char **argv, boolean_t reset, const char *use) +{ + int option; + ipadm_status_t status = IPADM_SUCCESS; + char *protostr, *nv, *prop_name, *prop_val; + boolean_t p_arg = _B_FALSE; + uint_t proto; + uint_t flags = IPADM_OPT_PERSIST; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":p:t", set_prop_longopts, + NULL)) != -1) { + switch (option) { + case 'p': + if (p_arg) + die("-p must be specified once only"); + p_arg = _B_TRUE; + + ipadm_check_propstr(optarg, reset, use); + nv = optarg; + break; + case 't': + flags &= ~IPADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option, use); + } + } + + if (!p_arg || optind != argc - 1) + die("Usage: %s", use); + + parse_modifiers(nv, &flags, use); + prop_name = nv; + prop_val = strchr(nv, '='); + if (prop_val != NULL) { + if (flags & (IPADM_OPT_APPEND|IPADM_OPT_REMOVE)) + *(prop_val - 1) = '\0'; + *prop_val++ = '\0'; + } + protostr = argv[optind]; + if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) + die("invalid protocol '%s' specified", protostr); + + if (reset) + flags |= IPADM_OPT_DEFAULT; + else + flags |= IPADM_OPT_ACTIVE; + status = ipadm_set_prop(iph, prop_name, prop_val, proto, flags); +done: + if (status != IPADM_SUCCESS) { + if (reset) + die("reset-prop: %s: %s", + prop_name, ipadm_status2str(status)); + else + die("set-prop: %s: %s", + prop_name, ipadm_status2str(status)); + } +} + +static void +do_set_prop(int argc, char **argv, const char *use) +{ + set_prop(argc, argv, _B_FALSE, use); +} + +static void +do_reset_prop(int argc, char **argv, const char *use) +{ + set_prop(argc, argv, _B_TRUE, use); +} + +/* + * Called on reboot by /lib/inet/netstart. Reads the persistent store + * and applies all the global protocol properties. + */ +/* ARGSUSED */ +static void +do_init_prop(int argc, char **argv, const char *use) +{ + (void) ipadm_init_prop(); +} + +/* PRINTFLIKE1 */ +static void +warn(const char *format, ...) +{ + va_list alist; + + format = gettext(format); + (void) fprintf(stderr, gettext("%s: warning: "), progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + + (void) fprintf(stderr, "\n"); +} + +/* PRINTFLIKE1 */ +static void +die(const char *format, ...) +{ + va_list alist; + + format = gettext(format); + (void) fprintf(stderr, "%s: ", progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + + (void) putchar('\n'); + + ipadm_destroy_addrobj(ipaddr); + ipadm_close(iph); + exit(EXIT_FAILURE); +} + +static void +die_opterr(int opt, int opterr, const char *usage) +{ + switch (opterr) { + case ':': + die("option '-%c' requires a value\nusage: %s", opt, + gettext(usage)); + break; + case '?': + default: + die("unrecognized option '-%c'\nusage: %s", opt, + gettext(usage)); + break; + } +} + +/* PRINTFLIKE2 */ +static void +warn_ipadmerr(ipadm_status_t err, const char *format, ...) +{ + va_list alist; + + format = gettext(format); + (void) fprintf(stderr, gettext("%s: warning: "), progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + + (void) fprintf(stderr, "%s\n", ipadm_status2str(err)); +} + +static void +process_static_addrargs(const char *use, char *addrarg, const char *aobjname) +{ + int option; + char *val; + char *laddr = NULL; + char *raddr = NULL; + char *save_input_arg = addrarg; + boolean_t found_mismatch = _B_FALSE; + ipadm_status_t status; + enum { A_LOCAL, A_REMOTE }; + static char *addr_optstr[] = { + "local", + "remote", + NULL, + }; + + while (*addrarg != '\0') { + option = getsubopt(&addrarg, addr_optstr, &val); + switch (option) { + case A_LOCAL: + if (laddr != NULL) + die("Multiple local addresses provided"); + laddr = val; + break; + case A_REMOTE: + if (raddr != NULL) + die("Multiple remote addresses provided"); + raddr = val; + break; + default: + if (found_mismatch) + die("Invalid address provided\nusage: %s", use); + found_mismatch = _B_TRUE; + break; + } + } + if (raddr != NULL && laddr == NULL) + die("Missing local address\nusage: %s", use); + + /* If only one address is provided, it is assumed a local address. */ + if (laddr == NULL) { + if (found_mismatch) + laddr = save_input_arg; + else + die("Missing local address\nusage: %s", use); + } + + /* Initialize the addrobj for static addresses. */ + status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname, &ipaddr); + if (status != IPADM_SUCCESS) { + die("Error in creating address object: %s", + ipadm_status2str(status)); + } + + /* Set the local and remote addresses */ + status = ipadm_set_addr(ipaddr, laddr, AF_UNSPEC); + if (status != IPADM_SUCCESS) { + die("Error in setting local address: %s", + ipadm_status2str(status)); + } + if (raddr != NULL) { + status = ipadm_set_dst_addr(ipaddr, raddr, AF_UNSPEC); + if (status != IPADM_SUCCESS) { + die("Error in setting remote address: %s", + ipadm_status2str(status)); + } + } +} + +static void +process_addrconf_addrargs(const char *use, char *addrarg) +{ + int option; + char *val; + enum { P_STATELESS, P_STATEFUL }; + static char *addr_optstr[] = { + "stateless", + "stateful", + NULL, + }; + boolean_t stateless; + boolean_t stateless_arg = _B_FALSE; + boolean_t stateful; + boolean_t stateful_arg = _B_FALSE; + ipadm_status_t status; + + while (*addrarg != '\0') { + option = getsubopt(&addrarg, addr_optstr, &val); + switch (option) { + case P_STATELESS: + if (stateless_arg) + die("Duplicate option"); + if (strcmp(val, "yes") == 0) + stateless = _B_TRUE; + else if (strcmp(val, "no") == 0) + stateless = _B_FALSE; + else + die("Invalid argument"); + stateless_arg = _B_TRUE; + break; + case P_STATEFUL: + if (stateful_arg) + die("Duplicate option"); + if (strcmp(val, "yes") == 0) + stateful = _B_TRUE; + else if (strcmp(val, "no") == 0) + stateful = _B_FALSE; + else + die("Invalid argument"); + stateful_arg = _B_TRUE; + break; + default: + die_opterr(optopt, option, use); + } + } + + if (!stateless_arg && !stateful_arg) + die("Invalid arguments for option -p"); + + /* Set the addrobj fields for addrconf */ + if (stateless_arg) { + status = ipadm_set_stateless(ipaddr, stateless); + if (status != IPADM_SUCCESS) { + die("Error in setting stateless option: %s", + ipadm_status2str(status)); + } + } + if (stateful_arg) { + status = ipadm_set_stateful(ipaddr, stateful); + if (status != IPADM_SUCCESS) { + die("Error in setting stateful option: %s", + ipadm_status2str(status)); + } + } +} + +/* + * Creates static, dhcp or addrconf addresses and associates the created + * addresses with the specified address object name. + */ +static void +do_create_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int option; + uint32_t flags = + IPADM_OPT_PERSIST|IPADM_OPT_ACTIVE|IPADM_OPT_UP; + char *cp; + char *atype = NULL; + char *static_arg = NULL; + char *addrconf_arg = NULL; + char *interface_id = NULL; + char *wait = NULL; + boolean_t s_opt = _B_FALSE; /* static addr options */ + boolean_t auto_opt = _B_FALSE; /* Addrconf options */ + boolean_t dhcp_opt = _B_FALSE; /* dhcp options */ + + opterr = 0; + while ((option = getopt_long(argc, argv, ":T:a:di:p:w:t", + addr_longopts, NULL)) != -1) { + switch (option) { + case 'T': + atype = optarg; + break; + case 'a': + static_arg = optarg; + s_opt = _B_TRUE; + break; + case 'd': + flags &= ~IPADM_OPT_UP; + s_opt = _B_TRUE; + break; + case 'i': + interface_id = optarg; + auto_opt = _B_TRUE; + break; + case 'p': + addrconf_arg = optarg; + auto_opt = _B_TRUE; + break; + case 'w': + wait = optarg; + dhcp_opt = _B_TRUE; + break; + case 't': + flags &= ~IPADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option, use); + } + } + if (atype == NULL || optind != (argc - 1)) { + die("Invalid arguments\nusage: %s", use); + } else if ((cp = strchr(argv[optind], '/')) == NULL || + strlen(++cp) == 0) { + die("invalid address object name: %s\nusage: %s", + argv[optind], use); + } + + /* + * Allocate and initialize the addrobj based on the address type. + */ + if (strcmp(atype, "static") == 0) { + if (static_arg == NULL || auto_opt || dhcp_opt) { + die("Invalid arguments for type %s\nusage: %s", + atype, use); + } + process_static_addrargs(use, static_arg, argv[optind]); + } else if (strcmp(atype, "dhcp") == 0) { + if (auto_opt || s_opt) { + die("Invalid arguments for type %s\nusage: %s", + atype, use); + } + + /* Initialize the addrobj for dhcp addresses. */ + status = ipadm_create_addrobj(IPADM_ADDR_DHCP, argv[optind], + &ipaddr); + if (status != IPADM_SUCCESS) { + die("Error in creating address object: %s", + ipadm_status2str(status)); + } + if (wait != NULL) { + int32_t ipadm_wait; + + if (strcmp(wait, "forever") == 0) { + ipadm_wait = IPADM_DHCP_WAIT_FOREVER; + } else { + char *end; + long timeout = strtol(wait, &end, 10); + + if (*end != '\0' || timeout < 0) + die("Invalid argument"); + ipadm_wait = (int32_t)timeout; + } + status = ipadm_set_wait_time(ipaddr, ipadm_wait); + if (status != IPADM_SUCCESS) { + die("Error in setting wait time: %s", + ipadm_status2str(status)); + } + } + } else if (strcmp(atype, "addrconf") == 0) { + if (dhcp_opt || s_opt) { + die("Invalid arguments for type %s\nusage: %s", + atype, use); + } + + /* Initialize the addrobj for dhcp addresses. */ + status = ipadm_create_addrobj(IPADM_ADDR_IPV6_ADDRCONF, + argv[optind], &ipaddr); + if (status != IPADM_SUCCESS) { + die("Error in creating address object: %s", + ipadm_status2str(status)); + } + if (interface_id != NULL) { + status = ipadm_set_interface_id(ipaddr, interface_id); + if (status != IPADM_SUCCESS) { + die("Error in setting interface ID: %s", + ipadm_status2str(status)); + } + } + if (addrconf_arg) + process_addrconf_addrargs(use, addrconf_arg); + } else { + die("Invalid address type %s", atype); + } + + status = ipadm_create_addr(iph, ipaddr, flags); + if (status == IPADM_DHCP_IPC_TIMEOUT) + warn_ipadmerr(status, ""); + else if (status != IPADM_SUCCESS) + die("Could not create address: %s", ipadm_status2str(status)); +} + +/* + * Used by some address management functions to parse the command line + * arguments and create `ipaddr' address object. + */ +static void +process_misc_addrargs(int argc, char *argv[], const char *use, int *index, + uint32_t *flags) +{ + int option; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":t", addr_misc_longopts, + NULL)) != -1) { + switch (option) { + case 't': + *flags &= ~IPADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option, use); + } + } + if (optind != (argc - 1)) + die("Usage: %s", use); + + *index = optind; +} + +/* + * Remove an addrobj from both active and persistent configuration. + */ +static void +do_delete_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + int option; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":r", addr_misc_longopts, + NULL)) != -1) { + switch (option) { + case 'r': + flags |= IPADM_OPT_RELEASE; + break; + default: + die_opterr(optopt, option, use); + } + } + if (optind != (argc - 1)) + die("Usage: %s", use); + + status = ipadm_delete_addr(iph, argv[optind], flags); + if (status != IPADM_SUCCESS) { + die("could not delete address: %s", + ipadm_status2str(status)); + } +} + +/* + * Enable an IP address based on the persistent configuration for that + * IP address + */ +static void +do_enable_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int index; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + process_misc_addrargs(argc, argv, use, &index, &flags); + if (flags & IPADM_OPT_PERSIST) + die("persistent operation not supported for enable-addr"); + + status = ipadm_enable_addr(iph, argv[index], flags); + if (status != IPADM_SUCCESS) + die("could not enable address: %s", ipadm_status2str(status)); +} + +/* + * Mark the address identified by addrobj 'up' + */ +static void +do_up_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int index; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + process_misc_addrargs(argc, argv, use, &index, &flags); + status = ipadm_up_addr(iph, argv[index], flags); + if (status != IPADM_SUCCESS) { + die("Could not mark the address up: %s", + ipadm_status2str(status)); + } +} + +/* + * Disable the specified addrobj by removing it from active cofiguration + */ +static void +do_disable_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int index; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + process_misc_addrargs(argc, argv, use, &index, &flags); + if (flags & IPADM_OPT_PERSIST) + die("persistent operation not supported for disable-addr"); + + status = ipadm_disable_addr(iph, argv[index], flags); + if (status != IPADM_SUCCESS) { + die("could not disable address: %s", + ipadm_status2str(status)); + } +} + +/* + * Mark the address identified by addrobj 'down' + */ +static void +do_down_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int index; + uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + process_misc_addrargs(argc, argv, use, &index, &flags); + status = ipadm_down_addr(iph, argv[index], flags); + if (status != IPADM_SUCCESS) + die("Could not mark the address down: %s", + ipadm_status2str(status)); +} + +/* + * Restart DAD for static address. Extend lease duration for DHCP addresses + */ +static void +do_refresh_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + int option; + uint32_t flags = 0; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":i", addr_misc_longopts, + NULL)) != -1) { + switch (option) { + case 'i': + flags |= IPADM_OPT_INFORM; + break; + default: + die_opterr(optopt, option, use); + } + } + if (optind != (argc - 1)) + die("Usage: %s", use); + + status = ipadm_refresh_addr(iph, argv[optind], flags); + if (status == IPADM_DHCP_IPC_TIMEOUT) + warn_ipadmerr(status, ""); + else if (status != IPADM_SUCCESS) + die("could not refresh address %s", ipadm_status2str(status)); +} + +static void +sockaddr2str(const struct sockaddr_storage *ssp, char *buf, uint_t bufsize) +{ + socklen_t socklen; + struct sockaddr *sp = (struct sockaddr *)ssp; + + switch (ssp->ss_family) { + case AF_INET: + socklen = sizeof (struct sockaddr_in); + break; + case AF_INET6: + socklen = sizeof (struct sockaddr_in6); + break; + default: + (void) strlcpy(buf, STR_UNKNOWN_VAL, bufsize); + return; + } + + (void) getnameinfo(sp, socklen, buf, bufsize, NULL, 0, + (NI_NOFQDN | NI_NUMERICHOST)); +} + +static void +flags2str(uint64_t flags, fmask_t *tbl, boolean_t is_bits, + char *buf, uint_t bufsize) +{ + int i; + boolean_t first = _B_TRUE; + + if (is_bits) { + for (i = 0; tbl[i].name; i++) { + if ((flags & tbl[i].mask) == tbl[i].bits) + (void) strlcat(buf, tbl[i].name, bufsize); + else + (void) strlcat(buf, "-", bufsize); + } + } else { + for (i = 0; tbl[i].name; i++) { + if ((flags & tbl[i].mask) == tbl[i].bits) { + if (!first) + (void) strlcat(buf, ",", bufsize); + (void) strlcat(buf, tbl[i].name, bufsize); + first = _B_FALSE; + } + } + } +} + +static boolean_t +print_sa_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + show_addr_args_t *arg = ofarg->ofmt_cbarg; + ipadm_addr_info_t *ainfo = arg->sa_info; + char interface[LIFNAMSIZ]; + char addrbuf[MAXPROPVALLEN]; + char dstbuf[MAXPROPVALLEN]; + char prefixlenstr[MAXPROPVALLEN]; + int prefixlen; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + sa_family_t af; + char *phyname = NULL; + struct ifaddrs *ifa = &ainfo->ia_ifa; + fmask_t cflags_mask[] = { + { "U", IA_UP, IA_UP }, + { "u", IA_UNNUMBERED, IA_UNNUMBERED }, + { "p", IA_PRIVATE, IA_PRIVATE }, + { "t", IA_TEMPORARY, IA_TEMPORARY }, + { "d", IA_DEPRECATED, IA_DEPRECATED }, + { NULL, 0, 0 } + }; + fmask_t pflags_mask[] = { + { "U", IA_UP, IA_UP }, + { "p", IA_PRIVATE, IA_PRIVATE }, + { "d", IA_DEPRECATED, IA_DEPRECATED }, + { NULL, 0, 0 } + }; + fmask_t type[] = { + { "static", IPADM_ADDR_STATIC, IPADM_ALL_BITS}, + { "addrconf", IPADM_ADDR_IPV6_ADDRCONF, IPADM_ALL_BITS}, + { "dhcp", IPADM_ADDR_DHCP, IPADM_ALL_BITS}, + { NULL, 0, 0 } + }; + fmask_t addr_state[] = { + { "disabled", IFA_DISABLED, IPADM_ALL_BITS}, + { "duplicate", IFA_DUPLICATE, IPADM_ALL_BITS}, + { "down", IFA_DOWN, IPADM_ALL_BITS}, + { "tentative", IFA_TENTATIVE, IPADM_ALL_BITS}, + { "ok", IFA_OK, IPADM_ALL_BITS}, + { "inaccessible", IFA_INACCESSIBLE, IPADM_ALL_BITS}, + { NULL, 0, 0 } + }; + + buf[0] = '\0'; + switch (ofarg->ofmt_id) { + case SA_ADDROBJ: + if (ainfo->ia_aobjname[0] == '\0') { + (void) strncpy(interface, ifa->ifa_name, LIFNAMSIZ); + phyname = strrchr(interface, ':'); + if (phyname) + *phyname = '\0'; + (void) snprintf(buf, bufsize, "%s/%s", interface, + STR_UNKNOWN_VAL); + } else { + (void) snprintf(buf, bufsize, "%s", ainfo->ia_aobjname); + } + break; + case SA_STATE: + flags2str(ainfo->ia_state, addr_state, _B_FALSE, + buf, bufsize); + break; + case SA_TYPE: + flags2str(ainfo->ia_atype, type, _B_FALSE, buf, bufsize); + break; + case SA_CURRENT: + flags2str(ainfo->ia_cflags, cflags_mask, _B_TRUE, buf, bufsize); + break; + case SA_PERSISTENT: + flags2str(ainfo->ia_pflags, pflags_mask, _B_TRUE, buf, bufsize); + break; + case SA_ADDR: + af = ifa->ifa_addr->ss_family; + /* + * If the address is 0.0.0.0 or :: and the origin is DHCP, + * print STR_UNKNOWN_VAL. + */ + if (ainfo->ia_atype == IPADM_ADDR_DHCP) { + sin = (struct sockaddr_in *)ifa->ifa_addr; + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if ((af == AF_INET && + sin->sin_addr.s_addr == INADDR_ANY) || + (af == AF_INET6 && + IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))) { + (void) snprintf(buf, bufsize, STR_UNKNOWN_VAL); + break; + } + } + if (ifa->ifa_netmask == NULL) + prefixlen = 0; + else + prefixlen = mask2plen(ifa->ifa_netmask); + bzero(prefixlenstr, sizeof (prefixlenstr)); + if (prefixlen > 0) { + (void) snprintf(prefixlenstr, sizeof (prefixlenstr), + "/%d", prefixlen); + } + bzero(addrbuf, sizeof (addrbuf)); + bzero(dstbuf, sizeof (dstbuf)); + if (ainfo->ia_atype == IPADM_ADDR_STATIC) { + /* + * Print the hostname fields if the address is not + * in active configuration. + */ + if (ainfo->ia_state == IFA_DISABLED) { + (void) snprintf(buf, bufsize, "%s", + ainfo->ia_sname); + if (ainfo->ia_dname[0] != '\0') { + (void) snprintf(dstbuf, sizeof (dstbuf), + "->%s", ainfo->ia_dname); + (void) strlcat(buf, dstbuf, bufsize); + } else { + (void) strlcat(buf, prefixlenstr, + bufsize); + } + break; + } + /* + * For the non-persistent case, we need to show the + * currently configured addresses for source and + * destination. + */ + if (ifa->ifa_flags & IFF_POINTOPOINT) { + sockaddr2str( + (struct sockaddr_storage *)ifa->ifa_dstaddr, + dstbuf, sizeof (dstbuf)); + } + } + sockaddr2str((struct sockaddr_storage *)ifa->ifa_addr, + addrbuf, sizeof (addrbuf)); + if (dstbuf[0] != '\0') { + (void) snprintf(buf, bufsize, "%s->%s", addrbuf, + dstbuf); + } else { + (void) snprintf(buf, bufsize, "%s%s", addrbuf, + prefixlenstr); + } + break; + default: + die("invalid input"); + break; + } + + return (_B_TRUE); +} + +/* + * Display address information, either for the given address or + * for all the addresses managed by ipadm. + */ +static void +do_show_addr(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + show_addr_state_t state; + char *def_fields_str = "addrobj,type,state,addr"; + char *fields_str = NULL; + ipadm_addr_info_t *ainfo; + ipadm_addr_info_t *ptr; + show_addr_args_t sargs; + int option; + ofmt_handle_t ofmt; + ofmt_status_t oferr; + uint_t ofmtflags = 0; + char *aname; + char *ifname = NULL; + char *cp; + boolean_t found = _B_FALSE; + + opterr = 0; + state.sa_parsable = _B_FALSE; + state.sa_persist = _B_FALSE; + while ((option = getopt_long(argc, argv, "po:", show_addr_longopts, + NULL)) != -1) { + switch (option) { + case 'p': + state.sa_parsable = _B_TRUE; + break; + case 'o': + fields_str = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + if (state.sa_parsable && fields_str == NULL) + die("-p requires -o"); + + if (optind == argc - 1) { + aname = argv[optind]; + if ((cp = strchr(aname, '/')) == NULL) + die("Invalid address object name provided"); + if (*(cp + 1) == '\0') { + ifname = aname; + *cp = '\0'; + aname = NULL; + } + } else if (optind == argc) { + aname = NULL; + } else { + die("Usage: %s", use); + } + + if (state.sa_parsable) + ofmtflags |= OFMT_PARSABLE; + if (fields_str == NULL) + fields_str = def_fields_str; + oferr = ofmt_open(fields_str, show_addr_fields, ofmtflags, 0, &ofmt); + + ipadm_ofmt_check(oferr, state.sa_parsable, ofmt); + state.sa_ofmt = ofmt; + + status = ipadm_addr_info(iph, ifname, &ainfo, 0, LIFC_DEFAULT); + /* + * Return without printing any error, if no addresses were found, + * for the case where all addresses are requested. + */ + if (status != IPADM_SUCCESS) + die("Could not get address: %s", ipadm_status2str(status)); + if (ainfo == NULL) { + ofmt_close(ofmt); + return; + } + + bzero(&sargs, sizeof (sargs)); + sargs.sa_state = &state; + for (ptr = ainfo; ptr != NULL; ptr = IA_NEXT(ptr)) { + sargs.sa_info = ptr; + if (aname != NULL) { + if (strcmp(sargs.sa_info->ia_aobjname, aname) != 0) + continue; + found = _B_TRUE; + } + ofmt_print(state.sa_ofmt, &sargs); + } + if (ainfo) + ipadm_free_addr_info(ainfo); + if (aname != NULL && !found) + die("Address object not found"); +} + +static boolean_t +print_si_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + show_if_args_t *arg = ofarg->ofmt_cbarg; + ipadm_if_info_t *ifinfo = arg->si_info; + char *ifname = ifinfo->ifi_name; + fmask_t intf_state[] = { + { "ok", IFIS_OK, IPADM_ALL_BITS}, + { "down", IFIS_DOWN, IPADM_ALL_BITS}, + { "disabled", IFIS_DISABLED, IPADM_ALL_BITS}, + { "failed", IFIS_FAILED, IPADM_ALL_BITS}, + { "offline", IFIS_OFFLINE, IPADM_ALL_BITS}, + { NULL, 0, 0 } + }; + fmask_t intf_pflags[] = { + { "s", IFIF_STANDBY, IFIF_STANDBY }, + { "4", IFIF_IPV4, IFIF_IPV4 }, + { "6", IFIF_IPV6, IFIF_IPV6 }, + { NULL, 0, 0 } + }; + fmask_t intf_cflags[] = { + { "b", IFIF_BROADCAST, IFIF_BROADCAST }, + { "m", IFIF_MULTICAST, IFIF_MULTICAST }, + { "p", IFIF_POINTOPOINT, IFIF_POINTOPOINT}, + { "v", IFIF_VIRTUAL, IFIF_VIRTUAL }, + { "I", IFIF_IPMP, IFIF_IPMP }, + { "s", IFIF_STANDBY, IFIF_STANDBY }, + { "i", IFIF_INACTIVE, IFIF_INACTIVE }, + { "V", IFIF_VRRP, IFIF_VRRP }, + { "a", IFIF_NOACCEPT, IFIF_NOACCEPT }, + { "4", IFIF_IPV4, IFIF_IPV4 }, + { "6", IFIF_IPV6, IFIF_IPV6 }, + { NULL, 0, 0 } + }; + + buf[0] = '\0'; + switch (ofarg->ofmt_id) { + case SI_IFNAME: + (void) snprintf(buf, bufsize, "%s", ifname); + break; + case SI_STATE: + flags2str(ifinfo->ifi_state, intf_state, _B_FALSE, + buf, bufsize); + break; + case SI_CURRENT: + flags2str(ifinfo->ifi_cflags, intf_cflags, _B_TRUE, + buf, bufsize); + break; + case SI_PERSISTENT: + flags2str(ifinfo->ifi_pflags, intf_pflags, _B_TRUE, + buf, bufsize); + break; + default: + die("invalid input"); + break; + } + + return (_B_TRUE); +} + +/* + * Display interface information, either for the given interface or + * for all the interfaces in the system. + */ +static void +do_show_if(int argc, char *argv[], const char *use) +{ + ipadm_status_t status; + show_if_state_t state; + char *fields_str = NULL; + ipadm_if_info_t *if_info, *ptr; + show_if_args_t sargs; + int option; + ofmt_handle_t ofmt; + ofmt_status_t oferr; + uint_t ofmtflags = 0; + char *ifname = NULL; + + opterr = 0; + state.si_parsable = _B_FALSE; + + while ((option = getopt_long(argc, argv, "po:", show_if_longopts, + NULL)) != -1) { + switch (option) { + case 'p': + state.si_parsable = _B_TRUE; + break; + case 'o': + fields_str = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + if (optind == argc - 1) + ifname = argv[optind]; + else if (optind != argc) + die("Usage: %s", use); + if (state.si_parsable) + ofmtflags |= OFMT_PARSABLE; + oferr = ofmt_open(fields_str, show_if_fields, ofmtflags, 0, &ofmt); + ipadm_ofmt_check(oferr, state.si_parsable, ofmt); + state.si_ofmt = ofmt; + bzero(&sargs, sizeof (sargs)); + sargs.si_state = &state; + status = ipadm_if_info(iph, ifname, &if_info, 0, LIFC_DEFAULT); + /* + * Return without printing any error, if no addresses were found. + */ + if (status != IPADM_SUCCESS) { + die("Could not get interface(s): %s", + ipadm_status2str(status)); + } + + for (ptr = if_info; ptr; ptr = ptr->ifi_next) { + sargs.si_info = ptr; + ofmt_print(state.si_ofmt, &sargs); + } + if (if_info) + ipadm_free_if_info(if_info); +} + +/* + * set/reset the address property for a given address + */ +static void +set_addrprop(int argc, char **argv, boolean_t reset, const char *use) +{ + int option; + ipadm_status_t status = IPADM_SUCCESS; + boolean_t p_arg = _B_FALSE; + char *nv, *aobjname; + char *prop_name, *prop_val; + uint_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; + + opterr = 0; + while ((option = getopt_long(argc, argv, ":i:p:t", set_ifprop_longopts, + NULL)) != -1) { + switch (option) { + case 'p': + if (p_arg) + die("-p must be specified once only"); + p_arg = _B_TRUE; + + ipadm_check_propstr(optarg, reset, use); + nv = optarg; + break; + case 't': + flags &= ~IPADM_OPT_PERSIST; + break; + default: + die_opterr(optopt, option, use); + } + } + + if (!p_arg || optind != (argc - 1)) + die("Usage: %s", use); + + prop_name = nv; + prop_val = strchr(nv, '='); + if (prop_val != NULL) + *prop_val++ = '\0'; + aobjname = argv[optind]; + if (reset) + flags |= IPADM_OPT_DEFAULT; + status = ipadm_set_addrprop(iph, prop_name, prop_val, aobjname, flags); + if (status != IPADM_SUCCESS) { + if (reset) + die("reset-addrprop: %s: %s", prop_name, + ipadm_status2str(status)); + else + die("set-addrprop: %s: %s", prop_name, + ipadm_status2str(status)); + } +} + +/* + * Sets a property on an address object. + */ +static void +do_set_addrprop(int argc, char **argv, const char *use) +{ + set_addrprop(argc, argv, _B_FALSE, use); +} + +/* + * Resets a property to its default value on an address object. + */ +static void +do_reset_addrprop(int argc, char **argv, const char *use) +{ + set_addrprop(argc, argv, _B_TRUE, use); +} + +/* + * Display information for all or specific address properties, either for a + * given address or for all the addresses in the system. + */ +static void +do_show_addrprop(int argc, char *argv[], const char *use) +{ + int option; + nvlist_t *proplist = NULL; + char *fields_str = NULL; + show_prop_state_t state; + ofmt_handle_t ofmt; + ofmt_status_t oferr; + uint_t ofmtflags = 0; + char *aobjname; + char *ifname = NULL; + char *cp; + + opterr = 0; + bzero(&state, sizeof (state)); + state.sps_propval = NULL; + state.sps_parsable = _B_FALSE; + state.sps_addrprop = _B_TRUE; + state.sps_proto = MOD_PROTO_NONE; + state.sps_status = state.sps_retstatus = IPADM_SUCCESS; + while ((option = getopt_long(argc, argv, ":p:i:cPo:", + show_prop_longopts, NULL)) != -1) { + switch (option) { + case 'p': + if (ipadm_str2nvlist(optarg, &proplist, + IPADM_NORVAL) != 0) + die("invalid interface properties specified"); + break; + case 'c': + state.sps_parsable = _B_TRUE; + break; + case 'o': + fields_str = optarg; + break; + default: + die_opterr(optopt, option, use); + break; + } + } + if (optind == argc - 1) { + aobjname = argv[optind]; + cp = strchr(aobjname, '/'); + if (cp == NULL) + die("Invalid address object name provided"); + if (*(cp + 1) == '\0') { + ifname = aobjname; + *cp = '\0'; + aobjname = NULL; + } + } else if (optind == argc) { + aobjname = NULL; + } else { + die("Usage: %s", use); + } + state.sps_proplist = proplist; + if (state.sps_parsable) + ofmtflags |= OFMT_PARSABLE; + oferr = ofmt_open(fields_str, addrprop_fields, ofmtflags, 0, &ofmt); + ipadm_ofmt_check(oferr, state.sps_parsable, ofmt); + state.sps_ofmt = ofmt; + + if (aobjname != NULL) { + (void) strlcpy(state.sps_aobjname, aobjname, + sizeof (state.sps_aobjname)); + show_properties(&state, IPADMPROP_CLASS_ADDR); + } else { + ipadm_addr_info_t *ainfop = NULL; + ipadm_addr_info_t *ptr; + ipadm_status_t status; + + status = ipadm_addr_info(iph, ifname, &ainfop, 0, LIFC_DEFAULT); + /* + * Return without printing any error, if no addresses were + * found. + */ + if (status == IPADM_NOTFOUND) + return; + if (status != IPADM_SUCCESS) { + die("Error retrieving address: %s", + ipadm_status2str(status)); + } + for (ptr = ainfop; ptr; ptr = IA_NEXT(ptr)) { + aobjname = ptr->ia_aobjname; + if (aobjname[0] == '\0' || + ptr->ia_atype == IPADM_ADDR_IPV6_ADDRCONF) { + continue; + } + (void) strlcpy(state.sps_aobjname, aobjname, + sizeof (state.sps_aobjname)); + show_properties(&state, IPADMPROP_CLASS_ADDR); + } + ipadm_free_addr_info(ainfop); + } + nvlist_free(proplist); + ofmt_close(ofmt); + if (state.sps_retstatus != IPADM_SUCCESS) { + ipadm_close(iph); + exit(EXIT_FAILURE); + } +} + +static void +ipadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable, + ofmt_handle_t ofmt) +{ + char buf[OFMT_BUFSIZE]; + + if (oferr == OFMT_SUCCESS) + return; + (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); + /* + * All errors are considered fatal in parsable mode. + * NOMEM errors are always fatal, regardless of mode. + * For other errors, we print diagnostics in human-readable + * mode and processs what we can. + */ + if (parsable || oferr == OFMT_ENOFIELDS) { + ofmt_close(ofmt); + die(buf); + } else { + warn(buf); + } +} + +/* + * check if the `pstr' adheres to following syntax + * - prop=<value[,...]> (for set) + * - prop (for reset) + */ +static void +ipadm_check_propstr(const char *pstr, boolean_t reset, const char *use) +{ + char *nv; + + nv = strchr(pstr, '='); + if (reset) { + if (nv != NULL) + die("incorrect syntax used for -p.\n%s", use); + } else { + if (nv == NULL || *++nv == '\0') + die("please specify the value to be set.\n%s", use); + nv = strchr(nv, '='); + /* cannot have multiple 'prop=val' for single -p */ + if (nv != NULL) + die("cannot specify more than one prop=val at " + "a time.\n%s", use); + } +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.xcl b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.xcl new file mode 100644 index 0000000000..cf59500dc1 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.xcl @@ -0,0 +1,137 @@ +# +# 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 "\n" +msgid "%s" +msgid "%s\n" +msgid "%s%s" +msgid "%s->%s" +msgid "%s/%s" +msgid "%s: " +msgid "'%s'" +msgid "+-" +msgid "," +msgid "-" +msgid "->%s" +msgid "/%d" +msgid "4" +msgid "6" +msgid ":T:a:di:p:w:t" +msgid ":i" +msgid ":i:p:t" +msgid ":m:p:t" +msgid ":p:co:" +msgid ":p:i:cPo:" +msgid ":p:m:co:" +msgid ":p:t" +msgid ":r" +msgid ":t" +msgid "ADDR" +msgid "ADDROBJ" +msgid "CURRENT" +msgid "DEFAULT" +msgid "I" +msgid "IFNAME" +msgid "PERM" +msgid "PERSISTENT" +msgid "POSSIBLE" +msgid "PROPERTY" +msgid "PROTO" +msgid "STATE" +msgid "TYPE" +msgid "U" +msgid "V" +msgid "a" +msgid "addrconf" +msgid "address" +msgid "addrobj,type,state,addr" +msgid "b" +msgid "create-addr" +msgid "create-if" +msgid "d" +msgid "delete-addr" +msgid "delete-if" +msgid "dhcp" +msgid "disable-addr" +msgid "disable-if" +msgid "disabled" +msgid "down" +msgid "down-addr" +msgid "duplicate" +msgid "enable-addr" +msgid "enable-if" +msgid "failed" +msgid "forever" +msgid "i" +msgid "inaccessible" +msgid "inform" +msgid "init-if" +msgid "init-prop" +msgid "interface-id" +msgid "ip" +msgid "local" +msgid "m" +msgid "module" +msgid "no" +msgid "offline" +msgid "ok" +msgid "output" +msgid "p" +msgid "parsable" +msgid "po:" +msgid "prop" +msgid "refresh-addr" +msgid "release" +msgid "remote" +msgid "reset-addrprop" +msgid "reset-addrprop: %s: %s" +msgid "reset-ifprop" +msgid "reset-ifprop: %s: %s" +msgid "reset-prop" +msgid "reset-prop: %s: %s" +msgid "s" +msgid "set-addrprop" +msgid "set-addrprop: %s: %s" +msgid "set-ifprop" +msgid "set-ifprop: %s: %s" +msgid "set-prop" +msgid "set-prop: %s: %s" +msgid "show-addr" +msgid "show-addrprop" +msgid "show-if" +msgid "show-ifprop" +msgid "show-prop" +msgid "stateful" +msgid "stateless" +msgid "static" +msgid "t" +msgid "temporary" +msgid "tentative" +msgid "type" +msgid "u" +msgid "up-addr" +msgid "v" +msgid "wait" +msgid "yes" diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ndd.c b/usr/src/cmd/cmd-inet/usr.sbin/ndd.c index 23b0ae9e20..e45ecc12ea 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ndd.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ndd.c @@ -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. */ @@ -36,11 +36,14 @@ #include <unistd.h> #include <sys/types.h> #include <stropts.h> +#include <inet/tunables.h> #include <inet/nd.h> #include <string.h> +#include <strings.h> #include <stdlib.h> #include <libdllink.h> #include <libintl.h> +#include <libipadm.h> static boolean_t do_getset(int fd, int cmd, char *buf, int buf_len); static int get_value(char *msg, char *buf, int buf_len); @@ -51,11 +54,259 @@ static char *errmsg(int err); static void fatal(char *fmt, ...); static void printe(boolean_t print_errno, char *fmt, ...); -static char gbuf[65536]; /* Need 20k for 160 IREs ... */ +static char modpath[128]; /* path to module */ +static char gbuf[65536]; /* need large buffer to retrieve all names */ static char usage_str[] = "usage: ndd -set device_name name value\n" " ndd [-get] device_name name [name ...]"; /* + * Maps old ndd_name to the new ipadm_name. Any ndd property that is moved to + * libipadm should have an entry here to ensure backward compatibility + */ +typedef struct ndd2ipadm_map { + char *ndd_name; + char *ipadm_name; + uint_t ipadm_proto; + uint_t ipadm_flags; + uint_t ndd_perm; +} ndd2ipadm_map_t; + +static ndd2ipadm_map_t map[] = { + { "ip_def_ttl", "ttl", MOD_PROTO_IPV4, 0, 0 }, + { "ip6_def_hops", "hoplimit", MOD_PROTO_IPV6, 0, 0 }, + { "ip_forwarding", "forwarding", MOD_PROTO_IPV4, 0, 0 }, + { "ip6_forwarding", "forwarding", MOD_PROTO_IPV6, 0, 0 }, + { "icmp_recv_hiwat", "recv_maxbuf", MOD_PROTO_RAWIP, 0, 0 }, + { "icmp_xmit_hiwat", "send_maxbuf", MOD_PROTO_RAWIP, 0, 0 }, + { "tcp_ecn_permitted", "ecn", MOD_PROTO_TCP, 0, 0 }, + { "tcp_extra_priv_ports_add", "extra_priv_ports", MOD_PROTO_TCP, + IPADM_OPT_APPEND, MOD_PROP_PERM_WRITE }, + { "tcp_extra_priv_ports_del", "extra_priv_ports", MOD_PROTO_TCP, + IPADM_OPT_REMOVE, MOD_PROP_PERM_WRITE }, + { "tcp_extra_priv_ports", "extra_priv_ports", MOD_PROTO_TCP, + 0, MOD_PROP_PERM_READ }, + { "tcp_largest_anon_port", "largest_anon_port", MOD_PROTO_TCP, + 0, 0 }, + { "tcp_recv_hiwat", "recv_maxbuf", MOD_PROTO_TCP, 0, 0 }, + { "tcp_sack_permitted", "sack", MOD_PROTO_TCP, 0, 0 }, + { "tcp_xmit_hiwat", "send_maxbuf", MOD_PROTO_TCP, 0, 0 }, + { "tcp_smallest_anon_port", "smallest_anon_port", MOD_PROTO_TCP, + 0, 0 }, + { "tcp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_TCP, + 0, 0 }, + { "udp_extra_priv_ports_add", "extra_priv_ports", MOD_PROTO_UDP, + IPADM_OPT_APPEND, MOD_PROP_PERM_WRITE }, + { "udp_extra_priv_ports_del", "extra_priv_ports", MOD_PROTO_UDP, + IPADM_OPT_REMOVE, MOD_PROP_PERM_WRITE }, + { "udp_extra_priv_ports", "extra_priv_ports", MOD_PROTO_UDP, + 0, MOD_PROP_PERM_READ }, + { "udp_largest_anon_port", "largest_anon_port", MOD_PROTO_UDP, + 0, 0 }, + { "udp_recv_hiwat", "recv_maxbuf", MOD_PROTO_UDP, 0, 0 }, + { "udp_xmit_hiwat", "send_maxbuf", MOD_PROTO_UDP, 0, 0 }, + { "udp_smallest_anon_port", "smallest_anon_port", MOD_PROTO_UDP, + 0, 0 }, + { "udp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_UDP, + 0, 0 }, + { "sctp_extra_priv_ports_add", "extra_priv_ports", MOD_PROTO_SCTP, + IPADM_OPT_APPEND, MOD_PROP_PERM_WRITE }, + { "sctp_extra_priv_ports_del", "extra_priv_ports", MOD_PROTO_SCTP, + IPADM_OPT_REMOVE, MOD_PROP_PERM_WRITE }, + { "sctp_extra_priv_ports", "extra_priv_ports", MOD_PROTO_SCTP, + 0, MOD_PROP_PERM_READ }, + { "sctp_largest_anon_port", "largest_anon_port", MOD_PROTO_SCTP, + 0, 0 }, + { "sctp_recv_hiwat", "recv_maxbuf", MOD_PROTO_SCTP, 0, 0 }, + { "sctp_xmit_hiwat", "send_maxbuf", MOD_PROTO_SCTP, 0, 0 }, + { "sctp_smallest_anon_port", "smallest_anon_port", MOD_PROTO_SCTP, + 0, 0 }, + { "sctp_smallest_nonpriv_port", "smallest_nonpriv_port", MOD_PROTO_SCTP, + 0, 0 }, + { NULL, NULL, 0, 0, 0 } +}; + +static uint_t +ndd_str2proto(const char *protostr) +{ + if (strcmp(protostr, "tcp") == 0 || + strcmp(protostr, "tcp6") == 0) { + return (MOD_PROTO_TCP); + } else if (strcmp(protostr, "udp") == 0 || + strcmp(protostr, "udp6") == 0) { + return (MOD_PROTO_UDP); + } else if (strcmp(protostr, "ip") == 0 || + strcmp(protostr, "ip6") == 0 || + strcmp(protostr, "arp") == 0) { + return (MOD_PROTO_IP); + } else if (strcmp(protostr, "icmp") == 0 || + strcmp(protostr, "icmp6") == 0) { + return (MOD_PROTO_RAWIP); + } else if (strcmp(protostr, "sctp") == 0 || + strcmp(protostr, "sctp6") == 0) { + return (MOD_PROTO_SCTP); + } + return (MOD_PROTO_NONE); +} + +static char * +ndd_perm2str(uint_t perm) +{ + switch (perm) { + case MOD_PROP_PERM_READ: + return ("read only"); + case MOD_PROP_PERM_WRITE: + return ("write only"); + case MOD_PROP_PERM_RW: + return ("read and write"); + } + + return (NULL); +} + +/* + * This function converts any new property names to old ndd name by consulting + * ndd2ipadm_map_t. This is done to preserve backward compatibility. + */ +static void +print_ipadm2ndd(char *oldbuf, uint_t obufsize) +{ + ndd2ipadm_map_t *nimap; + char *pname, *rwtag, *protostr; + uint_t proto, perm; + boolean_t matched; + + pname = oldbuf; + while (pname[0] && pname < (oldbuf + obufsize - 1)) { + for (protostr = pname; !isspace(*protostr); protostr++) + ; + *protostr++ = '\0'; + /* protostr now points to protocol */ + + for (rwtag = protostr; !isspace(*rwtag); rwtag++) + ; + *rwtag++ = '\0'; + /* rwtag now points to permissions */ + + proto = atoi(protostr); + perm = atoi(rwtag); + matched = B_FALSE; + for (nimap = map; nimap->ndd_name != NULL; nimap++) { + if (strcmp(pname, nimap->ipadm_name) != 0 || + !(nimap->ipadm_proto & proto)) + continue; + + matched = B_TRUE; + if (nimap->ndd_perm != 0) + perm = nimap->ndd_perm; + (void) printf("%-30s (%s)\n", nimap->ndd_name, + ndd_perm2str(perm)); + } + if (!matched) + (void) printf("%-30s (%s)\n", pname, + ndd_perm2str(perm)); + for (pname = rwtag; *pname++; ) + ; + } +} + +/* + * get/set the value for a given property by calling into libipadm. The + * IPH_LEGACY flag is used by libipadm for special handling. For some + * properties, libipadm.so displays strings (for e.g., on/off, + * never/passive/active, et al) instead of numerals. However ndd(1M) always + * printed numberals. This flag will help in avoiding printing strings. + */ +static boolean_t +do_ipadm_getset(int cmd, char *buf, int buflen) +{ + ndd2ipadm_map_t *nimap; + ipadm_handle_t iph = NULL; + ipadm_status_t status; + char *mod; + uint_t proto, perm = 0, flags = 0; + char *pname, *pvalp; + int i; + + if ((mod = strrchr(modpath, '/')) == NULL) + mod = modpath; + else + ++mod; + if ((proto = ndd_str2proto(mod)) == MOD_PROTO_NONE) + return (B_FALSE); + + if ((status = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) + goto fail; + + pname = buf; + for (nimap = map; nimap->ndd_name != NULL; nimap++) { + if (strcmp(pname, nimap->ndd_name) == 0) + break; + } + if (nimap->ndd_name != NULL) { + pname = nimap->ipadm_name; + proto = nimap->ipadm_proto; + flags = nimap->ipadm_flags; + perm = nimap->ndd_perm; + } + if (cmd == ND_GET) { + char propval[MAXPROPVALLEN], allprop[64536]; + uint_t pvalsz; + sa_family_t af = AF_UNSPEC; + int err; + + if (perm == MOD_PROP_PERM_WRITE) + fatal("operation failed: Permission denied"); + + if (strcmp(pname, "?") == 0) { + pvalp = allprop; + pvalsz = sizeof (allprop); + } else { + pvalp = propval; + pvalsz = sizeof (propval); + } + + status = ipadm_get_prop(iph, pname, pvalp, &pvalsz, proto, + IPADM_OPT_ACTIVE); + if (status != IPADM_SUCCESS) + goto fail; + + if (strcmp(pname, "?") == 0) { + (void) print_ipadm2ndd(pvalp, pvalsz); + } else { + char *tmp = pvalp; + + /* + * For backward compatibility if there are multiple + * values print each value in it's own line. + */ + while (*tmp != '\0') { + if (*tmp == ',') + *tmp = '\n'; + tmp++; + } + (void) printf("%s\n", pvalp); + } + (void) fflush(stdout); + } else { + if (perm == MOD_PROP_PERM_READ) + fatal("operation failed: Permission denied"); + + /* walk past the property name to find the property value */ + for (i = 0; buf[i] != '\0'; i++) + ; + + pvalp = &buf[++i]; + status = ipadm_set_prop(iph, pname, pvalp, proto, + flags|IPADM_OPT_ACTIVE); + } +fail: + ipadm_close(iph); + if (status != IPADM_SUCCESS) + fatal("operation failed: %s", ipadm_status2str(status)); + return (B_TRUE); +} + +/* * gldv3_warning() catches the case of /sbin/ndd abuse to administer * ethernet/MII props. Note that /sbin/ndd has not been abused * for administration of other datalink types, which makes it permissible @@ -98,10 +349,9 @@ gldv3_warning(char *module) int main(int argc, char **argv) { - char *cp, *value; + char *cp, *value, *mod; int cmd; - int fd; - + int fd = 0; if (!(cp = *++argv)) { while ((fd = open_device()) != -1) { @@ -120,14 +370,23 @@ main(int argc, char **argv) if (!(cp = *++argv)) fatal(usage_str); } + gldv3_warning(cp); - if ((fd = open(cp, O_RDWR)) == -1) - fatal("open of %s failed: %s", cp, errmsg(errno)); + mod = strrchr(cp, '/'); + if (mod != NULL) + mod++; + else + mod = cp; - if (!isastream(fd)) - fatal("%s is not a streams device", cp); + if (ndd_str2proto(mod) == MOD_PROTO_NONE) { + if ((fd = open(cp, O_RDWR)) == -1) + fatal("open of %s failed: %s", cp, errmsg(errno)); + if (!isastream(fd)) + fatal("%s is not a streams device", cp); + } + (void) strlcpy(modpath, cp, sizeof (modpath)); if (!(cp = *++argv)) { getset_interactive(fd); (void) close(fd); @@ -199,6 +458,13 @@ do_getset(int fd, int cmd, char *buf, int buf_len) if (is_obsolete(buf)) return (B_TRUE); + /* + * See if libipadm can handle this request, i.e., properties on + * following modules arp, ip, ipv4, ipv6, tcp, udp and sctp + */ + if (do_ipadm_getset(cmd, buf, buf_len)) + return (B_TRUE); + stri.ic_cmd = cmd; stri.ic_timout = 0; stri.ic_len = buf_len; @@ -293,31 +559,40 @@ printe(boolean_t print_errno, char *fmt, ...) (void) printf("\n"); } - static int open_device() { - char name[80]; int fd, len; + char *mod; for (;;) { - len = get_value("module to query ? ", name, sizeof (name)); + len = get_value("module to query ? ", modpath, + sizeof (modpath)); if (len <= 1 || - (len == 2 && (name[0] == 'q' || name[0] == 'Q'))) + (len == 2 && (modpath[0] == 'q' || modpath[0] == 'Q'))) return (-1); - if ((fd = open(name, O_RDWR)) == -1) { - printe(B_TRUE, "open of %s failed", name); - continue; + mod = strrchr(modpath, '/'); + if (mod != NULL) + mod++; + else + mod = modpath; + if (ndd_str2proto(mod) == MOD_PROTO_NONE) { + if ((fd = open(modpath, O_RDWR)) == -1) { + printe(B_TRUE, "open of %s failed", modpath); + continue; + } + } else { + return (0); } - gldv3_warning(name); + gldv3_warning(modpath); if (isastream(fd)) return (fd); (void) close(fd); - printe(B_FALSE, "%s is not a streams device", name); + printe(B_FALSE, "%s is not a streams device", modpath); } } diff --git a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/svc-forwarding b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/svc-forwarding index db3ead4bf7..48f77f98e1 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/svc-forwarding +++ b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/svc-forwarding @@ -20,31 +20,15 @@ # CDDL HEADER END # # -# Copyright 2007 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" # This script is the shared method script for the ipv4-routing, ipv6-routing, # ipv4-forwarding and ipv6-forwarding services. . /lib/svc/share/smf_include.sh -set_forwarding_flag() { - proto="$1" - value="$2" - ndd_flag="0" - if [ "$value" = "enabled" ]; then - ndd_flag="1" - fi - if [ "$proto" = "ipv4" ]; then - /usr/sbin/ndd -set /dev/ip ip_forwarding $ndd_flag - else - /usr/sbin/ndd -set /dev/ip ip6_forwarding $ndd_flag - /usr/sbin/ndd -set /dev/ip ip6_send_redirects $ndd_flag - fi -} - usage() { echo "Usage: $0 { start | stop | refresh } { ipv4 | ipv6 }" } @@ -65,18 +49,19 @@ case "$1" in # # Start ip forwarding. # - if [ -z "$proto" ]; then - usage - exit $SMF_ERROR_FATAL - fi if [ "$proto" = "ipv6" -a "$numv6ifs" = 0 ]; then echo "Error: no IPv6 interface configured" exit $SMF_EXIT_ERR_CONFIG fi - set_forwarding_flag $proto enabled + + /usr/sbin/ipadm set-prop -p forwarding=on $proto + [ "$proto" = "ipv6" ] && /usr/sbin/ipadm set-prop \ + -p ip6_send_redirects=1 ip ;; 'stop') - set_forwarding_flag $proto disabled + /usr/sbin/ipadm set-prop -p forwarding=off $proto + [ "$proto" = "ipv6" ] && /usr/sbin/ipadm set-prop \ + -p ip6_send_redirects=0 ip ;; *) usage diff --git a/usr/src/cmd/rcm_daemon/Makefile.com b/usr/src/cmd/rcm_daemon/Makefile.com index d5a7c72ff7..f053428473 100644 --- a/usr/src/cmd/rcm_daemon/Makefile.com +++ b/usr/src/cmd/rcm_daemon/Makefile.com @@ -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. # @@ -129,7 +129,7 @@ SUNW_network_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_vlan_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_vnic_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm SUNW_aggr_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm -SUNW_ip_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil -ldladm -lipmp +SUNW_ip_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil -ldladm -lipmp -lipadm SUNW_ip_anon_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -linetutil SUNW_bridge_rcm.so := LDLIBS_MODULES += -L$(ROOT)/lib -ldladm diff --git a/usr/src/cmd/rcm_daemon/common/ip_rcm.c b/usr/src/cmd/rcm_daemon/common/ip_rcm.c index 84c195f349..2ab9959372 100644 --- a/usr/src/cmd/rcm_daemon/common/ip_rcm.c +++ b/usr/src/cmd/rcm_daemon/common/ip_rcm.c @@ -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. */ @@ -53,6 +53,7 @@ #include <libdllink.h> #include <libgen.h> #include <ipmp_admin.h> +#include <libipadm.h> #include "rcm_module.h" @@ -157,6 +158,7 @@ static mutex_t cache_lock; static int events_registered = 0; static dladm_handle_t dld_handle = NULL; +static ipadm_handle_t ip_handle = NULL; /* * RCM module interface prototypes @@ -186,7 +188,7 @@ static ip_cache_t *cache_lookup(rcm_handle_t *, char *, char); static void free_node(ip_cache_t *); static void cache_insert(ip_cache_t *); static char *ip_usage(ip_cache_t *); -static int update_pif(rcm_handle_t *, int, int, struct lifreq *); +static int update_pif(rcm_handle_t *, int, int, struct ifaddrs *); static int ip_ipmp_offline(ip_cache_t *); static int ip_ipmp_undo_offline(ip_cache_t *); static int if_cfginfo(ip_cache_t *, uint_t); @@ -209,7 +211,9 @@ static void ip_consumer_notify(rcm_handle_t *, datalink_id_t, char **, uint_t, rcm_info_t **); static boolean_t ip_addrstr(ip_lif_t *, char *, size_t); -static int if_configure(datalink_id_t); +static int if_configure_hostname(datalink_id_t); +static int if_configure_ipadm(datalink_id_t); +static boolean_t if_hostname_exists(char *, sa_family_t); static boolean_t isgrouped(const char *); static int if_config_inst(const char *, FILE *, int, boolean_t); static uint_t ntok(const char *cp); @@ -240,6 +244,7 @@ rcm_mod_init(void) { char errmsg[DLADM_STRSIZE]; dladm_status_t status; + ipadm_status_t iph_status; rcm_log_message(RCM_TRACE1, "IP: mod_init\n"); @@ -256,6 +261,15 @@ rcm_mod_init(void) return (NULL); } + if ((iph_status = ipadm_open(&ip_handle, 0)) != IPADM_SUCCESS) { + rcm_log_message(RCM_ERROR, + "IP: mod_init failed: cannot get IP handle: %s\n", + ipadm_status2str(iph_status)); + dladm_close(dld_handle); + dld_handle = NULL; + return (NULL); + } + /* Return the ops vectors */ return (&ip_ops); } @@ -283,6 +297,7 @@ rcm_mod_fini(void) (void) mutex_destroy(&cache_lock); dladm_close(dld_handle); + ipadm_close(ip_handle); return (RCM_SUCCESS); } @@ -758,7 +773,25 @@ ip_notify_event(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, return (RCM_FAILURE); } linkid = (datalink_id_t)id64; - if (if_configure(linkid) != 0) { + /* + * Grovel through /etc/hostname* files and configure + * interface in the same way that they would be handled + * by network/physical. + */ + if (if_configure_hostname(linkid) != 0) { + rcm_log_message(RCM_ERROR, + _("IP: Configuration failed (%u)\n"), + linkid); + ip_log_err(NULL, errorp, + "Failed configuring one or more IP " + "addresses"); + } + + /* + * Query libipadm for persistent configuration info + * and resurrect that persistent configuration. + */ + if (if_configure_ipadm(linkid) != 0) { rcm_log_message(RCM_ERROR, _("IP: Configuration failed (%u)\n"), linkid); @@ -1018,9 +1051,8 @@ cache_remove(ip_cache_t *node) * update_pif() - Update physical interface properties * Call with cache_lock held */ -/*ARGSUSED*/ -static int -update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) +int +update_pif(rcm_handle_t *hd, int af, int sock, struct ifaddrs *ifa) { char *rsrc; ifspec_t ifspec; @@ -1034,11 +1066,11 @@ update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) uint64_t ifflags; int lif_listed = 0; - rcm_log_message(RCM_TRACE1, "IP: update_pif(%s)\n", lifr->lifr_name); + rcm_log_message(RCM_TRACE1, "IP: update_pif(%s)\n", ifa->ifa_name); - if (!ifparse_ifspec(lifr->lifr_name, &ifspec)) { + if (!ifparse_ifspec(ifa->ifa_name, &ifspec)) { rcm_log_message(RCM_ERROR, _("IP: bad network interface: %s\n"), - lifr->lifr_name); + ifa->ifa_name); return (-1); } @@ -1048,16 +1080,7 @@ update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) ifnumber = ifspec.ifsp_lun; /* Get the interface flags */ - (void) strlcpy(lifreq.lifr_name, lifr->lifr_name, LIFNAMSIZ); - if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) { - if (errno != ENXIO) { - rcm_log_message(RCM_ERROR, - _("IP: SIOCGLIFFLAGS(%s): %s\n"), - lifreq.lifr_name, strerror(errno)); - } - return (-1); - } - (void) memcpy(&ifflags, &lifreq.lifr_flags, sizeof (ifflags)); + ifflags = ifa->ifa_flags; /* * Ignore interfaces that are always incapable of DR: @@ -1077,6 +1100,9 @@ update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) } /* Get the interface group name for this interface */ + bzero(&lifreq, sizeof (lifreq)); + (void) strncpy(lifreq.lifr_name, ifa->ifa_name, LIFNAMSIZ); + if (ioctl(sock, SIOCGLIFGROUPNAME, (char *)&lifreq) < 0) { if (errno != ENXIO) { rcm_log_message(RCM_ERROR, @@ -1091,15 +1117,7 @@ update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) sizeof (pif.pi_grname)); /* Get the interface address for this interface */ - if (ioctl(sock, SIOCGLIFADDR, (char *)&lifreq) < 0) { - if (errno != ENXIO) { - rcm_log_message(RCM_ERROR, - _("IP: SIOCGLIFADDR(%s): %s\n"), - lifreq.lifr_name, strerror(errno)); - return (-1); - } - } - (void) memcpy(&ifaddr, &lifreq.lifr_addr, sizeof (ifaddr)); + ifaddr = *(ifa->ifa_addr); rsrc = get_link_resource(pif.pi_ifname); if (rsrc == NULL) { @@ -1220,14 +1238,12 @@ update_pif(rcm_handle_t *hd, int af, int sock, struct lifreq *lifr) static int update_ipifs(rcm_handle_t *hd, int af) { - int sock; - char *buf; - struct lifnum lifn; - struct lifconf lifc; - struct lifreq *lifrp; - int i; - rcm_log_message(RCM_TRACE2, "IP: update_ipifs\n"); + struct ifaddrs *ifa; + ipadm_addr_info_t *ainfo; + ipadm_addr_info_t *ptr; + ipadm_status_t status; + int sock; if ((sock = socket(af, SOCK_DGRAM, 0)) == -1) { rcm_log_message(RCM_ERROR, @@ -1236,46 +1252,20 @@ update_ipifs(rcm_handle_t *hd, int af) return (-1); } - lifn.lifn_family = af; - lifn.lifn_flags = LIFC_UNDER_IPMP; - if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) { - rcm_log_message(RCM_ERROR, - _("IP: SIOCLGIFNUM failed: %s\n"), - strerror(errno)); - (void) close(sock); - return (-1); - } - - if ((buf = calloc(lifn.lifn_count, sizeof (struct lifreq))) == NULL) { - rcm_log_message(RCM_ERROR, _("IP: calloc: %s\n"), - strerror(errno)); - (void) close(sock); - return (-1); - } - - lifc.lifc_family = af; - lifc.lifc_flags = LIFC_UNDER_IPMP; - lifc.lifc_len = sizeof (struct lifreq) * lifn.lifn_count; - lifc.lifc_buf = buf; - - if (ioctl(sock, SIOCGLIFCONF, (char *)&lifc) < 0) { - rcm_log_message(RCM_ERROR, - _("IP: SIOCGLIFCONF failed: %s\n"), - strerror(errno)); - free(buf); + status = ipadm_addr_info(ip_handle, NULL, &ainfo, IPADM_OPT_ZEROADDR, + LIFC_UNDER_IPMP); + if (status != IPADM_SUCCESS) { (void) close(sock); return (-1); } - - /* now we need to search for active interfaces */ - lifrp = lifc.lifc_req; - for (i = 0; i < lifn.lifn_count; i++) { - (void) update_pif(hd, af, sock, lifrp); - lifrp++; + for (ptr = ainfo; ptr; ptr = IA_NEXT(ptr)) { + ifa = &ptr->ia_ifa; + if (ptr->ia_state != IFA_DISABLED && + af == ifa->ifa_addr->ss_family) + (void) update_pif(hd, af, sock, ifa); } - - free(buf); (void) close(sock); + ipadm_free_addr_info(ainfo); return (0); } @@ -2270,20 +2260,15 @@ ip_consumer_notify(rcm_handle_t *hd, datalink_id_t linkid, char **errorp, } /* - * if_configure() - Configure a physical interface after attach + * Gets the interface name for the given linkid. Returns -1 if there is + * any error. It fills in the interface name in `ifinst' if the interface + * is not already configured. Otherwise, it puts a null string in `ifinst'. */ static int -if_configure(datalink_id_t linkid) +if_configure_get_linkid(datalink_id_t linkid, char *ifinst, size_t len) { - char ifinst[MAXLINKNAMELEN]; - char cfgfile[MAXPATHLEN]; char cached_name[RCM_LINK_RESOURCE_MAX]; - FILE *hostfp, *host6fp; ip_cache_t *node; - boolean_t ipmp = B_FALSE; - - assert(linkid != DATALINK_INVALID_LINKID); - rcm_log_message(RCM_TRACE1, _("IP: if_configure(%u)\n"), linkid); /* Check for the interface in the cache */ (void) snprintf(cached_name, sizeof (cached_name), "%s/%u", @@ -2296,16 +2281,42 @@ if_configure(datalink_id_t linkid) rcm_log_message(RCM_TRACE1, _("IP: Skipping configured interface(%u)\n"), linkid); (void) mutex_unlock(&cache_lock); + *ifinst = '\0'; return (0); } (void) mutex_unlock(&cache_lock); if (dladm_datalink_id2info(dld_handle, linkid, NULL, NULL, NULL, ifinst, - sizeof (ifinst)) != DLADM_STATUS_OK) { + len) != DLADM_STATUS_OK) { rcm_log_message(RCM_ERROR, _("IP: get %u link name failed\n"), linkid); return (-1); } + return (0); +} + +/* + * if_configure_hostname() - Configure a physical interface after attach + * based on the information in /etc/hostname.* + */ +static int +if_configure_hostname(datalink_id_t linkid) +{ + FILE *hostfp, *host6fp; + boolean_t ipmp = B_FALSE; + char ifinst[MAXLINKNAMELEN]; + char cfgfile[MAXPATHLEN]; + + assert(linkid != DATALINK_INVALID_LINKID); + rcm_log_message(RCM_TRACE1, _("IP: if_configure_hostname(%u)\n"), + linkid); + + if (if_configure_get_linkid(linkid, ifinst, sizeof (ifinst)) != 0) + return (-1); + + /* Check if the interface is already configured. */ + if (ifinst[0] == '\0') + return (0); /* * Scan the IPv4 and IPv6 hostname files to see if (a) they exist @@ -2344,7 +2355,8 @@ if_configure(datalink_id_t linkid) (void) fclose(hostfp); (void) fclose(host6fp); - rcm_log_message(RCM_TRACE1, "IP: if_configure(%s) success\n", ifinst); + rcm_log_message(RCM_TRACE1, "IP: if_configure_hostname(%s) success\n", + ifinst); return (0); fail: (void) fclose(hostfp); @@ -2353,6 +2365,76 @@ fail: } /* + * if_configure_ipadm() - Configure a physical interface after attach + * Queries libipadm for persistent configuration information and then + * resurrects that persistent configuration. + */ +static int +if_configure_ipadm(datalink_id_t linkid) +{ + char ifinst[MAXLINKNAMELEN]; + boolean_t found; + ipadm_if_info_t *ifinfo, *ptr; + ipadm_status_t status; + + assert(linkid != DATALINK_INVALID_LINKID); + rcm_log_message(RCM_TRACE1, _("IP: if_configure_ipadm(%u)\n"), + linkid); + + if (if_configure_get_linkid(linkid, ifinst, sizeof (ifinst)) != 0) + return (-1); + + /* Check if the interface is already configured. */ + if (ifinst[0] == '\0') + return (0); + + status = ipadm_if_info(ip_handle, ifinst, &ifinfo, 0, LIFC_UNDER_IPMP); + if (status == IPADM_ENXIO) + goto done; + if (status != IPADM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("IP: IPv4 Post-attach failed (%s) Error %s\n"), + ifinst, ipadm_status2str(status)); + goto fail; + } + if (ifinfo != NULL) { + found = B_FALSE; + for (ptr = ifinfo; ptr; ptr = ptr->ifi_next) { + if (strncmp(ptr->ifi_name, ifinst, + sizeof (ifinst)) == 0) { + found = B_TRUE; + break; + } + } + free(ifinfo); + if (!found) { + return (0); + } + if (if_hostname_exists(ifinst, AF_INET) || + if_hostname_exists(ifinst, AF_INET6)) { + rcm_log_message(RCM_WARNING, + _("IP: IPv4 Post-attach (%s) found both " + "/etc/hostname and ipadm persistent configuration. " + "Ignoring ipadm config\n"), ifinst); + return (0); + } + status = ipadm_enable_if(ip_handle, ifinst, IPADM_OPT_ACTIVE); + if (status != IPADM_SUCCESS) { + rcm_log_message(RCM_ERROR, + _("IP: Post-attach failed (%s) Error %s\n"), + ifinst, ipadm_status2str(status)); + goto fail; + } + } +done: + rcm_log_message(RCM_TRACE1, "IP: if_configure_ipadm(%s) success\n", + ifinst); + return (0); +fail: + return (-1); +} + +/* * isgrouped() - Scans the given config file to see if this interface is * using IPMP. Returns B_TRUE or B_FALSE. */ @@ -2662,3 +2744,23 @@ ifconfig(const char *ifinst, const char *fstr, const char *buf, boolean_t stdif) } return (B_TRUE); } + +/* + * Return TRUE if a writeable /etc/hostname[6].ifname file exists. + */ +static boolean_t +if_hostname_exists(char *ifname, sa_family_t af) +{ + char cfgfile[MAXPATHLEN]; + + if (af == AF_INET) { + (void) snprintf(cfgfile, MAXPATHLEN, CFGFILE_FMT_IPV4, ifname); + if (access(cfgfile, W_OK|F_OK) == 0) + return (B_TRUE); + } else if (af == AF_INET6) { + (void) snprintf(cfgfile, MAXPATHLEN, CFGFILE_FMT_IPV6, ifname); + if (access(cfgfile, W_OK|F_OK) == 0) + return (B_TRUE); + } + return (B_FALSE); +} diff --git a/usr/src/cmd/svc/milestone/net-iptun b/usr/src/cmd/svc/milestone/net-iptun index 778f76b1f7..f912d6ee9a 100644 --- a/usr/src/cmd/svc/milestone/net-iptun +++ b/usr/src/cmd/svc/milestone/net-iptun @@ -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. # # This service configures IP tunnel links and IP interfaces over IP @@ -97,6 +97,26 @@ start) if [ -f /etc/hostname6.$intf_name ]; then plumb_tunnel $intf_name inet6 /etc/hostname6.$intf_name fi + # + # Configure IP tunnel interfaces set up using ipadm + # + state=`/sbin/ipadm show-if -p -o state $intf_name` + if [ $? -ne 0 ] || [ "$state" != "disabled" ]; then + # + # skip if not managed my ipadm or if not a persistent + # interface + # + continue; + elif [ -f /etc/hostname.$intf_name ] ||\ + [ -f /etc/hostname6.$intf_name ]; then + echo "found /etc/hostname.$intf_name or "\ + "/etc/hostname6.$intfi_name, ignoring ipadm "\ + "configuration" > /dev/msglog + continue; + else + # Enable the interface managed by ipadm + /sbin/ipadm enable-if -t $intf_name + fi done # @@ -124,6 +144,7 @@ stop) for tun in $tunnel_links; do /sbin/ifconfig $tun unplumb > /dev/null 2>&1 /sbin/ifconfig $tun inet6 unplumb > /dev/null 2>&1 + /sbin/ipadm disable-if -t $tun > /dev/null 2>&1 done # Take down the IP tunnel links diff --git a/usr/src/cmd/svc/milestone/net-loopback b/usr/src/cmd/svc/milestone/net-loopback index d07afd4ada..bd52bbc719 100644 --- a/usr/src/cmd/svc/milestone/net-loopback +++ b/usr/src/cmd/svc/milestone/net-loopback @@ -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. # @@ -34,26 +34,40 @@ # smf_configure_ip || exit $SMF_EXIT_OK -# -# Before any interfaces are configured, we need to set the system -# default IP forwarding behavior. This will be the setting for -# interfaces that don't modify the per-interface setting with the -# router or -router ifconfig command in their /etc/hostname.<intf> -# files. Due to their dependency on this service, the IP forwarding services -# will run at this point (though routing daemons will not run until later -# in the boot process) and set forwarding flags. -# +if [ -f /etc/hostname.lo0 ] || [ -f /etc/hostname6.lo0 ]; then + echo "found /etc/hostname.lo0 or /etc/hostname6.lo0; "\ + "using ifconfig to create lo0" > /dev/msglog + # IPv4 loopback + /sbin/ifconfig lo0 plumb 127.0.0.1 up + + # IPv6 loopback + /sbin/ifconfig lo0 inet6 plumb ::1 up -# IPv4 loopback -/sbin/ifconfig lo0 plumb 127.0.0.1 up + # Trusted Extensions shares the loopback interface with all zones + if (smf_is_system_labeled); then + if smf_is_globalzone; then + /sbin/ifconfig lo0 all-zones + /sbin/ifconfig lo0 inet6 all-zones + fi + fi +else + state=`/sbin/ipadm show-if -p -o state lo0 2>/dev/null` + if [ $? -eq 0 -a "$state" = "disabled" ]; then + /sbin/ipadm enable-if -t lo0 + else + # IPv4 loopback + /sbin/ipadm create-addr -t -T static -a 127.0.0.1/8 lo0/v4 -# IPv6 loopback -/sbin/ifconfig lo0 inet6 plumb ::1 up + # IPv6 loopback + /sbin/ipadm create-addr -t -T static -a ::1/128 lo0/v6 + fi -# Trusted Extensions shares the loopback interface with all zones -if (smf_is_system_labeled); then - if smf_is_globalzone; then - /sbin/ifconfig lo0 all-zones - /sbin/ifconfig lo0 inet6 all-zones + # Trusted Extensions shares the loopback interface with all zones + if (smf_is_system_labeled); then + if smf_is_globalzone; then + /sbin/ipadm set-addrprop -t -p zone=all-zones lo0/v4 + /sbin/ipadm set-addrprop -t -p zone=all-zones lo0/v6 + fi fi fi + diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical index 63e4264204..2a5b7d7515 100644 --- a/usr/src/cmd/svc/milestone/net-physical +++ b/usr/src/cmd/svc/milestone/net-physical @@ -38,6 +38,7 @@ # smf_configure_ip || exit $SMF_EXIT_OK + # Make sure that the libraries essential to this stage of booting can be found. LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH @@ -138,6 +139,8 @@ is_iptun () # # +# First deal with /etc/hostname +# # Get the list of IPv4 interfaces to configure by breaking # /etc/hostname.* into separate args by using "." as a shell separator # character. @@ -289,6 +292,28 @@ if [ -n "$ipmp6_list" ]; then fi # +# Finally configure interfaces set up with ipadm. +# +for showif_output in `/sbin/ipadm show-if -p -o ifname,state`; do + intf=`echo $showif_output | /usr/bin/cut -f1 -d:` + state=`echo $showif_output | /usr/bin/cut -f2 -d:` + if [ "$state" != "disabled" ]; then + # skip if not a persistent interface + continue; + elif is_iptun $intf; then + # skip IP tunnel interfaces plumbed by net-iptun + continue; + elif [ -f /etc/hostname.$intf ] || [ -f /etc/hostname6.$intf ]; then + echo "found /etc/hostname.$intf or /etc/hostname6.$intf, "\ + "ignoring ipadm configuration" > /dev/msglog + continue; + fi + + # Enable the interface managed by ipadm + /sbin/ipadm enable-if -t $intf +done + +# # Process the /etc/hostname[6].* files for IPMP interfaces. Processing these # before non-IPMP interfaces avoids accidental implicit IPMP group creation. # diff --git a/usr/src/cmd/svc/seed/Makefile b/usr/src/cmd/svc/seed/Makefile index 7e69e27a54..1fffd9d283 100644 --- a/usr/src/cmd/svc/seed/Makefile +++ b/usr/src/cmd/svc/seed/Makefile @@ -67,7 +67,8 @@ COMMON_DESCRIPTIONS = \ ../milestone/single-user.xml \ ../milestone/usr-fs.xml \ ../../dlmgmtd/dlmgmt.xml \ - ../../rpcbind/bind.xml \ + ../../cmd-inet/lib/ipmgmtd/network-ipmgmt.xml \ + ../../rpcbind/bind.xml # # Additional manifests for standalone Solaris diff --git a/usr/src/cmd/truss/codes.c b/usr/src/cmd/truss/codes.c index 0bec448ea6..03f7b40ab0 100644 --- a/usr/src/cmd/truss/codes.c +++ b/usr/src/cmd/truss/codes.c @@ -879,6 +879,8 @@ const struct ioc { { (uint_t)SIOCSMSFILTER, "SIOCSMSFILTER", "group_filter" }, { (uint_t)SIOCGIPMSFILTER, "SIOCGIPMSFILTER", "ip_msfilter" }, { (uint_t)SIOCSIPMSFILTER, "SIOCSIPMSFILTER", "ip_msfilter" }, + { (uint_t)SIOCGLIFDADSTATE, "SIOCGLIFDADSTATE", "lifreq" }, + { (uint_t)SIOCSLIFPREFIX, "SIOCSLIFPREFIX", "lifreq" }, /* DES encryption */ { (uint_t)DESIOCBLOCK, "DESIOCBLOCK", "desparams" }, diff --git a/usr/src/head/Makefile b/usr/src/head/Makefile index c2e0c2f0e8..2a51b192ed 100644 --- a/usr/src/head/Makefile +++ b/usr/src/head/Makefile @@ -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. # # head/Makefile @@ -79,6 +79,7 @@ HDRS= $($(MACH)_HDRS) $(ATTRDB_HDRS) \ grp.h \ iconv.h \ ieeefp.h \ + ifaddrs.h \ inttypes.h \ iso646.h \ klpd.h \ diff --git a/usr/src/head/ifaddrs.h b/usr/src/head/ifaddrs.h new file mode 100644 index 0000000000..f29aee16e9 --- /dev/null +++ b/usr/src/head/ifaddrs.h @@ -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. + */ +#ifndef _IFADDRS_H +#define _IFADDRS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/* + * The `getifaddrs' function generates a linked list of these structures. + * Each element of the list describes one network interface. + */ +#if defined(_INT64_TYPE) +struct ifaddrs { + struct ifaddrs *ifa_next; /* Pointer to the next structure. */ + char *ifa_name; /* Name of this network interface. */ + uint64_t ifa_flags; /* Flags as from SIOCGLIFFLAGS ioctl. */ + struct sockaddr_storage *ifa_addr; + /* Network address of this interface. */ + struct sockaddr_storage *ifa_netmask; + /* Netmask of this interface. */ + union { + /* + * At most one of the following two is valid. If the + * IFF_BROADCAST bit is set in `ifa_flags', then + * `ifa_broadaddr' is valid. If the IFF_POINTOPOINT bit is + * set, then `ifa_dstaddr' is valid. It is never the case that + * both these bits are set at once. + */ + struct sockaddr_storage *ifu_broadaddr; + struct sockaddr_storage *ifu_dstaddr; + } ifa_ifu; + void *ifa_data; /* Address-specific data (may be unused). */ +/* + * This may have been defined in <net/if.h>. + */ +#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 +}; +#endif + +/* + * Create a linked list of `struct ifaddrs' structures, one for each + * network interface on the host machine. 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'. + */ +extern int getifaddrs(struct ifaddrs **); + +/* Reclaim the storage allocated by a previous `getifaddrs' call. */ +extern void freeifaddrs(struct ifaddrs *); + + +#ifdef __cplusplus +} +#endif + +#endif /* _IFADDRS_H */ 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, ¶ms, + 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/uts/common/inet/sctp/sctp.conf b/usr/src/lib/libipadm/i386/Makefile index 7755efa251..4d9d4abd5b 100644 --- a/usr/src/uts/common/inet/sctp/sctp.conf +++ b/usr/src/lib/libipadm/i386/Makefile @@ -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. @@ -19,10 +18,11 @@ # # 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. # -#ident ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com -name="sctp" parent="pseudo" instance=0; +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/uts/common/inet/sctp/sctp6.conf b/usr/src/lib/libipadm/sparc/Makefile index 75151c0c63..8069eadad1 100644 --- a/usr/src/uts/common/inet/sctp/sctp6.conf +++ b/usr/src/lib/libipadm/sparc/Makefile @@ -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,9 +19,11 @@ # 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. # -#ident ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com -name="sctp6" parent="pseudo" instance=0; +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/uts/common/inet/sctp/sctp6ddi.c b/usr/src/lib/libsocket/common/libsocket_priv.h index db262a10ed..8d84ffb5da 100644 --- a/usr/src/uts/common/inet/sctp/sctp6ddi.c +++ b/usr/src/lib/libsocket/common/libsocket_priv.h @@ -19,44 +19,25 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +#ifndef _LIBSOCKET_PRIV_H +#define _LIBSOCKET_PRIV_H -#pragma ident "%Z%%M% %I% %E% SMI" +#include <net/if.h> +#include <ifaddrs.h> -#include <sys/types.h> -#include <sys/conf.h> -#include <sys/modctl.h> -#include <inet/common.h> -#include <inet/ip.h> +#ifdef __cplusplus +extern "C" { +#endif -#define INET_NAME "sctp6" -#define INET_DEVDESC "SCTP6 device" -#define INET_DEVSTRTAB sctpinfo -#define INET_DEVMINOR 0 -#define INET_DEVMTFLAGS D_MP +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 *); -#include "../inetddi.c" - -int -_init(void) -{ - /* - * device initialization happens when the actual code containing - * module (/kernel/drv/ip) is loaded, and driven from ip_ddi_init() - */ - return (mod_install(&modlinkage)); -} - -int -_fini(void) -{ - return (mod_remove(&modlinkage)); +#ifdef __cplusplus } +#endif -int -_info(struct modinfo *modinfop) -{ - return (mod_info(&modlinkage, modinfop)); -} +#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); +} diff --git a/usr/src/pkg/manifests/SUNWcs.mf b/usr/src/pkg/manifests/SUNWcs.mf index af8d0d9d59..5ccb4a0b87 100644 --- a/usr/src/pkg/manifests/SUNWcs.mf +++ b/usr/src/pkg/manifests/SUNWcs.mf @@ -486,6 +486,7 @@ file path=etc/user_attr.d/SUNWcs group=sys file path=etc/vfstab group=sys preserve=true file path=lib/crypto/kcfd mode=0555 file path=lib/inet/in.mpathd mode=0555 +file path=lib/inet/ipmgmtd mode=0555 file path=lib/inet/netcfgd mode=0555 file path=lib/inet/nwamd mode=0555 file path=lib/svc/bin/lsvcrun group=sys mode=0555 @@ -512,6 +513,7 @@ file path=lib/svc/method/ldap-client mode=0555 file path=lib/svc/method/manifest-import mode=0555 file path=lib/svc/method/mpxio-upgrade mode=0555 file path=lib/svc/method/net-init mode=0555 +file path=lib/svc/method/net-ipmgmt mode=0555 file path=lib/svc/method/net-ipqos mode=0555 file path=lib/svc/method/net-iptun mode=0555 file path=lib/svc/method/net-loc mode=0555 @@ -968,6 +970,7 @@ file path=usr/lib/help/auths/locale/C/NetworkAutoconfWrite.html file path=usr/lib/help/auths/locale/C/NetworkHeader.html file path=usr/lib/help/auths/locale/C/NetworkILBconf.html file path=usr/lib/help/auths/locale/C/NetworkILBenable.html +file path=usr/lib/help/auths/locale/C/NetworkInterfaceConfig.html file path=usr/lib/help/auths/locale/C/NetworkVRRP.html file path=usr/lib/help/auths/locale/C/PriAdmin.html file path=usr/lib/help/auths/locale/C/ProfmgrHeader.html @@ -1930,6 +1933,7 @@ file path=lib/svc/manifest/network/ipsec/manual-key.xml group=sys mode=0444 file path=lib/svc/manifest/network/ipsec/policy.xml group=sys mode=0444 file path=lib/svc/manifest/network/ldap/client.xml group=sys mode=0444 file path=lib/svc/manifest/network/network-initial.xml group=sys mode=0444 +file path=lib/svc/manifest/network/network-ipmgmt.xml group=sys mode=0444 file path=lib/svc/manifest/network/network-ipqos.xml group=sys mode=0444 file path=lib/svc/manifest/network/network-iptun.xml group=sys mode=0444 file path=lib/svc/manifest/network/network-location.xml group=sys mode=0444 @@ -2689,6 +2693,7 @@ link path=usr/sbin/ifconfig target=../../sbin/ifconfig link path=usr/sbin/inetd target=../lib/inet/inetd link path=usr/sbin/init target=../../sbin/init $(i386_ONLY)link path=usr/sbin/installgrub target=../../sbin/installgrub +link path=usr/sbin/ipadm target=../../sbin/ipadm link path=usr/sbin/ipmpstat target=../../sbin/ipmpstat link path=usr/sbin/labelit target=./clri link path=usr/sbin/lockfs target=../lib/fs/ufs/lockfs diff --git a/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf b/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf index fc19087040..de6c33c804 100644 --- a/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf +++ b/usr/src/pkg/manifests/consolidation-osnet-osnet-message-files.mf @@ -137,6 +137,7 @@ file path=usr/lib/help/auths/locale/NetworkAutoconfWrite.html file path=usr/lib/help/auths/locale/NetworkHeader.html file path=usr/lib/help/auths/locale/NetworkILBconf.html file path=usr/lib/help/auths/locale/NetworkILBenable.html +file path=usr/lib/help/auths/locale/NetworkInterfaceConfig.html file path=usr/lib/help/auths/locale/NetworkVRRP.html file path=usr/lib/help/auths/locale/PriAdmin.html file path=usr/lib/help/auths/locale/PrintAdmin.html diff --git a/usr/src/pkg/manifests/system-header.mf b/usr/src/pkg/manifests/system-header.mf index b95dd46026..915d16adc2 100644 --- a/usr/src/pkg/manifests/system-header.mf +++ b/usr/src/pkg/manifests/system-header.mf @@ -383,6 +383,7 @@ $(i386_ONLY)file path=usr/include/ia32/sys/traptrace.h file path=usr/include/iconv.h file path=usr/include/idmap.h file path=usr/include/ieeefp.h +file path=usr/include/ifaddrs.h file path=usr/include/inet/arp.h file path=usr/include/inet/common.h file path=usr/include/inet/ip.h @@ -412,6 +413,7 @@ file path=usr/include/inet/tcp.h file path=usr/include/inet/tcp_sack.h file path=usr/include/inet/tcp_stack.h file path=usr/include/inet/tcp_stats.h +file path=usr/include/inet/tunables.h file path=usr/include/inet/wifi_ioctl.h file path=usr/include/inttypes.h file path=usr/include/ipmp.h diff --git a/usr/src/pkg/manifests/system-kernel.mf b/usr/src/pkg/manifests/system-kernel.mf index a1152c7d29..5d4a719b58 100644 --- a/usr/src/pkg/manifests/system-kernel.mf +++ b/usr/src/pkg/manifests/system-kernel.mf @@ -214,8 +214,6 @@ driver name=rts perms="rts 0666 root sys" driver name=sad perms="admin 0666 root sys" perms="user 0666 root sys" driver name=scsi_vhci class=scsi-self-identifying perms="* 0666 root sys" \ policy="devctl write_priv_set=sys_devices" -driver name=sctp perms="* 0666 root sys" -driver name=sctp6 perms="* 0666 root sys" $(sparc_ONLY)driver name=sd perms="* 0640 root sys" \ alias=ide-cdrom \ alias=scsiclass,00 \ @@ -365,8 +363,6 @@ file path=kernel/drv/$(ARCH64)/random group=sys file path=kernel/drv/$(ARCH64)/rts group=sys file path=kernel/drv/$(ARCH64)/sad group=sys file path=kernel/drv/$(ARCH64)/scsi_vhci group=sys -file path=kernel/drv/$(ARCH64)/sctp group=sys -file path=kernel/drv/$(ARCH64)/sctp6 group=sys file path=kernel/drv/$(ARCH64)/sd group=sys file path=kernel/drv/$(ARCH64)/sgen group=sys file path=kernel/drv/$(ARCH64)/simnet group=sys @@ -507,10 +503,6 @@ $(i386_ONLY)file path=kernel/drv/scsi_vhci group=sys file path=kernel/drv/scsi_vhci.conf group=sys \ original_name=SUNWckr:kernel/drv/scsi_vhci.conf preserve=true \ reboot-needed=false -$(i386_ONLY)file path=kernel/drv/sctp group=sys -file path=kernel/drv/sctp.conf group=sys reboot-needed=false -$(i386_ONLY)file path=kernel/drv/sctp6 group=sys -file path=kernel/drv/sctp6.conf group=sys reboot-needed=false $(sparc_ONLY)file path=kernel/drv/sd.conf group=sys \ original_name=SUNWckr:kernel/drv/sd.conf preserve=true reboot-needed=false $(i386_ONLY)file path=kernel/drv/sgen group=sys diff --git a/usr/src/pkg/manifests/system-library.mf b/usr/src/pkg/manifests/system-library.mf index 6823b4149e..4bf5e75395 100644 --- a/usr/src/pkg/manifests/system-library.mf +++ b/usr/src/pkg/manifests/system-library.mf @@ -181,6 +181,7 @@ file path=lib/libgen.so.1 file path=lib/libinetcfg.so.1 file path=lib/libinetutil.so.1 file path=lib/libintl.so.1 +file path=lib/libipadm.so.1 file path=lib/libipmp.so.1 file path=lib/libkcfd.so.1 file path=lib/libkmf.so.1 diff --git a/usr/src/pkg/manifests/system-network.mf b/usr/src/pkg/manifests/system-network.mf index 14e3840ade..fe17448d86 100644 --- a/usr/src/pkg/manifests/system-network.mf +++ b/usr/src/pkg/manifests/system-network.mf @@ -40,6 +40,7 @@ dir path=etc/inet/ike/crls group=sys dir path=etc/inet/ike/publickeys group=sys dir path=etc/inet/secret group=sys mode=0700 dir path=etc/inet/secret/ike.privatekeys group=sys mode=0700 +dir path=etc/ipadm group=netadm owner=netadm dir path=etc/nwam group=netadm owner=netadm dir path=etc/nwam/loc group=netadm owner=netadm dir path=etc/nwam/loc/NoNet group=netadm owner=netadm @@ -70,6 +71,7 @@ file path=etc/inet/secret/ike.preshared group=sys mode=0600 \ file path=etc/inet/secret/ipseckeys.sample group=sys mode=0600 file path=etc/inet/sock2path group=sys \ original_name=SUNWcnetr:etc/inet/sock2path preserve=true +file path=etc/ipadm/ipadm.conf group=netadm owner=netadm preserve=true file path=etc/nwam/loc/NoNet/ipf.conf.dfl group=netadm owner=netadm \ preserve=true file path=etc/nwam/loc/NoNet/ipf6.conf.dfl group=netadm owner=netadm \ @@ -81,6 +83,7 @@ file path=sbin/dlstat mode=0555 file path=sbin/flowadm mode=0555 file path=sbin/flowstat mode=0555 group groupname=netadm gid=65 +file path=sbin/ipadm mode=0555 legacy pkg=SUNWcnetr arch=$(ARCH) category=system \ desc="core software for network infrastructure configuration" \ hotline="Please contact your local service provider" \ diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh index 0ea2212622..657b063139 100644 --- a/usr/src/tools/scripts/bfu.sh +++ b/usr/src/tools/scripts/bfu.sh @@ -93,6 +93,7 @@ all_zones_files=" etc/inet/* etc/init.d/* etc/inittab + etc/ipadm/ipadm.conf etc/ipf/ipf.conf etc/iu.ap etc/krb5/kadm5.acl @@ -2451,6 +2452,12 @@ EOFA fi # + # Import the ip-interface-management service. + # + smf_import_service network/network-ipmgmt.xml \ + svc:/network/ip-interface-management:default + + # # Import the ldap/client service. This is to get the service # (with correct dependencies) in the repository before reboot. # @@ -3145,6 +3152,7 @@ bfucmd=" /usr/sbin/chroot /usr/sbin/fstyp /usr/sbin/halt + /usr/sbin/ifconfig /usr/sbin/lockfs /usr/sbin/lofiadm /usr/sbin/logadm @@ -3152,6 +3160,7 @@ bfucmd=" /usr/sbin/mkfs /usr/sbin/mknod /usr/sbin/mount + /usr/sbin/ndd /usr/sbin/newfs /usr/sbin/pkgrm /usr/sbin/prtconf @@ -7963,6 +7972,16 @@ mondo_loop() { rm -f $root/kernel/drv/mscsi.conf rm -f $root/platform/i86pc/kernel/drv/amd64/mscsi + # Remove sctp, sctp6 + rm -f $root/kernel/drv/sctp + rm -f $root/kernel/drv/sctp.conf + rm -f $root/kernel/drv/sctp6 + rm -f $root/kernel/drv/sctp6.conf + rm -f $root/kernel/drv/amd64/sctp + rm -f $root/kernel/drv/amd64/sctp6 + rm -f $root/kernel/drv/sparcv9/sctp + rm -f $root/kernel/drv/sparcv9/sctp6 + # Remove obsolete pfil modules, binaries, and configuration files rm -f $root/kernel/drv/pfil rm -f $root/kernel/drv/pfil.conf diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 43bee44792..40d04be785 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -529,24 +529,25 @@ IP_ICMP_OBJS = icmp.o icmp_opt_data.o IP_RTS_OBJS = rts.o rts_opt_data.o IP_TCP_OBJS = tcp.o tcp_fusion.o tcp_kssl.o tcp_opt_data.o tcp_sack.o \ tcp_stats.o tcp_misc.o tcp_timers.o tcp_time_wait.o tcp_tpi.o \ - tcp_output.o tcp_input.o tcp_socket.o tcp_bind.o tcp_cluster.o -IP_UDP_OBJS = udp.o udp_opt_data.o + tcp_output.o tcp_input.o tcp_socket.o tcp_bind.o tcp_cluster.o \ + tcp_tunables.o +IP_UDP_OBJS = udp.o udp_opt_data.o udp_tunables.o IP_SCTP_OBJS = sctp.o sctp_opt_data.o sctp_output.o \ sctp_init.o sctp_input.o sctp_cookie.o \ sctp_conn.o sctp_error.o sctp_snmp.o \ - sctp_param.o sctp_shutdown.o sctp_common.o \ + sctp_tunables.o sctp_shutdown.o sctp_common.o \ sctp_timer.o sctp_heartbeat.o sctp_hash.o \ - sctp_ioc.o sctp_bind.o sctp_notify.o sctp_asconf.o \ + sctp_bind.o sctp_notify.o sctp_asconf.o \ sctp_addr.o tn_ipopt.o tnet.o ip_netinfo.o IP_ILB_OBJS = ilb.o ilb_nat.o ilb_conn.o ilb_alg_hash.o ilb_alg_rr.o IP_OBJS += igmp.o ipmp.o ip.o ip6.o ip6_asp.o ip6_if.o ip6_ire.o \ ip6_rts.o ip_if.o ip_ire.o ip_listutils.o ip_mroute.o \ ip_multi.o ip2mac.o ip_ndp.o ip_rts.o ip_srcid.o \ - ipddi.o ipdrop.o mi.o nd.o optcom.o snmpcom.o ipsec_loader.o \ - spd.o ipclassifier.o inet_common.o ip_squeue.o squeue.o \ - ip_sadb.o ip_ftable.o proto_set.o radix.o ip_dummy.o \ - ip_helper_stream.o \ + ipddi.o ipdrop.o mi.o nd.o tunables.o optcom.o snmpcom.o \ + ipsec_loader.o spd.o ipclassifier.o inet_common.o ip_squeue.o \ + squeue.o ip_sadb.o ip_ftable.o proto_set.o radix.o ip_dummy.o \ + ip_helper_stream.o ip_tunables.o \ ip_output.o ip_input.o ip6_input.o ip6_output.o ip_arp.o \ conn_opt.o ip_attr.o ip_dce.o \ $(IP_ICMP_OBJS) \ @@ -585,10 +586,6 @@ TCP_OBJS += tcpddi.o TCP6_OBJS += tcp6ddi.o -SCTP_OBJS += sctpddi.o - -SCTP6_OBJS += sctp6ddi.o - NCA_OBJS += ncaddi.o SDP_SOCK_MOD_OBJS += sockmod_sdp.o socksdp.o socksdpsubr.o diff --git a/usr/src/uts/common/inet/Makefile b/usr/src/uts/common/inet/Makefile index a5da360b01..14ce78a884 100644 --- a/usr/src/uts/common/inet/Makefile +++ b/usr/src/uts/common/inet/Makefile @@ -32,7 +32,7 @@ HDRS= arp.h common.h ipclassifier.h ip.h ip6.h ipdrop.h ipnet.h \ ipsecah.h ipsecesp.h ipsec_info.h iptun.h ip6_asp.h ip_if.h ip_ire.h \ ip_multi.h ip_netinfo.h ip_ndp.h ip_rts.h ipsec_impl.h keysock.h \ led.h mi.h mib2.h nd.h optcom.h sadb.h sctp_itf.h snmpcom.h tcp.h \ - tcp_sack.h tcp_stack.h udp_impl.h rawip_impl.h ipp_common.h \ + tcp_sack.h tcp_stack.h tunables.h udp_impl.h rawip_impl.h ipp_common.h \ ip_ftable.h ip_impl.h ip_stack.h ip_arp.h tcp_impl.h wifi_ioctl.h \ ip2mac.h ip2mac_impl.h tcp_stats.h diff --git a/usr/src/uts/common/inet/ip.h b/usr/src/uts/common/inet/ip.h index ba57cf4406..b8bd229b5a 100644 --- a/usr/src/uts/common/inet/ip.h +++ b/usr/src/uts/common/inet/ip.h @@ -174,11 +174,11 @@ typedef struct ipoptp_s #define IPOPTP_ERROR 0x00000001 #endif /* _KERNEL */ -/* Controls forwarding of IP packets, set via ndd */ +/* Controls forwarding of IP packets, set via ipadm(1M)/ndd(1M) */ #define IP_FORWARD_NEVER 0 #define IP_FORWARD_ALWAYS 1 -#define WE_ARE_FORWARDING(ipst) ((ipst)->ips_ip_g_forward == IP_FORWARD_ALWAYS) +#define WE_ARE_FORWARDING(ipst) ((ipst)->ips_ip_forwarding == IP_FORWARD_ALWAYS) #define IPH_HDR_LENGTH(ipha) \ ((int)(((ipha_t *)ipha)->ipha_version_and_hdr_length & 0xF) << 2) @@ -945,7 +945,6 @@ typedef struct ipif_s { in6_addr_t ipif_v6brd_addr; /* Broadcast addr for this interface. */ in6_addr_t ipif_v6pp_dst_addr; /* Point-to-point dest address. */ uint64_t ipif_flags; /* Interface flags. */ - uint_t ipif_metric; /* BSD if metric, for compatibility. */ uint_t ipif_ire_type; /* IRE_LOCAL or IRE_LOOPBACK */ /* @@ -1018,7 +1017,6 @@ typedef struct ipif_s { * ipif_v6brd_addr * ipif_v6pp_dst_addr * ipif_flags ill_lock ill_lock - * ipif_metric * ipif_ire_type ipsq + down ill up ill * * ipif_ib_pkt_count Approx @@ -1608,10 +1606,10 @@ typedef struct ill_s { uint_t ill_max_frag; /* Max IDU from DLPI. */ uint_t ill_current_frag; /* Current IDU from DLPI. */ uint_t ill_mtu; /* User-specified MTU; SIOCSLIFMTU */ + uint_t ill_metric; /* BSD if metric, for compatibility. */ char *ill_name; /* Our name. */ uint_t ill_ipif_dup_count; /* Number of duplicate addresses. */ uint_t ill_name_length; /* Name length, incl. terminator. */ - char *ill_ndd_name; /* Name + ":ip?_forwarding" for NDD. */ uint_t ill_net_type; /* IRE_IF_RESOLVER/IRE_IF_NORESOLVER. */ /* * Physical Point of Attachment num. If DLPI style 1 provider @@ -1691,7 +1689,11 @@ typedef struct ill_s { ill_fragtimer_executing : 1, ill_fragtimer_needrestart : 1, ill_manual_token : 1, /* system won't override ill_token */ - ill_manual_linklocal : 1, /* system won't auto-conf linklocal */ + /* + * ill_manual_linklocal : system will not change the + * linklocal whenever ill_token changes. + */ + ill_manual_linklocal : 1, ill_manual_dst_linklocal : 1, /* same for pt-pt dst linklocal */ @@ -2025,14 +2027,6 @@ enum { IDCS_FAILED }; -/* Named Dispatch Parameter Management Structure */ -typedef struct ipparam_s { - uint_t ip_param_min; - uint_t ip_param_max; - uint_t ip_param_value; - char *ip_param_name; -} ipparam_t; - /* Extended NDP Management Structure */ typedef struct ipndp_s { ndgetf_t ip_ndp_getf; @@ -2932,88 +2926,94 @@ extern vmem_t *ip_minor_arena_la; * /etc/rc2.d/S69inet script. */ -#define ips_ip_respond_to_address_mask_broadcast ips_param_arr[0].ip_param_value -#define ips_ip_g_resp_to_echo_bcast ips_param_arr[1].ip_param_value -#define ips_ip_g_resp_to_echo_mcast ips_param_arr[2].ip_param_value -#define ips_ip_g_resp_to_timestamp ips_param_arr[3].ip_param_value -#define ips_ip_g_resp_to_timestamp_bcast ips_param_arr[4].ip_param_value -#define ips_ip_g_send_redirects ips_param_arr[5].ip_param_value -#define ips_ip_g_forward_directed_bcast ips_param_arr[6].ip_param_value -#define ips_ip_mrtdebug ips_param_arr[7].ip_param_value -#define ips_ip_ire_reclaim_fraction ips_param_arr[8].ip_param_value -#define ips_ip_nce_reclaim_fraction ips_param_arr[9].ip_param_value -#define ips_ip_dce_reclaim_fraction ips_param_arr[10].ip_param_value -#define ips_ip_def_ttl ips_param_arr[11].ip_param_value -#define ips_ip_forward_src_routed ips_param_arr[12].ip_param_value -#define ips_ip_wroff_extra ips_param_arr[13].ip_param_value -#define ips_ip_pathmtu_interval ips_param_arr[14].ip_param_value -#define ips_ip_icmp_return ips_param_arr[15].ip_param_value -#define ips_ip_path_mtu_discovery ips_param_arr[16].ip_param_value -#define ips_ip_pmtu_min ips_param_arr[17].ip_param_value -#define ips_ip_ignore_redirect ips_param_arr[18].ip_param_value -#define ips_ip_arp_icmp_error ips_param_arr[19].ip_param_value -#define ips_ip_broadcast_ttl ips_param_arr[20].ip_param_value -#define ips_ip_icmp_err_interval ips_param_arr[21].ip_param_value -#define ips_ip_icmp_err_burst ips_param_arr[22].ip_param_value -#define ips_ip_reass_queue_bytes ips_param_arr[23].ip_param_value -#define ips_ip_strict_dst_multihoming ips_param_arr[24].ip_param_value -#define ips_ip_addrs_per_if ips_param_arr[25].ip_param_value -#define ips_ipsec_override_persocket_policy ips_param_arr[26].ip_param_value -#define ips_icmp_accept_clear_messages ips_param_arr[27].ip_param_value -#define ips_igmp_accept_clear_messages ips_param_arr[28].ip_param_value +#define ips_ip_respond_to_address_mask_broadcast \ + ips_propinfo_tbl[0].prop_cur_bval +#define ips_ip_g_resp_to_echo_bcast ips_propinfo_tbl[1].prop_cur_bval +#define ips_ip_g_resp_to_echo_mcast ips_propinfo_tbl[2].prop_cur_bval +#define ips_ip_g_resp_to_timestamp ips_propinfo_tbl[3].prop_cur_bval +#define ips_ip_g_resp_to_timestamp_bcast ips_propinfo_tbl[4].prop_cur_bval +#define ips_ip_g_send_redirects ips_propinfo_tbl[5].prop_cur_bval +#define ips_ip_g_forward_directed_bcast ips_propinfo_tbl[6].prop_cur_bval +#define ips_ip_mrtdebug ips_propinfo_tbl[7].prop_cur_uval +#define ips_ip_ire_reclaim_fraction ips_propinfo_tbl[8].prop_cur_uval +#define ips_ip_nce_reclaim_fraction ips_propinfo_tbl[9].prop_cur_uval +#define ips_ip_dce_reclaim_fraction ips_propinfo_tbl[10].prop_cur_uval +#define ips_ip_def_ttl ips_propinfo_tbl[11].prop_cur_uval +#define ips_ip_forward_src_routed ips_propinfo_tbl[12].prop_cur_bval +#define ips_ip_wroff_extra ips_propinfo_tbl[13].prop_cur_uval +#define ips_ip_pathmtu_interval ips_propinfo_tbl[14].prop_cur_uval +#define ips_ip_icmp_return ips_propinfo_tbl[15].prop_cur_uval +#define ips_ip_path_mtu_discovery ips_propinfo_tbl[16].prop_cur_bval +#define ips_ip_pmtu_min ips_propinfo_tbl[17].prop_cur_uval +#define ips_ip_ignore_redirect ips_propinfo_tbl[18].prop_cur_bval +#define ips_ip_arp_icmp_error ips_propinfo_tbl[19].prop_cur_bval +#define ips_ip_broadcast_ttl ips_propinfo_tbl[20].prop_cur_uval +#define ips_ip_icmp_err_interval ips_propinfo_tbl[21].prop_cur_uval +#define ips_ip_icmp_err_burst ips_propinfo_tbl[22].prop_cur_uval +#define ips_ip_reass_queue_bytes ips_propinfo_tbl[23].prop_cur_uval +#define ips_ip_strict_dst_multihoming ips_propinfo_tbl[24].prop_cur_uval +#define ips_ip_addrs_per_if ips_propinfo_tbl[25].prop_cur_uval +#define ips_ipsec_override_persocket_policy ips_propinfo_tbl[26].prop_cur_bval +#define ips_icmp_accept_clear_messages ips_propinfo_tbl[27].prop_cur_bval +#define ips_igmp_accept_clear_messages ips_propinfo_tbl[28].prop_cur_bval /* IPv6 configuration knobs */ -#define ips_delay_first_probe_time ips_param_arr[29].ip_param_value -#define ips_max_unicast_solicit ips_param_arr[30].ip_param_value -#define ips_ipv6_def_hops ips_param_arr[31].ip_param_value -#define ips_ipv6_icmp_return ips_param_arr[32].ip_param_value -#define ips_ipv6_forward_src_routed ips_param_arr[33].ip_param_value -#define ips_ipv6_resp_echo_mcast ips_param_arr[34].ip_param_value -#define ips_ipv6_send_redirects ips_param_arr[35].ip_param_value -#define ips_ipv6_ignore_redirect ips_param_arr[36].ip_param_value -#define ips_ipv6_strict_dst_multihoming ips_param_arr[37].ip_param_value -#define ips_src_check ips_param_arr[38].ip_param_value -#define ips_ipsec_policy_log_interval ips_param_arr[39].ip_param_value -#define ips_pim_accept_clear_messages ips_param_arr[40].ip_param_value -#define ips_ip_ndp_unsolicit_interval ips_param_arr[41].ip_param_value -#define ips_ip_ndp_unsolicit_count ips_param_arr[42].ip_param_value -#define ips_ipv6_ignore_home_address_opt ips_param_arr[43].ip_param_value +#define ips_delay_first_probe_time ips_propinfo_tbl[29].prop_cur_uval +#define ips_max_unicast_solicit ips_propinfo_tbl[30].prop_cur_uval +#define ips_ipv6_def_hops ips_propinfo_tbl[31].prop_cur_uval +#define ips_ipv6_icmp_return ips_propinfo_tbl[32].prop_cur_uval +#define ips_ipv6_forward_src_routed ips_propinfo_tbl[33].prop_cur_bval +#define ips_ipv6_resp_echo_mcast ips_propinfo_tbl[34].prop_cur_bval +#define ips_ipv6_send_redirects ips_propinfo_tbl[35].prop_cur_bval +#define ips_ipv6_ignore_redirect ips_propinfo_tbl[36].prop_cur_bval +#define ips_ipv6_strict_dst_multihoming ips_propinfo_tbl[37].prop_cur_uval +#define ips_src_check ips_propinfo_tbl[38].prop_cur_uval +#define ips_ipsec_policy_log_interval ips_propinfo_tbl[39].prop_cur_uval +#define ips_pim_accept_clear_messages ips_propinfo_tbl[40].prop_cur_bval +#define ips_ip_ndp_unsolicit_interval ips_propinfo_tbl[41].prop_cur_uval +#define ips_ip_ndp_unsolicit_count ips_propinfo_tbl[42].prop_cur_uval +#define ips_ipv6_ignore_home_address_opt ips_propinfo_tbl[43].prop_cur_bval /* Misc IP configuration knobs */ -#define ips_ip_policy_mask ips_param_arr[44].ip_param_value -#define ips_ip_ecmp_behavior ips_param_arr[45].ip_param_value -#define ips_ip_multirt_ttl ips_param_arr[46].ip_param_value -#define ips_ip_ire_badcnt_lifetime ips_param_arr[47].ip_param_value -#define ips_ip_max_temp_idle ips_param_arr[48].ip_param_value -#define ips_ip_max_temp_defend ips_param_arr[49].ip_param_value -#define ips_ip_max_defend ips_param_arr[50].ip_param_value -#define ips_ip_defend_interval ips_param_arr[51].ip_param_value -#define ips_ip_dup_recovery ips_param_arr[52].ip_param_value -#define ips_ip_restrict_interzone_loopback ips_param_arr[53].ip_param_value -#define ips_ip_lso_outbound ips_param_arr[54].ip_param_value -#define ips_igmp_max_version ips_param_arr[55].ip_param_value -#define ips_mld_max_version ips_param_arr[56].ip_param_value -#define ips_ipv6_drop_inbound_icmpv6 ips_param_arr[57].ip_param_value -#define ips_arp_probe_delay ips_param_arr[58].ip_param_value -#define ips_arp_fastprobe_delay ips_param_arr[59].ip_param_value -#define ips_arp_probe_interval ips_param_arr[60].ip_param_value -#define ips_arp_fastprobe_interval ips_param_arr[61].ip_param_value -#define ips_arp_probe_count ips_param_arr[62].ip_param_value -#define ips_arp_fastprobe_count ips_param_arr[63].ip_param_value -#define ips_ipv4_dad_announce_interval ips_param_arr[64].ip_param_value -#define ips_ipv6_dad_announce_interval ips_param_arr[65].ip_param_value -#define ips_arp_defend_interval ips_param_arr[66].ip_param_value -#define ips_arp_defend_rate ips_param_arr[67].ip_param_value -#define ips_ndp_defend_interval ips_param_arr[68].ip_param_value -#define ips_ndp_defend_rate ips_param_arr[69].ip_param_value -#define ips_arp_defend_period ips_param_arr[70].ip_param_value -#define ips_ndp_defend_period ips_param_arr[71].ip_param_value -#define ips_ipv4_icmp_return_pmtu ips_param_arr[72].ip_param_value -#define ips_ipv6_icmp_return_pmtu ips_param_arr[73].ip_param_value -#define ips_ip_arp_publish_count ips_param_arr[74].ip_param_value -#define ips_ip_arp_publish_interval ips_param_arr[75].ip_param_value -#define ips_ip_strict_src_multihoming ips_param_arr[76].ip_param_value -#define ips_ipv6_strict_src_multihoming ips_param_arr[77].ip_param_value +#define ips_ip_policy_mask ips_propinfo_tbl[44].prop_cur_uval +#define ips_ip_ecmp_behavior ips_propinfo_tbl[45].prop_cur_uval +#define ips_ip_multirt_ttl ips_propinfo_tbl[46].prop_cur_uval +#define ips_ip_ire_badcnt_lifetime ips_propinfo_tbl[47].prop_cur_uval +#define ips_ip_max_temp_idle ips_propinfo_tbl[48].prop_cur_uval +#define ips_ip_max_temp_defend ips_propinfo_tbl[49].prop_cur_uval +#define ips_ip_max_defend ips_propinfo_tbl[50].prop_cur_uval +#define ips_ip_defend_interval ips_propinfo_tbl[51].prop_cur_uval +#define ips_ip_dup_recovery ips_propinfo_tbl[52].prop_cur_uval +#define ips_ip_restrict_interzone_loopback ips_propinfo_tbl[53].prop_cur_bval +#define ips_ip_lso_outbound ips_propinfo_tbl[54].prop_cur_bval +#define ips_igmp_max_version ips_propinfo_tbl[55].prop_cur_uval +#define ips_mld_max_version ips_propinfo_tbl[56].prop_cur_uval +#define ips_ip_forwarding ips_propinfo_tbl[57].prop_cur_bval +#define ips_ipv6_forwarding ips_propinfo_tbl[58].prop_cur_bval +#define ips_ip_reassembly_timeout ips_propinfo_tbl[59].prop_cur_uval +#define ips_ipv6_reassembly_timeout ips_propinfo_tbl[60].prop_cur_uval +#define ips_ip_cgtp_filter ips_propinfo_tbl[61].prop_cur_bval +#define ips_arp_probe_delay ips_propinfo_tbl[62].prop_cur_uval +#define ips_arp_fastprobe_delay ips_propinfo_tbl[63].prop_cur_uval +#define ips_arp_probe_interval ips_propinfo_tbl[64].prop_cur_uval +#define ips_arp_fastprobe_interval ips_propinfo_tbl[65].prop_cur_uval +#define ips_arp_probe_count ips_propinfo_tbl[66].prop_cur_uval +#define ips_arp_fastprobe_count ips_propinfo_tbl[67].prop_cur_uval +#define ips_ipv4_dad_announce_interval ips_propinfo_tbl[68].prop_cur_uval +#define ips_ipv6_dad_announce_interval ips_propinfo_tbl[69].prop_cur_uval +#define ips_arp_defend_interval ips_propinfo_tbl[70].prop_cur_uval +#define ips_arp_defend_rate ips_propinfo_tbl[71].prop_cur_uval +#define ips_ndp_defend_interval ips_propinfo_tbl[72].prop_cur_uval +#define ips_ndp_defend_rate ips_propinfo_tbl[73].prop_cur_uval +#define ips_arp_defend_period ips_propinfo_tbl[74].prop_cur_uval +#define ips_ndp_defend_period ips_propinfo_tbl[75].prop_cur_uval +#define ips_ipv4_icmp_return_pmtu ips_propinfo_tbl[76].prop_cur_bval +#define ips_ipv6_icmp_return_pmtu ips_propinfo_tbl[77].prop_cur_bval +#define ips_ip_arp_publish_count ips_propinfo_tbl[78].prop_cur_uval +#define ips_ip_arp_publish_interval ips_propinfo_tbl[79].prop_cur_uval +#define ips_ip_strict_src_multihoming ips_propinfo_tbl[80].prop_cur_uval +#define ips_ipv6_strict_src_multihoming ips_propinfo_tbl[81].prop_cur_uval +#define ips_ipv6_drop_inbound_icmpv6 ips_propinfo_tbl[82].prop_cur_bval extern int dohwcksum; /* use h/w cksum if supported by the h/w */ #ifdef ZC_TEST diff --git a/usr/src/uts/common/inet/ip/icmp.c b/usr/src/uts/common/inet/ip/icmp.c index e2eed17eb2..b0ecad4317 100644 --- a/usr/src/uts/common/inet/ip/icmp.c +++ b/usr/src/uts/common/inet/ip/icmp.c @@ -128,10 +128,6 @@ int icmp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr); static int icmp_output_newdst(conn_t *connp, mblk_t *data_mp, sin_t *sin, sin6_t *sin6, cred_t *cr, pid_t pid, ip_xmit_attr_t *ixa); -static int icmp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr); -static boolean_t icmp_param_register(IDP *ndp, icmpparam_t *icmppa, int cnt); -static int icmp_param_set(queue_t *q, mblk_t *mp, char *value, - caddr_t cp, cred_t *cr); static mblk_t *icmp_prepend_hdr(conn_t *, ip_xmit_attr_t *, const ip_pkt_t *, const in6_addr_t *, const in6_addr_t *, uint32_t, mblk_t *, int *); static mblk_t *icmp_prepend_header_template(conn_t *, ip_xmit_attr_t *, @@ -219,33 +215,69 @@ static struct T_info_ack icmp_g_t_info_ack = { }; /* - * Table of ND variables supported by icmp. These are loaded into is_nd - * when the stack instance is created. * All of these are alterable, within the min/max values given, at run time. + * + * Note: All those tunables which do not start with "icmp_" are Committed and + * therefore are public. See PSARC 2009/306. */ -static icmpparam_t icmp_param_arr[] = { - /* min max value name */ - { 0, 128, 32, "icmp_wroff_extra" }, - { 1, 255, 255, "icmp_ipv4_ttl" }, - { 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS, "icmp_ipv6_hoplimit"}, - { 0, 1, 1, "icmp_bsd_compat" }, - { 4096, 65536, 8192, "icmp_xmit_hiwat"}, - { 0, 65536, 1024, "icmp_xmit_lowat"}, - { 4096, 65536, 8192, "icmp_recv_hiwat"}, - { 65536, 1024*1024*1024, 256*1024, "icmp_max_buf"}, - { 0, 1, 0, "icmp_pmtu_discovery" }, - { 0, 1, 0, "icmp_sendto_ignerr" }, +static mod_prop_info_t icmp_propinfo_tbl[] = { + /* tunable - 0 */ + { "icmp_wroff_extra", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {0, 128, 32}, {32} }, + + { "icmp_ipv4_ttl", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {1, 255, 255}, {255} }, + + { "icmp_ipv6_hoplimit", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS}, + {IPV6_DEFAULT_HOPS} }, + + { "icmp_bsd_compat", MOD_PROTO_RAWIP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "send_maxbuf", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {4096, 65536, 8192}, {8192} }, + + { "icmp_xmit_lowat", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {0, 65536, 1024}, {1024} }, + + { "recv_maxbuf", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {4096, 65536, 8192}, {8192} }, + + { "icmp_max_buf", MOD_PROTO_RAWIP, + mod_set_uint32, mod_get_uint32, + {65536, 1024*1024*1024, 256*1024}, {256 * 1024} }, + + { "icmp_pmtu_discovery", MOD_PROTO_RAWIP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "icmp_sendto_ignerr", MOD_PROTO_RAWIP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "?", MOD_PROTO_RAWIP, NULL, mod_get_allprop, {0}, {0} }, + + { NULL, 0, NULL, NULL, {0}, {0} } }; -#define is_wroff_extra is_param_arr[0].icmp_param_value -#define is_ipv4_ttl is_param_arr[1].icmp_param_value -#define is_ipv6_hoplimit is_param_arr[2].icmp_param_value -#define is_bsd_compat is_param_arr[3].icmp_param_value -#define is_xmit_hiwat is_param_arr[4].icmp_param_value -#define is_xmit_lowat is_param_arr[5].icmp_param_value -#define is_recv_hiwat is_param_arr[6].icmp_param_value -#define is_max_buf is_param_arr[7].icmp_param_value -#define is_pmtu_discovery is_param_arr[8].icmp_param_value -#define is_sendto_ignerr is_param_arr[9].icmp_param_value + +#define is_wroff_extra is_propinfo_tbl[0].prop_cur_uval +#define is_ipv4_ttl is_propinfo_tbl[1].prop_cur_uval +#define is_ipv6_hoplimit is_propinfo_tbl[2].prop_cur_uval +#define is_bsd_compat is_propinfo_tbl[3].prop_cur_bval +#define is_xmit_hiwat is_propinfo_tbl[4].prop_cur_uval +#define is_xmit_lowat is_propinfo_tbl[5].prop_cur_uval +#define is_recv_hiwat is_propinfo_tbl[6].prop_cur_uval +#define is_max_buf is_propinfo_tbl[7].prop_cur_uval +#define is_pmtu_discovery is_propinfo_tbl[8].prop_cur_bval +#define is_sendto_ignerr is_propinfo_tbl[9].prop_cur_bval typedef union T_primitives *t_primp_t; @@ -2423,63 +2455,6 @@ icmp_build_hdr_template(conn_t *connp, const in6_addr_t *v6src, return (0); } -/* - * This routine retrieves the value of an ND variable in a icmpparam_t - * structure. It is called through nd_getset when a user reads the - * variable. - */ -/* ARGSUSED */ -static int -icmp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - icmpparam_t *icmppa = (icmpparam_t *)cp; - - (void) mi_mpprintf(mp, "%d", icmppa->icmp_param_value); - return (0); -} - -/* - * Walk through the param array specified registering each element with the - * named dispatch (ND) handler. - */ -static boolean_t -icmp_param_register(IDP *ndp, icmpparam_t *icmppa, int cnt) -{ - for (; cnt-- > 0; icmppa++) { - if (icmppa->icmp_param_name && icmppa->icmp_param_name[0]) { - if (!nd_load(ndp, icmppa->icmp_param_name, - icmp_param_get, icmp_param_set, - (caddr_t)icmppa)) { - nd_free(ndp); - return (B_FALSE); - } - } - } - return (B_TRUE); -} - -/* This routine sets an ND variable in a icmpparam_t structure. */ -/* ARGSUSED */ -static int -icmp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr) -{ - long new_value; - icmpparam_t *icmppa = (icmpparam_t *)cp; - - /* - * Fail the request if the new value does not lie within the - * required bounds. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < icmppa->icmp_param_min || - new_value > icmppa->icmp_param_max) { - return (EINVAL); - } - /* Set the new value */ - icmppa->icmp_param_value = new_value; - return (0); -} - static mblk_t * icmp_queue_fallback(icmp_t *icmp, mblk_t *mp) { @@ -4706,7 +4681,6 @@ icmp_wput_other(queue_t *q, mblk_t *mp) struct iocblk *iocp; conn_t *connp = Q_TO_CONN(q); icmp_t *icmp = connp->conn_icmp; - icmp_stack_t *is = icmp->icmp_is; cred_t *cr; switch (mp->b_datap->db_type) { @@ -4837,14 +4811,6 @@ icmp_wput_other(queue_t *q, mblk_t *mp) mi_copyin(q, mp, NULL, SIZEOF_STRUCT(strbuf, iocp->ioc_flag)); return; - case ND_SET: - /* nd_getset performs the necessary checking */ - case ND_GET: - if (nd_getset(q, is->is_nd, mp)) { - qreply(q, mp); - return; - } - break; default: break; } @@ -4990,19 +4956,17 @@ static void * rawip_stack_init(netstackid_t stackid, netstack_t *ns) { icmp_stack_t *is; - icmpparam_t *pa; int error = 0; + size_t arrsz; major_t major; is = (icmp_stack_t *)kmem_zalloc(sizeof (*is), KM_SLEEP); is->is_netstack = ns; - pa = (icmpparam_t *)kmem_alloc(sizeof (icmp_param_arr), KM_SLEEP); - is->is_param_arr = pa; - bcopy(icmp_param_arr, is->is_param_arr, sizeof (icmp_param_arr)); + arrsz = sizeof (icmp_propinfo_tbl); + is->is_propinfo_tbl = (mod_prop_info_t *)kmem_alloc(arrsz, KM_SLEEP); + bcopy(icmp_propinfo_tbl, is->is_propinfo_tbl, arrsz); - (void) icmp_param_register(&is->is_nd, - is->is_param_arr, A_CNT(icmp_param_arr)); is->is_ksp = rawip_kstat_init(stackid); major = mod_name_to_major(INET_NAME); @@ -5019,9 +4983,8 @@ rawip_stack_fini(netstackid_t stackid, void *arg) { icmp_stack_t *is = (icmp_stack_t *)arg; - nd_free(&is->is_nd); - kmem_free(is->is_param_arr, sizeof (icmp_param_arr)); - is->is_param_arr = NULL; + kmem_free(is->is_propinfo_tbl, sizeof (icmp_propinfo_tbl)); + is->is_propinfo_tbl = NULL; rawip_kstat_fini(stackid, is->is_ksp); is->is_ksp = NULL; @@ -5599,8 +5562,6 @@ rawip_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg, } switch (cmd) { - case ND_SET: - case ND_GET: case _SIOCSOCKFALLBACK: case TI_GETPEERNAME: case TI_GETMYNAME: diff --git a/usr/src/uts/common/inet/ip/igmp.c b/usr/src/uts/common/inet/ip/igmp.c index daed958617..470c0afb24 100644 --- a/usr/src/uts/common/inet/ip/igmp.c +++ b/usr/src/uts/common/inet/ip/igmp.c @@ -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. */ /* Copyright (c) 1990 Mentat Inc. */ @@ -61,6 +61,7 @@ #include <inet/common.h> #include <inet/mi.h> #include <inet/nd.h> +#include <inet/tunables.h> #include <inet/ip.h> #include <inet/ip6.h> #include <inet/ip_multi.h> diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c index 0a84ee99a8..31febd92bb 100644 --- a/usr/src/uts/common/inet/ip/ip.c +++ b/usr/src/uts/common/inet/ip/ip.c @@ -299,11 +299,6 @@ void (*cl_inet_idlesa)(netstackid_t, uint8_t, uint32_t, sa_family_t, * - phyint_lock: This is a per phyint mutex lock. Protects just the * phyint_flags * - * - ip_g_nd_lock: This is a global reader/writer lock. - * Any call to nd_load to load a new parameter to the ND table must hold the - * lock as writer. ND_GET/ND_SET routines that read the ND table hold the lock - * as reader. - * * - ip_addr_avail_lock: This is used to ensure the uniqueness of IP addresses. * This lock is held in ipif_up_done and the ipif is marked IPIF_UP and the * uniqueness check also done atomically. @@ -662,9 +657,6 @@ boolean_t ip_squeue_fanout = 0; */ uint_t ip_max_frag_dups = 10; -/* RFC 1122 Conformance */ -#define IP_FORWARD_DEFAULT IP_FORWARD_NEVER - static int ip_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp, boolean_t isv6); static mblk_t *ip_xmit_attach_llhdr(mblk_t *, nce_t *); @@ -691,11 +683,6 @@ static char *ip_dot_saddr(uchar_t *, char *); static void ip_lrput(queue_t *, mblk_t *); ipaddr_t ip_net_mask(ipaddr_t); char *ip_nv_lookup(nv_t *, int); -static int ip_param_get(queue_t *, mblk_t *, caddr_t, cred_t *); -static int ip_param_generic_get(queue_t *, mblk_t *, caddr_t, cred_t *); -static boolean_t ip_param_register(IDP *ndp, ipparam_t *, size_t, - ipndp_t *, size_t); -static int ip_param_set(queue_t *, mblk_t *, char *, caddr_t, cred_t *); void ip_rput(queue_t *, mblk_t *); static void ip_rput_dlpi_writer(ipsq_t *dummy_sq, queue_t *q, mblk_t *mp, void *dummy_arg); @@ -749,20 +736,11 @@ static void *ip_stack_init(netstackid_t stackid, netstack_t *ns); static void ip_stack_shutdown(netstackid_t stackid, void *arg); static void ip_stack_fini(netstackid_t stackid, void *arg); -static int ip_forward_set(queue_t *, mblk_t *, char *, caddr_t, cred_t *); - static int ip_multirt_apply_membership(int (*fn)(conn_t *, boolean_t, const in6_addr_t *, ipaddr_t, uint_t, mcast_record_t, const in6_addr_t *), ire_t *, conn_t *, boolean_t, const in6_addr_t *, mcast_record_t, const in6_addr_t *); -static int ip_cgtp_filter_get(queue_t *, mblk_t *, caddr_t, cred_t *); -static int ip_cgtp_filter_set(queue_t *, mblk_t *, char *, - caddr_t, cred_t *); -static int ip_input_proc_set(queue_t *q, mblk_t *mp, char *value, - caddr_t cp, cred_t *cr); -static int ip_int_set(queue_t *, mblk_t *, char *, caddr_t, - cred_t *); static int ip_squeue_switch(int); static void *ip_kstat_init(netstackid_t, ip_stack_t *); @@ -779,10 +757,6 @@ static void ipobs_fini(ip_stack_t *); ipaddr_t ip_g_all_ones = IP_HOST_MASK; -/* How long, in seconds, we allow frags to hang around. */ -#define IP_FRAG_TIMEOUT 15 -#define IPV6_FRAG_TIMEOUT 60 - static long ip_rput_pullups; int dohwcksum = 1; /* use h/w cksum if supported by the hardware */ @@ -797,191 +771,10 @@ int ip_debug; int ip_cgtp_filter_rev = CGTP_FILTER_REV; /* CGTP hooks version */ /* - * Named Dispatch Parameter Table. - * All of these are alterable, within the min/max values given, at run time. - */ -static ipparam_t lcl_param_arr[] = { - /* min max value name */ - { 0, 1, 0, "ip_respond_to_address_mask_broadcast"}, - { 0, 1, 1, "ip_respond_to_echo_broadcast"}, - { 0, 1, 1, "ip_respond_to_echo_multicast"}, - { 0, 1, 0, "ip_respond_to_timestamp"}, - { 0, 1, 0, "ip_respond_to_timestamp_broadcast"}, - { 0, 1, 1, "ip_send_redirects"}, - { 0, 1, 0, "ip_forward_directed_broadcasts"}, - { 0, 10, 0, "ip_mrtdebug"}, - { 1, 8, 3, "ip_ire_reclaim_fraction" }, - { 1, 8, 3, "ip_nce_reclaim_fraction" }, - { 1, 8, 3, "ip_dce_reclaim_fraction" }, - { 1, 255, 255, "ip_def_ttl" }, - { 0, 1, 0, "ip_forward_src_routed"}, - { 0, 256, 32, "ip_wroff_extra" }, - { 2, 999999999, 60*20, "ip_pathmtu_interval" }, /* In seconds */ - { 8, 65536, 64, "ip_icmp_return_data_bytes" }, - { 0, 1, 1, "ip_path_mtu_discovery" }, - { 68, 65535, 576, "ip_pmtu_min" }, - { 0, 1, 0, "ip_ignore_redirect" }, - { 0, 1, 0, "ip_arp_icmp_error" }, - { 1, 254, 1, "ip_broadcast_ttl" }, - { 0, 99999, 100, "ip_icmp_err_interval" }, - { 1, 99999, 10, "ip_icmp_err_burst" }, - { 0, 999999999, 1000000, "ip_reass_queue_bytes" }, - /* - * See comments for ip_strict_src_multihoming for an explanation - * of the semantics of ip_strict_dst_multihoming - */ - { 0, 1, 0, "ip_strict_dst_multihoming" }, - { 1, MAX_ADDRS_PER_IF, 256, "ip_addrs_per_if"}, - { 0, 1, 0, "ipsec_override_persocket_policy" }, - { 0, 1, 1, "icmp_accept_clear_messages" }, - { 0, 1, 1, "igmp_accept_clear_messages" }, - { 2, 999999999, ND_DELAY_FIRST_PROBE_TIME, - "ip_ndp_delay_first_probe_time"}, - { 1, 999999999, ND_MAX_UNICAST_SOLICIT, - "ip_ndp_max_unicast_solicit"}, - { 1, 255, IPV6_MAX_HOPS, "ip6_def_hops" }, - { 8, IPV6_MIN_MTU, IPV6_MIN_MTU, "ip6_icmp_return_data_bytes" }, - { 0, 1, 0, "ip6_forward_src_routed"}, - { 0, 1, 1, "ip6_respond_to_echo_multicast"}, - { 0, 1, 1, "ip6_send_redirects"}, - { 0, 1, 0, "ip6_ignore_redirect" }, - /* - * See comments for ip6_strict_src_multihoming for an explanation - * of the semantics of ip6_strict_dst_multihoming - */ - { 0, 1, 0, "ip6_strict_dst_multihoming" }, - - { 0, 2, 2, "ip_src_check" }, - - { 0, 999999, 1000, "ipsec_policy_log_interval" }, - - { 0, 1, 1, "pim_accept_clear_messages" }, - { 1000, 20000, 2000, "ip_ndp_unsolicit_interval" }, - { 1, 20, 3, "ip_ndp_unsolicit_count" }, - { 0, 1, 1, "ip6_ignore_home_address_opt" }, - { 0, 15, 0, "ip_policy_mask" }, - { 0, 2, 2, "ip_ecmp_behavior" }, - { 0, 255, 1, "ip_multirt_ttl" }, - { 0, 3600, 60, "ip_ire_badcnt_lifetime" }, /* In seconds */ - { 0, 999999, 60*60*24, "ip_max_temp_idle" }, - { 0, 1000, 1, "ip_max_temp_defend" }, - /* - * when a conflict of an active address is detected, - * defend up to ip_max_defend times, within any - * ip_defend_interval span. - */ - { 0, 1000, 3, "ip_max_defend" }, - { 0, 999999, 30, "ip_defend_interval" }, - { 0, 3600000, 300000, "ip_dup_recovery" }, - { 0, 1, 1, "ip_restrict_interzone_loopback" }, - { 0, 1, 1, "ip_lso_outbound" }, - { IGMP_V1_ROUTER, IGMP_V3_ROUTER, IGMP_V3_ROUTER, "igmp_max_version" }, - { MLD_V1_ROUTER, MLD_V2_ROUTER, MLD_V2_ROUTER, "mld_max_version" }, -#ifdef DEBUG - { 0, 1, 0, "ip6_drop_inbound_icmpv6" }, -#else - { 0, 0, 0, "" }, -#endif - /* delay before sending first probe: */ - { 0, 20000, 1000, "arp_probe_delay" }, - { 0, 20000, 100, "arp_fastprobe_delay" }, - /* interval at which DAD probes are sent: */ - { 10, 20000, 1500, "arp_probe_interval" }, - { 10, 20000, 150, "arp_fastprobe_interval" }, - /* setting probe count to 0 will disable ARP probing for DAD. */ - { 0, 20, 3, "arp_probe_count" }, - { 0, 20, 3, "arp_fastprobe_count" }, - - { 0, 3600000, 15000, "ipv4_dad_announce_interval"}, - { 0, 3600000, 15000, "ipv6_dad_announce_interval"}, - /* - * Rate limiting parameters for DAD defense used in - * ill_defend_rate_limit(): - * defend_rate : pkts/hour permitted - * defend_interval : time that can elapse before we send out a - * DAD defense. - * defend_period: denominator for defend_rate (in seconds). - */ - { 0, 3600000, 300000, "arp_defend_interval"}, - { 0, 20000, 100, "arp_defend_rate"}, - { 0, 3600000, 300000, "ndp_defend_interval"}, - { 0, 20000, 100, "ndp_defend_rate"}, - { 5, 86400, 3600, "arp_defend_period"}, - { 5, 86400, 3600, "ndp_defend_period"}, - { 0, 1, 1, "ipv4_icmp_return_pmtu" }, - { 0, 1, 1, "ipv6_icmp_return_pmtu" }, - /* - * publish count/interval values used to announce local addresses - * for IPv4, IPv6. - */ - { 1, 20, 5, "ip_arp_publish_count" }, - { 1000, 20000, 2000, "ip_arp_publish_interval" }, - /* - * The ip*strict_src_multihoming and ip*strict_dst_multihoming provide - * a range of choices for setting strong/weak/preferred end-system - * behavior. The semantics for setting these are: - * - * ip*_strict_dst_multihoming = 0 - * weak end system model for managing ip destination addresses. - * A packet with IP dst D1 that's received on interface I1 will be - * accepted as long as D1 is one of the local addresses on - * the machine, even if D1 is not configured on I1. - * ip*strict_dst_multihioming = 1 - * strong end system model for managing ip destination addresses. - * A packet with IP dst D1 that's received on interface I1 will be - * accepted if, and only if, D1 is configured on I1. - * - * ip*strict_src_multihoming = 0 - * Source agnostic route selection for outgoing packets: the - * outgoing interface for a packet will be computed using - * default algorithms for route selection, where the route - * with the longest matching prefix is chosen for the output - * unless other route selection constraints are explicitly - * specified during routing table lookup. This may result - * in packet being sent out on interface I2 with source - * address S1, even though S1 is not a configured address on I2. - * ip*strict_src_multihoming = 1 - * Preferred source aware route selection for outgoing packets: for - * a packet with source S2, destination D2, the route selection - * algorithm will first attempt to find a route for the destination - * that goes out through an interface where S2 is - * configured. If such a route cannot be found, then the - * best-matching route for D2 will be selected. - * ip*strict_src_multihoming = 2 - * Source aware route selection for outgoing packets: a packet will - * be sent out on an interface I2 only if the src address S2 of the - * packet is a configured address on I2. In conjunction with - * the setting 'ip_strict_dst_multihoming == 1', this will result in - * the implementation of Strong ES as defined in Section 3.3.4.2 of - * RFC 1122 - */ - { 0, 2, 0, "ip_strict_src_multihoming" }, - { 0, 2, 0, "ip6_strict_src_multihoming" } -}; - -/* - * Extended NDP table - * The addresses for the first two are filled in to be ips_ip_g_forward - * and ips_ipv6_forward at init time. - */ -static ipndp_t lcl_ndp_arr[] = { - /* getf setf data name */ -#define IPNDP_IP_FORWARDING_OFFSET 0 - { ip_param_generic_get, ip_forward_set, NULL, - "ip_forwarding" }, -#define IPNDP_IP6_FORWARDING_OFFSET 1 - { ip_param_generic_get, ip_forward_set, NULL, - "ip6_forwarding" }, - { ip_param_generic_get, ip_input_proc_set, - (caddr_t)&ip_squeue_enter, "ip_squeue_enter" }, - { ip_param_generic_get, ip_int_set, - (caddr_t)&ip_squeue_fanout, "ip_squeue_fanout" }, -#define IPNDP_CGTP_FILTER_OFFSET 4 - { ip_cgtp_filter_get, ip_cgtp_filter_set, NULL, - "ip_cgtp_filter" }, - { ip_param_generic_get, ip_int_set, (caddr_t)&ip_debug, - "ip_debug" }, -}; + * IP tunables related declarations. Definitions are in ip_tunables.c + */ +extern mod_prop_info_t ip_propinfo_tbl[]; +extern int ip_propinfo_count; /* * Table of IP ioctls encoding the various properties of the ioctl and @@ -1324,6 +1117,12 @@ ip_ioctl_cmd_t ip_ndx_ioctl_table[] = { /* 186 */ { IPI_DONTCARE /* SIOCGSTAMP */, 0, 0, 0, NULL, NULL }, /* 187 */ { SIOCILB, 0, IPI_PRIV | IPI_GET_CMD, MISC_CMD, ip_sioctl_ilb_cmd, NULL }, + /* 188 */ { SIOCGETPROP, 0, IPI_GET_CMD, 0, NULL, NULL }, + /* 189 */ { SIOCSETPROP, 0, IPI_PRIV | IPI_WR, 0, NULL, NULL}, + /* 190 */ { SIOCGLIFDADSTATE, sizeof (struct lifreq), + IPI_GET_CMD, LIF_CMD, ip_sioctl_get_dadstate, NULL }, + /* 191 */ { SIOCSLIFPREFIX, sizeof (struct lifreq), IPI_PRIV | IPI_WR, + LIF_CMD, ip_sioctl_prefix, ip_sioctl_prefix_restart } }; int ip_ndx_ioctl_count = sizeof (ip_ndx_ioctl_table) / sizeof (ip_ioctl_cmd_t); @@ -4584,18 +4383,15 @@ ip_stack_fini(netstackid_t stackid, void *arg) ipst->ips_ip6_kstat = NULL; bzero(&ipst->ips_ip6_statistics, sizeof (ipst->ips_ip6_statistics)); - nd_free(&ipst->ips_ip_g_nd); - kmem_free(ipst->ips_param_arr, sizeof (lcl_param_arr)); - ipst->ips_param_arr = NULL; - kmem_free(ipst->ips_ndp_arr, sizeof (lcl_ndp_arr)); - ipst->ips_ndp_arr = NULL; + kmem_free(ipst->ips_propinfo_tbl, + ip_propinfo_count * sizeof (mod_prop_info_t)); + ipst->ips_propinfo_tbl = NULL; dce_stack_destroy(ipst); ip_mrouter_stack_destroy(ipst); mutex_destroy(&ipst->ips_ip_mi_lock); rw_destroy(&ipst->ips_ill_g_usesrc_lock); - rw_destroy(&ipst->ips_ip_g_nd_lock); ret = untimeout(ipst->ips_igmp_timeout_id); if (ret == -1) { @@ -4753,8 +4549,7 @@ static void * ip_stack_init(netstackid_t stackid, netstack_t *ns) { ip_stack_t *ipst; - ipparam_t *pa; - ipndp_t *na; + size_t arrsz; major_t major; #ifdef NS_DEBUG @@ -4773,7 +4568,6 @@ ip_stack_init(netstackid_t stackid, netstack_t *ns) mutex_init(&ipst->ips_ndp4->ndp_g_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&ipst->ips_ndp6->ndp_g_lock, NULL, MUTEX_DEFAULT, NULL); - rw_init(&ipst->ips_ip_g_nd_lock, NULL, RW_DEFAULT, NULL); mutex_init(&ipst->ips_igmp_timer_lock, NULL, MUTEX_DEFAULT, NULL); ipst->ips_igmp_deferred_next = INFINITY; mutex_init(&ipst->ips_mld_timer_lock, NULL, MUTEX_DEFAULT, NULL); @@ -4793,39 +4587,16 @@ ip_stack_init(netstackid_t stackid, netstack_t *ns) ip_mrouter_stack_init(ipst); dce_stack_init(ipst); - ipst->ips_ip_g_frag_timeout = IP_FRAG_TIMEOUT; - ipst->ips_ip_g_frag_timo_ms = IP_FRAG_TIMEOUT * 1000; - ipst->ips_ipv6_frag_timeout = IPV6_FRAG_TIMEOUT; - ipst->ips_ipv6_frag_timo_ms = IPV6_FRAG_TIMEOUT * 1000; - ipst->ips_ip_multirt_log_interval = 1000; - ipst->ips_ip_g_forward = IP_FORWARD_DEFAULT; - ipst->ips_ipv6_forward = IP_FORWARD_DEFAULT; ipst->ips_ill_index = 1; - ipst->ips_saved_ip_g_forward = -1; + ipst->ips_saved_ip_forwarding = -1; ipst->ips_reg_vif_num = ALL_VIFS; /* Index to Register vif */ - pa = (ipparam_t *)kmem_alloc(sizeof (lcl_param_arr), KM_SLEEP); - ipst->ips_param_arr = pa; - bcopy(lcl_param_arr, ipst->ips_param_arr, sizeof (lcl_param_arr)); - - na = (ipndp_t *)kmem_alloc(sizeof (lcl_ndp_arr), KM_SLEEP); - ipst->ips_ndp_arr = na; - bcopy(lcl_ndp_arr, ipst->ips_ndp_arr, sizeof (lcl_ndp_arr)); - ipst->ips_ndp_arr[IPNDP_IP_FORWARDING_OFFSET].ip_ndp_data = - (caddr_t)&ipst->ips_ip_g_forward; - ipst->ips_ndp_arr[IPNDP_IP6_FORWARDING_OFFSET].ip_ndp_data = - (caddr_t)&ipst->ips_ipv6_forward; - ASSERT(strcmp(ipst->ips_ndp_arr[IPNDP_CGTP_FILTER_OFFSET].ip_ndp_name, - "ip_cgtp_filter") == 0); - ipst->ips_ndp_arr[IPNDP_CGTP_FILTER_OFFSET].ip_ndp_data = - (caddr_t)&ipst->ips_ip_cgtp_filter; - - (void) ip_param_register(&ipst->ips_ip_g_nd, - ipst->ips_param_arr, A_CNT(lcl_param_arr), - ipst->ips_ndp_arr, A_CNT(lcl_ndp_arr)); + arrsz = ip_propinfo_count * sizeof (mod_prop_info_t); + ipst->ips_propinfo_tbl = (mod_prop_info_t *)kmem_alloc(arrsz, KM_SLEEP); + bcopy(ip_propinfo_tbl, ipst->ips_propinfo_tbl, arrsz); ipst->ips_ip_mibkp = ip_kstat_init(stackid, ipst); ipst->ips_icmp_mibkp = icmp_kstat_init(stackid); @@ -6726,103 +6497,6 @@ ip_fill_mtuinfo(conn_t *connp, ip_xmit_attr_t *ixa, struct ip6_mtuinfo *mtuinfo) return (sizeof (struct ip6_mtuinfo)); } -/* Named Dispatch routine to get a current value out of our parameter table. */ -/* ARGSUSED */ -static int -ip_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *ioc_cr) -{ - ipparam_t *ippa = (ipparam_t *)cp; - - (void) mi_mpprintf(mp, "%d", ippa->ip_param_value); - return (0); -} - -/* ARGSUSED */ -static int -ip_param_generic_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *ioc_cr) -{ - - (void) mi_mpprintf(mp, "%d", *(int *)cp); - return (0); -} - -/* - * Set ip{,6}_forwarding values. This means walking through all of the - * ill's and toggling their forwarding values. - */ -/* ARGSUSED */ -static int -ip_forward_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *ioc_cr) -{ - long new_value; - int *forwarding_value = (int *)cp; - ill_t *ill; - boolean_t isv6; - ill_walk_context_t ctx; - ip_stack_t *ipst = CONNQ_TO_IPST(q); - - isv6 = (forwarding_value == &ipst->ips_ipv6_forward); - - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < 0 || new_value > 1) { - return (EINVAL); - } - - *forwarding_value = new_value; - - /* - * Regardless of the current value of ip_forwarding, set all per-ill - * values of ip_forwarding to the value being set. - * - * Bring all the ill's up to date with the new global value. - */ - rw_enter(&ipst->ips_ill_g_lock, RW_READER); - - if (isv6) - ill = ILL_START_WALK_V6(&ctx, ipst); - else - ill = ILL_START_WALK_V4(&ctx, ipst); - - for (; ill != NULL; ill = ill_next(&ctx, ill)) - (void) ill_forward_set(ill, new_value != 0); - - rw_exit(&ipst->ips_ill_g_lock); - return (0); -} - -/* - * Walk through the param array specified registering each element with the - * Named Dispatch handler. This is called only during init. So it is ok - * not to acquire any locks - */ -static boolean_t -ip_param_register(IDP *ndp, ipparam_t *ippa, size_t ippa_cnt, - ipndp_t *ipnd, size_t ipnd_cnt) -{ - for (; ippa_cnt-- > 0; ippa++) { - if (ippa->ip_param_name && ippa->ip_param_name[0]) { - if (!nd_load(ndp, ippa->ip_param_name, - ip_param_get, ip_param_set, (caddr_t)ippa)) { - nd_free(ndp); - return (B_FALSE); - } - } - } - - for (; ipnd_cnt-- > 0; ipnd++) { - if (ipnd->ip_ndp_name && ipnd->ip_ndp_name[0]) { - if (!nd_load(ndp, ipnd->ip_ndp_name, - ipnd->ip_ndp_getf, ipnd->ip_ndp_setf, - ipnd->ip_ndp_data)) { - nd_free(ndp); - return (B_FALSE); - } - } - } - - return (B_TRUE); -} - /* * When the src multihoming is changed from weak to [strong, preferred] * ip_ire_rebind_walker is called to walk the list of all ire_t entries @@ -6832,7 +6506,7 @@ ip_param_register(IDP *ndp, ipparam_t *ippa, size_t ippa_cnt, * is selected by finding an interface route for the gateway. */ /* ARGSUSED */ -static void +void ip_ire_rebind_walker(ire_t *ire, void *notused) { if (!ire->ire_unbound || ire->ire_ill != NULL) @@ -6848,7 +6522,7 @@ ip_ire_rebind_walker(ire_t *ire, void *notused) * (i.e., without RTA_IFP) back to having a NULL ire_ill. */ /* ARGSUSED */ -static void +void ip_ire_unbind_walker(ire_t *ire, void *notused) { ire_t *new_ire; @@ -6891,7 +6565,7 @@ ip_ire_unbind_walker(ire_t *ire, void *notused) * The cached ixa_ire entires for all conn_t entries are marked as * "verify" so that they will be recomputed for the next packet. */ -static void +void conn_ire_revalidate(conn_t *connp, void *arg) { boolean_t isv6 = (boolean_t)arg; @@ -6902,45 +6576,6 @@ conn_ire_revalidate(conn_t *connp, void *arg) connp->conn_ixa->ixa_ire_generation = IRE_GENERATION_VERIFY; } -/* Named Dispatch routine to negotiate a new value for one of our parameters. */ -/* ARGSUSED */ -static int -ip_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *ioc_cr) -{ - long new_value; - ipparam_t *ippa = (ipparam_t *)cp; - ip_stack_t *ipst = CONNQ_TO_IPST(q); - int strict_src4, strict_src6; - - strict_src4 = ipst->ips_ip_strict_src_multihoming; - strict_src6 = ipst->ips_ipv6_strict_src_multihoming; - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < ippa->ip_param_min || new_value > ippa->ip_param_max) { - return (EINVAL); - } - ippa->ip_param_value = new_value; - if (ipst->ips_ip_strict_src_multihoming != strict_src4) { - if (strict_src4 == 0) { - ire_walk_v4(ip_ire_rebind_walker, NULL, ALL_ZONES, - ipst); - } else { - ire_walk_v4(ip_ire_unbind_walker, NULL, ALL_ZONES, - ipst); - } - ipcl_walk(conn_ire_revalidate, (void *)B_FALSE, ipst); - } else if (ipst->ips_ipv6_strict_src_multihoming != strict_src6) { - if (strict_src6 == 0) { - ire_walk_v6(ip_ire_rebind_walker, NULL, ALL_ZONES, - ipst); - } else { - ire_walk_v4(ip_ire_unbind_walker, NULL, ALL_ZONES, - ipst); - } - ipcl_walk(conn_ire_revalidate, (void *)B_TRUE, ipst); - } - return (0); -} - /* * Handles both IPv4 and IPv6 reassembly - doing the out-of-order cases, * When an ipf is passed here for the first time, if @@ -9413,7 +9048,7 @@ ill_frag_timer(void *arg) { ill_t *ill = (ill_t *)arg; boolean_t frag_pending; - ip_stack_t *ipst = ill->ill_ipst; + ip_stack_t *ipst = ill->ill_ipst; time_t timeout; mutex_enter(&ill->ill_lock); @@ -9426,10 +9061,8 @@ ill_frag_timer(void *arg) ill->ill_fragtimer_executing = 1; mutex_exit(&ill->ill_lock); - if (ill->ill_isv6) - timeout = ipst->ips_ipv6_frag_timeout; - else - timeout = ipst->ips_ip_g_frag_timeout; + timeout = (ill->ill_isv6 ? ipst->ips_ipv6_reassembly_timeout : + ipst->ips_ip_reassembly_timeout); frag_pending = ill_frag_timeout(ill, timeout); @@ -9448,7 +9081,7 @@ ill_frag_timer(void *arg) void ill_frag_timer_start(ill_t *ill) { - ip_stack_t *ipst = ill->ill_ipst; + ip_stack_t *ipst = ill->ill_ipst; clock_t timeo_ms; ASSERT(MUTEX_HELD(&ill->ill_lock)); @@ -9469,10 +9102,9 @@ ill_frag_timer_start(ill_t *ill) } if (ill->ill_frag_timer_id == 0) { - if (ill->ill_isv6) - timeo_ms = ipst->ips_ipv6_frag_timo_ms; - else - timeo_ms = ipst->ips_ip_g_frag_timo_ms; + timeo_ms = (ill->ill_isv6 ? ipst->ips_ipv6_reassembly_timeout : + ipst->ips_ip_reassembly_timeout) * SECONDS; + /* * The timer is neither running nor is the timeout handler * executing. Post a timeout so that ill_frag_timer will be @@ -10000,7 +9632,7 @@ ip_snmp_get_mib2_ip(queue_t *q, mblk_t *mpctl, mib2_ipIfStatsEntry_t *ipmib, SET_MIB(old_ip_mib.ipDefaultTTL, (uint32_t)ipst->ips_ip_def_ttl); SET_MIB(old_ip_mib.ipReasmTimeout, - ipst->ips_ip_g_frag_timeout); + ipst->ips_ip_reassembly_timeout); SET_MIB(old_ip_mib.ipAddrEntrySize, sizeof (mib2_ipAddrEntry_t)); SET_MIB(old_ip_mib.ipRouteEntrySize, @@ -10097,7 +9729,7 @@ ip_snmp_get_mib2_ip_traffic_stats(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst) ipst->ips_ip_mib.ipIfStatsIfIndex = MIB2_UNKNOWN_INTERFACE; /* Flag to netstat */ SET_MIB(ipst->ips_ip_mib.ipIfStatsForwarding, - (ipst->ips_ip_g_forward ? 1 : 2)); + (ipst->ips_ip_forwarding ? 1 : 2)); SET_MIB(ipst->ips_ip_mib.ipIfStatsDefaultTTL, (uint32_t)ipst->ips_ip_def_ttl); SET_MIB(ipst->ips_ip_mib.ipIfStatsEntrySize, @@ -10128,7 +9760,7 @@ ip_snmp_get_mib2_ip_traffic_stats(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst) ill->ill_ip_mib->ipIfStatsIfIndex = ill->ill_phyint->phyint_ifindex; SET_MIB(ill->ill_ip_mib->ipIfStatsForwarding, - (ipst->ips_ip_g_forward ? 1 : 2)); + (ipst->ips_ip_forwarding ? 1 : 2)); SET_MIB(ill->ill_ip_mib->ipIfStatsDefaultTTL, (uint32_t)ipst->ips_ip_def_ttl); @@ -10295,7 +9927,7 @@ ip_snmp_get_mib2_ip_addr(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst) mae.ipAdEntBcastAddr = bitval; mae.ipAdEntReasmMaxSize = IP_MAXPACKET; mae.ipAdEntInfo.ae_mtu = ipif->ipif_ill->ill_mtu; - mae.ipAdEntInfo.ae_metric = ipif->ipif_metric; + mae.ipAdEntInfo.ae_metric = ipif->ipif_ill->ill_metric; mae.ipAdEntInfo.ae_broadcast_addr = ipif->ipif_brd_addr; mae.ipAdEntInfo.ae_pp_dst_addr = @@ -10398,7 +10030,8 @@ ip_snmp_get_mib2_ip6_addr(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst) else mae6.ipv6AddrStatus = 1; mae6.ipv6AddrInfo.ae_mtu = ipif->ipif_ill->ill_mtu; - mae6.ipv6AddrInfo.ae_metric = ipif->ipif_metric; + mae6.ipv6AddrInfo.ae_metric = + ipif->ipif_ill->ill_metric; mae6.ipv6AddrInfo.ae_pp_dst_addr = ipif->ipif_v6pp_dst_addr; mae6.ipv6AddrInfo.ae_flags = ipif->ipif_flags | @@ -10980,7 +10613,7 @@ ip_snmp_get_mib2_ip6(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst) ipst->ips_ip6_mib.ipIfStatsIfIndex = MIB2_UNKNOWN_INTERFACE; /* Flag to netstat */ SET_MIB(ipst->ips_ip6_mib.ipIfStatsForwarding, - ipst->ips_ipv6_forward ? 1 : 2); + ipst->ips_ipv6_forwarding ? 1 : 2); SET_MIB(ipst->ips_ip6_mib.ipIfStatsDefaultHopLimit, ipst->ips_ipv6_def_hops); SET_MIB(ipst->ips_ip6_mib.ipIfStatsEntrySize, @@ -11024,7 +10657,7 @@ ip_snmp_get_mib2_ip6(queue_t *q, mblk_t *mpctl, ip_stack_t *ipst) ill->ill_ip_mib->ipIfStatsIfIndex = ill->ill_phyint->phyint_ifindex; SET_MIB(ill->ill_ip_mib->ipIfStatsForwarding, - ipst->ips_ipv6_forward ? 1 : 2); + ipst->ips_ipv6_forwarding ? 1 : 2); SET_MIB(ill->ill_ip_mib->ipIfStatsDefaultHopLimit, ill->ill_max_hops); @@ -14014,71 +13647,6 @@ ip_multirt_apply_membership(int (*fn)(conn_t *, boolean_t, } /* - * Get the CGTP (multirouting) filtering status. - * If 0, the CGTP hooks are transparent. - */ -/* ARGSUSED */ -static int -ip_cgtp_filter_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *ioc_cr) -{ - boolean_t *ip_cgtp_filter_value = (boolean_t *)cp; - - (void) mi_mpprintf(mp, "%d", (int)*ip_cgtp_filter_value); - return (0); -} - -/* - * Set the CGTP (multirouting) filtering status. - * If the status is changed from active to transparent - * or from transparent to active, forward the new status - * to the filtering module (if loaded). - */ -/* ARGSUSED */ -static int -ip_cgtp_filter_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *ioc_cr) -{ - long new_value; - boolean_t *ip_cgtp_filter_value = (boolean_t *)cp; - ip_stack_t *ipst = CONNQ_TO_IPST(q); - - if (secpolicy_ip_config(ioc_cr, B_FALSE) != 0) - return (EPERM); - - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < 0 || new_value > 1) { - return (EINVAL); - } - - if ((!*ip_cgtp_filter_value) && new_value) { - cmn_err(CE_NOTE, "IP: enabling CGTP filtering%s", - ipst->ips_ip_cgtp_filter_ops == NULL ? - " (module not loaded)" : ""); - } - if (*ip_cgtp_filter_value && (!new_value)) { - cmn_err(CE_NOTE, "IP: disabling CGTP filtering%s", - ipst->ips_ip_cgtp_filter_ops == NULL ? - " (module not loaded)" : ""); - } - - if (ipst->ips_ip_cgtp_filter_ops != NULL) { - int res; - netstackid_t stackid; - - stackid = ipst->ips_netstack->netstack_stackid; - res = ipst->ips_ip_cgtp_filter_ops->cfo_change_state(stackid, - new_value); - if (res) - return (res); - } - - *ip_cgtp_filter_value = (boolean_t)new_value; - - ill_set_inputfn_all(ipst); - return (0); -} - -/* * Return the expected CGTP hooks version number. */ int @@ -14200,47 +13768,6 @@ ip_squeue_switch(int val) return (rval); } -/* ARGSUSED */ -static int -ip_input_proc_set(queue_t *q, mblk_t *mp, char *value, - caddr_t addr, cred_t *cr) -{ - int *v = (int *)addr; - long new_value; - - if (secpolicy_net_config(cr, B_FALSE) != 0) - return (EPERM); - - if (ddi_strtol(value, NULL, 10, &new_value) != 0) - return (EINVAL); - - ip_squeue_flag = ip_squeue_switch(new_value); - *v = new_value; - return (0); -} - -/* - * Handle ndd set of variables which require PRIV_SYS_NET_CONFIG such as - * ip_debug. - */ -/* ARGSUSED */ -static int -ip_int_set(queue_t *q, mblk_t *mp, char *value, - caddr_t addr, cred_t *cr) -{ - int *v = (int *)addr; - long new_value; - - if (secpolicy_net_config(cr, B_FALSE) != 0) - return (EPERM); - - if (ddi_strtol(value, NULL, 10, &new_value) != 0) - return (EINVAL); - - *v = new_value; - return (0); -} - static void * ip_kstat2_init(netstackid_t stackid, ip_stat_t *ip_statisticsp) { @@ -14365,7 +13892,7 @@ ip_kstat_init(netstackid_t stackid, ip_stack_t *ipst) template.forwarding.value.ui32 = WE_ARE_FORWARDING(ipst) ? 1:2; template.defaultTTL.value.ui32 = (uint32_t)ipst->ips_ip_def_ttl; - template.reasmTimeout.value.ui32 = ipst->ips_ip_g_frag_timeout; + template.reasmTimeout.value.ui32 = ipst->ips_ip_reassembly_timeout; template.addrEntrySize.value.i32 = sizeof (mib2_ipAddrEntry_t); template.routeEntrySize.value.i32 = sizeof (mib2_ipRouteEntry_t); @@ -14437,7 +13964,7 @@ ip_kstat_update(kstat_t *kp, int rw) ipkp->outRequests.value.ui64 = ipmib.ipIfStatsHCOutRequests; ipkp->outDiscards.value.ui32 = ipmib.ipIfStatsOutDiscards; ipkp->outNoRoutes.value.ui32 = ipmib.ipIfStatsOutNoRoutes; - ipkp->reasmTimeout.value.ui32 = ipst->ips_ip_g_frag_timeout; + ipkp->reasmTimeout.value.ui32 = ipst->ips_ip_reassembly_timeout; ipkp->reasmReqds.value.ui32 = ipmib.ipIfStatsReasmReqds; ipkp->reasmOKs.value.ui32 = ipmib.ipIfStatsReasmOKs; ipkp->reasmFails.value.ui32 = ipmib.ipIfStatsReasmFails; diff --git a/usr/src/uts/common/inet/ip/ip6_if.c b/usr/src/uts/common/inet/ip/ip6_if.c index a9dc126ae0..e4826bb1a2 100644 --- a/usr/src/uts/common/inet/ip/ip6_if.c +++ b/usr/src/uts/common/inet/ip/ip6_if.c @@ -56,6 +56,7 @@ #include <inet/common.h> #include <inet/nd.h> +#include <inet/tunables.h> #include <inet/mib2.h> #include <inet/ip.h> #include <inet/ip6.h> @@ -1153,6 +1154,14 @@ ipif_setlinklocal(ipif_t *ipif) ASSERT(IAM_WRITER_ILL(ill)); /* + * If the interface was created with no link-local address + * on it and the flag ILLF_NOLINKLOCAL was set, then we + * dont want to update the link-local. + */ + if ((ill->ill_flags & ILLF_NOLINKLOCAL) && + IN6_IS_ADDR_UNSPECIFIED(&ipif->ipif_v6lcl_addr)) + return; + /* * ill_manual_linklocal is set when the link-local address was * manually configured. */ diff --git a/usr/src/uts/common/inet/ip/ip6_ire.c b/usr/src/uts/common/inet/ip/ip6_ire.c index 0b84c0a2d9..e02f4fef87 100644 --- a/usr/src/uts/common/inet/ip/ip6_ire.c +++ b/usr/src/uts/common/inet/ip/ip6_ire.c @@ -54,6 +54,7 @@ #include <inet/ip_ire.h> #include <inet/ipclassifier.h> #include <inet/nd.h> +#include <inet/tunables.h> #include <sys/kmem.h> #include <sys/zone.h> diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c index 21d66b0aff..5c7b406001 100644 --- a/usr/src/uts/common/inet/ip/ip_if.c +++ b/usr/src/uts/common/inet/ip/ip_if.c @@ -71,6 +71,7 @@ #include <inet/common.h> /* for various inet/mi.h and inet/nd.h needs */ #include <inet/mi.h> #include <inet/nd.h> +#include <inet/tunables.h> #include <inet/arp.h> #include <inet/ip_arp.h> #include <inet/mib2.h> @@ -101,6 +102,9 @@ #include <sys/tsol/tndb.h> #include <sys/tsol/tnet.h> +#include <inet/rawip_impl.h> /* needed for icmp_stack_t */ +#include <inet/udp_impl.h> /* needed for udp_stack_t */ + /* The character which tells where the ill_name ends */ #define IPIF_SEPARATOR_CHAR ':' @@ -275,8 +279,6 @@ static ip_m_t ip_m_tbl[] = { static ill_t ill_null; /* Empty ILL for init. */ char ipif_loopback_name[] = "lo0"; -static char *ipv4_forward_suffix = ":ip_forwarding"; -static char *ipv6_forward_suffix = ":ip6_forwarding"; /* These are used by all IP network modules. */ sin6_t sin6_null; /* Zero address for quick clears */ @@ -483,7 +485,7 @@ ill_delete_tail(ill_t *ill) { mblk_t **mpp; ipif_t *ipif; - ip_stack_t *ipst = ill->ill_ipst; + ip_stack_t *ipst = ill->ill_ipst; for (ipif = ill->ill_ipif; ipif != NULL; ipif = ipif->ipif_next) { ipif_non_duplicate(ipif); @@ -573,11 +575,6 @@ ill_delete_tail(ill_t *ill) */ (void) ill_glist_delete(ill); - rw_enter(&ipst->ips_ip_g_nd_lock, RW_WRITER); - if (ill->ill_ndd_name != NULL) - nd_unload(&ipst->ips_ip_g_nd, ill->ill_ndd_name); - rw_exit(&ipst->ips_ip_g_nd_lock); - if (ill->ill_frag_ptr != NULL) { uint_t count; @@ -2644,52 +2641,6 @@ ill_frag_free_pkts(ill_t *ill, ipfb_t *ipfb, ipf_t *ipf, int free_cnt) ipfp[0] = ipf; } -#define ND_FORWARD_WARNING "The <if>:ip*_forwarding ndd variables are " \ - "obsolete and may be removed in a future release of Solaris. Use " \ - "ifconfig(1M) to manipulate the forwarding status of an interface." - -/* - * For obsolete per-interface forwarding configuration; - * called in response to ND_GET. - */ -/* ARGSUSED */ -static int -nd_ill_forward_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *ioc_cr) -{ - ill_t *ill = (ill_t *)cp; - - cmn_err(CE_WARN, ND_FORWARD_WARNING); - - (void) mi_mpprintf(mp, "%d", (ill->ill_flags & ILLF_ROUTER) != 0); - return (0); -} - -/* - * For obsolete per-interface forwarding configuration; - * called in response to ND_SET. - */ -/* ARGSUSED */ -static int -nd_ill_forward_set(queue_t *q, mblk_t *mp, char *valuestr, caddr_t cp, - cred_t *ioc_cr) -{ - long value; - int retval; - ip_stack_t *ipst = CONNQ_TO_IPST(q); - - cmn_err(CE_WARN, ND_FORWARD_WARNING); - - if (ddi_strtol(valuestr, NULL, 10, &value) != 0 || - value < 0 || value > 1) { - return (EINVAL); - } - - rw_enter(&ipst->ips_ill_g_lock, RW_READER); - retval = ill_forward_set((ill_t *)cp, (value != 0)); - rw_exit(&ipst->ips_ill_g_lock); - return (retval); -} - /* * Helper function for ill_forward_set(). */ @@ -2788,58 +2739,6 @@ ill_set_nce_router_flags(ill_t *ill, boolean_t enable) } /* - * Given an ill with a _valid_ name, add the ip_forwarding ndd variable - * for this ill. Make sure the v6/v4 question has been answered about this - * ill. The creation of this ndd variable is only for backwards compatibility. - * The preferred way to control per-interface IP forwarding is through the - * ILLF_ROUTER interface flag. - */ -static int -ill_set_ndd_name(ill_t *ill) -{ - char *suffix; - ip_stack_t *ipst = ill->ill_ipst; - - ASSERT(IAM_WRITER_ILL(ill)); - - if (ill->ill_isv6) - suffix = ipv6_forward_suffix; - else - suffix = ipv4_forward_suffix; - - ill->ill_ndd_name = ill->ill_name + ill->ill_name_length; - bcopy(ill->ill_name, ill->ill_ndd_name, ill->ill_name_length - 1); - /* - * Copies over the '\0'. - * Note that strlen(suffix) is always bounded. - */ - bcopy(suffix, ill->ill_ndd_name + ill->ill_name_length - 1, - strlen(suffix) + 1); - - /* - * Use of the nd table requires holding the reader lock. - * Modifying the nd table thru nd_load/nd_unload requires - * the writer lock. - */ - rw_enter(&ipst->ips_ip_g_nd_lock, RW_WRITER); - if (!nd_load(&ipst->ips_ip_g_nd, ill->ill_ndd_name, nd_ill_forward_get, - nd_ill_forward_set, (caddr_t)ill)) { - /* - * If the nd_load failed, it only meant that it could not - * allocate a new bunch of room for further NDD expansion. - * Because of that, the ill_ndd_name will be set to 0, and - * this interface is at the mercy of the global ip_forwarding - * variable. - */ - rw_exit(&ipst->ips_ip_g_nd_lock); - ill->ill_ndd_name = NULL; - return (ENOMEM); - } - rw_exit(&ipst->ips_ip_g_nd_lock); - return (0); -} - -/* * Intializes the context structure and returns the first ill in the list * cuurently start_list and end_list can have values: * MAX_G_HEADS Traverse both IPV4 and IPV6 lists. @@ -3448,8 +3347,7 @@ ill_init(queue_t *q, ill_t *ill) * Allocate sufficient space to contain our fragment hash table and * the device name. */ - frag_ptr = (uchar_t *)mi_zalloc(ILL_FRAG_HASH_TBL_SIZE + - 2 * LIFNAMSIZ + strlen(ipv6_forward_suffix)); + frag_ptr = (uchar_t *)mi_zalloc(ILL_FRAG_HASH_TBL_SIZE + 2 * LIFNAMSIZ); if (frag_ptr == NULL) { freemsg(info_mp); return (ENOMEM); @@ -5439,6 +5337,27 @@ ip_mcast_mapping(ill_t *ill, uchar_t *addr, uchar_t *hwaddr) } /* + * Returns B_FALSE if the IPv4 netmask pointed by `mask' is non-contiguous. + * Otherwise returns B_TRUE. + * + * The netmask can be verified to be contiguous with 32 shifts and or + * operations. Take the contiguous mask (in host byte order) and compute + * mask | mask << 1 | mask << 2 | ... | mask << 31 + * the result will be the same as the 'mask' for contiguous mask. + */ +static boolean_t +ip_contiguous_mask(uint32_t mask) +{ + uint32_t m = mask; + int i; + + for (i = 1; i < 32; i++) + m |= (mask << i); + + return (m == mask); +} + +/* * ip_rt_add is called to add an IPv4 route to the forwarding table. * ill is passed in to associate it with the correct interface. * If ire_arg is set, then we return the held IRE in that location. @@ -5464,6 +5383,10 @@ ip_rt_add(ipaddr_t dst_addr, ipaddr_t mask, ipaddr_t gw_addr, if (ire_arg != NULL) *ire_arg = NULL; + /* disallow non-contiguous netmasks */ + if (!ip_contiguous_mask(ntohl(mask))) + return (ENOTSUP); + /* * If this is the case of RTF_HOST being set, then we set the netmask * to all ones (regardless if one was supplied). @@ -8840,6 +8763,248 @@ ip_sioctl_lookup(int ioc_cmd) } /* + * helper function for ip_sioctl_getsetprop(), which does some sanity checks + */ +static boolean_t +getset_ioctl_checks(mblk_t *mp) +{ + struct iocblk *iocp = (struct iocblk *)mp->b_rptr; + mblk_t *mp1 = mp->b_cont; + mod_ioc_prop_t *pioc; + uint_t flags; + uint_t pioc_size; + + /* do sanity checks on various arguments */ + if (mp1 == NULL || iocp->ioc_count == 0 || + iocp->ioc_count == TRANSPARENT) { + return (B_FALSE); + } + if (msgdsize(mp1) < iocp->ioc_count) { + if (!pullupmsg(mp1, iocp->ioc_count)) + return (B_FALSE); + } + + pioc = (mod_ioc_prop_t *)mp1->b_rptr; + + /* sanity checks on mpr_valsize */ + pioc_size = sizeof (mod_ioc_prop_t); + if (pioc->mpr_valsize != 0) + pioc_size += pioc->mpr_valsize - 1; + + if (iocp->ioc_count != pioc_size) + return (B_FALSE); + + flags = pioc->mpr_flags; + if (iocp->ioc_cmd == SIOCSETPROP) { + /* + * One can either reset the value to it's default value or + * change the current value or append/remove the value from + * a multi-valued properties. + */ + if ((flags & MOD_PROP_DEFAULT) != MOD_PROP_DEFAULT && + flags != MOD_PROP_ACTIVE && + flags != (MOD_PROP_ACTIVE|MOD_PROP_APPEND) && + flags != (MOD_PROP_ACTIVE|MOD_PROP_REMOVE)) + return (B_FALSE); + } else { + ASSERT(iocp->ioc_cmd == SIOCGETPROP); + + /* + * One can retrieve only one kind of property information + * at a time. + */ + if ((flags & MOD_PROP_ACTIVE) != MOD_PROP_ACTIVE && + (flags & MOD_PROP_DEFAULT) != MOD_PROP_DEFAULT && + (flags & MOD_PROP_POSSIBLE) != MOD_PROP_POSSIBLE && + (flags & MOD_PROP_PERM) != MOD_PROP_PERM) + return (B_FALSE); + } + + return (B_TRUE); +} + +/* + * process the SIOC{SET|GET}PROP ioctl's + */ +/* ARGSUSED */ +static void +ip_sioctl_getsetprop(queue_t *q, mblk_t *mp) +{ + struct iocblk *iocp = (struct iocblk *)mp->b_rptr; + mblk_t *mp1 = mp->b_cont; + mod_ioc_prop_t *pioc; + mod_prop_info_t *ptbl = NULL, *pinfo = NULL; + ip_stack_t *ipst; + icmp_stack_t *is; + tcp_stack_t *tcps; + sctp_stack_t *sctps; + udp_stack_t *us; + netstack_t *stack; + void *cbarg; + cred_t *cr; + boolean_t set; + int err; + + ASSERT(q->q_next == NULL); + ASSERT(CONN_Q(q)); + + if (!getset_ioctl_checks(mp)) { + miocnak(q, mp, 0, EINVAL); + return; + } + ipst = CONNQ_TO_IPST(q); + stack = ipst->ips_netstack; + pioc = (mod_ioc_prop_t *)mp1->b_rptr; + + switch (pioc->mpr_proto) { + case MOD_PROTO_IP: + case MOD_PROTO_IPV4: + case MOD_PROTO_IPV6: + ptbl = ipst->ips_propinfo_tbl; + cbarg = ipst; + break; + case MOD_PROTO_RAWIP: + is = stack->netstack_icmp; + ptbl = is->is_propinfo_tbl; + cbarg = is; + break; + case MOD_PROTO_TCP: + tcps = stack->netstack_tcp; + ptbl = tcps->tcps_propinfo_tbl; + cbarg = tcps; + break; + case MOD_PROTO_UDP: + us = stack->netstack_udp; + ptbl = us->us_propinfo_tbl; + cbarg = us; + break; + case MOD_PROTO_SCTP: + sctps = stack->netstack_sctp; + ptbl = sctps->sctps_propinfo_tbl; + cbarg = sctps; + break; + default: + miocnak(q, mp, 0, EINVAL); + return; + } + + /* search for given property in respective protocol propinfo table */ + for (pinfo = ptbl; pinfo->mpi_name != NULL; pinfo++) { + if (strcmp(pinfo->mpi_name, pioc->mpr_name) == 0 && + pinfo->mpi_proto == pioc->mpr_proto) + break; + } + if (pinfo->mpi_name == NULL) { + miocnak(q, mp, 0, ENOENT); + return; + } + + set = (iocp->ioc_cmd == SIOCSETPROP) ? B_TRUE : B_FALSE; + if (set && pinfo->mpi_setf != NULL) { + cr = msg_getcred(mp, NULL); + if (cr == NULL) + cr = iocp->ioc_cr; + err = pinfo->mpi_setf(cbarg, cr, pinfo, pioc->mpr_ifname, + pioc->mpr_val, pioc->mpr_flags); + } else if (!set && pinfo->mpi_getf != NULL) { + err = pinfo->mpi_getf(cbarg, pinfo, pioc->mpr_ifname, + pioc->mpr_val, pioc->mpr_valsize, pioc->mpr_flags); + } else { + err = EPERM; + } + + if (err != 0) { + miocnak(q, mp, 0, err); + } else { + if (set) + miocack(q, mp, 0, 0); + else /* For get, we need to return back the data */ + miocack(q, mp, iocp->ioc_count, 0); + } +} + +/* + * process the legacy ND_GET, ND_SET ioctl just for {ip|ip6}_forwarding + * as several routing daemons have unfortunately used this 'unpublished' + * but well-known ioctls. + */ +/* ARGSUSED */ +static void +ip_process_legacy_nddprop(queue_t *q, mblk_t *mp) +{ + struct iocblk *iocp = (struct iocblk *)mp->b_rptr; + mblk_t *mp1 = mp->b_cont; + char *pname, *pval, *buf; + uint_t bufsize, proto; + mod_prop_info_t *ptbl = NULL, *pinfo = NULL; + ip_stack_t *ipst; + int err = 0; + + ASSERT(CONN_Q(q)); + ipst = CONNQ_TO_IPST(q); + + if (iocp->ioc_count == 0 || mp1 == NULL) { + miocnak(q, mp, 0, EINVAL); + return; + } + + mp1->b_datap->db_lim[-1] = '\0'; /* Force null termination */ + pval = buf = pname = (char *)mp1->b_rptr; + bufsize = MBLKL(mp1); + + if (strcmp(pname, "ip_forwarding") == 0) { + pname = "forwarding"; + proto = MOD_PROTO_IPV4; + } else if (strcmp(pname, "ip6_forwarding") == 0) { + pname = "forwarding"; + proto = MOD_PROTO_IPV6; + } else { + miocnak(q, mp, 0, EINVAL); + return; + } + + ptbl = ipst->ips_propinfo_tbl; + for (pinfo = ptbl; pinfo->mpi_name != NULL; pinfo++) { + if (strcmp(pinfo->mpi_name, pname) == 0 && + pinfo->mpi_proto == proto) + break; + } + + ASSERT(pinfo->mpi_name != NULL); + + switch (iocp->ioc_cmd) { + case ND_GET: + if ((err = pinfo->mpi_getf(ipst, pinfo, NULL, buf, bufsize, + 0)) == 0) { + miocack(q, mp, iocp->ioc_count, 0); + return; + } + break; + case ND_SET: + /* + * buffer will have property name and value in the following + * format, + * <property name>'\0'<property value>'\0', extract them; + */ + while (*pval++) + noop; + + if (!*pval || pval >= (char *)mp1->b_wptr) { + err = EINVAL; + } else if ((err = pinfo->mpi_setf(ipst, NULL, pinfo, NULL, + pval, 0)) == 0) { + miocack(q, mp, 0, 0); + return; + } + break; + default: + err = EINVAL; + break; + } + miocnak(q, mp, 0, err); +} + +/* * Wrapper function for resuming deferred ioctl processing * Used for SIOCGDSTINFO, SIOCGIP6ADDRPOLICY, SIOCGMSFILTER, * SIOCSMSFILTER, SIOCGIPMSFILTER, and SIOCSIPMSFILTER currently. @@ -8972,6 +9137,7 @@ ip_sioctl_copyin_setup(queue_t *q, mblk_t *mp) copyin_size = SIZEOF_STRUCT(lifsrcof, iocp->ioc_flag); mi_copyin(q, mp, NULL, copyin_size); return; + case SIOCGIP6ADDRPOLICY: ip_sioctl_ip6addrpolicy(q, mp); ip6_asp_table_refrele(ipst); @@ -8986,6 +9152,16 @@ ip_sioctl_copyin_setup(queue_t *q, mblk_t *mp) ip6_asp_table_refrele(ipst); return; + case ND_SET: + case ND_GET: + ip_process_legacy_nddprop(q, mp); + return; + + case SIOCSETPROP: + case SIOCGETPROP: + ip_sioctl_getsetprop(q, mp); + return; + case I_PLINK: case I_PUNLINK: case I_LINK: @@ -9004,38 +9180,6 @@ ip_sioctl_copyin_setup(queue_t *q, mblk_t *mp) ip_sioctl_plink(NULL, q, mp, NULL); return; - case ND_GET: - case ND_SET: - /* - * Use of the nd table requires holding the reader lock. - * Modifying the nd table thru nd_load/nd_unload requires - * the writer lock. - */ - rw_enter(&ipst->ips_ip_g_nd_lock, RW_READER); - if (nd_getset(q, ipst->ips_ip_g_nd, mp)) { - rw_exit(&ipst->ips_ip_g_nd_lock); - - if (iocp->ioc_error) - iocp->ioc_count = 0; - mp->b_datap->db_type = M_IOCACK; - qreply(q, mp); - return; - } - rw_exit(&ipst->ips_ip_g_nd_lock); - /* - * We don't understand this subioctl of ND_GET / ND_SET. - * Maybe intended for some driver / module below us - */ - if (q->q_next) { - putnext(q, mp); - } else { - iocp->ioc_error = ENOENT; - mp->b_datap->db_type = M_IOCNAK; - iocp->ioc_count = 0; - qreply(q, mp); - } - return; - case IP_IOCTL: ip_wput_ioctl(q, mp); return; @@ -9468,6 +9612,61 @@ ip_sioctl_removeif_restart(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, } /* + * Set the local interface address using the given prefix and ill_token. + */ +/* ARGSUSED */ +int +ip_sioctl_prefix(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, + ip_ioctl_cmd_t *dummy_ipip, void *dummy_ifreq) +{ + int err; + in6_addr_t v6addr; + sin6_t *sin6; + ill_t *ill; + int i; + + ip1dbg(("ip_sioctl_prefix(%s:%u %p)\n", + ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif)); + + ASSERT(IAM_WRITER_IPIF(ipif)); + + if (!ipif->ipif_isv6) + return (EINVAL); + + if (sin->sin_family != AF_INET6) + return (EAFNOSUPPORT); + + sin6 = (sin6_t *)sin; + v6addr = sin6->sin6_addr; + ill = ipif->ipif_ill; + + if (IN6_IS_ADDR_UNSPECIFIED(&v6addr) || + IN6_IS_ADDR_UNSPECIFIED(&ill->ill_token)) + return (EADDRNOTAVAIL); + + for (i = 0; i < 4; i++) + sin6->sin6_addr.s6_addr32[i] |= ill->ill_token.s6_addr32[i]; + + err = ip_sioctl_addr(ipif, sin, q, mp, + &ip_ndx_ioctl_table[SIOCLIFADDR_NDX], dummy_ifreq); + return (err); +} + +/* + * Restart entry point to restart the address set operation after the + * refcounts have dropped to zero. + */ +/* ARGSUSED */ +int +ip_sioctl_prefix_restart(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, + ip_ioctl_cmd_t *ipip, void *ifreq) +{ + ip1dbg(("ip_sioctl_prefix_restart(%s:%u %p)\n", + ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif)); + return (ip_sioctl_addr_restart(ipif, sin, q, mp, ipip, ifreq)); +} + +/* * Set the local interface address. * Allow an address of all zero when the interface is down. */ @@ -9501,13 +9700,28 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, /* * Enforce that true multicast interfaces have a link-local * address for logical unit 0. + * + * However for those ipif's for which link-local address was + * not created by default, also allow setting :: as the address. + * This scenario would arise, when we delete an address on ipif + * with logical unit 0, we would want to set :: as the address. */ if (ipif->ipif_id == 0 && (ill->ill_flags & ILLF_MULTICAST) && !(ipif->ipif_flags & (IPIF_POINTOPOINT)) && !(phyi->phyint_flags & (PHYI_LOOPBACK)) && !IN6_IS_ADDR_LINKLOCAL(&v6addr)) { - return (EADDRNOTAVAIL); + + /* + * if default link-local was not created by kernel for + * this ill, allow setting :: as the address on ipif:0. + */ + if (ill->ill_flags & ILLF_NOLINKLOCAL) { + if (!IN6_IS_ADDR_UNSPECIFIED(&v6addr)) + return (EADDRNOTAVAIL); + } else { + return (EADDRNOTAVAIL); + } } /* @@ -9532,8 +9746,9 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, addr = sin->sin_addr.s_addr; - /* Allow 0 as the local address. */ - if (addr != 0 && !ip_addr_ok_v4(addr, ipif->ipif_net_mask)) + /* Allow INADDR_ANY as the local address. */ + if (addr != INADDR_ANY && + !ip_addr_ok_v4(addr, ipif->ipif_net_mask)) return (EADDRNOTAVAIL); IN6_IPADDR_TO_V4MAPPED(addr, &v6addr); @@ -9636,8 +9851,11 @@ ip_sioctl_addr_tail(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, * in this address getting automatically reconfigured from under the * administrator. */ - if (ipif->ipif_isv6 && ipif->ipif_id == 0) - ill->ill_manual_linklocal = 1; + if (ipif->ipif_isv6 && ipif->ipif_id == 0) { + if (iocp == NULL || (iocp->ioc_cmd == SIOCSLIFADDR && + !IN6_IS_ADDR_UNSPECIFIED(&v6addr))) + ill->ill_manual_linklocal = 1; + } /* * When publishing an interface address change event, we only notify @@ -9767,8 +9985,10 @@ ip_sioctl_dstaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, return (EAFNOSUPPORT); addr = sin->sin_addr.s_addr; - if (!ip_addr_ok_v4(addr, ipif->ipif_net_mask)) + if (addr != INADDR_ANY && + !ip_addr_ok_v4(addr, ipif->ipif_net_mask)) { return (EADDRNOTAVAIL); + } IN6_IPADDR_TO_V4MAPPED(addr, &v6addr); } @@ -10628,6 +10848,7 @@ ip_sioctl_brdaddr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, return (EAFNOSUPPORT); addr = sin->sin_addr.s_addr; + if (ipif->ipif_flags & IPIF_UP) { /* * If we are already up, make sure the new @@ -10704,6 +10925,8 @@ ip_sioctl_netmask(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, return (EAFNOSUPPORT); mask = sin->sin_addr.s_addr; + if (!ip_contiguous_mask(ntohl(mask))) + return (ENOTSUP); V4MASK_TO_V6(mask, v6mask); } @@ -10846,12 +11069,12 @@ ip_sioctl_metric(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, struct ifreq *ifr; ifr = (struct ifreq *)if_req; - ipif->ipif_metric = ifr->ifr_metric; + ipif->ipif_ill->ill_metric = ifr->ifr_metric; } else { struct lifreq *lifr; lifr = (struct lifreq *)if_req; - ipif->ipif_metric = lifr->lifr_metric; + ipif->ipif_ill->ill_metric = lifr->lifr_metric; } return (0); } @@ -10869,12 +11092,12 @@ ip_sioctl_get_metric(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, struct ifreq *ifr; ifr = (struct ifreq *)if_req; - ifr->ifr_metric = ipif->ipif_metric; + ifr->ifr_metric = ipif->ipif_ill->ill_metric; } else { struct lifreq *lifr; lifr = (struct lifreq *)if_req; - lifr->lifr_metric = ipif->ipif_metric; + lifr->lifr_metric = ipif->ipif_ill->ill_metric; } return (0); @@ -11542,7 +11765,6 @@ ipif_clone(const ipif_t *sipif, ipif_t *dipif) ASSERT(sipif->ipif_ire_type == dipif->ipif_ire_type); dipif->ipif_flags = sipif->ipif_flags; - dipif->ipif_metric = sipif->ipif_metric; dipif->ipif_zoneid = sipif->ipif_zoneid; dipif->ipif_v6subnet = sipif->ipif_v6subnet; dipif->ipif_v6lcl_addr = sipif->ipif_v6lcl_addr; @@ -15447,6 +15669,9 @@ ip_sioctl_slifname(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, if ((new_flags & IFF_IPV6) != 0) { ill->ill_flags |= ILLF_IPV6; ill->ill_flags &= ~ILLF_IPV4; + + if (lifr->lifr_flags & IFF_NOLINKLOCAL) + ill->ill_flags |= ILLF_NOLINKLOCAL; } if ((new_flags & IFF_BROADCAST) != 0) @@ -15971,8 +16196,11 @@ ip_sioctl_slifusesrc(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, } usesrc_ill = ill_lookup_on_ifindex(ifindex, isv6, ipst); - if (usesrc_ill == NULL) { + if (usesrc_ill == NULL) return (ENXIO); + if (usesrc_ill == ipif->ipif_ill) { + ill_refrele(usesrc_ill); + return (EINVAL); } ipsq = ipsq_try_enter(NULL, usesrc_ill, q, mp, ip_process_ioctl, @@ -16076,6 +16304,27 @@ done: return (err); } +/* ARGSUSED */ +int +ip_sioctl_get_dadstate(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, + ip_ioctl_cmd_t *ipip, void *if_req) +{ + struct lifreq *lifr = (struct lifreq *)if_req; + ill_t *ill = ipif->ipif_ill; + + /* + * Need a lock since IFF_UP can be set even when there are + * references to the ipif. + */ + mutex_enter(&ill->ill_lock); + if ((ipif->ipif_flags & IPIF_UP) && ipif->ipif_addr_ready == 0) + lifr->lifr_dadstate = DAD_IN_PROGRESS; + else + lifr->lifr_dadstate = DAD_DONE; + mutex_exit(&ill->ill_lock); + return (0); +} + /* * comparison function used by avl. */ @@ -16355,13 +16604,6 @@ ipif_set_values_tail(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) ip_stack_t *ipst = ill->ill_ipst; phyint_t *phyi = ill->ill_phyint; - /* Set the obsolete NDD per-interface forwarding name. */ - err = ill_set_ndd_name(ill); - if (err != 0) { - cmn_err(CE_WARN, "ipif_set_values: ill_set_ndd_name (%d)\n", - err); - } - /* * Now that ill_name is set, the configuration for the IPMP * meta-interface can be performed. @@ -16494,7 +16736,6 @@ ipif_set_values(queue_t *q, mblk_t *mp, char *interf_name, uint_t *new_ppa_ptr) * which makes the ill globally visible and also merges it with the * other protocol instance of this phyint. The remaining work is * done after entering the ipsq which may happen sometime later. - * ill_set_ndd_name occurs after the ill has been made globally visible. */ ipif = ill->ill_ipif; @@ -16546,7 +16787,7 @@ ipif_set_values(queue_t *q, mblk_t *mp, char *interf_name, uint_t *new_ppa_ptr) * Set the ILLF_ROUTER flag according to the global * IPv6 forwarding policy. */ - if (ipst->ips_ipv6_forward != 0) + if (ipst->ips_ipv6_forwarding != 0) ill->ill_flags |= ILLF_ROUTER; } else if (ill->ill_flags & ILLF_IPV4) { ill->ill_isv6 = B_FALSE; @@ -16561,7 +16802,7 @@ ipif_set_values(queue_t *q, mblk_t *mp, char *interf_name, uint_t *new_ppa_ptr) * Set the ILLF_ROUTER flag according to the global * IPv4 forwarding policy. */ - if (ipst->ips_ip_g_forward != 0) + if (ipst->ips_ip_forwarding != 0) ill->ill_flags |= ILLF_ROUTER; } diff --git a/usr/src/uts/common/inet/ip/ip_ire.c b/usr/src/uts/common/inet/ip/ip_ire.c index 85e9570916..9c457d0489 100644 --- a/usr/src/uts/common/inet/ip/ip_ire.c +++ b/usr/src/uts/common/inet/ip/ip_ire.c @@ -59,6 +59,7 @@ #include <inet/ip_ftable.h> #include <inet/ip_rts.h> #include <inet/nd.h> +#include <inet/tunables.h> #include <inet/tcp.h> #include <inet/ipclassifier.h> diff --git a/usr/src/uts/common/inet/ip/ip_mroute.c b/usr/src/uts/common/inet/ip/ip_mroute.c index 0ad211dd5b..973afd04e7 100644 --- a/usr/src/uts/common/inet/ip/ip_mroute.c +++ b/usr/src/uts/common/inet/ip/ip_mroute.c @@ -65,6 +65,7 @@ #include <inet/common.h> #include <inet/mi.h> #include <inet/nd.h> +#include <inet/tunables.h> #include <inet/mib2.h> #include <netinet/ip6.h> #include <inet/ip.h> @@ -565,8 +566,8 @@ ip_mrouter_init(conn_t *connp, uchar_t *data, int datalen, ip_stack_t *ipst) (void) mi_strlog(connp->conn_rq, 1, SL_TRACE, "ip_mrouter_init: turning on forwarding"); } - ipst->ips_saved_ip_g_forward = ipst->ips_ip_g_forward; - ipst->ips_ip_g_forward = IP_FORWARD_ALWAYS; + ipst->ips_saved_ip_forwarding = ipst->ips_ip_forwarding; + ipst->ips_ip_forwarding = IP_FORWARD_ALWAYS; } mutex_exit(&ipst->ips_ip_g_mrouter_mutex); @@ -620,13 +621,13 @@ ip_mrouter_done(ip_stack_t *ipst) mrouter = ipst->ips_ip_g_mrouter; - if (ipst->ips_saved_ip_g_forward != -1) { + if (ipst->ips_saved_ip_forwarding != -1) { if (ipst->ips_ip_mrtdebug > 1) { (void) mi_strlog(mrouter->conn_rq, 1, SL_TRACE, "ip_mrouter_done: turning off forwarding"); } - ipst->ips_ip_g_forward = ipst->ips_saved_ip_g_forward; - ipst->ips_saved_ip_g_forward = -1; + ipst->ips_ip_forwarding = ipst->ips_saved_ip_forwarding; + ipst->ips_saved_ip_forwarding = -1; } /* diff --git a/usr/src/uts/common/inet/ip/ip_rts.c b/usr/src/uts/common/inet/ip/ip_rts.c index 8a44a0bcd4..9486945532 100644 --- a/usr/src/uts/common/inet/ip/ip_rts.c +++ b/usr/src/uts/common/inet/ip/ip_rts.c @@ -1351,7 +1351,8 @@ rts_getifdata(if_data_t *if_data, const ipif_t *ipif) if_data->ifi_addrlen = 0; /* media address length */ if_data->ifi_hdrlen = 0; /* media header length */ if_data->ifi_mtu = ipif->ipif_ill->ill_mtu; /* mtu */ - if_data->ifi_metric = ipif->ipif_metric; /* metric (external only) */ + /* metric (external only) */ + if_data->ifi_metric = ipif->ipif_ill->ill_metric; if_data->ifi_baudrate = 0; /* linespeed */ if_data->ifi_ipackets = 0; /* packets received on if */ @@ -2027,7 +2028,7 @@ rts_new_rtsmsg(int cmd, int error, const ipif_t *ipif, uint_t flags) ifam = (ifa_msghdr_t *)mp->b_rptr; ifam->ifam_index = ipif->ipif_ill->ill_phyint->phyint_ifindex; - ifam->ifam_metric = ipif->ipif_metric; + ifam->ifam_metric = ipif->ipif_ill->ill_metric; ifam->ifam_flags = ((cmd == RTM_NEWADDR) ? RTF_UP : 0); ifam->ifam_addrs = rtm_addrs; } else { diff --git a/usr/src/uts/common/inet/ip/ip_tunables.c b/usr/src/uts/common/inet/ip/ip_tunables.c new file mode 100644 index 0000000000..d78639f1bf --- /dev/null +++ b/usr/src/uts/common/inet/ip/ip_tunables.c @@ -0,0 +1,820 @@ +/* + * 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 <inet/ip.h> +#include <inet/ip6.h> +#include <inet/ip_if.h> +#include <inet/ip_ire.h> +#include <inet/ipclassifier.h> +#include <inet/ip_impl.h> +#include <inet/tunables.h> +#include <sys/sunddi.h> +#include <sys/policy.h> + +/* How long, in seconds, we allow frags to hang around. */ +#define IP_REASM_TIMEOUT 15 +#define IPV6_REASM_TIMEOUT 60 + +/* + * Set ip{,6}_forwarding values. If the value is being set on an ill, + * find the ill and set the value on it. On the other hand if we are modifying + * global property, modify the global value and set the value on all the ills. + */ +/* ARGSUSED */ +static int +ip_set_forwarding(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + char *end; + unsigned long new_value; + boolean_t per_ill, isv6; + ill_walk_context_t ctx; + ill_t *ill; + ip_stack_t *ipst = (ip_stack_t *)cbarg; + + if (flags & MOD_PROP_DEFAULT) { + new_value = pinfo->prop_def_bval; + } else { + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || + *end != '\0') + return (EINVAL); + if (new_value != B_TRUE && new_value != B_FALSE) + return (EINVAL); + } + + per_ill = (ifname != NULL && ifname[0] != '\0'); + /* + * if it's not per ill then set the global property and bring all the + * ills up to date with the new global value. + */ + if (!per_ill) + pinfo->prop_cur_bval = (new_value == 1 ? B_TRUE : B_FALSE); + + isv6 = (pinfo->mpi_proto == MOD_PROTO_IPV6 ? B_TRUE : B_FALSE); + rw_enter(&ipst->ips_ill_g_lock, RW_READER); + if (isv6) + ill = ILL_START_WALK_V6(&ctx, ipst); + else + ill = ILL_START_WALK_V4(&ctx, ipst); + + for (; ill != NULL; ill = ill_next(&ctx, ill)) { + /* + * if the property needs to be set on a particular + * interface, look for that interface. + */ + if (per_ill && strcmp(ifname, ill->ill_name) != 0) + continue; + (void) ill_forward_set(ill, new_value != 0); + } + rw_exit(&ipst->ips_ill_g_lock); + + return (0); +} + +static int +ip_get_forwarding(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *pval, uint_t pr_size, uint_t flags) +{ + boolean_t value; + ill_walk_context_t ctx; + ill_t *ill; + ip_stack_t *ipst = (ip_stack_t *)cbarg; + boolean_t get_def = (flags & MOD_PROP_DEFAULT); + boolean_t get_perm = (flags & MOD_PROP_PERM); + boolean_t isv6; + size_t nbytes = 0; + + if (get_perm) { + nbytes = snprintf(pval, pr_size, "%d", MOD_PROP_PERM_RW); + goto ret; + } else if (get_def) { + nbytes = snprintf(pval, pr_size, "%d", pinfo->prop_def_bval); + goto ret; + } + + /* + * if per interface value is not asked for return the current + * global value + */ + if (ifname == NULL || ifname[0] == '\0') { + nbytes = snprintf(pval, pr_size, "%d", pinfo->prop_cur_bval); + goto ret; + } + + isv6 = (pinfo->mpi_proto == MOD_PROTO_IPV6 ? B_TRUE : B_FALSE); + rw_enter(&ipst->ips_ill_g_lock, RW_READER); + if (isv6) + ill = ILL_START_WALK_V6(&ctx, ipst); + else + ill = ILL_START_WALK_V4(&ctx, ipst); + for (; ill != NULL; ill = ill_next(&ctx, ill)) { + /* + * if the property needs to be obtained on a particular + * interface, look for that interface. + */ + if (strcmp(ifname, ill->ill_name) == 0) + break; + } + if (ill == NULL) { + rw_exit(&ipst->ips_ill_g_lock); + return (ENXIO); + } + value = ((ill->ill_flags & ILLF_ROUTER) ? B_TRUE : B_FALSE); + rw_exit(&ipst->ips_ill_g_lock); + nbytes = snprintf(pval, pr_size, "%d", value); +ret: + if (nbytes >= pr_size) + return (ENOBUFS); + return (0); +} + +/* + * `ip_debug' is a global variable. So, we will be modifying the global + * variable here. + */ +/* ARGSUSED */ +int +ip_set_debug(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + char *end; + unsigned long new_value; + + if (cr != NULL && secpolicy_net_config(cr, B_FALSE) != 0) + return (EPERM); + + if (flags & MOD_PROP_DEFAULT) { + ip_debug = pinfo->prop_def_uval; + return (0); + } + + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || *end != '\0') + return (EINVAL); + if (new_value < pinfo->prop_min_uval || + new_value > pinfo->prop_max_uval) { + return (ERANGE); + } + ip_debug = (uint32_t)new_value; + return (0); +} + +/* + * ip_debug is a global property. For default, permission and value range + * we retrieve the value from `pinfo'. However for the current value we + * retrieve the value from the global variable `ip_debug' + */ +/* ARGSUSED */ +int +ip_get_debug(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *pval, uint_t psize, uint_t flags) +{ + boolean_t get_def = (flags & MOD_PROP_DEFAULT); + boolean_t get_perm = (flags & MOD_PROP_PERM); + boolean_t get_range = (flags & MOD_PROP_POSSIBLE); + size_t nbytes; + + bzero(pval, psize); + if (get_perm) + nbytes = snprintf(pval, psize, "%u", MOD_PROP_PERM_RW); + else if (get_range) + nbytes = snprintf(pval, psize, "%u-%u", + pinfo->prop_min_uval, pinfo->prop_max_uval); + else if (get_def) + nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_uval); + else + nbytes = snprintf(pval, psize, "%u", ip_debug); + if (nbytes >= psize) + return (ENOBUFS); + return (0); +} + +/* + * Set the CGTP (multirouting) filtering status. If the status is changed + * from active to transparent or from transparent to active, forward the + * new status to the filtering module (if loaded). + */ +/* ARGSUSED */ +static int +ip_set_cgtp_filter(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + unsigned long new_value; + ip_stack_t *ipst = (ip_stack_t *)cbarg; + char *end; + + if (flags & MOD_PROP_DEFAULT) { + new_value = pinfo->prop_def_bval; + } else { + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || + *end != '\0' || new_value > 1) { + return (EINVAL); + } + } + if (!pinfo->prop_cur_bval && new_value) { + cmn_err(CE_NOTE, "IP: enabling CGTP filtering%s", + ipst->ips_ip_cgtp_filter_ops == NULL ? + " (module not loaded)" : ""); + } + if (pinfo->prop_cur_bval && !new_value) { + cmn_err(CE_NOTE, "IP: disabling CGTP filtering%s", + ipst->ips_ip_cgtp_filter_ops == NULL ? + " (module not loaded)" : ""); + } + if (ipst->ips_ip_cgtp_filter_ops != NULL) { + int res; + netstackid_t stackid = ipst->ips_netstack->netstack_stackid; + + res = ipst->ips_ip_cgtp_filter_ops->cfo_change_state(stackid, + new_value); + if (res) + return (res); + } + pinfo->prop_cur_bval = (new_value == 1 ? B_TRUE : B_FALSE); + ill_set_inputfn_all(ipst); + return (0); +} + +/* + * Retrieve the default MTU or min-max MTU range for a given interface. + * + * -- ill_max_frag value tells us the maximum MTU that can be handled by the + * datalink. This value is advertised by the driver via DLPI messages + * (DL_NOTE_SDU_SIZE/DL_INFO_ACK). + * + * -- ill_current_frag for the most link-types will be same as ill_max_frag + * to begin with. However it is dynamically computed for some link-types + * like tunnels, based on the tunnel PMTU. + * + * -- ill_mtu is the user set MTU using SIOCSLIFMTU and must lie between + * (IPV6_MIN_MTU/IP_MIN_MTU) and ill_max_frag. + * + * -- ill_user_mtu is set by in.ndpd using SIOCSLIFLNKINFO and must lie between + * (IPV6_MIN_MTU/IP_MIN_MTU) and ill_max_frag. + */ +int +ip_get_mtu(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *pval, uint_t psize, uint_t flags) +{ + ill_walk_context_t ctx; + ill_t *ill; + ip_stack_t *ipst = (ip_stack_t *)cbarg; + boolean_t isv6; + uint32_t max_mtu, def_mtu; + size_t nbytes = 0; + + if (!(flags & (MOD_PROP_DEFAULT|MOD_PROP_POSSIBLE))) + return (ENOTSUP); + + if (ifname == NULL || ifname[0] == '\0') + return (ENOTSUP); + + isv6 = (pinfo->mpi_proto == MOD_PROTO_IPV6 ? B_TRUE : B_FALSE); + rw_enter(&ipst->ips_ill_g_lock, RW_READER); + if (isv6) + ill = ILL_START_WALK_V6(&ctx, ipst); + else + ill = ILL_START_WALK_V4(&ctx, ipst); + for (; ill != NULL; ill = ill_next(&ctx, ill)) { + if (strcmp(ifname, ill->ill_name) == 0) + break; + } + if (ill == NULL) { + rw_exit(&ipst->ips_ill_g_lock); + return (ENXIO); + } + max_mtu = ill->ill_max_frag; + def_mtu = ill->ill_current_frag; + rw_exit(&ipst->ips_ill_g_lock); + + if (flags & MOD_PROP_DEFAULT) { + nbytes = snprintf(pval, psize, "%u", def_mtu); + } else if (flags & MOD_PROP_POSSIBLE) { + uint32_t min_mtu; + + min_mtu = isv6 ? IPV6_MIN_MTU : IP_MIN_MTU; + nbytes = snprintf(pval, psize, "%u-%u", min_mtu, max_mtu); + } else { + return (ENOTSUP); + } + + if (nbytes >= psize) + return (ENOBUFS); + return (0); +} + +/* + * See the comments for ip[6]_strict_src_multihoming for an explanation + * of the semanitcs. + */ +/* ARGSUSED */ +static int +ip_set_src_multihoming(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + char *end; + unsigned long new_value, old_value; + boolean_t isv6; + ip_stack_t *ipst = (ip_stack_t *)cbarg; + + old_value = pinfo->prop_cur_uval; + + if (flags & MOD_PROP_DEFAULT) { + new_value = pinfo->prop_def_uval; + } else { + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || + *end != '\0') + return (EINVAL); + if (new_value < pinfo->prop_min_uval || + new_value > pinfo->prop_max_uval) + return (ERANGE); + } + pinfo->prop_cur_uval = new_value; + isv6 = (strcmp(pinfo->mpi_name, "ip6_strict_src_multihoming") == 0); + if (new_value != old_value) { + if (!isv6) { + if (old_value == 0) { + ire_walk_v4(ip_ire_rebind_walker, NULL, + ALL_ZONES, ipst); + } else { + ire_walk_v4(ip_ire_unbind_walker, NULL, + ALL_ZONES, ipst); + } + ipcl_walk(conn_ire_revalidate, (void *)B_FALSE, ipst); + } else { + if (old_value == 0) { + ire_walk_v6(ip_ire_rebind_walker, NULL, + ALL_ZONES, ipst); + } else { + ire_walk_v6(ip_ire_unbind_walker, NULL, + ALL_ZONES, ipst); + } + ipcl_walk(conn_ire_revalidate, (void *)B_TRUE, ipst); + } + } + return (0); +} + +/* + * All of these are alterable, within the min/max values given, at run time. + * + * Note: All those tunables which do not start with "ip_" are Committed and + * therefore are public. See PSARC 2009/306. + */ +mod_prop_info_t ip_propinfo_tbl[] = { + /* tunable - 0 */ + { "ip_respond_to_address_mask_broadcast", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip_respond_to_echo_broadcast", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_respond_to_echo_multicast", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_respond_to_timestamp", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip_respond_to_timestamp_broadcast", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip_send_redirects", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_forward_directed_broadcasts", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip_mrtdebug", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 10, 0}, {0} }, + + { "ip_ire_reclaim_fraction", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 8, 3}, {3} }, + + { "ip_nce_reclaim_fraction", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 8, 3}, {3} }, + + /* tunable - 10 */ + { "ip_dce_reclaim_fraction", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 8, 3}, {3} }, + + { "ttl", MOD_PROTO_IPV4, + mod_set_uint32, mod_get_uint32, + {1, 255, 255}, {255} }, + + { "ip_forward_src_routed", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip_wroff_extra", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 256, 32}, {32} }, + + /* following tunable is in seconds - a deviant! */ + { "ip_pathmtu_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {2, 999999999, 60*20}, {60*20} }, + + { "ip_icmp_return_data_bytes", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {8, 65536, 64}, {64} }, + + { "ip_path_mtu_discovery", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_pmtu_min", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {68, 65535, 576}, {576} }, + + { "ip_ignore_redirect", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip_arp_icmp_error", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + /* tunable - 20 */ + { "ip_broadcast_ttl", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 254, 1}, {1} }, + + { "ip_icmp_err_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 99999, 100}, {100} }, + + { "ip_icmp_err_burst", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 99999, 10}, {10} }, + + { "ip_reass_queue_bytes", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 999999999, 1000000}, {1000000} }, + + /* + * See comments for ip_strict_src_multihoming for an explanation + * of the semantics of ip_strict_dst_multihoming + */ + { "ip_strict_dst_multihoming", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 1, 0}, {0} }, + + { "ip_addrs_per_if", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, MAX_ADDRS_PER_IF, 256}, {256} }, + + { "ipsec_override_persocket_policy", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "icmp_accept_clear_messages", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "igmp_accept_clear_messages", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_ndp_delay_first_probe_time", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {2, 999999999, ND_DELAY_FIRST_PROBE_TIME}, + {ND_DELAY_FIRST_PROBE_TIME} }, + + /* tunable - 30 */ + { "ip_ndp_max_unicast_solicit", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 999999999, ND_MAX_UNICAST_SOLICIT}, {ND_MAX_UNICAST_SOLICIT} }, + + { "hoplimit", MOD_PROTO_IPV6, + mod_set_uint32, mod_get_uint32, + {1, 255, IPV6_MAX_HOPS}, {IPV6_MAX_HOPS} }, + + { "ip6_icmp_return_data_bytes", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {8, IPV6_MIN_MTU, IPV6_MIN_MTU}, {IPV6_MIN_MTU} }, + + { "ip6_forward_src_routed", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "ip6_respond_to_echo_multicast", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip6_send_redirects", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip6_ignore_redirect", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + /* + * See comments for ip6_strict_src_multihoming for an explanation + * of the semantics of ip6_strict_dst_multihoming + */ + { "ip6_strict_dst_multihoming", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 1, 0}, {0} }, + + { "ip_src_check", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 2, 2}, {2} }, + + { "ipsec_policy_log_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 999999, 1000}, {1000} }, + + /* tunable - 40 */ + { "pim_accept_clear_messages", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_ndp_unsolicit_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1000, 20000, 2000}, {2000} }, + + { "ip_ndp_unsolicit_count", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 20, 3}, {3} }, + + { "ip6_ignore_home_address_opt", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_policy_mask", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 15, 0}, {0} }, + + { "ip_ecmp_behavior", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 2, 2}, {2} }, + + { "ip_multirt_ttl", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 255, 1}, {1} }, + + /* following tunable is in seconds - a deviant */ + { "ip_ire_badcnt_lifetime", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 3600, 60}, {60} }, + + { "ip_max_temp_idle", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 999999, 60*60*24}, {60*60*24} }, + + { "ip_max_temp_defend", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 1000, 1}, {1} }, + + /* tunable - 50 */ + /* + * when a conflict of an active address is detected, + * defend up to ip_max_defend times, within any + * ip_defend_interval span. + */ + { "ip_max_defend", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 1000, 3}, {3} }, + + { "ip_defend_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 999999, 30}, {30} }, + + { "ip_dup_recovery", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 3600000, 300000}, {300000} }, + + { "ip_restrict_interzone_loopback", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ip_lso_outbound", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "igmp_max_version", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {IGMP_V1_ROUTER, IGMP_V3_ROUTER, IGMP_V3_ROUTER}, + {IGMP_V3_ROUTER} }, + + { "mld_max_version", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {MLD_V1_ROUTER, MLD_V2_ROUTER, MLD_V2_ROUTER}, {MLD_V2_ROUTER} }, + + { "forwarding", MOD_PROTO_IPV4, + ip_set_forwarding, ip_get_forwarding, + {IP_FORWARD_NEVER}, {IP_FORWARD_NEVER} }, + + { "forwarding", MOD_PROTO_IPV6, + ip_set_forwarding, ip_get_forwarding, + {IP_FORWARD_NEVER}, {IP_FORWARD_NEVER} }, + + { "ip_reasm_timeout", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {5, 255, IP_REASM_TIMEOUT}, + {IP_REASM_TIMEOUT} }, + + /* tunable - 60 */ + { "ip6_reasm_timeout", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {5, 255, IPV6_REASM_TIMEOUT}, + {IPV6_REASM_TIMEOUT} }, + + { "ip_cgtp_filter", MOD_PROTO_IP, + ip_set_cgtp_filter, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + /* delay before sending first probe: */ + { "arp_probe_delay", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 20000, 1000}, {1000} }, + + { "arp_fastprobe_delay", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 20000, 100}, {100} }, + + /* interval at which DAD probes are sent: */ + { "arp_probe_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {10, 20000, 1500}, {1500} }, + + { "arp_fastprobe_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {10, 20000, 150}, {150} }, + + { "arp_probe_count", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 20, 3}, {3} }, + + { "arp_fastprobe_count", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 20, 3}, {3} }, + + { "ipv4_dad_announce_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 3600000, 15000}, {15000} }, + + { "ipv6_dad_announce_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 3600000, 15000}, {15000} }, + + /* tunable - 70 */ + /* + * Rate limiting parameters for DAD defense used in + * ill_defend_rate_limit(): + * defend_rate : pkts/hour permitted + * defend_interval : time that can elapse before we send out a + * DAD defense. + * defend_period: denominator for defend_rate (in seconds). + */ + { "arp_defend_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 3600000, 300000}, {300000} }, + + { "arp_defend_rate", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 20000, 100}, {100} }, + + { "ndp_defend_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 3600000, 300000}, {300000} }, + + { "ndp_defend_rate", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {0, 20000, 100}, {100} }, + + { "arp_defend_period", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {5, 86400, 3600}, {3600} }, + + { "ndp_defend_period", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {5, 86400, 3600}, {3600} }, + + { "ipv4_icmp_return_pmtu", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "ipv6_icmp_return_pmtu", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + /* + * publish count/interval values used to announce local addresses + * for IPv4, IPv6. + */ + { "ip_arp_publish_count", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1, 20, 5}, {5} }, + + { "ip_arp_publish_interval", MOD_PROTO_IP, + mod_set_uint32, mod_get_uint32, + {1000, 20000, 2000}, {2000} }, + + /* tunable - 80 */ + /* + * The ip*strict_src_multihoming and ip*strict_dst_multihoming provide + * a range of choices for setting strong/weak/preferred end-system + * behavior. The semantics for setting these are: + * + * ip*_strict_dst_multihoming = 0 + * weak end system model for managing ip destination addresses. + * A packet with IP dst D1 that's received on interface I1 will be + * accepted as long as D1 is one of the local addresses on + * the machine, even if D1 is not configured on I1. + * ip*strict_dst_multihioming = 1 + * strong end system model for managing ip destination addresses. + * A packet with IP dst D1 that's received on interface I1 will be + * accepted if, and only if, D1 is configured on I1. + * + * ip*strict_src_multihoming = 0 + * Source agnostic route selection for outgoing packets: the + * outgoing interface for a packet will be computed using + * default algorithms for route selection, where the route + * with the longest matching prefix is chosen for the output + * unless other route selection constraints are explicitly + * specified during routing table lookup. This may result + * in packet being sent out on interface I2 with source + * address S1, even though S1 is not a configured address on I2. + * ip*strict_src_multihoming = 1 + * Preferred source aware route selection for outgoing packets: for + * a packet with source S2, destination D2, the route selection + * algorithm will first attempt to find a route for the destination + * that goes out through an interface where S2 is + * configured. If such a route cannot be found, then the + * best-matching route for D2 will be selected. + * ip*strict_src_multihoming = 2 + * Source aware route selection for outgoing packets: a packet will + * be sent out on an interface I2 only if the src address S2 of the + * packet is a configured address on I2. In conjunction with + * the setting 'ip_strict_dst_multihoming == 1', this will result in + * the implementation of Strong ES as defined in Section 3.3.4.2 of + * RFC 1122 + */ + { "ip_strict_src_multihoming", MOD_PROTO_IP, + ip_set_src_multihoming, mod_get_uint32, + {0, 2, 0}, {0} }, + + { "ip6_strict_src_multihoming", MOD_PROTO_IP, + ip_set_src_multihoming, mod_get_uint32, + {0, 2, 0}, {0} }, + +#ifdef DEBUG + { "ip6_drop_inbound_icmpv6", MOD_PROTO_IP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, +#else + { "", 0, NULL, NULL, {0}, {0} }, +#endif + { "mtu", MOD_PROTO_IPV4, NULL, ip_get_mtu, {0}, {0} }, + + { "mtu", MOD_PROTO_IPV6, NULL, ip_get_mtu, {0}, {0} }, + + /* + * The following entry is a placeholder for `ip_debug' global + * variable. Within these callback functions, we will be + * setting/getting the global variable + */ + { "ip_debug", MOD_PROTO_IP, + ip_set_debug, ip_get_debug, + {0, 20, 0}, {0} }, + + { "?", MOD_PROTO_IP, NULL, mod_get_allprop, {0}, {0} }, + + { NULL, 0, NULL, NULL, {0}, {0} } +}; + +int ip_propinfo_count = A_CNT(ip_propinfo_tbl); diff --git a/usr/src/uts/common/inet/ip_if.h b/usr/src/uts/common/inet/ip_if.h index 1ded817625..c1ffa33189 100644 --- a/usr/src/uts/common/inet/ip_if.h +++ b/usr/src/uts/common/inet/ip_if.h @@ -80,7 +80,7 @@ extern "C" { #define IFF_PHYINTINST_FLAGS (IFF_DEBUG|IFF_NOTRAILERS|IFF_NOARP| \ IFF_MULTICAST|IFF_ROUTER|IFF_NONUD|IFF_NORTEXCH|IFF_IPV4|IFF_IPV6| \ - IFF_COS_ENABLED|IFF_FIXEDMTU|IFF_VRRP|IFF_NOACCEPT) + IFF_COS_ENABLED|IFF_FIXEDMTU|IFF_VRRP|IFF_NOACCEPT|IFF_NOLINKLOCAL) #define IFF_LOGINT_FLAGS (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT| \ IFF_UNNUMBERED|IFF_DHCPRUNNING|IFF_PRIVATE|IFF_NOXMIT|IFF_NOLOCAL| \ @@ -113,6 +113,7 @@ extern "C" { #define ILLF_FIXEDMTU IFF_FIXEDMTU /* set with SIOCSLIFMTU */ #define ILLF_VRRP IFF_VRRP /* managed by VRRP */ #define ILLF_NOACCEPT IFF_NOACCEPT /* accept only ND messagees */ +#define ILLF_NOLINKLOCAL IFF_NOLINKLOCAL /* No default linklocal */ #define IPIF_UP IFF_UP /* interface is up */ #define IPIF_BROADCAST IFF_BROADCAST /* broadcast address valid */ @@ -338,6 +339,11 @@ extern int ip_siocaddrt(ipif_t *, sin_t *, queue_t *, mblk_t *, extern int ip_siocdelrt(ipif_t *, sin_t *, queue_t *, mblk_t *, ip_ioctl_cmd_t *, void *); +extern int ip_sioctl_prefix(ipif_t *, sin_t *, queue_t *, mblk_t *, + ip_ioctl_cmd_t *, void *); +extern int ip_sioctl_prefix_restart(ipif_t *, sin_t *, queue_t *, mblk_t *, + ip_ioctl_cmd_t *, void *); + extern int ip_sioctl_addr(ipif_t *, sin_t *, queue_t *, mblk_t *, ip_ioctl_cmd_t *, void *); extern int ip_sioctl_addr_restart(ipif_t *, sin_t *, queue_t *, mblk_t *, @@ -470,6 +476,9 @@ extern int ip_sioctl_slifusesrc(ipif_t *, sin_t *, queue_t *, extern int ip_sioctl_get_lifsrcof(ipif_t *, sin_t *, queue_t *, mblk_t *, ip_ioctl_cmd_t *, void *); +extern int ip_sioctl_get_dadstate(ipif_t *, sin_t *, queue_t *, mblk_t *, + ip_ioctl_cmd_t *, void *); + extern void ip_sioctl_copyin_resume(ipsq_t *, queue_t *, mblk_t *, void *); extern void ip_sioctl_copyin_setup(queue_t *, mblk_t *); extern ip_ioctl_cmd_t *ip_sioctl_lookup(int); diff --git a/usr/src/uts/common/inet/ip_impl.h b/usr/src/uts/common/inet/ip_impl.h index 694f7a63b0..2b37528eb9 100644 --- a/usr/src/uts/common/inet/ip_impl.h +++ b/usr/src/uts/common/inet/ip_impl.h @@ -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. */ @@ -41,6 +41,7 @@ extern "C" { #include <sys/sdt.h> #include <sys/dld.h> +#include <inet/tunables.h> #define IP_MOD_ID 5701 @@ -179,6 +180,9 @@ extern zoneid_t ip_get_zoneid_v4(ipaddr_t, mblk_t *, ip_recv_attr_t *, zoneid_t); extern zoneid_t ip_get_zoneid_v6(in6_addr_t *, mblk_t *, const ill_t *, ip_recv_attr_t *, zoneid_t); +extern void conn_ire_revalidate(conn_t *, void *); +extern void ip_ire_unbind_walker(ire_t *, void *); +extern void ip_ire_rebind_walker(ire_t *, void *); /* * flag passed in by IP based protocols to get a private ip stream with diff --git a/usr/src/uts/common/inet/ip_stack.h b/usr/src/uts/common/inet/ip_stack.h index d2f6c07234..a564376cfb 100644 --- a/usr/src/uts/common/inet/ip_stack.h +++ b/usr/src/uts/common/inet/ip_stack.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. */ @@ -148,8 +148,7 @@ struct ip_stack { uint_t ips_src_generation; /* Both IPv4 and IPv6 */ - struct ipparam_s *ips_param_arr; /* ndd variable table */ - struct ipndp_s *ips_ndp_arr; + struct mod_prop_info_s *ips_propinfo_tbl; /* ip tunables table */ mib2_ipIfStatsEntry_t ips_ip_mib; /* SNMP fixed size info */ mib2_icmp_t ips_icmp_mib; @@ -170,7 +169,6 @@ struct ip_stack { ip6_stat_t ips_ip6_statistics; /* ip.c */ - krwlock_t ips_ip_g_nd_lock; kmutex_t ips_igmp_timer_lock; kmutex_t ips_mld_timer_lock; kmutex_t ips_ip_mi_lock; @@ -287,14 +285,6 @@ struct ip_stack { /* The number of policy entries in the table */ uint_t ips_ip6_asp_table_count; - int ips_ip_g_forward; - int ips_ipv6_forward; - - time_t ips_ip_g_frag_timeout; - clock_t ips_ip_g_frag_timo_ms; - time_t ips_ipv6_frag_timeout; - clock_t ips_ipv6_frag_timo_ms; - struct conn_s *ips_ip_g_mrouter; /* Time since last icmp_pkt_err */ @@ -306,16 +296,17 @@ struct ip_stack { void *ips_ip_g_head; /* IP Instance Data List Head */ void *ips_arp_g_head; /* ARP Instance Data List Head */ - caddr_t ips_ip_g_nd; /* Named Dispatch List Head */ - /* Multirouting stuff */ /* Interval (in ms) between consecutive 'bad MTU' warnings */ hrtime_t ips_ip_multirt_log_interval; /* Time since last warning issued. */ hrtime_t ips_multirt_bad_mtu_last_time; - struct cgtp_filter_ops *ips_ip_cgtp_filter_ops; /* CGTP hooks */ - boolean_t ips_ip_cgtp_filter; /* Enable/disable CGTP hooks */ + /* + * CGTP hooks. Enabling and disabling of hooks is controlled by an + * IP tunable 'ips_ip_cgtp_filter'. + */ + struct cgtp_filter_ops *ips_ip_cgtp_filter_ops; struct ipsq_s *ips_ipsq_g_head; uint_t ips_ill_index; /* Used to assign interface indicies */ @@ -332,7 +323,7 @@ struct ip_stack { kmutex_t ips_ip_g_mrouter_mutex; struct mrtstat *ips_mrtstat; /* Stats for netstat */ - int ips_saved_ip_g_forward; + int ips_saved_ip_forwarding; /* numvifs is only a hint about the max interface being used. */ ushort_t ips_numvifs; diff --git a/usr/src/uts/common/inet/nd.c b/usr/src/uts/common/inet/nd.c index cf903733b7..58f80a8330 100644 --- a/usr/src/uts/common/inet/nd.c +++ b/usr/src/uts/common/inet/nd.c @@ -19,13 +19,11 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1990 Mentat Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/systm.h> #include <inet/common.h> @@ -97,25 +95,8 @@ nd_getset(queue_t *q, caddr_t nd_param, MBLKP mp) valp = NULL; switch (iocp->ioc_cmd) { case ND_GET: - /* - * (temporary) hack: "*valp" is size of user buffer for - * copyout. If result of action routine is too big, free - * excess and return ioc_rval as buffer size needed. Return - * as many mblocks as will fit, free the rest. For backward - * compatibility, assume size of original ioctl buffer if - * "*valp" bad or not given. - */ - if (valp) - (void) ddi_strtol(valp, NULL, 10, &avail); /* We overwrite the name/value with the reply data */ - { - mblk_t *mp2 = mp1; - - while (mp2) { - mp2->b_wptr = mp2->b_rptr; - mp2 = mp2->b_cont; - } - } + mp1->b_wptr = mp1->b_rptr; err = (*nde->nde_get_pfi)(q, mp1, nde->nde_data, iocp->ioc_cr); if (!err) { int size_out; @@ -141,7 +122,7 @@ nd_getset(queue_t *q, caddr_t nd_param, MBLKP mp) if (valp) { if ((iocp->ioc_cr != NULL) && ((err = secpolicy_ip_config(iocp->ioc_cr, B_FALSE)) - == 0)) { + == 0)) { err = (*nde->nde_set_pfi)(q, mp1, valp, nde->nde_data, iocp->ioc_cr); } diff --git a/usr/src/uts/common/inet/rawip_impl.h b/usr/src/uts/common/inet/rawip_impl.h index 348c4f5239..71b4f3f228 100644 --- a/usr/src/uts/common/inet/rawip_impl.h +++ b/usr/src/uts/common/inet/rawip_impl.h @@ -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. */ /* Copyright (c) 1990 Mentat Inc. */ @@ -43,14 +43,7 @@ extern "C" { #include <inet/common.h> #include <inet/ip.h> #include <inet/optcom.h> - -/* Named Dispatch Parameter Management Structure */ -typedef struct icmpparam_s { - uint_t icmp_param_min; - uint_t icmp_param_max; - uint_t icmp_param_value; - char *icmp_param_name; -} icmpparam_t; +#include <inet/tunables.h> /* * ICMP stack instances @@ -58,8 +51,7 @@ typedef struct icmpparam_s { struct icmp_stack { netstack_t *is_netstack; /* Common netstack */ void *is_head; /* Head for list of open icmps */ - IDP is_nd; /* Points to table of ICMP ND variables. */ - icmpparam_t *is_param_arr; /* ndd variable table */ + mod_prop_info_t *is_propinfo_tbl; /* holds the icmp tunables */ kstat_t *is_ksp; /* kstats */ mib2_rawip_t is_rawip_mib; /* SNMP fixed size info */ ldi_ident_t is_ldi_ident; diff --git a/usr/src/uts/common/inet/sctp/sctp.c b/usr/src/uts/common/inet/sctp/sctp.c index 5dab2ba37d..77d15a5e6d 100644 --- a/usr/src/uts/common/inet/sctp/sctp.c +++ b/usr/src/uts/common/inet/sctp/sctp.c @@ -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. */ @@ -62,7 +62,6 @@ #include <inet/mi.h> #include <inet/mib2.h> #include <inet/kstatcom.h> -#include <inet/nd.h> #include <inet/optcom.h> #include <inet/ipclassifier.h> #include <inet/ipsec_impl.h> @@ -119,6 +118,12 @@ int sctp_recvq_tq_task_min = 5; /* The maxiimum number of tasks for each taskq. */ int sctp_recvq_tq_task_max = 50; +/* + * SCTP tunables related declarations. Definitions are in sctp_tunables.c + */ +extern mod_prop_info_t sctp_propinfo_tbl[]; +extern int sctp_propinfo_count; + /* sctp_t/conn_t kmem cache */ struct kmem_cache *sctp_conn_cache; @@ -1565,6 +1570,7 @@ static void * sctp_stack_init(netstackid_t stackid, netstack_t *ns) { sctp_stack_t *sctps; + size_t arrsz; sctps = kmem_zalloc(sizeof (*sctps), KM_SLEEP); sctps->sctps_netstack = ns; @@ -1573,15 +1579,16 @@ sctp_stack_init(netstackid_t stackid, netstack_t *ns) mutex_init(&sctps->sctps_g_lock, NULL, MUTEX_DEFAULT, NULL); mutex_init(&sctps->sctps_epriv_port_lock, NULL, MUTEX_DEFAULT, NULL); sctps->sctps_g_num_epriv_ports = SCTP_NUM_EPRIV_PORTS; - sctps->sctps_g_epriv_ports[0] = 2049; - sctps->sctps_g_epriv_ports[1] = 4045; + sctps->sctps_g_epriv_ports[0] = ULP_DEF_EPRIV_PORT1; + sctps->sctps_g_epriv_ports[1] = ULP_DEF_EPRIV_PORT2; /* Initialize SCTP hash arrays. */ sctp_hash_init(sctps); - if (!sctp_nd_init(sctps)) { - sctp_nd_free(sctps); - } + arrsz = sctp_propinfo_count * sizeof (mod_prop_info_t); + sctps->sctps_propinfo_tbl = (mod_prop_info_t *)kmem_alloc(arrsz, + KM_SLEEP); + bcopy(sctp_propinfo_tbl, sctps->sctps_propinfo_tbl, arrsz); /* Initialize the recvq taskq. */ sctp_rq_tq_init(sctps); @@ -1630,7 +1637,9 @@ sctp_stack_fini(netstackid_t stackid, void *arg) { sctp_stack_t *sctps = (sctp_stack_t *)arg; - sctp_nd_free(sctps); + kmem_free(sctps->sctps_propinfo_tbl, + sctp_propinfo_count * sizeof (mod_prop_info_t)); + sctps->sctps_propinfo_tbl = NULL; /* Destroy the recvq taskqs. */ sctp_rq_tq_fini(sctps); diff --git a/usr/src/uts/common/inet/sctp/sctp_impl.h b/usr/src/uts/common/inet/sctp/sctp_impl.h index 0eaa790a11..5823e03de2 100644 --- a/usr/src/uts/common/inet/sctp/sctp_impl.h +++ b/usr/src/uts/common/inet/sctp/sctp_impl.h @@ -34,6 +34,7 @@ #include <sys/zone.h> #include <netinet/ip6.h> #include <inet/optcom.h> +#include <inet/tunables.h> #include <netinet/sctp.h> #include <inet/sctp_itf.h> #include "sctp_stack.h" @@ -74,80 +75,66 @@ typedef struct sctpt_s { IN6_IS_ADDR_UNSPECIFIED(&(addr))) /* - * SCTP parameters + * SCTP properties/tunables */ -/* Named Dispatch Parameter Management Structure */ -typedef struct sctpparam_s { - uint32_t sctp_param_min; - uint32_t sctp_param_max; - uint32_t sctp_param_val; - char *sctp_param_name; -} sctpparam_t; - -#define sctps_max_init_retr sctps_params[0].sctp_param_val -#define sctps_max_init_retr_high sctps_params[0].sctp_param_max -#define sctps_max_init_retr_low sctps_params[0].sctp_param_min -#define sctps_pa_max_retr sctps_params[1].sctp_param_val -#define sctps_pa_max_retr_high sctps_params[1].sctp_param_max -#define sctps_pa_max_retr_low sctps_params[1].sctp_param_min -#define sctps_pp_max_retr sctps_params[2].sctp_param_val -#define sctps_pp_max_retr_high sctps_params[2].sctp_param_max -#define sctps_pp_max_retr_low sctps_params[2].sctp_param_min -#define sctps_cwnd_max_ sctps_params[3].sctp_param_val -#define __sctps_not_used1 sctps_params[4].sctp_param_val -#define sctps_smallest_nonpriv_port sctps_params[5].sctp_param_val -#define sctps_ipv4_ttl sctps_params[6].sctp_param_val -#define sctps_heartbeat_interval sctps_params[7].sctp_param_val -#define sctps_heartbeat_interval_high sctps_params[7].sctp_param_max -#define sctps_heartbeat_interval_low sctps_params[7].sctp_param_min -#define sctps_initial_mtu sctps_params[8].sctp_param_val -#define sctps_mtu_probe_interval sctps_params[9].sctp_param_val -#define sctps_new_secret_interval sctps_params[10].sctp_param_val -#define sctps_deferred_ack_interval sctps_params[11].sctp_param_val -#define sctps_snd_lowat_fraction sctps_params[12].sctp_param_val -#define sctps_ignore_path_mtu sctps_params[13].sctp_param_val -#define sctps_initial_ssthresh sctps_params[14].sctp_param_val -#define sctps_smallest_anon_port sctps_params[15].sctp_param_val -#define sctps_largest_anon_port sctps_params[16].sctp_param_val -#define sctps_xmit_hiwat sctps_params[17].sctp_param_val -#define sctps_xmit_lowat sctps_params[18].sctp_param_val -#define sctps_recv_hiwat sctps_params[19].sctp_param_val -#define sctps_max_buf sctps_params[20].sctp_param_val -#define sctps_rtt_updates sctps_params[21].sctp_param_val -#define sctps_ipv6_hoplimit sctps_params[22].sctp_param_val -#define sctps_rto_ming sctps_params[23].sctp_param_val -#define sctps_rto_ming_high sctps_params[23].sctp_param_max -#define sctps_rto_ming_low sctps_params[23].sctp_param_min -#define sctps_rto_maxg sctps_params[24].sctp_param_val -#define sctps_rto_maxg_high sctps_params[24].sctp_param_max -#define sctps_rto_maxg_low sctps_params[24].sctp_param_min -#define sctps_rto_initialg sctps_params[25].sctp_param_val -#define sctps_rto_initialg_high sctps_params[25].sctp_param_max -#define sctps_rto_initialg_low sctps_params[25].sctp_param_min -#define sctps_cookie_life sctps_params[26].sctp_param_val -#define sctps_cookie_life_high sctps_params[26].sctp_param_max -#define sctps_cookie_life_low sctps_params[26].sctp_param_min -#define sctps_max_in_streams sctps_params[27].sctp_param_val -#define sctps_max_in_streams_high sctps_params[27].sctp_param_max -#define sctps_max_in_streams_low sctps_params[27].sctp_param_min -#define sctps_initial_out_streams sctps_params[28].sctp_param_val -#define sctps_initial_out_streams_high sctps_params[28].sctp_param_max -#define sctps_initial_out_streams_low sctps_params[28].sctp_param_min -#define sctps_shutack_wait_bound sctps_params[29].sctp_param_val -#define sctps_maxburst sctps_params[30].sctp_param_val -#define sctps_addip_enabled sctps_params[31].sctp_param_val -#define sctps_recv_hiwat_minmss sctps_params[32].sctp_param_val -#define sctps_slow_start_initial sctps_params[33].sctp_param_val -#define sctps_slow_start_after_idle sctps_params[34].sctp_param_val -#define sctps_prsctp_enabled sctps_params[35].sctp_param_val -#define sctps_fast_rxt_thresh sctps_params[36].sctp_param_val -#define sctps_deferred_acks_max sctps_params[37].sctp_param_val - -/* - * sctp_wroff_xtra is the extra space in front of SCTP/IP header for link - * layer header. It has to be a multiple of 4. - */ -#define sctps_wroff_xtra sctps_wroff_xtra_param->sctp_param_val +#define sctps_max_init_retr sctps_propinfo_tbl[0].prop_cur_uval +#define sctps_max_init_retr_high sctps_propinfo_tbl[0].prop_max_uval +#define sctps_max_init_retr_low sctps_propinfo_tbl[0].prop_min_uval +#define sctps_pa_max_retr sctps_propinfo_tbl[1].prop_cur_uval +#define sctps_pa_max_retr_high sctps_propinfo_tbl[1].prop_max_uval +#define sctps_pa_max_retr_low sctps_propinfo_tbl[1].prop_min_uval +#define sctps_pp_max_retr sctps_propinfo_tbl[2].prop_cur_uval +#define sctps_pp_max_retr_high sctps_propinfo_tbl[2].prop_max_uval +#define sctps_pp_max_retr_low sctps_propinfo_tbl[2].prop_min_uval +#define sctps_cwnd_max_ sctps_propinfo_tbl[3].prop_cur_uval +#define sctps_smallest_nonpriv_port sctps_propinfo_tbl[4].prop_cur_uval +#define sctps_ipv4_ttl sctps_propinfo_tbl[5].prop_cur_uval +#define sctps_heartbeat_interval sctps_propinfo_tbl[6].prop_cur_uval +#define sctps_heartbeat_interval_high sctps_propinfo_tbl[6].prop_max_uval +#define sctps_heartbeat_interval_low sctps_propinfo_tbl[6].prop_min_uval +#define sctps_initial_mtu sctps_propinfo_tbl[7].prop_cur_uval +#define sctps_mtu_probe_interval sctps_propinfo_tbl[8].prop_cur_uval +#define sctps_new_secret_interval sctps_propinfo_tbl[9].prop_cur_uval +#define sctps_deferred_ack_interval sctps_propinfo_tbl[10].prop_cur_uval +#define sctps_snd_lowat_fraction sctps_propinfo_tbl[11].prop_cur_uval +#define sctps_ignore_path_mtu sctps_propinfo_tbl[12].prop_cur_bval +#define sctps_initial_ssthresh sctps_propinfo_tbl[13].prop_cur_uval +#define sctps_smallest_anon_port sctps_propinfo_tbl[14].prop_cur_uval +#define sctps_largest_anon_port sctps_propinfo_tbl[15].prop_cur_uval +#define sctps_xmit_hiwat sctps_propinfo_tbl[16].prop_cur_uval +#define sctps_xmit_lowat sctps_propinfo_tbl[17].prop_cur_uval +#define sctps_recv_hiwat sctps_propinfo_tbl[18].prop_cur_uval +#define sctps_max_buf sctps_propinfo_tbl[19].prop_cur_uval +#define sctps_rtt_updates sctps_propinfo_tbl[20].prop_cur_uval +#define sctps_ipv6_hoplimit sctps_propinfo_tbl[21].prop_cur_uval +#define sctps_rto_ming sctps_propinfo_tbl[22].prop_cur_uval +#define sctps_rto_ming_high sctps_propinfo_tbl[22].prop_max_uval +#define sctps_rto_ming_low sctps_propinfo_tbl[22].prop_min_uval +#define sctps_rto_maxg sctps_propinfo_tbl[23].prop_cur_uval +#define sctps_rto_maxg_high sctps_propinfo_tbl[23].prop_max_uval +#define sctps_rto_maxg_low sctps_propinfo_tbl[23].prop_min_uval +#define sctps_rto_initialg sctps_propinfo_tbl[24].prop_cur_uval +#define sctps_rto_initialg_high sctps_propinfo_tbl[24].prop_max_uval +#define sctps_rto_initialg_low sctps_propinfo_tbl[24].prop_min_uval +#define sctps_cookie_life sctps_propinfo_tbl[25].prop_cur_uval +#define sctps_cookie_life_high sctps_propinfo_tbl[25].prop_max_uval +#define sctps_cookie_life_low sctps_propinfo_tbl[25].prop_min_uval +#define sctps_max_in_streams sctps_propinfo_tbl[26].prop_cur_uval +#define sctps_max_in_streams_high sctps_propinfo_tbl[26].prop_max_uval +#define sctps_max_in_streams_low sctps_propinfo_tbl[26].prop_min_uval +#define sctps_initial_out_streams sctps_propinfo_tbl[27].prop_cur_uval +#define sctps_initial_out_streams_high sctps_propinfo_tbl[27].prop_max_uval +#define sctps_initial_out_streams_low sctps_propinfo_tbl[27].prop_min_uval +#define sctps_shutack_wait_bound sctps_propinfo_tbl[28].prop_cur_uval +#define sctps_maxburst sctps_propinfo_tbl[29].prop_cur_uval +#define sctps_addip_enabled sctps_propinfo_tbl[30].prop_cur_bval +#define sctps_recv_hiwat_minmss sctps_propinfo_tbl[31].prop_cur_uval +#define sctps_slow_start_initial sctps_propinfo_tbl[32].prop_cur_uval +#define sctps_slow_start_after_idle sctps_propinfo_tbl[33].prop_cur_uval +#define sctps_prsctp_enabled sctps_propinfo_tbl[34].prop_cur_bval +#define sctps_fast_rxt_thresh sctps_propinfo_tbl[35].prop_cur_uval +#define sctps_deferred_acks_max sctps_propinfo_tbl[36].prop_cur_uval +#define sctps_wroff_xtra sctps_propinfo_tbl[37].prop_cur_uval /* * Retransmission timer start and stop macro for a given faddr. @@ -1005,9 +992,6 @@ extern mblk_t *sctp_make_sack(sctp_t *, sctp_faddr_t *, mblk_t *); extern void sctp_maxpsz_set(sctp_t *); extern void sctp_move_faddr_timers(queue_t *, sctp_t *); -extern void sctp_nd_free(sctp_stack_t *); -extern int sctp_nd_getset(queue_t *, MBLKP); -extern boolean_t sctp_nd_init(sctp_stack_t *); extern sctp_parm_hdr_t *sctp_next_parm(sctp_parm_hdr_t *, ssize_t *); extern void sctp_ootb_shutdown_ack(mblk_t *, uint_t, ip_recv_attr_t *, @@ -1016,7 +1000,6 @@ extern size_t sctp_options_param(const sctp_t *, void *, int); extern size_t sctp_options_param_len(const sctp_t *, int); extern void sctp_output(sctp_t *, uint_t); -extern boolean_t sctp_param_register(IDP *, sctpparam_t *, int, sctp_stack_t *); extern void sctp_partial_delivery_event(sctp_t *); extern int sctp_process_cookie(sctp_t *, sctp_chunk_hdr_t *, mblk_t *, sctp_init_chunk_t **, sctp_hdr_t *, int *, in6_addr_t *, @@ -1081,8 +1064,6 @@ extern void sctp_user_abort(sctp_t *, mblk_t *); extern void sctp_validate_peer(sctp_t *); -extern void sctp_wput_ioctl(queue_t *, mblk_t *); - extern int sctp_xmit_list_clean(sctp_t *, ssize_t); extern void sctp_zap_addrs(sctp_t *); diff --git a/usr/src/uts/common/inet/sctp/sctp_ioc.c b/usr/src/uts/common/inet/sctp/sctp_ioc.c deleted file mode 100644 index aecf61002e..0000000000 --- a/usr/src/uts/common/inet/sctp/sctp_ioc.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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 <sys/types.h> -#include <sys/stream.h> -#include <sys/strsubr.h> -#include <sys/stropts.h> -#include <sys/strsun.h> -#define _SUN_TPI_VERSION 2 -#include <sys/tihdr.h> -#include <sys/ddi.h> -#include <sys/sunddi.h> -#include <sys/errno.h> -#include <sys/socket.h> -#include <sys/kmem.h> -#include <sys/random.h> -#include <sys/policy.h> - -#include <netinet/in.h> - -#include <inet/common.h> -#include <inet/ip.h> -#include <inet/nd.h> -#include <inet/ipclassifier.h> -#include <inet/optcom.h> -#include <inet/sctp_ip.h> -#include "sctp_impl.h" - -/* - * sctp_wput_ioctl is called by sctp_wput to handle all - * M_IOCTL messages. - */ -void -sctp_wput_ioctl(queue_t *q, mblk_t *mp) -{ - conn_t *connp = (conn_t *)q->q_ptr; - struct iocblk *iocp; - - if (connp == NULL) { - ip0dbg(("sctp_wput_ioctl: null conn\n")); - return; - } - - iocp = (struct iocblk *)mp->b_rptr; - switch (iocp->ioc_cmd) { - case ND_SET: - /* sctp_nd_getset() -> nd_getset() does the checking. */ - case ND_GET: - if (!sctp_nd_getset(q, mp)) { - break; - } - qreply(q, mp); - return; - default: - iocp->ioc_error = EOPNOTSUPP; - break; - } -err_ret: - iocp->ioc_count = 0; - mp->b_datap->db_type = M_IOCNAK; - qreply(q, mp); -} - -/* - * A SCTP streams driver which is there just to handle ioctls on /dev/sctp. - */ -static int sctp_str_close(queue_t *); -static int sctp_str_open(queue_t *, dev_t *, int, int, cred_t *); - -static struct module_info sctp_mod_info = { - 5711, "sctp", 1, INFPSZ, 512, 128 -}; - -static struct qinit sctprinit = { - NULL, NULL, sctp_str_open, sctp_str_close, NULL, &sctp_mod_info -}; - -static struct qinit sctpwinit = { - (pfi_t)sctp_wput, NULL, NULL, NULL, NULL, &sctp_mod_info -}; - -struct streamtab sctpinfo = { - &sctprinit, &sctpwinit -}; - -static int -sctp_str_close(queue_t *q) -{ - conn_t *connp = Q_TO_CONN(q); - - qprocsoff(connp->conn_rq); - - ASSERT(connp->conn_ref == 1); - - inet_minor_free(connp->conn_minor_arena, connp->conn_dev); - - q->q_ptr = WR(q)->q_ptr = NULL; - CONN_DEC_REF(connp); - - return (0); -} - -/*ARGSUSED2*/ -static int -sctp_str_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp) -{ - conn_t *connp; - major_t maj; - netstack_t *ns; - zoneid_t zoneid; - - /* If the stream is already open, return immediately. */ - if (q->q_ptr != NULL) - return (0); - - /* If this is not a driver open, fail. */ - if (sflag == MODOPEN) - return (EINVAL); - - ns = netstack_find_by_cred(credp); - ASSERT(ns != NULL); - - /* - * For exclusive stacks we set the zoneid to zero - * to make IP operate as if in the global zone. - */ - if (ns->netstack_stackid != GLOBAL_NETSTACKID) - zoneid = GLOBAL_ZONEID; - else - zoneid = crgetzoneid(credp); - - /* - * We are opening as a device. This is an IP client stream, and we - * allocate an conn_t as the instance data. - */ - connp = ipcl_conn_create(IPCL_IPCCONN, KM_SLEEP, ns); - - /* - * ipcl_conn_create did a netstack_hold. Undo the hold that was - * done by netstack_find_by_cred() - */ - netstack_rele(ns); - - connp->conn_zoneid = zoneid; - connp->conn_ixa->ixa_flags |= IXAF_SET_ULP_CKSUM; - /* conn_allzones can not be set this early, hence no IPCL_ZONEID */ - connp->conn_ixa->ixa_zoneid = zoneid; - - connp->conn_rq = q; - connp->conn_wq = WR(q); - q->q_ptr = WR(q)->q_ptr = connp; - - if ((ip_minor_arena_la != NULL) && - (connp->conn_dev = inet_minor_alloc(ip_minor_arena_la)) != 0) { - connp->conn_minor_arena = ip_minor_arena_la; - } else { - /* - * Minor numbers in the large arena are exhausted. - * Try to allocate from the small arena. - */ - if ((connp->conn_dev = inet_minor_alloc(ip_minor_arena_sa)) - == 0) { - /* CONN_DEC_REF takes care of netstack_rele() */ - q->q_ptr = WR(q)->q_ptr = NULL; - CONN_DEC_REF(connp); - return (EBUSY); - } - connp->conn_minor_arena = ip_minor_arena_sa; - } - - maj = getemajor(*devp); - *devp = makedevice(maj, (minor_t)connp->conn_dev); - - /* - * connp->conn_cred is crfree()ed in ipcl_conn_destroy() - */ - ASSERT(connp->conn_cred == NULL); - connp->conn_cred = credp; - crhold(connp->conn_cred); - connp->conn_cpid = curproc->p_pid; - /* Cache things in ixa without an extra refhold */ - ASSERT(!(connp->conn_ixa->ixa_free_flags & IXA_FREE_CRED)); - connp->conn_ixa->ixa_cred = connp->conn_cred; - connp->conn_ixa->ixa_cpid = connp->conn_cpid; - if (is_system_labeled()) - connp->conn_ixa->ixa_tsl = crgetlabel(connp->conn_cred); - - /* - * Make the conn globally visible to walkers - */ - mutex_enter(&connp->conn_lock); - connp->conn_state_flags &= ~CONN_INCIPIENT; - mutex_exit(&connp->conn_lock); - ASSERT(connp->conn_ref == 1); - - qprocson(q); - - return (0); -} - - -/* - * The SCTP write put procedure which is used only to handle ioctls. - */ -void -sctp_wput(queue_t *q, mblk_t *mp) -{ - uchar_t *rptr; - t_scalar_t type; - - switch (mp->b_datap->db_type) { - case M_IOCTL: - sctp_wput_ioctl(q, mp); - break; - case M_DATA: - /* Should be handled in sctp_output() */ - ASSERT(0); - freemsg(mp); - break; - case M_PROTO: - case M_PCPROTO: - rptr = mp->b_rptr; - if ((mp->b_wptr - rptr) >= sizeof (t_scalar_t)) { - type = ((union T_primitives *)rptr)->type; - /* - * There is no "standard" way on how to respond - * to T_CAPABILITY_REQ if a module does not - * understand it. And the current TI mod - * has problems handling an error ack. So we - * catch the request here and reply with a response - * which the TI mod knows how to respond to. - */ - switch (type) { - case T_CAPABILITY_REQ: - (void) putnextctl1(RD(q), M_ERROR, EPROTO); - break; - default: - if ((mp = mi_tpi_err_ack_alloc(mp, - TNOTSUPPORT, 0)) != NULL) { - qreply(q, mp); - return; - } - } - } - /* FALLTHRU */ - default: - freemsg(mp); - return; - } -} diff --git a/usr/src/uts/common/inet/sctp/sctp_param.c b/usr/src/uts/common/inet/sctp/sctp_param.c deleted file mode 100644 index 26365c5a06..0000000000 --- a/usr/src/uts/common/inet/sctp/sctp_param.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * 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 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include <sys/stream.h> -#include <sys/socket.h> -#include <sys/ddi.h> -#include <sys/sunddi.h> - -#include <netinet/in.h> -#include <netinet/ip6.h> - -#include <inet/common.h> -#include <inet/ip.h> -#include <inet/ip6.h> -#include <inet/mi.h> -#include <inet/mib2.h> -#include <inet/nd.h> -#include <inet/ipclassifier.h> -#include "sctp_impl.h" -#include "sctp_addr.h" - -#define MS 1L -#define SECONDS (1000 * MS) -#define MINUTES (60 * SECONDS) -#define HOURS (60 * MINUTES) -#define DAYS (24 * HOURS) - -#define PARAM_MAX (~(uint32_t)0) - -/* Max size IP datagram is 64k - 1 */ -#define SCTP_MSS_MAX_IPV4 (IP_MAXPACKET - (sizeof (ipha_t) + \ - sizeof (sctp_hdr_t))) -#define SCTP_MSS_MAX_IPV6 (IP_MAXPACKET - (sizeof (ip6_t) + \ - sizeof (sctp_hdr_t))) -/* Max of the above */ -#define SCTP_MSS_MAX SCTP_MSS_MAX_IPV4 - -/* Largest SCTP port number */ -#define SCTP_MAX_PORT (64 * 1024 - 1) - -/* - * Extra privileged ports. In host byte order. - * Protected by sctp_epriv_port_lock. - */ -#define SCTP_NUM_EPRIV_PORTS 64 - -/* - * sctp_wroff_xtra is the extra space in front of SCTP/IP header for link - * layer header. It has to be a multiple of 4. - */ -sctpparam_t lcl_sctp_wroff_xtra_param = { 0, 256, 32, "sctp_wroff_xtra" }; - -/* - * All of these are alterable, within the min/max values given, at run time. - * Note that the default value of "sctp_time_wait_interval" is four minutes, - * per the SCTP spec. - */ -/* BEGIN CSTYLED */ -sctpparam_t lcl_sctp_param_arr[] = { - /*min max value name */ - { 0, 128, 8, "sctp_max_init_retr"}, - { 1, 128, 10, "sctp_pa_max_retr"}, - { 1, 128, 5, "sctp_pp_max_retr" }, - { 128, (1<<30), 1024*1024, "sctp_cwnd_max" }, - { 0, 10, 0, "sctp_debug" }, - { 1024, (32*1024), 1024, "sctp_smallest_nonpriv_port"}, - { 1, 255, 64, "sctp_ipv4_ttl"}, - { 0, 1*DAYS, 30*SECONDS, "sctp_heartbeat_interval"}, - { 68, 65535, 1500, "sctp_initial_mtu" }, - { 0, 1*DAYS, 10*MINUTES, "sctp_mtu_probe_interval"}, - { 0, 1*DAYS, 2*MINUTES, "sctp_new_secret_interval"}, - { 10*MS, 1*MINUTES, 100*MS, "sctp_deferred_ack_interval" }, - { 0, 16, 0, "sctp_snd_lowat_fraction" }, - { 0, 1, 0, "sctp_ignore_path_mtu" }, - { 1024, PARAM_MAX, SCTP_RECV_HIWATER,"sctp_initial_ssthresh" }, - { 1024, SCTP_MAX_PORT, 32*1024, "sctp_smallest_anon_port"}, - { 1024, SCTP_MAX_PORT, SCTP_MAX_PORT, "sctp_largest_anon_port"}, - { SCTP_XMIT_LOWATER, (1<<30), SCTP_XMIT_HIWATER,"sctp_xmit_hiwat"}, - { SCTP_XMIT_LOWATER, (1<<30), SCTP_XMIT_LOWATER,"sctp_xmit_lowat"}, - { SCTP_RECV_LOWATER, (1<<30), SCTP_RECV_HIWATER,"sctp_recv_hiwat"}, - { 8192, (1<<30), 1024*1024, "sctp_max_buf"}, - { 0, 65536, 20, "sctp_rtt_updates"}, - { 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS, "sctp_ipv6_hoplimit"}, - { 500*MS, 60*SECONDS, 1*SECONDS, "sctp_rto_min"}, - { 1*SECONDS, 60000*SECONDS, 60*SECONDS, "sctp_rto_max"}, - { 1*SECONDS, 60000*SECONDS, 3*SECONDS, "sctp_rto_initial"}, - { 10*MS, 60000*SECONDS, 60*SECONDS, "sctp_cookie_life"}, - { 1, UINT16_MAX, 32, "sctp_max_in_streams"}, - { 1, UINT16_MAX, 32, "sctp_initial_out_streams"}, - { 0, 300*SECONDS, 60*SECONDS, "sctp_shutack_wait_bound" }, - { 2, 8, 4, "sctp_maxburst" }, - { 0, 1, 0, "sctp_addip_enabled" }, - { 1, 65536, 4, "sctp_recv_hiwat_minmss" }, - { 1, 16, 4, "sctp_slow_start_initial"}, - { 1, 16384, 4, "sctp_slow_start_after_idle"}, - { 0, 1, 1, "sctp_prsctp_enabled"}, - { 1, 10000, 3, "sctp_fast_rxt_thresh"}, - { 1, 16, 2, "sctp_deferred_acks_max"}, -}; -/* END CSTYLED */ - -/* Get callback routine passed to nd_load by sctp_param_register */ -/* ARGSUSED */ -static int -sctp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - sctpparam_t *sctppa = (sctpparam_t *)cp; - - (void) mi_mpprintf(mp, "%u", sctppa->sctp_param_val); - return (0); -} - -/* Set callback routine passed to nd_load by sctp_param_register */ -/* ARGSUSED */ -static int -sctp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr) -{ - long new_value; - sctpparam_t *sctppa = (sctpparam_t *)cp; - - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < sctppa->sctp_param_min || - new_value > sctppa->sctp_param_max) { - return (EINVAL); - } - sctppa->sctp_param_val = new_value; - return (0); -} - -/* ndd set routine for sctp_wroff_xtra. */ -/* ARGSUSED */ -static int -sctp_wroff_xtra_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - sctpparam_t *sctppa = (sctpparam_t *)cp; - - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < sctppa->sctp_param_min || - new_value > sctppa->sctp_param_max) { - return (EINVAL); - } - /* - * Need to make sure new_value is a multiple of 8. If it is not, - * round it up. - */ - if (new_value & 0x7) { - new_value = (new_value & ~0x7) + 0x8; - } - sctppa->sctp_param_val = new_value; - return (0); -} - -/* - * Note: No locks are held when inspecting sctp_g_*epriv_ports - * but instead the code relies on: - * - the fact that the address of the array and its size never changes - * - the atomic assignment of the elements of the array - */ -/* ARGSUSED */ -static int -sctp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - int i; - sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; - - for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { - if (sctps->sctps_g_epriv_ports[i] != 0) - (void) mi_mpprintf(mp, "%d ", - sctps->sctps_g_epriv_ports[i]); - } - return (0); -} - -/* - * Hold a lock while changing sctp_g_epriv_ports to prevent multiple - * threads from changing it at the same time. - */ -/* ARGSUSED */ -static int -sctp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - int i; - sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; - - /* - * Fail the request if the new value does not lie within the - * port number limits. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value <= 0 || new_value >= 65536) { - return (EINVAL); - } - - mutex_enter(&sctps->sctps_epriv_port_lock); - /* Check if the value is already in the list */ - for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { - if (new_value == sctps->sctps_g_epriv_ports[i]) { - mutex_exit(&sctps->sctps_epriv_port_lock); - return (EEXIST); - } - } - /* Find an empty slot */ - for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { - if (sctps->sctps_g_epriv_ports[i] == 0) - break; - } - if (i == sctps->sctps_g_num_epriv_ports) { - mutex_exit(&sctps->sctps_epriv_port_lock); - return (EOVERFLOW); - } - /* Set the new value */ - sctps->sctps_g_epriv_ports[i] = (uint16_t)new_value; - mutex_exit(&sctps->sctps_epriv_port_lock); - return (0); -} - -/* - * Hold a lock while changing sctp_g_epriv_ports to prevent multiple - * threads from changing it at the same time. - */ -/* ARGSUSED */ -static int -sctp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - int i; - sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; - - /* - * Fail the request if the new value does not lie within the - * port number limits. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value <= 0 || new_value >= 65536) { - return (EINVAL); - } - - mutex_enter(&sctps->sctps_epriv_port_lock); - /* Check that the value is already in the list */ - for (i = 0; i < sctps->sctps_g_num_epriv_ports; i++) { - if (sctps->sctps_g_epriv_ports[i] == new_value) - break; - } - if (i == sctps->sctps_g_num_epriv_ports) { - mutex_exit(&sctps->sctps_epriv_port_lock); - return (ESRCH); - } - /* Clear the value */ - sctps->sctps_g_epriv_ports[i] = 0; - mutex_exit(&sctps->sctps_epriv_port_lock); - return (0); -} - -/* - * Walk through the param array specified registering each element with the - * named dispatch handler. - */ -boolean_t -sctp_param_register(IDP *ndp, sctpparam_t *sctppa, int cnt, sctp_stack_t *sctps) -{ - - if (*ndp != NULL) { - return (B_TRUE); - } - - for (; cnt-- > 0; sctppa++) { - if (sctppa->sctp_param_name && sctppa->sctp_param_name[0]) { - if (!nd_load(ndp, sctppa->sctp_param_name, - sctp_param_get, sctp_param_set, - (caddr_t)sctppa)) { - nd_free(ndp); - return (B_FALSE); - } - } - } - sctps->sctps_wroff_xtra_param = kmem_zalloc(sizeof (sctpparam_t), - KM_SLEEP); - bcopy(&lcl_sctp_wroff_xtra_param, sctps->sctps_wroff_xtra_param, - sizeof (sctpparam_t)); - if (!nd_load(ndp, sctps->sctps_wroff_xtra_param->sctp_param_name, - sctp_param_get, sctp_wroff_xtra_set, - (caddr_t)sctps->sctps_wroff_xtra_param)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "sctp_extra_priv_ports", - sctp_extra_priv_ports_get, NULL, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "sctp_extra_priv_ports_add", - NULL, sctp_extra_priv_ports_add, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "sctp_extra_priv_ports_del", - NULL, sctp_extra_priv_ports_del, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - return (B_TRUE); -} - -boolean_t -sctp_nd_init(sctp_stack_t *sctps) -{ - sctpparam_t *pa; - - pa = kmem_alloc(sizeof (lcl_sctp_param_arr), KM_SLEEP); - bcopy(lcl_sctp_param_arr, pa, sizeof (lcl_sctp_param_arr)); - sctps->sctps_params = pa; - return (sctp_param_register(&sctps->sctps_g_nd, pa, - A_CNT(lcl_sctp_param_arr), sctps)); -} - -int -sctp_nd_getset(queue_t *q, MBLKP mp) -{ - sctp_stack_t *sctps = Q_TO_CONN(q)->conn_netstack->netstack_sctp; - - return (nd_getset(q, sctps->sctps_g_nd, mp)); -} - -void -sctp_nd_free(sctp_stack_t *sctps) -{ - nd_free(&sctps->sctps_g_nd); - kmem_free(sctps->sctps_params, sizeof (lcl_sctp_param_arr)); - sctps->sctps_params = NULL; - kmem_free(sctps->sctps_wroff_xtra_param, sizeof (sctpparam_t)); - sctps->sctps_wroff_xtra_param = NULL; - -} diff --git a/usr/src/uts/common/inet/sctp/sctp_stack.h b/usr/src/uts/common/inet/sctp/sctp_stack.h index e9ad5cf9c7..c4c6b4955f 100644 --- a/usr/src/uts/common/inet/sctp/sctp_stack.h +++ b/usr/src/uts/common/inet/sctp/sctp_stack.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. */ @@ -80,7 +80,7 @@ struct sctp_stack { #define SCTP_NUM_EPRIV_PORTS 64 int sctps_g_num_epriv_ports; - uint16_t sctps_g_epriv_ports[SCTP_NUM_EPRIV_PORTS]; + in_port_t sctps_g_epriv_ports[SCTP_NUM_EPRIV_PORTS]; kmutex_t sctps_epriv_port_lock; uint_t sctps_next_port_to_try; @@ -91,10 +91,8 @@ struct sctp_stack { struct sctp_tf_s *sctps_conn_fanout; uint_t sctps_conn_hash_size; - /* Only modified during _init and _fini thus no locking is needed. */ - caddr_t sctps_g_nd; - struct sctpparam_s *sctps_params; - struct sctpparam_s *sctps_wroff_xtra_param; + /* holds sctp tunables */ + struct mod_prop_info_s *sctps_propinfo_tbl; /* This lock protects the SCTP recvq_tq_list array and recvq_tq_list_cur_sz. */ kmutex_t sctps_rq_tq_lock; diff --git a/usr/src/uts/common/inet/sctp/sctp_tunables.c b/usr/src/uts/common/inet/sctp/sctp_tunables.c new file mode 100644 index 0000000000..8d3bd6fb52 --- /dev/null +++ b/usr/src/uts/common/inet/sctp/sctp_tunables.c @@ -0,0 +1,218 @@ +/* + * 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 <inet/ip.h> +#include <inet/ip6.h> +#include <inet/sctp/sctp_stack.h> +#include <inet/sctp/sctp_impl.h> +#include <sys/sunddi.h> + +/* Max size IP datagram is 64k - 1 */ +#define SCTP_MSS_MAX_IPV4 (IP_MAXPACKET - (sizeof (ipha_t) + \ + sizeof (sctp_hdr_t))) +#define SCTP_MSS_MAX_IPV6 (IP_MAXPACKET - (sizeof (ip6_t) + \ + sizeof (sctp_hdr_t))) +/* Max of the above */ +#define SCTP_MSS_MAX SCTP_MSS_MAX_IPV4 + +/* + * All of these are alterable, within the min/max values given, at run time. + * + * Note: All those tunables which do not start with "sctp_" are Committed and + * therefore are public. See PSARC 2009/306. + */ +mod_prop_info_t sctp_propinfo_tbl[] = { + { "sctp_max_init_retr", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 128, 8}, {8} }, + + { "sctp_pa_max_retr", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 128, 10}, {10} }, + + { "sctp_pp_max_retr", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 128, 5}, {5} }, + + { "sctp_cwnd_max", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {128, (1<<30), 1024*1024}, {1024*1024} }, + + { "smallest_nonpriv_port", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1024, (32*1024), 1024}, {1024} }, + + { "sctp_ipv4_ttl", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 255, 64}, {64} }, + + { "sctp_heartbeat_interval", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 1*DAYS, 30*SECONDS}, {30*SECONDS} }, + + { "sctp_initial_mtu", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {68, 65535, 1500}, {1500} }, + + { "sctp_mtu_probe_interval", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 1*DAYS, 10*MINUTES}, {10*MINUTES} }, + + { "sctp_new_secret_interval", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 1*DAYS, 2*MINUTES}, {2*MINUTES} }, + + /* tunable - 10 */ + { "sctp_deferred_ack_interval", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {10*MS, 1*MINUTES, 100*MS}, {100*MS} }, + + { "sctp_snd_lowat_fraction", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 16, 0}, {0} }, + + { "sctp_ignore_path_mtu", MOD_PROTO_SCTP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "sctp_initial_ssthresh", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1024, UINT32_MAX, SCTP_RECV_HIWATER}, { SCTP_RECV_HIWATER} }, + + { "smallest_anon_port", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1024, ULP_MAX_PORT, 32*1024}, {32*1024} }, + + { "largest_anon_port", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1024, ULP_MAX_PORT, ULP_MAX_PORT}, {ULP_MAX_PORT} }, + + { "send_maxbuf", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {SCTP_XMIT_LOWATER, (1<<30), SCTP_XMIT_HIWATER}, + {SCTP_XMIT_HIWATER} }, + + { "sctp_xmit_lowat", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {SCTP_XMIT_LOWATER, (1<<30), SCTP_XMIT_LOWATER}, + {SCTP_XMIT_LOWATER} }, + + { "recv_maxbuf", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {SCTP_RECV_LOWATER, (1<<30), SCTP_RECV_HIWATER}, + {SCTP_RECV_HIWATER} }, + + { "sctp_max_buf", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {8192, (1<<30), 1024*1024}, {1024*1024} }, + + /* tunable - 20 */ + { "sctp_rtt_updates", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 65536, 20}, {20} }, + + { "sctp_ipv6_hoplimit", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS}, {IPV6_DEFAULT_HOPS} }, + + { "sctp_rto_min", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {500*MS, 60*SECONDS, 1*SECONDS}, {1*SECONDS} }, + + { "sctp_rto_max", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, 60000*SECONDS, 60*SECONDS}, {60*SECONDS} }, + + { "sctp_rto_initial", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, 60000*SECONDS, 3*SECONDS}, {3*SECONDS} }, + + { "sctp_cookie_life", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {10*MS, 60000*SECONDS, 60*SECONDS}, {60*SECONDS} }, + + { "sctp_max_in_streams", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, UINT16_MAX, 32}, {32} }, + + { "sctp_initial_out_streams", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, UINT16_MAX, 32}, {32} }, + + { "sctp_shutack_wait_bound", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {0, 300*SECONDS, 60*SECONDS}, {60*SECONDS} }, + + { "sctp_maxburst", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {2, 8, 4}, {4} }, + + /* tunable - 30 */ + { "sctp_addip_enabled", MOD_PROTO_SCTP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "sctp_recv_hiwat_minmss", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 65536, 4}, {4} }, + + { "sctp_slow_start_initial", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 16, 4}, {4} }, + + { "sctp_slow_start_after_idle", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 16384, 4}, {4} }, + + { "sctp_prsctp_enabled", MOD_PROTO_SCTP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "sctp_fast_rxt_thresh", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + {1, 10000, 3}, {3} }, + + { "sctp_deferred_acks_max", MOD_PROTO_SCTP, + mod_set_uint32, mod_get_uint32, + { 1, 16, 2}, {2} }, + + /* + * sctp_wroff_xtra is the extra space in front of SCTP/IP header + * for link layer header. It has to be a multiple of 8. + */ + { "sctp_wroff_xtra", MOD_PROTO_SCTP, + mod_set_aligned, mod_get_uint32, + {0, 256, 32}, {32} }, + + { "extra_priv_ports", MOD_PROTO_SCTP, + mod_set_extra_privports, mod_get_extra_privports, + {1, ULP_MAX_PORT, 0}, {0} }, + + { "?", MOD_PROTO_SCTP, NULL, mod_get_allprop, {0}, {0} }, + + { NULL, 0, NULL, NULL, {0}, {0} } +}; + +int sctp_propinfo_count = A_CNT(sctp_propinfo_tbl); diff --git a/usr/src/uts/common/inet/sctp_ip.h b/usr/src/uts/common/inet/sctp_ip.h index 9e4c2ef7ec..95c74bcbfe 100644 --- a/usr/src/uts/common/inet/sctp_ip.h +++ b/usr/src/uts/common/inet/sctp_ip.h @@ -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. */ @@ -45,7 +45,6 @@ extern conn_t *sctp_fanout(in6_addr_t *, in6_addr_t *, uint32_t, ip_recv_attr_t *, mblk_t *, sctp_stack_t *); extern void sctp_input(conn_t *, ipha_t *, ip6_t *, mblk_t *, ip_recv_attr_t *); -extern void sctp_wput(queue_t *, mblk_t *); extern void sctp_ootb_input(mblk_t *, ip_recv_attr_t *, ip_stack_t *); extern void sctp_hash_init(sctp_stack_t *); extern void sctp_hash_destroy(sctp_stack_t *); diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c index 63f99b1282..2cc0377fd4 100644 --- a/usr/src/uts/common/inet/tcp/tcp.c +++ b/usr/src/uts/common/inet/tcp/tcp.c @@ -77,7 +77,6 @@ #include <inet/ip_ndp.h> #include <inet/proto_set.h> #include <inet/mib2.h> -#include <inet/nd.h> #include <inet/optcom.h> #include <inet/snmpcom.h> #include <inet/kstatcom.h> @@ -285,23 +284,7 @@ static int tcp_connect_ipv4(tcp_t *tcp, ipaddr_t *dstaddrp, static int tcp_connect_ipv6(tcp_t *tcp, in6_addr_t *dstaddrp, in_port_t dstport, uint32_t flowinfo, uint_t srcid, uint32_t scope_id); -static int tcp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, - cred_t *cr); -static int tcp_extra_priv_ports_add(queue_t *q, mblk_t *mp, - char *value, caddr_t cp, cred_t *cr); -static int tcp_extra_priv_ports_del(queue_t *q, mblk_t *mp, - char *value, caddr_t cp, cred_t *cr); static void tcp_iss_init(tcp_t *tcp); -static int tcp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr); -static boolean_t tcp_param_register(IDP *ndp, tcpparam_t *tcppa, int cnt, - tcp_stack_t *); -static int tcp_param_set(queue_t *q, mblk_t *mp, char *value, - caddr_t cp, cred_t *cr); -static int tcp_param_set_aligned(queue_t *q, mblk_t *mp, char *value, - caddr_t cp, cred_t *cr); -static void tcp_iss_key_init(uint8_t *phrase, int len, tcp_stack_t *); -static int tcp_1948_phrase_set(queue_t *q, mblk_t *mp, char *value, - caddr_t cp, cred_t *cr); static void tcp_reinit(tcp_t *tcp); static void tcp_reinit_values(tcp_t *tcp); @@ -415,98 +398,14 @@ struct T_info_ack tcp_g_t_info_ack_v6 = { (XPG4_1|EXPINLINE) /* PROVIDER_flag */ }; -#define PARAM_MAX (~(uint32_t)0) - -/* Max size IP datagram is 64k - 1 */ -#define TCP_MSS_MAX_IPV4 (IP_MAXPACKET - (sizeof (ipha_t) + sizeof (tcpha_t))) -#define TCP_MSS_MAX_IPV6 (IP_MAXPACKET - (sizeof (ip6_t) + sizeof (tcpha_t))) -/* Max of the above */ -#define TCP_MSS_MAX TCP_MSS_MAX_IPV4 - -/* Largest TCP port number */ -#define TCP_MAX_PORT (64 * 1024 - 1) - /* - * tcp_wroff_xtra is the extra space in front of TCP/IP header for link - * layer header. It has to be a multiple of 4. + * TCP tunables related declarations. Definitions are in tcp_tunables.c */ -static tcpparam_t lcl_tcp_wroff_xtra_param = { 0, 256, 32, "tcp_wroff_xtra" }; +extern mod_prop_info_t tcp_propinfo_tbl[]; +extern int tcp_propinfo_count; #define MB (1024 * 1024) -/* - * All of these are alterable, within the min/max values given, at run time. - * Note that the default value of "tcp_time_wait_interval" is four minutes, - * per the TCP spec. - */ -/* BEGIN CSTYLED */ -static tcpparam_t lcl_tcp_param_arr[] = { - /*min max value name */ - { 1*SECONDS, 10*MINUTES, 1*MINUTES, "tcp_time_wait_interval"}, - { 1, PARAM_MAX, 128, "tcp_conn_req_max_q" }, - { 0, PARAM_MAX, 1024, "tcp_conn_req_max_q0" }, - { 1, 1024, 1, "tcp_conn_req_min" }, - { 0*MS, 20*SECONDS, 0*MS, "tcp_conn_grace_period" }, - { 128, (1<<30), 1*MB, "tcp_cwnd_max" }, - { 0, 10, 0, "tcp_debug" }, - { 1024, (32*1024), 1024, "tcp_smallest_nonpriv_port"}, - { 1*SECONDS, PARAM_MAX, 3*MINUTES, "tcp_ip_abort_cinterval"}, - { 1*SECONDS, PARAM_MAX, 3*MINUTES, "tcp_ip_abort_linterval"}, - { 500*MS, PARAM_MAX, 5*MINUTES, "tcp_ip_abort_interval"}, - { 1*SECONDS, PARAM_MAX, 10*SECONDS, "tcp_ip_notify_cinterval"}, - { 500*MS, PARAM_MAX, 10*SECONDS, "tcp_ip_notify_interval"}, - { 1, 255, 64, "tcp_ipv4_ttl"}, - { 10*SECONDS, 10*DAYS, 2*HOURS, "tcp_keepalive_interval"}, - { 0, 100, 10, "tcp_maxpsz_multiplier" }, - { 1, TCP_MSS_MAX_IPV4, 536, "tcp_mss_def_ipv4"}, - { 1, TCP_MSS_MAX_IPV4, TCP_MSS_MAX_IPV4, "tcp_mss_max_ipv4"}, - { 1, TCP_MSS_MAX, 108, "tcp_mss_min"}, - { 1, (64*1024)-1, (4*1024)-1, "tcp_naglim_def"}, - { 1*MS, 20*SECONDS, 1*SECONDS, "tcp_rexmit_interval_initial"}, - { 1*MS, 2*HOURS, 60*SECONDS, "tcp_rexmit_interval_max"}, - { 1*MS, 2*HOURS, 400*MS, "tcp_rexmit_interval_min"}, - { 1*MS, 1*MINUTES, 100*MS, "tcp_deferred_ack_interval" }, - { 0, 16, 0, "tcp_snd_lowat_fraction" }, - { 1, 10000, 3, "tcp_dupack_fast_retransmit" }, - { 0, 1, 0, "tcp_ignore_path_mtu" }, - { 1024, TCP_MAX_PORT, 32*1024, "tcp_smallest_anon_port"}, - { 1024, TCP_MAX_PORT, TCP_MAX_PORT, "tcp_largest_anon_port"}, - { TCP_XMIT_LOWATER, (1<<30), TCP_XMIT_HIWATER,"tcp_xmit_hiwat"}, - { TCP_XMIT_LOWATER, (1<<30), TCP_XMIT_LOWATER,"tcp_xmit_lowat"}, - { TCP_RECV_LOWATER, (1<<30), TCP_RECV_HIWATER,"tcp_recv_hiwat"}, - { 1, 65536, 4, "tcp_recv_hiwat_minmss"}, - { 1*SECONDS, PARAM_MAX, 675*SECONDS, "tcp_fin_wait_2_flush_interval"}, - { 8192, (1<<30), 1*MB, "tcp_max_buf"}, -/* - * Question: What default value should I set for tcp_strong_iss? - */ - { 0, 2, 1, "tcp_strong_iss"}, - { 0, 65536, 20, "tcp_rtt_updates"}, - { 0, 1, 1, "tcp_wscale_always"}, - { 0, 1, 0, "tcp_tstamp_always"}, - { 0, 1, 1, "tcp_tstamp_if_wscale"}, - { 0*MS, 2*HOURS, 0*MS, "tcp_rexmit_interval_extra"}, - { 0, 16, 2, "tcp_deferred_acks_max"}, - { 1, 16384, 4, "tcp_slow_start_after_idle"}, - { 1, 4, 4, "tcp_slow_start_initial"}, - { 0, 2, 2, "tcp_sack_permitted"}, - { 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS, "tcp_ipv6_hoplimit"}, - { 1, TCP_MSS_MAX_IPV6, 1220, "tcp_mss_def_ipv6"}, - { 1, TCP_MSS_MAX_IPV6, TCP_MSS_MAX_IPV6, "tcp_mss_max_ipv6"}, - { 0, 1, 0, "tcp_rev_src_routes"}, - { 10*MS, 500*MS, 50*MS, "tcp_local_dack_interval"}, - { 0, 16, 8, "tcp_local_dacks_max"}, - { 0, 2, 1, "tcp_ecn_permitted"}, - { 0, 1, 1, "tcp_rst_sent_rate_enabled"}, - { 0, PARAM_MAX, 40, "tcp_rst_sent_rate"}, - { 0, 100*MS, 50*MS, "tcp_push_timer_interval"}, - { 0, 1, 0, "tcp_use_smss_as_mss_opt"}, - { 0, PARAM_MAX, 8*MINUTES, "tcp_keepalive_abort_interval"}, - { 0, 1, 0, "tcp_dev_flow_ctl"}, - { 0, PARAM_MAX, 100*SECONDS, "tcp_reass_timeout"} -}; -/* END CSTYLED */ - #define IS_VMLOANED_MBLK(mp) \ (((mp)->b_datap->db_struioflag & STRUIO_ZC) != 0) @@ -1957,110 +1856,6 @@ tcp_disconnect(tcp_t *tcp, mblk_t *mp) } /* - * Note: No locks are held when inspecting tcp_g_*epriv_ports - * but instead the code relies on: - * - the fact that the address of the array and its size never changes - * - the atomic assignment of the elements of the array - */ -/* ARGSUSED */ -static int -tcp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - int i; - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - - for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) { - if (tcps->tcps_g_epriv_ports[i] != 0) - (void) mi_mpprintf(mp, "%d ", - tcps->tcps_g_epriv_ports[i]); - } - return (0); -} - -/* - * Hold a lock while changing tcp_g_epriv_ports to prevent multiple - * threads from changing it at the same time. - */ -/* ARGSUSED */ -static int -tcp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - int i; - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - - /* - * Fail the request if the new value does not lie within the - * port number limits. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value <= 0 || new_value >= 65536) { - return (EINVAL); - } - - mutex_enter(&tcps->tcps_epriv_port_lock); - /* Check if the value is already in the list */ - for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) { - if (new_value == tcps->tcps_g_epriv_ports[i]) { - mutex_exit(&tcps->tcps_epriv_port_lock); - return (EEXIST); - } - } - /* Find an empty slot */ - for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) { - if (tcps->tcps_g_epriv_ports[i] == 0) - break; - } - if (i == tcps->tcps_g_num_epriv_ports) { - mutex_exit(&tcps->tcps_epriv_port_lock); - return (EOVERFLOW); - } - /* Set the new value */ - tcps->tcps_g_epriv_ports[i] = (uint16_t)new_value; - mutex_exit(&tcps->tcps_epriv_port_lock); - return (0); -} - -/* - * Hold a lock while changing tcp_g_epriv_ports to prevent multiple - * threads from changing it at the same time. - */ -/* ARGSUSED */ -static int -tcp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - int i; - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - - /* - * Fail the request if the new value does not lie within the - * port number limits. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || new_value <= 0 || - new_value >= 65536) { - return (EINVAL); - } - - mutex_enter(&tcps->tcps_epriv_port_lock); - /* Check that the value is already in the list */ - for (i = 0; i < tcps->tcps_g_num_epriv_ports; i++) { - if (tcps->tcps_g_epriv_ports[i] == new_value) - break; - } - if (i == tcps->tcps_g_num_epriv_ports) { - mutex_exit(&tcps->tcps_epriv_port_lock); - return (ESRCH); - } - /* Clear the value */ - tcps->tcps_g_epriv_ports[i] = 0; - mutex_exit(&tcps->tcps_epriv_port_lock); - return (0); -} - -/* * Handle reinitialization of a tcp structure. * Maintain "binding state" resetting the state to BOUND, LISTEN, or IDLE. */ @@ -3068,139 +2863,6 @@ tcp_build_hdrs(tcp_t *tcp) return (0); } -/* Get callback routine passed to nd_load by tcp_param_register */ -/* ARGSUSED */ -static int -tcp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - tcpparam_t *tcppa = (tcpparam_t *)cp; - - (void) mi_mpprintf(mp, "%u", tcppa->tcp_param_val); - return (0); -} - -/* - * Walk through the param array specified registering each element with the - * named dispatch handler. - */ -static boolean_t -tcp_param_register(IDP *ndp, tcpparam_t *tcppa, int cnt, tcp_stack_t *tcps) -{ - for (; cnt-- > 0; tcppa++) { - if (tcppa->tcp_param_name && tcppa->tcp_param_name[0]) { - if (!nd_load(ndp, tcppa->tcp_param_name, - tcp_param_get, tcp_param_set, - (caddr_t)tcppa)) { - nd_free(ndp); - return (B_FALSE); - } - } - } - tcps->tcps_wroff_xtra_param = kmem_zalloc(sizeof (tcpparam_t), - KM_SLEEP); - bcopy(&lcl_tcp_wroff_xtra_param, tcps->tcps_wroff_xtra_param, - sizeof (tcpparam_t)); - if (!nd_load(ndp, tcps->tcps_wroff_xtra_param->tcp_param_name, - tcp_param_get, tcp_param_set_aligned, - (caddr_t)tcps->tcps_wroff_xtra_param)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "tcp_extra_priv_ports", - tcp_extra_priv_ports_get, NULL, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "tcp_extra_priv_ports_add", - NULL, tcp_extra_priv_ports_add, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "tcp_extra_priv_ports_del", - NULL, tcp_extra_priv_ports_del, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "tcp_1948_phrase", NULL, - tcp_1948_phrase_set, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - - - if (!nd_load(ndp, "tcp_listener_limit_conf", - tcp_listener_conf_get, NULL, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "tcp_listener_limit_conf_add", - NULL, tcp_listener_conf_add, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "tcp_listener_limit_conf_del", - NULL, tcp_listener_conf_del, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - - /* - * Dummy ndd variables - only to convey obsolescence information - * through printing of their name (no get or set routines) - * XXX Remove in future releases ? - */ - if (!nd_load(ndp, - "tcp_close_wait_interval(obsoleted - " - "use tcp_time_wait_interval)", NULL, NULL, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - return (B_TRUE); -} - -/* ndd set routine for tcp_wroff_xtra. */ -/* ARGSUSED */ -static int -tcp_param_set_aligned(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - tcpparam_t *tcppa = (tcpparam_t *)cp; - - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < tcppa->tcp_param_min || - new_value > tcppa->tcp_param_max) { - return (EINVAL); - } - /* - * Need to make sure new_value is a multiple of 4. If it is not, - * round it up. For future 64 bit requirement, we actually make it - * a multiple of 8. - */ - if (new_value & 0x7) { - new_value = (new_value & ~0x7) + 0x8; - } - tcppa->tcp_param_val = new_value; - return (0); -} - -/* Set callback routine passed to nd_load by tcp_param_register */ -/* ARGSUSED */ -static int -tcp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr) -{ - long new_value; - tcpparam_t *tcppa = (tcpparam_t *)cp; - - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < tcppa->tcp_param_min || - new_value > tcppa->tcp_param_max) { - return (EINVAL); - } - tcppa->tcp_param_val = new_value; - return (0); -} - /* * tcp_rwnd_set() is called to adjust the receive window to a desired value. * We do not allow the receive window to shrink. After setting rwnd, @@ -4292,7 +3954,7 @@ tcp_random(void) #define PASSWD_SIZE 16 /* MUST be multiple of 4 */ -static void +void tcp_iss_key_init(uint8_t *phrase, int len, tcp_stack_t *tcps) { struct { @@ -4346,23 +4008,6 @@ tcp_iss_key_init(uint8_t *phrase, int len, tcp_stack_t *tcps) mutex_exit(&tcps->tcps_iss_key_lock); } -/* - * Set the RFC 1948 pass phrase - */ -/* ARGSUSED */ -static int -tcp_1948_phrase_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - - /* - * Basically, value contains a new pass phrase. Pass it along! - */ - tcp_iss_key_init((uint8_t *)value, strlen(value), tcps); - return (0); -} - /* ARGSUSED */ static int tcp_sack_info_constructor(void *buf, void *cdrarg, int kmflags) @@ -4419,10 +4064,10 @@ static void * tcp_stack_init(netstackid_t stackid, netstack_t *ns) { tcp_stack_t *tcps; - tcpparam_t *pa; int i; int error = 0; major_t major; + size_t arrsz; tcps = (tcp_stack_t *)kmem_zalloc(sizeof (*tcps), KM_SLEEP); tcps->tcps_netstack = ns; @@ -4432,8 +4077,8 @@ tcp_stack_init(netstackid_t stackid, netstack_t *ns) mutex_init(&tcps->tcps_epriv_port_lock, NULL, MUTEX_DEFAULT, NULL); tcps->tcps_g_num_epriv_ports = TCP_NUM_EPRIV_PORTS; - tcps->tcps_g_epriv_ports[0] = 2049; - tcps->tcps_g_epriv_ports[1] = 4045; + tcps->tcps_g_epriv_ports[0] = ULP_DEF_EPRIV_PORT1; + tcps->tcps_g_epriv_ports[1] = ULP_DEF_EPRIV_PORT2; tcps->tcps_min_anonpriv_port = 512; tcps->tcps_bind_fanout = kmem_zalloc(sizeof (tf_t) * @@ -4454,12 +4099,10 @@ tcp_stack_init(netstackid_t stackid, netstack_t *ns) /* TCP's IPsec code calls the packet dropper. */ ip_drop_register(&tcps->tcps_dropper, "TCP IPsec policy enforcement"); - pa = (tcpparam_t *)kmem_alloc(sizeof (lcl_tcp_param_arr), KM_SLEEP); - tcps->tcps_params = pa; - bcopy(lcl_tcp_param_arr, tcps->tcps_params, sizeof (lcl_tcp_param_arr)); - - (void) tcp_param_register(&tcps->tcps_g_nd, tcps->tcps_params, - A_CNT(lcl_tcp_param_arr), tcps); + arrsz = tcp_propinfo_count * sizeof (mod_prop_info_t); + tcps->tcps_propinfo_tbl = (mod_prop_info_t *)kmem_alloc(arrsz, + KM_SLEEP); + bcopy(tcp_propinfo_tbl, tcps->tcps_propinfo_tbl, arrsz); /* * Note: To really walk the device tree you need the devinfo @@ -4577,11 +4220,9 @@ tcp_stack_fini(netstackid_t stackid, void *arg) kmem_free(tcps->tcps_sc[i], sizeof (tcp_stats_cpu_t)); kmem_free(tcps->tcps_sc, max_ncpus * sizeof (tcp_stats_cpu_t *)); - nd_free(&tcps->tcps_g_nd); - kmem_free(tcps->tcps_params, sizeof (lcl_tcp_param_arr)); - tcps->tcps_params = NULL; - kmem_free(tcps->tcps_wroff_xtra_param, sizeof (tcpparam_t)); - tcps->tcps_wroff_xtra_param = NULL; + kmem_free(tcps->tcps_propinfo_tbl, + tcp_propinfo_count * sizeof (mod_prop_info_t)); + tcps->tcps_propinfo_tbl = NULL; for (i = 0; i < TCP_BIND_FANOUT_SIZE; i++) { ASSERT(tcps->tcps_bind_fanout[i].tf_tcp == NULL); diff --git a/usr/src/uts/common/inet/tcp/tcp_misc.c b/usr/src/uts/common/inet/tcp/tcp_misc.c index dfea8c8b0e..556df3a1d3 100644 --- a/usr/src/uts/common/inet/tcp/tcp_misc.c +++ b/usr/src/uts/common/inet/tcp/tcp_misc.c @@ -632,102 +632,6 @@ tcp_find_listener_conf(tcp_stack_t *tcps, in_port_t port) } /* - * Ndd param helper routine to return the current list of listener limit - * configuration. - */ -/* ARGSUSED */ -int -tcp_listener_conf_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - tcp_listener_t *tl; - - mutex_enter(&tcps->tcps_listener_conf_lock); - for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL; - tl = list_next(&tcps->tcps_listener_conf, tl)) { - (void) mi_mpprintf(mp, "%d:%d ", tl->tl_port, tl->tl_ratio); - } - mutex_exit(&tcps->tcps_listener_conf_lock); - return (0); -} - -/* - * Ndd param helper routine to add a new listener limit configuration. - */ -/* ARGSUSED */ -int -tcp_listener_conf_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - tcp_listener_t *new_tl; - tcp_listener_t *tl; - long lport; - long ratio; - char *colon; - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - - if (ddi_strtol(value, &colon, 10, &lport) != 0 || lport <= 0 || - lport > USHRT_MAX || *colon != ':') { - return (EINVAL); - } - if (ddi_strtol(colon + 1, NULL, 10, &ratio) != 0 || ratio <= 0) - return (EINVAL); - - mutex_enter(&tcps->tcps_listener_conf_lock); - for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL; - tl = list_next(&tcps->tcps_listener_conf, tl)) { - /* There is an existing entry, so update its ratio value. */ - if (tl->tl_port == lport) { - tl->tl_ratio = ratio; - mutex_exit(&tcps->tcps_listener_conf_lock); - return (0); - } - } - - if ((new_tl = kmem_alloc(sizeof (tcp_listener_t), KM_NOSLEEP)) == - NULL) { - mutex_exit(&tcps->tcps_listener_conf_lock); - return (ENOMEM); - } - - new_tl->tl_port = lport; - new_tl->tl_ratio = ratio; - list_insert_tail(&tcps->tcps_listener_conf, new_tl); - mutex_exit(&tcps->tcps_listener_conf_lock); - return (0); -} - -/* - * Ndd param helper routine to remove a listener limit configuration. - */ -/* ARGSUSED */ -int -tcp_listener_conf_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - tcp_listener_t *tl; - long lport; - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; - - if (ddi_strtol(value, NULL, 10, &lport) != 0 || lport <= 0 || - lport > USHRT_MAX) { - return (EINVAL); - } - mutex_enter(&tcps->tcps_listener_conf_lock); - for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL; - tl = list_next(&tcps->tcps_listener_conf, tl)) { - if (tl->tl_port == lport) { - list_remove(&tcps->tcps_listener_conf, tl); - mutex_exit(&tcps->tcps_listener_conf_lock); - kmem_free(tl, sizeof (tcp_listener_t)); - return (0); - } - } - mutex_exit(&tcps->tcps_listener_conf_lock); - return (ESRCH); -} - -/* * To remove all listener limit configuration in a tcp_stack_t. */ void diff --git a/usr/src/uts/common/inet/tcp/tcp_output.c b/usr/src/uts/common/inet/tcp/tcp_output.c index 01b383bb34..2a02d214f5 100644 --- a/usr/src/uts/common/inet/tcp/tcp_output.c +++ b/usr/src/uts/common/inet/tcp/tcp_output.c @@ -90,7 +90,6 @@ tcp_wput(queue_t *q, mblk_t *mp) uchar_t *rptr; struct iocblk *iocp; size_t size; - tcp_stack_t *tcps = Q_TO_TCP(q)->tcp_tcps; ASSERT(connp->conn_ref >= 2); @@ -182,17 +181,6 @@ tcp_wput(queue_t *q, mblk_t *mp) mi_copyin(q, mp, NULL, SIZEOF_STRUCT(strbuf, iocp->ioc_flag)); return; - case ND_SET: - /* nd_getset does the necessary checks */ - case ND_GET: - if (nd_getset(q, tcps->tcps_g_nd, mp)) { - qreply(q, mp); - return; - } - CONN_INC_IOCTLREF(connp); - ip_wput_nondata(q, mp); - CONN_DEC_IOCTLREF(connp); - return; default: output_proc = tcp_wput_ioctl; diff --git a/usr/src/uts/common/inet/tcp/tcp_tunables.c b/usr/src/uts/common/inet/tcp/tcp_tunables.c new file mode 100644 index 0000000000..1d01d9a1b1 --- /dev/null +++ b/usr/src/uts/common/inet/tcp/tcp_tunables.c @@ -0,0 +1,480 @@ +/* + * 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 <inet/ip.h> +#include <inet/tcp_impl.h> +#include <sys/multidata.h> +#include <sys/sunddi.h> + +/* Max size IP datagram is 64k - 1 */ +#define TCP_MSS_MAX_IPV4 (IP_MAXPACKET - (sizeof (ipha_t) + sizeof (tcpha_t))) +#define TCP_MSS_MAX_IPV6 (IP_MAXPACKET - (sizeof (ip6_t) + sizeof (tcpha_t))) + +/* Max of the above */ +#define TCP_MSS_MAX TCP_MSS_MAX_IPV4 + +#define TCP_XMIT_LOWATER 4096 +#define TCP_XMIT_HIWATER 49152 +#define TCP_RECV_LOWATER 2048 +#define TCP_RECV_HIWATER 128000 + +/* + * Set the RFC 1948 pass phrase + */ +/* ARGSUSED */ +static int +tcp_set_1948phrase(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pr_val, uint_t flags) +{ + tcp_stack_t *tcps = (tcp_stack_t *)cbarg; + + if (flags & MOD_PROP_DEFAULT) + return (ENOTSUP); + + /* + * Basically, value contains a new pass phrase. Pass it along! + */ + tcp_iss_key_init((uint8_t *)pr_val, strlen(pr_val), tcps); + return (0); +} + +/* + * returns the current list of listener limit configuration. + */ +/* ARGSUSED */ +static int +tcp_listener_conf_get(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *val, uint_t psize, uint_t flags) +{ + tcp_stack_t *tcps = (tcp_stack_t *)cbarg; + tcp_listener_t *tl; + char *pval = val; + size_t nbytes = 0, tbytes = 0; + uint_t size; + int err = 0; + + bzero(pval, psize); + size = psize; + + if (flags & (MOD_PROP_DEFAULT|MOD_PROP_PERM|MOD_PROP_POSSIBLE)) + return (0); + + mutex_enter(&tcps->tcps_listener_conf_lock); + for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL; + tl = list_next(&tcps->tcps_listener_conf, tl)) { + if (psize == size) + nbytes = snprintf(pval, size, "%d:%d", tl->tl_port, + tl->tl_ratio); + else + nbytes = snprintf(pval, size, ",%d:%d", tl->tl_port, + tl->tl_ratio); + size -= nbytes; + pval += nbytes; + tbytes += nbytes; + if (tbytes >= psize) { + /* Buffer overflow, stop copying information */ + err = ENOBUFS; + break; + } + } +ret: + mutex_exit(&tcps->tcps_listener_conf_lock); + return (err); +} + +/* + * add a new listener limit configuration. + */ +/* ARGSUSED */ +static int +tcp_listener_conf_add(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + tcp_listener_t *new_tl; + tcp_listener_t *tl; + long lport; + long ratio; + char *colon; + tcp_stack_t *tcps = (tcp_stack_t *)cbarg; + + if (flags & MOD_PROP_DEFAULT) + return (ENOTSUP); + + if (ddi_strtol(pval, &colon, 10, &lport) != 0 || lport <= 0 || + lport > USHRT_MAX || *colon != ':') { + return (EINVAL); + } + if (ddi_strtol(colon + 1, NULL, 10, &ratio) != 0 || ratio <= 0) + return (EINVAL); + + mutex_enter(&tcps->tcps_listener_conf_lock); + for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL; + tl = list_next(&tcps->tcps_listener_conf, tl)) { + /* There is an existing entry, so update its ratio value. */ + if (tl->tl_port == lport) { + tl->tl_ratio = ratio; + mutex_exit(&tcps->tcps_listener_conf_lock); + return (0); + } + } + + if ((new_tl = kmem_alloc(sizeof (tcp_listener_t), KM_NOSLEEP)) == + NULL) { + mutex_exit(&tcps->tcps_listener_conf_lock); + return (ENOMEM); + } + + new_tl->tl_port = lport; + new_tl->tl_ratio = ratio; + list_insert_tail(&tcps->tcps_listener_conf, new_tl); + mutex_exit(&tcps->tcps_listener_conf_lock); + return (0); +} + +/* + * remove a listener limit configuration. + */ +/* ARGSUSED */ +static int +tcp_listener_conf_del(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + tcp_listener_t *tl; + long lport; + tcp_stack_t *tcps = (tcp_stack_t *)cbarg; + + if (flags & MOD_PROP_DEFAULT) + return (ENOTSUP); + + if (ddi_strtol(pval, NULL, 10, &lport) != 0 || lport <= 0 || + lport > USHRT_MAX) { + return (EINVAL); + } + mutex_enter(&tcps->tcps_listener_conf_lock); + for (tl = list_head(&tcps->tcps_listener_conf); tl != NULL; + tl = list_next(&tcps->tcps_listener_conf, tl)) { + if (tl->tl_port == lport) { + list_remove(&tcps->tcps_listener_conf, tl); + mutex_exit(&tcps->tcps_listener_conf_lock); + kmem_free(tl, sizeof (tcp_listener_t)); + return (0); + } + } + mutex_exit(&tcps->tcps_listener_conf_lock); + return (ESRCH); +} + +/* + * All of these are alterable, within the min/max values given, at run time. + * + * Note: All those tunables which do not start with "tcp_" are Committed and + * therefore are public. See PSARC 2009/306. + */ +mod_prop_info_t tcp_propinfo_tbl[] = { + /* tunable - 0 */ + { "tcp_time_wait_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, 10*MINUTES, 1*MINUTES}, {1*MINUTES} }, + + { "tcp_conn_req_max_q", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, UINT32_MAX, 128}, {128} }, + + { "tcp_conn_req_max_q0", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, UINT32_MAX, 1024}, {1024} }, + + { "tcp_conn_req_min", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, 1024, 1}, {1} }, + + { "tcp_conn_grace_period", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0*MS, 20*SECONDS, 0*MS}, {0*MS} }, + + { "tcp_cwnd_max", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {128, (1<<30), 1024*1024}, {1024*1024} }, + + { "tcp_debug", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 10, 0}, {0} }, + + { "smallest_nonpriv_port", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1024, (32*1024), 1024}, {1024} }, + + { "tcp_ip_abort_cinterval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, UINT32_MAX, 3*MINUTES}, {3*MINUTES} }, + + { "tcp_ip_abort_linterval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, UINT32_MAX, 3*MINUTES}, {3*MINUTES} }, + + /* tunable - 10 */ + { "tcp_ip_abort_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {500*MS, UINT32_MAX, 5*MINUTES}, {5*MINUTES} }, + + { "tcp_ip_notify_cinterval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, UINT32_MAX, 10*SECONDS}, + {10*SECONDS} }, + + { "tcp_ip_notify_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {500*MS, UINT32_MAX, 10*SECONDS}, {10*SECONDS} }, + + { "tcp_ipv4_ttl", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, 255, 64}, {64} }, + + { "tcp_keepalive_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {10*SECONDS, 10*DAYS, 2*HOURS}, {2*HOURS} }, + + { "tcp_maxpsz_multiplier", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 100, 10}, {10} }, + + { "tcp_mss_def_ipv4", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, TCP_MSS_MAX_IPV4, 536}, {536} }, + + { "tcp_mss_max_ipv4", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, TCP_MSS_MAX_IPV4, TCP_MSS_MAX_IPV4}, + {TCP_MSS_MAX_IPV4} }, + + { "tcp_mss_min", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, TCP_MSS_MAX, 108}, {108} }, + + { "tcp_naglim_def", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, (64*1024)-1, (4*1024)-1}, {(4*1024)-1} }, + + /* tunable - 20 */ + { "tcp_rexmit_interval_initial", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*MS, 20*SECONDS, 1*SECONDS}, {1*SECONDS} }, + + { "tcp_rexmit_interval_max", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*MS, 2*HOURS, 60*SECONDS}, {60*SECONDS} }, + + { "tcp_rexmit_interval_min", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*MS, 2*HOURS, 400*MS}, {400*MS} }, + + { "tcp_deferred_ack_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*MS, 1*MINUTES, 100*MS}, {100*MS} }, + + { "tcp_snd_lowat_fraction", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 16, 0}, {0} }, + + { "tcp_dupack_fast_retransmit", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, 10000, 3}, {3} }, + + { "tcp_ignore_path_mtu", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "smallest_anon_port", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1024, ULP_MAX_PORT, 32*1024}, {32*1024} }, + + { "largest_anon_port", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1024, ULP_MAX_PORT, ULP_MAX_PORT}, + {ULP_MAX_PORT} }, + + { "send_maxbuf", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {TCP_XMIT_LOWATER, (1<<30), TCP_XMIT_HIWATER}, + {TCP_XMIT_HIWATER} }, + + /* tunable - 30 */ + { "tcp_xmit_lowat", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {TCP_XMIT_LOWATER, (1<<30), TCP_XMIT_LOWATER}, + {TCP_XMIT_LOWATER} }, + + { "recv_maxbuf", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {TCP_RECV_LOWATER, (1<<30), TCP_RECV_HIWATER}, + {TCP_RECV_HIWATER} }, + + { "tcp_recv_hiwat_minmss", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, 65536, 4}, {4} }, + + { "tcp_fin_wait_2_flush_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1*SECONDS, UINT32_MAX, 675*SECONDS}, + {675*SECONDS} }, + + { "tcp_max_buf", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {8192, (1<<30), 1024*1024}, {1024*1024} }, + + /* + * Question: What default value should I set for tcp_strong_iss? + */ + { "tcp_strong_iss", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 2, 1}, {1} }, + + { "tcp_rtt_updates", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 65536, 20}, {20} }, + + { "tcp_wscale_always", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "tcp_tstamp_always", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "tcp_tstamp_if_wscale", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + /* tunable - 40 */ + { "tcp_rexmit_interval_extra", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0*MS, 2*HOURS, 0*MS}, {0*MS} }, + + { "tcp_deferred_acks_max", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 16, 2}, {2} }, + + { "tcp_slow_start_after_idle", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, 16384, 4}, {4} }, + + { "tcp_slow_start_initial", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, 4, 4}, {4} }, + + { "sack", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 2, 2}, {2} }, + + { "tcp_ipv6_hoplimit", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS}, + {IPV6_DEFAULT_HOPS} }, + + { "tcp_mss_def_ipv6", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, TCP_MSS_MAX_IPV6, 1220}, {1220} }, + + { "tcp_mss_max_ipv6", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {1, TCP_MSS_MAX_IPV6, TCP_MSS_MAX_IPV6}, + {TCP_MSS_MAX_IPV6} }, + + { "tcp_rev_src_routes", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "tcp_local_dack_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {10*MS, 500*MS, 50*MS}, {50*MS} }, + + /* tunable - 50 */ + { "tcp_local_dacks_max", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 16, 8}, {8} }, + + { "ecn", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 2, 1}, {1} }, + + { "tcp_rst_sent_rate_enabled", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "tcp_rst_sent_rate", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, UINT32_MAX, 40}, {40} }, + + { "tcp_push_timer_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, 100*MS, 50*MS}, {50*MS} }, + + { "tcp_use_smss_as_mss_opt", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "tcp_keepalive_abort_interval", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, UINT32_MAX, 8*MINUTES}, {8*MINUTES} }, + + /* + * tcp_wroff_xtra is the extra space in front of TCP/IP header for link + * layer header. It has to be a multiple of 8. + */ + { "tcp_wroff_xtra", MOD_PROTO_TCP, + mod_set_aligned, mod_get_uint32, + {0, 256, 32}, {32} }, + + { "tcp_dev_flow_ctl", MOD_PROTO_TCP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "tcp_reass_timeout", MOD_PROTO_TCP, + mod_set_uint32, mod_get_uint32, + {0, UINT32_MAX, 100*SECONDS}, {100*SECONDS} }, + + /* tunable - 60 */ + { "extra_priv_ports", MOD_PROTO_TCP, + mod_set_extra_privports, mod_get_extra_privports, + {1, ULP_MAX_PORT, 0}, {0} }, + + { "tcp_1948_phrase", MOD_PROTO_TCP, + tcp_set_1948phrase, NULL, {0}, {0} }, + + { "tcp_listener_limit_conf", MOD_PROTO_TCP, + NULL, tcp_listener_conf_get, {0}, {0} }, + + { "tcp_listener_limit_conf_add", MOD_PROTO_TCP, + tcp_listener_conf_add, NULL, {0}, {0} }, + + { "tcp_listener_limit_conf_del", MOD_PROTO_TCP, + tcp_listener_conf_del, NULL, {0}, {0} }, + + { "?", MOD_PROTO_TCP, NULL, mod_get_allprop, {0}, {0} }, + + { NULL, 0, NULL, NULL, {0}, {0} } +}; + +int tcp_propinfo_count = A_CNT(tcp_propinfo_tbl); diff --git a/usr/src/uts/common/inet/tcp_impl.h b/usr/src/uts/common/inet/tcp_impl.h index 2ee2b6cb39..a7d27350b3 100644 --- a/usr/src/uts/common/inet/tcp_impl.h +++ b/usr/src/uts/common/inet/tcp_impl.h @@ -43,6 +43,7 @@ extern "C" { #include <sys/clock_impl.h> /* For LBOLT_FASTPATH{,64} */ #include <inet/optcom.h> #include <inet/tcp.h> +#include <inet/tunables.h> #define TCP_MOD_ID 5105 @@ -65,13 +66,6 @@ extern sock_downcalls_t sock_tcp_downcalls; */ #define TCP_OLD_URP_INTERPRETATION 1 -/* Handy time related macros. */ -#define MS 1L -#define SECONDS (1000 * MS) -#define MINUTES (60 * SECONDS) -#define HOURS (60 * MINUTES) -#define DAYS (24 * HOURS) - /* TCP option length */ #define TCPOPT_NOP_LEN 1 #define TCPOPT_MAXSEG_LEN 4 @@ -407,80 +401,73 @@ extern uint32_t tcp_early_abort; #define TCP_REASS_SET_END(mp, u) ((mp)->b_prev = \ (mblk_t *)(uintptr_t)(u)) -/* Named Dispatch Parameter Management Structure */ -typedef struct tcpparam_s { - uint32_t tcp_param_min; - uint32_t tcp_param_max; - uint32_t tcp_param_val; - char *tcp_param_name; -} tcpparam_t; - - -#define tcps_time_wait_interval tcps_params[0].tcp_param_val -#define tcps_conn_req_max_q tcps_params[1].tcp_param_val -#define tcps_conn_req_max_q0 tcps_params[2].tcp_param_val -#define tcps_conn_req_min tcps_params[3].tcp_param_val -#define tcps_conn_grace_period tcps_params[4].tcp_param_val -#define tcps_cwnd_max_ tcps_params[5].tcp_param_val -#define tcps_dbg tcps_params[6].tcp_param_val -#define tcps_smallest_nonpriv_port tcps_params[7].tcp_param_val -#define tcps_ip_abort_cinterval tcps_params[8].tcp_param_val -#define tcps_ip_abort_linterval tcps_params[9].tcp_param_val -#define tcps_ip_abort_interval tcps_params[10].tcp_param_val -#define tcps_ip_notify_cinterval tcps_params[11].tcp_param_val -#define tcps_ip_notify_interval tcps_params[12].tcp_param_val -#define tcps_ipv4_ttl tcps_params[13].tcp_param_val -#define tcps_keepalive_interval_high tcps_params[14].tcp_param_max -#define tcps_keepalive_interval tcps_params[14].tcp_param_val -#define tcps_keepalive_interval_low tcps_params[14].tcp_param_min -#define tcps_maxpsz_multiplier tcps_params[15].tcp_param_val -#define tcps_mss_def_ipv4 tcps_params[16].tcp_param_val -#define tcps_mss_max_ipv4 tcps_params[17].tcp_param_val -#define tcps_mss_min tcps_params[18].tcp_param_val -#define tcps_naglim_def tcps_params[19].tcp_param_val -#define tcps_rexmit_interval_initial tcps_params[20].tcp_param_val -#define tcps_rexmit_interval_max tcps_params[21].tcp_param_val -#define tcps_rexmit_interval_min tcps_params[22].tcp_param_val -#define tcps_deferred_ack_interval tcps_params[23].tcp_param_val -#define tcps_snd_lowat_fraction tcps_params[24].tcp_param_val -#define tcps_dupack_fast_retransmit tcps_params[25].tcp_param_val -#define tcps_ignore_path_mtu tcps_params[26].tcp_param_val -#define tcps_smallest_anon_port tcps_params[27].tcp_param_val -#define tcps_largest_anon_port tcps_params[28].tcp_param_val -#define tcps_xmit_hiwat tcps_params[29].tcp_param_val -#define tcps_xmit_lowat tcps_params[30].tcp_param_val -#define tcps_recv_hiwat tcps_params[31].tcp_param_val -#define tcps_recv_hiwat_minmss tcps_params[32].tcp_param_val -#define tcps_fin_wait_2_flush_interval tcps_params[33].tcp_param_val -#define tcps_max_buf tcps_params[34].tcp_param_val -#define tcps_strong_iss tcps_params[35].tcp_param_val -#define tcps_rtt_updates tcps_params[36].tcp_param_val -#define tcps_wscale_always tcps_params[37].tcp_param_val -#define tcps_tstamp_always tcps_params[38].tcp_param_val -#define tcps_tstamp_if_wscale tcps_params[39].tcp_param_val -#define tcps_rexmit_interval_extra tcps_params[40].tcp_param_val -#define tcps_deferred_acks_max tcps_params[41].tcp_param_val -#define tcps_slow_start_after_idle tcps_params[42].tcp_param_val -#define tcps_slow_start_initial tcps_params[43].tcp_param_val -#define tcps_sack_permitted tcps_params[44].tcp_param_val -#define tcps_ipv6_hoplimit tcps_params[45].tcp_param_val -#define tcps_mss_def_ipv6 tcps_params[46].tcp_param_val -#define tcps_mss_max_ipv6 tcps_params[47].tcp_param_val -#define tcps_rev_src_routes tcps_params[48].tcp_param_val -#define tcps_local_dack_interval tcps_params[49].tcp_param_val -#define tcps_local_dacks_max tcps_params[50].tcp_param_val -#define tcps_ecn_permitted tcps_params[51].tcp_param_val -#define tcps_rst_sent_rate_enabled tcps_params[52].tcp_param_val -#define tcps_rst_sent_rate tcps_params[53].tcp_param_val -#define tcps_push_timer_interval tcps_params[54].tcp_param_val -#define tcps_use_smss_as_mss_opt tcps_params[55].tcp_param_val -#define tcps_keepalive_abort_interval_high tcps_params[56].tcp_param_max -#define tcps_keepalive_abort_interval tcps_params[56].tcp_param_val -#define tcps_keepalive_abort_interval_low tcps_params[56].tcp_param_min -#define tcps_dev_flow_ctl tcps_params[57].tcp_param_val -#define tcps_reass_timeout tcps_params[58].tcp_param_val - -#define tcps_wroff_xtra tcps_wroff_xtra_param->tcp_param_val +#define tcps_time_wait_interval tcps_propinfo_tbl[0].prop_cur_uval +#define tcps_conn_req_max_q tcps_propinfo_tbl[1].prop_cur_uval +#define tcps_conn_req_max_q0 tcps_propinfo_tbl[2].prop_cur_uval +#define tcps_conn_req_min tcps_propinfo_tbl[3].prop_cur_uval +#define tcps_conn_grace_period tcps_propinfo_tbl[4].prop_cur_uval +#define tcps_cwnd_max_ tcps_propinfo_tbl[5].prop_cur_uval +#define tcps_dbg tcps_propinfo_tbl[6].prop_cur_uval +#define tcps_smallest_nonpriv_port tcps_propinfo_tbl[7].prop_cur_uval +#define tcps_ip_abort_cinterval tcps_propinfo_tbl[8].prop_cur_uval +#define tcps_ip_abort_linterval tcps_propinfo_tbl[9].prop_cur_uval +#define tcps_ip_abort_interval tcps_propinfo_tbl[10].prop_cur_uval +#define tcps_ip_notify_cinterval tcps_propinfo_tbl[11].prop_cur_uval +#define tcps_ip_notify_interval tcps_propinfo_tbl[12].prop_cur_uval +#define tcps_ipv4_ttl tcps_propinfo_tbl[13].prop_cur_uval +#define tcps_keepalive_interval_high tcps_propinfo_tbl[14].prop_max_uval +#define tcps_keepalive_interval tcps_propinfo_tbl[14].prop_cur_uval +#define tcps_keepalive_interval_low tcps_propinfo_tbl[14].prop_min_uval +#define tcps_maxpsz_multiplier tcps_propinfo_tbl[15].prop_cur_uval +#define tcps_mss_def_ipv4 tcps_propinfo_tbl[16].prop_cur_uval +#define tcps_mss_max_ipv4 tcps_propinfo_tbl[17].prop_cur_uval +#define tcps_mss_min tcps_propinfo_tbl[18].prop_cur_uval +#define tcps_naglim_def tcps_propinfo_tbl[19].prop_cur_uval +#define tcps_rexmit_interval_initial tcps_propinfo_tbl[20].prop_cur_uval +#define tcps_rexmit_interval_max tcps_propinfo_tbl[21].prop_cur_uval +#define tcps_rexmit_interval_min tcps_propinfo_tbl[22].prop_cur_uval +#define tcps_deferred_ack_interval tcps_propinfo_tbl[23].prop_cur_uval +#define tcps_snd_lowat_fraction tcps_propinfo_tbl[24].prop_cur_uval +#define tcps_dupack_fast_retransmit tcps_propinfo_tbl[25].prop_cur_uval +#define tcps_ignore_path_mtu tcps_propinfo_tbl[26].prop_cur_bval +#define tcps_smallest_anon_port tcps_propinfo_tbl[27].prop_cur_uval +#define tcps_largest_anon_port tcps_propinfo_tbl[28].prop_cur_uval +#define tcps_xmit_hiwat tcps_propinfo_tbl[29].prop_cur_uval +#define tcps_xmit_lowat tcps_propinfo_tbl[30].prop_cur_uval +#define tcps_recv_hiwat tcps_propinfo_tbl[31].prop_cur_uval +#define tcps_recv_hiwat_minmss tcps_propinfo_tbl[32].prop_cur_uval +#define tcps_fin_wait_2_flush_interval tcps_propinfo_tbl[33].prop_cur_uval +#define tcps_max_buf tcps_propinfo_tbl[34].prop_cur_uval +#define tcps_strong_iss tcps_propinfo_tbl[35].prop_cur_uval +#define tcps_rtt_updates tcps_propinfo_tbl[36].prop_cur_uval +#define tcps_wscale_always tcps_propinfo_tbl[37].prop_cur_bval +#define tcps_tstamp_always tcps_propinfo_tbl[38].prop_cur_bval +#define tcps_tstamp_if_wscale tcps_propinfo_tbl[39].prop_cur_bval +#define tcps_rexmit_interval_extra tcps_propinfo_tbl[40].prop_cur_uval +#define tcps_deferred_acks_max tcps_propinfo_tbl[41].prop_cur_uval +#define tcps_slow_start_after_idle tcps_propinfo_tbl[42].prop_cur_uval +#define tcps_slow_start_initial tcps_propinfo_tbl[43].prop_cur_uval +#define tcps_sack_permitted tcps_propinfo_tbl[44].prop_cur_uval +#define tcps_ipv6_hoplimit tcps_propinfo_tbl[45].prop_cur_uval +#define tcps_mss_def_ipv6 tcps_propinfo_tbl[46].prop_cur_uval +#define tcps_mss_max_ipv6 tcps_propinfo_tbl[47].prop_cur_uval +#define tcps_rev_src_routes tcps_propinfo_tbl[48].prop_cur_bval +#define tcps_local_dack_interval tcps_propinfo_tbl[49].prop_cur_uval +#define tcps_local_dacks_max tcps_propinfo_tbl[50].prop_cur_uval +#define tcps_ecn_permitted tcps_propinfo_tbl[51].prop_cur_uval +#define tcps_rst_sent_rate_enabled tcps_propinfo_tbl[52].prop_cur_bval +#define tcps_rst_sent_rate tcps_propinfo_tbl[53].prop_cur_uval +#define tcps_push_timer_interval tcps_propinfo_tbl[54].prop_cur_uval +#define tcps_use_smss_as_mss_opt tcps_propinfo_tbl[55].prop_cur_bval +#define tcps_keepalive_abort_interval_high \ + tcps_propinfo_tbl[56].prop_max_uval +#define tcps_keepalive_abort_interval \ + tcps_propinfo_tbl[56].prop_cur_uval +#define tcps_keepalive_abort_interval_low \ + tcps_propinfo_tbl[56].prop_min_uval +#define tcps_wroff_xtra tcps_propinfo_tbl[57].prop_cur_uval +#define tcps_dev_flow_ctl tcps_propinfo_tbl[58].prop_cur_bval +#define tcps_reass_timeout tcps_propinfo_tbl[59].prop_cur_uval extern struct qinit tcp_rinitv4, tcp_rinitv6; extern boolean_t do_tcp_fusion; @@ -574,6 +561,7 @@ extern boolean_t tcp_fuse_rcv_drain(queue_t *, tcp_t *, mblk_t **); extern size_t tcp_fuse_set_rcv_hiwat(tcp_t *, size_t); extern int tcp_fuse_maxpsz(tcp_t *); extern void tcp_fuse_backenable(tcp_t *); +extern void tcp_iss_key_init(uint8_t *, int, tcp_stack_t *); /* * Output related functions in tcp_output.c. @@ -682,11 +670,6 @@ extern void tcp_time_wait_processing(tcp_t *, mblk_t *, uint32_t, extern int tcp_cpu_update(cpu_setup_t, int, void *); extern void tcp_ioctl_abort_conn(queue_t *, mblk_t *); extern uint32_t tcp_find_listener_conf(tcp_stack_t *, in_port_t); -extern int tcp_listener_conf_get(queue_t *, mblk_t *, caddr_t, cred_t *); -extern int tcp_listener_conf_add(queue_t *, mblk_t *, char *, caddr_t, - cred_t *); -extern int tcp_listener_conf_del(queue_t *, mblk_t *, char *, caddr_t, - cred_t *); extern void tcp_listener_conf_cleanup(tcp_stack_t *); #endif /* _KERNEL */ diff --git a/usr/src/uts/common/inet/tcp_stack.h b/usr/src/uts/common/inet/tcp_stack.h index 1a6e374f3e..5cff22c5f0 100644 --- a/usr/src/uts/common/inet/tcp_stack.h +++ b/usr/src/uts/common/inet/tcp_stack.h @@ -52,7 +52,7 @@ struct tcp_stack { */ #define TCP_NUM_EPRIV_PORTS 64 int tcps_g_num_epriv_ports; - uint16_t tcps_g_epriv_ports[TCP_NUM_EPRIV_PORTS]; + in_port_t tcps_g_epriv_ports[TCP_NUM_EPRIV_PORTS]; kmutex_t tcps_epriv_port_lock; /* @@ -61,10 +61,8 @@ struct tcp_stack { */ in_port_t tcps_min_anonpriv_port; - /* Only modified during _init and _fini thus no locking is needed. */ - caddr_t tcps_g_nd; - struct tcpparam_s *tcps_params; /* ndd parameters */ - struct tcpparam_s *tcps_wroff_xtra_param; + /* holds the tcp tunables */ + struct mod_prop_info_s *tcps_propinfo_tbl; /* Hint not protected by any lock */ uint_t tcps_next_port_to_try; diff --git a/usr/src/uts/common/inet/tunables.c b/usr/src/uts/common/inet/tunables.c new file mode 100644 index 0000000000..e9926151f0 --- /dev/null +++ b/usr/src/uts/common/inet/tunables.c @@ -0,0 +1,444 @@ +/* + * 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 <inet/tunables.h> +#include <sys/md5.h> +#include <inet/common.h> +#include <inet/ip.h> +#include <inet/ip6.h> +#include <netinet/icmp6.h> +#include <inet/ip_stack.h> +#include <inet/rawip_impl.h> +#include <inet/tcp_stack.h> +#include <inet/tcp_impl.h> +#include <inet/udp_impl.h> +#include <inet/sctp/sctp_stack.h> +#include <inet/sctp/sctp_impl.h> +#include <inet/tunables.h> + +static int +prop_perm2const(mod_prop_info_t *pinfo) +{ + if (pinfo->mpi_setf == NULL) + return (MOD_PROP_PERM_READ); + if (pinfo->mpi_getf == NULL) + return (MOD_PROP_PERM_WRITE); + return (MOD_PROP_PERM_RW); +} + +/* + * Modifies the value of the property to default value or to the `pval' + * specified by the user. + */ +/* ARGSUSED */ +int +mod_set_boolean(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + char *end; + unsigned long new_value; + + if (flags & MOD_PROP_DEFAULT) { + pinfo->prop_cur_bval = pinfo->prop_def_bval; + return (0); + } + + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || *end != '\0') + return (EINVAL); + if (new_value != B_TRUE && new_value != B_FALSE) + return (EINVAL); + pinfo->prop_cur_bval = new_value; + return (0); +} + +/* + * Retrieves property permission, default value, current value or possible + * values for those properties whose value type is boolean_t. + */ +/* ARGSUSED */ +int +mod_get_boolean(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *pval, uint_t psize, uint_t flags) +{ + boolean_t get_def = (flags & MOD_PROP_DEFAULT); + boolean_t get_perm = (flags & MOD_PROP_PERM); + boolean_t get_range = (flags & MOD_PROP_POSSIBLE); + size_t nbytes; + + bzero(pval, psize); + if (get_perm) + nbytes = snprintf(pval, psize, "%u", prop_perm2const(pinfo)); + else if (get_range) + nbytes = snprintf(pval, psize, "%u,%u", B_FALSE, B_TRUE); + else if (get_def) + nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_bval); + else + nbytes = snprintf(pval, psize, "%u", pinfo->prop_cur_bval); + if (nbytes >= psize) + return (ENOBUFS); + return (0); +} + +/* + * Modifies the value of the property to default value or to the `pval' + * specified by the user. + */ +/* ARGSUSED */ +int +mod_set_uint32(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + char *end; + unsigned long new_value; + + if (flags & MOD_PROP_DEFAULT) { + pinfo->prop_cur_uval = pinfo->prop_def_uval; + return (0); + } + + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || *end != '\0') + return (EINVAL); + if (new_value < pinfo->prop_min_uval || + new_value > pinfo->prop_max_uval) { + return (ERANGE); + } + pinfo->prop_cur_uval = (uint32_t)new_value; + return (0); +} + +/* + * Rounds up the value to make it multiple of 8. + */ +/* ARGSUSED */ +int +mod_set_aligned(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* pval, uint_t flags) +{ + int err; + + if ((err = mod_set_uint32(cbarg, cr, pinfo, ifname, pval, flags)) != 0) + return (err); + + /* if required, align the value to multiple of 8 */ + if (pinfo->prop_cur_uval & 0x7) { + pinfo->prop_cur_uval &= ~0x7; + pinfo->prop_cur_uval += 0x8; + } + + return (0); +} + +/* + * Retrieves property permission, default value, current value or possible + * values for those properties whose value type is uint32_t. + */ +/* ARGSUSED */ +int +mod_get_uint32(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *pval, uint_t psize, uint_t flags) +{ + boolean_t get_def = (flags & MOD_PROP_DEFAULT); + boolean_t get_perm = (flags & MOD_PROP_PERM); + boolean_t get_range = (flags & MOD_PROP_POSSIBLE); + size_t nbytes; + + bzero(pval, psize); + if (get_perm) + nbytes = snprintf(pval, psize, "%u", prop_perm2const(pinfo)); + else if (get_range) + nbytes = snprintf(pval, psize, "%u-%u", + pinfo->prop_min_uval, pinfo->prop_max_uval); + else if (get_def) + nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_uval); + else + nbytes = snprintf(pval, psize, "%u", pinfo->prop_cur_uval); + if (nbytes >= psize) + return (ENOBUFS); + return (0); +} + +/* + * Implements /sbin/ndd -get /dev/ip ?, for all the modules. Needed for + * backward compatibility with /sbin/ndd. + */ +/* ARGSUSED */ +int +mod_get_allprop(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *val, uint_t psize, uint_t flags) +{ + char *pval = val; + mod_prop_info_t *ptbl, *prop; + ip_stack_t *ipst; + tcp_stack_t *tcps; + sctp_stack_t *sctps; + udp_stack_t *us; + icmp_stack_t *is; + uint_t size; + size_t nbytes = 0, tbytes = 0; + + bzero(pval, psize); + size = psize; + + switch (pinfo->mpi_proto) { + case MOD_PROTO_IP: + case MOD_PROTO_IPV4: + case MOD_PROTO_IPV6: + ipst = (ip_stack_t *)cbarg; + ptbl = ipst->ips_propinfo_tbl; + break; + case MOD_PROTO_RAWIP: + is = (icmp_stack_t *)cbarg; + ptbl = is->is_propinfo_tbl; + break; + case MOD_PROTO_TCP: + tcps = (tcp_stack_t *)cbarg; + ptbl = tcps->tcps_propinfo_tbl; + break; + case MOD_PROTO_UDP: + us = (udp_stack_t *)cbarg; + ptbl = us->us_propinfo_tbl; + break; + case MOD_PROTO_SCTP: + sctps = (sctp_stack_t *)cbarg; + ptbl = sctps->sctps_propinfo_tbl; + break; + default: + return (EINVAL); + } + + for (prop = ptbl; prop->mpi_name != NULL; prop++) { + if (prop->mpi_name[0] == '\0' || + strcmp(prop->mpi_name, "mtu") == 0 || + strcmp(prop->mpi_name, "?") == 0) + continue; + nbytes = snprintf(pval, size, "%s %d %d", prop->mpi_name, + prop->mpi_proto, prop_perm2const(prop)); + size -= nbytes + 1; + pval += nbytes + 1; + tbytes += nbytes + 1; + if (tbytes >= psize) { + /* Buffer overflow, stop copying information */ + return (ENOBUFS); + } + } + return (0); +} + +/* + * Hold a lock while changing *_epriv_ports to prevent multiple + * threads from changing it at the same time. + */ +/* ARGSUSED */ +int +mod_set_extra_privports(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo, + const char *ifname, const void* val, uint_t flags) +{ + uint_t proto = pinfo->mpi_proto; + tcp_stack_t *tcps; + sctp_stack_t *sctps; + udp_stack_t *us; + unsigned long new_value; + char *end; + kmutex_t *lock; + uint_t i, nports; + in_port_t *ports; + boolean_t def = (flags & MOD_PROP_DEFAULT); + const char *pval = val; + + if (!def) { + if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || + *end != '\0') { + return (EINVAL); + } + + if (new_value < pinfo->prop_min_uval || + new_value > pinfo->prop_max_uval) { + return (ERANGE); + } + } + + switch (proto) { + case MOD_PROTO_TCP: + tcps = (tcp_stack_t *)cbarg; + lock = &tcps->tcps_epriv_port_lock; + ports = tcps->tcps_g_epriv_ports; + nports = tcps->tcps_g_num_epriv_ports; + break; + case MOD_PROTO_UDP: + us = (udp_stack_t *)cbarg; + lock = &us->us_epriv_port_lock; + ports = us->us_epriv_ports; + nports = us->us_num_epriv_ports; + break; + case MOD_PROTO_SCTP: + sctps = (sctp_stack_t *)cbarg; + lock = &sctps->sctps_epriv_port_lock; + ports = sctps->sctps_g_epriv_ports; + nports = sctps->sctps_g_num_epriv_ports; + break; + default: + return (ENOTSUP); + } + + mutex_enter(lock); + + /* if MOD_PROP_DEFAULT is set then reset the ports list to default */ + if (def) { + for (i = 0; i < nports; i++) + ports[i] = 0; + ports[0] = ULP_DEF_EPRIV_PORT1; + ports[1] = ULP_DEF_EPRIV_PORT2; + mutex_exit(lock); + return (0); + } + + /* Check if the value is already in the list */ + for (i = 0; i < nports; i++) { + if (new_value == ports[i]) + break; + } + + if (flags & MOD_PROP_REMOVE) { + if (i == nports) { + mutex_exit(lock); + return (ESRCH); + } + /* Clear the value */ + ports[i] = 0; + } else if (flags & MOD_PROP_APPEND) { + if (i != nports) { + mutex_exit(lock); + return (EEXIST); + } + + /* Find an empty slot */ + for (i = 0; i < nports; i++) { + if (ports[i] == 0) + break; + } + if (i == nports) { + mutex_exit(lock); + return (EOVERFLOW); + } + /* Set the new value */ + ports[i] = (in_port_t)new_value; + } else { + /* + * If the user used 'assignment' modifier. + * For eg: + * # ipadm set-prop -p extra_priv_ports=3001 tcp + * + * We clear all the ports and then just add 3001. + */ + ASSERT(flags == MOD_PROP_ACTIVE); + for (i = 0; i < nports; i++) + ports[i] = 0; + ports[0] = (in_port_t)new_value; + } + + mutex_exit(lock); + return (0); +} + +/* + * Note: No locks are held when inspecting *_epriv_ports + * but instead the code relies on: + * - the fact that the address of the array and its size never changes + * - the atomic assignment of the elements of the array + */ +/* ARGSUSED */ +int +mod_get_extra_privports(void *cbarg, mod_prop_info_t *pinfo, const char *ifname, + void *val, uint_t psize, uint_t flags) +{ + uint_t proto = pinfo->mpi_proto; + tcp_stack_t *tcps; + sctp_stack_t *sctps; + udp_stack_t *us; + uint_t i, nports, size; + in_port_t *ports; + char *pval = val; + size_t nbytes = 0, tbytes = 0; + boolean_t get_def = (flags & MOD_PROP_DEFAULT); + boolean_t get_perm = (flags & MOD_PROP_PERM); + boolean_t get_range = (flags & MOD_PROP_POSSIBLE); + + bzero(pval, psize); + size = psize; + + if (get_def) { + tbytes = snprintf(pval, psize, "%u,%u", ULP_DEF_EPRIV_PORT1, + ULP_DEF_EPRIV_PORT2); + goto ret; + } else if (get_perm) { + tbytes = snprintf(pval, psize, "%u", MOD_PROP_PERM_RW); + goto ret; + } + + switch (proto) { + case MOD_PROTO_TCP: + tcps = (tcp_stack_t *)cbarg; + ports = tcps->tcps_g_epriv_ports; + nports = tcps->tcps_g_num_epriv_ports; + break; + case MOD_PROTO_UDP: + us = (udp_stack_t *)cbarg; + ports = us->us_epriv_ports; + nports = us->us_num_epriv_ports; + break; + case MOD_PROTO_SCTP: + sctps = (sctp_stack_t *)cbarg; + ports = sctps->sctps_g_epriv_ports; + nports = sctps->sctps_g_num_epriv_ports; + break; + default: + return (ENOTSUP); + } + + if (get_range) { + tbytes = snprintf(pval, psize, "%u-%u", pinfo->prop_min_uval, + pinfo->prop_max_uval); + goto ret; + } + + for (i = 0; i < nports; i++) { + if (ports[i] != 0) { + if (psize == size) + nbytes = snprintf(pval, size, "%u", ports[i]); + else + nbytes = snprintf(pval, size, ",%u", ports[i]); + size -= nbytes; + pval += nbytes; + tbytes += nbytes; + if (tbytes >= psize) + return (ENOBUFS); + } + } + return (0); +ret: + if (tbytes >= psize) + return (ENOBUFS); + return (0); +} diff --git a/usr/src/uts/common/inet/tunables.h b/usr/src/uts/common/inet/tunables.h new file mode 100644 index 0000000000..bf7c908d3f --- /dev/null +++ b/usr/src/uts/common/inet/tunables.h @@ -0,0 +1,163 @@ +/* + * 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 _INET_TUNABLES_H +#define _INET_TUNABLES_H + +#include <sys/types.h> +#include <net/if.h> +#ifdef _KERNEL +#include <sys/netstack.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAXPROPNAMELEN 64 + +/* + * The `mod_ioc_prop_s' datastructure is used as an IOCTL argument for + * SIOCSETPROP and SIOCGETPROP ioctls. This datastructure identifies the + * protocol (`mpr_proto') property (`mpr_name'), which needs to be modified + * or retrieved (`mpr_valsize' and `mpr_val'). If the property applies to an + * interface then `mpr_ifname' contains the name of the interface. + */ +typedef struct mod_ioc_prop_s { + uint_t mpr_version; + uint_t mpr_flags; /* see below */ + /* name of the interface (ill) for which property will be applied */ + char mpr_ifname[LIFNAMSIZ]; + uint_t mpr_proto; /* see below */ + char mpr_name[MAXPROPNAMELEN]; /* property name */ + uint_t mpr_valsize; /* size of mpr_val */ + char mpr_val[1]; +} mod_ioc_prop_t; + +#define MOD_PROP_VERSION 1 + +/* permission flags for properties */ +#define MOD_PROP_PERM_READ 0x1 +#define MOD_PROP_PERM_WRITE 0x2 +#define MOD_PROP_PERM_RW (MOD_PROP_PERM_READ|MOD_PROP_PERM_WRITE) + +/* mpr_flags values */ +#define MOD_PROP_ACTIVE 0x01 /* current value of the property */ +#define MOD_PROP_DEFAULT 0x02 /* default value of the property */ +#define MOD_PROP_POSSIBLE 0x04 /* possible values for the property */ +#define MOD_PROP_PERM 0x08 /* read/write permission for property */ +#define MOD_PROP_APPEND 0x10 /* append to multi-valued property */ +#define MOD_PROP_REMOVE 0x20 /* remove from multi-valued property */ + +/* mpr_proto values */ +#define MOD_PROTO_NONE 0x00 +#define MOD_PROTO_IPV4 0x01 /* property is applicable to IPV4 */ +#define MOD_PROTO_IPV6 0x02 /* property is applicable to IPV6 */ +#define MOD_PROTO_RAWIP 0x04 /* property is applicable to ICMP */ +#define MOD_PROTO_TCP 0x08 /* property is applicable to TCP */ +#define MOD_PROTO_UDP 0x10 /* property is applicable to UDP */ +#define MOD_PROTO_SCTP 0x20 /* property is applicable to SCTP */ + +/* property is applicable to both IPV[4|6] */ +#define MOD_PROTO_IP (MOD_PROTO_IPV4|MOD_PROTO_IPV6) + +#ifdef _KERNEL + +typedef struct mod_prop_info_s mod_prop_info_t; + +/* set/get property callback functions */ +typedef int mod_prop_setf_t(void *, cred_t *, mod_prop_info_t *, + const char *, const void *, uint_t); +typedef int mod_prop_getf_t(void *, mod_prop_info_t *, const char *, + void *val, uint_t, uint_t); + +typedef struct mod_propval_uint32_s { + uint32_t mod_propval_umin; + uint32_t mod_propval_umax; + uint32_t mod_propval_ucur; +} mod_propval_uint32_t; + +/* + * protocol property information + */ +struct mod_prop_info_s { + char *mpi_name; /* property name */ + uint_t mpi_proto; /* property protocol */ + mod_prop_setf_t *mpi_setf; /* sets the property value */ + mod_prop_getf_t *mpi_getf; /* gets the property value */ + /* + * Holds the current value of the property. Whenever applicable + * holds the min/max value too. + */ + union { + mod_propval_uint32_t mpi_uval; + boolean_t mpi_bval; + uint64_t _pad[2]; + } u; + /* + * Holds the default value of the property, that is value of + * the property at boot time. + */ + union { + uint32_t mpi_def_uval; + boolean_t mpi_def_bval; + } u_def; +}; + +/* shortcuts to access current/default values */ +#define prop_min_uval u.mpi_uval.mod_propval_umin +#define prop_max_uval u.mpi_uval.mod_propval_umax +#define prop_cur_uval u.mpi_uval.mod_propval_ucur +#define prop_cur_bval u.mpi_bval +#define prop_def_uval u_def.mpi_def_uval +#define prop_def_bval u_def.mpi_def_bval + +#define MS 1L +#define SECONDS (1000 * MS) +#define MINUTES (60 * SECONDS) +#define HOURS (60 * MINUTES) +#define DAYS (24 * HOURS) + +/* Largest TCP/UDP/SCTP port number */ +#define ULP_MAX_PORT (64 * 1024 - 1) + +/* extra privilege ports for upper layer protocols, tcp, sctp and udp */ +#define ULP_DEF_EPRIV_PORT1 2049 +#define ULP_DEF_EPRIV_PORT2 4045 + +/* generic function to set/get global module properties */ +extern mod_prop_setf_t mod_set_boolean, mod_set_uint32, + mod_set_aligned, mod_set_extra_privports; + +extern mod_prop_getf_t mod_get_boolean, mod_get_uint32, + mod_get_allprop, mod_get_extra_privports; + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _INET_TUNABLES_H */ diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c index fc9842bb7d..79eaab55a5 100644 --- a/usr/src/uts/common/inet/udp/udp.c +++ b/usr/src/uts/common/inet/udp/udp.c @@ -68,7 +68,6 @@ #include <inet/ip_ndp.h> #include <inet/proto_set.h> #include <inet/mib2.h> -#include <inet/nd.h> #include <inet/optcom.h> #include <inet/snmpcom.h> #include <inet/kstatcom.h> @@ -165,10 +164,6 @@ static int udp_output_lastdst(conn_t *connp, mblk_t *mp, cred_t *cr, static int udp_output_newdst(conn_t *connp, mblk_t *data_mp, sin_t *sin, sin6_t *sin6, ushort_t ipversion, cred_t *cr, pid_t, ip_xmit_attr_t *ixa); -static int udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr); -static boolean_t udp_param_register(IDP *ndp, udpparam_t *udppa, int cnt); -static int udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr); static mblk_t *udp_prepend_hdr(conn_t *, ip_xmit_attr_t *, const ip_pkt_t *, const in6_addr_t *, const in6_addr_t *, in_port_t, uint32_t, mblk_t *, int *); @@ -211,11 +206,6 @@ int udp_getpeername(sock_lower_handle_t, static int udp_do_connect(conn_t *, const struct sockaddr *, socklen_t, cred_t *, pid_t); -#define UDP_RECV_HIWATER (56 * 1024) -#define UDP_RECV_LOWATER 128 -#define UDP_XMIT_HIWATER (56 * 1024) -#define UDP_XMIT_LOWATER 1024 - #pragma inline(udp_output_connected, udp_output_newdst, udp_output_lastdst) /* @@ -336,32 +326,11 @@ static struct T_info_ack udp_g_t_info_ack_ipv6 = { (XPG4_1|SENDZERO) /* PROVIDER_flag */ }; -/* largest UDP port number */ -#define UDP_MAX_PORT 65535 - /* - * Table of ND variables supported by udp. These are loaded into us_nd - * in udp_open. - * All of these are alterable, within the min/max values given, at run time. + * UDP tunables related declarations. Definitions are in udp_tunables.c */ -/* BEGIN CSTYLED */ -udpparam_t udp_param_arr[] = { - /*min max value name */ - { 0L, 256, 32, "udp_wroff_extra" }, - { 1L, 255, 255, "udp_ipv4_ttl" }, - { 0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS, "udp_ipv6_hoplimit"}, - { 1024, (32 * 1024), 1024, "udp_smallest_nonpriv_port" }, - { 0, 1, 1, "udp_do_checksum" }, - { 1024, UDP_MAX_PORT, (32 * 1024), "udp_smallest_anon_port" }, - { 1024, UDP_MAX_PORT, UDP_MAX_PORT, "udp_largest_anon_port" }, - { UDP_XMIT_LOWATER, (1<<30), UDP_XMIT_HIWATER, "udp_xmit_hiwat"}, - { 0, (1<<30), UDP_XMIT_LOWATER, "udp_xmit_lowat"}, - { UDP_RECV_LOWATER, (1<<30), UDP_RECV_HIWATER, "udp_recv_hiwat"}, - { 65536, (1<<30), 2*1024*1024, "udp_max_buf"}, - { 0, 1, 0, "udp_pmtu_discovery" }, - { 0, 1, 0, "udp_sendto_ignerr" }, -}; -/* END CSTYLED */ +extern mod_prop_info_t udp_propinfo_tbl[]; +extern int udp_propinfo_count; /* Setable in /etc/system */ /* If set to 0, pick ephemeral port sequentially; otherwise randomly. */ @@ -912,93 +881,6 @@ udp_err_ack_prim(queue_t *q, mblk_t *mp, t_scalar_t primitive, } } -/*ARGSUSED2*/ -static int -udp_extra_priv_ports_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - int i; - udp_t *udp = Q_TO_UDP(q); - udp_stack_t *us = udp->udp_us; - - for (i = 0; i < us->us_num_epriv_ports; i++) { - if (us->us_epriv_ports[i] != 0) - (void) mi_mpprintf(mp, "%d ", us->us_epriv_ports[i]); - } - return (0); -} - -/* ARGSUSED1 */ -static int -udp_extra_priv_ports_add(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - int i; - udp_t *udp = Q_TO_UDP(q); - udp_stack_t *us = udp->udp_us; - - /* - * Fail the request if the new value does not lie within the - * port number limits. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value <= 0 || new_value >= 65536) { - return (EINVAL); - } - - /* Check if the value is already in the list */ - for (i = 0; i < us->us_num_epriv_ports; i++) { - if (new_value == us->us_epriv_ports[i]) { - return (EEXIST); - } - } - /* Find an empty slot */ - for (i = 0; i < us->us_num_epriv_ports; i++) { - if (us->us_epriv_ports[i] == 0) - break; - } - if (i == us->us_num_epriv_ports) { - return (EOVERFLOW); - } - - /* Set the new value */ - us->us_epriv_ports[i] = (in_port_t)new_value; - return (0); -} - -/* ARGSUSED1 */ -static int -udp_extra_priv_ports_del(queue_t *q, mblk_t *mp, char *value, caddr_t cp, - cred_t *cr) -{ - long new_value; - int i; - udp_t *udp = Q_TO_UDP(q); - udp_stack_t *us = udp->udp_us; - - /* - * Fail the request if the new value does not lie within the - * port number limits. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value <= 0 || new_value >= 65536) { - return (EINVAL); - } - - /* Check that the value is already in the list */ - for (i = 0; i < us->us_num_epriv_ports; i++) { - if (us->us_epriv_ports[i] == new_value) - break; - } - if (i == us->us_num_epriv_ports) { - return (ESRCH); - } - - /* Clear the value */ - us->us_epriv_ports[i] = 0; - return (0); -} - /* At minimum we need 4 bytes of UDP header */ #define ICMP_MIN_UDP_HDR 4 @@ -2225,79 +2107,6 @@ udp_build_hdr_template(conn_t *connp, const in6_addr_t *v6src, return (0); } -/* - * This routine retrieves the value of an ND variable in a udpparam_t - * structure. It is called through nd_getset when a user reads the - * variable. - */ -/* ARGSUSED */ -static int -udp_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) -{ - udpparam_t *udppa = (udpparam_t *)cp; - - (void) mi_mpprintf(mp, "%d", udppa->udp_param_value); - return (0); -} - -/* - * Walk through the param array specified registering each element with the - * named dispatch (ND) handler. - */ -static boolean_t -udp_param_register(IDP *ndp, udpparam_t *udppa, int cnt) -{ - for (; cnt-- > 0; udppa++) { - if (udppa->udp_param_name && udppa->udp_param_name[0]) { - if (!nd_load(ndp, udppa->udp_param_name, - udp_param_get, udp_param_set, - (caddr_t)udppa)) { - nd_free(ndp); - return (B_FALSE); - } - } - } - if (!nd_load(ndp, "udp_extra_priv_ports", - udp_extra_priv_ports_get, NULL, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "udp_extra_priv_ports_add", - NULL, udp_extra_priv_ports_add, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - if (!nd_load(ndp, "udp_extra_priv_ports_del", - NULL, udp_extra_priv_ports_del, NULL)) { - nd_free(ndp); - return (B_FALSE); - } - return (B_TRUE); -} - -/* This routine sets an ND variable in a udpparam_t structure. */ -/* ARGSUSED */ -static int -udp_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr) -{ - long new_value; - udpparam_t *udppa = (udpparam_t *)cp; - - /* - * Fail the request if the new value does not lie within the - * required bounds. - */ - if (ddi_strtol(value, NULL, 10, &new_value) != 0 || - new_value < udppa->udp_param_min || - new_value > udppa->udp_param_max) { - return (EINVAL); - } - - /* Set the new value */ - udppa->udp_param_value = new_value; - return (0); -} - static mblk_t * udp_queue_fallback(udp_t *udp, mblk_t *mp) { @@ -4521,7 +4330,6 @@ udp_wput_other(queue_t *q, mblk_t *mp) struct iocblk *iocp; conn_t *connp = Q_TO_CONN(q); udp_t *udp = connp->conn_udp; - udp_stack_t *us = udp->udp_us; cred_t *cr; switch (mp->b_datap->db_type) { @@ -4655,14 +4463,6 @@ udp_wput_other(queue_t *q, mblk_t *mp) mi_copyin(q, mp, NULL, SIZEOF_STRUCT(strbuf, iocp->ioc_flag)); return; - case ND_SET: - /* nd_getset performs the necessary checking */ - case ND_GET: - if (nd_getset(q, us->us_nd, mp)) { - qreply(q, mp); - return; - } - break; case _SIOCSOCKFALLBACK: /* * Either sockmod is about to be popped and the @@ -4826,17 +4626,18 @@ static void * udp_stack_init(netstackid_t stackid, netstack_t *ns) { udp_stack_t *us; - udpparam_t *pa; int i; int error = 0; major_t major; + size_t arrsz; us = (udp_stack_t *)kmem_zalloc(sizeof (*us), KM_SLEEP); us->us_netstack = ns; + mutex_init(&us->us_epriv_port_lock, NULL, MUTEX_DEFAULT, NULL); us->us_num_epriv_ports = UDP_NUM_EPRIV_PORTS; - us->us_epriv_ports[0] = 2049; - us->us_epriv_ports[1] = 4045; + us->us_epriv_ports[0] = ULP_DEF_EPRIV_PORT1; + us->us_epriv_ports[1] = ULP_DEF_EPRIV_PORT2; /* * The smallest anonymous port in the priviledged port range which UDP @@ -4862,13 +4663,10 @@ udp_stack_init(netstackid_t stackid, netstack_t *ns) NULL); } - pa = (udpparam_t *)kmem_alloc(sizeof (udp_param_arr), KM_SLEEP); - - us->us_param_arr = pa; - bcopy(udp_param_arr, us->us_param_arr, sizeof (udp_param_arr)); - - (void) udp_param_register(&us->us_nd, - us->us_param_arr, A_CNT(udp_param_arr)); + arrsz = udp_propinfo_count * sizeof (mod_prop_info_t); + us->us_propinfo_tbl = (mod_prop_info_t *)kmem_alloc(arrsz, + KM_SLEEP); + bcopy(udp_propinfo_tbl, us->us_propinfo_tbl, arrsz); us->us_kstat = udp_kstat2_init(stackid, &us->us_statistics); us->us_mibkp = udp_kstat_init(stackid); @@ -4897,9 +4695,9 @@ udp_stack_fini(netstackid_t stackid, void *arg) us->us_bind_fanout = NULL; - nd_free(&us->us_nd); - kmem_free(us->us_param_arr, sizeof (udp_param_arr)); - us->us_param_arr = NULL; + kmem_free(us->us_propinfo_tbl, + udp_propinfo_count * sizeof (mod_prop_info_t)); + us->us_propinfo_tbl = NULL; udp_kstat_fini(stackid, us->us_mibkp); us->us_mibkp = NULL; @@ -4908,6 +4706,7 @@ udp_stack_fini(netstackid_t stackid, void *arg) us->us_kstat = NULL; bzero(&us->us_statistics, sizeof (us->us_statistics)); + mutex_destroy(&us->us_epriv_port_lock); ldi_ident_release(us->us_ldi_ident); kmem_free(us, sizeof (*us)); } @@ -6944,8 +6743,6 @@ udp_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg, } switch (cmd) { - case ND_SET: - case ND_GET: case _SIOCSOCKFALLBACK: case TI_GETPEERNAME: case TI_GETMYNAME: diff --git a/usr/src/uts/common/inet/udp/udp_tunables.c b/usr/src/uts/common/inet/udp/udp_tunables.c new file mode 100644 index 0000000000..ce43dd2cd9 --- /dev/null +++ b/usr/src/uts/common/inet/udp/udp_tunables.c @@ -0,0 +1,104 @@ +/* + * 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 <inet/ip.h> +#include <inet/ip6.h> +#include <inet/udp_impl.h> +#include <sys/sunddi.h> + +/* + * All of these are alterable, within the min/max values given, at run time. + * + * Note: All those tunables which do not start with "udp_" are Committed and + * therefore are public. See PSARC 2009/306. + */ +mod_prop_info_t udp_propinfo_tbl[] = { + /* tunable - 0 */ + { "udp_wroff_extra", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {0, 256, 32}, {32} }, + + { "udp_ipv4_ttl", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {1, 255, 255}, {255} }, + + { "udp_ipv6_hoplimit", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {0, IPV6_MAX_HOPS, IPV6_DEFAULT_HOPS}, {IPV6_DEFAULT_HOPS} }, + + { "smallest_nonpriv_port", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {1024, (32 * 1024), 1024}, {1024} }, + + { "udp_do_checksum", MOD_PROTO_UDP, + mod_set_boolean, mod_get_boolean, + {B_TRUE}, {B_TRUE} }, + + { "smallest_anon_port", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {1024, ULP_MAX_PORT, (32 * 1024)}, {(32 * 1024)} }, + + { "largest_anon_port", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {1024, ULP_MAX_PORT, ULP_MAX_PORT}, {ULP_MAX_PORT} }, + + { "send_maxbuf", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {UDP_XMIT_LOWATER, (1<<30), UDP_XMIT_HIWATER}, + {UDP_XMIT_HIWATER} }, + + { "udp_xmit_lowat", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {0, (1<<30), UDP_XMIT_LOWATER}, + {UDP_XMIT_LOWATER} }, + + { "recv_maxbuf", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {UDP_RECV_LOWATER, (1<<30), UDP_RECV_HIWATER}, + {UDP_RECV_HIWATER} }, + + /* tunable - 10 */ + { "udp_max_buf", MOD_PROTO_UDP, + mod_set_uint32, mod_get_uint32, + {65536, (1<<30), 2*1024*1024}, {2*1024*1024} }, + + { "udp_pmtu_discovery", MOD_PROTO_UDP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "udp_sendto_ignerr", MOD_PROTO_UDP, + mod_set_boolean, mod_get_boolean, + {B_FALSE}, {B_FALSE} }, + + { "extra_priv_ports", MOD_PROTO_UDP, + mod_set_extra_privports, mod_get_extra_privports, + {1, ULP_MAX_PORT, 0}, {0} }, + + { "?", MOD_PROTO_UDP, NULL, mod_get_allprop, {0}, {0} }, + + { NULL, 0, NULL, NULL, {0}, {0} } +}; + +int udp_propinfo_count = A_CNT(udp_propinfo_tbl); diff --git a/usr/src/uts/common/inet/udp_impl.h b/usr/src/uts/common/inet/udp_impl.h index 4da82a0377..11ca9f9810 100644 --- a/usr/src/uts/common/inet/udp_impl.h +++ b/usr/src/uts/common/inet/udp_impl.h @@ -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. */ @@ -48,6 +48,7 @@ extern "C" { #include <inet/common.h> #include <inet/ip.h> #include <inet/optcom.h> +#include <inet/tunables.h> #define UDP_MOD_ID 5607 @@ -89,16 +90,14 @@ typedef struct udp_stat { /* Class "net" kstats */ } udp_stat_t; -/* Named Dispatch Parameter Management Structure */ -typedef struct udpparam_s { - uint32_t udp_param_min; - uint32_t udp_param_max; - uint32_t udp_param_value; - char *udp_param_name; -} udpparam_t; - #define UDP_NUM_EPRIV_PORTS 64 +/* Default buffer size and flow control wake up threshold. */ +#define UDP_RECV_HIWATER (56 * 1024) +#define UDP_RECV_LOWATER 128 +#define UDP_XMIT_HIWATER (56 * 1024) +#define UDP_XMIT_LOWATER 1024 + /* * UDP stack instances */ @@ -110,12 +109,13 @@ struct udp_stack { int us_num_epriv_ports; in_port_t us_epriv_ports[UDP_NUM_EPRIV_PORTS]; + kmutex_t us_epriv_port_lock; /* Hint not protected by any lock */ in_port_t us_next_port_to_try; - IDP us_nd; /* Points to table of UDP ND variables. */ - udpparam_t *us_param_arr; /* ndd variable table */ + /* UDP tunables table */ + struct mod_prop_info_s *us_propinfo_tbl; kstat_t *us_mibkp; /* kstats exporting mib data */ kstat_t *us_kstat; @@ -181,20 +181,19 @@ typedef struct udpahdr_s { uint16_t uha_checksum; /* UDP checksum */ } udpha_t; -#define us_wroff_extra us_param_arr[0].udp_param_value -#define us_ipv4_ttl us_param_arr[1].udp_param_value -#define us_ipv6_hoplimit us_param_arr[2].udp_param_value -#define us_smallest_nonpriv_port us_param_arr[3].udp_param_value -#define us_do_checksum us_param_arr[4].udp_param_value -#define us_smallest_anon_port us_param_arr[5].udp_param_value -#define us_largest_anon_port us_param_arr[6].udp_param_value -#define us_xmit_hiwat us_param_arr[7].udp_param_value -#define us_xmit_lowat us_param_arr[8].udp_param_value -#define us_recv_hiwat us_param_arr[9].udp_param_value -#define us_max_buf us_param_arr[10].udp_param_value -#define us_pmtu_discovery us_param_arr[11].udp_param_value -#define us_sendto_ignerr us_param_arr[12].udp_param_value - +#define us_wroff_extra us_propinfo_tbl[0].prop_cur_uval +#define us_ipv4_ttl us_propinfo_tbl[1].prop_cur_uval +#define us_ipv6_hoplimit us_propinfo_tbl[2].prop_cur_uval +#define us_smallest_nonpriv_port us_propinfo_tbl[3].prop_cur_uval +#define us_do_checksum us_propinfo_tbl[4].prop_cur_bval +#define us_smallest_anon_port us_propinfo_tbl[5].prop_cur_uval +#define us_largest_anon_port us_propinfo_tbl[6].prop_cur_uval +#define us_xmit_hiwat us_propinfo_tbl[7].prop_cur_uval +#define us_xmit_lowat us_propinfo_tbl[8].prop_cur_uval +#define us_recv_hiwat us_propinfo_tbl[9].prop_cur_uval +#define us_max_buf us_propinfo_tbl[10].prop_cur_uval +#define us_pmtu_discovery us_propinfo_tbl[11].prop_cur_bval +#define us_sendto_ignerr us_propinfo_tbl[12].prop_cur_bval #define UDP_STAT(us, x) ((us)->us_statistics.x.value.ui64++) #define UDP_STAT_UPDATE(us, x, n) \ diff --git a/usr/src/uts/common/io/strplumb.c b/usr/src/uts/common/io/strplumb.c index 473f7bc72e..1a3cb37248 100644 --- a/usr/src/uts/common/io/strplumb.c +++ b/usr/src/uts/common/io/strplumb.c @@ -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. */ @@ -123,8 +123,6 @@ _info(struct modinfo *modinfop) #define TCP6 "tcp6" #define UDP "udp" #define UDP6 "udp6" -#define SCTP "sctp" -#define SCTP6 "sctp6" #define ICMP "icmp" #define ICMP6 "icmp6" #define IP "ip" @@ -134,7 +132,6 @@ _info(struct modinfo *modinfop) #define UDPDEV "/devices/pseudo/udp@0:udp" #define TCP6DEV "/devices/pseudo/tcp6@0:tcp6" #define UDP6DEV "/devices/pseudo/udp6@0:udp6" -#define SCTP6DEV "/devices/pseudo/sctp6@0:sctp6" #define IP6DEV "/devices/pseudo/ip6@0:ip6" typedef struct strplumb_modspec { @@ -150,8 +147,6 @@ static strplumb_modspec_t strplumb_modlist[] = { { "drv", TCP6 }, { "drv", UDP }, { "drv", UDP6 }, - { "drv", SCTP }, - { "drv", SCTP6 }, { "drv", ICMP }, { "drv", ICMP6 }, { "drv", ARP }, diff --git a/usr/src/uts/common/net/if.h b/usr/src/uts/common/net/if.h index d92a4ff6c3..41457e2edb 100644 --- a/usr/src/uts/common/net/if.h +++ b/usr/src/uts/common/net/if.h @@ -1,5 +1,5 @@ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -170,13 +170,15 @@ struct ifnet { #define IFF_IPMP 0x8000000000ll /* IPMP IP interface */ #define IFF_VRRP 0x10000000000ll /* Managed by VRRP */ +#define IFF_NOLINKLOCAL 0x20000000000ll /* No default linklocal */ + /* flags that cannot be changed by userland on any interface */ #define IFF_CANTCHANGE \ (IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING | IFF_PROMISC | \ IFF_MULTICAST | IFF_MULTI_BCAST | IFF_UNNUMBERED | IFF_IPV4 | \ IFF_IPV6 | IFF_IPMP | IFF_FIXEDMTU | IFF_VIRTUAL | \ IFF_LOOPBACK | IFF_ALLMULTI | IFF_DUPLICATE | IFF_COS_ENABLED | \ - IFF_VRRP) + IFF_VRRP | IFF_NOLINKLOCAL) /* flags that cannot be changed by userland on an IPMP interface */ #define IFF_IPMP_CANTCHANGE IFF_FAILED @@ -378,6 +380,7 @@ struct lifreq { char lifru_groupname[LIFGRNAMSIZ]; /* SIOC[GS]LIFGROUPNAME */ char lifru_binding[LIFNAMSIZ]; /* SIOCGLIFBINDING */ zoneid_t lifru_zoneid; /* SIOC[GS]LIFZONE */ + uint_t lifru_dadstate; /* SIOCGLIFDADSTATE */ } lifr_lifru; #define lifr_addr lifr_lifru.lifru_addr /* address */ @@ -396,6 +399,7 @@ struct lifreq { #define lifr_groupname lifr_lifru.lifru_groupname #define lifr_binding lifr_lifru.lifru_binding #define lifr_zoneid lifr_lifru.lifru_zoneid +#define lifr_dadstate lifr_lifru.lifru_dadstate }; #endif /* defined(_INT64_TYPE) */ @@ -421,6 +425,12 @@ struct sioc_lsg_req { uint_t slr_pad; }; +/* Argument structure for SIOCGLIFDADSTATE ioctl */ +typedef enum { + DAD_IN_PROGRESS = 0x1, + DAD_DONE = 0x2 +} glif_dad_state_t; + /* * OBSOLETE: Replaced by struct lifreq. Supported for compatibility. * @@ -561,6 +571,7 @@ struct lifsrcof { #define LIFC_ALLZONES 0x08 /* Include all zones */ /* (must be issued from global zone) */ #define LIFC_UNDER_IPMP 0x10 /* Include underlying IPMP interfaces */ +#define LIFC_ENABLED 0x20 /* Include only IFF_UP interfaces */ #if defined(_SYSCALL32) diff --git a/usr/src/uts/common/sys/sockio.h b/usr/src/uts/common/sys/sockio.h index 0fca7c5c43..b81c30b373 100644 --- a/usr/src/uts/common/sys/sockio.h +++ b/usr/src/uts/common/sys/sockio.h @@ -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. */ @@ -318,6 +318,25 @@ extern "C" { */ #define SIOCILB _IOWR('i', 187, 0) +/* + * IOCTL's to get/set module specific or interface specific properties. + * Argument is a struct mod_ioc_prop_s. These ioctls are Consolidation Private. + */ +#define SIOCGETPROP _IOWRN('p', 188, 0) +#define SIOCSETPROP _IOW('p', 189, 0) + +/* + * IOCTL used to check for the given ipif, whether DAD is in progress or + * DAD has completed. This ioctl is Consolidation Private. + */ +#define SIOCGLIFDADSTATE _IOWR('i', 190, struct lifreq) + +/* + * IOCTL used to generate an IPv6 address using the given prefix and the + * default token for the interface. + */ +#define SIOCSLIFPREFIX _IOWR('i', 191, struct lifreq) + #ifdef __cplusplus } #endif diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared index fe175e3d5e..8ee27ab393 100644 --- a/usr/src/uts/intel/Makefile.intel.shared +++ b/usr/src/uts/intel/Makefile.intel.shared @@ -318,8 +318,6 @@ DRV_KMODS += rum DRV_KMODS += rwd DRV_KMODS += rwn DRV_KMODS += sad -DRV_KMODS += sctp -DRV_KMODS += sctp6 DRV_KMODS += sd DRV_KMODS += sdcard DRV_KMODS += sdhost diff --git a/usr/src/uts/intel/ip/ip.global-objs.debug64 b/usr/src/uts/intel/ip/ip.global-objs.debug64 index bfad66f0a2..acaa8bfec6 100644 --- a/usr/src/uts/intel/ip/ip.global-objs.debug64 +++ b/usr/src/uts/intel/ip/ip.global-objs.debug64 @@ -76,7 +76,7 @@ icmp_max_optsize icmp_mod_info icmp_opt_arr icmp_opt_obj -icmp_param_arr +icmp_propinfo_tbl icmp_valid_levels_arr icmpinfov4 icmpinfov6 @@ -128,6 +128,8 @@ ip_ndx_ioctl_count ip_ndx_ioctl_table ip_poll_normal_ms ip_poll_normal_ticks +ip_propinfo_tbl +ip_propinfo_count ip_rput_pullups ip_six_byte_all_ones ip_squeue_create_callback @@ -161,14 +163,12 @@ ipsec_policy_failure_msgs ipsec_sel_cache ipsec_spd_hashsize ipsec_weird_null_inbound_policy -ipv4_forward_suffix ipv4info ipv6_all_hosts_mcast ipv6_all_ones ipv6_all_rtrs_mcast ipv6_all_v2rtrs_mcast ipv6_all_zeros -ipv6_forward_suffix ipv6_ll_template ipv6_loopback ipv6_solicited_node_mcast @@ -180,12 +180,7 @@ ire_gw_secattr_cache ire_null ire_nv_arr ire_nv_tbl -lcl_ndp_arr lcl_param_arr -lcl_sctp_param_arr -lcl_sctp_wroff_xtra_param -lcl_tcp_param_arr -lcl_tcp_wroff_xtra_param mask_rnhead max_keylen modldrv @@ -224,18 +219,16 @@ sctp_conn_hash_size sctp_kmem_faddr_cache sctp_kmem_ftsn_set_cache sctp_kmem_set_cache -sctp_mod_info sctp_opt_arr sctp_opt_arr_size +sctp_propinfo_tbl +sctp_propinfo_count sctp_recvq_tq_task_max sctp_recvq_tq_task_min sctp_recvq_tq_thr_max sctp_recvq_tq_thr_min sctp_sin6_null sctpdebug -sctpinfo -sctprinit -sctpwinit sin6_null sin_null skip_sctp_cksum @@ -272,6 +265,8 @@ tcp_min_conn_listener tcp_opt_arr tcp_opt_obj tcp_outbound_squeue_switch +tcp_propinfo_tbl +tcp_propinfo_count tcp_random_anon_port tcp_random_end_ptr tcp_random_fptr @@ -308,7 +303,8 @@ udp_max_optsize udp_mod_info udp_opt_arr udp_opt_obj -udp_param_arr +udp_propinfo_tbl +udp_propinfo_count udp_random_anon_port udp_rinitv4 udp_rinitv6 diff --git a/usr/src/uts/intel/ip/ip.global-objs.obj64 b/usr/src/uts/intel/ip/ip.global-objs.obj64 index 0d3f7a73f7..8f1eaf800e 100644 --- a/usr/src/uts/intel/ip/ip.global-objs.obj64 +++ b/usr/src/uts/intel/ip/ip.global-objs.obj64 @@ -76,7 +76,7 @@ icmp_max_optsize icmp_mod_info icmp_opt_arr icmp_opt_obj -icmp_param_arr +icmp_propinfo_tbl icmp_valid_levels_arr icmpinfov4 icmpinfov6 @@ -128,6 +128,8 @@ ip_ndx_ioctl_count ip_ndx_ioctl_table ip_poll_normal_ms ip_poll_normal_ticks +ip_propinfo_tbl +ip_propinfo_count ip_rput_pullups ip_six_byte_all_ones ip_squeue_create_callback @@ -161,14 +163,12 @@ ipsec_policy_failure_msgs ipsec_sel_cache ipsec_spd_hashsize ipsec_weird_null_inbound_policy -ipv4_forward_suffix ipv4info ipv6_all_hosts_mcast ipv6_all_ones ipv6_all_rtrs_mcast ipv6_all_v2rtrs_mcast ipv6_all_zeros -ipv6_forward_suffix ipv6_ll_template ipv6_loopback ipv6_solicited_node_mcast @@ -180,12 +180,7 @@ ire_gw_secattr_cache ire_null ire_nv_arr ire_nv_tbl -lcl_ndp_arr lcl_param_arr -lcl_sctp_param_arr -lcl_sctp_wroff_xtra_param -lcl_tcp_param_arr -lcl_tcp_wroff_xtra_param mask_rnhead max_keylen modldrv @@ -222,18 +217,16 @@ sctp_conn_hash_size sctp_kmem_faddr_cache sctp_kmem_ftsn_set_cache sctp_kmem_set_cache -sctp_mod_info sctp_opt_arr sctp_opt_arr_size +sctp_propinfo_tbl +sctp_propinfo_count sctp_recvq_tq_task_max sctp_recvq_tq_task_min sctp_recvq_tq_thr_max sctp_recvq_tq_thr_min sctp_sin6_null sctpdebug -sctpinfo -sctprinit -sctpwinit sin6_null sin_null sock_rawip_downcalls @@ -269,6 +262,8 @@ tcp_min_conn_listener tcp_opt_arr tcp_opt_obj tcp_outbound_squeue_switch +tcp_propinfo_tbl +tcp_propinfo_count tcp_random_anon_port tcp_random_end_ptr tcp_random_fptr @@ -305,7 +300,8 @@ udp_max_optsize udp_mod_info udp_opt_arr udp_opt_obj -udp_param_arr +udp_propinfo_tbl +udp_propinfo_count udp_random_anon_port udp_rinitv4 udp_rinitv6 diff --git a/usr/src/uts/intel/sctp/Makefile b/usr/src/uts/intel/sctp/Makefile deleted file mode 100644 index 94ec17f3c6..0000000000 --- a/usr/src/uts/intel/sctp/Makefile +++ /dev/null @@ -1,95 +0,0 @@ -# -# 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. -# -# 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 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the sctp driver kernel module. -# -# intel implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = sctp -OBJECTS = $(SCTP_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(SCTP_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/inet/sctp - -# -# Extra for $(MODULE).check target -# -# Need to remove ipddi.o since it has non-static defines for _init etc. -IP_CHECK_OBJS = $(IP_OBJS:ipddi.o=ip.o) -EXTRA_CHECK_OBJS = $(IP_CHECK_OBJS:%=../ip/$(OBJS_DIR)/%) - -# -# Include common rules. -# -include $(UTSBASE)/intel/Makefile.intel - -# -# Define targets -# -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) - -# -# depends on ip -# -LDFLAGS += -dy -Ndrv/ip - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/intel/sctp6/Makefile b/usr/src/uts/intel/sctp6/Makefile deleted file mode 100644 index f53a6e849f..0000000000 --- a/usr/src/uts/intel/sctp6/Makefile +++ /dev/null @@ -1,95 +0,0 @@ -# -# 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. -# -# 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 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the sctp6 driver kernel module. -# -# intel implementation architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = sctp6 -OBJECTS = $(SCTP6_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(SCTP6_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/inet/sctp - -# -# Extra for $(MODULE).check target -# -# Need to remove ipddi.o since it has non-static defines for _init etc. -IP_CHECK_OBJS = $(IP_OBJS:ipddi.o=ip.o) -EXTRA_CHECK_OBJS = $(IP_CHECK_OBJS:%=../ip/$(OBJS_DIR)/%) - -# -# Include common rules. -# -include $(UTSBASE)/intel/Makefile.intel - -# -# Define targets -# -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) - -# -# depends on sctp ip -# -LDFLAGS += -dy -Ndrv/sctp -Ndrv/ip - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/intel/Makefile.targ diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index 0fbac6b400..7ce341b003 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -212,7 +212,7 @@ DRV_KMODS += pseudo ptc ptm pts ptsl ramdisk random rsm rts sad DRV_KMODS += simnet softmac sppp sppptun sy sysevent sysmsg DRV_KMODS += spdsock DRV_KMODS += tcp tcp6 tl tnf ttymux udp udp6 wc winlock zcons -DRV_KMODS += ippctl sctp sctp6 +DRV_KMODS += ippctl DRV_KMODS += dld DRV_KMODS += ipf DRV_KMODS += rpcib diff --git a/usr/src/uts/sparc/ip/ip.global-objs.debug64 b/usr/src/uts/sparc/ip/ip.global-objs.debug64 index bfad66f0a2..acaa8bfec6 100644 --- a/usr/src/uts/sparc/ip/ip.global-objs.debug64 +++ b/usr/src/uts/sparc/ip/ip.global-objs.debug64 @@ -76,7 +76,7 @@ icmp_max_optsize icmp_mod_info icmp_opt_arr icmp_opt_obj -icmp_param_arr +icmp_propinfo_tbl icmp_valid_levels_arr icmpinfov4 icmpinfov6 @@ -128,6 +128,8 @@ ip_ndx_ioctl_count ip_ndx_ioctl_table ip_poll_normal_ms ip_poll_normal_ticks +ip_propinfo_tbl +ip_propinfo_count ip_rput_pullups ip_six_byte_all_ones ip_squeue_create_callback @@ -161,14 +163,12 @@ ipsec_policy_failure_msgs ipsec_sel_cache ipsec_spd_hashsize ipsec_weird_null_inbound_policy -ipv4_forward_suffix ipv4info ipv6_all_hosts_mcast ipv6_all_ones ipv6_all_rtrs_mcast ipv6_all_v2rtrs_mcast ipv6_all_zeros -ipv6_forward_suffix ipv6_ll_template ipv6_loopback ipv6_solicited_node_mcast @@ -180,12 +180,7 @@ ire_gw_secattr_cache ire_null ire_nv_arr ire_nv_tbl -lcl_ndp_arr lcl_param_arr -lcl_sctp_param_arr -lcl_sctp_wroff_xtra_param -lcl_tcp_param_arr -lcl_tcp_wroff_xtra_param mask_rnhead max_keylen modldrv @@ -224,18 +219,16 @@ sctp_conn_hash_size sctp_kmem_faddr_cache sctp_kmem_ftsn_set_cache sctp_kmem_set_cache -sctp_mod_info sctp_opt_arr sctp_opt_arr_size +sctp_propinfo_tbl +sctp_propinfo_count sctp_recvq_tq_task_max sctp_recvq_tq_task_min sctp_recvq_tq_thr_max sctp_recvq_tq_thr_min sctp_sin6_null sctpdebug -sctpinfo -sctprinit -sctpwinit sin6_null sin_null skip_sctp_cksum @@ -272,6 +265,8 @@ tcp_min_conn_listener tcp_opt_arr tcp_opt_obj tcp_outbound_squeue_switch +tcp_propinfo_tbl +tcp_propinfo_count tcp_random_anon_port tcp_random_end_ptr tcp_random_fptr @@ -308,7 +303,8 @@ udp_max_optsize udp_mod_info udp_opt_arr udp_opt_obj -udp_param_arr +udp_propinfo_tbl +udp_propinfo_count udp_random_anon_port udp_rinitv4 udp_rinitv6 diff --git a/usr/src/uts/sparc/ip/ip.global-objs.obj64 b/usr/src/uts/sparc/ip/ip.global-objs.obj64 index 0d3f7a73f7..8f1eaf800e 100644 --- a/usr/src/uts/sparc/ip/ip.global-objs.obj64 +++ b/usr/src/uts/sparc/ip/ip.global-objs.obj64 @@ -76,7 +76,7 @@ icmp_max_optsize icmp_mod_info icmp_opt_arr icmp_opt_obj -icmp_param_arr +icmp_propinfo_tbl icmp_valid_levels_arr icmpinfov4 icmpinfov6 @@ -128,6 +128,8 @@ ip_ndx_ioctl_count ip_ndx_ioctl_table ip_poll_normal_ms ip_poll_normal_ticks +ip_propinfo_tbl +ip_propinfo_count ip_rput_pullups ip_six_byte_all_ones ip_squeue_create_callback @@ -161,14 +163,12 @@ ipsec_policy_failure_msgs ipsec_sel_cache ipsec_spd_hashsize ipsec_weird_null_inbound_policy -ipv4_forward_suffix ipv4info ipv6_all_hosts_mcast ipv6_all_ones ipv6_all_rtrs_mcast ipv6_all_v2rtrs_mcast ipv6_all_zeros -ipv6_forward_suffix ipv6_ll_template ipv6_loopback ipv6_solicited_node_mcast @@ -180,12 +180,7 @@ ire_gw_secattr_cache ire_null ire_nv_arr ire_nv_tbl -lcl_ndp_arr lcl_param_arr -lcl_sctp_param_arr -lcl_sctp_wroff_xtra_param -lcl_tcp_param_arr -lcl_tcp_wroff_xtra_param mask_rnhead max_keylen modldrv @@ -222,18 +217,16 @@ sctp_conn_hash_size sctp_kmem_faddr_cache sctp_kmem_ftsn_set_cache sctp_kmem_set_cache -sctp_mod_info sctp_opt_arr sctp_opt_arr_size +sctp_propinfo_tbl +sctp_propinfo_count sctp_recvq_tq_task_max sctp_recvq_tq_task_min sctp_recvq_tq_thr_max sctp_recvq_tq_thr_min sctp_sin6_null sctpdebug -sctpinfo -sctprinit -sctpwinit sin6_null sin_null sock_rawip_downcalls @@ -269,6 +262,8 @@ tcp_min_conn_listener tcp_opt_arr tcp_opt_obj tcp_outbound_squeue_switch +tcp_propinfo_tbl +tcp_propinfo_count tcp_random_anon_port tcp_random_end_ptr tcp_random_fptr @@ -305,7 +300,8 @@ udp_max_optsize udp_mod_info udp_opt_arr udp_opt_obj -udp_param_arr +udp_propinfo_tbl +udp_propinfo_count udp_random_anon_port udp_rinitv4 udp_rinitv6 diff --git a/usr/src/uts/sparc/sctp/Makefile b/usr/src/uts/sparc/sctp/Makefile deleted file mode 100644 index c06523e32f..0000000000 --- a/usr/src/uts/sparc/sctp/Makefile +++ /dev/null @@ -1,100 +0,0 @@ -# -# 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. -# -# 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 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the sctp driver kernel module. -# -# sparc architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = sctp -OBJECTS = $(SCTP_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(SCTP_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/inet/sctp - -# -# Extra for $(MODULE).check target -# -# Need to remove ipddi.o since it has non-static defines for _init etc. -IP_CHECK_OBJS = $(IP_OBJS:ipddi.o=ip.o) -EXTRA_CHECK_OBJS = $(IP_CHECK_OBJS:%=../ip/$(OBJS_DIR)/%) - -# -# Include common rules. -# -include $(UTSBASE)/sparc/Makefile.sparc - -# -# Define targets -# -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) - -# -# lint pass one enforcement -# -CFLAGS += -v - -# -# depends on IP -# -LDFLAGS += -dy -Ndrv/ip - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/sparc/Makefile.targ diff --git a/usr/src/uts/sparc/sctp6/Makefile b/usr/src/uts/sparc/sctp6/Makefile deleted file mode 100644 index 215320f438..0000000000 --- a/usr/src/uts/sparc/sctp6/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# -# 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. -# -# 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 2004 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -#ident "%Z%%M% %I% %E% SMI" -# -# This makefile drives the production of the sctp6 driver kernel module. -# -# sparc architecture dependent -# - -# -# Path to the base of the uts directory tree (usually /usr/src/uts). -# -UTSBASE = ../.. - -# -# Define the module and object file sets. -# -MODULE = sctp6 -OBJECTS = $(SCTP6_OBJS:%=$(OBJS_DIR)/%) -LINTS = $(SCTP6_OBJS:%.o=$(LINTS_DIR)/%.ln) -ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) -CONF_SRCDIR = $(UTSBASE)/common/inet/sctp - -# -# Include common rules. -# -include $(UTSBASE)/sparc/Makefile.sparc - -# -# Define targets -# -ALL_TARGET = $(BINARY) $(SRC_CONFFILE) -LINT_TARGET = $(MODULE).lint -INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) - -# -# lint pass one enforcement -# -CFLAGS += -v -LDFLAGS += -dy -Ndrv/sctp -Ndrv/ip - -# -# Default build targets. -# -.KEEP_STATE: - -def: $(DEF_DEPS) - -all: $(ALL_DEPS) - -clean: $(CLEAN_DEPS) - -clobber: $(CLOBBER_DEPS) - -lint: $(LINT_DEPS) - -modlintlib: $(MODLINTLIB_DEPS) - -clean.lint: $(CLEAN_LINT_DEPS) - -install: $(INSTALL_DEPS) - -# -# Include common targets. -# -include $(UTSBASE)/sparc/Makefile.targ |