blob: 351e48c9710ca4b2928b12c68ffa376dfd9129b8 [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
Johannes Bergc2c98fd2011-09-29 16:04:36 +020095 set_sta_flag(sta, WLAN_STA_AUTH);
96 set_sta_flag(sta, WLAN_STA_AUTHORIZED);
97 set_sta_flag(sta, WLAN_STA_WME);
Johannes Berg323ce792008-09-11 02:45:11 +020098 sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
Christian Lamparterb973c312008-12-27 22:19:49 +010099 rate_control_rate_init(sta);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100100
101 return sta;
102}
103
104/**
John W. Linvillec9370192010-06-21 17:14:07 -0400105 * __mesh_plink_deactivate - deactivate mesh peer link
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100106 *
107 * @sta: mesh peer link to deactivate
108 *
109 * All mesh paths with this peer as next hop will be flushed
110 *
Johannes Berg07346f812008-05-03 01:02:02 +0200111 * Locking: the caller must hold sta->lock
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100112 */
John W. Linvillec9370192010-06-21 17:14:07 -0400113static bool __mesh_plink_deactivate(struct sta_info *sta)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100114{
Johannes Bergd0709a62008-02-25 16:27:46 +0100115 struct ieee80211_sub_if_data *sdata = sta->sdata;
John W. Linvillec9370192010-06-21 17:14:07 -0400116 bool deactivated = false;
Johannes Bergd0709a62008-02-25 16:27:46 +0100117
Javier Cardona57cf8042011-05-13 10:45:43 -0700118 if (sta->plink_state == NL80211_PLINK_ESTAB) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100119 mesh_plink_dec_estab_count(sdata);
John W. Linvillec9370192010-06-21 17:14:07 -0400120 deactivated = true;
121 }
Javier Cardona57cf8042011-05-13 10:45:43 -0700122 sta->plink_state = NL80211_PLINK_BLOCKED;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100123 mesh_path_flush_by_nexthop(sta);
John W. Linvillec9370192010-06-21 17:14:07 -0400124
125 return deactivated;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100126}
127
Johannes Berg902acc72008-02-23 15:17:19 +0100128/**
John W. Linvillec9370192010-06-21 17:14:07 -0400129 * mesh_plink_deactivate - deactivate mesh peer link
Johannes Berg902acc72008-02-23 15:17:19 +0100130 *
131 * @sta: mesh peer link to deactivate
132 *
133 * All mesh paths with this peer as next hop will be flushed
134 */
135void mesh_plink_deactivate(struct sta_info *sta)
136{
John W. Linvillec9370192010-06-21 17:14:07 -0400137 struct ieee80211_sub_if_data *sdata = sta->sdata;
138 bool deactivated;
139
Johannes Berg07346f812008-05-03 01:02:02 +0200140 spin_lock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400141 deactivated = __mesh_plink_deactivate(sta);
Thomas Pedersenba4a14e2011-09-20 13:43:32 -0700142 sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED);
143 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
144 sta->sta.addr, sta->llid, sta->plid,
145 sta->reason);
Johannes Berg07346f812008-05-03 01:02:02 +0200146 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400147
148 if (deactivated)
149 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Johannes Berg902acc72008-02-23 15:17:19 +0100150}
151
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200152static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700153 enum ieee80211_self_protected_actioncode action,
154 u8 *da, __le16 llid, __le16 plid, __le16 reason) {
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200155 struct ieee80211_local *local = sdata->local;
Thomas Pedersen3b69a9c2011-10-26 14:47:25 -0700156 struct sk_buff *skb;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100157 struct ieee80211_mgmt *mgmt;
158 bool include_plid = false;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700159 u16 peering_proto = 0;
Thomas Pedersen3b69a9c2011-10-26 14:47:25 -0700160 u8 *pos, ie_len = 4;
161 int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) +
162 sizeof(mgmt->u.action.u.self_prot);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100163
Thomas Pedersen3b69a9c2011-10-26 14:47:25 -0700164 skb = dev_alloc_skb(local->hw.extra_tx_headroom +
165 hdr_len +
166 2 + /* capability info */
167 2 + /* AID */
168 2 + 8 + /* supported rates */
169 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
170 2 + sdata->u.mesh.mesh_id_len +
171 2 + sizeof(struct ieee80211_meshconf_ie) +
172 2 + 8 + /* peering IE */
173 sdata->u.mesh.ie_len);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100174 if (!skb)
175 return -1;
176 skb_reserve(skb, local->hw.extra_tx_headroom);
Thomas Pedersen3b69a9c2011-10-26 14:47:25 -0700177 mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
178 memset(mgmt, 0, hdr_len);
Harvey Harrisone7827a72008-07-15 18:44:13 -0700179 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
180 IEEE80211_STYPE_ACTION);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100181 memcpy(mgmt->da, da, ETH_ALEN);
Johannes Berg47846c92009-11-25 17:46:19 +0100182 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
Javier Cardona915b5c52011-05-03 16:57:10 -0700183 memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700184 mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED;
185 mgmt->u.action.u.self_prot.action_code = action;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100186
Thomas Pedersen8db09852011-08-12 20:01:00 -0700187 if (action != WLAN_SP_MESH_PEERING_CLOSE) {
188 /* capability info */
189 pos = skb_put(skb, 2);
190 memset(pos, 0, 2);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700191 if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
Thomas Pedersen8db09852011-08-12 20:01:00 -0700192 /* AID */
193 pos = skb_put(skb, 2);
Rui Paulo77fa76b2009-11-09 23:46:52 +0000194 memcpy(pos + 2, &plid, 2);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100195 }
Arik Nemtsov768db342011-09-28 14:12:51 +0300196 if (ieee80211_add_srates_ie(&sdata->vif, skb) ||
197 ieee80211_add_ext_srates_ie(&sdata->vif, skb) ||
Thomas Pedersen082ebb02011-08-11 19:35:10 -0700198 mesh_add_rsn_ie(skb, sdata) ||
199 mesh_add_meshid_ie(skb, sdata) ||
200 mesh_add_meshconf_ie(skb, sdata))
201 return -1;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700202 } else { /* WLAN_SP_MESH_PEERING_CLOSE */
203 if (mesh_add_meshid_ie(skb, sdata))
204 return -1;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100205 }
206
Thomas Pedersen8db09852011-08-12 20:01:00 -0700207 /* Add Mesh Peering Management element */
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100208 switch (action) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700209 case WLAN_SP_MESH_PEERING_OPEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100210 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700211 case WLAN_SP_MESH_PEERING_CONFIRM:
Thomas Pedersen8db09852011-08-12 20:01:00 -0700212 ie_len += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100213 include_plid = true;
214 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700215 case WLAN_SP_MESH_PEERING_CLOSE:
Thomas Pedersen8db09852011-08-12 20:01:00 -0700216 if (plid) {
217 ie_len += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100218 include_plid = true;
219 }
Thomas Pedersen8db09852011-08-12 20:01:00 -0700220 ie_len += 2; /* reason code */
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100221 break;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700222 default:
223 return -EINVAL;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100224 }
225
Thomas Pedersen8db09852011-08-12 20:01:00 -0700226 if (WARN_ON(skb_tailroom(skb) < 2 + ie_len))
227 return -ENOMEM;
228
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100229 pos = skb_put(skb, 2 + ie_len);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700230 *pos++ = WLAN_EID_PEER_MGMT;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100231 *pos++ = ie_len;
Thomas Pedersen8db09852011-08-12 20:01:00 -0700232 memcpy(pos, &peering_proto, 2);
233 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100234 memcpy(pos, &llid, 2);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700235 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100236 if (include_plid) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100237 memcpy(pos, &plid, 2);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700238 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100239 }
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700240 if (action == WLAN_SP_MESH_PEERING_CLOSE) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100241 memcpy(pos, &reason, 2);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700242 pos += 2;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100243 }
Thomas Pedersen8db09852011-08-12 20:01:00 -0700244 if (mesh_add_vendor_ies(skb, sdata))
245 return -1;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100246
Johannes Berg62ae67b2009-11-18 18:42:05 +0100247 ieee80211_tx_skb(sdata, skb);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100248 return 0;
249}
250
Javier Cardona1570ca52011-04-07 15:08:35 -0700251void mesh_neighbour_update(u8 *hw_addr, u32 rates,
252 struct ieee80211_sub_if_data *sdata,
253 struct ieee802_11_elems *elems)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100254{
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200255 struct ieee80211_local *local = sdata->local;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100256 struct sta_info *sta;
257
Johannes Bergd0709a62008-02-25 16:27:46 +0100258 rcu_read_lock();
259
Johannes Bergabe60632009-11-25 17:46:18 +0100260 sta = sta_info_get(sdata, hw_addr);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100261 if (!sta) {
Johannes Berg34e89502010-02-03 13:59:58 +0100262 rcu_read_unlock();
Javier Cardona1570ca52011-04-07 15:08:35 -0700263 /* Userspace handles peer allocation when security is enabled
264 * */
Javier Cardonab130e5c2011-05-03 16:57:07 -0700265 if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED)
Javier Cardona1570ca52011-04-07 15:08:35 -0700266 cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
267 elems->ie_start, elems->total_len,
268 GFP_KERNEL);
269 else
270 sta = mesh_plink_alloc(sdata, hw_addr, rates);
Johannes Berg34e89502010-02-03 13:59:58 +0100271 if (!sta)
Johannes Berg73651ee2008-02-25 16:27:47 +0100272 return;
Johannes Berg34e89502010-02-03 13:59:58 +0100273 if (sta_info_insert_rcu(sta)) {
Johannes Bergd0709a62008-02-25 16:27:46 +0100274 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100275 return;
Johannes Bergd0709a62008-02-25 16:27:46 +0100276 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100277 }
278
279 sta->last_rx = jiffies;
Johannes Berg323ce792008-09-11 02:45:11 +0200280 sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
Javier Cardona1570ca52011-04-07 15:08:35 -0700281 if (mesh_peer_accepts_plinks(elems) &&
Javier Cardona57cf8042011-05-13 10:45:43 -0700282 sta->plink_state == NL80211_PLINK_LISTEN &&
Johannes Berg472dbc42008-09-11 00:01:49 +0200283 sdata->u.mesh.accepting_plinks &&
284 sdata->u.mesh.mshcfg.auto_open_plinks)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100285 mesh_plink_open(sta);
286
Johannes Bergd0709a62008-02-25 16:27:46 +0100287 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100288}
289
290static void mesh_plink_timer(unsigned long data)
291{
292 struct sta_info *sta;
293 __le16 llid, plid, reason;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100294 struct ieee80211_sub_if_data *sdata;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100295
Johannes Bergd0709a62008-02-25 16:27:46 +0100296 /*
297 * This STA is valid because sta_info_destroy() will
298 * del_timer_sync() this timer after having made sure
299 * it cannot be readded (by deleting the plink.)
300 */
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100301 sta = (struct sta_info *) data;
302
Johannes Berg5bb644a2009-05-17 11:40:42 +0200303 if (sta->sdata->local->quiescing) {
304 sta->plink_timer_was_running = true;
305 return;
306 }
307
Johannes Berg07346f812008-05-03 01:02:02 +0200308 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100309 if (sta->ignore_plink_timer) {
310 sta->ignore_plink_timer = false;
Johannes Berg07346f812008-05-03 01:02:02 +0200311 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100312 return;
313 }
Johannes Berg0c68ae262008-10-27 15:56:10 -0700314 mpl_dbg("Mesh plink timer for %pM fired on state %d\n",
315 sta->sta.addr, sta->plink_state);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100316 reason = 0;
317 llid = sta->llid;
318 plid = sta->plid;
Johannes Bergd0709a62008-02-25 16:27:46 +0100319 sdata = sta->sdata;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100320
321 switch (sta->plink_state) {
Javier Cardona57cf8042011-05-13 10:45:43 -0700322 case NL80211_PLINK_OPN_RCVD:
323 case NL80211_PLINK_OPN_SNT:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100324 /* retry timer */
325 if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
326 u32 rand;
Johannes Berg0c68ae262008-10-27 15:56:10 -0700327 mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n",
328 sta->sta.addr, sta->plink_retries,
329 sta->plink_timeout);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100330 get_random_bytes(&rand, sizeof(u32));
331 sta->plink_timeout = sta->plink_timeout +
332 rand % sta->plink_timeout;
333 ++sta->plink_retries;
Johannes Bergd0709a62008-02-25 16:27:46 +0100334 mod_plink_timer(sta, sta->plink_timeout);
Johannes Berg07346f812008-05-03 01:02:02 +0200335 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700336 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
337 sta->sta.addr, llid, 0, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100338 break;
339 }
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700340 reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100341 /* fall through on else */
Javier Cardona57cf8042011-05-13 10:45:43 -0700342 case NL80211_PLINK_CNF_RCVD:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100343 /* confirm timer */
344 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700345 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
Javier Cardona57cf8042011-05-13 10:45:43 -0700346 sta->plink_state = NL80211_PLINK_HOLDING;
Johannes Bergd0709a62008-02-25 16:27:46 +0100347 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200348 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700349 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
350 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100351 break;
Javier Cardona57cf8042011-05-13 10:45:43 -0700352 case NL80211_PLINK_HOLDING:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100353 /* holding timer */
Johannes Bergd0709a62008-02-25 16:27:46 +0100354 del_timer(&sta->plink_timer);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100355 mesh_plink_fsm_restart(sta);
Johannes Berg07346f812008-05-03 01:02:02 +0200356 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100357 break;
358 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200359 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100360 break;
361 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100362}
363
Johannes Berg5bb644a2009-05-17 11:40:42 +0200364#ifdef CONFIG_PM
365void mesh_plink_quiesce(struct sta_info *sta)
366{
367 if (del_timer_sync(&sta->plink_timer))
368 sta->plink_timer_was_running = true;
369}
370
371void mesh_plink_restart(struct sta_info *sta)
372{
373 if (sta->plink_timer_was_running) {
374 add_timer(&sta->plink_timer);
375 sta->plink_timer_was_running = false;
376 }
377}
378#endif
379
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100380static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
381{
382 sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
383 sta->plink_timer.data = (unsigned long) sta;
384 sta->plink_timer.function = mesh_plink_timer;
385 sta->plink_timeout = timeout;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100386 add_timer(&sta->plink_timer);
387}
388
389int mesh_plink_open(struct sta_info *sta)
390{
391 __le16 llid;
Johannes Bergd0709a62008-02-25 16:27:46 +0100392 struct ieee80211_sub_if_data *sdata = sta->sdata;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100393
Johannes Bergc2c98fd2011-09-29 16:04:36 +0200394 if (!test_sta_flag(sta, WLAN_STA_AUTH))
Javier Cardona53e80512011-04-07 15:08:32 -0700395 return -EPERM;
396
Johannes Berg07346f812008-05-03 01:02:02 +0200397 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100398 get_random_bytes(&llid, 2);
399 sta->llid = llid;
Javier Cardona57cf8042011-05-13 10:45:43 -0700400 if (sta->plink_state != NL80211_PLINK_LISTEN) {
Johannes Berg07346f812008-05-03 01:02:02 +0200401 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100402 return -EBUSY;
403 }
Javier Cardona57cf8042011-05-13 10:45:43 -0700404 sta->plink_state = NL80211_PLINK_OPN_SNT;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100405 mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200406 spin_unlock_bh(&sta->lock);
Johannes Berg0c68ae262008-10-27 15:56:10 -0700407 mpl_dbg("Mesh plink: starting establishment with %pM\n",
408 sta->sta.addr);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100409
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700410 return mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
Johannes Berg17741cd2008-09-11 00:02:02 +0200411 sta->sta.addr, llid, 0, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100412}
413
414void mesh_plink_block(struct sta_info *sta)
415{
John W. Linvillec9370192010-06-21 17:14:07 -0400416 struct ieee80211_sub_if_data *sdata = sta->sdata;
417 bool deactivated;
418
Johannes Berg07346f812008-05-03 01:02:02 +0200419 spin_lock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400420 deactivated = __mesh_plink_deactivate(sta);
Javier Cardona57cf8042011-05-13 10:45:43 -0700421 sta->plink_state = NL80211_PLINK_BLOCKED;
Johannes Berg07346f812008-05-03 01:02:02 +0200422 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400423
424 if (deactivated)
425 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100426}
427
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100428
Jasper Bryant-Greenef698d852008-08-03 12:04:37 +1200429void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100430 size_t len, struct ieee80211_rx_status *rx_status)
431{
Johannes Bergd0709a62008-02-25 16:27:46 +0100432 struct ieee80211_local *local = sdata->local;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100433 struct ieee802_11_elems elems;
434 struct sta_info *sta;
435 enum plink_event event;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700436 enum ieee80211_self_protected_actioncode ftype;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100437 size_t baselen;
Christian Lamparterd12c7452010-10-08 22:27:07 +0200438 bool deactivated, matches_local = true;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100439 u8 ie_len;
440 u8 *baseaddr;
441 __le16 plid, llid, reason;
Rui Paulo1460dd12009-11-09 23:46:48 +0000442#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
443 static const char *mplstates[] = {
Javier Cardona57cf8042011-05-13 10:45:43 -0700444 [NL80211_PLINK_LISTEN] = "LISTEN",
445 [NL80211_PLINK_OPN_SNT] = "OPN-SNT",
446 [NL80211_PLINK_OPN_RCVD] = "OPN-RCVD",
447 [NL80211_PLINK_CNF_RCVD] = "CNF_RCVD",
448 [NL80211_PLINK_ESTAB] = "ESTAB",
449 [NL80211_PLINK_HOLDING] = "HOLDING",
450 [NL80211_PLINK_BLOCKED] = "BLOCKED"
Rui Paulo1460dd12009-11-09 23:46:48 +0000451 };
452#endif
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100453
Johannes Berg9c80d3d2008-09-08 15:41:59 +0200454 /* need action_code, aux */
455 if (len < IEEE80211_MIN_ACTION_SIZE + 3)
456 return;
457
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100458 if (is_multicast_ether_addr(mgmt->da)) {
459 mpl_dbg("Mesh plink: ignore frame from multicast address");
460 return;
461 }
462
Thomas Pedersen8db09852011-08-12 20:01:00 -0700463 baseaddr = mgmt->u.action.u.self_prot.variable;
464 baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt;
465 if (mgmt->u.action.u.self_prot.action_code ==
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700466 WLAN_SP_MESH_PEERING_CONFIRM) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100467 baseaddr += 4;
David Woo70bdb6b2009-08-12 11:03:44 -0700468 baselen += 4;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100469 }
470 ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
Thomas Pedersen8db09852011-08-12 20:01:00 -0700471 if (!elems.peering) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100472 mpl_dbg("Mesh plink: missing necessary peer link ie\n");
473 return;
474 }
Javier Cardonab130e5c2011-05-03 16:57:07 -0700475 if (elems.rsn_len &&
476 sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
Javier Cardona5cff5e02011-04-07 15:08:29 -0700477 mpl_dbg("Mesh plink: can't establish link with secure peer\n");
478 return;
479 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100480
Thomas Pedersen8db09852011-08-12 20:01:00 -0700481 ftype = mgmt->u.action.u.self_prot.action_code;
482 ie_len = elems.peering_len;
483 if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
484 (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
485 (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
486 && ie_len != 8)) {
Rui Paulo09383932009-11-09 23:46:43 +0000487 mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n",
488 ftype, ie_len);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100489 return;
490 }
491
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700492 if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
493 (!elems.mesh_id || !elems.mesh_config)) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100494 mpl_dbg("Mesh plink: missing necessary ie\n");
495 return;
496 }
497 /* Note the lines below are correct, the llid in the frame is the plid
498 * from the point of view of this host.
499 */
Thomas Pedersen8db09852011-08-12 20:01:00 -0700500 memcpy(&plid, PLINK_GET_LLID(elems.peering), 2);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700501 if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
Thomas Pedersen8db09852011-08-12 20:01:00 -0700502 (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
503 memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100504
Johannes Bergd0709a62008-02-25 16:27:46 +0100505 rcu_read_lock();
506
Johannes Bergabe60632009-11-25 17:46:18 +0100507 sta = sta_info_get(sdata, mgmt->sa);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700508 if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100509 mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
Johannes Bergd0709a62008-02-25 16:27:46 +0100510 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100511 return;
512 }
513
Johannes Bergc2c98fd2011-09-29 16:04:36 +0200514 if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) {
Javier Cardona53e80512011-04-07 15:08:32 -0700515 mpl_dbg("Mesh plink: Action frame from non-authed peer\n");
516 rcu_read_unlock();
517 return;
518 }
519
Javier Cardona57cf8042011-05-13 10:45:43 -0700520 if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) {
Johannes Bergd0709a62008-02-25 16:27:46 +0100521 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100522 return;
523 }
524
525 /* Now we will figure out the appropriate event... */
526 event = PLINK_UNDEFINED;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700527 if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
528 (!mesh_matches_local(&elems, sdata))) {
Christian Lamparterd12c7452010-10-08 22:27:07 +0200529 matches_local = false;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100530 switch (ftype) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700531 case WLAN_SP_MESH_PEERING_OPEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100532 event = OPN_RJCT;
533 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700534 case WLAN_SP_MESH_PEERING_CONFIRM:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100535 event = CNF_RJCT;
536 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700537 default:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100538 break;
539 }
Christian Lamparterd12c7452010-10-08 22:27:07 +0200540 }
541
542 if (!sta && !matches_local) {
543 rcu_read_unlock();
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700544 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Christian Lamparterd12c7452010-10-08 22:27:07 +0200545 llid = 0;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700546 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
547 mgmt->sa, llid, plid, reason);
Christian Lamparterd12c7452010-10-08 22:27:07 +0200548 return;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100549 } else if (!sta) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700550 /* ftype == WLAN_SP_MESH_PEERING_OPEN */
Johannes Berg881d9482009-01-21 15:13:48 +0100551 u32 rates;
Johannes Berg34e89502010-02-03 13:59:58 +0100552
553 rcu_read_unlock();
554
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100555 if (!mesh_plink_free_count(sdata)) {
556 mpl_dbg("Mesh plink error: no more free plinks\n");
557 return;
558 }
559
560 rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
Johannes Berg03e44972008-02-27 09:56:40 +0100561 sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
Johannes Berg73651ee2008-02-25 16:27:47 +0100562 if (!sta) {
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100563 mpl_dbg("Mesh plink error: plink table full\n");
564 return;
565 }
Johannes Berg34e89502010-02-03 13:59:58 +0100566 if (sta_info_insert_rcu(sta)) {
Johannes Berg73651ee2008-02-25 16:27:47 +0100567 rcu_read_unlock();
568 return;
569 }
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100570 event = OPN_ACPT;
Johannes Berg07346f812008-05-03 01:02:02 +0200571 spin_lock_bh(&sta->lock);
Christian Lamparterd12c7452010-10-08 22:27:07 +0200572 } else if (matches_local) {
Johannes Berg07346f812008-05-03 01:02:02 +0200573 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100574 switch (ftype) {
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700575 case WLAN_SP_MESH_PEERING_OPEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100576 if (!mesh_plink_free_count(sdata) ||
Johannes Bergd0709a62008-02-25 16:27:46 +0100577 (sta->plid && sta->plid != plid))
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100578 event = OPN_IGNR;
579 else
580 event = OPN_ACPT;
581 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700582 case WLAN_SP_MESH_PEERING_CONFIRM:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100583 if (!mesh_plink_free_count(sdata) ||
Johannes Bergd0709a62008-02-25 16:27:46 +0100584 (sta->llid != llid || sta->plid != plid))
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100585 event = CNF_IGNR;
586 else
587 event = CNF_ACPT;
588 break;
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700589 case WLAN_SP_MESH_PEERING_CLOSE:
Javier Cardona57cf8042011-05-13 10:45:43 -0700590 if (sta->plink_state == NL80211_PLINK_ESTAB)
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100591 /* Do not check for llid or plid. This does not
592 * follow the standard but since multiple plinks
593 * per sta are not supported, it is necessary in
594 * order to avoid a livelock when MP A sees an
595 * establish peer link to MP B but MP B does not
596 * see it. This can be caused by a timeout in
597 * B's peer link establishment or B beign
598 * restarted.
599 */
600 event = CLS_ACPT;
601 else if (sta->plid != plid)
602 event = CLS_IGNR;
603 else if (ie_len == 7 && sta->llid != llid)
604 event = CLS_IGNR;
605 else
606 event = CLS_ACPT;
607 break;
608 default:
609 mpl_dbg("Mesh plink: unknown frame subtype\n");
Johannes Berg07346f812008-05-03 01:02:02 +0200610 spin_unlock_bh(&sta->lock);
Johannes Bergd0709a62008-02-25 16:27:46 +0100611 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100612 return;
613 }
Christian Lamparterd12c7452010-10-08 22:27:07 +0200614 } else {
615 spin_lock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100616 }
617
Rui Paulo1460dd12009-11-09 23:46:48 +0000618 mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n",
619 mgmt->sa, mplstates[sta->plink_state],
Johannes Berg0c68ae262008-10-27 15:56:10 -0700620 le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
621 event);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100622 reason = 0;
623 switch (sta->plink_state) {
624 /* spin_unlock as soon as state is updated at each case */
Javier Cardona57cf8042011-05-13 10:45:43 -0700625 case NL80211_PLINK_LISTEN:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100626 switch (event) {
627 case CLS_ACPT:
628 mesh_plink_fsm_restart(sta);
Johannes Berg07346f812008-05-03 01:02:02 +0200629 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100630 break;
631 case OPN_ACPT:
Javier Cardona57cf8042011-05-13 10:45:43 -0700632 sta->plink_state = NL80211_PLINK_OPN_RCVD;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100633 sta->plid = plid;
634 get_random_bytes(&llid, 2);
635 sta->llid = llid;
636 mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200637 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700638 mesh_plink_frame_tx(sdata,
639 WLAN_SP_MESH_PEERING_OPEN,
640 sta->sta.addr, llid, 0, 0);
641 mesh_plink_frame_tx(sdata,
642 WLAN_SP_MESH_PEERING_CONFIRM,
643 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100644 break;
645 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200646 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100647 break;
648 }
649 break;
650
Javier Cardona57cf8042011-05-13 10:45:43 -0700651 case NL80211_PLINK_OPN_SNT:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100652 switch (event) {
653 case OPN_RJCT:
654 case CNF_RJCT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700655 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100656 case CLS_ACPT:
657 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700658 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100659 sta->reason = reason;
Javier Cardona57cf8042011-05-13 10:45:43 -0700660 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100661 if (!mod_plink_timer(sta,
662 dot11MeshHoldingTimeout(sdata)))
663 sta->ignore_plink_timer = true;
664
665 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200666 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700667 mesh_plink_frame_tx(sdata,
668 WLAN_SP_MESH_PEERING_CLOSE,
669 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100670 break;
671 case OPN_ACPT:
672 /* retry timer is left untouched */
Javier Cardona57cf8042011-05-13 10:45:43 -0700673 sta->plink_state = NL80211_PLINK_OPN_RCVD;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100674 sta->plid = plid;
675 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200676 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700677 mesh_plink_frame_tx(sdata,
678 WLAN_SP_MESH_PEERING_CONFIRM,
679 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100680 break;
681 case CNF_ACPT:
Javier Cardona57cf8042011-05-13 10:45:43 -0700682 sta->plink_state = NL80211_PLINK_CNF_RCVD;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100683 if (!mod_plink_timer(sta,
684 dot11MeshConfirmTimeout(sdata)))
685 sta->ignore_plink_timer = true;
686
Johannes Berg07346f812008-05-03 01:02:02 +0200687 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100688 break;
689 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200690 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100691 break;
692 }
693 break;
694
Javier Cardona57cf8042011-05-13 10:45:43 -0700695 case NL80211_PLINK_OPN_RCVD:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100696 switch (event) {
697 case OPN_RJCT:
698 case CNF_RJCT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700699 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100700 case CLS_ACPT:
701 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700702 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100703 sta->reason = reason;
Javier Cardona57cf8042011-05-13 10:45:43 -0700704 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100705 if (!mod_plink_timer(sta,
706 dot11MeshHoldingTimeout(sdata)))
707 sta->ignore_plink_timer = true;
708
709 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200710 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700711 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
712 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100713 break;
714 case OPN_ACPT:
715 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200716 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700717 mesh_plink_frame_tx(sdata,
718 WLAN_SP_MESH_PEERING_CONFIRM,
719 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100720 break;
721 case CNF_ACPT:
Johannes Bergd0709a62008-02-25 16:27:46 +0100722 del_timer(&sta->plink_timer);
Javier Cardona57cf8042011-05-13 10:45:43 -0700723 sta->plink_state = NL80211_PLINK_ESTAB;
Johannes Berg07346f812008-05-03 01:02:02 +0200724 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400725 mesh_plink_inc_estab_count(sdata);
726 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Johannes Berg0c68ae262008-10-27 15:56:10 -0700727 mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
728 sta->sta.addr);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100729 break;
730 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200731 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100732 break;
733 }
734 break;
735
Javier Cardona57cf8042011-05-13 10:45:43 -0700736 case NL80211_PLINK_CNF_RCVD:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100737 switch (event) {
738 case OPN_RJCT:
739 case CNF_RJCT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700740 reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100741 case CLS_ACPT:
742 if (!reason)
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700743 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100744 sta->reason = reason;
Javier Cardona57cf8042011-05-13 10:45:43 -0700745 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100746 if (!mod_plink_timer(sta,
747 dot11MeshHoldingTimeout(sdata)))
748 sta->ignore_plink_timer = true;
749
750 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200751 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700752 mesh_plink_frame_tx(sdata,
753 WLAN_SP_MESH_PEERING_CLOSE,
754 sta->sta.addr, llid, plid, reason);
Johannes Bergff59dc72008-02-25 10:11:50 +0100755 break;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100756 case OPN_ACPT:
Johannes Bergd0709a62008-02-25 16:27:46 +0100757 del_timer(&sta->plink_timer);
Javier Cardona57cf8042011-05-13 10:45:43 -0700758 sta->plink_state = NL80211_PLINK_ESTAB;
Johannes Berg07346f812008-05-03 01:02:02 +0200759 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400760 mesh_plink_inc_estab_count(sdata);
761 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Johannes Berg0c68ae262008-10-27 15:56:10 -0700762 mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
763 sta->sta.addr);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700764 mesh_plink_frame_tx(sdata,
765 WLAN_SP_MESH_PEERING_CONFIRM,
766 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100767 break;
768 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200769 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100770 break;
771 }
772 break;
773
Javier Cardona57cf8042011-05-13 10:45:43 -0700774 case NL80211_PLINK_ESTAB:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100775 switch (event) {
776 case CLS_ACPT:
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700777 reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100778 sta->reason = reason;
John W. Linvillec9370192010-06-21 17:14:07 -0400779 deactivated = __mesh_plink_deactivate(sta);
Javier Cardona57cf8042011-05-13 10:45:43 -0700780 sta->plink_state = NL80211_PLINK_HOLDING;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100781 llid = sta->llid;
Johannes Bergd0709a62008-02-25 16:27:46 +0100782 mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
Johannes Berg07346f812008-05-03 01:02:02 +0200783 spin_unlock_bh(&sta->lock);
John W. Linvillec9370192010-06-21 17:14:07 -0400784 if (deactivated)
785 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700786 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
787 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100788 break;
789 case OPN_ACPT:
790 llid = sta->llid;
Johannes Berg07346f812008-05-03 01:02:02 +0200791 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700792 mesh_plink_frame_tx(sdata,
793 WLAN_SP_MESH_PEERING_CONFIRM,
794 sta->sta.addr, llid, plid, 0);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100795 break;
796 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200797 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100798 break;
799 }
800 break;
Javier Cardona57cf8042011-05-13 10:45:43 -0700801 case NL80211_PLINK_HOLDING:
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100802 switch (event) {
803 case CLS_ACPT:
Johannes Bergd0709a62008-02-25 16:27:46 +0100804 if (del_timer(&sta->plink_timer))
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100805 sta->ignore_plink_timer = 1;
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100806 mesh_plink_fsm_restart(sta);
Johannes Berg07346f812008-05-03 01:02:02 +0200807 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100808 break;
809 case OPN_ACPT:
810 case CNF_ACPT:
811 case OPN_RJCT:
812 case CNF_RJCT:
813 llid = sta->llid;
814 reason = sta->reason;
Johannes Berg07346f812008-05-03 01:02:02 +0200815 spin_unlock_bh(&sta->lock);
Thomas Pedersen54ef6562011-08-11 19:35:12 -0700816 mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
817 sta->sta.addr, llid, plid, reason);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100818 break;
819 default:
Johannes Berg07346f812008-05-03 01:02:02 +0200820 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100821 }
822 break;
823 default:
Luis Carlos Cobob4e08ea2008-02-29 15:46:08 -0800824 /* should not get here, PLINK_BLOCKED is dealt with at the
Daniel Mack3ad2f3f2010-02-03 08:01:28 +0800825 * beginning of the function
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100826 */
Johannes Berg07346f812008-05-03 01:02:02 +0200827 spin_unlock_bh(&sta->lock);
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100828 break;
829 }
Johannes Bergd0709a62008-02-25 16:27:46 +0100830
831 rcu_read_unlock();
Luis Carlos Coboc3896d2c2008-02-23 15:17:13 +0100832}