From: Jouni Malinen Date: Wed, 10 Dec 2025 10:38:23 +0000 (+0200) Subject: proc_coord: Process coordination for hostapd and wpa_supplicant instances X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fa1f975c2936d55160520bc42780abe3756cb86b;p=thirdparty%2Fhostap.git proc_coord: Process coordination for hostapd and wpa_supplicant instances Add a framework for coordinating operations between multiple hostapd and wpa_supplicant processes running on the same CPU. This provides functionality for performing request/response operations and sending event messages between the processes. This could be used, e.g., to coordinate channel selection between multiple hostapd instances operating BSSs on the same radio or between hostapd and wpa_supplicant processes to coordinate channel switching of the AP interface based on backhaul connection switching its operating channel. UNIX domain sockets are used for sending the messages between processes. The new command line argument -z can be used to enable this functionality in hostapd and wpa_supplicant. The directory needs to be created before starting hostapd/wpa_supplicant and the permissions for that directory should be set in a manner that prevents access from untrusted processes. There is no additional access control for this within hostapd/wpa_supplicant. The messages exchanged between the processes are assuming the same source code snapshot and build parameters are used in all participating processes. The encoding of the messages and the performed functions can be modified from one snapshot to another and from one build configuration to another and any kind of mixing of different versions or build configurations is not supported and can result in unexpected behavior. Signed-off-by: Jouni Malinen --- diff --git a/hostapd/Android.mk b/hostapd/Android.mk index 4b1daa30b..8d1355294 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -1137,6 +1137,11 @@ ifdef CONFIG_ANDROID_LOG L_CFLAGS += -DCONFIG_ANDROID_LOG endif +ifdef CONFIG_PROCESS_COORDINATION +L_CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += src/common/proc_coord.c +endif + OBJS_c = hostapd_cli.c OBJS_c += src/common/wpa_ctrl.c OBJS_c += src/utils/os_$(CONFIG_OS).c diff --git a/hostapd/Makefile b/hostapd/Makefile index afd2e1cf5..70029bb0d 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -1311,6 +1311,11 @@ ifdef CONFIG_NO_TKIP CFLAGS += -DCONFIG_NO_TKIP endif +ifdef CONFIG_PROCESS_COORDINATION +CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += ../src/common/proc_coord.o +endif + $(DESTDIR)$(BINDIR)/%: % install -D $(<) $(@) diff --git a/hostapd/main.c b/hostapd/main.c index f8d417fa2..a7f5a3207 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -20,6 +20,7 @@ #include "crypto/tls.h" #include "common/version.h" #include "common/dpp.h" +#include "common/proc_coord.h" #include "drivers/driver.h" #include "eap_server/eap.h" #include "eap_server/tncs.h" @@ -615,6 +616,9 @@ static void usage(void) "\\\n" " [-g ] [-G ]\\\n" " [-i ]\\\n" +#ifdef CONFIG_PROCESS_COORDINATION + " [-z] \\\n" +#endif /* CONFIG_PROCESS_COORDINATION */ " \n" "\n" "options:\n" @@ -640,6 +644,9 @@ static void usage(void) " -S start all the interfaces synchronously\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n" +#ifdef CONFIG_PROCESS_COORDINATION + " -z process coordination directory\n" +#endif /* CONFIG_PROCESS_COORDINATION */ " -q show less debug messages (-qq for even less)\n"); exit(1); @@ -825,6 +832,7 @@ int main(int argc, char *argv[]) #ifdef CONFIG_DPP struct dpp_global_config dpp_conf; #endif /* CONFIG_DPP */ + const char *proc_coord_dir = NULL; if (os_program_init()) return -1; @@ -859,7 +867,7 @@ int main(int argc, char *argv[]) #endif /* CONFIG_DPP */ for (;;) { - c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:qz:"); if (c < 0) break; switch (c) { @@ -935,6 +943,11 @@ int main(int argc, char *argv[]) case 'q': wpa_debug_level++; break; +#ifdef CONFIG_PROCESS_COORDINATION + case 'z': + proc_coord_dir = optarg; + break; +#endif /* CONFIG_PROCESS_COORDINATION */ default: usage(); break; @@ -983,6 +996,14 @@ int main(int argc, char *argv[]) eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, hostapd_periodic, &interfaces, NULL); +#ifdef CONFIG_PROCESS_COORDINATION + if (proc_coord_dir) { + interfaces.pc = proc_coord_init(proc_coord_dir); + if (!interfaces.pc) + goto out; + } +#endif /* CONFIG_PROCESS_COORDINATION */ + if (fst_global_init()) { wpa_printf(MSG_ERROR, "Failed to initialize global FST context"); @@ -1095,6 +1116,10 @@ int main(int argc, char *argv[]) dpp_global_deinit(interfaces.dpp); #endif /* CONFIG_DPP */ +#ifdef CONFIG_PROCESS_COORDINATION + proc_coord_deinit(interfaces.pc); +#endif /* CONFIG_PROCESS_COORDINATION */ + if (interfaces.eloop_initialized) eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); hostapd_global_deinit(pid_file, interfaces.eloop_initialized); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 2956981c7..ec2cb9c02 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -100,6 +100,10 @@ struct hapd_interfaces { int (*mld_ctrl_iface_init)(struct hostapd_mld *mld); void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld); #endif /* CONFIG_IEEE80211BE */ + +#ifdef CONFIG_PROCESS_COORDINATION + struct proc_coord *pc; +#endif /* CONFIG_PROCESS_COORDINATION */ }; enum hostapd_chan_status { diff --git a/src/common/proc_coord.c b/src/common/proc_coord.c new file mode 100644 index 000000000..d8fa82067 --- /dev/null +++ b/src/common/proc_coord.c @@ -0,0 +1,815 @@ +/* + * Coordination of operations between processes + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include +#include +#include + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "proc_coord.h" + + +struct proc_coord_msg_header { + u32 msg_type; /* enum proc_coord_message_types */ + u32 cmd; /* proc_coord_commands */ + u32 seq; +}; + +enum proc_coord_peer_state { + PROC_COORD_PEER_WAITING, + PROC_COORD_PEER_ACTIVE, + PROC_COORD_PEER_TIMED_OUT, +}; + +struct proc_coord_peer { + struct dl_list list; + pid_t pid; + enum proc_coord_peer_state state; + struct os_reltime last_rx; +}; + +struct proc_coord_pending_request { + struct dl_list list; + struct proc_coord_peer *peer; + enum proc_coord_commands cmd; + u32 seq; + proc_coord_response_cb cb; + void *cb_ctx; + struct os_reltime timeout; +}; + +struct proc_coord_handler { + struct dl_list list; + proc_coord_cb cb; + void *cb_ctx; +}; + +struct proc_coord { + pid_t pid; + char *dir; + char *own_sock; + int sock; + u32 next_seq; + struct dl_list handlers; /* struct proc_coord_handler::list */ + struct dl_list peers; /* struct proc_coord_peer::list */ + struct dl_list requests; /* struct proc_coord_pending_request::list */ +}; + + +static void proc_coord_set_req_expire_timer(struct proc_coord *pc); + + +static struct proc_coord_peer * proc_coord_get_peer(struct proc_coord *pc, + pid_t pid) +{ + struct proc_coord_peer *peer; + + dl_list_for_each(peer, &pc->peers, struct proc_coord_peer, list) { + if (peer->pid == pid) + return peer; + } + + return NULL; +} + + +static struct proc_coord_peer * proc_coord_add_peer(struct proc_coord *pc, + pid_t pid) +{ + struct proc_coord_peer *peer; + + peer = os_zalloc(sizeof(*peer)); + if (!peer) + return NULL; + + peer->pid = pid; + peer->state = PROC_COORD_PEER_WAITING; + dl_list_add(&pc->peers, &peer->list); + + return peer; +} + + +static void proc_coord_remove_req(struct proc_coord_pending_request *req) +{ + dl_list_del(&req->list); + if (req->cb) + req->cb(req->cb_ctx, req->peer->pid, NULL); + os_free(req); +} + + +static void proc_coord_remove_peer(struct proc_coord *pc, + struct proc_coord_peer *peer) +{ + struct proc_coord_pending_request *req, *tmp; + + dl_list_for_each_safe(req, tmp, &pc->requests, + struct proc_coord_pending_request, list) { + if (req->peer == peer) + proc_coord_remove_req(req); + } + + dl_list_del(&peer->list); + os_free(peer); +} + + +static void proc_coord_expire_requests(void *eloop_ctx, void *timeout_ctx) +{ + struct proc_coord *pc = eloop_ctx; + struct proc_coord_pending_request *req, *tmp; + struct os_reltime now; + + os_get_reltime(&now); + dl_list_for_each_safe(req, tmp, &pc->requests, + struct proc_coord_pending_request, list) { + if (os_reltime_before(&req->timeout, &now)) { + wpa_printf(MSG_DEBUG, + "proc_coord: Pending request peer=%u cmd=%d seq=%u timed out", + req->peer->pid, req->cmd, req->seq); + proc_coord_remove_req(req); + } + } + + proc_coord_set_req_expire_timer(pc); +} + + +void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb, + void *cb_ctx) +{ + struct proc_coord_pending_request *req, *tmp; + + dl_list_for_each_safe(req, tmp, &pc->requests, + struct proc_coord_pending_request, list) { + if (req->cb == cb && req->cb_ctx == cb_ctx) { + req->cb = NULL; + req->cb_ctx = NULL; + proc_coord_remove_req(req); + } + } + + proc_coord_set_req_expire_timer(pc); +} + + +static void proc_coord_set_req_expire_timer(struct proc_coord *pc) +{ + struct proc_coord_pending_request *req; + struct os_reltime *first = NULL; + + eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL); + dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request, + list) { + if (!first || os_reltime_before(&req->timeout, first)) + first = &req->timeout; + } + + if (first) { + struct os_reltime now, res; + unsigned int ms; + + os_get_reltime(&now); + if (os_reltime_before(first, &now)) { + ms = 0; + } else { + os_reltime_sub(first, &now, &res); + ms = os_reltime_in_ms(&res); + } + eloop_register_timeout(ms / 1000, (ms % 1000) * 1000, + proc_coord_expire_requests, pc, NULL); + } +} + + +static struct proc_coord_pending_request * +proc_coord_get_request(struct proc_coord *pc, struct proc_coord_peer *peer, + enum proc_coord_commands cmd, u32 seq) +{ + struct proc_coord_pending_request *req; + + dl_list_for_each(req, &pc->requests, struct proc_coord_pending_request, + list) { + if (req->peer == peer && req->cmd == cmd && req->seq == seq) + return req; + } + + return NULL; +} + + +static int proc_coord_send_msg(struct proc_coord *pc, + struct proc_coord_peer *peer, + enum proc_coord_message_types msg_type, + enum proc_coord_commands cmd, + u32 seq, const struct wpabuf *msg) +{ + struct proc_coord_msg_header hdr; + ssize_t res; + struct sockaddr_un addr; + struct msghdr mh; + struct iovec io[2]; + + wpa_printf(MSG_DEBUG, + "proc_coord: Send message to %d (msg_type=%u cmd=%u seq=%u)", + peer->pid, msg_type, cmd, seq); + + os_memset(&hdr, 0, sizeof(hdr)); + hdr.msg_type = msg_type; + hdr.cmd = cmd; + hdr.seq = seq; + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%u", + pc->dir, peer->pid); + + io[0].iov_base = &hdr; + io[0].iov_len = sizeof(hdr); + if (msg) { + io[1].iov_base = (void *) wpabuf_head(msg); + io[1].iov_len = wpabuf_len(msg); + } + + os_memset(&mh, 0, sizeof(mh)); + mh.msg_iov = io; + mh.msg_iovlen = msg ? 2 : 1; + mh.msg_name = (void *) &addr; + mh.msg_namelen = sizeof(addr); + + res = sendmsg(pc->sock, &mh, MSG_DONTWAIT); + if (res < 0) { + int err = errno; + + wpa_printf(MSG_INFO, "proc_coord: sendmsg: %s", + strerror(errno)); + if (err == ENOENT || err == ECONNREFUSED) { + wpa_printf(MSG_INFO, + "proc_coord: Remove peer %u due to connection being refused", + peer->pid); + proc_coord_remove_peer(pc, peer); + } + return -1; + } + return 0; +} + + +static void proc_coord_rx_starting(struct proc_coord *pc, pid_t pid) +{ + wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STARTING", pid); +} + + +static void proc_coord_rx_stopping(struct proc_coord *pc, pid_t pid) +{ + struct proc_coord_peer *peer; + + wpa_printf(MSG_DEBUG, "proc_coord: Peer %u STOPPING", pid); + + peer = proc_coord_get_peer(pc, pid); + if (peer) { + wpa_printf(MSG_INFO, + "proc_coord: Remove peer %u due to STOPPING event", + pid); + proc_coord_remove_peer(pc, peer); + } +} + + +static void proc_coord_rx_ping(struct proc_coord *pc, pid_t pid, + enum proc_coord_message_types msg_type, u32 seq) +{ + if (msg_type != PROC_COORD_MSG_REQUEST) + return; + + wpa_printf(MSG_DEBUG, "proc_coord: Reply to peer %u PING", pid); + proc_coord_send_response(pc, pid, PROC_COORD_CMD_PING, seq, NULL); +} + + +static void proc_coord_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct proc_coord *pc = eloop_ctx; + struct sockaddr_un from; + struct wpabuf *msg; + size_t msg_len; + ssize_t res; + struct proc_coord_msg_header hdr; + pid_t pid; + char tmp[20], *pos, *end; + struct proc_coord_peer *peer; + struct msghdr mh; + struct iovec io[2]; + struct proc_coord_handler *handler, *h_tmp; + + res = recv(sock, tmp, 0, MSG_PEEK | MSG_TRUNC); + if (res < 0) { + wpa_printf(MSG_ERROR, "proc_coord: recv: %s", + strerror(errno)); + return; + } + msg_len = res; + if (msg_len < sizeof(hdr)) + return; + msg_len -= sizeof(hdr); + + msg = wpabuf_alloc(msg_len); + if (!msg) + return; + + io[0].iov_base = &hdr; + io[0].iov_len = sizeof(hdr); + io[1].iov_base = wpabuf_mhead(msg); + io[1].iov_len = msg_len; + + os_memset(&mh, 0, sizeof(mh)); + mh.msg_iov = io; + mh.msg_iovlen = 2; + mh.msg_name = (void *) &from; + mh.msg_namelen = sizeof(from); + + res = recvmsg(sock, &mh, MSG_DONTWAIT); + if (res < 0) { + wpa_printf(MSG_ERROR, "proc_coord: recvmsg: %s", + strerror(errno)); + goto out; + } + if ((size_t) res < sizeof(hdr)) + goto out; + + wpa_printf(MSG_DEBUG, "proc_coord: Received message from %s", + from.sun_path); + if ((size_t) res > sizeof(hdr)) { + wpabuf_put(msg, res - sizeof(hdr)); + wpa_hexdump_buf(MSG_MSGDUMP, + "proc_coord: Received message payload", msg); + } + + end = ((char *) &from) + mh.msg_namelen; + + /* Require same directory for client socket */ + if ((size_t) (end - from.sun_path) < os_strlen(pc->dir) || + os_strncmp(pc->dir, from.sun_path, os_strlen(pc->dir)) != 0) + goto out; + + /* Find the peer PID from the socket name */ + pos = end - 1; + while (pos > from.sun_path) { + if (*pos == '/') + break; + pos--; + } + if (pos == from.sun_path) + goto out; + pos++; + os_memcpy(tmp, pos, end - pos); + pid = atoi(tmp); + + wpa_printf(MSG_DEBUG, "proc_coord: pid=%u msg_type=%u cmd=%u seq=%u", + pid, hdr.msg_type, hdr.cmd, hdr.seq); + + peer = proc_coord_get_peer(pc, pid); + + switch (hdr.msg_type) { + case PROC_COORD_MSG_REQUEST: + break; + case PROC_COORD_MSG_RESPONSE: { + struct proc_coord_pending_request *req; + + if (!peer) { + wpa_printf(MSG_DEBUG, + "proc_coord: Discard msg_type=RESPONSE from %u since there is no peer entry for it", + pid); + goto out; + } + + req = proc_coord_get_request(pc, peer, hdr.cmd, hdr.seq); + if (!req) { + wpa_printf(MSG_DEBUG, + "proc_coord: Discard msg_type=RESPONSE from %u since there is no pending request for it", + pid); + goto out; + } + if (req->cb) + req->cb(req->cb_ctx, peer->pid, msg); + dl_list_del(&req->list); + os_free(req); + break; + } + case PROC_COORD_MSG_EVENT: + break; + default: + wpa_printf(MSG_DEBUG, + "proc_coord: Discard unknown msg_type=%u from %u", + hdr.msg_type, pid); + goto out; + } + + if (!peer) { + wpa_printf(MSG_DEBUG, + "proc_coord: Add new peer entry for %d based on received message", + pid); + peer = proc_coord_add_peer(pc, pid); + if (!peer) { + wpa_printf(MSG_ERROR, + "proc_coord: Could not add peer entry for %u", + pid); + goto out; + } + } + + if (peer->state != PROC_COORD_PEER_ACTIVE) { + wpa_printf(MSG_DEBUG, + "proc_coord: Mark peer %d active due to received message", + pid); + peer->state = PROC_COORD_PEER_ACTIVE; + } + + os_get_reltime(&peer->last_rx); + + switch (hdr.cmd) { + case PROC_COORD_CMD_STARTING : + proc_coord_rx_starting(pc, pid); + break; + case PROC_COORD_CMD_STOPPING: + proc_coord_rx_stopping(pc, pid); + break; + case PROC_COORD_CMD_PING: + proc_coord_rx_ping(pc, pid, hdr.msg_type, hdr.seq); + break; + default: + if (hdr.msg_type == PROC_COORD_MSG_RESPONSE) + break; + + dl_list_for_each_safe(handler, h_tmp, &pc->handlers, + struct proc_coord_handler, list) { + if (handler->cb(handler->cb_ctx, pid, hdr.msg_type, + hdr.cmd, hdr.seq, msg)) + break; + } + break; + } + +out: + wpabuf_free(msg); +} + + +static void proc_coord_send_starting(struct proc_coord *pc) +{ + struct proc_coord_peer *peer, *tmp; + int count = 0; + + if (dl_list_empty(&pc->peers)) + return; + + wpa_printf(MSG_DEBUG, "proc_coord: Send STARTING event to all peers"); + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT, + PROC_COORD_CMD_STARTING, 0, NULL) == 0) + count++; + } + wpa_printf(MSG_DEBUG, "proc_coord: STARTING sent to %d peer(s)", count); +} + + +static void proc_coord_send_stopping(struct proc_coord *pc) +{ + if (dl_list_empty(&pc->peers)) + return; + + wpa_printf(MSG_DEBUG, + "proc_coord: Send STOPPING event to all active peers"); + proc_coord_send_event(pc, 0, PROC_COORD_CMD_STOPPING, NULL); +} + + +static void proc_coord_cb_ping(void *ctx, int pid, const struct wpabuf *msg) +{ + struct proc_coord *pc = ctx; + struct proc_coord_peer *peer; + + peer = proc_coord_get_peer(pc, pid); + if (!peer) + return; + if (msg) { + if (peer->state != PROC_COORD_PEER_ACTIVE) { + wpa_printf(MSG_DEBUG, + "proc_coord: Mark peer %d active due to response to PING", + pid); + peer->state = PROC_COORD_PEER_ACTIVE; + } + } else { + if (peer->state != PROC_COORD_PEER_TIMED_OUT) { + wpa_printf(MSG_DEBUG, + "proc_coord: Mark peer %d timed out due to no response to PING", + pid); + peer->state = PROC_COORD_PEER_TIMED_OUT; + } + } +} + + +static void proc_coord_update_peers_from_dir(struct proc_coord *pc) +{ + DIR *dir; + struct dirent *de; + struct proc_coord_peer *peer, *tmp; + + /* Remove peers that do not have a socket file */ + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + char fname[256]; + + os_snprintf(fname, sizeof(fname), "%s/%d", pc->dir, peer->pid); + if (!os_file_exists(fname)) { + wpa_printf(MSG_DEBUG, + "proc_coord: Remove peer %d due to socket file not present", + peer->pid); + proc_coord_remove_peer(pc, peer); + } + } + + /* Add peer entries for all new sockets in the directory */ + dir = opendir(pc->dir); + if (!dir) { + wpa_printf(MSG_ERROR, "proc_coord: opendir: %s", + strerror(errno)); + return; + } + + while ((de = readdir(dir))) { + int pid = atoi(de->d_name); + + if (pid <= 0 || pid == pc->pid) + continue; + + peer = proc_coord_get_peer(pc, pid); + if (peer) + continue; + + wpa_printf(MSG_DEBUG, "proc_coord: Add new peer entry for %u", + pid); + peer = proc_coord_add_peer(pc, pid); + if (!peer) { + wpa_printf(MSG_ERROR, + "proc_coord: Could not add peer entry for %u", + pid); + continue; + } + + wpa_printf(MSG_DEBUG, "proc_coord: Ping new peer %u", pid); + proc_coord_send_request(pc, pid, PROC_COORD_CMD_PING, NULL, + 10000, proc_coord_cb_ping, pc); + } +} + + +static void proc_coord_update_peers(void *eloop_ctx, void *timeout_ctx) +{ + struct proc_coord *pc = eloop_ctx; + struct proc_coord_peer *peer, *tmp; + struct os_reltime now; + + proc_coord_update_peers_from_dir(pc); + + os_get_reltime(&now); + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (!os_reltime_expired(&now, &peer->last_rx, 60)) + continue; + proc_coord_send_request(pc, peer->pid, PROC_COORD_CMD_PING, + NULL, 10000, NULL, NULL); + } + + eloop_register_timeout(10, 0, proc_coord_update_peers, pc, NULL); +} + + +struct proc_coord * proc_coord_init(const char *dir) +{ + struct proc_coord *pc; + struct sockaddr_un addr; + size_t len; + int flags; + + pc = os_zalloc(sizeof(*pc)); + if (!pc) + return NULL; + + dl_list_init(&pc->peers); + dl_list_init(&pc->requests); + dl_list_init(&pc->handlers); + pc->sock = -1; + + pc->dir = os_strdup(dir); + if (!pc->dir) + goto fail; + + len = os_strlen(dir) + 20; + pc->own_sock = os_zalloc(len); + if (!pc->own_sock) + goto fail; + pc->pid = getpid(); + os_snprintf(pc->own_sock, len, "%s/%d", dir, pc->pid); + wpa_printf(MSG_DEBUG, "proc_coord: Own socket at %s", pc->own_sock); + unlink(pc->own_sock); + + pc->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (pc->sock < 0) { + wpa_printf(MSG_ERROR, "proc_coord: socket(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, pc->own_sock, sizeof(addr.sun_path)); + if (bind(pc->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_ERROR, "proc_coord: bind(PF_UNIX) failed: %s", + strerror(errno)); + goto fail; + } + + flags = fcntl(pc->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(pc->sock, F_SETFL, flags) < 0) { + wpa_printf(MSG_INFO, + "proc_coord: fcntl(O_NONBLOCK): %s", + strerror(errno)); + /* Not fatal, continue on.*/ + } + } + + eloop_register_read_sock(pc->sock, proc_coord_receive, pc, NULL); + + proc_coord_update_peers_from_dir(pc); + + /* Start periodic updates quickly to recover from potential race + * conditions if the processes are started at the same time. */ + eloop_register_timeout(1, 0, proc_coord_update_peers, pc, NULL); + + proc_coord_send_starting(pc); + + return pc; + +fail: + proc_coord_deinit(pc); + return NULL; +} + + +void proc_coord_deinit(struct proc_coord *pc) +{ + struct proc_coord_peer *peer, *tmp; + struct proc_coord_pending_request *req, *tmp2; + + if (!pc) + return; + + eloop_cancel_timeout(proc_coord_update_peers, pc, NULL); + eloop_cancel_timeout(proc_coord_expire_requests, pc, NULL); + + if (pc->sock >= 0) { + proc_coord_send_stopping(pc); + eloop_unregister_read_sock(pc->sock); + close(pc->sock); + unlink(pc->own_sock); + } + + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) + proc_coord_remove_peer(pc, peer); + + dl_list_for_each_safe(req, tmp2, &pc->requests, + struct proc_coord_pending_request, list) { + dl_list_del(&req->list); + os_free(req); + } + + os_free(pc->dir); + os_free(pc->own_sock); + os_free(pc); +} + + +int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx) +{ + struct proc_coord_handler *handler; + + handler = os_zalloc(sizeof(*handler)); + if (!handler) + return -1; + + handler->cb = cb; + handler->cb_ctx = cb_ctx; + dl_list_add(&pc->handlers, &handler->list); + return 0; +} + + +void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx) +{ + struct proc_coord_handler *handler; + + dl_list_for_each(handler, &pc->handlers, struct proc_coord_handler, + list) { + if (handler->cb == cb && handler->cb_ctx == cb_ctx) { + dl_list_del(&handler->list); + os_free(handler); + break; + } + } +} + + +int proc_coord_send_event(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg) +{ + struct proc_coord_peer *peer, *tmp; + int count = 0; + u32 seq = 0; + + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (dst && peer->pid != dst) + continue; + if (!dst && peer->state != PROC_COORD_PEER_ACTIVE) + continue; + if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_EVENT, cmd, + seq, msg) == 0) + count++; + } + + return count; +} + + +int proc_coord_send_request(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg, + unsigned int timeout_ms, + proc_coord_response_cb cb, + void *cb_ctx) +{ + struct proc_coord_peer *peer, *tmp; + int count = 0; + struct proc_coord_pending_request *req; + + pc->next_seq++; + + dl_list_for_each_safe(peer, tmp, &pc->peers, struct proc_coord_peer, + list) { + if (dst && peer->pid != dst) + continue; + if (!dst && peer->state != PROC_COORD_PEER_ACTIVE) + continue; + if (proc_coord_send_msg(pc, peer, PROC_COORD_MSG_REQUEST, cmd, + pc->next_seq, msg) < 0) + continue; + count++; + + req = os_zalloc(sizeof(*req)); + if (!req) + break; + req->peer = peer; + req->cmd = cmd; + req->seq = pc->next_seq; + req->cb = cb; + req->cb_ctx = cb_ctx; + os_get_reltime(&req->timeout); + os_reltime_add_ms(&req->timeout, timeout_ms); + dl_list_add(&pc->requests, &req->list); + } + proc_coord_set_req_expire_timer(pc); + + return count; +} + + +int proc_coord_send_response(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, u32 seq, + const struct wpabuf *msg) +{ + struct proc_coord_peer *peer = proc_coord_get_peer(pc, dst); + + if (!peer) + return -1; + return proc_coord_send_msg(pc, peer, PROC_COORD_MSG_RESPONSE, cmd, + seq, msg); +} diff --git a/src/common/proc_coord.h b/src/common/proc_coord.h new file mode 100644 index 000000000..47b09c40b --- /dev/null +++ b/src/common/proc_coord.h @@ -0,0 +1,138 @@ +/* + * Coordination of operations between processes + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PROC_COORD_H +#define PROC_COORD_H + +struct proc_coord; + +enum proc_coord_message_types { + PROC_COORD_MSG_REQUEST = 0, + PROC_COORD_MSG_RESPONSE = 1, + PROC_COORD_MSG_EVENT = 2, +}; + +enum proc_coord_commands { + PROC_COORD_CMD_STARTING = 0, + PROC_COORD_CMD_STOPPING = 1, + PROC_COORD_CMD_PING = 2, +}; + +/** + * proc_coord_init - Initialize process coordinations + * @dir: Access controlled directory for process coordination + * Returns: Context pointer on success or %NULL on failure + * + * The returned context must be released with a call to proc_coord_deinit(). + */ +struct proc_coord * proc_coord_init(const char *dir); + +/** + * proc_coord_deinit - Deinitialize process coordinations + * @pc: Process coordination context from proc_coord_init() + */ +void proc_coord_deinit(struct proc_coord *pc); + +typedef bool (*proc_coord_cb)(void *ctx, int src, + enum proc_coord_message_types msg_type, + enum proc_coord_commands cmd, + u32 seq, const struct wpabuf *msg); + +/** + * proc_coord_register_handler - Register a handler for process coordination + * @pc: Process coordination context from proc_coord_init() + * @cb: Callback function + * @cb_ctx: Context for the callback function + * Returns: 0 on success or -1 on failure + * + * The registered handler will be called for received request and event + * messages. Received request messages are delivered to the separate handler + * registered with proc_coord_send_request(). + * + * The handler function can return true to stop iteration of handler functions + * or false to allow the iteration to continue reporting the message to other + * registered handler functions, if any. + */ +int proc_coord_register_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx); + +/** + * proc_coord_unregister_handler - Unregister a handler for process coordination + * @pc: Process coordination context from proc_coord_init() + * @cb: Callback function + * @cb_ctx: Context for the callback function + */ +void proc_coord_unregister_handler(struct proc_coord *pc, proc_coord_cb cb, + void *cb_ctx); + +/** + * proc_coord_send_event - Send an event message + * @pc: Process coordination context from proc_coord_init() + * @dst: Destination peer (PID) or 0 for all active peers + * @cmd: The command ID for the message + * @msg: Payload of the message + * Returns: The number of peers the message was sent to + */ +int proc_coord_send_event(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg); + +typedef void (*proc_coord_response_cb)(void *ctx, int pid, + const struct wpabuf *msg); + +/** + * proc_coord_send_request - Send a request message + * @pc: Process coordination context from proc_coord_init() + * @dst: Destination peer (PID) or 0 for all active peers + * @cmd: The command ID for the message + * @msg: Payload of the message + * @timeout_ms: Timeout for receiving a response + * @cb: Callback function to report the responses or %NULL for no callback + * @cb_ctx: Context for the callback function + * Returns: The number of peers the message was sent to + * + * If a response is received from a peer, the response is reported to the + * callback function. If no response is received within the specified timeout, + * the callback function is called with msg == NULL. The specified @cb_ctx has + * to remain valid until all the pending responses have been reported or until + * proc_coord_cancel_wait() has been used to cancel any pending wait. + */ +int proc_coord_send_request(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, + const struct wpabuf *msg, + unsigned int timeout_ms, + proc_coord_response_cb cb, + void *cb_ctx); + +/** + * proc_coord_cancel_wait - Cancel wait for a pending response message + * @pc: Process coordination context from proc_coord_init() + * @cb: Callback function registered with proc_coord_send_request() + * @cb_ctx: Context for the callback function + */ +void proc_coord_cancel_wait(struct proc_coord *pc, proc_coord_response_cb cb, + void *cb_ctx); + +/** + * proc_coord_send_event - Send a response message + * @pc: Process coordination context from proc_coord_init() + * @dst: Destination peer (PID) + * @cmd: The command ID for the message + * @seq: The sequence number from the received request message + * @msg: Payload of the message + * Returns: 0 on success or -1 on failure + * + * This is used to send a response to a request message that was reported + * through a call to the handler function that was registered with + * proc_coord_register_handler(). + */ +int proc_coord_send_response(struct proc_coord *pc, int dst, + enum proc_coord_commands cmd, u32 seq, + const struct wpabuf *msg); + +#endif /* PROC_COORD_H */ diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 3fa733180..b8bda48d8 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -1720,6 +1720,11 @@ OBJS += src/utils/json.c L_CFLAGS += -DCONFIG_JSON endif +ifdef CONFIG_PROCESS_COORDINATION +L_CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += src/common/proc_coord.c +endif + OBJS += src/drivers/driver_common.c OBJS += wpa_supplicant.c events.c bssid_ignore.c wpas_glue.c scan.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index a7e15658e..b92b1e3f2 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -1918,6 +1918,11 @@ OBJS += ../src/utils/json.o CFLAGS += -DCONFIG_JSON endif +ifdef CONFIG_PROCESS_COORDINATION +CFLAGS += -DCONFIG_PROCESS_COORDINATION +OBJS += ../src/common/proc_coord.o +endif + ifdef CONFIG_MODULE_TESTS CFLAGS += -DCONFIG_MODULE_TESTS OBJS += wpas_module_tests.o diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index 5445f20da..ec8a90bc0 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -42,6 +42,9 @@ static void usage(void) " [-f]" #endif /* CONFIG_DEBUG_FILE */ " \\\n" +#ifdef CONFIG_PROCESS_COORDINATION + " [-z] \\\n" +#endif /* CONFIG_PROCESS_COORDINATION */ " [-o] [-O] \\\n" " [-N -i -c [-C] " "[-D] \\\n" @@ -104,7 +107,11 @@ static void usage(void) #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ " -v = show version\n" " -W = wait for a control interface monitor before starting\n" - " -y = show configuration parsing details in debug log\n"); + " -y = show configuration parsing details in debug log\n" +#ifdef CONFIG_PROCESS_COORDINATION + " -z = process coordination directory\n" +#endif /* CONFIG_PROCESS_COORDINATION */ + ); printf("example:\n" " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n", @@ -206,7 +213,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, - "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvWy"); + "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvWyz:"); if (c < 0) break; switch (c) { @@ -339,6 +346,11 @@ int main(int argc, char *argv[]) case 'y': params.show_details = true; break; +#ifdef CONFIG_PROCESS_COORDINATION + case 'z': + params.proc_coord_dir = optarg; + break; +#endif /* CONFIG_PROCESS_COORDINATION */ default: usage(); exitcode = 0; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 2c2d7352f..293d4920e 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -43,6 +43,7 @@ #include "common/gas_server.h" #include "common/dpp.h" #include "common/ptksa_cache.h" +#include "common/proc_coord.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "bssid_ignore.h" @@ -8625,6 +8626,16 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) random_init(params->entropy_file); +#ifdef CONFIG_PROCESS_COORDINATION + if (params->proc_coord_dir) { + global->pc = proc_coord_init(params->proc_coord_dir); + if (!global->pc) { + wpa_supplicant_deinit(global); + return NULL; + } + } +#endif /* CONFIG_PROCESS_COORDINATION */ + global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global); if (global->ctrl_iface == NULL) { wpa_supplicant_deinit(global); @@ -8745,6 +8756,10 @@ void wpa_supplicant_deinit(struct wpa_global *global) random_deinit(); +#ifdef CONFIG_PROCESS_COORDINATION + proc_coord_deinit(global->pc); +#endif /* CONFIG_PROCESS_COORDINATION */ + eloop_destroy(); if (global->params.pid_file) { diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 486ea0b98..4379b053c 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -263,6 +263,11 @@ struct wpa_params { * show_details - Whether to show config parsing details in debug log */ bool show_details; + + /** + * proc_coord_dir - Process coordination directory + */ + const char *proc_coord_dir; }; struct p2p_srv_bonjour { @@ -326,6 +331,10 @@ struct wpa_global { #endif /* CONFIG_WIFI_DISPLAY */ struct psk_list_entry *add_psk; /* From group formation */ + +#ifdef CONFIG_PROCESS_COORDINATION + struct proc_coord *pc; +#endif /* CONFIG_PROCESS_COORDINATION */ };