/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Simulated network device (simnet) driver: simulates a pseudo GLDv3 network
* device. Can simulate an Ethernet or WiFi network device. In addition, another
* simnet instance can be attached as a peer to create a point-to-point link on
* the same system.
*/
#include <sys/priv_names.h>
#include <sys/ethernet.h>
#include <sys/mac_ether.h>
#include <sys/mac_provider.h>
#include <sys/mac_client_priv.h>
#include <sys/sysmacros.h>
#include <sys/mac_wifi.h>
#include <sys/mac_impl.h>
#include <inet/wifi_ioctl.h>
#include "simnet_impl.h"
};
&mod_driverops, /* Type of module. This one is a driver */
SIMNETINFO, /* short description */
&simnet_dev_ops /* driver specific ops */
};
};
/* MAC callback function declarations */
static int simnet_m_start(void *);
static void simnet_m_stop(void *);
static int simnet_m_promisc(void *, boolean_t);
static int simnet_m_unicst(void *, const uint8_t *);
static int simnet_m_setprop(void *, const char *, mac_prop_id_t,
uint_t, const void *);
static int simnet_m_getprop(void *, const char *, mac_prop_id_t,
uint_t, void *);
static void simnet_m_propinfo(void *, const char *, mac_prop_id_t,
NULL,
NULL,
NULL,
NULL,
};
/*
* simnet_dev_lock protects the simnet device list.
* sd_instlock in each simnet_dev_t protects access to
* a single simnet_dev_t.
*/
int
_init(void)
{
int status;
if (status != DDI_SUCCESS)
return (status);
}
int
_fini(void)
{
int status;
if (status == DDI_SUCCESS)
return (status);
}
int
{
}
static boolean_t
simnet_init(void)
{
TASKQ_DEFAULTPRI, 0)) == NULL)
return (B_FALSE);
return (B_TRUE);
}
static void
simnet_fini(void)
{
ASSERT(simnet_count == 0);
}
/*ARGSUSED*/
static int
void **result)
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = simnet_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
if (ddi_get_instance(dip) != 0) {
/* we only allow instance 0 to attach */
return (DDI_FAILURE);
}
DLDIOCCNT(simnet_ioc_list)) != 0)
return (DDI_FAILURE);
simnet_dip = dip;
if (!simnet_init())
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_DETACH:
/*
* Allow the simnet instance to be detached only if there
* are no simnets configured.
*/
if (simnet_count > 0)
return (DDI_FAILURE);
simnet_fini();
simnet_dip = NULL;
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* Caller must hold simnet_dev_lock */
static simnet_dev_t *
{
return (sdev);
}
}
return (NULL);
}
static void
{
int i;
for (i = 0; i < wdev->swd_esslist_num; i++) {
sizeof (wl_ess_conf_t));
}
}
static void
{
return;
}
simnet_count--;
}
static int
{
int err;
return (ENOMEM);
return (err);
}
static int
{
int err;
return (err);
}
static int
{
int err;
return (ENOMEM);
else
return (err);
}
/* ARGSUSED */
static int
{
int err = 0;
return (ENOMEM);
return (EEXIST);
}
sdev->sd_refcount++;
simnet_count++;
/* Simnets created from configuration on boot pass saved MAC address */
if (create_arg->sic_mac_len == 0) {
/* Generate random MAC address */
/* Ensure MAC address is not multicast and is local */
} else {
}
goto exit;
}
crgetzoneid(cred))) != 0) {
goto exit;
}
/* Always return MAC address back to caller */
sdev->sd_mac_len);
exit:
return (err);
}
/* Caller must hold writer simnet_dev_lock */
static datalink_id_t
{
/* Release previous references held on both simnets */
}
return (peer_link_id);
}
/* ARGSUSED */
static int
{
return (ENOENT);
}
return (ENOENT);
}
/* Cannot peer with self */
return (EINVAL);
}
/* Nothing to modify */
return (0);
}
/* Peer simnet device not available */
return (ENOENT);
}
/* The two peers must be in the same zone (for now). */
return (EACCES);
}
}
/* First remove any previous peer */
(void) simnet_remove_peer(sdev);
/* Remove any previous peer of sdev_peer */
(void) simnet_remove_peer(sdev_peer);
/* Update both devices with the new peer */
/* Hold references on both devices */
} else {
/* Release sdev lookup reference */
}
return (0);
}
/* ARGSUSED */
static int
{
int err;
return (ENOENT);
}
return (ENOENT);
}
return (err);
}
/* Remove any attached peer link */
/* Prevent new threads from using the instance */
/* Wait until all active threads using the instance exit */
while (sdev->sd_threadcount > 0) {
&sdev->sd_instlock) == 0) {
/* Signaled */
goto fail;
}
}
/* Try disabling the MAC */
goto fail;
return (err);
fail:
/* Re-create simnet instance and add any previous peer */
crgetzoneid(cred));
if (peerid != DATALINK_INVALID_LINKID &&
/* Attach peer device back */
/* Hold reference on both devices */
} else {
/*
* No previous peer or previous peer no longer
* available so release lookup reference.
*/
}
return (err);
}
/* ARGSUSED */
static int
{
/* Make sure that the simnet link is visible from the caller's zone. */
return (ENOENT);
return (ENOENT);
}
sdev->sd_mac_len);
return (0);
}
static boolean_t
{
return (B_FALSE);
}
sdev->sd_threadcount++;
return (B_TRUE);
}
static void
{
if (--sdev->sd_threadcount == 0)
}
static void
{
/* Check for valid packet header */
goto rx_done;
}
/*
* When we are NOT in promiscuous mode we only receive
* unicast packets addressed to us and multicast packets that
* MAC clients have requested.
*/
if (!sdev->sd_promisc &&
ETHERADDRL) != 0) {
goto rx_done;
NULL) {
goto rx_done;
}
}
}
}
static mblk_t *
{
/* Discard packets when no peer exists */
return (NULL);
}
/*
* Discard packets when either device is shutting down or not ready.
* Though MAC layer ensures a reference is held on the MAC while we
* process the packet chain, there is no guarantee the peer MAC will
* remain enabled. So we increment per-instance threadcount to ensure
* either MAC instance is not disabled while we handle the chain of
* packets. It is okay if the peer device is disconnected while we are
* here since we lookup the peer device while holding simnet_dev_lock
* (reader lock) and increment the threadcount of the peer, the peer
* MAC cannot be disabled in simnet_ioc_delete.
*/
if (!simnet_thread_ref(sdev_rx)) {
return (NULL);
}
if (!simnet_thread_ref(sdev)) {
return (NULL);
}
int len;
int size;
/* Pad packet to minimum Ethernet frame size */
continue;
}
}
/* Pullup packet into a single mblk */
continue;
}
/* Fix mblk checksum as the pkt dest is local */
continue;
}
/* Hold reference for taskq receive processing per-pkt */
if (!simnet_thread_ref(sdev_rx)) {
break;
}
/* Use taskq for pkt receive to avoid kernel stack explosion */
DDI_NOSLEEP) == DDI_SUCCESS) {
} else {
}
}
return (NULL);
}
static int
{
/* LINTED E_BAD_PTR_CAST_ALIGN */
case WL_DISASSOCIATE:
break;
default:
break;
}
return (rc);
}
static void
{
int rc;
return;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
return;
}
/* We only claim support for WiFi operation commands */
switch (cmd) {
default:
return;
case WLAN_GET_PARAM:
case WLAN_SET_PARAM:
case WLAN_COMMAND:
break;
}
/* overwrite everything */
}
static int
{
int rval = 0;
switch (stat) {
case MAC_STAT_IFSPEED:
break;
case MAC_STAT_LINK_STATE:
*val = LINK_DUPLEX_FULL;
break;
case MAC_STAT_LINK_UP:
*val = LINK_STATE_UP;
else
*val = LINK_STATE_DOWN;
break;
case MAC_STAT_PROMISC:
case MAC_STAT_MULTIRCV:
case MAC_STAT_MULTIXMT:
case MAC_STAT_BRDCSTRCV:
case MAC_STAT_BRDCSTXMT:
break;
case MAC_STAT_OPACKETS:
break;
case MAC_STAT_OBYTES:
break;
case MAC_STAT_IERRORS:
break;
case MAC_STAT_OERRORS:
break;
case MAC_STAT_RBYTES:
break;
case MAC_STAT_IPACKETS:
break;
case WIFI_STAT_FCS_ERRORS:
case WIFI_STAT_WEP_ERRORS:
case WIFI_STAT_TX_FRAGS:
case WIFI_STAT_MCAST_TX:
case WIFI_STAT_RTS_SUCCESS:
case WIFI_STAT_RTS_FAILURE:
case WIFI_STAT_ACK_FAILURE:
case WIFI_STAT_RX_FRAGS:
case WIFI_STAT_MCAST_RX:
case WIFI_STAT_RX_DUPS:
break;
default:
break;
}
return (rval);
}
static int
{
return (0);
}
static void
{
}
static int
{
return (0);
}
/*
* Returns matching multicast address enabled on the simnet instance.
* Assumes simnet instance mutex lock is held.
*/
static uint8_t *
{
int idx;
return (maddrptr);
maddrptr += ETHERADDRL;
}
return (NULL);
}
/* Add or remove Multicast addresses on simnet instance */
static int
{
goto alloc_retry;
}
/* Removing a Multicast address */
/* LINTED: E_PTRDIFF_OVERFLOW */
}
/* Adding a new Multicast address */
} else {
/* Error: removing a non-existing Multicast address */
"Multicast address failed");
return (EINVAL);
}
return (0);
}
static int
{
return (0);
}
/* Parse WiFi scan list entry arguments and return the arg count */
static int
char args[][MAX_ESSLIST_ARGLEN])
{
char *sep;
int arg = 0;
/* LINTED E_PTRDIFF_OVERFLOW */
/* If first arg is zero then return none to delete all */
return (0);
if (len > MAX_ESSLIST_ARGLEN)
arg++;
}
return (arg);
}
/* Set WiFi scan list entry from private property _wl_esslist */
static int
const void *pr_val)
{
long result;
int i;
for (i = 0; i < wdev->swd_esslist_num; i++) {
}
wdev->swd_esslist_num = 0;
return (0);
}
for (i = 0; i < wdev->swd_esslist_num; i++) {
essargs[0]) == 0)
return (EEXIST);
}
return (EINVAL);
(void) random_get_pseudo_bytes((uint8_t *)
wdev->swd_esslist_num++;
return (0);
}
static int
{
long result;
return (EINVAL);
return (EINVAL);
return (0);
}
return (EINVAL);
}
static int
{
int err = 0;
switch (wldp_pr_num) {
case MAC_PROP_MTU:
else
return (EINVAL);
default:
break;
}
return (ENOTSUP);
/* mac_prop_id */
switch (wldp_pr_num) {
case MAC_PROP_WL_ESSID: {
int i;
sizeof (wl_essid_t));
/* Lookup the signal strength of the connected ESSID */
for (i = 0; i < wdev->swd_esslist_num; i++) {
break;
}
}
break;
}
case MAC_PROP_WL_BSSID: {
sizeof (wl_bssid_t));
break;
}
case MAC_PROP_WL_PHY_CONFIG:
case MAC_PROP_WL_KEY_TAB:
case MAC_PROP_WL_AUTH_MODE:
case MAC_PROP_WL_ENCRYPTION:
case MAC_PROP_WL_BSSTYPE:
break;
case MAC_PROP_PRIVATE:
break;
default:
break;
}
return (err);
}
static int
{
int err = 0;
int value;
/* Returns num of _wl_ess_conf_t that have been set */
} else {
}
if (err == 0)
return (err);
}
static int
{
int err = 0;
int i;
return (ENOTSUP);
/* mac_prop_id */
switch (wldp_pr_num) {
case MAC_PROP_WL_ESSID:
sizeof (wl_essid_t));
break;
case MAC_PROP_WL_BSSID:
sizeof (wl_bssid_t));
break;
case MAC_PROP_WL_PHY_CONFIG:
case MAC_PROP_WL_AUTH_MODE:
case MAC_PROP_WL_ENCRYPTION:
break;
case MAC_PROP_WL_LINKSTATUS:
sizeof (wdev->swd_linkstatus));
break;
case MAC_PROP_WL_ESS_LIST: {
/* LINTED E_BAD_PTR_CAST_ALIGN */
for (i = 0; i < wdev->swd_esslist_num; i++) {
sizeof (wl_ess_conf_t));
w_ess_conf++;
}
break;
}
case MAC_PROP_WL_RSSI:
break;
case MAC_PROP_WL_RADIO:
break;
case MAC_PROP_WL_POWER_MODE:
break;
break;
case MAC_PROP_PRIVATE:
wldp_buf);
break;
default:
break;
}
return (err);
}
static void
{
}
}
static void
{
return;
switch (wldp_pr_num) {
case MAC_PROP_WL_BSSTYPE:
case MAC_PROP_WL_ESS_LIST:
case MAC_PROP_WL_RSSI:
break;
case MAC_PROP_PRIVATE:
break;
}
}