diff options
Diffstat (limited to 'net/mac80211/mesh_plink.c')
-rw-r--r-- | net/mac80211/mesh_plink.c | 709 |
1 files changed, 368 insertions, 341 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4301aa5aa227..e8f60aa2e848 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -19,12 +19,6 @@ | |||
19 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ | 19 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ |
20 | jiffies + HZ * t / 1000)) | 20 | jiffies + HZ * t / 1000)) |
21 | 21 | ||
22 | /* We only need a valid sta if user configured a minimum rssi_threshold. */ | ||
23 | #define rssi_threshold_check(sta, sdata) \ | ||
24 | (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ | ||
25 | (sta && (s8) -ewma_read(&sta->avg_signal) > \ | ||
26 | sdata->u.mesh.mshcfg.rssi_threshold)) | ||
27 | |||
28 | enum plink_event { | 22 | enum plink_event { |
29 | PLINK_UNDEFINED, | 23 | PLINK_UNDEFINED, |
30 | OPN_ACPT, | 24 | OPN_ACPT, |
@@ -61,7 +55,17 @@ static const char * const mplevents[] = { | |||
61 | 55 | ||
62 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | 56 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, |
63 | enum ieee80211_self_protected_actioncode action, | 57 | enum ieee80211_self_protected_actioncode action, |
64 | u8 *da, __le16 llid, __le16 plid, __le16 reason); | 58 | u8 *da, u16 llid, u16 plid, u16 reason); |
59 | |||
60 | |||
61 | /* We only need a valid sta if user configured a minimum rssi_threshold. */ | ||
62 | static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata, | ||
63 | struct sta_info *sta) | ||
64 | { | ||
65 | s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold; | ||
66 | return rssi_threshold == 0 || | ||
67 | (sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold); | ||
68 | } | ||
65 | 69 | ||
66 | /** | 70 | /** |
67 | * mesh_plink_fsm_restart - restart a mesh peer link finite state machine | 71 | * mesh_plink_fsm_restart - restart a mesh peer link finite state machine |
@@ -242,7 +246,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta) | |||
242 | 246 | ||
243 | spin_lock_bh(&sta->lock); | 247 | spin_lock_bh(&sta->lock); |
244 | changed = __mesh_plink_deactivate(sta); | 248 | changed = __mesh_plink_deactivate(sta); |
245 | sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); | 249 | sta->reason = WLAN_REASON_MESH_PEER_CANCELED; |
246 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | 250 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, |
247 | sta->sta.addr, sta->llid, sta->plid, | 251 | sta->sta.addr, sta->llid, sta->plid, |
248 | sta->reason); | 252 | sta->reason); |
@@ -253,7 +257,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta) | |||
253 | 257 | ||
254 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | 258 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, |
255 | enum ieee80211_self_protected_actioncode action, | 259 | enum ieee80211_self_protected_actioncode action, |
256 | u8 *da, __le16 llid, __le16 plid, __le16 reason) | 260 | u8 *da, u16 llid, u16 plid, u16 reason) |
257 | { | 261 | { |
258 | struct ieee80211_local *local = sdata->local; | 262 | struct ieee80211_local *local = sdata->local; |
259 | struct sk_buff *skb; | 263 | struct sk_buff *skb; |
@@ -279,7 +283,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | |||
279 | 2 + 8 + /* peering IE */ | 283 | 2 + 8 + /* peering IE */ |
280 | sdata->u.mesh.ie_len); | 284 | sdata->u.mesh.ie_len); |
281 | if (!skb) | 285 | if (!skb) |
282 | return -1; | 286 | return err; |
283 | info = IEEE80211_SKB_CB(skb); | 287 | info = IEEE80211_SKB_CB(skb); |
284 | skb_reserve(skb, local->tx_headroom); | 288 | skb_reserve(skb, local->tx_headroom); |
285 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); | 289 | mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); |
@@ -301,7 +305,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | |||
301 | if (action == WLAN_SP_MESH_PEERING_CONFIRM) { | 305 | if (action == WLAN_SP_MESH_PEERING_CONFIRM) { |
302 | /* AID */ | 306 | /* AID */ |
303 | pos = skb_put(skb, 2); | 307 | pos = skb_put(skb, 2); |
304 | memcpy(pos + 2, &plid, 2); | 308 | put_unaligned_le16(plid, pos + 2); |
305 | } | 309 | } |
306 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || | 310 | if (ieee80211_add_srates_ie(sdata, skb, true, band) || |
307 | ieee80211_add_ext_srates_ie(sdata, skb, true, band) || | 311 | ieee80211_add_ext_srates_ie(sdata, skb, true, band) || |
@@ -343,14 +347,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | |||
343 | *pos++ = ie_len; | 347 | *pos++ = ie_len; |
344 | memcpy(pos, &peering_proto, 2); | 348 | memcpy(pos, &peering_proto, 2); |
345 | pos += 2; | 349 | pos += 2; |
346 | memcpy(pos, &llid, 2); | 350 | put_unaligned_le16(llid, pos); |
347 | pos += 2; | 351 | pos += 2; |
348 | if (include_plid) { | 352 | if (include_plid) { |
349 | memcpy(pos, &plid, 2); | 353 | put_unaligned_le16(plid, pos); |
350 | pos += 2; | 354 | pos += 2; |
351 | } | 355 | } |
352 | if (action == WLAN_SP_MESH_PEERING_CLOSE) { | 356 | if (action == WLAN_SP_MESH_PEERING_CLOSE) { |
353 | memcpy(pos, &reason, 2); | 357 | put_unaligned_le16(reason, pos); |
354 | pos += 2; | 358 | pos += 2; |
355 | } | 359 | } |
356 | 360 | ||
@@ -433,6 +437,7 @@ __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) | |||
433 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); | 437 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); |
434 | 438 | ||
435 | set_sta_flag(sta, WLAN_STA_WME); | 439 | set_sta_flag(sta, WLAN_STA_WME); |
440 | sta->sta.wme = true; | ||
436 | 441 | ||
437 | return sta; | 442 | return sta; |
438 | } | 443 | } |
@@ -518,7 +523,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, | |||
518 | sta->plink_state == NL80211_PLINK_LISTEN && | 523 | sta->plink_state == NL80211_PLINK_LISTEN && |
519 | sdata->u.mesh.accepting_plinks && | 524 | sdata->u.mesh.accepting_plinks && |
520 | sdata->u.mesh.mshcfg.auto_open_plinks && | 525 | sdata->u.mesh.mshcfg.auto_open_plinks && |
521 | rssi_threshold_check(sta, sdata)) | 526 | rssi_threshold_check(sdata, sta)) |
522 | changed = mesh_plink_open(sta); | 527 | changed = mesh_plink_open(sta); |
523 | 528 | ||
524 | ieee80211_mps_frame_release(sta, elems); | 529 | ieee80211_mps_frame_release(sta, elems); |
@@ -530,9 +535,10 @@ out: | |||
530 | static void mesh_plink_timer(unsigned long data) | 535 | static void mesh_plink_timer(unsigned long data) |
531 | { | 536 | { |
532 | struct sta_info *sta; | 537 | struct sta_info *sta; |
533 | __le16 llid, plid, reason; | 538 | u16 reason = 0; |
534 | struct ieee80211_sub_if_data *sdata; | 539 | struct ieee80211_sub_if_data *sdata; |
535 | struct mesh_config *mshcfg; | 540 | struct mesh_config *mshcfg; |
541 | enum ieee80211_self_protected_actioncode action = 0; | ||
536 | 542 | ||
537 | /* | 543 | /* |
538 | * This STA is valid because sta_info_destroy() will | 544 | * This STA is valid because sta_info_destroy() will |
@@ -553,9 +559,6 @@ static void mesh_plink_timer(unsigned long data) | |||
553 | mpl_dbg(sta->sdata, | 559 | mpl_dbg(sta->sdata, |
554 | "Mesh plink timer for %pM fired on state %s\n", | 560 | "Mesh plink timer for %pM fired on state %s\n", |
555 | sta->sta.addr, mplstates[sta->plink_state]); | 561 | sta->sta.addr, mplstates[sta->plink_state]); |
556 | reason = 0; | ||
557 | llid = sta->llid; | ||
558 | plid = sta->plid; | ||
559 | sdata = sta->sdata; | 562 | sdata = sta->sdata; |
560 | mshcfg = &sdata->u.mesh.mshcfg; | 563 | mshcfg = &sdata->u.mesh.mshcfg; |
561 | 564 | ||
@@ -574,33 +577,31 @@ static void mesh_plink_timer(unsigned long data) | |||
574 | rand % sta->plink_timeout; | 577 | rand % sta->plink_timeout; |
575 | ++sta->plink_retries; | 578 | ++sta->plink_retries; |
576 | mod_plink_timer(sta, sta->plink_timeout); | 579 | mod_plink_timer(sta, sta->plink_timeout); |
577 | spin_unlock_bh(&sta->lock); | 580 | action = WLAN_SP_MESH_PEERING_OPEN; |
578 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, | ||
579 | sta->sta.addr, llid, 0, 0); | ||
580 | break; | 581 | break; |
581 | } | 582 | } |
582 | reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES); | 583 | reason = WLAN_REASON_MESH_MAX_RETRIES; |
583 | /* fall through on else */ | 584 | /* fall through on else */ |
584 | case NL80211_PLINK_CNF_RCVD: | 585 | case NL80211_PLINK_CNF_RCVD: |
585 | /* confirm timer */ | 586 | /* confirm timer */ |
586 | if (!reason) | 587 | if (!reason) |
587 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); | 588 | reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; |
588 | sta->plink_state = NL80211_PLINK_HOLDING; | 589 | sta->plink_state = NL80211_PLINK_HOLDING; |
589 | mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); | 590 | mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); |
590 | spin_unlock_bh(&sta->lock); | 591 | action = WLAN_SP_MESH_PEERING_CLOSE; |
591 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | ||
592 | sta->sta.addr, llid, plid, reason); | ||
593 | break; | 592 | break; |
594 | case NL80211_PLINK_HOLDING: | 593 | case NL80211_PLINK_HOLDING: |
595 | /* holding timer */ | 594 | /* holding timer */ |
596 | del_timer(&sta->plink_timer); | 595 | del_timer(&sta->plink_timer); |
597 | mesh_plink_fsm_restart(sta); | 596 | mesh_plink_fsm_restart(sta); |
598 | spin_unlock_bh(&sta->lock); | ||
599 | break; | 597 | break; |
600 | default: | 598 | default: |
601 | spin_unlock_bh(&sta->lock); | ||
602 | break; | 599 | break; |
603 | } | 600 | } |
601 | spin_unlock_bh(&sta->lock); | ||
602 | if (action) | ||
603 | mesh_plink_frame_tx(sdata, action, sta->sta.addr, | ||
604 | sta->llid, sta->plid, reason); | ||
604 | } | 605 | } |
605 | 606 | ||
606 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | 607 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) |
@@ -612,9 +613,40 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | |||
612 | add_timer(&sta->plink_timer); | 613 | add_timer(&sta->plink_timer); |
613 | } | 614 | } |
614 | 615 | ||
616 | static bool llid_in_use(struct ieee80211_sub_if_data *sdata, | ||
617 | u16 llid) | ||
618 | { | ||
619 | struct ieee80211_local *local = sdata->local; | ||
620 | bool in_use = false; | ||
621 | struct sta_info *sta; | ||
622 | |||
623 | rcu_read_lock(); | ||
624 | list_for_each_entry_rcu(sta, &local->sta_list, list) { | ||
625 | if (!memcmp(&sta->llid, &llid, sizeof(llid))) { | ||
626 | in_use = true; | ||
627 | break; | ||
628 | } | ||
629 | } | ||
630 | rcu_read_unlock(); | ||
631 | |||
632 | return in_use; | ||
633 | } | ||
634 | |||
635 | static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata) | ||
636 | { | ||
637 | u16 llid; | ||
638 | |||
639 | do { | ||
640 | get_random_bytes(&llid, sizeof(llid)); | ||
641 | /* for mesh PS we still only have the AID range for TIM bits */ | ||
642 | llid = (llid % IEEE80211_MAX_AID) + 1; | ||
643 | } while (llid_in_use(sdata, llid)); | ||
644 | |||
645 | return llid; | ||
646 | } | ||
647 | |||
615 | u32 mesh_plink_open(struct sta_info *sta) | 648 | u32 mesh_plink_open(struct sta_info *sta) |
616 | { | 649 | { |
617 | __le16 llid; | ||
618 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 650 | struct ieee80211_sub_if_data *sdata = sta->sdata; |
619 | u32 changed; | 651 | u32 changed; |
620 | 652 | ||
@@ -622,8 +654,7 @@ u32 mesh_plink_open(struct sta_info *sta) | |||
622 | return 0; | 654 | return 0; |
623 | 655 | ||
624 | spin_lock_bh(&sta->lock); | 656 | spin_lock_bh(&sta->lock); |
625 | get_random_bytes(&llid, 2); | 657 | sta->llid = mesh_get_new_llid(sdata); |
626 | sta->llid = llid; | ||
627 | if (sta->plink_state != NL80211_PLINK_LISTEN && | 658 | if (sta->plink_state != NL80211_PLINK_LISTEN && |
628 | sta->plink_state != NL80211_PLINK_BLOCKED) { | 659 | sta->plink_state != NL80211_PLINK_BLOCKED) { |
629 | spin_unlock_bh(&sta->lock); | 660 | spin_unlock_bh(&sta->lock); |
@@ -640,7 +671,7 @@ u32 mesh_plink_open(struct sta_info *sta) | |||
640 | changed = ieee80211_mps_local_status_update(sdata); | 671 | changed = ieee80211_mps_local_status_update(sdata); |
641 | 672 | ||
642 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, | 673 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, |
643 | sta->sta.addr, llid, 0, 0); | 674 | sta->sta.addr, sta->llid, 0, 0); |
644 | return changed; | 675 | return changed; |
645 | } | 676 | } |
646 | 677 | ||
@@ -656,390 +687,147 @@ u32 mesh_plink_block(struct sta_info *sta) | |||
656 | return changed; | 687 | return changed; |
657 | } | 688 | } |
658 | 689 | ||
659 | 690 | static void mesh_plink_close(struct ieee80211_sub_if_data *sdata, | |
660 | void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, | 691 | struct sta_info *sta, |
661 | struct ieee80211_mgmt *mgmt, size_t len, | 692 | enum plink_event event) |
662 | struct ieee80211_rx_status *rx_status) | ||
663 | { | 693 | { |
664 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; | 694 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; |
665 | struct ieee802_11_elems elems; | ||
666 | struct sta_info *sta; | ||
667 | enum plink_event event; | ||
668 | enum ieee80211_self_protected_actioncode ftype; | ||
669 | size_t baselen; | ||
670 | bool matches_local = true; | ||
671 | u8 ie_len; | ||
672 | u8 *baseaddr; | ||
673 | u32 changed = 0; | ||
674 | __le16 plid, llid, reason; | ||
675 | |||
676 | /* need action_code, aux */ | ||
677 | if (len < IEEE80211_MIN_ACTION_SIZE + 3) | ||
678 | return; | ||
679 | |||
680 | if (sdata->u.mesh.user_mpm) | ||
681 | /* userspace must register for these */ | ||
682 | return; | ||
683 | |||
684 | if (is_multicast_ether_addr(mgmt->da)) { | ||
685 | mpl_dbg(sdata, | ||
686 | "Mesh plink: ignore frame from multicast address\n"); | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | baseaddr = mgmt->u.action.u.self_prot.variable; | ||
691 | baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; | ||
692 | if (mgmt->u.action.u.self_prot.action_code == | ||
693 | WLAN_SP_MESH_PEERING_CONFIRM) { | ||
694 | baseaddr += 4; | ||
695 | baselen += 4; | ||
696 | } | ||
697 | ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); | ||
698 | |||
699 | if (!elems.peering) { | ||
700 | mpl_dbg(sdata, | ||
701 | "Mesh plink: missing necessary peer link ie\n"); | ||
702 | return; | ||
703 | } | ||
704 | 695 | ||
705 | if (elems.rsn_len && | 696 | u16 reason = (event == CLS_ACPT) ? |
706 | sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { | 697 | WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG; |
707 | mpl_dbg(sdata, | ||
708 | "Mesh plink: can't establish link with secure peer\n"); | ||
709 | return; | ||
710 | } | ||
711 | 698 | ||
712 | ftype = mgmt->u.action.u.self_prot.action_code; | 699 | sta->reason = reason; |
713 | ie_len = elems.peering_len; | 700 | sta->plink_state = NL80211_PLINK_HOLDING; |
714 | if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || | 701 | mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); |
715 | (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || | 702 | } |
716 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 | ||
717 | && ie_len != 8)) { | ||
718 | mpl_dbg(sdata, | ||
719 | "Mesh plink: incorrect plink ie length %d %d\n", | ||
720 | ftype, ie_len); | ||
721 | return; | ||
722 | } | ||
723 | |||
724 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && | ||
725 | (!elems.mesh_id || !elems.mesh_config)) { | ||
726 | mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); | ||
727 | return; | ||
728 | } | ||
729 | /* Note the lines below are correct, the llid in the frame is the plid | ||
730 | * from the point of view of this host. | ||
731 | */ | ||
732 | memcpy(&plid, PLINK_GET_LLID(elems.peering), 2); | ||
733 | if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || | ||
734 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) | ||
735 | memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); | ||
736 | |||
737 | /* WARNING: Only for sta pointer, is dropped & re-acquired */ | ||
738 | rcu_read_lock(); | ||
739 | |||
740 | sta = sta_info_get(sdata, mgmt->sa); | ||
741 | if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) { | ||
742 | mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); | ||
743 | rcu_read_unlock(); | ||
744 | return; | ||
745 | } | ||
746 | |||
747 | if (ftype == WLAN_SP_MESH_PEERING_OPEN && | ||
748 | !rssi_threshold_check(sta, sdata)) { | ||
749 | mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", | ||
750 | mgmt->sa); | ||
751 | rcu_read_unlock(); | ||
752 | return; | ||
753 | } | ||
754 | |||
755 | if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
756 | mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); | ||
757 | rcu_read_unlock(); | ||
758 | return; | ||
759 | } | ||
760 | 703 | ||
761 | if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) { | 704 | static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata, |
762 | rcu_read_unlock(); | 705 | struct sta_info *sta) |
763 | return; | 706 | { |
764 | } | 707 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; |
708 | u32 changed = 0; | ||
765 | 709 | ||
766 | /* Now we will figure out the appropriate event... */ | 710 | del_timer(&sta->plink_timer); |
767 | event = PLINK_UNDEFINED; | 711 | sta->plink_state = NL80211_PLINK_ESTAB; |
768 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && | 712 | changed |= mesh_plink_inc_estab_count(sdata); |
769 | !mesh_matches_local(sdata, &elems)) { | 713 | changed |= mesh_set_ht_prot_mode(sdata); |
770 | matches_local = false; | 714 | changed |= mesh_set_short_slot_time(sdata); |
771 | switch (ftype) { | 715 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr); |
772 | case WLAN_SP_MESH_PEERING_OPEN: | 716 | ieee80211_mps_sta_status_update(sta); |
773 | event = OPN_RJCT; | 717 | changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode); |
774 | break; | 718 | return changed; |
775 | case WLAN_SP_MESH_PEERING_CONFIRM: | 719 | } |
776 | event = CNF_RJCT; | ||
777 | break; | ||
778 | default: | ||
779 | break; | ||
780 | } | ||
781 | } | ||
782 | 720 | ||
783 | if (!sta && !matches_local) { | 721 | /** |
784 | rcu_read_unlock(); | 722 | * mesh_plink_fsm - step @sta MPM based on @event |
785 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); | 723 | * |
786 | llid = 0; | 724 | * @sdata: interface |
787 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | 725 | * @sta: mesh neighbor |
788 | mgmt->sa, llid, plid, reason); | 726 | * @event: peering event |
789 | return; | 727 | * |
790 | } else if (!sta) { | 728 | * Return: changed MBSS flags |
791 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ | 729 | */ |
792 | if (!mesh_plink_free_count(sdata)) { | 730 | static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata, |
793 | mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); | 731 | struct sta_info *sta, enum plink_event event) |
794 | rcu_read_unlock(); | 732 | { |
795 | return; | 733 | struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; |
796 | } | 734 | enum ieee80211_self_protected_actioncode action = 0; |
797 | event = OPN_ACPT; | 735 | u32 changed = 0; |
798 | } else if (matches_local) { | ||
799 | switch (ftype) { | ||
800 | case WLAN_SP_MESH_PEERING_OPEN: | ||
801 | if (!mesh_plink_free_count(sdata) || | ||
802 | (sta->plid && sta->plid != plid)) | ||
803 | event = OPN_IGNR; | ||
804 | else | ||
805 | event = OPN_ACPT; | ||
806 | break; | ||
807 | case WLAN_SP_MESH_PEERING_CONFIRM: | ||
808 | if (!mesh_plink_free_count(sdata) || | ||
809 | (sta->llid != llid || sta->plid != plid)) | ||
810 | event = CNF_IGNR; | ||
811 | else | ||
812 | event = CNF_ACPT; | ||
813 | break; | ||
814 | case WLAN_SP_MESH_PEERING_CLOSE: | ||
815 | if (sta->plink_state == NL80211_PLINK_ESTAB) | ||
816 | /* Do not check for llid or plid. This does not | ||
817 | * follow the standard but since multiple plinks | ||
818 | * per sta are not supported, it is necessary in | ||
819 | * order to avoid a livelock when MP A sees an | ||
820 | * establish peer link to MP B but MP B does not | ||
821 | * see it. This can be caused by a timeout in | ||
822 | * B's peer link establishment or B beign | ||
823 | * restarted. | ||
824 | */ | ||
825 | event = CLS_ACPT; | ||
826 | else if (sta->plid != plid) | ||
827 | event = CLS_IGNR; | ||
828 | else if (ie_len == 7 && sta->llid != llid) | ||
829 | event = CLS_IGNR; | ||
830 | else | ||
831 | event = CLS_ACPT; | ||
832 | break; | ||
833 | default: | ||
834 | mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); | ||
835 | rcu_read_unlock(); | ||
836 | return; | ||
837 | } | ||
838 | } | ||
839 | 736 | ||
840 | if (event == OPN_ACPT) { | 737 | mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr, |
841 | rcu_read_unlock(); | 738 | mplstates[sta->plink_state], mplevents[event]); |
842 | /* allocate sta entry if necessary and update info */ | ||
843 | sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); | ||
844 | if (!sta) { | ||
845 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); | ||
846 | rcu_read_unlock(); | ||
847 | return; | ||
848 | } | ||
849 | } | ||
850 | 739 | ||
851 | mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa, | ||
852 | mplstates[sta->plink_state], mplevents[event]); | ||
853 | reason = 0; | ||
854 | spin_lock_bh(&sta->lock); | 740 | spin_lock_bh(&sta->lock); |
855 | switch (sta->plink_state) { | 741 | switch (sta->plink_state) { |
856 | /* spin_unlock as soon as state is updated at each case */ | ||
857 | case NL80211_PLINK_LISTEN: | 742 | case NL80211_PLINK_LISTEN: |
858 | switch (event) { | 743 | switch (event) { |
859 | case CLS_ACPT: | 744 | case CLS_ACPT: |
860 | mesh_plink_fsm_restart(sta); | 745 | mesh_plink_fsm_restart(sta); |
861 | spin_unlock_bh(&sta->lock); | ||
862 | break; | 746 | break; |
863 | case OPN_ACPT: | 747 | case OPN_ACPT: |
864 | sta->plink_state = NL80211_PLINK_OPN_RCVD; | 748 | sta->plink_state = NL80211_PLINK_OPN_RCVD; |
865 | sta->plid = plid; | 749 | sta->llid = mesh_get_new_llid(sdata); |
866 | get_random_bytes(&llid, 2); | ||
867 | sta->llid = llid; | ||
868 | mesh_plink_timer_set(sta, | 750 | mesh_plink_timer_set(sta, |
869 | mshcfg->dot11MeshRetryTimeout); | 751 | mshcfg->dot11MeshRetryTimeout); |
870 | 752 | ||
871 | /* set the non-peer mode to active during peering */ | 753 | /* set the non-peer mode to active during peering */ |
872 | changed |= ieee80211_mps_local_status_update(sdata); | 754 | changed |= ieee80211_mps_local_status_update(sdata); |
873 | 755 | action = WLAN_SP_MESH_PEERING_OPEN; | |
874 | spin_unlock_bh(&sta->lock); | ||
875 | mesh_plink_frame_tx(sdata, | ||
876 | WLAN_SP_MESH_PEERING_OPEN, | ||
877 | sta->sta.addr, llid, 0, 0); | ||
878 | mesh_plink_frame_tx(sdata, | ||
879 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
880 | sta->sta.addr, llid, plid, 0); | ||
881 | break; | 756 | break; |
882 | default: | 757 | default: |
883 | spin_unlock_bh(&sta->lock); | ||
884 | break; | 758 | break; |
885 | } | 759 | } |
886 | break; | 760 | break; |
887 | |||
888 | case NL80211_PLINK_OPN_SNT: | 761 | case NL80211_PLINK_OPN_SNT: |
889 | switch (event) { | 762 | switch (event) { |
890 | case OPN_RJCT: | 763 | case OPN_RJCT: |
891 | case CNF_RJCT: | 764 | case CNF_RJCT: |
892 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); | ||
893 | case CLS_ACPT: | 765 | case CLS_ACPT: |
894 | if (!reason) | 766 | mesh_plink_close(sdata, sta, event); |
895 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); | 767 | action = WLAN_SP_MESH_PEERING_CLOSE; |
896 | sta->reason = reason; | ||
897 | sta->plink_state = NL80211_PLINK_HOLDING; | ||
898 | if (!mod_plink_timer(sta, | ||
899 | mshcfg->dot11MeshHoldingTimeout)) | ||
900 | sta->ignore_plink_timer = true; | ||
901 | |||
902 | llid = sta->llid; | ||
903 | spin_unlock_bh(&sta->lock); | ||
904 | mesh_plink_frame_tx(sdata, | ||
905 | WLAN_SP_MESH_PEERING_CLOSE, | ||
906 | sta->sta.addr, llid, plid, reason); | ||
907 | break; | 768 | break; |
908 | case OPN_ACPT: | 769 | case OPN_ACPT: |
909 | /* retry timer is left untouched */ | 770 | /* retry timer is left untouched */ |
910 | sta->plink_state = NL80211_PLINK_OPN_RCVD; | 771 | sta->plink_state = NL80211_PLINK_OPN_RCVD; |
911 | sta->plid = plid; | 772 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
912 | llid = sta->llid; | ||
913 | spin_unlock_bh(&sta->lock); | ||
914 | mesh_plink_frame_tx(sdata, | ||
915 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
916 | sta->sta.addr, llid, plid, 0); | ||
917 | break; | 773 | break; |
918 | case CNF_ACPT: | 774 | case CNF_ACPT: |
919 | sta->plink_state = NL80211_PLINK_CNF_RCVD; | 775 | sta->plink_state = NL80211_PLINK_CNF_RCVD; |
920 | if (!mod_plink_timer(sta, | 776 | if (!mod_plink_timer(sta, |
921 | mshcfg->dot11MeshConfirmTimeout)) | 777 | mshcfg->dot11MeshConfirmTimeout)) |
922 | sta->ignore_plink_timer = true; | 778 | sta->ignore_plink_timer = true; |
923 | |||
924 | spin_unlock_bh(&sta->lock); | ||
925 | break; | 779 | break; |
926 | default: | 780 | default: |
927 | spin_unlock_bh(&sta->lock); | ||
928 | break; | 781 | break; |
929 | } | 782 | } |
930 | break; | 783 | break; |
931 | |||
932 | case NL80211_PLINK_OPN_RCVD: | 784 | case NL80211_PLINK_OPN_RCVD: |
933 | switch (event) { | 785 | switch (event) { |
934 | case OPN_RJCT: | 786 | case OPN_RJCT: |
935 | case CNF_RJCT: | 787 | case CNF_RJCT: |
936 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); | ||
937 | case CLS_ACPT: | 788 | case CLS_ACPT: |
938 | if (!reason) | 789 | mesh_plink_close(sdata, sta, event); |
939 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); | 790 | action = WLAN_SP_MESH_PEERING_CLOSE; |
940 | sta->reason = reason; | ||
941 | sta->plink_state = NL80211_PLINK_HOLDING; | ||
942 | if (!mod_plink_timer(sta, | ||
943 | mshcfg->dot11MeshHoldingTimeout)) | ||
944 | sta->ignore_plink_timer = true; | ||
945 | |||
946 | llid = sta->llid; | ||
947 | spin_unlock_bh(&sta->lock); | ||
948 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | ||
949 | sta->sta.addr, llid, plid, reason); | ||
950 | break; | 791 | break; |
951 | case OPN_ACPT: | 792 | case OPN_ACPT: |
952 | llid = sta->llid; | 793 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
953 | spin_unlock_bh(&sta->lock); | ||
954 | mesh_plink_frame_tx(sdata, | ||
955 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
956 | sta->sta.addr, llid, plid, 0); | ||
957 | break; | 794 | break; |
958 | case CNF_ACPT: | 795 | case CNF_ACPT: |
959 | del_timer(&sta->plink_timer); | 796 | changed |= mesh_plink_establish(sdata, sta); |
960 | sta->plink_state = NL80211_PLINK_ESTAB; | ||
961 | spin_unlock_bh(&sta->lock); | ||
962 | changed |= mesh_plink_inc_estab_count(sdata); | ||
963 | changed |= mesh_set_ht_prot_mode(sdata); | ||
964 | changed |= mesh_set_short_slot_time(sdata); | ||
965 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", | ||
966 | sta->sta.addr); | ||
967 | ieee80211_mps_sta_status_update(sta); | ||
968 | changed |= ieee80211_mps_set_sta_local_pm(sta, | ||
969 | mshcfg->power_mode); | ||
970 | break; | 797 | break; |
971 | default: | 798 | default: |
972 | spin_unlock_bh(&sta->lock); | ||
973 | break; | 799 | break; |
974 | } | 800 | } |
975 | break; | 801 | break; |
976 | |||
977 | case NL80211_PLINK_CNF_RCVD: | 802 | case NL80211_PLINK_CNF_RCVD: |
978 | switch (event) { | 803 | switch (event) { |
979 | case OPN_RJCT: | 804 | case OPN_RJCT: |
980 | case CNF_RJCT: | 805 | case CNF_RJCT: |
981 | reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG); | ||
982 | case CLS_ACPT: | 806 | case CLS_ACPT: |
983 | if (!reason) | 807 | mesh_plink_close(sdata, sta, event); |
984 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); | 808 | action = WLAN_SP_MESH_PEERING_CLOSE; |
985 | sta->reason = reason; | ||
986 | sta->plink_state = NL80211_PLINK_HOLDING; | ||
987 | if (!mod_plink_timer(sta, | ||
988 | mshcfg->dot11MeshHoldingTimeout)) | ||
989 | sta->ignore_plink_timer = true; | ||
990 | |||
991 | llid = sta->llid; | ||
992 | spin_unlock_bh(&sta->lock); | ||
993 | mesh_plink_frame_tx(sdata, | ||
994 | WLAN_SP_MESH_PEERING_CLOSE, | ||
995 | sta->sta.addr, llid, plid, reason); | ||
996 | break; | 809 | break; |
997 | case OPN_ACPT: | 810 | case OPN_ACPT: |
998 | del_timer(&sta->plink_timer); | 811 | changed |= mesh_plink_establish(sdata, sta); |
999 | sta->plink_state = NL80211_PLINK_ESTAB; | 812 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
1000 | spin_unlock_bh(&sta->lock); | ||
1001 | changed |= mesh_plink_inc_estab_count(sdata); | ||
1002 | changed |= mesh_set_ht_prot_mode(sdata); | ||
1003 | changed |= mesh_set_short_slot_time(sdata); | ||
1004 | mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", | ||
1005 | sta->sta.addr); | ||
1006 | mesh_plink_frame_tx(sdata, | ||
1007 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
1008 | sta->sta.addr, llid, plid, 0); | ||
1009 | ieee80211_mps_sta_status_update(sta); | ||
1010 | changed |= ieee80211_mps_set_sta_local_pm(sta, | ||
1011 | mshcfg->power_mode); | ||
1012 | break; | 813 | break; |
1013 | default: | 814 | default: |
1014 | spin_unlock_bh(&sta->lock); | ||
1015 | break; | 815 | break; |
1016 | } | 816 | } |
1017 | break; | 817 | break; |
1018 | |||
1019 | case NL80211_PLINK_ESTAB: | 818 | case NL80211_PLINK_ESTAB: |
1020 | switch (event) { | 819 | switch (event) { |
1021 | case CLS_ACPT: | 820 | case CLS_ACPT: |
1022 | reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE); | ||
1023 | sta->reason = reason; | ||
1024 | changed |= __mesh_plink_deactivate(sta); | 821 | changed |= __mesh_plink_deactivate(sta); |
1025 | sta->plink_state = NL80211_PLINK_HOLDING; | ||
1026 | llid = sta->llid; | ||
1027 | mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); | ||
1028 | spin_unlock_bh(&sta->lock); | ||
1029 | changed |= mesh_set_ht_prot_mode(sdata); | 822 | changed |= mesh_set_ht_prot_mode(sdata); |
1030 | changed |= mesh_set_short_slot_time(sdata); | 823 | changed |= mesh_set_short_slot_time(sdata); |
1031 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | 824 | mesh_plink_close(sdata, sta, event); |
1032 | sta->sta.addr, llid, plid, reason); | 825 | action = WLAN_SP_MESH_PEERING_CLOSE; |
1033 | break; | 826 | break; |
1034 | case OPN_ACPT: | 827 | case OPN_ACPT: |
1035 | llid = sta->llid; | 828 | action = WLAN_SP_MESH_PEERING_CONFIRM; |
1036 | spin_unlock_bh(&sta->lock); | ||
1037 | mesh_plink_frame_tx(sdata, | ||
1038 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
1039 | sta->sta.addr, llid, plid, 0); | ||
1040 | break; | 829 | break; |
1041 | default: | 830 | default: |
1042 | spin_unlock_bh(&sta->lock); | ||
1043 | break; | 831 | break; |
1044 | } | 832 | } |
1045 | break; | 833 | break; |
@@ -1049,32 +837,271 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, | |||
1049 | if (del_timer(&sta->plink_timer)) | 837 | if (del_timer(&sta->plink_timer)) |
1050 | sta->ignore_plink_timer = 1; | 838 | sta->ignore_plink_timer = 1; |
1051 | mesh_plink_fsm_restart(sta); | 839 | mesh_plink_fsm_restart(sta); |
1052 | spin_unlock_bh(&sta->lock); | ||
1053 | break; | 840 | break; |
1054 | case OPN_ACPT: | 841 | case OPN_ACPT: |
1055 | case CNF_ACPT: | 842 | case CNF_ACPT: |
1056 | case OPN_RJCT: | 843 | case OPN_RJCT: |
1057 | case CNF_RJCT: | 844 | case CNF_RJCT: |
1058 | llid = sta->llid; | 845 | action = WLAN_SP_MESH_PEERING_CLOSE; |
1059 | reason = sta->reason; | ||
1060 | spin_unlock_bh(&sta->lock); | ||
1061 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | ||
1062 | sta->sta.addr, llid, plid, reason); | ||
1063 | break; | 846 | break; |
1064 | default: | 847 | default: |
1065 | spin_unlock_bh(&sta->lock); | 848 | break; |
1066 | } | 849 | } |
1067 | break; | 850 | break; |
1068 | default: | 851 | default: |
1069 | /* should not get here, PLINK_BLOCKED is dealt with at the | 852 | /* should not get here, PLINK_BLOCKED is dealt with at the |
1070 | * beginning of the function | 853 | * beginning of the function |
1071 | */ | 854 | */ |
1072 | spin_unlock_bh(&sta->lock); | ||
1073 | break; | 855 | break; |
1074 | } | 856 | } |
857 | spin_unlock_bh(&sta->lock); | ||
858 | if (action) { | ||
859 | mesh_plink_frame_tx(sdata, action, sta->sta.addr, | ||
860 | sta->llid, sta->plid, sta->reason); | ||
861 | |||
862 | /* also send confirm in open case */ | ||
863 | if (action == WLAN_SP_MESH_PEERING_OPEN) { | ||
864 | mesh_plink_frame_tx(sdata, | ||
865 | WLAN_SP_MESH_PEERING_CONFIRM, | ||
866 | sta->sta.addr, sta->llid, | ||
867 | sta->plid, 0); | ||
868 | } | ||
869 | } | ||
870 | |||
871 | return changed; | ||
872 | } | ||
873 | |||
874 | /* | ||
875 | * mesh_plink_get_event - get correct MPM event | ||
876 | * | ||
877 | * @sdata: interface | ||
878 | * @sta: peer, leave NULL if processing a frame from a new suitable peer | ||
879 | * @elems: peering management IEs | ||
880 | * @ftype: frame type | ||
881 | * @llid: peer's peer link ID | ||
882 | * @plid: peer's local link ID | ||
883 | * | ||
884 | * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as | ||
885 | * an error. | ||
886 | */ | ||
887 | static enum plink_event | ||
888 | mesh_plink_get_event(struct ieee80211_sub_if_data *sdata, | ||
889 | struct sta_info *sta, | ||
890 | struct ieee802_11_elems *elems, | ||
891 | enum ieee80211_self_protected_actioncode ftype, | ||
892 | u16 llid, u16 plid) | ||
893 | { | ||
894 | enum plink_event event = PLINK_UNDEFINED; | ||
895 | u8 ie_len = elems->peering_len; | ||
896 | bool matches_local; | ||
897 | |||
898 | matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE || | ||
899 | mesh_matches_local(sdata, elems)); | ||
900 | |||
901 | /* deny open request from non-matching peer */ | ||
902 | if (!matches_local && !sta) { | ||
903 | event = OPN_RJCT; | ||
904 | goto out; | ||
905 | } | ||
906 | |||
907 | if (!sta) { | ||
908 | if (ftype != WLAN_SP_MESH_PEERING_OPEN) { | ||
909 | mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n"); | ||
910 | goto out; | ||
911 | } | ||
912 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ | ||
913 | if (!mesh_plink_free_count(sdata)) { | ||
914 | mpl_dbg(sdata, "Mesh plink error: no more free plinks\n"); | ||
915 | goto out; | ||
916 | } | ||
917 | } else { | ||
918 | if (!test_sta_flag(sta, WLAN_STA_AUTH)) { | ||
919 | mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n"); | ||
920 | goto out; | ||
921 | } | ||
922 | if (sta->plink_state == NL80211_PLINK_BLOCKED) | ||
923 | goto out; | ||
924 | } | ||
925 | |||
926 | /* new matching peer */ | ||
927 | if (!sta) { | ||
928 | event = OPN_ACPT; | ||
929 | goto out; | ||
930 | } | ||
931 | |||
932 | switch (ftype) { | ||
933 | case WLAN_SP_MESH_PEERING_OPEN: | ||
934 | if (!matches_local) | ||
935 | event = OPN_RJCT; | ||
936 | if (!mesh_plink_free_count(sdata) || | ||
937 | (sta->plid && sta->plid != plid)) | ||
938 | event = OPN_IGNR; | ||
939 | else | ||
940 | event = OPN_ACPT; | ||
941 | break; | ||
942 | case WLAN_SP_MESH_PEERING_CONFIRM: | ||
943 | if (!matches_local) | ||
944 | event = CNF_RJCT; | ||
945 | if (!mesh_plink_free_count(sdata) || | ||
946 | (sta->llid != llid || sta->plid != plid)) | ||
947 | event = CNF_IGNR; | ||
948 | else | ||
949 | event = CNF_ACPT; | ||
950 | break; | ||
951 | case WLAN_SP_MESH_PEERING_CLOSE: | ||
952 | if (sta->plink_state == NL80211_PLINK_ESTAB) | ||
953 | /* Do not check for llid or plid. This does not | ||
954 | * follow the standard but since multiple plinks | ||
955 | * per sta are not supported, it is necessary in | ||
956 | * order to avoid a livelock when MP A sees an | ||
957 | * establish peer link to MP B but MP B does not | ||
958 | * see it. This can be caused by a timeout in | ||
959 | * B's peer link establishment or B beign | ||
960 | * restarted. | ||
961 | */ | ||
962 | event = CLS_ACPT; | ||
963 | else if (sta->plid != plid) | ||
964 | event = CLS_IGNR; | ||
965 | else if (ie_len == 8 && sta->llid != llid) | ||
966 | event = CLS_IGNR; | ||
967 | else | ||
968 | event = CLS_ACPT; | ||
969 | break; | ||
970 | default: | ||
971 | mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n"); | ||
972 | break; | ||
973 | } | ||
974 | |||
975 | out: | ||
976 | return event; | ||
977 | } | ||
1075 | 978 | ||
979 | static void | ||
980 | mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata, | ||
981 | struct ieee80211_mgmt *mgmt, | ||
982 | struct ieee802_11_elems *elems) | ||
983 | { | ||
984 | |||
985 | struct sta_info *sta; | ||
986 | enum plink_event event; | ||
987 | enum ieee80211_self_protected_actioncode ftype; | ||
988 | u32 changed = 0; | ||
989 | u8 ie_len = elems->peering_len; | ||
990 | __le16 _plid, _llid; | ||
991 | u16 plid, llid = 0; | ||
992 | |||
993 | if (!elems->peering) { | ||
994 | mpl_dbg(sdata, | ||
995 | "Mesh plink: missing necessary peer link ie\n"); | ||
996 | return; | ||
997 | } | ||
998 | |||
999 | if (elems->rsn_len && | ||
1000 | sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) { | ||
1001 | mpl_dbg(sdata, | ||
1002 | "Mesh plink: can't establish link with secure peer\n"); | ||
1003 | return; | ||
1004 | } | ||
1005 | |||
1006 | ftype = mgmt->u.action.u.self_prot.action_code; | ||
1007 | if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) || | ||
1008 | (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) || | ||
1009 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6 | ||
1010 | && ie_len != 8)) { | ||
1011 | mpl_dbg(sdata, | ||
1012 | "Mesh plink: incorrect plink ie length %d %d\n", | ||
1013 | ftype, ie_len); | ||
1014 | return; | ||
1015 | } | ||
1016 | |||
1017 | if (ftype != WLAN_SP_MESH_PEERING_CLOSE && | ||
1018 | (!elems->mesh_id || !elems->mesh_config)) { | ||
1019 | mpl_dbg(sdata, "Mesh plink: missing necessary ie\n"); | ||
1020 | return; | ||
1021 | } | ||
1022 | /* Note the lines below are correct, the llid in the frame is the plid | ||
1023 | * from the point of view of this host. | ||
1024 | */ | ||
1025 | memcpy(&_plid, PLINK_GET_LLID(elems->peering), sizeof(__le16)); | ||
1026 | plid = le16_to_cpu(_plid); | ||
1027 | if (ftype == WLAN_SP_MESH_PEERING_CONFIRM || | ||
1028 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) { | ||
1029 | memcpy(&_llid, PLINK_GET_PLID(elems->peering), sizeof(__le16)); | ||
1030 | llid = le16_to_cpu(_llid); | ||
1031 | } | ||
1032 | |||
1033 | /* WARNING: Only for sta pointer, is dropped & re-acquired */ | ||
1034 | rcu_read_lock(); | ||
1035 | |||
1036 | sta = sta_info_get(sdata, mgmt->sa); | ||
1037 | |||
1038 | if (ftype == WLAN_SP_MESH_PEERING_OPEN && | ||
1039 | !rssi_threshold_check(sdata, sta)) { | ||
1040 | mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n", | ||
1041 | mgmt->sa); | ||
1042 | goto unlock_rcu; | ||
1043 | } | ||
1044 | |||
1045 | /* Now we will figure out the appropriate event... */ | ||
1046 | event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid); | ||
1047 | |||
1048 | if (event == OPN_ACPT) { | ||
1049 | rcu_read_unlock(); | ||
1050 | /* allocate sta entry if necessary and update info */ | ||
1051 | sta = mesh_sta_info_get(sdata, mgmt->sa, elems); | ||
1052 | if (!sta) { | ||
1053 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); | ||
1054 | goto unlock_rcu; | ||
1055 | } | ||
1056 | sta->plid = plid; | ||
1057 | } else if (!sta && event == OPN_RJCT) { | ||
1058 | mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, | ||
1059 | mgmt->sa, 0, plid, | ||
1060 | WLAN_REASON_MESH_CONFIG); | ||
1061 | goto unlock_rcu; | ||
1062 | } else if (!sta || event == PLINK_UNDEFINED) { | ||
1063 | /* something went wrong */ | ||
1064 | goto unlock_rcu; | ||
1065 | } | ||
1066 | |||
1067 | changed |= mesh_plink_fsm(sdata, sta, event); | ||
1068 | |||
1069 | unlock_rcu: | ||
1076 | rcu_read_unlock(); | 1070 | rcu_read_unlock(); |
1077 | 1071 | ||
1078 | if (changed) | 1072 | if (changed) |
1079 | ieee80211_mbss_info_change_notify(sdata, changed); | 1073 | ieee80211_mbss_info_change_notify(sdata, changed); |
1080 | } | 1074 | } |
1075 | |||
1076 | void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, | ||
1077 | struct ieee80211_mgmt *mgmt, size_t len, | ||
1078 | struct ieee80211_rx_status *rx_status) | ||
1079 | { | ||
1080 | struct ieee802_11_elems elems; | ||
1081 | size_t baselen; | ||
1082 | u8 *baseaddr; | ||
1083 | |||
1084 | /* need action_code, aux */ | ||
1085 | if (len < IEEE80211_MIN_ACTION_SIZE + 3) | ||
1086 | return; | ||
1087 | |||
1088 | if (sdata->u.mesh.user_mpm) | ||
1089 | /* userspace must register for these */ | ||
1090 | return; | ||
1091 | |||
1092 | if (is_multicast_ether_addr(mgmt->da)) { | ||
1093 | mpl_dbg(sdata, | ||
1094 | "Mesh plink: ignore frame from multicast address\n"); | ||
1095 | return; | ||
1096 | } | ||
1097 | |||
1098 | baseaddr = mgmt->u.action.u.self_prot.variable; | ||
1099 | baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt; | ||
1100 | if (mgmt->u.action.u.self_prot.action_code == | ||
1101 | WLAN_SP_MESH_PEERING_CONFIRM) { | ||
1102 | baseaddr += 4; | ||
1103 | baselen += 4; | ||
1104 | } | ||
1105 | ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems); | ||
1106 | mesh_process_plink_frame(sdata, mgmt, &elems); | ||
1107 | } | ||