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 };