/*
* 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.
* Copyright 2012, Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright (c) 2013 Joyent, Inc. All rights reserved.
*/
/*
* Data-Link Services Module
*/
#include <sys/dld_impl.h>
#include <sys/mac_client_priv.h>
int
{
int err;
/*
* Check whether this client belongs to the zone of this dlp. Note that
* a global zone client is allowed to open a local zone dlp.
*/
return (ENOENT);
/*
* mac_start() is required for non-legacy MACs to show accurate
* kstats even before the interface is brought up. For legacy
* drivers, this is not needed. Further, calling mac_start() for
* legacy drivers would make the shared-lower-stream to stay in
* the DL_IDLE state, which in turn causes performance regression.
*/
return (err);
}
/*
* Cache a copy of the MAC interface handle, a pointer to the
* immutable MAC info.
*/
return (0);
}
void
{
dlp->dl_zone_ref--;
/*
* Walk the list of multicast addresses, disabling each at the MAC.
* Note that we must remove multicast address before
* mac_unicast_remove() (called by dls_active_clear()) because
* mac_multicast_remove() relies on the unicast flows on the mac
* client.
*/
kmem_free(p, sizeof (dls_multicst_addr_t));
}
/*
* If the dld_str_t is bound then unbind it.
*/
}
/*
* If the MAC has been set in promiscuous mode then disable it.
* This needs to be done before resetting ds_rx.
*/
(void) dls_promisc(dsp, 0);
/*
* At this point we have cutoff inbound packet flow from the mac
* for this 'dsp'. The dls_link_remove above cut off packets meant
* for us and waited for upcalls to finish. Similarly the dls_promisc
* reset above waited for promisc callbacks to finish. Now we can
* safely reset ds_rx to NULL
*/
/*
* Release our reference to the dls_link_t allowing that to be
* destroyed if there are no more dls_impl_t.
*/
}
int
{
/*
* Check to see the value is legal for the media type.
*/
return (EINVAL);
/*
* Set up the dld_str_t to mark it as able to receive packets.
*/
/*
* The MAC layer does the VLAN demultiplexing and will only pass up
* untagged packets to non-promiscuous primary MAC clients. In order to
* support the binding to the VLAN SAP which is required by DLPI, dls
* needs to get a copy of all tagged packets when the client binds to
* the VLAN SAP. We do this by registering a separate promiscuous
* callback for each dls client binding to that SAP.
*
* Note: even though there are two promiscuous handles in dld_str_t,
* ds_mph is for the regular promiscuous mode, ds_vlan_mph is the handle
* to receive VLAN pkt when promiscuous mode is not on. Only one of
* them can be non-NULL at the same time, to avoid receiving dup copies
* of pkts.
*/
int err;
return (EINVAL);
return (err);
}
/*
* Now bind the dld_str_t by adding it into the hash table in the
* dls_link_t.
*/
return (0);
}
void
{
/*
* For VLAN SAP, there was a promisc handle registered when dls_bind.
* When unbind this dls link, we need to remove the promisc handle.
* See comments in dls_bind().
*/
return;
}
/*
* Unbind the dld_str_t by removing it from the hash table in the
* dls_link_t.
*/
}
/*
* In order to prevent promiscuous-mode processing with dsp->ds_promisc
* set to inaccurate values, this function sets dsp->ds_promisc with new
* flags. For enabling (mac_promisc_add), the flags are set prior to the
* actual enabling. For disabling (mac_promisc_remove), the flags are set
* after the actual disabling.
*/
int
{
int err = 0;
DLS_PROMISC_PHYS)));
/*
* If the user has only requested DLS_PROMISC_MULTI then we need to make
* sure that they don't see all packets.
*/
if (new_flags == DLS_PROMISC_MULTI)
/*
* If only DLS_PROMISC_SAP, we don't turn on the
* physical promisc mode
*/
(new_flags != DLS_PROMISC_SAP) ? 0 :
if (err != 0) {
return (err);
}
/* Remove vlan promisc handle to avoid sending dup copy up */
}
return (EINVAL);
}
/*
* If the old flag is PROMISC_SAP, but the current flag has
* changed to some new non-zero value, we need to turn the
* physical promiscuous mode.
*/
/* Honors both after-remove and before-add semantics! */
if (err != 0)
} else {
/* No adding or removing, but record the new flags anyway. */
}
return (err);
}
int
{
int err;
/*
* Check whether the address is in the list of enabled addresses for
* this dld_str_t.
*/
/*
* Protect against concurrent access of ds_dmap by data threads using
* ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
* remove operations. Dropping the ds_rw_lock across mac calls is thus
* ok and is also required by the locking protocol.
*/
/*
* It is there so there's nothing to do.
*/
err = 0;
goto done;
}
}
/*
* Allocate a new list item and add it to the list.
*/
*pp = p;
/*
* Enable the address at the MAC.
*/
if (err == 0)
return (0);
/* Undo the operation as it has failed */
kmem_free(p, sizeof (dls_multicst_addr_t));
done:
return (err);
}
int
{
/*
* Find the address in the list of enabled addresses for this
* dld_str_t.
*/
/*
* Protect against concurrent access to ds_dmap by data threads using
* ds_rw_lock. The mac perimeter serializes the dls_multicst_add and
* remove operations. Dropping the ds_rw_lock across mac calls is thus
* ok and is also required by the locking protocol.
*/
break;
}
/*
* If we walked to the end of the list then the given address is
* not currently enabled for this dld_str_t.
*/
if (p == NULL) {
return (ENOENT);
}
/*
* Remove the address from the list.
*/
/*
* Disable the address at the MAC.
*/
kmem_free(p, sizeof (dls_multicst_addr_t));
return (0);
}
mblk_t *
{
/*
* In the case of Ethernet, we need to tell mac_header() if we need
* extra room beyond the Ethernet header for a VLAN header. We'll
* need to add a VLAN header if this isn't an ETHERTYPE_VLAN listener
* (because such streams will be handling VLAN headers on their own)
* and one of the following conditions is satisfied:
*
* - This is a VLAN stream
* - This is a physical stream, the priority is not 0, and user
* priority tagging is allowed.
*/
(vid != VLAN_ID_NONE ||
extra_len = sizeof (struct ether_vlan_header) -
sizeof (struct ether_header);
} else {
extra_len = 0;
}
return (NULL);
return (mp);
/*
* Fill in the tag information.
*/
if (extra_len != 0) {
} else {
/*
* The stream is ETHERTYPE_VLAN listener, so its VLAN tag is
* in the payload. Update the priority.
*/
/*
* Because some DLS consumers only check the db_ref
* count of the first mblk, we pullup 'payload' into
* a single mblk.
*/
return (NULL);
} else {
}
}
}
return (mp);
}
void
{
}
static boolean_t
{
/*
* We must not accept packets if the dld_str_t is not marked as bound
* or is being removed.
*/
goto refuse;
if (dsp->ds_promisc != 0) {
/*
* Filter out packets that arrived from the data path
* (i_dls_link_rx) when promisc mode is on. We need to correlate
* the ds_promisc flags with the mac header destination type. If
* only DLS_PROMISC_MULTI is enabled, we need to only reject
* multicast packets as those are the only ones which filter up
* the promiscuous path. If we have DLS_PROMISC_PHYS or
* DLS_PROMISC_SAP set, then we know that we'll be seeing
* everything, so we should drop it now.
*/
goto refuse;
/*
* If the dls_impl_t is in 'all physical' mode then
* always accept.
*/
goto accept;
/*
* Loopback packets i.e. packets sent out by DLS on a given
* mac end point, will be accepted back by DLS on loopback
* from the mac, only in the 'all physical' mode which has been
* covered by the previous check above
*/
if (promisc_loopback)
goto refuse;
}
switch (mhip->mhi_dsttype) {
case MAC_ADDRTYPE_UNICAST:
case MAC_ADDRTYPE_BROADCAST:
/*
* We can accept unicast and broadcast packets because
* filtering is already done by the mac layer.
*/
goto accept;
case MAC_ADDRTYPE_MULTICAST:
/*
* Additional filtering is needed for multicast addresses
* because different streams may be interested in different
* addresses.
*/
goto accept;
addr_length) == 0) {
goto accept;
}
}
break;
}
return (B_FALSE);
/*
* the returned ds_rx and ds_rx_arg will always be in sync.
*/
return (B_TRUE);
}
/* ARGSUSED */
void **ds_rx_arg)
{
B_FALSE));
}
{
loopback));
}
int
{
int err = 0;
/*
* First client; add the primary unicast address.
*/
if (dlp->dl_nactive == 0) {
/*
* First client; add the primary unicast address.
*/
/* request the primary MAC address */
&diag)) != 0) {
return (err);
}
/*
* Set the function to start receiving packets.
*/
}
dlp->dl_nactive++;
return (0);
}
void
{
if (--dlp->dl_nactive == 0) {
}
}
int
{
int err = 0;
return (0);
/* If we're already active, then there's nothing more to do. */
if ((dsp->ds_nactive == 0) &&
/* except for ENXIO all other errors are mapped to EBUSY */
return (EBUSY);
return (err);
}
dsp->ds_nactive++;
return (0);
}
/*
* Note that dls_active_set() is called whenever an active operation
* (DL_BIND_REQ, DL_ENABMULTI_REQ ...) is processed and
* dls_active_clear(dsp, B_FALSE) is called whenever the active operation
* is being undone (DL_UNBIND_REQ, DL_DISABMULTI_REQ ...). In some cases,
* a stream is closed without every active operation being undone and we
* need to clear all the "active" states by calling
* dls_active_clear(dsp, B_TRUE).
*/
void
{
return;
return;
if (dsp->ds_nactive != 0)
return;
}