diff options
author | Sowmini Varadhan <Sowmini.Varadhan@oracle.COM> | 2010-07-01 17:10:52 -0400 |
---|---|---|
committer | Sowmini Varadhan <Sowmini.Varadhan@oracle.COM> | 2010-07-01 17:10:52 -0400 |
commit | 550b6e4083768ca350e9e7c3a1ebbf720b23dcad (patch) | |
tree | 68629051e97e6173c4b53d2483015eeea30cacbc | |
parent | bf7fda8965eb0f1d22b8e7bf1684b99227cd2b64 (diff) | |
download | illumos-joyent-550b6e4083768ca350e9e7c3a1ebbf720b23dcad.tar.gz |
PSARC 2010/166 layer-3 net properties for exclusive-IP zones
6944327 need to support address and defrouter resources for exclusive-IP zones
45 files changed, 2198 insertions, 163 deletions
diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile index 4e7f342ed5..73ec4241c2 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile @@ -44,7 +44,7 @@ $(ROOTCFGFILES) := FILEMODE= 644 ROOTCMDDIR= $(ROOTFS_LIBDIR)/inet -LDLIBS += -lipadm -lnvpair -lsecdb -lnsl -lumem +LDLIBS += -lipadm -lnvpair -lsecdb -lnsl -lumem -lscf # # Instrument ipmgmtd with CTF data to ease debugging. diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c index 54472333d2..8e9e153b21 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c @@ -554,33 +554,9 @@ i_ipmgmt_delif_aobjs(char *ifname, sa_family_t af, uint32_t flags) 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); + rval.ir_err = ipmgmt_persist_if(argp); (void) door_return((char *)&rval, sizeof (rval), NULL, 0); } @@ -856,3 +832,33 @@ fail: rvalp->ir_err = err; (void) door_return((char *)rvalp, sizeof (*rvalp), NULL, 0); } + +int +ipmgmt_persist_if(ipmgmt_if_arg_t *sargp) +{ + 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: + nvlist_free(nvl); + return (err); +} diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h index f693a34266..36768b5ae0 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h @@ -38,6 +38,7 @@ extern "C" { #include <pthread.h> #define IPMGMT_STRSIZE 256 +#define IPMGMTD_FMRI "svc:/network/ip-interface-management:default" /* ipmgmt_door.c */ extern void ipmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t); @@ -145,6 +146,8 @@ extern boolean_t ipmgmt_aobjmap_init(void *, nvlist_t *, char *, extern int ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *, ipadm_db_op_t); +extern boolean_t ipmgmt_first_boot(); +extern int ipmgmt_persist_if(ipmgmt_if_arg_t *); #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c index 011126d010..63b1e48a89 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -63,6 +62,13 @@ #include <sys/stat.h> #include <unistd.h> #include "ipmgmt_impl.h" +#include <zone.h> +#include <libipadm.h> +#include <libdladm.h> +#include <libdllink.h> +#include <net/route.h> +#include <ipadm_ipmgmt.h> +#include <sys/brand.h> const char *progname; @@ -81,6 +87,18 @@ static int ipmgmt_door_fd = -1; static void ipmgmt_exit(int); static int ipmgmt_init(); static int ipmgmt_init_privileges(); +static void ipmgmt_ngz_init(); +static void ipmgmt_ngz_persist_if(); + +static ipadm_handle_t iph; +typedef struct ipmgmt_pif_s { + struct ipmgmt_pif_s *pif_next; + char pif_ifname[LIFNAMSIZ]; + boolean_t pif_v4; + boolean_t pif_v6; +} ipmgmt_pif_t; + +static ipmgmt_pif_t *ngz_pifs; static int ipmgmt_db_init() @@ -115,6 +133,9 @@ ipmgmt_db_init() } (void) pthread_rwlock_init(&ipmgmt_dbconf_lock, NULL); + + ipmgmt_ngz_persist_if(); /* create persistent interface info for NGZ */ + return (err); } @@ -214,11 +235,89 @@ ipmgmt_exit(int signo) } /* - * Set the uid of this daemon to the "ipadm" user. Finish the following + * On the first reboot after installation of an ipkg zone, + * ipmgmt_persist_if_cb() is used in non-global zones to track the interfaces + * that have IP address configuration assignments from the global zone. + * Persistent configuration for the interfaces is created on the first boot + * by ipmgmtd, and the addresses assigned to the interfaces by the GZ + * will be subsequently configured when the interface is enabled. + * Note that ipmgmt_persist_if_cb() only sets up a list of interfaces + * that need to be persisted- the actual update of the ipadm data-store happens + * in ipmgmt_persist_if() after the appropriate privs/uid state has been set up. + */ +static void +ipmgmt_persist_if_cb(char *ifname, boolean_t v4, boolean_t v6) +{ + ipmgmt_pif_t *pif; + + pif = calloc(1, sizeof (*pif)); + if (pif == NULL) { + ipmgmt_log(LOG_WARNING, + "Could not allocate memory to configure %s", ifname); + return; + } + (void) strlcpy(pif->pif_ifname, ifname, sizeof (pif->pif_ifname)); + pif->pif_v4 = v4; + pif->pif_v6 = v6; + pif->pif_next = ngz_pifs; + ngz_pifs = pif; +} + +/* + * ipmgmt_ngz_init() initializes exclusive-IP stack non-global zones by + * extracting configuration that has been saved in the kernel and applying + * it at zone boot. + */ +static void +ipmgmt_ngz_init() +{ + zoneid_t zoneid; + boolean_t firstboot = B_TRUE, s10c = B_FALSE; + char brand[MAXNAMELEN]; + ipadm_status_t ipstatus; + + zoneid = getzoneid(); + if (zoneid != GLOBAL_ZONEID) { + + if (zone_getattr(zoneid, ZONE_ATTR_BRAND, brand, + sizeof (brand)) < 0) { + ipmgmt_log(LOG_ERR, "Could not get brand name"); + return; + } + /* + * firstboot is always true for S10C zones, where ipadm is not + * available for restoring persistent configuration. + */ + if (strcmp(brand, NATIVE_BRAND_NAME) == 0) + firstboot = ipmgmt_first_boot(); + else + s10c = B_TRUE; + + if (!firstboot) + return; + + ipstatus = ipadm_open(&iph, IPH_IPMGMTD); + if (ipstatus != IPADM_SUCCESS) { + ipmgmt_log(LOG_ERR, "could not open ipadm handle", + ipadm_status2str(ipstatus)); + return; + } + /* + * Only pass down the callback to persist the interface + * for NATIVE (ipkg) zones. + */ + (void) ipadm_init_net_from_gz(iph, NULL, + (s10c ? NULL : ipmgmt_persist_if_cb)); + ipadm_close(iph); + } +} + +/* + * Set the uid of this daemon to the "netadm" 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"; + * - change its uid/gid to "netadm"/"netadm"; */ static int ipmgmt_init_privileges() @@ -246,6 +345,14 @@ ipmgmt_init_privileges() } /* + * initialize any NGZ specific network information before dropping + * privileges. We need these privileges to plumb IP interfaces handed + * down from the GZ (for dlpi_open() etc.) and also to configure the + * address itself (for any IPI_PRIV ioctls like SLIFADDR) + */ + ipmgmt_ngz_init(); + + /* * limit the privileges of this daemon and set the uid of this * daemon to UID_NETADM */ @@ -380,3 +487,61 @@ child_out: ipmgmt_inform_parent_exit(EXIT_FAILURE); return (EXIT_FAILURE); } + +/* + * Return TRUE if `ifname' has persistent configuration for the `af' address + * family in the datastore + */ +static boolean_t +ipmgmt_persist_if_exists(char *ifname, sa_family_t af) +{ + ipmgmt_getif_cbarg_t cbarg; + boolean_t exists = B_FALSE; + ipadm_if_info_t *ifp; + + bzero(&cbarg, sizeof (cbarg)); + cbarg.cb_ifname = ifname; + (void) ipmgmt_db_walk(ipmgmt_db_getif, &cbarg, IPADM_DB_READ); + if ((ifp = cbarg.cb_ifinfo) != NULL) { + if ((af == AF_INET && (ifp->ifi_pflags & IFIF_IPV4)) || + (af == AF_INET6 && (ifp->ifi_pflags & IFIF_IPV6))) { + exists = B_TRUE; + } + } + free(ifp); + return (exists); +} + +/* + * Persist any NGZ interfaces assigned to us from the global zone if they do + * not already exist in the persistent db. We need to + * do this before any calls to ipadm_enable_if() can succeed (i.e., + * before opening up for door_calls), and after setuid to 'netadm' so that + * the persistent db is created with the right permissions. + */ +static void +ipmgmt_ngz_persist_if() +{ + ipmgmt_pif_t *pif, *next; + ipmgmt_if_arg_t ifarg; + + for (pif = ngz_pifs; pif != NULL; pif = next) { + next = pif->pif_next; + bzero(&ifarg, sizeof (ifarg)); + (void) strlcpy(ifarg.ia_ifname, pif->pif_ifname, + sizeof (ifarg.ia_ifname)); + ifarg.ia_flags = IPMGMT_PERSIST; + if (pif->pif_v4 && + !ipmgmt_persist_if_exists(pif->pif_ifname, AF_INET)) { + ifarg.ia_family = AF_INET; + (void) ipmgmt_persist_if(&ifarg); + } + if (pif->pif_v6 && + !ipmgmt_persist_if_exists(pif->pif_ifname, AF_INET6)) { + ifarg.ia_family = AF_INET6; + (void) ipmgmt_persist_if(&ifarg); + } + free(pif); + } + ngz_pifs = NULL; /* no red herrings */ +} diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c index 09275519a6..25e2f3a5ee 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c @@ -56,6 +56,7 @@ #include <arpa/inet.h> #include <unistd.h> #include "ipmgmt_impl.h" +#include <libscf.h> #define ATYPE "_atype" /* name of the address type nvpair */ #define FLAGS "_flags" /* name of the flags nvpair */ @@ -1164,3 +1165,203 @@ ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) } return (err); } + +typedef struct scf_resources { + scf_handle_t *sr_handle; + scf_instance_t *sr_inst; + scf_propertygroup_t *sr_pg; + scf_property_t *sr_prop; + scf_value_t *sr_val; + scf_transaction_t *sr_tx; + scf_transaction_entry_t *sr_ent; +} scf_resources_t; + +/* + * Inputs: + * res is a pointer to the scf_resources_t to be released. + */ +static void +ipmgmt_release_scf_resources(scf_resources_t *res) +{ + scf_entry_destroy(res->sr_ent); + scf_transaction_destroy(res->sr_tx); + scf_value_destroy(res->sr_val); + scf_property_destroy(res->sr_prop); + scf_pg_destroy(res->sr_pg); + scf_instance_destroy(res->sr_inst); + (void) scf_handle_unbind(res->sr_handle); + scf_handle_destroy(res->sr_handle); +} + +/* + * Inputs: + * fmri is the instance to look up + * Outputs: + * res is a pointer to an scf_resources_t. This is an internal + * structure that holds all the handles needed to get a specific + * property from the running snapshot; on a successful return it + * contains the scf_value_t that should be passed to the desired + * scf_value_get_foo() function, and must be freed after use by + * calling release_scf_resources(). On a failure return, any + * resources that may have been assigned to res are released, so + * the caller does not need to do any cleanup in the failure case. + * Returns: + * 0 on success + * -1 on failure + */ + +static int +ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res) +{ + res->sr_tx = NULL; + res->sr_ent = NULL; + res->sr_inst = NULL; + res->sr_pg = NULL; + res->sr_prop = NULL; + res->sr_val = NULL; + + if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) { + return (-1); + } + + if (scf_handle_bind(res->sr_handle) != 0) { + scf_handle_destroy(res->sr_handle); + return (-1); + } + if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) { + goto failure; + } + if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL, + res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { + goto failure; + } + if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) { + goto failure; + } + if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) { + goto failure; + } + if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) { + goto failure; + } + if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) { + goto failure; + } + if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) { + goto failure; + } + return (0); + +failure: + ipmgmt_release_scf_resources(res); + return (-1); +} + +static int +ipmgmt_set_property_value(scf_resources_t *res, const char *propname, + scf_type_t proptype) +{ + int result = -1; + boolean_t new; + +retry: + new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0); + + if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) { + goto failure; + } + if (new) { + if (scf_transaction_property_new(res->sr_tx, res->sr_ent, + propname, proptype) == -1) { + goto failure; + } + } else { + if (scf_transaction_property_change(res->sr_tx, res->sr_ent, + propname, proptype) == -1) { + goto failure; + } + } + + if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) { + goto failure; + } + + result = scf_transaction_commit(res->sr_tx); + if (result == 0) { + scf_transaction_reset(res->sr_tx); + if (scf_pg_update(res->sr_pg) == -1) { + goto failure; + } + goto retry; + } + if (result == -1) + goto failure; + return (0); + +failure: + return (-1); +} + +/* + * Returns TRUE if this is the first boot, else return FALSE. The + * "ipmgmtd/first_boot_done" property is persistently set up on + * IPMGMTD_FMRI on the first boot. Note that the presence of + * "first_boot_done" itself is sufficient to indicate that this is + * not the first boot i.e., the value of the property is immaterial. + */ +extern boolean_t +ipmgmt_first_boot() +{ + scf_simple_prop_t *prop; + ssize_t numvals; + scf_resources_t res; + scf_error_t err; + + if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0) + return (B_TRUE); /* err on the side of caution */ + prop = scf_simple_prop_get(res.sr_handle, + IPMGMTD_FMRI, "ipmgmtd", "first_boot_done"); + numvals = scf_simple_prop_numvalues(prop); + if (numvals > 0) { + scf_simple_prop_free(prop); + ipmgmt_release_scf_resources(&res); + return (B_FALSE); + } + + /* + * mark the first boot by setting ipmgmtd/first_boot_done to true + */ + if (scf_instance_add_pg(res.sr_inst, "ipmgmtd", SCF_GROUP_APPLICATION, + 0, res.sr_pg) != 0) { + if ((err = scf_error()) != SCF_ERROR_EXISTS) + goto failure; + /* + * err == SCF_ERROR_EXISTS is by itself sufficient to declare + * that this is not the first boot, but we create a simple + * property as a place-holder, so that we don't leave an + * empty process group behind. + */ + if (scf_instance_get_pg_composed(res.sr_inst, NULL, "ipmgmtd", + res.sr_pg) != 0) { + err = scf_error(); + goto failure; + } + } + + if (scf_value_set_astring(res.sr_val, "true") != 0) { + err = scf_error(); + goto failure; + } + + if (ipmgmt_set_property_value(&res, "first_boot_done", + SCF_TYPE_ASTRING) != 0) { + ipmgmt_log(LOG_WARNING, + "Could not set rval of first_boot_done"); + } + +failure: + ipmgmt_log(LOG_WARNING, "ipmgmt_first_boot scf error %s", + scf_strerror(err)); + ipmgmt_release_scf_resources(&res); + return (B_TRUE); +} diff --git a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c index 65c9d83cfb..119293f472 100644 --- a/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c +++ b/usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c @@ -19,11 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1990 Mentat Inc. * netstat.c 2.2, last change 9/9/91 * MROUTING Revision 3.5 @@ -314,8 +310,9 @@ static m_label_t *zone_security_label = NULL; #define FLF_I 0x00000400 /* RTF_INDIRECT */ #define FLF_R 0x00000800 /* RTF_REJECT */ #define FLF_B 0x00001000 /* RTF_BLACKHOLE */ +#define FLF_Z 0x00100000 /* RTF_ZONE */ -static const char flag_list[] = "AbDGHLUMSCIRB"; +static const char flag_list[] = "AbDGHLUMSCIRBZ"; typedef struct filter_rule filter_t; @@ -4342,6 +4339,10 @@ form_v4_route_flags(const mib2_ipRouteEntry_t *rp, char *flags) (void) strcat(flags, "B"); flag_b |= FLF_B; } + if (rp->ipRouteInfo.re_flags & RTF_ZONE) { + (void) strcat(flags, "Z"); + flag_b |= FLF_Z; + } return (flag_b); } @@ -4589,6 +4590,10 @@ form_v6_route_flags(const mib2_ipv6RouteEntry_t *rp6, char *flags) (void) strcat(flags, "B"); flag_b |= FLF_B; } + if (rp6->ipv6RouteInfo.re_flags & RTF_ZONE) { + (void) strcat(flags, "Z"); + flag_b |= FLF_Z; + } return (flag_b); } 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 b1409d8420..a2b477c06b 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c @@ -73,7 +73,8 @@ static if_flags_t if_flags_tbl[] = { { IFF_DUPLICATE, "DUPLICATE" }, { IFF_IPMP, "IPMP"}, { IFF_VRRP, "VRRP"}, - { IFF_NOACCEPT, "NOACCEPT"} + { IFF_NOACCEPT, "NOACCEPT"}, + { IFF_L3PROTECT, "L3PROTECT"} }; typedef struct { diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c index bae4b9915e..a3b2fcd19e 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c @@ -308,7 +308,7 @@ 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}, +{ "CURRENT", 13, SI_CURRENT, print_si_cb}, { "PERSISTENT", 11, SI_PERSISTENT, print_si_cb}, { NULL, 0, 0, NULL} }; @@ -1604,6 +1604,43 @@ flags2str(uint64_t flags, fmask_t *tbl, boolean_t is_bits, } } +/* + * return true if the address for lifname comes to us from the global zone + * with 'allowed-ips' constraints. + */ +static boolean_t +is_from_gz(const char *lifname) +{ + ipadm_if_info_t *if_info; + char phyname[LIFNAMSIZ], *cp; + boolean_t ret = _B_FALSE; + ipadm_status_t status; + zoneid_t zoneid; + ushort_t zflags; + + if ((zoneid = getzoneid()) == GLOBAL_ZONEID) + return (_B_FALSE); /* from-gz only makes sense in a NGZ */ + + if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags, sizeof (zflags)) < 0) + return (_B_FALSE); + + if (!(zflags & ZF_NET_EXCL)) + return (_B_TRUE); /* everything is from the GZ for shared-ip */ + + (void) strncpy(phyname, lifname, sizeof (phyname)); + if ((cp = strchr(phyname, ':')) != NULL) + *cp = '\0'; + status = ipadm_if_info(iph, phyname, &if_info, 0, LIFC_DEFAULT); + if (status != IPADM_SUCCESS) + return (ret); + + if (if_info->ifi_cflags & IFIF_L3PROTECT) + ret = _B_TRUE; + if (if_info) + ipadm_free_if_info(if_info); + return (ret); +} + static boolean_t print_sa_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { @@ -1668,7 +1705,11 @@ print_sa_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) buf, bufsize); break; case SA_TYPE: - flags2str(ainfo->ia_atype, type, _B_FALSE, buf, bufsize); + if (is_from_gz(ifa->ifa_name)) + (void) snprintf(buf, bufsize, "from-gz"); + else + flags2str(ainfo->ia_atype, type, _B_FALSE, buf, + bufsize); break; case SA_CURRENT: flags2str(ainfo->ia_cflags, cflags_mask, _B_TRUE, buf, bufsize); @@ -1877,6 +1918,7 @@ print_si_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { "i", IFIF_INACTIVE, IFIF_INACTIVE }, { "V", IFIF_VRRP, IFIF_VRRP }, { "a", IFIF_NOACCEPT, IFIF_NOACCEPT }, + { "Z", IFIF_L3PROTECT, IFIF_L3PROTECT }, { "4", IFIF_IPV4, IFIF_IPV4 }, { "6", IFIF_IPV6, IFIF_IPV6 }, { NULL, 0, 0 } diff --git a/usr/src/cmd/cmd-inet/usr.sbin/route.c b/usr/src/cmd/cmd-inet/usr.sbin/route.c index e078d9df38..a2fd0d2d17 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/route.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/route.c @@ -1,6 +1,5 @@ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -2567,7 +2566,8 @@ static char metricnames[] = static char routeflags[] = "\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT" "\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE" - "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT"; + "\016PRIVATE\017PROTO2\020PROTO1\021MULTIRT\022SETSRC\023INDIRECT" + "\024KERNEL\025ZONE"; static char ifnetflags[] = "\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6NOTRAILERS\7RUNNING\010NOARP" "\011PPROMISC\012ALLMULTI\013INTELLIGENT\014MULTICAST" diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical index b7a8144358..3a873db121 100644 --- a/usr/src/cmd/svc/milestone/net-physical +++ b/usr/src/cmd/svc/milestone/net-physical @@ -305,21 +305,35 @@ if [ -n "$ipmp6_list" ]; then fi # -# Finally configure interfaces set up with ipadm. +# Finally configure interfaces set up with ipadm. Any /etc/hostname*.intf +# files take precedence over ipadm defined configurations except when +# we are in a non-global zone and Layer-3 protection of IP addresses is +# enforced on the interface by the global zone. # -for showif_output in `/sbin/ipadm show-if -p -o ifname,state`; do +for showif_output in `/sbin/ipadm show-if -p -o ifname,state,current`; 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 + current=`echo $showif_output | /usr/bin/cut -f3 -d:` + if [[ "$state" != "disabled" && $current != *Z* ]]; then + # + # skip if not a persistent interface, or if it should get IP + # configuration from the global zone ('Z' flag is set) + # 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; + if [[ $current != *Z* ]]; then + echo "found /etc/hostname.$intf "\ + "or /etc/hostname6.$intf, "\ + "ignoring ipadm configuration" > /dev/msglog + continue; + else + echo "Ignoring /etc/hostname*.$intf" > /dev/msglog + /sbin/ifconfig $intf unplumb > /dev/null 2>&1 + /sbin/ifconfig $intf inet6 unplumb > /dev/null 2>&1 + fi fi # Enable the interface managed by ipadm diff --git a/usr/src/cmd/svc/shell/net_include.sh b/usr/src/cmd/svc/shell/net_include.sh index cbc5b051b5..ce5972ccee 100644 --- a/usr/src/cmd/svc/shell/net_include.sh +++ b/usr/src/cmd/svc/shell/net_include.sh @@ -20,8 +20,7 @@ # CDDL HEADER END # # -# Copyright 2010 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. # All rights reserved. @@ -547,6 +546,38 @@ move_addresses() } # +# ipadm_from_gz_if ifname +# +# Return true if we are in a non-global zone and Layer-3 protection of +# IP addresses is being enforced on the interface by the global zone +# +ipadm_from_gz_if() +{ + pif=`/sbin/ipadm show-if -o persistent -p $1 2>/dev/null | egrep '4|6'` + if smf_is_globalzone || ![[ $pif == *4* || $pif == *6* ]]; then + return 1 + else + # + # In the non-global zone, plumb the interface to show current + # flags and check if Layer-3 protection has been enforced by + # the global zone. Note that this function may return + # with a plumbed interface. Ideally, we would not have to + # plumb the interface to check l3protect, but since we + # the `allowed-ips' datalink property cannot currently be + # examined in any other way from the non-global zone, we + # resort to plumbing the interface + # + /sbin/ifconfig $1 plumb > /dev/null 2>&1 + l3protect=`/sbin/ipadm show-if -o current -p $1|grep -c 'Z'` + if [ $l3protect = 0 ]; then + return 1 + else + return 0 + fi + fi +} + +# # if_configure type class interface_list # # Configure all of the interfaces of type `type' (e.g., "inet6") in @@ -575,7 +606,10 @@ if_configure() while [ $# -gt 0 ]; do $process_func /sbin/ifconfig $1 $type < $hostpfx.$1 >/dev/null if [ $? != 0 ]; then - fail="$fail $1" + ipadm_from_gz_if $1 + if [ $? != 0 ]; then + fail="$fail $1" + fi elif [ "$type" = inet6 ]; then # # only bring the interface up if it is not a diff --git a/usr/src/cmd/zoneadmd/Makefile b/usr/src/cmd/zoneadmd/Makefile index 2ca1d2f01e..cb03ef459a 100644 --- a/usr/src/cmd/zoneadmd/Makefile +++ b/usr/src/cmd/zoneadmd/Makefile @@ -22,10 +22,7 @@ # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # PROG= zoneadmd @@ -41,7 +38,8 @@ POFILES= $(OBJS:%.o=%.po) CFLAGS += $(CCVERBOSE) LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ - -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol + -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ + -linetutil XGETFLAGS += -a -x zoneadmd.xcl .KEEP_STATE: diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 998fc6a750..b4cc20951f 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -119,6 +119,7 @@ #include <tsol/label.h> #include <libtsnet.h> #include <sys/priv.h> +#include <libinetutil.h> #define V4_ADDR_LEN 32 #define V6_ADDR_LEN 128 @@ -159,11 +160,29 @@ extern int getnetmaskbyaddr(struct in_addr, struct in_addr *); extern char query_hook[]; /* + * For each "net" resource configured in zonecfg, we track a zone_addr_list_t + * node in a linked list that is sorted by linkid. The list is constructed as + * the xml configuration file is parsed, and the information + * contained in each node is added to the kernel before the zone is + * booted, to be retrieved and applied from within the exclusive-IP NGZ + * on boot. + */ +typedef struct zone_addr_list { + struct zone_addr_list *za_next; + datalink_id_t za_linkid; /* datalink_id_t of interface */ + struct zone_nwiftab za_nwiftab; /* address, defrouter properties */ +} zone_addr_list_t; + +/* * An optimization for build_mnttable: reallocate (and potentially copy the * data) only once every N times through the loop. */ #define MNTTAB_HUNK 32 +/* some handy macros */ +#define SIN(s) ((struct sockaddr_in *)s) +#define SIN6(s) ((struct sockaddr_in6 *)s) + /* * Private autofs system call */ @@ -2502,13 +2521,369 @@ add_datalink(zlog_t *zlogp, char *zone_name, datalink_id_t linkid, char *dlname) return (0); } +static boolean_t +sockaddr_to_str(sa_family_t af, const struct sockaddr *sockaddr, + char *straddr, size_t len) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + const char *str = NULL; + + if (af == AF_INET) { + /* LINTED E_BAD_PTR_CAST_ALIGN */ + sin = SIN(sockaddr); + str = inet_ntop(AF_INET, (void *)&sin->sin_addr, straddr, len); + } else if (af == AF_INET6) { + /* LINTED E_BAD_PTR_CAST_ALIGN */ + sin6 = SIN6(sockaddr); + str = inet_ntop(AF_INET6, (void *)&sin6->sin6_addr, straddr, + len); + } + + return (str != NULL); +} + +static int +ipv4_prefixlen(struct sockaddr_in *sin) +{ + struct sockaddr_in *m; + struct sockaddr_storage mask; + + m = SIN(&mask); + m->sin_family = AF_INET; + if (getnetmaskbyaddr(sin->sin_addr, &m->sin_addr) == 0) { + return (mask2plen(&mask)); + } else if (IN_CLASSA(htonl(sin->sin_addr.s_addr))) { + return (8); + } else if (IN_CLASSB(ntohl(sin->sin_addr.s_addr))) { + return (16); + } else if (IN_CLASSC(ntohl(sin->sin_addr.s_addr))) { + return (24); + } + return (0); +} + +static int +zone_setattr_network(int type, zoneid_t zoneid, datalink_id_t linkid, + void *buf, size_t bufsize) +{ + zone_net_data_t *zndata; + size_t znsize; + int err; + + znsize = sizeof (*zndata) + bufsize; + zndata = calloc(1, znsize); + if (zndata == NULL) + return (ENOMEM); + zndata->zn_type = type; + zndata->zn_len = bufsize; + zndata->zn_linkid = linkid; + bcopy(buf, zndata->zn_val, zndata->zn_len); + err = zone_setattr(zoneid, ZONE_ATTR_NETWORK, zndata, znsize); + free(zndata); + return (err); +} + +static int +add_net_for_linkid(zlog_t *zlogp, zoneid_t zoneid, zone_addr_list_t *start) +{ + struct lifreq lifr; + char **astr, *address; + dladm_status_t dlstatus; + char *ip_nospoof = "ip-nospoof"; + int nnet, naddr, err = 0, j; + size_t zlen, cpleft; + zone_addr_list_t *ptr, *end; + char tmp[INET6_ADDRSTRLEN], *maskstr; + char *zaddr, *cp; + struct in6_addr *routes = NULL; + boolean_t is_set; + datalink_id_t linkid; + + assert(start != NULL); + naddr = 0; /* number of addresses */ + nnet = 0; /* number of net resources */ + linkid = start->za_linkid; + for (ptr = start; ptr != NULL && ptr->za_linkid == linkid; + ptr = ptr->za_next) { + nnet++; + } + end = ptr; + zlen = nnet * (INET6_ADDRSTRLEN + 1); + astr = calloc(1, nnet * sizeof (uintptr_t)); + zaddr = calloc(1, zlen); + if (astr == NULL || zaddr == NULL) { + err = ENOMEM; + goto done; + } + cp = zaddr; + cpleft = zlen; + j = 0; + for (ptr = start; ptr != end; ptr = ptr->za_next) { + address = ptr->za_nwiftab.zone_nwif_allowed_address; + if (address[0] == '\0') + continue; + (void) snprintf(tmp, sizeof (tmp), "%s", address); + /* + * Validate the data. zonecfg_valid_net_address() clobbers + * the /<mask> in the address string. + */ + if (zonecfg_valid_net_address(address, &lifr) != Z_OK) { + zerror(zlogp, B_FALSE, "invalid address [%s]\n", + address); + err = EINVAL; + goto done; + } + /* + * convert any hostnames to numeric address strings. + */ + if (!sockaddr_to_str(lifr.lifr_addr.ss_family, + (const struct sockaddr *)&lifr.lifr_addr, cp, cpleft)) { + err = EINVAL; + goto done; + } + /* + * make a copy of the numeric string for the data needed + * by the "allowed-ips" datalink property. + */ + astr[j] = strdup(cp); + if (astr[j] == NULL) { + err = ENOMEM; + goto done; + } + j++; + /* + * compute the default netmask from the address, if necessary + */ + if ((maskstr = strchr(tmp, '/')) == NULL) { + int prefixlen; + + if (lifr.lifr_addr.ss_family == AF_INET) { + prefixlen = ipv4_prefixlen( + SIN(&lifr.lifr_addr)); + } else { + struct sockaddr_in6 *sin6; + + sin6 = SIN6(&lifr.lifr_addr); + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + prefixlen = 10; + else + prefixlen = 64; + } + (void) snprintf(tmp, sizeof (tmp), "%d", prefixlen); + maskstr = tmp; + } else { + maskstr++; + } + /* append the "/<netmask>" */ + (void) strlcat(cp, "/", cpleft); + (void) strlcat(cp, maskstr, cpleft); + (void) strlcat(cp, ",", cpleft); + cp += strnlen(cp, zlen); + cpleft = &zaddr[INET6_ADDRSTRLEN] - cp; + } + naddr = j; /* the actual number of addresses in the net resource */ + assert(naddr <= nnet); + + /* + * zonecfg has already verified that the defrouter property can only + * be set if there is at least one address defined for the net resource. + * If j is 0, there are no addresses defined, and therefore no routers + * to configure, and we are done at that point. + */ + if (j == 0) + goto done; + + /* over-write last ',' with '\0' */ + zaddr[strnlen(zaddr, zlen) + 1] = '\0'; + + /* + * First make sure L3 protection is not already set on the link. + */ + dlstatus = dladm_linkprop_is_set(dld_handle, linkid, DLADM_OPT_ACTIVE, + "protection", &is_set); + if (dlstatus != DLADM_STATUS_OK) { + err = EINVAL; + zerror(zlogp, B_FALSE, "unable to check if protection is set"); + goto done; + } + if (is_set) { + err = EINVAL; + zerror(zlogp, B_FALSE, "Protection is already set"); + goto done; + } + dlstatus = dladm_linkprop_is_set(dld_handle, linkid, DLADM_OPT_ACTIVE, + "allowed-ips", &is_set); + if (dlstatus != DLADM_STATUS_OK) { + err = EINVAL; + zerror(zlogp, B_FALSE, "unable to check if allowed-ips is set"); + goto done; + } + if (is_set) { + zerror(zlogp, B_FALSE, "allowed-ips is already set"); + err = EINVAL; + goto done; + } + + /* + * Enable ip-nospoof for the link, and add address to the allowed-ips + * list. + */ + dlstatus = dladm_set_linkprop(dld_handle, linkid, "protection", + &ip_nospoof, 1, DLADM_OPT_ACTIVE); + if (dlstatus != DLADM_STATUS_OK) { + zerror(zlogp, B_FALSE, "could not set protection\n"); + err = EINVAL; + goto done; + } + dlstatus = dladm_set_linkprop(dld_handle, linkid, "allowed-ips", + astr, naddr, DLADM_OPT_ACTIVE); + if (dlstatus != DLADM_STATUS_OK) { + zerror(zlogp, B_FALSE, "could not set allowed-ips\n"); + err = EINVAL; + goto done; + } + + /* now set the address in the data-store */ + err = zone_setattr_network(ZONE_NETWORK_ADDRESS, zoneid, linkid, + zaddr, strnlen(zaddr, zlen) + 1); + if (err != 0) + goto done; + + /* + * add the defaultrouters + */ + routes = calloc(1, nnet * sizeof (*routes)); + j = 0; + for (ptr = start; ptr != end; ptr = ptr->za_next) { + address = ptr->za_nwiftab.zone_nwif_defrouter; + if (address[0] == '\0') + continue; + if (strchr(address, '/') == NULL && strchr(address, ':') != 0) { + /* + * zonecfg_valid_net_address() expects numeric IPv6 + * addresses to have a CIDR format netmask. + */ + (void) snprintf(tmp, sizeof (tmp), "/%d", V6_ADDR_LEN); + (void) strlcat(address, tmp, INET6_ADDRSTRLEN); + } + if (zonecfg_valid_net_address(address, &lifr) != Z_OK) { + zerror(zlogp, B_FALSE, + "invalid router [%s]\n", address); + err = EINVAL; + goto done; + } + if (lifr.lifr_addr.ss_family == AF_INET6) { + routes[j] = SIN6(&lifr.lifr_addr)->sin6_addr; + } else { + IN6_INADDR_TO_V4MAPPED(&SIN(&lifr.lifr_addr)->sin_addr, + &routes[j]); + } + j++; + } + assert(j <= nnet); + if (j > 0) { + err = zone_setattr_network(ZONE_NETWORK_DEFROUTER, zoneid, + linkid, routes, j * sizeof (*routes)); + } +done: + free(routes); + for (j = 0; j < naddr; j++) + free(astr[j]); + free(astr); + free(zaddr); + return (err); + +} + +static int +add_net(zlog_t *zlogp, zoneid_t zoneid, zone_addr_list_t *zalist) +{ + zone_addr_list_t *ptr; + datalink_id_t linkid; + int err; + + if (zalist == NULL) + return (0); + + linkid = zalist->za_linkid; + + err = add_net_for_linkid(zlogp, zoneid, zalist); + if (err != 0) + return (err); + + for (ptr = zalist; ptr != NULL; ptr = ptr->za_next) { + if (ptr->za_linkid == linkid) + continue; + linkid = ptr->za_linkid; + err = add_net_for_linkid(zlogp, zoneid, ptr); + if (err != 0) + return (err); + } + return (0); +} + +/* + * Add "new" to the list of network interfaces to be configured by + * add_net on zone boot in "old". The list of interfaces in "old" is + * sorted by datalink_id_t, with interfaces sorted FIFO for a given + * datalink_id_t. + * + * Returns the merged list of IP interfaces containing "old" and "new" + */ +static zone_addr_list_t * +add_ip_interface(zone_addr_list_t *old, zone_addr_list_t *new) +{ + zone_addr_list_t *ptr, *next; + datalink_id_t linkid = new->za_linkid; + + assert(old != new); + + if (old == NULL) + return (new); + for (ptr = old; ptr != NULL; ptr = ptr->za_next) { + if (ptr->za_linkid == linkid) + break; + } + if (ptr == NULL) { + /* linkid does not already exist, add to the beginning */ + new->za_next = old; + return (new); + } + /* + * adding to the middle of the list; ptr points at the first + * occurrence of linkid. Find the last occurrence. + */ + while ((next = ptr->za_next) != NULL) { + if (next->za_linkid != linkid) + break; + ptr = next; + } + /* insert new after ptr */ + new->za_next = next; + ptr->za_next = new; + return (old); +} + +void +free_ip_interface(zone_addr_list_t *zalist) +{ + zone_addr_list_t *ptr, *new; + + for (ptr = zalist; ptr != NULL; ) { + new = ptr; + ptr = ptr->za_next; + free(new); + } +} + /* * Add the kernel access control information for the interface names. * If anything goes wrong, we log a general error message, attempt to tear down * whatever we set up, and return an error. */ static int -configure_exclusive_network_interfaces(zlog_t *zlogp) +configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) { zone_dochandle_t handle; struct zone_nwiftab nwiftab; @@ -2517,6 +2892,7 @@ configure_exclusive_network_interfaces(zlog_t *zlogp) datalink_id_t linkid; di_prof_t prof = NULL; boolean_t added = B_FALSE; + zone_addr_list_t *zalist = NULL, *new; if ((handle = zonecfg_init_handle()) == NULL) { zerror(zlogp, B_TRUE, "getting zone configuration handle"); @@ -2579,6 +2955,28 @@ configure_exclusive_network_interfaces(zlog_t *zlogp) zerror(zlogp, B_TRUE, "failed to add network device"); return (-1); } + /* set up the new IP interface, and add them all later */ + new = malloc(sizeof (*new)); + if (new == NULL) { + zerror(zlogp, B_TRUE, "no memory for %s", + nwiftab.zone_nwif_physical); + zonecfg_fini_handle(handle); + free_ip_interface(zalist); + } + bzero(new, sizeof (*new)); + new->za_nwiftab = nwiftab; + new->za_linkid = linkid; + zalist = add_ip_interface(zalist, new); + } + if (zalist != NULL) { + if ((errno = add_net(zlogp, zoneid, zalist)) != 0) { + (void) zonecfg_endnwifent(handle); + zonecfg_fini_handle(handle); + zerror(zlogp, B_TRUE, "failed to add address"); + free_ip_interface(zalist); + return (-1); + } + free_ip_interface(zalist); } (void) zonecfg_endnwifent(handle); zonecfg_fini_handle(handle); @@ -2610,8 +3008,7 @@ remove_datalink_pool(zlog_t *zlogp, zoneid_t zoneid) if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags, sizeof (flags)) < 0) { if (vplat_get_iptype(zlogp, &iptype) < 0) { - zerror(zlogp, B_TRUE, "unable to determine " - "ip-type"); + zerror(zlogp, B_FALSE, "unable to determine ip-type"); return (-1); } } else { @@ -2662,6 +3059,74 @@ remove_datalink_pool(zlog_t *zlogp, zoneid_t zoneid) } static int +remove_datalink_protect(zlog_t *zlogp, zoneid_t zoneid) +{ + ushort_t flags; + zone_iptype_t iptype; + int i, dlnum = 0; + dladm_status_t dlstatus; + datalink_id_t *dllink, *dllinks = NULL; + + if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags, + sizeof (flags)) < 0) { + if (vplat_get_iptype(zlogp, &iptype) < 0) { + zerror(zlogp, B_FALSE, "unable to determine ip-type"); + return (-1); + } + } else { + if (flags & ZF_NET_EXCL) + iptype = ZS_EXCLUSIVE; + else + iptype = ZS_SHARED; + } + + if (iptype != ZS_EXCLUSIVE) + return (0); + + /* + * Get the datalink count and for each datalink, + * attempt to clear the pool property and clear + * the pool_name. + */ + if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) { + zerror(zlogp, B_TRUE, "unable to count network interfaces"); + return (-1); + } + + if (dlnum == 0) + return (0); + + if ((dllinks = malloc(dlnum * sizeof (datalink_id_t))) == NULL) { + zerror(zlogp, B_TRUE, "memory allocation failed"); + return (-1); + } + if (zone_list_datalink(zoneid, &dlnum, dllinks) != 0) { + zerror(zlogp, B_TRUE, "unable to list network interfaces"); + free(dllinks); + return (-1); + } + + for (i = 0, dllink = dllinks; i < dlnum; i++, dllink++) { + dlstatus = dladm_set_linkprop(dld_handle, *dllink, + "protection", NULL, 0, DLADM_OPT_ACTIVE); + if (dlstatus != DLADM_STATUS_OK) { + zerror(zlogp, B_TRUE, "could not reset protection\n"); + free(dllinks); + return (-1); + } + dlstatus = dladm_set_linkprop(dld_handle, *dllink, + "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE); + if (dlstatus != DLADM_STATUS_OK) { + zerror(zlogp, B_TRUE, "could not reset allowed-ips\n"); + free(dllinks); + return (-1); + } + } + free(dllinks); + return (0); +} + +static int unconfigure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) { int dlnum = 0; @@ -4542,7 +5007,8 @@ vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) } break; case ZS_EXCLUSIVE: - if (configure_exclusive_network_interfaces(zlogp) != + if (configure_exclusive_network_interfaces(zlogp, + zoneid) != 0) { lofs_discard_mnttab(); return (-1); @@ -4667,6 +5133,19 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) goto error; } + if (remove_datalink_protect(zlogp, zoneid) != 0) { + zerror(zlogp, B_FALSE, + "unable clear datalink protect property"); + goto error; + } + + /* + * The datalinks assigned to the zone will be removed from the NGZ as + * part of zone_shutdown() so that we need to remove protect/pool etc. + * before zone_shutdown(). Even if the shutdown itself fails, the zone + * will not be able to violate any constraints applied because the + * datalinks are no longer available to the zone. + */ if (zone_shutdown(zoneid) != 0) { zerror(zlogp, B_TRUE, "unable to shutdown zone"); goto error; diff --git a/usr/src/cmd/zonecfg/zonecfg.c b/usr/src/cmd/zonecfg/zonecfg.c index a9568128fa..5261e82342 100644 --- a/usr/src/cmd/zonecfg/zonecfg.c +++ b/usr/src/cmd/zonecfg/zonecfg.c @@ -76,6 +76,7 @@ #include <libdladm.h> #include <libinetutil.h> #include <pwd.h> +#include <inet/ip.h> #include <libzonecfg.h> #include "zonecfg.h" @@ -230,6 +231,7 @@ char *prop_types[] = { "auths", "fs-allowed", ALIAS_MAXPROCS, + "allowed-address", NULL }; @@ -500,8 +502,18 @@ static const char *admin_res_scope_cmds[] = { NULL }; +struct xif { + struct xif *xif_next; + char xif_name[LIFNAMSIZ]; + boolean_t xif_has_address; + boolean_t xif_has_defrouter; +}; + /* Global variables */ +/* list of network interfaces specified for exclusive IP zone */ +struct xif *xif; + /* set early in main(), never modified thereafter, used all over the place */ static char *execname; @@ -976,17 +988,30 @@ usage(boolean_t verbose, uint_t flags) (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), gettext("<IP-address>")); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_ALLOWED_ADDRESS), + gettext("<IP-address>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_PHYSICAL), gettext("<interface>")); (void) fprintf(fp, gettext("See ifconfig(1M) for " "details of the <interface> string.\n")); - (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), - pt_to_str(PT_DEFROUTER), gettext("<IP-address>")); - (void) fprintf(fp, gettext("%s %s and %s %s are valid " - "if the %s property is set to %s, otherwise they " + (void) fprintf(fp, gettext("%s %s is valid " + "if the %s property is set to %s, otherwise it " "must not be set.\n"), cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), + pt_to_str(PT_IPTYPE), gettext("shared")); + (void) fprintf(fp, gettext("%s %s is valid " + "if the %s property is set to %s, otherwise it " + "must not be set.\n"), + cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS), + pt_to_str(PT_IPTYPE), gettext("exclusive")); + (void) fprintf(fp, gettext("\t%s %s=%s\n%s %s " + "is valid if the %s or %s property is set, " + "otherwise it must not be set\n"), + cmd_to_str(CMD_SET), + pt_to_str(PT_DEFROUTER), gettext("<IP-address>"), cmd_to_str(CMD_SET), pt_to_str(PT_DEFROUTER), - pt_to_str(PT_IPTYPE), "shared"); + gettext(pt_to_str(PT_ADDRESS)), + gettext(pt_to_str(PT_ALLOWED_ADDRESS))); break; case RT_DEVICE: (void) fprintf(fp, gettext("The '%s' resource scope is " @@ -1220,9 +1245,9 @@ usage(boolean_t verbose, uint_t flags) rt_to_str(RT_FS), pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW), pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS)); - (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_NET), - pt_to_str(PT_ADDRESS), pt_to_str(PT_PHYSICAL), - pt_to_str(PT_DEFROUTER)); + (void) fprintf(fp, "\t%s\t\t%s, %s, %s|%s\n", rt_to_str(RT_NET), + pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS), + pt_to_str(PT_PHYSICAL), pt_to_str(PT_DEFROUTER)); (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE), pt_to_str(PT_MATCH)); (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL), @@ -1849,6 +1874,8 @@ export_func(cmd_t *cmd) (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), rt_to_str(RT_NET)); export_prop(of, PT_ADDRESS, nwiftab.zone_nwif_address); + export_prop(of, PT_ALLOWED_ADDRESS, + nwiftab.zone_nwif_allowed_address); export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical); export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter); (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); @@ -2601,6 +2628,11 @@ fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, (void) strlcpy(nwiftab->zone_nwif_address, pp->pv_simple, sizeof (nwiftab->zone_nwif_address)); break; + case PT_ALLOWED_ADDRESS: + (void) strlcpy(nwiftab->zone_nwif_allowed_address, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_allowed_address)); + break; case PT_PHYSICAL: (void) strlcpy(nwiftab->zone_nwif_physical, pp->pv_simple, @@ -3758,10 +3790,14 @@ select_func(cmd_t *cmd) * IPv4 addresses and host names, 0 to 128 for IPv6 addresses. * Host names must start with an alpha-numeric character, and all subsequent * characters must be either alpha-numeric or "-". + * + * In some cases, e.g., the nexthop for the defrouter, the context indicates + * that this is the IPV4_ABITS or IPV6_ABITS netmask, in which case we don't + * require the /<prefix length> (and should ignore it if provided). */ static int -validate_net_address_syntax(char *address) +validate_net_address_syntax(char *address, boolean_t ishost) { char *slashp, part1[MAXHOSTNAMELEN]; struct in6_addr in6; @@ -3781,8 +3817,17 @@ validate_net_address_syntax(char *address) (void) strlcpy(part1, address, sizeof (part1)); } + if (ishost && slashp != NULL) { + zerr(gettext("Warning: prefix length in %s is not required and " + "will be ignored. The default host-prefix length " + "will be used"), address); + } + + if (inet_pton(AF_INET6, part1, &in6) == 1) { - if (slashp == NULL) { + if (ishost) { + prefixlen = IPV6_ABITS; + } else if (slashp == NULL) { zerr(gettext("%s: IPv6 addresses " "require /prefix-length suffix."), address); return (Z_ERR); @@ -3796,13 +3841,16 @@ validate_net_address_syntax(char *address) } /* At this point, any /prefix must be for IPv4. */ - if (slashp != NULL) { + if (ishost) + prefixlen = IPV4_ABITS; + else if (slashp != NULL) { if (prefixlen < 0 || prefixlen > 32) { zerr(gettext("%s: IPv4 address " "prefix lengths must be 0 - 32."), address); return (Z_ERR); } } + if (inet_pton(AF_INET, part1, &in4) == 1) return (Z_OK); @@ -3953,6 +4001,20 @@ set_aliased_rctl(char *alias, int prop_type, char *s) } } +static void +set_in_progress_nwiftab_address(char *prop_id, int prop_type) +{ + if (prop_type == PT_ADDRESS) { + (void) strlcpy(in_progress_nwiftab.zone_nwif_address, prop_id, + sizeof (in_progress_nwiftab.zone_nwif_address)); + } else { + assert(prop_type == PT_ALLOWED_ADDRESS); + (void) strlcpy(in_progress_nwiftab.zone_nwif_allowed_address, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_allowed_address)); + } +} + void set_func(cmd_t *cmd) { @@ -4312,13 +4374,13 @@ set_func(cmd_t *cmd) case RT_NET: switch (prop_type) { case PT_ADDRESS: - if (validate_net_address_syntax(prop_id) != Z_OK) { + case PT_ALLOWED_ADDRESS: + if (validate_net_address_syntax(prop_id, B_FALSE) + != Z_OK) { saw_error = B_TRUE; return; } - (void) strlcpy(in_progress_nwiftab.zone_nwif_address, - prop_id, - sizeof (in_progress_nwiftab.zone_nwif_address)); + set_in_progress_nwiftab_address(prop_id, prop_type); break; case PT_PHYSICAL: if (validate_net_physical_syntax(prop_id) != Z_OK) { @@ -4330,7 +4392,8 @@ set_func(cmd_t *cmd) sizeof (in_progress_nwiftab.zone_nwif_physical)); break; case PT_DEFROUTER: - if (validate_net_address_syntax(prop_id) != Z_OK) { + if (validate_net_address_syntax(prop_id, B_TRUE) + != Z_OK) { saw_error = B_TRUE; return; } @@ -4879,6 +4942,8 @@ output_net(FILE *fp, struct zone_nwiftab *nwiftab) { (void) fprintf(fp, "%s:\n", rt_to_str(RT_NET)); output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE); + output_prop(fp, PT_ALLOWED_ADDRESS, + nwiftab->zone_nwif_allowed_address, B_TRUE); output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE); output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE); } @@ -5597,6 +5662,41 @@ brand_verify(zone_dochandle_t handle) } /* + * Track the network interfaces listed in zonecfg(1m) in a linked list + * so that we can later check that defrouter is specified for an exclusive IP + * zone if and only if at least one allowed-address has been specified. + */ +static boolean_t +add_nwif(struct zone_nwiftab *nwif) +{ + struct xif *tmp; + + for (tmp = xif; tmp != NULL; tmp = tmp->xif_next) { + if (strcmp(tmp->xif_name, nwif->zone_nwif_physical) == 0) { + if (strlen(nwif->zone_nwif_allowed_address) > 0) + tmp->xif_has_address = B_TRUE; + if (strlen(nwif->zone_nwif_defrouter) > 0) + tmp->xif_has_defrouter = B_TRUE; + return (B_TRUE); + } + } + + tmp = malloc(sizeof (*tmp)); + if (tmp == NULL) { + zerr(gettext("memory allocation failed for %s"), + nwif->zone_nwif_physical); + return (B_FALSE); + } + strlcpy(tmp->xif_name, nwif->zone_nwif_physical, + sizeof (tmp->xif_name)); + tmp->xif_has_defrouter = (strlen(nwif->zone_nwif_defrouter) > 0); + tmp->xif_has_address = (strlen(nwif->zone_nwif_allowed_address) > 0); + tmp->xif_next = xif; + xif = tmp; + return (B_TRUE); +} + +/* * See the DTD for which attributes are required for which resources. * * This function can be called by commit_func(), which needs to save things, @@ -5627,6 +5727,7 @@ verify_func(cmd_t *cmd) zone_iptype_t iptype; boolean_t has_cpu_shares = B_FALSE; boolean_t has_cpu_cap = B_FALSE; + struct xif *tmp; optind = 0; while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?")) != EOF) { @@ -5740,27 +5841,46 @@ verify_func(cmd_t *cmd) case ZS_SHARED: check_reqd_prop(nwiftab.zone_nwif_address, RT_NET, PT_ADDRESS, &ret_val); - break; - case ZS_EXCLUSIVE: - if (strlen(nwiftab.zone_nwif_address) > 0) { + if (strlen(nwiftab.zone_nwif_allowed_address) > 0) { zerr(gettext("%s: %s cannot be specified " - "for an exclusive IP type"), - rt_to_str(RT_NET), pt_to_str(PT_ADDRESS)); + "for a shared IP type"), + rt_to_str(RT_NET), + pt_to_str(PT_ALLOWED_ADDRESS)); saw_error = B_TRUE; if (ret_val == Z_OK) ret_val = Z_INVAL; } - if (strlen(nwiftab.zone_nwif_defrouter) > 0) { + break; + case ZS_EXCLUSIVE: + if (strlen(nwiftab.zone_nwif_address) > 0) { zerr(gettext("%s: %s cannot be specified " "for an exclusive IP type"), - rt_to_str(RT_NET), pt_to_str(PT_DEFROUTER)); + rt_to_str(RT_NET), pt_to_str(PT_ADDRESS)); saw_error = B_TRUE; if (ret_val == Z_OK) ret_val = Z_INVAL; + } else { + if (!add_nwif(&nwiftab)) { + saw_error = B_TRUE; + if (ret_val == Z_OK) + ret_val = Z_INVAL; + } } break; } } + for (tmp = xif; tmp != NULL; tmp = tmp->xif_next) { + if (!tmp->xif_has_address && tmp->xif_has_defrouter) { + zerr(gettext("%s: %s for %s cannot be specified " + "without %s for an exclusive IP type"), + rt_to_str(RT_NET), pt_to_str(PT_DEFROUTER), + tmp->xif_name, pt_to_str(PT_ALLOWED_ADDRESS)); + saw_error = B_TRUE; + ret_val = Z_INVAL; + } + } + free(xif); + xif = NULL; (void) zonecfg_endnwifent(handle); if ((err = zonecfg_setrctlent(handle)) != Z_OK) { @@ -6023,6 +6143,28 @@ end_check_reqd(char *attr, int pt, boolean_t *validation_failed) return (Z_OK); } +static void +net_exists_error(struct zone_nwiftab nwif) +{ + if (strlen(nwif.zone_nwif_address) > 0) { + zerr(gettext("A %s resource with the %s '%s', " + "and %s '%s' already exists."), + rt_to_str(RT_NET), + pt_to_str(PT_PHYSICAL), + nwif.zone_nwif_physical, + pt_to_str(PT_ADDRESS), + in_progress_nwiftab.zone_nwif_address); + } else { + zerr(gettext("A %s resource with the %s '%s', " + "and %s '%s' already exists."), + rt_to_str(RT_NET), + pt_to_str(PT_PHYSICAL), + nwif.zone_nwif_physical, + pt_to_str(PT_ALLOWED_ADDRESS), + nwif.zone_nwif_allowed_address); + } +} + void end_func(cmd_t *cmd) { @@ -6151,14 +6293,14 @@ end_func(cmd_t *cmd) (void) strlcpy(tmp_nwiftab.zone_nwif_address, in_progress_nwiftab.zone_nwif_address, sizeof (tmp_nwiftab.zone_nwif_address)); + (void) strlcpy(tmp_nwiftab.zone_nwif_allowed_address, + in_progress_nwiftab.zone_nwif_allowed_address, + sizeof (tmp_nwiftab.zone_nwif_allowed_address)); + (void) strlcpy(tmp_nwiftab.zone_nwif_defrouter, + in_progress_nwiftab.zone_nwif_defrouter, + sizeof (tmp_nwiftab.zone_nwif_defrouter)); if (zonecfg_lookup_nwif(handle, &tmp_nwiftab) == Z_OK) { - zerr(gettext("A %s resource with the %s '%s', " - "and %s '%s' already exists."), - rt_to_str(RT_NET), - pt_to_str(PT_PHYSICAL), - in_progress_nwiftab.zone_nwif_physical, - pt_to_str(PT_ADDRESS), - in_progress_nwiftab.zone_nwif_address); + net_exists_error(in_progress_nwiftab); saw_error = B_TRUE; return; } diff --git a/usr/src/cmd/zonecfg/zonecfg.h b/usr/src/cmd/zonecfg/zonecfg.h index 1ac8acf9de..7bfecad72f 100644 --- a/usr/src/cmd/zonecfg/zonecfg.h +++ b/usr/src/cmd/zonecfg/zonecfg.h @@ -136,9 +136,10 @@ extern "C" { #define PT_AUTHS 38 #define PT_FS_ALLOWED 39 #define PT_MAXPROCS 40 +#define PT_ALLOWED_ADDRESS 41 #define PT_MIN PT_UNKNOWN -#define PT_MAX PT_MAXPROCS +#define PT_MAX PT_ALLOWED_ADDRESS #define MAX_EQ_PROP_PAIRS 3 diff --git a/usr/src/cmd/zonecfg/zonecfg_grammar.y b/usr/src/cmd/zonecfg/zonecfg_grammar.y index 3143080a1e..86dde3c99d 100644 --- a/usr/src/cmd/zonecfg/zonecfg_grammar.y +++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y @@ -123,7 +123,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %token HELP CREATE EXPORT ADD DELETE REMOVE SELECT SET INFO CANCEL END VERIFY %token COMMIT REVERT EXIT SEMICOLON TOKEN ZONENAME ZONEPATH AUTOBOOT POOL NET %token FS ATTR DEVICE RCTL SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL -%token IPTYPE HOSTID FS_ALLOWED +%token IPTYPE HOSTID FS_ALLOWED ALLOWED_ADDRESS %token NAME MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS @@ -137,6 +137,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV BOOTARGS VALUE PRIV LIMIT ACTION BRAND SCHED IPTYPE DEFROUTER HOSTID USER AUTHS FS_ALLOWED + ALLOWED_ADDRESS %type <cmd> command %type <cmd> add_command ADD %type <cmd> cancel_command CANCEL @@ -956,6 +957,7 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | LIMITPRIV { $$ = PT_LIMITPRIV; } | BOOTARGS { $$ = PT_BOOTARGS; } | ADDRESS { $$ = PT_ADDRESS; } + | ALLOWED_ADDRESS { $$ = PT_ALLOWED_ADDRESS; } | PHYSICAL { $$ = PT_PHYSICAL; } | DEFROUTER { $$ = PT_DEFROUTER; } | NAME { $$ = PT_NAME; } diff --git a/usr/src/cmd/zonecfg/zonecfg_lex.l b/usr/src/cmd/zonecfg/zonecfg_lex.l index 8285a778f7..8353603148 100644 --- a/usr/src/cmd/zonecfg/zonecfg_lex.l +++ b/usr/src/cmd/zonecfg/zonecfg_lex.l @@ -204,6 +204,9 @@ char *safe_strdup(char *s); <TSTATE>options { return OPTIONS; } <CSTATE>options { return OPTIONS; } +<TSTATE>allowed-address { return ALLOWED_ADDRESS; } +<CSTATE>allowed-address { return ALLOWED_ADDRESS; } + <TSTATE>address { return ADDRESS; } <CSTATE>address { return ADDRESS; } diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h index 1a0dd6cc17..ad8d6d2298 100644 --- a/usr/src/head/libzonecfg.h +++ b/usr/src/head/libzonecfg.h @@ -192,7 +192,8 @@ struct zone_fstab { }; struct zone_nwiftab { - char zone_nwif_address[INET6_ADDRSTRLEN]; + char zone_nwif_address[INET6_ADDRSTRLEN]; /* shared-ip only */ + char zone_nwif_allowed_address[INET6_ADDRSTRLEN]; /* excl-ip only */ char zone_nwif_physical[LIFNAMSIZ]; char zone_nwif_defrouter[INET6_ADDRSTRLEN]; }; diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c index e37ab46f11..b9f38012ed 100644 --- a/usr/src/lib/libdladm/common/linkprop.c +++ b/usr/src/lib/libdladm/common/linkprop.c @@ -4503,11 +4503,13 @@ dladm_linkprop_is_set(dladm_handle_t handle, datalink_id_t linkid, uint_t valcnt = DLADM_MAX_PROP_VALCNT; int i; dladm_status_t status = DLADM_STATUS_OK; + size_t bufsize; *is_set = B_FALSE; - if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * - DLADM_MAX_PROP_VALCNT)) == NULL) + bufsize = (sizeof (char *) + DLADM_PROP_VAL_MAX) * + DLADM_MAX_PROP_VALCNT; + if ((buf = calloc(1, bufsize)) == NULL) return (DLADM_STATUS_NOMEM); propvals = (char **)(void *)buf; @@ -4522,7 +4524,15 @@ dladm_linkprop_is_set(dladm_handle_t handle, datalink_id_t linkid, goto done; } - if ((strcmp(prop_name, "pool") == 0) && (strlen(*propvals) != 0)) { + /* + * valcnt is always set to 1 by get_pool(), hence we need to check + * for a non-null string to see if it is set. For protection and + * allowed-ips, we can check either the *propval or the valcnt. + */ + if ((strcmp(prop_name, "pool") == 0 || + strcmp(prop_name, "protection") == 0 || + strcmp(prop_name, "allowed-ips") == 0) && + (strlen(*propvals) != 0)) { *is_set = B_TRUE; } else if ((strcmp(prop_name, "cpus") == 0) && (valcnt != 0)) { *is_set = B_TRUE; diff --git a/usr/src/lib/libipadm/Makefile.com b/usr/src/lib/libipadm/Makefile.com index 60f677736e..3b6827a7fb 100644 --- a/usr/src/lib/libipadm/Makefile.com +++ b/usr/src/lib/libipadm/Makefile.com @@ -19,15 +19,14 @@ # CDDL HEADER END # # -# Copyright 2010 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # # LIBRARY = libipadm.a VERS = .1 OBJECTS = libipadm.o ipadm_prop.o ipadm_persist.o ipadm_addr.o ipadm_if.o \ - ipadm_ndpd.o + ipadm_ndpd.o ipadm_ngz.o include ../../Makefile.lib diff --git a/usr/src/lib/libipadm/common/ipadm_addr.c b/usr/src/lib/libipadm/common/ipadm_addr.c index b647abf0b1..b30fe7c9cd 100644 --- a/usr/src/lib/libipadm/common/ipadm_addr.c +++ b/usr/src/lib/libipadm/common/ipadm_addr.c @@ -1312,7 +1312,7 @@ i_ipadm_get_zone(ipadm_handle_t iph, const void *arg, int s; size_t nbytes = 0; - if (getzoneid() != GLOBAL_ZONEID) { + if (iph->iph_zoneid != GLOBAL_ZONEID) { buf[0] = '\0'; return (IPADM_SUCCESS); } @@ -1660,7 +1660,7 @@ i_ipadm_get_default_prefixlen(struct sockaddr_storage *addr, uint32_t *plen) return (IPADM_SUCCESS); } -static ipadm_status_t +ipadm_status_t i_ipadm_resolve_addr(const char *name, sa_family_t af, struct sockaddr_storage *ss) { @@ -1935,6 +1935,9 @@ i_ipadm_setlifnum_addrobj(ipadm_handle_t iph, ipadm_addrobj_t ipaddr) ipmgmt_retval_t rval, *rvalp; int err; + if (iph->iph_flags & IPH_IPMGMTD) + return (IPADM_SUCCESS); + bzero(&larg, sizeof (larg)); larg.ia_cmd = IPMGMT_CMD_ADDROBJ_SETLIFNUM; (void) strlcpy(larg.ia_aobjname, ipaddr->ipadm_aobjname, @@ -2266,6 +2269,8 @@ i_ipadm_addrobj2lifname(ipadm_addrobj_t ipaddr, char *lifname, int lifnamesize) * 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. + * + * Note that *exists will not be initialized if an error is encountered. */ static ipadm_status_t i_ipadm_addr_exists_on_if(ipadm_handle_t iph, const char *ifname, @@ -2405,6 +2410,7 @@ ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags) boolean_t is_6to4; struct lifreq lifr; uint64_t ifflags; + boolean_t is_boot = (iph->iph_flags & IPH_IPMGMTD); /* check for solaris.network.interface.config authorization */ if (!ipadm_check_auth()) @@ -2450,10 +2456,16 @@ ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags) 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. + * Skip this step if we are booting a zone (and therefore being called + * from ipmgmtd itself), and, for IPH_LEGACY case if the + * addrobj already exists. + * + * Note that the placeholder is not needed in the NGZ boot case, + * when zoneadmd has itself applied the "allowed-ips" property to clamp + * down any interface configuration, so the namespace for the interface + * is fully controlled by the GZ. */ - if (!legacy || !aobjfound) { + if (!is_boot && (!legacy || !aobjfound)) { status = i_ipadm_lookupadd_addrobj(iph, addr); if (status != IPADM_SUCCESS) return (status); @@ -2479,11 +2491,23 @@ ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t addr, uint32_t flags) created_other_af = B_TRUE; } - /* Validate static addresses for IFF_POINTOPOINT interfaces. */ + /* + * Some input validation based on the interface flags: + * 1. in non-global zones, make sure that we are not persistently + * creating addresses on interfaces that are acquiring + * address from the global zone. + * 2. 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; + + if (iph->iph_zoneid != GLOBAL_ZONEID && + (ifflags & IFF_L3PROTECT) && (flags & IPADM_OPT_PERSIST)) { + status = IPADM_GZ_PERM; + goto fail; + } daf = addr->ipadm_static_dst_addr.ss_family; if (ifflags & IFF_POINTOPOINT) { if (is_6to4) { @@ -2596,7 +2620,9 @@ i_ipadm_create_addr(ipadm_handle_t iph, ipadm_addrobj_t ipaddr, uint32_t flags) boolean_t legacy = (iph->iph_flags & IPH_LEGACY); struct ipadm_addrobj_s legacy_addr; boolean_t default_prefixlen = B_FALSE; + boolean_t is_boot; + is_boot = ((iph->iph_flags & IPH_IPMGMTD) != 0); af = ipaddr->ipadm_af; sock = (af == AF_INET ? iph->iph_sock : iph->iph_sock6); @@ -2668,7 +2694,7 @@ retry: status = IPADM_SUCCESS; } - if (status == IPADM_SUCCESS) { + if (status == IPADM_SUCCESS && !is_boot) { /* * For IPH_LEGACY, we might be modifying the address on * an address object that already exists e.g. by doing diff --git a/usr/src/lib/libipadm/common/ipadm_if.c b/usr/src/lib/libipadm/common/ipadm_if.c index 4591d42e9c..6d1e27dcbf 100644 --- a/usr/src/lib/libipadm/common/ipadm_if.c +++ b/usr/src/lib/libipadm/common/ipadm_if.c @@ -169,6 +169,8 @@ i_ipadm_active_if_info(ipadm_handle_t iph, const char *ifname, ifp->ifi_cflags |= IFIF_IPV4; if (lifrl.lifr_flags & IFF_IPV6) ifp->ifi_cflags |= IFIF_IPV6; + if (lifrl.lifr_flags & IFF_L3PROTECT) + ifp->ifi_cflags |= IFIF_L3PROTECT; } free(buf); return (IPADM_SUCCESS); @@ -376,6 +378,15 @@ i_ipadm_if_pexists(ipadm_handle_t iph, const char *ifname, sa_family_t af, ipadm_if_info_t *ifinfo; ipadm_status_t status; + /* + * if IPH_IPMGMTD is set, we know that the caller (ipmgmtd) already + * knows about persistent configuration in the first place, so we + * just return success. + */ + if (iph->iph_flags & IPH_IPMGMTD) { + *exists = B_FALSE; + return (IPADM_SUCCESS); + } status = i_ipadm_persist_if_info(iph, ifname, &ifinfo); if (status == IPADM_SUCCESS) { *exists = ((af == AF_INET && @@ -688,7 +699,7 @@ i_ipadm_plumb_if(ipadm_handle_t iph, char *ifname, sa_family_t af, * 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) { + if (iph->iph_zoneid == 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. */ diff --git a/usr/src/lib/libipadm/common/ipadm_ipmgmt.h b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h index 57d1023603..105e879de1 100644 --- a/usr/src/lib/libipadm/common/ipadm_ipmgmt.h +++ b/usr/src/lib/libipadm/common/ipadm_ipmgmt.h @@ -265,6 +265,17 @@ 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); +/* zone related functions */ +/* + * callback function to persist an interface in ipmgmtd data store + */ +typedef void (*persist_cb_t)(char *, boolean_t, boolean_t); +/* + * ipmgmtd/libipadm network initialization interface. + */ +extern ipadm_status_t ipadm_init_net_from_gz(ipadm_handle_t, char *, + persist_cb_t); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libipadm/common/ipadm_ngz.c b/usr/src/lib/libipadm/common/ipadm_ngz.c new file mode 100644 index 0000000000..e5f59fe6d2 --- /dev/null +++ b/usr/src/lib/libipadm/common/ipadm_ngz.c @@ -0,0 +1,485 @@ +/* + * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#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 <zone.h> +#include <libipadm.h> +#include <libdladm.h> +#include <libdllink.h> +#include <net/route.h> +#include <netinet/in.h> +#include <net/route.h> +#include <errno.h> +#include <inet/ip.h> +#include <string.h> +#include <libinetutil.h> +#include <unistd.h> +#include <libipadm_impl.h> +#include <sys/brand.h> + +#define ROUNDUP_LONG(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long)) +#define HOST_MASK 0xffffffffU + +typedef struct ngz_walk_data_s { + ipadm_handle_t ngz_iph; + zoneid_t ngz_zoneid; + char *ngz_ifname; + boolean_t ngz_s10c; + ipadm_status_t ngz_ipstatus; + persist_cb_t ngz_persist_if; +} ngz_walk_data_t; + +/* + * Tell the kernel to add, delete or change a route + */ +static void +i_ipadm_rtioctl4(int rtsock, + int action, /* RTM_DELETE, etc */ + in_addr_t dst, + in_addr_t gate, + uint_t masklen, + char *ifname, + uint8_t metric, + int flags) +{ + static int rt_sock_seqno = 0; + struct { + struct rt_msghdr w_rtm; + struct sockaddr_in w_dst; + struct sockaddr_in w_gate; + uint8_t w_space[512]; + } w; + struct sockaddr_in w_mask; + struct sockaddr_dl w_ifp; + uint8_t *cp; + long cc; + +again: + (void) memset(&w, 0, sizeof (w)); + (void) memset(&w_mask, 0, sizeof (w_mask)); + (void) memset(&w_ifp, 0, sizeof (w_ifp)); + cp = w.w_space; + w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) + + 2 * ROUNDUP_LONG(sizeof (struct sockaddr_in)); + w.w_rtm.rtm_version = RTM_VERSION; + w.w_rtm.rtm_type = action; + w.w_rtm.rtm_flags = (flags | RTF_ZONE); + w.w_rtm.rtm_seq = ++rt_sock_seqno; + w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; + if (metric != 0 || action == RTM_CHANGE) { + w.w_rtm.rtm_rmx.rmx_hopcount = metric; + w.w_rtm.rtm_inits |= RTV_HOPCOUNT; + } + w.w_dst.sin_family = AF_INET; + w.w_dst.sin_addr.s_addr = dst; + w.w_gate.sin_family = AF_INET; + w.w_gate.sin_addr.s_addr = gate; + if (masklen == HOST_MASK) { + w.w_rtm.rtm_flags |= RTF_HOST; + } else { + struct sockaddr_storage m4; + + w.w_rtm.rtm_addrs |= RTA_NETMASK; + w_mask.sin_family = AF_INET; + if (plen2mask(masklen, AF_INET, &m4) != 0) { + return; + } + w_mask.sin_addr = ((struct sockaddr_in *)&m4)->sin_addr; + (void) memmove(cp, &w_mask, sizeof (w_mask)); + cp += ROUNDUP_LONG(sizeof (struct sockaddr_in)); + w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_in)); + } + w_ifp.sdl_family = AF_LINK; + w.w_rtm.rtm_addrs |= RTA_IFP; + w_ifp.sdl_index = if_nametoindex(ifname); + (void) memmove(cp, &w_ifp, sizeof (w_ifp)); + w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_dl)); + + cc = write(rtsock, &w, w.w_rtm.rtm_msglen); + if (cc < 0) { + if (errno == ESRCH && (action == RTM_CHANGE || + action == RTM_DELETE)) { + if (action == RTM_CHANGE) { + action = RTM_ADD; + goto again; + } + return; + } + return; + } else if (cc != w.w_rtm.rtm_msglen) { + return; + } +} + +static void +i_ipadm_rtioctl6(int rtsock, + int action, /* RTM_DELETE, etc */ + in6_addr_t dst, + in6_addr_t gate, + uint_t prefix_length, + char *ifname, + int flags) +{ + static int rt_sock_seqno = 0; + struct { + struct rt_msghdr w_rtm; + struct sockaddr_in6 w_dst; + struct sockaddr_in6 w_gate; + uint8_t w_space[512]; + } w; + struct sockaddr_in6 w_mask; + struct sockaddr_dl w_ifp; + uint8_t *cp; + long cc; + +again: + (void) memset(&w, 0, sizeof (w)); + (void) memset(&w_mask, 0, sizeof (w_mask)); + (void) memset(&w_ifp, 0, sizeof (w_ifp)); + cp = w.w_space; + w.w_rtm.rtm_msglen = sizeof (struct rt_msghdr) + + 2 * ROUNDUP_LONG(sizeof (struct sockaddr_in6)); + w.w_rtm.rtm_version = RTM_VERSION; + w.w_rtm.rtm_type = action; + w.w_rtm.rtm_flags = (flags | RTF_ZONE); + w.w_rtm.rtm_seq = ++rt_sock_seqno; + w.w_rtm.rtm_addrs = RTA_DST|RTA_GATEWAY; + w.w_dst.sin6_family = AF_INET6; + w.w_dst.sin6_addr = dst; + w.w_gate.sin6_family = AF_INET6; + w.w_gate.sin6_addr = gate; + if (prefix_length == IPV6_ABITS) { + w.w_rtm.rtm_flags |= RTF_HOST; + } else { + struct sockaddr_storage m6; + + w.w_rtm.rtm_addrs |= RTA_NETMASK; + w_mask.sin6_family = AF_INET6; + if (plen2mask(prefix_length, AF_INET6, &m6) != 0) { + return; + } + w_mask.sin6_addr = ((struct sockaddr_in6 *)&m6)->sin6_addr; + (void) memmove(cp, &w_mask, sizeof (w_mask)); + cp += ROUNDUP_LONG(sizeof (struct sockaddr_in6)); + w.w_rtm.rtm_msglen += + ROUNDUP_LONG(sizeof (struct sockaddr_in6)); + } + w_ifp.sdl_family = AF_LINK; + w.w_rtm.rtm_addrs |= RTA_IFP; + w_ifp.sdl_index = if_nametoindex(ifname); + (void) memmove(cp, &w_ifp, sizeof (w_ifp)); + w.w_rtm.rtm_msglen += ROUNDUP_LONG(sizeof (struct sockaddr_dl)); + + cc = write(rtsock, &w, w.w_rtm.rtm_msglen); + if (cc < 0) { + if (errno == ESRCH && (action == RTM_CHANGE || + action == RTM_DELETE)) { + if (action == RTM_CHANGE) { + action = RTM_ADD; + goto again; + } + return; + } + return; + } else if (cc != w.w_rtm.rtm_msglen) { + return; + } +} + +/* + * Return TRUE if running in a Solaris 10 Container. + */ +static boolean_t +i_ipadm_zone_is_s10c(zoneid_t zoneid) +{ + char brand[MAXNAMELEN]; + + if (zone_getattr(zoneid, ZONE_ATTR_BRAND, brand, sizeof (brand)) < 0) + return (B_FALSE); + return (strcmp(brand, NATIVE_BRAND_NAME) != 0); +} + +/* + * Configure addresses on link. `buf' is a string of comma-separated + * IP addresses. + */ +static ipadm_status_t +i_ipadm_ngz_addr(ipadm_handle_t iph, char *link, char *buf) +{ + ipadm_status_t ipstatus; + ipadm_addrobj_t ipaddr; + char *cp; + + for (cp = strtok(buf, ","); cp != NULL; cp = strtok(NULL, ",")) { + ipstatus = ipadm_create_addrobj(IPADM_ADDR_STATIC, link, + &ipaddr); + if (ipstatus != IPADM_SUCCESS) + return (ipstatus); + /* + * ipadm_set_addr does the appropriate name resolution and + * sets up the ipadm_static_addr field. + */ + ipstatus = ipadm_set_addr(ipaddr, cp, AF_UNSPEC); + if (ipstatus != IPADM_SUCCESS) { + ipadm_destroy_addrobj(ipaddr); + return (ipstatus); + } + + ipstatus = ipadm_create_addr(iph, ipaddr, + (IPADM_OPT_ACTIVE | IPADM_OPT_UP)); + if (ipstatus != IPADM_SUCCESS) { + ipadm_destroy_addrobj(ipaddr); + return (ipstatus); + } + ipadm_destroy_addrobj(ipaddr); + } + return (IPADM_SUCCESS); +} + +/* + * The (*persist_if)() will set up persistent information for the interface, + * based on what interface families are required, so just resolve the + * address and inform the callback about the linkname, and required address + * families. + */ +static ipadm_status_t +i_ipadm_ngz_persist_if(char *link, char *buf, + void (*ngz_persist_if)(char *, boolean_t, boolean_t)) +{ + char *cp, *slashp, addr[INET6_ADDRSTRLEN]; + ipadm_status_t ipstatus; + struct sockaddr_storage ss; + boolean_t v4 = B_FALSE; + boolean_t v6 = B_FALSE; + + for (cp = strtok(buf, ","); cp != NULL; cp = strtok(NULL, ",")) { + /* remove the /<masklen> that's always added by zoneadmd */ + slashp = strchr(cp, '/'); + (void) strlcpy(addr, cp, (slashp - cp + 1)); + + /* resolve the address to find the family */ + bzero(&ss, sizeof (ss)); + ipstatus = i_ipadm_resolve_addr(addr, AF_UNSPEC, &ss); + if (ipstatus != IPADM_SUCCESS) + return (ipstatus); + switch (ss.ss_family) { + case AF_INET: + v4 = B_TRUE; + break; + case AF_INET6: + v6 = B_TRUE; + break; + default: + return (IPADM_BAD_ADDR); + } + } + (*ngz_persist_if)(link, v4, v6); + return (IPADM_SUCCESS); +} + +static void +i_ipadm_create_ngz_route(int rtsock, char *link, uint8_t *buf, size_t buflen) +{ + struct in6_addr defrouter; + boolean_t isv6; + struct in_addr gw4; + uint8_t *cp; + const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 }; + + if (rtsock == -1) + return; + + for (cp = buf; cp < buf + buflen; cp += sizeof (defrouter)) { + bcopy(cp, &defrouter, sizeof (defrouter)); + if (IN6_IS_ADDR_UNSPECIFIED(&defrouter)) + break; + isv6 = !IN6_IS_ADDR_V4MAPPED(&defrouter); + if (isv6) { + i_ipadm_rtioctl6(rtsock, RTM_ADD, ipv6_all_zeros, + defrouter, 0, link, RTF_GATEWAY); + } else { + IN6_V4MAPPED_TO_INADDR(&defrouter, &gw4); + i_ipadm_rtioctl4(rtsock, RTM_ADD, INADDR_ANY, + gw4.s_addr, 0, link, 0, RTF_GATEWAY); + } + } +} + +/* + * Wrapper function to zone_getattr() for retrieving from-gz attributes that + * were made availabe for exclusive IP non-global zones by zoneadmd from teh + * global zone. + */ +static ipadm_status_t +i_ipadm_zone_get_network(zoneid_t zoneid, datalink_id_t linkid, int type, + void *buf, size_t *bufsize) +{ + zone_net_data_t *zndata; + + zndata = calloc(1, sizeof (*zndata) + *bufsize); + if (zndata == NULL) + return (IPADM_NO_MEMORY); + zndata->zn_type = type; + zndata->zn_linkid = linkid; + zndata->zn_len = *bufsize; + + if (zone_getattr(zoneid, ZONE_ATTR_NETWORK, zndata, + sizeof (*zndata) + *bufsize) < 0) { + return (ipadm_errno2status(errno)); + } + *bufsize = zndata->zn_len; + bcopy(zndata->zn_val, buf, *bufsize); + return (IPADM_SUCCESS); +} + +/* + * Callback function that configures a single datalink in a non-global zone. + */ +static int +i_ipadm_zone_network_attr(dladm_handle_t dh, datalink_id_t linkid, void *arg) +{ + ngz_walk_data_t *nwd = arg; + zoneid_t zoneid = nwd->ngz_zoneid; + uint8_t buf[PIPE_BUF]; + dladm_status_t dlstatus; + ipadm_status_t ipstatus; + char link[MAXLINKNAMELEN]; + ipadm_handle_t iph = nwd->ngz_iph; + int rtsock = iph->iph_rtsock; + char *ifname = nwd->ngz_ifname; + boolean_t s10c = nwd->ngz_s10c; + boolean_t is_ipmgmtd = (iph->iph_flags & IPH_IPMGMTD); + size_t bufsize = sizeof (buf); + + bzero(buf, bufsize); + ipstatus = i_ipadm_zone_get_network(zoneid, linkid, + ZONE_NETWORK_ADDRESS, buf, &bufsize); + if (ipstatus != IPADM_SUCCESS) + goto fail; + + dlstatus = dladm_datalink_id2info(dh, linkid, NULL, NULL, + NULL, link, sizeof (link)); + if (dlstatus != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + /* + * if ifname has been specified, then skip interfaces that don't match + */ + if (ifname != NULL && strcmp(ifname, link) != 0) + return (DLADM_WALK_CONTINUE); + + /* + * Plumb the interface and configure addresses on for S10 Containers. + * We need to always do this for S10C because ipadm persistent + * configuration is not available in S10C. For ipkg zones, + * we skip the actual plumbing/configuration, but will call the + * (*ngz_persist_if)() callback to create the persistent state for the + * interface. The interface will be configured in ipkg zones when + * ipadm_enable_if() is invoked to restore persistent configuration. + */ + if (is_ipmgmtd && !s10c) { + (void) i_ipadm_ngz_persist_if(link, (char *)buf, + nwd->ngz_persist_if); + return (DLADM_WALK_CONTINUE); + } + ipstatus = i_ipadm_ngz_addr(iph, link, (char *)buf); + if (ipstatus != IPADM_SUCCESS) + goto fail; + + /* apply any default router information. */ + bufsize = sizeof (buf); + bzero(buf, bufsize); + ipstatus = i_ipadm_zone_get_network(zoneid, linkid, + ZONE_NETWORK_DEFROUTER, buf, &bufsize); + if (ipstatus != IPADM_SUCCESS) + goto fail; + + i_ipadm_create_ngz_route(rtsock, link, buf, bufsize); + + return (DLADM_WALK_CONTINUE); +fail: + if (ifname != NULL) { + nwd->ngz_ipstatus = ipstatus; + return (DLADM_WALK_TERMINATE); + } + return (DLADM_WALK_CONTINUE); +} + +/* + * ipmgmt_net_from_gz_init() initializes exclusive-IP stack non-global zones by + * extracting configuration that has been saved in the kernel and applying + * that information to the appropriate datalinks for the zone. If an ifname + * argument is passed in, only the selected IP interface corresponding to + * datalink will be initialized, otherwise all datalinks will be plumbed for IP + * and IP address and route information will be configured. + */ +ipadm_status_t +ipadm_init_net_from_gz(ipadm_handle_t iph, char *ifname, + void (*persist_if)(char *, boolean_t, boolean_t)) +{ + ngz_walk_data_t nwd; + uint64_t flags; + dladm_handle_t dlh = iph->iph_dlh; + datalink_id_t linkid; + + if (iph->iph_zoneid == GLOBAL_ZONEID) + return (IPADM_NOTSUP); + + if (ifname != NULL && + i_ipadm_get_flags(iph, ifname, AF_INET, &flags) != IPADM_SUCCESS && + i_ipadm_get_flags(iph, ifname, AF_INET6, &flags) != IPADM_SUCCESS) + return (IPADM_ENXIO); + + if (ifname != NULL && !(flags & IFF_L3PROTECT)) + return (IPADM_SUCCESS); /* nothing to initialize */ + + nwd.ngz_iph = iph; + nwd.ngz_zoneid = iph->iph_zoneid; + nwd.ngz_ifname = ifname; + nwd.ngz_persist_if = persist_if; + nwd.ngz_s10c = i_ipadm_zone_is_s10c(iph->iph_zoneid); + nwd.ngz_ipstatus = IPADM_SUCCESS; + if (ifname != NULL) { + if (dladm_name2info(dlh, ifname, &linkid, NULL, NULL, + NULL) != DLADM_STATUS_OK) { + return (IPADM_ENXIO); + } + (void) i_ipadm_zone_network_attr(dlh, linkid, &nwd); + } else { + (void) dladm_walk_datalink_id(i_ipadm_zone_network_attr, dlh, + &nwd, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_PERSIST); + } + return (nwd.ngz_ipstatus); +} diff --git a/usr/src/lib/libipadm/common/libipadm.c b/usr/src/lib/libipadm/common/libipadm.c index 4103ebbd85..21aeab72ba 100644 --- a/usr/src/lib/libipadm/common/libipadm.c +++ b/usr/src/lib/libipadm/common/libipadm.c @@ -97,7 +97,8 @@ static struct ipadm_error_info { { 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" } + { IPADM_EBADE, "Invalid data exchange with daemon" }, + { IPADM_GZ_PERM, "Operation not permitted on from-gz interface"} }; #define IPADM_NUM_ERRORS (sizeof (ipadm_errors) / sizeof (*ipadm_errors)) @@ -186,7 +187,7 @@ ipadm_open(ipadm_handle_t *handle, uint32_t flags) return (IPADM_INVALID_ARG); *handle = NULL; - if (flags & ~(IPH_VRRP|IPH_LEGACY|IPH_INIT)) + if (flags & ~(IPH_VRRP|IPH_LEGACY|IPH_INIT|IPH_IPMGMTD)) return (IPADM_INVALID_ARG); if ((iph = calloc(1, sizeof (struct ipadm_handle))) == NULL) @@ -194,6 +195,7 @@ ipadm_open(ipadm_handle_t *handle, uint32_t flags) iph->iph_sock = -1; iph->iph_sock6 = -1; iph->iph_door_fd = -1; + iph->iph_rtsock = -1; iph->iph_flags = flags; (void) pthread_mutex_init(&iph->iph_lock, NULL); @@ -213,6 +215,7 @@ ipadm_open(ipadm_handle_t *handle, uint32_t flags) * dladm_open() for such zones. */ zoneid = getzoneid(); + iph->iph_zoneid = zoneid; if (zoneid != GLOBAL_ZONEID) { if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags, sizeof (zflags)) < 0) { @@ -224,6 +227,14 @@ ipadm_open(ipadm_handle_t *handle, uint32_t flags) ipadm_close(iph); return (IPADM_DLADM_FAILURE); } + if (zoneid != GLOBAL_ZONEID) { + iph->iph_rtsock = socket(PF_ROUTE, SOCK_RAW, 0); + /* + * Failure to open rtsock is ignored as this is + * only used in non-global zones to initialize + * routing socket information. + */ + } } else { assert(zoneid != GLOBAL_ZONEID); iph->iph_dlh = NULL; @@ -256,6 +267,8 @@ ipadm_close(ipadm_handle_t iph) (void) close(iph->iph_sock); if (iph->iph_sock6 != -1) (void) close(iph->iph_sock6); + if (iph->iph_rtsock != -1) + (void) close(iph->iph_rtsock); if (iph->iph_door_fd != -1) (void) close(iph->iph_door_fd); dladm_close(iph->iph_dlh); @@ -494,7 +507,7 @@ i_ipadm_is_6to4(ipadm_handle_t iph, char *ifname) datalink_id_t linkid; if (iph->iph_dlh == NULL) { - assert(getzoneid() != GLOBAL_ZONEID); + assert(iph->iph_zoneid != GLOBAL_ZONEID); return (B_FALSE); } dlstatus = dladm_name2info(iph->iph_dlh, ifname, &linkid, NULL, @@ -682,6 +695,8 @@ i_ipadm_init_ifobj(ipadm_handle_t iph, const char *ifname, nvlist_t *ifnvl) ipadm_status_t ret_status = IPADM_SUCCESS; char newifname[LIFNAMSIZ]; char *aobjstr; + sa_family_t af = AF_UNSPEC; + boolean_t is_ngz = (iph->iph_zoneid != GLOBAL_ZONEID); (void) strlcpy(newifname, ifname, sizeof (newifname)); /* @@ -705,6 +720,9 @@ i_ipadm_init_ifobj(ipadm_handle_t iph, const char *ifname, nvlist_t *ifnvl) */ if (status == IPADM_IF_EXISTS) status = IPADM_SUCCESS; + + if (is_ngz) + af = atoi(afstr); } else if (nvlist_lookup_string(nvl, IPADM_NVP_AOBJNAME, &aobjstr) == 0) { /* @@ -736,6 +754,9 @@ i_ipadm_init_ifobj(ipadm_handle_t iph, const char *ifname, nvlist_t *ifnvl) if (status != IPADM_SUCCESS) return (status); } + + if (is_ngz && af != AF_UNSPEC) + ret_status = ipadm_init_net_from_gz(iph, newifname, NULL); return (ret_status); } diff --git a/usr/src/lib/libipadm/common/libipadm.h b/usr/src/lib/libipadm/common/libipadm.h index 32fb89e2e8..8ecc8db706 100644 --- a/usr/src/lib/libipadm/common/libipadm.h +++ b/usr/src/lib/libipadm/common/libipadm.h @@ -91,7 +91,8 @@ typedef enum { 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_EBADE, /* Invalid data exchange with ipmgmtd */ + IPADM_GZ_PERM /* Operation not permitted on from-gz intf */ } ipadm_status_t; /* @@ -171,6 +172,7 @@ 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 */ +#define IPH_IPMGMTD 0x00000004 /* Caller is ipmgmtd itself */ /* opaque address object structure */ typedef struct ipadm_addrobj_s *ipadm_addrobj_t; @@ -204,6 +206,7 @@ typedef struct ipadm_if_info_s { #define IFIF_NOACCEPT 0x00000100 #define IFIF_IPV4 0x00000200 #define IFIF_IPV6 0x00000400 +#define IFIF_L3PROTECT 0x00000800 /* ipadm_addr_info_t state */ typedef enum { diff --git a/usr/src/lib/libipadm/common/libipadm_impl.h b/usr/src/lib/libipadm/common/libipadm_impl.h index ff953dfd1f..b5604307b2 100644 --- a/usr/src/lib/libipadm/common/libipadm_impl.h +++ b/usr/src/lib/libipadm/common/libipadm_impl.h @@ -55,9 +55,11 @@ struct ipadm_handle { int iph_sock; /* socket to interface */ int iph_sock6; /* socket to interface */ int iph_door_fd; /* door descriptor to ipmgmtd */ + int iph_rtsock; /* routing socket */ 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 */ + zoneid_t iph_zoneid; /* zoneid where handle was opened */ }; /* @@ -210,6 +212,8 @@ 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 *); +extern ipadm_status_t i_ipadm_resolve_addr(const char *, sa_family_t, + struct sockaddr_storage *); /* ipadm_if.c */ extern ipadm_status_t i_ipadm_create_if(ipadm_handle_t, char *, sa_family_t, diff --git a/usr/src/lib/libipadm/common/mapfile-vers b/usr/src/lib/libipadm/common/mapfile-vers index 4819963afb..e893f5ae37 100644 --- a/usr/src/lib/libipadm/common/mapfile-vers +++ b/usr/src/lib/libipadm/common/mapfile-vers @@ -67,6 +67,7 @@ SYMBOL_VERSION SUNWprivate_1.1 { ipadm_if_info; ipadm_if_move; ipadm_init_prop; + ipadm_init_net_from_gz; ipadm_ndpd_read; ipadm_ndpd_write; ipadm_nvlist2str; diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index b927b6b954..c9da45b76a 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.c @@ -99,6 +99,7 @@ #define DTD_ATTR_ACTION (const xmlChar *) "action" #define DTD_ATTR_ADDRESS (const xmlChar *) "address" +#define DTD_ATTR_ALLOWED_ADDRESS (const xmlChar *) "allowed-address" #define DTD_ATTR_AUTOBOOT (const xmlChar *) "autoboot" #define DTD_ATTR_IPTYPE (const xmlChar *) "ip-type" #define DTD_ATTR_DEFROUTER (const xmlChar *) "defrouter" @@ -2090,6 +2091,8 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) size_t addrspec; /* nonzero if tabptr has IP addr */ size_t physspec; /* nonzero if tabptr has interface */ size_t defrouterspec; /* nonzero if tabptr has def. router */ + size_t allowed_addrspec; + zone_iptype_t iptype; if (tabptr == NULL) return (Z_INVAL); @@ -2104,12 +2107,18 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) addrspec = strlen(tabptr->zone_nwif_address); physspec = strlen(tabptr->zone_nwif_physical); defrouterspec = strlen(tabptr->zone_nwif_defrouter); - if (addrspec == 0 && physspec == 0 && defrouterspec == 0) + allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address); + if (addrspec != 0 && allowed_addrspec != 0) + return (Z_INVAL); /* can't specify both */ + if (addrspec == 0 && physspec == 0 && defrouterspec == 0 && + allowed_addrspec == 0) return (Z_INSUFFICIENT_SPEC); if ((err = operation_prep(handle)) != Z_OK) return (err); + if ((err = zonecfg_get_iptype(handle, &iptype)) != Z_OK) + return (err); /* * Iterate over the configuration's elements and look for net elements * that match the query. @@ -2129,11 +2138,18 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) physical, sizeof (physical)) != Z_OK || strcmp(tabptr->zone_nwif_physical, physical) != 0)) continue; - if (addrspec != 0 && (fetchprop(cur, DTD_ATTR_ADDRESS, address, + if (iptype == ZS_SHARED && addrspec != 0 && + (fetchprop(cur, DTD_ATTR_ADDRESS, address, sizeof (address)) != Z_OK || !zonecfg_same_net_address(tabptr->zone_nwif_address, address))) continue; + if (iptype == ZS_EXCLUSIVE && allowed_addrspec != 0 && + (fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS, address, + sizeof (address)) != Z_OK || + !zonecfg_same_net_address(tabptr->zone_nwif_allowed_address, + address))) + continue; if (defrouterspec != 0 && (fetchprop(cur, DTD_ATTR_DEFROUTER, address, sizeof (address)) != Z_OK || !zonecfg_same_net_address(tabptr->zone_nwif_defrouter, @@ -2158,10 +2174,17 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr) sizeof (tabptr->zone_nwif_physical))) != Z_OK) return (err); - if ((err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address, + if (iptype == ZS_SHARED && + (err = fetchprop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address, sizeof (tabptr->zone_nwif_address))) != Z_OK) return (err); + if (iptype == ZS_EXCLUSIVE && + (err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS, + tabptr->zone_nwif_allowed_address, + sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK) + return (err); + if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER, tabptr->zone_nwif_defrouter, sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) @@ -2177,9 +2200,14 @@ zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) int err; newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL); - if ((err = newprop(newnode, DTD_ATTR_ADDRESS, + if (strlen(tabptr->zone_nwif_address) > 0 && + (err = newprop(newnode, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address)) != Z_OK) return (err); + if (strlen(tabptr->zone_nwif_allowed_address) > 0 && + (err = newprop(newnode, DTD_ATTR_ALLOWED_ADDRESS, + tabptr->zone_nwif_allowed_address)) != Z_OK) + return (err); if ((err = newprop(newnode, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical)) != Z_OK) return (err); @@ -2215,7 +2243,7 @@ static int zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) { xmlNodePtr cur = handle->zone_dh_cur; - boolean_t addr_match, phys_match; + boolean_t addr_match, phys_match, allowed_addr_match; for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) { if (xmlStrcmp(cur->name, DTD_ELEM_NET)) @@ -2223,10 +2251,12 @@ zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr) addr_match = match_prop(cur, DTD_ATTR_ADDRESS, tabptr->zone_nwif_address); + allowed_addr_match = match_prop(cur, DTD_ATTR_ALLOWED_ADDRESS, + tabptr->zone_nwif_allowed_address); phys_match = match_prop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical); - if (addr_match && phys_match) { + if ((addr_match || allowed_addr_match) && phys_match) { xmlUnlinkNode(cur); xmlFreeNode(cur); return (Z_OK); @@ -4734,6 +4764,13 @@ zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr) return (err); } + if ((err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS, + tabptr->zone_nwif_allowed_address, + sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK) { + handle->zone_dh_cur = handle->zone_dh_top; + return (err); + } + if ((err = fetchprop(cur, DTD_ATTR_PHYSICAL, tabptr->zone_nwif_physical, sizeof (tabptr->zone_nwif_physical))) != Z_OK) { handle->zone_dh_cur = handle->zone_dh_top; diff --git a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 index d1857cd7c5..d94bb09c5f 100644 --- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 +++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 @@ -49,6 +49,7 @@ <!ELEMENT network EMPTY> <!ATTLIST network address CDATA "" + allowed-address CDATA "" defrouter CDATA "" physical CDATA #REQUIRED> diff --git a/usr/src/uts/common/inet/ip.h b/usr/src/uts/common/inet/ip.h index 5e4f19b23e..16e45960ac 100644 --- a/usr/src/uts/common/inet/ip.h +++ b/usr/src/uts/common/inet/ip.h @@ -1801,6 +1801,8 @@ typedef struct ill_s { timeout_id_t ill_refresh_tid; /* ill refresh retry timeout id */ uint32_t ill_mrouter_cnt; /* mrouter allmulti joins */ + uint32_t ill_allowed_ips_cnt; + in6_addr_t *ill_allowed_ips; } ill_t; /* diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c index 5df30d18d9..4510d37f6a 100644 --- a/usr/src/uts/common/inet/ip/ip.c +++ b/usr/src/uts/common/inet/ip/ip.c @@ -8579,6 +8579,10 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg) ill_capability_reset(ill, B_TRUE); ipsq_current_finish(ipsq); break; + + case DL_NOTE_ALLOWED_IPS: + ill_set_allowed_ips(ill, mp); + break; default: ip0dbg(("ip_rput_dlpi_writer: unknown notification " "type 0x%x for DL_NOTIFY_IND\n", diff --git a/usr/src/uts/common/inet/ip/ip6_if.c b/usr/src/uts/common/inet/ip/ip6_if.c index e4826bb1a2..c7fc05e2e0 100644 --- a/usr/src/uts/common/inet/ip/ip6_if.c +++ b/usr/src/uts/common/inet/ip/ip6_if.c @@ -19,10 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ -/* + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1990 Mentat Inc. */ @@ -2302,7 +2299,7 @@ ill_dl_phys(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q) (DL_NOTE_PHYS_ADDR | DL_NOTE_SDU_SIZE | DL_NOTE_FASTPATH_FLUSH | DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_CAPAB_RENEG | DL_NOTE_PROMISC_ON_PHYS | DL_NOTE_PROMISC_OFF_PHYS | - DL_NOTE_REPLUMB); + DL_NOTE_REPLUMB | DL_NOTE_ALLOWED_IPS); phys_mp = ip_dlpi_alloc(sizeof (dl_phys_addr_req_t) + sizeof (t_scalar_t), DL_PHYS_ADDR_REQ); diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c index 689c66d66a..19a6821595 100644 --- a/usr/src/uts/common/inet/ip/ip_if.c +++ b/usr/src/uts/common/inet/ip/ip_if.c @@ -94,6 +94,7 @@ #include <inet/ipclassifier.h> #include <sys/mac_client.h> #include <sys/dld.h> +#include <sys/mac_flow.h> #include <sys/systeminfo.h> #include <sys/bootconf.h> @@ -546,6 +547,15 @@ ill_delete_tail(ill_t *ill) ill->ill_dld_capab = NULL; } + /* Clean up ill_allowed_ips* related state */ + if (ill->ill_allowed_ips != NULL) { + ASSERT(ill->ill_allowed_ips_cnt > 0); + kmem_free(ill->ill_allowed_ips, + ill->ill_allowed_ips_cnt * sizeof (in6_addr_t)); + ill->ill_allowed_ips = NULL; + ill->ill_allowed_ips_cnt = 0; + } + while (ill->ill_ipif != NULL) ipif_free_tail(ill->ill_ipif); @@ -2683,6 +2693,9 @@ ill_forward_set(ill_t *ill, boolean_t enable) if (IS_LOOPBACK(ill)) return (EINVAL); + if (enable && ill->ill_allowed_ips_cnt > 0) + return (EPERM); + if (IS_IPMP(ill) || IS_UNDER_IPMP(ill)) { /* * Update all of the interfaces in the group. @@ -9656,15 +9669,17 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, int err = 0; in6_addr_t v6addr; boolean_t need_up = B_FALSE; + ill_t *ill; + int i; ip1dbg(("ip_sioctl_addr(%s:%u %p)\n", ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif)); ASSERT(IAM_WRITER_IPIF(ipif)); + ill = ipif->ipif_ill; if (ipif->ipif_isv6) { sin6_t *sin6; - ill_t *ill; phyint_t *phyi; if (sin->sin_family != AF_INET6) @@ -9672,7 +9687,6 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, sin6 = (sin6_t *)sin; v6addr = sin6->sin6_addr; - ill = ipif->ipif_ill; phyi = ill->ill_phyint; /* @@ -9731,7 +9745,21 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, IN6_IPADDR_TO_V4MAPPED(addr, &v6addr); } - + /* + * verify that the address being configured is permitted by the + * ill_allowed_ips[] for the interface. + */ + if (ill->ill_allowed_ips_cnt > 0) { + for (i = 0; i < ill->ill_allowed_ips_cnt; i++) { + if (IN6_ARE_ADDR_EQUAL(&ill->ill_allowed_ips[i], + &v6addr)) + break; + } + if (i == ill->ill_allowed_ips_cnt) { + pr_addr_dbg("!allowed addr %s\n", AF_INET6, &v6addr); + return (EPERM); + } + } /* * Even if there is no change we redo things just to rerun * ipif_set_default. @@ -10373,8 +10401,11 @@ ip_sioctl_flags(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp, * If ILLF_ROUTER changes, we need to change the ip forwarding * status of the interface. */ - if ((turn_on | turn_off) & ILLF_ROUTER) - (void) ill_forward_set(ill, ((turn_on & ILLF_ROUTER) != 0)); + if ((turn_on | turn_off) & ILLF_ROUTER) { + err = ill_forward_set(ill, ((turn_on & ILLF_ROUTER) != 0)); + if (err != 0) + return (err); + } /* * If the interface is not UP and we are not going to @@ -17781,6 +17812,51 @@ ill_set_phys_addr(ill_t *ill, mblk_t *mp) } /* + * When the allowed-ips link property is set on the datalink, IP receives a + * DL_NOTE_ALLOWED_IPS notification that is processed in ill_set_allowed_ips() + * to initialize the ill_allowed_ips[] array in the ill_t. This array is then + * used to vet addresses passed to ip_sioctl_addr() and to ensure that the + * only IP addresses configured on the ill_t are those in the ill_allowed_ips[] + * array. + */ +void +ill_set_allowed_ips(ill_t *ill, mblk_t *mp) +{ + ipsq_t *ipsq = ill->ill_phyint->phyint_ipsq; + dl_notify_ind_t *dlip = (dl_notify_ind_t *)mp->b_rptr; + mac_protect_t *mrp; + int i; + + ASSERT(IAM_WRITER_IPSQ(ipsq)); + mrp = (mac_protect_t *)&dlip[1]; + + if (mrp->mp_ipaddrcnt == 0) { /* reset allowed-ips */ + kmem_free(ill->ill_allowed_ips, + ill->ill_allowed_ips_cnt * sizeof (in6_addr_t)); + ill->ill_allowed_ips_cnt = 0; + ill->ill_allowed_ips = NULL; + mutex_enter(&ill->ill_phyint->phyint_lock); + ill->ill_phyint->phyint_flags &= ~PHYI_L3PROTECT; + mutex_exit(&ill->ill_phyint->phyint_lock); + return; + } + + if (ill->ill_allowed_ips != NULL) { + kmem_free(ill->ill_allowed_ips, + ill->ill_allowed_ips_cnt * sizeof (in6_addr_t)); + } + ill->ill_allowed_ips_cnt = mrp->mp_ipaddrcnt; + ill->ill_allowed_ips = kmem_alloc( + ill->ill_allowed_ips_cnt * sizeof (in6_addr_t), KM_SLEEP); + for (i = 0; i < mrp->mp_ipaddrcnt; i++) + ill->ill_allowed_ips[i] = mrp->mp_ipaddrs[i].ip_addr; + + mutex_enter(&ill->ill_phyint->phyint_lock); + ill->ill_phyint->phyint_flags |= PHYI_L3PROTECT; + mutex_exit(&ill->ill_phyint->phyint_lock); +} + +/* * Once the ill associated with `q' has quiesced, set its physical address * information to the values in `addrmp'. Note that two copies of `addrmp' * are passed (linked by b_cont), since we sometimes need to save two distinct diff --git a/usr/src/uts/common/inet/ip_if.h b/usr/src/uts/common/inet/ip_if.h index c1ffa33189..08dfc920f3 100644 --- a/usr/src/uts/common/inet/ip_if.h +++ b/usr/src/uts/common/inet/ip_if.h @@ -19,10 +19,9 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1990 Mentat Inc. */ -/* Copyright (c) 1990 Mentat Inc. */ #ifndef _INET_IP_IF_H #define _INET_IP_IF_H @@ -76,7 +75,7 @@ extern "C" { */ #define IFF_PHYINT_FLAGS (IFF_LOOPBACK|IFF_RUNNING|IFF_PROMISC| \ IFF_ALLMULTI|IFF_INTELLIGENT|IFF_MULTI_BCAST|IFF_FAILED|IFF_STANDBY| \ - IFF_INACTIVE|IFF_OFFLINE|IFF_VIRTUAL|IFF_IPMP) + IFF_INACTIVE|IFF_OFFLINE|IFF_VIRTUAL|IFF_IPMP|IFF_L3PROTECT) #define IFF_PHYINTINST_FLAGS (IFF_DEBUG|IFF_NOTRAILERS|IFF_NOARP| \ IFF_MULTICAST|IFF_ROUTER|IFF_NONUD|IFF_NORTEXCH|IFF_IPV4|IFF_IPV6| \ @@ -99,6 +98,7 @@ extern "C" { #define PHYI_OFFLINE IFF_OFFLINE /* NIC has been offlined */ #define PHYI_VIRTUAL IFF_VIRTUAL /* Will not send or recv pkts */ #define PHYI_IPMP IFF_IPMP /* IPMP meta-interface */ +#define PHYI_L3PROTECT IFF_L3PROTECT /* Layer-3 protected */ #define ILLF_DEBUG IFF_DEBUG /* turn on debugging */ #define ILLF_NOTRAILERS IFF_NOTRAILERS /* avoid use of trailers */ @@ -195,6 +195,7 @@ extern void ill_setdesttoken(ill_t *); extern void ill_set_inputfn(ill_t *); extern void ill_set_inputfn_all(ip_stack_t *); extern int ill_set_phys_addr(ill_t *, mblk_t *); +extern void ill_set_allowed_ips(ill_t *, mblk_t *); extern int ill_replumb(ill_t *, mblk_t *); extern void ill_set_ndmp(ill_t *, mblk_t *, uint_t, uint_t); diff --git a/usr/src/uts/common/io/dld/dld_proto.c b/usr/src/uts/common/io/dld/dld_proto.c index 1231a0cced..facd2fbfb8 100644 --- a/usr/src/uts/common/io/dld/dld_proto.c +++ b/usr/src/uts/common/io/dld/dld_proto.c @@ -1127,7 +1127,8 @@ proto_notify_req(dld_str_t *dsp, mblk_t *mp) DL_NOTE_CAPAB_RENEG | DL_NOTE_FASTPATH_FLUSH | DL_NOTE_SPEED | - DL_NOTE_SDU_SIZE; + DL_NOTE_SDU_SIZE| + DL_NOTE_ALLOWED_IPS; if (MBLKL(mp) < sizeof (dl_notify_req_t)) { dl_err = DL_BADPRIM; diff --git a/usr/src/uts/common/io/dld/dld_str.c b/usr/src/uts/common/io/dld/dld_str.c index 63d2bb6511..c4d46090c1 100644 --- a/usr/src/uts/common/io/dld/dld_str.c +++ b/usr/src/uts/common/io/dld/dld_str.c @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -38,6 +37,7 @@ #include <sys/list.h> #include <sys/mac_client.h> #include <sys/mac_client_priv.h> +#include <sys/mac_flow.h> static int str_constructor(void *, void *, int); static void str_destructor(void *, void *); @@ -1719,6 +1719,35 @@ str_notify_fastpath_flush(dld_str_t *dsp) qreply(dsp->ds_wq, mp); } +static void +str_notify_allowed_ips(dld_str_t *dsp) +{ + mblk_t *mp; + dl_notify_ind_t *dlip; + size_t mp_size; + mac_protect_t mrp; + + if (!(dsp->ds_notifications & DL_NOTE_ALLOWED_IPS)) + return; + + mp_size = sizeof (mac_protect_t) + sizeof (dl_notify_ind_t); + if ((mp = mexchange(dsp->ds_wq, NULL, mp_size, M_PROTO, 0)) == NULL) + return; + + mac_protect_get(dsp->ds_mh, &mrp); + bzero(mp->b_rptr, mp_size); + dlip = (dl_notify_ind_t *)mp->b_rptr; + dlip->dl_primitive = DL_NOTIFY_IND; + dlip->dl_notification = DL_NOTE_ALLOWED_IPS; + dlip->dl_data = 0; + dlip->dl_addr_offset = sizeof (dl_notify_ind_t); + dlip->dl_addr_length = sizeof (mac_protect_t); + bcopy(&mrp, mp->b_rptr + sizeof (dl_notify_ind_t), + sizeof (mac_protect_t)); + + qreply(dsp->ds_wq, mp); +} + /* * MAC notification callback. */ @@ -1849,6 +1878,10 @@ str_notify(void *arg, mac_notify_type_t type) case MAC_NOTE_MARGIN: break; + case MAC_NOTE_ALLOWED_IPS: + str_notify_allowed_ips(dsp); + break; + default: ASSERT(B_FALSE); break; diff --git a/usr/src/uts/common/io/mac/mac_protect.c b/usr/src/uts/common/io/mac/mac_protect.c index c923bcdbe2..30b6b18bc4 100644 --- a/usr/src/uts/common/io/mac/mac_protect.c +++ b/usr/src/uts/common/io/mac/mac_protect.c @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ #include <sys/strsun.h> @@ -194,6 +193,7 @@ typedef struct dhcpv6_txn { } dhcpv6_txn_t; static void start_txn_cleanup_timer(mac_client_impl_t *); +static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t); #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++ @@ -552,29 +552,34 @@ txn_cleanup_v4(mac_client_impl_t *mcip) /* * Core logic for intercepting outbound DHCPv4 packets. */ -static void +static boolean_t intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end) { - struct dhcp *dh4; - uchar_t *opt; - dhcpv4_txn_t *txn, *ctxn; - ipaddr_t ipaddr; - uint8_t opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len; + struct dhcp *dh4; + uchar_t *opt; + dhcpv4_txn_t *txn, *ctxn; + ipaddr_t ipaddr; + uint8_t opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len; + mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); if (get_dhcpv4_info(ipha, end, &dh4) != 0) - return; + return (B_TRUE); + + /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ + if (allowed_ips_set(mrp, IPV4_VERSION)) + return (B_FALSE); if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || opt_len != 1) { DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip, struct dhcp *, dh4); - return; + return (B_TRUE); } mtype = *opt; if (mtype != REQUEST && mtype != RELEASE) { DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip, struct dhcp *, dh4, uint8_t, mtype); - return; + return (B_TRUE); } /* client ID is optional for IPv4 */ @@ -639,6 +644,7 @@ intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end) done: mutex_exit(&mcip->mci_protect_lock); + return (B_TRUE); } /* @@ -1208,7 +1214,7 @@ txn_cleanup_v6(mac_client_impl_t *mcip) /* * Core logic for intercepting outbound DHCPv6 packets. */ -static void +static boolean_t intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end) { dhcpv6_message_t *dh6; @@ -1216,17 +1222,22 @@ intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end) dhcpv6_cid_t *cid = NULL; uint32_t xid; uint8_t mtype; + mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); if (get_dhcpv6_info(ip6h, end, &dh6) != 0) - return; + return (B_TRUE); + + /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ + if (allowed_ips_set(mrp, IPV6_VERSION)) + return (B_FALSE); mtype = dh6->d6m_msg_type; if (mtype != DHCPV6_MSG_REQUEST && mtype != DHCPV6_MSG_RENEW && mtype != DHCPV6_MSG_REBIND && mtype != DHCPV6_MSG_RELEASE) - return; + return (B_TRUE); if ((cid = create_dhcpv6_cid(dh6, end)) == NULL) - return; + return (B_TRUE); mutex_enter(&mcip->mci_protect_lock); if (mtype == DHCPV6_MSG_RELEASE) { @@ -1260,6 +1271,7 @@ done: free_dhcpv6_cid(cid); mutex_exit(&mcip->mci_protect_lock); + return (B_TRUE); } /* @@ -1524,7 +1536,8 @@ ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect, V4_PART_OF_V6(v4addr->ip_addr) == *addr) return (B_TRUE); } - return (check_dhcpv4_dyn_ip(mcip, *addr)); + return (protect->mp_ipaddrcnt == 0 ? + check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE); } static boolean_t @@ -1549,7 +1562,8 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, IN6_ARE_ADDR_EQUAL(&v6addr->ip_addr, addr)) return (B_TRUE); } - return (check_dhcpv6_dyn_ip(mcip, addr)); + return (protect->mp_ipaddrcnt == 0 ? + check_dhcpv6_dyn_ip(mcip, addr) : B_FALSE); } /* @@ -1694,7 +1708,8 @@ ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src)) goto fail; - intercept_dhcpv4_outbound(mcip, ipha, end); + if (!intercept_dhcpv4_outbound(mcip, ipha, end)) + goto fail; break; } case ETHERTYPE_ARP: { @@ -1739,7 +1754,8 @@ ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, if (!ipnospoof_check_ndp(mcip, protect, ip6h, end)) goto fail; - intercept_dhcpv6_outbound(mcip, ip6h, end); + if (!intercept_dhcpv6_outbound(mcip, ip6h, end)) + goto fail; break; } } @@ -2187,7 +2203,12 @@ mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp) if ((err = mac_protect_validate(mrp)) != 0) return (err); + if (err != 0) + return (err); + mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE); + i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ? + mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS); return (0); } @@ -2261,3 +2282,23 @@ mac_protect_fini(mac_client_impl_t *mcip) mcip->mci_protect_flags = 0; mutex_destroy(&mcip->mci_protect_lock); } + +static boolean_t +allowed_ips_set(mac_resource_props_t *mrp, uint32_t af) +{ + int i; + + for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) { + if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af) + return (B_TRUE); + } + return (B_FALSE); +} + +void +mac_protect_get(mac_handle_t mh, mac_protect_t *mrp) +{ + mac_impl_t *mip = (mac_impl_t *)mh; + + *mrp = mip->mi_resource_props.mrp_protect; +} diff --git a/usr/src/uts/common/net/if.h b/usr/src/uts/common/net/if.h index 68ed7b6078..53d6996961 100644 --- a/usr/src/uts/common/net/if.h +++ b/usr/src/uts/common/net/if.h @@ -1,6 +1,5 @@ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ /* @@ -171,6 +170,7 @@ struct ifnet { #define IFF_VRRP 0x10000000000ll /* Managed by VRRP */ #define IFF_NOLINKLOCAL 0x20000000000ll /* No default linklocal */ +#define IFF_L3PROTECT 0x40000000000ll /* Layer-3 protection enforced */ /* flags that cannot be changed by userland on any interface */ #define IFF_CANTCHANGE \ @@ -178,7 +178,7 @@ struct ifnet { 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_NOLINKLOCAL) + IFF_VRRP | IFF_NOLINKLOCAL | IFF_L3PROTECT) /* flags that cannot be changed by userland on an IPMP interface */ #define IFF_IPMP_CANTCHANGE IFF_FAILED diff --git a/usr/src/uts/common/net/route.h b/usr/src/uts/common/net/route.h index ee31935863..af42382b06 100644 --- a/usr/src/uts/common/net/route.h +++ b/usr/src/uts/common/net/route.h @@ -1,6 +1,5 @@ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * Copyright (c) 1980, 1986, 1993 @@ -132,6 +131,7 @@ struct rtentry { #define RTF_SETSRC 0x20000 /* set default outgoing src address */ #define RTF_INDIRECT 0x40000 /* gateway not directly reachable */ #define RTF_KERNEL 0x80000 /* created by kernel; can't delete */ +#define RTF_ZONE 0x100000 /* (NGZ only) route from global zone */ /* * OLD statistics not used by the kernel. The kernel uses <inet/mib2.h>. diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c index caf10ee8df..bbc91e3fa7 100644 --- a/usr/src/uts/common/os/zone.c +++ b/usr/src/uts/common/os/zone.c @@ -252,6 +252,7 @@ /* List of data link IDs which are accessible from the zone */ typedef struct zone_dl { datalink_id_t zdl_id; + nvlist_t *zdl_net; list_node_t zdl_linkage; } zone_dl_t; @@ -364,6 +365,8 @@ static int zone_shutdown(zoneid_t zoneid); static int zone_add_datalink(zoneid_t, datalink_id_t); static int zone_remove_datalink(zoneid_t, datalink_id_t); static int zone_list_datalink(zoneid_t, int *, datalink_id_t *); +static int zone_set_network(zoneid_t, zone_net_data_t *); +static int zone_get_network(zoneid_t, zone_net_data_t *); typedef boolean_t zsd_applyfn_t(kmutex_t *, boolean_t, zone_t *, zone_key_t); @@ -4719,6 +4722,7 @@ zone_getattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize) boolean_t global = (curzone == global_zone); boolean_t inzone = (curzone->zone_id == zoneid); ushort_t flags; + zone_net_data_t *zbuf; mutex_enter(&zonehash_lock); if ((zone = zone_find_all_by_id(zoneid)) == NULL) { @@ -4973,6 +4977,17 @@ zone_getattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize) error = EFAULT; } break; + case ZONE_ATTR_NETWORK: + zbuf = kmem_alloc(bufsize, KM_SLEEP); + if (copyin(buf, zbuf, bufsize) != 0) { + error = EFAULT; + } else { + error = zone_get_network(zoneid, zbuf); + if (error == 0 && copyout(zbuf, buf, bufsize) != 0) + error = EFAULT; + } + kmem_free(zbuf, bufsize); + break; default: if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone)) { size = bufsize; @@ -4998,6 +5013,7 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize) zone_t *zone; zone_status_t zone_status; int err; + zone_net_data_t *zbuf; if (secpolicy_zone_config(CRED()) != 0) return (set_errno(EPERM)); @@ -5055,6 +5071,19 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize) err = EINVAL; } break; + case ZONE_ATTR_NETWORK: + if (bufsize > (PIPE_BUF + sizeof (zone_net_data_t))) { + err = EINVAL; + goto done; + } + zbuf = kmem_alloc(bufsize, KM_SLEEP); + if (copyin(buf, zbuf, bufsize) != 0) { + err = EFAULT; + goto done; + } + err = zone_set_network(zoneid, zbuf); + kmem_free(zbuf, bufsize); + break; default: if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone)) err = ZBROP(zone)->b_setattr(zone, attr, buf, bufsize); @@ -6328,6 +6357,7 @@ zone_add_datalink(zoneid_t zoneid, datalink_id_t linkid) zdl = kmem_zalloc(sizeof (*zdl), KM_SLEEP); zdl->zdl_id = linkid; + zdl->zdl_net = NULL; mutex_enter(&thiszone->zone_lock); list_insert_head(&thiszone->zone_dl_list, zdl); mutex_exit(&thiszone->zone_lock); @@ -6351,6 +6381,8 @@ zone_remove_datalink(zoneid_t zoneid, datalink_id_t linkid) err = ENXIO; } else { list_remove(&zone->zone_dl_list, zdl); + if (zdl->zdl_net != NULL) + nvlist_free(zdl->zdl_net); kmem_free(zdl, sizeof (zone_dl_t)); } mutex_exit(&zone->zone_lock); @@ -6524,3 +6556,125 @@ zone_datalink_walk(zoneid_t zoneid, int (*cb)(datalink_id_t, void *), kmem_free(idarray, sizeof (datalink_id_t) * idcount); return (ret); } + +static char * +zone_net_type2name(int type) +{ + switch (type) { + case ZONE_NETWORK_ADDRESS: + return (ZONE_NET_ADDRNAME); + case ZONE_NETWORK_DEFROUTER: + return (ZONE_NET_RTRNAME); + default: + return (NULL); + } +} + +static int +zone_set_network(zoneid_t zoneid, zone_net_data_t *znbuf) +{ + zone_t *zone; + zone_dl_t *zdl; + nvlist_t *nvl; + int err = 0; + uint8_t *new = NULL; + char *nvname; + int bufsize; + datalink_id_t linkid = znbuf->zn_linkid; + + if (secpolicy_zone_config(CRED()) != 0) + return (set_errno(EPERM)); + + if (zoneid == GLOBAL_ZONEID) + return (set_errno(EINVAL)); + + nvname = zone_net_type2name(znbuf->zn_type); + bufsize = znbuf->zn_len; + new = znbuf->zn_val; + if (nvname == NULL) + return (set_errno(EINVAL)); + + if ((zone = zone_find_by_id(zoneid)) == NULL) { + return (set_errno(EINVAL)); + } + + mutex_enter(&zone->zone_lock); + if ((zdl = zone_find_dl(zone, linkid)) == NULL) { + err = ENXIO; + goto done; + } + if ((nvl = zdl->zdl_net) == NULL) { + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) { + err = ENOMEM; + goto done; + } else { + zdl->zdl_net = nvl; + } + } + if (nvlist_exists(nvl, nvname)) { + err = EINVAL; + goto done; + } + err = nvlist_add_uint8_array(nvl, nvname, new, bufsize); + ASSERT(err == 0); +done: + mutex_exit(&zone->zone_lock); + zone_rele(zone); + if (err != 0) + return (set_errno(err)); + else + return (0); +} + +static int +zone_get_network(zoneid_t zoneid, zone_net_data_t *znbuf) +{ + zone_t *zone; + zone_dl_t *zdl; + nvlist_t *nvl; + uint8_t *ptr; + uint_t psize; + int err = 0; + char *nvname; + int bufsize; + void *buf; + datalink_id_t linkid = znbuf->zn_linkid; + + if (zoneid == GLOBAL_ZONEID) + return (set_errno(EINVAL)); + + nvname = zone_net_type2name(znbuf->zn_type); + bufsize = znbuf->zn_len; + buf = znbuf->zn_val; + + if (nvname == NULL) + return (set_errno(EINVAL)); + if ((zone = zone_find_by_id(zoneid)) == NULL) + return (set_errno(EINVAL)); + + mutex_enter(&zone->zone_lock); + if ((zdl = zone_find_dl(zone, linkid)) == NULL) { + err = ENXIO; + goto done; + } + if ((nvl = zdl->zdl_net) == NULL || !nvlist_exists(nvl, nvname)) { + err = ENOENT; + goto done; + } + err = nvlist_lookup_uint8_array(nvl, nvname, &ptr, &psize); + ASSERT(err == 0); + + if (psize > bufsize) { + err = ENOBUFS; + goto done; + } + znbuf->zn_len = psize; + bcopy(ptr, buf, psize); +done: + mutex_exit(&zone->zone_lock); + zone_rele(zone); + if (err != 0) + return (set_errno(err)); + else + return (0); +} diff --git a/usr/src/uts/common/sys/dlpi.h b/usr/src/uts/common/sys/dlpi.h index 9613c6491c..72e2c890da 100644 --- a/usr/src/uts/common/sys/dlpi.h +++ b/usr/src/uts/common/sys/dlpi.h @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -407,6 +406,7 @@ typedef struct dl_ipnetinfo { #define DL_NOTE_FASTPATH_FLUSH 0x0200 /* Fast Path info changes */ #define DL_NOTE_CAPAB_RENEG 0x0400 /* Initiate capability renegotiation */ #define DL_NOTE_REPLUMB 0x0800 /* Inform the link to replumb */ +#define DL_NOTE_ALLOWED_IPS 0x1000 /* "allowed-ips" notification */ /* * DLPI notification codes for DL_NOTIFY_CONF primitives. diff --git a/usr/src/uts/common/sys/mac.h b/usr/src/uts/common/sys/mac.h index 224987e220..7288db385c 100644 --- a/usr/src/uts/common/sys/mac.h +++ b/usr/src/uts/common/sys/mac.h @@ -349,6 +349,7 @@ typedef enum { MAC_NOTE_MARGIN, MAC_NOTE_CAPAB_CHG, MAC_NOTE_LOWLINK, + MAC_NOTE_ALLOWED_IPS, MAC_NNOTE /* must be the last entry */ } mac_notify_type_t; diff --git a/usr/src/uts/common/sys/mac_provider.h b/usr/src/uts/common/sys/mac_provider.h index c96d07b594..3dd8024904 100644 --- a/usr/src/uts/common/sys/mac_provider.h +++ b/usr/src/uts/common/sys/mac_provider.h @@ -20,8 +20,7 @@ */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _SYS_MAC_PROVIDER_H @@ -33,6 +32,7 @@ #include <sys/stream.h> #include <sys/mkdev.h> #include <sys/mac.h> +#include <sys/mac_flow.h> /* * MAC Provider Interface @@ -455,6 +455,7 @@ typedef struct mac_register_s { /* * Driver interface functions. */ +extern void mac_protect_get(mac_handle_t, mac_protect_t *); extern void mac_sdu_get(mac_handle_t, uint_t *, uint_t *); extern int mac_maxsdu_update(mac_handle_t, uint_t); diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h index 0a513f8cf7..b89a6902db 100644 --- a/usr/src/uts/common/sys/zone.h +++ b/usr/src/uts/common/sys/zone.h @@ -36,6 +36,8 @@ #include <sys/netstack.h> #include <sys/uadmin.h> #include <sys/ksynch.h> +#include <sys/socket_impl.h> +#include <netinet/in.h> #ifdef __cplusplus extern "C" { @@ -97,6 +99,7 @@ extern "C" { #define ZONE_ATTR_FLAGS 14 #define ZONE_ATTR_HOSTID 15 #define ZONE_ATTR_FS_ALLOWED 16 +#define ZONE_ATTR_NETWORK 17 /* Start of the brand-specific attribute namespace */ #define ZONE_ATTR_BRAND_ATTRS 32768 @@ -279,6 +282,21 @@ typedef struct zone_cmd_rval { /* zone_create flags */ #define ZCF_NET_EXCL 0x1 /* Create a zone with exclusive IP */ +/* zone network properties */ +#define ZONE_NETWORK_ADDRESS 1 +#define ZONE_NETWORK_DEFROUTER 2 + +#define ZONE_NET_ADDRNAME "address" +#define ZONE_NET_RTRNAME "route" + +typedef struct zone_net_data { + int zn_type; + int zn_len; + datalink_id_t zn_linkid; + uint8_t zn_val[1]; +} zone_net_data_t; + + #ifdef _KERNEL /* * We need to protect the definition of 'list_t' from userland applications and |