]>
Commit | Line | Data |
---|---|---|
b5b969e9 JM |
1 | /* |
2 | * hostapd / Callback functions for driver wrappers | |
3 | * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> | |
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 | * | |
9 | * Alternatively, this software may be distributed under the terms of BSD | |
10 | * license. | |
11 | * | |
12 | * See README and COPYING for more details. | |
13 | */ | |
14 | ||
15 | #include "includes.h" | |
16 | ||
4dbfe5c5 | 17 | #include "common.h" |
b5b969e9 JM |
18 | #include "hostapd.h" |
19 | #include "driver_i.h" | |
20 | #include "ieee802_11.h" | |
21 | #include "radius/radius.h" | |
bcd154c3 | 22 | #include "sta_flags.h" |
b5b969e9 JM |
23 | #include "sta_info.h" |
24 | #include "accounting.h" | |
25 | #include "tkip_countermeasures.h" | |
26 | #include "ieee802_1x.h" | |
27 | #include "wpa.h" | |
28 | #include "iapp.h" | |
29 | #include "wme.h" | |
3fed6f25 | 30 | #include "wps_hostapd.h" |
b5b969e9 JM |
31 | |
32 | ||
33 | struct prune_data { | |
34 | struct hostapd_data *hapd; | |
35 | const u8 *addr; | |
36 | }; | |
37 | ||
38 | static int prune_associations(struct hostapd_iface *iface, void *ctx) | |
39 | { | |
40 | struct prune_data *data = ctx; | |
41 | struct sta_info *osta; | |
42 | struct hostapd_data *ohapd; | |
43 | size_t j; | |
44 | ||
45 | for (j = 0; j < iface->num_bss; j++) { | |
46 | ohapd = iface->bss[j]; | |
47 | if (ohapd == data->hapd) | |
48 | continue; | |
49 | osta = ap_get_sta(ohapd, data->addr); | |
50 | if (!osta) | |
51 | continue; | |
52 | ||
53 | ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED); | |
54 | } | |
55 | ||
56 | return 0; | |
57 | } | |
58 | ||
59 | /** | |
60 | * hostapd_prune_associations - Remove extraneous associations | |
61 | * @hapd: Pointer to BSS data for the most recent association | |
62 | * @sta: Pointer to the associated STA data | |
63 | * | |
64 | * This function looks through all radios and BSS's for previous | |
65 | * (stale) associations of STA. If any are found they are removed. | |
66 | */ | |
67 | static void hostapd_prune_associations(struct hostapd_data *hapd, | |
68 | struct sta_info *sta) | |
69 | { | |
70 | struct prune_data data; | |
71 | data.hapd = hapd; | |
72 | data.addr = sta->addr; | |
73 | hostapd_for_each_interface(prune_associations, &data); | |
74 | } | |
75 | ||
76 | ||
77 | /** | |
78 | * hostapd_new_assoc_sta - Notify that a new station associated with the AP | |
79 | * @hapd: Pointer to BSS data | |
80 | * @sta: Pointer to the associated STA data | |
81 | * @reassoc: 1 to indicate this was a re-association; 0 = first association | |
82 | * | |
83 | * This function will be called whenever a station associates with the AP. It | |
84 | * can be called from ieee802_11.c for drivers that export MLME to hostapd and | |
85 | * from driver_*.c for drivers that take care of management frames (IEEE 802.11 | |
86 | * authentication and association) internally. | |
87 | */ | |
88 | void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, | |
89 | int reassoc) | |
90 | { | |
91 | if (hapd->tkip_countermeasures) { | |
92 | hostapd_sta_deauth(hapd, sta->addr, | |
93 | WLAN_REASON_MICHAEL_MIC_FAILURE); | |
94 | return; | |
95 | } | |
96 | ||
97 | hostapd_prune_associations(hapd, sta); | |
98 | ||
99 | /* IEEE 802.11F (IAPP) */ | |
100 | if (hapd->conf->ieee802_11f) | |
101 | iapp_new_station(hapd->iapp, sta); | |
102 | ||
103 | /* Start accounting here, if IEEE 802.1X and WPA are not used. | |
104 | * IEEE 802.1X/WPA code will start accounting after the station has | |
105 | * been authorized. */ | |
106 | if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) | |
107 | accounting_sta_start(hapd, sta); | |
108 | ||
3ae0800c | 109 | hostapd_wmm_sta_config(hapd, sta); |
b5b969e9 JM |
110 | |
111 | /* Start IEEE 802.1X authentication process for new stations */ | |
112 | ieee802_1x_new_station(hapd, sta); | |
113 | if (reassoc) { | |
114 | if (sta->auth_alg != WLAN_AUTH_FT && | |
115 | !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) | |
116 | wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); | |
117 | } else | |
118 | wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); | |
119 | } | |
120 | ||
121 | ||
122 | void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, | |
123 | const u8 *buf, size_t len, int ack) | |
124 | { | |
125 | struct sta_info *sta; | |
4b9841d3 | 126 | struct hostapd_iface *iface = hapd->iface; |
b5b969e9 JM |
127 | |
128 | sta = ap_get_sta(hapd, addr); | |
4b9841d3 JM |
129 | if (sta == NULL && iface->num_bss > 1) { |
130 | size_t j; | |
131 | for (j = 0; j < iface->num_bss; j++) { | |
132 | hapd = iface->bss[j]; | |
133 | sta = ap_get_sta(hapd, addr); | |
134 | if (sta) | |
135 | break; | |
136 | } | |
137 | } | |
138 | if (sta == NULL) | |
139 | return; | |
140 | if (sta->flags & WLAN_STA_PENDING_POLL) { | |
b5b969e9 JM |
141 | wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " |
142 | "activity poll", MAC2STR(sta->addr), | |
143 | ack ? "ACKed" : "did not ACK"); | |
144 | if (ack) | |
145 | sta->flags &= ~WLAN_STA_PENDING_POLL; | |
146 | } | |
4b9841d3 JM |
147 | |
148 | ieee802_1x_tx_status(hapd, sta, buf, len, ack); | |
149 | } | |
150 | ||
151 | ||
152 | static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) | |
153 | { | |
154 | u16 fc, type, stype; | |
155 | ||
156 | /* | |
157 | * PS-Poll frames are 16 bytes. All other frames are | |
158 | * 24 bytes or longer. | |
159 | */ | |
160 | if (len < 16) | |
161 | return NULL; | |
162 | ||
163 | fc = le_to_host16(hdr->frame_control); | |
164 | type = WLAN_FC_GET_TYPE(fc); | |
165 | stype = WLAN_FC_GET_STYPE(fc); | |
166 | ||
167 | switch (type) { | |
168 | case WLAN_FC_TYPE_DATA: | |
169 | if (len < 24) | |
170 | return NULL; | |
171 | switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { | |
172 | case WLAN_FC_TODS: | |
173 | return hdr->addr1; | |
174 | case WLAN_FC_FROMDS: | |
175 | return hdr->addr2; | |
176 | default: | |
177 | return NULL; | |
178 | } | |
179 | case WLAN_FC_TYPE_CTRL: | |
180 | if (stype != WLAN_FC_STYPE_PSPOLL) | |
181 | return NULL; | |
182 | return hdr->addr1; | |
183 | case WLAN_FC_TYPE_MGMT: | |
184 | return hdr->addr3; | |
185 | default: | |
186 | return NULL; | |
187 | } | |
188 | } | |
189 | ||
190 | ||
191 | #define HAPD_BROADCAST ((struct hostapd_data *) -1) | |
192 | ||
193 | static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, | |
194 | const u8 *bssid) | |
195 | { | |
196 | size_t i; | |
197 | ||
198 | if (bssid == NULL) | |
199 | return NULL; | |
200 | if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff && | |
201 | bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff) | |
202 | return HAPD_BROADCAST; | |
203 | ||
204 | for (i = 0; i < iface->num_bss; i++) { | |
205 | if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) | |
206 | return iface->bss[i]; | |
207 | } | |
208 | ||
209 | return NULL; | |
b5b969e9 JM |
210 | } |
211 | ||
212 | ||
4b9841d3 JM |
213 | void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd, |
214 | const struct ieee80211_hdr *hdr, size_t len) | |
b5b969e9 JM |
215 | { |
216 | struct sta_info *sta; | |
4b9841d3 JM |
217 | const u8 *addr; |
218 | ||
219 | hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); | |
220 | if (hapd == NULL || hapd == HAPD_BROADCAST) | |
221 | return; | |
b5b969e9 | 222 | |
4b9841d3 | 223 | addr = hdr->addr2; |
b5b969e9 JM |
224 | sta = ap_get_sta(hapd, addr); |
225 | if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { | |
226 | wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated " | |
227 | "STA " MACSTR, MAC2STR(addr)); | |
228 | if (sta && (sta->flags & WLAN_STA_AUTH)) | |
229 | hostapd_sta_disassoc( | |
230 | hapd, addr, | |
231 | WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); | |
232 | else | |
233 | hostapd_sta_deauth( | |
234 | hapd, addr, | |
235 | WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); | |
236 | } | |
237 | } | |
238 | ||
239 | ||
05310066 JM |
240 | int hostapd_notif_new_sta(struct hostapd_data *hapd, const u8 *addr) |
241 | { | |
242 | struct sta_info *sta = ap_get_sta(hapd, addr); | |
243 | if (sta) | |
244 | return 0; | |
245 | ||
246 | wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR | |
247 | " - adding a new STA", MAC2STR(addr)); | |
248 | sta = ap_sta_add(hapd, addr); | |
249 | if (sta) { | |
250 | hostapd_new_assoc_sta(hapd, sta, 0); | |
251 | } else { | |
252 | wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR, | |
253 | MAC2STR(addr)); | |
254 | return -1; | |
255 | } | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | ||
b5b969e9 JM |
261 | int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, |
262 | const u8 *ie, size_t ielen) | |
263 | { | |
264 | struct sta_info *sta; | |
265 | int new_assoc, res; | |
266 | ||
267 | hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, | |
268 | HOSTAPD_LEVEL_INFO, "associated"); | |
269 | ||
270 | sta = ap_get_sta(hapd, addr); | |
271 | if (sta) { | |
272 | accounting_sta_stop(hapd, sta); | |
273 | } else { | |
274 | sta = ap_sta_add(hapd, addr); | |
275 | if (sta == NULL) | |
276 | return -1; | |
277 | } | |
278 | sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); | |
279 | ||
280 | if (hapd->conf->wpa) { | |
281 | if (ie == NULL || ielen == 0) { | |
282 | if (hapd->conf->wps_state) { | |
283 | wpa_printf(MSG_DEBUG, "STA did not include " | |
284 | "WPA/RSN IE in (Re)Association " | |
285 | "Request - possible WPS use"); | |
286 | sta->flags |= WLAN_STA_MAYBE_WPS; | |
287 | goto skip_wpa_check; | |
288 | } | |
289 | ||
290 | wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); | |
291 | return -1; | |
292 | } | |
293 | if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && | |
294 | os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { | |
295 | sta->flags |= WLAN_STA_WPS; | |
296 | goto skip_wpa_check; | |
297 | } | |
298 | ||
299 | if (sta->wpa_sm == NULL) | |
300 | sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, | |
301 | sta->addr); | |
302 | if (sta->wpa_sm == NULL) { | |
303 | wpa_printf(MSG_ERROR, "Failed to initialize WPA state " | |
304 | "machine"); | |
305 | return -1; | |
306 | } | |
307 | res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, | |
308 | ie, ielen, NULL, 0); | |
309 | if (res != WPA_IE_OK) { | |
355d36a7 | 310 | int resp; |
b5b969e9 JM |
311 | wpa_printf(MSG_DEBUG, "WPA/RSN information element " |
312 | "rejected? (res %u)", res); | |
313 | wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); | |
355d36a7 AT |
314 | if (res == WPA_INVALID_GROUP) |
315 | resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; | |
316 | else if (res == WPA_INVALID_PAIRWISE) | |
317 | resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; | |
318 | else if (res == WPA_INVALID_AKMP) | |
319 | resp = WLAN_REASON_AKMP_NOT_VALID; | |
320 | #ifdef CONFIG_IEEE80211W | |
321 | else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) | |
322 | resp = WLAN_REASON_INVALID_IE; | |
323 | else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) | |
324 | resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; | |
325 | #endif /* CONFIG_IEEE80211W */ | |
326 | else | |
327 | resp = WLAN_REASON_INVALID_IE; | |
328 | hostapd_sta_disassoc(hapd, sta->addr, resp); | |
329 | ap_free_sta(hapd, sta); | |
b5b969e9 JM |
330 | return -1; |
331 | } | |
a9aca28b JM |
332 | } else if (hapd->conf->wps_state) { |
333 | if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 && | |
334 | os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { | |
335 | sta->flags |= WLAN_STA_WPS; | |
336 | } else | |
337 | sta->flags |= WLAN_STA_MAYBE_WPS; | |
b5b969e9 JM |
338 | } |
339 | skip_wpa_check: | |
340 | ||
341 | new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; | |
342 | sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; | |
343 | wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); | |
344 | ||
345 | hostapd_new_assoc_sta(hapd, sta, !new_assoc); | |
346 | ||
347 | ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
352 | ||
353 | void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) | |
354 | { | |
355 | struct sta_info *sta; | |
356 | ||
357 | hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, | |
358 | HOSTAPD_LEVEL_INFO, "disassociated"); | |
359 | ||
360 | sta = ap_get_sta(hapd, addr); | |
361 | if (sta == NULL) { | |
362 | wpa_printf(MSG_DEBUG, "Disassociation notification for " | |
363 | "unknown STA " MACSTR, MAC2STR(addr)); | |
364 | return; | |
365 | } | |
366 | ||
367 | sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); | |
368 | wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); | |
369 | sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; | |
370 | ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); | |
371 | ap_free_sta(hapd, sta); | |
372 | } | |
373 | ||
374 | ||
375 | void hostapd_eapol_receive(struct hostapd_data *hapd, const u8 *sa, | |
376 | const u8 *buf, size_t len) | |
377 | { | |
378 | ieee802_1x_receive(hapd, sa, buf, len); | |
379 | } | |
380 | ||
381 | ||
fe6bdb77 | 382 | #ifdef NEED_AP_MLME |
b5b969e9 JM |
383 | void hostapd_mgmt_rx(struct hostapd_data *hapd, u8 *buf, size_t len, |
384 | u16 stype, struct hostapd_frame_info *fi) | |
385 | { | |
4b9841d3 JM |
386 | struct hostapd_iface *iface = hapd->iface; |
387 | struct ieee80211_hdr *hdr; | |
388 | const u8 *bssid; | |
389 | ||
390 | hdr = (struct ieee80211_hdr *) buf; | |
391 | bssid = get_hdr_bssid(hdr, len); | |
392 | if (bssid == NULL) | |
393 | return; | |
394 | ||
395 | hapd = get_hapd_bssid(iface, bssid); | |
396 | if (hapd == NULL) { | |
397 | u16 fc; | |
398 | fc = le_to_host16(hdr->frame_control); | |
399 | ||
400 | /* | |
401 | * Drop frames to unknown BSSIDs except for Beacon frames which | |
402 | * could be used to update neighbor information. | |
403 | */ | |
404 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && | |
405 | WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) | |
406 | hapd = iface->bss[0]; | |
407 | else | |
408 | return; | |
409 | } | |
410 | ||
411 | if (hapd == HAPD_BROADCAST) { | |
412 | size_t i; | |
413 | for (i = 0; i < iface->num_bss; i++) | |
414 | ieee802_11_mgmt(iface->bss[i], buf, len, stype, fi); | |
415 | } else | |
416 | ieee802_11_mgmt(hapd, buf, len, stype, fi); | |
b5b969e9 JM |
417 | } |
418 | ||
419 | ||
420 | void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, u8 *buf, size_t len, | |
421 | u16 stype, int ok) | |
422 | { | |
4b9841d3 JM |
423 | struct ieee80211_hdr *hdr; |
424 | hdr = (struct ieee80211_hdr *) buf; | |
425 | hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); | |
426 | if (hapd == NULL || hapd == HAPD_BROADCAST) | |
427 | return; | |
b5b969e9 JM |
428 | ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); |
429 | } | |
fe6bdb77 | 430 | #endif /* NEED_AP_MLME */ |
b5b969e9 JM |
431 | |
432 | ||
433 | void hostapd_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr) | |
434 | { | |
435 | michael_mic_failure(hapd, addr, 1); | |
436 | } | |
f82ef4d8 JM |
437 | |
438 | ||
439 | struct hostapd_data * hostapd_sta_get_bss(struct hostapd_data *hapd, | |
440 | const u8 *addr) | |
441 | { | |
442 | struct hostapd_iface *iface = hapd->iface; | |
443 | size_t j; | |
444 | ||
445 | for (j = 0; j < iface->num_bss; j++) { | |
446 | hapd = iface->bss[j]; | |
447 | if (ap_get_sta(hapd, addr)) | |
448 | return hapd; | |
449 | } | |
450 | ||
451 | return NULL; | |
452 | } | |
ad1e68e6 JM |
453 | |
454 | ||
455 | #ifndef CONFIG_AP | |
456 | void wpa_supplicant_event(void *ctx, wpa_event_type event, | |
457 | union wpa_event_data *data) | |
458 | { | |
459 | struct hostapd_data *hapd = ctx; | |
460 | ||
461 | switch (event) { | |
462 | case EVENT_MICHAEL_MIC_FAILURE: | |
463 | michael_mic_failure(hapd, data->michael_mic_failure.src, 1); | |
464 | break; | |
465 | case EVENT_SCAN_RESULTS: | |
466 | if (hapd->iface->scan_cb) | |
467 | hapd->iface->scan_cb(hapd->iface); | |
468 | break; | |
469 | default: | |
470 | wpa_printf(MSG_DEBUG, "Unknown event %d", event); | |
471 | break; | |
472 | } | |
473 | } | |
474 | #endif /* CONFIG_AP */ | |
3fed6f25 JM |
475 | |
476 | ||
477 | void hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, | |
478 | const u8 *ie, size_t ie_len) | |
479 | { | |
fa16028d JM |
480 | size_t i; |
481 | ||
482 | for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) | |
483 | hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, | |
484 | sa, ie, ie_len); | |
3fed6f25 | 485 | } |
0c3abf8d JM |
486 | |
487 | ||
488 | void hostapd_button_pushed(struct hostapd_data *hapd) | |
489 | { | |
490 | hostapd_wps_button_pushed(hapd); | |
491 | } |