Merge branch 'vendor/GCC50'
[dragonfly.git] / contrib / wpa_supplicant / src / common / ieee802_11_common.c
1 /*
2  * IEEE 802.11 Common routines
3  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "defs.h"
13 #include "ieee802_11_defs.h"
14 #include "ieee802_11_common.h"
15
16
17 static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
18                                             struct ieee802_11_elems *elems,
19                                             int show_errors)
20 {
21         unsigned int oui;
22
23         /* first 3 bytes in vendor specific information element are the IEEE
24          * OUI of the vendor. The following byte is used a vendor specific
25          * sub-type. */
26         if (elen < 4) {
27                 if (show_errors) {
28                         wpa_printf(MSG_MSGDUMP, "short vendor specific "
29                                    "information element ignored (len=%lu)",
30                                    (unsigned long) elen);
31                 }
32                 return -1;
33         }
34
35         oui = WPA_GET_BE24(pos);
36         switch (oui) {
37         case OUI_MICROSOFT:
38                 /* Microsoft/Wi-Fi information elements are further typed and
39                  * subtyped */
40                 switch (pos[3]) {
41                 case 1:
42                         /* Microsoft OUI (00:50:F2) with OUI Type 1:
43                          * real WPA information element */
44                         elems->wpa_ie = pos;
45                         elems->wpa_ie_len = elen;
46                         break;
47                 case WMM_OUI_TYPE:
48                         /* WMM information element */
49                         if (elen < 5) {
50                                 wpa_printf(MSG_MSGDUMP, "short WMM "
51                                            "information element ignored "
52                                            "(len=%lu)",
53                                            (unsigned long) elen);
54                                 return -1;
55                         }
56                         switch (pos[4]) {
57                         case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
58                         case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
59                                 /*
60                                  * Share same pointer since only one of these
61                                  * is used and they start with same data.
62                                  * Length field can be used to distinguish the
63                                  * IEs.
64                                  */
65                                 elems->wmm = pos;
66                                 elems->wmm_len = elen;
67                                 break;
68                         case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
69                                 elems->wmm_tspec = pos;
70                                 elems->wmm_tspec_len = elen;
71                                 break;
72                         default:
73                                 wpa_printf(MSG_EXCESSIVE, "unknown WMM "
74                                            "information element ignored "
75                                            "(subtype=%d len=%lu)",
76                                            pos[4], (unsigned long) elen);
77                                 return -1;
78                         }
79                         break;
80                 case 4:
81                         /* Wi-Fi Protected Setup (WPS) IE */
82                         elems->wps_ie = pos;
83                         elems->wps_ie_len = elen;
84                         break;
85                 default:
86                         wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
87                                    "information element ignored "
88                                    "(type=%d len=%lu)",
89                                    pos[3], (unsigned long) elen);
90                         return -1;
91                 }
92                 break;
93
94         case OUI_WFA:
95                 switch (pos[3]) {
96                 case P2P_OUI_TYPE:
97                         /* Wi-Fi Alliance - P2P IE */
98                         elems->p2p = pos;
99                         elems->p2p_len = elen;
100                         break;
101                 case WFD_OUI_TYPE:
102                         /* Wi-Fi Alliance - WFD IE */
103                         elems->wfd = pos;
104                         elems->wfd_len = elen;
105                         break;
106                 case HS20_INDICATION_OUI_TYPE:
107                         /* Hotspot 2.0 */
108                         elems->hs20 = pos;
109                         elems->hs20_len = elen;
110                         break;
111                 default:
112                         wpa_printf(MSG_MSGDUMP, "Unknown WFA "
113                                    "information element ignored "
114                                    "(type=%d len=%lu)\n",
115                                    pos[3], (unsigned long) elen);
116                         return -1;
117                 }
118                 break;
119
120         case OUI_BROADCOM:
121                 switch (pos[3]) {
122                 case VENDOR_HT_CAPAB_OUI_TYPE:
123                         elems->vendor_ht_cap = pos;
124                         elems->vendor_ht_cap_len = elen;
125                         break;
126                 default:
127                         wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
128                                    "information element ignored "
129                                    "(type=%d len=%lu)",
130                                    pos[3], (unsigned long) elen);
131                         return -1;
132                 }
133                 break;
134
135         default:
136                 wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
137                            "information element ignored (vendor OUI "
138                            "%02x:%02x:%02x len=%lu)",
139                            pos[0], pos[1], pos[2], (unsigned long) elen);
140                 return -1;
141         }
142
143         return 0;
144 }
145
146
147 /**
148  * ieee802_11_parse_elems - Parse information elements in management frames
149  * @start: Pointer to the start of IEs
150  * @len: Length of IE buffer in octets
151  * @elems: Data structure for parsed elements
152  * @show_errors: Whether to show parsing errors in debug log
153  * Returns: Parsing result
154  */
155 ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
156                                 struct ieee802_11_elems *elems,
157                                 int show_errors)
158 {
159         size_t left = len;
160         const u8 *pos = start;
161         int unknown = 0;
162
163         os_memset(elems, 0, sizeof(*elems));
164
165         while (left >= 2) {
166                 u8 id, elen;
167
168                 id = *pos++;
169                 elen = *pos++;
170                 left -= 2;
171
172                 if (elen > left) {
173                         if (show_errors) {
174                                 wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
175                                            "parse failed (id=%d elen=%d "
176                                            "left=%lu)",
177                                            id, elen, (unsigned long) left);
178                                 wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
179                         }
180                         return ParseFailed;
181                 }
182
183                 switch (id) {
184                 case WLAN_EID_SSID:
185                         elems->ssid = pos;
186                         elems->ssid_len = elen;
187                         break;
188                 case WLAN_EID_SUPP_RATES:
189                         elems->supp_rates = pos;
190                         elems->supp_rates_len = elen;
191                         break;
192                 case WLAN_EID_DS_PARAMS:
193                         elems->ds_params = pos;
194                         elems->ds_params_len = elen;
195                         break;
196                 case WLAN_EID_CF_PARAMS:
197                 case WLAN_EID_TIM:
198                         break;
199                 case WLAN_EID_CHALLENGE:
200                         elems->challenge = pos;
201                         elems->challenge_len = elen;
202                         break;
203                 case WLAN_EID_ERP_INFO:
204                         elems->erp_info = pos;
205                         elems->erp_info_len = elen;
206                         break;
207                 case WLAN_EID_EXT_SUPP_RATES:
208                         elems->ext_supp_rates = pos;
209                         elems->ext_supp_rates_len = elen;
210                         break;
211                 case WLAN_EID_VENDOR_SPECIFIC:
212                         if (ieee802_11_parse_vendor_specific(pos, elen,
213                                                              elems,
214                                                              show_errors))
215                                 unknown++;
216                         break;
217                 case WLAN_EID_RSN:
218                         elems->rsn_ie = pos;
219                         elems->rsn_ie_len = elen;
220                         break;
221                 case WLAN_EID_PWR_CAPABILITY:
222                         break;
223                 case WLAN_EID_SUPPORTED_CHANNELS:
224                         elems->supp_channels = pos;
225                         elems->supp_channels_len = elen;
226                         break;
227                 case WLAN_EID_MOBILITY_DOMAIN:
228                         elems->mdie = pos;
229                         elems->mdie_len = elen;
230                         break;
231                 case WLAN_EID_FAST_BSS_TRANSITION:
232                         elems->ftie = pos;
233                         elems->ftie_len = elen;
234                         break;
235                 case WLAN_EID_TIMEOUT_INTERVAL:
236                         elems->timeout_int = pos;
237                         elems->timeout_int_len = elen;
238                         break;
239                 case WLAN_EID_HT_CAP:
240                         elems->ht_capabilities = pos;
241                         elems->ht_capabilities_len = elen;
242                         break;
243                 case WLAN_EID_HT_OPERATION:
244                         elems->ht_operation = pos;
245                         elems->ht_operation_len = elen;
246                         break;
247                 case WLAN_EID_VHT_CAP:
248                         elems->vht_capabilities = pos;
249                         elems->vht_capabilities_len = elen;
250                         break;
251                 case WLAN_EID_VHT_OPERATION:
252                         elems->vht_operation = pos;
253                         elems->vht_operation_len = elen;
254                         break;
255                 case WLAN_EID_LINK_ID:
256                         if (elen < 18)
257                                 break;
258                         elems->link_id = pos;
259                         break;
260                 case WLAN_EID_INTERWORKING:
261                         elems->interworking = pos;
262                         elems->interworking_len = elen;
263                         break;
264                 case WLAN_EID_QOS_MAP_SET:
265                         if (elen < 16)
266                                 break;
267                         elems->qos_map_set = pos;
268                         elems->qos_map_set_len = elen;
269                         break;
270                 case WLAN_EID_EXT_CAPAB:
271                         elems->ext_capab = pos;
272                         elems->ext_capab_len = elen;
273                         break;
274                 case WLAN_EID_BSS_MAX_IDLE_PERIOD:
275                         if (elen < 3)
276                                 break;
277                         elems->bss_max_idle_period = pos;
278                         break;
279                 case WLAN_EID_SSID_LIST:
280                         elems->ssid_list = pos;
281                         elems->ssid_list_len = elen;
282                         break;
283                 default:
284                         unknown++;
285                         if (!show_errors)
286                                 break;
287                         wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
288                                    "ignored unknown element (id=%d elen=%d)",
289                                    id, elen);
290                         break;
291                 }
292
293                 left -= elen;
294                 pos += elen;
295         }
296
297         if (left)
298                 return ParseFailed;
299
300         return unknown ? ParseUnknown : ParseOK;
301 }
302
303
304 int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
305 {
306         int count = 0;
307         const u8 *pos, *end;
308
309         if (ies == NULL)
310                 return 0;
311
312         pos = ies;
313         end = ies + ies_len;
314
315         while (pos + 2 <= end) {
316                 if (pos + 2 + pos[1] > end)
317                         break;
318                 count++;
319                 pos += 2 + pos[1];
320         }
321
322         return count;
323 }
324
325
326 struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
327                                             u32 oui_type)
328 {
329         struct wpabuf *buf;
330         const u8 *end, *pos, *ie;
331
332         pos = ies;
333         end = ies + ies_len;
334         ie = NULL;
335
336         while (pos + 1 < end) {
337                 if (pos + 2 + pos[1] > end)
338                         return NULL;
339                 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
340                     WPA_GET_BE32(&pos[2]) == oui_type) {
341                         ie = pos;
342                         break;
343                 }
344                 pos += 2 + pos[1];
345         }
346
347         if (ie == NULL)
348                 return NULL; /* No specified vendor IE found */
349
350         buf = wpabuf_alloc(ies_len);
351         if (buf == NULL)
352                 return NULL;
353
354         /*
355          * There may be multiple vendor IEs in the message, so need to
356          * concatenate their data fields.
357          */
358         while (pos + 1 < end) {
359                 if (pos + 2 + pos[1] > end)
360                         break;
361                 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
362                     WPA_GET_BE32(&pos[2]) == oui_type)
363                         wpabuf_put_data(buf, pos + 6, pos[1] - 4);
364                 pos += 2 + pos[1];
365         }
366
367         return buf;
368 }
369
370
371 const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
372 {
373         u16 fc, type, stype;
374
375         /*
376          * PS-Poll frames are 16 bytes. All other frames are
377          * 24 bytes or longer.
378          */
379         if (len < 16)
380                 return NULL;
381
382         fc = le_to_host16(hdr->frame_control);
383         type = WLAN_FC_GET_TYPE(fc);
384         stype = WLAN_FC_GET_STYPE(fc);
385
386         switch (type) {
387         case WLAN_FC_TYPE_DATA:
388                 if (len < 24)
389                         return NULL;
390                 switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
391                 case WLAN_FC_FROMDS | WLAN_FC_TODS:
392                 case WLAN_FC_TODS:
393                         return hdr->addr1;
394                 case WLAN_FC_FROMDS:
395                         return hdr->addr2;
396                 default:
397                         return NULL;
398                 }
399         case WLAN_FC_TYPE_CTRL:
400                 if (stype != WLAN_FC_STYPE_PSPOLL)
401                         return NULL;
402                 return hdr->addr1;
403         case WLAN_FC_TYPE_MGMT:
404                 return hdr->addr3;
405         default:
406                 return NULL;
407         }
408 }
409
410
411 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
412                           const char *name, const char *val)
413 {
414         int num, v;
415         const char *pos;
416         struct hostapd_wmm_ac_params *ac;
417
418         /* skip 'wme_ac_' or 'wmm_ac_' prefix */
419         pos = name + 7;
420         if (os_strncmp(pos, "be_", 3) == 0) {
421                 num = 0;
422                 pos += 3;
423         } else if (os_strncmp(pos, "bk_", 3) == 0) {
424                 num = 1;
425                 pos += 3;
426         } else if (os_strncmp(pos, "vi_", 3) == 0) {
427                 num = 2;
428                 pos += 3;
429         } else if (os_strncmp(pos, "vo_", 3) == 0) {
430                 num = 3;
431                 pos += 3;
432         } else {
433                 wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
434                 return -1;
435         }
436
437         ac = &wmm_ac_params[num];
438
439         if (os_strcmp(pos, "aifs") == 0) {
440                 v = atoi(val);
441                 if (v < 1 || v > 255) {
442                         wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
443                         return -1;
444                 }
445                 ac->aifs = v;
446         } else if (os_strcmp(pos, "cwmin") == 0) {
447                 v = atoi(val);
448                 if (v < 0 || v > 12) {
449                         wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
450                         return -1;
451                 }
452                 ac->cwmin = v;
453         } else if (os_strcmp(pos, "cwmax") == 0) {
454                 v = atoi(val);
455                 if (v < 0 || v > 12) {
456                         wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
457                         return -1;
458                 }
459                 ac->cwmax = v;
460         } else if (os_strcmp(pos, "txop_limit") == 0) {
461                 v = atoi(val);
462                 if (v < 0 || v > 0xffff) {
463                         wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
464                         return -1;
465                 }
466                 ac->txop_limit = v;
467         } else if (os_strcmp(pos, "acm") == 0) {
468                 v = atoi(val);
469                 if (v < 0 || v > 1) {
470                         wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
471                         return -1;
472                 }
473                 ac->admission_control_mandatory = v;
474         } else {
475                 wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
476                 return -1;
477         }
478
479         return 0;
480 }
481
482
483 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
484 {
485         enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES;
486
487         if (freq >= 2412 && freq <= 2472) {
488                 mode = HOSTAPD_MODE_IEEE80211G;
489                 *channel = (freq - 2407) / 5;
490         } else if (freq == 2484) {
491                 mode = HOSTAPD_MODE_IEEE80211B;
492                 *channel = 14;
493         } else if (freq >= 4900 && freq < 5000) {
494                 mode = HOSTAPD_MODE_IEEE80211A;
495                 *channel = (freq - 4000) / 5;
496         } else if (freq >= 5000 && freq < 5900) {
497                 mode = HOSTAPD_MODE_IEEE80211A;
498                 *channel = (freq - 5000) / 5;
499         } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
500                 mode = HOSTAPD_MODE_IEEE80211AD;
501                 *channel = (freq - 56160) / 2160;
502         }
503
504         return mode;
505 }
506
507
508 static int is_11b(u8 rate)
509 {
510         return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
511 }
512
513
514 int supp_rates_11b_only(struct ieee802_11_elems *elems)
515 {
516         int num_11b = 0, num_others = 0;
517         int i;
518
519         if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
520                 return 0;
521
522         for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
523                 if (is_11b(elems->supp_rates[i]))
524                         num_11b++;
525                 else
526                         num_others++;
527         }
528
529         for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
530              i++) {
531                 if (is_11b(elems->ext_supp_rates[i]))
532                         num_11b++;
533                 else
534                         num_others++;
535         }
536
537         return num_11b > 0 && num_others == 0;
538 }