Skip to content

Commit

Permalink
cfg80211: Enable GO operation on additional channels
Browse files Browse the repository at this point in the history
Allow GO operation on a channel marked with IEEE80211_CHAN_GO_CONCURRENT
iff there is an active station interface that is associated to
an AP operating on the same channel in the 2 GHz band or the same UNII band
(in the 5 GHz band). This relaxation is not allowed if the channel is
marked with IEEE80211_CHAN_RADAR.

Note that this is a permissive approach to the FCC definitions,
that require a clear assessment that the device operating the AP is
an authorized master, i.e., with radar detection and DFS capabilities.

It is assumed that such restrictions are enforced by user space.
Furthermore, it is assumed, that if the conditions that allowed for
the operation of the GO on such a channel change, i.e., the station
interface disconnected from the AP, it is the responsibility of user
space to evacuate the GO from the channel.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Ilan Peer authored and Johannes Berg committed Apr 9, 2014
1 parent 94fc661 commit 174e0cd
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 16 deletions.
4 changes: 3 additions & 1 deletion include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -4539,12 +4539,14 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
* cfg80211_reg_can_beacon - check if beaconing is allowed
* @wiphy: the wiphy
* @chandef: the channel definition
* @iftype: interface type
*
* Return: %true if there is no secondary channel or the secondary channel(s)
* can be used for beaconing (i.e. is not a radar channel etc.)
*/
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef);
struct cfg80211_chan_def *chandef,
enum nl80211_iftype iftype);

/*
* cfg80211_ch_switch_notify - update wdev channel and notify userspace
Expand Down
6 changes: 6 additions & 0 deletions include/net/regulatory.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,19 @@ struct regulatory_request {
* all country IE information processed by the regulatory core. This will
* override %REGULATORY_COUNTRY_IE_FOLLOW_POWER as all country IEs will
* be ignored.
* @REGULATORY_ENABLE_RELAX_NO_IR: for devices that wish to allow the
* NO_IR relaxation, which enables transmissions on channels on which
* otherwise initiating radiation is not allowed. This will enable the
* relaxations enabled under the CFG80211_REG_RELAX_NO_IR configuration
* option
*/
enum ieee80211_regulatory_flags {
REGULATORY_CUSTOM_REG = BIT(0),
REGULATORY_STRICT_REG = BIT(1),
REGULATORY_DISABLE_BEACON_HINTS = BIT(2),
REGULATORY_COUNTRY_IE_FOLLOW_POWER = BIT(3),
REGULATORY_COUNTRY_IE_IGNORE = BIT(4),
REGULATORY_ENABLE_RELAX_NO_IR = BIT(5),
};

struct ieee80211_freq_range {
Expand Down
9 changes: 6 additions & 3 deletions net/mac80211/ibss.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
/* make a copy of the chandef, it could be modified below. */
chandef = *req_chandef;
chan = chandef.chan;
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
NL80211_IFTYPE_ADHOC)) {
if (chandef.width == NL80211_CHAN_WIDTH_5 ||
chandef.width == NL80211_CHAN_WIDTH_10 ||
chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
Expand All @@ -274,7 +275,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
chandef.width = NL80211_CHAN_WIDTH_20;
chandef.center_freq1 = chan->center_freq;
/* check again for downgraded chandef */
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef,
NL80211_IFTYPE_ADHOC)) {
sdata_info(sdata,
"Failed to join IBSS, beacons forbidden\n");
return;
Expand Down Expand Up @@ -861,7 +863,8 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
goto disconnect;
}

if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef)) {
if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, &params.chandef,
NL80211_IFTYPE_ADHOC)) {
sdata_info(sdata,
"IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
ifibss->bssid,
Expand Down
24 changes: 24 additions & 0 deletions net/wireless/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,30 @@ config CFG80211_REG_CELLULAR_HINTS
feature if you have tested and validated this feature on your
systems.

config CFG80211_REG_RELAX_NO_IR
bool "cfg80211 support for NO_IR relaxation"
depends on CFG80211_CERTIFICATION_ONUS
---help---
This option enables support for relaxation of the NO_IR flag for
situations that certain regulatory bodies have provided clarifications
on how relaxation can occur. This feature has an inherent dependency on
userspace features which must have been properly tested and as such is
not enabled by default.

A relaxation feature example is allowing the operation of a P2P group
owner (GO) on channels marked with NO_IR if there is an additional BSS
interface which associated to an AP which userspace assumes or confirms
to be an authorized master, i.e., with radar detection support and DFS
capabilities. However, note that in order to not create daisy chain
scenarios, this relaxation is not allowed in cases that the BSS client
is associated to P2P GO and in addition the P2P GO instantiated on
a channel due to this relaxation should not allow connection from
non P2P clients.

The regulatory core will apply these relaxations only for drivers that
support this feature by declaring the appropriate channel flags and
capabilities in their registration flow.

config CFG80211_DEFAULT_PS
bool "enable powersave by default"
depends on CFG80211
Expand Down
76 changes: 73 additions & 3 deletions net/wireless/chan.c
Original file line number Diff line number Diff line change
Expand Up @@ -661,15 +661,85 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_chandef_usable);

/*
* For GO only, check if the channel can be used under permissive conditions
* mandated by the some regulatory bodies, i.e., the channel is marked with
* IEEE80211_CHAN_GO_CONCURRENT and there is an additional station interface
* associated to an AP on the same channel or on the same UNII band
* (assuming that the AP is an authorized master).
*/
static bool cfg80211_go_permissive_chan(struct cfg80211_registered_device *rdev,
struct ieee80211_channel *chan)
{
struct wireless_dev *wdev_iter;
struct wiphy *wiphy = wiphy_idx_to_wiphy(rdev->wiphy_idx);

ASSERT_RTNL();

if (!config_enabled(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
!(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR) ||
!(chan->flags & IEEE80211_CHAN_GO_CONCURRENT))
return false;

/*
* Generally, it is possible to rely on another device/driver to allow
* the GO concurrent relaxation, however, since the device can further
* enforce the relaxation (by doing a similar verifications as this),
* and thus fail the GO instantiation, consider only the interfaces of
* the current registered device.
*/
list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
struct ieee80211_channel *other_chan = NULL;
int r1, r2;

if (wdev_iter->iftype != NL80211_IFTYPE_STATION ||
!netif_running(wdev_iter->netdev))
continue;

wdev_lock(wdev_iter);
if (wdev_iter->current_bss)
other_chan = wdev_iter->current_bss->pub.channel;
wdev_unlock(wdev_iter);

if (!other_chan)
continue;

if (chan == other_chan)
return true;

if (chan->band != IEEE80211_BAND_5GHZ)
continue;

r1 = cfg80211_get_unii(chan->center_freq);
r2 = cfg80211_get_unii(other_chan->center_freq);

if (r1 != -EINVAL && r1 == r2)
return true;
}

return false;
}

bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
struct cfg80211_chan_def *chandef,
enum nl80211_iftype iftype)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
bool res;
u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_RADAR;

trace_cfg80211_reg_can_beacon(wiphy, chandef);
trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype);

/*
* Under certain conditions suggested by the some regulatory bodies
* a GO can operate on channels marked with IEEE80211_NO_IR
* so set this flag only if such relaxations are not enabled and
* the conditions are not met.
*/
if (iftype != NL80211_IFTYPE_P2P_GO ||
!cfg80211_go_permissive_chan(rdev, chandef->chan))
prohibited_flags |= IEEE80211_CHAN_NO_IR;

if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 &&
cfg80211_chandef_dfs_available(wiphy, chandef)) {
Expand Down
3 changes: 2 additions & 1 deletion net/wireless/mesh.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
scan_width);
}

if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef,
NL80211_IFTYPE_MESH_POINT))
return -EINVAL;

err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef);
Expand Down
11 changes: 7 additions & 4 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
result = -EBUSY;
break;
}
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
result = -EINVAL;
break;
}
Expand Down Expand Up @@ -3271,7 +3271,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
} else if (!nl80211_get_ap_channel(rdev, &params))
return -EINVAL;

if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef,
wdev->iftype))
return -EINVAL;

err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
Expand Down Expand Up @@ -5941,7 +5942,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;

if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef,
wdev->iftype))
return -EINVAL;

switch (dev->ieee80211_ptr->iftype) {
Expand Down Expand Up @@ -6717,7 +6719,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;

if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef,
NL80211_IFTYPE_ADHOC))
return -EINVAL;

switch (ibss.chandef.width) {
Expand Down
29 changes: 29 additions & 0 deletions net/wireless/reg.c
Original file line number Diff line number Diff line change
Expand Up @@ -2616,6 +2616,35 @@ static void reg_timeout_work(struct work_struct *work)
rtnl_unlock();
}

/*
* See http://www.fcc.gov/document/5-ghz-unlicensed-spectrum-unii, for
* UNII band definitions
*/
int cfg80211_get_unii(int freq)
{
/* UNII-1 */
if (freq >= 5150 && freq <= 5250)
return 0;

/* UNII-2A */
if (freq > 5250 && freq <= 5350)
return 1;

/* UNII-2B */
if (freq > 5350 && freq <= 5470)
return 2;

/* UNII-2C */
if (freq > 5470 && freq <= 5725)
return 3;

/* UNII-3 */
if (freq > 5725 && freq <= 5825)
return 4;

return -EINVAL;
}

int __init regulatory_init(void)
{
int err = 0;
Expand Down
12 changes: 12 additions & 0 deletions net/wireless/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,16 @@ void regulatory_hint_country_ie(struct wiphy *wiphy,
*/
void regulatory_hint_disconnect(void);

/**
* cfg80211_get_unii - get the U-NII band for the frequency
* @freq: the frequency for which we want to get the UNII band.
* Get a value specifying the U-NII band frequency belongs to.
* U-NII bands are defined by the FCC in C.F.R 47 part 15.
*
* Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A,
* 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3.
*/
int cfg80211_get_unii(int freq);

#endif /* __NET_WIRELESS_REG_H */
11 changes: 7 additions & 4 deletions net/wireless/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -2193,18 +2193,21 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify,
);

TRACE_EVENT(cfg80211_reg_can_beacon,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
TP_ARGS(wiphy, chandef),
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
enum nl80211_iftype iftype),
TP_ARGS(wiphy, chandef, iftype),
TP_STRUCT__entry(
WIPHY_ENTRY
CHAN_DEF_ENTRY
__field(enum nl80211_iftype, iftype)
),
TP_fast_assign(
WIPHY_ASSIGN;
CHAN_DEF_ASSIGN(chandef);
__entry->iftype = iftype;
),
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", iftype=%d",
WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->iftype)
);

TRACE_EVENT(cfg80211_chandef_dfs_required,
Expand Down

0 comments on commit 174e0cd

Please sign in to comment.