ieee802_11_shared.c revision 1f69aa52ea2e0a73ac502565df8c666ee49cab6a
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
28u8 * 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 */
55void 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) < 0)
78		perror("ieee802_11_send_sa_query_req: send");
79}
80
81
82static 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) < 0)
116		perror("ieee80211_mgmt_sa_query_request: send");
117}
118
119
120void 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
173u8 * 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
211u8 * 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
251u8 * 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
270u8 * 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
317u8 * 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
337u8 * 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
355int 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