123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- /*
- * Wireless utility functions
- *
- * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
- */
- #include <linux/bitops.h>
- #include <linux/etherdevice.h>
- #include <net/cfg80211.h>
- #include <net/ip.h>
- #include "core.h"
- struct ieee80211_rate *
- ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
- u32 basic_rates, int bitrate)
- {
- struct ieee80211_rate *result = &sband->bitrates[0];
- int i;
- for (i = 0; i < sband->n_bitrates; i++) {
- if (!(basic_rates & BIT(i)))
- continue;
- if (sband->bitrates[i].bitrate > bitrate)
- continue;
- result = &sband->bitrates[i];
- }
- return result;
- }
- EXPORT_SYMBOL(ieee80211_get_response_rate);
- int ieee80211_channel_to_frequency(int chan)
- {
- if (chan < 14)
- return 2407 + chan * 5;
- if (chan == 14)
- return 2484;
- /* FIXME: 802.11j 17.3.8.3.2 */
- return (chan + 1000) * 5;
- }
- EXPORT_SYMBOL(ieee80211_channel_to_frequency);
- int ieee80211_frequency_to_channel(int freq)
- {
- if (freq == 2484)
- return 14;
- if (freq < 2484)
- return (freq - 2407) / 5;
- /* FIXME: 802.11j 17.3.8.3.2 */
- return freq/5 - 1000;
- }
- EXPORT_SYMBOL(ieee80211_frequency_to_channel);
- struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy,
- int freq)
- {
- enum ieee80211_band band;
- struct ieee80211_supported_band *sband;
- int i;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
- sband = wiphy->bands[band];
- if (!sband)
- continue;
- for (i = 0; i < sband->n_channels; i++) {
- if (sband->channels[i].center_freq == freq)
- return &sband->channels[i];
- }
- }
- return NULL;
- }
- EXPORT_SYMBOL(__ieee80211_get_channel);
- static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
- enum ieee80211_band band)
- {
- int i, want;
- switch (band) {
- case IEEE80211_BAND_5GHZ:
- want = 3;
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].bitrate == 60 ||
- sband->bitrates[i].bitrate == 120 ||
- sband->bitrates[i].bitrate == 240) {
- sband->bitrates[i].flags |=
- IEEE80211_RATE_MANDATORY_A;
- want--;
- }
- }
- WARN_ON(want);
- break;
- case IEEE80211_BAND_2GHZ:
- want = 7;
- for (i = 0; i < sband->n_bitrates; i++) {
- if (sband->bitrates[i].bitrate == 10) {
- sband->bitrates[i].flags |=
- IEEE80211_RATE_MANDATORY_B |
- IEEE80211_RATE_MANDATORY_G;
- want--;
- }
- if (sband->bitrates[i].bitrate == 20 ||
- sband->bitrates[i].bitrate == 55 ||
- sband->bitrates[i].bitrate == 110 ||
- sband->bitrates[i].bitrate == 60 ||
- sband->bitrates[i].bitrate == 120 ||
- sband->bitrates[i].bitrate == 240) {
- sband->bitrates[i].flags |=
- IEEE80211_RATE_MANDATORY_G;
- want--;
- }
- if (sband->bitrates[i].bitrate != 10 &&
- sband->bitrates[i].bitrate != 20 &&
- sband->bitrates[i].bitrate != 55 &&
- sband->bitrates[i].bitrate != 110)
- sband->bitrates[i].flags |=
- IEEE80211_RATE_ERP_G;
- }
- WARN_ON(want != 0 && want != 3 && want != 6);
- break;
- case IEEE80211_NUM_BANDS:
- WARN_ON(1);
- break;
- }
- }
- void ieee80211_set_bitrate_flags(struct wiphy *wiphy)
- {
- enum ieee80211_band band;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
- if (wiphy->bands[band])
- set_mandatory_flags_band(wiphy->bands[band], band);
- }
- int cfg80211_validate_key_settings(struct key_params *params, int key_idx,
- const u8 *mac_addr)
- {
- if (key_idx > 5)
- return -EINVAL;
- /*
- * Disallow pairwise keys with non-zero index unless it's WEP
- * (because current deployments use pairwise WEP keys with
- * non-zero indizes but 802.11i clearly specifies to use zero)
- */
- if (mac_addr && key_idx &&
- params->cipher != WLAN_CIPHER_SUITE_WEP40 &&
- params->cipher != WLAN_CIPHER_SUITE_WEP104)
- return -EINVAL;
- switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- if (params->key_len != WLAN_KEY_LEN_WEP40)
- return -EINVAL;
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- if (params->key_len != WLAN_KEY_LEN_TKIP)
- return -EINVAL;
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- if (params->key_len != WLAN_KEY_LEN_CCMP)
- return -EINVAL;
- break;
- case WLAN_CIPHER_SUITE_WEP104:
- if (params->key_len != WLAN_KEY_LEN_WEP104)
- return -EINVAL;
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- if (params->key_len != WLAN_KEY_LEN_AES_CMAC)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- if (params->seq) {
- switch (params->cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- /* These ciphers do not use key sequence */
- return -EINVAL;
- case WLAN_CIPHER_SUITE_TKIP:
- case WLAN_CIPHER_SUITE_CCMP:
- case WLAN_CIPHER_SUITE_AES_CMAC:
- if (params->seq_len != 6)
- return -EINVAL;
- break;
- }
- }
- return 0;
- }
- /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
- /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
- const unsigned char rfc1042_header[] __aligned(2) =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
- EXPORT_SYMBOL(rfc1042_header);
- /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */
- const unsigned char bridge_tunnel_header[] __aligned(2) =
- { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
- EXPORT_SYMBOL(bridge_tunnel_header);
- unsigned int ieee80211_hdrlen(__le16 fc)
- {
- unsigned int hdrlen = 24;
- if (ieee80211_is_data(fc)) {
- if (ieee80211_has_a4(fc))
- hdrlen = 30;
- if (ieee80211_is_data_qos(fc))
- hdrlen += IEEE80211_QOS_CTL_LEN;
- goto out;
- }
- if (ieee80211_is_ctl(fc)) {
- /*
- * ACK and CTS are 10 bytes, all others 16. To see how
- * to get this condition consider
- * subtype mask: 0b0000000011110000 (0x00F0)
- * ACK subtype: 0b0000000011010000 (0x00D0)
- * CTS subtype: 0b0000000011000000 (0x00C0)
- * bits that matter: ^^^ (0x00E0)
- * value of those: 0b0000000011000000 (0x00C0)
- */
- if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0))
- hdrlen = 10;
- else
- hdrlen = 16;
- }
- out:
- return hdrlen;
- }
- EXPORT_SYMBOL(ieee80211_hdrlen);
- unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
- {
- const struct ieee80211_hdr *hdr =
- (const struct ieee80211_hdr *)skb->data;
- unsigned int hdrlen;
- if (unlikely(skb->len < 10))
- return 0;
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if (unlikely(hdrlen > skb->len))
- return 0;
- return hdrlen;
- }
- EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
- static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
- {
- int ae = meshhdr->flags & MESH_FLAGS_AE;
- /* 7.1.3.5a.2 */
- switch (ae) {
- case 0:
- return 6;
- case 1:
- return 12;
- case 2:
- return 18;
- case 3:
- return 24;
- default:
- return 6;
- }
- }
- int ieee80211_data_to_8023(struct sk_buff *skb, u8 *addr,
- enum nl80211_iftype iftype)
- {
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 hdrlen, ethertype;
- u8 *payload;
- u8 dst[ETH_ALEN];
- u8 src[ETH_ALEN] __aligned(2);
- if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
- return -1;
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
- /* convert IEEE 802.11 header + possible LLC headers into Ethernet
- * header
- * IEEE 802.11 address fields:
- * ToDS FromDS Addr1 Addr2 Addr3 Addr4
- * 0 0 DA SA BSSID n/a
- * 0 1 DA BSSID SA n/a
- * 1 0 BSSID SA DA n/a
- * 1 1 RA TA DA SA
- */
- memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN);
- memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN);
- switch (hdr->frame_control &
- cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) {
- case cpu_to_le16(IEEE80211_FCTL_TODS):
- if (unlikely(iftype != NL80211_IFTYPE_AP &&
- iftype != NL80211_IFTYPE_AP_VLAN))
- return -1;
- break;
- case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS):
- if (unlikely(iftype != NL80211_IFTYPE_WDS &&
- iftype != NL80211_IFTYPE_MESH_POINT))
- return -1;
- if (iftype == NL80211_IFTYPE_MESH_POINT) {
- struct ieee80211s_hdr *meshdr =
- (struct ieee80211s_hdr *) (skb->data + hdrlen);
- hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
- if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
- memcpy(dst, meshdr->eaddr1, ETH_ALEN);
- memcpy(src, meshdr->eaddr2, ETH_ALEN);
- }
- }
- break;
- case cpu_to_le16(IEEE80211_FCTL_FROMDS):
- if (iftype != NL80211_IFTYPE_STATION ||
- (is_multicast_ether_addr(dst) &&
- !compare_ether_addr(src, addr)))
- return -1;
- break;
- case cpu_to_le16(0):
- if (iftype != NL80211_IFTYPE_ADHOC)
- return -1;
- break;
- }
- if (unlikely(skb->len - hdrlen < 8))
- return -1;
- payload = skb->data + hdrlen;
- ethertype = (payload[6] << 8) | payload[7];
- if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
- ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
- compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
- /* remove RFC1042 or Bridge-Tunnel encapsulation and
- * replace EtherType */
- skb_pull(skb, hdrlen + 6);
- memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN);
- memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN);
- } else {
- struct ethhdr *ehdr;
- __be16 len;
- skb_pull(skb, hdrlen);
- len = htons(skb->len);
- ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr));
- memcpy(ehdr->h_dest, dst, ETH_ALEN);
- memcpy(ehdr->h_source, src, ETH_ALEN);
- ehdr->h_proto = len;
- }
- return 0;
- }
- EXPORT_SYMBOL(ieee80211_data_to_8023);
- int ieee80211_data_from_8023(struct sk_buff *skb, u8 *addr,
- enum nl80211_iftype iftype, u8 *bssid, bool qos)
- {
- struct ieee80211_hdr hdr;
- u16 hdrlen, ethertype;
- __le16 fc;
- const u8 *encaps_data;
- int encaps_len, skip_header_bytes;
- int nh_pos, h_pos;
- int head_need;
- if (unlikely(skb->len < ETH_HLEN))
- return -EINVAL;
- nh_pos = skb_network_header(skb) - skb->data;
- h_pos = skb_transport_header(skb) - skb->data;
- /* convert Ethernet header to proper 802.11 header (based on
- * operation mode) */
- ethertype = (skb->data[12] << 8) | skb->data[13];
- fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
- switch (iftype) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
- /* DA BSSID SA */
- memcpy(hdr.addr1, skb->data, ETH_ALEN);
- memcpy(hdr.addr2, addr, ETH_ALEN);
- memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
- hdrlen = 24;
- break;
- case NL80211_IFTYPE_STATION:
- fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
- /* BSSID SA DA */
- memcpy(hdr.addr1, bssid, ETH_ALEN);
- memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- hdrlen = 24;
- break;
- case NL80211_IFTYPE_ADHOC:
- /* DA SA BSSID */
- memcpy(hdr.addr1, skb->data, ETH_ALEN);
- memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, bssid, ETH_ALEN);
- hdrlen = 24;
- break;
- default:
- return -EOPNOTSUPP;
- }
- if (qos) {
- fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
- hdrlen += 2;
- }
- hdr.frame_control = fc;
- hdr.duration_id = 0;
- hdr.seq_ctrl = 0;
- skip_header_bytes = ETH_HLEN;
- if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) {
- encaps_data = bridge_tunnel_header;
- encaps_len = sizeof(bridge_tunnel_header);
- skip_header_bytes -= 2;
- } else if (ethertype > 0x600) {
- encaps_data = rfc1042_header;
- encaps_len = sizeof(rfc1042_header);
- skip_header_bytes -= 2;
- } else {
- encaps_data = NULL;
- encaps_len = 0;
- }
- skb_pull(skb, skip_header_bytes);
- nh_pos -= skip_header_bytes;
- h_pos -= skip_header_bytes;
- head_need = hdrlen + encaps_len - skb_headroom(skb);
- if (head_need > 0 || skb_cloned(skb)) {
- head_need = max(head_need, 0);
- if (head_need)
- skb_orphan(skb);
- if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) {
- printk(KERN_ERR "failed to reallocate Tx buffer\n");
- return -ENOMEM;
- }
- skb->truesize += head_need;
- }
- if (encaps_data) {
- memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len);
- nh_pos += encaps_len;
- h_pos += encaps_len;
- }
- memcpy(skb_push(skb, hdrlen), &hdr, hdrlen);
- nh_pos += hdrlen;
- h_pos += hdrlen;
- /* Update skb pointers to various headers since this modified frame
- * is going to go through Linux networking code that may potentially
- * need things like pointer to IP header. */
- skb_set_mac_header(skb, 0);
- skb_set_network_header(skb, nh_pos);
- skb_set_transport_header(skb, h_pos);
- return 0;
- }
- EXPORT_SYMBOL(ieee80211_data_from_8023);
- /* Given a data frame determine the 802.1p/1d tag to use. */
- unsigned int cfg80211_classify8021d(struct sk_buff *skb)
- {
- unsigned int dscp;
- /* skb->priority values from 256->263 are magic values to
- * directly indicate a specific 802.1d priority. This is used
- * to allow 802.1d priority to be passed directly in from VLAN
- * tags, etc.
- */
- if (skb->priority >= 256 && skb->priority <= 263)
- return skb->priority - 256;
- switch (skb->protocol) {
- case htons(ETH_P_IP):
- dscp = ip_hdr(skb)->tos & 0xfc;
- break;
- default:
- return 0;
- }
- return dscp >> 5;
- }
- EXPORT_SYMBOL(cfg80211_classify8021d);
|