diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/main.c | 16 | ||||
-rw-r--r-- | net/wireless/core.c | 69 | ||||
-rw-r--r-- | net/wireless/core.h | 11 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 105 | ||||
-rw-r--r-- | net/wireless/util.c | 80 |
5 files changed, 263 insertions, 18 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 7f89011fa22d..79a2281678bf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -685,7 +685,7 @@ EXPORT_SYMBOL(ieee80211_alloc_hw); | |||
685 | int ieee80211_register_hw(struct ieee80211_hw *hw) | 685 | int ieee80211_register_hw(struct ieee80211_hw *hw) |
686 | { | 686 | { |
687 | struct ieee80211_local *local = hw_to_local(hw); | 687 | struct ieee80211_local *local = hw_to_local(hw); |
688 | int result; | 688 | int result, i; |
689 | enum ieee80211_band band; | 689 | enum ieee80211_band band; |
690 | int channels, max_bitrates; | 690 | int channels, max_bitrates; |
691 | bool supp_ht; | 691 | bool supp_ht; |
@@ -743,11 +743,19 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
743 | return -ENOMEM; | 743 | return -ENOMEM; |
744 | 744 | ||
745 | /* if low-level driver supports AP, we also support VLAN */ | 745 | /* if low-level driver supports AP, we also support VLAN */ |
746 | if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) | 746 | if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { |
747 | local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); | 747 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP_VLAN); |
748 | hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_AP_VLAN); | ||
749 | } | ||
748 | 750 | ||
749 | /* mac80211 always supports monitor */ | 751 | /* mac80211 always supports monitor */ |
750 | local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); | 752 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); |
753 | hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); | ||
754 | |||
755 | /* mac80211 doesn't support more than 1 channel */ | ||
756 | for (i = 0; i < hw->wiphy->n_iface_combinations; i++) | ||
757 | if (hw->wiphy->iface_combinations[i].num_different_channels > 1) | ||
758 | return -EINVAL; | ||
751 | 759 | ||
752 | #ifndef CONFIG_MAC80211_MESH | 760 | #ifndef CONFIG_MAC80211_MESH |
753 | /* mesh depends on Kconfig, but drivers should set it if they want */ | 761 | /* mesh depends on Kconfig, but drivers should set it if they want */ |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 18b002f16860..c22ef3492ee6 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -416,6 +416,67 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
416 | } | 416 | } |
417 | EXPORT_SYMBOL(wiphy_new); | 417 | EXPORT_SYMBOL(wiphy_new); |
418 | 418 | ||
419 | static int wiphy_verify_combinations(struct wiphy *wiphy) | ||
420 | { | ||
421 | const struct ieee80211_iface_combination *c; | ||
422 | int i, j; | ||
423 | |||
424 | /* If we have combinations enforce them */ | ||
425 | if (wiphy->n_iface_combinations) | ||
426 | wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; | ||
427 | |||
428 | for (i = 0; i < wiphy->n_iface_combinations; i++) { | ||
429 | u32 cnt = 0; | ||
430 | u16 all_iftypes = 0; | ||
431 | |||
432 | c = &wiphy->iface_combinations[i]; | ||
433 | |||
434 | /* Combinations with just one interface aren't real */ | ||
435 | if (WARN_ON(c->max_interfaces < 2)) | ||
436 | return -EINVAL; | ||
437 | |||
438 | /* Need at least one channel */ | ||
439 | if (WARN_ON(!c->num_different_channels)) | ||
440 | return -EINVAL; | ||
441 | |||
442 | if (WARN_ON(!c->n_limits)) | ||
443 | return -EINVAL; | ||
444 | |||
445 | for (j = 0; j < c->n_limits; j++) { | ||
446 | u16 types = c->limits[j].types; | ||
447 | |||
448 | /* | ||
449 | * interface types shouldn't overlap, this is | ||
450 | * used in cfg80211_can_change_interface() | ||
451 | */ | ||
452 | if (WARN_ON(types & all_iftypes)) | ||
453 | return -EINVAL; | ||
454 | all_iftypes |= types; | ||
455 | |||
456 | if (WARN_ON(!c->limits[j].max)) | ||
457 | return -EINVAL; | ||
458 | |||
459 | /* Shouldn't list software iftypes in combinations! */ | ||
460 | if (WARN_ON(wiphy->software_iftypes & types)) | ||
461 | return -EINVAL; | ||
462 | |||
463 | cnt += c->limits[j].max; | ||
464 | /* | ||
465 | * Don't advertise an unsupported type | ||
466 | * in a combination. | ||
467 | */ | ||
468 | if (WARN_ON((wiphy->interface_modes & types) != types)) | ||
469 | return -EINVAL; | ||
470 | } | ||
471 | |||
472 | /* You can't even choose that many! */ | ||
473 | if (WARN_ON(cnt < c->max_interfaces)) | ||
474 | return -EINVAL; | ||
475 | } | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
419 | int wiphy_register(struct wiphy *wiphy) | 480 | int wiphy_register(struct wiphy *wiphy) |
420 | { | 481 | { |
421 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 482 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
@@ -444,6 +505,10 @@ int wiphy_register(struct wiphy *wiphy) | |||
444 | if (WARN_ON(ifmodes != wiphy->interface_modes)) | 505 | if (WARN_ON(ifmodes != wiphy->interface_modes)) |
445 | wiphy->interface_modes = ifmodes; | 506 | wiphy->interface_modes = ifmodes; |
446 | 507 | ||
508 | res = wiphy_verify_combinations(wiphy); | ||
509 | if (res) | ||
510 | return res; | ||
511 | |||
447 | /* sanity check supported bands/channels */ | 512 | /* sanity check supported bands/channels */ |
448 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 513 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
449 | sband = wiphy->bands[band]; | 514 | sband = wiphy->bands[band]; |
@@ -698,6 +763,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
698 | struct net_device *dev = ndev; | 763 | struct net_device *dev = ndev; |
699 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 764 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
700 | struct cfg80211_registered_device *rdev; | 765 | struct cfg80211_registered_device *rdev; |
766 | int ret; | ||
701 | 767 | ||
702 | if (!wdev) | 768 | if (!wdev) |
703 | return NOTIFY_DONE; | 769 | return NOTIFY_DONE; |
@@ -893,6 +959,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
893 | return notifier_from_errno(-EOPNOTSUPP); | 959 | return notifier_from_errno(-EOPNOTSUPP); |
894 | if (rfkill_blocked(rdev->rfkill)) | 960 | if (rfkill_blocked(rdev->rfkill)) |
895 | return notifier_from_errno(-ERFKILL); | 961 | return notifier_from_errno(-ERFKILL); |
962 | ret = cfg80211_can_add_interface(rdev, wdev->iftype); | ||
963 | if (ret) | ||
964 | return notifier_from_errno(ret); | ||
896 | break; | 965 | break; |
897 | } | 966 | } |
898 | 967 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index d4b8f4c0bbbb..bf0fb40e3c8b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -422,6 +422,17 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
422 | u32 *flags, struct vif_params *params); | 422 | u32 *flags, struct vif_params *params); |
423 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); | 423 | void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); |
424 | 424 | ||
425 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | ||
426 | struct wireless_dev *wdev, | ||
427 | enum nl80211_iftype iftype); | ||
428 | |||
429 | static inline int | ||
430 | cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, | ||
431 | enum nl80211_iftype iftype) | ||
432 | { | ||
433 | return cfg80211_can_change_interface(rdev, NULL, iftype); | ||
434 | } | ||
435 | |||
425 | struct ieee80211_channel * | 436 | struct ieee80211_channel * |
426 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, | 437 | rdev_freq_to_chan(struct cfg80211_registered_device *rdev, |
427 | int freq, enum nl80211_channel_type channel_type); | 438 | int freq, enum nl80211_channel_type channel_type); |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 9ef8e287d61b..beac296b1fde 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -564,6 +564,88 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) | |||
564 | return 0; | 564 | return 0; |
565 | } | 565 | } |
566 | 566 | ||
567 | static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) | ||
568 | { | ||
569 | struct nlattr *nl_modes = nla_nest_start(msg, attr); | ||
570 | int i; | ||
571 | |||
572 | if (!nl_modes) | ||
573 | goto nla_put_failure; | ||
574 | |||
575 | i = 0; | ||
576 | while (ifmodes) { | ||
577 | if (ifmodes & 1) | ||
578 | NLA_PUT_FLAG(msg, i); | ||
579 | ifmodes >>= 1; | ||
580 | i++; | ||
581 | } | ||
582 | |||
583 | nla_nest_end(msg, nl_modes); | ||
584 | return 0; | ||
585 | |||
586 | nla_put_failure: | ||
587 | return -ENOBUFS; | ||
588 | } | ||
589 | |||
590 | static int nl80211_put_iface_combinations(struct wiphy *wiphy, | ||
591 | struct sk_buff *msg) | ||
592 | { | ||
593 | struct nlattr *nl_combis; | ||
594 | int i, j; | ||
595 | |||
596 | nl_combis = nla_nest_start(msg, | ||
597 | NL80211_ATTR_INTERFACE_COMBINATIONS); | ||
598 | if (!nl_combis) | ||
599 | goto nla_put_failure; | ||
600 | |||
601 | for (i = 0; i < wiphy->n_iface_combinations; i++) { | ||
602 | const struct ieee80211_iface_combination *c; | ||
603 | struct nlattr *nl_combi, *nl_limits; | ||
604 | |||
605 | c = &wiphy->iface_combinations[i]; | ||
606 | |||
607 | nl_combi = nla_nest_start(msg, i + 1); | ||
608 | if (!nl_combi) | ||
609 | goto nla_put_failure; | ||
610 | |||
611 | nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); | ||
612 | if (!nl_limits) | ||
613 | goto nla_put_failure; | ||
614 | |||
615 | for (j = 0; j < c->n_limits; j++) { | ||
616 | struct nlattr *nl_limit; | ||
617 | |||
618 | nl_limit = nla_nest_start(msg, j + 1); | ||
619 | if (!nl_limit) | ||
620 | goto nla_put_failure; | ||
621 | NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, | ||
622 | c->limits[j].max); | ||
623 | if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, | ||
624 | c->limits[j].types)) | ||
625 | goto nla_put_failure; | ||
626 | nla_nest_end(msg, nl_limit); | ||
627 | } | ||
628 | |||
629 | nla_nest_end(msg, nl_limits); | ||
630 | |||
631 | if (c->beacon_int_infra_match) | ||
632 | NLA_PUT_FLAG(msg, | ||
633 | NL80211_IFACE_COMB_STA_AP_BI_MATCH); | ||
634 | NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, | ||
635 | c->num_different_channels); | ||
636 | NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, | ||
637 | c->max_interfaces); | ||
638 | |||
639 | nla_nest_end(msg, nl_combi); | ||
640 | } | ||
641 | |||
642 | nla_nest_end(msg, nl_combis); | ||
643 | |||
644 | return 0; | ||
645 | nla_put_failure: | ||
646 | return -ENOBUFS; | ||
647 | } | ||
648 | |||
567 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | 649 | static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, |
568 | struct cfg80211_registered_device *dev) | 650 | struct cfg80211_registered_device *dev) |
569 | { | 651 | { |
@@ -571,13 +653,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
571 | struct nlattr *nl_bands, *nl_band; | 653 | struct nlattr *nl_bands, *nl_band; |
572 | struct nlattr *nl_freqs, *nl_freq; | 654 | struct nlattr *nl_freqs, *nl_freq; |
573 | struct nlattr *nl_rates, *nl_rate; | 655 | struct nlattr *nl_rates, *nl_rate; |
574 | struct nlattr *nl_modes; | ||
575 | struct nlattr *nl_cmds; | 656 | struct nlattr *nl_cmds; |
576 | enum ieee80211_band band; | 657 | enum ieee80211_band band; |
577 | struct ieee80211_channel *chan; | 658 | struct ieee80211_channel *chan; |
578 | struct ieee80211_rate *rate; | 659 | struct ieee80211_rate *rate; |
579 | int i; | 660 | int i; |
580 | u16 ifmodes = dev->wiphy.interface_modes; | ||
581 | const struct ieee80211_txrx_stypes *mgmt_stypes = | 661 | const struct ieee80211_txrx_stypes *mgmt_stypes = |
582 | dev->wiphy.mgmt_stypes; | 662 | dev->wiphy.mgmt_stypes; |
583 | 663 | ||
@@ -637,20 +717,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
637 | } | 717 | } |
638 | } | 718 | } |
639 | 719 | ||
640 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); | 720 | if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, |
641 | if (!nl_modes) | 721 | dev->wiphy.interface_modes)) |
642 | goto nla_put_failure; | 722 | goto nla_put_failure; |
643 | 723 | ||
644 | i = 0; | ||
645 | while (ifmodes) { | ||
646 | if (ifmodes & 1) | ||
647 | NLA_PUT_FLAG(msg, i); | ||
648 | ifmodes >>= 1; | ||
649 | i++; | ||
650 | } | ||
651 | |||
652 | nla_nest_end(msg, nl_modes); | ||
653 | |||
654 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); | 724 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); |
655 | if (!nl_bands) | 725 | if (!nl_bands) |
656 | goto nla_put_failure; | 726 | goto nla_put_failure; |
@@ -865,6 +935,13 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
865 | nla_nest_end(msg, nl_wowlan); | 935 | nla_nest_end(msg, nl_wowlan); |
866 | } | 936 | } |
867 | 937 | ||
938 | if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, | ||
939 | dev->wiphy.software_iftypes)) | ||
940 | goto nla_put_failure; | ||
941 | |||
942 | if (nl80211_put_iface_combinations(&dev->wiphy, msg)) | ||
943 | goto nla_put_failure; | ||
944 | |||
868 | return genlmsg_end(msg, hdr); | 945 | return genlmsg_end(msg, hdr); |
869 | 946 | ||
870 | nla_put_failure: | 947 | nla_put_failure: |
diff --git a/net/wireless/util.c b/net/wireless/util.c index 414c9f604df6..95e4e254da0a 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -803,6 +803,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
803 | return -EBUSY; | 803 | return -EBUSY; |
804 | 804 | ||
805 | if (ntype != otype) { | 805 | if (ntype != otype) { |
806 | err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, | ||
807 | ntype); | ||
808 | if (err) | ||
809 | return err; | ||
810 | |||
806 | dev->ieee80211_ptr->use_4addr = false; | 811 | dev->ieee80211_ptr->use_4addr = false; |
807 | dev->ieee80211_ptr->mesh_id_up_len = 0; | 812 | dev->ieee80211_ptr->mesh_id_up_len = 0; |
808 | 813 | ||
@@ -921,3 +926,78 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | |||
921 | 926 | ||
922 | return res; | 927 | return res; |
923 | } | 928 | } |
929 | |||
930 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | ||
931 | struct wireless_dev *wdev, | ||
932 | enum nl80211_iftype iftype) | ||
933 | { | ||
934 | struct wireless_dev *wdev_iter; | ||
935 | int num[NUM_NL80211_IFTYPES]; | ||
936 | int total = 1; | ||
937 | int i, j; | ||
938 | |||
939 | ASSERT_RTNL(); | ||
940 | |||
941 | /* Always allow software iftypes */ | ||
942 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | ||
943 | return 0; | ||
944 | |||
945 | /* | ||
946 | * Drivers will gradually all set this flag, until all | ||
947 | * have it we only enforce for those that set it. | ||
948 | */ | ||
949 | if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) | ||
950 | return 0; | ||
951 | |||
952 | memset(num, 0, sizeof(num)); | ||
953 | |||
954 | num[iftype] = 1; | ||
955 | |||
956 | mutex_lock(&rdev->devlist_mtx); | ||
957 | list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { | ||
958 | if (wdev_iter == wdev) | ||
959 | continue; | ||
960 | if (!netif_running(wdev_iter->netdev)) | ||
961 | continue; | ||
962 | |||
963 | if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) | ||
964 | continue; | ||
965 | |||
966 | num[wdev_iter->iftype]++; | ||
967 | total++; | ||
968 | } | ||
969 | mutex_unlock(&rdev->devlist_mtx); | ||
970 | |||
971 | for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { | ||
972 | const struct ieee80211_iface_combination *c; | ||
973 | struct ieee80211_iface_limit *limits; | ||
974 | |||
975 | c = &rdev->wiphy.iface_combinations[i]; | ||
976 | |||
977 | limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, | ||
978 | GFP_KERNEL); | ||
979 | if (!limits) | ||
980 | return -ENOMEM; | ||
981 | if (total > c->max_interfaces) | ||
982 | goto cont; | ||
983 | |||
984 | for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { | ||
985 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | ||
986 | continue; | ||
987 | for (j = 0; j < c->n_limits; j++) { | ||
988 | if (!(limits[j].types & iftype)) | ||
989 | continue; | ||
990 | if (limits[j].max < num[iftype]) | ||
991 | goto cont; | ||
992 | limits[j].max -= num[iftype]; | ||
993 | } | ||
994 | } | ||
995 | /* yay, it fits */ | ||
996 | kfree(limits); | ||
997 | return 0; | ||
998 | cont: | ||
999 | kfree(limits); | ||
1000 | } | ||
1001 | |||
1002 | return -EBUSY; | ||
1003 | } | ||