}
+#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)
{