vnic_dev.c revision 568a765b090ca47ac51e3db4143a65f9358495bf
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/mac_ether.h>
#if 0
#endif
#include <sys/vnic_impl.h>
static int vnic_m_start(void *);
static void vnic_m_stop(void *);
static int vnic_m_promisc(void *, boolean_t);
static int vnic_m_unicst(void *, const uint8_t *);
static void vnic_m_resources(void *);
static void vnic_mac_free(vnic_mac_t *);
static void vnic_notify_cb(void *, mac_notify_type_t);
static kmem_cache_t *vnic_cache;
static kmem_cache_t *vnic_mac_cache;
static kmutex_t vnic_mac_lock;
static uint_t vnic_count;
/* hash of VNICs (vnic_t's), keyed by VNIC id */
static mod_hash_t *vnic_hash;
#define VNIC_HASHSZ 64
/*
* Hash of underlying open MACs (vnic_mac_t's), keyed by the string
* "<device name><instance number>/<port number>".
*/
static mod_hash_t *vnic_mac_hash;
#define VNIC_MAC_HASHSZ 64
#define VNIC_MAC_REFHOLD(va) { \
}
#define VNIC_MAC_REFRELE(va) { \
vnic_mac_free(va); \
}
/* used by vnic_walker */
typedef struct vnic_info_state {
char vs_dev_name[MAXNAMELEN];
void *vs_fn_arg;
int vs_rc;
static mac_callbacks_t vnic_m_callbacks = {
NULL, /* m_ioctl */
};
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static void
{
}
void
vnic_dev_init(void)
{
vnic_count = 0;
}
void
vnic_dev_fini(void)
{
ASSERT(vnic_count == 0);
}
vnic_dev_count(void)
{
return (vnic_count);
}
static int
{
char *str_key;
int err;
char driver[MAXNAMELEN];
const mac_info_t *mip;
return (EINVAL);
(mod_hash_val_t *)&vnic_mac);
if (err == 0) {
/* this MAC is already opened, increment reference count */
return (0);
}
goto bail;
}
/* only ethernet support, for now */
goto bail;
}
goto bail;
}
/* add entry to hash table */
/* initialize the flow table associated with lower MAC */
KM_SLEEP);
return (0);
bail:
}
return (err);
}
/*
* Create a new flow for the active MAC client sharing the NIC
* with the VNICs. This allows the unicast packets for that NIC
* to be classified and passed up to the active MAC client. It
* also allows packets sent from a VNIC to the active link to
* be classified by the VNIC transmit function and delivered via
* the MAC module locally. Returns B_TRUE on success, B_FALSE on
* failure.
*/
static int
{
return (B_TRUE);
return (B_TRUE);
}
static void
{
return;
}
static void
{
return;
(void) vnic_init_active_rx(vnic_mac);
}
/*
* Copy an mblk, preserving its hardware checksum flags.
*/
mblk_t *
{
return (NULL);
flags, KM_NOSLEEP);
return (mp1);
}
/*
* Copy an mblk chain, presenting the hardware checksum flags of the
* individual mblks.
*/
mblk_t *
{
return (NULL);
}
}
return (nmp);
}
/*
* Process the specified mblk chain for proper handling of hardware
* checksum offload. This routine is invoked for loopback VNIC traffic.
* The function handles a NULL mblk chain passed as argument.
*/
mblk_t *
{
struct ether_header *ehp;
&flags);
if (flags == 0)
continue;
/*
* Since the processing of checksum offload for loopback
* traffic requires modification of the packet contents,
* ensure sure that we are always modifying our own copy.
*/
continue;
else
}
/*
* Ethernet, and optionally VLAN header.
*/
/*LINTED*/
struct ether_vlan_header *evhp;
sizeof (struct ether_vlan_header));
/*LINTED*/
offset = sizeof (struct ether_vlan_header);
} else {
offset = sizeof (struct ether_header);
}
/* corrupted packet, skip it */
else
continue;
}
}
/*
* In order to compute the full and header
* checksums, we need to find and parse
*/
/*
* IP header.
*/
if (sap != ETHERTYPE_IP)
continue;
/*LINTED*/
if (flags & HCK_FULLCKSUM) {
/*
* Pointer to checksum field in ULP header.
*/
if (proto == IPPROTO_TCP) {
/*LINTED*/
} else {
/*LINTED*/
}
/*
* Pseudo-header checksum.
*/
/*
* The checksum value stored in the packet needs
* to be correct. Compute it here.
*/
*up = 0;
value = 0xffff;
}
if (flags & HCK_IPV4_HDRCKSUM) {
}
}
if (flags & HCK_PARTIALCKSUM) {
continue;
else
}
/*LINTED*/
*up = 0;
/*
* Since we already computed the whole checksum,
* indicate to the stack that it has already
* been verified by the hardware.
*/
flags &= ~HCK_PARTIALCKSUM;
value = 0xffff;
}
}
return (new_chain);
}
static void
{
}
static void
{
if (vnic_mac->va_mac_set) {
}
(void) mod_hash_remove(vnic_mac_hash,
}
/*
* Initial VNIC receive routine. Invoked for packets that are steered
* to a VNIC but the VNIC has not been started yet.
*/
/* ARGSUSED */
static void
{
/* update stats */
vnic->vn_stat_ierrors++;
}
/*
* VNIC receive routine invoked after the classifier for the VNIC
* has been initialized and the VNIC has been started.
*/
/* ARGSUSED */
void
{
/* update stats */
vnic->vn_stat_ipackets++;
}
/* pass packet up */
}
/*
* Routine to create a MAC-based VNIC. Adds the passed MAC address
* to an unused slot in the NIC if one is available. Otherwise it
* sets the NIC in promiscuous mode and assigns the MAC address to
* a Rx ring if available or a soft ring.
*/
static int
{
int err;
return (EINVAL);
&(vnic->vn_mma_capab))) {
if (vnic->vn_maddr_naddrfree == 0) {
/*
* No free address slots available.
* Enable promiscuous mode.
*/
goto set_promisc;
}
if (err != 0) {
/*
* There was a race to add addresses
* with other multiple address consumers,
* and we lost out. Use promisc mode.
*/
goto set_promisc;
}
return (err);
}
} else {
/*
* Either multiple MAC address support is not
* available or all available addresses have
* been used up.
*/
if (err != 0) {
return (err);
}
}
return (err);
}
/*
* VNIC is getting deleted. Remove the MAC address from the slot.
* If promiscuous mode was being used, then unset the promiscuous mode.
*/
static int
{
int err;
if (vnic->vn_multi_mac) {
vnic->vn_slot_id);
}
if (vnic->vn_promisc_mac) {
}
return (err);
}
/*
* Create a new VNIC upon request from administrator.
* Returns 0 on success, an errno on failure.
*/
int
{
int err;
const mac_info_t *lower_mac_info;
if (mac_len != ETHERADDRL) {
/* currently only ethernet NICs are supported */
return (EINVAL);
}
/* does a VNIC with the same id already exist? */
(mod_hash_val_t *)&vnic);
if (err == 0) {
return (EEXIST);
}
return (ENOMEM);
}
/* open underlying MAC */
if (err != 0) {
return (err);
}
/* set the VNIC MAC address */
goto bail;
/* set the initial VNIC capabilities */
vnic->vn_hcksum_txflags = 0;
/* register with the MAC module */
goto bail;
if (err != 0)
goto bail;
/* add new VNIC to hash table */
vnic_count++;
/* Create a flow, initialized with the MAC address of the VNIC */
(void) vnic_dev_delete(vnic_id);
goto bail_unlocked;
}
/* setup VNIC to receive broadcast packets */
if (err != 0) {
(void) vnic_dev_delete(vnic_id);
goto bail_unlocked;
}
if (!vnic_mac->va_mac_set) {
/*
* We want to MAC layer to call the VNIC tx outbound
* routine, so that local broadcast packets sent by
* the active interface sharing the underlying NIC (if
* any), can be broadcast to every VNIC.
*/
vnic_m_capab_get, vnic)) {
(void) vnic_dev_delete(vnic_id);
goto bail_unlocked;
}
}
/* allow passing packets to NIC's active MAC client */
if (!vnic_init_active_rx(vnic_mac)) {
(void) vnic_dev_delete(vnic_id);
goto bail_unlocked;
}
return (0);
bail:
(void) vnic_remove_unicstaddr(vnic);
}
return (err);
}
/*
* Modify the properties of an existing VNIC.
*/
/* ARGSUSED */
int
{
int rv = 0;
(mod_hash_val_t *)&vnic) != 0) {
return (ENOENT);
}
if (modify_mask & VNIC_IOC_MODIFY_ADDR) {
if (rv == 0)
}
if (notify_mac_addr)
return (rv);
}
int
{
int rc;
(mod_hash_val_t *)&vnic) != 0) {
return (ENOENT);
}
/*
* We cannot unregister the MAC yet. Unregistering would
* free up mac_impl_t which should not happen at this time.
* Packets could be entering vnic_rx() through the
* flow entry and so mac_impl_t cannot be NULL. So disable
* mac_impl_t by calling mac_disable(). This will prevent any
* new claims on mac_impl_t.
*/
return (EBUSY);
}
if (vnic->vn_bcast_grp)
/*
* vnic_classifier_flow_destroy() ensures that the
* flow is no longer used.
*/
}
(void) vnic_remove_unicstaddr(vnic);
vnic_count--;
return (0);
}
/*
* For the specified packet chain, return a sub-chain to be sent
* and the transmit function to be used to send the packet. Also
* return a pointer to the sub-chain of packets that should
* be re-classified. If the function returns NULL, the packet
* should be sent using the underlying NIC.
*/
static vnic_flow_t *
{
/* one packet at a time */
/* do classification on the packet */
return (flow_ent);
}
/*
* Send a packet chain to a local VNIC or an active MAC client.
*/
static void
{
const vnic_flow_fn_info_t *fn_info;
if (!vnic_classifier_is_active(flow_ent) &&
/*
* If the MAC is in promiscous mode,
* send a copy of the active client.
*/
goto sendit;
goto sendit;
}
/*
* If the vnic to which we would deliver this packet is in
* promiscuous mode then it already received the packet via
* vnic_promisc_rx().
*
* XXX assumes that ff_arg2 is a vnic_t pointer if it is
* non-NULL (currently always true).
*/
}
/*
* This function is invoked when a MAC client needs to send a packet
* to a NIC which is shared by VNICs. It is passed to the MAC layer
* by a call to mac_vnic_set() when the NIC is opened, and is returned
* to MAC clients by mac_tx_get() when VNICs are present.
*/
mblk_t *
{
void *flow_cookie;
flow_ent);
if (flow_cookie != NULL) {
/*
* Send a copy to every VNIC defined on the
* interface, as well as the underlying MAC.
*/
} else {
/*
* loopback the packet to a local VNIC or
* an active MAC client.
*/
}
} else {
/*
* Non-VNIC destination, send via the underlying
* NIC. In order to avoid a recursive call
* to this function, we ensured that mtp points
* to the unerlying NIC transmit function
* by inilizating through mac_vnic_tx_get().
*/
break;
}
}
}
return (mp_chain);
}
/*
* VNIC transmit function.
*/
mblk_t *
{
void *flow_cookie;
/*
* Update stats.
*/
vnic->vn_stat_opackets++;
}
flow_ent);
if (flow_cookie != NULL) {
/*
* The vnic_bcast_send function expects
* to receive the sender VNIC as value
* for arg2.
*/
} else {
/*
* loopback the packet to a local VNIC or
* an active MAC client.
*/
}
} else {
/*
* Non-local destination, send via the underlying
* NIC.
*/
break;
}
}
/* update stats to account for unsent packets */
vnic->vn_stat_opackets--;
vnic->vn_stat_oerrors++;
/*
* link back in the last portion not counted due to bandwidth
* control.
*/
break;
}
}
return (mp_chain);
}
/* ARGSUSED */
static void
vnic_m_resources(void *arg)
{
/* no resources to advertise */
}
static int
{
int rval = 0;
switch (stat) {
case ETHER_STAT_LINK_DUPLEX:
break;
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_MULTIRCV:
break;
case MAC_STAT_BRDCSTRCV:
break;
case MAC_STAT_MULTIXMT:
break;
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_OPACKETS:
break;
default:
}
return (rval);
}
/*
* Return information about the specified capability.
*/
/* ARGSUSED */
static boolean_t
{
switch (cap) {
case MAC_CAPAB_POLL:
return (B_TRUE);
case MAC_CAPAB_HCKSUM: {
break;
}
default:
return (B_FALSE);
}
return (B_TRUE);
}
static int
vnic_m_start(void *arg)
{
int rc;
if (rc != 0)
return (rc);
return (0);
}
static void
vnic_m_stop(void *arg)
{
}
/* ARGSUSED */
static int
{
}
static int
{
int rc = 0;
if (add)
else
return (rc);
}
static int
{
int rv;
if (rv == 0)
return (0);
}
int
{
int rc = 0;
*nvnics = vnic_count;
return (rc);
}
/*
* Walker invoked when building a list of vnics that must be passed
* up to user space.
*/
/*ARGSUSED*/
static uint_t
{
return (MH_WALK_TERMINATE); /* terminate walk */
goto bail;
bail:
}
/*
* vnic_notify_cb() and vnic_notify_walker() below are used to
* process events received from an underlying NIC and, if needed,
* forward these events to the VNICs defined on top of that NIC.
*/
typedef struct vnic_notify_state {
/* ARGSUSED */
static uint_t
{
/* ignore VNICs that don't use the specified underlying MAC */
return (MH_WALK_CONTINUE);
case MAC_NOTE_TX:
break;
case MAC_NOTE_LINK:
/*
* The VNIC link state must be up regardless of
* the link state of the underlying NIC to maintain
* connectivity between VNICs on the same host.
*/
break;
case MAC_NOTE_UNICST:
break;
case MAC_NOTE_VNIC:
/* only for clients which share a NIC with a VNIC */
break;
case MAC_NOTE_PROMISC:
break;
}
return (MH_WALK_CONTINUE);
}
static void
{
}
static int
{
return (EINVAL);
return (0);
}
static int
{
int r = -1;
return (0);
if (on) {
if (r != 0)
return (r);
return (0);
} else {
}
MAC_DEVPROMISC)) == 0)) {
else
}
return (r);
}
}
void
{
const vnic_flow_fn_info_t *fn_info;
goto done;
goto done;
/*
* If this is broadcast or multicast then the destination
* address need not match for us to deliver it.
*/
continue;
if (dst_must_match &&
continue;
if (!flow->vf_is_active) {
break;
break;
break;
}
}
done:
}