]>
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) { | |
221 | perror("recvfrom(ctrl_iface)"); | |
222 | return; | |
223 | } | |
afadf423 JD |
224 | |
225 | #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE | |
4db216fc JD |
226 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
227 | inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from)); | |
228 | if (os_strcmp(addr, "::1")) { | |
229 | wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s", | |
230 | addr); | |
231 | } | |
232 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
233 | if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { |
234 | /* | |
235 | * The OS networking stack is expected to drop this kind of | |
236 | * frames since the socket is bound to only localhost address. | |
237 | * Just in case, drop the frame if it is coming from any other | |
238 | * address. | |
239 | */ | |
240 | wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " | |
241 | "source %s", inet_ntoa(from.sin_addr)); | |
242 | return; | |
243 | } | |
4db216fc | 244 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
afadf423 JD |
245 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
246 | ||
6fc6879b JM |
247 | buf[res] = '\0'; |
248 | ||
249 | if (os_strcmp(buf, "GET_COOKIE") == 0) { | |
250 | reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len); | |
251 | goto done; | |
252 | } | |
253 | ||
254 | /* | |
255 | * Require that the client includes a prefix with the 'cookie' value | |
256 | * fetched with GET_COOKIE command. This is used to verify that the | |
257 | * client has access to a bidirectional link over UDP in order to | |
258 | * avoid attacks using forged localhost IP address even if the OS does | |
259 | * not block such frames from remote destinations. | |
260 | */ | |
261 | if (os_strncmp(buf, "COOKIE=", 7) != 0) { | |
262 | wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " | |
263 | "drop request"); | |
264 | return; | |
265 | } | |
266 | ||
267 | if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { | |
268 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " | |
269 | "request - drop request"); | |
270 | return; | |
271 | } | |
272 | ||
273 | if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { | |
274 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " | |
275 | "drop request"); | |
276 | return; | |
277 | } | |
278 | ||
279 | pos = buf + 7 + 2 * COOKIE_LEN; | |
280 | while (*pos == ' ') | |
281 | pos++; | |
282 | ||
283 | if (os_strcmp(pos, "ATTACH") == 0) { | |
284 | if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) | |
285 | reply_len = 1; | |
286 | else { | |
287 | new_attached = 1; | |
288 | reply_len = 2; | |
289 | } | |
290 | } else if (os_strcmp(pos, "DETACH") == 0) { | |
291 | if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) | |
292 | reply_len = 1; | |
293 | else | |
294 | reply_len = 2; | |
295 | } else if (os_strncmp(pos, "LEVEL ", 6) == 0) { | |
296 | if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, | |
297 | pos + 6)) | |
298 | reply_len = 1; | |
299 | else | |
300 | reply_len = 2; | |
301 | } else { | |
302 | reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos, | |
303 | &reply_len); | |
304 | } | |
305 | ||
306 | done: | |
307 | if (reply) { | |
308 | sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, | |
309 | fromlen); | |
310 | os_free(reply); | |
311 | } else if (reply_len == 1) { | |
312 | sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, | |
313 | fromlen); | |
314 | } else if (reply_len == 2) { | |
315 | sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, | |
316 | fromlen); | |
317 | } | |
318 | ||
319 | if (new_attached) | |
320 | eapol_sm_notify_ctrl_attached(wpa_s->eapol); | |
321 | } | |
322 | ||
323 | ||
47bfe49c | 324 | static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, |
6fc6879b JM |
325 | const char *txt, size_t len) |
326 | { | |
327 | struct wpa_supplicant *wpa_s = ctx; | |
328 | if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) | |
329 | return; | |
330 | wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); | |
331 | } | |
332 | ||
333 | ||
334 | struct ctrl_iface_priv * | |
335 | wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) | |
336 | { | |
337 | struct ctrl_iface_priv *priv; | |
afadf423 | 338 | int port = WPA_CTRL_IFACE_PORT; |
4db216fc JD |
339 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
340 | struct sockaddr_in6 addr; | |
341 | int domain = PF_INET6; | |
342 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
343 | struct sockaddr_in addr; | |
344 | int domain = PF_INET; | |
345 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
346 | |
347 | priv = os_zalloc(sizeof(*priv)); | |
348 | if (priv == NULL) | |
349 | return NULL; | |
350 | priv->wpa_s = wpa_s; | |
351 | priv->sock = -1; | |
352 | os_get_random(priv->cookie, COOKIE_LEN); | |
353 | ||
354 | if (wpa_s->conf->ctrl_interface == NULL) | |
355 | return priv; | |
356 | ||
4db216fc | 357 | priv->sock = socket(domain, SOCK_DGRAM, 0); |
6fc6879b JM |
358 | if (priv->sock < 0) { |
359 | perror("socket(PF_INET)"); | |
360 | goto fail; | |
361 | } | |
362 | ||
363 | os_memset(&addr, 0, sizeof(addr)); | |
4db216fc JD |
364 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
365 | addr.sin6_family = AF_INET6; | |
366 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE | |
367 | addr.sin6_addr = in6addr_any; | |
368 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
369 | inet_pton(AF_INET6, "::1", &addr.sin6_addr); | |
370 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
371 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b | 372 | addr.sin_family = AF_INET; |
afadf423 JD |
373 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
374 | addr.sin_addr.s_addr = INADDR_ANY; | |
375 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 376 | addr.sin_addr.s_addr = htonl((127 << 24) | 1); |
afadf423 | 377 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
4db216fc | 378 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
afadf423 | 379 | try_again: |
4db216fc JD |
380 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
381 | addr.sin6_port = htons(port); | |
382 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
afadf423 | 383 | addr.sin_port = htons(port); |
4db216fc | 384 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b | 385 | if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
afadf423 JD |
386 | port--; |
387 | if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) | |
388 | goto try_again; | |
6fc6879b JM |
389 | perror("bind(AF_INET)"); |
390 | goto fail; | |
391 | } | |
392 | ||
afadf423 JD |
393 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
394 | wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); | |
395 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
396 | ||
6fc6879b JM |
397 | eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, |
398 | wpa_s, priv); | |
399 | wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); | |
400 | ||
401 | return priv; | |
402 | ||
403 | fail: | |
404 | if (priv->sock >= 0) | |
405 | close(priv->sock); | |
406 | os_free(priv); | |
407 | return NULL; | |
408 | } | |
409 | ||
410 | ||
411 | void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) | |
412 | { | |
413 | struct wpa_ctrl_dst *dst, *prev; | |
414 | ||
415 | if (priv->sock > -1) { | |
416 | eloop_unregister_read_sock(priv->sock); | |
417 | if (priv->ctrl_dst) { | |
418 | /* | |
e0591c3c | 419 | * Wait before closing the control socket if |
6fc6879b JM |
420 | * there are any attached monitors in order to allow |
421 | * them to receive any pending messages. | |
422 | */ | |
423 | wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " | |
424 | "monitors to receive messages"); | |
e0591c3c | 425 | os_sleep(0, 100000); |
6fc6879b JM |
426 | } |
427 | close(priv->sock); | |
428 | priv->sock = -1; | |
429 | } | |
430 | ||
431 | dst = priv->ctrl_dst; | |
432 | while (dst) { | |
433 | prev = dst; | |
434 | dst = dst->next; | |
435 | os_free(prev); | |
436 | } | |
437 | os_free(priv); | |
438 | } | |
439 | ||
440 | ||
441 | static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, | |
442 | int level, const char *buf, | |
443 | size_t len) | |
444 | { | |
445 | struct wpa_ctrl_dst *dst, *next; | |
446 | char levelstr[10]; | |
447 | int idx; | |
448 | char *sbuf; | |
449 | int llen; | |
4db216fc JD |
450 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
451 | char addr[INET6_ADDRSTRLEN]; | |
452 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
453 | |
454 | dst = priv->ctrl_dst; | |
455 | if (priv->sock < 0 || dst == NULL) | |
456 | return; | |
457 | ||
458 | os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); | |
459 | ||
460 | llen = os_strlen(levelstr); | |
461 | sbuf = os_malloc(llen + len); | |
462 | if (sbuf == NULL) | |
463 | return; | |
464 | ||
465 | os_memcpy(sbuf, levelstr, llen); | |
466 | os_memcpy(sbuf + llen, buf, len); | |
467 | ||
468 | idx = 0; | |
469 | while (dst) { | |
470 | next = dst->next; | |
471 | if (level >= dst->debug_level) { | |
4db216fc JD |
472 | #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 |
473 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", | |
474 | inet_ntop(AF_INET6, &dst->addr.sin6_addr, | |
475 | addr, sizeof(dst->addr)), | |
476 | ntohs(dst->addr.sin6_port)); | |
477 | #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ | |
6fc6879b JM |
478 | wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", |
479 | inet_ntoa(dst->addr.sin_addr), | |
480 | ntohs(dst->addr.sin_port)); | |
4db216fc | 481 | #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ |
6fc6879b JM |
482 | if (sendto(priv->sock, sbuf, llen + len, 0, |
483 | (struct sockaddr *) &dst->addr, | |
484 | sizeof(dst->addr)) < 0) { | |
485 | perror("sendto(CTRL_IFACE monitor)"); | |
486 | dst->errors++; | |
487 | if (dst->errors > 10) { | |
488 | wpa_supplicant_ctrl_iface_detach( | |
489 | priv, &dst->addr, | |
490 | dst->addrlen); | |
491 | } | |
492 | } else | |
493 | dst->errors = 0; | |
494 | } | |
495 | idx++; | |
496 | dst = next; | |
497 | } | |
498 | os_free(sbuf); | |
499 | } | |
500 | ||
501 | ||
502 | void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) | |
503 | { | |
504 | wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor", | |
505 | priv->wpa_s->ifname); | |
506 | eloop_wait_for_read_sock(priv->sock); | |
507 | } | |
508 | ||
509 | ||
510 | /* Global ctrl_iface */ | |
511 | ||
512 | struct ctrl_iface_global_priv { | |
513 | int sock; | |
514 | u8 cookie[COOKIE_LEN]; | |
515 | }; | |
516 | ||
517 | ||
518 | static char * | |
519 | wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv, | |
520 | size_t *reply_len) | |
521 | { | |
522 | char *reply; | |
523 | reply = os_malloc(7 + 2 * COOKIE_LEN + 1); | |
524 | if (reply == NULL) { | |
525 | *reply_len = 1; | |
526 | return NULL; | |
527 | } | |
528 | ||
529 | os_memcpy(reply, "COOKIE=", 7); | |
530 | wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, | |
531 | priv->cookie, COOKIE_LEN); | |
532 | ||
533 | *reply_len = 7 + 2 * COOKIE_LEN; | |
534 | return reply; | |
535 | } | |
536 | ||
537 | ||
538 | static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, | |
539 | void *sock_ctx) | |
540 | { | |
541 | struct wpa_global *global = eloop_ctx; | |
542 | struct ctrl_iface_global_priv *priv = sock_ctx; | |
543 | char buf[256], *pos; | |
544 | int res; | |
545 | struct sockaddr_in from; | |
546 | socklen_t fromlen = sizeof(from); | |
547 | char *reply; | |
548 | size_t reply_len; | |
549 | u8 cookie[COOKIE_LEN]; | |
550 | ||
551 | res = recvfrom(sock, buf, sizeof(buf) - 1, 0, | |
552 | (struct sockaddr *) &from, &fromlen); | |
553 | if (res < 0) { | |
554 | perror("recvfrom(ctrl_iface)"); | |
555 | return; | |
556 | } | |
afadf423 JD |
557 | |
558 | #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE | |
6fc6879b JM |
559 | if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { |
560 | /* | |
561 | * The OS networking stack is expected to drop this kind of | |
562 | * frames since the socket is bound to only localhost address. | |
563 | * Just in case, drop the frame if it is coming from any other | |
564 | * address. | |
565 | */ | |
566 | wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected " | |
567 | "source %s", inet_ntoa(from.sin_addr)); | |
568 | return; | |
569 | } | |
afadf423 JD |
570 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
571 | ||
6fc6879b JM |
572 | buf[res] = '\0'; |
573 | ||
574 | if (os_strcmp(buf, "GET_COOKIE") == 0) { | |
575 | reply = wpa_supplicant_global_get_cookie(priv, &reply_len); | |
576 | goto done; | |
577 | } | |
578 | ||
579 | if (os_strncmp(buf, "COOKIE=", 7) != 0) { | |
580 | wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - " | |
581 | "drop request"); | |
582 | return; | |
583 | } | |
584 | ||
585 | if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) { | |
586 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the " | |
587 | "request - drop request"); | |
588 | return; | |
589 | } | |
590 | ||
591 | if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) { | |
592 | wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - " | |
593 | "drop request"); | |
594 | return; | |
595 | } | |
596 | ||
597 | pos = buf + 7 + 2 * COOKIE_LEN; | |
598 | while (*pos == ' ') | |
599 | pos++; | |
600 | ||
601 | reply = wpa_supplicant_global_ctrl_iface_process(global, pos, | |
602 | &reply_len); | |
603 | ||
604 | done: | |
605 | if (reply) { | |
606 | sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, | |
607 | fromlen); | |
608 | os_free(reply); | |
609 | } else if (reply_len) { | |
610 | sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, | |
611 | fromlen); | |
612 | } | |
613 | } | |
614 | ||
615 | ||
616 | struct ctrl_iface_global_priv * | |
617 | wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) | |
618 | { | |
619 | struct ctrl_iface_global_priv *priv; | |
620 | struct sockaddr_in addr; | |
afadf423 | 621 | int port = WPA_GLOBAL_CTRL_IFACE_PORT; |
6fc6879b JM |
622 | |
623 | priv = os_zalloc(sizeof(*priv)); | |
624 | if (priv == NULL) | |
625 | return NULL; | |
626 | priv->sock = -1; | |
627 | os_get_random(priv->cookie, COOKIE_LEN); | |
628 | ||
629 | if (global->params.ctrl_interface == NULL) | |
630 | return priv; | |
631 | ||
632 | wpa_printf(MSG_DEBUG, "Global control interface '%s'", | |
633 | global->params.ctrl_interface); | |
634 | ||
635 | priv->sock = socket(PF_INET, SOCK_DGRAM, 0); | |
636 | if (priv->sock < 0) { | |
637 | perror("socket(PF_INET)"); | |
638 | goto fail; | |
639 | } | |
640 | ||
641 | os_memset(&addr, 0, sizeof(addr)); | |
642 | addr.sin_family = AF_INET; | |
afadf423 JD |
643 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
644 | addr.sin_addr.s_addr = INADDR_ANY; | |
645 | #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
6fc6879b | 646 | addr.sin_addr.s_addr = htonl((127 << 24) | 1); |
afadf423 JD |
647 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ |
648 | try_again: | |
649 | addr.sin_port = htons(port); | |
6fc6879b | 650 | if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
afadf423 JD |
651 | port++; |
652 | if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < | |
653 | WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) | |
654 | goto try_again; | |
6fc6879b JM |
655 | perror("bind(AF_INET)"); |
656 | goto fail; | |
657 | } | |
658 | ||
afadf423 JD |
659 | #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE |
660 | wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port); | |
661 | #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ | |
662 | ||
6fc6879b JM |
663 | eloop_register_read_sock(priv->sock, |
664 | wpa_supplicant_global_ctrl_iface_receive, | |
665 | global, priv); | |
666 | ||
667 | return priv; | |
668 | ||
669 | fail: | |
670 | if (priv->sock >= 0) | |
671 | close(priv->sock); | |
672 | os_free(priv); | |
673 | return NULL; | |
674 | } | |
675 | ||
676 | ||
677 | void | |
678 | wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) | |
679 | { | |
680 | if (priv->sock >= 0) { | |
681 | eloop_unregister_read_sock(priv->sock); | |
682 | close(priv->sock); | |
683 | } | |
684 | os_free(priv); | |
685 | } |