]>
Commit | Line | Data |
---|---|---|
a149fcc7 JM |
1 | /* |
2 | * Received frame processing | |
3 | * Copyright (c) 2010, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
a149fcc7 JM |
7 | */ |
8 | ||
9 | #include "utils/includes.h" | |
10 | ||
11 | #include "utils/common.h" | |
e2991ee5 | 12 | #include "utils/crc32.h" |
a149fcc7 JM |
13 | #include "utils/radiotap.h" |
14 | #include "utils/radiotap_iter.h" | |
15 | #include "common/ieee802_11_defs.h" | |
30476e4f | 16 | #include "common/qca-vendor.h" |
a149fcc7 JM |
17 | #include "wlantest.h" |
18 | ||
19 | ||
7e7a57ae JM |
20 | static struct wlantest_sta * rx_get_sta(struct wlantest *wt, |
21 | const struct ieee80211_hdr *hdr, | |
22 | size_t len, int *to_ap) | |
a912dd16 JM |
23 | { |
24 | u16 fc; | |
a912dd16 JM |
25 | const u8 *sta_addr, *bssid; |
26 | struct wlantest_bss *bss; | |
a912dd16 | 27 | |
7e7a57ae | 28 | *to_ap = 0; |
a912dd16 | 29 | if (hdr->addr1[0] & 0x01) |
7e7a57ae | 30 | return NULL; /* Ignore group addressed frames */ |
a912dd16 JM |
31 | |
32 | fc = le_to_host16(hdr->frame_control); | |
7e7a57ae JM |
33 | switch (WLAN_FC_GET_TYPE(fc)) { |
34 | case WLAN_FC_TYPE_MGMT: | |
35 | if (len < 24) | |
36 | return NULL; | |
a912dd16 JM |
37 | bssid = hdr->addr3; |
38 | if (os_memcmp(bssid, hdr->addr2, ETH_ALEN) == 0) { | |
39 | sta_addr = hdr->addr1; | |
7e7a57ae | 40 | *to_ap = 0; |
a912dd16 JM |
41 | } else { |
42 | if (os_memcmp(bssid, hdr->addr1, ETH_ALEN) != 0) | |
7e7a57ae | 43 | return NULL; /* Unsupported STA-to-STA frame */ |
a912dd16 | 44 | sta_addr = hdr->addr2; |
7e7a57ae | 45 | *to_ap = 1; |
a912dd16 | 46 | } |
7e7a57ae JM |
47 | break; |
48 | case WLAN_FC_TYPE_DATA: | |
49 | if (len < 24) | |
50 | return NULL; | |
a912dd16 JM |
51 | switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) { |
52 | case 0: | |
7e7a57ae | 53 | return NULL; /* IBSS not supported */ |
a912dd16 JM |
54 | case WLAN_FC_FROMDS: |
55 | sta_addr = hdr->addr1; | |
56 | bssid = hdr->addr2; | |
7e7a57ae | 57 | *to_ap = 0; |
a912dd16 JM |
58 | break; |
59 | case WLAN_FC_TODS: | |
60 | sta_addr = hdr->addr2; | |
61 | bssid = hdr->addr1; | |
7e7a57ae | 62 | *to_ap = 1; |
a912dd16 JM |
63 | break; |
64 | case WLAN_FC_TODS | WLAN_FC_FROMDS: | |
7e7a57ae | 65 | return NULL; /* WDS not supported */ |
a912dd16 | 66 | default: |
7e7a57ae | 67 | return NULL; |
a912dd16 | 68 | } |
7e7a57ae JM |
69 | break; |
70 | case WLAN_FC_TYPE_CTRL: | |
71 | if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PSPOLL && | |
72 | len >= 16) { | |
73 | sta_addr = hdr->addr2; | |
74 | bssid = hdr->addr1; | |
75 | *to_ap = 1; | |
76 | } else | |
77 | return NULL; | |
78 | break; | |
dfaeda04 JM |
79 | default: |
80 | return NULL; | |
a912dd16 JM |
81 | } |
82 | ||
83 | bss = bss_find(wt, bssid); | |
84 | if (bss == NULL) | |
dfaeda04 | 85 | return NULL; |
7e7a57ae JM |
86 | return sta_find(bss, sta_addr); |
87 | } | |
88 | ||
89 | ||
90 | static void rx_update_ps(struct wlantest *wt, const struct ieee80211_hdr *hdr, | |
91 | size_t len, struct wlantest_sta *sta, int to_ap) | |
92 | { | |
93 | u16 fc, type, stype; | |
94 | ||
95 | if (sta == NULL) | |
96 | return; | |
97 | ||
98 | fc = le_to_host16(hdr->frame_control); | |
99 | type = WLAN_FC_GET_TYPE(fc); | |
100 | stype = WLAN_FC_GET_STYPE(fc); | |
101 | ||
102 | if (!to_ap) { | |
103 | if (sta->pwrmgt && !sta->pspoll) { | |
104 | u16 seq_ctrl = le_to_host16(hdr->seq_ctrl); | |
e4d99217 JM |
105 | add_note(wt, MSG_DEBUG, "AP " MACSTR " sent a frame " |
106 | "(%u:%u) to a sleeping STA " MACSTR | |
107 | " (seq=%u)", | |
108 | MAC2STR(sta->bss->bssid), | |
109 | type, stype, MAC2STR(sta->addr), | |
110 | WLAN_GET_SEQ_SEQ(seq_ctrl)); | |
7e7a57ae JM |
111 | } else |
112 | sta->pspoll = 0; | |
113 | return; | |
114 | } | |
115 | ||
116 | sta->pspoll = 0; | |
117 | ||
118 | if (type == WLAN_FC_TYPE_DATA || type == WLAN_FC_TYPE_MGMT || | |
119 | (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)) { | |
120 | /* | |
121 | * In theory, the PS state changes only at the end of the frame | |
122 | * exchange that is ACKed by the AP. However, most cases are | |
123 | * handled with this simpler implementation that does not | |
124 | * maintain state through the frame exchange. | |
125 | */ | |
126 | if (sta->pwrmgt && !(fc & WLAN_FC_PWRMGT)) { | |
e4d99217 JM |
127 | add_note(wt, MSG_DEBUG, "STA " MACSTR " woke up from " |
128 | "sleep", MAC2STR(sta->addr)); | |
7e7a57ae JM |
129 | sta->pwrmgt = 0; |
130 | } else if (!sta->pwrmgt && (fc & WLAN_FC_PWRMGT)) { | |
e4d99217 JM |
131 | add_note(wt, MSG_DEBUG, "STA " MACSTR " went to sleep", |
132 | MAC2STR(sta->addr)); | |
7e7a57ae JM |
133 | sta->pwrmgt = 1; |
134 | } | |
135 | } | |
136 | ||
137 | if (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL) | |
138 | sta->pspoll = 1; | |
139 | } | |
140 | ||
141 | ||
142 | static int rx_duplicate(struct wlantest *wt, const struct ieee80211_hdr *hdr, | |
143 | size_t len, struct wlantest_sta *sta, int to_ap) | |
144 | { | |
145 | u16 fc; | |
146 | int tid = 16; | |
147 | le16 *seq_ctrl; | |
148 | ||
a912dd16 JM |
149 | if (sta == NULL) |
150 | return 0; | |
151 | ||
7e7a57ae JM |
152 | fc = le_to_host16(hdr->frame_control); |
153 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && | |
154 | (WLAN_FC_GET_STYPE(fc) & 0x08) && len >= 26) { | |
155 | const u8 *qos = ((const u8 *) hdr) + 24; | |
156 | tid = qos[0] & 0x0f; | |
157 | } | |
158 | ||
a912dd16 JM |
159 | if (to_ap) |
160 | seq_ctrl = &sta->seq_ctrl_to_ap[tid]; | |
161 | else | |
162 | seq_ctrl = &sta->seq_ctrl_to_sta[tid]; | |
163 | ||
164 | if ((fc & WLAN_FC_RETRY) && hdr->seq_ctrl == *seq_ctrl) { | |
165 | u16 s = le_to_host16(hdr->seq_ctrl); | |
e4d99217 JM |
166 | add_note(wt, MSG_MSGDUMP, "Ignore duplicated frame (seq=%u " |
167 | "frag=%u A1=" MACSTR " A2=" MACSTR ")", | |
168 | WLAN_GET_SEQ_SEQ(s), WLAN_GET_SEQ_FRAG(s), | |
169 | MAC2STR(hdr->addr1), MAC2STR(hdr->addr2)); | |
a912dd16 JM |
170 | return 1; |
171 | } | |
172 | ||
173 | *seq_ctrl = hdr->seq_ctrl; | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | ||
fb8f5fc6 JM |
179 | static void rx_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr) |
180 | { | |
181 | struct ieee80211_hdr *last = (struct ieee80211_hdr *) wt->last_hdr; | |
182 | u16 fc; | |
183 | ||
184 | if (wt->last_len < 24 || (last->addr1[0] & 0x01) || | |
185 | os_memcmp(hdr->addr1, last->addr2, ETH_ALEN) != 0) { | |
e4d99217 JM |
186 | add_note(wt, MSG_MSGDUMP, "Unknown Ack frame (previous frame " |
187 | "not seen)"); | |
fb8f5fc6 JM |
188 | return; |
189 | } | |
190 | ||
191 | /* Ack to the previous frame */ | |
192 | fc = le_to_host16(last->frame_control); | |
193 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) | |
194 | rx_mgmt_ack(wt, last); | |
195 | } | |
196 | ||
197 | ||
a149fcc7 JM |
198 | static void rx_frame(struct wlantest *wt, const u8 *data, size_t len) |
199 | { | |
200 | const struct ieee80211_hdr *hdr; | |
201 | u16 fc; | |
7e7a57ae JM |
202 | struct wlantest_sta *sta; |
203 | int to_ap; | |
a149fcc7 JM |
204 | |
205 | wpa_hexdump(MSG_EXCESSIVE, "RX frame", data, len); | |
206 | if (len < 2) | |
207 | return; | |
208 | ||
209 | hdr = (const struct ieee80211_hdr *) data; | |
210 | fc = le_to_host16(hdr->frame_control); | |
211 | if (fc & WLAN_FC_PVER) { | |
212 | wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected pver=%d", | |
213 | fc & WLAN_FC_PVER); | |
214 | return; | |
215 | } | |
216 | ||
7e7a57ae JM |
217 | sta = rx_get_sta(wt, hdr, len, &to_ap); |
218 | ||
a149fcc7 JM |
219 | switch (WLAN_FC_GET_TYPE(fc)) { |
220 | case WLAN_FC_TYPE_MGMT: | |
a912dd16 JM |
221 | if (len < 24) |
222 | break; | |
7e7a57ae | 223 | if (rx_duplicate(wt, hdr, len, sta, to_ap)) |
a912dd16 | 224 | break; |
7e7a57ae | 225 | rx_update_ps(wt, hdr, len, sta, to_ap); |
a149fcc7 JM |
226 | rx_mgmt(wt, data, len); |
227 | break; | |
228 | case WLAN_FC_TYPE_CTRL: | |
229 | if (len < 10) | |
a912dd16 | 230 | break; |
a149fcc7 | 231 | wt->rx_ctrl++; |
7e7a57ae | 232 | rx_update_ps(wt, hdr, len, sta, to_ap); |
fb8f5fc6 JM |
233 | if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACK) |
234 | rx_ack(wt, hdr); | |
a149fcc7 JM |
235 | break; |
236 | case WLAN_FC_TYPE_DATA: | |
a912dd16 JM |
237 | if (len < 24) |
238 | break; | |
7e7a57ae | 239 | if (rx_duplicate(wt, hdr, len, sta, to_ap)) |
a912dd16 | 240 | break; |
7e7a57ae | 241 | rx_update_ps(wt, hdr, len, sta, to_ap); |
a149fcc7 JM |
242 | rx_data(wt, data, len); |
243 | break; | |
244 | default: | |
245 | wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected type %d", | |
246 | WLAN_FC_GET_TYPE(fc)); | |
247 | break; | |
248 | } | |
fb8f5fc6 JM |
249 | |
250 | os_memcpy(wt->last_hdr, data, len > sizeof(wt->last_hdr) ? | |
251 | sizeof(wt->last_hdr) : len); | |
252 | wt->last_len = len; | |
a149fcc7 JM |
253 | } |
254 | ||
255 | ||
256 | static void tx_status(struct wlantest *wt, const u8 *data, size_t len, int ack) | |
257 | { | |
258 | wpa_printf(MSG_DEBUG, "TX status: ack=%d", ack); | |
259 | wpa_hexdump(MSG_EXCESSIVE, "TX status frame", data, len); | |
260 | } | |
261 | ||
262 | ||
263 | static int check_fcs(const u8 *frame, size_t frame_len, const u8 *fcs) | |
264 | { | |
265 | if (WPA_GET_LE32(fcs) != crc32(frame, frame_len)) | |
266 | return -1; | |
267 | return 0; | |
268 | } | |
269 | ||
270 | ||
271 | void wlantest_process(struct wlantest *wt, const u8 *data, size_t len) | |
272 | { | |
273 | struct ieee80211_radiotap_iterator iter; | |
274 | int ret; | |
275 | int rxflags = 0, txflags = 0, failed = 0, fcs = 0; | |
276 | const u8 *frame, *fcspos; | |
277 | size_t frame_len; | |
278 | ||
279 | wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len); | |
280 | ||
bacb984b | 281 | if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len, NULL)) { |
e4d99217 | 282 | add_note(wt, MSG_INFO, "Invalid radiotap frame"); |
a149fcc7 JM |
283 | return; |
284 | } | |
285 | ||
286 | for (;;) { | |
287 | ret = ieee80211_radiotap_iterator_next(&iter); | |
288 | wpa_printf(MSG_EXCESSIVE, "radiotap iter: %d " | |
289 | "this_arg_index=%d", ret, iter.this_arg_index); | |
290 | if (ret == -ENOENT) | |
291 | break; | |
292 | if (ret) { | |
e4d99217 JM |
293 | add_note(wt, MSG_INFO, "Invalid radiotap header: %d", |
294 | ret); | |
a149fcc7 JM |
295 | return; |
296 | } | |
297 | switch (iter.this_arg_index) { | |
298 | case IEEE80211_RADIOTAP_FLAGS: | |
299 | if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) | |
300 | fcs = 1; | |
301 | break; | |
302 | case IEEE80211_RADIOTAP_RX_FLAGS: | |
303 | rxflags = 1; | |
304 | break; | |
305 | case IEEE80211_RADIOTAP_TX_FLAGS: | |
306 | txflags = 1; | |
307 | failed = le_to_host16((*(u16 *) iter.this_arg)) & | |
308 | IEEE80211_RADIOTAP_F_TX_FAIL; | |
309 | break; | |
30476e4f JB |
310 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: |
311 | if (WPA_GET_BE24(iter.this_arg) == OUI_QCA && | |
312 | iter.this_arg[3] == QCA_RADIOTAP_VID_WLANTEST) { | |
313 | add_note(wt, MSG_DEBUG, | |
314 | "Skip frame inserted by wlantest"); | |
315 | return; | |
316 | } | |
a149fcc7 JM |
317 | } |
318 | } | |
319 | ||
bacb984b JB |
320 | frame = data + iter._max_length; |
321 | frame_len = len - iter._max_length; | |
a149fcc7 JM |
322 | |
323 | if (fcs && frame_len >= 4) { | |
324 | frame_len -= 4; | |
325 | fcspos = frame + frame_len; | |
326 | if (check_fcs(frame, frame_len, fcspos) < 0) { | |
e4d99217 JM |
327 | add_note(wt, MSG_EXCESSIVE, "Drop RX frame with " |
328 | "invalid FCS"); | |
a149fcc7 JM |
329 | wt->fcs_error++; |
330 | return; | |
331 | } | |
332 | } | |
333 | ||
334 | if (rxflags && txflags) | |
335 | return; | |
336 | if (!txflags) | |
337 | rx_frame(wt, frame, frame_len); | |
44f6d554 | 338 | else { |
e4d99217 JM |
339 | add_note(wt, MSG_EXCESSIVE, "TX status - process as RX of " |
340 | "local frame"); | |
a149fcc7 | 341 | tx_status(wt, frame, frame_len, !failed); |
44f6d554 JM |
342 | /* Process as RX frame to support local monitor interface */ |
343 | rx_frame(wt, frame, frame_len); | |
344 | } | |
a149fcc7 | 345 | } |
350132be JM |
346 | |
347 | ||
348 | void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len) | |
349 | { | |
350 | int fcs = 0; | |
351 | const u8 *frame, *fcspos; | |
352 | size_t frame_len; | |
353 | u32 hdrlen; | |
354 | ||
355 | wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len); | |
356 | ||
357 | if (len < 8) | |
358 | return; | |
359 | hdrlen = WPA_GET_LE32(data + 4); | |
360 | ||
361 | if (len < hdrlen) { | |
362 | wpa_printf(MSG_INFO, "Too short frame to include prism " | |
363 | "header"); | |
364 | return; | |
365 | } | |
366 | ||
367 | frame = data + hdrlen; | |
368 | frame_len = len - hdrlen; | |
369 | fcs = 1; | |
370 | ||
371 | if (fcs && frame_len >= 4) { | |
372 | frame_len -= 4; | |
373 | fcspos = frame + frame_len; | |
374 | if (check_fcs(frame, frame_len, fcspos) < 0) { | |
e4d99217 JM |
375 | add_note(wt, MSG_EXCESSIVE, "Drop RX frame with " |
376 | "invalid FCS"); | |
350132be JM |
377 | wt->fcs_error++; |
378 | return; | |
379 | } | |
380 | } | |
381 | ||
382 | rx_frame(wt, frame, frame_len); | |
383 | } | |
0f3e4f2a JM |
384 | |
385 | ||
386 | void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len) | |
387 | { | |
388 | wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len); | |
25315176 JM |
389 | |
390 | if (wt->assume_fcs && len >= 4) { | |
391 | const u8 *fcspos; | |
392 | ||
393 | len -= 4; | |
394 | fcspos = data + len; | |
395 | if (check_fcs(data, len, fcspos) < 0) { | |
e4d99217 JM |
396 | add_note(wt, MSG_EXCESSIVE, "Drop RX frame with " |
397 | "invalid FCS"); | |
25315176 JM |
398 | wt->fcs_error++; |
399 | return; | |
400 | } | |
401 | } | |
402 | ||
0f3e4f2a JM |
403 | rx_frame(wt, data, len); |
404 | } |