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