diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 160 |
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 | ||
1363 | static 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 | |||
1384 | static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, | 1368 | static 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) | |||
1800 | static int nl80211_send_chandef(struct sk_buff *msg, | 1841 | static 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 | } |