diff options
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r-- | net/mac80211/util.c | 328 |
1 files changed, 295 insertions, 33 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d5230ecc784d..9919892575f4 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/etherdevice.h> | 20 | #include <linux/etherdevice.h> |
21 | #include <linux/if_arp.h> | 21 | #include <linux/if_arp.h> |
22 | #include <linux/bitmap.h> | 22 | #include <linux/bitmap.h> |
23 | #include <linux/crc32.h> | ||
23 | #include <net/net_namespace.h> | 24 | #include <net/net_namespace.h> |
24 | #include <net/cfg80211.h> | 25 | #include <net/cfg80211.h> |
25 | #include <net/rtnetlink.h> | 26 | #include <net/rtnetlink.h> |
@@ -96,13 +97,13 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, | |||
96 | 97 | ||
97 | void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) | 98 | void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx) |
98 | { | 99 | { |
99 | struct sk_buff *skb = tx->skb; | 100 | struct sk_buff *skb; |
100 | struct ieee80211_hdr *hdr; | 101 | struct ieee80211_hdr *hdr; |
101 | 102 | ||
102 | do { | 103 | skb_queue_walk(&tx->skbs, skb) { |
103 | hdr = (struct ieee80211_hdr *) skb->data; | 104 | hdr = (struct ieee80211_hdr *) skb->data; |
104 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); | 105 | hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); |
105 | } while ((skb = skb->next)); | 106 | } |
106 | } | 107 | } |
107 | 108 | ||
108 | int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, | 109 | int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, |
@@ -564,6 +565,172 @@ void ieee80211_queue_delayed_work(struct ieee80211_hw *hw, | |||
564 | } | 565 | } |
565 | EXPORT_SYMBOL(ieee80211_queue_delayed_work); | 566 | EXPORT_SYMBOL(ieee80211_queue_delayed_work); |
566 | 567 | ||
568 | u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, | ||
569 | struct ieee802_11_elems *elems, | ||
570 | u64 filter, u32 crc) | ||
571 | { | ||
572 | size_t left = len; | ||
573 | u8 *pos = start; | ||
574 | bool calc_crc = filter != 0; | ||
575 | |||
576 | memset(elems, 0, sizeof(*elems)); | ||
577 | elems->ie_start = start; | ||
578 | elems->total_len = len; | ||
579 | |||
580 | while (left >= 2) { | ||
581 | u8 id, elen; | ||
582 | |||
583 | id = *pos++; | ||
584 | elen = *pos++; | ||
585 | left -= 2; | ||
586 | |||
587 | if (elen > left) | ||
588 | break; | ||
589 | |||
590 | if (calc_crc && id < 64 && (filter & (1ULL << id))) | ||
591 | crc = crc32_be(crc, pos - 2, elen + 2); | ||
592 | |||
593 | switch (id) { | ||
594 | case WLAN_EID_SSID: | ||
595 | elems->ssid = pos; | ||
596 | elems->ssid_len = elen; | ||
597 | break; | ||
598 | case WLAN_EID_SUPP_RATES: | ||
599 | elems->supp_rates = pos; | ||
600 | elems->supp_rates_len = elen; | ||
601 | break; | ||
602 | case WLAN_EID_FH_PARAMS: | ||
603 | elems->fh_params = pos; | ||
604 | elems->fh_params_len = elen; | ||
605 | break; | ||
606 | case WLAN_EID_DS_PARAMS: | ||
607 | elems->ds_params = pos; | ||
608 | elems->ds_params_len = elen; | ||
609 | break; | ||
610 | case WLAN_EID_CF_PARAMS: | ||
611 | elems->cf_params = pos; | ||
612 | elems->cf_params_len = elen; | ||
613 | break; | ||
614 | case WLAN_EID_TIM: | ||
615 | if (elen >= sizeof(struct ieee80211_tim_ie)) { | ||
616 | elems->tim = (void *)pos; | ||
617 | elems->tim_len = elen; | ||
618 | } | ||
619 | break; | ||
620 | case WLAN_EID_IBSS_PARAMS: | ||
621 | elems->ibss_params = pos; | ||
622 | elems->ibss_params_len = elen; | ||
623 | break; | ||
624 | case WLAN_EID_CHALLENGE: | ||
625 | elems->challenge = pos; | ||
626 | elems->challenge_len = elen; | ||
627 | break; | ||
628 | case WLAN_EID_VENDOR_SPECIFIC: | ||
629 | if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && | ||
630 | pos[2] == 0xf2) { | ||
631 | /* Microsoft OUI (00:50:F2) */ | ||
632 | |||
633 | if (calc_crc) | ||
634 | crc = crc32_be(crc, pos - 2, elen + 2); | ||
635 | |||
636 | if (pos[3] == 1) { | ||
637 | /* OUI Type 1 - WPA IE */ | ||
638 | elems->wpa = pos; | ||
639 | elems->wpa_len = elen; | ||
640 | } else if (elen >= 5 && pos[3] == 2) { | ||
641 | /* OUI Type 2 - WMM IE */ | ||
642 | if (pos[4] == 0) { | ||
643 | elems->wmm_info = pos; | ||
644 | elems->wmm_info_len = elen; | ||
645 | } else if (pos[4] == 1) { | ||
646 | elems->wmm_param = pos; | ||
647 | elems->wmm_param_len = elen; | ||
648 | } | ||
649 | } | ||
650 | } | ||
651 | break; | ||
652 | case WLAN_EID_RSN: | ||
653 | elems->rsn = pos; | ||
654 | elems->rsn_len = elen; | ||
655 | break; | ||
656 | case WLAN_EID_ERP_INFO: | ||
657 | elems->erp_info = pos; | ||
658 | elems->erp_info_len = elen; | ||
659 | break; | ||
660 | case WLAN_EID_EXT_SUPP_RATES: | ||
661 | elems->ext_supp_rates = pos; | ||
662 | elems->ext_supp_rates_len = elen; | ||
663 | break; | ||
664 | case WLAN_EID_HT_CAPABILITY: | ||
665 | if (elen >= sizeof(struct ieee80211_ht_cap)) | ||
666 | elems->ht_cap_elem = (void *)pos; | ||
667 | break; | ||
668 | case WLAN_EID_HT_INFORMATION: | ||
669 | if (elen >= sizeof(struct ieee80211_ht_info)) | ||
670 | elems->ht_info_elem = (void *)pos; | ||
671 | break; | ||
672 | case WLAN_EID_MESH_ID: | ||
673 | elems->mesh_id = pos; | ||
674 | elems->mesh_id_len = elen; | ||
675 | break; | ||
676 | case WLAN_EID_MESH_CONFIG: | ||
677 | if (elen >= sizeof(struct ieee80211_meshconf_ie)) | ||
678 | elems->mesh_config = (void *)pos; | ||
679 | break; | ||
680 | case WLAN_EID_PEER_MGMT: | ||
681 | elems->peering = pos; | ||
682 | elems->peering_len = elen; | ||
683 | break; | ||
684 | case WLAN_EID_PREQ: | ||
685 | elems->preq = pos; | ||
686 | elems->preq_len = elen; | ||
687 | break; | ||
688 | case WLAN_EID_PREP: | ||
689 | elems->prep = pos; | ||
690 | elems->prep_len = elen; | ||
691 | break; | ||
692 | case WLAN_EID_PERR: | ||
693 | elems->perr = pos; | ||
694 | elems->perr_len = elen; | ||
695 | break; | ||
696 | case WLAN_EID_RANN: | ||
697 | if (elen >= sizeof(struct ieee80211_rann_ie)) | ||
698 | elems->rann = (void *)pos; | ||
699 | break; | ||
700 | case WLAN_EID_CHANNEL_SWITCH: | ||
701 | elems->ch_switch_elem = pos; | ||
702 | elems->ch_switch_elem_len = elen; | ||
703 | break; | ||
704 | case WLAN_EID_QUIET: | ||
705 | if (!elems->quiet_elem) { | ||
706 | elems->quiet_elem = pos; | ||
707 | elems->quiet_elem_len = elen; | ||
708 | } | ||
709 | elems->num_of_quiet_elem++; | ||
710 | break; | ||
711 | case WLAN_EID_COUNTRY: | ||
712 | elems->country_elem = pos; | ||
713 | elems->country_elem_len = elen; | ||
714 | break; | ||
715 | case WLAN_EID_PWR_CONSTRAINT: | ||
716 | elems->pwr_constr_elem = pos; | ||
717 | elems->pwr_constr_elem_len = elen; | ||
718 | break; | ||
719 | case WLAN_EID_TIMEOUT_INTERVAL: | ||
720 | elems->timeout_int = pos; | ||
721 | elems->timeout_int_len = elen; | ||
722 | break; | ||
723 | default: | ||
724 | break; | ||
725 | } | ||
726 | |||
727 | left -= elen; | ||
728 | pos += elen; | ||
729 | } | ||
730 | |||
731 | return crc; | ||
732 | } | ||
733 | |||
567 | void ieee802_11_parse_elems(u8 *start, size_t len, | 734 | void ieee802_11_parse_elems(u8 *start, size_t len, |
568 | struct ieee802_11_elems *elems) | 735 | struct ieee802_11_elems *elems) |
569 | { | 736 | { |
@@ -812,23 +979,9 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, | |||
812 | offset = noffset; | 979 | offset = noffset; |
813 | } | 980 | } |
814 | 981 | ||
815 | if (sband->ht_cap.ht_supported) { | 982 | if (sband->ht_cap.ht_supported) |
816 | u16 cap = sband->ht_cap.cap; | 983 | pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, |
817 | __le16 tmp; | 984 | sband->ht_cap.cap); |
818 | |||
819 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
820 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
821 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | ||
822 | tmp = cpu_to_le16(cap); | ||
823 | memcpy(pos, &tmp, sizeof(u16)); | ||
824 | pos += sizeof(u16); | ||
825 | *pos++ = sband->ht_cap.ampdu_factor | | ||
826 | (sband->ht_cap.ampdu_density << | ||
827 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
828 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | ||
829 | pos += sizeof(sband->ht_cap.mcs); | ||
830 | pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ | ||
831 | } | ||
832 | 985 | ||
833 | /* | 986 | /* |
834 | * If adding more here, adjust code in main.c | 987 | * If adding more here, adjust code in main.c |
@@ -989,16 +1142,6 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
989 | */ | 1142 | */ |
990 | } | 1143 | } |
991 | #endif | 1144 | #endif |
992 | |||
993 | /* setup fragmentation threshold */ | ||
994 | drv_set_frag_threshold(local, hw->wiphy->frag_threshold); | ||
995 | |||
996 | /* setup RTS threshold */ | ||
997 | drv_set_rts_threshold(local, hw->wiphy->rts_threshold); | ||
998 | |||
999 | /* reset coverage class */ | ||
1000 | drv_set_coverage_class(local, hw->wiphy->coverage_class); | ||
1001 | |||
1002 | /* everything else happens only if HW was up & running */ | 1145 | /* everything else happens only if HW was up & running */ |
1003 | if (!local->open_count) | 1146 | if (!local->open_count) |
1004 | goto wake_up; | 1147 | goto wake_up; |
@@ -1017,6 +1160,15 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1017 | return res; | 1160 | return res; |
1018 | } | 1161 | } |
1019 | 1162 | ||
1163 | /* setup fragmentation threshold */ | ||
1164 | drv_set_frag_threshold(local, hw->wiphy->frag_threshold); | ||
1165 | |||
1166 | /* setup RTS threshold */ | ||
1167 | drv_set_rts_threshold(local, hw->wiphy->rts_threshold); | ||
1168 | |||
1169 | /* reset coverage class */ | ||
1170 | drv_set_coverage_class(local, hw->wiphy->coverage_class); | ||
1171 | |||
1020 | ieee80211_led_radio(local, true); | 1172 | ieee80211_led_radio(local, true); |
1021 | ieee80211_mod_tpt_led_trig(local, | 1173 | ieee80211_mod_tpt_led_trig(local, |
1022 | IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); | 1174 | IEEE80211_TPT_LEDTRIG_FL_RADIO, 0); |
@@ -1026,7 +1178,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1026 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && | 1178 | if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && |
1027 | sdata->vif.type != NL80211_IFTYPE_MONITOR && | 1179 | sdata->vif.type != NL80211_IFTYPE_MONITOR && |
1028 | ieee80211_sdata_running(sdata)) | 1180 | ieee80211_sdata_running(sdata)) |
1029 | res = drv_add_interface(local, &sdata->vif); | 1181 | res = drv_add_interface(local, sdata); |
1030 | } | 1182 | } |
1031 | 1183 | ||
1032 | /* add STAs back */ | 1184 | /* add STAs back */ |
@@ -1076,11 +1228,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1076 | BSS_CHANGED_BEACON_INT | | 1228 | BSS_CHANGED_BEACON_INT | |
1077 | BSS_CHANGED_BSSID | | 1229 | BSS_CHANGED_BSSID | |
1078 | BSS_CHANGED_CQM | | 1230 | BSS_CHANGED_CQM | |
1079 | BSS_CHANGED_QOS; | 1231 | BSS_CHANGED_QOS | |
1232 | BSS_CHANGED_IDLE; | ||
1080 | 1233 | ||
1081 | switch (sdata->vif.type) { | 1234 | switch (sdata->vif.type) { |
1082 | case NL80211_IFTYPE_STATION: | 1235 | case NL80211_IFTYPE_STATION: |
1083 | changed |= BSS_CHANGED_ASSOC; | 1236 | changed |= BSS_CHANGED_ASSOC | |
1237 | BSS_CHANGED_ARP_FILTER; | ||
1084 | mutex_lock(&sdata->u.mgd.mtx); | 1238 | mutex_lock(&sdata->u.mgd.mtx); |
1085 | ieee80211_bss_info_change_notify(sdata, changed); | 1239 | ieee80211_bss_info_change_notify(sdata, changed); |
1086 | mutex_unlock(&sdata->u.mgd.mtx); | 1240 | mutex_unlock(&sdata->u.mgd.mtx); |
@@ -1090,6 +1244,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1090 | /* fall through */ | 1244 | /* fall through */ |
1091 | case NL80211_IFTYPE_AP: | 1245 | case NL80211_IFTYPE_AP: |
1092 | changed |= BSS_CHANGED_SSID; | 1246 | changed |= BSS_CHANGED_SSID; |
1247 | |||
1248 | if (sdata->vif.type == NL80211_IFTYPE_AP) | ||
1249 | changed |= BSS_CHANGED_AP_PROBE_RESP; | ||
1250 | |||
1093 | /* fall through */ | 1251 | /* fall through */ |
1094 | case NL80211_IFTYPE_MESH_POINT: | 1252 | case NL80211_IFTYPE_MESH_POINT: |
1095 | changed |= BSS_CHANGED_BEACON | | 1253 | changed |= BSS_CHANGED_BEACON | |
@@ -1111,6 +1269,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) | |||
1111 | } | 1269 | } |
1112 | } | 1270 | } |
1113 | 1271 | ||
1272 | ieee80211_recalc_ps(local, -1); | ||
1273 | |||
1114 | /* | 1274 | /* |
1115 | * Clear the WLAN_STA_BLOCK_BA flag so new aggregation | 1275 | * Clear the WLAN_STA_BLOCK_BA flag so new aggregation |
1116 | * sessions can be established after a resume. | 1276 | * sessions can be established after a resume. |
@@ -1366,6 +1526,108 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) | |||
1366 | } | 1526 | } |
1367 | EXPORT_SYMBOL(ieee80211_disable_rssi_reports); | 1527 | EXPORT_SYMBOL(ieee80211_disable_rssi_reports); |
1368 | 1528 | ||
1529 | u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, | ||
1530 | u16 cap) | ||
1531 | { | ||
1532 | __le16 tmp; | ||
1533 | |||
1534 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
1535 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
1536 | memset(pos, 0, sizeof(struct ieee80211_ht_cap)); | ||
1537 | |||
1538 | /* capability flags */ | ||
1539 | tmp = cpu_to_le16(cap); | ||
1540 | memcpy(pos, &tmp, sizeof(u16)); | ||
1541 | pos += sizeof(u16); | ||
1542 | |||
1543 | /* AMPDU parameters */ | ||
1544 | *pos++ = ht_cap->ampdu_factor | | ||
1545 | (ht_cap->ampdu_density << | ||
1546 | IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); | ||
1547 | |||
1548 | /* MCS set */ | ||
1549 | memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs)); | ||
1550 | pos += sizeof(ht_cap->mcs); | ||
1551 | |||
1552 | /* extended capabilities */ | ||
1553 | pos += sizeof(__le16); | ||
1554 | |||
1555 | /* BF capabilities */ | ||
1556 | pos += sizeof(__le32); | ||
1557 | |||
1558 | /* antenna selection */ | ||
1559 | pos += sizeof(u8); | ||
1560 | |||
1561 | return pos; | ||
1562 | } | ||
1563 | |||
1564 | u8 *ieee80211_ie_build_ht_info(u8 *pos, | ||
1565 | struct ieee80211_sta_ht_cap *ht_cap, | ||
1566 | struct ieee80211_channel *channel, | ||
1567 | enum nl80211_channel_type channel_type) | ||
1568 | { | ||
1569 | struct ieee80211_ht_info *ht_info; | ||
1570 | /* Build HT Information */ | ||
1571 | *pos++ = WLAN_EID_HT_INFORMATION; | ||
1572 | *pos++ = sizeof(struct ieee80211_ht_info); | ||
1573 | ht_info = (struct ieee80211_ht_info *)pos; | ||
1574 | ht_info->control_chan = | ||
1575 | ieee80211_frequency_to_channel(channel->center_freq); | ||
1576 | switch (channel_type) { | ||
1577 | case NL80211_CHAN_HT40MINUS: | ||
1578 | ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; | ||
1579 | break; | ||
1580 | case NL80211_CHAN_HT40PLUS: | ||
1581 | ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; | ||
1582 | break; | ||
1583 | case NL80211_CHAN_HT20: | ||
1584 | default: | ||
1585 | ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; | ||
1586 | break; | ||
1587 | } | ||
1588 | if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | ||
1589 | ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; | ||
1590 | |||
1591 | /* | ||
1592 | * Note: According to 802.11n-2009 9.13.3.1, HT Protection field and | ||
1593 | * RIFS Mode are reserved in IBSS mode, therefore keep them at 0 | ||
1594 | */ | ||
1595 | ht_info->operation_mode = 0x0000; | ||
1596 | ht_info->stbc_param = 0x0000; | ||
1597 | |||
1598 | /* It seems that Basic MCS set and Supported MCS set | ||
1599 | are identical for the first 10 bytes */ | ||
1600 | memset(&ht_info->basic_set, 0, 16); | ||
1601 | memcpy(&ht_info->basic_set, &ht_cap->mcs, 10); | ||
1602 | |||
1603 | return pos + sizeof(struct ieee80211_ht_info); | ||
1604 | } | ||
1605 | |||
1606 | enum nl80211_channel_type | ||
1607 | ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info) | ||
1608 | { | ||
1609 | enum nl80211_channel_type channel_type; | ||
1610 | |||
1611 | if (!ht_info) | ||
1612 | return NL80211_CHAN_NO_HT; | ||
1613 | |||
1614 | switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { | ||
1615 | case IEEE80211_HT_PARAM_CHA_SEC_NONE: | ||
1616 | channel_type = NL80211_CHAN_HT20; | ||
1617 | break; | ||
1618 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
1619 | channel_type = NL80211_CHAN_HT40PLUS; | ||
1620 | break; | ||
1621 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
1622 | channel_type = NL80211_CHAN_HT40MINUS; | ||
1623 | break; | ||
1624 | default: | ||
1625 | channel_type = NL80211_CHAN_NO_HT; | ||
1626 | } | ||
1627 | |||
1628 | return channel_type; | ||
1629 | } | ||
1630 | |||
1369 | int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) | 1631 | int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) |
1370 | { | 1632 | { |
1371 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 1633 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |