summaryrefslogtreecommitdiff
path: root/net/mac80211/mesh_plink.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-01-25 11:17:34 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-25 11:17:34 -0800
commit4ba9920e5e9c0e16b5ed24292d45322907bb9035 (patch)
tree7d023baea59ed0886ded1f0b6d1c6385690b88f7 /net/mac80211/mesh_plink.c
parent82c477669a4665eb4e52030792051e0559ee2a36 (diff)
parent8b662fe70c68282f78482dc272df0c4f355e49f5 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller: 1) BPF debugger and asm tool by Daniel Borkmann. 2) Speed up create/bind in AF_PACKET, also from Daniel Borkmann. 3) Correct reciprocal_divide and update users, from Hannes Frederic Sowa and Daniel Borkmann. 4) Currently we only have a "set" operation for the hw timestamp socket ioctl, add a "get" operation to match. From Ben Hutchings. 5) Add better trace events for debugging driver datapath problems, also from Ben Hutchings. 6) Implement auto corking in TCP, from Eric Dumazet. Basically, if we have a small send and a previous packet is already in the qdisc or device queue, defer until TX completion or we get more data. 7) Allow userspace to manage ipv6 temporary addresses, from Jiri Pirko. 8) Add a qdisc bypass option for AF_PACKET sockets, from Daniel Borkmann. 9) Share IP header compression code between Bluetooth and IEEE802154 layers, from Jukka Rissanen. 10) Fix ipv6 router reachability probing, from Jiri Benc. 11) Allow packets to be captured on macvtap devices, from Vlad Yasevich. 12) Support tunneling in GRO layer, from Jerry Chu. 13) Allow bonding to be configured fully using netlink, from Scott Feldman. 14) Allow AF_PACKET users to obtain the VLAN TPID, just like they can already get the TCI. From Atzm Watanabe. 15) New "Heavy Hitter" qdisc, from Terry Lam. 16) Significantly improve the IPSEC support in pktgen, from Fan Du. 17) Allow ipv4 tunnels to cache routes, just like sockets. From Tom Herbert. 18) Add Proportional Integral Enhanced packet scheduler, from Vijay Subramanian. 19) Allow openvswitch to mmap'd netlink, from Thomas Graf. 20) Key TCP metrics blobs also by source address, not just destination address. From Christoph Paasch. 21) Support 10G in generic phylib. From Andy Fleming. 22) Try to short-circuit GRO flow compares using device provided RX hash, if provided. From Tom Herbert. The wireless and netfilter folks have been busy little bees too. * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2064 commits) net/cxgb4: Fix referencing freed adapter ipv6: reallocate addrconf router for ipv6 address when lo device up fib_frontend: fix possible NULL pointer dereference rtnetlink: remove IFLA_BOND_SLAVE definition rtnetlink: remove check for fill_slave_info in rtnl_have_link_slave_info qlcnic: update version to 5.3.55 qlcnic: Enhance logic to calculate msix vectors. qlcnic: Refactor interrupt coalescing code for all adapters. qlcnic: Update poll controller code path qlcnic: Interrupt code cleanup qlcnic: Enhance Tx timeout debugging. qlcnic: Use bool for rx_mac_learn. bonding: fix u64 division rtnetlink: add missing IFLA_BOND_AD_INFO_UNSPEC sfc: Use the correct maximum TX DMA ring size for SFC9100 Add Shradha Shah as the sfc driver maintainer. net/vxlan: Share RX skb de-marking and checksum checks with ovs tulip: cleanup by using ARRAY_SIZE() ip_tunnel: clear IPCB in ip_tunnel_xmit() in case dst_link_failure() is called net/cxgb4: Don't retrieve stats during recovery ...
Diffstat (limited to 'net/mac80211/mesh_plink.c')
-rw-r--r--net/mac80211/mesh_plink.c709
1 files changed, 368 insertions, 341 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 4301aa5aa227..e8f60aa2e848 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -19,12 +19,6 @@
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
-/* We only need a valid sta if user configured a minimum rssi_threshold. */
-#define rssi_threshold_check(sta, sdata) \
- (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\
- (sta && (s8) -ewma_read(&sta->avg_signal) > \
- sdata->u.mesh.mshcfg.rssi_threshold))
-
enum plink_event {
PLINK_UNDEFINED,
OPN_ACPT,
@@ -61,7 +55,17 @@ static const char * const mplevents[] = {
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
- u8 *da, __le16 llid, __le16 plid, __le16 reason);
+ u8 *da, u16 llid, u16 plid, u16 reason);
+
+
+/* We only need a valid sta if user configured a minimum rssi_threshold. */
+static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
+ return rssi_threshold == 0 ||
+ (sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold);
+}
/**
* mesh_plink_fsm_restart - restart a mesh peer link finite state machine
@@ -242,7 +246,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
spin_lock_bh(&sta->lock);
changed = __mesh_plink_deactivate(sta);
- sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED);
+ sta->reason = WLAN_REASON_MESH_PEER_CANCELED;
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, sta->llid, sta->plid,
sta->reason);
@@ -253,7 +257,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
- u8 *da, __le16 llid, __le16 plid, __le16 reason)
+ u8 *da, u16 llid, u16 plid, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -279,7 +283,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
2 + 8 + /* peering IE */
sdata->u.mesh.ie_len);
if (!skb)
- return -1;
+ return err;
info = IEEE80211_SKB_CB(skb);
skb_reserve(skb, local->tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
@@ -301,7 +305,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
/* AID */
pos = skb_put(skb, 2);
- memcpy(pos + 2, &plid, 2);
+ put_unaligned_le16(plid, pos + 2);
}
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@@ -343,14 +347,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
*pos++ = ie_len;
memcpy(pos, &peering_proto, 2);
pos += 2;
- memcpy(pos, &llid, 2);
+ put_unaligned_le16(llid, pos);
pos += 2;
if (include_plid) {
- memcpy(pos, &plid, 2);
+ put_unaligned_le16(plid, pos);
pos += 2;
}
if (action == WLAN_SP_MESH_PEERING_CLOSE) {
- memcpy(pos, &reason, 2);
+ put_unaligned_le16(reason, pos);
pos += 2;
}
@@ -433,6 +437,7 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
set_sta_flag(sta, WLAN_STA_WME);
+ sta->sta.wme = true;
return sta;
}
@@ -518,7 +523,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
sta->plink_state == NL80211_PLINK_LISTEN &&
sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks &&
- rssi_threshold_check(sta, sdata))
+ rssi_threshold_check(sdata, sta))
changed = mesh_plink_open(sta);
ieee80211_mps_frame_release(sta, elems);
@@ -530,9 +535,10 @@ out:
static void mesh_plink_timer(unsigned long data)
{
struct sta_info *sta;
- __le16 llid, plid, reason;
+ u16 reason = 0;
struct ieee80211_sub_if_data *sdata;
struct mesh_config *mshcfg;
+ enum ieee80211_self_protected_actioncode action = 0;
/*
* This STA is valid because sta_info_destroy() will
@@ -553,9 +559,6 @@ static void mesh_plink_timer(unsigned long data)
mpl_dbg(sta->sdata,
"Mesh plink timer for %pM fired on state %s\n",
sta->sta.addr, mplstates[sta->plink_state]);
- reason = 0;
- llid = sta->llid;
- plid = sta->plid;
sdata = sta->sdata;
mshcfg = &sdata->u.mesh.mshcfg;
@@ -574,33 +577,31 @@ static void mesh_plink_timer(unsigned long data)
rand % sta->plink_timeout;
++sta->plink_retries;
mod_plink_timer(sta, sta->plink_timeout);
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
+ action = WLAN_SP_MESH_PEERING_OPEN;
break;
}
- reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES);
+ reason = WLAN_REASON_MESH_MAX_RETRIES;
/* fall through on else */
case NL80211_PLINK_CNF_RCVD:
/* confirm timer */
if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
+ reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
sta->plink_state = NL80211_PLINK_HOLDING;
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case NL80211_PLINK_HOLDING:
/* holding timer */
del_timer(&sta->plink_timer);
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->lock);
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
+ spin_unlock_bh(&sta->lock);
+ if (action)
+ mesh_plink_frame_tx(sdata, action, sta->sta.addr,
+ sta->llid, sta->plid, reason);
}
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
@@ -612,9 +613,40 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
add_timer(&sta->plink_timer);
}
+static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
+ u16 llid)
+{
+ struct ieee80211_local *local = sdata->local;
+ bool in_use = false;
+ struct sta_info *sta;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (!memcmp(&sta->llid, &llid, sizeof(llid))) {
+ in_use = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return in_use;
+}
+
+static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
+{
+ u16 llid;
+
+ do {
+ get_random_bytes(&llid, sizeof(llid));
+ /* for mesh PS we still only have the AID range for TIM bits */
+ llid = (llid % IEEE80211_MAX_AID) + 1;
+ } while (llid_in_use(sdata, llid));
+
+ return llid;
+}
+
u32 mesh_plink_open(struct sta_info *sta)
{
- __le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed;
@@ -622,8 +654,7 @@ u32 mesh_plink_open(struct sta_info *sta)
return 0;
spin_lock_bh(&sta->lock);
- get_random_bytes(&llid, 2);
- sta->llid = llid;
+ sta->llid = mesh_get_new_llid(sdata);
if (sta->plink_state != NL80211_PLINK_LISTEN &&
sta->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->lock);
@@ -640,7 +671,7 @@ u32 mesh_plink_open(struct sta_info *sta)
changed = ieee80211_mps_local_status_update(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
+ sta->sta.addr, sta->llid, 0, 0);
return changed;
}
@@ -656,390 +687,147 @@ u32 mesh_plink_block(struct sta_info *sta)
return changed;
}
-
-void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
+static void mesh_plink_close(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ enum plink_event event)
{
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
- struct ieee802_11_elems elems;
- struct sta_info *sta;
- enum plink_event event;
- enum ieee80211_self_protected_actioncode ftype;
- size_t baselen;
- bool matches_local = true;
- u8 ie_len;
- u8 *baseaddr;
- u32 changed = 0;
- __le16 plid, llid, reason;
-
- /* need action_code, aux */
- if (len < IEEE80211_MIN_ACTION_SIZE + 3)
- return;
-
- if (sdata->u.mesh.user_mpm)
- /* userspace must register for these */
- return;
-
- if (is_multicast_ether_addr(mgmt->da)) {
- mpl_dbg(sdata,
- "Mesh plink: ignore frame from multicast address\n");
- return;
- }
-
- baseaddr = mgmt->u.action.u.self_prot.variable;
- baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt;
- if (mgmt->u.action.u.self_prot.action_code ==
- WLAN_SP_MESH_PEERING_CONFIRM) {
- baseaddr += 4;
- baselen += 4;
- }
- ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
-
- if (!elems.peering) {
- mpl_dbg(sdata,
- "Mesh plink: missing necessary peer link ie\n");
- return;
- }
- if (elems.rsn_len &&
- sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
- mpl_dbg(sdata,
- "Mesh plink: can't establish link with secure peer\n");
- return;
- }
+ u16 reason = (event == CLS_ACPT) ?
+ WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
- ftype = mgmt->u.action.u.self_prot.action_code;
- ie_len = elems.peering_len;
- if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
- (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
- (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
- && ie_len != 8)) {
- mpl_dbg(sdata,
- "Mesh plink: incorrect plink ie length %d %d\n",
- ftype, ie_len);
- return;
- }
-
- if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
- (!elems.mesh_id || !elems.mesh_config)) {
- mpl_dbg(sdata, "Mesh plink: missing necessary ie\n");
- return;
- }
- /* Note the lines below are correct, the llid in the frame is the plid
- * from the point of view of this host.
- */
- memcpy(&plid, PLINK_GET_LLID(elems.peering), 2);
- if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
- (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
- memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
-
- /* WARNING: Only for sta pointer, is dropped & re-acquired */
- rcu_read_lock();
-
- sta = sta_info_get(sdata, mgmt->sa);
- if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) {
- mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n");
- rcu_read_unlock();
- return;
- }
-
- if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
- !rssi_threshold_check(sta, sdata)) {
- mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n",
- mgmt->sa);
- rcu_read_unlock();
- return;
- }
-
- if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) {
- mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
- rcu_read_unlock();
- return;
- }
+ sta->reason = reason;
+ sta->plink_state = NL80211_PLINK_HOLDING;
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
+}
- if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) {
- rcu_read_unlock();
- return;
- }
+static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
+ u32 changed = 0;
- /* Now we will figure out the appropriate event... */
- event = PLINK_UNDEFINED;
- if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
- !mesh_matches_local(sdata, &elems)) {
- matches_local = false;
- switch (ftype) {
- case WLAN_SP_MESH_PEERING_OPEN:
- event = OPN_RJCT;
- break;
- case WLAN_SP_MESH_PEERING_CONFIRM:
- event = CNF_RJCT;
- break;
- default:
- break;
- }
- }
+ del_timer(&sta->plink_timer);
+ sta->plink_state = NL80211_PLINK_ESTAB;
+ changed |= mesh_plink_inc_estab_count(sdata);
+ changed |= mesh_set_ht_prot_mode(sdata);
+ changed |= mesh_set_short_slot_time(sdata);
+ mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr);
+ ieee80211_mps_sta_status_update(sta);
+ changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode);
+ return changed;
+}
- if (!sta && !matches_local) {
- rcu_read_unlock();
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
- llid = 0;
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- mgmt->sa, llid, plid, reason);
- return;
- } else if (!sta) {
- /* ftype == WLAN_SP_MESH_PEERING_OPEN */
- if (!mesh_plink_free_count(sdata)) {
- mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
- rcu_read_unlock();
- return;
- }
- event = OPN_ACPT;
- } else if (matches_local) {
- switch (ftype) {
- case WLAN_SP_MESH_PEERING_OPEN:
- if (!mesh_plink_free_count(sdata) ||
- (sta->plid && sta->plid != plid))
- event = OPN_IGNR;
- else
- event = OPN_ACPT;
- break;
- case WLAN_SP_MESH_PEERING_CONFIRM:
- if (!mesh_plink_free_count(sdata) ||
- (sta->llid != llid || sta->plid != plid))
- event = CNF_IGNR;
- else
- event = CNF_ACPT;
- break;
- case WLAN_SP_MESH_PEERING_CLOSE:
- if (sta->plink_state == NL80211_PLINK_ESTAB)
- /* Do not check for llid or plid. This does not
- * follow the standard but since multiple plinks
- * per sta are not supported, it is necessary in
- * order to avoid a livelock when MP A sees an
- * establish peer link to MP B but MP B does not
- * see it. This can be caused by a timeout in
- * B's peer link establishment or B beign
- * restarted.
- */
- event = CLS_ACPT;
- else if (sta->plid != plid)
- event = CLS_IGNR;
- else if (ie_len == 7 && sta->llid != llid)
- event = CLS_IGNR;
- else
- event = CLS_ACPT;
- break;
- default:
- mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n");
- rcu_read_unlock();
- return;
- }
- }
+/**
+ * mesh_plink_fsm - step @sta MPM based on @event
+ *
+ * @sdata: interface
+ * @sta: mesh neighbor
+ * @event: peering event
+ *
+ * Return: changed MBSS flags
+ */
+static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, enum plink_event event)
+{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
+ enum ieee80211_self_protected_actioncode action = 0;
+ u32 changed = 0;
- if (event == OPN_ACPT) {
- rcu_read_unlock();
- /* allocate sta entry if necessary and update info */
- sta = mesh_sta_info_get(sdata, mgmt->sa, &elems);
- if (!sta) {
- mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
- rcu_read_unlock();
- return;
- }
- }
+ mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
+ mplstates[sta->plink_state], mplevents[event]);
- mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa,
- mplstates[sta->plink_state], mplevents[event]);
- reason = 0;
spin_lock_bh(&sta->lock);
switch (sta->plink_state) {
- /* spin_unlock as soon as state is updated at each case */
case NL80211_PLINK_LISTEN:
switch (event) {
case CLS_ACPT:
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
sta->plink_state = NL80211_PLINK_OPN_RCVD;
- sta->plid = plid;
- get_random_bytes(&llid, 2);
- sta->llid = llid;
+ sta->llid = mesh_get_new_llid(sdata);
mesh_plink_timer_set(sta,
mshcfg->dot11MeshRetryTimeout);
/* set the non-peer mode to active during peering */
changed |= ieee80211_mps_local_status_update(sdata);
-
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_OPEN;
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_OPN_SNT:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
case CLS_ACPT:
- if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
- sta->plink_state = NL80211_PLINK_HOLDING;
- if (!mod_plink_timer(sta,
- mshcfg->dot11MeshHoldingTimeout))
- sta->ignore_plink_timer = true;
-
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
/* retry timer is left untouched */
sta->plink_state = NL80211_PLINK_OPN_RCVD;
- sta->plid = plid;
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
mshcfg->dot11MeshConfirmTimeout))
sta->ignore_plink_timer = true;
-
- spin_unlock_bh(&sta->lock);
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_OPN_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
case CLS_ACPT:
- if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
- sta->plink_state = NL80211_PLINK_HOLDING;
- if (!mod_plink_timer(sta,
- mshcfg->dot11MeshHoldingTimeout))
- sta->ignore_plink_timer = true;
-
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
case CNF_ACPT:
- del_timer(&sta->plink_timer);
- sta->plink_state = NL80211_PLINK_ESTAB;
- spin_unlock_bh(&sta->lock);
- changed |= mesh_plink_inc_estab_count(sdata);
- changed |= mesh_set_ht_prot_mode(sdata);
- changed |= mesh_set_short_slot_time(sdata);
- mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
- sta->sta.addr);
- ieee80211_mps_sta_status_update(sta);
- changed |= ieee80211_mps_set_sta_local_pm(sta,
- mshcfg->power_mode);
+ changed |= mesh_plink_establish(sdata, sta);
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_CNF_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
case CLS_ACPT:
- if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
- sta->plink_state = NL80211_PLINK_HOLDING;
- if (!mod_plink_timer(sta,
- mshcfg->dot11MeshHoldingTimeout))
- sta->ignore_plink_timer = true;
-
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
- del_timer(&sta->plink_timer);
- sta->plink_state = NL80211_PLINK_ESTAB;
- spin_unlock_bh(&sta->lock);
- changed |= mesh_plink_inc_estab_count(sdata);
- changed |= mesh_set_ht_prot_mode(sdata);
- changed |= mesh_set_short_slot_time(sdata);
- mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
- sta->sta.addr);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
- ieee80211_mps_sta_status_update(sta);
- changed |= ieee80211_mps_set_sta_local_pm(sta,
- mshcfg->power_mode);
+ changed |= mesh_plink_establish(sdata, sta);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_ESTAB:
switch (event) {
case CLS_ACPT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
changed |= __mesh_plink_deactivate(sta);
- sta->plink_state = NL80211_PLINK_HOLDING;
- llid = sta->llid;
- mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
- spin_unlock_bh(&sta->lock);
changed |= mesh_set_ht_prot_mode(sdata);
changed |= mesh_set_short_slot_time(sdata);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -1049,32 +837,271 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (del_timer(&sta->plink_timer))
sta->ignore_plink_timer = 1;
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
case CNF_ACPT:
case OPN_RJCT:
case CNF_RJCT:
- llid = sta->llid;
- reason = sta->reason;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
default:
- spin_unlock_bh(&sta->lock);
+ break;
}
break;
default:
/* should not get here, PLINK_BLOCKED is dealt with at the
* beginning of the function
*/
- spin_unlock_bh(&sta->lock);
break;
}
+ spin_unlock_bh(&sta->lock);
+ if (action) {
+ mesh_plink_frame_tx(sdata, action, sta->sta.addr,
+ sta->llid, sta->plid, sta->reason);
+
+ /* also send confirm in open case */
+ if (action == WLAN_SP_MESH_PEERING_OPEN) {
+ mesh_plink_frame_tx(sdata,
+ WLAN_SP_MESH_PEERING_CONFIRM,
+ sta->sta.addr, sta->llid,
+ sta->plid, 0);
+ }
+ }
+
+ return changed;
+}
+
+/*
+ * mesh_plink_get_event - get correct MPM event
+ *
+ * @sdata: interface
+ * @sta: peer, leave NULL if processing a frame from a new suitable peer
+ * @elems: peering management IEs
+ * @ftype: frame type
+ * @llid: peer's peer link ID
+ * @plid: peer's local link ID
+ *
+ * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as
+ * an error.
+ */
+static enum plink_event
+mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee802_11_elems *elems,
+ enum ieee80211_self_protected_actioncode ftype,
+ u16 llid, u16 plid)
+{
+ enum plink_event event = PLINK_UNDEFINED;
+ u8 ie_len = elems->peering_len;
+ bool matches_local;
+
+ matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE ||
+ mesh_matches_local(sdata, elems));
+
+ /* deny open request from non-matching peer */
+ if (!matches_local && !sta) {
+ event = OPN_RJCT;
+ goto out;
+ }
+
+ if (!sta) {
+ if (ftype != WLAN_SP_MESH_PEERING_OPEN) {
+ mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n");
+ goto out;
+ }
+ /* ftype == WLAN_SP_MESH_PEERING_OPEN */
+ if (!mesh_plink_free_count(sdata)) {
+ mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
+ goto out;
+ }
+ } else {
+ if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
+ mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
+ goto out;
+ }
+ if (sta->plink_state == NL80211_PLINK_BLOCKED)
+ goto out;
+ }
+
+ /* new matching peer */
+ if (!sta) {
+ event = OPN_ACPT;
+ goto out;
+ }
+
+ switch (ftype) {
+ case WLAN_SP_MESH_PEERING_OPEN:
+ if (!matches_local)
+ event = OPN_RJCT;
+ if (!mesh_plink_free_count(sdata) ||
+ (sta->plid && sta->plid != plid))
+ event = OPN_IGNR;
+ else
+ event = OPN_ACPT;
+ break;
+ case WLAN_SP_MESH_PEERING_CONFIRM:
+ if (!matches_local)
+ event = CNF_RJCT;
+ if (!mesh_plink_free_count(sdata) ||
+ (sta->llid != llid || sta->plid != plid))
+ event = CNF_IGNR;
+ else
+ event = CNF_ACPT;
+ break;
+ case WLAN_SP_MESH_PEERING_CLOSE:
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ /* Do not check for llid or plid. This does not
+ * follow the standard but since multiple plinks
+ * per sta are not supported, it is necessary in
+ * order to avoid a livelock when MP A sees an
+ * establish peer link to MP B but MP B does not
+ * see it. This can be caused by a timeout in
+ * B's peer link establishment or B beign
+ * restarted.
+ */
+ event = CLS_ACPT;
+ else if (sta->plid != plid)
+ event = CLS_IGNR;
+ else if (ie_len == 8 && sta->llid != llid)
+ event = CLS_IGNR;
+ else
+ event = CLS_ACPT;
+ break;
+ default:
+ mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n");
+ break;
+ }
+
+out:
+ return event;
+}
+static void
+mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems)
+{
+
+ struct sta_info *sta;
+ enum plink_event event;
+ enum ieee80211_self_protected_actioncode ftype;
+ u32 changed = 0;
+ u8 ie_len = elems->peering_len;
+ __le16 _plid, _llid;
+ u16 plid, llid = 0;
+
+ if (!elems->peering) {
+ mpl_dbg(sdata,
+ "Mesh plink: missing necessary peer link ie\n");
+ return;
+ }
+
+ if (elems->rsn_len &&
+ sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
+ mpl_dbg(sdata,
+ "Mesh plink: can't establish link with secure peer\n");
+ return;
+ }
+
+ ftype = mgmt->u.action.u.self_prot.action_code;
+ if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
+ (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
+ (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
+ && ie_len != 8)) {
+ mpl_dbg(sdata,
+ "Mesh plink: incorrect plink ie length %d %d\n",
+ ftype, ie_len);
+ return;
+ }
+
+ if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
+ (!elems->mesh_id || !elems->mesh_config)) {
+ mpl_dbg(sdata, "Mesh plink: missing necessary ie\n");
+ return;
+ }
+ /* Note the lines below are correct, the llid in the frame is the plid
+ * from the point of view of this host.
+ */
+ memcpy(&_plid, PLINK_GET_LLID(elems->peering), sizeof(__le16));
+ plid = le16_to_cpu(_plid);
+ if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
+ (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) {
+ memcpy(&_llid, PLINK_GET_PLID(elems->peering), sizeof(__le16));
+ llid = le16_to_cpu(_llid);
+ }
+
+ /* WARNING: Only for sta pointer, is dropped & re-acquired */
+ rcu_read_lock();
+
+ sta = sta_info_get(sdata, mgmt->sa);
+
+ if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
+ !rssi_threshold_check(sdata, sta)) {
+ mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n",
+ mgmt->sa);
+ goto unlock_rcu;
+ }
+
+ /* Now we will figure out the appropriate event... */
+ event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid);
+
+ if (event == OPN_ACPT) {
+ rcu_read_unlock();
+ /* allocate sta entry if necessary and update info */
+ sta = mesh_sta_info_get(sdata, mgmt->sa, elems);
+ if (!sta) {
+ mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
+ goto unlock_rcu;
+ }
+ sta->plid = plid;
+ } else if (!sta && event == OPN_RJCT) {
+ mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
+ mgmt->sa, 0, plid,
+ WLAN_REASON_MESH_CONFIG);
+ goto unlock_rcu;
+ } else if (!sta || event == PLINK_UNDEFINED) {
+ /* something went wrong */
+ goto unlock_rcu;
+ }
+
+ changed |= mesh_plink_fsm(sdata, sta, event);
+
+unlock_rcu:
rcu_read_unlock();
if (changed)
ieee80211_mbss_info_change_notify(sdata, changed);
}
+
+void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee802_11_elems elems;
+ size_t baselen;
+ u8 *baseaddr;
+
+ /* need action_code, aux */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 3)
+ return;
+
+ if (sdata->u.mesh.user_mpm)
+ /* userspace must register for these */
+ return;
+
+ if (is_multicast_ether_addr(mgmt->da)) {
+ mpl_dbg(sdata,
+ "Mesh plink: ignore frame from multicast address\n");
+ return;
+ }
+
+ baseaddr = mgmt->u.action.u.self_prot.variable;
+ baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt;
+ if (mgmt->u.action.u.self_prot.action_code ==
+ WLAN_SP_MESH_PEERING_CONFIRM) {
+ baseaddr += 4;
+ baselen += 4;
+ }
+ ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
+ mesh_process_plink_frame(sdata, mgmt, &elems);
+}