aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/nl80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r--net/wireless/nl80211.c160
1 files changed, 110 insertions, 50 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 999108cd947c..15158a3d64a3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
223 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, 223 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
224 .len = 20-1 }, 224 .len = 20-1 },
225 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, 225 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
226
226 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, 227 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
227 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, 228 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
229 [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
230 [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
231 [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
232
228 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, 233 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
229 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, 234 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
230 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, 235 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
@@ -1360,35 +1365,13 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1360 wdev->iftype == NL80211_IFTYPE_P2P_GO; 1365 wdev->iftype == NL80211_IFTYPE_P2P_GO;
1361} 1366}
1362 1367
1363static bool nl80211_valid_channel_type(struct genl_info *info,
1364 enum nl80211_channel_type *channel_type)
1365{
1366 enum nl80211_channel_type tmp;
1367
1368 if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
1369 return false;
1370
1371 tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1372 if (tmp != NL80211_CHAN_NO_HT &&
1373 tmp != NL80211_CHAN_HT20 &&
1374 tmp != NL80211_CHAN_HT40PLUS &&
1375 tmp != NL80211_CHAN_HT40MINUS)
1376 return false;
1377
1378 if (channel_type)
1379 *channel_type = tmp;
1380
1381 return true;
1382}
1383
1384static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, 1368static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
1385 struct genl_info *info, 1369 struct genl_info *info,
1386 struct cfg80211_chan_def *chandef) 1370 struct cfg80211_chan_def *chandef)
1387{ 1371{
1388 struct ieee80211_sta_ht_cap *ht_cap; 1372 struct ieee80211_sta_ht_cap *ht_cap;
1389 struct ieee80211_channel *sc; 1373 struct ieee80211_sta_vht_cap *vht_cap;
1390 u32 control_freq; 1374 u32 control_freq, width;
1391 int offs;
1392 1375
1393 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) 1376 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1394 return -EINVAL; 1377 return -EINVAL;
@@ -1396,47 +1379,105 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
1396 control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); 1379 control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1397 1380
1398 chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); 1381 chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
1399 chandef->_type = NL80211_CHAN_NO_HT; 1382 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
1400 1383 chandef->center_freq1 = control_freq;
1401 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && 1384 chandef->center_freq2 = 0;
1402 !nl80211_valid_channel_type(info, &chandef->_type))
1403 return -EINVAL;
1404 1385
1405 /* Primary channel not allowed */ 1386 /* Primary channel not allowed */
1406 if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) 1387 if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
1407 return -EINVAL; 1388 return -EINVAL;
1408 1389
1390 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1391 enum nl80211_channel_type chantype;
1392
1393 chantype = nla_get_u32(
1394 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1395
1396 switch (chantype) {
1397 case NL80211_CHAN_NO_HT:
1398 case NL80211_CHAN_HT20:
1399 case NL80211_CHAN_HT40PLUS:
1400 case NL80211_CHAN_HT40MINUS:
1401 cfg80211_chandef_create(chandef, chandef->chan,
1402 chantype);
1403 break;
1404 default:
1405 return -EINVAL;
1406 }
1407 } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
1408 chandef->width =
1409 nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
1410 if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
1411 chandef->center_freq1 =
1412 nla_get_u32(
1413 info->attrs[NL80211_ATTR_CENTER_FREQ1]);
1414 if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
1415 chandef->center_freq2 =
1416 nla_get_u32(
1417 info->attrs[NL80211_ATTR_CENTER_FREQ2]);
1418 }
1419
1409 ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; 1420 ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
1421 vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap;
1410 1422
1411 switch (chandef->_type) { 1423 if (!cfg80211_chan_def_valid(chandef))
1412 case NL80211_CHAN_NO_HT: 1424 return -EINVAL;
1425
1426 switch (chandef->width) {
1427 case NL80211_CHAN_WIDTH_20:
1428 if (!ht_cap->ht_supported)
1429 return -EINVAL;
1430 case NL80211_CHAN_WIDTH_20_NOHT:
1431 width = 20;
1413 break; 1432 break;
1414 case NL80211_CHAN_HT40MINUS: 1433 case NL80211_CHAN_WIDTH_40:
1415 if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) 1434 width = 40;
1435 /* quick early regulatory check */
1436 if (chandef->center_freq1 < control_freq &&
1437 chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
1438 return -EINVAL;
1439 if (chandef->center_freq1 > control_freq &&
1440 chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
1441 return -EINVAL;
1442 if (!ht_cap->ht_supported)
1416 return -EINVAL; 1443 return -EINVAL;
1417 offs = -20;
1418 /* fall through */
1419 case NL80211_CHAN_HT40PLUS:
1420 if (chandef->_type == NL80211_CHAN_HT40PLUS) {
1421 if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
1422 return -EINVAL;
1423 offs = 20;
1424 }
1425 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || 1444 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
1426 ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) 1445 ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
1427 return -EINVAL; 1446 return -EINVAL;
1428 1447 break;
1429 sc = ieee80211_get_channel(&rdev->wiphy, 1448 case NL80211_CHAN_WIDTH_80:
1430 chandef->chan->center_freq + offs); 1449 width = 80;
1431 if (!sc || sc->flags & IEEE80211_CHAN_DISABLED) 1450 if (!vht_cap->vht_supported)
1432 return -EINVAL; 1451 return -EINVAL;
1433 /* fall through */ 1452 break;
1434 case NL80211_CHAN_HT20: 1453 case NL80211_CHAN_WIDTH_80P80:
1435 if (!ht_cap->ht_supported) 1454 width = 80;
1455 if (!vht_cap->vht_supported)
1456 return -EINVAL;
1457 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
1458 return -EINVAL;
1459 break;
1460 case NL80211_CHAN_WIDTH_160:
1461 width = 160;
1462 if (!vht_cap->vht_supported)
1463 return -EINVAL;
1464 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
1436 return -EINVAL; 1465 return -EINVAL;
1437 break; 1466 break;
1467 default:
1468 return -EINVAL;
1438 } 1469 }
1439 1470
1471 if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1,
1472 width, IEEE80211_CHAN_DISABLED))
1473 return -EINVAL;
1474 if (chandef->center_freq2 &&
1475 !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2,
1476 width, IEEE80211_CHAN_DISABLED))
1477 return -EINVAL;
1478
1479 /* TODO: missing regulatory check on bandwidth */
1480
1440 return 0; 1481 return 0;
1441} 1482}
1442 1483
@@ -1800,10 +1841,28 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
1800static int nl80211_send_chandef(struct sk_buff *msg, 1841static int nl80211_send_chandef(struct sk_buff *msg,
1801 struct cfg80211_chan_def *chandef) 1842 struct cfg80211_chan_def *chandef)
1802{ 1843{
1844 WARN_ON(!cfg80211_chan_def_valid(chandef));
1845
1803 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, 1846 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
1804 chandef->chan->center_freq)) 1847 chandef->chan->center_freq))
1805 return -ENOBUFS; 1848 return -ENOBUFS;
1806 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, chandef->_type)) 1849 switch (chandef->width) {
1850 case NL80211_CHAN_WIDTH_20_NOHT:
1851 case NL80211_CHAN_WIDTH_20:
1852 case NL80211_CHAN_WIDTH_40:
1853 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
1854 cfg80211_get_chandef_type(chandef)))
1855 return -ENOBUFS;
1856 break;
1857 default:
1858 break;
1859 }
1860 if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
1861 return -ENOBUFS;
1862 if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
1863 return -ENOBUFS;
1864 if (chandef->center_freq2 &&
1865 nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
1807 return -ENOBUFS; 1866 return -ENOBUFS;
1808 return 0; 1867 return 0;
1809} 1868}
@@ -5447,7 +5506,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
5447 if (IS_ERR(connkeys)) 5506 if (IS_ERR(connkeys))
5448 return PTR_ERR(connkeys); 5507 return PTR_ERR(connkeys);
5449 5508
5450 if ((ibss.chandef._type != NL80211_CHAN_NO_HT) && no_ht) { 5509 if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
5510 no_ht) {
5451 kfree(connkeys); 5511 kfree(connkeys);
5452 return -EINVAL; 5512 return -EINVAL;
5453 } 5513 }