mac.c revision 605445d5657096e69d948ccb554c9ff024fa34df
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* MAC Services Module
*/
#include <sys/mac_impl.h>
static kmem_cache_t *i_mac_impl_cachep;
static mod_hash_t *i_mac_impl_hash;
#define MACTYPE_KMODDIR "mac"
#define MACTYPE_HASHSZ 67
static mod_hash_t *i_mactype_hash;
static void i_mac_notify_task(void *);
/*
* Private functions.
*/
/*ARGSUSED*/
static int
{
return (0);
}
/*ARGSUSED*/
static void
{
}
static void
{
if (mip->mi_disabled)
goto exit;
goto exit;
}
mip->mi_notify_ref++;
TQ_NOSLEEP) == NULL) {
if (--mip->mi_notify_ref == 0)
}
return;
exit:
}
static void
i_mac_notify_task(void *notify_arg)
{
void *arg;
/*
* Walk the list of notifications.
*/
}
if (--mip->mi_notify_ref == 0)
}
static mactype_t *
i_mactype_getplugin(const char *plugin_name)
{
(mod_hash_val_t *)&mtype) == 0) {
/*
* Because the reference count is initialized at 1 (see
* mactype_register()), we don't need to bump up the
* reference count if we're the first reference.
*/
if (!tried_modload)
return (mtype);
} else if (tried_modload) {
return (NULL);
}
/*
* If the plugin has not yet been loaded, then attempt to load it
* now. If modload succeeds, the plugin should have registered
* using mactype_register(), in which case we can go back and
* attempt to find it again.
*/
goto find_registered_mactype;
}
return (NULL);
}
/*
* Module initialization functions.
*/
void
mac_init(void)
{
i_mac_impl_count = 0;
}
int
mac_fini(void)
{
if (i_mac_impl_count > 0)
return (EBUSY);
return (0);
}
/*
* Client functions.
*/
int
{
char driver[MAXNAMELEN];
int err;
/*
* Check the device name length to make sure it won't overflow our
* buffer.
*/
return (EINVAL);
/*
* Split the device name into driver and instance components.
*/
return (EINVAL);
/*
* Get the major number of the driver.
*/
return (EINVAL);
/*
* Hold the given instance to prevent it from being detached.
* This will also attach the instance if it is not currently attached.
* Currently we ensure that mac_register() (called by the driver's
* attach entry point) and all code paths under it cannot possibly
* call mac_open() because this would lead to a recursive attach
* panic.
*/
return (EINVAL);
/*
* Look up its entry in the global hash table.
*/
(mod_hash_val_t *)&mip);
if (err != 0) {
goto failed;
}
if (mip->mi_disabled) {
goto again;
}
return (0);
return (err);
}
void
{
}
}
const mac_info_t *
{
}
{
}
{
int ret;
/*
* The range of stat determines where it is maintained. Stat
* values from 0 up to (but not including) MAC_STAT_MIN are
* mainteined by the mac module itself. Everything else is
* maintained by the driver.
*/
if (stat < MAC_STAT_MIN) {
/* These stats are maintained by the mac module itself. */
switch (stat) {
case MAC_STAT_LINK_STATE:
return (mip->mi_linkstate);
case MAC_STAT_LINK_UP:
case MAC_STAT_PROMISC:
return (mip->mi_devpromisc != 0);
default:
}
}
/*
* Call the driver to get the given statistic.
*/
if (ret != 0) {
/*
* The driver doesn't support this statistic. Get the
* statistic's default value.
*/
}
return (val);
}
int
{
int err;
/*
* Check whether the device is already started.
*/
/*
* It's already started so there's nothing more to do.
*/
err = 0;
goto done;
}
/*
* Start the device.
*/
done:
return (err);
}
void
{
/*
* Check whether the device is still needed.
*/
/*
* It's still needed so there's nothing more to do.
*/
goto done;
}
/*
* Stop the device.
*/
done:
}
int
{
int err;
/*
* Verify the address.
*/
return (err);
}
/*
* Check whether the given address is already enabled.
*/
0) {
/*
* The address is already enabled so just bump the
* reference count.
*/
p->mma_ref++;
err = 0;
goto done;
}
}
/*
* Allocate a new list entry.
*/
if ((p = kmem_zalloc(sizeof (mac_multicst_addr_t),
KM_NOSLEEP)) == NULL) {
goto done;
}
/*
* Enable a new multicast address.
*/
kmem_free(p, sizeof (mac_multicst_addr_t));
goto done;
}
/*
* Add the address to the list of enabled addresses.
*/
p->mma_ref++;
*pp = p;
done:
return (err);
}
int
{
int err;
/*
* Find the entry in the list for the given address.
*/
0) {
if (--p->mma_ref == 0)
break;
/*
* There is still a reference to this address so
* there's nothing more to do.
*/
err = 0;
goto done;
}
}
/*
* We did not find an entry for the given address so it is not
* currently enabled.
*/
if (p == NULL) {
goto done;
}
/*
* Disable the multicast address.
*/
p->mma_ref++;
goto done;
}
/*
* Remove it from the list.
*/
kmem_free(p, sizeof (mac_multicst_addr_t));
done:
return (err);
}
/*
* mac_unicst_verify: Verifies the passed address. It fails
* if the passed address is a group address or has incorrect length.
*/
{
/*
* Verify the address.
*/
return (B_FALSE);
} else {
return (B_TRUE);
}
}
int
{
int err;
/*
* Verify the address.
*/
return (err);
}
/*
* Program the new unicast address.
*/
/*
* If address doesn't change, do nothing.
* This check is necessary otherwise it may call into mac_unicst_set
* recursively.
*/
err = 0;
goto done;
}
goto done;
/*
* Save the address and flag that we need to send a notification.
*/
done:
if (notify)
return (err);
}
void
{
/*
* Copy out the current unicast source address.
*/
}
void
{
/*
* Copy out the current destination address.
*/
}
int
{
int err = 0;
/*
* Determine whether we should enable or disable promiscuous mode.
* For details on the distinction between "device promiscuous mode"
* and "MAC promiscuous mode", see PSARC/2005/289.
*/
if (on) {
/*
* Enable promiscuous mode on the device if not yet enabled.
*/
if (mip->mi_devpromisc++ == 0) {
if (err != 0) {
mip->mi_devpromisc--;
goto done;
}
}
/*
* Enable promiscuous mode on the MAC if not yet enabled.
*/
} else {
if (mip->mi_devpromisc == 0) {
goto done;
}
/*
* Disable promiscuous mode on the device if this is the last
* enabling.
*/
if (--mip->mi_devpromisc == 0) {
if (err != 0) {
mip->mi_devpromisc++;
goto done;
}
}
/*
* Disable promiscuous mode on the MAC if this is the last
* enabling.
*/
}
done:
return (err);
}
{
/*
* Return the current promiscuity.
*/
if (ptype == MAC_DEVPROMISC)
return (mip->mi_devpromisc != 0);
else
return (mip->mi_promisc != 0);
}
void
{
/*
* If the driver supports resource registration, call the driver to
* ask it to register its resources.
*/
}
void
{
/*
* Call the driver to handle the ioctl. The driver may not support
* any ioctls, in which case we reply with a NAK on its behalf.
*/
else
}
const mac_txinfo_t *
{
/*
* Grab the lock to prevent us from racing with MAC_PROMISC being
* changed. This is sufficient since MAC clients are careful to always
* call mac_txloop_add() prior to enabling MAC_PROMISC, and to disable
* MAC_PROMISC prior to calling mac_txloop_remove().
*/
} else {
/*
* Note that we cannot ASSERT() that mip->mi_mtfp is NULL,
* because to satisfy the above ASSERT(), we have to disable
* MAC_PROMISC prior to calling mac_txloop_remove().
*/
}
return (mtp);
}
{
}
{
/*
* Add it to the head of the 'notify' callback list.
*/
return ((mac_notify_handle_t)mnfp);
}
void
{
mac_notify_fn_t *p;
/*
* Search the 'notify' callback list for the function closure.
*/
if (p == mnfp)
break;
}
/*
* Remove it from the list.
*/
/*
* Free it.
*/
}
void
{
}
{
/*
* Add it to the head of the 'rx' callback list.
*/
return ((mac_rx_handle_t)mrfp);
}
/*
* Unregister a receive function for this mac. This removes the function
* from the list of receive functions for this mac.
*/
void
{
mac_rx_fn_t **pp;
mac_rx_fn_t *p;
/*
* Search the 'rx' callback list for the function closure.
*/
if (p == mrfp)
break;
}
/* Remove it from the list. */
}
{
/*
* Add it to the head of the 'tx' callback list.
*/
return ((mac_txloop_handle_t)mtfp);
}
/*
* Unregister a transmit function for this mac. This removes the function
* from the list of transmit functions for this mac.
*/
void
{
mac_txloop_fn_t *p;
/*
* Search the 'tx' callback list for the function.
*/
if (p == mtfp)
break;
}
/* Remove it from the list. */
}
void
{
/*
* Update the 'resource_add' callbacks.
*/
}
/*
* Driver support functions.
*/
{
/*
* Make sure there isn't a version mismatch between the driver and
* the framework. In the future, if multiple versions are
* supported, this check could become more sophisticated.
*/
if (mac_version != MAC_VERSION)
return (NULL);
return (mregp);
}
void
{
}
/*
* mac_register() is how drivers register new MACs with the GLDv3
* framework. The mregp argument is allocated by drivers using the
* mac_alloc() function, and can be freed using mac_free() immediately upon
* return from mac_register(). Upon success (0 return value), the mhp
* opaque pointer becomes the driver's handle to its MAC interface, and is
* the argument to all other mac module entry points.
*/
int
{
int err;
/* Find the required MAC-Type plugin. */
return (EINVAL);
/* Create a mac_impl_t to represent this MAC. */
/*
* The mac is not ready for open yet.
*/
/*
* Some drivers such as aggr need to register multiple MACs. Such
* drivers must supply a non-zero "instance" argument so that each
* MAC can be assigned a unique MAC name and can have unique
* kstats.
*/
/* Construct the MAC name as <drvname><instance> */
return (EEXIST);
}
goto fail;
}
/*
* If the media supports a broadcast address, cache a pointer to it
* in the mac_info_t so that upper layers can use it.
*/
/*
* Copy the unicast source address into the mac_info_t, but only if
* the MAC-Type defines a non-zero address length. We need to
* handle MAC-Types that have an address length of 0
* (point-to-point protocol MACs for example).
*/
goto fail;
}
/*
* Copy the fixed 'factory' MAC address from the immutable
* info. This is taken to be the MAC address currently in
* use.
*/
/* Copy the destination address if one is provided. */
}
goto fail;
}
/*
* The format of the m_pdata is specific to the plugin. It is
* passed in as an argument to all of the plugin callbacks. The
* driver can update this information by calling
* mac_pdata_update().
*/
/*
* Verify that the plugin supports MAC plugin data and that
* the supplied data is valid.
*/
goto fail;
}
mregp->m_pdata_size)) {
goto fail;
}
}
/*
* Stash the driver callbacks into the mac_impl_t, but first sanity
* check to make sure all mandatory callbacks are set.
*/
goto fail;
}
/*
* Set up the two possible transmit routines.
*/
/*
* Initialize the kstats for this device.
*/
/* Create a style-2 DLPI device */
goto fail;
/* Create a style-1 DLPI device */
DDI_NT_NET, 0) != DDI_SUCCESS)
goto fail;
/*
* Create a link for this MAC. The link name will be the same as
* the MAC name.
*/
if (err != 0)
goto fail;
/* set the gldv3 flag in dn_flags */
/*
* Mark the MAC to be ready for open.
*/
return (0);
fail:
&val);
}
if (style1_created)
if (style2_created)
}
mip->mi_pdata_size = 0;
}
return (err);
}
int
{
int err;
mac_multicst_addr_t *p, *nextp;
/*
* See if there are any other references to this mac_t (e.g., VLAN's).
* If not, set mi_disabled to prevent any new VLAN's from being
* created while we're destroying this mac.
*/
return (EBUSY);
}
/*
* Wait for all taskqs which process the mac notifications to finish.
*/
while (mip->mi_notify_ref != 0)
return (err);
}
/*
* Remove both style 1 and style 2 minor nodes
*/
&val);
ASSERT(i_mac_impl_count > 0);
mip->mi_pdata_size = 0;
/*
* Free the list of multicast addresses.
*/
kmem_free(p, sizeof (mac_multicst_addr_t));
}
return (0);
}
void
{
/*
* Call all registered receive functions.
*/
/* There are no registered receive functions. */
return;
}
do {
/* XXX Do we bump a counter if copymsgchain() fails? */
} else {
}
}
/*
* Transmit function -- ONLY used when there are registered loopback listeners.
*/
mblk_t *
{
goto noresources;
goto noresources;
}
/* XXX counter bump if copymsg() fails? */
else
}
/*
* It's possible we've raced with the disabling of promiscuous
* mode, in which case we can discard our copy.
*/
}
return (NULL);
return (bp);
}
void
{
/*
* Save the link state.
*/
/*
* Send a MAC_NOTE_LINK notification.
*/
}
void
{
return;
/*
* Save the address.
*/
/*
* Send a MAC_NOTE_UNICST notification.
*/
}
void
{
/*
* Send a MAC_NOTE_TX notification.
*/
}
void
{
/*
* Send a MAC_NOTE_RESOURCE notification.
*/
}
{
void *arg;
else
return (mrh);
}
int
{
/*
* Verify that the plugin supports MAC plugin data and that the
* supplied data is valid.
*/
return (EINVAL);
return (EINVAL);
/*
* Since the MAC plugin data is used to construct MAC headers that
* were cached in fast-path headers, we need to flush fast-path
* information for links associated with this mac.
*/
return (0);
}
void
{
/*
* If no specific refresh function was given then default to the
* driver's m_multicst entry point.
*/
}
/*
* Walk the multicast address list and call the refresh function for
* each address.
*/
}
void
{
/*
* If no specific refresh function was given then default to the
* driver's mi_unicst entry point.
*/
}
/*
* Call the refresh function with the current unicast address.
*/
}
void
{
/*
* If no specific refresh function was given then default to the
* driver's m_promisc entry point.
*/
}
/*
* Call the refresh function with the current promiscuity.
*/
}
{
if (mip->mi_activelink) {
return (B_FALSE);
}
return (B_TRUE);
}
void
{
}
/*
* mac_info_get() is used for retrieving the mac_info when a DL_INFO_REQ is
* issued before a DL_ATTACH_REQ. we walk the i_mac_impl_hash table and find
* the first mac_impl_t with a matching driver name; then we copy its mac_info_t
* to the caller. we do all this with i_mac_impl_lock held so the mac_impl_t
* cannot disappear while we are accessing it.
*/
typedef struct i_mac_info_state_s {
const char *mi_name;
/*ARGSUSED*/
static uint_t
{
if (mip->mi_disabled)
return (MH_WALK_CONTINUE);
return (MH_WALK_CONTINUE);
return (MH_WALK_TERMINATE);
}
{
return (B_FALSE);
}
return (B_TRUE);
}
{
else
return (B_FALSE);
}
{
}
mblk_t *
{
}
int
{
mhip));
}
mblk_t *
{
return (NULL);
}
}
return (mp);
}
mblk_t *
{
return (NULL);
}
}
return (mp);
}
void
{
}
void
{
}
/*
* MAC Type Plugin functions.
*/
{
/*
* Make sure there isn't a version mismatch between the plugin and
* the framework. In the future, if multiple versions are
* supported, this check could become more sophisticated.
*/
if (mactype_version != MACTYPE_VERSION)
return (NULL);
return (mtrp);
}
void
{
}
int
{
/* Do some sanity checking before we register this MAC type. */
return (EINVAL);
/*
* Verify that all mandatory callbacks are set in the ops
* vector.
*/
return (EINVAL);
}
mtrp->mtr_addrlen);
}
/*
* A MAC-Type plugin only registers when i_mactype_getplugin() does
* an explicit modload() as a result of a driver requesting to use
* that plugin in mac_register(). We pre-emptively set the initial
* reference count to 1 here to prevent the plugin module from
* unloading before the driver's mac_register() completes. If we
* were to initialize the reference count to 0, then there would be
* a window during which the module could unload before the
* reference count could be bumped up to 1.
*/
return (EEXIST);
}
return (0);
}
int
mactype_unregister(const char *ident)
{
int err;
/*
* Let's not allow MAC drivers to use this plugin while we're
* trying to unregister it...
*/
(mod_hash_val_t *)&mtp)) != 0) {
/* A plugin is trying to unregister, but it never registered. */
return (ENXIO);
}
return (EBUSY);
}
if (err != 0) {
/* This should never happen, thus the ASSERT() above. */
return (EINVAL);
}
return (0);
}