| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2008 open80211s Ltd. | 
|  | 3 | * Author:     Luis Carlos Cobo <luisca@cozybit.com> | 
|  | 4 | * | 
|  | 5 | * This program is free software; you can redistribute it and/or modify | 
|  | 6 | * it under the terms of the GNU General Public License version 2 as | 
|  | 7 | * published by the Free Software Foundation. | 
|  | 8 | */ | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 9 | #include <linux/kernel.h> | 
|  | 10 | #include <linux/random.h> | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 11 | #include "ieee80211_i.h" | 
| Johannes Berg | 2c8dccc | 2008-04-08 15:14:40 -0400 | [diff] [blame] | 12 | #include "rate.h" | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 13 | #include "mesh.h" | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 14 |  | 
|  | 15 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | 
|  | 16 | #define mpl_dbg(fmt, args...)	printk(KERN_DEBUG fmt, ##args) | 
|  | 17 | #else | 
|  | 18 | #define mpl_dbg(fmt, args...)	do { (void)(0); } while (0) | 
|  | 19 | #endif | 
|  | 20 |  | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 21 | #define PLINK_GET_FRAME_SUBTYPE(p) (p) | 
|  | 22 | #define PLINK_GET_LLID(p) (p + 1) | 
|  | 23 | #define PLINK_GET_PLID(p) (p + 3) | 
|  | 24 |  | 
|  | 25 | #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ | 
|  | 26 | jiffies + HZ * t / 1000)) | 
|  | 27 |  | 
|  | 28 | /* Peer link cancel reasons, all subject to ANA approval */ | 
|  | 29 | #define MESH_LINK_CANCELLED			2 | 
|  | 30 | #define MESH_MAX_NEIGHBORS			3 | 
|  | 31 | #define MESH_CAPABILITY_POLICY_VIOLATION	4 | 
|  | 32 | #define MESH_CLOSE_RCVD				5 | 
|  | 33 | #define MESH_MAX_RETRIES			6 | 
|  | 34 | #define MESH_CONFIRM_TIMEOUT			7 | 
|  | 35 | #define MESH_SECURITY_ROLE_NEGOTIATION_DIFFERS	8 | 
|  | 36 | #define MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE	9 | 
|  | 37 | #define MESH_SECURITY_FAILED_VERIFICATION	10 | 
|  | 38 |  | 
| Johannes Berg | 472dbc4 | 2008-09-11 00:01:49 +0200 | [diff] [blame] | 39 | #define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) | 
|  | 40 | #define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) | 
|  | 41 | #define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) | 
|  | 42 | #define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) | 
|  | 43 | #define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 44 |  | 
|  | 45 | enum plink_frame_type { | 
|  | 46 | PLINK_OPEN = 0, | 
|  | 47 | PLINK_CONFIRM, | 
|  | 48 | PLINK_CLOSE | 
|  | 49 | }; | 
|  | 50 |  | 
|  | 51 | enum plink_event { | 
|  | 52 | PLINK_UNDEFINED, | 
|  | 53 | OPN_ACPT, | 
|  | 54 | OPN_RJCT, | 
|  | 55 | OPN_IGNR, | 
|  | 56 | CNF_ACPT, | 
|  | 57 | CNF_RJCT, | 
|  | 58 | CNF_IGNR, | 
|  | 59 | CLS_ACPT, | 
|  | 60 | CLS_IGNR | 
|  | 61 | }; | 
|  | 62 |  | 
|  | 63 | static inline | 
|  | 64 | void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) | 
|  | 65 | { | 
| Johannes Berg | 472dbc4 | 2008-09-11 00:01:49 +0200 | [diff] [blame] | 66 | atomic_inc(&sdata->u.mesh.mshstats.estab_plinks); | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 67 | mesh_accept_plinks_update(sdata); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 68 | } | 
|  | 69 |  | 
|  | 70 | static inline | 
|  | 71 | void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata) | 
|  | 72 | { | 
| Johannes Berg | 472dbc4 | 2008-09-11 00:01:49 +0200 | [diff] [blame] | 73 | atomic_dec(&sdata->u.mesh.mshstats.estab_plinks); | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 74 | mesh_accept_plinks_update(sdata); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 75 | } | 
|  | 76 |  | 
|  | 77 | /** | 
|  | 78 | * mesh_plink_fsm_restart - restart a mesh peer link finite state machine | 
|  | 79 | * | 
|  | 80 | * @sta: mes peer link to restart | 
|  | 81 | * | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 82 | * Locking: this function must be called holding sta->lock | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 83 | */ | 
|  | 84 | static inline void mesh_plink_fsm_restart(struct sta_info *sta) | 
|  | 85 | { | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 86 | sta->plink_state = PLINK_LISTEN; | 
| Luis Carlos Cobo | 37659ff | 2008-02-29 12:13:38 -0800 | [diff] [blame] | 87 | sta->llid = sta->plid = sta->reason = 0; | 
|  | 88 | sta->plink_retries = 0; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 89 | } | 
|  | 90 |  | 
| Johannes Berg | 93e5deb | 2008-04-01 15:21:00 +0200 | [diff] [blame] | 91 | /* | 
|  | 92 | * NOTE: This is just an alias for sta_info_alloc(), see notes | 
|  | 93 | *       on it in the lifecycle management section! | 
|  | 94 | */ | 
| Johannes Berg | 03e4497 | 2008-02-27 09:56:40 +0100 | [diff] [blame] | 95 | static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, | 
|  | 96 | u8 *hw_addr, u64 rates) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 97 | { | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 98 | struct ieee80211_local *local = sdata->local; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 99 | struct sta_info *sta; | 
|  | 100 |  | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 101 | if (local->num_sta >= MESH_MAX_PLINKS) | 
| Johannes Berg | 73651ee | 2008-02-25 16:27:47 +0100 | [diff] [blame] | 102 | return NULL; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 103 |  | 
| Johannes Berg | 03e4497 | 2008-02-27 09:56:40 +0100 | [diff] [blame] | 104 | sta = sta_info_alloc(sdata, hw_addr, GFP_ATOMIC); | 
| Johannes Berg | 73651ee | 2008-02-25 16:27:47 +0100 | [diff] [blame] | 105 | if (!sta) | 
|  | 106 | return NULL; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 107 |  | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 108 | sta->flags = WLAN_STA_AUTHORIZED; | 
| Johannes Berg | 323ce79 | 2008-09-11 02:45:11 +0200 | [diff] [blame] | 109 | sta->sta.supp_rates[local->hw.conf.channel->band] = rates; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 110 |  | 
|  | 111 | return sta; | 
|  | 112 | } | 
|  | 113 |  | 
|  | 114 | /** | 
| Johannes Berg | 42096b6 | 2008-02-25 21:36:27 +0100 | [diff] [blame] | 115 | * mesh_plink_deactivate - deactivate mesh peer link | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 116 | * | 
|  | 117 | * @sta: mesh peer link to deactivate | 
|  | 118 | * | 
|  | 119 | * All mesh paths with this peer as next hop will be flushed | 
|  | 120 | * | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 121 | * Locking: the caller must hold sta->lock | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 122 | */ | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 123 | static void __mesh_plink_deactivate(struct sta_info *sta) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 124 | { | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 125 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 
|  | 126 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 127 | if (sta->plink_state == PLINK_ESTAB) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 128 | mesh_plink_dec_estab_count(sdata); | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 129 | sta->plink_state = PLINK_BLOCKED; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 130 | mesh_path_flush_by_nexthop(sta); | 
|  | 131 | } | 
|  | 132 |  | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 133 | /** | 
|  | 134 | * __mesh_plink_deactivate - deactivate mesh peer link | 
|  | 135 | * | 
|  | 136 | * @sta: mesh peer link to deactivate | 
|  | 137 | * | 
|  | 138 | * All mesh paths with this peer as next hop will be flushed | 
|  | 139 | */ | 
|  | 140 | void mesh_plink_deactivate(struct sta_info *sta) | 
|  | 141 | { | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 142 | spin_lock_bh(&sta->lock); | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 143 | __mesh_plink_deactivate(sta); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 144 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 145 | } | 
|  | 146 |  | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 147 | static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 148 | enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid, | 
|  | 149 | __le16 reason) { | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 150 | struct ieee80211_local *local = sdata->local; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 151 | struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); | 
|  | 152 | struct ieee80211_mgmt *mgmt; | 
|  | 153 | bool include_plid = false; | 
|  | 154 | u8 *pos; | 
|  | 155 | int ie_len; | 
|  | 156 |  | 
|  | 157 | if (!skb) | 
|  | 158 | return -1; | 
|  | 159 | skb_reserve(skb, local->hw.extra_tx_headroom); | 
|  | 160 | /* 25 is the size of the common mgmt part (24) plus the size of the | 
|  | 161 | * common action part (1) | 
|  | 162 | */ | 
|  | 163 | mgmt = (struct ieee80211_mgmt *) | 
|  | 164 | skb_put(skb, 25 + sizeof(mgmt->u.action.u.plink_action)); | 
|  | 165 | memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.plink_action)); | 
| Harvey Harrison | e7827a7 | 2008-07-15 18:44:13 -0700 | [diff] [blame] | 166 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | 
|  | 167 | IEEE80211_STYPE_ACTION); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 168 | memcpy(mgmt->da, da, ETH_ALEN); | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 169 | memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 170 | /* BSSID is left zeroed, wildcard value */ | 
|  | 171 | mgmt->u.action.category = PLINK_CATEGORY; | 
|  | 172 | mgmt->u.action.u.plink_action.action_code = action; | 
|  | 173 |  | 
|  | 174 | if (action == PLINK_CLOSE) | 
|  | 175 | mgmt->u.action.u.plink_action.aux = reason; | 
|  | 176 | else { | 
|  | 177 | mgmt->u.action.u.plink_action.aux = cpu_to_le16(0x0); | 
|  | 178 | if (action == PLINK_CONFIRM) { | 
|  | 179 | pos = skb_put(skb, 4); | 
|  | 180 | /* two-byte status code followed by two-byte AID */ | 
|  | 181 | memset(pos, 0, 4); | 
|  | 182 | } | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 183 | mesh_mgmt_ies_add(skb, sdata); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 184 | } | 
|  | 185 |  | 
|  | 186 | /* Add Peer Link Management element */ | 
|  | 187 | switch (action) { | 
|  | 188 | case PLINK_OPEN: | 
|  | 189 | ie_len = 3; | 
|  | 190 | break; | 
|  | 191 | case PLINK_CONFIRM: | 
|  | 192 | ie_len = 5; | 
|  | 193 | include_plid = true; | 
|  | 194 | break; | 
|  | 195 | case PLINK_CLOSE: | 
|  | 196 | default: | 
|  | 197 | if (!plid) | 
|  | 198 | ie_len = 5; | 
|  | 199 | else { | 
|  | 200 | ie_len = 7; | 
|  | 201 | include_plid = true; | 
|  | 202 | } | 
|  | 203 | break; | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | pos = skb_put(skb, 2 + ie_len); | 
|  | 207 | *pos++ = WLAN_EID_PEER_LINK; | 
|  | 208 | *pos++ = ie_len; | 
|  | 209 | *pos++ = action; | 
|  | 210 | memcpy(pos, &llid, 2); | 
|  | 211 | if (include_plid) { | 
|  | 212 | pos += 2; | 
|  | 213 | memcpy(pos, &plid, 2); | 
|  | 214 | } | 
|  | 215 | if (action == PLINK_CLOSE) { | 
|  | 216 | pos += 2; | 
|  | 217 | memcpy(pos, &reason, 2); | 
|  | 218 | } | 
|  | 219 |  | 
| Johannes Berg | e50db65 | 2008-09-09 15:07:09 +0200 | [diff] [blame] | 220 | ieee80211_tx_skb(sdata, skb, 0); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 221 | return 0; | 
|  | 222 | } | 
|  | 223 |  | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 224 | void mesh_neighbour_update(u8 *hw_addr, u64 rates, struct ieee80211_sub_if_data *sdata, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 225 | bool peer_accepting_plinks) | 
|  | 226 | { | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 227 | struct ieee80211_local *local = sdata->local; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 228 | struct sta_info *sta; | 
|  | 229 |  | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 230 | rcu_read_lock(); | 
|  | 231 |  | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 232 | sta = sta_info_get(local, hw_addr); | 
|  | 233 | if (!sta) { | 
| Johannes Berg | 03e4497 | 2008-02-27 09:56:40 +0100 | [diff] [blame] | 234 | sta = mesh_plink_alloc(sdata, hw_addr, rates); | 
| Johannes Berg | 73651ee | 2008-02-25 16:27:47 +0100 | [diff] [blame] | 235 | if (!sta) { | 
|  | 236 | rcu_read_unlock(); | 
|  | 237 | return; | 
|  | 238 | } | 
|  | 239 | if (sta_info_insert(sta)) { | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 240 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 241 | return; | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 242 | } | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 243 | } | 
|  | 244 |  | 
|  | 245 | sta->last_rx = jiffies; | 
| Johannes Berg | 323ce79 | 2008-09-11 02:45:11 +0200 | [diff] [blame] | 246 | sta->sta.supp_rates[local->hw.conf.channel->band] = rates; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 247 | if (peer_accepting_plinks && sta->plink_state == PLINK_LISTEN && | 
| Johannes Berg | 472dbc4 | 2008-09-11 00:01:49 +0200 | [diff] [blame] | 248 | sdata->u.mesh.accepting_plinks && | 
|  | 249 | sdata->u.mesh.mshcfg.auto_open_plinks) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 250 | mesh_plink_open(sta); | 
|  | 251 |  | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 252 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 253 | } | 
|  | 254 |  | 
|  | 255 | static void mesh_plink_timer(unsigned long data) | 
|  | 256 | { | 
|  | 257 | struct sta_info *sta; | 
|  | 258 | __le16 llid, plid, reason; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 259 | struct ieee80211_sub_if_data *sdata; | 
|  | 260 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | 
|  | 261 | DECLARE_MAC_BUF(mac); | 
|  | 262 | #endif | 
|  | 263 |  | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 264 | /* | 
|  | 265 | * This STA is valid because sta_info_destroy() will | 
|  | 266 | * del_timer_sync() this timer after having made sure | 
|  | 267 | * it cannot be readded (by deleting the plink.) | 
|  | 268 | */ | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 269 | sta = (struct sta_info *) data; | 
|  | 270 |  | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 271 | spin_lock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 272 | if (sta->ignore_plink_timer) { | 
|  | 273 | sta->ignore_plink_timer = false; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 274 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 275 | return; | 
|  | 276 | } | 
|  | 277 | mpl_dbg("Mesh plink timer for %s fired on state %d\n", | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 278 | print_mac(mac, sta->sta.addr), sta->plink_state); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 279 | reason = 0; | 
|  | 280 | llid = sta->llid; | 
|  | 281 | plid = sta->plid; | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 282 | sdata = sta->sdata; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 283 |  | 
|  | 284 | switch (sta->plink_state) { | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 285 | case PLINK_OPN_RCVD: | 
|  | 286 | case PLINK_OPN_SNT: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 287 | /* retry timer */ | 
|  | 288 | if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { | 
|  | 289 | u32 rand; | 
|  | 290 | mpl_dbg("Mesh plink for %s (retry, timeout): %d %d\n", | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 291 | print_mac(mac, sta->sta.addr), | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 292 | sta->plink_retries, sta->plink_timeout); | 
|  | 293 | get_random_bytes(&rand, sizeof(u32)); | 
|  | 294 | sta->plink_timeout = sta->plink_timeout + | 
|  | 295 | rand % sta->plink_timeout; | 
|  | 296 | ++sta->plink_retries; | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 297 | mod_plink_timer(sta, sta->plink_timeout); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 298 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 299 | mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 300 | 0, 0); | 
|  | 301 | break; | 
|  | 302 | } | 
|  | 303 | reason = cpu_to_le16(MESH_MAX_RETRIES); | 
|  | 304 | /* fall through on else */ | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 305 | case PLINK_CNF_RCVD: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 306 | /* confirm timer */ | 
|  | 307 | if (!reason) | 
|  | 308 | reason = cpu_to_le16(MESH_CONFIRM_TIMEOUT); | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 309 | sta->plink_state = PLINK_HOLDING; | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 310 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 311 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 312 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, plid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 313 | reason); | 
|  | 314 | break; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 315 | case PLINK_HOLDING: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 316 | /* holding timer */ | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 317 | del_timer(&sta->plink_timer); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 318 | mesh_plink_fsm_restart(sta); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 319 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 320 | break; | 
|  | 321 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 322 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 323 | break; | 
|  | 324 | } | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 325 | } | 
|  | 326 |  | 
|  | 327 | static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) | 
|  | 328 | { | 
|  | 329 | sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); | 
|  | 330 | sta->plink_timer.data = (unsigned long) sta; | 
|  | 331 | sta->plink_timer.function = mesh_plink_timer; | 
|  | 332 | sta->plink_timeout = timeout; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 333 | add_timer(&sta->plink_timer); | 
|  | 334 | } | 
|  | 335 |  | 
|  | 336 | int mesh_plink_open(struct sta_info *sta) | 
|  | 337 | { | 
|  | 338 | __le16 llid; | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 339 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 340 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | 
|  | 341 | DECLARE_MAC_BUF(mac); | 
|  | 342 | #endif | 
|  | 343 |  | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 344 | spin_lock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 345 | get_random_bytes(&llid, 2); | 
|  | 346 | sta->llid = llid; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 347 | if (sta->plink_state != PLINK_LISTEN) { | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 348 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 349 | return -EBUSY; | 
|  | 350 | } | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 351 | sta->plink_state = PLINK_OPN_SNT; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 352 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 353 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 354 | mpl_dbg("Mesh plink: starting establishment with %s\n", | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 355 | print_mac(mac, sta->sta.addr)); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 356 |  | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 357 | return mesh_plink_frame_tx(sdata, PLINK_OPEN, | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 358 | sta->sta.addr, llid, 0, 0); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 359 | } | 
|  | 360 |  | 
|  | 361 | void mesh_plink_block(struct sta_info *sta) | 
|  | 362 | { | 
|  | 363 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | 
|  | 364 | DECLARE_MAC_BUF(mac); | 
|  | 365 | #endif | 
|  | 366 |  | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 367 | spin_lock_bh(&sta->lock); | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 368 | __mesh_plink_deactivate(sta); | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 369 | sta->plink_state = PLINK_BLOCKED; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 370 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 371 | } | 
|  | 372 |  | 
|  | 373 | int mesh_plink_close(struct sta_info *sta) | 
|  | 374 | { | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 375 | struct ieee80211_sub_if_data *sdata = sta->sdata; | 
| Luis Carlos Cobo | 37659ff | 2008-02-29 12:13:38 -0800 | [diff] [blame] | 376 | __le16 llid, plid, reason; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 377 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | 
|  | 378 | DECLARE_MAC_BUF(mac); | 
|  | 379 | #endif | 
|  | 380 |  | 
|  | 381 | mpl_dbg("Mesh plink: closing link with %s\n", | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 382 | print_mac(mac, sta->sta.addr)); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 383 | spin_lock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 384 | sta->reason = cpu_to_le16(MESH_LINK_CANCELLED); | 
|  | 385 | reason = sta->reason; | 
|  | 386 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 387 | if (sta->plink_state == PLINK_LISTEN || | 
|  | 388 | sta->plink_state == PLINK_BLOCKED) { | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 389 | mesh_plink_fsm_restart(sta); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 390 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 391 | return 0; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 392 | } else if (sta->plink_state == PLINK_ESTAB) { | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 393 | __mesh_plink_deactivate(sta); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 394 | /* The timer should not be running */ | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 395 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 396 | } else if (!mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata))) | 
|  | 397 | sta->ignore_plink_timer = true; | 
|  | 398 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 399 | sta->plink_state = PLINK_HOLDING; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 400 | llid = sta->llid; | 
|  | 401 | plid = sta->plid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 402 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 403 | mesh_plink_frame_tx(sta->sdata, PLINK_CLOSE, sta->sta.addr, llid, | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 404 | plid, reason); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 405 | return 0; | 
|  | 406 | } | 
|  | 407 |  | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 408 | void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 409 | size_t len, struct ieee80211_rx_status *rx_status) | 
|  | 410 | { | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 411 | struct ieee80211_local *local = sdata->local; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 412 | struct ieee802_11_elems elems; | 
|  | 413 | struct sta_info *sta; | 
|  | 414 | enum plink_event event; | 
|  | 415 | enum plink_frame_type ftype; | 
|  | 416 | size_t baselen; | 
|  | 417 | u8 ie_len; | 
|  | 418 | u8 *baseaddr; | 
|  | 419 | __le16 plid, llid, reason; | 
|  | 420 | #ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG | 
|  | 421 | DECLARE_MAC_BUF(mac); | 
|  | 422 | #endif | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 423 |  | 
| Johannes Berg | 9c80d3d | 2008-09-08 15:41:59 +0200 | [diff] [blame] | 424 | /* need action_code, aux */ | 
|  | 425 | if (len < IEEE80211_MIN_ACTION_SIZE + 3) | 
|  | 426 | return; | 
|  | 427 |  | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 428 | if (is_multicast_ether_addr(mgmt->da)) { | 
|  | 429 | mpl_dbg("Mesh plink: ignore frame from multicast address"); | 
|  | 430 | return; | 
|  | 431 | } | 
|  | 432 |  | 
|  | 433 | baseaddr = mgmt->u.action.u.plink_action.variable; | 
|  | 434 | baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt; | 
|  | 435 | if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) { | 
|  | 436 | baseaddr += 4; | 
|  | 437 | baselen -= 4; | 
|  | 438 | } | 
|  | 439 | ieee802_11_parse_elems(baseaddr, len - baselen, &elems); | 
|  | 440 | if (!elems.peer_link) { | 
|  | 441 | mpl_dbg("Mesh plink: missing necessary peer link ie\n"); | 
|  | 442 | return; | 
|  | 443 | } | 
|  | 444 |  | 
|  | 445 | ftype = *((u8 *)PLINK_GET_FRAME_SUBTYPE(elems.peer_link)); | 
|  | 446 | ie_len = elems.peer_link_len; | 
|  | 447 | if ((ftype == PLINK_OPEN && ie_len != 3) || | 
|  | 448 | (ftype == PLINK_CONFIRM && ie_len != 5) || | 
|  | 449 | (ftype == PLINK_CLOSE && ie_len != 5 && ie_len != 7)) { | 
|  | 450 | mpl_dbg("Mesh plink: incorrect plink ie length\n"); | 
|  | 451 | return; | 
|  | 452 | } | 
|  | 453 |  | 
|  | 454 | if (ftype != PLINK_CLOSE && (!elems.mesh_id || !elems.mesh_config)) { | 
|  | 455 | mpl_dbg("Mesh plink: missing necessary ie\n"); | 
|  | 456 | return; | 
|  | 457 | } | 
|  | 458 | /* Note the lines below are correct, the llid in the frame is the plid | 
|  | 459 | * from the point of view of this host. | 
|  | 460 | */ | 
|  | 461 | memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2); | 
|  | 462 | if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7)) | 
|  | 463 | memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2); | 
|  | 464 |  | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 465 | rcu_read_lock(); | 
|  | 466 |  | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 467 | sta = sta_info_get(local, mgmt->sa); | 
|  | 468 | if (!sta && ftype != PLINK_OPEN) { | 
|  | 469 | mpl_dbg("Mesh plink: cls or cnf from unknown peer\n"); | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 470 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 471 | return; | 
|  | 472 | } | 
|  | 473 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 474 | if (sta && sta->plink_state == PLINK_BLOCKED) { | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 475 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 476 | return; | 
|  | 477 | } | 
|  | 478 |  | 
|  | 479 | /* Now we will figure out the appropriate event... */ | 
|  | 480 | event = PLINK_UNDEFINED; | 
| Jasper Bryant-Greene | f698d85 | 2008-08-03 12:04:37 +1200 | [diff] [blame] | 481 | if (ftype != PLINK_CLOSE && (!mesh_matches_local(&elems, sdata))) { | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 482 | switch (ftype) { | 
|  | 483 | case PLINK_OPEN: | 
|  | 484 | event = OPN_RJCT; | 
|  | 485 | break; | 
|  | 486 | case PLINK_CONFIRM: | 
|  | 487 | event = CNF_RJCT; | 
|  | 488 | break; | 
|  | 489 | case PLINK_CLOSE: | 
|  | 490 | /* avoid warning */ | 
|  | 491 | break; | 
|  | 492 | } | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 493 | spin_lock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 494 | } else if (!sta) { | 
|  | 495 | /* ftype == PLINK_OPEN */ | 
|  | 496 | u64 rates; | 
|  | 497 | if (!mesh_plink_free_count(sdata)) { | 
|  | 498 | mpl_dbg("Mesh plink error: no more free plinks\n"); | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 499 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 500 | return; | 
|  | 501 | } | 
|  | 502 |  | 
|  | 503 | rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); | 
| Johannes Berg | 03e4497 | 2008-02-27 09:56:40 +0100 | [diff] [blame] | 504 | sta = mesh_plink_alloc(sdata, mgmt->sa, rates); | 
| Johannes Berg | 73651ee | 2008-02-25 16:27:47 +0100 | [diff] [blame] | 505 | if (!sta) { | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 506 | mpl_dbg("Mesh plink error: plink table full\n"); | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 507 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 508 | return; | 
|  | 509 | } | 
| Johannes Berg | 73651ee | 2008-02-25 16:27:47 +0100 | [diff] [blame] | 510 | if (sta_info_insert(sta)) { | 
| Johannes Berg | 73651ee | 2008-02-25 16:27:47 +0100 | [diff] [blame] | 511 | rcu_read_unlock(); | 
|  | 512 | return; | 
|  | 513 | } | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 514 | event = OPN_ACPT; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 515 | spin_lock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 516 | } else { | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 517 | spin_lock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 518 | switch (ftype) { | 
|  | 519 | case PLINK_OPEN: | 
|  | 520 | if (!mesh_plink_free_count(sdata) || | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 521 | (sta->plid && sta->plid != plid)) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 522 | event = OPN_IGNR; | 
|  | 523 | else | 
|  | 524 | event = OPN_ACPT; | 
|  | 525 | break; | 
|  | 526 | case PLINK_CONFIRM: | 
|  | 527 | if (!mesh_plink_free_count(sdata) || | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 528 | (sta->llid != llid || sta->plid != plid)) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 529 | event = CNF_IGNR; | 
|  | 530 | else | 
|  | 531 | event = CNF_ACPT; | 
|  | 532 | break; | 
|  | 533 | case PLINK_CLOSE: | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 534 | if (sta->plink_state == PLINK_ESTAB) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 535 | /* Do not check for llid or plid. This does not | 
|  | 536 | * follow the standard but since multiple plinks | 
|  | 537 | * per sta are not supported, it is necessary in | 
|  | 538 | * order to avoid a livelock when MP A sees an | 
|  | 539 | * establish peer link to MP B but MP B does not | 
|  | 540 | * see it. This can be caused by a timeout in | 
|  | 541 | * B's peer link establishment or B beign | 
|  | 542 | * restarted. | 
|  | 543 | */ | 
|  | 544 | event = CLS_ACPT; | 
|  | 545 | else if (sta->plid != plid) | 
|  | 546 | event = CLS_IGNR; | 
|  | 547 | else if (ie_len == 7 && sta->llid != llid) | 
|  | 548 | event = CLS_IGNR; | 
|  | 549 | else | 
|  | 550 | event = CLS_ACPT; | 
|  | 551 | break; | 
|  | 552 | default: | 
|  | 553 | mpl_dbg("Mesh plink: unknown frame subtype\n"); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 554 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 555 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 556 | return; | 
|  | 557 | } | 
|  | 558 | } | 
|  | 559 |  | 
|  | 560 | mpl_dbg("Mesh plink (peer, state, llid, plid, event): %s %d %d %d %d\n", | 
|  | 561 | print_mac(mac, mgmt->sa), sta->plink_state, | 
| Luis Carlos Cobo | aa2b592 | 2008-02-29 14:30:32 -0800 | [diff] [blame] | 562 | le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 563 | event); | 
|  | 564 | reason = 0; | 
|  | 565 | switch (sta->plink_state) { | 
|  | 566 | /* spin_unlock as soon as state is updated at each case */ | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 567 | case PLINK_LISTEN: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 568 | switch (event) { | 
|  | 569 | case CLS_ACPT: | 
|  | 570 | mesh_plink_fsm_restart(sta); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 571 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 572 | break; | 
|  | 573 | case OPN_ACPT: | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 574 | sta->plink_state = PLINK_OPN_RCVD; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 575 | sta->plid = plid; | 
|  | 576 | get_random_bytes(&llid, 2); | 
|  | 577 | sta->llid = llid; | 
|  | 578 | mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 579 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 580 | mesh_plink_frame_tx(sdata, PLINK_OPEN, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 581 | 0, 0); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 582 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 583 | llid, plid, 0); | 
|  | 584 | break; | 
|  | 585 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 586 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 587 | break; | 
|  | 588 | } | 
|  | 589 | break; | 
|  | 590 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 591 | case PLINK_OPN_SNT: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 592 | switch (event) { | 
|  | 593 | case OPN_RJCT: | 
|  | 594 | case CNF_RJCT: | 
|  | 595 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 
|  | 596 | case CLS_ACPT: | 
|  | 597 | if (!reason) | 
|  | 598 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 
|  | 599 | sta->reason = reason; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 600 | sta->plink_state = PLINK_HOLDING; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 601 | if (!mod_plink_timer(sta, | 
|  | 602 | dot11MeshHoldingTimeout(sdata))) | 
|  | 603 | sta->ignore_plink_timer = true; | 
|  | 604 |  | 
|  | 605 | llid = sta->llid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 606 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 607 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 608 | plid, reason); | 
|  | 609 | break; | 
|  | 610 | case OPN_ACPT: | 
|  | 611 | /* retry timer is left untouched */ | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 612 | sta->plink_state = PLINK_OPN_RCVD; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 613 | sta->plid = plid; | 
|  | 614 | llid = sta->llid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 615 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 616 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 617 | plid, 0); | 
|  | 618 | break; | 
|  | 619 | case CNF_ACPT: | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 620 | sta->plink_state = PLINK_CNF_RCVD; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 621 | if (!mod_plink_timer(sta, | 
|  | 622 | dot11MeshConfirmTimeout(sdata))) | 
|  | 623 | sta->ignore_plink_timer = true; | 
|  | 624 |  | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 625 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 626 | break; | 
|  | 627 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 628 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 629 | break; | 
|  | 630 | } | 
|  | 631 | break; | 
|  | 632 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 633 | case PLINK_OPN_RCVD: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 634 | switch (event) { | 
|  | 635 | case OPN_RJCT: | 
|  | 636 | case CNF_RJCT: | 
|  | 637 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 
|  | 638 | case CLS_ACPT: | 
|  | 639 | if (!reason) | 
|  | 640 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 
|  | 641 | sta->reason = reason; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 642 | sta->plink_state = PLINK_HOLDING; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 643 | if (!mod_plink_timer(sta, | 
|  | 644 | dot11MeshHoldingTimeout(sdata))) | 
|  | 645 | sta->ignore_plink_timer = true; | 
|  | 646 |  | 
|  | 647 | llid = sta->llid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 648 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 649 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 650 | plid, reason); | 
|  | 651 | break; | 
|  | 652 | case OPN_ACPT: | 
|  | 653 | llid = sta->llid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 654 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 655 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 656 | plid, 0); | 
|  | 657 | break; | 
|  | 658 | case CNF_ACPT: | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 659 | del_timer(&sta->plink_timer); | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 660 | sta->plink_state = PLINK_ESTAB; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 661 | mesh_plink_inc_estab_count(sdata); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 662 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 663 | mpl_dbg("Mesh plink with %s ESTABLISHED\n", | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 664 | print_mac(mac, sta->sta.addr)); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 665 | break; | 
|  | 666 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 667 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 668 | break; | 
|  | 669 | } | 
|  | 670 | break; | 
|  | 671 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 672 | case PLINK_CNF_RCVD: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 673 | switch (event) { | 
|  | 674 | case OPN_RJCT: | 
|  | 675 | case CNF_RJCT: | 
|  | 676 | reason = cpu_to_le16(MESH_CAPABILITY_POLICY_VIOLATION); | 
|  | 677 | case CLS_ACPT: | 
|  | 678 | if (!reason) | 
|  | 679 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 
|  | 680 | sta->reason = reason; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 681 | sta->plink_state = PLINK_HOLDING; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 682 | if (!mod_plink_timer(sta, | 
|  | 683 | dot11MeshHoldingTimeout(sdata))) | 
|  | 684 | sta->ignore_plink_timer = true; | 
|  | 685 |  | 
|  | 686 | llid = sta->llid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 687 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 688 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 689 | plid, reason); | 
| Johannes Berg | ff59dc7 | 2008-02-25 10:11:50 +0100 | [diff] [blame] | 690 | break; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 691 | case OPN_ACPT: | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 692 | del_timer(&sta->plink_timer); | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 693 | sta->plink_state = PLINK_ESTAB; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 694 | mesh_plink_inc_estab_count(sdata); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 695 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 696 | mpl_dbg("Mesh plink with %s ESTABLISHED\n", | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 697 | print_mac(mac, sta->sta.addr)); | 
|  | 698 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 699 | plid, 0); | 
|  | 700 | break; | 
|  | 701 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 702 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 703 | break; | 
|  | 704 | } | 
|  | 705 | break; | 
|  | 706 |  | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 707 | case PLINK_ESTAB: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 708 | switch (event) { | 
|  | 709 | case CLS_ACPT: | 
|  | 710 | reason = cpu_to_le16(MESH_CLOSE_RCVD); | 
|  | 711 | sta->reason = reason; | 
| Johannes Berg | 902acc7 | 2008-02-23 15:17:19 +0100 | [diff] [blame] | 712 | __mesh_plink_deactivate(sta); | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 713 | sta->plink_state = PLINK_HOLDING; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 714 | llid = sta->llid; | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 715 | mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 716 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 717 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 718 | plid, reason); | 
|  | 719 | break; | 
|  | 720 | case OPN_ACPT: | 
|  | 721 | llid = sta->llid; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 722 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 723 | mesh_plink_frame_tx(sdata, PLINK_CONFIRM, sta->sta.addr, llid, | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 724 | plid, 0); | 
|  | 725 | break; | 
|  | 726 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 727 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 728 | break; | 
|  | 729 | } | 
|  | 730 | break; | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 731 | case PLINK_HOLDING: | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 732 | switch (event) { | 
|  | 733 | case CLS_ACPT: | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 734 | if (del_timer(&sta->plink_timer)) | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 735 | sta->ignore_plink_timer = 1; | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 736 | mesh_plink_fsm_restart(sta); | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 737 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 738 | break; | 
|  | 739 | case OPN_ACPT: | 
|  | 740 | case CNF_ACPT: | 
|  | 741 | case OPN_RJCT: | 
|  | 742 | case CNF_RJCT: | 
|  | 743 | llid = sta->llid; | 
|  | 744 | reason = sta->reason; | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 745 | spin_unlock_bh(&sta->lock); | 
| Johannes Berg | 17741cd | 2008-09-11 00:02:02 +0200 | [diff] [blame] | 746 | mesh_plink_frame_tx(sdata, PLINK_CLOSE, sta->sta.addr, | 
|  | 747 | llid, plid, reason); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 748 | break; | 
|  | 749 | default: | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 750 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 751 | } | 
|  | 752 | break; | 
|  | 753 | default: | 
| Luis Carlos Cobo | b4e08ea | 2008-02-29 15:46:08 -0800 | [diff] [blame] | 754 | /* should not get here, PLINK_BLOCKED is dealt with at the | 
|  | 755 | * beggining of the function | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 756 | */ | 
| Johannes Berg | 07346f81 | 2008-05-03 01:02:02 +0200 | [diff] [blame] | 757 | spin_unlock_bh(&sta->lock); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 758 | break; | 
|  | 759 | } | 
| Johannes Berg | d0709a6 | 2008-02-25 16:27:46 +0100 | [diff] [blame] | 760 |  | 
|  | 761 | rcu_read_unlock(); | 
| Luis Carlos Cobo | c3896d2 | 2008-02-23 15:17:13 +0100 | [diff] [blame] | 762 | } |