1214503Srpaulo/*
2214503Srpaulo * hostapd / UNIX domain socket -based control interface
3214503Srpaulo * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
4214503Srpaulo *
5214503Srpaulo * This program is free software; you can redistribute it and/or modify
6214503Srpaulo * it under the terms of the GNU General Public License version 2 as
7214503Srpaulo * published by the Free Software Foundation.
8214503Srpaulo *
9214503Srpaulo * Alternatively, this software may be distributed under the terms of BSD
10214503Srpaulo * license.
11214503Srpaulo *
12214503Srpaulo * See README and COPYING for more details.
13214503Srpaulo */
14214503Srpaulo
15214503Srpaulo#include "utils/includes.h"
16214503Srpaulo
17214503Srpaulo#ifndef CONFIG_NATIVE_WINDOWS
18214503Srpaulo
19214503Srpaulo#include <sys/un.h>
20214503Srpaulo#include <sys/stat.h>
21214503Srpaulo#include <stddef.h>
22214503Srpaulo
23214503Srpaulo#include "utils/common.h"
24214503Srpaulo#include "utils/eloop.h"
25214503Srpaulo#include "common/ieee802_11_defs.h"
26214503Srpaulo#include "drivers/driver.h"
27214503Srpaulo#include "radius/radius_client.h"
28214503Srpaulo#include "ap/hostapd.h"
29214503Srpaulo#include "ap/ap_config.h"
30214503Srpaulo#include "ap/ieee802_1x.h"
31214503Srpaulo#include "ap/wpa_auth.h"
32214503Srpaulo#include "ap/ieee802_11.h"
33214503Srpaulo#include "ap/sta_info.h"
34214503Srpaulo#include "ap/accounting.h"
35214503Srpaulo#include "ap/wps_hostapd.h"
36214503Srpaulo#include "ap/ctrl_iface_ap.h"
37214503Srpaulo#include "ctrl_iface.h"
38214503Srpaulo
39214503Srpaulo
40214503Srpaulostruct wpa_ctrl_dst {
41214503Srpaulo	struct wpa_ctrl_dst *next;
42214503Srpaulo	struct sockaddr_un addr;
43214503Srpaulo	socklen_t addrlen;
44214503Srpaulo	int debug_level;
45214503Srpaulo	int errors;
46214503Srpaulo};
47214503Srpaulo
48214503Srpaulo
49214503Srpaulostatic void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
50214503Srpaulo				    const char *buf, size_t len);
51214503Srpaulo
52214503Srpaulo
53214503Srpaulostatic int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
54214503Srpaulo				     struct sockaddr_un *from,
55214503Srpaulo				     socklen_t fromlen)
56214503Srpaulo{
57214503Srpaulo	struct wpa_ctrl_dst *dst;
58214503Srpaulo
59214503Srpaulo	dst = os_zalloc(sizeof(*dst));
60214503Srpaulo	if (dst == NULL)
61214503Srpaulo		return -1;
62214503Srpaulo	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
63214503Srpaulo	dst->addrlen = fromlen;
64214503Srpaulo	dst->debug_level = MSG_INFO;
65214503Srpaulo	dst->next = hapd->ctrl_dst;
66214503Srpaulo	hapd->ctrl_dst = dst;
67214503Srpaulo	wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
68214503Srpaulo		    (u8 *) from->sun_path,
69214503Srpaulo		    fromlen - offsetof(struct sockaddr_un, sun_path));
70214503Srpaulo	return 0;
71214503Srpaulo}
72214503Srpaulo
73214503Srpaulo
74214503Srpaulostatic int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
75214503Srpaulo				     struct sockaddr_un *from,
76214503Srpaulo				     socklen_t fromlen)
77214503Srpaulo{
78214503Srpaulo	struct wpa_ctrl_dst *dst, *prev = NULL;
79214503Srpaulo
80214503Srpaulo	dst = hapd->ctrl_dst;
81214503Srpaulo	while (dst) {
82214503Srpaulo		if (fromlen == dst->addrlen &&
83214503Srpaulo		    os_memcmp(from->sun_path, dst->addr.sun_path,
84214503Srpaulo			      fromlen - offsetof(struct sockaddr_un, sun_path))
85214503Srpaulo		    == 0) {
86214503Srpaulo			if (prev == NULL)
87214503Srpaulo				hapd->ctrl_dst = dst->next;
88214503Srpaulo			else
89214503Srpaulo				prev->next = dst->next;
90214503Srpaulo			os_free(dst);
91214503Srpaulo			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
92214503Srpaulo				    (u8 *) from->sun_path,
93214503Srpaulo				    fromlen -
94214503Srpaulo				    offsetof(struct sockaddr_un, sun_path));
95214503Srpaulo			return 0;
96214503Srpaulo		}
97214503Srpaulo		prev = dst;
98214503Srpaulo		dst = dst->next;
99214503Srpaulo	}
100214503Srpaulo	return -1;
101214503Srpaulo}
102214503Srpaulo
103214503Srpaulo
104214503Srpaulostatic int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
105214503Srpaulo				    struct sockaddr_un *from,
106214503Srpaulo				    socklen_t fromlen,
107214503Srpaulo				    char *level)
108214503Srpaulo{
109214503Srpaulo	struct wpa_ctrl_dst *dst;
110214503Srpaulo
111214503Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
112214503Srpaulo
113214503Srpaulo	dst = hapd->ctrl_dst;
114214503Srpaulo	while (dst) {
115214503Srpaulo		if (fromlen == dst->addrlen &&
116214503Srpaulo		    os_memcmp(from->sun_path, dst->addr.sun_path,
117214503Srpaulo			      fromlen - offsetof(struct sockaddr_un, sun_path))
118214503Srpaulo		    == 0) {
119214503Srpaulo			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
120214503Srpaulo				    "level", (u8 *) from->sun_path, fromlen -
121214503Srpaulo				    offsetof(struct sockaddr_un, sun_path));
122214503Srpaulo			dst->debug_level = atoi(level);
123214503Srpaulo			return 0;
124214503Srpaulo		}
125214503Srpaulo		dst = dst->next;
126214503Srpaulo	}
127214503Srpaulo
128214503Srpaulo	return -1;
129214503Srpaulo}
130214503Srpaulo
131214503Srpaulo
132214503Srpaulostatic int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
133214503Srpaulo				      const char *txtaddr)
134214503Srpaulo{
135214503Srpaulo	u8 addr[ETH_ALEN];
136214503Srpaulo	struct sta_info *sta;
137214503Srpaulo
138214503Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
139214503Srpaulo
140214503Srpaulo	if (hwaddr_aton(txtaddr, addr))
141214503Srpaulo		return -1;
142214503Srpaulo
143214503Srpaulo	sta = ap_get_sta(hapd, addr);
144214503Srpaulo	if (sta)
145214503Srpaulo		return 0;
146214503Srpaulo
147214503Srpaulo	wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
148214503Srpaulo		   "notification", MAC2STR(addr));
149214503Srpaulo	sta = ap_sta_add(hapd, addr);
150214503Srpaulo	if (sta == NULL)
151214503Srpaulo		return -1;
152214503Srpaulo
153214503Srpaulo	hostapd_new_assoc_sta(hapd, sta, 0);
154214503Srpaulo	return 0;
155214503Srpaulo}
156214503Srpaulo
157214503Srpaulo
158214503Srpaulostatic int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
159214503Srpaulo					     const char *txtaddr)
160214503Srpaulo{
161214503Srpaulo	u8 addr[ETH_ALEN];
162214503Srpaulo	struct sta_info *sta;
163214503Srpaulo	const char *pos;
164214503Srpaulo
165214503Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr);
166214503Srpaulo
167214503Srpaulo	if (hwaddr_aton(txtaddr, addr))
168214503Srpaulo		return -1;
169214503Srpaulo
170214503Srpaulo	pos = os_strstr(txtaddr, " test=");
171214503Srpaulo	if (pos) {
172214503Srpaulo		struct ieee80211_mgmt mgmt;
173214503Srpaulo		int encrypt;
174214503Srpaulo		if (hapd->driver->send_frame == NULL)
175214503Srpaulo			return -1;
176214503Srpaulo		pos += 6;
177214503Srpaulo		encrypt = atoi(pos);
178214503Srpaulo		os_memset(&mgmt, 0, sizeof(mgmt));
179214503Srpaulo		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
180214503Srpaulo						  WLAN_FC_STYPE_DEAUTH);
181214503Srpaulo		os_memcpy(mgmt.da, addr, ETH_ALEN);
182214503Srpaulo		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
183214503Srpaulo		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
184214503Srpaulo		mgmt.u.deauth.reason_code =
185214503Srpaulo			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
186214503Srpaulo		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
187214503Srpaulo					     IEEE80211_HDRLEN +
188214503Srpaulo					     sizeof(mgmt.u.deauth),
189214503Srpaulo					     encrypt) < 0)
190214503Srpaulo			return -1;
191214503Srpaulo		return 0;
192214503Srpaulo	}
193214503Srpaulo
194214503Srpaulo	hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
195214503Srpaulo	sta = ap_get_sta(hapd, addr);
196214503Srpaulo	if (sta)
197214503Srpaulo		ap_sta_deauthenticate(hapd, sta,
198214503Srpaulo				      WLAN_REASON_PREV_AUTH_NOT_VALID);
199214503Srpaulo
200214503Srpaulo	return 0;
201214503Srpaulo}
202214503Srpaulo
203214503Srpaulo
204214503Srpaulostatic int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
205214503Srpaulo					   const char *txtaddr)
206214503Srpaulo{
207214503Srpaulo	u8 addr[ETH_ALEN];
208214503Srpaulo	struct sta_info *sta;
209214503Srpaulo	const char *pos;
210214503Srpaulo
211214503Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr);
212214503Srpaulo
213214503Srpaulo	if (hwaddr_aton(txtaddr, addr))
214214503Srpaulo		return -1;
215214503Srpaulo
216214503Srpaulo	pos = os_strstr(txtaddr, " test=");
217214503Srpaulo	if (pos) {
218214503Srpaulo		struct ieee80211_mgmt mgmt;
219214503Srpaulo		int encrypt;
220214503Srpaulo		if (hapd->driver->send_frame == NULL)
221214503Srpaulo			return -1;
222214503Srpaulo		pos += 6;
223214503Srpaulo		encrypt = atoi(pos);
224214503Srpaulo		os_memset(&mgmt, 0, sizeof(mgmt));
225214503Srpaulo		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
226214503Srpaulo						  WLAN_FC_STYPE_DISASSOC);
227214503Srpaulo		os_memcpy(mgmt.da, addr, ETH_ALEN);
228214503Srpaulo		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
229214503Srpaulo		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
230214503Srpaulo		mgmt.u.disassoc.reason_code =
231214503Srpaulo			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
232214503Srpaulo		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
233214503Srpaulo					     IEEE80211_HDRLEN +
234214503Srpaulo					     sizeof(mgmt.u.deauth),
235214503Srpaulo					     encrypt) < 0)
236214503Srpaulo			return -1;
237214503Srpaulo		return 0;
238214503Srpaulo	}
239214503Srpaulo
240214503Srpaulo	hapd->drv.sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
241214503Srpaulo	sta = ap_get_sta(hapd, addr);
242214503Srpaulo	if (sta)
243214503Srpaulo		ap_sta_disassociate(hapd, sta,
244214503Srpaulo				    WLAN_REASON_PREV_AUTH_NOT_VALID);
245214503Srpaulo
246214503Srpaulo	return 0;
247214503Srpaulo}
248214503Srpaulo
249214503Srpaulo
250214503Srpaulo#ifdef CONFIG_IEEE80211W
251214503Srpaulo#ifdef NEED_AP_MLME
252214503Srpaulostatic int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
253214503Srpaulo				       const char *txtaddr)
254214503Srpaulo{
255214503Srpaulo	u8 addr[ETH_ALEN];
256214503Srpaulo	u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
257214503Srpaulo
258214503Srpaulo	wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
259214503Srpaulo
260214503Srpaulo	if (hwaddr_aton(txtaddr, addr) ||
261214503Srpaulo	    os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
262214503Srpaulo		return -1;
263214503Srpaulo
264214503Srpaulo	ieee802_11_send_sa_query_req(hapd, addr, trans_id);
265214503Srpaulo
266214503Srpaulo	return 0;
267214503Srpaulo}
268214503Srpaulo#endif /* NEED_AP_MLME */
269214503Srpaulo#endif /* CONFIG_IEEE80211W */
270214503Srpaulo
271214503Srpaulo
272214503Srpaulo#ifdef CONFIG_WPS
273214503Srpaulostatic int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
274214503Srpaulo{
275214503Srpaulo	char *pin = os_strchr(txt, ' ');
276214503Srpaulo	char *timeout_txt;
277214503Srpaulo	int timeout;
278214503Srpaulo
279214503Srpaulo	if (pin == NULL)
280214503Srpaulo		return -1;
281214503Srpaulo	*pin++ = '\0';
282214503Srpaulo
283214503Srpaulo	timeout_txt = os_strchr(pin, ' ');
284214503Srpaulo	if (timeout_txt) {
285214503Srpaulo		*timeout_txt++ = '\0';
286214503Srpaulo		timeout = atoi(timeout_txt);
287214503Srpaulo	} else
288214503Srpaulo		timeout = 0;
289214503Srpaulo
290214503Srpaulo	return hostapd_wps_add_pin(hapd, txt, pin, timeout);
291214503Srpaulo}
292214503Srpaulo
293214503Srpaulo
294214503Srpaulo#ifdef CONFIG_WPS_OOB
295214503Srpaulostatic int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
296214503Srpaulo{
297214503Srpaulo	char *path, *method, *name;
298214503Srpaulo
299214503Srpaulo	path = os_strchr(txt, ' ');
300214503Srpaulo	if (path == NULL)
301214503Srpaulo		return -1;
302214503Srpaulo	*path++ = '\0';
303214503Srpaulo
304214503Srpaulo	method = os_strchr(path, ' ');
305214503Srpaulo	if (method == NULL)
306214503Srpaulo		return -1;
307214503Srpaulo	*method++ = '\0';
308214503Srpaulo
309214503Srpaulo	name = os_strchr(method, ' ');
310214503Srpaulo	if (name != NULL)
311214503Srpaulo		*name++ = '\0';
312214503Srpaulo
313214503Srpaulo	return hostapd_wps_start_oob(hapd, txt, path, method, name);
314214503Srpaulo}
315214503Srpaulo#endif /* CONFIG_WPS_OOB */
316214503Srpaulo
317214503Srpaulo
318214503Srpaulostatic int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
319214503Srpaulo					 char *buf, size_t buflen)
320214503Srpaulo{
321214503Srpaulo	int timeout = 300;
322214503Srpaulo	char *pos;
323214503Srpaulo	const char *pin_txt;
324214503Srpaulo
325214503Srpaulo	pos = os_strchr(txt, ' ');
326214503Srpaulo	if (pos)
327214503Srpaulo		*pos++ = '\0';
328214503Srpaulo
329214503Srpaulo	if (os_strcmp(txt, "disable") == 0) {
330214503Srpaulo		hostapd_wps_ap_pin_disable(hapd);
331214503Srpaulo		return os_snprintf(buf, buflen, "OK\n");
332214503Srpaulo	}
333214503Srpaulo
334214503Srpaulo	if (os_strcmp(txt, "random") == 0) {
335214503Srpaulo		if (pos)
336214503Srpaulo			timeout = atoi(pos);
337214503Srpaulo		pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
338214503Srpaulo		if (pin_txt == NULL)
339214503Srpaulo			return -1;
340214503Srpaulo		return os_snprintf(buf, buflen, "%s", pin_txt);
341214503Srpaulo	}
342214503Srpaulo
343214503Srpaulo	if (os_strcmp(txt, "get") == 0) {
344214503Srpaulo		pin_txt = hostapd_wps_ap_pin_get(hapd);
345214503Srpaulo		if (pin_txt == NULL)
346214503Srpaulo			return -1;
347214503Srpaulo		return os_snprintf(buf, buflen, "%s", pin_txt);
348214503Srpaulo	}
349214503Srpaulo
350214503Srpaulo	if (os_strcmp(txt, "set") == 0) {
351214503Srpaulo		char *pin;
352214503Srpaulo		if (pos == NULL)
353214503Srpaulo			return -1;
354214503Srpaulo		pin = pos;
355214503Srpaulo		pos = os_strchr(pos, ' ');
356214503Srpaulo		if (pos) {
357214503Srpaulo			*pos++ = '\0';
358214503Srpaulo			timeout = atoi(pos);
359214503Srpaulo		}
360214503Srpaulo		if (os_strlen(pin) > buflen)
361214503Srpaulo			return -1;
362214503Srpaulo		if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
363214503Srpaulo			return -1;
364214503Srpaulo		return os_snprintf(buf, buflen, "%s", pin);
365214503Srpaulo	}
366214503Srpaulo
367214503Srpaulo	return -1;
368214503Srpaulo}
369214503Srpaulo#endif /* CONFIG_WPS */
370214503Srpaulo
371214503Srpaulo
372214503Srpaulostatic void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
373214503Srpaulo				       void *sock_ctx)
374214503Srpaulo{
375214503Srpaulo	struct hostapd_data *hapd = eloop_ctx;
376214503Srpaulo	char buf[256];
377214503Srpaulo	int res;
378214503Srpaulo	struct sockaddr_un from;
379214503Srpaulo	socklen_t fromlen = sizeof(from);
380214503Srpaulo	char *reply;
381214503Srpaulo	const int reply_size = 4096;
382214503Srpaulo	int reply_len;
383214503Srpaulo
384214503Srpaulo	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
385214503Srpaulo		       (struct sockaddr *) &from, &fromlen);
386214503Srpaulo	if (res < 0) {
387214503Srpaulo		perror("recvfrom(ctrl_iface)");
388214503Srpaulo		return;
389214503Srpaulo	}
390214503Srpaulo	buf[res] = '\0';
391214503Srpaulo	wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
392214503Srpaulo
393214503Srpaulo	reply = os_malloc(reply_size);
394214503Srpaulo	if (reply == NULL) {
395214503Srpaulo		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
396214503Srpaulo		       fromlen);
397214503Srpaulo		return;
398214503Srpaulo	}
399214503Srpaulo
400214503Srpaulo	os_memcpy(reply, "OK\n", 3);
401214503Srpaulo	reply_len = 3;
402214503Srpaulo
403214503Srpaulo	if (os_strcmp(buf, "PING") == 0) {
404214503Srpaulo		os_memcpy(reply, "PONG\n", 5);
405214503Srpaulo		reply_len = 5;
406214503Srpaulo	} else if (os_strcmp(buf, "MIB") == 0) {
407214503Srpaulo		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
408214503Srpaulo		if (reply_len >= 0) {
409214503Srpaulo			res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
410214503Srpaulo					  reply_size - reply_len);
411214503Srpaulo			if (res < 0)
412214503Srpaulo				reply_len = -1;
413214503Srpaulo			else
414214503Srpaulo				reply_len += res;
415214503Srpaulo		}
416214503Srpaulo		if (reply_len >= 0) {
417214503Srpaulo			res = ieee802_1x_get_mib(hapd, reply + reply_len,
418214503Srpaulo						 reply_size - reply_len);
419214503Srpaulo			if (res < 0)
420214503Srpaulo				reply_len = -1;
421214503Srpaulo			else
422214503Srpaulo				reply_len += res;
423214503Srpaulo		}
424214503Srpaulo#ifndef CONFIG_NO_RADIUS
425214503Srpaulo		if (reply_len >= 0) {
426214503Srpaulo			res = radius_client_get_mib(hapd->radius,
427214503Srpaulo						    reply + reply_len,
428214503Srpaulo						    reply_size - reply_len);
429214503Srpaulo			if (res < 0)
430214503Srpaulo				reply_len = -1;
431214503Srpaulo			else
432214503Srpaulo				reply_len += res;
433214503Srpaulo		}
434214503Srpaulo#endif /* CONFIG_NO_RADIUS */
435214503Srpaulo	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
436214503Srpaulo		reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
437214503Srpaulo							 reply_size);
438214503Srpaulo	} else if (os_strncmp(buf, "STA ", 4) == 0) {
439214503Srpaulo		reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
440214503Srpaulo						   reply_size);
441214503Srpaulo	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
442214503Srpaulo		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
443214503Srpaulo							reply_size);
444214503Srpaulo	} else if (os_strcmp(buf, "ATTACH") == 0) {
445214503Srpaulo		if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
446214503Srpaulo			reply_len = -1;
447214503Srpaulo	} else if (os_strcmp(buf, "DETACH") == 0) {
448214503Srpaulo		if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
449214503Srpaulo			reply_len = -1;
450214503Srpaulo	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
451214503Srpaulo		if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
452214503Srpaulo						    buf + 6))
453214503Srpaulo			reply_len = -1;
454214503Srpaulo	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
455214503Srpaulo		if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
456214503Srpaulo			reply_len = -1;
457214503Srpaulo	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
458214503Srpaulo		if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
459214503Srpaulo			reply_len = -1;
460214503Srpaulo	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
461214503Srpaulo		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
462214503Srpaulo			reply_len = -1;
463214503Srpaulo#ifdef CONFIG_IEEE80211W
464214503Srpaulo#ifdef NEED_AP_MLME
465214503Srpaulo	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
466214503Srpaulo		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
467214503Srpaulo			reply_len = -1;
468214503Srpaulo#endif /* NEED_AP_MLME */
469214503Srpaulo#endif /* CONFIG_IEEE80211W */
470214503Srpaulo#ifdef CONFIG_WPS
471214503Srpaulo	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
472214503Srpaulo		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
473214503Srpaulo			reply_len = -1;
474214503Srpaulo	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
475214503Srpaulo		if (hostapd_wps_button_pushed(hapd))
476214503Srpaulo			reply_len = -1;
477214503Srpaulo#ifdef CONFIG_WPS_OOB
478214503Srpaulo	} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
479214503Srpaulo		if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
480214503Srpaulo			reply_len = -1;
481214503Srpaulo#endif /* CONFIG_WPS_OOB */
482214503Srpaulo	} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
483214503Srpaulo		reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
484214503Srpaulo							  reply, reply_size);
485214503Srpaulo#endif /* CONFIG_WPS */
486214503Srpaulo	} else {
487214503Srpaulo		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
488214503Srpaulo		reply_len = 16;
489214503Srpaulo	}
490214503Srpaulo
491214503Srpaulo	if (reply_len < 0) {
492214503Srpaulo		os_memcpy(reply, "FAIL\n", 5);
493214503Srpaulo		reply_len = 5;
494214503Srpaulo	}
495214503Srpaulo	sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
496214503Srpaulo	os_free(reply);
497214503Srpaulo}
498214503Srpaulo
499214503Srpaulo
500214503Srpaulostatic char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
501214503Srpaulo{
502214503Srpaulo	char *buf;
503214503Srpaulo	size_t len;
504214503Srpaulo
505214503Srpaulo	if (hapd->conf->ctrl_interface == NULL)
506214503Srpaulo		return NULL;
507214503Srpaulo
508214503Srpaulo	len = os_strlen(hapd->conf->ctrl_interface) +
509214503Srpaulo		os_strlen(hapd->conf->iface) + 2;
510214503Srpaulo	buf = os_malloc(len);
511214503Srpaulo	if (buf == NULL)
512214503Srpaulo		return NULL;
513214503Srpaulo
514214503Srpaulo	os_snprintf(buf, len, "%s/%s",
515214503Srpaulo		    hapd->conf->ctrl_interface, hapd->conf->iface);
516214503Srpaulo	buf[len - 1] = '\0';
517214503Srpaulo	return buf;
518214503Srpaulo}
519214503Srpaulo
520214503Srpaulo
521214503Srpaulostatic void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
522214503Srpaulo				      const char *txt, size_t len)
523214503Srpaulo{
524214503Srpaulo	struct hostapd_data *hapd = ctx;
525214503Srpaulo	if (hapd == NULL)
526214503Srpaulo		return;
527214503Srpaulo	hostapd_ctrl_iface_send(hapd, level, txt, len);
528214503Srpaulo}
529214503Srpaulo
530214503Srpaulo
531214503Srpauloint hostapd_ctrl_iface_init(struct hostapd_data *hapd)
532214503Srpaulo{
533214503Srpaulo	struct sockaddr_un addr;
534214503Srpaulo	int s = -1;
535214503Srpaulo	char *fname = NULL;
536214503Srpaulo
537214503Srpaulo	hapd->ctrl_sock = -1;
538214503Srpaulo
539214503Srpaulo	if (hapd->conf->ctrl_interface == NULL)
540214503Srpaulo		return 0;
541214503Srpaulo
542214503Srpaulo	if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
543214503Srpaulo		if (errno == EEXIST) {
544214503Srpaulo			wpa_printf(MSG_DEBUG, "Using existing control "
545214503Srpaulo				   "interface directory.");
546214503Srpaulo		} else {
547214503Srpaulo			perror("mkdir[ctrl_interface]");
548214503Srpaulo			goto fail;
549214503Srpaulo		}
550214503Srpaulo	}
551214503Srpaulo
552214503Srpaulo	if (hapd->conf->ctrl_interface_gid_set &&
553214503Srpaulo	    chown(hapd->conf->ctrl_interface, 0,
554214503Srpaulo		  hapd->conf->ctrl_interface_gid) < 0) {
555214503Srpaulo		perror("chown[ctrl_interface]");
556214503Srpaulo		return -1;
557214503Srpaulo	}
558214503Srpaulo
559214503Srpaulo	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
560214503Srpaulo	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
561214503Srpaulo		goto fail;
562214503Srpaulo
563214503Srpaulo	s = socket(PF_UNIX, SOCK_DGRAM, 0);
564214503Srpaulo	if (s < 0) {
565214503Srpaulo		perror("socket(PF_UNIX)");
566214503Srpaulo		goto fail;
567214503Srpaulo	}
568214503Srpaulo
569214503Srpaulo	os_memset(&addr, 0, sizeof(addr));
570214503Srpaulo#ifdef __FreeBSD__
571214503Srpaulo	addr.sun_len = sizeof(addr);
572214503Srpaulo#endif /* __FreeBSD__ */
573214503Srpaulo	addr.sun_family = AF_UNIX;
574214503Srpaulo	fname = hostapd_ctrl_iface_path(hapd);
575214503Srpaulo	if (fname == NULL)
576214503Srpaulo		goto fail;
577214503Srpaulo	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
578214503Srpaulo	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
579214503Srpaulo		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
580214503Srpaulo			   strerror(errno));
581214503Srpaulo		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
582214503Srpaulo			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
583214503Srpaulo				   " allow connections - assuming it was left"
584214503Srpaulo				   "over from forced program termination");
585214503Srpaulo			if (unlink(fname) < 0) {
586214503Srpaulo				perror("unlink[ctrl_iface]");
587214503Srpaulo				wpa_printf(MSG_ERROR, "Could not unlink "
588214503Srpaulo					   "existing ctrl_iface socket '%s'",
589214503Srpaulo					   fname);
590214503Srpaulo				goto fail;
591214503Srpaulo			}
592214503Srpaulo			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
593214503Srpaulo			    0) {
594214503Srpaulo				perror("bind(PF_UNIX)");
595214503Srpaulo				goto fail;
596214503Srpaulo			}
597214503Srpaulo			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
598214503Srpaulo				   "ctrl_iface socket '%s'", fname);
599214503Srpaulo		} else {
600214503Srpaulo			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
601214503Srpaulo				   "be in use - cannot override it");
602214503Srpaulo			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
603214503Srpaulo				   "not used anymore", fname);
604214503Srpaulo			os_free(fname);
605214503Srpaulo			fname = NULL;
606214503Srpaulo			goto fail;
607214503Srpaulo		}
608214503Srpaulo	}
609214503Srpaulo
610214503Srpaulo	if (hapd->conf->ctrl_interface_gid_set &&
611214503Srpaulo	    chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
612214503Srpaulo		perror("chown[ctrl_interface/ifname]");
613214503Srpaulo		goto fail;
614214503Srpaulo	}
615214503Srpaulo
616214503Srpaulo	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
617214503Srpaulo		perror("chmod[ctrl_interface/ifname]");
618214503Srpaulo		goto fail;
619214503Srpaulo	}
620214503Srpaulo	os_free(fname);
621214503Srpaulo
622214503Srpaulo	hapd->ctrl_sock = s;
623214503Srpaulo	eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
624214503Srpaulo				 NULL);
625214503Srpaulo	hapd->msg_ctx = hapd;
626214503Srpaulo	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
627214503Srpaulo
628214503Srpaulo	return 0;
629214503Srpaulo
630214503Srpaulofail:
631214503Srpaulo	if (s >= 0)
632214503Srpaulo		close(s);
633214503Srpaulo	if (fname) {
634214503Srpaulo		unlink(fname);
635214503Srpaulo		os_free(fname);
636214503Srpaulo	}
637214503Srpaulo	return -1;
638214503Srpaulo}
639214503Srpaulo
640214503Srpaulo
641214503Srpaulovoid hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
642214503Srpaulo{
643214503Srpaulo	struct wpa_ctrl_dst *dst, *prev;
644214503Srpaulo
645214503Srpaulo	if (hapd->ctrl_sock > -1) {
646214503Srpaulo		char *fname;
647214503Srpaulo		eloop_unregister_read_sock(hapd->ctrl_sock);
648214503Srpaulo		close(hapd->ctrl_sock);
649214503Srpaulo		hapd->ctrl_sock = -1;
650214503Srpaulo		fname = hostapd_ctrl_iface_path(hapd);
651214503Srpaulo		if (fname)
652214503Srpaulo			unlink(fname);
653214503Srpaulo		os_free(fname);
654214503Srpaulo
655214503Srpaulo		if (hapd->conf->ctrl_interface &&
656214503Srpaulo		    rmdir(hapd->conf->ctrl_interface) < 0) {
657214503Srpaulo			if (errno == ENOTEMPTY) {
658214503Srpaulo				wpa_printf(MSG_DEBUG, "Control interface "
659214503Srpaulo					   "directory not empty - leaving it "
660214503Srpaulo					   "behind");
661214503Srpaulo			} else {
662214503Srpaulo				perror("rmdir[ctrl_interface]");
663214503Srpaulo			}
664214503Srpaulo		}
665214503Srpaulo	}
666214503Srpaulo
667214503Srpaulo	dst = hapd->ctrl_dst;
668214503Srpaulo	while (dst) {
669214503Srpaulo		prev = dst;
670214503Srpaulo		dst = dst->next;
671214503Srpaulo		os_free(prev);
672214503Srpaulo	}
673214503Srpaulo}
674214503Srpaulo
675214503Srpaulo
676214503Srpaulostatic void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
677214503Srpaulo				    const char *buf, size_t len)
678214503Srpaulo{
679214503Srpaulo	struct wpa_ctrl_dst *dst, *next;
680214503Srpaulo	struct msghdr msg;
681214503Srpaulo	int idx;
682214503Srpaulo	struct iovec io[2];
683214503Srpaulo	char levelstr[10];
684214503Srpaulo
685214503Srpaulo	dst = hapd->ctrl_dst;
686214503Srpaulo	if (hapd->ctrl_sock < 0 || dst == NULL)
687214503Srpaulo		return;
688214503Srpaulo
689214503Srpaulo	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
690214503Srpaulo	io[0].iov_base = levelstr;
691214503Srpaulo	io[0].iov_len = os_strlen(levelstr);
692214503Srpaulo	io[1].iov_base = (char *) buf;
693214503Srpaulo	io[1].iov_len = len;
694214503Srpaulo	os_memset(&msg, 0, sizeof(msg));
695214503Srpaulo	msg.msg_iov = io;
696214503Srpaulo	msg.msg_iovlen = 2;
697214503Srpaulo
698214503Srpaulo	idx = 0;
699214503Srpaulo	while (dst) {
700214503Srpaulo		next = dst->next;
701214503Srpaulo		if (level >= dst->debug_level) {
702214503Srpaulo			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
703214503Srpaulo				    (u8 *) dst->addr.sun_path, dst->addrlen -
704214503Srpaulo				    offsetof(struct sockaddr_un, sun_path));
705214503Srpaulo			msg.msg_name = &dst->addr;
706214503Srpaulo			msg.msg_namelen = dst->addrlen;
707214503Srpaulo			if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
708214503Srpaulo				int _errno = errno;
709214503Srpaulo				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
710214503Srpaulo					   "%d - %s",
711214503Srpaulo					   idx, errno, strerror(errno));
712214503Srpaulo				dst->errors++;
713214503Srpaulo				if (dst->errors > 10 || _errno == ENOENT) {
714214503Srpaulo					hostapd_ctrl_iface_detach(
715214503Srpaulo						hapd, &dst->addr,
716214503Srpaulo						dst->addrlen);
717214503Srpaulo				}
718214503Srpaulo			} else
719214503Srpaulo				dst->errors = 0;
720214503Srpaulo		}
721214503Srpaulo		idx++;
722214503Srpaulo		dst = next;
723214503Srpaulo	}
724214503Srpaulo}
725214503Srpaulo
726214503Srpaulo#endif /* CONFIG_NATIVE_WINDOWS */
727