diff options
Diffstat (limited to 'usr/src/cmd')
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile | 2 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c | 56 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h | 3 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c | 173 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c | 201 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.bin/netstat/netstat.c | 17 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c | 3 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm.c | 46 | ||||
-rw-r--r-- | usr/src/cmd/cmd-inet/usr.sbin/route.c | 6 | ||||
-rw-r--r-- | usr/src/cmd/svc/milestone/net-physical | 28 | ||||
-rw-r--r-- | usr/src/cmd/svc/shell/net_include.sh | 40 | ||||
-rw-r--r-- | usr/src/cmd/zoneadmd/Makefile | 8 | ||||
-rw-r--r-- | usr/src/cmd/zoneadmd/vplat.c | 487 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg.c | 202 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg.h | 3 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg_grammar.y | 4 | ||||
-rw-r--r-- | usr/src/cmd/zonecfg/zonecfg_lex.l | 3 |
17 files changed, 1189 insertions, 93 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; } |