1/*-
2 * Copyright (c) 2001 Atsushi Onoe
3 * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * Alternatively, this software may be distributed under the terms of the
18 * GNU General Public License ("GPL") version 2 as published by the Free
19 * Software Foundation.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32/*
33 * IEEE 802.11 input handling.
34 */
35
36#include "a_config.h"
37#include "athdefs.h"
38#include "a_types.h"
39#include "a_osapi.h"
40#include <wmi.h>
41#include <ieee80211.h>
42#include <wlan_api.h>
43
44#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do {         \
45    if ((_len) < (_minlen)) {                   \
46        return A_EINVAL;                         \
47    }                               \
48} while (0)
49
50#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {         \
51    if ((__elem) == NULL) {                     \
52        return A_EINVAL;                         \
53    }                               \
54    if ((__elem)[1] > (__maxlen)) {                 \
55        return A_EINVAL;                         \
56    }                               \
57} while (0)
58
59
60/* unaligned little endian access */
61#define LE_READ_2(p)                            \
62    ((A_UINT16)                            \
63     ((((A_UINT8 *)(p))[0]      ) | (((A_UINT8 *)(p))[1] <<  8)))
64
65#define LE_READ_4(p)                            \
66    ((A_UINT32)                            \
67     ((((A_UINT8 *)(p))[0]      ) | (((A_UINT8 *)(p))[1] <<  8) | \
68      (((A_UINT8 *)(p))[2] << 16) | (((A_UINT8 *)(p))[3] << 24)))
69
70
71static int __inline
72iswpaoui(const A_UINT8 *frm)
73{
74    return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
75}
76
77static int __inline
78iswmmoui(const A_UINT8 *frm)
79{
80    return frm[1] > 3 && LE_READ_4(frm+2) == ((WMM_OUI_TYPE<<24)|WMM_OUI);
81}
82
83static int __inline
84iswmmparam(const A_UINT8 *frm)
85{
86    return frm[1] > 5 && frm[6] == WMM_PARAM_OUI_SUBTYPE;
87}
88
89static int __inline
90iswmminfo(const A_UINT8 *frm)
91{
92    return frm[1] > 5 && frm[6] == WMM_INFO_OUI_SUBTYPE;
93}
94
95static int __inline
96isatherosoui(const A_UINT8 *frm)
97{
98    return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
99}
100
101static int __inline
102iswscoui(const A_UINT8 *frm)
103{
104    return frm[1] > 3 && LE_READ_4(frm+2) == ((0x04<<24)|WPA_OUI);
105}
106
107A_STATUS
108wlan_parse_beacon(A_UINT8 *buf, int framelen, struct ieee80211_common_ie *cie)
109{
110    A_UINT8 *frm, *efrm;
111
112    frm = buf;
113    efrm = (A_UINT8 *) (frm + framelen);
114
115    /*
116     * beacon/probe response frame format
117     *  [8] time stamp
118     *  [2] beacon interval
119     *  [2] capability information
120     *  [tlv] ssid
121     *  [tlv] supported rates
122     *  [tlv] country information
123     *  [tlv] parameter set (FH/DS)
124     *  [tlv] erp information
125     *  [tlv] extended supported rates
126     *  [tlv] WMM
127     *  [tlv] WPA or RSN
128     *  [tlv] Atheros Advanced Capabilities
129     */
130    IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
131    A_MEMZERO(cie, sizeof(*cie));
132
133    cie->ie_tstamp = frm; frm += 8;
134    cie->ie_beaconInt = A_LE2CPU16(*(A_UINT16 *)frm);  frm += 2;
135    cie->ie_capInfo = A_LE2CPU16(*(A_UINT16 *)frm);  frm += 2;
136    cie->ie_chan = 0;
137
138    while (frm < efrm) {
139        switch (*frm) {
140        case IEEE80211_ELEMID_SSID:
141            cie->ie_ssid = frm;
142            break;
143        case IEEE80211_ELEMID_RATES:
144            cie->ie_rates = frm;
145            break;
146        case IEEE80211_ELEMID_COUNTRY:
147            cie->ie_country = frm;
148            break;
149        case IEEE80211_ELEMID_FHPARMS:
150            break;
151        case IEEE80211_ELEMID_DSPARMS:
152            cie->ie_chan = frm[2];
153            break;
154        case IEEE80211_ELEMID_TIM:
155            cie->ie_tim = frm;
156            break;
157        case IEEE80211_ELEMID_IBSSPARMS:
158            break;
159        case IEEE80211_ELEMID_XRATES:
160            cie->ie_xrates = frm;
161            break;
162        case IEEE80211_ELEMID_ERP:
163            if (frm[1] != 1) {
164                //A_PRINTF("Discarding ERP Element - Bad Len\n");
165                return A_EINVAL;
166            }
167            cie->ie_erp = frm[2];
168            break;
169        case IEEE80211_ELEMID_RSN:
170            cie->ie_rsn = frm;
171            break;
172        case IEEE80211_ELEMID_VENDOR:
173            if (iswpaoui(frm)) {
174                cie->ie_wpa = frm;
175            } else if (iswmmoui(frm)) {
176                cie->ie_wmm = frm;
177            } else if (isatherosoui(frm)) {
178                cie->ie_ath = frm;
179            } else if(iswscoui(frm)) {
180                cie->ie_wsc = frm;
181            }
182            break;
183        default:
184            break;
185        }
186        frm += frm[1] + 2;
187    }
188    IEEE80211_VERIFY_ELEMENT(cie->ie_rates, IEEE80211_RATE_MAXSIZE);
189    IEEE80211_VERIFY_ELEMENT(cie->ie_ssid, IEEE80211_NWID_LEN);
190
191    return A_OK;
192}
193