mesh_plink.c 18.9 KB
Newer Older
1
2
3
4
5
6
7
8
/*
 * Copyright (c) 2008 open80211s Ltd.
 * Author:     Luis Carlos Cobo <luisca@cozybit.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
Johannes Berg's avatar
Johannes Berg committed
9
10
#include <linux/kernel.h>
#include <linux/random.h>
11
#include "ieee80211_i.h"
Johannes Berg's avatar
Johannes Berg committed
12
#include "rate.h"
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "mesh.h"

#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
#define mpl_dbg(fmt, args...)	printk(KERN_DEBUG fmt, ##args)
#else
#define mpl_dbg(fmt, args...)	do { (void)(0); } while (0)
#endif

#define PLINK_GET_FRAME_SUBTYPE(p) (p)
#define PLINK_GET_LLID(p) (p + 1)
#define PLINK_GET_PLID(p) (p + 3)

#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
				jiffies + HZ * t / 1000))

/* Peer link cancel reasons, all subject to ANA approval */
#define MESH_LINK_CANCELLED			2
#define MESH_MAX_NEIGHBORS			3
#define MESH_CAPABILITY_POLICY_VIOLATION	4
#define MESH_CLOSE_RCVD				5
#define MESH_MAX_RETRIES			6
#define MESH_CONFIRM_TIMEOUT			7
#define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS	8
#define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE	9
#define MESH_SECURITY_FAILED_VERIFICATION	10

39
40
41
42
43
#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries)
#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout)
#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

enum plink_frame_type {
	PLINK_OPEN = 0,
	PLINK_CONFIRM,
	PLINK_CLOSE
};

enum plink_event {
	PLINK_UNDEFINED,
	OPN_ACPT,
	OPN_RJCT,
	OPN_IGNR,
	CNF_ACPT,
	CNF_RJCT,
	CNF_IGNR,
	CLS_ACPT,
	CLS_IGNR
};

static inline
void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{
66
	atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
67
	mesh_accept_plinks_update(sdata);
68
69
70
71
72
}

static inline
void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{
73
	atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
74
	mesh_accept_plinks_update(sdata);
75
76
77
78
79
80
81
}

/**
 * mesh_plink_fsm_restart - restart a mesh peer link finite state machine
 *
 * @sta: mes peer link to restart
 *
82
 * Locking: this function must be called holding sta->lock
83
84
85
 */
static inline void mesh_plink_fsm_restart(struct sta_info *sta)
{
86
	sta->plink_state = PLINK_LISTEN;
87
88
	sta->llid = sta->plid = sta->reason = 0;
	sta->plink_retries = 0;
89
90
}

91
92
93
94
/*
 * NOTE: This is just an alias for sta_info_alloc(), see notes
 *       on it in the lifecycle management section!
 */
95
96
static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata,
					 u8 *hw_addr, u64 rates)
97
{
98
	struct ieee80211_local *local = sdata->local;
99
100
101
	struct sta_info *sta;

	if (local->num_sta >= MESH_MAX_PLINKS)
Johannes Berg's avatar
Johannes Berg committed
102
		return NULL;
103

104
	sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC);
Johannes Berg's avatar
Johannes Berg committed
105
106
	if (!sta)
		return NULL;
107

108
	sta->flags = WLAN_STA_AUTHORIZED;
109
	sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
110
	rate_control_rate_init(sta);
111
112
113
114
115

	return sta;
}

/**
116
 * mesh_plink_deactivate - deactivate mesh peer link
117
118
119
120
121
 *
 * @sta: mesh peer link to deactivate
 *
 * All mesh paths with this peer as next hop will be flushed
 *
122
 * Locking: the caller must hold sta->lock
123
 */
Johannes Berg's avatar
Johannes Berg committed
124
static void __mesh_plink_deactivate(struct sta_info *sta)
125
{
126
127
	struct ieee80211_sub_if_data *sdata = sta->sdata;

128
	if (sta->plink_state == PLINK_ESTAB)
129
		mesh_plink_dec_estab_count(sdata);
130
	sta->plink_state = PLINK_BLOCKED;
131
132
133
	mesh_path_flush_by_nexthop(sta);
}

Johannes Berg's avatar
Johannes Berg committed
134
135
136
137
138
139
140
141
142
/**
 * __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
 */
void mesh_plink_deactivate(struct sta_info *sta)
{
143
	spin_lock_bh(&sta->lock);
Johannes Berg's avatar
Johannes Berg committed
144
	__mesh_plink_deactivate(sta);
145
	spin_unlock_bh(&sta->lock);
Johannes Berg's avatar
Johannes Berg committed
146
147
}

148
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
149
150
		enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid,
		__le16 reason) {
151
	struct ieee80211_local *local = sdata->local;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
	struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
	struct ieee80211_mgmt *mgmt;
	bool include_plid = false;
	u8 *pos;
	int ie_len;

	if (!skb)
		return -1;
	skb_reserve(skb, local->hw.extra_tx_headroom);
	/* 25 is the size of the common mgmt part (24) plus the size of the
	 * common action part (1)
	 */
	mgmt = (struct ieee80211_mgmt *)
		skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action));
	memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action));
167
168
	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
					  IEEE80211_STYPE_ACTION);
169
	memcpy(mgmt->da, da, ETH_ALEN);
170
	memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
171
172
173
174
175
176
177
178
179
180
181
182
183
	/* BSSID is left zeroed, wildcard value */
	mgmt->u.action.category = PLINK_CATEGORY;
	mgmt->u.action.u.plink_action.action_code = action;

	if (action == PLINK_CLOSE)
		mgmt->u.action.u.plink_action.aux = reason;
	else {
		mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0);
		if (action == PLINK_CONFIRM) {
			pos = skb_put(skb, 4);
			/* two-byte status code followed by two-byte AID */
			memset(pos, 0, 4);
		}
184
		mesh_mgmt_ies_add(skb, sdata);
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
	}

	/* Add Peer Link Management element */
	switch (action) {
	case PLINK_OPEN:
		ie_len = 3;
		break;
	case PLINK_CONFIRM:
		ie_len = 5;
		include_plid = true;
		break;
	case PLINK_CLOSE:
	default:
		if (!plid)
			ie_len = 5;
		else {
			ie_len = 7;
			include_plid = true;
		}
		break;
	}

	pos = skb_put(skb, 2 + ie_len);
	*pos++ = WLAN_EID_PEER_LINK;
	*pos++ = ie_len;
	*pos++ = action;
	memcpy(pos, &llid, 2);
	if (include_plid) {
		pos += 2;
		memcpy(pos, &plid, 2);
	}
	if (action == PLINK_CLOSE) {
		pos += 2;
		memcpy(pos, &reason, 2);
	}

221
	ieee80211_tx_skb(sdata, skb, 1);
222
223
224
	return 0;
}

225
void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct ieee80211_sub_if_data *sdata,
226
227
			   bool peer_accepting_plinks)
{
228
	struct ieee80211_local *local = sdata->local;
229
230
	struct sta_info *sta;

231
232
	rcu_read_lock();

233
234
	sta = sta_info_get(local, hw_addr);
	if (!sta) {
235
		sta = mesh_plink_alloc(sdata, hw_addr, rates);
Johannes Berg's avatar
Johannes Berg committed
236
237
238
239
240
		if (!sta) {
			rcu_read_unlock();
			return;
		}
		if (sta_info_insert(sta)) {
241
			rcu_read_unlock();
242
			return;
243
		}
244
245
246
	}

	sta->last_rx = jiffies;
247
	sta->sta.supp_rates[local->hw.conf.channel->band] = rates;
248
	if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN &&
249
250
			sdata->u.mesh.accepting_plinks &&
			sdata->u.mesh.mshcfg.auto_open_plinks)
251
252
		mesh_plink_open(sta);

253
	rcu_read_unlock();
254
255
256
257
258
259
260
261
}

static void mesh_plink_timer(unsigned long data)
{
	struct sta_info *sta;
	__le16 llid, plid, reason;
	struct ieee80211_sub_if_data *sdata;

262
263
264
265
266
	/*
	 * 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.)
	 */
267
268
	sta = (struct sta_info *) data;

269
	spin_lock_bh(&sta->lock);
270
271
	if (sta->ignore_plink_timer) {
		sta->ignore_plink_timer = false;
272
		spin_unlock_bh(&sta->lock);
273
274
		return;
	}
275
276
	mpl_dbg("Mesh plink timer for %pM fired on state %d\n",
		sta->sta.addr, sta->plink_state);
277
278
279
	reason = 0;
	llid = sta->llid;
	plid = sta->plid;
280
	sdata = sta->sdata;
281
282

	switch (sta->plink_state) {
283
284
	case PLINK_OPN_RCVD:
	case PLINK_OPN_SNT:
285
286
287
		/* retry timer */
		if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
			u32 rand;
288
289
290
			mpl_dbg("Mesh plink for %pM (retry, timeout): %d %d\n",
				sta->sta.addr, sta->plink_retries,
				sta->plink_timeout);
291
292
293
294
			get_random_bytes(&rand, sizeof(u32));
			sta->plink_timeout = sta->plink_timeout +
					     rand % sta->plink_timeout;
			++sta->plink_retries;
295
			mod_plink_timer(sta, sta->plink_timeout);
296
			spin_unlock_bh(&sta->lock);
297
			mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid,
298
299
300
301
302
					    0, 0);
			break;
		}
		reason = cpu_to_le16(MESH_MAX_RETRIES);
		/* fall through on else */
303
	case PLINK_CNF_RCVD:
304
305
306
		/* confirm timer */
		if (!reason)
			reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT);
307
		sta->plink_state = PLINK_HOLDING;
308
		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
309
		spin_unlock_bh(&sta->lock);
310
		mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid,
311
312
				    reason);
		break;
313
	case PLINK_HOLDING:
314
		/* holding timer */
315
		del_timer(&sta->plink_timer);
316
		mesh_plink_fsm_restart(sta);
317
		spin_unlock_bh(&sta->lock);
318
319
		break;
	default:
320
		spin_unlock_bh(&sta->lock);
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
		break;
	}
}

static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
{
	sta->plink_timer.expires = jiffies + (HZ * timeout / 1000);
	sta->plink_timer.data = (unsigned long) sta;
	sta->plink_timer.function = mesh_plink_timer;
	sta->plink_timeout = timeout;
	add_timer(&sta->plink_timer);
}

int mesh_plink_open(struct sta_info *sta)
{
	__le16 llid;
337
	struct ieee80211_sub_if_data *sdata = sta->sdata;
338

339
	spin_lock_bh(&sta->lock);
340
341
	get_random_bytes(&llid, 2);
	sta->llid = llid;
342
	if (sta->plink_state != PLINK_LISTEN) {
343
		spin_unlock_bh(&sta->lock);
344
345
		return -EBUSY;
	}
346
	sta->plink_state = PLINK_OPN_SNT;
347
	mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
348
	spin_unlock_bh(&sta->lock);
349
350
	mpl_dbg("Mesh plink: starting establishment with %pM\n",
		sta->sta.addr);
351

352
	return mesh_plink_frame_tx(sdata, PLINK_OPEN,
353
				   sta->sta.addr, llid, 0, 0);
354
355
356
357
}

void mesh_plink_block(struct sta_info *sta)
{
358
	spin_lock_bh(&sta->lock);
Johannes Berg's avatar
Johannes Berg committed
359
	__mesh_plink_deactivate(sta);
360
	sta->plink_state = PLINK_BLOCKED;
361
	spin_unlock_bh(&sta->lock);
362
363
364
365
}

int mesh_plink_close(struct sta_info *sta)
{
366
	struct ieee80211_sub_if_data *sdata = sta->sdata;
367
	__le16 llid, plid, reason;
368

369
	mpl_dbg("Mesh plink: closing link with %pM\n", sta->sta.addr);
370
	spin_lock_bh(&sta->lock);
371
372
373
	sta->reason = cpu_to_le16(MESH_LINK_CANCELLED);
	reason = sta->reason;

374
375
	if (sta->plink_state == PLINK_LISTEN ||
	    sta->plink_state == PLINK_BLOCKED) {
376
		mesh_plink_fsm_restart(sta);
377
		spin_unlock_bh(&sta->lock);
378
		return 0;
379
	} else if (sta->plink_state == PLINK_ESTAB) {
Johannes Berg's avatar
Johannes Berg committed
380
		__mesh_plink_deactivate(sta);
381
		/* The timer should not be running */
382
		mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
383
384
385
	} else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)))
		sta->ignore_plink_timer = true;

386
	sta->plink_state = PLINK_HOLDING;
387
388
	llid = sta->llid;
	plid = sta->plid;
389
	spin_unlock_bh(&sta->lock);
390
	mesh_plink_frame_tx(sta->sdata, PLINK_CLOSE, sta->sta.addr, llid,
391
			    plid, reason);
392
393
394
	return 0;
}

395
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
396
397
			 size_t len, struct ieee80211_rx_status *rx_status)
{
398
	struct ieee80211_local *local = sdata->local;
399
400
401
402
403
404
405
406
407
	struct ieee802_11_elems elems;
	struct sta_info *sta;
	enum plink_event event;
	enum plink_frame_type ftype;
	size_t baselen;
	u8 ie_len;
	u8 *baseaddr;
	__le16 plid, llid, reason;

408
409
410
411
	/* need action_code, aux */
	if (len < IEEE80211_MIN_ACTION_SIZE + 3)
		return;

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
	if (is_multicast_ether_addr(mgmt->da)) {
		mpl_dbg("Mesh plink: ignore frame from multicast address");
		return;
	}

	baseaddr = mgmt->u.action.u.plink_action.variable;
	baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt;
	if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) {
		baseaddr += 4;
		baselen -= 4;
	}
	ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
	if (!elems.peer_link) {
		mpl_dbg("Mesh plink: missing necessary peer link ie\n");
		return;
	}

	ftype = *((u8 *)PLINK_GET_FRAME_SUBTYPE(elems.peer_link));
	ie_len = elems.peer_link_len;
	if ((ftype == PLINK_OPEN && ie_len != 3) ||
	    (ftype == PLINK_CONFIRM && ie_len != 5) ||
	    (ftype == PLINK_CLOSE && ie_len != 5 && ie_len != 7)) {
		mpl_dbg("Mesh plink: incorrect plink ie length\n");
		return;
	}

	if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) {
		mpl_dbg("Mesh plink: missing necessary ie\n");
		return;
	}
	/* Note the lines below are correct, the llid in the frame is the plid
	 * from the point of view of this host.
	 */
	memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2);
	if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7))
		memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2);

449
450
	rcu_read_lock();

451
452
453
	sta = sta_info_get(local, mgmt->sa);
	if (!sta && ftype != PLINK_OPEN) {
		mpl_dbg("Mesh plink: cls or cnf from unknown peer\n");
454
		rcu_read_unlock();
455
456
457
		return;
	}

458
	if (sta && sta->plink_state == PLINK_BLOCKED) {
459
		rcu_read_unlock();
460
461
462
463
464
		return;
	}

	/* Now we will figure out the appropriate event... */
	event = PLINK_UNDEFINED;
465
	if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) {
466
467
468
469
470
471
472
473
474
475
476
		switch (ftype) {
		case PLINK_OPEN:
			event = OPN_RJCT;
			break;
		case PLINK_CONFIRM:
			event = CNF_RJCT;
			break;
		case PLINK_CLOSE:
			/* avoid warning */
			break;
		}
477
		spin_lock_bh(&sta->lock);
478
479
480
481
482
	} else if (!sta) {
		/* ftype == PLINK_OPEN */
		u64 rates;
		if (!mesh_plink_free_count(sdata)) {
			mpl_dbg("Mesh plink error: no more free plinks\n");
483
			rcu_read_unlock();
484
485
486
487
			return;
		}

		rates = ieee80211_sta_get_rates(local, &elems, rx_status->band);
488
		sta = mesh_plink_alloc(sdata, mgmt->sa, rates);
Johannes Berg's avatar
Johannes Berg committed
489
		if (!sta) {
490
			mpl_dbg("Mesh plink error: plink table full\n");
491
			rcu_read_unlock();
492
493
			return;
		}
Johannes Berg's avatar
Johannes Berg committed
494
495
496
497
		if (sta_info_insert(sta)) {
			rcu_read_unlock();
			return;
		}
498
		event = OPN_ACPT;
499
		spin_lock_bh(&sta->lock);
500
	} else {
501
		spin_lock_bh(&sta->lock);
502
503
504
		switch (ftype) {
		case PLINK_OPEN:
			if (!mesh_plink_free_count(sdata) ||
505
			    (sta->plid && sta->plid != plid))
506
507
508
509
510
511
				event = OPN_IGNR;
			else
				event = OPN_ACPT;
			break;
		case PLINK_CONFIRM:
			if (!mesh_plink_free_count(sdata) ||
512
			    (sta->llid != llid || sta->plid != plid))
513
514
515
516
517
				event = CNF_IGNR;
			else
				event = CNF_ACPT;
			break;
		case PLINK_CLOSE:
518
			if (sta->plink_state == PLINK_ESTAB)
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
				/* Do not check for llid or plid. This does not
				 * follow the standard but since multiple plinks
				 * per sta are not supported, it is necessary in
				 * order to avoid a livelock when MP A sees an
				 * establish peer link to MP B but MP B does not
				 * see it. This can be caused by a timeout in
				 * B's peer link establishment or B beign
				 * restarted.
				 */
				event = CLS_ACPT;
			else if (sta->plid != plid)
				event = CLS_IGNR;
			else if (ie_len == 7 && sta->llid != llid)
				event = CLS_IGNR;
			else
				event = CLS_ACPT;
			break;
		default:
			mpl_dbg("Mesh plink: unknown frame subtype\n");
538
			spin_unlock_bh(&sta->lock);
539
			rcu_read_unlock();
540
541
542
543
			return;
		}
	}

544
545
546
547
	mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %d %d %d %d\n",
		mgmt->sa, sta->plink_state,
		le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
		event);
548
549
550
	reason = 0;
	switch (sta->plink_state) {
		/* spin_unlock as soon as state is updated at each case */
551
	case PLINK_LISTEN:
552
553
554
		switch (event) {
		case CLS_ACPT:
			mesh_plink_fsm_restart(sta);
555
			spin_unlock_bh(&sta->lock);
556
557
			break;
		case OPN_ACPT:
558
			sta->plink_state = PLINK_OPN_RCVD;
559
560
561
562
			sta->plid = plid;
			get_random_bytes(&llid, 2);
			sta->llid = llid;
			mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
563
			spin_unlock_bh(&sta->lock);
564
			mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid,
565
					    0, 0);
566
			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr,
567
568
569
					    llid, plid, 0);
			break;
		default:
570
			spin_unlock_bh(&sta->lock);
571
572
573
574
			break;
		}
		break;

575
	case PLINK_OPN_SNT:
576
577
578
579
580
581
582
583
		switch (event) {
		case OPN_RJCT:
		case CNF_RJCT:
			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
		case CLS_ACPT:
			if (!reason)
				reason = cpu_to_le16(MESH_CLOSE_RCVD);
			sta->reason = reason;
584
			sta->plink_state = PLINK_HOLDING;
585
586
587
588
589
			if (!mod_plink_timer(sta,
					     dot11MeshHoldingTimeout(sdata)))
				sta->ignore_plink_timer = true;

			llid = sta->llid;
590
			spin_unlock_bh(&sta->lock);
591
			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
592
593
594
595
					    plid, reason);
			break;
		case OPN_ACPT:
			/* retry timer is left untouched */
596
			sta->plink_state = PLINK_OPN_RCVD;
597
598
			sta->plid = plid;
			llid = sta->llid;
599
			spin_unlock_bh(&sta->lock);
600
			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
601
602
603
					    plid, 0);
			break;
		case CNF_ACPT:
604
			sta->plink_state = PLINK_CNF_RCVD;
605
606
607
608
			if (!mod_plink_timer(sta,
					     dot11MeshConfirmTimeout(sdata)))
				sta->ignore_plink_timer = true;

609
			spin_unlock_bh(&sta->lock);
610
611
			break;
		default:
612
			spin_unlock_bh(&sta->lock);
613
614
615
616
			break;
		}
		break;

617
	case PLINK_OPN_RCVD:
618
619
620
621
622
623
624
625
		switch (event) {
		case OPN_RJCT:
		case CNF_RJCT:
			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
		case CLS_ACPT:
			if (!reason)
				reason = cpu_to_le16(MESH_CLOSE_RCVD);
			sta->reason = reason;
626
			sta->plink_state = PLINK_HOLDING;
627
628
629
630
631
			if (!mod_plink_timer(sta,
					     dot11MeshHoldingTimeout(sdata)))
				sta->ignore_plink_timer = true;

			llid = sta->llid;
632
			spin_unlock_bh(&sta->lock);
633
			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
634
635
636
637
					    plid, reason);
			break;
		case OPN_ACPT:
			llid = sta->llid;
638
			spin_unlock_bh(&sta->lock);
639
			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
640
641
642
					    plid, 0);
			break;
		case CNF_ACPT:
643
			del_timer(&sta->plink_timer);
644
			sta->plink_state = PLINK_ESTAB;
645
			mesh_plink_inc_estab_count(sdata);
646
			spin_unlock_bh(&sta->lock);
647
648
			mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
				sta->sta.addr);
649
650
			break;
		default:
651
			spin_unlock_bh(&sta->lock);
652
653
654
655
			break;
		}
		break;

656
	case PLINK_CNF_RCVD:
657
658
659
660
661
662
663
664
		switch (event) {
		case OPN_RJCT:
		case CNF_RJCT:
			reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION);
		case CLS_ACPT:
			if (!reason)
				reason = cpu_to_le16(MESH_CLOSE_RCVD);
			sta->reason = reason;
665
			sta->plink_state = PLINK_HOLDING;
666
667
668
669
670
			if (!mod_plink_timer(sta,
					     dot11MeshHoldingTimeout(sdata)))
				sta->ignore_plink_timer = true;

			llid = sta->llid;
671
			spin_unlock_bh(&sta->lock);
672
			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
673
					    plid, reason);
674
			break;
675
		case OPN_ACPT:
676
			del_timer(&sta->plink_timer);
677
			sta->plink_state = PLINK_ESTAB;
678
			mesh_plink_inc_estab_count(sdata);
679
			spin_unlock_bh(&sta->lock);
680
681
			mpl_dbg("Mesh plink with %pM ESTABLISHED\n",
				sta->sta.addr);
682
			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
683
684
685
					    plid, 0);
			break;
		default:
686
			spin_unlock_bh(&sta->lock);
687
688
689
690
			break;
		}
		break;

691
	case PLINK_ESTAB:
692
693
694
695
		switch (event) {
		case CLS_ACPT:
			reason = cpu_to_le16(MESH_CLOSE_RCVD);
			sta->reason = reason;
Johannes Berg's avatar
Johannes Berg committed
696
			__mesh_plink_deactivate(sta);
697
			sta->plink_state = PLINK_HOLDING;
698
			llid = sta->llid;
699
			mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
700
			spin_unlock_bh(&sta->lock);
701
			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid,
702
703
704
705
					    plid, reason);
			break;
		case OPN_ACPT:
			llid = sta->llid;
706
			spin_unlock_bh(&sta->lock);
707
			mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid,
708
709
710
					    plid, 0);
			break;
		default:
711
			spin_unlock_bh(&sta->lock);
712
713
714
			break;
		}
		break;
715
	case PLINK_HOLDING:
716
717
		switch (event) {
		case CLS_ACPT:
718
			if (del_timer(&sta->plink_timer))
719
720
				sta->ignore_plink_timer = 1;
			mesh_plink_fsm_restart(sta);
721
			spin_unlock_bh(&sta->lock);
722
723
724
725
726
727
728
			break;
		case OPN_ACPT:
		case CNF_ACPT:
		case OPN_RJCT:
		case CNF_RJCT:
			llid = sta->llid;
			reason = sta->reason;
729
			spin_unlock_bh(&sta->lock);
730
731
			mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr,
					    llid, plid, reason);
732
733
			break;
		default:
734
			spin_unlock_bh(&sta->lock);
735
736
737
		}
		break;
	default:
738
739
		/* should not get here, PLINK_BLOCKED is dealt with at the
		 * beggining of the function
740
		 */
741
		spin_unlock_bh(&sta->lock);
742
743
		break;
	}
744
745

	rcu_read_unlock();
746
}