blob: 1213a23ff0fa7e28cfb547bbbcb29855891974f2 [file] [log] [blame]
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +01001/*
Rui Paulo264d9b72009-11-09 23:46:58 +00002 * Copyright (c) 2008, 2009 open80211s Ltd.
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +01003 * Author: Luis Carlos Cobo <luisca@cozybit.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09009#include <linux/gfp.h>
Johannes Berg902acc72008-02-23 15:17:19 +010010#include <linux/kernel.h>
11#include <linux/random.h>
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010012#include "ieee80211_i.h"
Johannes Berg2c8dccc2008-04-08 15:14:40 -040013#include "rate.h"
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010014#include "mesh.h"
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010015
16#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
17#define mpl_dbg(fmt, args...) printk(KERN_DEBUG fmt, ##args)
18#else
19#define mpl_dbg(fmt, args...) do { (void)(0); } while (0)
20#endif
21
Thomas Pedersen8db09852011-08-12 20:01:00 -070022#define PLINK_GET_LLID(p) (p + 2)
23#define PLINK_GET_PLID(p) (p + 4)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010024
25#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
26 jiffies + HZ * t / 1000))
27
Johannes Berg472dbc42008-09-11 00:01:49 +020028#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries)
29#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout)
30#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
31#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
32#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010033
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010034enum plink_event {
35 PLINK_UNDEFINED,
36 OPN_ACPT,
37 OPN_RJCT,
38 OPN_IGNR,
39 CNF_ACPT,
40 CNF_RJCT,
41 CNF_IGNR,
42 CLS_ACPT,
43 CLS_IGNR
44};
45
Thomas Pedersenba4a14e2011-09-20 13:43:32 -070046static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
47 enum ieee80211_self_protected_actioncode action,
48 u8 *da, __le16 llid, __le16 plid, __le16 reason);
49
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010050static inline
51void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
52{
Johannes Berg472dbc42008-09-11 00:01:49 +020053 atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
Johannes Bergd0709a62008-02-25 16:27:46 +010054 mesh_accept_plinks_update(sdata);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010055}
56
57static inline
58void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
59{
Johannes Berg472dbc42008-09-11 00:01:49 +020060 atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
Johannes Bergd0709a62008-02-25 16:27:46 +010061 mesh_accept_plinks_update(sdata);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010062}
63
64/**
65 * mesh_plink_fsm_restart - restart a mesh peer link finite state machine
66 *
Rui Paulo23c7a292009-11-09 23:46:42 +000067 * @sta: mesh peer link to restart
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010068 *
Johannes Berg07346f812008-05-03 01:02:02 +020069 * Locking: this function must be called holding sta->lock
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010070 */
71static inline void mesh_plink_fsm_restart(struct sta_info *sta)
72{
Javier Cardona57cf8042011-05-13 10:45:43 -070073 sta->plink_state = NL80211_PLINK_LISTEN;
Luis Carlos Cobo37659ff2008-02-29 12:13:38 -080074 sta->llid = sta->plid = sta->reason = 0;
75 sta->plink_retries = 0;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010076}
77
Johannes Berg93e5deb2008-04-01 15:21:00 +020078/*
79 * NOTE: This is just an alias for sta_info_alloc(), see notes
80 * on it in the lifecycle management section!
81 */
Johannes Berg03e44972008-02-27 09:56:40 +010082static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
Johannes Berg881d9482009-01-21 15:13:48 +010083 u8 *hw_addr, u32 rates)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010084{
Johannes Bergd0709a62008-02-25 16:27:46 +010085 struct ieee80211_local *local = sdata->local;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010086 struct sta_info *sta;
87
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010088 if (local->num_sta >= MESH_MAX_PLINKS)
Johannes Berg73651ee2008-02-25 16:27:47 +010089 return NULL;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010090
Johannes Berg34e89502010-02-03 13:59:58 +010091 sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
Johannes Berg73651ee2008-02-25 16:27:47 +010092 if (!sta)
93 return NULL;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010094
Javier Cardona5fbdf4a2011-09-07 17:49:54 -070095 sta->flags = WLAN_STA_AUTHORIZED | WLAN_STA_AUTH | WLAN_STA_WME;
Johannes Berg323ce792008-09-11 02:45:11 +020096 sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
Christian Lamparterb973c312008-12-27 22:19:49 +010097 rate_control_rate_init(sta);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +010098
99 return sta;
100}
101
102/**
John W. Linvillec9370192010-06-21 17:14:07 -0400103 * __mesh_plink_deactivate - deactivate mesh peer link
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100104 *
105 * @sta: mesh peer link to deactivate
106 *
107 * All mesh paths with this peer as next hop will be flushed
108 *
Johannes Berg07346f812008-05-03 01:02:02 +0200109 * Locking: the caller must hold sta->lock
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100110 */
John W. Linvillec9370192010-06-21 17:14:07 -0400111static bool __mesh_plink_deactivate(struct sta_info *sta)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100112{
Johannes Bergd0709a62008-02-25 16:27:46 +0100113 struct ieee80211_sub_if_data *sdata = sta->sdata;
John W. Linvillec9370192010-06-21 17:14:07 -0400114 bool deactivated = false;
Johannes Bergd0709a62008-02-25 16:27:46 +0100115
Javier Cardona57cf8042011-05-13 10:45:43 -0700116 if (sta->plink_state == NL80211_PLINK_ESTAB) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100117 mesh_plink_dec_estab_count(sdata);
John W. Linvillec9370192010-06-21 17:14:07 -0400118 deactivated = true;
119 }
Javier Cardona57cf8042011-05-13 10:45:43 -0700120 sta->plink_state = NL80211_PLINK_BLOCKED;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100121 mesh_path_flush_by_nexthop(sta);
John W. Linvillec9370192010-06-21 17:14:07 -0400122
123 return deactivated;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100124}
125
Johannes Berg902acc72008-02-23 15:17:19 +0100126/**
John W. Linvillec9370192010-06-21 17:14:07 -0400127 * mesh_plink_deactivate - deactivate mesh peer link
Johannes Berg902acc72008-02-23 15:17:19 +0100128 *
129 * @sta: mesh peer link to deactivate
130 *
131 * All mesh paths with this peer as next hop will be flushed
132 */
133void mesh_plink_deactivate(struct sta_info *sta)
134{
John W. Linvillec9370192010-06-21 17:14:07 -0400135 struct ieee80211_sub_if_data *sdata = sta->sdata;
136 bool deactivated;
137
Johannes Berg07346f812008-05-03 01:02:02 +0200138 spin_lock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400139 deactivated = __mesh_plink_deactivate(sta);
Thomas Pedersenba4a14e2011-09-20 13:43:32 -0700140 sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED);
141 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
142 sta->sta.addr, sta->llid, sta->plid,
143 sta->reason);
Johannes Berg07346f812008-05-03 01:02:02 +0200144 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400145
146 if (deactivated)
147 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Johannes Berg902acc72008-02-23 15:17:19 +0100148}
149
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200150static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700151 enum ieee80211_self_protected_actioncode action,
152 u8 *da, __le16 llid, __le16 plid, __le16 reason) {
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200153 struct ieee80211_local *local = sdata->local;
Javier Cardonac80d5452010-12-16 17:37:49 -0800154 struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
Javier Cardona581a8b02011-04-07 15:08:27 -0700155 sdata->u.mesh.ie_len);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100156 struct ieee80211_mgmt *mgmt;
157 bool include_plid = false;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700158 int ie_len = 4;
159 u16 peering_proto = 0;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100160 u8 *pos;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100161
162 if (!skb)
163 return -1;
164 skb_reserve(skb, local->hw.extra_tx_headroom);
165 /* 25 is the size of the common mgmt part (24) plus the size of the
166 * common action part (1)
167 */
168 mgmt = (struct ieee80211_mgmt *)
Thomas Pedersen8db09852011-08-12 20:01:00 -0700169 skb_put(skb, 25 + sizeof(mgmt->u.action.u.self_prot));
170 memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.self_prot));
Harvey Harrisone7827a72008-07-15 18:44:13 -0700171 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
172 IEEE80211_STYPE_ACTION);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100173 memcpy(mgmt->da, da, ETH_ALEN);
Johannes Berg47846c92009-11-25 17:46:19 +0100174 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
Javier Cardona915b5c52011-05-03 16:57:10 -0700175 memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700176 mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED;
177 mgmt->u.action.u.self_prot.action_code = action;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100178
Thomas Pedersen8db09852011-08-12 20:01:00 -0700179 if (action != WLAN_SP_MESH_PEERING_CLOSE) {
180 /* capability info */
181 pos = skb_put(skb, 2);
182 memset(pos, 0, 2);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700183 if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
Thomas Pedersen8db09852011-08-12 20:01:00 -0700184 /* AID */
185 pos = skb_put(skb, 2);
Rui Paulo77fa76b2009-11-09 23:46:52 +0000186 memcpy(pos + 2, &plid, 2);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100187 }
Thomas Pedersen082ebb02011-08-11 19:35:10 -0700188 if (mesh_add_srates_ie(skb, sdata) ||
189 mesh_add_ext_srates_ie(skb, sdata) ||
190 mesh_add_rsn_ie(skb, sdata) ||
191 mesh_add_meshid_ie(skb, sdata) ||
192 mesh_add_meshconf_ie(skb, sdata))
193 return -1;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700194 } else { /* WLAN_SP_MESH_PEERING_CLOSE */
195 if (mesh_add_meshid_ie(skb, sdata))
196 return -1;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100197 }
198
Thomas Pedersen8db09852011-08-12 20:01:00 -0700199 /* Add Mesh Peering Management element */
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100200 switch (action) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700201 case WLAN_SP_MESH_PEERING_OPEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100202 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700203 case WLAN_SP_MESH_PEERING_CONFIRM:
Thomas Pedersen8db09852011-08-12 20:01:00 -0700204 ie_len += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100205 include_plid = true;
206 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700207 case WLAN_SP_MESH_PEERING_CLOSE:
Thomas Pedersen8db09852011-08-12 20:01:00 -0700208 if (plid) {
209 ie_len += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100210 include_plid = true;
211 }
Thomas Pedersen8db09852011-08-12 20:01:00 -0700212 ie_len += 2; /* reason code */
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100213 break;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700214 default:
215 return -EINVAL;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100216 }
217
Thomas Pedersen8db09852011-08-12 20:01:00 -0700218 if (WARN_ON(skb_tailroom(skb) < 2 + ie_len))
219 return -ENOMEM;
220
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100221 pos = skb_put(skb, 2 + ie_len);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700222 *pos++ = WLAN_EID_PEER_MGMT;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100223 *pos++ = ie_len;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700224 memcpy(pos, &peering_proto, 2);
225 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100226 memcpy(pos, &llid, 2);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700227 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100228 if (include_plid) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100229 memcpy(pos, &plid, 2);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700230 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100231 }
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700232 if (action == WLAN_SP_MESH_PEERING_CLOSE) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100233 memcpy(pos, &reason, 2);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700234 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100235 }
Thomas Pedersen8db09852011-08-12 20:01:00 -0700236 if (mesh_add_vendor_ies(skb, sdata))
237 return -1;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100238
Johannes Berg62ae67b2009-11-18 18:42:05 +0100239 ieee80211_tx_skb(sdata, skb);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100240 return 0;
241}
242
Javier Cardona1570ca52011-04-07 15:08:35 -0700243void mesh_neighbour_update(u8 *hw_addr, u32 rates,
244 struct ieee80211_sub_if_data *sdata,
245 struct ieee802_11_elems *elems)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100246{
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200247 struct ieee80211_local *local = sdata->local;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100248 struct sta_info *sta;
249
Johannes Bergd0709a62008-02-25 16:27:46 +0100250 rcu_read_lock();
251
Johannes Bergabe60632009-11-25 17:46:18 +0100252 sta = sta_info_get(sdata, hw_addr);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100253 if (!sta) {
Johannes Berg34e89502010-02-03 13:59:58 +0100254 rcu_read_unlock();
Javier Cardona1570ca52011-04-07 15:08:35 -0700255 /* Userspace handles peer allocation when security is enabled
256 * */
Javier Cardonab130e5c2011-05-03 16:57:07 -0700257 if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
Javier Cardona1570ca52011-04-07 15:08:35 -0700258 cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
259 elems->ie_start, elems->total_len,
260 GFP_KERNEL);
261 else
262 sta = mesh_plink_alloc(sdata, hw_addr, rates);
Johannes Berg34e89502010-02-03 13:59:58 +0100263 if (!sta)
Johannes Berg73651ee2008-02-25 16:27:47 +0100264 return;
Johannes Berg34e89502010-02-03 13:59:58 +0100265 if (sta_info_insert_rcu(sta)) {
Johannes Bergd0709a62008-02-25 16:27:46 +0100266 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100267 return;
Johannes Bergd0709a62008-02-25 16:27:46 +0100268 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100269 }
270
271 sta->last_rx = jiffies;
Johannes Berg323ce792008-09-11 02:45:11 +0200272 sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
Javier Cardona1570ca52011-04-07 15:08:35 -0700273 if (mesh_peer_accepts_plinks(elems) &&
Javier Cardona57cf8042011-05-13 10:45:43 -0700274 sta->plink_state == NL80211_PLINK_LISTEN &&
Johannes Berg472dbc42008-09-11 00:01:49 +0200275 sdata->u.mesh.accepting_plinks &&
276 sdata->u.mesh.mshcfg.auto_open_plinks)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100277 mesh_plink_open(sta);
278
Johannes Bergd0709a62008-02-25 16:27:46 +0100279 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100280}
281
282static void mesh_plink_timer(unsigned long data)
283{
284 struct sta_info *sta;
285 __le16 llid, plid, reason;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100286 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100287
Johannes Bergd0709a62008-02-25 16:27:46 +0100288 /*
289 * This STA is valid because sta_info_destroy() will
290 * del_timer_sync() this timer after having made sure
291 * it cannot be readded (by deleting the plink.)
292 */
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100293 sta = (struct sta_info *) data;
294
Johannes Berg5bb644a2009-05-17 11:40:42 +0200295 if (sta->sdata->local->quiescing) {
296 sta->plink_timer_was_running = true;
297 return;
298 }
299
Johannes Berg07346f812008-05-03 01:02:02 +0200300 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100301 if (sta->ignore_plink_timer) {
302 sta->ignore_plink_timer = false;
Johannes Berg07346f812008-05-03 01:02:02 +0200303 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100304 return;
305 }
Johannes Berg0c68ae262008-10-27 15:56:10 -0700306 mpl_dbg("Mesh plink timer for %pM fired on state %d\n",
307 sta->sta.addr, sta->plink_state);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100308 reason = 0;
309 llid = sta->llid;
310 plid = sta->plid;
Johannes Bergd0709a62008-02-25 16:27:46 +0100311 sdata = sta->sdata;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100312
313 switch (sta->plink_state) {
Javier Cardona57cf8042011-05-13 10:45:43 -0700314 case NL80211_PLINK_OPN_RCVD:
315 case NL80211_PLINK_OPN_SNT:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100316 /* retry timer */
317 if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
318 u32 rand;
Johannes Berg0c68ae262008-10-27 15:56:10 -0700319 mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n",
320 sta->sta.addr, sta->plink_retries,
321 sta->plink_timeout);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100322 get_random_bytes(&rand, sizeof(u32));
323 sta->plink_timeout = sta->plink_timeout +
324 rand % sta->plink_timeout;
325 ++sta->plink_retries;
Johannes Bergd0709a62008-02-25 16:27:46 +0100326 mod_plink_timer(sta, sta->plink_timeout);
Johannes Berg07346f812008-05-03 01:02:02 +0200327 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700328 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
329 sta->sta.addr, llid, 0, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100330 break;
331 }
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700332 reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100333 /* fall through on else */
Javier Cardona57cf8042011-05-13 10:45:43 -0700334 case NL80211_PLINK_CNF_RCVD:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100335 /* confirm timer */
336 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700337 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
Javier Cardona57cf8042011-05-13 10:45:43 -0700338 sta->plink_state = NL80211_PLINK_HOLDING;
Johannes Bergd0709a62008-02-25 16:27:46 +0100339 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200340 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700341 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
342 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100343 break;
Javier Cardona57cf8042011-05-13 10:45:43 -0700344 case NL80211_PLINK_HOLDING:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100345 /* holding timer */
Johannes Bergd0709a62008-02-25 16:27:46 +0100346 del_timer(&sta->plink_timer);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100347 mesh_plink_fsm_restart(sta);
Johannes Berg07346f812008-05-03 01:02:02 +0200348 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100349 break;
350 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200351 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100352 break;
353 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100354}
355
Johannes Berg5bb644a2009-05-17 11:40:42 +0200356#ifdef CONFIG_PM
357void mesh_plink_quiesce(struct sta_info *sta)
358{
359 if (del_timer_sync(&sta->plink_timer))
360 sta->plink_timer_was_running = true;
361}
362
363void mesh_plink_restart(struct sta_info *sta)
364{
365 if (sta->plink_timer_was_running) {
366 add_timer(&sta->plink_timer);
367 sta->plink_timer_was_running = false;
368 }
369}
370#endif
371
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100372static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
373{
374 sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
375 sta->plink_timer.data = (unsigned long) sta;
376 sta->plink_timer.function = mesh_plink_timer;
377 sta->plink_timeout = timeout;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100378 add_timer(&sta->plink_timer);
379}
380
381int mesh_plink_open(struct sta_info *sta)
382{
383 __le16 llid;
Johannes Bergd0709a62008-02-25 16:27:46 +0100384 struct ieee80211_sub_if_data *sdata = sta->sdata;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100385
Javier Cardona53e80512011-04-07 15:08:32 -0700386 if (!test_sta_flags(sta, WLAN_STA_AUTH))
387 return -EPERM;
388
Johannes Berg07346f812008-05-03 01:02:02 +0200389 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100390 get_random_bytes(&llid, 2);
391 sta->llid = llid;
Javier Cardona57cf8042011-05-13 10:45:43 -0700392 if (sta->plink_state != NL80211_PLINK_LISTEN) {
Johannes Berg07346f812008-05-03 01:02:02 +0200393 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100394 return -EBUSY;
395 }
Javier Cardona57cf8042011-05-13 10:45:43 -0700396 sta->plink_state = NL80211_PLINK_OPN_SNT;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100397 mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200398 spin_unlock_bh(&sta->lock);
Johannes Berg0c68ae262008-10-27 15:56:10 -0700399 mpl_dbg("Mesh plink: starting establishment with %pM\n",
400 sta->sta.addr);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100401
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700402 return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
Johannes Berg17741cd2008-09-11 00:02:02 +0200403 sta->sta.addr, llid, 0, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100404}
405
406void mesh_plink_block(struct sta_info *sta)
407{
John W. Linvillec9370192010-06-21 17:14:07 -0400408 struct ieee80211_sub_if_data *sdata = sta->sdata;
409 bool deactivated;
410
Johannes Berg07346f812008-05-03 01:02:02 +0200411 spin_lock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400412 deactivated = __mesh_plink_deactivate(sta);
Javier Cardona57cf8042011-05-13 10:45:43 -0700413 sta->plink_state = NL80211_PLINK_BLOCKED;
Johannes Berg07346f812008-05-03 01:02:02 +0200414 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400415
416 if (deactivated)
417 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100418}
419
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100420
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200421void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100422 size_t len, struct ieee80211_rx_status *rx_status)
423{
Johannes Bergd0709a62008-02-25 16:27:46 +0100424 struct ieee80211_local *local = sdata->local;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100425 struct ieee802_11_elems elems;
426 struct sta_info *sta;
427 enum plink_event event;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700428 enum ieee80211_self_protected_actioncode ftype;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100429 size_t baselen;
Christian Lamparterd12c7452010-10-08 22:27:07 +0200430 bool deactivated, matches_local = true;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100431 u8 ie_len;
432 u8 *baseaddr;
433 __le16 plid, llid, reason;
Rui Paulo1460dd12009-11-09 23:46:48 +0000434#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
435 static const char *mplstates[] = {
Javier Cardona57cf8042011-05-13 10:45:43 -0700436 [NL80211_PLINK_LISTEN] = "LISTEN",
437 [NL80211_PLINK_OPN_SNT] = "OPN-SNT",
438 [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD",
439 [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD",
440 [NL80211_PLINK_ESTAB] = "ESTAB",
441 [NL80211_PLINK_HOLDING] = "HOLDING",
442 [NL80211_PLINK_BLOCKED] = "BLOCKED"
Rui Paulo1460dd12009-11-09 23:46:48 +0000443 };
444#endif
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100445
Johannes Berg9c80d3d2008-09-08 15:41:59 +0200446 /* need action_code, aux */
447 if (len < IEEE80211_MIN_ACTION_SIZE + 3)
448 return;
449
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100450 if (is_multicast_ether_addr(mgmt->da)) {
451 mpl_dbg("Mesh plink: ignore frame from multicast address");
452 return;
453 }
454
Thomas Pedersen8db09852011-08-12 20:01:00 -0700455 baseaddr = mgmt->u.action.u.self_prot.variable;
456 baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt;
457 if (mgmt->u.action.u.self_prot.action_code ==
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700458 WLAN_SP_MESH_PEERING_CONFIRM) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100459 baseaddr += 4;
David Woo70bdb6b2009-08-12 11:03:44 -0700460 baselen += 4;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100461 }
462 ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700463 if (!elems.peering) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100464 mpl_dbg("Mesh plink: missing necessary peer link ie\n");
465 return;
466 }
Javier Cardonab130e5c2011-05-03 16:57:07 -0700467 if (elems.rsn_len &&
468 sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
Javier Cardona5cff5e02011-04-07 15:08:29 -0700469 mpl_dbg("Mesh plink: can't establish link with secure peer\n");
470 return;
471 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100472
Thomas Pedersen8db09852011-08-12 20:01:00 -0700473 ftype = mgmt->u.action.u.self_prot.action_code;
474 ie_len = elems.peering_len;
475 if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
476 (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
477 (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
478 && ie_len != 8)) {
Rui Paulo09383932009-11-09 23:46:43 +0000479 mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n",
480 ftype, ie_len);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100481 return;
482 }
483
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700484 if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
485 (!elems.mesh_id || !elems.mesh_config)) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100486 mpl_dbg("Mesh plink: missing necessary ie\n");
487 return;
488 }
489 /* Note the lines below are correct, the llid in the frame is the plid
490 * from the point of view of this host.
491 */
Thomas Pedersen8db09852011-08-12 20:01:00 -0700492 memcpy(&plid, PLINK_GET_LLID(elems.peering), 2);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700493 if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
Thomas Pedersen8db09852011-08-12 20:01:00 -0700494 (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
495 memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100496
Johannes Bergd0709a62008-02-25 16:27:46 +0100497 rcu_read_lock();
498
Johannes Bergabe60632009-11-25 17:46:18 +0100499 sta = sta_info_get(sdata, mgmt->sa);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700500 if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100501 mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
Johannes Bergd0709a62008-02-25 16:27:46 +0100502 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100503 return;
504 }
505
Javier Cardona53e80512011-04-07 15:08:32 -0700506 if (sta && !test_sta_flags(sta, WLAN_STA_AUTH)) {
507 mpl_dbg("Mesh plink: Action frame from non-authed peer\n");
508 rcu_read_unlock();
509 return;
510 }
511
Javier Cardona57cf8042011-05-13 10:45:43 -0700512 if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) {
Johannes Bergd0709a62008-02-25 16:27:46 +0100513 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100514 return;
515 }
516
517 /* Now we will figure out the appropriate event... */
518 event = PLINK_UNDEFINED;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700519 if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
520 (!mesh_matches_local(&elems, sdata))) {
Christian Lamparterd12c7452010-10-08 22:27:07 +0200521 matches_local = false;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100522 switch (ftype) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700523 case WLAN_SP_MESH_PEERING_OPEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100524 event = OPN_RJCT;
525 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700526 case WLAN_SP_MESH_PEERING_CONFIRM:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100527 event = CNF_RJCT;
528 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700529 default:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100530 break;
531 }
Christian Lamparterd12c7452010-10-08 22:27:07 +0200532 }
533
534 if (!sta && !matches_local) {
535 rcu_read_unlock();
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700536 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Christian Lamparterd12c7452010-10-08 22:27:07 +0200537 llid = 0;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700538 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
539 mgmt->sa, llid, plid, reason);
Christian Lamparterd12c7452010-10-08 22:27:07 +0200540 return;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100541 } else if (!sta) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700542 /* ftype == WLAN_SP_MESH_PEERING_OPEN */
Johannes Berg881d9482009-01-21 15:13:48 +0100543 u32 rates;
Johannes Berg34e89502010-02-03 13:59:58 +0100544
545 rcu_read_unlock();
546
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100547 if (!mesh_plink_free_count(sdata)) {
548 mpl_dbg("Mesh plink error: no more free plinks\n");
549 return;
550 }
551
552 rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
Johannes Berg03e44972008-02-27 09:56:40 +0100553 sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
Johannes Berg73651ee2008-02-25 16:27:47 +0100554 if (!sta) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100555 mpl_dbg("Mesh plink error: plink table full\n");
556 return;
557 }
Johannes Berg34e89502010-02-03 13:59:58 +0100558 if (sta_info_insert_rcu(sta)) {
Johannes Berg73651ee2008-02-25 16:27:47 +0100559 rcu_read_unlock();
560 return;
561 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100562 event = OPN_ACPT;
Johannes Berg07346f812008-05-03 01:02:02 +0200563 spin_lock_bh(&sta->lock);
Christian Lamparterd12c7452010-10-08 22:27:07 +0200564 } else if (matches_local) {
Johannes Berg07346f812008-05-03 01:02:02 +0200565 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100566 switch (ftype) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700567 case WLAN_SP_MESH_PEERING_OPEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100568 if (!mesh_plink_free_count(sdata) ||
Johannes Bergd0709a62008-02-25 16:27:46 +0100569 (sta->plid && sta->plid != plid))
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100570 event = OPN_IGNR;
571 else
572 event = OPN_ACPT;
573 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700574 case WLAN_SP_MESH_PEERING_CONFIRM:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100575 if (!mesh_plink_free_count(sdata) ||
Johannes Bergd0709a62008-02-25 16:27:46 +0100576 (sta->llid != llid || sta->plid != plid))
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100577 event = CNF_IGNR;
578 else
579 event = CNF_ACPT;
580 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700581 case WLAN_SP_MESH_PEERING_CLOSE:
Javier Cardona57cf8042011-05-13 10:45:43 -0700582 if (sta->plink_state == NL80211_PLINK_ESTAB)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100583 /* Do not check for llid or plid. This does not
584 * follow the standard but since multiple plinks
585 * per sta are not supported, it is necessary in
586 * order to avoid a livelock when MP A sees an
587 * establish peer link to MP B but MP B does not
588 * see it. This can be caused by a timeout in
589 * B's peer link establishment or B beign
590 * restarted.
591 */
592 event = CLS_ACPT;
593 else if (sta->plid != plid)
594 event = CLS_IGNR;
595 else if (ie_len == 7 && sta->llid != llid)
596 event = CLS_IGNR;
597 else
598 event = CLS_ACPT;
599 break;
600 default:
601 mpl_dbg("Mesh plink: unknown frame subtype\n");
Johannes Berg07346f812008-05-03 01:02:02 +0200602 spin_unlock_bh(&sta->lock);
Johannes Bergd0709a62008-02-25 16:27:46 +0100603 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100604 return;
605 }
Christian Lamparterd12c7452010-10-08 22:27:07 +0200606 } else {
607 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100608 }
609
Rui Paulo1460dd12009-11-09 23:46:48 +0000610 mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n",
611 mgmt->sa, mplstates[sta->plink_state],
Johannes Berg0c68ae262008-10-27 15:56:10 -0700612 le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
613 event);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100614 reason = 0;
615 switch (sta->plink_state) {
616 /* spin_unlock as soon as state is updated at each case */
Javier Cardona57cf8042011-05-13 10:45:43 -0700617 case NL80211_PLINK_LISTEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100618 switch (event) {
619 case CLS_ACPT:
620 mesh_plink_fsm_restart(sta);
Johannes Berg07346f812008-05-03 01:02:02 +0200621 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100622 break;
623 case OPN_ACPT:
Javier Cardona57cf8042011-05-13 10:45:43 -0700624 sta->plink_state = NL80211_PLINK_OPN_RCVD;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100625 sta->plid = plid;
626 get_random_bytes(&llid, 2);
627 sta->llid = llid;
628 mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200629 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700630 mesh_plink_frame_tx(sdata,
631 WLAN_SP_MESH_PEERING_OPEN,
632 sta->sta.addr, llid, 0, 0);
633 mesh_plink_frame_tx(sdata,
634 WLAN_SP_MESH_PEERING_CONFIRM,
635 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100636 break;
637 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200638 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100639 break;
640 }
641 break;
642
Javier Cardona57cf8042011-05-13 10:45:43 -0700643 case NL80211_PLINK_OPN_SNT:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100644 switch (event) {
645 case OPN_RJCT:
646 case CNF_RJCT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700647 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100648 case CLS_ACPT:
649 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700650 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100651 sta->reason = reason;
Javier Cardona57cf8042011-05-13 10:45:43 -0700652 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100653 if (!mod_plink_timer(sta,
654 dot11MeshHoldingTimeout(sdata)))
655 sta->ignore_plink_timer = true;
656
657 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200658 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700659 mesh_plink_frame_tx(sdata,
660 WLAN_SP_MESH_PEERING_CLOSE,
661 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100662 break;
663 case OPN_ACPT:
664 /* retry timer is left untouched */
Javier Cardona57cf8042011-05-13 10:45:43 -0700665 sta->plink_state = NL80211_PLINK_OPN_RCVD;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100666 sta->plid = plid;
667 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200668 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700669 mesh_plink_frame_tx(sdata,
670 WLAN_SP_MESH_PEERING_CONFIRM,
671 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100672 break;
673 case CNF_ACPT:
Javier Cardona57cf8042011-05-13 10:45:43 -0700674 sta->plink_state = NL80211_PLINK_CNF_RCVD;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100675 if (!mod_plink_timer(sta,
676 dot11MeshConfirmTimeout(sdata)))
677 sta->ignore_plink_timer = true;
678
Johannes Berg07346f812008-05-03 01:02:02 +0200679 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100680 break;
681 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200682 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100683 break;
684 }
685 break;
686
Javier Cardona57cf8042011-05-13 10:45:43 -0700687 case NL80211_PLINK_OPN_RCVD:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100688 switch (event) {
689 case OPN_RJCT:
690 case CNF_RJCT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700691 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100692 case CLS_ACPT:
693 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700694 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100695 sta->reason = reason;
Javier Cardona57cf8042011-05-13 10:45:43 -0700696 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100697 if (!mod_plink_timer(sta,
698 dot11MeshHoldingTimeout(sdata)))
699 sta->ignore_plink_timer = true;
700
701 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200702 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700703 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
704 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100705 break;
706 case OPN_ACPT:
707 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200708 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700709 mesh_plink_frame_tx(sdata,
710 WLAN_SP_MESH_PEERING_CONFIRM,
711 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100712 break;
713 case CNF_ACPT:
Johannes Bergd0709a62008-02-25 16:27:46 +0100714 del_timer(&sta->plink_timer);
Javier Cardona57cf8042011-05-13 10:45:43 -0700715 sta->plink_state = NL80211_PLINK_ESTAB;
Johannes Berg07346f812008-05-03 01:02:02 +0200716 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400717 mesh_plink_inc_estab_count(sdata);
718 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Johannes Berg0c68ae262008-10-27 15:56:10 -0700719 mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
720 sta->sta.addr);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100721 break;
722 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200723 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100724 break;
725 }
726 break;
727
Javier Cardona57cf8042011-05-13 10:45:43 -0700728 case NL80211_PLINK_CNF_RCVD:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100729 switch (event) {
730 case OPN_RJCT:
731 case CNF_RJCT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700732 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100733 case CLS_ACPT:
734 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700735 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100736 sta->reason = reason;
Javier Cardona57cf8042011-05-13 10:45:43 -0700737 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100738 if (!mod_plink_timer(sta,
739 dot11MeshHoldingTimeout(sdata)))
740 sta->ignore_plink_timer = true;
741
742 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200743 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700744 mesh_plink_frame_tx(sdata,
745 WLAN_SP_MESH_PEERING_CLOSE,
746 sta->sta.addr, llid, plid, reason);
Johannes Bergff59dc72008-02-25 10:11:50 +0100747 break;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100748 case OPN_ACPT:
Johannes Bergd0709a62008-02-25 16:27:46 +0100749 del_timer(&sta->plink_timer);
Javier Cardona57cf8042011-05-13 10:45:43 -0700750 sta->plink_state = NL80211_PLINK_ESTAB;
Johannes Berg07346f812008-05-03 01:02:02 +0200751 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400752 mesh_plink_inc_estab_count(sdata);
753 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Johannes Berg0c68ae262008-10-27 15:56:10 -0700754 mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
755 sta->sta.addr);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700756 mesh_plink_frame_tx(sdata,
757 WLAN_SP_MESH_PEERING_CONFIRM,
758 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100759 break;
760 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200761 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100762 break;
763 }
764 break;
765
Javier Cardona57cf8042011-05-13 10:45:43 -0700766 case NL80211_PLINK_ESTAB:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100767 switch (event) {
768 case CLS_ACPT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700769 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100770 sta->reason = reason;
John W. Linvillec9370192010-06-21 17:14:07 -0400771 deactivated = __mesh_plink_deactivate(sta);
Javier Cardona57cf8042011-05-13 10:45:43 -0700772 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100773 llid = sta->llid;
Johannes Bergd0709a62008-02-25 16:27:46 +0100774 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200775 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400776 if (deactivated)
777 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700778 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
779 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100780 break;
781 case OPN_ACPT:
782 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200783 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700784 mesh_plink_frame_tx(sdata,
785 WLAN_SP_MESH_PEERING_CONFIRM,
786 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100787 break;
788 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200789 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100790 break;
791 }
792 break;
Javier Cardona57cf8042011-05-13 10:45:43 -0700793 case NL80211_PLINK_HOLDING:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100794 switch (event) {
795 case CLS_ACPT:
Johannes Bergd0709a62008-02-25 16:27:46 +0100796 if (del_timer(&sta->plink_timer))
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100797 sta->ignore_plink_timer = 1;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100798 mesh_plink_fsm_restart(sta);
Johannes Berg07346f812008-05-03 01:02:02 +0200799 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100800 break;
801 case OPN_ACPT:
802 case CNF_ACPT:
803 case OPN_RJCT:
804 case CNF_RJCT:
805 llid = sta->llid;
806 reason = sta->reason;
Johannes Berg07346f812008-05-03 01:02:02 +0200807 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700808 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
809 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100810 break;
811 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200812 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100813 }
814 break;
815 default:
Luis Carlos Cobob4e08ea2008-02-29 15:46:08 -0800816 /* should not get here, PLINK_BLOCKED is dealt with at the
Daniel Mack3ad2f3f2010-02-03 08:01:28 +0800817 * beginning of the function
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100818 */
Johannes Berg07346f812008-05-03 01:02:02 +0200819 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100820 break;
821 }
Johannes Bergd0709a62008-02-25 16:27:46 +0100822
823 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100824}