xref: /netbsd-src/external/bsd/wpa/dist/src/ap/ieee802_11_shared.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*
2  * hostapd / IEEE 802.11 Management
3  * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14 
15 #include "utils/includes.h"
16 
17 #include "utils/common.h"
18 #include "common/ieee802_11_defs.h"
19 #include "hostapd.h"
20 #include "sta_info.h"
21 #include "ap_config.h"
22 #include "ap_drv_ops.h"
23 #include "ieee802_11.h"
24 
25 
26 #ifdef CONFIG_IEEE80211W
27 
28 u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
29 				     struct sta_info *sta, u8 *eid)
30 {
31 	u8 *pos = eid;
32 	u32 timeout, tu;
33 	struct os_time now, passed;
34 
35 	*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
36 	*pos++ = 5;
37 	*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
38 	os_get_time(&now);
39 	os_time_sub(&now, &sta->sa_query_start, &passed);
40 	tu = (passed.sec * 1000000 + passed.usec) / 1024;
41 	if (hapd->conf->assoc_sa_query_max_timeout > tu)
42 		timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
43 	else
44 		timeout = 0;
45 	if (timeout < hapd->conf->assoc_sa_query_max_timeout)
46 		timeout++; /* add some extra time for local timers */
47 	WPA_PUT_LE32(pos, timeout);
48 	pos += 4;
49 
50 	return pos;
51 }
52 
53 
54 /* MLME-SAQuery.request */
55 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
56 				  const u8 *addr, const u8 *trans_id)
57 {
58 	struct ieee80211_mgmt mgmt;
59 	u8 *end;
60 
61 	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
62 		   MACSTR, MAC2STR(addr));
63 	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
64 		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
65 
66 	os_memset(&mgmt, 0, sizeof(mgmt));
67 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
68 					  WLAN_FC_STYPE_ACTION);
69 	os_memcpy(mgmt.da, addr, ETH_ALEN);
70 	os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
71 	os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
72 	mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
73 	mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
74 	os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
75 		  WLAN_SA_QUERY_TR_ID_LEN);
76 	end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
77 	if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
78 		perror("ieee802_11_send_sa_query_req: send");
79 }
80 
81 
82 void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
83 				   const u8 *sa, const u8 *trans_id)
84 {
85 	struct sta_info *sta;
86 	struct ieee80211_mgmt resp;
87 	u8 *end;
88 
89 	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
90 		   MACSTR, MAC2STR(sa));
91 	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
92 		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
93 
94 	sta = ap_get_sta(hapd, sa);
95 	if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
96 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
97 			   "from unassociated STA " MACSTR, MAC2STR(sa));
98 		return;
99 	}
100 
101 	wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
102 		   MACSTR, MAC2STR(sa));
103 
104 	os_memset(&resp, 0, sizeof(resp));
105 	resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
106 					  WLAN_FC_STYPE_ACTION);
107 	os_memcpy(resp.da, sa, ETH_ALEN);
108 	os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
109 	os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
110 	resp.u.action.category = WLAN_ACTION_SA_QUERY;
111 	resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
112 	os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
113 		  WLAN_SA_QUERY_TR_ID_LEN);
114 	end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
115 	if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0)
116 		perror("ieee80211_mgmt_sa_query_request: send");
117 }
118 
119 
120 void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
121 				const u8 action_type, const u8 *trans_id)
122 {
123 	struct sta_info *sta;
124 	int i;
125 
126 	if (action_type == WLAN_SA_QUERY_REQUEST) {
127 		ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
128 		return;
129 	}
130 
131 	if (action_type != WLAN_SA_QUERY_RESPONSE) {
132 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
133 			   "Action %d", action_type);
134 		return;
135 	}
136 
137 	wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
138 		   MACSTR, MAC2STR(sa));
139 	wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
140 		    trans_id, WLAN_SA_QUERY_TR_ID_LEN);
141 
142 	/* MLME-SAQuery.confirm */
143 
144 	sta = ap_get_sta(hapd, sa);
145 	if (sta == NULL || sta->sa_query_trans_id == NULL) {
146 		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
147 			   "pending SA Query request found");
148 		return;
149 	}
150 
151 	for (i = 0; i < sta->sa_query_count; i++) {
152 		if (os_memcmp(sta->sa_query_trans_id +
153 			      i * WLAN_SA_QUERY_TR_ID_LEN,
154 			      trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
155 			break;
156 	}
157 
158 	if (i >= sta->sa_query_count) {
159 		wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
160 			   "transaction identifier found");
161 		return;
162 	}
163 
164 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
165 		       HOSTAPD_LEVEL_DEBUG,
166 		       "Reply to pending SA Query received");
167 	ap_sta_stop_sa_query(hapd, sta);
168 }
169 
170 #endif /* CONFIG_IEEE80211W */
171 
172 
173 u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
174 {
175 	u8 *pos = eid;
176 	u8 len = 0;
177 
178 	if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
179 		len = 5;
180 	if (len < 4 && hapd->conf->interworking)
181 		len = 4;
182 	if (len == 0)
183 		return eid;
184 
185 	*pos++ = WLAN_EID_EXT_CAPAB;
186 	*pos++ = len;
187 	*pos++ = 0x00;
188 	*pos++ = 0x00;
189 	*pos++ = 0x00;
190 
191 	*pos = 0x00;
192 	if (hapd->conf->time_advertisement == 2)
193 		*pos |= 0x08; /* Bit 27 - UTC TSF Offset */
194 	if (hapd->conf->interworking)
195 		*pos |= 0x80; /* Bit 31 - Interworking */
196 	pos++;
197 
198 	if (len < 5)
199 		return pos;
200 	*pos = 0x00;
201 	if (hapd->conf->tdls & TDLS_PROHIBIT)
202 		*pos |= 0x40; /* Bit 38 - TDLS Prohibited */
203 	if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
204 		*pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
205 	pos++;
206 
207 	return pos;
208 }
209 
210 
211 u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
212 {
213 	u8 *pos = eid;
214 #ifdef CONFIG_INTERWORKING
215 	u8 *len;
216 
217 	if (!hapd->conf->interworking)
218 		return eid;
219 
220 	*pos++ = WLAN_EID_INTERWORKING;
221 	len = pos++;
222 
223 	*pos = hapd->conf->access_network_type;
224 	if (hapd->conf->internet)
225 		*pos |= INTERWORKING_ANO_INTERNET;
226 	if (hapd->conf->asra)
227 		*pos |= INTERWORKING_ANO_ASRA;
228 	if (hapd->conf->esr)
229 		*pos |= INTERWORKING_ANO_ESR;
230 	if (hapd->conf->uesa)
231 		*pos |= INTERWORKING_ANO_UESA;
232 	pos++;
233 
234 	if (hapd->conf->venue_info_set) {
235 		*pos++ = hapd->conf->venue_group;
236 		*pos++ = hapd->conf->venue_type;
237 	}
238 
239 	if (!is_zero_ether_addr(hapd->conf->hessid)) {
240 		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
241 		pos += ETH_ALEN;
242 	}
243 
244 	*len = pos - len - 1;
245 #endif /* CONFIG_INTERWORKING */
246 
247 	return pos;
248 }
249 
250 
251 u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid)
252 {
253 	u8 *pos = eid;
254 #ifdef CONFIG_INTERWORKING
255 
256 	/* TODO: Separate configuration for ANQP? */
257 	if (!hapd->conf->interworking)
258 		return eid;
259 
260 	*pos++ = WLAN_EID_ADV_PROTO;
261 	*pos++ = 2;
262 	*pos++ = 0x7F; /* Query Response Length Limit | PAME-BI */
263 	*pos++ = ACCESS_NETWORK_QUERY_PROTOCOL;
264 #endif /* CONFIG_INTERWORKING */
265 
266 	return pos;
267 }
268 
269 
270 u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid)
271 {
272 	u8 *pos = eid;
273 #ifdef CONFIG_INTERWORKING
274 	u8 *len;
275 	unsigned int i, count;
276 
277 	if (!hapd->conf->interworking ||
278 	    hapd->conf->roaming_consortium == NULL ||
279 	    hapd->conf->roaming_consortium_count == 0)
280 		return eid;
281 
282 	*pos++ = WLAN_EID_ROAMING_CONSORTIUM;
283 	len = pos++;
284 
285 	/* Number of ANQP OIs (in addition to the max 3 listed here) */
286 	if (hapd->conf->roaming_consortium_count > 3 + 255)
287 		*pos++ = 255;
288 	else if (hapd->conf->roaming_consortium_count > 3)
289 		*pos++ = hapd->conf->roaming_consortium_count - 3;
290 	else
291 		*pos++ = 0;
292 
293 	/* OU #1 and #2 Lengths */
294 	*pos = hapd->conf->roaming_consortium[0].len;
295 	if (hapd->conf->roaming_consortium_count > 1)
296 		*pos |= hapd->conf->roaming_consortium[1].len << 4;
297 	pos++;
298 
299 	if (hapd->conf->roaming_consortium_count > 3)
300 		count = 3;
301 	else
302 		count = hapd->conf->roaming_consortium_count;
303 
304 	for (i = 0; i < count; i++) {
305 		os_memcpy(pos, hapd->conf->roaming_consortium[i].oi,
306 			  hapd->conf->roaming_consortium[i].len);
307 		pos += hapd->conf->roaming_consortium[i].len;
308 	}
309 
310 	*len = pos - len - 1;
311 #endif /* CONFIG_INTERWORKING */
312 
313 	return pos;
314 }
315 
316 
317 u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid)
318 {
319 	if (hapd->conf->time_advertisement != 2)
320 		return eid;
321 
322 	if (hapd->time_adv == NULL &&
323 	    hostapd_update_time_adv(hapd) < 0)
324 		return eid;
325 
326 	if (hapd->time_adv == NULL)
327 		return eid;
328 
329 	os_memcpy(eid, wpabuf_head(hapd->time_adv),
330 		  wpabuf_len(hapd->time_adv));
331 	eid += wpabuf_len(hapd->time_adv);
332 
333 	return eid;
334 }
335 
336 
337 u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid)
338 {
339 	size_t len;
340 
341 	if (hapd->conf->time_advertisement != 2)
342 		return eid;
343 
344 	len = os_strlen(hapd->conf->time_zone);
345 
346 	*eid++ = WLAN_EID_TIME_ZONE;
347 	*eid++ = len;
348 	os_memcpy(eid, hapd->conf->time_zone, len);
349 	eid += len;
350 
351 	return eid;
352 }
353 
354 
355 int hostapd_update_time_adv(struct hostapd_data *hapd)
356 {
357 	const int elen = 2 + 1 + 10 + 5 + 1;
358 	struct os_time t;
359 	struct os_tm tm;
360 	u8 *pos;
361 
362 	if (hapd->conf->time_advertisement != 2)
363 		return 0;
364 
365 	if (os_get_time(&t) < 0 || os_gmtime(t.sec, &tm) < 0)
366 		return -1;
367 
368 	if (!hapd->time_adv) {
369 		hapd->time_adv = wpabuf_alloc(elen);
370 		if (hapd->time_adv == NULL)
371 			return -1;
372 		pos = wpabuf_put(hapd->time_adv, elen);
373 	} else
374 		pos = wpabuf_mhead_u8(hapd->time_adv);
375 
376 	*pos++ = WLAN_EID_TIME_ADVERTISEMENT;
377 	*pos++ = 1 + 10 + 5 + 1;
378 
379 	*pos++ = 2; /* UTC time at which the TSF timer is 0 */
380 
381 	/* Time Value at TSF 0 */
382 	/* FIX: need to calculate this based on the current TSF value */
383 	WPA_PUT_LE16(pos, tm.year); /* Year */
384 	pos += 2;
385 	*pos++ = tm.month; /* Month */
386 	*pos++ = tm.day; /* Day of month */
387 	*pos++ = tm.hour; /* Hours */
388 	*pos++ = tm.min; /* Minutes */
389 	*pos++ = tm.sec; /* Seconds */
390 	WPA_PUT_LE16(pos, 0); /* Milliseconds (not used) */
391 	pos += 2;
392 	*pos++ = 0; /* Reserved */
393 
394 	/* Time Error */
395 	/* TODO: fill in an estimate on the error */
396 	*pos++ = 0;
397 	*pos++ = 0;
398 	*pos++ = 0;
399 	*pos++ = 0;
400 	*pos++ = 0;
401 
402 	*pos++ = hapd->time_update_counter++;
403 
404 	return 0;
405 }
406