]>
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) { | |
247 | /* capability info */ | |
248 | wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0); | |
249 | ||
5cfb672d | 250 | /* aid */ |
5f92659d BC |
251 | if (type == PLINK_CONFIRM) |
252 | wpabuf_put_le16(buf, sta->peer_lid); | |
253 | ||
254 | /* IE: supp + ext. supp rates */ | |
255 | pos = hostapd_eid_supp_rates(bss, supp_rates); | |
256 | pos = hostapd_eid_ext_supp_rates(bss, pos); | |
257 | wpabuf_put_data(buf, supp_rates, pos - supp_rates); | |
258 | ||
259 | /* IE: Mesh ID */ | |
260 | wpabuf_put_u8(buf, WLAN_EID_MESH_ID); | |
261 | wpabuf_put_u8(buf, conf->meshid_len); | |
262 | wpabuf_put_data(buf, conf->meshid, conf->meshid_len); | |
263 | ||
264 | /* IE: mesh conf */ | |
265 | wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG); | |
266 | wpabuf_put_u8(buf, 7); | |
267 | wpabuf_put_u8(buf, conf->mesh_pp_id); | |
268 | wpabuf_put_u8(buf, conf->mesh_pm_id); | |
269 | wpabuf_put_u8(buf, conf->mesh_cc_id); | |
270 | wpabuf_put_u8(buf, conf->mesh_sp_id); | |
271 | wpabuf_put_u8(buf, conf->mesh_auth_id); | |
272 | /* TODO: formation info */ | |
273 | wpabuf_put_u8(buf, 0); | |
274 | /* always forwarding & accepting plinks for now */ | |
275 | wpabuf_put_u8(buf, 0x1 | 0x8); | |
276 | } else { /* Peer closing frame */ | |
277 | /* IE: Mesh ID */ | |
278 | wpabuf_put_u8(buf, WLAN_EID_MESH_ID); | |
279 | wpabuf_put_u8(buf, conf->meshid_len); | |
280 | wpabuf_put_data(buf, conf->meshid, conf->meshid_len); | |
281 | } | |
282 | ||
283 | /* IE: Mesh Peering Management element */ | |
284 | ie_len = 4; | |
285 | if (ampe) | |
286 | ie_len += PMKID_LEN; | |
287 | switch (type) { | |
288 | case PLINK_OPEN: | |
289 | break; | |
290 | case PLINK_CONFIRM: | |
291 | ie_len += 2; | |
292 | add_plid = 1; | |
293 | break; | |
294 | case PLINK_CLOSE: | |
295 | ie_len += 2; | |
296 | add_plid = 1; | |
297 | ie_len += 2; /* reason code */ | |
298 | break; | |
299 | } | |
300 | ||
301 | wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT); | |
302 | wpabuf_put_u8(buf, ie_len); | |
303 | /* peering protocol */ | |
304 | if (ampe) | |
305 | wpabuf_put_le16(buf, 1); | |
306 | else | |
307 | wpabuf_put_le16(buf, 0); | |
308 | wpabuf_put_le16(buf, sta->my_lid); | |
309 | if (add_plid) | |
310 | wpabuf_put_le16(buf, sta->peer_lid); | |
311 | if (type == PLINK_CLOSE) | |
312 | wpabuf_put_le16(buf, close_reason); | |
608b0ff5 JM |
313 | if (ampe) { |
314 | if (sta->sae == NULL) { | |
315 | wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session"); | |
316 | goto fail; | |
317 | } | |
0f950df0 TP |
318 | mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta, |
319 | wpabuf_put(buf, PMKID_LEN)); | |
608b0ff5 | 320 | } |
5f92659d | 321 | |
5cfb672d JM |
322 | #ifdef CONFIG_IEEE80211N |
323 | if (type != PLINK_CLOSE && | |
9204e738 | 324 | wpa_s->current_ssid->mesh_ht_mode > CHAN_NO_HT) { |
5cfb672d JM |
325 | pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); |
326 | pos = hostapd_eid_ht_operation(bss, pos); | |
327 | wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); | |
328 | } | |
329 | #endif /* CONFIG_IEEE80211N */ | |
5f92659d | 330 | |
0f950df0 TP |
331 | if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { |
332 | wpa_msg(wpa_s, MSG_INFO, | |
333 | "Mesh MPM: failed to add AMPE and MIC IE"); | |
334 | goto fail; | |
335 | } | |
336 | ||
5f92659d BC |
337 | ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, |
338 | sta->addr, wpa_s->own_addr, wpa_s->own_addr, | |
339 | wpabuf_head(buf), wpabuf_len(buf), 0); | |
340 | if (ret < 0) | |
341 | wpa_msg(wpa_s, MSG_INFO, | |
342 | "Mesh MPM: failed to send peering frame"); | |
343 | ||
0f950df0 | 344 | fail: |
5f92659d BC |
345 | wpabuf_free(buf); |
346 | } | |
347 | ||
348 | ||
349 | /* configure peering state in ours and driver's station entry */ | |
350 | static void | |
351 | wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, | |
352 | enum mesh_plink_state state) | |
353 | { | |
354 | struct hostapd_sta_add_params params; | |
355 | int ret; | |
356 | ||
357 | sta->plink_state = state; | |
358 | ||
359 | os_memset(¶ms, 0, sizeof(params)); | |
360 | params.addr = sta->addr; | |
361 | params.plink_state = state; | |
362 | params.set = 1; | |
363 | ||
364 | wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s", | |
365 | MAC2STR(sta->addr), mplstate[state]); | |
366 | ret = wpa_drv_sta_add(wpa_s, ¶ms); | |
367 | if (ret) { | |
368 | wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR | |
369 | ": %d", MAC2STR(sta->addr), ret); | |
370 | } | |
371 | } | |
372 | ||
373 | ||
374 | static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s, | |
375 | struct sta_info *sta) | |
376 | { | |
377 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
378 | ||
379 | eloop_cancel_timeout(plink_timer, wpa_s, sta); | |
380 | ||
381 | if (sta->mpm_close_reason == WLAN_REASON_MESH_CLOSE_RCVD) { | |
382 | ap_free_sta(hapd, sta); | |
383 | return; | |
384 | } | |
385 | ||
386 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_LISTEN); | |
387 | sta->my_lid = sta->peer_lid = sta->mpm_close_reason = 0; | |
388 | sta->mpm_retries = 0; | |
389 | } | |
390 | ||
391 | ||
392 | static void plink_timer(void *eloop_ctx, void *user_data) | |
393 | { | |
394 | struct wpa_supplicant *wpa_s = eloop_ctx; | |
395 | struct sta_info *sta = user_data; | |
396 | u16 reason = 0; | |
e6096799 | 397 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
5f92659d BC |
398 | |
399 | switch (sta->plink_state) { | |
400 | case PLINK_OPEN_RCVD: | |
401 | case PLINK_OPEN_SENT: | |
402 | /* retry timer */ | |
e6096799 MH |
403 | if (sta->mpm_retries < conf->dot11MeshMaxRetries) { |
404 | eloop_register_timeout( | |
405 | conf->dot11MeshRetryTimeout / 1000, | |
406 | (conf->dot11MeshRetryTimeout % 1000) * 1000, | |
407 | plink_timer, wpa_s, sta); | |
5f92659d BC |
408 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); |
409 | sta->mpm_retries++; | |
410 | break; | |
411 | } | |
412 | reason = WLAN_REASON_MESH_MAX_RETRIES; | |
413 | /* fall through on else */ | |
414 | ||
415 | case PLINK_CNF_RCVD: | |
416 | /* confirm timer */ | |
417 | if (!reason) | |
418 | reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; | |
419 | sta->plink_state = PLINK_HOLDING; | |
e6096799 MH |
420 | eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000, |
421 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
422 | plink_timer, wpa_s, sta); | |
5f92659d BC |
423 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); |
424 | break; | |
425 | case PLINK_HOLDING: | |
426 | /* holding timer */ | |
427 | mesh_mpm_fsm_restart(wpa_s, sta); | |
428 | break; | |
429 | default: | |
430 | break; | |
431 | } | |
432 | } | |
433 | ||
434 | ||
435 | /* initiate peering with station */ | |
436 | static void | |
437 | mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta, | |
438 | enum mesh_plink_state next_state) | |
439 | { | |
e6096799 MH |
440 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
441 | ||
5f92659d | 442 | eloop_cancel_timeout(plink_timer, wpa_s, sta); |
e6096799 MH |
443 | eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000, |
444 | (conf->dot11MeshRetryTimeout % 1000) * 1000, | |
445 | plink_timer, wpa_s, sta); | |
5f92659d BC |
446 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); |
447 | wpa_mesh_set_plink_state(wpa_s, sta, next_state); | |
448 | } | |
449 | ||
450 | ||
451 | int mesh_mpm_plink_close(struct hostapd_data *hapd, | |
452 | struct sta_info *sta, void *ctx) | |
453 | { | |
454 | struct wpa_supplicant *wpa_s = ctx; | |
455 | int reason = WLAN_REASON_MESH_PEERING_CANCELLED; | |
456 | ||
457 | if (sta) { | |
458 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
459 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); | |
460 | wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR, | |
461 | MAC2STR(sta->addr)); | |
462 | eloop_cancel_timeout(plink_timer, wpa_s, sta); | |
463 | return 0; | |
464 | } | |
465 | ||
466 | return 1; | |
467 | } | |
468 | ||
469 | ||
470 | void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) | |
471 | { | |
472 | struct hostapd_data *hapd = ifmsh->bss[0]; | |
473 | ||
474 | /* notify peers we're leaving */ | |
475 | ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s); | |
476 | ||
477 | hapd->num_plinks = 0; | |
478 | hostapd_free_stas(hapd); | |
479 | } | |
480 | ||
481 | ||
482 | /* for mesh_rsn to indicate this peer has completed authentication, and we're | |
483 | * ready to start AMPE */ | |
484 | void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) | |
485 | { | |
486 | struct hostapd_data *data = wpa_s->ifmsh->bss[0]; | |
487 | struct hostapd_sta_add_params params; | |
488 | struct sta_info *sta; | |
489 | int ret; | |
490 | ||
491 | sta = ap_get_sta(data, addr); | |
492 | if (!sta) { | |
493 | wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer"); | |
494 | return; | |
495 | } | |
496 | ||
497 | /* TODO: Should do nothing if this STA is already authenticated, but | |
498 | * the AP code already sets this flag. */ | |
499 | sta->flags |= WLAN_STA_AUTH; | |
500 | ||
0f950df0 TP |
501 | mesh_rsn_init_ampe_sta(wpa_s, sta); |
502 | ||
5f92659d BC |
503 | os_memset(¶ms, 0, sizeof(params)); |
504 | params.addr = sta->addr; | |
505 | params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED; | |
506 | params.set = 1; | |
507 | ||
508 | wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR, | |
509 | MAC2STR(sta->addr)); | |
510 | ret = wpa_drv_sta_add(wpa_s, ¶ms); | |
511 | if (ret) { | |
512 | wpa_msg(wpa_s, MSG_ERROR, | |
513 | "Driver failed to set " MACSTR ": %d", | |
514 | MAC2STR(sta->addr), ret); | |
515 | } | |
516 | ||
517 | if (!sta->my_lid) | |
518 | mesh_mpm_init_link(wpa_s, sta); | |
519 | ||
520 | mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); | |
521 | } | |
522 | ||
523 | ||
524 | void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, | |
525 | struct ieee802_11_elems *elems) | |
526 | { | |
527 | struct hostapd_sta_add_params params; | |
528 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; | |
529 | struct hostapd_data *data = wpa_s->ifmsh->bss[0]; | |
530 | struct sta_info *sta; | |
07cb45cc | 531 | struct wpa_ssid *ssid = wpa_s->current_ssid; |
5f92659d BC |
532 | int ret = 0; |
533 | ||
534 | sta = ap_get_sta(data, addr); | |
535 | if (!sta) { | |
536 | sta = ap_sta_add(data, addr); | |
537 | if (!sta) | |
538 | return; | |
539 | } | |
540 | ||
541 | /* initialize sta */ | |
542 | if (copy_supp_rates(wpa_s, sta, elems)) | |
543 | return; | |
544 | ||
545 | mesh_mpm_init_link(wpa_s, sta); | |
546 | ||
5cfb672d JM |
547 | #ifdef CONFIG_IEEE80211N |
548 | copy_sta_ht_capab(data, sta, elems->ht_capabilities, | |
549 | elems->ht_capabilities_len); | |
550 | update_ht_state(data, sta); | |
551 | #endif /* CONFIG_IEEE80211N */ | |
552 | ||
5f92659d BC |
553 | /* insert into driver */ |
554 | os_memset(¶ms, 0, sizeof(params)); | |
555 | params.supp_rates = sta->supported_rates; | |
556 | params.supp_rates_len = sta->supported_rates_len; | |
557 | params.addr = addr; | |
558 | params.plink_state = sta->plink_state; | |
559 | params.aid = sta->peer_lid; | |
560 | params.listen_interval = 100; | |
5cfb672d | 561 | params.ht_capabilities = sta->ht_capabilities; |
5f92659d BC |
562 | params.flags |= WPA_STA_WMM; |
563 | params.flags_mask |= WPA_STA_AUTHENTICATED; | |
564 | if (conf->security == MESH_CONF_SEC_NONE) { | |
565 | params.flags |= WPA_STA_AUTHORIZED; | |
566 | params.flags |= WPA_STA_AUTHENTICATED; | |
567 | } else { | |
568 | sta->flags |= WLAN_STA_MFP; | |
569 | params.flags |= WPA_STA_MFP; | |
570 | } | |
571 | ||
572 | ret = wpa_drv_sta_add(wpa_s, ¶ms); | |
573 | if (ret) { | |
574 | wpa_msg(wpa_s, MSG_ERROR, | |
575 | "Driver failed to insert " MACSTR ": %d", | |
576 | MAC2STR(addr), ret); | |
577 | return; | |
578 | } | |
579 | ||
07cb45cc TP |
580 | if (ssid && ssid->no_auto_peer) { |
581 | wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " | |
582 | MACSTR " because of no_auto_peer", MAC2STR(addr)); | |
583 | return; | |
584 | } | |
585 | ||
0f950df0 TP |
586 | if (conf->security == MESH_CONF_SEC_NONE) |
587 | mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); | |
588 | else | |
589 | mesh_rsn_auth_sae_sta(wpa_s, sta); | |
5f92659d BC |
590 | } |
591 | ||
592 | ||
593 | void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt) | |
594 | { | |
595 | struct hostapd_frame_info fi; | |
596 | ||
597 | os_memset(&fi, 0, sizeof(fi)); | |
598 | fi.datarate = rx_mgmt->datarate; | |
599 | fi.ssi_signal = rx_mgmt->ssi_signal; | |
600 | ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame, | |
601 | rx_mgmt->frame_len, &fi); | |
602 | } | |
603 | ||
604 | ||
605 | static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, | |
606 | struct sta_info *sta) | |
607 | { | |
608 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
0f950df0 TP |
609 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
610 | u8 seq[6] = {}; | |
5f92659d BC |
611 | |
612 | wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established", | |
613 | MAC2STR(sta->addr)); | |
614 | ||
0f950df0 TP |
615 | if (conf->security & MESH_CONF_SEC_AMPE) { |
616 | wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0, | |
617 | seq, sizeof(seq), sta->mtk, sizeof(sta->mtk)); | |
618 | wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0, | |
619 | seq, sizeof(seq), | |
620 | sta->mgtk, sizeof(sta->mgtk)); | |
621 | wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0, | |
622 | seq, sizeof(seq), | |
623 | sta->mgtk, sizeof(sta->mgtk)); | |
624 | ||
625 | wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk)); | |
626 | wpa_hexdump_key(MSG_DEBUG, "mgtk:", | |
627 | sta->mgtk, sizeof(sta->mgtk)); | |
628 | } | |
629 | ||
5f92659d BC |
630 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB); |
631 | hapd->num_plinks++; | |
632 | ||
633 | sta->flags |= WLAN_STA_ASSOC; | |
634 | ||
635 | eloop_cancel_timeout(plink_timer, wpa_s, sta); | |
636 | ||
637 | /* Send ctrl event */ | |
638 | wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, | |
639 | MAC2STR(sta->addr)); | |
640 | } | |
641 | ||
642 | ||
643 | static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, | |
644 | enum plink_event event) | |
645 | { | |
646 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
0f950df0 | 647 | struct mesh_conf *conf = wpa_s->ifmsh->mconf; |
5f92659d BC |
648 | u16 reason = 0; |
649 | ||
650 | wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s", | |
651 | MAC2STR(sta->addr), mplstate[sta->plink_state], | |
652 | mplevent[event]); | |
653 | ||
654 | switch (sta->plink_state) { | |
655 | case PLINK_LISTEN: | |
656 | switch (event) { | |
657 | case CLS_ACPT: | |
658 | mesh_mpm_fsm_restart(wpa_s, sta); | |
659 | break; | |
660 | case OPN_ACPT: | |
661 | mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD); | |
662 | mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, | |
663 | 0); | |
664 | break; | |
665 | default: | |
666 | break; | |
667 | } | |
668 | break; | |
669 | case PLINK_OPEN_SENT: | |
670 | switch (event) { | |
671 | case OPN_RJCT: | |
672 | case CNF_RJCT: | |
673 | reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; | |
674 | /* fall-through */ | |
675 | case CLS_ACPT: | |
676 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
677 | if (!reason) | |
678 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
e6096799 MH |
679 | eloop_register_timeout( |
680 | conf->dot11MeshHoldingTimeout / 1000, | |
681 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
682 | plink_timer, wpa_s, sta); | |
5f92659d BC |
683 | mesh_mpm_send_plink_action(wpa_s, sta, |
684 | PLINK_CLOSE, reason); | |
685 | break; | |
686 | case OPN_ACPT: | |
687 | /* retry timer is left untouched */ | |
688 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD); | |
689 | mesh_mpm_send_plink_action(wpa_s, sta, | |
690 | PLINK_CONFIRM, 0); | |
691 | break; | |
692 | case CNF_ACPT: | |
693 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD); | |
e6096799 MH |
694 | eloop_register_timeout( |
695 | conf->dot11MeshConfirmTimeout / 1000, | |
696 | (conf->dot11MeshConfirmTimeout % 1000) * 1000, | |
697 | plink_timer, wpa_s, sta); | |
5f92659d BC |
698 | break; |
699 | default: | |
700 | break; | |
701 | } | |
702 | break; | |
703 | case PLINK_OPEN_RCVD: | |
704 | switch (event) { | |
705 | case OPN_RJCT: | |
706 | case CNF_RJCT: | |
707 | reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; | |
708 | /* fall-through */ | |
709 | case CLS_ACPT: | |
710 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
711 | if (!reason) | |
712 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
e6096799 MH |
713 | eloop_register_timeout( |
714 | conf->dot11MeshHoldingTimeout / 1000, | |
715 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
716 | plink_timer, wpa_s, sta); | |
5f92659d BC |
717 | sta->mpm_close_reason = reason; |
718 | mesh_mpm_send_plink_action(wpa_s, sta, | |
719 | PLINK_CLOSE, reason); | |
720 | break; | |
721 | case OPN_ACPT: | |
722 | mesh_mpm_send_plink_action(wpa_s, sta, | |
723 | PLINK_CONFIRM, 0); | |
724 | break; | |
725 | case CNF_ACPT: | |
0f950df0 TP |
726 | if (conf->security & MESH_CONF_SEC_AMPE) |
727 | mesh_rsn_derive_mtk(wpa_s, sta); | |
5f92659d BC |
728 | mesh_mpm_plink_estab(wpa_s, sta); |
729 | break; | |
730 | default: | |
731 | break; | |
732 | } | |
733 | break; | |
734 | case PLINK_CNF_RCVD: | |
735 | switch (event) { | |
736 | case OPN_RJCT: | |
737 | case CNF_RJCT: | |
738 | reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; | |
739 | /* fall-through */ | |
740 | case CLS_ACPT: | |
741 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
742 | if (!reason) | |
743 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
e6096799 MH |
744 | eloop_register_timeout( |
745 | conf->dot11MeshHoldingTimeout / 1000, | |
746 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
747 | plink_timer, wpa_s, sta); | |
5f92659d BC |
748 | sta->mpm_close_reason = reason; |
749 | mesh_mpm_send_plink_action(wpa_s, sta, | |
750 | PLINK_CLOSE, reason); | |
751 | break; | |
752 | case OPN_ACPT: | |
753 | mesh_mpm_plink_estab(wpa_s, sta); | |
754 | mesh_mpm_send_plink_action(wpa_s, sta, | |
755 | PLINK_CONFIRM, 0); | |
756 | break; | |
757 | default: | |
758 | break; | |
759 | } | |
760 | break; | |
761 | case PLINK_ESTAB: | |
762 | switch (event) { | |
763 | case CLS_ACPT: | |
764 | wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); | |
765 | reason = WLAN_REASON_MESH_CLOSE_RCVD; | |
766 | ||
e6096799 MH |
767 | eloop_register_timeout( |
768 | conf->dot11MeshHoldingTimeout / 1000, | |
769 | (conf->dot11MeshHoldingTimeout % 1000) * 1000, | |
770 | plink_timer, wpa_s, sta); | |
5f92659d BC |
771 | sta->mpm_close_reason = reason; |
772 | ||
773 | wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR | |
774 | " closed with reason %d", | |
775 | MAC2STR(sta->addr), reason); | |
776 | ||
777 | wpa_msg_ctrl(wpa_s, MSG_INFO, | |
778 | MESH_PEER_DISCONNECTED MACSTR, | |
779 | MAC2STR(sta->addr)); | |
780 | ||
781 | hapd->num_plinks--; | |
782 | ||
783 | mesh_mpm_send_plink_action(wpa_s, sta, | |
784 | PLINK_CLOSE, reason); | |
785 | break; | |
786 | case OPN_ACPT: | |
787 | mesh_mpm_send_plink_action(wpa_s, sta, | |
788 | PLINK_CONFIRM, 0); | |
789 | break; | |
790 | default: | |
791 | break; | |
792 | } | |
793 | break; | |
794 | case PLINK_HOLDING: | |
795 | switch (event) { | |
796 | case CLS_ACPT: | |
797 | mesh_mpm_fsm_restart(wpa_s, sta); | |
798 | break; | |
799 | case OPN_ACPT: | |
800 | case CNF_ACPT: | |
801 | case OPN_RJCT: | |
802 | case CNF_RJCT: | |
803 | reason = sta->mpm_close_reason; | |
804 | mesh_mpm_send_plink_action(wpa_s, sta, | |
805 | PLINK_CLOSE, reason); | |
806 | break; | |
807 | default: | |
808 | break; | |
809 | } | |
810 | break; | |
811 | default: | |
812 | wpa_msg(wpa_s, MSG_DEBUG, | |
813 | "Unsupported MPM event %s for state %s", | |
814 | mplevent[event], mplstate[sta->plink_state]); | |
815 | break; | |
816 | } | |
817 | } | |
818 | ||
819 | ||
820 | void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, | |
821 | const struct ieee80211_mgmt *mgmt, size_t len) | |
822 | { | |
823 | u8 action_field; | |
824 | struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; | |
0f950df0 | 825 | struct mesh_conf *mconf = wpa_s->ifmsh->mconf; |
5f92659d BC |
826 | struct sta_info *sta; |
827 | u16 plid = 0, llid = 0; | |
828 | enum plink_event event; | |
829 | struct ieee802_11_elems elems; | |
830 | struct mesh_peer_mgmt_ie peer_mgmt_ie; | |
831 | const u8 *ies; | |
832 | size_t ie_len; | |
833 | int ret; | |
834 | ||
835 | if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED) | |
836 | return; | |
837 | ||
838 | action_field = mgmt->u.action.u.slf_prot_action.action; | |
c932b43e JM |
839 | if (action_field != PLINK_OPEN && |
840 | action_field != PLINK_CONFIRM && | |
841 | action_field != PLINK_CLOSE) | |
842 | return; | |
5f92659d BC |
843 | |
844 | ies = mgmt->u.action.u.slf_prot_action.variable; | |
845 | ie_len = (const u8 *) mgmt + len - | |
846 | mgmt->u.action.u.slf_prot_action.variable; | |
847 | ||
848 | /* at least expect mesh id and peering mgmt */ | |
c932b43e JM |
849 | if (ie_len < 2 + 2) { |
850 | wpa_printf(MSG_DEBUG, | |
851 | "MPM: Ignore too short action frame %u ie_len %u", | |
852 | action_field, (unsigned int) ie_len); | |
5f92659d | 853 | return; |
c932b43e JM |
854 | } |
855 | wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field); | |
5f92659d BC |
856 | |
857 | if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) { | |
c932b43e JM |
858 | wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x", |
859 | WPA_GET_LE16(ies)); | |
5f92659d BC |
860 | ies += 2; /* capability */ |
861 | ie_len -= 2; | |
862 | } | |
863 | if (action_field == PLINK_CONFIRM) { | |
c932b43e | 864 | wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies)); |
5f92659d BC |
865 | ies += 2; /* aid */ |
866 | ie_len -= 2; | |
867 | } | |
868 | ||
869 | /* check for mesh peering, mesh id and mesh config IEs */ | |
c932b43e JM |
870 | if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { |
871 | wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs"); | |
5f92659d | 872 | return; |
c932b43e JM |
873 | } |
874 | if (!elems.peer_mgmt) { | |
875 | wpa_printf(MSG_DEBUG, | |
876 | "MPM: No Mesh Peering Management element"); | |
5f92659d | 877 | return; |
c932b43e JM |
878 | } |
879 | if (action_field != PLINK_CLOSE) { | |
880 | if (!elems.mesh_id || !elems.mesh_config) { | |
881 | wpa_printf(MSG_DEBUG, | |
882 | "MPM: No Mesh ID or Mesh Configuration element"); | |
883 | return; | |
884 | } | |
5f92659d | 885 | |
c932b43e JM |
886 | if (!matches_local(wpa_s, &elems)) { |
887 | wpa_printf(MSG_DEBUG, | |
888 | "MPM: Mesh ID or Mesh Configuration element do not match local MBSS"); | |
889 | return; | |
890 | } | |
891 | } | |
5f92659d BC |
892 | |
893 | ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field, | |
894 | elems.peer_mgmt, | |
895 | elems.peer_mgmt_len, | |
896 | &peer_mgmt_ie); | |
c932b43e JM |
897 | if (ret) { |
898 | wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame"); | |
5f92659d | 899 | return; |
c932b43e | 900 | } |
5f92659d BC |
901 | |
902 | /* the sender's llid is our plid and vice-versa */ | |
903 | plid = WPA_GET_LE16(peer_mgmt_ie.llid); | |
904 | if (peer_mgmt_ie.plid) | |
905 | llid = WPA_GET_LE16(peer_mgmt_ie.plid); | |
c932b43e | 906 | wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); |
5f92659d BC |
907 | |
908 | sta = ap_get_sta(hapd, mgmt->sa); | |
c932b43e JM |
909 | if (!sta) { |
910 | wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer"); | |
5f92659d | 911 | return; |
c932b43e | 912 | } |
5f92659d | 913 | |
0f950df0 TP |
914 | #ifdef CONFIG_SAE |
915 | /* peer is in sae_accepted? */ | |
c932b43e JM |
916 | if (sta->sae && sta->sae->state != SAE_ACCEPTED) { |
917 | wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer"); | |
0f950df0 | 918 | return; |
c932b43e | 919 | } |
0f950df0 TP |
920 | #endif /* CONFIG_SAE */ |
921 | ||
5f92659d BC |
922 | if (!sta->my_lid) |
923 | mesh_mpm_init_link(wpa_s, sta); | |
924 | ||
c932b43e JM |
925 | if ((mconf->security & MESH_CONF_SEC_AMPE) && |
926 | mesh_rsn_process_ampe(wpa_s, sta, &elems, | |
927 | &mgmt->u.action.category, | |
928 | ies, ie_len)) { | |
929 | wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); | |
930 | return; | |
931 | } | |
0f950df0 | 932 | |
c932b43e JM |
933 | if (sta->plink_state == PLINK_BLOCKED) { |
934 | wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED"); | |
5f92659d | 935 | return; |
c932b43e | 936 | } |
5f92659d BC |
937 | |
938 | /* Now we will figure out the appropriate event... */ | |
939 | switch (action_field) { | |
940 | case PLINK_OPEN: | |
941 | if (!plink_free_count(hapd) || | |
942 | (sta->peer_lid && sta->peer_lid != plid)) { | |
943 | event = OPN_IGNR; | |
944 | } else { | |
945 | sta->peer_lid = plid; | |
946 | event = OPN_ACPT; | |
947 | } | |
948 | break; | |
949 | case PLINK_CONFIRM: | |
950 | if (!plink_free_count(hapd) || | |
951 | sta->my_lid != llid || | |
952 | (sta->peer_lid && sta->peer_lid != plid)) { | |
953 | event = CNF_IGNR; | |
954 | } else { | |
955 | if (!sta->peer_lid) | |
956 | sta->peer_lid = plid; | |
957 | event = CNF_ACPT; | |
958 | } | |
959 | break; | |
960 | case PLINK_CLOSE: | |
961 | if (sta->plink_state == PLINK_ESTAB) | |
962 | /* Do not check for llid or plid. This does not | |
963 | * follow the standard but since multiple plinks | |
964 | * per cand are not supported, it is necessary in | |
965 | * order to avoid a livelock when MP A sees an | |
966 | * establish peer link to MP B but MP B does not | |
967 | * see it. This can be caused by a timeout in | |
968 | * B's peer link establishment or B being | |
969 | * restarted. | |
970 | */ | |
971 | event = CLS_ACPT; | |
972 | else if (sta->peer_lid != plid) | |
973 | event = CLS_IGNR; | |
974 | else if (peer_mgmt_ie.plid && sta->my_lid != llid) | |
975 | event = CLS_IGNR; | |
976 | else | |
977 | event = CLS_ACPT; | |
978 | break; | |
763041b2 JM |
979 | default: |
980 | /* | |
981 | * This cannot be hit due to the action_field check above, but | |
982 | * compilers may not be able to figure that out and can warn | |
983 | * about uninitialized event below. | |
984 | */ | |
985 | return; | |
5f92659d BC |
986 | } |
987 | mesh_mpm_fsm(wpa_s, sta, event); | |
988 | } | |
c596f3f0 CYY |
989 | |
990 | ||
991 | /* called by ap_free_sta */ | |
992 | void mesh_mpm_free_sta(struct sta_info *sta) | |
993 | { | |
994 | eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta); | |
995 | eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta); | |
996 | } |