xref: /netbsd-src/external/bsd/wpa/dist/hostapd/ctrl_iface.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*
2  * hostapd / UNIX domain socket -based control interface
3  * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14 
15 #include "utils/includes.h"
16 
17 #ifndef CONFIG_NATIVE_WINDOWS
18 
19 #include <sys/un.h>
20 #include <sys/stat.h>
21 #include <stddef.h>
22 
23 #include "utils/common.h"
24 #include "utils/eloop.h"
25 #include "common/ieee802_11_defs.h"
26 #include "drivers/driver.h"
27 #include "radius/radius_client.h"
28 #include "ap/hostapd.h"
29 #include "ap/ap_config.h"
30 #include "ap/ieee802_1x.h"
31 #include "ap/wpa_auth.h"
32 #include "ap/ieee802_11.h"
33 #include "ap/sta_info.h"
34 #include "ap/accounting.h"
35 #include "ap/wps_hostapd.h"
36 #include "ap/ctrl_iface_ap.h"
37 #include "ctrl_iface.h"
38 
39 
40 struct wpa_ctrl_dst {
41 	struct wpa_ctrl_dst *next;
42 	struct sockaddr_un addr;
43 	socklen_t addrlen;
44 	int debug_level;
45 	int errors;
46 };
47 
48 
49 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
50 				    const char *buf, size_t len);
51 
52 
53 static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
54 				     struct sockaddr_un *from,
55 				     socklen_t fromlen)
56 {
57 	struct wpa_ctrl_dst *dst;
58 
59 	dst = os_zalloc(sizeof(*dst));
60 	if (dst == NULL)
61 		return -1;
62 	os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
63 	dst->addrlen = fromlen;
64 	dst->debug_level = MSG_INFO;
65 	dst->next = hapd->ctrl_dst;
66 	hapd->ctrl_dst = dst;
67 	wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
68 		    (u8 *) from->sun_path,
69 		    fromlen - offsetof(struct sockaddr_un, sun_path));
70 	return 0;
71 }
72 
73 
74 static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
75 				     struct sockaddr_un *from,
76 				     socklen_t fromlen)
77 {
78 	struct wpa_ctrl_dst *dst, *prev = NULL;
79 
80 	dst = hapd->ctrl_dst;
81 	while (dst) {
82 		if (fromlen == dst->addrlen &&
83 		    os_memcmp(from->sun_path, dst->addr.sun_path,
84 			      fromlen - offsetof(struct sockaddr_un, sun_path))
85 		    == 0) {
86 			if (prev == NULL)
87 				hapd->ctrl_dst = dst->next;
88 			else
89 				prev->next = dst->next;
90 			os_free(dst);
91 			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
92 				    (u8 *) from->sun_path,
93 				    fromlen -
94 				    offsetof(struct sockaddr_un, sun_path));
95 			return 0;
96 		}
97 		prev = dst;
98 		dst = dst->next;
99 	}
100 	return -1;
101 }
102 
103 
104 static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
105 				    struct sockaddr_un *from,
106 				    socklen_t fromlen,
107 				    char *level)
108 {
109 	struct wpa_ctrl_dst *dst;
110 
111 	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
112 
113 	dst = hapd->ctrl_dst;
114 	while (dst) {
115 		if (fromlen == dst->addrlen &&
116 		    os_memcmp(from->sun_path, dst->addr.sun_path,
117 			      fromlen - offsetof(struct sockaddr_un, sun_path))
118 		    == 0) {
119 			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
120 				    "level", (u8 *) from->sun_path, fromlen -
121 				    offsetof(struct sockaddr_un, sun_path));
122 			dst->debug_level = atoi(level);
123 			return 0;
124 		}
125 		dst = dst->next;
126 	}
127 
128 	return -1;
129 }
130 
131 
132 static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
133 				      const char *txtaddr)
134 {
135 	u8 addr[ETH_ALEN];
136 	struct sta_info *sta;
137 
138 	wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
139 
140 	if (hwaddr_aton(txtaddr, addr))
141 		return -1;
142 
143 	sta = ap_get_sta(hapd, addr);
144 	if (sta)
145 		return 0;
146 
147 	wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
148 		   "notification", MAC2STR(addr));
149 	sta = ap_sta_add(hapd, addr);
150 	if (sta == NULL)
151 		return -1;
152 
153 	hostapd_new_assoc_sta(hapd, sta, 0);
154 	return 0;
155 }
156 
157 
158 static int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
159 					     const char *txtaddr)
160 {
161 	u8 addr[ETH_ALEN];
162 	struct sta_info *sta;
163 	const char *pos;
164 
165 	wpa_printf(MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s", txtaddr);
166 
167 	if (hwaddr_aton(txtaddr, addr))
168 		return -1;
169 
170 	pos = os_strstr(txtaddr, " test=");
171 	if (pos) {
172 		struct ieee80211_mgmt mgmt;
173 		int encrypt;
174 		if (hapd->driver->send_frame == NULL)
175 			return -1;
176 		pos += 6;
177 		encrypt = atoi(pos);
178 		os_memset(&mgmt, 0, sizeof(mgmt));
179 		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
180 						  WLAN_FC_STYPE_DEAUTH);
181 		os_memcpy(mgmt.da, addr, ETH_ALEN);
182 		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
183 		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
184 		mgmt.u.deauth.reason_code =
185 			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
186 		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
187 					     IEEE80211_HDRLEN +
188 					     sizeof(mgmt.u.deauth),
189 					     encrypt) < 0)
190 			return -1;
191 		return 0;
192 	}
193 
194 	hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
195 	sta = ap_get_sta(hapd, addr);
196 	if (sta)
197 		ap_sta_deauthenticate(hapd, sta,
198 				      WLAN_REASON_PREV_AUTH_NOT_VALID);
199 
200 	return 0;
201 }
202 
203 
204 static int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
205 					   const char *txtaddr)
206 {
207 	u8 addr[ETH_ALEN];
208 	struct sta_info *sta;
209 	const char *pos;
210 
211 	wpa_printf(MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s", txtaddr);
212 
213 	if (hwaddr_aton(txtaddr, addr))
214 		return -1;
215 
216 	pos = os_strstr(txtaddr, " test=");
217 	if (pos) {
218 		struct ieee80211_mgmt mgmt;
219 		int encrypt;
220 		if (hapd->driver->send_frame == NULL)
221 			return -1;
222 		pos += 6;
223 		encrypt = atoi(pos);
224 		os_memset(&mgmt, 0, sizeof(mgmt));
225 		mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
226 						  WLAN_FC_STYPE_DISASSOC);
227 		os_memcpy(mgmt.da, addr, ETH_ALEN);
228 		os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
229 		os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
230 		mgmt.u.deauth.reason_code =
231 			host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
232 		if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
233 					     IEEE80211_HDRLEN +
234 					     sizeof(mgmt.u.deauth),
235 					     encrypt) < 0)
236 			return -1;
237 		return 0;
238 	}
239 
240 	hapd->drv.sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
241 	sta = ap_get_sta(hapd, addr);
242 	if (sta)
243 		ap_sta_disassociate(hapd, sta,
244 				    WLAN_REASON_PREV_AUTH_NOT_VALID);
245 
246 	return 0;
247 }
248 
249 
250 #ifdef CONFIG_IEEE80211W
251 #ifdef NEED_AP_MLME
252 static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
253 				       const char *txtaddr)
254 {
255 	u8 addr[ETH_ALEN];
256 	u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
257 
258 	wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
259 
260 	if (hwaddr_aton(txtaddr, addr) ||
261 	    os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
262 		return -1;
263 
264 	ieee802_11_send_sa_query_req(hapd, addr, trans_id);
265 
266 	return 0;
267 }
268 #endif /* NEED_AP_MLME */
269 #endif /* CONFIG_IEEE80211W */
270 
271 
272 #ifdef CONFIG_WPS
273 static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
274 {
275 	char *pin = os_strchr(txt, ' ');
276 	char *timeout_txt;
277 	int timeout;
278 
279 	if (pin == NULL)
280 		return -1;
281 	*pin++ = '\0';
282 
283 	timeout_txt = os_strchr(pin, ' ');
284 	if (timeout_txt) {
285 		*timeout_txt++ = '\0';
286 		timeout = atoi(timeout_txt);
287 	} else
288 		timeout = 0;
289 
290 	return hostapd_wps_add_pin(hapd, txt, pin, timeout);
291 }
292 
293 
294 #ifdef CONFIG_WPS_OOB
295 static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
296 {
297 	char *path, *method, *name;
298 
299 	path = os_strchr(txt, ' ');
300 	if (path == NULL)
301 		return -1;
302 	*path++ = '\0';
303 
304 	method = os_strchr(path, ' ');
305 	if (method == NULL)
306 		return -1;
307 	*method++ = '\0';
308 
309 	name = os_strchr(method, ' ');
310 	if (name != NULL)
311 		*name++ = '\0';
312 
313 	return hostapd_wps_start_oob(hapd, txt, path, method, name);
314 }
315 #endif /* CONFIG_WPS_OOB */
316 #endif /* CONFIG_WPS */
317 
318 
319 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
320 				       void *sock_ctx)
321 {
322 	struct hostapd_data *hapd = eloop_ctx;
323 	char buf[256];
324 	int res;
325 	struct sockaddr_un from;
326 	socklen_t fromlen = sizeof(from);
327 	char *reply;
328 	const int reply_size = 4096;
329 	int reply_len;
330 
331 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
332 		       (struct sockaddr *) &from, &fromlen);
333 	if (res < 0) {
334 		perror("recvfrom(ctrl_iface)");
335 		return;
336 	}
337 	buf[res] = '\0';
338 	wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res);
339 
340 	reply = os_malloc(reply_size);
341 	if (reply == NULL) {
342 		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
343 		       fromlen);
344 		return;
345 	}
346 
347 	os_memcpy(reply, "OK\n", 3);
348 	reply_len = 3;
349 
350 	if (os_strcmp(buf, "PING") == 0) {
351 		os_memcpy(reply, "PONG\n", 5);
352 		reply_len = 5;
353 	} else if (os_strcmp(buf, "MIB") == 0) {
354 		reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
355 		if (reply_len >= 0) {
356 			res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
357 					  reply_size - reply_len);
358 			if (res < 0)
359 				reply_len = -1;
360 			else
361 				reply_len += res;
362 		}
363 		if (reply_len >= 0) {
364 			res = ieee802_1x_get_mib(hapd, reply + reply_len,
365 						 reply_size - reply_len);
366 			if (res < 0)
367 				reply_len = -1;
368 			else
369 				reply_len += res;
370 		}
371 #ifndef CONFIG_NO_RADIUS
372 		if (reply_len >= 0) {
373 			res = radius_client_get_mib(hapd->radius,
374 						    reply + reply_len,
375 						    reply_size - reply_len);
376 			if (res < 0)
377 				reply_len = -1;
378 			else
379 				reply_len += res;
380 		}
381 #endif /* CONFIG_NO_RADIUS */
382 	} else if (os_strcmp(buf, "STA-FIRST") == 0) {
383 		reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
384 							 reply_size);
385 	} else if (os_strncmp(buf, "STA ", 4) == 0) {
386 		reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
387 						   reply_size);
388 	} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
389 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
390 							reply_size);
391 	} else if (os_strcmp(buf, "ATTACH") == 0) {
392 		if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
393 			reply_len = -1;
394 	} else if (os_strcmp(buf, "DETACH") == 0) {
395 		if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
396 			reply_len = -1;
397 	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
398 		if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
399 						    buf + 6))
400 			reply_len = -1;
401 	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
402 		if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
403 			reply_len = -1;
404 	} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
405 		if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
406 			reply_len = -1;
407 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
408 		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
409 			reply_len = -1;
410 #ifdef CONFIG_IEEE80211W
411 #ifdef NEED_AP_MLME
412 	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
413 		if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
414 			reply_len = -1;
415 #endif /* NEED_AP_MLME */
416 #endif /* CONFIG_IEEE80211W */
417 #ifdef CONFIG_WPS
418 	} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
419 		if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
420 			reply_len = -1;
421 	} else if (os_strcmp(buf, "WPS_PBC") == 0) {
422 		if (hostapd_wps_button_pushed(hapd))
423 			reply_len = -1;
424 #ifdef CONFIG_WPS_OOB
425 	} else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
426 		if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
427 			reply_len = -1;
428 #endif /* CONFIG_WPS_OOB */
429 #endif /* CONFIG_WPS */
430 	} else {
431 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
432 		reply_len = 16;
433 	}
434 
435 	if (reply_len < 0) {
436 		os_memcpy(reply, "FAIL\n", 5);
437 		reply_len = 5;
438 	}
439 	sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
440 	os_free(reply);
441 }
442 
443 
444 static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
445 {
446 	char *buf;
447 	size_t len;
448 
449 	if (hapd->conf->ctrl_interface == NULL)
450 		return NULL;
451 
452 	len = os_strlen(hapd->conf->ctrl_interface) +
453 		os_strlen(hapd->conf->iface) + 2;
454 	buf = os_malloc(len);
455 	if (buf == NULL)
456 		return NULL;
457 
458 	os_snprintf(buf, len, "%s/%s",
459 		    hapd->conf->ctrl_interface, hapd->conf->iface);
460 	buf[len - 1] = '\0';
461 	return buf;
462 }
463 
464 
465 static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
466 				      const char *txt, size_t len)
467 {
468 	struct hostapd_data *hapd = ctx;
469 	if (hapd == NULL)
470 		return;
471 	hostapd_ctrl_iface_send(hapd, level, txt, len);
472 }
473 
474 
475 int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
476 {
477 	struct sockaddr_un addr;
478 	int s = -1;
479 	char *fname = NULL;
480 
481 	hapd->ctrl_sock = -1;
482 
483 	if (hapd->conf->ctrl_interface == NULL)
484 		return 0;
485 
486 	if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
487 		if (errno == EEXIST) {
488 			wpa_printf(MSG_DEBUG, "Using existing control "
489 				   "interface directory.");
490 		} else {
491 			perror("mkdir[ctrl_interface]");
492 			goto fail;
493 		}
494 	}
495 
496 	if (hapd->conf->ctrl_interface_gid_set &&
497 	    chown(hapd->conf->ctrl_interface, 0,
498 		  hapd->conf->ctrl_interface_gid) < 0) {
499 		perror("chown[ctrl_interface]");
500 		return -1;
501 	}
502 
503 	if (os_strlen(hapd->conf->ctrl_interface) + 1 +
504 	    os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
505 		goto fail;
506 
507 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
508 	if (s < 0) {
509 		perror("socket(PF_UNIX)");
510 		goto fail;
511 	}
512 
513 	os_memset(&addr, 0, sizeof(addr));
514 #ifdef __FreeBSD__
515 	addr.sun_len = sizeof(addr);
516 #endif /* __FreeBSD__ */
517 	addr.sun_family = AF_UNIX;
518 	fname = hostapd_ctrl_iface_path(hapd);
519 	if (fname == NULL)
520 		goto fail;
521 	os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
522 	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
523 		wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
524 			   strerror(errno));
525 		if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
526 			wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
527 				   " allow connections - assuming it was left"
528 				   "over from forced program termination");
529 			if (unlink(fname) < 0) {
530 				perror("unlink[ctrl_iface]");
531 				wpa_printf(MSG_ERROR, "Could not unlink "
532 					   "existing ctrl_iface socket '%s'",
533 					   fname);
534 				goto fail;
535 			}
536 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
537 			    0) {
538 				perror("bind(PF_UNIX)");
539 				goto fail;
540 			}
541 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
542 				   "ctrl_iface socket '%s'", fname);
543 		} else {
544 			wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
545 				   "be in use - cannot override it");
546 			wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
547 				   "not used anymore", fname);
548 			os_free(fname);
549 			fname = NULL;
550 			goto fail;
551 		}
552 	}
553 
554 	if (hapd->conf->ctrl_interface_gid_set &&
555 	    chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) {
556 		perror("chown[ctrl_interface/ifname]");
557 		goto fail;
558 	}
559 
560 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
561 		perror("chmod[ctrl_interface/ifname]");
562 		goto fail;
563 	}
564 	os_free(fname);
565 
566 	hapd->ctrl_sock = s;
567 	eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
568 				 NULL);
569 	hapd->msg_ctx = hapd;
570 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
571 
572 	return 0;
573 
574 fail:
575 	if (s >= 0)
576 		close(s);
577 	if (fname) {
578 		unlink(fname);
579 		os_free(fname);
580 	}
581 	return -1;
582 }
583 
584 
585 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
586 {
587 	struct wpa_ctrl_dst *dst, *prev;
588 
589 	if (hapd->ctrl_sock > -1) {
590 		char *fname;
591 		eloop_unregister_read_sock(hapd->ctrl_sock);
592 		close(hapd->ctrl_sock);
593 		hapd->ctrl_sock = -1;
594 		fname = hostapd_ctrl_iface_path(hapd);
595 		if (fname)
596 			unlink(fname);
597 		os_free(fname);
598 
599 		if (hapd->conf->ctrl_interface &&
600 		    rmdir(hapd->conf->ctrl_interface) < 0) {
601 			if (errno == ENOTEMPTY) {
602 				wpa_printf(MSG_DEBUG, "Control interface "
603 					   "directory not empty - leaving it "
604 					   "behind");
605 			} else {
606 				perror("rmdir[ctrl_interface]");
607 			}
608 		}
609 	}
610 
611 	dst = hapd->ctrl_dst;
612 	while (dst) {
613 		prev = dst;
614 		dst = dst->next;
615 		os_free(prev);
616 	}
617 }
618 
619 
620 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
621 				    const char *buf, size_t len)
622 {
623 	struct wpa_ctrl_dst *dst, *next;
624 	struct msghdr msg;
625 	int idx;
626 	struct iovec io[2];
627 	char levelstr[10];
628 
629 	dst = hapd->ctrl_dst;
630 	if (hapd->ctrl_sock < 0 || dst == NULL)
631 		return;
632 
633 	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
634 	io[0].iov_base = levelstr;
635 	io[0].iov_len = os_strlen(levelstr);
636 	io[1].iov_base = (char *) buf;
637 	io[1].iov_len = len;
638 	os_memset(&msg, 0, sizeof(msg));
639 	msg.msg_iov = io;
640 	msg.msg_iovlen = 2;
641 
642 	idx = 0;
643 	while (dst) {
644 		next = dst->next;
645 		if (level >= dst->debug_level) {
646 			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
647 				    (u8 *) dst->addr.sun_path, dst->addrlen -
648 				    offsetof(struct sockaddr_un, sun_path));
649 			msg.msg_name = &dst->addr;
650 			msg.msg_namelen = dst->addrlen;
651 			if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
652 				int _errno = errno;
653 				wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
654 					   "%d - %s",
655 					   idx, errno, strerror(errno));
656 				dst->errors++;
657 				if (dst->errors > 10 || _errno == ENOENT) {
658 					hostapd_ctrl_iface_detach(
659 						hapd, &dst->addr,
660 						dst->addrlen);
661 				}
662 			} else
663 				dst->errors = 0;
664 		}
665 		idx++;
666 		dst = next;
667 	}
668 }
669 
670 #endif /* CONFIG_NATIVE_WINDOWS */
671