mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
mac80211: process the CSA frame for mesh accordingly
Process the CSA frame according to the procedures define in IEEE Std 802.11-2012 section 10.9.8.4.3 as follow: * The mesh channel switch parameters element (MCSP) must be availabe. * If the MCSP's TTL is 1, drop the frame but still process the CSA. * If the MCSP's precedence value is less than or equal to the current precedence value, drop the frame and do not process the CSA. * The CSA frame is forwarded after TTL is decremented by 1 and the initiator field is set to 0. Transmit restrict field and others are maintained as is. * No beacon or probe response frame are handled here. Also, introduce the debug message used for mesh CSA purpose. Signed-off-by: Chun-Yeow Yeoh <yeohchunyeow@cozybit.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
c0f17eb9b2
commit
8f2535b92d
@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie {
|
||||
u8 sec_chan_offs;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE
|
||||
*
|
||||
* This structure represents the "Mesh Channel Switch Paramters element"
|
||||
*/
|
||||
struct ieee80211_mesh_chansw_params_ie {
|
||||
u8 mesh_ttl;
|
||||
u8 mesh_flags;
|
||||
__le16 mesh_reason;
|
||||
__le16 mesh_pre_value;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE
|
||||
*/
|
||||
@ -750,6 +762,14 @@ enum mesh_config_capab_flags {
|
||||
IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40,
|
||||
};
|
||||
|
||||
/**
|
||||
* mesh channel switch parameters element's flag indicator
|
||||
*
|
||||
*/
|
||||
#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0)
|
||||
#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1)
|
||||
#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2)
|
||||
|
||||
/**
|
||||
* struct ieee80211_rann_ie
|
||||
*
|
||||
|
@ -259,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_MESH_CSA_DEBUG
|
||||
bool "Verbose mesh channel switch debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
depends on MAC80211_MESH
|
||||
---help---
|
||||
Selecting this option causes mac80211 to print out very verbose mesh
|
||||
channel switch debugging messages (when mac80211 is taking part in a
|
||||
mesh network).
|
||||
|
||||
Do not select this option.
|
||||
|
||||
config MAC80211_MESH_PS_DEBUG
|
||||
bool "Verbose mesh powersave debugging"
|
||||
depends on MAC80211_DEBUG_MENU
|
||||
|
@ -44,6 +44,12 @@
|
||||
#define MAC80211_MESH_SYNC_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG
|
||||
#define MAC80211_MESH_CSA_DEBUG 1
|
||||
#else
|
||||
#define MAC80211_MESH_CSA_DEBUG 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH_PS_DEBUG
|
||||
#define MAC80211_MESH_PS_DEBUG 1
|
||||
#else
|
||||
@ -157,6 +163,10 @@ do { \
|
||||
_sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define mcsa_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_MESH_CSA_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define mps_dbg(sdata, fmt, ...) \
|
||||
_sdata_dbg(MAC80211_MESH_PS_DEBUG, \
|
||||
sdata, fmt, ##__VA_ARGS__)
|
||||
|
@ -603,6 +603,9 @@ struct ieee80211_if_mesh {
|
||||
int ps_peers_light_sleep;
|
||||
int ps_peers_deep_sleep;
|
||||
struct ps_data ps;
|
||||
/* Channel Switching Support */
|
||||
bool chsw_init;
|
||||
u16 pre_value;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
@ -1252,6 +1255,7 @@ struct ieee802_11_elems {
|
||||
const struct ieee80211_timeout_interval_ie *timeout_int;
|
||||
const u8 *opmode_notif;
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
|
||||
|
||||
/* length of them, respectively */
|
||||
u8 ssid_len;
|
||||
|
@ -920,6 +920,82 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
|
||||
stype, mgmt, &elems, rx_status);
|
||||
}
|
||||
|
||||
static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct ieee80211_mgmt *mgmt_fwd;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u8 *pos = mgmt->u.action.u.chan_switch.variable;
|
||||
size_t offset_ttl;
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len);
|
||||
|
||||
/* offset_ttl is based on whether the secondary channel
|
||||
* offset is available or not. Substract 1 from the mesh TTL
|
||||
* and disable the initiator flag before forwarding.
|
||||
*/
|
||||
offset_ttl = (len < 42) ? 7 : 10;
|
||||
*(pos + offset_ttl) -= 1;
|
||||
*(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR;
|
||||
|
||||
memcpy(mgmt_fwd, mgmt, len);
|
||||
eth_broadcast_addr(mgmt_fwd->da);
|
||||
memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN);
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
struct ieee802_11_elems elems;
|
||||
u16 pre_value;
|
||||
bool block_tx, fwd_csa = true;
|
||||
size_t baselen;
|
||||
u8 *pos, ttl;
|
||||
|
||||
if (mgmt->u.action.u.measurement.action_code !=
|
||||
WLAN_ACTION_SPCT_CHL_SWITCH)
|
||||
return;
|
||||
|
||||
pos = mgmt->u.action.u.chan_switch.variable;
|
||||
baselen = offsetof(struct ieee80211_mgmt,
|
||||
u.action.u.chan_switch.variable);
|
||||
ieee802_11_parse_elems(pos, len - baselen, false, &elems);
|
||||
|
||||
ttl = elems.mesh_chansw_params_ie->mesh_ttl;
|
||||
if (!--ttl)
|
||||
fwd_csa = false;
|
||||
|
||||
pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value);
|
||||
if (ifmsh->pre_value >= pre_value)
|
||||
return;
|
||||
|
||||
ifmsh->pre_value = pre_value;
|
||||
|
||||
/* forward or re-broadcast the CSA frame */
|
||||
if (fwd_csa) {
|
||||
if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0)
|
||||
mcsa_dbg(sdata, "Failed to forward the CSA frame");
|
||||
}
|
||||
|
||||
/* block the Tx only after forwarding the CSA frame if required */
|
||||
block_tx = elems.mesh_chansw_params_ie->mesh_flags &
|
||||
WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT;
|
||||
if (block_tx)
|
||||
ieee80211_stop_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
}
|
||||
|
||||
static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len,
|
||||
@ -939,6 +1015,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
|
||||
if (mesh_action_is_path_sel(mgmt))
|
||||
mesh_rx_path_sel_frame(sdata, mgmt, len);
|
||||
break;
|
||||
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
||||
mesh_rx_csa_frame(sdata, mgmt, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1056,13 +1135,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
(unsigned long) sdata);
|
||||
|
||||
ifmsh->accepting_plinks = true;
|
||||
ifmsh->preq_id = 0;
|
||||
ifmsh->sn = 0;
|
||||
ifmsh->num_gates = 0;
|
||||
atomic_set(&ifmsh->mpaths, 0);
|
||||
mesh_rmc_init(sdata);
|
||||
ifmsh->last_preq = jiffies;
|
||||
ifmsh->next_perr = jiffies;
|
||||
ifmsh->chsw_init = false;
|
||||
/* Allocate all mesh structures when creating the first mesh interface. */
|
||||
if (!mesh_allocated)
|
||||
ieee80211s_init();
|
||||
|
@ -740,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
case WLAN_EID_TIMEOUT_INTERVAL:
|
||||
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
case WLAN_EID_CHAN_SWITCH_PARAM:
|
||||
/*
|
||||
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
|
||||
* that if the content gets bigger it might be needed more than once
|
||||
@ -905,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
}
|
||||
elems->sec_chan_offs = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_CHAN_SWITCH_PARAM:
|
||||
if (elen !=
|
||||
sizeof(*elems->mesh_chansw_params_ie)) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->mesh_chansw_params_ie = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
if (!action ||
|
||||
elen != sizeof(*elems->wide_bw_chansw_ie)) {
|
||||
|
Loading…
Reference in New Issue
Block a user