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