blob: 448534eb8d4274c5a4564c384ce880847904e4dd [file] [log] [blame]
developer05f3b2b2024-08-19 19:17:34 +08001From 631a48c1a241bf9515d296eeea5d6060bef96cff Mon Sep 17 00:00:00 2001
2From: Aditya Kumar Singh <quic_adisi@quicinc.com>
3Date: Tue, 23 Apr 2024 11:01:24 +0530
4Subject: [PATCH 002/126] ctrl_iface: MLO: introduce MLD level socket
5
6With MLO, each link have socket created with "<ifname>_link<link id>" under
7the control interface directory.
8
9Introduce a MLD level socket - "<ifname>" as well under the same control
10interface directory. This socket can be used to pass the command to its
11partner links directly instead of using the link level socket. Link ID
12needs to be passed with the command.
13
14The structure of the command is -
15 "<COMMAND APPLICABALE FOR THE LINK> LINKID <link id>"
16
17Directory looks something like this -
18 $ ls /var/run/hostapd/
19 wlan0
20 wlan0_link0
21 wlan0_link1
22
23wlan0 here is the MLD level socket. Rest are each link level.
24
25This would also help to maintain backwards compatibility with applications
26which looks for <ifname> under the control interface directory.`
27
28Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
29---
30 hostapd/ctrl_iface.c | 335 +++++++++++++++++++++++++++++++++++++++++++
31 hostapd/ctrl_iface.h | 4 +
32 hostapd/main.c | 5 +
33 src/ap/hostapd.c | 11 ++
34 src/ap/hostapd.h | 6 +
35 5 files changed, 361 insertions(+)
36
37diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
38index 3fa33be7a..5fe29147f 100644
39--- a/hostapd/ctrl_iface.c
40+++ b/hostapd/ctrl_iface.c
41@@ -4682,6 +4682,341 @@ done:
42 }
43
44
45+#ifdef CONFIG_IEEE80211BE
46+#ifndef CONFIG_CTRL_IFACE_UDP
47+static int hostapd_mld_ctrl_iface_attach(struct hostapd_mld *mld,
48+ struct sockaddr_storage *from,
49+ socklen_t fromlen, const char *input)
50+{
51+ return ctrl_iface_attach(&mld->ctrl_dst, from, fromlen, input);
52+}
53+
54+
55+static int hostapd_mld_ctrl_iface_detach(struct hostapd_mld *mld,
56+ struct sockaddr_storage *from,
57+ socklen_t fromlen)
58+{
59+ return ctrl_iface_detach(&mld->ctrl_dst, from, fromlen);
60+}
61+
62+
63+static int hostapd_mld_ctrl_iface_receive_process(struct hostapd_mld *mld,
64+ char *buf, char *reply,
65+ int reply_size,
66+ struct sockaddr_storage *from,
67+ socklen_t fromlen)
68+{
69+ struct hostapd_data *link_hapd, *link_itr;
70+ int reply_len, link_id = -1;
71+ char *link_cmd;
72+ bool found = false;
73+
74+ os_memcpy(reply, "OK\n", 3);
75+ reply_len = 3;
76+
77+ /* Check if link id is provided in the command or not */
78+ link_cmd = os_strstr(buf, "LINKID");
79+ if (link_cmd) {
80+ /* Trim the link id part now */
81+ *(link_cmd - 1) = '\0';
82+
83+ link_cmd += 7;
84+ link_id = atoi(link_cmd);
85+
86+ if (link_id < 0 || link_id >= 15) {
87+ os_memcpy(reply, "INVALID LINK ID\n", 16);
88+ reply_len = 16;
89+ return reply_len;
90+ }
91+
92+ link_hapd = mld->fbss;
93+ if (!link_hapd) {
94+ os_memcpy(reply, "NO LINKS ACTIVE\n", 16);
95+ reply_len = 16;
96+ return reply_len;
97+ }
98+
99+ for_each_mld_link(link_itr, link_hapd) {
100+ if (link_itr->mld_link_id == link_id) {
101+ found = true;
102+ break;
103+ }
104+ }
105+
106+ if (!found) {
107+ os_memcpy(reply, "FAIL\n", 5);
108+ reply_len = 5;
109+ return reply_len;
110+ }
111+
112+ link_hapd = link_itr;
113+ } else {
114+ link_hapd = mld->fbss;
115+ }
116+
117+ if (os_strcmp(buf, "PING") == 0) {
118+ os_memcpy(reply, "PONG\n", 5);
119+ reply_len = 5;
120+ } else if (os_strcmp(buf, "ATTACH") == 0) {
121+ if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, NULL))
122+ reply_len = -1;
123+ } else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
124+ if (hostapd_mld_ctrl_iface_attach(mld, from, fromlen, buf + 7))
125+ reply_len = -1;
126+ } else if (os_strcmp(buf, "DETACH") == 0) {
127+ if (hostapd_mld_ctrl_iface_detach(mld, from, fromlen))
128+ reply_len = -1;
129+ } else {
130+ if (link_id == -1)
131+ wpa_printf(MSG_DEBUG, "Link ID not provided, using first link BSS (if available)");
132+
133+ if (!link_hapd)
134+ reply_len = -1;
135+ else
136+ reply_len =
137+ hostapd_ctrl_iface_receive_process(link_hapd, buf,
138+ reply, reply_size,
139+ from, fromlen);
140+ }
141+
142+ if (reply_len < 0) {
143+ os_memcpy(reply, "FAIL\n", 5);
144+ reply_len = 5;
145+ }
146+
147+ return reply_len;
148+}
149+
150+
151+static void hostapd_mld_ctrl_iface_receive(int sock, void *eloop_ctx,
152+ void *sock_ctx)
153+{
154+ struct hostapd_mld *mld = eloop_ctx;
155+ char buf[4096];
156+ int res;
157+ struct sockaddr_storage from;
158+ socklen_t fromlen = sizeof(from);
159+ char *reply, *pos = buf;
160+ const int reply_size = 4096;
161+ int reply_len;
162+ int level = MSG_DEBUG;
163+
164+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
165+ (struct sockaddr *) &from, &fromlen);
166+ if (res < 0) {
167+ wpa_printf(MSG_ERROR, "recvfrom(mld ctrl_iface): %s",
168+ strerror(errno));
169+ return;
170+ }
171+ buf[res] = '\0';
172+
173+ reply = os_malloc(reply_size);
174+ if (reply == NULL) {
175+ if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
176+ fromlen) < 0) {
177+ wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s",
178+ strerror(errno));
179+ }
180+ return;
181+ }
182+
183+ if (os_strcmp(pos, "PING") == 0)
184+ level = MSG_EXCESSIVE;
185+
186+ wpa_hexdump_ascii(level, "RX MLD ctrl_iface", pos, res);
187+
188+ reply_len = hostapd_mld_ctrl_iface_receive_process(mld, pos,
189+ reply, reply_size,
190+ &from, fromlen);
191+
192+ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
193+ fromlen) < 0) {
194+ wpa_printf(MSG_DEBUG, "MLD CTRL: sendto failed: %s",
195+ strerror(errno));
196+ }
197+ os_free(reply);
198+}
199+
200+
201+static char * hostapd_mld_ctrl_iface_path(struct hostapd_mld *mld)
202+{
203+ char *buf;
204+ size_t len;
205+
206+ if (!mld->ctrl_interface)
207+ return NULL;
208+
209+ len = os_strlen(mld->ctrl_interface) + os_strlen(mld->name) + 2;
210+
211+ buf = os_malloc(len);
212+ if (buf == NULL)
213+ return NULL;
214+
215+ os_snprintf(buf, len, "%s/%s", mld->ctrl_interface, mld->name);
216+ buf[len - 1] = '\0';
217+ return buf;
218+}
219+#endif /* !CONFIG_CTRL_IFACE_UDP */
220+
221+
222+int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld)
223+{
224+#ifndef CONFIG_CTRL_IFACE_UDP
225+ struct sockaddr_un addr;
226+ int s = -1;
227+ char *fname = NULL;
228+
229+ if (!mld)
230+ return -1;
231+
232+ if (mld->ctrl_sock > -1) {
233+ wpa_printf(MSG_DEBUG, "MLD %s ctrl_iface already exists!",
234+ mld->name);
235+ return 0;
236+ }
237+
238+ dl_list_init(&mld->ctrl_dst);
239+
240+ if (mld->ctrl_interface == NULL)
241+ return 0;
242+
243+ if (mkdir(mld->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
244+ if (errno == EEXIST) {
245+ wpa_printf(MSG_DEBUG, "Using existing control "
246+ "interface directory.");
247+ } else {
248+ wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
249+ strerror(errno));
250+ goto fail;
251+ }
252+ }
253+
254+ if (os_strlen(mld->ctrl_interface) + 1 +
255+ os_strlen(mld->name) >= sizeof(addr.sun_path))
256+ goto fail;
257+
258+ s = socket(PF_UNIX, SOCK_DGRAM, 0);
259+ if (s < 0) {
260+ wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
261+ goto fail;
262+ }
263+
264+ os_memset(&addr, 0, sizeof(addr));
265+#ifdef __FreeBSD__
266+ addr.sun_len = sizeof(addr);
267+#endif /* __FreeBSD__ */
268+ addr.sun_family = AF_UNIX;
269+
270+ fname = hostapd_mld_ctrl_iface_path(mld);
271+ if (fname == NULL)
272+ goto fail;
273+
274+ os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
275+
276+ wpa_printf(MSG_DEBUG, "Setting up MLD %s ctrl_iface", mld->name);
277+
278+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
279+ wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
280+ strerror(errno));
281+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
282+ wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
283+ " allow connections - assuming it was left"
284+ "over from forced program termination");
285+ if (unlink(fname) < 0) {
286+ wpa_printf(MSG_ERROR,
287+ "Could not unlink existing ctrl_iface socket '%s': %s",
288+ fname, strerror(errno));
289+ goto fail;
290+ }
291+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
292+ 0) {
293+ wpa_printf(MSG_ERROR,
294+ "hostapd-ctrl-iface: bind(PF_UNIX): %s",
295+ strerror(errno));
296+ goto fail;
297+ }
298+ wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
299+ "ctrl_iface socket '%s'", fname);
300+ } else {
301+ wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
302+ "be in use - cannot override it");
303+ wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
304+ "not used anymore", fname);
305+ os_free(fname);
306+ fname = NULL;
307+ goto fail;
308+ }
309+ }
310+
311+ if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
312+ wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
313+ strerror(errno));
314+ goto fail;
315+ }
316+ os_free(fname);
317+
318+ mld->ctrl_sock = s;
319+
320+ if (eloop_register_read_sock(s, hostapd_mld_ctrl_iface_receive, mld,
321+ NULL) < 0)
322+ return -1;
323+
324+ return 0;
325+
326+fail:
327+ if (s >= 0)
328+ close(s);
329+ if (fname) {
330+ unlink(fname);
331+ os_free(fname);
332+ }
333+ return -1;
334+#endif /* !CONFIG_CTRL_IFACE_UDP */
335+ return 0;
336+}
337+
338+
339+void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld)
340+{
341+#ifndef CONFIG_CTRL_IFACE_UDP
342+ struct wpa_ctrl_dst *dst, *prev;
343+
344+ if (mld->ctrl_sock > -1) {
345+ char *fname;
346+ eloop_unregister_read_sock(mld->ctrl_sock);
347+ close(mld->ctrl_sock);
348+ mld->ctrl_sock = -1;
349+
350+ fname = hostapd_mld_ctrl_iface_path(mld);
351+ if (fname)
352+ unlink(fname);
353+ os_free(fname);
354+
355+ if (mld->ctrl_interface &&
356+ rmdir(mld->ctrl_interface) < 0) {
357+ if (errno == ENOTEMPTY) {
358+ wpa_printf(MSG_DEBUG, "MLD Control interface "
359+ "directory not empty - leaving it "
360+ "behind");
361+ } else {
362+ wpa_printf(MSG_ERROR,
363+ "rmdir[ctrl_interface=%s]: %s",
364+ mld->ctrl_interface,
365+ strerror(errno));
366+ }
367+ }
368+ }
369+
370+ dl_list_for_each_safe(dst, prev, &mld->ctrl_dst, struct wpa_ctrl_dst,
371+ list)
372+ os_free(dst);
373+#endif /* !CONFIG_CTRL_IFACE_UDP */
374+
375+ os_free(mld->ctrl_interface);
376+}
377+#endif /* CONFIG_IEEE80211BE */
378+
379+
380 #ifndef CONFIG_CTRL_IFACE_UDP
381 static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
382 {
383diff --git a/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h
384index 3341a66bd..ec5a95be7 100644
385--- a/hostapd/ctrl_iface.h
386+++ b/hostapd/ctrl_iface.h
387@@ -14,6 +14,10 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
388 void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
389 int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
390 void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
391+#ifdef CONFIG_IEEE80211BE
392+int hostapd_mld_ctrl_iface_init(struct hostapd_mld *mld);
393+void hostapd_mld_ctrl_iface_deinit(struct hostapd_mld *mld);
394+#endif /* CONFIG_IEEE80211BE */
395 #else /* CONFIG_NO_CTRL_IFACE */
396 static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
397 {
398diff --git a/hostapd/main.c b/hostapd/main.c
399index 00e02bb03..aa1f69812 100644
400--- a/hostapd/main.c
401+++ b/hostapd/main.c
402@@ -748,6 +748,7 @@ static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces)
403 if (!interfaces->mld[i])
404 continue;
405
406+ interfaces->mld_ctrl_iface_deinit(interfaces->mld[i]);
407 os_free(interfaces->mld[i]);
408 interfaces->mld[i] = NULL;
409 }
410@@ -793,6 +794,10 @@ int main(int argc, char *argv[])
411 interfaces.global_iface_path = NULL;
412 interfaces.global_iface_name = NULL;
413 interfaces.global_ctrl_sock = -1;
414+#ifdef CONFIG_IEEE80211BE
415+ interfaces.mld_ctrl_iface_init = hostapd_mld_ctrl_iface_init;
416+ interfaces.mld_ctrl_iface_deinit = hostapd_mld_ctrl_iface_deinit;
417+#endif /* CONFIG_IEEE80211BE */
418 dl_list_init(&interfaces.global_ctrl_dst);
419 #ifdef CONFIG_ETH_P_OUI
420 dl_list_init(&interfaces.eth_p_oui);
421diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
422index c819c30cf..36d48ae09 100644
423--- a/src/ap/hostapd.c
424+++ b/src/ap/hostapd.c
425@@ -3093,9 +3093,18 @@ static void hostapd_bss_setup_multi_link(struct hostapd_data *hapd,
426
427 os_strlcpy(mld->name, conf->iface, sizeof(conf->iface));
428 dl_list_init(&mld->links);
429+ mld->ctrl_sock = -1;
430+ mld->ctrl_interface = os_strdup(hapd->conf->ctrl_interface);
431
432 wpa_printf(MSG_DEBUG, "AP MLD %s created", mld->name);
433
434+ /*
435+ * Initialize MLD control interfaces early to allow external monitoring of
436+ * link setup operations.
437+ */
438+ if (interfaces->mld_ctrl_iface_init(mld))
439+ goto fail;
440+
441 hapd->mld = mld;
442 hostapd_mld_ref_inc(mld);
443 hostapd_bss_alloc_link_id(hapd);
444@@ -3155,6 +3164,8 @@ static void hostapd_cleanup_unused_mlds(struct hapd_interfaces *interfaces)
445 if (!remove && !forced_remove)
446 continue;
447
448+ interfaces->mld_ctrl_iface_deinit(mld);
449+
450 wpa_printf(MSG_DEBUG, "AP MLD %s: Freed%s", mld->name,
451 forced_remove ? " (forced)" : "");
452 os_free(mld);
453diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
454index 34a665562..2ef63e5f2 100644
455--- a/src/ap/hostapd.h
456+++ b/src/ap/hostapd.h
457@@ -97,6 +97,8 @@ struct hapd_interfaces {
458 #ifdef CONFIG_IEEE80211BE
459 struct hostapd_mld **mld;
460 size_t mld_count;
461+ int (*mld_ctrl_iface_init)(struct hostapd_mld *mld);
462+ void (*mld_ctrl_iface_deinit)(struct hostapd_mld *mld);
463 #endif /* CONFIG_IEEE80211BE */
464 };
465
466@@ -519,6 +521,10 @@ struct hostapd_mld {
467
468 struct hostapd_data *fbss;
469 struct dl_list links; /* List head of all affiliated links */
470+
471+ int ctrl_sock;
472+ struct dl_list ctrl_dst;
473+ char *ctrl_interface; /* directory for UNIX domain sockets */
474 };
475
476 #define HOSTAPD_MLD_MAX_REF_COUNT 0xFF
477--
4782.18.0
479