summaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2015-10-22 11:46:06 -0400
committerJohannes Berg <johannes.berg@intel.com>2015-11-03 04:56:26 -0500
commit2ed77ea69205139c3f6016b250d34e09bf48574d (patch)
tree1e86a54df572d804aae6b44c595a48bb7e2485bc /net/mac80211/mlme.c
parent730a755017139ddedac08d82f73c3532a020d372 (diff)
mac80211: treat bad WMM parameters more gracefully
As WMM is required for HT/VHT operation, treat bad WMM parameters more gracefully by falling back to default parameters instead of not using WMM assocation. This makes it possible to still use HT or VHT, although potentially with reduced quality of service due to unintended WMM parameters. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c142
1 files changed, 48 insertions, 94 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b9534c9902ac..b140cc6651f4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1744,10 +1744,10 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
1744 struct ieee80211_sub_if_data *sdata, 1744 struct ieee80211_sub_if_data *sdata,
1745 const u8 *wmm_param, size_t wmm_param_len) 1745 const u8 *wmm_param, size_t wmm_param_len)
1746{ 1746{
1747 struct ieee80211_tx_queue_params params; 1747 struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
1748 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 1748 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
1749 size_t left; 1749 size_t left;
1750 int count; 1750 int count, ac;
1751 const u8 *pos; 1751 const u8 *pos;
1752 u8 uapsd_queues = 0; 1752 u8 uapsd_queues = 0;
1753 1753
@@ -1781,25 +1781,24 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
1781 int aci = (pos[0] >> 5) & 0x03; 1781 int aci = (pos[0] >> 5) & 0x03;
1782 int acm = (pos[0] >> 4) & 0x01; 1782 int acm = (pos[0] >> 4) & 0x01;
1783 bool uapsd = false; 1783 bool uapsd = false;
1784 int queue;
1785 1784
1786 switch (aci) { 1785 switch (aci) {
1787 case 1: /* AC_BK */ 1786 case 1: /* AC_BK */
1788 queue = 3; 1787 ac = IEEE80211_AC_BK;
1789 if (acm) 1788 if (acm)
1790 sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ 1789 sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
1791 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) 1790 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
1792 uapsd = true; 1791 uapsd = true;
1793 break; 1792 break;
1794 case 2: /* AC_VI */ 1793 case 2: /* AC_VI */
1795 queue = 1; 1794 ac = IEEE80211_AC_VI;
1796 if (acm) 1795 if (acm)
1797 sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ 1796 sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
1798 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) 1797 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
1799 uapsd = true; 1798 uapsd = true;
1800 break; 1799 break;
1801 case 3: /* AC_VO */ 1800 case 3: /* AC_VO */
1802 queue = 0; 1801 ac = IEEE80211_AC_VO;
1803 if (acm) 1802 if (acm)
1804 sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ 1803 sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
1805 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) 1804 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
@@ -1807,7 +1806,7 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
1807 break; 1806 break;
1808 case 0: /* AC_BE */ 1807 case 0: /* AC_BE */
1809 default: 1808 default:
1810 queue = 2; 1809 ac = IEEE80211_AC_BE;
1811 if (acm) 1810 if (acm)
1812 sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ 1811 sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
1813 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) 1812 if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
@@ -1815,32 +1814,41 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
1815 break; 1814 break;
1816 } 1815 }
1817 1816
1818 params.aifs = pos[0] & 0x0f; 1817 params[ac].aifs = pos[0] & 0x0f;
1819 1818
1820 if (params.aifs < 2) { 1819 if (params[ac].aifs < 2) {
1821 sdata_info(sdata, 1820 sdata_info(sdata,
1822 "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n", 1821 "AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n",
1823 params.aifs, aci); 1822 params[ac].aifs, aci);
1824 params.aifs = 2; 1823 params[ac].aifs = 2;
1825 } 1824 }
1826 params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); 1825 params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
1827 params.cw_min = ecw2cw(pos[1] & 0x0f); 1826 params[ac].cw_min = ecw2cw(pos[1] & 0x0f);
1828 params.txop = get_unaligned_le16(pos + 2); 1827 params[ac].txop = get_unaligned_le16(pos + 2);
1829 params.acm = acm; 1828 params[ac].acm = acm;
1830 params.uapsd = uapsd; 1829 params[ac].uapsd = uapsd;
1831 1830
1831 if (params[ac].cw_min > params[ac].cw_max) {
1832 sdata_info(sdata,
1833 "AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n",
1834 params[ac].cw_min, params[ac].cw_max, aci);
1835 return false;
1836 }
1837 }
1838
1839 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
1832 mlme_dbg(sdata, 1840 mlme_dbg(sdata,
1833 "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", 1841 "WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
1834 queue, aci, acm, 1842 ac, params[ac].acm,
1835 params.aifs, params.cw_min, params.cw_max, 1843 params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
1836 params.txop, params.uapsd, 1844 params[ac].txop, params[ac].uapsd,
1837 ifmgd->tx_tspec[queue].downgraded); 1845 ifmgd->tx_tspec[ac].downgraded);
1838 sdata->tx_conf[queue] = params; 1846 sdata->tx_conf[ac] = params[ac];
1839 if (!ifmgd->tx_tspec[queue].downgraded && 1847 if (!ifmgd->tx_tspec[ac].downgraded &&
1840 drv_conf_tx(local, sdata, queue, &params)) 1848 drv_conf_tx(local, sdata, ac, &params[ac]))
1841 sdata_err(sdata, 1849 sdata_err(sdata,
1842 "failed to set TX queue parameters for queue %d\n", 1850 "failed to set TX queue parameters for AC %d\n",
1843 queue); 1851 ac);
1844 } 1852 }
1845 1853
1846 /* enable WMM or activate new settings */ 1854 /* enable WMM or activate new settings */
@@ -3051,11 +3059,21 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
3051 */ 3059 */
3052 ifmgd->wmm_last_param_set = -1; 3060 ifmgd->wmm_last_param_set = -1;
3053 3061
3054 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param) 3062 if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
3055 ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
3056 elems.wmm_param_len);
3057 else
3058 ieee80211_set_wmm_default(sdata, false, false); 3063 ieee80211_set_wmm_default(sdata, false, false);
3064 } else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
3065 elems.wmm_param_len)) {
3066 /* still enable QoS since we might have HT/VHT */
3067 ieee80211_set_wmm_default(sdata, false, true);
3068 /* set the disable-WMM flag in this case to disable
3069 * tracking WMM parameter changes in the beacon if
3070 * the parameters weren't actually valid. Doing so
3071 * avoids changing parameters very strangely when
3072 * the AP is going back and forth between valid and
3073 * invalid parameters.
3074 */
3075 ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
3076 }
3059 changed |= BSS_CHANGED_QOS; 3077 changed |= BSS_CHANGED_QOS;
3060 3078
3061 /* set AID and assoc capability, 3079 /* set AID and assoc capability,
@@ -4550,37 +4568,6 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
4550 return err; 4568 return err;
4551} 4569}
4552 4570
4553static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
4554 const u8 *wmm_param, int len)
4555{
4556 const u8 *pos;
4557 size_t left;
4558
4559 if (len < 8)
4560 return false;
4561
4562 if (wmm_param[5] != 1 /* version */)
4563 return false;
4564
4565 pos = wmm_param + 8;
4566 left = len - 8;
4567
4568 for (; left >= 4; left -= 4, pos += 4) {
4569 u8 ecwmin = pos[1] & 0x0f;
4570 u8 ecwmax = (pos[1] & 0xf0) >> 4;
4571 int aci = (pos[0] >> 5) & 0x03;
4572
4573 if (ecwmin > ecwmax) {
4574 sdata_info(sdata,
4575 "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
4576 ecwmin, ecwmax, aci);
4577 return false;
4578 }
4579 }
4580
4581 return true;
4582}
4583
4584int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, 4571int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
4585 struct cfg80211_assoc_request *req) 4572 struct cfg80211_assoc_request *req)
4586{ 4573{
@@ -4645,39 +4632,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
4645 4632
4646 assoc_data->wmm = bss->wmm_used && 4633 assoc_data->wmm = bss->wmm_used &&
4647 (local->hw.queues >= IEEE80211_NUM_ACS); 4634 (local->hw.queues >= IEEE80211_NUM_ACS);
4648 if (assoc_data->wmm) {
4649 /* try to check validity of WMM params IE */
4650 const struct cfg80211_bss_ies *ies;
4651 const u8 *wp, *start, *end;
4652
4653 rcu_read_lock();
4654 ies = rcu_dereference(req->bss->ies);
4655 start = ies->data;
4656 end = start + ies->len;
4657
4658 while (true) {
4659 wp = cfg80211_find_vendor_ie(
4660 WLAN_OUI_MICROSOFT,
4661 WLAN_OUI_TYPE_MICROSOFT_WMM,
4662 start, end - start);
4663 if (!wp)
4664 break;
4665 start = wp + wp[1] + 2;
4666 /* if this IE is too short, try the next */
4667 if (wp[1] <= 4)
4668 continue;
4669 /* if this IE is WMM params, we found what we wanted */
4670 if (wp[6] == 1)
4671 break;
4672 }
4673
4674 if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
4675 wp[1] - 2)) {
4676 assoc_data->wmm = false;
4677 ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
4678 }
4679 rcu_read_unlock();
4680 }
4681 4635
4682 /* 4636 /*
4683 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. 4637 * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.