2 * WPA Supplicant / UDP socket -based control interface
3 * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
20 #include "eapol_supp/eapol_supp_sm.h"
21 #include "wpa_supplicant_i.h"
22 #include "ctrl_iface.h"
23 #include "common/wpa_ctrl.h"
28 /* Per-interface ctrl_iface */
31 * struct wpa_ctrl_dst - Internal data structure of control interface monitors
33 * This structure is used to store information about registered control
34 * interface monitors into struct wpa_supplicant. This data is private to
35 * ctrl_iface_udp.c and should not be touched directly from other files.
38 struct wpa_ctrl_dst
*next
;
39 struct sockaddr_in addr
;
46 struct ctrl_iface_priv
{
47 struct wpa_supplicant
*wpa_s
;
49 struct wpa_ctrl_dst
*ctrl_dst
;
50 u8 cookie
[COOKIE_LEN
];
54 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv
*priv
,
55 int level
, const char *buf
,
59 static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv
*priv
,
60 struct sockaddr_in
*from
,
63 struct wpa_ctrl_dst
*dst
;
65 dst
= os_zalloc(sizeof(*dst
));
68 os_memcpy(&dst
->addr
, from
, sizeof(struct sockaddr_in
));
69 dst
->addrlen
= fromlen
;
70 dst
->debug_level
= MSG_INFO
;
71 dst
->next
= priv
->ctrl_dst
;
73 wpa_printf(MSG_DEBUG
, "CTRL_IFACE monitor attached %s:%d",
74 inet_ntoa(from
->sin_addr
), ntohs(from
->sin_port
));
79 static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv
*priv
,
80 struct sockaddr_in
*from
,
83 struct wpa_ctrl_dst
*dst
, *prev
= NULL
;
87 if (from
->sin_addr
.s_addr
== dst
->addr
.sin_addr
.s_addr
&&
88 from
->sin_port
== dst
->addr
.sin_port
) {
90 priv
->ctrl_dst
= dst
->next
;
92 prev
->next
= dst
->next
;
94 wpa_printf(MSG_DEBUG
, "CTRL_IFACE monitor detached "
95 "%s:%d", inet_ntoa(from
->sin_addr
),
96 ntohs(from
->sin_port
));
106 static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv
*priv
,
107 struct sockaddr_in
*from
,
111 struct wpa_ctrl_dst
*dst
;
113 wpa_printf(MSG_DEBUG
, "CTRL_IFACE LEVEL %s", level
);
115 dst
= priv
->ctrl_dst
;
117 if (from
->sin_addr
.s_addr
== dst
->addr
.sin_addr
.s_addr
&&
118 from
->sin_port
== dst
->addr
.sin_port
) {
119 wpa_printf(MSG_DEBUG
, "CTRL_IFACE changed monitor "
120 "level %s:%d", inet_ntoa(from
->sin_addr
),
121 ntohs(from
->sin_port
));
122 dst
->debug_level
= atoi(level
);
133 wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv
*priv
,
137 reply
= os_malloc(7 + 2 * COOKIE_LEN
+ 1);
143 os_memcpy(reply
, "COOKIE=", 7);
144 wpa_snprintf_hex(reply
+ 7, 2 * COOKIE_LEN
+ 1,
145 priv
->cookie
, COOKIE_LEN
);
147 *reply_len
= 7 + 2 * COOKIE_LEN
;
152 static void wpa_supplicant_ctrl_iface_receive(int sock
, void *eloop_ctx
,
155 struct wpa_supplicant
*wpa_s
= eloop_ctx
;
156 struct ctrl_iface_priv
*priv
= sock_ctx
;
159 struct sockaddr_in from
;
160 socklen_t fromlen
= sizeof(from
);
162 size_t reply_len
= 0;
163 int new_attached
= 0;
164 u8 cookie
[COOKIE_LEN
];
166 res
= recvfrom(sock
, buf
, sizeof(buf
) - 1, 0,
167 (struct sockaddr
*) &from
, &fromlen
);
169 perror("recvfrom(ctrl_iface)");
172 if (from
.sin_addr
.s_addr
!= htonl((127 << 24) | 1)) {
174 * The OS networking stack is expected to drop this kind of
175 * frames since the socket is bound to only localhost address.
176 * Just in case, drop the frame if it is coming from any other
179 wpa_printf(MSG_DEBUG
, "CTRL: Drop packet from unexpected "
180 "source %s", inet_ntoa(from
.sin_addr
));
185 if (os_strcmp(buf
, "GET_COOKIE") == 0) {
186 reply
= wpa_supplicant_ctrl_iface_get_cookie(priv
, &reply_len
);
191 * Require that the client includes a prefix with the 'cookie' value
192 * fetched with GET_COOKIE command. This is used to verify that the
193 * client has access to a bidirectional link over UDP in order to
194 * avoid attacks using forged localhost IP address even if the OS does
195 * not block such frames from remote destinations.
197 if (os_strncmp(buf
, "COOKIE=", 7) != 0) {
198 wpa_printf(MSG_DEBUG
, "CTLR: No cookie in the request - "
203 if (hexstr2bin(buf
+ 7, cookie
, COOKIE_LEN
) < 0) {
204 wpa_printf(MSG_DEBUG
, "CTLR: Invalid cookie format in the "
205 "request - drop request");
209 if (os_memcmp(cookie
, priv
->cookie
, COOKIE_LEN
) != 0) {
210 wpa_printf(MSG_DEBUG
, "CTLR: Invalid cookie in the request - "
215 pos
= buf
+ 7 + 2 * COOKIE_LEN
;
219 if (os_strcmp(pos
, "ATTACH") == 0) {
220 if (wpa_supplicant_ctrl_iface_attach(priv
, &from
, fromlen
))
226 } else if (os_strcmp(pos
, "DETACH") == 0) {
227 if (wpa_supplicant_ctrl_iface_detach(priv
, &from
, fromlen
))
231 } else if (os_strncmp(pos
, "LEVEL ", 6) == 0) {
232 if (wpa_supplicant_ctrl_iface_level(priv
, &from
, fromlen
,
238 reply
= wpa_supplicant_ctrl_iface_process(wpa_s
, pos
,
244 sendto(sock
, reply
, reply_len
, 0, (struct sockaddr
*) &from
,
247 } else if (reply_len
== 1) {
248 sendto(sock
, "FAIL\n", 5, 0, (struct sockaddr
*) &from
,
250 } else if (reply_len
== 2) {
251 sendto(sock
, "OK\n", 3, 0, (struct sockaddr
*) &from
,
256 eapol_sm_notify_ctrl_attached(wpa_s
->eapol
);
260 static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx
, int level
,
261 const char *txt
, size_t len
)
263 struct wpa_supplicant
*wpa_s
= ctx
;
264 if (wpa_s
== NULL
|| wpa_s
->ctrl_iface
== NULL
)
266 wpa_supplicant_ctrl_iface_send(wpa_s
->ctrl_iface
, level
, txt
, len
);
270 struct ctrl_iface_priv
*
271 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant
*wpa_s
)
273 struct ctrl_iface_priv
*priv
;
274 struct sockaddr_in addr
;
276 priv
= os_zalloc(sizeof(*priv
));
281 os_get_random(priv
->cookie
, COOKIE_LEN
);
283 if (wpa_s
->conf
->ctrl_interface
== NULL
)
286 priv
->sock
= socket(PF_INET
, SOCK_DGRAM
, 0);
287 if (priv
->sock
< 0) {
288 perror("socket(PF_INET)");
292 os_memset(&addr
, 0, sizeof(addr
));
293 addr
.sin_family
= AF_INET
;
294 addr
.sin_addr
.s_addr
= htonl((127 << 24) | 1);
295 addr
.sin_port
= htons(WPA_CTRL_IFACE_PORT
);
296 if (bind(priv
->sock
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
297 perror("bind(AF_INET)");
301 eloop_register_read_sock(priv
->sock
, wpa_supplicant_ctrl_iface_receive
,
303 wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb
);
315 void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv
*priv
)
317 struct wpa_ctrl_dst
*dst
, *prev
;
319 if (priv
->sock
> -1) {
320 eloop_unregister_read_sock(priv
->sock
);
321 if (priv
->ctrl_dst
) {
323 * Wait a second before closing the control socket if
324 * there are any attached monitors in order to allow
325 * them to receive any pending messages.
327 wpa_printf(MSG_DEBUG
, "CTRL_IFACE wait for attached "
328 "monitors to receive messages");
335 dst
= priv
->ctrl_dst
;
345 static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv
*priv
,
346 int level
, const char *buf
,
349 struct wpa_ctrl_dst
*dst
, *next
;
355 dst
= priv
->ctrl_dst
;
356 if (priv
->sock
< 0 || dst
== NULL
)
359 os_snprintf(levelstr
, sizeof(levelstr
), "<%d>", level
);
361 llen
= os_strlen(levelstr
);
362 sbuf
= os_malloc(llen
+ len
);
366 os_memcpy(sbuf
, levelstr
, llen
);
367 os_memcpy(sbuf
+ llen
, buf
, len
);
372 if (level
>= dst
->debug_level
) {
373 wpa_printf(MSG_DEBUG
, "CTRL_IFACE monitor send %s:%d",
374 inet_ntoa(dst
->addr
.sin_addr
),
375 ntohs(dst
->addr
.sin_port
));
376 if (sendto(priv
->sock
, sbuf
, llen
+ len
, 0,
377 (struct sockaddr
*) &dst
->addr
,
378 sizeof(dst
->addr
)) < 0) {
379 perror("sendto(CTRL_IFACE monitor)");
381 if (dst
->errors
> 10) {
382 wpa_supplicant_ctrl_iface_detach(
396 void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv
*priv
)
398 wpa_printf(MSG_DEBUG
, "CTRL_IFACE - %s - wait for monitor",
399 priv
->wpa_s
->ifname
);
400 eloop_wait_for_read_sock(priv
->sock
);
404 /* Global ctrl_iface */
406 struct ctrl_iface_global_priv
{
408 u8 cookie
[COOKIE_LEN
];
413 wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv
*priv
,
417 reply
= os_malloc(7 + 2 * COOKIE_LEN
+ 1);
423 os_memcpy(reply
, "COOKIE=", 7);
424 wpa_snprintf_hex(reply
+ 7, 2 * COOKIE_LEN
+ 1,
425 priv
->cookie
, COOKIE_LEN
);
427 *reply_len
= 7 + 2 * COOKIE_LEN
;
432 static void wpa_supplicant_global_ctrl_iface_receive(int sock
, void *eloop_ctx
,
435 struct wpa_global
*global
= eloop_ctx
;
436 struct ctrl_iface_global_priv
*priv
= sock_ctx
;
439 struct sockaddr_in from
;
440 socklen_t fromlen
= sizeof(from
);
443 u8 cookie
[COOKIE_LEN
];
445 res
= recvfrom(sock
, buf
, sizeof(buf
) - 1, 0,
446 (struct sockaddr
*) &from
, &fromlen
);
448 perror("recvfrom(ctrl_iface)");
451 if (from
.sin_addr
.s_addr
!= htonl((127 << 24) | 1)) {
453 * The OS networking stack is expected to drop this kind of
454 * frames since the socket is bound to only localhost address.
455 * Just in case, drop the frame if it is coming from any other
458 wpa_printf(MSG_DEBUG
, "CTRL: Drop packet from unexpected "
459 "source %s", inet_ntoa(from
.sin_addr
));
464 if (os_strcmp(buf
, "GET_COOKIE") == 0) {
465 reply
= wpa_supplicant_global_get_cookie(priv
, &reply_len
);
469 if (os_strncmp(buf
, "COOKIE=", 7) != 0) {
470 wpa_printf(MSG_DEBUG
, "CTLR: No cookie in the request - "
475 if (hexstr2bin(buf
+ 7, cookie
, COOKIE_LEN
) < 0) {
476 wpa_printf(MSG_DEBUG
, "CTLR: Invalid cookie format in the "
477 "request - drop request");
481 if (os_memcmp(cookie
, priv
->cookie
, COOKIE_LEN
) != 0) {
482 wpa_printf(MSG_DEBUG
, "CTLR: Invalid cookie in the request - "
487 pos
= buf
+ 7 + 2 * COOKIE_LEN
;
491 reply
= wpa_supplicant_global_ctrl_iface_process(global
, pos
,
496 sendto(sock
, reply
, reply_len
, 0, (struct sockaddr
*) &from
,
499 } else if (reply_len
) {
500 sendto(sock
, "FAIL\n", 5, 0, (struct sockaddr
*) &from
,
506 struct ctrl_iface_global_priv
*
507 wpa_supplicant_global_ctrl_iface_init(struct wpa_global
*global
)
509 struct ctrl_iface_global_priv
*priv
;
510 struct sockaddr_in addr
;
512 priv
= os_zalloc(sizeof(*priv
));
516 os_get_random(priv
->cookie
, COOKIE_LEN
);
518 if (global
->params
.ctrl_interface
== NULL
)
521 wpa_printf(MSG_DEBUG
, "Global control interface '%s'",
522 global
->params
.ctrl_interface
);
524 priv
->sock
= socket(PF_INET
, SOCK_DGRAM
, 0);
525 if (priv
->sock
< 0) {
526 perror("socket(PF_INET)");
530 os_memset(&addr
, 0, sizeof(addr
));
531 addr
.sin_family
= AF_INET
;
532 addr
.sin_addr
.s_addr
= htonl((127 << 24) | 1);
533 addr
.sin_port
= htons(WPA_GLOBAL_CTRL_IFACE_PORT
);
534 if (bind(priv
->sock
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0) {
535 perror("bind(AF_INET)");
539 eloop_register_read_sock(priv
->sock
,
540 wpa_supplicant_global_ctrl_iface_receive
,
554 wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv
*priv
)
556 if (priv
->sock
>= 0) {
557 eloop_unregister_read_sock(priv
->sock
);