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(7) */ 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