]>
Commit | Line | Data |
---|---|---|
5f92659d BC |
1 | /* |
2 | * WPA Supplicant - Basic mesh peer management | |
3 | * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. | |
4 | * | |
5 | * This software may be distributed under the terms of the BSD license. | |
6 | * See README for more details. | |
7 | */ | |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
12 | #include "utils/eloop.h" | |
13 | #include "common/ieee802_11_defs.h" | |
14 | #include "ap/hostapd.h" | |
15 | #include "ap/sta_info.h" | |
16 | #include "ap/ieee802_11.h" | |
17 | #include "wpa_supplicant_i.h" | |
18 | #include "driver_i.h" | |
19 | #include "mesh_mpm.h" | |
0f950df0 | 20 | #include "mesh_rsn.h" |
5f92659d | 21 | |
5f92659d BC |
22 | struct mesh_peer_mgmt_ie { |
23 | const u8 *proto_id; | |
24 | const u8 *llid; | |
25 | const u8 *plid; | |
26 | const u8 *reason; | |
27 | const u8 *pmk; | |
28 | }; | |
29 | ||
30 | static void plink_timer(void *eloop_ctx, void *user_data); | |
31 | ||
32 | ||
33 | enum plink_event { | |
34 | PLINK_UNDEFINED, | |
35 | OPN_ACPT, | |
36 | OPN_RJCT, | |
37 | OPN_IGNR, | |
38 | CNF_ACPT, | |
39 | CNF_RJCT, | |
40 | CNF_IGNR, | |
41 | CLS_ACPT, | |
42 | CLS_IGNR | |
43 | }; | |
44 | ||
45 | static const char * const mplstate[] = { | |
46 | [PLINK_LISTEN] = "LISTEN", | |
47 | [PLINK_OPEN_SENT] = "OPEN_SENT", | |
48 | [PLINK_OPEN_RCVD] = "OPEN_RCVD", | |
49 | [PLINK_CNF_RCVD] = "CNF_RCVD", | |
50 | [PLINK_ESTAB] = "ESTAB", | |
51 | [PLINK_HOLDING] = "HOLDING", | |
52 | [PLINK_BLOCKED] = "BLOCKED" | |
53 | }; | |
54 | ||
55 | static const char * const mplevent[] = { | |
56 | [PLINK_UNDEFINED] = "UNDEFINED", | |
57 | [OPN_ACPT] = "OPN_ACPT", | |
58 | [OPN_RJCT] = "OPN_RJCT", | |
59 | [OPN_IGNR] = "OPN_IGNR", | |
60 | [CNF_ACPT] = "CNF_ACPT", | |
61 | [CNF_RJCT] = "CNF_RJCT", | |
62 | [CNF_IGNR] = "CNF_IGNR", | |
63 | [CLS_ACPT] = "CLS_ACPT", | |
64 | [CLS_IGNR] = "CLS_IGNR" | |
65 | }; | |
66 | ||
67 | ||
68 | static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, | |
69 | u8 action_field, | |
70 | const u8 *ie, size_t len, | |
71 | struct mesh_peer_mgmt_ie *mpm_ie) | |
72 | { | |
73 | os_memset(mpm_ie, 0, sizeof(*mpm_ie)); | |
74 | ||
75 | /* remove optional PMK at end */ | |
76 | if (len >= 16) { | |
77 | len -= 16; | |
78 | mpm_ie->pmk = ie + len - 16; | |
79 | } | |
80 | ||
81 | if ((action_field == PLINK_OPEN && len != 4) || | |
82 | (action_field == PLINK_CONFIRM && len != 6) || | |
83 | (action_field == PLINK_CLOSE && len != 6 && len != 8)) { | |
84 | wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie"); | |
85 | return -1; | |
86 | } | |
87 | ||
88 | /* required fields */ | |
89 | if (len < 4) | |
90 | return -1; | |
91 | mpm_ie->proto_id = ie; | |
92 | mpm_ie->llid = ie + 2; | |
93 | ie += 4; | |
94 | len -= 4; | |
95 | ||
96 | /* close reason is always present at end for close */ | |
97 | if (action_field == PLINK_CLOSE) { | |
98 | if (len < 2) | |
99 | return -1; | |
100 | mpm_ie->reason = ie + len - 2; | |
101 | len -= 2; | |
102 | } | |
103 | ||
104 | /* plid, present for confirm, and possibly close */ | |
105 | if (len) | |
106 | mpm_ie->plid = ie; | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | ||
112 | static int plink_free_count(struct hostapd_data *hapd) | |
113 | { | |
114 | if (hapd->max_plinks > hapd->num_plinks) | |
115 | return hapd->max_plinks - hapd->num_plinks; | |
116 | return 0; | |
117 | } | |
118 | ||
119 | ||
120 | static u16 copy_supp_rates(struct wpa_supplicant *wpa_s, | |
121 | struct sta_info *sta, | |
122 | struct ieee802_11_elems *elems) | |
123 | { | |
124 | if (!elems->supp_rates) { | |
125 | wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR, | |
126 | MAC2STR(sta->addr)); | |
127 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | |
128 | } | |
129 | ||
130 | if (elems->supp_rates_len + elems->ext_supp_rates_len > | |
131 | sizeof(sta->supported_rates)) { | |
132 | wpa_msg(wpa_s, MSG_ERROR, | |
133 | "Invalid supported rates element length " MACSTR | |
134 | " %d+%d", MAC2STR(sta->addr), elems->supp_rates_len, | |
135 | elems->ext_supp_rates_len); | |
136 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | |
137 | } | |
138 | ||
139 | sta->supported_rates_len = merge_byte_arrays( | |
140 | sta->supported_rates, sizeof(sta->supported_rates), | |
141 | elems->supp_rates, elems->supp_rates_len, | |
142 | elems->ext_supp_rates, elems->ext_supp_rates_len); | |
143 | ||
144 | return WLAN_STATUS_SUCCESS; | |
145 | } | |
146 | ||
147 | ||
148 | /* return true if elems from a neighbor match this MBSS */ | |
149 | static Boolean matches_local(struct wpa_supplicant *wpa_s, | |
150 | struct ieee802_11_elems *elems) | |
151 | { | |
152 | struct mesh_conf *mconf = wpa_s->ifmsh->mconf; | |
153 | ||
154 | if (elems->mesh_config_len < 5) | |
155 | return FALSE; | |
156 | ||
157 | return (mconf->meshid_len == elems->mesh_id_len && | |
158 | os_memcmp(mconf->meshid, elems->mesh_id, | |
159 | elems->mesh_id_len) == 0 && | |
160 | mconf->mesh_pp_id == elems->mesh_config[0] && | |
161 | mconf->mesh_pm_id == elems->mesh_config[1] && | |
162 | mconf->mesh_cc_id == elems->mesh_config[2] && | |
163 | mconf->mesh_sp_id == elems->mesh_config[3] && | |
164 | mconf->mesh_auth_id == elems->mesh_config[4]); | |
165 | } | |
166 | ||
167 | ||
168 | /* check if local link id is already used with another peer */ | |
169 | static Boolean llid_in_use(struct wpa_supplicant *wpa_s, u16 llid) | |
170 | { | |
171 | struct sta_info *sta; | |
172 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
173 | ||
174 | for (sta = hapd->sta_list; sta; sta = sta->next) { | |
175 | if (sta->my_lid == llid) | |
176 | return TRUE; | |
177 | } | |
178 | ||
179 | return FALSE; | |
180 | } | |
181 | ||
182 | ||
183 | /* generate an llid for a link and set to initial state */ | |
184 | static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, | |
185 | struct sta_info *sta) | |
186 | { | |
187 | u16 llid; | |
188 | ||
189 | do { | |
190 | if (os_get_random((u8 *) &llid, sizeof(llid)) < 0) | |
191 | continue; | |
192 | } while (!llid || llid_in_use(wpa_s, llid)); | |
193 | ||
194 | sta->my_lid = llid; | |
195 | sta->peer_lid = 0; | |
196 | sta->plink_state = PLINK_LISTEN; | |
197 | } | |
198 | ||
199 | ||
200 | static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, | |
201 | struct sta_info *sta, | |
202 | enum plink_action_field type, | |
203 | u16 close_reason) | |
204 | { | |
205 | struct wpabuf *buf; | |
206 | struct hostapd_iface *ifmsh = wpa_s->ifmsh; | |
207 | struct hostapd_data *bss = ifmsh->bss[0]; | |
208 | struct mesh_conf *conf = ifmsh->mconf; | |
209 | u8 supp_rates[2 + 2 + 32]; | |
5cfb672d JM |
210 | #ifdef CONFIG_IEEE80211N |
211 | u8 ht_capa_oper[2 + 26 + 2 + 22]; | |
212 | #endif /* CONFIG_IEEE80211N */ | |
0f950df0 | 213 | u8 *pos, *cat; |
5f92659d BC |
214 | u8 ie_len, add_plid = 0; |
215 | int ret; | |
216 | int ampe = conf->security & MESH_CONF_SEC_AMPE; | |
798b3182 | 217 | size_t buf_len; |
5f92659d BC |
218 | |
219 | if (!sta) | |
220 | return; | |
221 | ||
798b3182 MH |
222 | buf_len = 2 + /* capability info */ |
223 | 2 + /* AID */ | |
224 | 2 + 8 + /* supported rates */ | |
225 | 2 + (32 - 8) + | |
226 | 2 + 32 + /* mesh ID */ | |
227 | 2 + 7 + /* mesh config */ | |
228 | 2 + 23 + /* peering management */ | |
229 | 2 + 96 + /* AMPE */ | |
230 | 2 + 16; /* MIC */ | |
231 | #ifdef CONFIG_IEEE80211N | |
232 | if (type != PLINK_CLOSE && | |
233 | wpa_s->current_ssid->mesh_ht_mode > CHAN_NO_HT) { | |
234 | buf_len += 2 + 26 + /* HT capabilities */ | |
235 | 2 + 22; /* HT operation */ | |
236 | } | |
237 | #endif /* CONFIG_IEEE80211N */ | |
238 | buf = wpabuf_alloc(buf_len); | |
5f92659d BC |
239 | if (!buf) |
240 | return; | |
241 | ||
0f950df0 | 242 | cat = wpabuf_mhead_u8(buf); |
5f92659d BC |
243 | wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED); |
244 | wpabuf_put_u8(buf, type); | |
245 | ||
246 | if (type != PLINK_CLOSE) { | |
d5b95325 MH |
247 | u8 info; |
248 | ||
5f92659d BC |
249 | /* capability info */ |
250 | wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0); | |
251 | ||
5cfb672d | 252 | /* aid */ |
5f92659d BC |
253 | if (type == PLINK_CONFIRM) |
254 | wpabuf_put_le16(buf, sta->peer_lid); | |
255 | ||
256 | /* IE: supp + ext. supp rates */ | |
257 | pos = hostapd_eid_supp_rates(bss, supp_rates); | |
258 | pos = hostapd_eid_ext_supp_rates(bss, pos); | |
259 | wpabuf_put_data(buf, supp_rates, pos - supp_rates); | |
260 | ||
261 | /* IE: Mesh ID */ | |
262 | wpabuf_put_u8(buf, WLAN_EID_MESH_ID); | |
263 | wpabuf_put_u8(buf, conf->meshid_len); | |
264 | wpabuf_put_data(buf, conf->meshid, conf->meshid_len); | |
265 | ||
266 | /* IE: mesh conf */ | |
267 | wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG); | |
268 | wpabuf_put_u8(buf, 7); | |
269 | wpabuf_put_u8(buf, conf->mesh_pp_id); | |
270 | wpabuf_put_u8(buf, conf->mesh_pm_id); | |
271 | wpabuf_put_u8(buf, conf->mesh_cc_id); | |
272 | wpabuf_put_u8(buf, conf->mesh_sp_id); | |
273 | wpabuf_put_u8(buf, conf->mesh_auth_id); | |
d5b95325 MH |
274 | info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1; |
275 | /* TODO: Add Connected to Mesh Gate/AS subfields */ | |
276 | wpabuf_put_u8(buf, info); | |
5f92659d BC |
277 | /* always forwarding & accepting plinks for now */ |
278 | wpabuf_put_u8(buf, 0x1 | 0x8); | |
279 | } else { /* Peer closing frame */ | |
280 | /* IE: Mesh ID */ | |
281 | wpabuf_put_u8(buf, WLAN_EID_MESH_ID); | |
282 | wpabuf_put_u8(buf, conf->meshid_len); | |
283 | wpabuf_put_data(buf, conf->meshid, conf->meshid_len); | |
284 | } | |
285 | ||
286 | /* IE: Mesh Peering Management element */ | |
287 | ie_len = 4; | |
288 | if (ampe) | |
289 | ie_len += PMKID_LEN; | |
290 | switch (type) { | |
291 | case PLINK_OPEN: | |
292 | break; | |
293 | case PLINK_CONFIRM: | |
294 | ie_len += 2; | |
295 | add_plid = 1; | |
296 | break; | |
297 | case PLINK_CLOSE: | |
298 | ie_len += 2; | |
299 | add_plid = 1; | |
300 | ie_len += 2; /* reason code */ | |
301 | break; | |
302 | } | |
303 | ||
304 | wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT); | |
305 | wpabuf_put_u8(buf, ie_len); | |
306 | /* peering protocol */ | |
307 | if (ampe) | |
308 | wpabuf_put_le16(buf, 1); | |
309 | else | |
310 | wpabuf_put_le16(buf, 0); | |
311 | wpabuf_put_le16(buf, sta->my_lid); | |
312 | if (add_plid) | |
313 | wpabuf_put_le16(buf, sta->peer_lid); | |
314 | if (type == PLINK_CLOSE) | |
315 | wpabuf_put_le16(buf, close_reason); | |
608b0ff5 JM |
316 | if (ampe) { |
317 | if (sta->sae == NULL) { | |
318 | wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session"); | |
319 | goto fail; | |
320 | } | |
0f950df0 TP |
321 | mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta, |
322 | wpabuf_put(buf, PMKID_LEN)); | |
608b0ff5 | 323 | } |
5f92659d | 324 | |
5cfb672d JM |
325 | #ifdef CONFIG_IEEE80211N |
326 | if (type != PLINK_CLOSE && | |
9204e738 | 327 | wpa_s->current_ssid->mesh_ht_mode > CHAN_NO_HT) { |
5cfb672d JM |
328 | pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); |
329 | pos = hostapd_eid_ht_operation(bss, pos); | |
330 | wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); | |
331 | } | |
332 | #endif /* CONFIG_IEEE80211N */ | |
5f92659d | 333 | |
0f950df0 TP |
334 | if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { |
335 | wpa_msg(wpa_s, MSG_INFO, | |
336 | "Mesh MPM: failed to add AMPE and MIC IE"); | |
337 | goto fail; | |
338 | } | |
339 | ||
5f92659d BC |
340 | ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, |
341 | sta->addr, wpa_s->own_addr, wpa_s->own_addr, | |
342 | wpabuf_head(buf), wpabuf_len(buf), 0); | |
343 | if (ret < 0) | |
344 | wpa_msg(wpa_s, MSG_INFO, | |
345 | "Mesh MPM: failed to send peering frame"); | |
346 | ||
0f950df0 | 347 | fail: |
5f92659d BC |
348 | wpabuf_free(buf); |
349 | } | |
350 | ||
351 | ||
352 | /* configure peering state in ours and driver's station entry */ | |
353 | static void | |
354 | wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, | |
355 | enum mesh_plink_state state) | |
356 | { | |
357 | struct hostapd_sta_add_params params; | |
358 | int ret; | |
359 | ||
360 | sta->plink_state = state; | |
361 | ||
362 | os_memset(¶ms, 0, sizeof(params)); | |
363 | params.addr = sta->addr; | |
364 | params.plink_state = state; | |
365 | params.set = 1; | |
366 | ||
367 | wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s", | |
368 | MAC2STR(sta->addr), mplstate[state]); | |
369 | ret = wpa_drv_sta_add(wpa_s, ¶ms); | |
370 | if (ret) { | |
371 | wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR | |
372 | ": %d", MAC2STR(sta->addr), ret); | |
373 | } | |
374 | } | |
375 | ||
376 | ||
377 | static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s, | |
378 | struct sta_info *sta) | |
379 | { | |
380 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
381 | ||
382 | eloop_cancel_timeout(plink_timer, wpa_s, sta); | |
383 | ||
384 | if (sta->mpm_close_reason == WLAN_REASON_MESH_CLOSE_RCVD) { | |
385 | ap_free_sta(hapd, sta); | |
386 | return; | |
387 | } | |
388 | ||
389 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_LISTEN); | |
390 | sta->my_lid = sta->peer_lid = sta->mpm_close_reason = 0; | |
391 | sta->mpm_retries = 0; | |
392 | } | |
393 | ||
394 | ||
395 | static void plink_timer(void *eloop_ctx, void *user_data) | |
396 | { | |
397 | struct wpa_supplicant *wpa_s = eloop_ctx; | |
398 | struct sta_info *sta = user_data; | |
399 | u16 reason = 0; | |
e6096799 | 400 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
5f92659d BC |
401 | |
402 | switch (sta->plink_state) { | |
403 | case PLINK_OPEN_RCVD: | |
404 | case PLINK_OPEN_SENT: | |
405 | /* retry timer */ | |
e6096799 MH |
406 | if (sta->mpm_retries < conf->dot11MeshMaxRetries) { |
407 | eloop_register_timeout( | |
408 | conf->dot11MeshRetryTimeout / 1000, | |
409 | (conf->dot11MeshRetryTimeout % 1000) * 1000, | |
410 | plink_timer, wpa_s, sta); | |
5f92659d BC |
411 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); |
412 | sta->mpm_retries++; | |
413 | break; | |
414 | } | |
415 | reason = WLAN_REASON_MESH_MAX_RETRIES; | |
416 | /* fall through on else */ | |
417 | ||
418 | case PLINK_CNF_RCVD: | |
419 | /* confirm timer */ | |
420 | if (!reason) | |
421 | reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; | |
422 | sta->plink_state = PLINK_HOLDING; | |
e6096799 MH |
423 | eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000, |
424 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
425 | plink_timer, wpa_s, sta); | |
5f92659d BC |
426 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); |
427 | break; | |
428 | case PLINK_HOLDING: | |
429 | /* holding timer */ | |
430 | mesh_mpm_fsm_restart(wpa_s, sta); | |
431 | break; | |
432 | default: | |
433 | break; | |
434 | } | |
435 | } | |
436 | ||
437 | ||
438 | /* initiate peering with station */ | |
439 | static void | |
440 | mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta, | |
441 | enum mesh_plink_state next_state) | |
442 | { | |
e6096799 MH |
443 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
444 | ||
5f92659d | 445 | eloop_cancel_timeout(plink_timer, wpa_s, sta); |
e6096799 MH |
446 | eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000, |
447 | (conf->dot11MeshRetryTimeout % 1000) * 1000, | |
448 | plink_timer, wpa_s, sta); | |
5f92659d BC |
449 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); |
450 | wpa_mesh_set_plink_state(wpa_s, sta, next_state); | |
451 | } | |
452 | ||
453 | ||
454 | int mesh_mpm_plink_close(struct hostapd_data *hapd, | |
455 | struct sta_info *sta, void *ctx) | |
456 | { | |
457 | struct wpa_supplicant *wpa_s = ctx; | |
458 | int reason = WLAN_REASON_MESH_PEERING_CANCELLED; | |
459 | ||
460 | if (sta) { | |
461 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
462 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); | |
463 | wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR, | |
464 | MAC2STR(sta->addr)); | |
465 | eloop_cancel_timeout(plink_timer, wpa_s, sta); | |
466 | return 0; | |
467 | } | |
468 | ||
469 | return 1; | |
470 | } | |
471 | ||
472 | ||
473 | void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) | |
474 | { | |
475 | struct hostapd_data *hapd = ifmsh->bss[0]; | |
476 | ||
477 | /* notify peers we're leaving */ | |
478 | ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s); | |
479 | ||
480 | hapd->num_plinks = 0; | |
481 | hostapd_free_stas(hapd); | |
482 | } | |
483 | ||
484 | ||
485 | /* for mesh_rsn to indicate this peer has completed authentication, and we're | |
486 | * ready to start AMPE */ | |
487 | void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) | |
488 | { | |
489 | struct hostapd_data *data = wpa_s->ifmsh->bss[0]; | |
490 | struct hostapd_sta_add_params params; | |
491 | struct sta_info *sta; | |
492 | int ret; | |
493 | ||
494 | sta = ap_get_sta(data, addr); | |
495 | if (!sta) { | |
496 | wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer"); | |
497 | return; | |
498 | } | |
499 | ||
500 | /* TODO: Should do nothing if this STA is already authenticated, but | |
501 | * the AP code already sets this flag. */ | |
502 | sta->flags |= WLAN_STA_AUTH; | |
503 | ||
0f950df0 TP |
504 | mesh_rsn_init_ampe_sta(wpa_s, sta); |
505 | ||
5f92659d BC |
506 | os_memset(¶ms, 0, sizeof(params)); |
507 | params.addr = sta->addr; | |
508 | params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED; | |
509 | params.set = 1; | |
510 | ||
511 | wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR, | |
512 | MAC2STR(sta->addr)); | |
513 | ret = wpa_drv_sta_add(wpa_s, ¶ms); | |
514 | if (ret) { | |
515 | wpa_msg(wpa_s, MSG_ERROR, | |
516 | "Driver failed to set " MACSTR ": %d", | |
517 | MAC2STR(sta->addr), ret); | |
518 | } | |
519 | ||
520 | if (!sta->my_lid) | |
521 | mesh_mpm_init_link(wpa_s, sta); | |
522 | ||
523 | mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); | |
524 | } | |
525 | ||
526 | ||
527 | void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, | |
528 | struct ieee802_11_elems *elems) | |
529 | { | |
530 | struct hostapd_sta_add_params params; | |
531 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; | |
532 | struct hostapd_data *data = wpa_s->ifmsh->bss[0]; | |
533 | struct sta_info *sta; | |
07cb45cc | 534 | struct wpa_ssid *ssid = wpa_s->current_ssid; |
5f92659d BC |
535 | int ret = 0; |
536 | ||
537 | sta = ap_get_sta(data, addr); | |
538 | if (!sta) { | |
539 | sta = ap_sta_add(data, addr); | |
540 | if (!sta) | |
541 | return; | |
542 | } | |
543 | ||
544 | /* initialize sta */ | |
545 | if (copy_supp_rates(wpa_s, sta, elems)) | |
546 | return; | |
547 | ||
548 | mesh_mpm_init_link(wpa_s, sta); | |
549 | ||
5cfb672d JM |
550 | #ifdef CONFIG_IEEE80211N |
551 | copy_sta_ht_capab(data, sta, elems->ht_capabilities, | |
552 | elems->ht_capabilities_len); | |
553 | update_ht_state(data, sta); | |
554 | #endif /* CONFIG_IEEE80211N */ | |
555 | ||
5f92659d BC |
556 | /* insert into driver */ |
557 | os_memset(¶ms, 0, sizeof(params)); | |
558 | params.supp_rates = sta->supported_rates; | |
559 | params.supp_rates_len = sta->supported_rates_len; | |
560 | params.addr = addr; | |
561 | params.plink_state = sta->plink_state; | |
562 | params.aid = sta->peer_lid; | |
563 | params.listen_interval = 100; | |
5cfb672d | 564 | params.ht_capabilities = sta->ht_capabilities; |
5f92659d BC |
565 | params.flags |= WPA_STA_WMM; |
566 | params.flags_mask |= WPA_STA_AUTHENTICATED; | |
567 | if (conf->security == MESH_CONF_SEC_NONE) { | |
568 | params.flags |= WPA_STA_AUTHORIZED; | |
569 | params.flags |= WPA_STA_AUTHENTICATED; | |
570 | } else { | |
571 | sta->flags |= WLAN_STA_MFP; | |
572 | params.flags |= WPA_STA_MFP; | |
573 | } | |
574 | ||
575 | ret = wpa_drv_sta_add(wpa_s, ¶ms); | |
576 | if (ret) { | |
577 | wpa_msg(wpa_s, MSG_ERROR, | |
578 | "Driver failed to insert " MACSTR ": %d", | |
579 | MAC2STR(addr), ret); | |
580 | return; | |
581 | } | |
582 | ||
07cb45cc TP |
583 | if (ssid && ssid->no_auto_peer) { |
584 | wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " | |
585 | MACSTR " because of no_auto_peer", MAC2STR(addr)); | |
3a322496 JM |
586 | if (data->mesh_pending_auth) { |
587 | struct os_reltime age; | |
588 | const struct ieee80211_mgmt *mgmt; | |
589 | struct hostapd_frame_info fi; | |
590 | ||
591 | mgmt = wpabuf_head(data->mesh_pending_auth); | |
592 | os_reltime_age(&data->mesh_pending_auth_time, &age); | |
593 | if (age.sec < 2 && | |
594 | os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) { | |
595 | wpa_printf(MSG_DEBUG, | |
596 | "mesh: Process pending Authentication frame from %u.%06u seconds ago", | |
597 | (unsigned int) age.sec, | |
598 | (unsigned int) age.usec); | |
599 | os_memset(&fi, 0, sizeof(fi)); | |
600 | ieee802_11_mgmt( | |
601 | data, | |
602 | wpabuf_head(data->mesh_pending_auth), | |
603 | wpabuf_len(data->mesh_pending_auth), | |
604 | &fi); | |
605 | } | |
606 | wpabuf_free(data->mesh_pending_auth); | |
607 | data->mesh_pending_auth = NULL; | |
608 | } | |
07cb45cc TP |
609 | return; |
610 | } | |
611 | ||
0f950df0 TP |
612 | if (conf->security == MESH_CONF_SEC_NONE) |
613 | mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); | |
614 | else | |
615 | mesh_rsn_auth_sae_sta(wpa_s, sta); | |
5f92659d BC |
616 | } |
617 | ||
618 | ||
619 | void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt) | |
620 | { | |
621 | struct hostapd_frame_info fi; | |
622 | ||
623 | os_memset(&fi, 0, sizeof(fi)); | |
624 | fi.datarate = rx_mgmt->datarate; | |
625 | fi.ssi_signal = rx_mgmt->ssi_signal; | |
626 | ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame, | |
627 | rx_mgmt->frame_len, &fi); | |
628 | } | |
629 | ||
630 | ||
631 | static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, | |
632 | struct sta_info *sta) | |
633 | { | |
634 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
0f950df0 TP |
635 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
636 | u8 seq[6] = {}; | |
5f92659d BC |
637 | |
638 | wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established", | |
639 | MAC2STR(sta->addr)); | |
640 | ||
0f950df0 TP |
641 | if (conf->security & MESH_CONF_SEC_AMPE) { |
642 | wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0, | |
643 | seq, sizeof(seq), sta->mtk, sizeof(sta->mtk)); | |
644 | wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0, | |
645 | seq, sizeof(seq), | |
646 | sta->mgtk, sizeof(sta->mgtk)); | |
647 | wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0, | |
648 | seq, sizeof(seq), | |
649 | sta->mgtk, sizeof(sta->mgtk)); | |
650 | ||
651 | wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk)); | |
652 | wpa_hexdump_key(MSG_DEBUG, "mgtk:", | |
653 | sta->mgtk, sizeof(sta->mgtk)); | |
654 | } | |
655 | ||
5f92659d BC |
656 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB); |
657 | hapd->num_plinks++; | |
658 | ||
659 | sta->flags |= WLAN_STA_ASSOC; | |
660 | ||
661 | eloop_cancel_timeout(plink_timer, wpa_s, sta); | |
662 | ||
663 | /* Send ctrl event */ | |
664 | wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, | |
665 | MAC2STR(sta->addr)); | |
666 | } | |
667 | ||
668 | ||
669 | static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, | |
670 | enum plink_event event) | |
671 | { | |
672 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
0f950df0 | 673 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
5f92659d BC |
674 | u16 reason = 0; |
675 | ||
676 | wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s", | |
677 | MAC2STR(sta->addr), mplstate[sta->plink_state], | |
678 | mplevent[event]); | |
679 | ||
680 | switch (sta->plink_state) { | |
681 | case PLINK_LISTEN: | |
682 | switch (event) { | |
683 | case CLS_ACPT: | |
684 | mesh_mpm_fsm_restart(wpa_s, sta); | |
685 | break; | |
686 | case OPN_ACPT: | |
687 | mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD); | |
688 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, | |
689 | 0); | |
690 | break; | |
691 | default: | |
692 | break; | |
693 | } | |
694 | break; | |
695 | case PLINK_OPEN_SENT: | |
696 | switch (event) { | |
697 | case OPN_RJCT: | |
698 | case CNF_RJCT: | |
699 | reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; | |
700 | /* fall-through */ | |
701 | case CLS_ACPT: | |
702 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
703 | if (!reason) | |
704 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
e6096799 MH |
705 | eloop_register_timeout( |
706 | conf->dot11MeshHoldingTimeout / 1000, | |
707 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
708 | plink_timer, wpa_s, sta); | |
5f92659d BC |
709 | mesh_mpm_send_plink_action(wpa_s, sta, |
710 | PLINK_CLOSE, reason); | |
711 | break; | |
712 | case OPN_ACPT: | |
713 | /* retry timer is left untouched */ | |
714 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD); | |
715 | mesh_mpm_send_plink_action(wpa_s, sta, | |
716 | PLINK_CONFIRM, 0); | |
717 | break; | |
718 | case CNF_ACPT: | |
719 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD); | |
e6096799 MH |
720 | eloop_register_timeout( |
721 | conf->dot11MeshConfirmTimeout / 1000, | |
722 | (conf->dot11MeshConfirmTimeout % 1000) * 1000, | |
723 | plink_timer, wpa_s, sta); | |
5f92659d BC |
724 | break; |
725 | default: | |
726 | break; | |
727 | } | |
728 | break; | |
729 | case PLINK_OPEN_RCVD: | |
730 | switch (event) { | |
731 | case OPN_RJCT: | |
732 | case CNF_RJCT: | |
733 | reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; | |
734 | /* fall-through */ | |
735 | case CLS_ACPT: | |
736 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
737 | if (!reason) | |
738 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
e6096799 MH |
739 | eloop_register_timeout( |
740 | conf->dot11MeshHoldingTimeout / 1000, | |
741 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
742 | plink_timer, wpa_s, sta); | |
5f92659d BC |
743 | sta->mpm_close_reason = reason; |
744 | mesh_mpm_send_plink_action(wpa_s, sta, | |
745 | PLINK_CLOSE, reason); | |
746 | break; | |
747 | case OPN_ACPT: | |
748 | mesh_mpm_send_plink_action(wpa_s, sta, | |
749 | PLINK_CONFIRM, 0); | |
750 | break; | |
751 | case CNF_ACPT: | |
0f950df0 TP |
752 | if (conf->security & MESH_CONF_SEC_AMPE) |
753 | mesh_rsn_derive_mtk(wpa_s, sta); | |
5f92659d BC |
754 | mesh_mpm_plink_estab(wpa_s, sta); |
755 | break; | |
756 | default: | |
757 | break; | |
758 | } | |
759 | break; | |
760 | case PLINK_CNF_RCVD: | |
761 | switch (event) { | |
762 | case OPN_RJCT: | |
763 | case CNF_RJCT: | |
764 | reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; | |
765 | /* fall-through */ | |
766 | case CLS_ACPT: | |
767 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
768 | if (!reason) | |
769 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
e6096799 MH |
770 | eloop_register_timeout( |
771 | conf->dot11MeshHoldingTimeout / 1000, | |
772 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
773 | plink_timer, wpa_s, sta); | |
5f92659d BC |
774 | sta->mpm_close_reason = reason; |
775 | mesh_mpm_send_plink_action(wpa_s, sta, | |
776 | PLINK_CLOSE, reason); | |
777 | break; | |
778 | case OPN_ACPT: | |
779 | mesh_mpm_plink_estab(wpa_s, sta); | |
780 | mesh_mpm_send_plink_action(wpa_s, sta, | |
781 | PLINK_CONFIRM, 0); | |
782 | break; | |
783 | default: | |
784 | break; | |
785 | } | |
786 | break; | |
787 | case PLINK_ESTAB: | |
788 | switch (event) { | |
789 | case CLS_ACPT: | |
790 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
791 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
792 | ||
e6096799 MH |
793 | eloop_register_timeout( |
794 | conf->dot11MeshHoldingTimeout / 1000, | |
795 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
796 | plink_timer, wpa_s, sta); | |
5f92659d BC |
797 | sta->mpm_close_reason = reason; |
798 | ||
799 | wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR | |
800 | " closed with reason %d", | |
801 | MAC2STR(sta->addr), reason); | |
802 | ||
803 | wpa_msg_ctrl(wpa_s, MSG_INFO, | |
804 | MESH_PEER_DISCONNECTED MACSTR, | |
805 | MAC2STR(sta->addr)); | |
806 | ||
807 | hapd->num_plinks--; | |
808 | ||
809 | mesh_mpm_send_plink_action(wpa_s, sta, | |
810 | PLINK_CLOSE, reason); | |
811 | break; | |
812 | case OPN_ACPT: | |
813 | mesh_mpm_send_plink_action(wpa_s, sta, | |
814 | PLINK_CONFIRM, 0); | |
815 | break; | |
816 | default: | |
817 | break; | |
818 | } | |
819 | break; | |
820 | case PLINK_HOLDING: | |
821 | switch (event) { | |
822 | case CLS_ACPT: | |
823 | mesh_mpm_fsm_restart(wpa_s, sta); | |
824 | break; | |
825 | case OPN_ACPT: | |
826 | case CNF_ACPT: | |
827 | case OPN_RJCT: | |
828 | case CNF_RJCT: | |
829 | reason = sta->mpm_close_reason; | |
830 | mesh_mpm_send_plink_action(wpa_s, sta, | |
831 | PLINK_CLOSE, reason); | |
832 | break; | |
833 | default: | |
834 | break; | |
835 | } | |
836 | break; | |
837 | default: | |
838 | wpa_msg(wpa_s, MSG_DEBUG, | |
839 | "Unsupported MPM event %s for state %s", | |
840 | mplevent[event], mplstate[sta->plink_state]); | |
841 | break; | |
842 | } | |
843 | } | |
844 | ||
845 | ||
846 | void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, | |
847 | const struct ieee80211_mgmt *mgmt, size_t len) | |
848 | { | |
849 | u8 action_field; | |
850 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
0f950df0 | 851 | struct mesh_conf *mconf = wpa_s->ifmsh->mconf; |
5f92659d BC |
852 | struct sta_info *sta; |
853 | u16 plid = 0, llid = 0; | |
854 | enum plink_event event; | |
855 | struct ieee802_11_elems elems; | |
856 | struct mesh_peer_mgmt_ie peer_mgmt_ie; | |
857 | const u8 *ies; | |
858 | size_t ie_len; | |
859 | int ret; | |
860 | ||
861 | if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED) | |
862 | return; | |
863 | ||
864 | action_field = mgmt->u.action.u.slf_prot_action.action; | |
c932b43e JM |
865 | if (action_field != PLINK_OPEN && |
866 | action_field != PLINK_CONFIRM && | |
867 | action_field != PLINK_CLOSE) | |
868 | return; | |
5f92659d BC |
869 | |
870 | ies = mgmt->u.action.u.slf_prot_action.variable; | |
871 | ie_len = (const u8 *) mgmt + len - | |
872 | mgmt->u.action.u.slf_prot_action.variable; | |
873 | ||
874 | /* at least expect mesh id and peering mgmt */ | |
c932b43e JM |
875 | if (ie_len < 2 + 2) { |
876 | wpa_printf(MSG_DEBUG, | |
877 | "MPM: Ignore too short action frame %u ie_len %u", | |
878 | action_field, (unsigned int) ie_len); | |
5f92659d | 879 | return; |
c932b43e JM |
880 | } |
881 | wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field); | |
5f92659d BC |
882 | |
883 | if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) { | |
c932b43e JM |
884 | wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x", |
885 | WPA_GET_LE16(ies)); | |
5f92659d BC |
886 | ies += 2; /* capability */ |
887 | ie_len -= 2; | |
888 | } | |
889 | if (action_field == PLINK_CONFIRM) { | |
c932b43e | 890 | wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies)); |
5f92659d BC |
891 | ies += 2; /* aid */ |
892 | ie_len -= 2; | |
893 | } | |
894 | ||
895 | /* check for mesh peering, mesh id and mesh config IEs */ | |
c932b43e JM |
896 | if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { |
897 | wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs"); | |
5f92659d | 898 | return; |
c932b43e JM |
899 | } |
900 | if (!elems.peer_mgmt) { | |
901 | wpa_printf(MSG_DEBUG, | |
902 | "MPM: No Mesh Peering Management element"); | |
5f92659d | 903 | return; |
c932b43e JM |
904 | } |
905 | if (action_field != PLINK_CLOSE) { | |
906 | if (!elems.mesh_id || !elems.mesh_config) { | |
907 | wpa_printf(MSG_DEBUG, | |
908 | "MPM: No Mesh ID or Mesh Configuration element"); | |
909 | return; | |
910 | } | |
5f92659d | 911 | |
c932b43e JM |
912 | if (!matches_local(wpa_s, &elems)) { |
913 | wpa_printf(MSG_DEBUG, | |
914 | "MPM: Mesh ID or Mesh Configuration element do not match local MBSS"); | |
915 | return; | |
916 | } | |
917 | } | |
5f92659d BC |
918 | |
919 | ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field, | |
920 | elems.peer_mgmt, | |
921 | elems.peer_mgmt_len, | |
922 | &peer_mgmt_ie); | |
c932b43e JM |
923 | if (ret) { |
924 | wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame"); | |
5f92659d | 925 | return; |
c932b43e | 926 | } |
5f92659d BC |
927 | |
928 | /* the sender's llid is our plid and vice-versa */ | |
929 | plid = WPA_GET_LE16(peer_mgmt_ie.llid); | |
930 | if (peer_mgmt_ie.plid) | |
931 | llid = WPA_GET_LE16(peer_mgmt_ie.plid); | |
c932b43e | 932 | wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); |
5f92659d BC |
933 | |
934 | sta = ap_get_sta(hapd, mgmt->sa); | |
c932b43e JM |
935 | if (!sta) { |
936 | wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer"); | |
5f92659d | 937 | return; |
c932b43e | 938 | } |
5f92659d | 939 | |
0f950df0 TP |
940 | #ifdef CONFIG_SAE |
941 | /* peer is in sae_accepted? */ | |
c932b43e JM |
942 | if (sta->sae && sta->sae->state != SAE_ACCEPTED) { |
943 | wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer"); | |
0f950df0 | 944 | return; |
c932b43e | 945 | } |
0f950df0 TP |
946 | #endif /* CONFIG_SAE */ |
947 | ||
5f92659d BC |
948 | if (!sta->my_lid) |
949 | mesh_mpm_init_link(wpa_s, sta); | |
950 | ||
c932b43e JM |
951 | if ((mconf->security & MESH_CONF_SEC_AMPE) && |
952 | mesh_rsn_process_ampe(wpa_s, sta, &elems, | |
953 | &mgmt->u.action.category, | |
954 | ies, ie_len)) { | |
955 | wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); | |
956 | return; | |
957 | } | |
0f950df0 | 958 | |
c932b43e JM |
959 | if (sta->plink_state == PLINK_BLOCKED) { |
960 | wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED"); | |
5f92659d | 961 | return; |
c932b43e | 962 | } |
5f92659d BC |
963 | |
964 | /* Now we will figure out the appropriate event... */ | |
965 | switch (action_field) { | |
966 | case PLINK_OPEN: | |
46e8d90f MH |
967 | if (plink_free_count(hapd) == 0) { |
968 | event = OPN_IGNR; | |
969 | wpa_printf(MSG_INFO, | |
970 | "MPM: Peer link num over quota(%d)", | |
971 | hapd->max_plinks); | |
972 | } else if (sta->peer_lid && sta->peer_lid != plid) { | |
5f92659d BC |
973 | event = OPN_IGNR; |
974 | } else { | |
975 | sta->peer_lid = plid; | |
976 | event = OPN_ACPT; | |
977 | } | |
978 | break; | |
979 | case PLINK_CONFIRM: | |
46e8d90f MH |
980 | if (plink_free_count(hapd) == 0) { |
981 | event = CNF_IGNR; | |
982 | wpa_printf(MSG_INFO, | |
983 | "MPM: Peer link num over quota(%d)", | |
984 | hapd->max_plinks); | |
985 | } else if (sta->my_lid != llid || | |
986 | (sta->peer_lid && sta->peer_lid != plid)) { | |
5f92659d BC |
987 | event = CNF_IGNR; |
988 | } else { | |
989 | if (!sta->peer_lid) | |
990 | sta->peer_lid = plid; | |
991 | event = CNF_ACPT; | |
992 | } | |
993 | break; | |
994 | case PLINK_CLOSE: | |
995 | if (sta->plink_state == PLINK_ESTAB) | |
996 | /* Do not check for llid or plid. This does not | |
997 | * follow the standard but since multiple plinks | |
998 | * per cand are not supported, it is necessary in | |
999 | * order to avoid a livelock when MP A sees an | |
1000 | * establish peer link to MP B but MP B does not | |
1001 | * see it. This can be caused by a timeout in | |
1002 | * B's peer link establishment or B being | |
1003 | * restarted. | |
1004 | */ | |
1005 | event = CLS_ACPT; | |
1006 | else if (sta->peer_lid != plid) | |
1007 | event = CLS_IGNR; | |
1008 | else if (peer_mgmt_ie.plid && sta->my_lid != llid) | |
1009 | event = CLS_IGNR; | |
1010 | else | |
1011 | event = CLS_ACPT; | |
1012 | break; | |
763041b2 JM |
1013 | default: |
1014 | /* | |
1015 | * This cannot be hit due to the action_field check above, but | |
1016 | * compilers may not be able to figure that out and can warn | |
1017 | * about uninitialized event below. | |
1018 | */ | |
1019 | return; | |
5f92659d BC |
1020 | } |
1021 | mesh_mpm_fsm(wpa_s, sta, event); | |
1022 | } | |
c596f3f0 CYY |
1023 | |
1024 | ||
1025 | /* called by ap_free_sta */ | |
1026 | void mesh_mpm_free_sta(struct sta_info *sta) | |
1027 | { | |
1028 | eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta); | |
1029 | eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta); | |
1030 | } |