xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_door.c (revision 0617d817)
1d62bc4baSyz /*
2d62bc4baSyz  * CDDL HEADER START
3d62bc4baSyz  *
4d62bc4baSyz  * The contents of this file are subject to the terms of the
5d62bc4baSyz  * Common Development and Distribution License (the "License").
6d62bc4baSyz  * You may not use this file except in compliance with the License.
7d62bc4baSyz  *
8d62bc4baSyz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz  * See the License for the specific language governing permissions
11d62bc4baSyz  * and limitations under the License.
12d62bc4baSyz  *
13d62bc4baSyz  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz  *
19d62bc4baSyz  * CDDL HEADER END
20d62bc4baSyz  */
22d62bc4baSyz /*
2332715170SCathy Zhou  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24d62bc4baSyz  */
26d62bc4baSyz /*
27d62bc4baSyz  * Main door handler functions used by dlmgmtd to process the different door
28d62bc4baSyz  * call requests. Door call requests can come from the user-land applications,
29024b0a25Sseb  * or from the kernel.
302b24ab6bSSebastien Roy  *
312b24ab6bSSebastien Roy  * Note on zones handling:
322b24ab6bSSebastien Roy  *
332b24ab6bSSebastien Roy  * There are two zoneid's associated with a link.  One is the zoneid of the
342b24ab6bSSebastien Roy  * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and
352b24ab6bSSebastien Roy  * the other is the zoneid of the zone where the link is currently assigned
362b24ab6bSSebastien Roy  * (the "zone" link property).  The two can be different if a datalink is
372b24ab6bSSebastien Roy  * created in the global zone and subsequently assigned to a non-global zone
382b24ab6bSSebastien Roy  * via zonecfg or via explicitly setting the "zone" link property.
392b24ab6bSSebastien Roy  *
402b24ab6bSSebastien Roy  * Door clients can see links that were created in their zone, and links that
412b24ab6bSSebastien Roy  * are currently assigned to their zone.  Door clients in a zone can only
422b24ab6bSSebastien Roy  * modify links that were created in their zone.
432b24ab6bSSebastien Roy  *
442b24ab6bSSebastien Roy  * The datalink ID space is global, while each zone has its own datalink name
452b24ab6bSSebastien Roy  * space.  This allows each zone to have complete freedom over the names that
462b24ab6bSSebastien Roy  * they assign to links created within the zone.
47d62bc4baSyz  */
49d62bc4baSyz #include <assert.h>
50d62bc4baSyz #include <alloca.h>
51024b0a25Sseb #include <errno.h>
52024b0a25Sseb #include <priv_utils.h>
53024b0a25Sseb #include <stdlib.h>
54d62bc4baSyz #include <strings.h>
550dc974a9SCathy Zhou #include <syslog.h>
560dc974a9SCathy Zhou #include <sys/sysevent/eventdefs.h>
572b24ab6bSSebastien Roy #include <zone.h>
580dc974a9SCathy Zhou #include <libsysevent.h>
59d62bc4baSyz #include <libdlmgmt.h>
600dc974a9SCathy Zhou #include <librcm.h>
61d62bc4baSyz #include "dlmgmt_impl.h"
6332715170SCathy Zhou typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
6432715170SCathy Zhou     ucred_t *);
66024b0a25Sseb typedef struct dlmgmt_door_info_s {
67024b0a25Sseb 	uint_t			di_cmd;
68024b0a25Sseb 	size_t			di_reqsz;
69024b0a25Sseb 	size_t			di_acksz;
70024b0a25Sseb 	dlmgmt_door_handler_t	*di_handler;
71024b0a25Sseb } dlmgmt_door_info_t;
732b24ab6bSSebastien Roy /*
742b24ab6bSSebastien Roy  * Check if the caller has the required privileges to operate on a link of the
752b24ab6bSSebastien Roy  * given class.
762b24ab6bSSebastien Roy  */
772b24ab6bSSebastien Roy static int
dlmgmt_checkprivs(datalink_class_t class,ucred_t * cred)782b24ab6bSSebastien Roy dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
792b24ab6bSSebastien Roy {
802b24ab6bSSebastien Roy 	const priv_set_t *eset;
812b24ab6bSSebastien Roy 
822b24ab6bSSebastien Roy 	eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
832b24ab6bSSebastien Roy 	if (eset != NULL && ((class == DATALINK_CLASS_IPTUN &&
842b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_IPTUN_CONFIG)) ||
852b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_DL_CONFIG) ||
862b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_NET_CONFIG)))
872b24ab6bSSebastien Roy 		return (0);
882b24ab6bSSebastien Roy 	return (EACCES);
892b24ab6bSSebastien Roy }
91d62bc4baSyz static dlmgmt_link_t *
dlmgmt_getlink_by_dev(char * devname,zoneid_t zoneid)922b24ab6bSSebastien Roy dlmgmt_getlink_by_dev(char *devname, zoneid_t zoneid)
93d62bc4baSyz {
94d62bc4baSyz 	dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl);
96d62bc4baSyz 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
972b24ab6bSSebastien Roy 		if (link_is_visible(linkp, zoneid) &&
982b24ab6bSSebastien Roy 		    (linkp->ll_class == DATALINK_CLASS_PHYS) &&
99d62bc4baSyz 		    linkattr_equal(&(linkp->ll_head), FDEVNAME, devname,
100d62bc4baSyz 		    strlen(devname) + 1)) {
101d62bc4baSyz 			return (linkp);
102d62bc4baSyz 		}
103d62bc4baSyz 	}
104d62bc4baSyz 	return (NULL);
105d62bc4baSyz }
1070dc974a9SCathy Zhou /*
1080dc974a9SCathy Zhou  * Post the EC_DATALINK sysevent for the given linkid. This sysevent will
1090dc974a9SCathy Zhou  * be consumed by the datalink sysevent module.
1100dc974a9SCathy Zhou  */
1110dc974a9SCathy Zhou static void
dlmgmt_post_sysevent(const char * subclass,datalink_id_t linkid,boolean_t reconfigured)1125093e103SCathy Zhou dlmgmt_post_sysevent(const char *subclass, datalink_id_t linkid,
1135093e103SCathy Zhou     boolean_t reconfigured)
1140dc974a9SCathy Zhou {
1150dc974a9SCathy Zhou 	nvlist_t	*nvl = NULL;
1160dc974a9SCathy Zhou 	sysevent_id_t	eid;
1170dc974a9SCathy Zhou 	int		err;
1180dc974a9SCathy Zhou 
1190dc974a9SCathy Zhou 	if (((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) ||
1205093e103SCathy Zhou 	    ((err = nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid)) != 0) ||
1215093e103SCathy Zhou 	    ((err = nvlist_add_boolean_value(nvl, RCM_NV_RECONFIGURED,
1225093e103SCathy Zhou 	    reconfigured)) != 0)) {
1230dc974a9SCathy Zhou 		goto done;
1240dc974a9SCathy Zhou 	}
1250dc974a9SCathy Zhou 
1260dc974a9SCathy Zhou 	if (sysevent_post_event(EC_DATALINK, (char *)subclass, SUNW_VENDOR,
1270dc974a9SCathy Zhou 	    (char *)progname, nvl, &eid) == -1) {
1280dc974a9SCathy Zhou 		err = errno;
1290dc974a9SCathy Zhou 	}
1300dc974a9SCathy Zhou 
1310dc974a9SCathy Zhou done:
1320dc974a9SCathy Zhou 	if (err != 0) {
1330dc974a9SCathy Zhou 		dlmgmt_log(LOG_WARNING, "dlmgmt_post_sysevent(%d) failed: %s",
1340dc974a9SCathy Zhou 		    linkid, strerror(err));
1350dc974a9SCathy Zhou 	}
1360dc974a9SCathy Zhou 	nvlist_free(nvl);
1370dc974a9SCathy Zhou }
1380dc974a9SCathy Zhou 
13932715170SCathy Zhou /* ARGSUSED */
140d62bc4baSyz static void
dlmgmt_upcall_create(void * argp,void * retp,size_t * sz,zoneid_t zoneid,ucred_t * cred)14132715170SCathy Zhou dlmgmt_upcall_create(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
14232715170SCathy Zhou     ucred_t *cred)
143d62bc4baSyz {
144024b0a25Sseb 	dlmgmt_upcall_arg_create_t *create = argp;
145024b0a25Sseb 	dlmgmt_create_retval_t	*retvalp = retp;
146d62bc4baSyz 	datalink_class_t	class;
147d62bc4baSyz 	uint32_t		media;
148d62bc4baSyz 	dlmgmt_link_t		*linkp;
149d62bc4baSyz 	char			link[MAXLINKNAMELEN];
150d62bc4baSyz 	uint32_t		flags;
1518a1c9a22SCathy Zhou 	int			err = 0;
152d62bc4baSyz 	boolean_t		created = B_FALSE;
1535093e103SCathy Zhou 	boolean_t		reconfigured = B_FALSE;
155d62bc4baSyz 	/*
156d62bc4baSyz 	 * Determine whether this link is persistent. Note that this request
157d62bc4baSyz 	 * is coming from kernel so this link must be active.
158d62bc4baSyz 	 */
159d62bc4baSyz 	flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0);
161d62bc4baSyz 	class = create->ld_class;
162d62bc4baSyz 	media = create->ld_media;
164d62bc4baSyz 	/*
165d62bc4baSyz 	 * Hold the writer lock to update the link table.
166d62bc4baSyz 	 */
167d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
1692b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(class, cred)) != 0)
1702b24ab6bSSebastien Roy 		goto done;
1712b24ab6bSSebastien Roy 
172d62bc4baSyz 	/*
173d62bc4baSyz 	 * Check to see whether this is the reattachment of an existing
174d62bc4baSyz 	 * physical link. If so, return its linkid.
175d62bc4baSyz 	 */
1762b24ab6bSSebastien Roy 	if ((class == DATALINK_CLASS_PHYS) && (linkp =
1772b24ab6bSSebastien Roy 	    dlmgmt_getlink_by_dev(create->ld_devname, zoneid)) != NULL) {
1788a1c9a22SCathy Zhou 		if (linkattr_equal(&(linkp->ll_head), FPHYMAJ,
1798a1c9a22SCathy Zhou 		    &create->ld_phymaj, sizeof (uint64_t)) &&
1808a1c9a22SCathy Zhou 		    linkattr_equal(&(linkp->ll_head), FPHYINST,
1818a1c9a22SCathy Zhou 		    &create->ld_phyinst, sizeof (uint64_t)) &&
1828a1c9a22SCathy Zhou 		    (linkp->ll_flags & flags) == flags) {
1838a1c9a22SCathy Zhou 			/*
1848a1c9a22SCathy Zhou 			 * If nothing has been changed, directly return.
1858a1c9a22SCathy Zhou 			 */
1868a1c9a22SCathy Zhou 			goto noupdate;
1878a1c9a22SCathy Zhou 		}
1888a1c9a22SCathy Zhou 
189d62bc4baSyz 		err = linkattr_set(&(linkp->ll_head), FPHYMAJ,
190d62bc4baSyz 		    &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64);
191d62bc4baSyz 		if (err != 0)
192d62bc4baSyz 			goto done;
194d62bc4baSyz 		err = linkattr_set(&(linkp->ll_head), FPHYINST,
195d62bc4baSyz 		    &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64);
196d62bc4baSyz 		if (err != 0)
197d62bc4baSyz 			goto done;
1995093e103SCathy Zhou 		/*
2005093e103SCathy Zhou 		 * This is a device that is dynamic reconfigured.
2015093e103SCathy Zhou 		 */
2025093e103SCathy Zhou 		if ((linkp->ll_flags & DLMGMT_ACTIVE) == 0)
2035093e103SCathy Zhou 			reconfigured = B_TRUE;
2045093e103SCathy Zhou 
2052b24ab6bSSebastien Roy 		if ((err = link_activate(linkp)) != 0)
2062b24ab6bSSebastien Roy 			goto done;
207d62bc4baSyz 		linkp->ll_flags |= flags;
208d62bc4baSyz 		linkp->ll_gen++;
2095093e103SCathy Zhou 
210d62bc4baSyz 		goto done;
211d62bc4baSyz 	}
213d62bc4baSyz 	if ((err = dlmgmt_create_common(create->ld_devname, class, media,
2142b24ab6bSSebastien Roy 	    zoneid, flags, &linkp)) == EEXIST) {
215d62bc4baSyz 		/*
216d62bc4baSyz 		 * The link name already exists. Return error if this is a
217d62bc4baSyz 		 * non-physical link (in that case, the link name must be
218d62bc4baSyz 		 * the same as the given name).
219d62bc4baSyz 		 */
220d62bc4baSyz 		if (class != DATALINK_CLASS_PHYS)
221d62bc4baSyz 			goto done;
223d62bc4baSyz 		/*
224d62bc4baSyz 		 * The physical link's name already exists, request
225d62bc4baSyz 		 * a suggested link name: net<nextppa>
226d62bc4baSyz 		 */
2272b24ab6bSSebastien Roy 		err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN, zoneid);
228d62bc4baSyz 		if (err != 0)
229d62bc4baSyz 			goto done;
2312b24ab6bSSebastien Roy 		err = dlmgmt_create_common(link, class, media, zoneid, flags,
2322b24ab6bSSebastien Roy 		    &linkp);
233d62bc4baSyz 	}
235d62bc4baSyz 	if (err != 0)
236d62bc4baSyz 		goto done;
238d62bc4baSyz 	created = B_TRUE;
240d62bc4baSyz 	/*
241d62bc4baSyz 	 * This is a new link.  Only need to persist link attributes for
242d62bc4baSyz 	 * physical links.
243d62bc4baSyz 	 */
244d62bc4baSyz 	if (class == DATALINK_CLASS_PHYS &&
245d62bc4baSyz 	    (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname,
246d62bc4baSyz 	    strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) ||
247d62bc4baSyz 	    ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj,
248d62bc4baSyz 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) ||
249d62bc4baSyz 	    ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst,
250d62bc4baSyz 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) {
251d62bc4baSyz 		(void) dlmgmt_destroy_common(linkp, flags);
252d62bc4baSyz 	}
254d62bc4baSyz done:
2552b24ab6bSSebastien Roy 	if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_link, linkp,
256d62bc4baSyz 	    linkp->ll_flags)) != 0) && created) {