]>
Commit | Line | Data |
---|---|---|
a149fcc7 JM |
1 | /* |
2 | * Received frame processing | |
42098908 | 3 | * Copyright (c) 2010-2019, Jouni Malinen <j@w1.fi> |
a149fcc7 | 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 | ||
b3c43c3c JM |
164 | if ((fc & WLAN_FC_RETRY) && hdr->seq_ctrl == *seq_ctrl && |
165 | !sta->allow_duplicate) { | |
a912dd16 | 166 | u16 s = le_to_host16(hdr->seq_ctrl); |
e4d99217 JM |
167 | add_note(wt, MSG_MSGDUMP, "Ignore duplicated frame (seq=%u " |
168 | "frag=%u A1=" MACSTR " A2=" MACSTR ")", | |
169 | WLAN_GET_SEQ_SEQ(s), WLAN_GET_SEQ_FRAG(s), | |
170 | MAC2STR(hdr->addr1), MAC2STR(hdr->addr2)); | |
a912dd16 JM |
171 | return 1; |
172 | } | |
173 | ||
174 | *seq_ctrl = hdr->seq_ctrl; | |
b3c43c3c | 175 | sta->allow_duplicate = 0; |
a912dd16 JM |
176 | |
177 | return 0; | |
178 | } | |
179 | ||
180 | ||
fb8f5fc6 JM |
181 | static void rx_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr) |
182 | { | |
183 | struct ieee80211_hdr *last = (struct ieee80211_hdr *) wt->last_hdr; | |
184 | u16 fc; | |
185 | ||
186 | if (wt->last_len < 24 || (last->addr1[0] & 0x01) || | |
187 | os_memcmp(hdr->addr1, last->addr2, ETH_ALEN) != 0) { | |
e4d99217 JM |
188 | add_note(wt, MSG_MSGDUMP, "Unknown Ack frame (previous frame " |
189 | "not seen)"); | |
fb8f5fc6 JM |
190 | return; |
191 | } | |
192 | ||
193 | /* Ack to the previous frame */ | |
194 | fc = le_to_host16(last->frame_control); | |
195 | if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) | |
196 | rx_mgmt_ack(wt, last); | |
197 | } | |
198 | ||
199 | ||
a149fcc7 JM |
200 | static void rx_frame(struct wlantest *wt, const u8 *data, size_t len) |
201 | { | |
202 | const struct ieee80211_hdr *hdr; | |
203 | u16 fc; | |
7e7a57ae JM |
204 | struct wlantest_sta *sta; |
205 | int to_ap; | |
a149fcc7 JM |
206 | |
207 | wpa_hexdump(MSG_EXCESSIVE, "RX frame", data, len); | |
208 | if (len < 2) | |
209 | return; | |
210 | ||
211 | hdr = (const struct ieee80211_hdr *) data; | |
212 | fc = le_to_host16(hdr->frame_control); | |
213 | if (fc & WLAN_FC_PVER) { | |
214 | wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected pver=%d", | |
215 | fc & WLAN_FC_PVER); | |
216 | return; | |
217 | } | |
218 | ||
7e7a57ae JM |
219 | sta = rx_get_sta(wt, hdr, len, &to_ap); |
220 | ||
a149fcc7 JM |
221 | switch (WLAN_FC_GET_TYPE(fc)) { |
222 | case WLAN_FC_TYPE_MGMT: | |
a912dd16 JM |
223 | if (len < 24) |
224 | break; | |
7e7a57ae | 225 | if (rx_duplicate(wt, hdr, len, sta, to_ap)) |
a912dd16 | 226 | break; |
7e7a57ae | 227 | rx_update_ps(wt, hdr, len, sta, to_ap); |
a149fcc7 JM |
228 | rx_mgmt(wt, data, len); |
229 | break; | |
230 | case WLAN_FC_TYPE_CTRL: | |
231 | if (len < 10) | |
a912dd16 | 232 | break; |
a149fcc7 | 233 | wt->rx_ctrl++; |
7e7a57ae | 234 | rx_update_ps(wt, hdr, len, sta, to_ap); |
fb8f5fc6 JM |
235 | if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACK) |
236 | rx_ack(wt, hdr); | |
a149fcc7 JM |
237 | break; |
238 | case WLAN_FC_TYPE_DATA: | |
a912dd16 JM |
239 | if (len < 24) |
240 | break; | |
7e7a57ae | 241 | if (rx_duplicate(wt, hdr, len, sta, to_ap)) |
a912dd16 | 242 | break; |
7e7a57ae | 243 | rx_update_ps(wt, hdr, len, sta, to_ap); |
a149fcc7 JM |
244 | rx_data(wt, data, len); |
245 | break; | |
246 | default: | |
247 | wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected type %d", | |
248 | WLAN_FC_GET_TYPE(fc)); | |
249 | break; | |
250 | } | |
fb8f5fc6 JM |
251 | |
252 | os_memcpy(wt->last_hdr, data, len > sizeof(wt->last_hdr) ? | |
253 | sizeof(wt->last_hdr) : len); | |
254 | wt->last_len = len; | |
a149fcc7 JM |
255 | } |
256 | ||
257 | ||
258 | static void tx_status(struct wlantest *wt, const u8 *data, size_t len, int ack) | |
259 | { | |
260 | wpa_printf(MSG_DEBUG, "TX status: ack=%d", ack); | |
261 | wpa_hexdump(MSG_EXCESSIVE, "TX status frame", data, len); | |
262 | } | |
263 | ||
264 | ||
265 | static int check_fcs(const u8 *frame, size_t frame_len, const u8 *fcs) | |
266 | { | |
267 | if (WPA_GET_LE32(fcs) != crc32(frame, frame_len)) | |
268 | return -1; | |
269 | return 0; | |
270 | } | |
271 | ||
272 | ||
273 | void wlantest_process(struct wlantest *wt, const u8 *data, size_t len) | |
274 | { | |
275 | struct ieee80211_radiotap_iterator iter; | |
276 | int ret; | |
277 | int rxflags = 0, txflags = 0, failed = 0, fcs = 0; | |
278 | const u8 *frame, *fcspos; | |
279 | size_t frame_len; | |
280 | ||
42098908 JM |
281 | if (wt->ethernet) |
282 | return; | |
283 | ||
a149fcc7 JM |
284 | wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len); |
285 | ||
bacb984b | 286 | if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len, NULL)) { |
e4d99217 | 287 | add_note(wt, MSG_INFO, "Invalid radiotap frame"); |
a149fcc7 JM |
288 | return; |
289 | } | |
290 | ||
291 | for (;;) { | |
292 | ret = ieee80211_radiotap_iterator_next(&iter); | |
293 | wpa_printf(MSG_EXCESSIVE, "radiotap iter: %d " | |
294 | "this_arg_index=%d", ret, iter.this_arg_index); | |
295 | if (ret == -ENOENT) | |
296 | break; | |
297 | if (ret) { | |
e4d99217 JM |
298 | add_note(wt, MSG_INFO, "Invalid radiotap header: %d", |
299 | ret); | |
a149fcc7 JM |
300 | return; |
301 | } | |
302 | switch (iter.this_arg_index) { | |
303 | case IEEE80211_RADIOTAP_FLAGS: | |
304 | if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) | |
305 | fcs = 1; | |
306 | break; | |
307 | case IEEE80211_RADIOTAP_RX_FLAGS: | |
308 | rxflags = 1; | |
309 | break; | |
310 | case IEEE80211_RADIOTAP_TX_FLAGS: | |
311 | txflags = 1; | |
312 | failed = le_to_host16((*(u16 *) iter.this_arg)) & | |
313 | IEEE80211_RADIOTAP_F_TX_FAIL; | |
314 | break; | |
30476e4f JB |
315 | case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: |
316 | if (WPA_GET_BE24(iter.this_arg) == OUI_QCA && | |
317 | iter.this_arg[3] == QCA_RADIOTAP_VID_WLANTEST) { | |
318 | add_note(wt, MSG_DEBUG, | |
319 | "Skip frame inserted by wlantest"); | |
320 | return; | |
321 | } | |
a149fcc7 JM |
322 | } |
323 | } | |
324 | ||
bacb984b JB |
325 | frame = data + iter._max_length; |
326 | frame_len = len - iter._max_length; | |
a149fcc7 JM |
327 | |
328 | if (fcs && frame_len >= 4) { | |
329 | frame_len -= 4; | |
330 | fcspos = frame + frame_len; | |
331 | if (check_fcs(frame, frame_len, fcspos) < 0) { | |
e4d99217 JM |
332 | add_note(wt, MSG_EXCESSIVE, "Drop RX frame with " |
333 | "invalid FCS"); | |
a149fcc7 JM |
334 | wt->fcs_error++; |
335 | return; | |
336 | } | |
337 | } | |
338 | ||
339 | if (rxflags && txflags) | |
340 | return; | |
341 | if (!txflags) | |
342 | rx_frame(wt, frame, frame_len); | |
44f6d554 | 343 | else { |
e4d99217 JM |
344 | add_note(wt, MSG_EXCESSIVE, "TX status - process as RX of " |
345 | "local frame"); | |
a149fcc7 | 346 | tx_status(wt, frame, frame_len, !failed); |
44f6d554 JM |
347 | /* Process as RX frame to support local monitor interface */ |
348 | rx_frame(wt, frame, frame_len); | |
349 | } | |
a149fcc7 | 350 | } |
350132be JM |
351 | |
352 | ||
353 | void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len) | |
354 | { | |
355 | int fcs = 0; | |
356 | const u8 *frame, *fcspos; | |
357 | size_t frame_len; | |
358 | u32 hdrlen; | |
359 | ||
360 | wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len); | |
361 | ||
362 | if (len < 8) | |
363 | return; | |
364 | hdrlen = WPA_GET_LE32(data + 4); | |
365 | ||
366 | if (len < hdrlen) { | |
367 | wpa_printf(MSG_INFO, "Too short frame to include prism " | |
368 | "header"); | |
369 | return; | |
370 | } | |
371 | ||
372 | frame = data + hdrlen; | |
373 | frame_len = len - hdrlen; | |
374 | fcs = 1; | |
375 | ||
376 | if (fcs && frame_len >= 4) { | |
377 | frame_len -= 4; | |
378 | fcspos = frame + frame_len; | |
379 | if (check_fcs(frame, frame_len, fcspos) < 0) { | |
e4d99217 JM |
380 | add_note(wt, MSG_EXCESSIVE, "Drop RX frame with " |
381 | "invalid FCS"); | |
350132be JM |
382 | wt->fcs_error++; |
383 | return; | |
384 | } | |
385 | } | |
386 | ||
387 | rx_frame(wt, frame, frame_len); | |
388 | } | |
0f3e4f2a JM |
389 | |
390 | ||
391 | void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len) | |
392 | { | |
393 | wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len); | |
25315176 JM |
394 | |
395 | if (wt->assume_fcs && len >= 4) { | |
396 | const u8 *fcspos; | |
397 | ||
398 | len -= 4; | |
399 | fcspos = data + len; | |
400 | if (check_fcs(data, len, fcspos) < 0) { | |
e4d99217 JM |
401 | add_note(wt, MSG_EXCESSIVE, "Drop RX frame with " |
402 | "invalid FCS"); | |
25315176 JM |
403 | wt->fcs_error++; |
404 | return; | |
405 | } | |
406 | } | |
407 | ||
0f3e4f2a JM |
408 | rx_frame(wt, data, len); |
409 | } |