xref: /onnv-gate/usr/src/uts/common/io/mac/plugins/mac_wifi.c (revision 10266:bbc5945eddd7)
13147Sxc151355 /*
23147Sxc151355  * CDDL HEADER START
33147Sxc151355  *
43147Sxc151355  * The contents of this file are subject to the terms of the
53147Sxc151355  * Common Development and Distribution License (the "License").
63147Sxc151355  * You may not use this file except in compliance with the License.
73147Sxc151355  *
83147Sxc151355  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93147Sxc151355  * or http://www.opensolaris.org/os/licensing.
103147Sxc151355  * See the License for the specific language governing permissions
113147Sxc151355  * and limitations under the License.
123147Sxc151355  *
133147Sxc151355  * When distributing Covered Code, include this CDDL HEADER in each
143147Sxc151355  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153147Sxc151355  * If applicable, add the following below this CDDL HEADER, with the
163147Sxc151355  * fields enclosed by brackets "[]" replaced with your own identifying
173147Sxc151355  * information: Portions Copyright [yyyy] [name of copyright owner]
183147Sxc151355  *
193147Sxc151355  * CDDL HEADER END
203147Sxc151355  */
213147Sxc151355 /*
22*10266SQuaker.Fang@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233147Sxc151355  * Use is subject to license terms.
243147Sxc151355  */
253147Sxc151355 
263147Sxc151355 /*
273147Sxc151355  * WiFi MAC Type plugin for the Nemo mac module
283147Sxc151355  *
293147Sxc151355  * This is a bit of mutant since we pretend to be mostly DL_ETHER.
303147Sxc151355  */
313147Sxc151355 
323147Sxc151355 #include <sys/types.h>
333147Sxc151355 #include <sys/modctl.h>
343147Sxc151355 #include <sys/dlpi.h>
358275SEric Cheng #include <sys/dld_impl.h>
363147Sxc151355 #include <sys/mac_wifi.h>
373147Sxc151355 #include <sys/ethernet.h>
383147Sxc151355 #include <sys/byteorder.h>
393147Sxc151355 #include <sys/strsun.h>
403147Sxc151355 #include <inet/common.h>
413147Sxc151355 
423147Sxc151355 uint8_t wifi_bcastaddr[]	= { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
433147Sxc151355 static uint8_t wifi_ietfmagic[]	= { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
443147Sxc151355 static uint8_t wifi_ieeemagic[]	= { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
453147Sxc151355 
463147Sxc151355 static mac_stat_info_t wifi_stats[] = {
473147Sxc151355 	/* statistics described in ieee802.11(5) */
483147Sxc151355 { WIFI_STAT_TX_FRAGS, 		"tx_frags",		KSTAT_DATA_UINT32, 0 },
493147Sxc151355 { WIFI_STAT_MCAST_TX,		"mcast_tx",		KSTAT_DATA_UINT32, 0 },
503147Sxc151355 { WIFI_STAT_TX_FAILED,		"tx_failed",		KSTAT_DATA_UINT32, 0 },
513147Sxc151355 { WIFI_STAT_TX_RETRANS,		"tx_retrans",		KSTAT_DATA_UINT32, 0 },
523147Sxc151355 { WIFI_STAT_TX_RERETRANS,	"tx_reretrans",		KSTAT_DATA_UINT32, 0 },
533147Sxc151355 { WIFI_STAT_RTS_SUCCESS,	"rts_success",		KSTAT_DATA_UINT32, 0 },
543147Sxc151355 { WIFI_STAT_RTS_FAILURE,	"rts_failure",		KSTAT_DATA_UINT32, 0 },
553147Sxc151355 { WIFI_STAT_ACK_FAILURE,	"ack_failure",		KSTAT_DATA_UINT32, 0 },
563147Sxc151355 { WIFI_STAT_RX_FRAGS, 		"rx_frags",		KSTAT_DATA_UINT32, 0 },
573147Sxc151355 { WIFI_STAT_MCAST_RX,		"mcast_rx", 		KSTAT_DATA_UINT32, 0 },
583147Sxc151355 { WIFI_STAT_FCS_ERRORS,		"fcs_errors", 		KSTAT_DATA_UINT32, 0 },
593147Sxc151355 { WIFI_STAT_WEP_ERRORS,		"wep_errors",		KSTAT_DATA_UINT32, 0 },
603147Sxc151355 { WIFI_STAT_RX_DUPS,		"rx_dups",		KSTAT_DATA_UINT32, 0 }
613147Sxc151355 };
623147Sxc151355 
633147Sxc151355 static struct modlmisc mac_wifi_modlmisc = {
643147Sxc151355 	&mod_miscops,
65*10266SQuaker.Fang@Sun.COM 	"WiFi MAC plugin 1.4"
663147Sxc151355 };
673147Sxc151355 
683147Sxc151355 static struct modlinkage mac_wifi_modlinkage = {
693147Sxc151355 	MODREV_1,
703147Sxc151355 	&mac_wifi_modlmisc,
713147Sxc151355 	NULL
723147Sxc151355 };
733147Sxc151355 
743147Sxc151355 static mactype_ops_t mac_wifi_type_ops;
753147Sxc151355 
763147Sxc151355 int
_init(void)773147Sxc151355 _init(void)
783147Sxc151355 {
793147Sxc151355 	mactype_register_t *mtrp = mactype_alloc(MACTYPE_VERSION);
803147Sxc151355 	int err;
813147Sxc151355 
823147Sxc151355 	/*
833147Sxc151355 	 * If `mtrp' is NULL, then this plugin is not compatible with
843147Sxc151355 	 * the system's MAC Type plugin framework.
853147Sxc151355 	 */
863147Sxc151355 	if (mtrp == NULL)
873147Sxc151355 		return (ENOTSUP);
883147Sxc151355 
893147Sxc151355 	mtrp->mtr_ops		= &mac_wifi_type_ops;
903147Sxc151355 	mtrp->mtr_ident		= MAC_PLUGIN_IDENT_WIFI;
913147Sxc151355 	mtrp->mtr_mactype	= DL_ETHER;
923147Sxc151355 	mtrp->mtr_nativetype	= DL_WIFI;
933147Sxc151355 	mtrp->mtr_stats		= wifi_stats;
943147Sxc151355 	mtrp->mtr_statcount	= A_CNT(wifi_stats);
953147Sxc151355 	mtrp->mtr_addrlen	= IEEE80211_ADDR_LEN;
963147Sxc151355 	mtrp->mtr_brdcst_addr	= wifi_bcastaddr;
973147Sxc151355 
983147Sxc151355 	if ((err = mactype_register(mtrp)) == 0) {
993147Sxc151355 		if ((err = mod_install(&mac_wifi_modlinkage)) != 0)
1003147Sxc151355 			(void) mactype_unregister(MAC_PLUGIN_IDENT_WIFI);
1013147Sxc151355 	}
1023147Sxc151355 	mactype_free(mtrp);
1033147Sxc151355 	return (err);
1043147Sxc151355 }
1053147Sxc151355 
1063147Sxc151355 int
_fini(void)1073147Sxc151355 _fini(void)
1083147Sxc151355 {
1093147Sxc151355 	int	err;
1103147Sxc151355 
1113147Sxc151355 	if ((err = mactype_unregister(MAC_PLUGIN_IDENT_WIFI)) != 0)
1123147Sxc151355 		return (err);
1133147Sxc151355 	return (mod_remove(&mac_wifi_modlinkage));
1143147Sxc151355 }
1153147Sxc151355 
1163147Sxc151355 int
_info(struct modinfo * modinfop)1173147Sxc151355 _info(struct modinfo *modinfop)
1183147Sxc151355 {
1193147Sxc151355 	return (mod_info(&mac_wifi_modlinkage, modinfop));
1203147Sxc151355 }
1213147Sxc151355 
1223147Sxc151355 /*
1233147Sxc151355  * MAC Type plugin operations
1243147Sxc151355  */
1253147Sxc151355 
1263147Sxc151355 static boolean_t
mac_wifi_pdata_verify(void * pdata,size_t pdata_size)1273147Sxc151355 mac_wifi_pdata_verify(void *pdata, size_t pdata_size)
1283147Sxc151355 {
1293147Sxc151355 	wifi_data_t *wdp = pdata;
1303147Sxc151355 
1313147Sxc151355 	return (pdata_size == sizeof (wifi_data_t) && wdp->wd_opts == 0);
1323147Sxc151355 }
1333147Sxc151355 
1343147Sxc151355 /* ARGSUSED */
1353147Sxc151355 static int
mac_wifi_unicst_verify(const void * addr,void * pdata)1363147Sxc151355 mac_wifi_unicst_verify(const void *addr, void *pdata)
1373147Sxc151355 {
1383147Sxc151355 	/* If it's not a group address, then it's a valid unicast address. */
1393147Sxc151355 	return (IEEE80211_IS_MULTICAST(addr) ? EINVAL : 0);
1403147Sxc151355 }
1413147Sxc151355 
1423147Sxc151355 /* ARGSUSED */
1433147Sxc151355 static int
mac_wifi_multicst_verify(const void * addr,void * pdata)1443147Sxc151355 mac_wifi_multicst_verify(const void *addr, void *pdata)
1453147Sxc151355 {
1463147Sxc151355 	/* The address must be a group address. */
1473147Sxc151355 	if (!IEEE80211_IS_MULTICAST(addr))
1483147Sxc151355 		return (EINVAL);
1493147Sxc151355 	/* The address must not be the media broadcast address. */
1503147Sxc151355 	if (bcmp(addr, wifi_bcastaddr, sizeof (wifi_bcastaddr)) == 0)
1513147Sxc151355 		return (EINVAL);
1523147Sxc151355 	return (0);
1533147Sxc151355 }
1543147Sxc151355 
1553147Sxc151355 /*
1563147Sxc151355  * Verify that `sap' is valid, and return the actual SAP to bind to in
1573147Sxc151355  * `*bind_sap'.  The WiFI SAP space is identical to Ethernet.
1583147Sxc151355  */
1593147Sxc151355 /* ARGSUSED */
1603147Sxc151355 static boolean_t
mac_wifi_sap_verify(uint32_t sap,uint32_t * bind_sap,void * pdata)1613147Sxc151355 mac_wifi_sap_verify(uint32_t sap, uint32_t *bind_sap, void *pdata)
1623147Sxc151355 {
1633147Sxc151355 	if (sap >= ETHERTYPE_802_MIN && sap <= ETHERTYPE_MAX) {
1643147Sxc151355 		if (bind_sap != NULL)
1653147Sxc151355 			*bind_sap = sap;
1663147Sxc151355 		return (B_TRUE);
1673147Sxc151355 	}
1683147Sxc151355 
1693147Sxc151355 	if (sap <= ETHERMTU) {
1703147Sxc151355 		if (bind_sap != NULL)
1713147Sxc151355 			*bind_sap = DLS_SAP_LLC;
1723147Sxc151355 		return (B_TRUE);
1733147Sxc151355 	}
1743147Sxc151355 	return (B_FALSE);
1753147Sxc151355 }
1763147Sxc151355 
1773147Sxc151355 /*
1783147Sxc151355  * Create a template WiFi datalink header for `sap' packets between `saddr'
1793147Sxc151355  * and `daddr'.  Any enabled modes and features relevant to building the
1803147Sxc151355  * header are passed via `pdata'.  Return NULL on failure.
1813147Sxc151355  */
1823147Sxc151355 /* ARGSUSED */
1833147Sxc151355 static mblk_t *
mac_wifi_header(const void * saddr,const void * daddr,uint32_t sap,void * pdata,mblk_t * payload,size_t extra_len)1843147Sxc151355 mac_wifi_header(const void *saddr, const void *daddr, uint32_t sap,
1853147Sxc151355     void *pdata, mblk_t *payload, size_t extra_len)
1863147Sxc151355 {
1873147Sxc151355 	struct ieee80211_frame	*wh;
1883147Sxc151355 	struct ieee80211_llc	*llc;
1893147Sxc151355 	mblk_t			*mp;
1903147Sxc151355 	wifi_data_t		*wdp = pdata;
1913147Sxc151355 
1923147Sxc151355 	if (!mac_wifi_sap_verify(sap, NULL, NULL))
1933147Sxc151355 		return (NULL);
1943147Sxc151355 
1953147Sxc151355 	if ((mp = allocb(WIFI_HDRSIZE + extra_len, BPRI_HI)) == NULL)
1963147Sxc151355 		return (NULL);
1973147Sxc151355 	bzero(mp->b_rptr, WIFI_HDRSIZE + extra_len);
1983147Sxc151355 
1993147Sxc151355 	/*
2003147Sxc151355 	 * Fill in the fixed parts of the ieee80211_frame.
2013147Sxc151355 	 */
2023147Sxc151355 	wh = (struct ieee80211_frame *)mp->b_rptr;
203*10266SQuaker.Fang@Sun.COM 	mp->b_wptr += sizeof (struct ieee80211_frame) + wdp->wd_qospad;
2043147Sxc151355 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
2053147Sxc151355 
2063147Sxc151355 	switch (wdp->wd_opmode) {
2073147Sxc151355 	case IEEE80211_M_STA:
2083147Sxc151355 		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
2093147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr1, wdp->wd_bssid);
2103147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr2, saddr);
2113147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr3, daddr);
2123147Sxc151355 		break;
2133147Sxc151355 
2143147Sxc151355 	case IEEE80211_M_IBSS:
2153147Sxc151355 	case IEEE80211_M_AHDEMO:
2163147Sxc151355 		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
2173147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr1, daddr);
2183147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr2, saddr);
2193147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr3, wdp->wd_bssid);
2203147Sxc151355 		break;
2213147Sxc151355 
2223147Sxc151355 	case IEEE80211_M_HOSTAP:
2233147Sxc151355 		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
2243147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr1, daddr);
2253147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr2, wdp->wd_bssid);
2263147Sxc151355 		IEEE80211_ADDR_COPY(wh->i_addr3, saddr);
2273147Sxc151355 		break;
2283147Sxc151355 	}
2293147Sxc151355 
230*10266SQuaker.Fang@Sun.COM 	if (wdp->wd_qospad) {
231*10266SQuaker.Fang@Sun.COM 		struct ieee80211_qosframe *qwh =
232*10266SQuaker.Fang@Sun.COM 		    (struct ieee80211_qosframe *)wh;
233*10266SQuaker.Fang@Sun.COM 		qwh->i_qos[1] = 0;
234*10266SQuaker.Fang@Sun.COM 		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
235*10266SQuaker.Fang@Sun.COM 	}
236*10266SQuaker.Fang@Sun.COM 
2374126Szf162725 	switch (wdp->wd_secalloc) {
2384126Szf162725 	case WIFI_SEC_WEP:
2394126Szf162725 		/*
2404126Szf162725 		 * Fill in the fixed parts of the WEP-portion of the frame.
2414126Szf162725 		 */
2423147Sxc151355 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
2433147Sxc151355 		/*
2443147Sxc151355 		 * The actual contents of the WEP-portion of the packet
2453147Sxc151355 		 * are computed when the packet is sent -- for now, we
2463147Sxc151355 		 * just need to account for the size.
2473147Sxc151355 		 */
2483147Sxc151355 		mp->b_wptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
2494126Szf162725 		break;
2504126Szf162725 
2514126Szf162725 	case WIFI_SEC_WPA:
2524126Szf162725 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
2534126Szf162725 		mp->b_wptr += IEEE80211_WEP_IVLEN +
2544126Szf162725 		    IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN;
2554126Szf162725 		break;
2564126Szf162725 
2574126Szf162725 	default:
2584126Szf162725 		break;
2593147Sxc151355 	}
2603147Sxc151355 
2613147Sxc151355 	/*
2623147Sxc151355 	 * Fill in the fixed parts of the ieee80211_llc header.
2633147Sxc151355 	 */
2643147Sxc151355 	llc = (struct ieee80211_llc *)mp->b_wptr;
2653147Sxc151355 	mp->b_wptr += sizeof (struct ieee80211_llc);
2663147Sxc151355 	bcopy(wifi_ietfmagic, llc, sizeof (wifi_ietfmagic));
2673147Sxc151355 	llc->illc_ether_type = htons(sap);
2683147Sxc151355 
2693147Sxc151355 	return (mp);
2703147Sxc151355 }
2713147Sxc151355 
2723147Sxc151355 /*
2733147Sxc151355  * Use the provided `mp' (which is expected to point to a WiFi header), and
2743147Sxc151355  * fill in the provided `mhp'.  Return an errno on failure.
2753147Sxc151355  */
2763147Sxc151355 /* ARGSUSED */
2773147Sxc151355 static int
mac_wifi_header_info(mblk_t * mp,void * pdata,mac_header_info_t * mhp)2783147Sxc151355 mac_wifi_header_info(mblk_t *mp, void *pdata, mac_header_info_t *mhp)
2793147Sxc151355 {
2803147Sxc151355 	struct ieee80211_frame	*wh;
2813147Sxc151355 	struct ieee80211_llc	*llc;
2823147Sxc151355 	uchar_t			*llcp;
2834126Szf162725 	wifi_data_t		*wdp = pdata;
2843147Sxc151355 
2853147Sxc151355 	if (MBLKL(mp) < sizeof (struct ieee80211_frame))
2863147Sxc151355 		return (EINVAL);
2873147Sxc151355 
2883147Sxc151355 	wh = (struct ieee80211_frame *)mp->b_rptr;
2893147Sxc151355 	llcp = mp->b_rptr + sizeof (struct ieee80211_frame);
2903147Sxc151355 
2913147Sxc151355 	/*
292*10266SQuaker.Fang@Sun.COM 	 * Generally, QoS data field takes 2 bytes, but some special hardware,
293*10266SQuaker.Fang@Sun.COM 	 * such as Atheros, will need the 802.11 header padded to a 32-bit
294*10266SQuaker.Fang@Sun.COM 	 * boundary for 4-address and QoS frames, at this time, it's 4 bytes.
295*10266SQuaker.Fang@Sun.COM 	 */
296*10266SQuaker.Fang@Sun.COM 	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
297*10266SQuaker.Fang@Sun.COM 		llcp += wdp->wd_qospad;
298*10266SQuaker.Fang@Sun.COM 
299*10266SQuaker.Fang@Sun.COM 	/*
3003147Sxc151355 	 * When we receive frames from other hosts, the hardware will have
3013147Sxc151355 	 * already performed WEP decryption, and thus there will not be a WEP
3023147Sxc151355 	 * portion.  However, when we receive a loopback copy of our own
3033147Sxc151355 	 * packets, it will still have a WEP portion.  Skip past it to get to
3043147Sxc151355 	 * the LLC header.
3053147Sxc151355 	 */
3064126Szf162725 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
3073147Sxc151355 		llcp += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
3084126Szf162725 		if (wdp->wd_secalloc == WIFI_SEC_WPA)
3094126Szf162725 			llcp += IEEE80211_WEP_EXTIVLEN;
3104126Szf162725 	}
3113147Sxc151355 
3126990Sgd78059 	if ((uintptr_t)mp->b_wptr - (uintptr_t)llcp <
3136990Sgd78059 	    sizeof (struct ieee80211_llc))
3143147Sxc151355 		return (EINVAL);
3153147Sxc151355 
3163147Sxc151355 	llc = (struct ieee80211_llc *)llcp;
3173147Sxc151355 	mhp->mhi_origsap = ntohs(llc->illc_ether_type);
3183147Sxc151355 	mhp->mhi_bindsap = mhp->mhi_origsap;
3193147Sxc151355 	mhp->mhi_pktsize = 0;
3206990Sgd78059 	mhp->mhi_hdrsize = (uintptr_t)llcp + sizeof (*llc) -
3216990Sgd78059 	    (uintptr_t)mp->b_rptr;
3223147Sxc151355 
3233147Sxc151355 	/*
3243147Sxc151355 	 * Verify the LLC header is one of the known formats.  As per MSFT's
3253147Sxc151355 	 * convention, if the header is using IEEE 802.1H encapsulation, then
3263147Sxc151355 	 * treat the LLC header as data.  As per DL_ETHER custom when treating
3273147Sxc151355 	 * the LLC header as data, set the mhi_bindsap to be DLS_SAP_LLC, and
3283147Sxc151355 	 * assume mhi_origsap contains the data length.
3293147Sxc151355 	 */
3303147Sxc151355 	if (bcmp(llc, wifi_ieeemagic, sizeof (wifi_ieeemagic)) == 0) {
3313147Sxc151355 		mhp->mhi_bindsap = DLS_SAP_LLC;
3323147Sxc151355 		mhp->mhi_hdrsize -= sizeof (*llc);
3333147Sxc151355 		mhp->mhi_pktsize = mhp->mhi_hdrsize + mhp->mhi_origsap;
3343147Sxc151355 	} else if (bcmp(llc, wifi_ietfmagic, sizeof (wifi_ietfmagic)) != 0) {
3353147Sxc151355 		return (EINVAL);
3363147Sxc151355 	}
3373147Sxc151355 
3383147Sxc151355 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
3393147Sxc151355 	case IEEE80211_FC1_DIR_NODS:
3403147Sxc151355 		mhp->mhi_daddr = wh->i_addr1;
3413147Sxc151355 		mhp->mhi_saddr = wh->i_addr2;
3423147Sxc151355 		break;
3433147Sxc151355 
3443147Sxc151355 	case IEEE80211_FC1_DIR_TODS:
3453147Sxc151355 		mhp->mhi_daddr = wh->i_addr3;
3463147Sxc151355 		mhp->mhi_saddr = wh->i_addr2;
3473147Sxc151355 		break;
3483147Sxc151355 
3493147Sxc151355 	case IEEE80211_FC1_DIR_FROMDS:
3503147Sxc151355 		mhp->mhi_daddr = wh->i_addr1;
3513147Sxc151355 		mhp->mhi_saddr = wh->i_addr3;
3523147Sxc151355 		break;
3533147Sxc151355 
3543147Sxc151355 	case IEEE80211_FC1_DIR_DSTODS:
3553147Sxc151355 		/* We don't support AP-to-AP mode yet */
3563147Sxc151355 		return (ENOTSUP);
3573147Sxc151355 	}
3583147Sxc151355 
3593147Sxc151355 	if (mac_wifi_unicst_verify(mhp->mhi_daddr, NULL) == 0)
3603147Sxc151355 		mhp->mhi_dsttype = MAC_ADDRTYPE_UNICAST;
3613147Sxc151355 	else if (mac_wifi_multicst_verify(mhp->mhi_daddr, NULL) == 0)
3623147Sxc151355 		mhp->mhi_dsttype = MAC_ADDRTYPE_MULTICAST;
3633147Sxc151355 	else
3643147Sxc151355 		mhp->mhi_dsttype = MAC_ADDRTYPE_BROADCAST;
3653147Sxc151355 
3663147Sxc151355 	return (0);
3673147Sxc151355 }
3683147Sxc151355 
3693147Sxc151355 /*
3703147Sxc151355  * Take the provided `mp' (which is expected to have an Ethernet header), and
3713147Sxc151355  * return a pointer to an mblk_t with a WiFi header.  Note that the returned
3723147Sxc151355  * header will not be complete until the driver finishes filling it in prior
3733147Sxc151355  * to transmit.  If the conversion cannot be performed, return NULL.
3743147Sxc151355  */
3753147Sxc151355 static mblk_t *
mac_wifi_header_cook(mblk_t * mp,void * pdata)3763147Sxc151355 mac_wifi_header_cook(mblk_t *mp, void *pdata)
3773147Sxc151355 {
3783147Sxc151355 	struct ether_header	*ehp;
3793147Sxc151355 	mblk_t			*llmp;
3803147Sxc151355 
3813147Sxc151355 	if (MBLKL(mp) < sizeof (struct ether_header))
3823147Sxc151355 		return (NULL);
3833147Sxc151355 
3846990Sgd78059 	ehp = (void *)mp->b_rptr;
3853147Sxc151355 	llmp = mac_wifi_header(&ehp->ether_shost, &ehp->ether_dhost,
3863147Sxc151355 	    ntohs(ehp->ether_type), pdata, NULL, 0);
3873147Sxc151355 	if (llmp == NULL)
3883147Sxc151355 		return (NULL);
3893147Sxc151355 
3903147Sxc151355 	/*
3913147Sxc151355 	 * The plugin framework guarantees that we have the only reference
3923147Sxc151355 	 * to the mblk_t, so we can safely modify it.
3933147Sxc151355 	 */
3943147Sxc151355 	ASSERT(DB_REF(mp) == 1);
3953147Sxc151355 	mp->b_rptr += sizeof (struct ether_header);
3963147Sxc151355 	llmp->b_cont = mp;
3973147Sxc151355 	return (llmp);
3983147Sxc151355 }
3993147Sxc151355 
4003147Sxc151355 /*
4013147Sxc151355  * Take the provided `mp' (which is expected to have a WiFi header), and
4023147Sxc151355  * return a pointer to an mblk_t with an Ethernet header.  If the conversion
4033147Sxc151355  * cannot be performed, return NULL.
4043147Sxc151355  */
4053147Sxc151355 static mblk_t *
mac_wifi_header_uncook(mblk_t * mp,void * pdata)4063147Sxc151355 mac_wifi_header_uncook(mblk_t *mp, void *pdata)
4073147Sxc151355 {
4083147Sxc151355 	mac_header_info_t	mhi;
4093147Sxc151355 	struct ether_header	eh;
4103147Sxc151355 
4113147Sxc151355 	if (mac_wifi_header_info(mp, pdata, &mhi) != 0) {
4123147Sxc151355 		/*
4133147Sxc151355 		 * The plugin framework guarantees the header is properly
4143147Sxc151355 		 * formed, so this should never happen.
4153147Sxc151355 		 */
4163147Sxc151355 		return (NULL);
4173147Sxc151355 	}
4183147Sxc151355 
4193147Sxc151355 	/*
4203147Sxc151355 	 * The plugin framework guarantees that we have the only reference to
4213147Sxc151355 	 * the mblk_t and the underlying dblk_t, so we can safely modify it.
4223147Sxc151355 	 */
4233147Sxc151355 	ASSERT(DB_REF(mp) == 1);
4243147Sxc151355 
4253147Sxc151355 	IEEE80211_ADDR_COPY(&eh.ether_dhost, mhi.mhi_daddr);
4263147Sxc151355 	IEEE80211_ADDR_COPY(&eh.ether_shost, mhi.mhi_saddr);
4273147Sxc151355 	eh.ether_type = htons(mhi.mhi_origsap);
4283147Sxc151355 
4293147Sxc151355 	ASSERT(mhi.mhi_hdrsize >= sizeof (struct ether_header));
4303147Sxc151355 	mp->b_rptr += mhi.mhi_hdrsize - sizeof (struct ether_header);
4313147Sxc151355 	bcopy(&eh, mp->b_rptr, sizeof (struct ether_header));
4323147Sxc151355 	return (mp);
4333147Sxc151355 }
4343147Sxc151355 
4353147Sxc151355 static mactype_ops_t mac_wifi_type_ops = {
4363147Sxc151355 	MTOPS_PDATA_VERIFY | MTOPS_HEADER_COOK | MTOPS_HEADER_UNCOOK,
4373147Sxc151355 	mac_wifi_unicst_verify,
4383147Sxc151355 	mac_wifi_multicst_verify,
4393147Sxc151355 	mac_wifi_sap_verify,
4403147Sxc151355 	mac_wifi_header,
4413147Sxc151355 	mac_wifi_header_info,
4423147Sxc151355 	mac_wifi_pdata_verify,
4433147Sxc151355 	mac_wifi_header_cook,
4443147Sxc151355 	mac_wifi_header_uncook
4453147Sxc151355 };
446