AP MLD: Introduce MLD level control interface socket
With MLO, each link have socket created with "<ifname>_link<link id>" under the control interface directory. Introduce a MLD level socket "<ifname>" as well under the same control interface directory. This socket can be used to pass the command to its partner links directly instead of using the link level socket. Link ID needs to be passed with the command in a prefix way. If no Link ID is provided the first link ID is selected. The structure of the command is - "LINKID <link id> <COMMAND APPLICABALE FOR THE LINK>" Directory looks something like this - $ ls /var/run/hostapd/ wlan0 wlan0_link0 wlan0_link1 wlan0 here is the MLD level socket. Rest are each link level sockets. This also helps to maintain backwards compatibility with applications which looks for <ifname> under the control interface directory. Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
This commit is contained in:
parent
757d8d9aac
commit
14cb3906af
5 changed files with 352 additions and 0 deletions
|
@ -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
|
#ifndef CONFIG_CTRL_IFACE_UDP
|
||||||
static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
|
static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,6 +14,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
|
||||||
void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
|
void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
|
||||||
int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
|
int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
|
||||||
void hostapd_global_ctrl_iface_deinit(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 */
|
#else /* CONFIG_NO_CTRL_IFACE */
|
||||||
static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
|
static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
|
||||||
{
|
{
|
||||||
|
|
|
@ -752,6 +752,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
|
||||||
if (!interfaces->mld[i])
|
if (!interfaces->mld[i])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]);
|
||||||
os_free(interfaces->mld[i]);
|
os_free(interfaces->mld[i]);
|
||||||
interfaces->mld[i] = NULL;
|
interfaces->mld[i] = NULL;
|
||||||
}
|
}
|
||||||
|
@ -797,6 +798,10 @@ 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;
|
||||||
|
#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);
|
dl_list_init(&interfaces.global_ctrl_dst);
|
||||||
#ifdef CONFIG_ETH_P_OUI
|
#ifdef CONFIG_ETH_P_OUI
|
||||||
dl_list_init(&interfaces.eth_p_oui);
|
dl_list_init(&interfaces.eth_p_oui);
|
||||||
|
|
|
@ -3115,9 +3115,17 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
|
||||||
|
|
||||||
os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
|
os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
|
||||||
dl_list_init(&mld->links);
|
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);
|
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;
|
hapd->mld = mld;
|
||||||
hostapd_mld_ref_inc(mld);
|
hostapd_mld_ref_inc(mld);
|
||||||
hostapd_bss_alloc_link_id(hapd);
|
hostapd_bss_alloc_link_id(hapd);
|
||||||
|
@ -3177,6 +3185,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
|
||||||
if (!remove && !forced_remove)
|
if (!remove && !forced_remove)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
interfaces->mld_ctrl_iface_deinit(mld);
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
|
wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
|
||||||
forced_remove ? " (forced)" : "");
|
forced_remove ? " (forced)" : "");
|
||||||
os_free(mld);
|
os_free(mld);
|
||||||
|
|
|
@ -97,6 +97,8 @@ struct hapd_interfaces {
|
||||||
#ifdef CONFIG_IEEE80211BE
|
#ifdef CONFIG_IEEE80211BE
|
||||||
struct hostapd_mld **mld;
|
struct hostapd_mld **mld;
|
||||||
size_t mld_count;
|
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 */
|
#endif /* CONFIG_IEEE80211BE */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -541,6 +543,10 @@ struct hostapd_mld {
|
||||||
|
|
||||||
struct hostapd_data *fbss;
|
struct hostapd_data *fbss;
|
||||||
struct dl_list links; /* List head of all affiliated links */
|
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
|
#define HOSTAPD_MLD_MAX_REF_COUNT 0xFF
|
||||||
|
|
Loading…
Reference in a new issue