hostapd: Global control interface notifications
This commit implements hostapd global control interface notifications infrastructure. hostapd global control interface clients issue ATTACH/DETACH commands to register and deregister with hostapd correspondingly - the same way as for any other hostapd/wpa_supplicant control interface. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
cb05808c46
commit
ee1e3f57b5
6 changed files with 156 additions and 12 deletions
|
@ -58,6 +58,7 @@ struct wpa_ctrl_dst {
|
||||||
|
|
||||||
|
|
||||||
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
|
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
|
||||||
|
enum wpa_msg_type type,
|
||||||
const char *buf, size_t len);
|
const char *buf, size_t len);
|
||||||
|
|
||||||
|
|
||||||
|
@ -2229,7 +2230,7 @@ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
|
||||||
struct hostapd_data *hapd = ctx;
|
struct hostapd_data *hapd = ctx;
|
||||||
if (hapd == NULL)
|
if (hapd == NULL)
|
||||||
return;
|
return;
|
||||||
hostapd_ctrl_iface_send(hapd, level, txt, len);
|
hostapd_ctrl_iface_send(hapd, level, type, txt, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2452,6 +2453,58 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
|
||||||
|
struct sockaddr_un *from,
|
||||||
|
socklen_t fromlen)
|
||||||
|
{
|
||||||
|
struct wpa_ctrl_dst *dst;
|
||||||
|
|
||||||
|
dst = os_zalloc(sizeof(*dst));
|
||||||
|
if (dst == NULL)
|
||||||
|
return -1;
|
||||||
|
os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
|
||||||
|
dst->addrlen = fromlen;
|
||||||
|
dst->debug_level = MSG_INFO;
|
||||||
|
dst->next = interfaces->global_ctrl_dst;
|
||||||
|
interfaces->global_ctrl_dst = dst;
|
||||||
|
wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)",
|
||||||
|
from->sun_path,
|
||||||
|
fromlen - offsetof(struct sockaddr_un, sun_path));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
|
||||||
|
struct sockaddr_un *from,
|
||||||
|
socklen_t fromlen)
|
||||||
|
{
|
||||||
|
struct wpa_ctrl_dst *dst, *prev = NULL;
|
||||||
|
|
||||||
|
dst = interfaces->global_ctrl_dst;
|
||||||
|
while (dst) {
|
||||||
|
if (fromlen == dst->addrlen &&
|
||||||
|
os_memcmp(from->sun_path, dst->addr.sun_path,
|
||||||
|
fromlen - offsetof(struct sockaddr_un, sun_path))
|
||||||
|
== 0) {
|
||||||
|
wpa_hexdump(MSG_DEBUG,
|
||||||
|
"CTRL_IFACE monitor detached (global)",
|
||||||
|
from->sun_path,
|
||||||
|
fromlen -
|
||||||
|
offsetof(struct sockaddr_un, sun_path));
|
||||||
|
if (prev == NULL)
|
||||||
|
interfaces->global_ctrl_dst = dst->next;
|
||||||
|
else
|
||||||
|
prev->next = dst->next;
|
||||||
|
os_free(dst);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
prev = dst;
|
||||||
|
dst = dst->next;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
|
static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_WPS_TESTING
|
#ifdef CONFIG_WPS_TESTING
|
||||||
|
@ -2470,8 +2523,9 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
|
||||||
int res;
|
int res;
|
||||||
struct sockaddr_un from;
|
struct sockaddr_un from;
|
||||||
socklen_t fromlen = sizeof(from);
|
socklen_t fromlen = sizeof(from);
|
||||||
char reply[24];
|
char *reply;
|
||||||
int reply_len;
|
int reply_len;
|
||||||
|
const int reply_size = 4096;
|
||||||
|
|
||||||
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
|
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
|
||||||
(struct sockaddr *) &from, &fromlen);
|
(struct sockaddr *) &from, &fromlen);
|
||||||
|
@ -2483,6 +2537,16 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
|
||||||
buf[res] = '\0';
|
buf[res] = '\0';
|
||||||
wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
|
wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
|
||||||
|
|
||||||
|
reply = os_malloc(reply_size);
|
||||||
|
if (reply == NULL) {
|
||||||
|
if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
|
||||||
|
fromlen) < 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
os_memcpy(reply, "OK\n", 3);
|
os_memcpy(reply, "OK\n", 3);
|
||||||
reply_len = 3;
|
reply_len = 3;
|
||||||
|
|
||||||
|
@ -2500,6 +2564,14 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
|
||||||
} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
|
} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
|
||||||
if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
|
if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
|
||||||
reply_len = -1;
|
reply_len = -1;
|
||||||
|
} else if (os_strcmp(buf, "ATTACH") == 0) {
|
||||||
|
if (hostapd_global_ctrl_iface_attach(interfaces, &from,
|
||||||
|
fromlen))
|
||||||
|
reply_len = -1;
|
||||||
|
} else if (os_strcmp(buf, "DETACH") == 0) {
|
||||||
|
if (hostapd_global_ctrl_iface_detach(interfaces, &from,
|
||||||
|
fromlen))
|
||||||
|
reply_len = -1;
|
||||||
#ifdef CONFIG_MODULE_TESTS
|
#ifdef CONFIG_MODULE_TESTS
|
||||||
} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
|
} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
|
||||||
int hapd_module_tests(void);
|
int hapd_module_tests(void);
|
||||||
|
@ -2522,6 +2594,7 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
|
||||||
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
|
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
|
os_free(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2659,6 +2732,7 @@ fail:
|
||||||
void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
|
void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
|
||||||
{
|
{
|
||||||
char *fname = NULL;
|
char *fname = NULL;
|
||||||
|
struct wpa_ctrl_dst *dst, *prev;
|
||||||
|
|
||||||
if (interfaces->global_ctrl_sock > -1) {
|
if (interfaces->global_ctrl_sock > -1) {
|
||||||
eloop_unregister_read_sock(interfaces->global_ctrl_sock);
|
eloop_unregister_read_sock(interfaces->global_ctrl_sock);
|
||||||
|
@ -2683,13 +2757,23 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os_free(interfaces->global_iface_path);
|
}
|
||||||
interfaces->global_iface_path = NULL;
|
|
||||||
|
os_free(interfaces->global_iface_path);
|
||||||
|
interfaces->global_iface_path = NULL;
|
||||||
|
|
||||||
|
dst = interfaces->global_ctrl_dst;
|
||||||
|
interfaces->global_ctrl_dst = NULL;
|
||||||
|
while (dst) {
|
||||||
|
prev = dst;
|
||||||
|
dst = dst->next;
|
||||||
|
os_free(prev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
|
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
|
||||||
|
enum wpa_msg_type type,
|
||||||
const char *buf, size_t len)
|
const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct wpa_ctrl_dst *dst, *next;
|
struct wpa_ctrl_dst *dst, *next;
|
||||||
|
@ -2697,9 +2781,17 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
|
||||||
int idx;
|
int idx;
|
||||||
struct iovec io[2];
|
struct iovec io[2];
|
||||||
char levelstr[10];
|
char levelstr[10];
|
||||||
|
int s;
|
||||||
|
|
||||||
dst = hapd->ctrl_dst;
|
if (type != WPA_MSG_ONLY_GLOBAL) {
|
||||||
if (hapd->ctrl_sock < 0 || dst == NULL)
|
s = hapd->ctrl_sock;
|
||||||
|
dst = hapd->ctrl_dst;
|
||||||
|
} else {
|
||||||
|
s = hapd->iface->interfaces->global_ctrl_sock;
|
||||||
|
dst = hapd->iface->interfaces->global_ctrl_dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s < 0 || dst == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
|
os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
|
||||||
|
@ -2720,16 +2812,22 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
|
||||||
offsetof(struct sockaddr_un, sun_path));
|
offsetof(struct sockaddr_un, sun_path));
|
||||||
msg.msg_name = &dst->addr;
|
msg.msg_name = &dst->addr;
|
||||||
msg.msg_namelen = dst->addrlen;
|
msg.msg_namelen = dst->addrlen;
|
||||||
if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
|
if (sendmsg(s, &msg, 0) < 0) {
|
||||||
int _errno = errno;
|
int _errno = errno;
|
||||||
wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
|
wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
|
||||||
"%d - %s",
|
"%d - %s",
|
||||||
idx, errno, strerror(errno));
|
idx, errno, strerror(errno));
|
||||||
dst->errors++;
|
dst->errors++;
|
||||||
if (dst->errors > 10 || _errno == ENOENT) {
|
if (dst->errors > 10 || _errno == ENOENT) {
|
||||||
hostapd_ctrl_iface_detach(
|
if (type != WPA_MSG_ONLY_GLOBAL)
|
||||||
hapd, &dst->addr,
|
hostapd_ctrl_iface_detach(
|
||||||
dst->addrlen);
|
hapd, &dst->addr,
|
||||||
|
dst->addrlen);
|
||||||
|
else
|
||||||
|
hostapd_global_ctrl_iface_detach(
|
||||||
|
hapd->iface->interfaces,
|
||||||
|
&dst->addr,
|
||||||
|
dst->addrlen);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
dst->errors = 0;
|
dst->errors = 0;
|
||||||
|
|
|
@ -561,6 +561,7 @@ int main(int argc, char *argv[])
|
||||||
interfaces.global_iface_path = NULL;
|
interfaces.global_iface_path = NULL;
|
||||||
interfaces.global_iface_name = NULL;
|
interfaces.global_iface_name = NULL;
|
||||||
interfaces.global_ctrl_sock = -1;
|
interfaces.global_ctrl_sock = -1;
|
||||||
|
interfaces.global_ctrl_dst = NULL;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
|
c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
|
||||||
|
|
|
@ -41,6 +41,7 @@ struct hapd_interfaces {
|
||||||
|
|
||||||
size_t count;
|
size_t count;
|
||||||
int global_ctrl_sock;
|
int global_ctrl_sock;
|
||||||
|
struct wpa_ctrl_dst *global_ctrl_dst;
|
||||||
char *global_iface_path;
|
char *global_iface_path;
|
||||||
char *global_iface_name;
|
char *global_iface_name;
|
||||||
#ifndef CONFIG_NATIVE_WINDOWS
|
#ifndef CONFIG_NATIVE_WINDOWS
|
||||||
|
|
|
@ -749,6 +749,33 @@ void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
|
||||||
bin_clear_free(buf, buflen);
|
bin_clear_free(buf, buflen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char *buf;
|
||||||
|
int buflen;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
buf = os_malloc(buflen);
|
||||||
|
if (buf == NULL) {
|
||||||
|
wpa_printf(MSG_ERROR, "%s: Failed to allocate message buffer",
|
||||||
|
__func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
va_start(ap, fmt);
|
||||||
|
len = vsnprintf(buf, buflen, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
wpa_printf(level, "%s", buf);
|
||||||
|
if (wpa_msg_cb)
|
||||||
|
wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
|
||||||
|
os_free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_NO_WPA_MSG */
|
#endif /* CONFIG_NO_WPA_MSG */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
|
||||||
#define wpa_msg_global(args...) do { } while (0)
|
#define wpa_msg_global(args...) do { } while (0)
|
||||||
#define wpa_msg_global_ctrl(args...) do { } while (0)
|
#define wpa_msg_global_ctrl(args...) do { } while (0)
|
||||||
#define wpa_msg_no_global(args...) do { } while (0)
|
#define wpa_msg_no_global(args...) do { } while (0)
|
||||||
|
#define wpa_msg_global_only(args...) do { } while (0)
|
||||||
#define wpa_msg_register_cb(f) do { } while (0)
|
#define wpa_msg_register_cb(f) do { } while (0)
|
||||||
#define wpa_msg_register_ifname_cb(f) do { } while (0)
|
#define wpa_msg_register_ifname_cb(f) do { } while (0)
|
||||||
#else /* CONFIG_NO_WPA_MSG */
|
#else /* CONFIG_NO_WPA_MSG */
|
||||||
|
@ -243,10 +244,25 @@ PRINTF_FORMAT(3, 4);
|
||||||
void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
|
void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
|
||||||
PRINTF_FORMAT(3, 4);
|
PRINTF_FORMAT(3, 4);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* wpa_msg_global_only - Conditional printf for ctrl_iface monitors
|
||||||
|
* @ctx: Pointer to context data; this is the ctx variable registered
|
||||||
|
* with struct wpa_driver_ops::init()
|
||||||
|
* @level: priority level (MSG_*) of the message
|
||||||
|
* @fmt: printf format string, followed by optional arguments
|
||||||
|
*
|
||||||
|
* This function is used to print conditional debugging and error messages.
|
||||||
|
* This function is like wpa_msg_global(), but it sends the output only as a
|
||||||
|
* global event.
|
||||||
|
*/
|
||||||
|
void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
|
||||||
|
PRINTF_FORMAT(3, 4);
|
||||||
|
|
||||||
enum wpa_msg_type {
|
enum wpa_msg_type {
|
||||||
WPA_MSG_PER_INTERFACE,
|
WPA_MSG_PER_INTERFACE,
|
||||||
WPA_MSG_GLOBAL,
|
WPA_MSG_GLOBAL,
|
||||||
WPA_MSG_NO_GLOBAL,
|
WPA_MSG_NO_GLOBAL,
|
||||||
|
WPA_MSG_ONLY_GLOBAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
|
typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
|
||||||
|
|
|
@ -316,13 +316,14 @@ static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
|
||||||
if (!dl_list_empty(&priv->ctrl_dst)) {
|
if (!dl_list_empty(&priv->ctrl_dst)) {
|
||||||
wpa_supplicant_ctrl_iface_send(
|
wpa_supplicant_ctrl_iface_send(
|
||||||
wpa_s,
|
wpa_s,
|
||||||
type == WPA_MSG_GLOBAL ? NULL : wpa_s->ifname,
|
type != WPA_MSG_PER_INTERFACE ?
|
||||||
|
NULL : wpa_s->ifname,
|
||||||
priv->sock, &priv->ctrl_dst, level, txt, len,
|
priv->sock, &priv->ctrl_dst, level, txt, len,
|
||||||
NULL, priv);
|
NULL, priv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wpa_s->ctrl_iface == NULL)
|
if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL)
|
||||||
return;
|
return;
|
||||||
wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
|
wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
|
||||||
&wpa_s->ctrl_iface->ctrl_dst,
|
&wpa_s->ctrl_iface->ctrl_dst,
|
||||||
|
|
Loading…
Reference in a new issue