Home | History | Annotate | Download | only in plugins
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * WiFi MAC Type plugin for the Nemo mac module
     28  *
     29  * This is a bit of mutant since we pretend to be mostly DL_ETHER.
     30  */
     31 
     32 #include <sys/types.h>
     33 #include <sys/modctl.h>
     34 #include <sys/dlpi.h>
     35 #include <sys/dld_impl.h>
     36 #include <sys/mac_wifi.h>
     37 #include <sys/ethernet.h>
     38 #include <sys/byteorder.h>
     39 #include <sys/strsun.h>
     40 #include <inet/common.h>
     41 
     42 uint8_t wifi_bcastaddr[]	= { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
     43 static uint8_t wifi_ietfmagic[]	= { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
     44 static uint8_t wifi_ieeemagic[]	= { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
     45 
     46 static mac_stat_info_t wifi_stats[] = {
     47 	/* statistics described in ieee802.11(5) */
     48 { WIFI_STAT_TX_FRAGS, 		"tx_frags",		KSTAT_DATA_UINT32, 0 },
     49 { WIFI_STAT_MCAST_TX,		"mcast_tx",		KSTAT_DATA_UINT32, 0 },
     50 { WIFI_STAT_TX_FAILED,		"tx_failed",		KSTAT_DATA_UINT32, 0 },
     51 { WIFI_STAT_TX_RETRANS,		"tx_retrans",		KSTAT_DATA_UINT32, 0 },
     52 { WIFI_STAT_TX_RERETRANS,	"tx_reretrans",		KSTAT_DATA_UINT32, 0 },
     53 { WIFI_STAT_RTS_SUCCESS,	"rts_success",		KSTAT_DATA_UINT32, 0 },
     54 { WIFI_STAT_RTS_FAILURE,	"rts_failure",		KSTAT_DATA_UINT32, 0 },
     55 { WIFI_STAT_ACK_FAILURE,	"ack_failure",		KSTAT_DATA_UINT32, 0 },
     56 { WIFI_STAT_RX_FRAGS, 		"rx_frags",		KSTAT_DATA_UINT32, 0 },
     57 { WIFI_STAT_MCAST_RX,		"mcast_rx", 		KSTAT_DATA_UINT32, 0 },
     58 { WIFI_STAT_FCS_ERRORS,		"fcs_errors", 		KSTAT_DATA_UINT32, 0 },
     59 { WIFI_STAT_WEP_ERRORS,		"wep_errors",		KSTAT_DATA_UINT32, 0 },
     60 { WIFI_STAT_RX_DUPS,		"rx_dups",		KSTAT_DATA_UINT32, 0 }
     61 };
     62 
     63 static struct modlmisc mac_wifi_modlmisc = {
     64 	&mod_miscops,
     65 	"WiFi MAC plugin 1.4"
     66 };
     67 
     68 static struct modlinkage mac_wifi_modlinkage = {
     69 	MODREV_1,
     70 	&mac_wifi_modlmisc,
     71 	NULL
     72 };
     73 
     74 static mactype_ops_t mac_wifi_type_ops;
     75 
     76 int
     77 _init(void)
     78 {
     79 	mactype_register_t *mtrp = mactype_alloc(MACTYPE_VERSION);
     80 	int err;
     81 
     82 	/*
     83 	 * If `mtrp' is NULL, then this plugin is not compatible with
     84 	 * the system's MAC Type plugin framework.
     85 	 */
     86 	if (mtrp == NULL)
     87 		return (ENOTSUP);
     88 
     89 	mtrp->mtr_ops		= &mac_wifi_type_ops;
     90 	mtrp->mtr_ident		= MAC_PLUGIN_IDENT_WIFI;
     91 	mtrp->mtr_mactype	= DL_ETHER;
     92 	mtrp->mtr_nativetype	= DL_WIFI;
     93 	mtrp->mtr_stats		= wifi_stats;
     94 	mtrp->mtr_statcount	= A_CNT(wifi_stats);
     95 	mtrp->mtr_addrlen	= IEEE80211_ADDR_LEN;
     96 	mtrp->mtr_brdcst_addr	= wifi_bcastaddr;
     97 
     98 	if ((err = mactype_register(mtrp)) == 0) {
     99 		if ((err = mod_install(&mac_wifi_modlinkage)) != 0)
    100 			(void) mactype_unregister(MAC_PLUGIN_IDENT_WIFI);
    101 	}
    102 	mactype_free(mtrp);
    103 	return (err);
    104 }
    105 
    106 int
    107 _fini(void)
    108 {
    109 	int	err;
    110 
    111 	if ((err = mactype_unregister(MAC_PLUGIN_IDENT_WIFI)) != 0)
    112 		return (err);
    113 	return (mod_remove(&mac_wifi_modlinkage));
    114 }
    115 
    116 int
    117 _info(struct modinfo *modinfop)
    118 {
    119 	return (mod_info(&mac_wifi_modlinkage, modinfop));
    120 }
    121 
    122 /*
    123  * MAC Type plugin operations
    124  */
    125 
    126 static boolean_t
    127 mac_wifi_pdata_verify(void *pdata, size_t pdata_size)
    128 {
    129 	wifi_data_t *wdp = pdata;
    130 
    131 	return (pdata_size == sizeof (wifi_data_t) && wdp->wd_opts == 0);
    132 }
    133 
    134 /* ARGSUSED */
    135 static int
    136 mac_wifi_unicst_verify(const void *addr, void *pdata)
    137 {
    138 	/* If it's not a group address, then it's a valid unicast address. */
    139 	return (IEEE80211_IS_MULTICAST(addr) ? EINVAL : 0);
    140 }
    141 
    142 /* ARGSUSED */
    143 static int
    144 mac_wifi_multicst_verify(const void *addr, void *pdata)
    145 {
    146 	/* The address must be a group address. */
    147 	if (!IEEE80211_IS_MULTICAST(addr))
    148 		return (EINVAL);
    149 	/* The address must not be the media broadcast address. */
    150 	if (bcmp(addr, wifi_bcastaddr, sizeof (wifi_bcastaddr)) == 0)
    151 		return (EINVAL);
    152 	return (0);
    153 }
    154 
    155 /*
    156  * Verify that `sap' is valid, and return the actual SAP to bind to in
    157  * `*bind_sap'.  The WiFI SAP space is identical to Ethernet.
    158  */
    159 /* ARGSUSED */
    160 static boolean_t
    161 mac_wifi_sap_verify(uint32_t sap, uint32_t *bind_sap, void *pdata)
    162 {
    163 	if (sap >= ETHERTYPE_802_MIN && sap <= ETHERTYPE_MAX) {
    164 		if (bind_sap != NULL)
    165 			*bind_sap = sap;
    166 		return (B_TRUE);
    167 	}
    168 
    169 	if (sap <= ETHERMTU) {
    170 		if (bind_sap != NULL)
    171 			*bind_sap = DLS_SAP_LLC;
    172 		return (B_TRUE);
    173 	}
    174 	return (B_FALSE);
    175 }
    176 
    177 /*
    178  * Create a template WiFi datalink header for `sap' packets between `saddr'
    179  * and `daddr'.  Any enabled modes and features relevant to building the
    180  * header are passed via `pdata'.  Return NULL on failure.
    181  */
    182 /* ARGSUSED */
    183 static mblk_t *
    184 mac_wifi_header(const void *saddr, const void *daddr, uint32_t sap,
    185     void *pdata, mblk_t *payload, size_t extra_len)
    186 {
    187 	struct ieee80211_frame	*wh;
    188 	struct ieee80211_llc	*llc;
    189 	mblk_t			*mp;
    190 	wifi_data_t		*wdp = pdata;
    191 
    192 	if (!mac_wifi_sap_verify(sap, NULL, NULL))
    193 		return (NULL);
    194 
    195 	if ((mp = allocb(WIFI_HDRSIZE + extra_len, BPRI_HI)) == NULL)
    196 		return (NULL);
    197 	bzero(mp->b_rptr, WIFI_HDRSIZE + extra_len);
    198 
    199 	/*
    200 	 * Fill in the fixed parts of the ieee80211_frame.
    201 	 */
    202 	wh = (struct ieee80211_frame *)mp->b_rptr;
    203 	mp->b_wptr += sizeof (struct ieee80211_frame) + wdp->wd_qospad;
    204 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
    205 
    206 	switch (wdp->wd_opmode) {
    207 	case IEEE80211_M_STA:
    208 		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
    209 		IEEE80211_ADDR_COPY(wh->i_addr1, wdp->wd_bssid);
    210 		IEEE80211_ADDR_COPY(wh->i_addr2, saddr);
    211 		IEEE80211_ADDR_COPY(wh->i_addr3, daddr);
    212 		break;
    213 
    214 	case IEEE80211_M_IBSS:
    215 	case IEEE80211_M_AHDEMO:
    216 		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
    217 		IEEE80211_ADDR_COPY(wh->i_addr1, daddr);
    218 		IEEE80211_ADDR_COPY(wh->i_addr2, saddr);
    219 		IEEE80211_ADDR_COPY(wh->i_addr3, wdp->wd_bssid);
    220 		break;
    221 
    222 	case IEEE80211_M_HOSTAP:
    223 		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
    224 		IEEE80211_ADDR_COPY(wh->i_addr1, daddr);
    225 		IEEE80211_ADDR_COPY(wh->i_addr2, wdp->wd_bssid);
    226 		IEEE80211_ADDR_COPY(wh->i_addr3, saddr);
    227 		break;
    228 	}
    229 
    230 	if (wdp->wd_qospad) {
    231 		struct ieee80211_qosframe *qwh =
    232 		    (struct ieee80211_qosframe *)wh;
    233 		qwh->i_qos[1] = 0;
    234 		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
    235 	}
    236 
    237 	switch (wdp->wd_secalloc) {
    238 	case WIFI_SEC_WEP:
    239 		/*
    240 		 * Fill in the fixed parts of the WEP-portion of the frame.
    241 		 */
    242 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
    243 		/*
    244 		 * The actual contents of the WEP-portion of the packet
    245 		 * are computed when the packet is sent -- for now, we
    246 		 * just need to account for the size.
    247 		 */
    248 		mp->b_wptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
    249 		break;
    250 
    251 	case WIFI_SEC_WPA:
    252 		wh->i_fc[1] |= IEEE80211_FC1_WEP;
    253 		mp->b_wptr += IEEE80211_WEP_IVLEN +
    254 		    IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN;
    255 		break;
    256 
    257 	default:
    258 		break;
    259 	}
    260 
    261 	/*
    262 	 * Fill in the fixed parts of the ieee80211_llc header.
    263 	 */
    264 	llc = (struct ieee80211_llc *)mp->b_wptr;
    265 	mp->b_wptr += sizeof (struct ieee80211_llc);
    266 	bcopy(wifi_ietfmagic, llc, sizeof (wifi_ietfmagic));
    267 	llc->illc_ether_type = htons(sap);
    268 
    269 	return (mp);
    270 }
    271 
    272 /*
    273  * Use the provided `mp' (which is expected to point to a WiFi header), and
    274  * fill in the provided `mhp'.  Return an errno on failure.
    275  */
    276 /* ARGSUSED */
    277 static int
    278 mac_wifi_header_info(mblk_t *mp, void *pdata, mac_header_info_t *mhp)
    279 {
    280 	struct ieee80211_frame	*wh;
    281 	struct ieee80211_llc	*llc;
    282 	uchar_t			*llcp;
    283 	wifi_data_t		*wdp = pdata;
    284 
    285 	if (MBLKL(mp) < sizeof (struct ieee80211_frame))
    286 		return (EINVAL);
    287 
    288 	wh = (struct ieee80211_frame *)mp->b_rptr;
    289 	llcp = mp->b_rptr + sizeof (struct ieee80211_frame);
    290 
    291 	/*
    292 	 * Generally, QoS data field takes 2 bytes, but some special hardware,
    293 	 * such as Atheros, will need the 802.11 header padded to a 32-bit
    294 	 * boundary for 4-address and QoS frames, at this time, it's 4 bytes.
    295 	 */
    296 	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
    297 		llcp += wdp->wd_qospad;
    298 
    299 	/*
    300 	 * When we receive frames from other hosts, the hardware will have
    301 	 * already performed WEP decryption, and thus there will not be a WEP
    302 	 * portion.  However, when we receive a loopback copy of our own
    303 	 * packets, it will still have a WEP portion.  Skip past it to get to
    304 	 * the LLC header.
    305 	 */
    306 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
    307 		llcp += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
    308 		if (wdp->wd_secalloc == WIFI_SEC_WPA)
    309 			llcp += IEEE80211_WEP_EXTIVLEN;
    310 	}
    311 
    312 	if ((uintptr_t)mp->b_wptr - (uintptr_t)llcp <
    313 	    sizeof (struct ieee80211_llc))
    314 		return (EINVAL);
    315 
    316 	llc = (struct ieee80211_llc *)llcp;
    317 	mhp->mhi_origsap = ntohs(llc->illc_ether_type);
    318 	mhp->mhi_bindsap = mhp->mhi_origsap;
    319 	mhp->mhi_pktsize = 0;
    320 	mhp->mhi_hdrsize = (uintptr_t)llcp + sizeof (*llc) -
    321 	    (uintptr_t)mp->b_rptr;
    322 
    323 	/*
    324 	 * Verify the LLC header is one of the known formats.  As per MSFT's
    325 	 * convention, if the header is using IEEE 802.1H encapsulation, then
    326 	 * treat the LLC header as data.  As per DL_ETHER custom when treating
    327 	 * the LLC header as data, set the mhi_bindsap to be DLS_SAP_LLC, and
    328 	 * assume mhi_origsap contains the data length.
    329 	 */
    330 	if (bcmp(llc, wifi_ieeemagic, sizeof (wifi_ieeemagic)) == 0) {
    331 		mhp->mhi_bindsap = DLS_SAP_LLC;
    332 		mhp->mhi_hdrsize -= sizeof (*llc);
    333 		mhp->mhi_pktsize = mhp->mhi_hdrsize + mhp->mhi_origsap;
    334 	} else if (bcmp(llc, wifi_ietfmagic, sizeof (wifi_ietfmagic)) != 0) {
    335 		return (EINVAL);
    336 	}
    337 
    338 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
    339 	case IEEE80211_FC1_DIR_NODS:
    340 		mhp->mhi_daddr = wh->i_addr1;
    341 		mhp->mhi_saddr = wh->i_addr2;
    342 		break;
    343 
    344 	case IEEE80211_FC1_DIR_TODS:
    345 		mhp->mhi_daddr = wh->i_addr3;
    346 		mhp->mhi_saddr = wh->i_addr2;
    347 		break;
    348 
    349 	case IEEE80211_FC1_DIR_FROMDS:
    350 		mhp->mhi_daddr = wh->i_addr1;
    351 		mhp->mhi_saddr = wh->i_addr3;
    352 		break;
    353 
    354 	case IEEE80211_FC1_DIR_DSTODS:
    355 		/* We don't support AP-to-AP mode yet */
    356 		return (ENOTSUP);
    357 	}
    358 
    359 	if (mac_wifi_unicst_verify(mhp->mhi_daddr, NULL) == 0)
    360 		mhp->mhi_dsttype = MAC_ADDRTYPE_UNICAST;
    361 	else if (mac_wifi_multicst_verify(mhp->mhi_daddr, NULL) == 0)
    362 		mhp->mhi_dsttype = MAC_ADDRTYPE_MULTICAST;
    363 	else
    364 		mhp->mhi_dsttype = MAC_ADDRTYPE_BROADCAST;
    365 
    366 	return (0);
    367 }
    368 
    369 /*
    370  * Take the provided `mp' (which is expected to have an Ethernet header), and
    371  * return a pointer to an mblk_t with a WiFi header.  Note that the returned
    372  * header will not be complete until the driver finishes filling it in prior
    373  * to transmit.  If the conversion cannot be performed, return NULL.
    374  */
    375 static mblk_t *
    376 mac_wifi_header_cook(mblk_t *mp, void *pdata)
    377 {
    378 	struct ether_header	*ehp;
    379 	mblk_t			*llmp;
    380 
    381 	if (MBLKL(mp) < sizeof (struct ether_header))
    382 		return (NULL);
    383 
    384 	ehp = (void *)mp->b_rptr;
    385 	llmp = mac_wifi_header(&ehp->ether_shost, &ehp->ether_dhost,
    386 	    ntohs(ehp->ether_type), pdata, NULL, 0);
    387 	if (llmp == NULL)
    388 		return (NULL);
    389 
    390 	/*
    391 	 * The plugin framework guarantees that we have the only reference
    392 	 * to the mblk_t, so we can safely modify it.
    393 	 */
    394 	ASSERT(DB_REF(mp) == 1);
    395 	mp->b_rptr += sizeof (struct ether_header);
    396 	llmp->b_cont = mp;
    397 	return (llmp);
    398 }
    399 
    400 /*
    401  * Take the provided `mp' (which is expected to have a WiFi header), and
    402  * return a pointer to an mblk_t with an Ethernet header.  If the conversion
    403  * cannot be performed, return NULL.
    404  */
    405 static mblk_t *
    406 mac_wifi_header_uncook(mblk_t *mp, void *pdata)
    407 {
    408 	mac_header_info_t	mhi;
    409 	struct ether_header	eh;
    410 
    411 	if (mac_wifi_header_info(mp, pdata, &mhi) != 0) {
    412 		/*
    413 		 * The plugin framework guarantees the header is properly
    414 		 * formed, so this should never happen.
    415 		 */
    416 		return (NULL);
    417 	}
    418 
    419 	/*
    420 	 * The plugin framework guarantees that we have the only reference to
    421 	 * the mblk_t and the underlying dblk_t, so we can safely modify it.
    422 	 */
    423 	ASSERT(DB_REF(mp) == 1);
    424 
    425 	IEEE80211_ADDR_COPY(&eh.ether_dhost, mhi.mhi_daddr);
    426 	IEEE80211_ADDR_COPY(&eh.ether_shost, mhi.mhi_saddr);
    427 	eh.ether_type = htons(mhi.mhi_origsap);
    428 
    429 	ASSERT(mhi.mhi_hdrsize >= sizeof (struct ether_header));
    430 	mp->b_rptr += mhi.mhi_hdrsize - sizeof (struct ether_header);
    431 	bcopy(&eh, mp->b_rptr, sizeof (struct ether_header));
    432 	return (mp);
    433 }
    434 
    435 static mactype_ops_t mac_wifi_type_ops = {
    436 	MTOPS_PDATA_VERIFY | MTOPS_HEADER_COOK | MTOPS_HEADER_UNCOOK,
    437 	mac_wifi_unicst_verify,
    438 	mac_wifi_multicst_verify,
    439 	mac_wifi_sap_verify,
    440 	mac_wifi_header,
    441 	mac_wifi_header_info,
    442 	mac_wifi_pdata_verify,
    443 	mac_wifi_header_cook,
    444 	mac_wifi_header_uncook
    445 };
    446