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:
Chun-Yeow Yeoh
2013-10-14 19:08:27 -07:00
committed by Johannes Berg
parent c0f17eb9b2
commit 8f2535b92d
6 changed files with 134 additions and 3 deletions

View File

@@ -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();