dls_mgmt.c revision 3ade6e843b7f9e2656892a172ecd9e302b0dee09
/*
* 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
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Datalink management routines.
*/
#include <sys/dls_impl.h>
#include <sys/netstack.h>
/*
* This vanity name management module is treated as part of the GLD framework
* and we don't hold any GLD framework lock across a call to any mac
* function that needs to acquire the mac perimeter. The hierarchy is
* mac perimeter -> framework locks
*/
typedef struct dls_stack {
} dls_stack_t;
static kmem_cache_t *i_dls_devnet_cachep;
static kmutex_t i_dls_mgmt_lock;
static krwlock_t i_dls_devnet_lock;
static mod_hash_t *i_dls_devnet_id_hash;
static mod_hash_t *i_dls_devnet_hash;
/*
* The following macros take a link name without the trailing PPA as input.
* implicitly created in dls_devnet_hold_by_name() for backward compatibility
* with Solaris 10 and prior.
*/
#define IS_IPTUN_LINK(name) ( \
/* Upcall door handle */
#define DD_CONDEMNED 0x1
#define DD_KSTAT_CHANGING 0x2
/*
* This structure is used to keep the <linkid, macname> mapping.
* This structure itself is not protected by the mac perimeter, but is
* protected by the dd_mutex and i_dls_devnet_lock. Thus most of the
* functions manipulating this structure such as dls_devnet_set/unset etc.
* may be called while not holding the mac perimeter.
*/
typedef struct dls_devnet_s {
char dd_linkname[MAXLINKNAMELEN];
char dd_mac[MAXNAMELEN];
} dls_devnet_t;
static int i_dls_devnet_create_iptun(const char *, const char *,
datalink_id_t *);
static int i_dls_devnet_destroy_iptun(datalink_id_t);
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static void
{
}
/* ARGSUSED */
static int
{
}
return (0);
}
/* ARGSUSED */
static void *
{
return (dlss);
}
/* ARGSUSED */
static void
{
/* Move remaining datalinks in this zone back to the global zone. */
}
/* ARGSUSED */
static void
{
}
/*
* Module initialization and finalization functions.
*/
void
dls_mgmt_init(void)
{
/*
* Create a kmem_cache of dls_devnet_t structures.
*/
sizeof (dls_devnet_t), 0, i_dls_devnet_constructor,
/*
* Create a hash table, keyed by dd_linkid, of dls_devnet_t.
*/
/*
* Create a hash table, keyed by dd_mac
*/
}
void
dls_mgmt_fini(void)
{
}
int
{
int err;
/* handle daemon restart */
if (dls_mgmt_dh != NULL) {
dls_mgmt_dh = NULL;
}
return (err);
}
/*
* Create and associate <link name, linkid> mapping for network devices
* which are already attached before the daemon is started.
*/
if (start)
return (0);
}
static boolean_t
{
extern int sys_shutdown;
if (sys_shutdown) {
return (B_TRUE);
}
return (B_TRUE);
}
/*
* Upcall to the datalink management daemon (dlmgmtd).
*/
static int
{
int err;
int retry = 0;
#define MAXRETRYNUM 3
dh = dls_mgmt_dh;
return (EBADF);
}
for (;;) {
retry++;
SIZE_MAX, 0)) == 0)
break;
/*
* handle door call errors
*/
switch (err) {
case EINTR:
/*
* If the operation which caused this door upcall gets
* interrupted, return directly.
*/
goto done;
case EAGAIN:
/*
* Repeat upcall if the maximum attempt limit has not
* been reached.
*/
if (retry < MAXRETRYNUM) {
break;
}
goto done;
default:
/* A fatal door error */
if (i_dls_mgmt_door_revoked(dh)) {
"dls: dlmgmtd door service revoked\n");
if (retry < MAXRETRYNUM) {
goto retry;
}
}
goto done;
}
}
/*
* The size of the input rbuf was not big enough, so the
* upcall allocated the rbuf itself. If this happens, assume
* that this was an invalid door call request.
*/
goto done;
}
goto done;
}
done:
return (err);
}
/*
* Request the datalink management daemon to create a link with the attributes
* below. Upon success, zero is returned and linkidp contains the linkid for
* the new link; otherwise, an errno is returned.
*
* - dev physical dev_t. required for all physical links,
* including GLDv3 links. It will be used to force the
* attachment of a physical device, hence the
* registration of its mac
* - class datalink class
* - media type media type; DL_OTHER means unknown
* - persist whether to persist the datalink
*/
int
{
int err;
sizeof (create.ld_devname))
return (EINVAL);
sizeof (retval))) == 0) {
}
return (err);
}
/*
* Request the datalink management daemon to destroy the specified link.
* Returns zero upon success, or an errno upon failure.
*/
int
{
}
/*
* for a physical link. Upon success, get its linkid.
*
* - media type media type
* - novanity whether this physical datalink supports vanity naming.
* physical links that do not use the GLDv3 MAC plugin
* cannot suport vanity naming
*
* This function could fail with ENOENT or EEXIST. Two cases return EEXIST:
*
* 1. A link with devname already exists, but the media type does not match.
* In this case, mediap will bee set to the media type of the existing link.
* 2. A link with devname already exists, but its link name does not match
* the device name, although this link does not support vanity naming.
*/
int
{
int err;
sizeof (update.ld_devname))
return (EINVAL);
} else if (err == 0) {
}
return (err);
}
/*
* Request the datalink management daemon to get the information for a link.
* Returns zero upon success, or an errno upon failure.
*
* Only fills in information for argument pointers that are non-NULL.
* Note that the link argument is expected to be MAXLINKNAMELEN bytes.
*/
int
{
sizeof (retval))) != 0) {
return (err);
}
return (EINVAL);
return (0);
}
/*
* Request the datalink management daemon to get the linkid for a link.
* Returns a non-zero error code on failure. The linkid argument is only
* set on success (when zero is returned.)
*/
int
{
int err;
sizeof (retval))) == 0) {
}
return (err);
}
{
sizeof (retval)) != 0) {
return (DATALINK_INVALID_LINKID);
}
}
static int
{
int err;
sizeof (retval))) == 0) {
return (EINVAL);
}
return (err);
}
/*
* Note that this function can only get devp successfully for non-VLAN link.
*/
int
{
return (EINVAL);
}
return (0);
}
/*
* Request the datalink management daemon to push in
* all properties associated with the link.
* Returns a non-zero error code on failure.
*/
int
{
int err;
return (err);
}
static void
dls_devnet_prop_task(void *arg)
{
}
/*
* Ensure property loading task is completed.
*/
void
{
}
void
{
}
int
dls_link_t **dlpp)
{
int err;
return (err);
return (err);
}
return (0);
}
void
{
}
/*
* "link" kstats related functions.
*/
/*
* Query the "link" kstats.
*
* We may be called from the kstat subsystem in an arbitrary context.
* If the caller is the stack, the context could be an upcall data
* thread. Hence we can't acquire the mac perimeter in this function
* for fear of deadlock.
*/
static int
{
int err;
/*
* Check the link is being renamed or if the link is going away
* before incrementing dd_tref which in turn prevents the link
* from being renamed or deleted until we finish.
*/
return (ENOENT);
}
/*
* If a device detach happens at this time, it will block in
* dls_devnet_unset since the dd_tref has been bumped up above. So the
* access to 'dlp' is safe even though we don't hold the mac perimeter.
*/
(mod_hash_val_t *)&dlp) != 0) {
return (ENOENT);
}
return (err);
}
/*
* Create the "link" kstats.
*/
static void
{
} else {
}
}
}
/*
* Destroy the "link" kstats.
*/
static void
{
}
} else {
}
}
}
/*
* The link has been renamed. Destroy the old non-legacy kstats ("link kstats")
* and create the new set using the new name.
*/
static void
{
}
/* We can't rename a link while it's assigned to a non-global zone. */
}
/*
* Associate a linkid with a given link (identified by macname)
*/
static int
dls_devnet_t **ddpp)
{
int err;
char linkname[MAXLINKNAMELEN];
/*
* Don't allow callers to set a link name with a linkid that already
* has a name association (that's what rename is for).
*/
if (linkid != DATALINK_INVALID_LINKID) {
(mod_hash_val_t *)&ddp) == 0) {
goto done;
}
goto done;
}
goto done;
}
/*
* This might be a physical link that has already
* been created, but which does not have a linkid
* because dlmgmtd was not running when it was created.
*/
if (linkid == DATALINK_INVALID_LINKID ||
class != DATALINK_CLASS_PHYS) {
goto done;
}
} else {
}
if (linkid != DATALINK_INVALID_LINKID) {
sizeof (ddp->dd_linkname));
(mod_hash_val_t)ddp) == 0);
}
}
err = 0;
done:
/*
* It is safe to drop the i_dls_devnet_lock at this point. In the case
* of physical devices, the softmac framework will fail the device
* detach based on the smac_state or smac_hold_cnt. Other cases like
* vnic and aggr use their own scheme to serialize creates and deletes
* and ensure that *ddp is valid.
*/
if (err == 0) {
if (zoneid != GLOBAL_ZONEID &&
/*
* The kstat subsystem holds its own locks (rather perimeter)
* before calling the ks_update (dls_devnet_stat_update) entry
* point which in turn grabs the i_dls_devnet_lock. So the
* lock hierarchy is kstat locks -> i_dls_devnet_lock.
*/
if (stat_create)
}
return (err);
}
/*
* Disassociate a linkid with a given link (identified by macname)
* This waits until temporary references to the dls_devnet_t are gone.
*/
static int
{
int err;
return (ENOENT);
}
/*
* Make sure downcalls into softmac_create or softmac_destroy from
* devfs don't cv_wait on any devfs related condition for fear of
* deadlock. Return EBUSY if the asynchronous thread started for
* property loading as part of the post attach hasn't yet completed.
*/
return (EBUSY);
}
/*
* Remove this dls_devnet_t from the hash table.
*/
}
if (wait) {
/*
* Wait until all temporary references are released.
*/
} else {
}
return (0);
}
static int
{
int err;
/*
* Hold this link to prevent it being detached in case of a
* physical link.
*/
return (ENOENT);
}
return (ENOENT);
}
if (tmp_hold)
else
return (0);
}
int
{
}
/*
* Hold the vanity naming structure (dls_devnet_t) temporarily. The request to
* delete the dls_devnet_t will wait until the temporary reference is released.
*/
int
{
}
/*
* This funtion is called when a DLS client tries to open a device node.
* devnet_create_rvp->dls_devnet_open()) or a direct /dev node access.
* In both cases, this function bumps up the reference count of the
* dls_devnet_t structure. The reference is held as long as the device node
* is held when the devnet_create_rvp->dls_devnet_open call happens, this
* initial reference is released immediately in devnet_inactive_callback ->
* dls_devnet_close(). (Note that devnet_inactive_callback() is called right
* To undo this function, call dls_devnet_rele()
*/
int
{
char name[MAXNAMELEN];
char *drv;
int err;
return (EINVAL);
/*
* Hold this link to prevent it being detached in case of a
* GLDv3 physical link.
*/
return (ENOENT);
}
return (ENOENT);
}
return (0);
}
void
{
return;
}
}
static int
{
char drv[MAXLINKNAMELEN];
int err;
/*
* If we failed to get the link's linkid because the dlmgmtd daemon
* has not been started, return ENOENT so that the application can
* fallback to open the /dev node.
*/
return (ENOENT);
return (err);
return (ENOENT);
if (IS_IPTUN_LINK(drv)) {
return (err);
/*
* At this point, an IP tunnel MAC has registered, which
* resulted in a link being created.
*/
if (err != 0) {
return (err);
}
/*
* dls_devnet_rele() will know to destroy the implicit IP
* tunnel on last reference release if DD_IMPLICIT_IPTUN is
* set.
*/
return (0);
}
/*
* If this link:
* (a) is a physical device, (b) this is the first boot, (c) the MAC
* is not registered yet, and (d) we cannot find its linkid, then the
* linkname is the same as the devname.
*
* First filter out invalid names.
*/
return (ENOENT);
return (ENOENT);
/*
* At this time, the MAC should be registered, check its phy_dev using
* the given name.
*/
return (err);
}
return (ENOENT);
}
return (err);
}
int
{
(mod_hash_val_t *)&ddp) != 0) {
return (ENOENT);
}
return (0);
}
/*
* Get linkid for the given dev.
*/
int
{
char macname[MAXNAMELEN];
char *drv;
return (EINVAL);
}
/*
* Get the link's physical dev_t. It this is a VLAN, get the dev_t of the
* link this VLAN is created on.
*/
int
{
int err;
return (err);
return (err);
}
/*
* Handle the renaming requests. There are two rename cases:
*
* 1. Request to rename a valid link (id1) to an non-existent link name
* (id2). In this case id2 is DATALINK_INVALID_LINKID. Just check whether
* id1 is held by any applications.
*
* In this case, the link's kstats need to be updated using the given name.
*
* 2. Request to rename a valid link (id1) to the name of a REMOVED
* physical link (id2). In this case, check that id1 and its associated
* mac is not held by any application, and update the link's linkid to id2.
*
* This case does not change the <link name, linkid> mapping, so the link's
* kstats need to be updated with using name associated the given id2.
*/
int
{
int err = 0;
/*
* In the second case, id2 must be a REMOVED physical link.
*/
if ((id2 != DATALINK_INVALID_LINKID) &&
return (EEXIST);
}
/*
* Hold id1 to prevent it from being detached (if a physical link).
*/
/*
* The framework does not hold hold locks across calls to the
* mac perimeter, hence enter the perimeter first. This also waits
* for the property loading to finish.
*/
return (err);
}
goto done;
}
/*
* Return EBUSY if any applications have this link open, if any thread
* is currently accessing the link kstats, or if the link is on-loan
* to a non-global zone. Then set the DD_KSTAT_CHANGING flag to
* prevent any access to the kstats while we delete and recreate
* kstats below.
*/
goto done;
}
if (id2 == DATALINK_INVALID_LINKID) {
sizeof (ddp->dd_linkname));
/* rename mac client name and its flow if exists */
goto done;
goto done;
}
/*
* The second case, check whether the MAC is used by any MAC
* user. This must be a physical link so ddh must not be NULL.
*/
goto done;
}
goto done;
/*
* We release the reference of the MAC which mac_open() is
* holding. Note that this mac will not be unregistered
* because the physical device is held.
*/
/*
* Check if there is any other MAC clients, if not, hold this mac
* exclusively until we are done.
*/
goto done;
/*
* Update the link's linkid.
*/
goto done;
}
if (err != 0) {
goto done;
}
(void) mod_hash_remove(i_dls_devnet_id_hash,
(void) mod_hash_insert(i_dls_devnet_id_hash,
/* load properties for new id */
done:
/*
* Change the name of the kstat based on the new link name.
* We can't hold the i_dls_devnet_lock across calls to the kstat
* subsystem. Instead the DD_KSTAT_CHANGING flag set above in this
* function prevents any access to the dd_ksp while we delete and
* recreate it below.
*/
if (err == 0)
if (clear_dd_flag) {
}
return (err);
}
static int
{
int err;
if (old_zoneid == new_zoneid)
return (0);
return (err);
/*
* When changing the zoneid of an existing link, we need to tell
* dlmgmtd about it. dlmgmtd already knows the zoneid associated with
* newly created links.
*/
if (setprop) {
sizeof (retval));
if (err != 0)
goto done;
}
}
done:
if (err != 0 && upcall_done) {
sizeof (retval));
}
return (err);
}
int
{
int err;
return (0);
/*
* Acquire an additional reference to the link if it is being assigned
* to a non-global zone from the global zone.
*/
return (err);
}
if (refheld)
return (err);
}
/*
* Release the additional reference if the link is returning to the
* global zone from a non-global zone.
*/
/* Re-create kstats in the appropriate zones. */
if (old_zid != GLOBAL_ZONEID)
if (new_zid != GLOBAL_ZONEID)
return (0);
}
{
}
{
}
/*
* Is linkid visible from zoneid? A link is visible if it was created in the
* zone, or if it is currently assigned to the zone.
*/
{
return (B_FALSE);
return (result);
}
/*
* Access a vanity naming node.
*/
int
{
int err;
return (err);
/*
* Opening a link that does not belong to the current non-global zone
* is not allowed.
*/
return (ENOENT);
}
if (err != 0) {
return (err);
}
if (err != 0) {
return (err);
}
return (0);
}
/*
* Close access to a vanity naming node.
*/
void
{
/*
* One rele for the hold placed in dls_devnet_open, another for
* the hold done just above
*/
}
/*
* critical and no protection is needed.
*/
{
return (updated);
}
int
{
int err;
/*
* Holding the mac perimeter ensures that the downcall from the
* dlmgmt daemon which does the property loading does not proceed
* until we relinquish the perimeter.
*/
/*
* Make this association before we call dls_link_hold_create as
* we need to use the linkid to get the user name for the link
* when we create the MAC client.
*/
return (err);
}
}
/*
* Tell BPF it is here, if BPF is there
*/
if (dls_bpfattach_fn != NULL) {
/*
* The zoneid is passed in explicitly to prevent the need to
* do a lookup in dls using the linkid. Such a lookup would need
* to use the same hash table that gets used for walking when
* dls_set_bpfattach() is called.
*/
}
return (err);
}
/*
* Set the linkid of the dls_devnet_t and add it into the i_dls_devnet_id_hash.
* This is called in the case that the dlmgmtd daemon is started later than
* the physical devices get attached, and the linkid is only known after the
* daemon starts.
*/
int
{
}
int
{
int err;
return (err);
/*
* Tell BPF that the link is going away, if BPF is there.
*/
if (dls_bpfdetach_fn != NULL)
if (err != 0) {
/*
* XXX It is a general GLDv3 bug that dls_devnet_set() has to
* be called to re-set the link when destroy fails. The
* zoneid below will be incorrect if this function is ever
* called from kernel context or from a zone other than that
* which initially created the link.
*/
NULL);
}
return (err);
}
/*
* Implicitly create an IP tunnel link.
*/
static int
{
int err;
netstack_t *ns;
/* First ensure that the iptun device is attached. */
return (EINVAL);
return (EINVAL);
if (IS_IPV4_TUN(drvname)) {
} else if (IS_6TO4_TUN(drvname)) {
} else if (IS_IPV6_TUN(drvname)) {
}
/* Obtain a datalink id for this tunnel. */
if (err != 0) {
return (err);
}
ns = netstack_get_current();
if (err != 0)
else
return (err);
}
static int
{
int err;
/*
* Note the use of zone_kcred() here as opposed to CRED(). This is
* may not have necessary privileges to delete this IP tunnel, but the
* tunnel must always be implicitly deleted on last close.
*/
return (err);
}
const char *
{
}
{
}
/*ARGSUSED*/
static uint_t
{
return (MH_WALK_CONTINUE);
}
/*
* Set the functions to call back to when adding or removing a mac so that
* BPF can keep its internal list of these up to date.
*/
void
{
/*
* If we're setting a new attach function, call it for every
* mac that has already been attached.
*/
}
}