]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * WPA Supplicant / UDP socket -based control interface | |
3 | * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
6fc6879b JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
12 | #include "eloop.h" | |
13 | #include "config.h" | |
14 | #include "eapol_supp/eapol_supp_sm.h" | |
15 | #include "wpa_supplicant_i.h" | |
16 | #include "ctrl_iface.h" | |
90973fb2 | 17 | #include "common/wpa_ctrl.h" |
6fc6879b JM |
18 | |
19 | ||
20 | #define COOKIE_LEN 8 | |
21 | ||
22 | /* Per-interface ctrl_iface */ | |
23 | ||
24 | /** | |
25 | * struct wpa_ctrl_dst - Internal data structure of control interface monitors | |
26 | * | |
27 | * This structure is used to store information about registered control | |
28 | * interface monitors into struct wpa_supplicant. This data is private to | |
29 | * ctrl_iface_udp.c and should not be touched directly from other files. | |
30 | */ | |
31 | struct wpa_ctrl_dst { | |
32 | struct wpa_ctrl_dst *next; | |
4db216fc JD |
33 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
34 | struct sockaddr_in6 addr; | |
35 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 36 | struct sockaddr_in addr; |
4db216fc | 37 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
38 | socklen_t addrlen; |
39 | int debug_level; | |
40 | int errors; | |
41 | }; | |
42 | ||
43 | ||
44 | struct ctrl_iface_priv { | |
45 | struct wpa_supplicant *wpa_s; | |
46 | int sock; | |
47 | struct wpa_ctrl_dst *ctrl_dst; | |
48 | u8 cookie[COOKIE_LEN]; | |
49 | }; | |
50 | ||
51 | ||
52 | static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, | |
53 | int level, const char *buf, | |
54 | size_t len); | |
55 | ||
56 | ||
57 | static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, | |
4db216fc JD |
58 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
59 | struct sockaddr_in6 *from, | |
60 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 61 | struct sockaddr_in *from, |
4db216fc | 62 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
63 | socklen_t fromlen) |
64 | { | |
65 | struct wpa_ctrl_dst *dst; | |
4db216fc JD |
66 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
67 | char addr[INET6_ADDRSTRLEN]; | |
68 | #endif /* CONFIG_UDP_IPV6 */ | |
6fc6879b JM |
69 | |
70 | dst = os_zalloc(sizeof(*dst)); | |
71 | if (dst == NULL) | |
72 | return -1; | |
4db216fc | 73 | os_memcpy(&dst->addr, from, sizeof(*from)); |
6fc6879b JM |
74 | dst->addrlen = fromlen; |
75 | dst->debug_level = MSG_INFO; | |
76 | dst->next = priv->ctrl_dst; | |
77 | priv->ctrl_dst = dst; | |
4db216fc JD |
78 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
79 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", | |
80 | inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)), | |
81 | ntohs(from->sin6_port)); | |
82 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
83 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", |
84 | inet_ntoa(from->sin_addr), ntohs(from->sin_port)); | |
4db216fc | 85 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
86 | return 0; |
87 | } | |
88 | ||
89 | ||
90 | static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, | |
4db216fc JD |
91 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
92 | struct sockaddr_in6 *from, | |
93 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 94 | struct sockaddr_in *from, |
4db216fc | 95 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
96 | socklen_t fromlen) |
97 | { | |
98 | struct wpa_ctrl_dst *dst, *prev = NULL; | |
4db216fc JD |
99 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
100 | char addr[INET6_ADDRSTRLEN]; | |
101 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
102 | |
103 | dst = priv->ctrl_dst; | |
104 | while (dst) { | |
4db216fc JD |
105 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
106 | if (from->sin6_port == dst->addr.sin6_port && | |
107 | !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr, | |
108 | sizeof(from->sin6_addr))) { | |
109 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d", | |
110 | inet_ntop(AF_INET6, &from->sin6_addr, addr, | |
111 | sizeof(*from)), | |
112 | ntohs(from->sin6_port)); | |
113 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
114 | if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && |
115 | from->sin_port == dst->addr.sin_port) { | |
a235aca3 JM |
116 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " |
117 | "%s:%d", inet_ntoa(from->sin_addr), | |
118 | ntohs(from->sin_port)); | |
4db216fc | 119 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
120 | if (prev == NULL) |
121 | priv->ctrl_dst = dst->next; | |
122 | else | |
123 | prev->next = dst->next; | |
124 | os_free(dst); | |
6fc6879b JM |
125 | return 0; |
126 | } | |
127 | prev = dst; | |
128 | dst = dst->next; | |
129 | } | |
130 | return -1; | |
131 | } | |
132 | ||
133 | ||
134 | static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, | |
4db216fc JD |
135 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
136 | struct sockaddr_in6 *from, | |
137 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 138 | struct sockaddr_in *from, |
4db216fc | 139 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
140 | socklen_t fromlen, |
141 | char *level) | |
142 | { | |
143 | struct wpa_ctrl_dst *dst; | |
4db216fc JD |
144 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
145 | char addr[INET6_ADDRSTRLEN]; | |
146 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
147 | |
148 | wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); | |
149 | ||
150 | dst = priv->ctrl_dst; | |
151 | while (dst) { | |
4db216fc JD |
152 | #if CONFIG_CTRL_IFACE_UDP_IPV6 |
153 | if (from->sin6_port == dst->addr.sin6_port && | |
154 | !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr, | |
155 | sizeof(from->sin6_addr))) { | |
156 | wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d", | |
157 | inet_ntop(AF_INET6, &from->sin6_addr, addr, | |
158 | sizeof(*from)), | |
159 | ntohs(from->sin6_port)); | |
160 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
161 | if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && |
162 | from->sin_port == dst->addr.sin_port) { | |
163 | wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor " | |
164 | "level %s:%d", inet_ntoa(from->sin_addr), | |
165 | ntohs(from->sin_port)); | |
4db216fc | 166 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
167 | dst->debug_level = atoi(level); |
168 | return 0; | |
169 | } | |
170 | dst = dst->next; | |
171 | } | |
172 | ||
173 | return -1; | |
174 | } | |
175 | ||
176 | ||
177 | static char * | |
178 | wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv, | |
179 | size_t *reply_len) | |
180 | { | |
181 | char *reply; | |
182 | reply = os_malloc(7 + 2 * COOKIE_LEN + 1); | |
183 | if (reply == NULL) { | |
184 | *reply_len = 1; | |
185 | return NULL; | |
186 | } | |
187 | ||
188 | os_memcpy(reply, "COOKIE=", 7); | |
189 | wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, | |
190 | priv->cookie, COOKIE_LEN); | |
191 | ||
192 | *reply_len = 7 + 2 * COOKIE_LEN; | |
193 | return reply; | |
194 | } | |
195 | ||
196 | ||
197 | static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, | |
198 | void *sock_ctx) | |
199 | { | |
200 | struct wpa_supplicant *wpa_s = eloop_ctx; | |
201 | struct ctrl_iface_priv *priv = sock_ctx; | |
202 | char buf[256], *pos; | |
203 | int res; | |
4db216fc JD |
204 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
205 | struct sockaddr_in6 from; | |
206 | #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE | |
207 | char addr[INET6_ADDRSTRLEN]; | |
208 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
209 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 210 | struct sockaddr_in from; |
4db216fc | 211 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
212 | socklen_t fromlen = sizeof(from); |
213 | char *reply = NULL; | |
214 | size_t reply_len = 0; | |
215 | int new_attached = 0; | |
216 | u8 cookie[COOKIE_LEN]; | |
217 | ||
218 | res = recvfrom(sock, buf, sizeof(buf) - 1, 0, | |
219 | (struct sockaddr *) &from, &fromlen); | |
220 | if (res < 0) { | |
a193231d JM |
221 | wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", |
222 | strerror(errno)); | |
6fc6879b JM |
223 | return; |
224 | } | |
afadf423 JD |
225 | |
226 | #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE | |
4db216fc JD |
227 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
228 | inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from)); | |
229 | if (os_strcmp(addr, "::1")) { | |
230 | wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s", | |
231 | addr); | |
232 | } | |
233 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
234 | if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { |
235 | /* | |
236 | * The OS networking stack is expected to drop this kind of | |
237 | * frames since the socket is bound to only localhost address. | |
238 | * Just in case, drop the frame if it is coming from any other | |
239 | * address. | |
240 | */ | |
241 | wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " | |
242 | "source %s", inet_ntoa(from.sin_addr)); | |
243 | return; | |
244 | } | |
4db216fc | 245 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
afadf423 JD |
246 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
247 | ||
6fc6879b JM |
248 | buf[res] = '\0'; |
249 | ||
250 | if (os_strcmp(buf, "GET_COOKIE") == 0) { | |
251 | reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len); | |
252 | goto done; | |
253 | } | |
254 | ||
255 | /* | |
256 | * Require that the client includes a prefix with the 'cookie' value | |
257 | * fetched with GET_COOKIE command. This is used to verify that the | |
258 | * client has access to a bidirectional link over UDP in order to | |
259 | * avoid attacks using forged localhost IP address even if the OS does | |
260 | * not block such frames from remote destinations. | |
261 | */ | |
262 | if (os_strncmp(buf, "COOKIE=", 7) != 0) { | |
263 | wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " | |
264 | "drop request"); | |
265 | return; | |
266 | } | |
267 | ||
268 | if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { | |
269 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " | |
270 | "request - drop request"); | |
271 | return; | |
272 | } | |
273 | ||
274 | if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { | |
275 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " | |
276 | "drop request"); | |
277 | return; | |
278 | } | |
279 | ||
280 | pos = buf + 7 + 2 * COOKIE_LEN; | |
281 | while (*pos == ' ') | |
282 | pos++; | |
283 | ||
284 | if (os_strcmp(pos, "ATTACH") == 0) { | |
285 | if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) | |
286 | reply_len = 1; | |
287 | else { | |
288 | new_attached = 1; | |
289 | reply_len = 2; | |
290 | } | |
291 | } else if (os_strcmp(pos, "DETACH") == 0) { | |
292 | if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) | |
293 | reply_len = 1; | |
294 | else | |
295 | reply_len = 2; | |
296 | } else if (os_strncmp(pos, "LEVEL ", 6) == 0) { | |
297 | if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, | |
298 | pos + 6)) | |
299 | reply_len = 1; | |
300 | else | |
301 | reply_len = 2; | |
302 | } else { | |
303 | reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos, | |
304 | &reply_len); | |
305 | } | |
306 | ||
307 | done: | |
308 | if (reply) { | |
309 | sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, | |
310 | fromlen); | |
311 | os_free(reply); | |
312 | } else if (reply_len == 1) { | |
313 | sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, | |
314 | fromlen); | |
315 | } else if (reply_len == 2) { | |
316 | sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, | |
317 | fromlen); | |
318 | } | |
319 | ||
320 | if (new_attached) | |
321 | eapol_sm_notify_ctrl_attached(wpa_s->eapol); | |
322 | } | |
323 | ||
324 | ||
47bfe49c | 325 | static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, |
6fc6879b JM |
326 | const char *txt, size_t len) |
327 | { | |
328 | struct wpa_supplicant *wpa_s = ctx; | |
329 | if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) | |
330 | return; | |
331 | wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); | |
332 | } | |
333 | ||
334 | ||
335 | struct ctrl_iface_priv * | |
336 | wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) | |
337 | { | |
338 | struct ctrl_iface_priv *priv; | |
afadf423 | 339 | int port = WPA_CTRL_IFACE_PORT; |
4db216fc JD |
340 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
341 | struct sockaddr_in6 addr; | |
342 | int domain = PF_INET6; | |
343 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
344 | struct sockaddr_in addr; | |
345 | int domain = PF_INET; | |
346 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
347 | |
348 | priv = os_zalloc(sizeof(*priv)); | |
349 | if (priv == NULL) | |
350 | return NULL; | |
351 | priv->wpa_s = wpa_s; | |
352 | priv->sock = -1; | |
353 | os_get_random(priv->cookie, COOKIE_LEN); | |
354 | ||
355 | if (wpa_s->conf->ctrl_interface == NULL) | |
356 | return priv; | |
357 | ||
4db216fc | 358 | priv->sock = socket(domain, SOCK_DGRAM, 0); |
6fc6879b | 359 | if (priv->sock < 0) { |
a193231d | 360 | wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); |
6fc6879b JM |
361 | goto fail; |
362 | } | |
363 | ||
364 | os_memset(&addr, 0, sizeof(addr)); | |
4db216fc JD |
365 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
366 | addr.sin6_family = AF_INET6; | |
367 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE | |
368 | addr.sin6_addr = in6addr_any; | |
369 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
370 | inet_pton(AF_INET6, "::1", &addr.sin6_addr); | |
371 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
372 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 373 | addr.sin_family = AF_INET; |
afadf423 JD |
374 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
375 | addr.sin_addr.s_addr = INADDR_ANY; | |
376 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 377 | addr.sin_addr.s_addr = htonl((127 << 24) | 1); |
afadf423 | 378 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
4db216fc | 379 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
afadf423 | 380 | try_again: |
4db216fc JD |
381 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
382 | addr.sin6_port = htons(port); | |
383 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
afadf423 | 384 | addr.sin_port = htons(port); |
4db216fc | 385 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b | 386 | if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
afadf423 JD |
387 | port--; |
388 | if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) | |
389 | goto try_again; | |
a193231d | 390 | wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); |
6fc6879b JM |
391 | goto fail; |
392 | } | |
393 | ||
afadf423 JD |
394 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
395 | wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); | |
396 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
397 | ||
6fc6879b JM |
398 | eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, |
399 | wpa_s, priv); | |
400 | wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); | |
401 | ||
402 | return priv; | |
403 | ||
404 | fail: | |
405 | if (priv->sock >= 0) | |
406 | close(priv->sock); | |
407 | os_free(priv); | |
408 | return NULL; | |
409 | } | |
410 | ||
411 | ||
412 | void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) | |
413 | { | |
414 | struct wpa_ctrl_dst *dst, *prev; | |
415 | ||
416 | if (priv->sock > -1) { | |
417 | eloop_unregister_read_sock(priv->sock); | |
418 | if (priv->ctrl_dst) { | |
419 | /* | |
e0591c3c | 420 | * Wait before closing the control socket if |
6fc6879b JM |
421 | * there are any attached monitors in order to allow |
422 | * them to receive any pending messages. | |
423 | */ | |
424 | wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " | |
425 | "monitors to receive messages"); | |
e0591c3c | 426 | os_sleep(0, 100000); |
6fc6879b JM |
427 | } |
428 | close(priv->sock); | |
429 | priv->sock = -1; | |
430 | } | |
431 | ||
432 | dst = priv->ctrl_dst; | |
433 | while (dst) { | |
434 | prev = dst; | |
435 | dst = dst->next; | |
436 | os_free(prev); | |
437 | } | |
438 | os_free(priv); | |
439 | } | |
440 | ||
441 | ||
442 | static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, | |
443 | int level, const char *buf, | |
444 | size_t len) | |
445 | { | |
446 | struct wpa_ctrl_dst *dst, *next; | |
447 | char levelstr[10]; | |
448 | int idx; | |
449 | char *sbuf; | |
450 | int llen; | |
4db216fc JD |
451 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
452 | char addr[INET6_ADDRSTRLEN]; | |
453 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
454 | |
455 | dst = priv->ctrl_dst; | |
456 | if (priv->sock < 0 || dst == NULL) | |
457 | return; | |
458 | ||
459 | os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); | |
460 | ||
461 | llen = os_strlen(levelstr); | |
462 | sbuf = os_malloc(llen + len); | |
463 | if (sbuf == NULL) | |
464 | return; | |
465 | ||
466 | os_memcpy(sbuf, levelstr, llen); | |
467 | os_memcpy(sbuf + llen, buf, len); | |
468 | ||
469 | idx = 0; | |
470 | while (dst) { | |
471 | next = dst->next; | |
472 | if (level >= dst->debug_level) { | |
4db216fc JD |
473 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
474 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", | |
475 | inet_ntop(AF_INET6, &dst->addr.sin6_addr, | |
476 | addr, sizeof(dst->addr)), | |
477 | ntohs(dst->addr.sin6_port)); | |
478 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
479 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", |
480 | inet_ntoa(dst->addr.sin_addr), | |
481 | ntohs(dst->addr.sin_port)); | |
4db216fc | 482 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
483 | if (sendto(priv->sock, sbuf, llen + len, 0, |
484 | (struct sockaddr *) &dst->addr, | |
485 | sizeof(dst->addr)) < 0) { | |
a193231d JM |
486 | wpa_printf(MSG_ERROR, |
487 | "sendto(CTRL_IFACE monitor): %s", | |
488 | strerror(errno)); | |
6fc6879b JM |
489 | dst->errors++; |
490 | if (dst->errors > 10) { | |
491 | wpa_supplicant_ctrl_iface_detach( | |
492 | priv, &dst->addr, | |
493 | dst->addrlen); | |
494 | } | |
495 | } else | |
496 | dst->errors = 0; | |
497 | } | |
498 | idx++; | |
499 | dst = next; | |
500 | } | |
501 | os_free(sbuf); | |
502 | } | |
503 | ||
504 | ||
505 | void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) | |
506 | { | |
507 | wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", | |
508 | priv->wpa_s->ifname); | |
509 | eloop_wait_for_read_sock(priv->sock); | |
510 | } | |
511 | ||
512 | ||
513 | /* Global ctrl_iface */ | |
514 | ||
515 | struct ctrl_iface_global_priv { | |
516 | int sock; | |
517 | u8 cookie[COOKIE_LEN]; | |
518 | }; | |
519 | ||
520 | ||
521 | static char * | |
522 | wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, | |
523 | size_t *reply_len) | |
524 | { | |
525 | char *reply; | |
526 | reply = os_malloc(7 + 2 * COOKIE_LEN + 1); | |
527 | if (reply == NULL) { | |
528 | *reply_len = 1; | |
529 | return NULL; | |
530 | } | |
531 | ||
532 | os_memcpy(reply, "COOKIE=", 7); | |
533 | wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, | |
534 | priv->cookie, COOKIE_LEN); | |
535 | ||
536 | *reply_len = 7 + 2 * COOKIE_LEN; | |
537 | return reply; | |
538 | } | |
539 | ||
540 | ||
541 | static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, | |
542 | void *sock_ctx) | |
543 | { | |
544 | struct wpa_global *global = eloop_ctx; | |
545 | struct ctrl_iface_global_priv *priv = sock_ctx; | |
546 | char buf[256], *pos; | |
547 | int res; | |
548 | struct sockaddr_in from; | |
549 | socklen_t fromlen = sizeof(from); | |
550 | char *reply; | |
551 | size_t reply_len; | |
552 | u8 cookie[COOKIE_LEN]; | |
553 | ||
554 | res = recvfrom(sock, buf, sizeof(buf) - 1, 0, | |
555 | (struct sockaddr *) &from, &fromlen); | |
556 | if (res < 0) { | |
a193231d JM |
557 | wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", |
558 | strerror(errno)); | |
6fc6879b JM |
559 | return; |
560 | } | |
afadf423 JD |
561 | |
562 | #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE | |
6fc6879b JM |
563 | if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { |
564 | /* | |
565 | * The OS networking stack is expected to drop this kind of | |
566 | * frames since the socket is bound to only localhost address. | |
567 | * Just in case, drop the frame if it is coming from any other | |
568 | * address. | |
569 | */ | |
570 | wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " | |
571 | "source %s", inet_ntoa(from.sin_addr)); | |
572 | return; | |
573 | } | |
afadf423 JD |
574 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
575 | ||
6fc6879b JM |
576 | buf[res] = '\0'; |
577 | ||
578 | if (os_strcmp(buf, "GET_COOKIE") == 0) { | |
579 | reply = wpa_supplicant_global_get_cookie(priv, &reply_len); | |
580 | goto done; | |
581 | } | |
582 | ||
583 | if (os_strncmp(buf, "COOKIE=", 7) != 0) { | |
584 | wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " | |
585 | "drop request"); | |
586 | return; | |
587 | } | |
588 | ||
589 | if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { | |
590 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " | |
591 | "request - drop request"); | |
592 | return; | |
593 | } | |
594 | ||
595 | if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { | |
596 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " | |
597 | "drop request"); | |
598 | return; | |
599 | } | |
600 | ||
601 | pos = buf + 7 + 2 * COOKIE_LEN; | |
602 | while (*pos == ' ') | |
603 | pos++; | |
604 | ||
605 | reply = wpa_supplicant_global_ctrl_iface_process(global, pos, | |
606 | &reply_len); | |
607 | ||
608 | done: | |
609 | if (reply) { | |
610 | sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, | |
611 | fromlen); | |
612 | os_free(reply); | |
613 | } else if (reply_len) { | |
614 | sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, | |
615 | fromlen); | |
616 | } | |
617 | } | |
618 | ||
619 | ||
620 | struct ctrl_iface_global_priv * | |
621 | wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) | |
622 | { | |
623 | struct ctrl_iface_global_priv *priv; | |
624 | struct sockaddr_in addr; | |
afadf423 | 625 | int port = WPA_GLOBAL_CTRL_IFACE_PORT; |
6fc6879b JM |
626 | |
627 | priv = os_zalloc(sizeof(*priv)); | |
628 | if (priv == NULL) | |
629 | return NULL; | |
630 | priv->sock = -1; | |
631 | os_get_random(priv->cookie, COOKIE_LEN); | |
632 | ||
633 | if (global->params.ctrl_interface == NULL) | |
634 | return priv; | |
635 | ||
636 | wpa_printf(MSG_DEBUG, "Global control interface '%s'", | |
637 | global->params.ctrl_interface); | |
638 | ||
639 | priv->sock = socket(PF_INET, SOCK_DGRAM, 0); | |
640 | if (priv->sock < 0) { | |
a193231d | 641 | wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); |
6fc6879b JM |
642 | goto fail; |
643 | } | |
644 | ||
645 | os_memset(&addr, 0, sizeof(addr)); | |
646 | addr.sin_family = AF_INET; | |
afadf423 JD |
647 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
648 | addr.sin_addr.s_addr = INADDR_ANY; | |
649 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 650 | addr.sin_addr.s_addr = htonl((127 << 24) | 1); |
afadf423 JD |
651 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
652 | try_again: | |
653 | addr.sin_port = htons(port); | |
6fc6879b | 654 | if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
afadf423 JD |
655 | port++; |
656 | if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < | |
657 | WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) | |
658 | goto try_again; | |
a193231d | 659 | wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); |
6fc6879b JM |
660 | goto fail; |
661 | } | |
662 | ||
afadf423 JD |
663 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
664 | wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port); | |
665 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
666 | ||
6fc6879b JM |
667 | eloop_register_read_sock(priv->sock, |
668 | wpa_supplicant_global_ctrl_iface_receive, | |
669 | global, priv); | |
670 | ||
671 | return priv; | |
672 | ||
673 | fail: | |
674 | if (priv->sock >= 0) | |
675 | close(priv->sock); | |
676 | os_free(priv); | |
677 | return NULL; | |
678 | } | |
679 | ||
680 | ||
681 | void | |
682 | wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) | |
683 | { | |
684 | if (priv->sock >= 0) { | |
685 | eloop_unregister_read_sock(priv->sock); | |
686 | close(priv->sock); | |
687 | } | |
688 | os_free(priv); | |
689 | } |