summaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorSteinar H. Gunderson <sgunderson@bigfoot.com>2014-09-03 09:48:37 -0400
committerJohannes Berg <johannes@sipsolutions.net>2014-09-08 04:52:00 -0400
commitc8d6591752e96c550cb98b781326d72d8eedcc79 (patch)
treed4d37883b8782d9a2fae6a32382f110dba7c2984 /net/mac80211/mlme.c
parent24a4e4008ca2a819c4c889163586a8a9b7a3a08d (diff)
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the client to reduce its transmission power. However, 802.11h is only defined for 5 GHz, where the need for this is much smaller than on 2.4 GHz. Cisco has their own solution, called DTPC (Dynamic Transmit Power Control). Cisco APs on a controller sometimes but not always send 802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support for parsing and honoring the DTPC IE in addition to the 802.11h element (they do not always contain the same limits, so both must be honored); the format is not documented, but very simple. Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet 1142 joined to a Cisco 2504 WLC, by setting various transmit power levels for the given access points and observing the results. The Wireshark 802.11 dissector agrees with the interpretation of the element, except for negative numbers, which seem to never happen anyway. Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c60
1 files changed, 49 insertions, 11 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 0b812a88f95f..a7b92f5f7161 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1230,14 +1230,30 @@ ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
1230 return have_chan_pwr; 1230 return have_chan_pwr;
1231} 1231}
1232 1232
1233static void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata,
1234 struct ieee80211_channel *channel,
1235 const u8 *cisco_dtpc_ie,
1236 int *pwr_level)
1237{
1238 /* From practical testing, the first data byte of the DTPC element
1239 * seems to contain the requested dBm level, and the CLI on Cisco
1240 * APs clearly state the range is -127 to 127 dBm, which indicates
1241 * a signed byte, although it seemingly never actually goes negative.
1242 * The other byte seems to always be zero.
1243 */
1244 *pwr_level = (__s8)cisco_dtpc_ie[4];
1245}
1246
1233static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, 1247static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
1234 struct ieee80211_channel *channel, 1248 struct ieee80211_channel *channel,
1235 struct ieee80211_mgmt *mgmt, 1249 struct ieee80211_mgmt *mgmt,
1236 const u8 *country_ie, u8 country_ie_len, 1250 const u8 *country_ie, u8 country_ie_len,
1237 const u8 *pwr_constr_ie) 1251 const u8 *pwr_constr_ie,
1252 const u8 *cisco_dtpc_ie)
1238{ 1253{
1239 bool has_80211h_pwr = false; 1254 bool has_80211h_pwr = false, has_cisco_pwr = false;
1240 int chan_pwr, pwr_reduction_80211h; 1255 int chan_pwr = 0, pwr_reduction_80211h = 0;
1256 int pwr_level_cisco, pwr_level_80211h;
1241 int new_ap_level; 1257 int new_ap_level;
1242 1258
1243 if (country_ie && pwr_constr_ie && 1259 if (country_ie && pwr_constr_ie &&
@@ -1246,16 +1262,35 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
1246 has_80211h_pwr = ieee80211_find_80211h_pwr_constr( 1262 has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
1247 sdata, channel, country_ie, country_ie_len, 1263 sdata, channel, country_ie, country_ie_len,
1248 pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h); 1264 pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
1249 new_ap_level = max_t(int, 0, chan_pwr - pwr_reduction_80211h); 1265 pwr_level_80211h =
1266 max_t(int, 0, chan_pwr - pwr_reduction_80211h);
1267 }
1268
1269 if (cisco_dtpc_ie) {
1270 ieee80211_find_cisco_dtpc(
1271 sdata, channel, cisco_dtpc_ie, &pwr_level_cisco);
1272 has_cisco_pwr = true;
1250 } 1273 }
1251 1274
1252 if (!has_80211h_pwr) 1275 if (!has_80211h_pwr && !has_cisco_pwr)
1253 return 0; 1276 return 0;
1254 1277
1255 sdata_info(sdata, 1278 /* If we have both 802.11h and Cisco DTPC, apply both limits
1256 "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", 1279 * by picking the smallest of the two power levels advertised.
1257 new_ap_level, chan_pwr, pwr_reduction_80211h, 1280 */
1258 sdata->u.mgd.bssid); 1281 if (has_80211h_pwr &&
1282 (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
1283 sdata_info(sdata,
1284 "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
1285 pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
1286 sdata->u.mgd.bssid);
1287 new_ap_level = pwr_level_80211h;
1288 } else { /* has_cisco_pwr is always true here. */
1289 sdata_info(sdata,
1290 "Limiting TX power to %d dBm as advertised by %pM\n",
1291 pwr_level_cisco, sdata->u.mgd.bssid);
1292 new_ap_level = pwr_level_cisco;
1293 }
1259 1294
1260 if (sdata->ap_power_level == new_ap_level) 1295 if (sdata->ap_power_level == new_ap_level)
1261 return 0; 1296 return 0;
@@ -2923,7 +2958,9 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
2923/* 2958/*
2924 * This is the canonical list of information elements we care about, 2959 * This is the canonical list of information elements we care about,
2925 * the filter code also gives us all changes to the Microsoft OUI 2960 * the filter code also gives us all changes to the Microsoft OUI
2926 * (00:50:F2) vendor IE which is used for WMM which we need to track. 2961 * (00:50:F2) vendor IE which is used for WMM which we need to track,
2962 * as well as the DTPC IE (part of the Cisco OUI) used for signaling
2963 * changes to requested client power.
2927 * 2964 *
2928 * We implement beacon filtering in software since that means we can 2965 * We implement beacon filtering in software since that means we can
2929 * avoid processing the frame here and in cfg80211, and userspace 2966 * avoid processing the frame here and in cfg80211, and userspace
@@ -3232,7 +3269,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
3232 changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt, 3269 changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
3233 elems.country_elem, 3270 elems.country_elem,
3234 elems.country_elem_len, 3271 elems.country_elem_len,
3235 elems.pwr_constr_elem); 3272 elems.pwr_constr_elem,
3273 elems.cisco_dtpc_elem);
3236 3274
3237 ieee80211_bss_info_change_notify(sdata, changed); 3275 ieee80211_bss_info_change_notify(sdata, changed);
3238} 3276}