Move Ext Capab and Interworking element construction into shared file
[mech_eap.git] / src / ap / ieee802_11_shared.c
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
24
25 #ifdef CONFIG_IEEE80211W
26
27 u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
28                                      struct sta_info *sta, u8 *eid)
29 {
30         u8 *pos = eid;
31         u32 timeout, tu;
32         struct os_time now, passed;
33
34         *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
35         *pos++ = 5;
36         *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
37         os_get_time(&now);
38         os_time_sub(&now, &sta->sa_query_start, &passed);
39         tu = (passed.sec * 1000000 + passed.usec) / 1024;
40         if (hapd->conf->assoc_sa_query_max_timeout > tu)
41                 timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
42         else
43                 timeout = 0;
44         if (timeout < hapd->conf->assoc_sa_query_max_timeout)
45                 timeout++; /* add some extra time for local timers */
46         WPA_PUT_LE32(pos, timeout);
47         pos += 4;
48
49         return pos;
50 }
51
52
53 /* MLME-SAQuery.request */
54 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
55                                   const u8 *addr, const u8 *trans_id)
56 {
57         struct ieee80211_mgmt mgmt;
58         u8 *end;
59
60         wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
61                    MACSTR, MAC2STR(addr));
62         wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
63                     trans_id, WLAN_SA_QUERY_TR_ID_LEN);
64
65         os_memset(&mgmt, 0, sizeof(mgmt));
66         mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
67                                           WLAN_FC_STYPE_ACTION);
68         os_memcpy(mgmt.da, addr, ETH_ALEN);
69         os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
70         os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
71         mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
72         mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
73         os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
74                   WLAN_SA_QUERY_TR_ID_LEN);
75         end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
76         if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
77                 perror("ieee802_11_send_sa_query_req: send");
78 }
79
80
81 void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
82                                    const u8 *sa, const u8 *trans_id)
83 {
84         struct sta_info *sta;
85         struct ieee80211_mgmt resp;
86         u8 *end;
87
88         wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
89                    MACSTR, MAC2STR(sa));
90         wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
91                     trans_id, WLAN_SA_QUERY_TR_ID_LEN);
92
93         sta = ap_get_sta(hapd, sa);
94         if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
95                 wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
96                            "from unassociated STA " MACSTR, MAC2STR(sa));
97                 return;
98         }
99
100         wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
101                    MACSTR, MAC2STR(sa));
102
103         os_memset(&resp, 0, sizeof(resp));
104         resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
105                                           WLAN_FC_STYPE_ACTION);
106         os_memcpy(resp.da, sa, ETH_ALEN);
107         os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
108         os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
109         resp.u.action.category = WLAN_ACTION_SA_QUERY;
110         resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
111         os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
112                   WLAN_SA_QUERY_TR_ID_LEN);
113         end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
114         if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp) < 0)
115                 perror("ieee80211_mgmt_sa_query_request: send");
116 }
117
118
119 void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
120                                 const u8 action_type, const u8 *trans_id)
121 {
122         struct sta_info *sta;
123         int i;
124
125         if (action_type == WLAN_SA_QUERY_REQUEST) {
126                 ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
127                 return;
128         }
129
130         if (action_type != WLAN_SA_QUERY_RESPONSE) {
131                 wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query "
132                            "Action %d", action_type);
133                 return;
134         }
135
136         wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
137                    MACSTR, MAC2STR(sa));
138         wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
139                     trans_id, WLAN_SA_QUERY_TR_ID_LEN);
140
141         /* MLME-SAQuery.confirm */
142
143         sta = ap_get_sta(hapd, sa);
144         if (sta == NULL || sta->sa_query_trans_id == NULL) {
145                 wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
146                            "pending SA Query request found");
147                 return;
148         }
149
150         for (i = 0; i < sta->sa_query_count; i++) {
151                 if (os_memcmp(sta->sa_query_trans_id +
152                               i * WLAN_SA_QUERY_TR_ID_LEN,
153                               trans_id, WLAN_SA_QUERY_TR_ID_LEN) == 0)
154                         break;
155         }
156
157         if (i >= sta->sa_query_count) {
158                 wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query "
159                            "transaction identifier found");
160                 return;
161         }
162
163         hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
164                        HOSTAPD_LEVEL_DEBUG,
165                        "Reply to pending SA Query received");
166         ap_sta_stop_sa_query(hapd, sta);
167 }
168
169 #endif /* CONFIG_IEEE80211W */
170
171
172 u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
173 {
174         u8 *pos = eid;
175         u8 len = 0;
176
177         if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
178                 len = 5;
179         if (len < 4 && hapd->conf->interworking)
180                 len = 4;
181         if (len == 0)
182                 return eid;
183
184         *pos++ = WLAN_EID_EXT_CAPAB;
185         *pos++ = len;
186         *pos++ = 0x00;
187         *pos++ = 0x00;
188         *pos++ = 0x00;
189
190         *pos = 0x00;
191         if (hapd->conf->interworking)
192                 *pos |= 0x80; /* Bit 31 - Interworking */
193         pos++;
194
195         if (len < 5)
196                 return pos;
197         *pos = 0x00;
198         if (hapd->conf->tdls & TDLS_PROHIBIT)
199                 *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
200         if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
201                 *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
202         pos++;
203
204         return pos;
205 }
206
207
208 u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
209 {
210         u8 *pos = eid;
211 #ifdef CONFIG_INTERWORKING
212         u8 *len;
213
214         if (!hapd->conf->interworking)
215                 return eid;
216
217         *pos++ = WLAN_EID_INTERWORKING;
218         len = pos++;
219
220         *pos = hapd->conf->access_network_type;
221         if (hapd->conf->internet)
222                 *pos |= INTERWORKING_ANO_INTERNET;
223         if (hapd->conf->asra)
224                 *pos |= INTERWORKING_ANO_ASRA;
225         if (hapd->conf->esr)
226                 *pos |= INTERWORKING_ANO_ESR;
227         if (hapd->conf->uesa)
228                 *pos |= INTERWORKING_ANO_UESA;
229         pos++;
230
231         if (hapd->conf->venue_info_set) {
232                 *pos++ = hapd->conf->venue_group;
233                 *pos++ = hapd->conf->venue_type;
234         }
235
236         if (!is_zero_ether_addr(hapd->conf->hessid)) {
237                 os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
238                 pos += ETH_ALEN;
239         }
240
241         *len = pos - len - 1;
242 #endif /* CONFIG_INTERWORKING */
243
244         return pos;
245 }