diff options
| author | Jonathan Doron <jond@wizery.com> | 2014-12-15 12:26:00 -0500 |
|---|---|---|
| committer | Johannes Berg <johannes.berg@intel.com> | 2014-12-17 05:49:55 -0500 |
| commit | b0d7aa59592b4270531de5ce65dcf18338a2d98c (patch) | |
| tree | 85d54dba5117fedd4d5b605be70ddbf8451d7838 /net/wireless | |
| parent | ad30ca2c03cecfb1b0749874bdceead269542de6 (diff) | |
cfg80211: allow wiphy specific regdomain management
Add a new regulatory flag that allows a driver to manage regdomain
changes/updates for its own wiphy.
A self-managed wiphys only employs regulatory information obtained from
the FW and driver and does not use other cfg80211 sources like
beacon-hints, country-code IEs and hints from other devices on the same
system. Conversely, a self-managed wiphy does not share its regulatory
hints with other devices in the system. If a system contains several
devices, one or more of which are self-managed, there might be
contradictory regulatory settings between them. Usage of flag is
generally discouraged. Only use it if the FW/driver is incompatible
with non-locally originated hints.
A new API lets the driver send a complete regdomain, to be applied on
its wiphy only.
After a wiphy-specific regdomain change takes place, usermode will get
a new type of change notification. The regulatory core also takes care
enforce regulatory restrictions, in case some interfaces are on
forbidden channels.
Signed-off-by: Jonathan Doron <jonathanx.doron@intel.com>
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Reviewed-by: Luis R. Rodriguez <mcgrof@suse.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
| -rw-r--r-- | net/wireless/core.c | 8 | ||||
| -rw-r--r-- | net/wireless/core.h | 7 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 49 | ||||
| -rw-r--r-- | net/wireless/nl80211.h | 16 | ||||
| -rw-r--r-- | net/wireless/reg.c | 88 |
5 files changed, 149 insertions, 19 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 4910758baab1..b661fcce7e3c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
| @@ -561,6 +561,14 @@ int wiphy_register(struct wiphy *wiphy) | |||
| 561 | BIT(NL80211_IFTYPE_MONITOR))) | 561 | BIT(NL80211_IFTYPE_MONITOR))) |
| 562 | wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; | 562 | wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; |
| 563 | 563 | ||
| 564 | if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) && | ||
| 565 | (wiphy->regulatory_flags & | ||
| 566 | (REGULATORY_CUSTOM_REG | | ||
| 567 | REGULATORY_STRICT_REG | | ||
| 568 | REGULATORY_COUNTRY_IE_FOLLOW_POWER | | ||
| 569 | REGULATORY_COUNTRY_IE_IGNORE)))) | ||
| 570 | return -EINVAL; | ||
| 571 | |||
| 564 | if (WARN_ON(wiphy->coalesce && | 572 | if (WARN_ON(wiphy->coalesce && |
| 565 | (!wiphy->coalesce->n_rules || | 573 | (!wiphy->coalesce->n_rules || |
| 566 | !wiphy->coalesce->n_patterns) && | 574 | !wiphy->coalesce->n_patterns) && |
diff --git a/net/wireless/core.h b/net/wireless/core.h index faa5b1609aae..e87cae57a83b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
| @@ -36,6 +36,13 @@ struct cfg80211_registered_device { | |||
| 36 | * the country on the country IE changed. */ | 36 | * the country on the country IE changed. */ |
| 37 | char country_ie_alpha2[2]; | 37 | char country_ie_alpha2[2]; |
| 38 | 38 | ||
| 39 | /* | ||
| 40 | * the driver requests the regulatory core to set this regulatory | ||
| 41 | * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED | ||
| 42 | * devices using the regulatory_set_wiphy_regd() API | ||
| 43 | */ | ||
| 44 | const struct ieee80211_regdomain *requested_regd; | ||
| 45 | |||
| 39 | /* If a Country IE has been received this tells us the environment | 46 | /* If a Country IE has been received this tells us the environment |
| 40 | * which its telling us its in. This defaults to ENVIRON_ANY */ | 47 | * which its telling us its in. This defaults to ENVIRON_ANY */ |
| 41 | enum environment_cap env; | 48 | enum environment_cap env; |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2d5dc428c5ab..eebb7e422989 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
| @@ -11042,25 +11042,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, | |||
| 11042 | NL80211_MCGRP_SCAN, GFP_KERNEL); | 11042 | NL80211_MCGRP_SCAN, GFP_KERNEL); |
| 11043 | } | 11043 | } |
| 11044 | 11044 | ||
| 11045 | /* | 11045 | static bool nl80211_reg_change_event_fill(struct sk_buff *msg, |
| 11046 | * This can happen on global regulatory changes or device specific settings | 11046 | struct regulatory_request *request) |
| 11047 | * based on custom world regulatory domains. | ||
| 11048 | */ | ||
| 11049 | void nl80211_send_reg_change_event(struct regulatory_request *request) | ||
| 11050 | { | 11047 | { |
| 11051 | struct sk_buff *msg; | ||
| 11052 | void *hdr; | ||
| 11053 | |||
| 11054 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 11055 | if (!msg) | ||
| 11056 | return; | ||
| 11057 | |||
| 11058 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); | ||
| 11059 | if (!hdr) { | ||
| 11060 | nlmsg_free(msg); | ||
| 11061 | return; | ||
| 11062 | } | ||
| 11063 | |||
| 11064 | /* Userspace can always count this one always being set */ | 11048 | /* Userspace can always count this one always being set */ |
| 11065 | if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator)) | 11049 | if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator)) |
| 11066 | goto nla_put_failure; | 11050 | goto nla_put_failure; |
| @@ -11094,6 +11078,35 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) | |||
| 11094 | goto nla_put_failure; | 11078 | goto nla_put_failure; |
| 11095 | } | 11079 | } |
| 11096 | 11080 | ||
| 11081 | return true; | ||
| 11082 | |||
| 11083 | nla_put_failure: | ||
| 11084 | return false; | ||
| 11085 | } | ||
| 11086 | |||
| 11087 | /* | ||
| 11088 | * This can happen on global regulatory changes or device specific settings | ||
| 11089 | * based on custom regulatory domains. | ||
| 11090 | */ | ||
| 11091 | void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, | ||
| 11092 | struct regulatory_request *request) | ||
| 11093 | { | ||
| 11094 | struct sk_buff *msg; | ||
| 11095 | void *hdr; | ||
| 11096 | |||
| 11097 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 11098 | if (!msg) | ||
| 11099 | return; | ||
| 11100 | |||
| 11101 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id); | ||
| 11102 | if (!hdr) { | ||
| 11103 | nlmsg_free(msg); | ||
| 11104 | return; | ||
| 11105 | } | ||
| 11106 | |||
| 11107 | if (nl80211_reg_change_event_fill(msg, request) == false) | ||
| 11108 | goto nla_put_failure; | ||
| 11109 | |||
| 11097 | genlmsg_end(msg, hdr); | 11110 | genlmsg_end(msg, hdr); |
| 11098 | 11111 | ||
| 11099 | rcu_read_lock(); | 11112 | rcu_read_lock(); |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 7ad70d6f0cc6..84d4edf1d545 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
| @@ -17,7 +17,21 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, | |||
| 17 | struct net_device *netdev, u32 cmd); | 17 | struct net_device *netdev, u32 cmd); |
| 18 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, | 18 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, |
| 19 | struct net_device *netdev); | 19 | struct net_device *netdev); |
| 20 | void nl80211_send_reg_change_event(struct regulatory_request *request); | 20 | void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, |
| 21 | struct regulatory_request *request); | ||
| 22 | |||
| 23 | static inline void | ||
| 24 | nl80211_send_reg_change_event(struct regulatory_request *request) | ||
| 25 | { | ||
| 26 | nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request); | ||
| 27 | } | ||
| 28 | |||
| 29 | static inline void | ||
| 30 | nl80211_send_wiphy_reg_change_event(struct regulatory_request *request) | ||
| 31 | { | ||
| 32 | nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request); | ||
| 33 | } | ||
| 34 | |||
| 21 | void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, | 35 | void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, |
| 22 | struct net_device *netdev, | 36 | struct net_device *netdev, |
| 23 | const u8 *buf, size_t len, gfp_t gfp); | 37 | const u8 *buf, size_t len, gfp_t gfp); |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 2d9760084b74..c040f8a0f1ed 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
| @@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
| 1307 | { | 1307 | { |
| 1308 | struct regulatory_request *lr = get_last_request(); | 1308 | struct regulatory_request *lr = get_last_request(); |
| 1309 | 1309 | ||
| 1310 | if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) | ||
| 1311 | return true; | ||
| 1312 | |||
| 1310 | if (!lr) { | 1313 | if (!lr) { |
| 1311 | REG_DBG_PRINT("Ignoring regulatory request set by %s " | 1314 | REG_DBG_PRINT("Ignoring regulatory request set by %s " |
| 1312 | "since last_request is not set\n", | 1315 | "since last_request is not set\n", |
| @@ -2147,11 +2150,52 @@ static void reg_process_pending_beacon_hints(void) | |||
| 2147 | spin_unlock_bh(®_pending_beacons_lock); | 2150 | spin_unlock_bh(®_pending_beacons_lock); |
| 2148 | } | 2151 | } |
| 2149 | 2152 | ||
| 2153 | static void reg_process_self_managed_hints(void) | ||
| 2154 | { | ||
| 2155 | struct cfg80211_registered_device *rdev; | ||
| 2156 | struct wiphy *wiphy; | ||
| 2157 | const struct ieee80211_regdomain *tmp; | ||
| 2158 | const struct ieee80211_regdomain *regd; | ||
| 2159 | enum ieee80211_band band; | ||
| 2160 | struct regulatory_request request = {}; | ||
| 2161 | |||
| 2162 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | ||
| 2163 | wiphy = &rdev->wiphy; | ||
| 2164 | |||
| 2165 | spin_lock(®_requests_lock); | ||
| 2166 | regd = rdev->requested_regd; | ||
| 2167 | rdev->requested_regd = NULL; | ||
| 2168 | spin_unlock(®_requests_lock); | ||
| 2169 | |||
| 2170 | if (regd == NULL) | ||
| 2171 | continue; | ||
| 2172 | |||
| 2173 | tmp = get_wiphy_regdom(wiphy); | ||
| 2174 | rcu_assign_pointer(wiphy->regd, regd); | ||
| 2175 | rcu_free_regdom(tmp); | ||
| 2176 | |||
| 2177 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | ||
| 2178 | handle_band_custom(wiphy, wiphy->bands[band], regd); | ||
| 2179 | |||
| 2180 | reg_process_ht_flags(wiphy); | ||
| 2181 | |||
| 2182 | request.wiphy_idx = get_wiphy_idx(wiphy); | ||
| 2183 | request.alpha2[0] = regd->alpha2[0]; | ||
| 2184 | request.alpha2[1] = regd->alpha2[1]; | ||
| 2185 | request.initiator = NL80211_REGDOM_SET_BY_DRIVER; | ||
| 2186 | |||
| 2187 | nl80211_send_wiphy_reg_change_event(&request); | ||
| 2188 | } | ||
| 2189 | |||
| 2190 | reg_check_channels(); | ||
| 2191 | } | ||
| 2192 | |||
| 2150 | static void reg_todo(struct work_struct *work) | 2193 | static void reg_todo(struct work_struct *work) |
| 2151 | { | 2194 | { |
| 2152 | rtnl_lock(); | 2195 | rtnl_lock(); |
| 2153 | reg_process_pending_hints(); | 2196 | reg_process_pending_hints(); |
| 2154 | reg_process_pending_beacon_hints(); | 2197 | reg_process_pending_beacon_hints(); |
| 2198 | reg_process_self_managed_hints(); | ||
| 2155 | rtnl_unlock(); | 2199 | rtnl_unlock(); |
| 2156 | } | 2200 | } |
| 2157 | 2201 | ||
| @@ -2432,6 +2476,8 @@ static void restore_regulatory_settings(bool reset_user) | |||
| 2432 | world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; | 2476 | world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; |
| 2433 | 2477 | ||
| 2434 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 2478 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
| 2479 | if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) | ||
| 2480 | continue; | ||
| 2435 | if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) | 2481 | if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) |
| 2436 | restore_custom_reg_settings(&rdev->wiphy); | 2482 | restore_custom_reg_settings(&rdev->wiphy); |
| 2437 | } | 2483 | } |
| @@ -2835,10 +2881,52 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
| 2835 | return 0; | 2881 | return 0; |
| 2836 | } | 2882 | } |
| 2837 | 2883 | ||
| 2884 | int regulatory_set_wiphy_regd(struct wiphy *wiphy, | ||
| 2885 | struct ieee80211_regdomain *rd) | ||
| 2886 | { | ||
| 2887 | const struct ieee80211_regdomain *regd; | ||
| 2888 | const struct ieee80211_regdomain *prev_regd; | ||
| 2889 | struct cfg80211_registered_device *rdev; | ||
| 2890 | |||
| 2891 | if (WARN_ON(!wiphy || !rd)) | ||
| 2892 | return -EINVAL; | ||
| 2893 | |||
| 2894 | if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED), | ||
| 2895 | "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n")) | ||
| 2896 | return -EPERM; | ||
| 2897 | |||
| 2898 | if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) { | ||
| 2899 | print_regdomain_info(rd); | ||
| 2900 | return -EINVAL; | ||
| 2901 | } | ||
| 2902 | |||
| 2903 | regd = reg_copy_regd(rd); | ||
| 2904 | if (IS_ERR(regd)) | ||
| 2905 | return PTR_ERR(regd); | ||
| 2906 | |||
| 2907 | rdev = wiphy_to_rdev(wiphy); | ||
| 2908 | |||
| 2909 | spin_lock(®_requests_lock); | ||
| 2910 | prev_regd = rdev->requested_regd; | ||
| 2911 | rdev->requested_regd = regd; | ||
| 2912 | spin_unlock(®_requests_lock); | ||
| 2913 | |||
| 2914 | kfree(prev_regd); | ||
| 2915 | |||
| 2916 | schedule_work(®_work); | ||
| 2917 | return 0; | ||
| 2918 | } | ||
| 2919 | EXPORT_SYMBOL(regulatory_set_wiphy_regd); | ||
| 2920 | |||
| 2838 | void wiphy_regulatory_register(struct wiphy *wiphy) | 2921 | void wiphy_regulatory_register(struct wiphy *wiphy) |
| 2839 | { | 2922 | { |
| 2840 | struct regulatory_request *lr; | 2923 | struct regulatory_request *lr; |
| 2841 | 2924 | ||
| 2925 | /* self-managed devices ignore external hints */ | ||
| 2926 | if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) | ||
| 2927 | wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | | ||
| 2928 | REGULATORY_COUNTRY_IE_IGNORE; | ||
| 2929 | |||
| 2842 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2930 | if (!reg_dev_ignore_cell_hint(wiphy)) |
| 2843 | reg_num_devs_support_basehint++; | 2931 | reg_num_devs_support_basehint++; |
| 2844 | 2932 | ||
