summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/zone.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/zone.c')
-rw-r--r--usr/src/uts/common/os/zone.c154
1 files changed, 154 insertions, 0 deletions
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);
+}