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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * WiFi MAC Type plugin for the Nemo mac module
29 * This is a bit of mutant since we pretend to be mostly DL_ETHER.
32 #include <sys/types.h>
33 #include <sys/modctl.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>
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 };
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 }
63 static struct modlmisc mac_wifi_modlmisc
= {
68 static struct modlinkage mac_wifi_modlinkage
= {
74 static mactype_ops_t mac_wifi_type_ops
;
79 mactype_register_t
*mtrp
= mactype_alloc(MACTYPE_VERSION
);
83 * If `mtrp' is NULL, then this plugin is not compatible with
84 * the system's MAC Type plugin framework.
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
;
98 if ((err
= mactype_register(mtrp
)) == 0) {
99 if ((err
= mod_install(&mac_wifi_modlinkage
)) != 0)
100 (void) mactype_unregister(MAC_PLUGIN_IDENT_WIFI
);
111 if ((err
= mactype_unregister(MAC_PLUGIN_IDENT_WIFI
)) != 0)
113 return (mod_remove(&mac_wifi_modlinkage
));
117 _info(struct modinfo
*modinfop
)
119 return (mod_info(&mac_wifi_modlinkage
, modinfop
));
123 * MAC Type plugin operations
127 mac_wifi_pdata_verify(void *pdata
, size_t pdata_size
)
129 wifi_data_t
*wdp
= pdata
;
131 return (pdata_size
== sizeof (wifi_data_t
) && wdp
->wd_opts
== 0);
136 mac_wifi_unicst_verify(const void *addr
, void *pdata
)
138 /* If it's not a group address, then it's a valid unicast address. */
139 return (IEEE80211_IS_MULTICAST(addr
) ? EINVAL
: 0);
144 mac_wifi_multicst_verify(const void *addr
, void *pdata
)
146 /* The address must be a group address. */
147 if (!IEEE80211_IS_MULTICAST(addr
))
149 /* The address must not be the media broadcast address. */
150 if (bcmp(addr
, wifi_bcastaddr
, sizeof (wifi_bcastaddr
)) == 0)
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.
161 mac_wifi_sap_verify(uint32_t sap
, uint32_t *bind_sap
, void *pdata
)
163 if (sap
>= ETHERTYPE_802_MIN
&& sap
<= ETHERTYPE_MAX
) {
164 if (bind_sap
!= NULL
)
169 if (sap
<= ETHERMTU
) {
170 if (bind_sap
!= NULL
)
171 *bind_sap
= DLS_SAP_LLC
;
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.
184 mac_wifi_header(const void *saddr
, const void *daddr
, uint32_t sap
,
185 void *pdata
, mblk_t
*payload
, size_t extra_len
)
187 struct ieee80211_frame
*wh
;
188 struct ieee80211_llc
*llc
;
190 wifi_data_t
*wdp
= pdata
;
192 if (!mac_wifi_sap_verify(sap
, NULL
, NULL
))
195 if ((mp
= allocb(WIFI_HDRSIZE
+ extra_len
, BPRI_HI
)) == NULL
)
197 bzero(mp
->b_rptr
, WIFI_HDRSIZE
+ extra_len
);
200 * Fill in the fixed parts of the ieee80211_frame.
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
;
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
);
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
);
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
);
230 if (wdp
->wd_qospad
) {
231 struct ieee80211_qosframe
*qwh
=
232 (struct ieee80211_qosframe
*)wh
;
234 qwh
->i_fc
[0] |= IEEE80211_FC0_SUBTYPE_QOS
;
237 switch (wdp
->wd_secalloc
) {
240 * Fill in the fixed parts of the WEP-portion of the frame.
242 wh
->i_fc
[1] |= IEEE80211_FC1_WEP
;
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.
248 mp
->b_wptr
+= IEEE80211_WEP_IVLEN
+ IEEE80211_WEP_KIDLEN
;
252 wh
->i_fc
[1] |= IEEE80211_FC1_WEP
;
253 mp
->b_wptr
+= IEEE80211_WEP_IVLEN
+
254 IEEE80211_WEP_KIDLEN
+ IEEE80211_WEP_EXTIVLEN
;
262 * Fill in the fixed parts of the ieee80211_llc header.
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
);
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.
278 mac_wifi_header_info(mblk_t
*mp
, void *pdata
, mac_header_info_t
*mhp
)
280 struct ieee80211_frame
*wh
;
281 struct ieee80211_llc
*llc
;
283 wifi_data_t
*wdp
= pdata
;
285 if (MBLKL(mp
) < sizeof (struct ieee80211_frame
))
288 wh
= (struct ieee80211_frame
*)mp
->b_rptr
;
289 llcp
= mp
->b_rptr
+ sizeof (struct ieee80211_frame
);
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.
296 if (wh
->i_fc
[0] & IEEE80211_FC0_SUBTYPE_QOS
)
297 llcp
+= wdp
->wd_qospad
;
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
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
;
312 if ((uintptr_t)mp
->b_wptr
- (uintptr_t)llcp
<
313 sizeof (struct ieee80211_llc
))
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
;
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.
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) {
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
;
344 case IEEE80211_FC1_DIR_TODS
:
345 mhp
->mhi_daddr
= wh
->i_addr3
;
346 mhp
->mhi_saddr
= wh
->i_addr2
;
349 case IEEE80211_FC1_DIR_FROMDS
:
350 mhp
->mhi_daddr
= wh
->i_addr1
;
351 mhp
->mhi_saddr
= wh
->i_addr3
;
354 case IEEE80211_FC1_DIR_DSTODS
:
355 /* We don't support AP-to-AP mode yet */
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
;
364 mhp
->mhi_dsttype
= MAC_ADDRTYPE_BROADCAST
;
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.
376 mac_wifi_header_cook(mblk_t
*mp
, void *pdata
)
378 struct ether_header
*ehp
;
381 if (MBLKL(mp
) < sizeof (struct ether_header
))
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);
391 * The plugin framework guarantees that we have the only reference
392 * to the mblk_t, so we can safely modify it.
394 ASSERT(DB_REF(mp
) == 1);
395 mp
->b_rptr
+= sizeof (struct ether_header
);
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.
406 mac_wifi_header_uncook(mblk_t
*mp
, void *pdata
)
408 mac_header_info_t mhi
;
409 struct ether_header eh
;
411 if (mac_wifi_header_info(mp
, pdata
, &mhi
) != 0) {
413 * The plugin framework guarantees the header is properly
414 * formed, so this should never happen.
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.
423 ASSERT(DB_REF(mp
) == 1);
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
);
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
));
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
,
441 mac_wifi_header_info
,
442 mac_wifi_pdata_verify
,
443 mac_wifi_header_cook
,
444 mac_wifi_header_uncook