diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index b9cfe4423..bae827caf 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -4734,6 +4734,335 @@ done: } +#ifdef CONFIG_IEEE80211BE +#ifndef CONFIG_CTRL_IFACE_UDP + +static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld, + char *buf, char *reply, + size_t reply_size, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct hostapd_data *link_hapd, *link_itr; + int reply_len = -1, link_id = -1; + char *cmd; + bool found = false; + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + cmd = buf; + + /* Check whether the link ID is provided in the command */ + if (os_strncmp(cmd, "LINKID ", 7) == 0) { + cmd += 7; + link_id = atoi(cmd); + if (link_id < 0 || link_id >= 15) { + os_memcpy(reply, "INVALID LINK ID\n", 16); + reply_len = 16; + goto out; + } + + cmd = os_strchr(cmd, ' '); + if (!cmd) + goto out; + cmd++; + } + if (link_id >= 0) { + link_hapd = mld->fbss; + if (!link_hapd) { + os_memcpy(reply, "NO LINKS ACTIVE\n", 16); + reply_len = 16; + goto out; + } + + for_each_mld_link(link_itr, link_hapd) { + if (link_itr->mld_link_id == link_id) { + found = true; + break; + } + } + + if (!found) + goto out; + + link_hapd = link_itr; + } else { + link_hapd = mld->fbss; + } + + if (os_strcmp(cmd, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strcmp(cmd, "ATTACH") == 0) { + if (ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(cmd, "ATTACH ", 7) == 0) { + if (ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, cmd + 7)) + reply_len = -1; + } else if (os_strcmp(cmd, "DETACH") == 0) { + if (ctrl_iface_detach(&mld->ctrl_dst, from, fromlen)) + reply_len = -1; + } else { + if (link_id == -1) + wpa_printf(MSG_DEBUG, + "Link ID not provided, using the first link BSS (if available)"); + + if (!link_hapd) + reply_len = -1; + else + reply_len = + hostapd_ctrl_iface_receive_process( + link_hapd, cmd, reply, reply_size, + from, fromlen); + } + +out: + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + return reply_len; +} + + +static void hostapd_mld_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_mld *mld = eloop_ctx; + char buf[4096]; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + char *reply, *pos = buf; + const size_t reply_size = 4096; + int reply_len; + int level = MSG_DEBUG; + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(mld ctrl_iface): %s", + strerror(errno)); + return; + } + buf[res] = '\0'; + + reply = os_malloc(reply_size); + if (!reply) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + + wpa_hexdump_ascii(level, "RX MLD ctrl_iface", pos, res); + + reply_len = hostapd_mld_ctrl_iface_receive_process(mld, pos, + reply, reply_size, + &from, fromlen); + + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s", + strerror(errno)); + } + os_free(reply); +} + + +static char * hostapd_mld_ctrl_iface_path(struct hostapd_mld *mld) +{ + size_t len; + char *buf; + int ret; + + if (!mld->ctrl_interface) + return NULL; + + len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2; + + buf = os_malloc(len); + if (!buf) + return NULL; + + ret = os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name); + if (os_snprintf_error(len, ret)) { + os_free(buf); + return NULL; + } + + return buf; +} + +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + +int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (!mld) + return -1; + + if (mld->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "MLD %s ctrl_iface already exists!", + mld->name); + return 0; + } + + dl_list_init(&mld->ctrl_dst); + + if (!mld->ctrl_interface) + return 0; + + if (mkdir(mld->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, + "Using existing control interface directory."); + } else { + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + } + + if (os_strlen(mld->ctrl_interface) + 1 + os_strlen(mld->name) >= + sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + + fname = hostapd_mld_ctrl_iface_path(mld); + if (!fname) + goto fail; + + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + + wpa_printf(MSG_DEBUG, "Setting up MLD %s ctrl_iface", mld->name); + + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not allow connections - assuming it was left over from forced program termination"); + if (unlink(fname) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, + "hostapd-ctrl-iface: bind(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, + "Successfully replaced leftover ctrl_iface socket '%s'", + fname); + } else { + wpa_printf(MSG_INFO, + "ctrl_iface exists and seems to be in use - cannot override it"); + wpa_printf(MSG_INFO, + "Delete '%s' manually if it is not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + os_free(fname); + + mld->ctrl_sock = s; + + if (eloop_register_read_sock(s, hostapd_mld_ctrl_iface_receive, mld, + NULL) < 0) + return -1; + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + return 0; +} + + +void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + struct wpa_ctrl_dst *dst, *prev; + + if (mld->ctrl_sock > -1) { + char *fname; + + eloop_unregister_read_sock(mld->ctrl_sock); + close(mld->ctrl_sock); + mld->ctrl_sock = -1; + + fname = hostapd_mld_ctrl_iface_path(mld); + if (fname) { + unlink(fname); + os_free(fname); + } + + if (mld->ctrl_interface && + rmdir(mld->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, + "MLD control interface directory not empty - leaving it behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + mld->ctrl_interface, + strerror(errno)); + } + } + } + + dl_list_for_each_safe(dst, prev, &mld->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + os_free(mld->ctrl_interface); +} + +#endif /* CONFIG_IEEE80211BE */ + + #ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h index 3341a66bd..6ce209d22 100644 --- a/hostapd/ctrl_iface.h +++ b/hostapd/ctrl_iface.h @@ -14,6 +14,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd); void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); +int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld); +void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld); #else /* CONFIG_NO_CTRL_IFACE */ static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { diff --git a/hostapd/main.c b/hostapd/main.c index be156ee60..512cd892b 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -752,6 +752,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces) if (!interfaces->mld[i]) continue; + interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]); os_free(interfaces->mld[i]); interfaces->mld[i] = NULL; } @@ -797,6 +798,10 @@ int main(int argc, char *argv[]) interfaces.global_iface_path = NULL; interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; +#ifdef CONFIG_IEEE80211BE + interfaces.mld_ctrl_iface_init = hostapd_mld_ctrl_iface_init; + interfaces.mld_ctrl_iface_deinit = hostapd_mld_ctrl_iface_deinit; +#endif /* CONFIG_IEEE80211BE */ dl_list_init(&interfaces.global_ctrl_dst); #ifdef CONFIG_ETH_P_OUI dl_list_init(&interfaces.eth_p_oui); diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index bf076916d..f97311587 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -3115,9 +3115,17 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd, os_strlcpy(mld->name, conf->iface, sizeof(conf->iface)); dl_list_init(&mld->links); + mld->ctrl_sock = -1; + if (hapd->conf->ctrl_interface) + mld->ctrl_interface = os_strdup(hapd->conf->ctrl_interface); wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name); + /* Initialize MLD control interfaces early to allow external monitoring + * of link setup operations. */ + if (interfaces->mld_ctrl_iface_init(mld)) + goto fail; + hapd->mld = mld; hostapd_mld_ref_inc(mld); hostapd_bss_alloc_link_id(hapd); @@ -3177,6 +3185,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces) if (!remove && !forced_remove) continue; + interfaces->mld_ctrl_iface_deinit(mld); + wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name, forced_remove ? " (forced)" : ""); os_free(mld); diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 02f7d5b29..dddefb593 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -97,6 +97,8 @@ struct hapd_interfaces { #ifdef CONFIG_IEEE80211BE struct hostapd_mld **mld; size_t mld_count; + int (*mld_ctrl_iface_init)(struct hostapd_mld *mld); + void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld); #endif /* CONFIG_IEEE80211BE */ }; @@ -541,6 +543,10 @@ struct hostapd_mld { struct hostapd_data *fbss; struct dl_list links; /* List head of all affiliated links */ + + int ctrl_sock; + struct dl_list ctrl_dst; + char *ctrl_interface; /* Directory for UNIX domain sockets */ }; #define HOSTAPD_MLD_MAX_REF_COUNT 0xFF