Line data Source code
1 : /*
2 : * Generic advertisement service (GAS) (IEEE 802.11u)
3 : * Copyright (c) 2009, Atheros Communications
4 : * Copyright (c) 2011-2012, Qualcomm Atheros
5 : *
6 : * This software may be distributed under the terms of the BSD license.
7 : * See README for more details.
8 : */
9 :
10 : #include "includes.h"
11 :
12 : #include "common.h"
13 : #include "ieee802_11_defs.h"
14 : #include "gas.h"
15 :
16 :
17 : static struct wpabuf *
18 465 : gas_build_req(u8 action, u8 dialog_token, size_t size)
19 : {
20 : struct wpabuf *buf;
21 :
22 465 : buf = wpabuf_alloc(100 + size);
23 465 : if (buf == NULL)
24 0 : return NULL;
25 :
26 465 : wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
27 465 : wpabuf_put_u8(buf, action);
28 465 : wpabuf_put_u8(buf, dialog_token);
29 :
30 465 : return buf;
31 : }
32 :
33 :
34 270 : struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size)
35 : {
36 270 : return gas_build_req(WLAN_PA_GAS_INITIAL_REQ, dialog_token,
37 : size);
38 : }
39 :
40 :
41 195 : struct wpabuf * gas_build_comeback_req(u8 dialog_token)
42 : {
43 195 : return gas_build_req(WLAN_PA_GAS_COMEBACK_REQ, dialog_token, 0);
44 : }
45 :
46 :
47 : static struct wpabuf *
48 317 : gas_build_resp(u8 action, u8 dialog_token, u16 status_code, u8 frag_id,
49 : u8 more, u16 comeback_delay, size_t size)
50 : {
51 : struct wpabuf *buf;
52 :
53 317 : buf = wpabuf_alloc(100 + size);
54 317 : if (buf == NULL)
55 0 : return NULL;
56 :
57 317 : wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
58 317 : wpabuf_put_u8(buf, action);
59 317 : wpabuf_put_u8(buf, dialog_token);
60 317 : wpabuf_put_le16(buf, status_code);
61 317 : if (action == WLAN_PA_GAS_COMEBACK_RESP)
62 55 : wpabuf_put_u8(buf, frag_id | (more ? 0x80 : 0));
63 317 : wpabuf_put_le16(buf, comeback_delay);
64 :
65 317 : return buf;
66 : }
67 :
68 :
69 : struct wpabuf *
70 262 : gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay,
71 : size_t size)
72 : {
73 262 : return gas_build_resp(WLAN_PA_GAS_INITIAL_RESP, dialog_token,
74 : status_code, 0, 0, comeback_delay, size);
75 : }
76 :
77 :
78 : static struct wpabuf *
79 55 : gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more,
80 : u16 comeback_delay, size_t size)
81 : {
82 55 : return gas_build_resp(WLAN_PA_GAS_COMEBACK_RESP, dialog_token,
83 : status_code, frag_id, more, comeback_delay,
84 : size);
85 : }
86 :
87 :
88 : /**
89 : * gas_add_adv_proto_anqp - Add an Advertisement Protocol element
90 : * @buf: Buffer to which the element is added
91 : * @query_resp_len_limit: Query Response Length Limit in units of 256 octets
92 : * @pame_bi: Pre-Association Message Exchange BSSID Independent (0/1)
93 : *
94 : *
95 : * @query_resp_len_limit is 0 for request and 1-0x7f for response. 0x7f means
96 : * that the maximum limit is determined by the maximum allowable number of
97 : * fragments in the GAS Query Response Fragment ID.
98 : */
99 578 : static void gas_add_adv_proto_anqp(struct wpabuf *buf, u8 query_resp_len_limit,
100 : u8 pame_bi)
101 : {
102 : /* Advertisement Protocol IE */
103 578 : wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
104 578 : wpabuf_put_u8(buf, 2); /* Length */
105 578 : wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
106 : (pame_bi ? 0x80 : 0));
107 : /* Advertisement Protocol */
108 578 : wpabuf_put_u8(buf, ACCESS_NETWORK_QUERY_PROTOCOL);
109 578 : }
110 :
111 :
112 262 : struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size)
113 : {
114 : struct wpabuf *buf;
115 :
116 262 : buf = gas_build_initial_req(dialog_token, 4 + size);
117 262 : if (buf == NULL)
118 0 : return NULL;
119 :
120 262 : gas_add_adv_proto_anqp(buf, 0, 0);
121 :
122 262 : wpabuf_put(buf, 2); /* Query Request Length to be filled */
123 :
124 262 : return buf;
125 : }
126 :
127 :
128 261 : struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code,
129 : u16 comeback_delay, size_t size)
130 : {
131 : struct wpabuf *buf;
132 :
133 261 : buf = gas_build_initial_resp(dialog_token, status_code, comeback_delay,
134 : 4 + size);
135 261 : if (buf == NULL)
136 0 : return NULL;
137 :
138 261 : gas_add_adv_proto_anqp(buf, 0x7f, 0);
139 :
140 261 : wpabuf_put(buf, 2); /* Query Response Length to be filled */
141 :
142 261 : return buf;
143 : }
144 :
145 :
146 201 : struct wpabuf * gas_anqp_build_initial_resp_buf(u8 dialog_token,
147 : u16 status_code,
148 : u16 comeback_delay,
149 : struct wpabuf *payload)
150 : {
151 : struct wpabuf *buf;
152 :
153 201 : buf = gas_anqp_build_initial_resp(dialog_token, status_code,
154 : comeback_delay,
155 : payload ? wpabuf_len(payload) : 0);
156 201 : if (buf == NULL)
157 0 : return NULL;
158 :
159 201 : if (payload)
160 185 : wpabuf_put_buf(buf, payload);
161 :
162 201 : gas_anqp_set_len(buf);
163 :
164 201 : return buf;
165 : }
166 :
167 :
168 55 : struct wpabuf * gas_anqp_build_comeback_resp(u8 dialog_token, u16 status_code,
169 : u8 frag_id, u8 more,
170 : u16 comeback_delay, size_t size)
171 : {
172 : struct wpabuf *buf;
173 :
174 55 : buf = gas_build_comeback_resp(dialog_token, status_code,
175 : frag_id, more, comeback_delay, 4 + size);
176 55 : if (buf == NULL)
177 0 : return NULL;
178 :
179 55 : gas_add_adv_proto_anqp(buf, 0x7f, 0);
180 :
181 55 : wpabuf_put(buf, 2); /* Query Response Length to be filled */
182 :
183 55 : return buf;
184 : }
185 :
186 :
187 37 : struct wpabuf * gas_anqp_build_comeback_resp_buf(u8 dialog_token,
188 : u16 status_code,
189 : u8 frag_id, u8 more,
190 : u16 comeback_delay,
191 : struct wpabuf *payload)
192 : {
193 : struct wpabuf *buf;
194 :
195 37 : buf = gas_anqp_build_comeback_resp(dialog_token, status_code, frag_id,
196 : more, comeback_delay,
197 : payload ? wpabuf_len(payload) : 0);
198 37 : if (buf == NULL)
199 0 : return NULL;
200 :
201 37 : if (payload)
202 36 : wpabuf_put_buf(buf, payload);
203 :
204 37 : gas_anqp_set_len(buf);
205 :
206 37 : return buf;
207 : }
208 :
209 :
210 : /**
211 : * gas_anqp_set_len - Set Query Request/Response Length
212 : * @buf: GAS message
213 : *
214 : * This function is used to update the Query Request/Response Length field once
215 : * the payload has been filled.
216 : */
217 579 : void gas_anqp_set_len(struct wpabuf *buf)
218 : {
219 : u8 action;
220 : size_t offset;
221 : u8 *len;
222 :
223 579 : if (buf == NULL || wpabuf_len(buf) < 2)
224 0 : return;
225 :
226 579 : action = *(wpabuf_head_u8(buf) + 1);
227 579 : switch (action) {
228 : case WLAN_PA_GAS_INITIAL_REQ:
229 262 : offset = 3 + 4;
230 262 : break;
231 : case WLAN_PA_GAS_INITIAL_RESP:
232 261 : offset = 7 + 4;
233 261 : break;
234 : case WLAN_PA_GAS_COMEBACK_RESP:
235 55 : offset = 8 + 4;
236 55 : break;
237 : default:
238 1 : return;
239 : }
240 :
241 578 : if (wpabuf_len(buf) < offset + 2)
242 0 : return;
243 :
244 578 : len = wpabuf_mhead_u8(buf) + offset;
245 578 : WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
246 : }
247 :
248 :
249 : /**
250 : * gas_anqp_add_element - Add ANQP element header
251 : * @buf: GAS message
252 : * @info_id: ANQP Info ID
253 : * Returns: Pointer to the Length field for gas_anqp_set_element_len()
254 : */
255 1329 : u8 * gas_anqp_add_element(struct wpabuf *buf, u16 info_id)
256 : {
257 1329 : wpabuf_put_le16(buf, info_id);
258 1329 : return wpabuf_put(buf, 2); /* Length to be filled */
259 : }
260 :
261 :
262 : /**
263 : * gas_anqp_set_element_len - Update ANQP element Length field
264 : * @buf: GAS message
265 : * @len_pos: Length field position from gas_anqp_add_element()
266 : *
267 : * This function is called after the ANQP element payload has been added to the
268 : * buffer.
269 : */
270 1631 : void gas_anqp_set_element_len(struct wpabuf *buf, u8 *len_pos)
271 : {
272 1631 : WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(buf, 0) - len_pos - 2);
273 1631 : }
|