]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * hostapd / UNIX domain socket -based control interface | |
6226e38d | 3 | * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> |
6fc6879b JM |
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 | ||
6226e38d | 15 | #include "utils/includes.h" |
6fc6879b JM |
16 | |
17 | #ifndef CONFIG_NATIVE_WINDOWS | |
18 | ||
19 | #include <sys/un.h> | |
20 | #include <sys/stat.h> | |
75864b7f | 21 | #include <stddef.h> |
6fc6879b | 22 | |
6226e38d JM |
23 | #include "utils/common.h" |
24 | #include "utils/eloop.h" | |
81f4f619 | 25 | #include "common/ieee802_11_defs.h" |
1057d78e | 26 | #include "drivers/driver.h" |
6fc6879b | 27 | #include "radius/radius_client.h" |
1057d78e | 28 | #include "ap/hostapd.h" |
6226e38d | 29 | #include "ap/ap_config.h" |
1057d78e | 30 | #include "ap/ieee802_1x.h" |
6226e38d | 31 | #include "ap/wpa_auth.h" |
1057d78e JM |
32 | #include "ap/ieee802_11.h" |
33 | #include "ap/sta_info.h" | |
34 | #include "ap/accounting.h" | |
32da61d9 | 35 | #include "ap/wps_hostapd.h" |
0e2d35c6 | 36 | #include "ap/ctrl_iface_ap.h" |
6fc6879b | 37 | #include "ctrl_iface.h" |
6fc6879b JM |
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 | ||
42d16805 JM |
49 | static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, |
50 | const char *buf, size_t len); | |
51 | ||
52 | ||
6fc6879b JM |
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", | |
75864b7f JM |
68 | (u8 *) from->sun_path, |
69 | fromlen - offsetof(struct sockaddr_un, sun_path)); | |
6fc6879b JM |
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 && | |
75864b7f JM |
83 | os_memcmp(from->sun_path, dst->addr.sun_path, |
84 | fromlen - offsetof(struct sockaddr_un, sun_path)) | |
85 | == 0) { | |
6fc6879b JM |
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", | |
75864b7f JM |
92 | (u8 *) from->sun_path, |
93 | fromlen - | |
94 | offsetof(struct sockaddr_un, sun_path)); | |
6fc6879b JM |
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 && | |
75864b7f JM |
116 | os_memcmp(from->sun_path, dst->addr.sun_path, |
117 | fromlen - offsetof(struct sockaddr_un, sun_path)) | |
118 | == 0) { | |
6fc6879b | 119 | wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " |
75864b7f JM |
120 | "level", (u8 *) from->sun_path, fromlen - |
121 | offsetof(struct sockaddr_un, sun_path)); | |
6fc6879b JM |
122 | dst->debug_level = atoi(level); |
123 | return 0; | |
124 | } | |
125 | dst = dst->next; | |
126 | } | |
127 | ||
128 | return -1; | |
129 | } | |
130 | ||
131 | ||
6fc6879b JM |
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); | |
6fc6879b JM |
154 | return 0; |
155 | } | |
156 | ||
157 | ||
88b4b424 | 158 | #ifdef CONFIG_IEEE80211W |
fe6bdb77 | 159 | #ifdef NEED_AP_MLME |
88b4b424 JM |
160 | static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, |
161 | const char *txtaddr) | |
162 | { | |
163 | u8 addr[ETH_ALEN]; | |
164 | u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; | |
165 | ||
166 | wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr); | |
167 | ||
f5455a2d JM |
168 | if (hwaddr_aton(txtaddr, addr) || |
169 | os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) | |
88b4b424 JM |
170 | return -1; |
171 | ||
88b4b424 JM |
172 | ieee802_11_send_sa_query_req(hapd, addr, trans_id); |
173 | ||
174 | return 0; | |
175 | } | |
fe6bdb77 | 176 | #endif /* NEED_AP_MLME */ |
88b4b424 JM |
177 | #endif /* CONFIG_IEEE80211W */ |
178 | ||
179 | ||
ad08c363 JM |
180 | #ifdef CONFIG_WPS |
181 | static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) | |
182 | { | |
183 | char *pin = os_strchr(txt, ' '); | |
077a781f JM |
184 | char *timeout_txt; |
185 | int timeout; | |
186 | ||
ad08c363 JM |
187 | if (pin == NULL) |
188 | return -1; | |
189 | *pin++ = '\0'; | |
077a781f JM |
190 | |
191 | timeout_txt = os_strchr(pin, ' '); | |
192 | if (timeout_txt) { | |
193 | *timeout_txt++ = '\0'; | |
194 | timeout = atoi(timeout_txt); | |
195 | } else | |
196 | timeout = 0; | |
197 | ||
198 | return hostapd_wps_add_pin(hapd, txt, pin, timeout); | |
ad08c363 | 199 | } |
46bdb83a MH |
200 | |
201 | ||
116f7bb0 | 202 | #ifdef CONFIG_WPS_OOB |
46bdb83a MH |
203 | static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt) |
204 | { | |
e1ee6b60 | 205 | char *path, *method, *name; |
46bdb83a MH |
206 | |
207 | path = os_strchr(txt, ' '); | |
208 | if (path == NULL) | |
209 | return -1; | |
210 | *path++ = '\0'; | |
211 | ||
212 | method = os_strchr(path, ' '); | |
213 | if (method == NULL) | |
214 | return -1; | |
215 | *method++ = '\0'; | |
216 | ||
e1ee6b60 MH |
217 | name = os_strchr(method, ' '); |
218 | if (name != NULL) | |
219 | *name++ = '\0'; | |
220 | ||
221 | return hostapd_wps_start_oob(hapd, txt, path, method, name); | |
46bdb83a | 222 | } |
116f7bb0 | 223 | #endif /* CONFIG_WPS_OOB */ |
ad08c363 JM |
224 | #endif /* CONFIG_WPS */ |
225 | ||
226 | ||
6fc6879b JM |
227 | static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, |
228 | void *sock_ctx) | |
229 | { | |
230 | struct hostapd_data *hapd = eloop_ctx; | |
231 | char buf[256]; | |
232 | int res; | |
233 | struct sockaddr_un from; | |
234 | socklen_t fromlen = sizeof(from); | |
235 | char *reply; | |
236 | const int reply_size = 4096; | |
237 | int reply_len; | |
238 | ||
239 | res = recvfrom(sock, buf, sizeof(buf) - 1, 0, | |
240 | (struct sockaddr *) &from, &fromlen); | |
241 | if (res < 0) { | |
242 | perror("recvfrom(ctrl_iface)"); | |
243 | return; | |
244 | } | |
245 | buf[res] = '\0'; | |
246 | wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface", (u8 *) buf, res); | |
247 | ||
248 | reply = os_malloc(reply_size); | |
249 | if (reply == NULL) { | |
250 | sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, | |
251 | fromlen); | |
252 | return; | |
253 | } | |
254 | ||
255 | os_memcpy(reply, "OK\n", 3); | |
256 | reply_len = 3; | |
257 | ||
258 | if (os_strcmp(buf, "PING") == 0) { | |
259 | os_memcpy(reply, "PONG\n", 5); | |
260 | reply_len = 5; | |
261 | } else if (os_strcmp(buf, "MIB") == 0) { | |
262 | reply_len = ieee802_11_get_mib(hapd, reply, reply_size); | |
263 | if (reply_len >= 0) { | |
264 | res = wpa_get_mib(hapd->wpa_auth, reply + reply_len, | |
265 | reply_size - reply_len); | |
266 | if (res < 0) | |
267 | reply_len = -1; | |
268 | else | |
269 | reply_len += res; | |
270 | } | |
271 | if (reply_len >= 0) { | |
272 | res = ieee802_1x_get_mib(hapd, reply + reply_len, | |
273 | reply_size - reply_len); | |
274 | if (res < 0) | |
275 | reply_len = -1; | |
276 | else | |
277 | reply_len += res; | |
278 | } | |
74784010 | 279 | #ifndef CONFIG_NO_RADIUS |
6fc6879b JM |
280 | if (reply_len >= 0) { |
281 | res = radius_client_get_mib(hapd->radius, | |
282 | reply + reply_len, | |
283 | reply_size - reply_len); | |
284 | if (res < 0) | |
285 | reply_len = -1; | |
286 | else | |
287 | reply_len += res; | |
288 | } | |
74784010 | 289 | #endif /* CONFIG_NO_RADIUS */ |
6fc6879b JM |
290 | } else if (os_strcmp(buf, "STA-FIRST") == 0) { |
291 | reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, | |
292 | reply_size); | |
293 | } else if (os_strncmp(buf, "STA ", 4) == 0) { | |
294 | reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply, | |
295 | reply_size); | |
296 | } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { | |
297 | reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, | |
298 | reply_size); | |
299 | } else if (os_strcmp(buf, "ATTACH") == 0) { | |
300 | if (hostapd_ctrl_iface_attach(hapd, &from, fromlen)) | |
301 | reply_len = -1; | |
302 | } else if (os_strcmp(buf, "DETACH") == 0) { | |
303 | if (hostapd_ctrl_iface_detach(hapd, &from, fromlen)) | |
304 | reply_len = -1; | |
305 | } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { | |
306 | if (hostapd_ctrl_iface_level(hapd, &from, fromlen, | |
307 | buf + 6)) | |
308 | reply_len = -1; | |
309 | } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { | |
310 | if (hostapd_ctrl_iface_new_sta(hapd, buf + 8)) | |
311 | reply_len = -1; | |
88b4b424 | 312 | #ifdef CONFIG_IEEE80211W |
fe6bdb77 | 313 | #ifdef NEED_AP_MLME |
88b4b424 JM |
314 | } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) { |
315 | if (hostapd_ctrl_iface_sa_query(hapd, buf + 9)) | |
316 | reply_len = -1; | |
fe6bdb77 | 317 | #endif /* NEED_AP_MLME */ |
88b4b424 | 318 | #endif /* CONFIG_IEEE80211W */ |
ad08c363 JM |
319 | #ifdef CONFIG_WPS |
320 | } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { | |
321 | if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) | |
322 | reply_len = -1; | |
323 | } else if (os_strcmp(buf, "WPS_PBC") == 0) { | |
324 | if (hostapd_wps_button_pushed(hapd)) | |
325 | reply_len = -1; | |
116f7bb0 | 326 | #ifdef CONFIG_WPS_OOB |
46bdb83a MH |
327 | } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) { |
328 | if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8)) | |
329 | reply_len = -1; | |
116f7bb0 | 330 | #endif /* CONFIG_WPS_OOB */ |
ad08c363 | 331 | #endif /* CONFIG_WPS */ |
6fc6879b JM |
332 | } else { |
333 | os_memcpy(reply, "UNKNOWN COMMAND\n", 16); | |
334 | reply_len = 16; | |
335 | } | |
336 | ||
337 | if (reply_len < 0) { | |
338 | os_memcpy(reply, "FAIL\n", 5); | |
339 | reply_len = 5; | |
340 | } | |
341 | sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); | |
342 | os_free(reply); | |
343 | } | |
344 | ||
345 | ||
346 | static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) | |
347 | { | |
348 | char *buf; | |
349 | size_t len; | |
350 | ||
351 | if (hapd->conf->ctrl_interface == NULL) | |
352 | return NULL; | |
353 | ||
354 | len = os_strlen(hapd->conf->ctrl_interface) + | |
355 | os_strlen(hapd->conf->iface) + 2; | |
356 | buf = os_malloc(len); | |
357 | if (buf == NULL) | |
358 | return NULL; | |
359 | ||
360 | os_snprintf(buf, len, "%s/%s", | |
361 | hapd->conf->ctrl_interface, hapd->conf->iface); | |
362 | buf[len - 1] = '\0'; | |
363 | return buf; | |
364 | } | |
365 | ||
366 | ||
42d16805 JM |
367 | static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, |
368 | const char *txt, size_t len) | |
369 | { | |
370 | struct hostapd_data *hapd = ctx; | |
371 | if (hapd == NULL) | |
372 | return; | |
373 | hostapd_ctrl_iface_send(hapd, level, txt, len); | |
374 | } | |
375 | ||
376 | ||
6fc6879b JM |
377 | int hostapd_ctrl_iface_init(struct hostapd_data *hapd) |
378 | { | |
379 | struct sockaddr_un addr; | |
380 | int s = -1; | |
381 | char *fname = NULL; | |
382 | ||
383 | hapd->ctrl_sock = -1; | |
384 | ||
385 | if (hapd->conf->ctrl_interface == NULL) | |
386 | return 0; | |
387 | ||
388 | if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { | |
389 | if (errno == EEXIST) { | |
390 | wpa_printf(MSG_DEBUG, "Using existing control " | |
391 | "interface directory."); | |
392 | } else { | |
393 | perror("mkdir[ctrl_interface]"); | |
394 | goto fail; | |
395 | } | |
396 | } | |
397 | ||
398 | if (hapd->conf->ctrl_interface_gid_set && | |
399 | chown(hapd->conf->ctrl_interface, 0, | |
400 | hapd->conf->ctrl_interface_gid) < 0) { | |
401 | perror("chown[ctrl_interface]"); | |
402 | return -1; | |
403 | } | |
404 | ||
405 | if (os_strlen(hapd->conf->ctrl_interface) + 1 + | |
406 | os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) | |
407 | goto fail; | |
408 | ||
409 | s = socket(PF_UNIX, SOCK_DGRAM, 0); | |
410 | if (s < 0) { | |
411 | perror("socket(PF_UNIX)"); | |
412 | goto fail; | |
413 | } | |
414 | ||
415 | os_memset(&addr, 0, sizeof(addr)); | |
75864b7f JM |
416 | #ifdef __FreeBSD__ |
417 | addr.sun_len = sizeof(addr); | |
418 | #endif /* __FreeBSD__ */ | |
6fc6879b JM |
419 | addr.sun_family = AF_UNIX; |
420 | fname = hostapd_ctrl_iface_path(hapd); | |
421 | if (fname == NULL) | |
422 | goto fail; | |
423 | os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); | |
424 | if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
617d1555 JM |
425 | wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", |
426 | strerror(errno)); | |
427 | if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
428 | wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" | |
429 | " allow connections - assuming it was left" | |
430 | "over from forced program termination"); | |
431 | if (unlink(fname) < 0) { | |
432 | perror("unlink[ctrl_iface]"); | |
433 | wpa_printf(MSG_ERROR, "Could not unlink " | |
434 | "existing ctrl_iface socket '%s'", | |
435 | fname); | |
436 | goto fail; | |
437 | } | |
438 | if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < | |
439 | 0) { | |
440 | perror("bind(PF_UNIX)"); | |
441 | goto fail; | |
442 | } | |
443 | wpa_printf(MSG_DEBUG, "Successfully replaced leftover " | |
444 | "ctrl_iface socket '%s'", fname); | |
445 | } else { | |
446 | wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " | |
447 | "be in use - cannot override it"); | |
448 | wpa_printf(MSG_INFO, "Delete '%s' manually if it is " | |
449 | "not used anymore", fname); | |
450 | os_free(fname); | |
451 | fname = NULL; | |
452 | goto fail; | |
453 | } | |
6fc6879b JM |
454 | } |
455 | ||
456 | if (hapd->conf->ctrl_interface_gid_set && | |
457 | chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { | |
458 | perror("chown[ctrl_interface/ifname]"); | |
459 | goto fail; | |
460 | } | |
461 | ||
462 | if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { | |
463 | perror("chmod[ctrl_interface/ifname]"); | |
464 | goto fail; | |
465 | } | |
466 | os_free(fname); | |
467 | ||
468 | hapd->ctrl_sock = s; | |
469 | eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, | |
470 | NULL); | |
4f760fcc | 471 | hapd->msg_ctx = hapd; |
42d16805 | 472 | wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); |
6fc6879b JM |
473 | |
474 | return 0; | |
475 | ||
476 | fail: | |
477 | if (s >= 0) | |
478 | close(s); | |
479 | if (fname) { | |
480 | unlink(fname); | |
481 | os_free(fname); | |
482 | } | |
483 | return -1; | |
484 | } | |
485 | ||
486 | ||
487 | void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) | |
488 | { | |
489 | struct wpa_ctrl_dst *dst, *prev; | |
490 | ||
491 | if (hapd->ctrl_sock > -1) { | |
492 | char *fname; | |
493 | eloop_unregister_read_sock(hapd->ctrl_sock); | |
494 | close(hapd->ctrl_sock); | |
495 | hapd->ctrl_sock = -1; | |
496 | fname = hostapd_ctrl_iface_path(hapd); | |
497 | if (fname) | |
498 | unlink(fname); | |
499 | os_free(fname); | |
500 | ||
501 | if (hapd->conf->ctrl_interface && | |
502 | rmdir(hapd->conf->ctrl_interface) < 0) { | |
503 | if (errno == ENOTEMPTY) { | |
504 | wpa_printf(MSG_DEBUG, "Control interface " | |
505 | "directory not empty - leaving it " | |
506 | "behind"); | |
507 | } else { | |
508 | perror("rmdir[ctrl_interface]"); | |
509 | } | |
510 | } | |
511 | } | |
512 | ||
513 | dst = hapd->ctrl_dst; | |
514 | while (dst) { | |
515 | prev = dst; | |
516 | dst = dst->next; | |
517 | os_free(prev); | |
518 | } | |
519 | } | |
520 | ||
521 | ||
42d16805 JM |
522 | static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, |
523 | const char *buf, size_t len) | |
6fc6879b JM |
524 | { |
525 | struct wpa_ctrl_dst *dst, *next; | |
526 | struct msghdr msg; | |
527 | int idx; | |
528 | struct iovec io[2]; | |
529 | char levelstr[10]; | |
530 | ||
531 | dst = hapd->ctrl_dst; | |
532 | if (hapd->ctrl_sock < 0 || dst == NULL) | |
533 | return; | |
534 | ||
535 | os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); | |
536 | io[0].iov_base = levelstr; | |
537 | io[0].iov_len = os_strlen(levelstr); | |
42d16805 | 538 | io[1].iov_base = (char *) buf; |
6fc6879b JM |
539 | io[1].iov_len = len; |
540 | os_memset(&msg, 0, sizeof(msg)); | |
541 | msg.msg_iov = io; | |
542 | msg.msg_iovlen = 2; | |
543 | ||
544 | idx = 0; | |
545 | while (dst) { | |
546 | next = dst->next; | |
547 | if (level >= dst->debug_level) { | |
548 | wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", | |
75864b7f JM |
549 | (u8 *) dst->addr.sun_path, dst->addrlen - |
550 | offsetof(struct sockaddr_un, sun_path)); | |
6fc6879b JM |
551 | msg.msg_name = &dst->addr; |
552 | msg.msg_namelen = dst->addrlen; | |
553 | if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) { | |
c5aaa015 JM |
554 | int _errno = errno; |
555 | wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " | |
556 | "%d - %s", | |
557 | idx, errno, strerror(errno)); | |
6fc6879b | 558 | dst->errors++; |
c5aaa015 | 559 | if (dst->errors > 10 || _errno == ENOENT) { |
6fc6879b JM |
560 | hostapd_ctrl_iface_detach( |
561 | hapd, &dst->addr, | |
562 | dst->addrlen); | |
563 | } | |
564 | } else | |
565 | dst->errors = 0; | |
566 | } | |
567 | idx++; | |
568 | dst = next; | |
569 | } | |
570 | } | |
571 | ||
572 | #endif /* CONFIG_NATIVE_WINDOWS */ |