blob: bef16b157bacac1c30d4d9bf90e50c80299ddbca [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * hostapd / UNIX domain socket -based control interface
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08003 * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "utils/includes.h"
10
11#ifndef CONFIG_NATIVE_WINDOWS
12
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -080013#ifdef CONFIG_TESTING_OPTIONS
14#include <net/ethernet.h>
15#include <netinet/ip.h>
16#endif /* CONFIG_TESTING_OPTIONS */
17
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070018#include <sys/un.h>
19#include <sys/stat.h>
20#include <stddef.h>
21
22#include "utils/common.h"
23#include "utils/eloop.h"
24#include "common/version.h"
25#include "common/ieee802_11_defs.h"
Dmitry Shmidtff787d52015-01-12 13:01:47 -080026#include "crypto/tls.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070027#include "drivers/driver.h"
28#include "radius/radius_client.h"
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -080029#include "radius/radius_server.h"
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -080030#include "l2_packet/l2_packet.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070031#include "ap/hostapd.h"
32#include "ap/ap_config.h"
33#include "ap/ieee802_1x.h"
34#include "ap/wpa_auth.h"
35#include "ap/ieee802_11.h"
36#include "ap/sta_info.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070037#include "ap/wps_hostapd.h"
38#include "ap/ctrl_iface_ap.h"
39#include "ap/ap_drv_ops.h"
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080040#include "ap/hs20.h"
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -080041#include "ap/wnm_ap.h"
Dmitry Shmidtf7e0a992013-05-23 11:03:10 -070042#include "ap/wpa_auth.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070043#include "wps/wps_defs.h"
44#include "wps/wps.h"
Dmitry Shmidt04949592012-07-19 12:16:46 -070045#include "config_file.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070046#include "ctrl_iface.h"
47
48
49struct wpa_ctrl_dst {
50 struct wpa_ctrl_dst *next;
51 struct sockaddr_un addr;
52 socklen_t addrlen;
53 int debug_level;
54 int errors;
55};
56
57
58static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
59 const char *buf, size_t len);
60
61
62static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
63 struct sockaddr_un *from,
64 socklen_t fromlen)
65{
66 struct wpa_ctrl_dst *dst;
67
68 dst = os_zalloc(sizeof(*dst));
69 if (dst == NULL)
70 return -1;
71 os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
72 dst->addrlen = fromlen;
73 dst->debug_level = MSG_INFO;
74 dst->next = hapd->ctrl_dst;
75 hapd->ctrl_dst = dst;
76 wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
77 (u8 *) from->sun_path,
78 fromlen - offsetof(struct sockaddr_un, sun_path));
79 return 0;
80}
81
82
83static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
84 struct sockaddr_un *from,
85 socklen_t fromlen)
86{
87 struct wpa_ctrl_dst *dst, *prev = NULL;
88
89 dst = hapd->ctrl_dst;
90 while (dst) {
91 if (fromlen == dst->addrlen &&
92 os_memcmp(from->sun_path, dst->addr.sun_path,
93 fromlen - offsetof(struct sockaddr_un, sun_path))
94 == 0) {
Dmitry Shmidt54605472013-11-08 11:10:19 -080095 wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
96 (u8 *) from->sun_path,
97 fromlen -
98 offsetof(struct sockaddr_un, sun_path));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070099 if (prev == NULL)
100 hapd->ctrl_dst = dst->next;
101 else
102 prev->next = dst->next;
103 os_free(dst);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700104 return 0;
105 }
106 prev = dst;
107 dst = dst->next;
108 }
109 return -1;
110}
111
112
113static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
114 struct sockaddr_un *from,
115 socklen_t fromlen,
116 char *level)
117{
118 struct wpa_ctrl_dst *dst;
119
120 wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
121
122 dst = hapd->ctrl_dst;
123 while (dst) {
124 if (fromlen == dst->addrlen &&
125 os_memcmp(from->sun_path, dst->addr.sun_path,
126 fromlen - offsetof(struct sockaddr_un, sun_path))
127 == 0) {
128 wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
129 "level", (u8 *) from->sun_path, fromlen -
130 offsetof(struct sockaddr_un, sun_path));
131 dst->debug_level = atoi(level);
132 return 0;
133 }
134 dst = dst->next;
135 }
136
137 return -1;
138}
139
140
141static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
142 const char *txtaddr)
143{
144 u8 addr[ETH_ALEN];
145 struct sta_info *sta;
146
147 wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
148
149 if (hwaddr_aton(txtaddr, addr))
150 return -1;
151
152 sta = ap_get_sta(hapd, addr);
153 if (sta)
154 return 0;
155
156 wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
157 "notification", MAC2STR(addr));
158 sta = ap_sta_add(hapd, addr);
159 if (sta == NULL)
160 return -1;
161
162 hostapd_new_assoc_sta(hapd, sta, 0);
163 return 0;
164}
165
166
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700167#ifdef CONFIG_IEEE80211W
168#ifdef NEED_AP_MLME
169static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
170 const char *txtaddr)
171{
172 u8 addr[ETH_ALEN];
173 u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
174
175 wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
176
177 if (hwaddr_aton(txtaddr, addr) ||
178 os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
179 return -1;
180
181 ieee802_11_send_sa_query_req(hapd, addr, trans_id);
182
183 return 0;
184}
185#endif /* NEED_AP_MLME */
186#endif /* CONFIG_IEEE80211W */
187
188
189#ifdef CONFIG_WPS
190static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
191{
192 char *pin = os_strchr(txt, ' ');
193 char *timeout_txt;
194 int timeout;
195 u8 addr_buf[ETH_ALEN], *addr = NULL;
196 char *pos;
197
198 if (pin == NULL)
199 return -1;
200 *pin++ = '\0';
201
202 timeout_txt = os_strchr(pin, ' ');
203 if (timeout_txt) {
204 *timeout_txt++ = '\0';
205 timeout = atoi(timeout_txt);
206 pos = os_strchr(timeout_txt, ' ');
207 if (pos) {
208 *pos++ = '\0';
209 if (hwaddr_aton(pos, addr_buf) == 0)
210 addr = addr_buf;
211 }
212 } else
213 timeout = 0;
214
215 return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
216}
217
218
219static int hostapd_ctrl_iface_wps_check_pin(
220 struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
221{
222 char pin[9];
223 size_t len;
224 char *pos;
225 int ret;
226
227 wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
228 (u8 *) cmd, os_strlen(cmd));
229 for (pos = cmd, len = 0; *pos != '\0'; pos++) {
230 if (*pos < '0' || *pos > '9')
231 continue;
232 pin[len++] = *pos;
233 if (len == 9) {
234 wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
235 return -1;
236 }
237 }
238 if (len != 4 && len != 8) {
239 wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
240 return -1;
241 }
242 pin[len] = '\0';
243
244 if (len == 8) {
245 unsigned int pin_val;
246 pin_val = atoi(pin);
247 if (!wps_pin_valid(pin_val)) {
248 wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
249 ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800250 if (os_snprintf_error(buflen, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700251 return -1;
252 return ret;
253 }
254 }
255
256 ret = os_snprintf(buf, buflen, "%s", pin);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800257 if (os_snprintf_error(buflen, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700258 return -1;
259
260 return ret;
261}
262
263
Dmitry Shmidt04949592012-07-19 12:16:46 -0700264#ifdef CONFIG_WPS_NFC
265static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
266 char *pos)
267{
268 size_t len;
269 struct wpabuf *buf;
270 int ret;
271
272 len = os_strlen(pos);
273 if (len & 0x01)
274 return -1;
275 len /= 2;
276
277 buf = wpabuf_alloc(len);
278 if (buf == NULL)
279 return -1;
280 if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
281 wpabuf_free(buf);
282 return -1;
283 }
284
285 ret = hostapd_wps_nfc_tag_read(hapd, buf);
286 wpabuf_free(buf);
287
288 return ret;
289}
290
291
292static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
293 char *cmd, char *reply,
294 size_t max_len)
295{
296 int ndef;
297 struct wpabuf *buf;
298 int res;
299
300 if (os_strcmp(cmd, "WPS") == 0)
301 ndef = 0;
302 else if (os_strcmp(cmd, "NDEF") == 0)
303 ndef = 1;
304 else
305 return -1;
306
307 buf = hostapd_wps_nfc_config_token(hapd, ndef);
308 if (buf == NULL)
309 return -1;
310
311 res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
312 wpabuf_len(buf));
313 reply[res++] = '\n';
314 reply[res] = '\0';
315
316 wpabuf_free(buf);
317
318 return res;
319}
320
321
322static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
323 char *reply, size_t max_len,
324 int ndef)
325{
326 struct wpabuf *buf;
327 int res;
328
329 buf = hostapd_wps_nfc_token_gen(hapd, ndef);
330 if (buf == NULL)
331 return -1;
332
333 res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
334 wpabuf_len(buf));
335 reply[res++] = '\n';
336 reply[res] = '\0';
337
338 wpabuf_free(buf);
339
340 return res;
341}
342
343
344static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
345 char *cmd, char *reply,
346 size_t max_len)
347{
348 if (os_strcmp(cmd, "WPS") == 0)
349 return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
350 max_len, 0);
351
352 if (os_strcmp(cmd, "NDEF") == 0)
353 return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
354 max_len, 1);
355
356 if (os_strcmp(cmd, "enable") == 0)
357 return hostapd_wps_nfc_token_enable(hapd);
358
359 if (os_strcmp(cmd, "disable") == 0) {
360 hostapd_wps_nfc_token_disable(hapd);
361 return 0;
362 }
363
364 return -1;
365}
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800366
367
368static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
369 char *cmd, char *reply,
370 size_t max_len)
371{
372 struct wpabuf *buf;
373 int res;
374 char *pos;
375 int ndef;
376
377 pos = os_strchr(cmd, ' ');
378 if (pos == NULL)
379 return -1;
380 *pos++ = '\0';
381
382 if (os_strcmp(cmd, "WPS") == 0)
383 ndef = 0;
384 else if (os_strcmp(cmd, "NDEF") == 0)
385 ndef = 1;
386 else
387 return -1;
388
389 if (os_strcmp(pos, "WPS-CR") == 0)
390 buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
391 else
392 buf = NULL;
393 if (buf == NULL)
394 return -1;
395
396 res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
397 wpabuf_len(buf));
398 reply[res++] = '\n';
399 reply[res] = '\0';
400
401 wpabuf_free(buf);
402
403 return res;
404}
405
406
407static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
408 char *cmd)
409{
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800410 size_t len;
411 struct wpabuf *req, *sel;
412 int ret;
413 char *pos, *role, *type, *pos2;
414
415 role = cmd;
416 pos = os_strchr(role, ' ');
417 if (pos == NULL)
418 return -1;
419 *pos++ = '\0';
420
421 type = pos;
422 pos = os_strchr(type, ' ');
423 if (pos == NULL)
424 return -1;
425 *pos++ = '\0';
426
427 pos2 = os_strchr(pos, ' ');
428 if (pos2 == NULL)
429 return -1;
430 *pos2++ = '\0';
431
432 len = os_strlen(pos);
433 if (len & 0x01)
434 return -1;
435 len /= 2;
436
437 req = wpabuf_alloc(len);
438 if (req == NULL)
439 return -1;
440 if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
441 wpabuf_free(req);
442 return -1;
443 }
444
445 len = os_strlen(pos2);
446 if (len & 0x01) {
447 wpabuf_free(req);
448 return -1;
449 }
450 len /= 2;
451
452 sel = wpabuf_alloc(len);
453 if (sel == NULL) {
454 wpabuf_free(req);
455 return -1;
456 }
457 if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
458 wpabuf_free(req);
459 wpabuf_free(sel);
460 return -1;
461 }
462
463 if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
464 ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
465 } else {
466 wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
467 "reported: role=%s type=%s", role, type);
468 ret = -1;
469 }
470 wpabuf_free(req);
471 wpabuf_free(sel);
472
473 return ret;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800474}
475
Dmitry Shmidt04949592012-07-19 12:16:46 -0700476#endif /* CONFIG_WPS_NFC */
477
478
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700479static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
480 char *buf, size_t buflen)
481{
482 int timeout = 300;
483 char *pos;
484 const char *pin_txt;
485
486 pos = os_strchr(txt, ' ');
487 if (pos)
488 *pos++ = '\0';
489
490 if (os_strcmp(txt, "disable") == 0) {
491 hostapd_wps_ap_pin_disable(hapd);
492 return os_snprintf(buf, buflen, "OK\n");
493 }
494
495 if (os_strcmp(txt, "random") == 0) {
496 if (pos)
497 timeout = atoi(pos);
498 pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
499 if (pin_txt == NULL)
500 return -1;
501 return os_snprintf(buf, buflen, "%s", pin_txt);
502 }
503
504 if (os_strcmp(txt, "get") == 0) {
505 pin_txt = hostapd_wps_ap_pin_get(hapd);
506 if (pin_txt == NULL)
507 return -1;
508 return os_snprintf(buf, buflen, "%s", pin_txt);
509 }
510
511 if (os_strcmp(txt, "set") == 0) {
512 char *pin;
513 if (pos == NULL)
514 return -1;
515 pin = pos;
516 pos = os_strchr(pos, ' ');
517 if (pos) {
518 *pos++ = '\0';
519 timeout = atoi(pos);
520 }
521 if (os_strlen(pin) > buflen)
522 return -1;
523 if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
524 return -1;
525 return os_snprintf(buf, buflen, "%s", pin);
526 }
527
528 return -1;
529}
530
531
532static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
533{
534 char *pos;
535 char *ssid, *auth, *encr = NULL, *key = NULL;
536
537 ssid = txt;
538 pos = os_strchr(txt, ' ');
539 if (!pos)
540 return -1;
541 *pos++ = '\0';
542
543 auth = pos;
544 pos = os_strchr(pos, ' ');
545 if (pos) {
546 *pos++ = '\0';
547 encr = pos;
548 pos = os_strchr(pos, ' ');
549 if (pos) {
550 *pos++ = '\0';
551 key = pos;
552 }
553 }
554
555 return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
556}
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700557
558
559static const char * pbc_status_str(enum pbc_status status)
560{
561 switch (status) {
562 case WPS_PBC_STATUS_DISABLE:
563 return "Disabled";
564 case WPS_PBC_STATUS_ACTIVE:
565 return "Active";
566 case WPS_PBC_STATUS_TIMEOUT:
567 return "Timed-out";
568 case WPS_PBC_STATUS_OVERLAP:
569 return "Overlap";
570 default:
571 return "Unknown";
572 }
573}
574
575
576static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
577 char *buf, size_t buflen)
578{
579 int ret;
580 char *pos, *end;
581
582 pos = buf;
583 end = buf + buflen;
584
585 ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
586 pbc_status_str(hapd->wps_stats.pbc_status));
587
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800588 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700589 return pos - buf;
590 pos += ret;
591
592 ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n",
593 (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
594 "Success":
595 (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
596 "Failed" : "None")));
597
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800598 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700599 return pos - buf;
600 pos += ret;
601
602 /* If status == Failure - Add possible Reasons */
603 if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
604 hapd->wps_stats.failure_reason > 0) {
605 ret = os_snprintf(pos, end - pos,
606 "Failure Reason: %s\n",
607 wps_ei_str(hapd->wps_stats.failure_reason));
608
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800609 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700610 return pos - buf;
611 pos += ret;
612 }
613
614 if (hapd->wps_stats.status) {
615 ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
616 MAC2STR(hapd->wps_stats.peer_addr));
617
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800618 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -0700619 return pos - buf;
620 pos += ret;
621 }
622
623 return pos - buf;
624}
625
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700626#endif /* CONFIG_WPS */
627
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800628#ifdef CONFIG_HS20
629
630static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
631 const char *cmd)
632{
633 u8 addr[ETH_ALEN];
634 const char *url;
635
636 if (hwaddr_aton(cmd, addr))
637 return -1;
638 url = cmd + 17;
639 if (*url == '\0') {
640 url = NULL;
641 } else {
642 if (*url != ' ')
643 return -1;
644 url++;
645 if (*url == '\0')
646 url = NULL;
647 }
648
649 return hs20_send_wnm_notification(hapd, addr, 1, url);
650}
651
652
653static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
654 const char *cmd)
655{
656 u8 addr[ETH_ALEN];
657 int code, reauth_delay, ret;
658 const char *pos;
659 size_t url_len;
660 struct wpabuf *req;
661
662 /* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
663 if (hwaddr_aton(cmd, addr))
664 return -1;
665
666 pos = os_strchr(cmd, ' ');
667 if (pos == NULL)
668 return -1;
669 pos++;
670 code = atoi(pos);
671
672 pos = os_strchr(pos, ' ');
673 if (pos == NULL)
674 return -1;
675 pos++;
676 reauth_delay = atoi(pos);
677
678 url_len = 0;
679 pos = os_strchr(pos, ' ');
680 if (pos) {
681 pos++;
682 url_len = os_strlen(pos);
683 }
684
685 req = wpabuf_alloc(4 + url_len);
686 if (req == NULL)
687 return -1;
688 wpabuf_put_u8(req, code);
689 wpabuf_put_le16(req, reauth_delay);
690 wpabuf_put_u8(req, url_len);
691 if (pos)
692 wpabuf_put_data(req, pos, url_len);
693
694 wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
695 " to indicate imminent deauthentication (code=%d "
696 "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
697 ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
698 wpabuf_free(req);
699 return ret;
700}
701
702#endif /* CONFIG_HS20 */
703
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700704
Dmitry Shmidt051af732013-10-22 13:52:46 -0700705#ifdef CONFIG_INTERWORKING
706
707static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
708 const char *cmd)
709{
710 u8 qos_map_set[16 + 2 * 21], count = 0;
711 const char *pos = cmd;
712 int val, ret;
713
714 for (;;) {
715 if (count == sizeof(qos_map_set)) {
716 wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
717 return -1;
718 }
719
720 val = atoi(pos);
721 if (val < 0 || val > 255) {
722 wpa_printf(MSG_INFO, "Invalid QoS Map Set");
723 return -1;
724 }
725
726 qos_map_set[count++] = val;
727 pos = os_strchr(pos, ',');
728 if (!pos)
729 break;
730 pos++;
731 }
732
733 if (count < 16 || count & 1) {
734 wpa_printf(MSG_INFO, "Invalid QoS Map Set");
735 return -1;
736 }
737
738 ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
739 if (ret) {
740 wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
741 return -1;
742 }
743
744 os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
745 hapd->conf->qos_map_set_len = count;
746
747 return 0;
748}
749
750
751static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
752 const char *cmd)
753{
754 u8 addr[ETH_ALEN];
755 struct sta_info *sta;
756 struct wpabuf *buf;
757 u8 *qos_map_set = hapd->conf->qos_map_set;
758 u8 qos_map_set_len = hapd->conf->qos_map_set_len;
759 int ret;
760
761 if (!qos_map_set_len) {
762 wpa_printf(MSG_INFO, "QoS Map Set is not set");
763 return -1;
764 }
765
766 if (hwaddr_aton(cmd, addr))
767 return -1;
768
769 sta = ap_get_sta(hapd, addr);
770 if (sta == NULL) {
771 wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
772 "for QoS Map Configuration message",
773 MAC2STR(addr));
774 return -1;
775 }
776
777 if (!sta->qos_map_enabled) {
778 wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
779 "support for QoS Map", MAC2STR(addr));
780 return -1;
781 }
782
783 buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
784 if (buf == NULL)
785 return -1;
786
787 wpabuf_put_u8(buf, WLAN_ACTION_QOS);
788 wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
789
790 /* QoS Map Set Element */
791 wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
792 wpabuf_put_u8(buf, qos_map_set_len);
793 wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
794
795 ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
796 wpabuf_head(buf), wpabuf_len(buf));
797 wpabuf_free(buf);
798
799 return ret;
800}
801
802#endif /* CONFIG_INTERWORKING */
803
804
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800805#ifdef CONFIG_WNM
806
807static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
808 const char *cmd)
809{
810 u8 addr[ETH_ALEN];
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800811 int disassoc_timer;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800812 struct sta_info *sta;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800813
814 if (hwaddr_aton(cmd, addr))
815 return -1;
816 if (cmd[17] != ' ')
817 return -1;
818 disassoc_timer = atoi(cmd + 17);
819
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800820 sta = ap_get_sta(hapd, addr);
821 if (sta == NULL) {
822 wpa_printf(MSG_DEBUG, "Station " MACSTR
823 " not found for disassociation imminent message",
824 MAC2STR(addr));
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800825 return -1;
826 }
827
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800828 return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800829}
830
831
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800832static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
833 const char *cmd)
834{
835 u8 addr[ETH_ALEN];
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700836 const char *url, *timerstr;
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700837 int disassoc_timer;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800838 struct sta_info *sta;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800839
840 if (hwaddr_aton(cmd, addr))
841 return -1;
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700842
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800843 sta = ap_get_sta(hapd, addr);
844 if (sta == NULL) {
845 wpa_printf(MSG_DEBUG, "Station " MACSTR
846 " not found for ESS disassociation imminent message",
847 MAC2STR(addr));
848 return -1;
849 }
850
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -0700851 timerstr = cmd + 17;
852 if (*timerstr != ' ')
853 return -1;
854 timerstr++;
855 disassoc_timer = atoi(timerstr);
856 if (disassoc_timer < 0 || disassoc_timer > 65535)
857 return -1;
858
859 url = os_strchr(timerstr, ' ');
Dmitry Shmidt8bae4132013-06-06 11:25:10 -0700860 if (url == NULL)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800861 return -1;
862 url++;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800863
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800864 return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800865}
866
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800867
868static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
869 const char *cmd)
870{
871 u8 addr[ETH_ALEN];
872 const char *pos, *end;
873 int disassoc_timer = 0;
874 struct sta_info *sta;
875 u8 req_mode = 0, valid_int = 0x01;
876 u8 bss_term_dur[12];
877 char *url = NULL;
878 int ret;
879 u8 nei_rep[1000];
880 u8 *nei_pos = nei_rep;
881
882 if (hwaddr_aton(cmd, addr)) {
883 wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
884 return -1;
885 }
886
887 sta = ap_get_sta(hapd, addr);
888 if (sta == NULL) {
889 wpa_printf(MSG_DEBUG, "Station " MACSTR
890 " not found for BSS TM Request message",
891 MAC2STR(addr));
892 return -1;
893 }
894
895 pos = os_strstr(cmd, " disassoc_timer=");
896 if (pos) {
897 pos += 16;
898 disassoc_timer = atoi(pos);
899 if (disassoc_timer < 0 || disassoc_timer > 65535) {
900 wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
901 return -1;
902 }
903 }
904
905 pos = os_strstr(cmd, " valid_int=");
906 if (pos) {
907 pos += 11;
908 valid_int = atoi(pos);
909 }
910
911 pos = os_strstr(cmd, " bss_term=");
912 if (pos) {
913 pos += 10;
914 req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
915 /* TODO: TSF configurable/learnable */
916 bss_term_dur[0] = 4; /* Subelement ID */
917 bss_term_dur[1] = 10; /* Length */
918 os_memset(bss_term_dur, 2, 8);
919 end = os_strchr(pos, ',');
920 if (end == NULL) {
921 wpa_printf(MSG_DEBUG, "Invalid bss_term data");
922 return -1;
923 }
924 end++;
925 WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
926 }
927
928
929 /*
930 * BSS Transition Candidate List Entries - Neighbor Report elements
931 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
932 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
933 */
934 pos = cmd;
935 while (pos) {
936 u8 *nei_start;
937 long int val;
938 char *endptr, *tmp;
939
940 pos = os_strstr(pos, " neighbor=");
941 if (!pos)
942 break;
943 if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
944 wpa_printf(MSG_DEBUG,
945 "Not enough room for additional neighbor");
946 return -1;
947 }
948 pos += 10;
949
950 nei_start = nei_pos;
951 *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
952 nei_pos++; /* length to be filled in */
953
954 if (hwaddr_aton(pos, nei_pos)) {
955 wpa_printf(MSG_DEBUG, "Invalid BSSID");
956 return -1;
957 }
958 nei_pos += ETH_ALEN;
959 pos += 17;
960 if (*pos != ',') {
961 wpa_printf(MSG_DEBUG, "Missing BSSID Information");
962 return -1;
963 }
964 pos++;
965
966 val = strtol(pos, &endptr, 0);
967 WPA_PUT_LE32(nei_pos, val);
968 nei_pos += 4;
969 if (*endptr != ',') {
970 wpa_printf(MSG_DEBUG, "Missing Operating Class");
971 return -1;
972 }
973 pos = endptr + 1;
974
975 *nei_pos++ = atoi(pos); /* Operating Class */
976 pos = os_strchr(pos, ',');
977 if (pos == NULL) {
978 wpa_printf(MSG_DEBUG, "Missing Channel Number");
979 return -1;
980 }
981 pos++;
982
983 *nei_pos++ = atoi(pos); /* Channel Number */
984 pos = os_strchr(pos, ',');
985 if (pos == NULL) {
986 wpa_printf(MSG_DEBUG, "Missing PHY Type");
987 return -1;
988 }
989 pos++;
990
991 *nei_pos++ = atoi(pos); /* PHY Type */
992 end = os_strchr(pos, ' ');
993 tmp = os_strchr(pos, ',');
994 if (tmp && (!end || tmp < end)) {
995 /* Optional Subelements (hexdump) */
996 size_t len;
997
998 pos = tmp + 1;
999 end = os_strchr(pos, ' ');
1000 if (end)
1001 len = end - pos;
1002 else
1003 len = os_strlen(pos);
1004 if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
1005 wpa_printf(MSG_DEBUG,
1006 "Not enough room for neighbor subelements");
1007 return -1;
1008 }
1009 if (len & 0x01 ||
1010 hexstr2bin(pos, nei_pos, len / 2) < 0) {
1011 wpa_printf(MSG_DEBUG,
1012 "Invalid neighbor subelement info");
1013 return -1;
1014 }
1015 nei_pos += len / 2;
1016 pos = end;
1017 }
1018
1019 nei_start[1] = nei_pos - nei_start - 2;
1020 }
1021
1022 pos = os_strstr(cmd, " url=");
1023 if (pos) {
1024 size_t len;
1025 pos += 5;
1026 end = os_strchr(pos, ' ');
1027 if (end)
1028 len = end - pos;
1029 else
1030 len = os_strlen(pos);
1031 url = os_malloc(len + 1);
1032 if (url == NULL)
1033 return -1;
1034 os_memcpy(url, pos, len);
1035 url[len] = '\0';
1036 req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
1037 }
1038
1039 if (os_strstr(cmd, " pref=1"))
1040 req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
1041 if (os_strstr(cmd, " abridged=1"))
1042 req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
1043 if (os_strstr(cmd, " disassoc_imminent=1"))
1044 req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
1045
1046 ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
1047 valid_int, bss_term_dur, url,
1048 nei_pos > nei_rep ? nei_rep : NULL,
1049 nei_pos - nei_rep);
1050 os_free(url);
1051 return ret;
1052}
1053
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001054#endif /* CONFIG_WNM */
1055
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08001056
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001057static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
1058 char *buf, size_t buflen)
1059{
1060 int ret;
1061 char *pos, *end;
1062
1063 pos = buf;
1064 end = buf + buflen;
1065
1066 ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
1067 "ssid=%s\n",
1068 MAC2STR(hapd->own_addr),
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001069 wpa_ssid_txt(hapd->conf->ssid.ssid,
1070 hapd->conf->ssid.ssid_len));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001071 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001072 return pos - buf;
1073 pos += ret;
1074
1075#ifdef CONFIG_WPS
1076 ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
1077 hapd->conf->wps_state == 0 ? "disabled" :
1078 (hapd->conf->wps_state == 1 ? "not configured" :
1079 "configured"));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001080 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001081 return pos - buf;
1082 pos += ret;
1083
1084 if (hapd->conf->wps_state && hapd->conf->wpa &&
1085 hapd->conf->ssid.wpa_passphrase) {
1086 ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
1087 hapd->conf->ssid.wpa_passphrase);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001088 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001089 return pos - buf;
1090 pos += ret;
1091 }
1092
1093 if (hapd->conf->wps_state && hapd->conf->wpa &&
1094 hapd->conf->ssid.wpa_psk &&
1095 hapd->conf->ssid.wpa_psk->group) {
1096 char hex[PMK_LEN * 2 + 1];
1097 wpa_snprintf_hex(hex, sizeof(hex),
1098 hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
1099 ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001100 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001101 return pos - buf;
1102 pos += ret;
1103 }
1104#endif /* CONFIG_WPS */
1105
1106 if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
1107 ret = os_snprintf(pos, end - pos, "key_mgmt=");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001108 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001109 return pos - buf;
1110 pos += ret;
1111
1112 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
1113 ret = os_snprintf(pos, end - pos, "WPA-PSK ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001114 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001115 return pos - buf;
1116 pos += ret;
1117 }
1118 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
1119 ret = os_snprintf(pos, end - pos, "WPA-EAP ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001120 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001121 return pos - buf;
1122 pos += ret;
1123 }
1124#ifdef CONFIG_IEEE80211R
1125 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
1126 ret = os_snprintf(pos, end - pos, "FT-PSK ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001127 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001128 return pos - buf;
1129 pos += ret;
1130 }
1131 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
1132 ret = os_snprintf(pos, end - pos, "FT-EAP ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001133 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001134 return pos - buf;
1135 pos += ret;
1136 }
Dmitry Shmidtd11f0192014-03-24 12:09:47 -07001137#ifdef CONFIG_SAE
1138 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
1139 ret = os_snprintf(pos, end - pos, "FT-SAE ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001140 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidtd11f0192014-03-24 12:09:47 -07001141 return pos - buf;
1142 pos += ret;
1143 }
1144#endif /* CONFIG_SAE */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001145#endif /* CONFIG_IEEE80211R */
1146#ifdef CONFIG_IEEE80211W
1147 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
1148 ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001149 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001150 return pos - buf;
1151 pos += ret;
1152 }
1153 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
1154 ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001155 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001156 return pos - buf;
1157 pos += ret;
1158 }
1159#endif /* CONFIG_IEEE80211W */
Dmitry Shmidtd11f0192014-03-24 12:09:47 -07001160#ifdef CONFIG_SAE
1161 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
1162 ret = os_snprintf(pos, end - pos, "SAE ");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001163 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidtd11f0192014-03-24 12:09:47 -07001164 return pos - buf;
1165 pos += ret;
1166 }
1167#endif /* CONFIG_SAE */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001168 if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
1169 ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
1170 if (os_snprintf_error(end - pos, ret))
1171 return pos - buf;
1172 pos += ret;
1173 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001174
1175 ret = os_snprintf(pos, end - pos, "\n");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001176 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001177 return pos - buf;
1178 pos += ret;
1179 }
1180
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001181 if (hapd->conf->wpa) {
1182 ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
1183 wpa_cipher_txt(hapd->conf->wpa_group));
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001184 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001185 return pos - buf;
1186 pos += ret;
1187 }
1188
1189 if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
1190 ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001191 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001192 return pos - buf;
1193 pos += ret;
1194
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001195 ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
1196 " ");
1197 if (ret < 0)
1198 return pos - buf;
1199 pos += ret;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001200
1201 ret = os_snprintf(pos, end - pos, "\n");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001202 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001203 return pos - buf;
1204 pos += ret;
1205 }
1206
1207 if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
1208 ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001209 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001210 return pos - buf;
1211 pos += ret;
1212
Dmitry Shmidt661b4f72014-09-29 14:58:27 -07001213 ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise,
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001214 " ");
1215 if (ret < 0)
1216 return pos - buf;
1217 pos += ret;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001218
1219 ret = os_snprintf(pos, end - pos, "\n");
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001220 if (os_snprintf_error(end - pos, ret))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001221 return pos - buf;
1222 pos += ret;
1223 }
1224
1225 return pos - buf;
1226}
1227
1228
1229static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
1230{
1231 char *value;
1232 int ret = 0;
1233
1234 value = os_strchr(cmd, ' ');
1235 if (value == NULL)
1236 return -1;
1237 *value++ = '\0';
1238
1239 wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
1240 if (0) {
1241#ifdef CONFIG_WPS_TESTING
1242 } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
1243 long int val;
1244 val = strtol(value, NULL, 0);
1245 if (val < 0 || val > 0xff) {
1246 ret = -1;
1247 wpa_printf(MSG_DEBUG, "WPS: Invalid "
1248 "wps_version_number %ld", val);
1249 } else {
1250 wps_version_number = val;
1251 wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
1252 "version %u.%u",
1253 (wps_version_number & 0xf0) >> 4,
1254 wps_version_number & 0x0f);
1255 hostapd_wps_update_ie(hapd);
1256 }
1257 } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
1258 wps_testing_dummy_cred = atoi(value);
1259 wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
1260 wps_testing_dummy_cred);
Dmitry Shmidtcf32e602014-01-28 10:57:39 -08001261 } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
1262 wps_corrupt_pkhash = atoi(value);
1263 wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
1264 wps_corrupt_pkhash);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001265#endif /* CONFIG_WPS_TESTING */
Dmitry Shmidt04949592012-07-19 12:16:46 -07001266#ifdef CONFIG_INTERWORKING
1267 } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
1268 int val = atoi(value);
1269 if (val <= 0)
1270 ret = -1;
1271 else
1272 hapd->gas_frag_limit = val;
1273#endif /* CONFIG_INTERWORKING */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001274#ifdef CONFIG_TESTING_OPTIONS
1275 } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
1276 hapd->ext_mgmt_frame_handling = atoi(value);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001277 } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
1278 hapd->ext_eapol_frame_io = atoi(value);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001279#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001280 } else {
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001281 struct sta_info *sta;
1282 int vlan_id;
1283
Dmitry Shmidt04949592012-07-19 12:16:46 -07001284 ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001285 if (ret)
1286 return ret;
1287
1288 if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
1289 for (sta = hapd->sta_list; sta; sta = sta->next) {
1290 if (hostapd_maclist_found(
1291 hapd->conf->deny_mac,
1292 hapd->conf->num_deny_mac, sta->addr,
1293 &vlan_id) &&
1294 (!vlan_id || vlan_id == sta->vlan_id))
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001295 ap_sta_disconnect(
1296 hapd, sta, sta->addr,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001297 WLAN_REASON_UNSPECIFIED);
1298 }
1299 } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED &&
1300 os_strcasecmp(cmd, "accept_mac_file") == 0) {
1301 for (sta = hapd->sta_list; sta; sta = sta->next) {
1302 if (!hostapd_maclist_found(
1303 hapd->conf->accept_mac,
1304 hapd->conf->num_accept_mac,
1305 sta->addr, &vlan_id) ||
1306 (vlan_id && vlan_id != sta->vlan_id))
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001307 ap_sta_disconnect(
1308 hapd, sta, sta->addr,
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08001309 WLAN_REASON_UNSPECIFIED);
1310 }
1311 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001312 }
1313
1314 return ret;
1315}
1316
1317
1318static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
1319 char *buf, size_t buflen)
1320{
1321 int res;
1322
1323 wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
1324
1325 if (os_strcmp(cmd, "version") == 0) {
1326 res = os_snprintf(buf, buflen, "%s", VERSION_STR);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001327 if (os_snprintf_error(buflen, res))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001328 return -1;
1329 return res;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001330 } else if (os_strcmp(cmd, "tls_library") == 0) {
1331 res = tls_get_library_version(buf, buflen);
1332 if (os_snprintf_error(buflen, res))
1333 return -1;
1334 return res;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001335 }
1336
1337 return -1;
1338}
1339
1340
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07001341static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
1342{
1343 if (hostapd_enable_iface(iface) < 0) {
1344 wpa_printf(MSG_ERROR, "Enabling of interface failed");
1345 return -1;
1346 }
1347 return 0;
1348}
1349
1350
1351static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
1352{
1353 if (hostapd_reload_iface(iface) < 0) {
1354 wpa_printf(MSG_ERROR, "Reloading of interface failed");
1355 return -1;
1356 }
1357 return 0;
1358}
1359
1360
1361static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
1362{
1363 if (hostapd_disable_iface(iface) < 0) {
1364 wpa_printf(MSG_ERROR, "Disabling of interface failed");
1365 return -1;
1366 }
1367 return 0;
1368}
1369
1370
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001371#ifdef CONFIG_TESTING_OPTIONS
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001372
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001373static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
1374{
1375 union wpa_event_data data;
1376 char *pos, *param;
1377 enum wpa_event_type event;
1378
1379 wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd);
1380
1381 os_memset(&data, 0, sizeof(data));
1382
1383 param = os_strchr(cmd, ' ');
1384 if (param == NULL)
1385 return -1;
1386 *param++ = '\0';
1387
1388 if (os_strcmp(cmd, "DETECTED") == 0)
1389 event = EVENT_DFS_RADAR_DETECTED;
1390 else if (os_strcmp(cmd, "CAC-FINISHED") == 0)
1391 event = EVENT_DFS_CAC_FINISHED;
1392 else if (os_strcmp(cmd, "CAC-ABORTED") == 0)
1393 event = EVENT_DFS_CAC_ABORTED;
1394 else if (os_strcmp(cmd, "NOP-FINISHED") == 0)
1395 event = EVENT_DFS_NOP_FINISHED;
1396 else {
1397 wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s",
1398 cmd);
1399 return -1;
1400 }
1401
1402 pos = os_strstr(param, "freq=");
1403 if (pos)
1404 data.dfs_event.freq = atoi(pos + 5);
1405
1406 pos = os_strstr(param, "ht_enabled=1");
1407 if (pos)
1408 data.dfs_event.ht_enabled = 1;
1409
1410 pos = os_strstr(param, "chan_offset=");
1411 if (pos)
1412 data.dfs_event.chan_offset = atoi(pos + 12);
1413
1414 pos = os_strstr(param, "chan_width=");
1415 if (pos)
1416 data.dfs_event.chan_width = atoi(pos + 11);
1417
1418 pos = os_strstr(param, "cf1=");
1419 if (pos)
1420 data.dfs_event.cf1 = atoi(pos + 4);
1421
1422 pos = os_strstr(param, "cf2=");
1423 if (pos)
1424 data.dfs_event.cf2 = atoi(pos + 4);
1425
1426 wpa_supplicant_event(hapd, event, &data);
1427
1428 return 0;
1429}
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001430
1431
1432static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
1433{
1434 size_t len;
1435 u8 *buf;
1436 int res;
1437
1438 wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
1439
1440 len = os_strlen(cmd);
1441 if (len & 1)
1442 return -1;
1443 len /= 2;
1444
1445 buf = os_malloc(len);
1446 if (buf == NULL)
1447 return -1;
1448
1449 if (hexstr2bin(cmd, buf, len) < 0) {
1450 os_free(buf);
1451 return -1;
1452 }
1453
1454 res = hostapd_drv_send_mlme(hapd, buf, len, 0);
1455 os_free(buf);
1456 return res;
1457}
1458
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001459
1460static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
1461{
1462 char *pos;
1463 u8 src[ETH_ALEN], *buf;
1464 int used;
1465 size_t len;
1466
1467 wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
1468
1469 pos = cmd;
1470 used = hwaddr_aton2(pos, src);
1471 if (used < 0)
1472 return -1;
1473 pos += used;
1474 while (*pos == ' ')
1475 pos++;
1476
1477 len = os_strlen(pos);
1478 if (len & 1)
1479 return -1;
1480 len /= 2;
1481
1482 buf = os_malloc(len);
1483 if (buf == NULL)
1484 return -1;
1485
1486 if (hexstr2bin(pos, buf, len) < 0) {
1487 os_free(buf);
1488 return -1;
1489 }
1490
1491 ieee802_1x_receive(hapd, src, buf, len);
1492 os_free(buf);
1493
1494 return 0;
1495}
1496
1497
1498static u16 ipv4_hdr_checksum(const void *buf, size_t len)
1499{
1500 size_t i;
1501 u32 sum = 0;
1502 const u16 *pos = buf;
1503
1504 for (i = 0; i < len / 2; i++)
1505 sum += *pos++;
1506
1507 while (sum >> 16)
1508 sum = (sum & 0xffff) + (sum >> 16);
1509
1510 return sum ^ 0xffff;
1511}
1512
1513
1514#define HWSIM_PACKETLEN 1500
1515#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
1516
1517void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
1518 size_t len)
1519{
1520 struct hostapd_data *hapd = ctx;
1521 const struct ether_header *eth;
1522 const struct iphdr *ip;
1523 const u8 *pos;
1524 unsigned int i;
1525
1526 if (len != HWSIM_PACKETLEN)
1527 return;
1528
1529 eth = (const struct ether_header *) buf;
1530 ip = (const struct iphdr *) (eth + 1);
1531 pos = (const u8 *) (ip + 1);
1532
1533 if (ip->ihl != 5 || ip->version != 4 ||
1534 ntohs(ip->tot_len) != HWSIM_IP_LEN)
1535 return;
1536
1537 for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
1538 if (*pos != (u8) i)
1539 return;
1540 pos++;
1541 }
1542
1543 wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
1544 MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
1545}
1546
1547
1548static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
1549 char *cmd)
1550{
1551 int enabled = atoi(cmd);
1552 char *pos;
1553 const char *ifname;
1554
1555 if (!enabled) {
1556 if (hapd->l2_test) {
1557 l2_packet_deinit(hapd->l2_test);
1558 hapd->l2_test = NULL;
1559 wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
1560 "test data: Disabled");
1561 }
1562 return 0;
1563 }
1564
1565 if (hapd->l2_test)
1566 return 0;
1567
1568 pos = os_strstr(cmd, " ifname=");
1569 if (pos)
1570 ifname = pos + 8;
1571 else
1572 ifname = hapd->conf->iface;
1573
1574 hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
1575 ETHERTYPE_IP, hostapd_data_test_rx,
1576 hapd, 1);
1577 if (hapd->l2_test == NULL)
1578 return -1;
1579
1580 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
1581
1582 return 0;
1583}
1584
1585
1586static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
1587{
1588 u8 dst[ETH_ALEN], src[ETH_ALEN];
1589 char *pos;
1590 int used;
1591 long int val;
1592 u8 tos;
1593 u8 buf[HWSIM_PACKETLEN];
1594 struct ether_header *eth;
1595 struct iphdr *ip;
1596 u8 *dpos;
1597 unsigned int i;
1598
1599 if (hapd->l2_test == NULL)
1600 return -1;
1601
1602 /* format: <dst> <src> <tos> */
1603
1604 pos = cmd;
1605 used = hwaddr_aton2(pos, dst);
1606 if (used < 0)
1607 return -1;
1608 pos += used;
1609 while (*pos == ' ')
1610 pos++;
1611 used = hwaddr_aton2(pos, src);
1612 if (used < 0)
1613 return -1;
1614 pos += used;
1615
1616 val = strtol(pos, NULL, 0);
1617 if (val < 0 || val > 0xff)
1618 return -1;
1619 tos = val;
1620
1621 eth = (struct ether_header *) buf;
1622 os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
1623 os_memcpy(eth->ether_shost, src, ETH_ALEN);
1624 eth->ether_type = htons(ETHERTYPE_IP);
1625 ip = (struct iphdr *) (eth + 1);
1626 os_memset(ip, 0, sizeof(*ip));
1627 ip->ihl = 5;
1628 ip->version = 4;
1629 ip->ttl = 64;
1630 ip->tos = tos;
1631 ip->tot_len = htons(HWSIM_IP_LEN);
1632 ip->protocol = 1;
1633 ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
1634 ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
1635 ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
1636 dpos = (u8 *) (ip + 1);
1637 for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
1638 *dpos++ = i;
1639
1640 if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf,
1641 HWSIM_PACKETLEN) < 0)
1642 return -1;
1643
1644 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
1645 " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
1646
1647 return 0;
1648}
1649
1650
1651static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
1652 char *cmd)
1653{
1654 u8 *buf;
1655 struct ether_header *eth;
1656 struct l2_packet_data *l2 = NULL;
1657 size_t len;
1658 u16 ethertype;
1659 int res = -1;
1660 const char *ifname = hapd->conf->iface;
1661
1662 if (os_strncmp(cmd, "ifname=", 7) == 0) {
1663 cmd += 7;
1664 ifname = cmd;
1665 cmd = os_strchr(cmd, ' ');
1666 if (cmd == NULL)
1667 return -1;
1668 *cmd++ = '\0';
1669 }
1670
1671 len = os_strlen(cmd);
1672 if (len & 1 || len < ETH_HLEN * 2)
1673 return -1;
1674 len /= 2;
1675
1676 buf = os_malloc(len);
1677 if (buf == NULL)
1678 return -1;
1679
1680 if (hexstr2bin(cmd, buf, len) < 0)
1681 goto done;
1682
1683 eth = (struct ether_header *) buf;
1684 ethertype = ntohs(eth->ether_type);
1685
1686 l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
1687 hostapd_data_test_rx, hapd, 1);
1688 if (l2 == NULL)
1689 goto done;
1690
1691 res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
1692 wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
1693done:
1694 if (l2)
1695 l2_packet_deinit(l2);
1696 os_free(buf);
1697
1698 return res < 0 ? -1 : 0;
1699}
1700
Dmitry Shmidtff787d52015-01-12 13:01:47 -08001701
1702static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
1703{
1704#ifdef WPA_TRACE_BFD
1705 extern char wpa_trace_fail_func[256];
1706 extern unsigned int wpa_trace_fail_after;
1707 char *pos;
1708
1709 wpa_trace_fail_after = atoi(cmd);
1710 pos = os_strchr(cmd, ':');
1711 if (pos) {
1712 pos++;
1713 os_strlcpy(wpa_trace_fail_func, pos,
1714 sizeof(wpa_trace_fail_func));
1715 } else {
1716 wpa_trace_fail_after = 0;
1717 }
1718
1719 return 0;
1720#else /* WPA_TRACE_BFD */
1721 return -1;
1722#endif /* WPA_TRACE_BFD */
1723}
1724
1725
1726static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
1727 char *buf, size_t buflen)
1728{
1729#ifdef WPA_TRACE_BFD
1730 extern char wpa_trace_fail_func[256];
1731 extern unsigned int wpa_trace_fail_after;
1732
1733 return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
1734 wpa_trace_fail_func);
1735#else /* WPA_TRACE_BFD */
1736 return -1;
1737#endif /* WPA_TRACE_BFD */
1738}
1739
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001740#endif /* CONFIG_TESTING_OPTIONS */
1741
1742
Dmitry Shmidtd30ac602014-06-30 09:54:22 -07001743static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
1744 char *pos)
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08001745{
1746#ifdef NEED_AP_MLME
1747 struct csa_settings settings;
Dmitry Shmidtd30ac602014-06-30 09:54:22 -07001748 int ret;
1749 unsigned int i;
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08001750
Dmitry Shmidtd30ac602014-06-30 09:54:22 -07001751 ret = hostapd_parse_csa_settings(pos, &settings);
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08001752 if (ret)
1753 return ret;
1754
Dmitry Shmidtd30ac602014-06-30 09:54:22 -07001755 for (i = 0; i < iface->num_bss; i++) {
1756 ret = hostapd_switch_channel(iface->bss[i], &settings);
1757 if (ret) {
1758 /* FIX: What do we do if CSA fails in the middle of
1759 * submitting multi-BSS CSA requests? */
1760 return ret;
1761 }
1762 }
1763
1764 return 0;
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08001765#else /* NEED_AP_MLME */
1766 return -1;
1767#endif /* NEED_AP_MLME */
1768}
1769
1770
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001771static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
1772 int reply_size, const char *param)
1773{
1774#ifdef RADIUS_SERVER
1775 if (os_strcmp(param, "radius_server") == 0) {
1776 return radius_server_get_mib(hapd->radius_srv, reply,
1777 reply_size);
1778 }
1779#endif /* RADIUS_SERVER */
1780 return -1;
1781}
1782
1783
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07001784static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
1785 char *buf, size_t buflen)
1786{
1787 int ret;
1788 char *pos;
1789 u8 *data = NULL;
1790 unsigned int vendor_id, subcmd;
1791 struct wpabuf *reply;
1792 size_t data_len = 0;
1793
1794 /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
1795 vendor_id = strtoul(cmd, &pos, 16);
1796 if (!isblank(*pos))
1797 return -EINVAL;
1798
1799 subcmd = strtoul(pos, &pos, 10);
1800
1801 if (*pos != '\0') {
1802 if (!isblank(*pos++))
1803 return -EINVAL;
1804 data_len = os_strlen(pos);
1805 }
1806
1807 if (data_len) {
1808 data_len /= 2;
1809 data = os_malloc(data_len);
1810 if (!data)
1811 return -ENOBUFS;
1812
1813 if (hexstr2bin(pos, data, data_len)) {
1814 wpa_printf(MSG_DEBUG,
1815 "Vendor command: wrong parameter format");
1816 os_free(data);
1817 return -EINVAL;
1818 }
1819 }
1820
1821 reply = wpabuf_alloc((buflen - 1) / 2);
1822 if (!reply) {
1823 os_free(data);
1824 return -ENOBUFS;
1825 }
1826
1827 ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
1828 reply);
1829
1830 if (ret == 0)
1831 ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
1832 wpabuf_len(reply));
1833
1834 wpabuf_free(reply);
1835 os_free(data);
1836
1837 return ret;
1838}
1839
1840
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001841static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
1842 void *sock_ctx)
1843{
1844 struct hostapd_data *hapd = eloop_ctx;
Dmitry Shmidtcf32e602014-01-28 10:57:39 -08001845 char buf[4096];
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001846 int res;
1847 struct sockaddr_un from;
1848 socklen_t fromlen = sizeof(from);
1849 char *reply;
1850 const int reply_size = 4096;
1851 int reply_len;
1852 int level = MSG_DEBUG;
1853
1854 res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
1855 (struct sockaddr *) &from, &fromlen);
1856 if (res < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001857 wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
1858 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001859 return;
1860 }
1861 buf[res] = '\0';
1862 if (os_strcmp(buf, "PING") == 0)
1863 level = MSG_EXCESSIVE;
1864 wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
1865
1866 reply = os_malloc(reply_size);
1867 if (reply == NULL) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08001868 if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
1869 fromlen) < 0) {
1870 wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
1871 strerror(errno));
1872 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001873 return;
1874 }
1875
1876 os_memcpy(reply, "OK\n", 3);
1877 reply_len = 3;
1878
1879 if (os_strcmp(buf, "PING") == 0) {
1880 os_memcpy(reply, "PONG\n", 5);
1881 reply_len = 5;
1882 } else if (os_strncmp(buf, "RELOG", 5) == 0) {
1883 if (wpa_debug_reopen_file() < 0)
1884 reply_len = -1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -08001885 } else if (os_strcmp(buf, "STATUS") == 0) {
1886 reply_len = hostapd_ctrl_iface_status(hapd, reply,
1887 reply_size);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001888 } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
1889 reply_len = hostapd_drv_status(hapd, reply, reply_size);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001890 } else if (os_strcmp(buf, "MIB") == 0) {
1891 reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
1892 if (reply_len >= 0) {
1893 res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
1894 reply_size - reply_len);
1895 if (res < 0)
1896 reply_len = -1;
1897 else
1898 reply_len += res;
1899 }
1900 if (reply_len >= 0) {
1901 res = ieee802_1x_get_mib(hapd, reply + reply_len,
1902 reply_size - reply_len);
1903 if (res < 0)
1904 reply_len = -1;
1905 else
1906 reply_len += res;
1907 }
1908#ifndef CONFIG_NO_RADIUS
1909 if (reply_len >= 0) {
1910 res = radius_client_get_mib(hapd->radius,
1911 reply + reply_len,
1912 reply_size - reply_len);
1913 if (res < 0)
1914 reply_len = -1;
1915 else
1916 reply_len += res;
1917 }
1918#endif /* CONFIG_NO_RADIUS */
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001919 } else if (os_strncmp(buf, "MIB ", 4) == 0) {
1920 reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size,
1921 buf + 4);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001922 } else if (os_strcmp(buf, "STA-FIRST") == 0) {
1923 reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
1924 reply_size);
1925 } else if (os_strncmp(buf, "STA ", 4) == 0) {
1926 reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
1927 reply_size);
1928 } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
1929 reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
1930 reply_size);
1931 } else if (os_strcmp(buf, "ATTACH") == 0) {
1932 if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
1933 reply_len = -1;
1934 } else if (os_strcmp(buf, "DETACH") == 0) {
1935 if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
1936 reply_len = -1;
1937 } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
1938 if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
1939 buf + 6))
1940 reply_len = -1;
1941 } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
1942 if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
1943 reply_len = -1;
1944 } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
1945 if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
1946 reply_len = -1;
1947 } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
1948 if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
1949 reply_len = -1;
1950#ifdef CONFIG_IEEE80211W
1951#ifdef NEED_AP_MLME
1952 } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
1953 if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
1954 reply_len = -1;
1955#endif /* NEED_AP_MLME */
1956#endif /* CONFIG_IEEE80211W */
1957#ifdef CONFIG_WPS
1958 } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
1959 if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
1960 reply_len = -1;
1961 } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
1962 reply_len = hostapd_ctrl_iface_wps_check_pin(
1963 hapd, buf + 14, reply, reply_size);
1964 } else if (os_strcmp(buf, "WPS_PBC") == 0) {
1965 if (hostapd_wps_button_pushed(hapd, NULL))
1966 reply_len = -1;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001967 } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
1968 if (hostapd_wps_cancel(hapd))
1969 reply_len = -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001970 } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
1971 reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
1972 reply, reply_size);
1973 } else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
1974 if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
1975 reply_len = -1;
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -07001976 } else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) {
1977 reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply,
1978 reply_size);
Dmitry Shmidt04949592012-07-19 12:16:46 -07001979#ifdef CONFIG_WPS_NFC
1980 } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
1981 if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
1982 reply_len = -1;
1983 } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
1984 reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
1985 hapd, buf + 21, reply, reply_size);
1986 } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
1987 reply_len = hostapd_ctrl_iface_wps_nfc_token(
1988 hapd, buf + 14, reply, reply_size);
Dmitry Shmidtf8623282013-02-20 14:34:59 -08001989 } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
1990 reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
1991 hapd, buf + 21, reply, reply_size);
1992 } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
1993 if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
1994 reply_len = -1;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001995#endif /* CONFIG_WPS_NFC */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001996#endif /* CONFIG_WPS */
Dmitry Shmidt051af732013-10-22 13:52:46 -07001997#ifdef CONFIG_INTERWORKING
1998 } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
1999 if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
2000 reply_len = -1;
2001 } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
2002 if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
2003 reply_len = -1;
2004#endif /* CONFIG_INTERWORKING */
Dmitry Shmidtf21452a2014-02-26 10:55:25 -08002005#ifdef CONFIG_HS20
2006 } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
2007 if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
2008 reply_len = -1;
2009 } else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
2010 if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
2011 reply_len = -1;
2012#endif /* CONFIG_HS20 */
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08002013#ifdef CONFIG_WNM
2014 } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
2015 if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
2016 reply_len = -1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -08002017 } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
2018 if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
2019 reply_len = -1;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002020 } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
2021 if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
2022 reply_len = -1;
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08002023#endif /* CONFIG_WNM */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002024 } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
2025 reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
2026 reply_size);
2027 } else if (os_strncmp(buf, "SET ", 4) == 0) {
2028 if (hostapd_ctrl_iface_set(hapd, buf + 4))
2029 reply_len = -1;
2030 } else if (os_strncmp(buf, "GET ", 4) == 0) {
2031 reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
2032 reply_size);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002033 } else if (os_strncmp(buf, "ENABLE", 6) == 0) {
2034 if (hostapd_ctrl_iface_enable(hapd->iface))
2035 reply_len = -1;
2036 } else if (os_strncmp(buf, "RELOAD", 6) == 0) {
2037 if (hostapd_ctrl_iface_reload(hapd->iface))
2038 reply_len = -1;
2039 } else if (os_strncmp(buf, "DISABLE", 7) == 0) {
2040 if (hostapd_ctrl_iface_disable(hapd->iface))
2041 reply_len = -1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -08002042#ifdef CONFIG_TESTING_OPTIONS
2043 } else if (os_strncmp(buf, "RADAR ", 6) == 0) {
2044 if (hostapd_ctrl_iface_radar(hapd, buf + 6))
2045 reply_len = -1;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08002046 } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
2047 if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
2048 reply_len = -1;
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002049 } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
2050 if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
2051 reply_len = -1;
2052 } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
2053 if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
2054 reply_len = -1;
2055 } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
2056 if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
2057 reply_len = -1;
2058 } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
2059 if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
2060 reply_len = -1;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08002061 } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
2062 if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
2063 reply_len = -1;
2064 } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
2065 reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
2066 reply_size);
Dmitry Shmidtcce06662013-11-04 18:44:24 -08002067#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08002068 } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
Dmitry Shmidtd30ac602014-06-30 09:54:22 -07002069 if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
Dmitry Shmidte0e48dc2013-11-18 12:00:06 -08002070 reply_len = -1;
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -07002071 } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
2072 reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
2073 reply_size);
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002074 } else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
2075 ieee802_1x_erp_flush(hapd);
2076#ifdef RADIUS_SERVER
2077 radius_server_erp_flush(hapd->radius_srv);
2078#endif /* RADIUS_SERVER */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002079 } else {
2080 os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
2081 reply_len = 16;
2082 }
2083
2084 if (reply_len < 0) {
2085 os_memcpy(reply, "FAIL\n", 5);
2086 reply_len = 5;
2087 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002088 if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
2089 fromlen) < 0) {
2090 wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
2091 strerror(errno));
2092 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002093 os_free(reply);
2094}
2095
2096
2097static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
2098{
2099 char *buf;
2100 size_t len;
2101
2102 if (hapd->conf->ctrl_interface == NULL)
2103 return NULL;
2104
2105 len = os_strlen(hapd->conf->ctrl_interface) +
2106 os_strlen(hapd->conf->iface) + 2;
2107 buf = os_malloc(len);
2108 if (buf == NULL)
2109 return NULL;
2110
2111 os_snprintf(buf, len, "%s/%s",
2112 hapd->conf->ctrl_interface, hapd->conf->iface);
2113 buf[len - 1] = '\0';
2114 return buf;
2115}
2116
2117
Dmitry Shmidtb6e9aaf2013-05-20 14:49:44 -07002118static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002119 const char *txt, size_t len)
2120{
2121 struct hostapd_data *hapd = ctx;
2122 if (hapd == NULL)
2123 return;
2124 hostapd_ctrl_iface_send(hapd, level, txt, len);
2125}
2126
2127
2128int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
2129{
2130 struct sockaddr_un addr;
2131 int s = -1;
2132 char *fname = NULL;
2133
Dmitry Shmidt04949592012-07-19 12:16:46 -07002134 if (hapd->ctrl_sock > -1) {
2135 wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
2136 return 0;
2137 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002138
2139 if (hapd->conf->ctrl_interface == NULL)
2140 return 0;
2141
2142 if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
2143 if (errno == EEXIST) {
2144 wpa_printf(MSG_DEBUG, "Using existing control "
2145 "interface directory.");
2146 } else {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002147 wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
2148 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002149 goto fail;
2150 }
2151 }
2152
2153 if (hapd->conf->ctrl_interface_gid_set &&
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08002154 chown(hapd->conf->ctrl_interface, -1,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002155 hapd->conf->ctrl_interface_gid) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002156 wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
2157 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002158 return -1;
2159 }
2160
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002161 if (!hapd->conf->ctrl_interface_gid_set &&
2162 hapd->iface->interfaces->ctrl_iface_group &&
2163 chown(hapd->conf->ctrl_interface, -1,
2164 hapd->iface->interfaces->ctrl_iface_group) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002165 wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
2166 strerror(errno));
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002167 return -1;
2168 }
2169
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08002170#ifdef ANDROID
2171 /*
2172 * Android is using umask 0077 which would leave the control interface
2173 * directory without group access. This breaks things since Wi-Fi
2174 * framework assumes that this directory can be accessed by other
2175 * applications in the wifi group. Fix this by adding group access even
2176 * if umask value would prevent this.
2177 */
2178 if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
2179 wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
2180 strerror(errno));
2181 /* Try to continue anyway */
2182 }
2183#endif /* ANDROID */
2184
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002185 if (os_strlen(hapd->conf->ctrl_interface) + 1 +
2186 os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
2187 goto fail;
2188
2189 s = socket(PF_UNIX, SOCK_DGRAM, 0);
2190 if (s < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002191 wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002192 goto fail;
2193 }
2194
2195 os_memset(&addr, 0, sizeof(addr));
2196#ifdef __FreeBSD__
2197 addr.sun_len = sizeof(addr);
2198#endif /* __FreeBSD__ */
2199 addr.sun_family = AF_UNIX;
2200 fname = hostapd_ctrl_iface_path(hapd);
2201 if (fname == NULL)
2202 goto fail;
2203 os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
2204 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2205 wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
2206 strerror(errno));
2207 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2208 wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
2209 " allow connections - assuming it was left"
2210 "over from forced program termination");
2211 if (unlink(fname) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002212 wpa_printf(MSG_ERROR,
2213 "Could not unlink existing ctrl_iface socket '%s': %s",
2214 fname, strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002215 goto fail;
2216 }
2217 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
2218 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002219 wpa_printf(MSG_ERROR,
2220 "hostapd-ctrl-iface: bind(PF_UNIX): %s",
2221 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002222 goto fail;
2223 }
2224 wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
2225 "ctrl_iface socket '%s'", fname);
2226 } else {
2227 wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
2228 "be in use - cannot override it");
2229 wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
2230 "not used anymore", fname);
2231 os_free(fname);
2232 fname = NULL;
2233 goto fail;
2234 }
2235 }
2236
2237 if (hapd->conf->ctrl_interface_gid_set &&
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08002238 chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002239 wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
2240 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002241 goto fail;
2242 }
2243
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002244 if (!hapd->conf->ctrl_interface_gid_set &&
2245 hapd->iface->interfaces->ctrl_iface_group &&
2246 chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002247 wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
2248 strerror(errno));
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002249 goto fail;
2250 }
2251
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002252 if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002253 wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
2254 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002255 goto fail;
2256 }
2257 os_free(fname);
2258
2259 hapd->ctrl_sock = s;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08002260 if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
2261 NULL) < 0) {
2262 hostapd_ctrl_iface_deinit(hapd);
2263 return -1;
2264 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002265 hapd->msg_ctx = hapd;
2266 wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
2267
2268 return 0;
2269
2270fail:
2271 if (s >= 0)
2272 close(s);
2273 if (fname) {
2274 unlink(fname);
2275 os_free(fname);
2276 }
2277 return -1;
2278}
2279
2280
2281void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
2282{
2283 struct wpa_ctrl_dst *dst, *prev;
2284
2285 if (hapd->ctrl_sock > -1) {
2286 char *fname;
2287 eloop_unregister_read_sock(hapd->ctrl_sock);
2288 close(hapd->ctrl_sock);
2289 hapd->ctrl_sock = -1;
2290 fname = hostapd_ctrl_iface_path(hapd);
2291 if (fname)
2292 unlink(fname);
2293 os_free(fname);
2294
2295 if (hapd->conf->ctrl_interface &&
2296 rmdir(hapd->conf->ctrl_interface) < 0) {
2297 if (errno == ENOTEMPTY) {
2298 wpa_printf(MSG_DEBUG, "Control interface "
2299 "directory not empty - leaving it "
2300 "behind");
2301 } else {
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -07002302 wpa_printf(MSG_ERROR,
2303 "rmdir[ctrl_interface=%s]: %s",
2304 hapd->conf->ctrl_interface,
2305 strerror(errno));
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002306 }
2307 }
2308 }
2309
2310 dst = hapd->ctrl_dst;
Dmitry Shmidtff787d52015-01-12 13:01:47 -08002311 hapd->ctrl_dst = NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002312 while (dst) {
2313 prev = dst;
2314 dst = dst->next;
2315 os_free(prev);
2316 }
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002317
2318#ifdef CONFIG_TESTING_OPTIONS
2319 l2_packet_deinit(hapd->l2_test);
2320 hapd->l2_test = NULL;
2321#endif /* CONFIG_TESTING_OPTIONS */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002322}
2323
2324
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002325static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
2326 char *buf)
2327{
2328 if (hostapd_add_iface(interfaces, buf) < 0) {
2329 wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
2330 return -1;
2331 }
2332 return 0;
2333}
2334
2335
2336static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
2337 char *buf)
2338{
2339 if (hostapd_remove_iface(interfaces, buf) < 0) {
2340 wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
2341 return -1;
2342 }
2343 return 0;
2344}
2345
2346
Dmitry Shmidtcf32e602014-01-28 10:57:39 -08002347static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
2348{
2349#ifdef CONFIG_WPS_TESTING
2350 wps_version_number = 0x20;
2351 wps_testing_dummy_cred = 0;
2352 wps_corrupt_pkhash = 0;
2353#endif /* CONFIG_WPS_TESTING */
2354}
2355
2356
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002357static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
2358 void *sock_ctx)
2359{
2360 void *interfaces = eloop_ctx;
2361 char buf[256];
2362 int res;
2363 struct sockaddr_un from;
2364 socklen_t fromlen = sizeof(from);
2365 char reply[24];
2366 int reply_len;
2367
2368 res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
2369 (struct sockaddr *) &from, &fromlen);
2370 if (res < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002371 wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
2372 strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002373 return;
2374 }
2375 buf[res] = '\0';
Dmitry Shmidtcce06662013-11-04 18:44:24 -08002376 wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002377
2378 os_memcpy(reply, "OK\n", 3);
2379 reply_len = 3;
2380
2381 if (os_strcmp(buf, "PING") == 0) {
2382 os_memcpy(reply, "PONG\n", 5);
2383 reply_len = 5;
Dmitry Shmidtcce06662013-11-04 18:44:24 -08002384 } else if (os_strncmp(buf, "RELOG", 5) == 0) {
2385 if (wpa_debug_reopen_file() < 0)
2386 reply_len = -1;
Dmitry Shmidtcf32e602014-01-28 10:57:39 -08002387 } else if (os_strcmp(buf, "FLUSH") == 0) {
2388 hostapd_ctrl_iface_flush(interfaces);
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002389 } else if (os_strncmp(buf, "ADD ", 4) == 0) {
2390 if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
2391 reply_len = -1;
2392 } else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
2393 if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
2394 reply_len = -1;
Dmitry Shmidt7f93d6f2014-02-21 11:22:49 -08002395#ifdef CONFIG_MODULE_TESTS
2396 } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
2397 int hapd_module_tests(void);
2398 if (hapd_module_tests() < 0)
2399 reply_len = -1;
2400#endif /* CONFIG_MODULE_TESTS */
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002401 } else {
2402 wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
2403 "ignored");
2404 reply_len = -1;
2405 }
2406
2407 if (reply_len < 0) {
2408 os_memcpy(reply, "FAIL\n", 5);
2409 reply_len = 5;
2410 }
2411
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002412 if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
2413 fromlen) < 0) {
2414 wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
2415 strerror(errno));
2416 }
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002417}
2418
2419
2420static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
2421{
2422 char *buf;
2423 size_t len;
2424
2425 if (interface->global_iface_path == NULL)
2426 return NULL;
2427
2428 len = os_strlen(interface->global_iface_path) +
2429 os_strlen(interface->global_iface_name) + 2;
2430 buf = os_malloc(len);
2431 if (buf == NULL)
2432 return NULL;
2433
2434 os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
2435 interface->global_iface_name);
2436 buf[len - 1] = '\0';
2437 return buf;
2438}
2439
2440
2441int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
2442{
2443 struct sockaddr_un addr;
2444 int s = -1;
2445 char *fname = NULL;
2446
2447 if (interface->global_iface_path == NULL) {
2448 wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
2449 return 0;
2450 }
2451
2452 if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
2453 if (errno == EEXIST) {
2454 wpa_printf(MSG_DEBUG, "Using existing control "
2455 "interface directory.");
2456 } else {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002457 wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
2458 strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002459 goto fail;
2460 }
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002461 } else if (interface->ctrl_iface_group &&
2462 chown(interface->global_iface_path, -1,
2463 interface->ctrl_iface_group) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002464 wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
2465 strerror(errno));
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002466 goto fail;
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002467 }
2468
2469 if (os_strlen(interface->global_iface_path) + 1 +
2470 os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
2471 goto fail;
2472
2473 s = socket(PF_UNIX, SOCK_DGRAM, 0);
2474 if (s < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002475 wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002476 goto fail;
2477 }
2478
2479 os_memset(&addr, 0, sizeof(addr));
2480#ifdef __FreeBSD__
2481 addr.sun_len = sizeof(addr);
2482#endif /* __FreeBSD__ */
2483 addr.sun_family = AF_UNIX;
2484 fname = hostapd_global_ctrl_iface_path(interface);
2485 if (fname == NULL)
2486 goto fail;
2487 os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
2488 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2489 wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
2490 strerror(errno));
2491 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2492 wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
2493 " allow connections - assuming it was left"
2494 "over from forced program termination");
2495 if (unlink(fname) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002496 wpa_printf(MSG_ERROR,
2497 "Could not unlink existing ctrl_iface socket '%s': %s",
2498 fname, strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002499 goto fail;
2500 }
2501 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
2502 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002503 wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
2504 strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002505 goto fail;
2506 }
2507 wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
2508 "ctrl_iface socket '%s'", fname);
2509 } else {
2510 wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
2511 "be in use - cannot override it");
2512 wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
2513 "not used anymore", fname);
2514 os_free(fname);
2515 fname = NULL;
2516 goto fail;
2517 }
2518 }
2519
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002520 if (interface->ctrl_iface_group &&
2521 chown(fname, -1, interface->ctrl_iface_group) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002522 wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
2523 strerror(errno));
Dmitry Shmidt0ccb66e2013-03-29 16:41:28 -07002524 goto fail;
2525 }
2526
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002527 if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -08002528 wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
2529 strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002530 goto fail;
2531 }
2532 os_free(fname);
2533
2534 interface->global_ctrl_sock = s;
2535 eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
2536 interface, NULL);
2537
2538 return 0;
2539
2540fail:
2541 if (s >= 0)
2542 close(s);
2543 if (fname) {
2544 unlink(fname);
2545 os_free(fname);
2546 }
2547 return -1;
2548}
2549
2550
2551void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
2552{
2553 char *fname = NULL;
2554
2555 if (interfaces->global_ctrl_sock > -1) {
2556 eloop_unregister_read_sock(interfaces->global_ctrl_sock);
2557 close(interfaces->global_ctrl_sock);
2558 interfaces->global_ctrl_sock = -1;
2559 fname = hostapd_global_ctrl_iface_path(interfaces);
2560 if (fname) {
2561 unlink(fname);
2562 os_free(fname);
2563 }
2564
2565 if (interfaces->global_iface_path &&
2566 rmdir(interfaces->global_iface_path) < 0) {
2567 if (errno == ENOTEMPTY) {
2568 wpa_printf(MSG_DEBUG, "Control interface "
2569 "directory not empty - leaving it "
2570 "behind");
2571 } else {
Dmitry Shmidtb7b4d0e2013-08-26 12:09:05 -07002572 wpa_printf(MSG_ERROR,
2573 "rmdir[ctrl_interface=%s]: %s",
2574 interfaces->global_iface_path,
2575 strerror(errno));
Dmitry Shmidt61d9df32012-08-29 16:22:06 -07002576 }
2577 }
2578 os_free(interfaces->global_iface_path);
2579 interfaces->global_iface_path = NULL;
2580 }
2581}
2582
2583
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07002584static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
2585 const char *buf, size_t len)
2586{
2587 struct wpa_ctrl_dst *dst, *next;
2588 struct msghdr msg;
2589 int idx;
2590 struct iovec io[2];
2591 char levelstr[10];
2592
2593 dst = hapd->ctrl_dst;
2594 if (hapd->ctrl_sock < 0 || dst == NULL)
2595 return;
2596
2597 os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
2598 io[0].iov_base = levelstr;
2599 io[0].iov_len = os_strlen(levelstr);
2600 io[1].iov_base = (char *) buf;
2601 io[1].iov_len = len;
2602 os_memset(&msg, 0, sizeof(msg));
2603 msg.msg_iov = io;
2604 msg.msg_iovlen = 2;
2605
2606 idx = 0;
2607 while (dst) {
2608 next = dst->next;
2609 if (level >= dst->debug_level) {
2610 wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
2611 (u8 *) dst->addr.sun_path, dst->addrlen -
2612 offsetof(struct sockaddr_un, sun_path));
2613 msg.msg_name = &dst->addr;
2614 msg.msg_namelen = dst->addrlen;
2615 if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
2616 int _errno = errno;
2617 wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
2618 "%d - %s",
2619 idx, errno, strerror(errno));
2620 dst->errors++;
2621 if (dst->errors > 10 || _errno == ENOENT) {
2622 hostapd_ctrl_iface_detach(
2623 hapd, &dst->addr,
2624 dst->addrlen);
2625 }
2626 } else
2627 dst->errors = 0;
2628 }
2629 idx++;
2630 dst = next;
2631 }
2632}
2633
2634#endif /* CONFIG_NATIVE_WINDOWS */