1 /*
2 * Driver interaction with extended Linux Wireless Extensions
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Alternatively, this software may be distributed under the terms of BSD
9 * license.
10 *
11 */
12
13 #include "includes.h"
14 #include <sys/ioctl.h>
15 #include <net/if_arp.h>
16 #include <net/if.h>
17
18 #include "wireless_copy.h"
19 #include "common.h"
20 #include "driver.h"
21 #include "eloop.h"
22 #include "priv_netlink.h"
23 #include "driver_wext.h"
24 #include "ieee802_11_defs.h"
25 #include "wpa_common.h"
26 #include "wpa_ctrl.h"
27 #include "wpa_supplicant_i.h"
28 #include "config.h"
29 #include "linux_ioctl.h"
30 #include "scan.h"
31
32 #include "driver_cmd_wext.h"
33 #include "driver_cmd_common.h"
34
35 /**
36 * wpa_driver_wext_set_scan_timeout - Set scan timeout to report scan completion
37 * @priv: Pointer to private wext data from wpa_driver_wext_init()
38 *
39 * This function can be used to set registered timeout when starting a scan to
40 * generate a scan completed event if the driver does not report this.
41 */
wpa_driver_wext_set_scan_timeout(void * priv)42 static void wpa_driver_wext_set_scan_timeout(void *priv)
43 {
44 struct wpa_driver_wext_data *drv = priv;
45 int timeout = 10; /* In case scan A and B bands it can be long */
46
47 /* Not all drivers generate "scan completed" wireless event, so try to
48 * read results after a timeout. */
49 if (drv->scan_complete_events) {
50 /*
51 * The driver seems to deliver SIOCGIWSCAN events to notify
52 * when scan is complete, so use longer timeout to avoid race
53 * conditions with scanning and following association request.
54 */
55 timeout = 30;
56 }
57 wpa_printf(MSG_DEBUG, "Scan requested - scan timeout %d seconds",
58 timeout);
59 eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
60 eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
61 drv->ctx);
62 }
63
64 /**
65 * wpa_driver_wext_combo_scan - Request the driver to initiate combo scan
66 * @priv: Pointer to private wext data from wpa_driver_wext_init()
67 * @params: Scan parameters
68 * Returns: 0 on success, -1 on failure
69 */
wpa_driver_wext_combo_scan(void * priv,struct wpa_driver_scan_params * params)70 int wpa_driver_wext_combo_scan(void *priv, struct wpa_driver_scan_params *params)
71 {
72 char buf[WEXT_CSCAN_BUF_LEN];
73 struct wpa_driver_wext_data *drv = priv;
74 struct iwreq iwr;
75 int ret, bp;
76 unsigned i;
77 struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx);
78
79 if (!drv->driver_is_started) {
80 wpa_printf(MSG_DEBUG, "%s: Driver stopped", __func__);
81 return 0;
82 }
83
84 wpa_printf(MSG_DEBUG, "%s: Start", __func__);
85
86 /* Set list of SSIDs */
87 bp = WEXT_CSCAN_HEADER_SIZE;
88 os_memcpy(buf, WEXT_CSCAN_HEADER, bp);
89 for(i=0; i < params->num_ssids; i++) {
90 if ((bp + IW_ESSID_MAX_SIZE + 10) >= (int)sizeof(buf))
91 break;
92 wpa_printf(MSG_DEBUG, "For Scan: %s", params->ssids[i].ssid);
93 buf[bp++] = WEXT_CSCAN_SSID_SECTION;
94 buf[bp++] = params->ssids[i].ssid_len;
95 os_memcpy(&buf[bp], params->ssids[i].ssid, params->ssids[i].ssid_len);
96 bp += params->ssids[i].ssid_len;
97 }
98
99 /* Set list of channels */
100 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION;
101 buf[bp++] = 0;
102
103 /* Set passive dwell time (default is 250) */
104 buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION;
105 buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME;
106 buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME >> 8);
107
108 /* Set home dwell time (default is 40) */
109 buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION;
110 buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME;
111 buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8);
112
113 os_memset(&iwr, 0, sizeof(iwr));
114 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
115 iwr.u.data.pointer = buf;
116 iwr.u.data.length = bp;
117
118 if ((ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr)) < 0) {
119 if (!drv->bgscan_enabled)
120 wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (cscan): %d", ret);
121 else
122 ret = 0; /* Hide error in case of bg scan */
123 }
124 return ret;
125 }
126
wpa_driver_wext_set_cscan_params(char * buf,size_t buf_len,char * cmd)127 static int wpa_driver_wext_set_cscan_params(char *buf, size_t buf_len, char *cmd)
128 {
129 char *pasv_ptr;
130 int bp, i;
131 u16 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF;
132 u8 channel;
133
134 wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
135
136 /* Get command parameters */
137 pasv_ptr = os_strstr(cmd, ",TIME=");
138 if (pasv_ptr) {
139 *pasv_ptr = '\0';
140 pasv_ptr += 6;
141 pasv_dwell = (u16)atoi(pasv_ptr);
142 if (pasv_dwell == 0)
143 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF;
144 }
145 channel = (u8)atoi(cmd + 5);
146
147 bp = WEXT_CSCAN_HEADER_SIZE;
148 os_memcpy(buf, WEXT_CSCAN_HEADER, bp);
149
150 /* Set list of channels */
151 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION;
152 buf[bp++] = channel;
153 if (channel != 0) {
154 i = (pasv_dwell - 1) / WEXT_CSCAN_PASV_DWELL_TIME_DEF;
155 for (; i > 0; i--) {
156 if ((size_t)(bp + 12) >= buf_len)
157 break;
158 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION;
159 buf[bp++] = channel;
160 }
161 } else {
162 if (pasv_dwell > WEXT_CSCAN_PASV_DWELL_TIME_MAX)
163 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_MAX;
164 }
165
166 /* Set passive dwell time (default is 250) */
167 buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION;
168 if (channel != 0) {
169 buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME_DEF;
170 buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME_DEF >> 8);
171 } else {
172 buf[bp++] = (u8)pasv_dwell;
173 buf[bp++] = (u8)(pasv_dwell >> 8);
174 }
175
176 /* Set home dwell time (default is 40) */
177 buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION;
178 buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME;
179 buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8);
180
181 /* Set cscan type */
182 buf[bp++] = WEXT_CSCAN_TYPE_SECTION;
183 buf[bp++] = WEXT_CSCAN_TYPE_PASSIVE;
184 return bp;
185 }
186
wpa_driver_get_country_code(int channels)187 static char *wpa_driver_get_country_code(int channels)
188 {
189 char *country = "US"; /* WEXT_NUMBER_SCAN_CHANNELS_FCC */
190
191 if (channels == WEXT_NUMBER_SCAN_CHANNELS_ETSI)
192 country = "EU";
193 else if( channels == WEXT_NUMBER_SCAN_CHANNELS_MKK1)
194 country = "JP";
195 return country;
196 }
197
wpa_driver_set_backgroundscan_params(void * priv)198 static int wpa_driver_set_backgroundscan_params(void *priv)
199 {
200 struct wpa_driver_wext_data *drv = priv;
201 struct wpa_supplicant *wpa_s;
202 struct iwreq iwr;
203 int ret = 0, i = 0, bp;
204 char buf[WEXT_PNO_MAX_COMMAND_SIZE];
205 struct wpa_ssid *ssid_conf;
206
207 if (drv == NULL) {
208 wpa_printf(MSG_ERROR, "%s: drv is NULL. Exiting", __func__);
209 return -1;
210 }
211 if (drv->ctx == NULL) {
212 wpa_printf(MSG_ERROR, "%s: drv->ctx is NULL. Exiting", __func__);
213 return -1;
214 }
215 wpa_s = (struct wpa_supplicant *)(drv->ctx);
216 if (wpa_s->conf == NULL) {
217 wpa_printf(MSG_ERROR, "%s: wpa_s->conf is NULL. Exiting", __func__);
218 return -1;
219 }
220 ssid_conf = wpa_s->conf->ssid;
221
222 bp = WEXT_PNOSETUP_HEADER_SIZE;
223 os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
224 buf[bp++] = WEXT_PNO_TLV_PREFIX;
225 buf[bp++] = WEXT_PNO_TLV_VERSION;
226 buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
227 buf[bp++] = WEXT_PNO_TLV_RESERVED;
228
229 while ((i < WEXT_PNO_AMOUNT) && (ssid_conf != NULL)) {
230 /* Check that there is enough space needed for 1 more SSID, the other sections and null termination */
231 if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int)sizeof(buf))
232 break;
233 if ((!ssid_conf->disabled) && (ssid_conf->ssid_len <= IW_ESSID_MAX_SIZE)){
234 wpa_printf(MSG_DEBUG, "For PNO Scan: %s", ssid_conf->ssid);
235 buf[bp++] = WEXT_PNO_SSID_SECTION;
236 buf[bp++] = ssid_conf->ssid_len;
237 os_memcpy(&buf[bp], ssid_conf->ssid, ssid_conf->ssid_len);
238 bp += ssid_conf->ssid_len;
239 i++;
240 }
241 ssid_conf = ssid_conf->next;
242 }
243
244 buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
245 os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL);
246 bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
247
248 buf[bp++] = WEXT_PNO_REPEAT_SECTION;
249 os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT);
250 bp += WEXT_PNO_REPEAT_LENGTH;
251
252 buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
253 os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT);
254 bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
255
256 os_memset(&iwr, 0, sizeof(iwr));
257 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
258 iwr.u.data.pointer = buf;
259 iwr.u.data.length = bp;
260
261 ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
262
263 if (ret < 0) {
264 wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret);
265 drv->errors++;
266 if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
267 drv->errors = 0;
268 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
269 }
270 } else {
271 drv->errors = 0;
272 }
273 return ret;
274
275 }
276
wpa_driver_wext_driver_cmd(void * priv,char * cmd,char * buf,size_t buf_len)277 int wpa_driver_wext_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len )
278 {
279 struct wpa_driver_wext_data *drv = priv;
280 struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx);
281 struct iwreq iwr;
282 int ret = 0, flags;
283
284 wpa_printf(MSG_DEBUG, "%s %s len = %d", __func__, cmd, buf_len);
285
286 if (!drv->driver_is_started && (os_strcasecmp(cmd, "START") != 0)) {
287 wpa_printf(MSG_ERROR,"WEXT: Driver not initialized yet");
288 return -1;
289 }
290
291 if (os_strcasecmp(cmd, "RSSI-APPROX") == 0) {
292 os_strncpy(cmd, RSSI_CMD, MAX_DRV_CMD_SIZE);
293 } else if( os_strncasecmp(cmd, "SCAN-CHANNELS", 13) == 0 ) {
294 int no_of_chan;
295
296 no_of_chan = atoi(cmd + 13);
297 os_snprintf(cmd, MAX_DRV_CMD_SIZE, "COUNTRY %s",
298 wpa_driver_get_country_code(no_of_chan));
299 } else if (os_strcasecmp(cmd, "STOP") == 0) {
300 linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
301 } else if( os_strcasecmp(cmd, "RELOAD") == 0 ) {
302 wpa_printf(MSG_DEBUG,"Reload command");
303 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
304 return ret;
305 } else if( os_strcasecmp(cmd, "BGSCAN-START") == 0 ) {
306 ret = wpa_driver_set_backgroundscan_params(priv);
307 if (ret < 0) {
308 return ret;
309 }
310 os_strncpy(cmd, "PNOFORCE 1", MAX_DRV_CMD_SIZE);
311 drv->bgscan_enabled = 1;
312 } else if( os_strcasecmp(cmd, "BGSCAN-STOP") == 0 ) {
313 os_strncpy(cmd, "PNOFORCE 0", MAX_DRV_CMD_SIZE);
314 drv->bgscan_enabled = 0;
315 }
316
317 os_memset(&iwr, 0, sizeof(iwr));
318 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
319 os_memcpy(buf, cmd, strlen(cmd) + 1);
320 iwr.u.data.pointer = buf;
321 iwr.u.data.length = buf_len;
322
323 if( os_strncasecmp(cmd, "CSCAN", 5) == 0 ) {
324 if (!wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) ||
325 (wpa_s->wpa_state >= WPA_COMPLETED))) {
326 iwr.u.data.length = wpa_driver_wext_set_cscan_params(buf, buf_len, cmd);
327 } else {
328 wpa_printf(MSG_ERROR, "Ongoing Scan action...");
329 return ret;
330 }
331 }
332
333 ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
334
335 if (ret < 0) {
336 wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, cmd);
337 drv->errors++;
338 if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
339 drv->errors = 0;
340 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
341 }
342 } else {
343 drv->errors = 0;
344 ret = 0;
345 if ((os_strcasecmp(cmd, RSSI_CMD) == 0) ||
346 (os_strcasecmp(cmd, LINKSPEED_CMD) == 0) ||
347 (os_strcasecmp(cmd, "MACADDR") == 0) ||
348 (os_strcasecmp(cmd, "GETPOWER") == 0) ||
349 (os_strcasecmp(cmd, "GETBAND") == 0)) {
350 ret = strlen(buf);
351 } else if (os_strcasecmp(cmd, "START") == 0) {
352 drv->driver_is_started = TRUE;
353 linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
354 /* os_sleep(0, WPA_DRIVER_WEXT_WAIT_US);
355 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); */
356 } else if (os_strcasecmp(cmd, "STOP") == 0) {
357 drv->driver_is_started = FALSE;
358 /* wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); */
359 } else if (os_strncasecmp(cmd, "CSCAN", 5) == 0) {
360 wpa_driver_wext_set_scan_timeout(priv);
361 wpa_supplicant_notify_scanning(wpa_s, 1);
362 }
363 wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf));
364 }
365 return ret;
366 }
367
wpa_driver_signal_poll(void * priv,struct wpa_signal_info * si)368 int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si)
369 {
370 char buf[MAX_DRV_CMD_SIZE];
371 struct wpa_driver_wext_data *drv = priv;
372 char *prssi;
373 int res;
374
375 os_memset(si, 0, sizeof(*si));
376 res = wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf));
377 /* Answer: SSID rssi -Val */
378 if (res < 0)
379 return res;
380 prssi = strcasestr(buf, RSSI_CMD);
381 if (!prssi)
382 return -1;
383 si->current_signal = atoi(prssi + strlen(RSSI_CMD) + 1);
384
385 res = wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf));
386 /* Answer: LinkSpeed Val */
387 if (res < 0)
388 return res;
389 si->current_txrate = atoi(buf + strlen(LINKSPEED_CMD) + 1) * 1000;
390
391 return 0;
392 }
393