1 /*
2  * Hostapd - command line interface for hostapd daemon
3  *                  for Zephyr (based on hostapd_cli.c)
4  * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
5  * Copyright (c) 2024, NXP
6  *
7  * This software may be distributed under the terms of the BSD license.
8  * See README for more details.
9  */
10 #include "includes.h"
11 
12 #include "common/cli.h"
13 #include "utils/common.h"
14 #include "utils/eloop.h"
15 #include "utils/edit.h"
16 #include "utils/list.h"
17 #include "ap/hostapd.h"
18 #include "ctrl_iface.h"
19 #include "common/version.h"
20 #include "common/ieee802_11_defs.h"
21 #include "supp_main.h"
22 #include "supp_events.h"
23 #include "ctrl_iface_zephyr.h"
24 #include "hostapd_cli_zephyr.h"
25 
26 #define CMD_BUF_LEN  1024
27 #define MAX_CMD_SIZE 512
28 #define MAX_ARGS 32
29 
30 int hapd_sockpair[2];
31 int hapd_mon_sockpair[2];
32 struct wpa_ctrl *hapd_ctrl_conn = NULL;
33 struct wpa_ctrl *hapd_mon_conn = NULL;
34 
supp_strlen(const char * str)35 static inline uint16_t supp_strlen(const char *str)
36 {
37 	return str == NULL ? 0U : (uint16_t)strlen(str);
38 }
39 
make_argv(char ** ppcmd,uint8_t c)40 static char make_argv(char **ppcmd, uint8_t c)
41 {
42 	char *cmd = *ppcmd;
43 	char quote = 0;
44 
45 	while (1) {
46 		c = *cmd;
47 
48 		if (c == '\0') {
49 			break;
50 		}
51 
52 		if (quote == c) {
53 			memmove(cmd, cmd + 1, supp_strlen(cmd));
54 			quote = 0;
55 			continue;
56 		}
57 
58 		if (quote && c == '\\') {
59 			char t = *(cmd + 1);
60 
61 			if (t == quote) {
62 				memmove(cmd, cmd + 1,
63 						supp_strlen(cmd));
64 				cmd += 1;
65 				continue;
66 			}
67 
68 			if (t == '0') {
69 				uint8_t i;
70 				uint8_t v = 0U;
71 
72 				for (i = 2U; i < (2 + 3); i++) {
73 					t = *(cmd + i);
74 
75 					if (t >= '0' && t <= '7') {
76 						v = (v << 3) | (t - '0');
77 					} else {
78 						break;
79 					}
80 				}
81 
82 				if (i > 2) {
83 					memmove(cmd, cmd + (i - 1),
84 						supp_strlen(cmd) - (i - 2));
85 					*cmd++ = v;
86 					continue;
87 				}
88 			}
89 
90 			if (t == 'x') {
91 				uint8_t i;
92 				uint8_t v = 0U;
93 
94 				for (i = 2U; i < (2 + 2); i++) {
95 					t = *(cmd + i);
96 
97 					if (t >= '0' && t <= '9') {
98 						v = (v << 4) | (t - '0');
99 					} else if ((t >= 'a') &&
100 						   (t <= 'f')) {
101 						v = (v << 4) | (t - 'a' + 10);
102 					} else if ((t >= 'A') && (t <= 'F')) {
103 						v = (v << 4) | (t - 'A' + 10);
104 					} else {
105 						break;
106 					}
107 				}
108 
109 				if (i > 2) {
110 					memmove(cmd, cmd + (i - 1),
111 						supp_strlen(cmd) - (i - 2));
112 					*cmd++ = v;
113 					continue;
114 				}
115 			}
116 		}
117 
118 		if (!quote && isspace((int) c)) {
119 			break;
120 		}
121 
122 		cmd += 1;
123 	}
124 	*ppcmd = cmd;
125 
126 	return quote;
127 }
128 
hostapd_make_argv(size_t * argc,const char ** argv,char * cmd,uint8_t max_argc)129 char hostapd_make_argv(size_t *argc, const char **argv, char *cmd,
130 		       uint8_t max_argc)
131 {
132 	char quote = 0;
133 	char c;
134 
135 	*argc = 0;
136 	do {
137 		c = *cmd;
138 		if (c == '\0') {
139 			break;
140 		}
141 
142 		if (isspace((int) c)) {
143 			*cmd++ = '\0';
144 			continue;
145 		}
146 
147 		argv[(*argc)++] = cmd;
148 		if (*argc == max_argc) {
149 			break;
150 		}
151 		quote = make_argv(&cmd, c);
152 	} while (true);
153 
154 	return quote;
155 }
156 
hostapd_cli_msg_cb(char * msg,size_t len)157 void hostapd_cli_msg_cb(char *msg, size_t len)
158 {
159 	wpa_printf(MSG_INFO, "%s", msg);
160 }
161 
_wpa_ctrl_command(struct wpa_ctrl * ctrl,const char * cmd,int print,char * resp)162 static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print, char *resp)
163 {
164 	char buf[CMD_BUF_LEN] = { 0 };
165 	size_t len;
166 	int ret;
167 
168 	if (hapd_ctrl_conn == NULL) {
169 		wpa_printf(MSG_ERROR, "Not connected to hostapd - command dropped.");
170 		return -1;
171 	}
172 
173 	if (ifname_prefix) {
174 		os_snprintf(buf, sizeof(buf), "IFNAME=%s %s", ifname_prefix, cmd);
175 		buf[sizeof(buf) - 1] = '\0';
176 		cmd = buf;
177 	}
178 
179 	len = sizeof(buf) - 1;
180 	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, hostapd_cli_msg_cb);
181 	if (ret == -2) {
182 		wpa_printf(MSG_ERROR, "'%s' command timed out.", cmd);
183 		return -2;
184 	} else if (ret < 0) {
185 		wpa_printf(MSG_ERROR, "'%s' command failed.", cmd);
186 		return -1;
187 	}
188 
189 	if (resp && len > 0) {
190 		os_memcpy(resp, buf, len);
191 		if (len > 1 && resp[len - 1] == '\n') {
192 			/* Remove the LF */
193 			resp[len - 1] = '\0';
194 		} else {
195 			resp[len] = '\0';
196 		}
197 		if (strncmp(resp, "FAIL", 4) == 0)
198 			return -3;
199 	}
200 
201 	if (print) {
202 		buf[len] = '\0';
203 		if (buf[0] != '\0')
204 			wpa_printf(MSG_INFO, "%s", buf);
205 	}
206 
207 	return 0;
208 }
209 
hostapd_ctrl_command(struct wpa_ctrl * ctrl,const char * cmd)210 int hostapd_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
211 {
212 	return _wpa_ctrl_command(ctrl, cmd, 0, NULL);
213 }
214 
hostapd_ctrl_command_interactive(struct wpa_ctrl * ctrl,const char * cmd)215 int hostapd_ctrl_command_interactive(struct wpa_ctrl *ctrl, const char *cmd)
216 {
217 	return _wpa_ctrl_command(ctrl, cmd, 1, NULL);
218 }
219 
zephyr_hostapd_cli_cmd_resp(const char * cmd,char * resp)220 int zephyr_hostapd_cli_cmd_resp(const char *cmd, char *resp)
221 {
222 	return _wpa_ctrl_command(hapd_ctrl_conn, cmd, 1, resp);
223 }
224 
zephyr_hostapd_ctrl_zephyr_cmd(int argc,const char * argv[])225 int zephyr_hostapd_ctrl_zephyr_cmd(int argc, const char *argv[])
226 {
227 	return hostapd_request(hapd_ctrl_conn, argc , (char **) argv);
228 }
229 
zephyr_hostapd_cli_cmd_v(const char * fmt,...)230 int zephyr_hostapd_cli_cmd_v(const char *fmt, ...)
231 {
232 	va_list cmd_args;
233 	int argc;
234 	const char *argv[MAX_ARGS];
235 	char cmd[MAX_CMD_SIZE];
236 
237 	va_start(cmd_args, fmt);
238 	vsnprintf(cmd, sizeof(cmd), fmt, cmd_args);
239 	va_end(cmd_args);
240 
241 	(void)hostapd_make_argv(&argc, &argv[0], cmd, MAX_ARGS);
242 
243 	wpa_printf(MSG_DEBUG, "Calling hostapd_cli: %s, argc: %d", cmd, argc);
244 	for (int i = 0; i < argc; i++)
245 		wpa_printf(MSG_DEBUG, "argv[%d]: %s", i, argv[i]);
246 
247 	return zephyr_hostapd_ctrl_zephyr_cmd(argc, argv);
248 }
249 
hostapd_cli_recv_pending(struct wpa_ctrl * ctrl,struct hostapd_data * hapd)250 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, struct hostapd_data *hapd)
251 {
252 	while (wpa_ctrl_pending(ctrl) > 0) {
253 		char buf[sizeof(struct conn_msg)];
254 		size_t hlen = sizeof(int);
255 		size_t plen = MAX_CTRL_MSG_LEN;
256 
257 		if (wpa_ctrl_recv(ctrl, buf, &hlen) == 0 &&
258 		    hlen == sizeof(int)) {
259 			plen = *((int *)buf);
260 		} else {
261 			wpa_printf(MSG_ERROR, "Could not read pending message header len %d.\n", hlen);
262 			continue;
263 		}
264 
265 		if (wpa_ctrl_recv(ctrl, buf + sizeof(int), &plen) == 0) {
266 			struct conn_msg *msg = (struct conn_msg *)buf;
267 
268 			msg->msg[msg->msg_len] = '\0';
269 			wpa_printf(MSG_DEBUG, "Received len: %d, msg_len:%d - %s->END\n",
270 				   plen, msg->msg_len, msg->msg);
271 			if (msg->msg_len >= MAX_CTRL_MSG_LEN) {
272 				wpa_printf(MSG_DEBUG, "Too long message received.\n");
273 				continue;
274 			}
275 
276 			if (msg->msg_len > 0) {
277 #ifdef CONFIG_WIFI_NM_WPA_SUPPLICANT_DPP
278 				if (strncmp(msg->msg, "DPP", 3) == 0) {
279 					hostapd_handle_dpp_event(hapd, msg->msg, msg->msg_len);
280 				}
281 #endif /* CONFIG_WIFI_NM_WPA_SUPPLICANT_DPP */
282 			}
283 		} else {
284 			wpa_printf(MSG_INFO, "Could not read pending message.\n");
285 		}
286 	}
287 }
288 
hostapd_cli_mon_receive(int sock,void * eloop_ctx,void * sock_ctx)289 static void hostapd_cli_mon_receive(int sock, void *eloop_ctx,
290 				    void *sock_ctx)
291 {
292 	struct hostapd_data *hapd = (struct hostapd_data *)eloop_ctx;
293 
294 	hostapd_cli_recv_pending(hapd_mon_conn, hapd);
295 }
296 
hostapd_cli_open_connection(struct hostapd_data * hapd)297 static int hostapd_cli_open_connection(struct hostapd_data *hapd)
298 {
299 	int ret;
300 
301 	if (!hapd_ctrl_conn) {
302 		hapd_ctrl_conn = wpa_ctrl_open(hapd_sockpair[0]);
303 		if (hapd_ctrl_conn == NULL) {
304 			wpa_printf(MSG_ERROR, "Failed to open control connection to %d",
305 				   hapd_sockpair[0]);
306 			return -1;
307 		}
308 	}
309 
310 	if (!hapd_mon_conn) {
311 		ret = socketpair(AF_UNIX, SOCK_STREAM, 0, hapd_mon_sockpair);
312 		if (ret != 0) {
313 			wpa_printf(MSG_ERROR, "Failed to open monitor connection: %s",
314 				   strerror(errno));
315 			goto fail;
316 		}
317 
318 		hapd_mon_conn = wpa_ctrl_open(hapd_mon_sockpair[0]);
319 		if (hapd_mon_conn) {
320 			eloop_register_read_sock(hapd_mon_sockpair[0],
321 						 hostapd_cli_mon_receive, hapd, NULL);
322 		}
323 	}
324 
325 	return 0;
326 fail:
327 	wpa_ctrl_close(hapd_ctrl_conn);
328 	return -1;
329 }
330 
hostapd_cli_close_connection(struct hostapd_data * hapd)331 static void hostapd_cli_close_connection(struct hostapd_data *hapd)
332 {
333 	int ret;
334 
335 	if (hapd_ctrl_conn == NULL)
336 		return;
337 
338 	ret = wpa_ctrl_detach(hapd_ctrl_conn);
339 	if (ret < 0) {
340 		wpa_printf(MSG_INFO, "Failed to detach from wpa_supplicant: %s",
341 			   strerror(errno));
342 	}
343 	wpa_ctrl_close(hapd_ctrl_conn);
344 	hapd_ctrl_conn = NULL;
345 
346 	eloop_unregister_read_sock(hapd_mon_sockpair[0]);
347 	wpa_ctrl_close(hapd_mon_conn);
348 	hapd_mon_conn = NULL;
349 
350 	close(hapd_mon_sockpair[1]);
351 	hapd_mon_sockpair[1] = -1;
352 }
353 
hostapd_msg_send(void * hapd,int level,enum wpa_msg_type type,const char * buf,size_t len)354 void hostapd_msg_send(void *hapd, int level,
355 		      enum wpa_msg_type type,
356 		      const char *buf, size_t len)
357 {
358 	struct conn_msg msg;
359 
360 	if (len > MAX_CTRL_MSG_LEN)
361 	{
362 		wpa_printf(MSG_ERROR, "CTRL_MSG too long");
363 		return;
364 	}
365 
366 	if (type == WPA_MSG_PER_INTERFACE && level >= MSG_INFO &&
367 	    hapd_mon_sockpair[1] > 0) {
368 		memcpy(&msg.msg, buf, len);
369 		msg.msg_len = len;
370 		if (send(hapd_mon_sockpair[1], &msg, len + 4, 0) < 0) {
371 			wpa_printf(MSG_ERROR,
372 				   "sendto(hostapd monitor sock): %s",
373 				   strerror(errno));
374 		}
375 	}
376 }
377 
zephyr_hostapd_ctrl_init(void * ctx)378 int zephyr_hostapd_ctrl_init(void *ctx)
379 {
380 	int ret;
381 	struct hostapd_data *hapd = ctx;
382 
383 	memset(hapd_sockpair, -1, sizeof(hapd_sockpair));
384 	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, hapd_sockpair);
385 	if (ret != 0) {
386 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
387 		goto fail;
388 	}
389 
390 	eloop_register_read_sock(hapd_sockpair[1], hostapd_ctrl_iface_receive,
391 				 hapd, NULL);
392 
393 	ret = hostapd_cli_open_connection(hapd);
394 	if (ret < 0) {
395 		wpa_printf(MSG_INFO, "Failed to initialize control interface: %s: %d", hapd->conf->iface, ret);
396 		return ret;
397 	}
398 
399 fail:
400 	return ret;
401 }
402 
zephyr_hostapd_ctrl_deinit(void * hapd)403 void zephyr_hostapd_ctrl_deinit(void *hapd)
404 {
405 	hostapd_cli_close_connection((struct hostapd_data *)hapd);
406 }
407 
408