Blame view

net/mac80211/mesh_plink.c 32.2 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
2
  /*
264d9b7d8   Rui Paulo   mac80211: update ...
3
   * Copyright (c) 2008, 2009 open80211s Ltd.
4abb52a46   Sara Sharon   mac80211: pass bs...
4
   * Copyright (C) 2019 Intel Corporation
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
5
   * Author:     Luis Carlos Cobo <luisca@cozybit.com>
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
6
   */
5a0e3ad6a   Tejun Heo   include cleanup: ...
7
  #include <linux/gfp.h>
902acc789   Johannes Berg   mac80211: clean u...
8
9
  #include <linux/kernel.h>
  #include <linux/random.h>
b2d091031   Ingo Molnar   sched/headers: Pr...
10
  #include <linux/rculist.h>
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
11
  #include "ieee80211_i.h"
2c8dccc77   Johannes Berg   mac80211: rename ...
12
  #include "rate.h"
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
13
  #include "mesh.h"
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
14

a69bd8e60   Bob Copeland   mac80211: mesh: s...
15
  #define PLINK_CNF_AID(mgmt) ((mgmt)->u.action.u.self_prot.variable + 2)
8db098507   Thomas Pedersen   mac80211: update ...
16
17
  #define PLINK_GET_LLID(p) (p + 2)
  #define PLINK_GET_PLID(p) (p + 4)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
18

433f5bc1c   Johannes Berg   mac80211: move me...
19
  #define mod_plink_timer(s, t) (mod_timer(&s->mesh->plink_timer, \
cc57ac536   Nicholas Mc Guire   mesh_plink: use m...
20
  				jiffies + msecs_to_jiffies(t)))
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
21

c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
22
23
24
25
26
27
28
29
30
31
32
  enum plink_event {
  	PLINK_UNDEFINED,
  	OPN_ACPT,
  	OPN_RJCT,
  	OPN_IGNR,
  	CNF_ACPT,
  	CNF_RJCT,
  	CNF_IGNR,
  	CLS_ACPT,
  	CLS_IGNR
  };
95e48addb   Thomas Pedersen   mac80211: stringi...
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  static const char * const mplstates[] = {
  	[NL80211_PLINK_LISTEN] = "LISTEN",
  	[NL80211_PLINK_OPN_SNT] = "OPN-SNT",
  	[NL80211_PLINK_OPN_RCVD] = "OPN-RCVD",
  	[NL80211_PLINK_CNF_RCVD] = "CNF_RCVD",
  	[NL80211_PLINK_ESTAB] = "ESTAB",
  	[NL80211_PLINK_HOLDING] = "HOLDING",
  	[NL80211_PLINK_BLOCKED] = "BLOCKED"
  };
  
  static const char * const mplevents[] = {
  	[PLINK_UNDEFINED] = "NONE",
  	[OPN_ACPT] = "OPN_ACPT",
  	[OPN_RJCT] = "OPN_RJCT",
  	[OPN_IGNR] = "OPN_IGNR",
  	[CNF_ACPT] = "CNF_ACPT",
  	[CNF_RJCT] = "CNF_RJCT",
  	[CNF_IGNR] = "CNF_IGNR",
  	[CLS_ACPT] = "CLS_ACPT",
  	[CLS_IGNR] = "CLS_IGNR"
  };
36c9bb29b   Bob Copeland   mac80211: mesh: r...
54
55
56
57
58
59
  /* 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 ||
e5a9f8d04   Johannes Berg   mac80211: move st...
60
  	       (sta &&
0be6ed133   Johannes Berg   mac80211: move av...
61
  		(s8)-ewma_signal_read(&sta->rx_stats_avg.signal) >
e5a9f8d04   Johannes Berg   mac80211: move st...
62
  						rssi_threshold);
36c9bb29b   Bob Copeland   mac80211: mesh: r...
63
  }
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
64
65
66
  /**
   * mesh_plink_fsm_restart - restart a mesh peer link finite state machine
   *
23c7a29cd   Rui Paulo   mac80211: fix typ...
67
   * @sta: mesh peer link to restart
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
68
   *
433f5bc1c   Johannes Berg   mac80211: move me...
69
   * Locking: this function must be called holding sta->mesh->plink_lock
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
70
71
72
   */
  static inline void mesh_plink_fsm_restart(struct sta_info *sta)
  {
433f5bc1c   Johannes Berg   mac80211: move me...
73
74
75
76
  	lockdep_assert_held(&sta->mesh->plink_lock);
  	sta->mesh->plink_state = NL80211_PLINK_LISTEN;
  	sta->mesh->llid = sta->mesh->plid = sta->mesh->reason = 0;
  	sta->mesh->plink_retries = 0;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
77
  }
3b144658b   Thomas Pedersen   mac80211: dynamic...
78
79
80
81
82
83
84
85
86
87
88
89
90
  /*
   * mesh_set_short_slot_time - enable / disable ERP short slot time.
   *
   * The standard indirectly mandates mesh STAs to turn off short slot time by
   * disallowing advertising this (802.11-2012 8.4.1.4), but that doesn't mean we
   * can't be sneaky about it. Enable short slot time if all mesh STAs in the
   * MBSS support ERP rates.
   *
   * Returns BSS_CHANGED_ERP_SLOT or 0 for no change.
   */
  static u32 mesh_set_short_slot_time(struct ieee80211_sub_if_data *sdata)
  {
  	struct ieee80211_local *local = sdata->local;
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
91
  	struct ieee80211_supported_band *sband;
3b144658b   Thomas Pedersen   mac80211: dynamic...
92
93
94
95
  	struct sta_info *sta;
  	u32 erp_rates = 0, changed = 0;
  	int i;
  	bool short_slot = false;
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
96
97
98
99
100
  	sband = ieee80211_get_sband(sdata);
  	if (!sband)
  		return changed;
  
  	if (sband->band == NL80211_BAND_5GHZ) {
3b144658b   Thomas Pedersen   mac80211: dynamic...
101
102
103
  		/* (IEEE 802.11-2012 19.4.5) */
  		short_slot = true;
  		goto out;
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
104
  	} else if (sband->band != NL80211_BAND_2GHZ) {
3b144658b   Thomas Pedersen   mac80211: dynamic...
105
  		goto out;
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
106
  	}
3b144658b   Thomas Pedersen   mac80211: dynamic...
107
108
109
110
111
112
113
114
115
116
117
  
  	for (i = 0; i < sband->n_bitrates; i++)
  		if (sband->bitrates[i].flags & IEEE80211_RATE_ERP_G)
  			erp_rates |= BIT(i);
  
  	if (!erp_rates)
  		goto out;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(sta, &local->sta_list, list) {
  		if (sdata != sta->sdata ||
433f5bc1c   Johannes Berg   mac80211: move me...
118
  		    sta->mesh->plink_state != NL80211_PLINK_ESTAB)
3b144658b   Thomas Pedersen   mac80211: dynamic...
119
120
121
  			continue;
  
  		short_slot = false;
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
122
  		if (erp_rates & sta->sta.supp_rates[sband->band])
3b144658b   Thomas Pedersen   mac80211: dynamic...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
  			short_slot = true;
  		 else
  			break;
  	}
  	rcu_read_unlock();
  
  out:
  	if (sdata->vif.bss_conf.use_short_slot != short_slot) {
  		sdata->vif.bss_conf.use_short_slot = short_slot;
  		changed = BSS_CHANGED_ERP_SLOT;
  		mpl_dbg(sdata, "mesh_plink %pM: ERP short slot time %d
  ",
  			sdata->vif.addr, short_slot);
  	}
  	return changed;
  }
2c53040f0   Ben Hutchings   net: Fix (nearly-...
139
  /**
cbf9322eb   Ashok Nagarajan   mac80211: Modify ...
140
   * mesh_set_ht_prot_mode - set correct HT protection mode
21439b652   Johannes Berg   mac80211: fix som...
141
   * @sdata: the (mesh) interface to handle
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
142
   *
cbf9322eb   Ashok Nagarajan   mac80211: Modify ...
143
144
145
146
147
148
   * Section 9.23.3.5 of IEEE 80211-2012 describes the protection rules for HT
   * mesh STA in a MBSS. Three HT protection modes are supported for now, non-HT
   * mixed mode, 20MHz-protection and no-protection mode. non-HT mixed mode is
   * selected if any non-HT peers are present in our MBSS.  20MHz-protection mode
   * is selected if all peers in our 20/40MHz MBSS support HT and atleast one
   * HT20 peer is present. Otherwise no-protection mode is selected.
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
149
150
151
152
153
   */
  static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
  {
  	struct ieee80211_local *local = sdata->local;
  	struct sta_info *sta;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
154
155
  	u16 ht_opmode;
  	bool non_ht_sta = false, ht20_sta = false;
0418a4458   Simon Wunderlich   mac80211: fix var...
156
157
158
159
  	switch (sdata->vif.bss_conf.chandef.width) {
  	case NL80211_CHAN_WIDTH_20_NOHT:
  	case NL80211_CHAN_WIDTH_5:
  	case NL80211_CHAN_WIDTH_10:
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
160
  		return 0;
0418a4458   Simon Wunderlich   mac80211: fix var...
161
162
163
  	default:
  		break;
  	}
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
164
165
166
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(sta, &local->sta_list, list) {
cbf9322eb   Ashok Nagarajan   mac80211: Modify ...
167
  		if (sdata != sta->sdata ||
433f5bc1c   Johannes Berg   mac80211: move me...
168
  		    sta->mesh->plink_state != NL80211_PLINK_ESTAB)
cbf9322eb   Ashok Nagarajan   mac80211: Modify ...
169
  			continue;
52ac8c488   Thomas Pedersen   mac80211: clean u...
170
171
172
173
174
175
176
  		if (sta->sta.bandwidth > IEEE80211_STA_RX_BW_20)
  			continue;
  
  		if (!sta->sta.ht_cap.ht_supported) {
  			mpl_dbg(sdata, "nonHT sta (%pM) is present
  ",
  				       sta->sta.addr);
cbf9322eb   Ashok Nagarajan   mac80211: Modify ...
177
  			non_ht_sta = true;
cbf9322eb   Ashok Nagarajan   mac80211: Modify ...
178
  			break;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
179
  		}
52ac8c488   Thomas Pedersen   mac80211: clean u...
180
181
182
183
  
  		mpl_dbg(sdata, "HT20 sta (%pM) is present
  ", sta->sta.addr);
  		ht20_sta = true;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
184
  	}
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
185
186
187
188
  	rcu_read_unlock();
  
  	if (non_ht_sta)
  		ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED;
466f310d1   Johannes Berg   mac80211: mesh: d...
189
  	else if (ht20_sta &&
4bf88530b   Johannes Berg   mac80211: convert...
190
  		 sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20)
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
191
192
193
  		ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ;
  	else
  		ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
52ac8c488   Thomas Pedersen   mac80211: clean u...
194
195
  	if (sdata->vif.bss_conf.ht_operation_mode == ht_opmode)
  		return 0;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
196

52ac8c488   Thomas Pedersen   mac80211: clean u...
197
198
199
200
201
  	sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
  	sdata->u.mesh.mshcfg.ht_opmode = ht_opmode;
  	mpl_dbg(sdata, "selected new HT protection mode %d
  ", ht_opmode);
  	return BSS_CHANGED_HT;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
202
  }
f698d856f   Jasper Bryant-Greene   replace net_devic...
203
  static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
a69bd8e60   Bob Copeland   mac80211: mesh: s...
204
  			       struct sta_info *sta,
bf7cd94dc   Johannes Berg   mac80211: clean u...
205
  			       enum ieee80211_self_protected_actioncode action,
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
206
  			       u8 *da, u16 llid, u16 plid, u16 reason)
bf7cd94dc   Johannes Berg   mac80211: clean u...
207
  {
f698d856f   Jasper Bryant-Greene   replace net_devic...
208
  	struct ieee80211_local *local = sdata->local;
3b69a9c5f   Thomas Pedersen   mac80211: comment...
209
  	struct sk_buff *skb;
e7570dfb6   Thomas Pedersen   mac80211: don't r...
210
  	struct ieee80211_tx_info *info;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
211
212
  	struct ieee80211_mgmt *mgmt;
  	bool include_plid = false;
8db098507   Thomas Pedersen   mac80211: update ...
213
  	u16 peering_proto = 0;
3b69a9c5f   Thomas Pedersen   mac80211: comment...
214
  	u8 *pos, ie_len = 4;
60ad72da5   Sven Eckelmann   mac80211: impleme...
215
  	u8 ie_len_he_cap;
4c121fd69   Johannes Berg   mac80211: use off...
216
  	int hdr_len = offsetofend(struct ieee80211_mgmt, u.action.u.self_prot);
f609a43dc   Thomas Pedersen   mac80211: skb lea...
217
  	int err = -ENOMEM;
3b69a9c5f   Thomas Pedersen   mac80211: comment...
218

60ad72da5   Sven Eckelmann   mac80211: impleme...
219
220
  	ie_len_he_cap = ieee80211_ie_len_he_cap(sdata,
  						NL80211_IFTYPE_MESH_POINT);
65e8b0ccb   Javier Cardona   mac80211: Use the...
221
  	skb = dev_alloc_skb(local->tx_headroom +
3b69a9c5f   Thomas Pedersen   mac80211: comment...
222
223
224
225
226
227
228
  			    hdr_len +
  			    2 + /* capability info */
  			    2 + /* AID */
  			    2 + 8 + /* supported rates */
  			    2 + (IEEE80211_MAX_SUPP_RATES - 8) +
  			    2 + sdata->u.mesh.mesh_id_len +
  			    2 + sizeof(struct ieee80211_meshconf_ie) +
176f36086   Thomas Pedersen   mac80211: add HT ...
229
  			    2 + sizeof(struct ieee80211_ht_cap) +
074d46d1d   Johannes Berg   wireless: rename ...
230
  			    2 + sizeof(struct ieee80211_ht_operation) +
c85fb53c4   Bob Copeland   mac80211: impleme...
231
232
  			    2 + sizeof(struct ieee80211_vht_cap) +
  			    2 + sizeof(struct ieee80211_vht_operation) +
60ad72da5   Sven Eckelmann   mac80211: impleme...
233
234
  			    ie_len_he_cap +
  			    2 + 1 + sizeof(struct ieee80211_he_operation) +
d1b7524b3   Rajkumar Manoharan   mac80211: build H...
235
  				    sizeof(struct ieee80211_he_6ghz_oper) +
24a2042cb   Rajkumar Manoharan   mac80211: add HE ...
236
  			    2 + 1 + sizeof(struct ieee80211_he_6ghz_capa) +
3b69a9c5f   Thomas Pedersen   mac80211: comment...
237
238
  			    2 + 8 + /* peering IE */
  			    sdata->u.mesh.ie_len);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
239
  	if (!skb)
87d84c452   Bob Copeland   mac80211: return ...
240
  		return err;
e7570dfb6   Thomas Pedersen   mac80211: don't r...
241
  	info = IEEE80211_SKB_CB(skb);
65e8b0ccb   Javier Cardona   mac80211: Use the...
242
  	skb_reserve(skb, local->tx_headroom);
b080db585   Johannes Berg   networking: conve...
243
  	mgmt = skb_put_zero(skb, hdr_len);
e7827a703   Harvey Harrison   mac80211: remove ...
244
245
  	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
  					  IEEE80211_STYPE_ACTION);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
246
  	memcpy(mgmt->da, da, ETH_ALEN);
47846c9b0   Johannes Berg   mac80211: reduce ...
247
  	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
915b5c50f   Javier Cardona   open80211s: Stop ...
248
  	memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
8db098507   Thomas Pedersen   mac80211: update ...
249
250
  	mgmt->u.action.category = WLAN_CATEGORY_SELF_PROTECTED;
  	mgmt->u.action.u.self_prot.action_code = action;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
251

8db098507   Thomas Pedersen   mac80211: update ...
252
  	if (action != WLAN_SP_MESH_PEERING_CLOSE) {
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
253
254
255
256
257
258
259
260
261
  		struct ieee80211_supported_band *sband;
  		enum nl80211_band band;
  
  		sband = ieee80211_get_sband(sdata);
  		if (!sband) {
  			err = -EINVAL;
  			goto free;
  		}
  		band = sband->band;
55de908ab   Johannes Berg   mac80211: use cha...
262

8db098507   Thomas Pedersen   mac80211: update ...
263
  		/* capability info */
e45a79da8   Johannes Berg   skbuff/mac80211: ...
264
  		pos = skb_put_zero(skb, 2);
54ef656b0   Thomas Pedersen   mac80211: update ...
265
  		if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
8db098507   Thomas Pedersen   mac80211: update ...
266
267
  			/* AID */
  			pos = skb_put(skb, 2);
a69bd8e60   Bob Copeland   mac80211: mesh: s...
268
  			put_unaligned_le16(sta->sta.aid, pos);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
269
  		}
55de908ab   Johannes Berg   mac80211: use cha...
270
271
  		if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
  		    ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
bf7cd94dc   Johannes Berg   mac80211: clean u...
272
273
274
  		    mesh_add_rsn_ie(sdata, skb) ||
  		    mesh_add_meshid_ie(sdata, skb) ||
  		    mesh_add_meshconf_ie(sdata, skb))
f609a43dc   Thomas Pedersen   mac80211: skb lea...
275
  			goto free;
8db098507   Thomas Pedersen   mac80211: update ...
276
  	} else {	/* WLAN_SP_MESH_PEERING_CLOSE */
e7570dfb6   Thomas Pedersen   mac80211: don't r...
277
  		info->flags |= IEEE80211_TX_CTL_NO_ACK;
bf7cd94dc   Johannes Berg   mac80211: clean u...
278
  		if (mesh_add_meshid_ie(sdata, skb))
f609a43dc   Thomas Pedersen   mac80211: skb lea...
279
  			goto free;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
280
  	}
8db098507   Thomas Pedersen   mac80211: update ...
281
  	/* Add Mesh Peering Management element */
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
282
  	switch (action) {
54ef656b0   Thomas Pedersen   mac80211: update ...
283
  	case WLAN_SP_MESH_PEERING_OPEN:
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
284
  		break;
54ef656b0   Thomas Pedersen   mac80211: update ...
285
  	case WLAN_SP_MESH_PEERING_CONFIRM:
8db098507   Thomas Pedersen   mac80211: update ...
286
  		ie_len += 2;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
287
288
  		include_plid = true;
  		break;
54ef656b0   Thomas Pedersen   mac80211: update ...
289
  	case WLAN_SP_MESH_PEERING_CLOSE:
8db098507   Thomas Pedersen   mac80211: update ...
290
291
  		if (plid) {
  			ie_len += 2;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
292
293
  			include_plid = true;
  		}
8db098507   Thomas Pedersen   mac80211: update ...
294
  		ie_len += 2;	/* reason code */
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
295
  		break;
8db098507   Thomas Pedersen   mac80211: update ...
296
  	default:
f609a43dc   Thomas Pedersen   mac80211: skb lea...
297
298
  		err = -EINVAL;
  		goto free;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
299
  	}
8db098507   Thomas Pedersen   mac80211: update ...
300
  	if (WARN_ON(skb_tailroom(skb) < 2 + ie_len))
f609a43dc   Thomas Pedersen   mac80211: skb lea...
301
  		goto free;
8db098507   Thomas Pedersen   mac80211: update ...
302

c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
303
  	pos = skb_put(skb, 2 + ie_len);
8db098507   Thomas Pedersen   mac80211: update ...
304
  	*pos++ = WLAN_EID_PEER_MGMT;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
305
  	*pos++ = ie_len;
8db098507   Thomas Pedersen   mac80211: update ...
306
307
  	memcpy(pos, &peering_proto, 2);
  	pos += 2;
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
308
  	put_unaligned_le16(llid, pos);
8db098507   Thomas Pedersen   mac80211: update ...
309
  	pos += 2;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
310
  	if (include_plid) {
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
311
  		put_unaligned_le16(plid, pos);
8db098507   Thomas Pedersen   mac80211: update ...
312
  		pos += 2;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
313
  	}
54ef656b0   Thomas Pedersen   mac80211: update ...
314
  	if (action == WLAN_SP_MESH_PEERING_CLOSE) {
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
315
  		put_unaligned_le16(reason, pos);
8db098507   Thomas Pedersen   mac80211: update ...
316
  		pos += 2;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
317
  	}
176f36086   Thomas Pedersen   mac80211: add HT ...
318
319
  
  	if (action != WLAN_SP_MESH_PEERING_CLOSE) {
bf7cd94dc   Johannes Berg   mac80211: clean u...
320
  		if (mesh_add_ht_cap_ie(sdata, skb) ||
c85fb53c4   Bob Copeland   mac80211: impleme...
321
322
  		    mesh_add_ht_oper_ie(sdata, skb) ||
  		    mesh_add_vht_cap_ie(sdata, skb) ||
60ad72da5   Sven Eckelmann   mac80211: impleme...
323
324
  		    mesh_add_vht_oper_ie(sdata, skb) ||
  		    mesh_add_he_cap_ie(sdata, skb, ie_len_he_cap) ||
24a2042cb   Rajkumar Manoharan   mac80211: add HE ...
325
326
  		    mesh_add_he_oper_ie(sdata, skb) ||
  		    mesh_add_he_6ghz_cap_ie(sdata, skb))
f609a43dc   Thomas Pedersen   mac80211: skb lea...
327
  			goto free;
176f36086   Thomas Pedersen   mac80211: add HT ...
328
  	}
bf7cd94dc   Johannes Berg   mac80211: clean u...
329
  	if (mesh_add_vendor_ies(sdata, skb))
f609a43dc   Thomas Pedersen   mac80211: skb lea...
330
  		goto free;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
331

62ae67be3   Johannes Berg   mac80211: remove ...
332
  	ieee80211_tx_skb(sdata, skb);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
333
  	return 0;
f609a43dc   Thomas Pedersen   mac80211: skb lea...
334
335
336
  free:
  	kfree_skb(skb);
  	return err;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
337
  }
fa87a6566   Bob Copeland   mac80211: reorder...
338
339
340
341
342
  /**
   * __mesh_plink_deactivate - deactivate mesh peer link
   *
   * @sta: mesh peer link to deactivate
   *
e596af827   Bob Copeland   mac80211: mesh: f...
343
344
345
   * Mesh paths with this peer as next hop should be flushed
   * by the caller outside of plink_lock.
   *
fa87a6566   Bob Copeland   mac80211: reorder...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
   * Returns beacon changed flag if the beacon content changed.
   *
   * Locking: the caller must hold sta->mesh->plink_lock
   */
  static u32 __mesh_plink_deactivate(struct sta_info *sta)
  {
  	struct ieee80211_sub_if_data *sdata = sta->sdata;
  	u32 changed = 0;
  
  	lockdep_assert_held(&sta->mesh->plink_lock);
  
  	if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
  		changed = mesh_plink_dec_estab_count(sdata);
  	sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
fa87a6566   Bob Copeland   mac80211: reorder...
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  
  	ieee80211_mps_sta_status_update(sta);
  	changed |= ieee80211_mps_set_sta_local_pm(sta,
  			NL80211_MESH_POWER_UNKNOWN);
  
  	return changed;
  }
  
  /**
   * mesh_plink_deactivate - deactivate mesh peer link
   *
   * @sta: mesh peer link to deactivate
   *
   * All mesh paths with this peer as next hop will be flushed
   */
  u32 mesh_plink_deactivate(struct sta_info *sta)
  {
  	struct ieee80211_sub_if_data *sdata = sta->sdata;
  	u32 changed;
  
  	spin_lock_bh(&sta->mesh->plink_lock);
  	changed = __mesh_plink_deactivate(sta);
efc401f49   Bob Copeland   mac80211: use com...
382
383
384
385
386
387
388
  
  	if (!sdata->u.mesh.user_mpm) {
  		sta->mesh->reason = WLAN_REASON_MESH_PEER_CANCELED;
  		mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_CLOSE,
  				    sta->sta.addr, sta->mesh->llid,
  				    sta->mesh->plid, sta->mesh->reason);
  	}
fa87a6566   Bob Copeland   mac80211: reorder...
389
  	spin_unlock_bh(&sta->mesh->plink_lock);
efc401f49   Bob Copeland   mac80211: use com...
390
391
  	if (!sdata->u.mesh.user_mpm)
  		del_timer_sync(&sta->mesh->plink_timer);
e596af827   Bob Copeland   mac80211: mesh: f...
392
  	mesh_path_flush_by_nexthop(sta);
fa87a6566   Bob Copeland   mac80211: reorder...
393

efc401f49   Bob Copeland   mac80211: use com...
394
395
  	/* make sure no readers can access nexthop sta from here on */
  	synchronize_net();
fa87a6566   Bob Copeland   mac80211: reorder...
396
397
  	return changed;
  }
296fcba3b   Thomas Pedersen   mac80211: clean u...
398
399
  static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata,
  			       struct sta_info *sta,
1d6741d86   Bob Copeland   mac80211: mesh: f...
400
  			       struct ieee802_11_elems *elems)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
401
  {
f698d856f   Jasper Bryant-Greene   replace net_devic...
402
  	struct ieee80211_local *local = sdata->local;
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
403
  	struct ieee80211_supported_band *sband;
f68d776a0   Thomas Pedersen   mac80211: support...
404
  	u32 rates, basic_rates = 0, changed = 0;
abcff6ef0   Janusz.Dziedzic@tieto.com   mac80211: add VHT...
405
  	enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
406

21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
407
408
409
410
411
412
  	sband = ieee80211_get_sband(sdata);
  	if (!sband)
  		return;
  
  	rates = ieee80211_sta_get_rates(sdata, elems, sband->band,
  					&basic_rates);
d0709a651   Johannes Berg   mac80211: RCU-ify...
413

433f5bc1c   Johannes Berg   mac80211: move me...
414
  	spin_lock_bh(&sta->mesh->plink_lock);
e5a9f8d04   Johannes Berg   mac80211: move st...
415
  	sta->rx_stats.last_rx = jiffies;
296fcba3b   Thomas Pedersen   mac80211: clean u...
416
417
  
  	/* rates and capabilities don't change during peering */
433f5bc1c   Johannes Berg   mac80211: move me...
418
419
  	if (sta->mesh->plink_state == NL80211_PLINK_ESTAB &&
  	    sta->mesh->processed_beacon)
296fcba3b   Thomas Pedersen   mac80211: clean u...
420
  		goto out;
433f5bc1c   Johannes Berg   mac80211: move me...
421
  	sta->mesh->processed_beacon = true;
bae35d92b   Chun-Yeow Yeoh   mac80211: don't r...
422

21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
423
  	if (sta->sta.supp_rates[sband->band] != rates)
f68d776a0   Thomas Pedersen   mac80211: support...
424
  		changed |= IEEE80211_RC_SUPP_RATES_CHANGED;
21a8e9dd5   Mohammed Shafi Shajakhan   mac80211: Fix pos...
425
  	sta->sta.supp_rates[sband->band] = rates;
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
426

52ac8c488   Thomas Pedersen   mac80211: clean u...
427
428
429
  	if (ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
  					      elems->ht_cap_elem, sta))
  		changed |= IEEE80211_RC_BW_CHANGED;
4bf88530b   Johannes Berg   mac80211: convert...
430

c85fb53c4   Bob Copeland   mac80211: impleme...
431
432
  	ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
  					    elems->vht_cap_elem, sta);
60ad72da5   Sven Eckelmann   mac80211: impleme...
433
  	ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband, elems->he_cap,
1bb9a8a4c   Johannes Berg   mac80211: use HE ...
434
435
436
  					  elems->he_cap_len,
  					  elems->he_6ghz_capa,
  					  sta);
60ad72da5   Sven Eckelmann   mac80211: impleme...
437

abcff6ef0   Janusz.Dziedzic@tieto.com   mac80211: add VHT...
438
439
  	if (bw != sta->sta.bandwidth)
  		changed |= IEEE80211_RC_BW_CHANGED;
52ac8c488   Thomas Pedersen   mac80211: clean u...
440
441
442
443
444
  	/* HT peer is operating 20MHz-only */
  	if (elems->ht_operation &&
  	    !(elems->ht_operation->ht_param &
  	      IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
  		if (sta->sta.bandwidth != IEEE80211_STA_RX_BW_20)
f68d776a0   Thomas Pedersen   mac80211: support...
445
  			changed |= IEEE80211_RC_BW_CHANGED;
52ac8c488   Thomas Pedersen   mac80211: clean u...
446
  		sta->sta.bandwidth = IEEE80211_STA_RX_BW_20;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
447
  	}
c7d258288   Thomas Pedersen   mac80211: don't t...
448

1d6741d86   Bob Copeland   mac80211: mesh: f...
449
  	if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
59cf1d65f   Helmut Schaa   mac80211: don't r...
450
  		rate_control_rate_init(sta);
f68d776a0   Thomas Pedersen   mac80211: support...
451
452
  	else
  		rate_control_rate_update(local, sband, sta, changed);
296fcba3b   Thomas Pedersen   mac80211: clean u...
453
  out:
433f5bc1c   Johannes Berg   mac80211: move me...
454
  	spin_unlock_bh(&sta->mesh->plink_lock);
296fcba3b   Thomas Pedersen   mac80211: clean u...
455
  }
0e0060fcf   Bob Copeland   mac80211: select ...
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
  static int mesh_allocate_aid(struct ieee80211_sub_if_data *sdata)
  {
  	struct sta_info *sta;
  	unsigned long *aid_map;
  	int aid;
  
  	aid_map = kcalloc(BITS_TO_LONGS(IEEE80211_MAX_AID + 1),
  			  sizeof(*aid_map), GFP_KERNEL);
  	if (!aid_map)
  		return -ENOMEM;
  
  	/* reserve aid 0 for mcast indication */
  	__set_bit(0, aid_map);
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list)
  		__set_bit(sta->sta.aid, aid_map);
  	rcu_read_unlock();
  
  	aid = find_first_zero_bit(aid_map, IEEE80211_MAX_AID + 1);
  	kfree(aid_map);
  
  	if (aid > IEEE80211_MAX_AID)
  		return -ENOBUFS;
  
  	return aid;
  }
296fcba3b   Thomas Pedersen   mac80211: clean u...
483
484
485
486
  static struct sta_info *
  __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr)
  {
  	struct sta_info *sta;
0e0060fcf   Bob Copeland   mac80211: select ...
487
  	int aid;
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
488

296fcba3b   Thomas Pedersen   mac80211: clean u...
489
  	if (sdata->local->num_sta >= MESH_MAX_PLINKS)
e87278e73   Thomas Pedersen   mac80211: insert ...
490
  		return NULL;
0e0060fcf   Bob Copeland   mac80211: select ...
491
492
493
  	aid = mesh_allocate_aid(sdata);
  	if (aid < 0)
  		return NULL;
296fcba3b   Thomas Pedersen   mac80211: clean u...
494
495
496
  	sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL);
  	if (!sta)
  		return NULL;
433f5bc1c   Johannes Berg   mac80211: move me...
497
  	sta->mesh->plink_state = NL80211_PLINK_LISTEN;
a74a8c846   Johannes Berg   mac80211: don't d...
498
  	sta->sta.wme = true;
0e0060fcf   Bob Copeland   mac80211: select ...
499
  	sta->sta.aid = aid;
296fcba3b   Thomas Pedersen   mac80211: clean u...
500
501
502
503
  
  	sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
  	sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC);
  	sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED);
296fcba3b   Thomas Pedersen   mac80211: clean u...
504
505
506
507
508
  	return sta;
  }
  
  static struct sta_info *
  mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr,
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
509
510
  		    struct ieee802_11_elems *elems,
  		    struct ieee80211_rx_status *rx_status)
296fcba3b   Thomas Pedersen   mac80211: clean u...
511
512
  {
  	struct sta_info *sta = NULL;
a6dad6a26   Thomas Pedersen   mac80211: support...
513
514
  	/* Userspace handles station allocation */
  	if (sdata->u.mesh.user_mpm ||
11197d006   Masashi Honma   mac80211: Suppres...
515
516
  	    sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) {
  		if (mesh_peer_accepts_plinks(elems) &&
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
517
518
519
520
521
  		    mesh_plink_availables(sdata)) {
  			int sig = 0;
  
  			if (ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM))
  				sig = rx_status->signal;
11197d006   Masashi Honma   mac80211: Suppres...
522
523
524
  			cfg80211_notify_new_peer_candidate(sdata->dev, addr,
  							   elems->ie_start,
  							   elems->total_len,
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
525
526
  							   sig, GFP_KERNEL);
  		}
11197d006   Masashi Honma   mac80211: Suppres...
527
  	} else
296fcba3b   Thomas Pedersen   mac80211: clean u...
528
  		sta = __mesh_sta_info_alloc(sdata, addr);
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
529
530
  	return sta;
  }
296fcba3b   Thomas Pedersen   mac80211: clean u...
531
532
533
534
535
536
  /*
   * mesh_sta_info_get - return mesh sta info entry for @addr.
   *
   * @sdata: local meshif
   * @addr: peer's address
   * @elems: IEs from beacon or mesh peering frame.
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
537
   * @rx_status: rx status for the frame for signal reporting
296fcba3b   Thomas Pedersen   mac80211: clean u...
538
539
540
541
542
543
   *
   * Return existing or newly allocated sta_info under RCU read lock.
   * (re)initialize with given IEs.
   */
  static struct sta_info *
  mesh_sta_info_get(struct ieee80211_sub_if_data *sdata,
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
544
545
  		  u8 *addr, struct ieee802_11_elems *elems,
  		  struct ieee80211_rx_status *rx_status) __acquires(RCU)
296fcba3b   Thomas Pedersen   mac80211: clean u...
546
547
548
549
550
551
  {
  	struct sta_info *sta = NULL;
  
  	rcu_read_lock();
  	sta = sta_info_get(sdata, addr);
  	if (sta) {
1d6741d86   Bob Copeland   mac80211: mesh: f...
552
  		mesh_sta_info_init(sdata, sta, elems);
296fcba3b   Thomas Pedersen   mac80211: clean u...
553
554
555
  	} else {
  		rcu_read_unlock();
  		/* can't run atomic */
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
556
  		sta = mesh_sta_info_alloc(sdata, addr, elems, rx_status);
296fcba3b   Thomas Pedersen   mac80211: clean u...
557
558
559
560
  		if (!sta) {
  			rcu_read_lock();
  			return NULL;
  		}
1d6741d86   Bob Copeland   mac80211: mesh: f...
561
  		mesh_sta_info_init(sdata, sta, elems);
3b4797bce   Thomas Pedersen   mac80211: fix mes...
562

296fcba3b   Thomas Pedersen   mac80211: clean u...
563
564
565
566
567
568
569
570
571
572
573
574
575
  		if (sta_info_insert_rcu(sta))
  			return NULL;
  	}
  
  	return sta;
  }
  
  /*
   * mesh_neighbour_update - update or initialize new mesh neighbor.
   *
   * @sdata: local meshif
   * @addr: peer's address
   * @elems: IEs from beacon or mesh peering frame
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
576
   * @rx_status: rx status for the frame for signal reporting
296fcba3b   Thomas Pedersen   mac80211: clean u...
577
578
579
   *
   * Initiates peering if appropriate.
   */
f743ff490   Thomas Pedersen   mac80211: refacto...
580
581
  void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
  			   u8 *hw_addr,
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
582
583
  			   struct ieee802_11_elems *elems,
  			   struct ieee80211_rx_status *rx_status)
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
584
585
  {
  	struct sta_info *sta;
39886b618   Thomas Pedersen   mac80211: consoli...
586
  	u32 changed = 0;
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
587

ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
588
  	sta = mesh_sta_info_get(sdata, hw_addr, elems, rx_status);
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
589
590
  	if (!sta)
  		goto out;
dbdaee7aa   Bob Copeland   {nl,mac}80211: re...
591
592
  	sta->mesh->connected_to_gate = elems->mesh_config->meshconf_form &
  		IEEE80211_MESHCONF_FORM_CONNECTED_TO_GATE;
1570ca592   Javier Cardona   mac80211: send no...
593
  	if (mesh_peer_accepts_plinks(elems) &&
433f5bc1c   Johannes Berg   mac80211: move me...
594
  	    sta->mesh->plink_state == NL80211_PLINK_LISTEN &&
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
595
596
  	    sdata->u.mesh.accepting_plinks &&
  	    sdata->u.mesh.mshcfg.auto_open_plinks &&
36c9bb29b   Bob Copeland   mac80211: mesh: r...
597
  	    rssi_threshold_check(sdata, sta))
39886b618   Thomas Pedersen   mac80211: consoli...
598
  		changed = mesh_plink_open(sta);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
599

3f52b7e32   Marco Porsch   mac80211: mesh po...
600
  	ieee80211_mps_frame_release(sta, elems);
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
601
  out:
d0709a651   Johannes Berg   mac80211: RCU-ify...
602
  	rcu_read_unlock();
2b5e19677   Thomas Pedersen   mac80211: cache m...
603
  	ieee80211_mbss_info_change_notify(sdata, changed);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
604
  }
4c02d62fa   Kees Cook   net/mac80211/mesh...
605
  void mesh_plink_timer(struct timer_list *t)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
606
  {
4c02d62fa   Kees Cook   net/mac80211/mesh...
607
  	struct mesh_sta *mesh = from_timer(mesh, t, plink_timer);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
608
  	struct sta_info *sta;
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
609
  	u16 reason = 0;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
610
  	struct ieee80211_sub_if_data *sdata;
453e66f24   Marco Porsch   mac80211: remove ...
611
  	struct mesh_config *mshcfg;
4efec4513   Thomas Pedersen   mac80211: consoli...
612
  	enum ieee80211_self_protected_actioncode action = 0;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
613

d0709a651   Johannes Berg   mac80211: RCU-ify...
614
615
616
617
618
  	/*
  	 * This STA is valid because sta_info_destroy() will
  	 * del_timer_sync() this timer after having made sure
  	 * it cannot be readded (by deleting the plink.)
  	 */
4c02d62fa   Kees Cook   net/mac80211/mesh...
619
  	sta = mesh->plink_sta;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
620

690205f18   Stanislaw Gruszka   mac80211: cleanup...
621
  	if (sta->sdata->local->quiescing)
5bb644a0f   Johannes Berg   mac80211: cancel/...
622
  		return;
5bb644a0f   Johannes Berg   mac80211: cancel/...
623

433f5bc1c   Johannes Berg   mac80211: move me...
624
  	spin_lock_bh(&sta->mesh->plink_lock);
2b470c39e   Bob Copeland   mac80211: remove ...
625
626
627
628
629
630
  
  	/* If a timer fires just before a state transition on another CPU,
  	 * we may have already extended the timeout and changed state by the
  	 * time we've acquired the lock and arrived  here.  In that case,
  	 * skip this timer and wait for the new one.
  	 */
433f5bc1c   Johannes Berg   mac80211: move me...
631
  	if (time_before(jiffies, sta->mesh->plink_timer.expires)) {
2b470c39e   Bob Copeland   mac80211: remove ...
632
633
  		mpl_dbg(sta->sdata,
  			"Ignoring timer for %pM in state %s (timer adjusted)",
433f5bc1c   Johannes Berg   mac80211: move me...
634
635
  			sta->sta.addr, mplstates[sta->mesh->plink_state]);
  		spin_unlock_bh(&sta->mesh->plink_lock);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
636
637
  		return;
  	}
2b470c39e   Bob Copeland   mac80211: remove ...
638
639
  
  	/* del_timer() and handler may race when entering these states */
433f5bc1c   Johannes Berg   mac80211: move me...
640
641
  	if (sta->mesh->plink_state == NL80211_PLINK_LISTEN ||
  	    sta->mesh->plink_state == NL80211_PLINK_ESTAB) {
2b470c39e   Bob Copeland   mac80211: remove ...
642
643
  		mpl_dbg(sta->sdata,
  			"Ignoring timer for %pM in state %s (timer deleted)",
433f5bc1c   Johannes Berg   mac80211: move me...
644
645
  			sta->sta.addr, mplstates[sta->mesh->plink_state]);
  		spin_unlock_bh(&sta->mesh->plink_lock);
2b470c39e   Bob Copeland   mac80211: remove ...
646
647
  		return;
  	}
bdcbd8e0e   Johannes Berg   mac80211: clean u...
648
  	mpl_dbg(sta->sdata,
3088f7d2d   Thomas Pedersen   mac80211: stringi...
649
650
  		"Mesh plink timer for %pM fired on state %s
  ",
433f5bc1c   Johannes Berg   mac80211: move me...
651
  		sta->sta.addr, mplstates[sta->mesh->plink_state]);
d0709a651   Johannes Berg   mac80211: RCU-ify...
652
  	sdata = sta->sdata;
453e66f24   Marco Porsch   mac80211: remove ...
653
  	mshcfg = &sdata->u.mesh.mshcfg;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
654

433f5bc1c   Johannes Berg   mac80211: move me...
655
  	switch (sta->mesh->plink_state) {
57cf8043a   Javier Cardona   nl80211: Move pee...
656
657
  	case NL80211_PLINK_OPN_RCVD:
  	case NL80211_PLINK_OPN_SNT:
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
658
  		/* retry timer */
433f5bc1c   Johannes Berg   mac80211: move me...
659
  		if (sta->mesh->plink_retries < mshcfg->dot11MeshMaxRetries) {
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
660
  			u32 rand;
bdcbd8e0e   Johannes Berg   mac80211: clean u...
661
662
663
  			mpl_dbg(sta->sdata,
  				"Mesh plink for %pM (retry, timeout): %d %d
  ",
433f5bc1c   Johannes Berg   mac80211: move me...
664
665
  				sta->sta.addr, sta->mesh->plink_retries,
  				sta->mesh->plink_timeout);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
666
  			get_random_bytes(&rand, sizeof(u32));
433f5bc1c   Johannes Berg   mac80211: move me...
667
668
669
670
  			sta->mesh->plink_timeout = sta->mesh->plink_timeout +
  					     rand % sta->mesh->plink_timeout;
  			++sta->mesh->plink_retries;
  			mod_plink_timer(sta, sta->mesh->plink_timeout);
4efec4513   Thomas Pedersen   mac80211: consoli...
671
  			action = WLAN_SP_MESH_PEERING_OPEN;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
672
673
  			break;
  		}
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
674
  		reason = WLAN_REASON_MESH_MAX_RETRIES;
fc0561dc6   Gustavo A. R. Silva   mac80211: Use fal...
675
  		fallthrough;
57cf8043a   Javier Cardona   nl80211: Move pee...
676
  	case NL80211_PLINK_CNF_RCVD:
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
677
678
  		/* confirm timer */
  		if (!reason)
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
679
  			reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
433f5bc1c   Johannes Berg   mac80211: move me...
680
  		sta->mesh->plink_state = NL80211_PLINK_HOLDING;
453e66f24   Marco Porsch   mac80211: remove ...
681
  		mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
4efec4513   Thomas Pedersen   mac80211: consoli...
682
  		action = WLAN_SP_MESH_PEERING_CLOSE;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
683
  		break;
57cf8043a   Javier Cardona   nl80211: Move pee...
684
  	case NL80211_PLINK_HOLDING:
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
685
  		/* holding timer */
433f5bc1c   Johannes Berg   mac80211: move me...
686
  		del_timer(&sta->mesh->plink_timer);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
687
  		mesh_plink_fsm_restart(sta);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
688
689
  		break;
  	default:
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
690
691
  		break;
  	}
433f5bc1c   Johannes Berg   mac80211: move me...
692
  	spin_unlock_bh(&sta->mesh->plink_lock);
4efec4513   Thomas Pedersen   mac80211: consoli...
693
  	if (action)
a69bd8e60   Bob Copeland   mac80211: mesh: s...
694
  		mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
433f5bc1c   Johannes Berg   mac80211: move me...
695
  				    sta->mesh->llid, sta->mesh->plid, reason);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
696
  }
0df2f6c11   Nicholas Mc Guire   mesh_plink: fixup...
697
  static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
698
  {
433f5bc1c   Johannes Berg   mac80211: move me...
699
  	sta->mesh->plink_timeout = timeout;
4c02d62fa   Kees Cook   net/mac80211/mesh...
700
  	mod_timer(&sta->mesh->plink_timer, jiffies + msecs_to_jiffies(timeout));
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
701
  }
204d13042   Thomas Pedersen   mac80211: clean u...
702
  static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
703
  			u16 llid)
204d13042   Thomas Pedersen   mac80211: clean u...
704
705
706
707
708
709
710
  {
  	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) {
520c75dca   Matthias Schiffer   mac80211: fix cra...
711
712
  		if (sdata != sta->sdata)
  			continue;
433f5bc1c   Johannes Berg   mac80211: move me...
713
  		if (!memcmp(&sta->mesh->llid, &llid, sizeof(llid))) {
204d13042   Thomas Pedersen   mac80211: clean u...
714
715
716
717
718
719
720
721
  			in_use = true;
  			break;
  		}
  	}
  	rcu_read_unlock();
  
  	return in_use;
  }
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
722
  static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
204d13042   Thomas Pedersen   mac80211: clean u...
723
724
725
726
727
  {
  	u16 llid;
  
  	do {
  		get_random_bytes(&llid, sizeof(llid));
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
728
  	} while (llid_in_use(sdata, llid));
204d13042   Thomas Pedersen   mac80211: clean u...
729

6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
730
  	return llid;
204d13042   Thomas Pedersen   mac80211: clean u...
731
  }
39886b618   Thomas Pedersen   mac80211: consoli...
732
  u32 mesh_plink_open(struct sta_info *sta)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
733
  {
d0709a651   Johannes Berg   mac80211: RCU-ify...
734
  	struct ieee80211_sub_if_data *sdata = sta->sdata;
39886b618   Thomas Pedersen   mac80211: consoli...
735
  	u32 changed;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
736

c2c98fdeb   Johannes Berg   mac80211: optimis...
737
  	if (!test_sta_flag(sta, WLAN_STA_AUTH))
39886b618   Thomas Pedersen   mac80211: consoli...
738
  		return 0;
53e805111   Javier Cardona   mac80211: ignore ...
739

433f5bc1c   Johannes Berg   mac80211: move me...
740
741
742
743
744
  	spin_lock_bh(&sta->mesh->plink_lock);
  	sta->mesh->llid = mesh_get_new_llid(sdata);
  	if (sta->mesh->plink_state != NL80211_PLINK_LISTEN &&
  	    sta->mesh->plink_state != NL80211_PLINK_BLOCKED) {
  		spin_unlock_bh(&sta->mesh->plink_lock);
39886b618   Thomas Pedersen   mac80211: consoli...
745
  		return 0;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
746
  	}
433f5bc1c   Johannes Berg   mac80211: move me...
747
  	sta->mesh->plink_state = NL80211_PLINK_OPN_SNT;
453e66f24   Marco Porsch   mac80211: remove ...
748
  	mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
433f5bc1c   Johannes Berg   mac80211: move me...
749
  	spin_unlock_bh(&sta->mesh->plink_lock);
bdcbd8e0e   Johannes Berg   mac80211: clean u...
750
751
752
  	mpl_dbg(sdata,
  		"Mesh plink: starting establishment with %pM
  ",
0c68ae260   Johannes Berg   mac80211: convert...
753
  		sta->sta.addr);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
754

3f52b7e32   Marco Porsch   mac80211: mesh po...
755
  	/* set the non-peer mode to active during peering */
39886b618   Thomas Pedersen   mac80211: consoli...
756
  	changed = ieee80211_mps_local_status_update(sdata);
3f52b7e32   Marco Porsch   mac80211: mesh po...
757

a69bd8e60   Bob Copeland   mac80211: mesh: s...
758
  	mesh_plink_frame_tx(sdata, sta, WLAN_SP_MESH_PEERING_OPEN,
433f5bc1c   Johannes Berg   mac80211: move me...
759
  			    sta->sta.addr, sta->mesh->llid, 0, 0);
39886b618   Thomas Pedersen   mac80211: consoli...
760
  	return changed;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
761
  }
39886b618   Thomas Pedersen   mac80211: consoli...
762
  u32 mesh_plink_block(struct sta_info *sta)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
763
  {
df3238189   Marco Porsch   mac80211: fix unn...
764
  	u32 changed;
c93701976   John W. Linville   mac80211: avoid s...
765

433f5bc1c   Johannes Berg   mac80211: move me...
766
  	spin_lock_bh(&sta->mesh->plink_lock);
df3238189   Marco Porsch   mac80211: fix unn...
767
  	changed = __mesh_plink_deactivate(sta);
433f5bc1c   Johannes Berg   mac80211: move me...
768
769
  	sta->mesh->plink_state = NL80211_PLINK_BLOCKED;
  	spin_unlock_bh(&sta->mesh->plink_lock);
e596af827   Bob Copeland   mac80211: mesh: f...
770
  	mesh_path_flush_by_nexthop(sta);
c93701976   John W. Linville   mac80211: avoid s...
771

39886b618   Thomas Pedersen   mac80211: consoli...
772
  	return changed;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
773
  }
e76d67f03   Bob Copeland   mac80211: mesh: f...
774
775
776
777
778
  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;
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
779
780
  	u16 reason = (event == CLS_ACPT) ?
  		     WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
e76d67f03   Bob Copeland   mac80211: mesh: f...
781

433f5bc1c   Johannes Berg   mac80211: move me...
782
783
  	sta->mesh->reason = reason;
  	sta->mesh->plink_state = NL80211_PLINK_HOLDING;
272a9e261   Bob Copeland   mac80211: mesh_pl...
784
  	mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
e76d67f03   Bob Copeland   mac80211: mesh: f...
785
786
787
788
789
790
791
  }
  
  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;
433f5bc1c   Johannes Berg   mac80211: move me...
792
793
  	del_timer(&sta->mesh->plink_timer);
  	sta->mesh->plink_state = NL80211_PLINK_ESTAB;
e76d67f03   Bob Copeland   mac80211: mesh: f...
794
795
796
797
798
799
800
801
802
  	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
  ", sta->sta.addr);
  	ieee80211_mps_sta_status_update(sta);
  	changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode);
  	return changed;
  }
c7e678115   Thomas Pedersen   mac80211: factor ...
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
  /**
   * 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;
e596af827   Bob Copeland   mac80211: mesh: f...
818
  	bool flush = false;
c7e678115   Thomas Pedersen   mac80211: factor ...
819
820
821
  
  	mpl_dbg(sdata, "peer %pM in state %s got event %s
  ", sta->sta.addr,
433f5bc1c   Johannes Berg   mac80211: move me...
822
  		mplstates[sta->mesh->plink_state], mplevents[event]);
c7e678115   Thomas Pedersen   mac80211: factor ...
823

433f5bc1c   Johannes Berg   mac80211: move me...
824
825
  	spin_lock_bh(&sta->mesh->plink_lock);
  	switch (sta->mesh->plink_state) {
c7e678115   Thomas Pedersen   mac80211: factor ...
826
827
828
829
830
831
  	case NL80211_PLINK_LISTEN:
  		switch (event) {
  		case CLS_ACPT:
  			mesh_plink_fsm_restart(sta);
  			break;
  		case OPN_ACPT:
433f5bc1c   Johannes Berg   mac80211: move me...
832
833
  			sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
  			sta->mesh->llid = mesh_get_new_llid(sdata);
c7e678115   Thomas Pedersen   mac80211: factor ...
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
  			mesh_plink_timer_set(sta,
  					     mshcfg->dot11MeshRetryTimeout);
  
  			/* set the non-peer mode to active during peering */
  			changed |= ieee80211_mps_local_status_update(sdata);
  			action = WLAN_SP_MESH_PEERING_OPEN;
  			break;
  		default:
  			break;
  		}
  		break;
  	case NL80211_PLINK_OPN_SNT:
  		switch (event) {
  		case OPN_RJCT:
  		case CNF_RJCT:
  		case CLS_ACPT:
  			mesh_plink_close(sdata, sta, event);
  			action = WLAN_SP_MESH_PEERING_CLOSE;
  			break;
  		case OPN_ACPT:
  			/* retry timer is left untouched */
433f5bc1c   Johannes Berg   mac80211: move me...
855
  			sta->mesh->plink_state = NL80211_PLINK_OPN_RCVD;
c7e678115   Thomas Pedersen   mac80211: factor ...
856
857
858
  			action = WLAN_SP_MESH_PEERING_CONFIRM;
  			break;
  		case CNF_ACPT:
433f5bc1c   Johannes Berg   mac80211: move me...
859
  			sta->mesh->plink_state = NL80211_PLINK_CNF_RCVD;
2b470c39e   Bob Copeland   mac80211: remove ...
860
  			mod_plink_timer(sta, mshcfg->dot11MeshConfirmTimeout);
c7e678115   Thomas Pedersen   mac80211: factor ...
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
  			break;
  		default:
  			break;
  		}
  		break;
  	case NL80211_PLINK_OPN_RCVD:
  		switch (event) {
  		case OPN_RJCT:
  		case CNF_RJCT:
  		case CLS_ACPT:
  			mesh_plink_close(sdata, sta, event);
  			action = WLAN_SP_MESH_PEERING_CLOSE;
  			break;
  		case OPN_ACPT:
  			action = WLAN_SP_MESH_PEERING_CONFIRM;
  			break;
  		case CNF_ACPT:
  			changed |= mesh_plink_establish(sdata, sta);
  			break;
  		default:
  			break;
  		}
  		break;
  	case NL80211_PLINK_CNF_RCVD:
  		switch (event) {
  		case OPN_RJCT:
  		case CNF_RJCT:
  		case CLS_ACPT:
  			mesh_plink_close(sdata, sta, event);
  			action = WLAN_SP_MESH_PEERING_CLOSE;
  			break;
  		case OPN_ACPT:
  			changed |= mesh_plink_establish(sdata, sta);
  			action = WLAN_SP_MESH_PEERING_CONFIRM;
  			break;
  		default:
  			break;
  		}
  		break;
  	case NL80211_PLINK_ESTAB:
  		switch (event) {
  		case CLS_ACPT:
  			changed |= __mesh_plink_deactivate(sta);
  			changed |= mesh_set_ht_prot_mode(sdata);
  			changed |= mesh_set_short_slot_time(sdata);
  			mesh_plink_close(sdata, sta, event);
  			action = WLAN_SP_MESH_PEERING_CLOSE;
e596af827   Bob Copeland   mac80211: mesh: f...
908
  			flush = true;
c7e678115   Thomas Pedersen   mac80211: factor ...
909
910
911
912
913
914
915
916
917
918
919
  			break;
  		case OPN_ACPT:
  			action = WLAN_SP_MESH_PEERING_CONFIRM;
  			break;
  		default:
  			break;
  		}
  		break;
  	case NL80211_PLINK_HOLDING:
  		switch (event) {
  		case CLS_ACPT:
433f5bc1c   Johannes Berg   mac80211: move me...
920
  			del_timer(&sta->mesh->plink_timer);
c7e678115   Thomas Pedersen   mac80211: factor ...
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
  			mesh_plink_fsm_restart(sta);
  			break;
  		case OPN_ACPT:
  		case CNF_ACPT:
  		case OPN_RJCT:
  		case CNF_RJCT:
  			action = WLAN_SP_MESH_PEERING_CLOSE;
  			break;
  		default:
  			break;
  		}
  		break;
  	default:
  		/* should not get here, PLINK_BLOCKED is dealt with at the
  		 * beginning of the function
  		 */
  		break;
  	}
433f5bc1c   Johannes Berg   mac80211: move me...
939
  	spin_unlock_bh(&sta->mesh->plink_lock);
e596af827   Bob Copeland   mac80211: mesh: f...
940
941
  	if (flush)
  		mesh_path_flush_by_nexthop(sta);
c7e678115   Thomas Pedersen   mac80211: factor ...
942
  	if (action) {
a69bd8e60   Bob Copeland   mac80211: mesh: s...
943
  		mesh_plink_frame_tx(sdata, sta, action, sta->sta.addr,
433f5bc1c   Johannes Berg   mac80211: move me...
944
945
  				    sta->mesh->llid, sta->mesh->plid,
  				    sta->mesh->reason);
c7e678115   Thomas Pedersen   mac80211: factor ...
946
947
948
  
  		/* also send confirm in open case */
  		if (action == WLAN_SP_MESH_PEERING_OPEN) {
a69bd8e60   Bob Copeland   mac80211: mesh: s...
949
  			mesh_plink_frame_tx(sdata, sta,
c7e678115   Thomas Pedersen   mac80211: factor ...
950
  					    WLAN_SP_MESH_PEERING_CONFIRM,
433f5bc1c   Johannes Berg   mac80211: move me...
951
952
  					    sta->sta.addr, sta->mesh->llid,
  					    sta->mesh->plid, 0);
c7e678115   Thomas Pedersen   mac80211: factor ...
953
954
955
956
957
  		}
  	}
  
  	return changed;
  }
c99a89edb   Thomas Pedersen   mac80211: factor ...
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
  /*
   * 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,
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
976
  		     u16 llid, u16 plid)
c99a89edb   Thomas Pedersen   mac80211: factor ...
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
  {
  	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
  ");
  			goto out;
  		}
  		/* ftype == WLAN_SP_MESH_PEERING_OPEN */
  		if (!mesh_plink_free_count(sdata)) {
  			mpl_dbg(sdata, "Mesh plink error: no more free plinks
  ");
  			goto out;
  		}
b8631c003   Sunil Shahu   mac80211: mesh_pl...
1003
1004
1005
1006
  
  		/* new matching peer */
  		event = OPN_ACPT;
  		goto out;
c99a89edb   Thomas Pedersen   mac80211: factor ...
1007
1008
1009
1010
1011
1012
  	} else {
  		if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
  			mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer
  ");
  			goto out;
  		}
433f5bc1c   Johannes Berg   mac80211: move me...
1013
  		if (sta->mesh->plink_state == NL80211_PLINK_BLOCKED)
c99a89edb   Thomas Pedersen   mac80211: factor ...
1014
1015
  			goto out;
  	}
c99a89edb   Thomas Pedersen   mac80211: factor ...
1016
1017
1018
1019
1020
  	switch (ftype) {
  	case WLAN_SP_MESH_PEERING_OPEN:
  		if (!matches_local)
  			event = OPN_RJCT;
  		if (!mesh_plink_free_count(sdata) ||
433f5bc1c   Johannes Berg   mac80211: move me...
1021
  		    (sta->mesh->plid && sta->mesh->plid != plid))
c99a89edb   Thomas Pedersen   mac80211: factor ...
1022
1023
1024
1025
1026
1027
1028
1029
  			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) ||
433f5bc1c   Johannes Berg   mac80211: move me...
1030
1031
  		    sta->mesh->llid != llid ||
  		    (sta->mesh->plid && sta->mesh->plid != plid))
c99a89edb   Thomas Pedersen   mac80211: factor ...
1032
1033
1034
1035
1036
  			event = CNF_IGNR;
  		else
  			event = CNF_ACPT;
  		break;
  	case WLAN_SP_MESH_PEERING_CLOSE:
433f5bc1c   Johannes Berg   mac80211: move me...
1037
  		if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
c99a89edb   Thomas Pedersen   mac80211: factor ...
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
  			/* 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;
433f5bc1c   Johannes Berg   mac80211: move me...
1048
  		else if (sta->mesh->plid != plid)
c99a89edb   Thomas Pedersen   mac80211: factor ...
1049
  			event = CLS_IGNR;
433f5bc1c   Johannes Berg   mac80211: move me...
1050
  		else if (ie_len == 8 && sta->mesh->llid != llid)
c99a89edb   Thomas Pedersen   mac80211: factor ...
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
  			event = CLS_IGNR;
  		else
  			event = CLS_ACPT;
  		break;
  	default:
  		mpl_dbg(sdata, "Mesh plink: unknown frame subtype
  ");
  		break;
  	}
  
  out:
  	return event;
  }
05a23ae92   Thomas Pedersen   mac80211: factor ...
1064
1065
1066
  static void
  mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
  			 struct ieee80211_mgmt *mgmt,
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
1067
1068
  			 struct ieee802_11_elems *elems,
  			 struct ieee80211_rx_status *rx_status)
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1069
  {
05a23ae92   Thomas Pedersen   mac80211: factor ...
1070

c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1071
1072
  	struct sta_info *sta;
  	enum plink_event event;
54ef656b0   Thomas Pedersen   mac80211: update ...
1073
  	enum ieee80211_self_protected_actioncode ftype;
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
1074
  	u32 changed = 0;
c99a89edb   Thomas Pedersen   mac80211: factor ...
1075
  	u8 ie_len = elems->peering_len;
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
1076
  	u16 plid, llid = 0;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1077

05a23ae92   Thomas Pedersen   mac80211: factor ...
1078
  	if (!elems->peering) {
bdcbd8e0e   Johannes Berg   mac80211: clean u...
1079
1080
1081
  		mpl_dbg(sdata,
  			"Mesh plink: missing necessary peer link ie
  ");
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1082
1083
  		return;
  	}
bf7cd94dc   Johannes Berg   mac80211: clean u...
1084

05a23ae92   Thomas Pedersen   mac80211: factor ...
1085
  	if (elems->rsn_len &&
bf7cd94dc   Johannes Berg   mac80211: clean u...
1086
  	    sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
bdcbd8e0e   Johannes Berg   mac80211: clean u...
1087
1088
1089
  		mpl_dbg(sdata,
  			"Mesh plink: can't establish link with secure peer
  ");
5cff5e01e   Javier Cardona   mac80211: ignore ...
1090
1091
  		return;
  	}
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1092

8db098507   Thomas Pedersen   mac80211: update ...
1093
  	ftype = mgmt->u.action.u.self_prot.action_code;
8db098507   Thomas Pedersen   mac80211: update ...
1094
1095
1096
1097
  	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)) {
bdcbd8e0e   Johannes Berg   mac80211: clean u...
1098
1099
1100
1101
  		mpl_dbg(sdata,
  			"Mesh plink: incorrect plink ie length %d %d
  ",
  			ftype, ie_len);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1102
1103
  		return;
  	}
54ef656b0   Thomas Pedersen   mac80211: update ...
1104
  	if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
05a23ae92   Thomas Pedersen   mac80211: factor ...
1105
  	    (!elems->mesh_id || !elems->mesh_config)) {
bdcbd8e0e   Johannes Berg   mac80211: clean u...
1106
1107
  		mpl_dbg(sdata, "Mesh plink: missing necessary ie
  ");
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1108
1109
1110
1111
1112
  		return;
  	}
  	/* Note the lines below are correct, the llid in the frame is the plid
  	 * from the point of view of this host.
  	 */
f8134fed8   Bob Copeland   mac80211: mesh_pl...
1113
  	plid = get_unaligned_le16(PLINK_GET_LLID(elems->peering));
54ef656b0   Thomas Pedersen   mac80211: update ...
1114
  	if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
f8134fed8   Bob Copeland   mac80211: mesh_pl...
1115
1116
  	    (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
  		llid = get_unaligned_le16(PLINK_GET_PLID(elems->peering));
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1117

296fcba3b   Thomas Pedersen   mac80211: clean u...
1118
  	/* WARNING: Only for sta pointer, is dropped & re-acquired */
d0709a651   Johannes Berg   mac80211: RCU-ify...
1119
  	rcu_read_lock();
abe60632f   Johannes Berg   mac80211: make st...
1120
  	sta = sta_info_get(sdata, mgmt->sa);
32cb05bfe   Bob Copeland   mac80211: mesh_pl...
1121

553351378   Ashok Nagarajan   {nl,cfg,mac}80211...
1122
  	if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
36c9bb29b   Bob Copeland   mac80211: mesh: r...
1123
  	    !rssi_threshold_check(sdata, sta)) {
bdcbd8e0e   Johannes Berg   mac80211: clean u...
1124
1125
  		mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold
  ",
3d4f96997   Ashok Nagarajan   mac80211: Fix pot...
1126
  			mgmt->sa);
fc10302ef   Thomas Pedersen   mac80211: consoli...
1127
  		goto unlock_rcu;
553351378   Ashok Nagarajan   {nl,cfg,mac}80211...
1128
  	}
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1129
  	/* Now we will figure out the appropriate event... */
c99a89edb   Thomas Pedersen   mac80211: factor ...
1130
  	event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid);
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
1131
1132
  
  	if (event == OPN_ACPT) {
296fcba3b   Thomas Pedersen   mac80211: clean u...
1133
  		rcu_read_unlock();
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
1134
  		/* allocate sta entry if necessary and update info */
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
1135
  		sta = mesh_sta_info_get(sdata, mgmt->sa, elems, rx_status);
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
1136
  		if (!sta) {
bdcbd8e0e   Johannes Berg   mac80211: clean u...
1137
1138
  			mpl_dbg(sdata, "Mesh plink: failed to init peer!
  ");
fc10302ef   Thomas Pedersen   mac80211: consoli...
1139
  			goto unlock_rcu;
54ab1ffb6   Thomas Pedersen   mac80211: refacto...
1140
  		}
433f5bc1c   Johannes Berg   mac80211: move me...
1141
  		sta->mesh->plid = plid;
c99a89edb   Thomas Pedersen   mac80211: factor ...
1142
  	} else if (!sta && event == OPN_RJCT) {
a69bd8e60   Bob Copeland   mac80211: mesh: s...
1143
  		mesh_plink_frame_tx(sdata, NULL, WLAN_SP_MESH_PEERING_CLOSE,
c99a89edb   Thomas Pedersen   mac80211: factor ...
1144
  				    mgmt->sa, 0, plid,
6f101ef04   Chun-Yeow Yeoh   mac80211: use put...
1145
  				    WLAN_REASON_MESH_CONFIG);
c99a89edb   Thomas Pedersen   mac80211: factor ...
1146
1147
1148
1149
  		goto unlock_rcu;
  	} else if (!sta || event == PLINK_UNDEFINED) {
  		/* something went wrong */
  		goto unlock_rcu;
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1150
  	}
a69bd8e60   Bob Copeland   mac80211: mesh: s...
1151
1152
  	if (event == CNF_ACPT) {
  		/* 802.11-2012 13.3.7.2 - update plid on CNF if not set */
0e0060fcf   Bob Copeland   mac80211: select ...
1153
  		if (!sta->mesh->plid)
a69bd8e60   Bob Copeland   mac80211: mesh: s...
1154
  			sta->mesh->plid = plid;
a69bd8e60   Bob Copeland   mac80211: mesh: s...
1155
1156
1157
  
  		sta->mesh->aid = get_unaligned_le16(PLINK_CNF_AID(mgmt));
  	}
6c6fa4964   Bob Copeland   mac80211: mesh_pl...
1158

c7e678115   Thomas Pedersen   mac80211: factor ...
1159
  	changed |= mesh_plink_fsm(sdata, sta, event);
d0709a651   Johannes Berg   mac80211: RCU-ify...
1160

fc10302ef   Thomas Pedersen   mac80211: consoli...
1161
  unlock_rcu:
d0709a651   Johannes Berg   mac80211: RCU-ify...
1162
  	rcu_read_unlock();
57aac7c51   Ashok Nagarajan   mac80211: Impleme...
1163

ecccd072b   Thomas Pedersen   mac80211: fix mes...
1164
  	if (changed)
2b5e19677   Thomas Pedersen   mac80211: cache m...
1165
  		ieee80211_mbss_info_change_notify(sdata, changed);
c3896d2ca   Luis Carlos Cobo   mac80211: mesh pe...
1166
  }
05a23ae92   Thomas Pedersen   mac80211: factor ...
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
  
  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
  ");
  		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;
b3e7de873   Bob Copeland   mac80211: add mis...
1197
1198
1199
  
  		if (baselen > len)
  			return;
05a23ae92   Thomas Pedersen   mac80211: factor ...
1200
  	}
4abb52a46   Sara Sharon   mac80211: pass bs...
1201
1202
  	ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems,
  			       mgmt->bssid, NULL);
ecbc12ad6   Bob Copeland   {nl,mac}80211: ad...
1203
  	mesh_process_plink_frame(sdata, mgmt, &elems, rx_status);
05a23ae92   Thomas Pedersen   mac80211: factor ...
1204
  }