1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /* @file
8  * @brief wpa_cli implementation for Zephyr OS
9  */
10 
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/shell/shell.h>
15 #include <zephyr/net/net_if.h>
16 #include <zephyr/net/socket.h>
17 #include <zephyr/init.h>
18 
19 
20 #include "supp_main.h"
21 
22 #include "common.h"
23 #include "wpa_supplicant_i.h"
24 #include "wpa_cli_zephyr.h"
25 #ifdef CONFIG_WIFI_NM_HOSTAPD_AP
26 #include "hostapd.h"
27 #include "hapd_main.h"
28 #include "hostapd_cli_zephyr.h"
29 #endif
30 
cmd_wpa_cli(const struct shell * sh,size_t argc,const char * argv[])31 static int cmd_wpa_cli(const struct shell *sh, size_t argc, const char *argv[])
32 {
33 	struct net_if *iface = NULL;
34 	char if_name[CONFIG_NET_INTERFACE_NAME_LEN + 1];
35 	struct wpa_supplicant *wpa_s = NULL;
36 	size_t arg_offset = 1;
37 	int idx = -1;
38 	bool iface_found = false;
39 
40 	if (argc > 2 &&
41 	    ((strcmp(argv[1], "-i") == 0) ||
42 	     (strncmp(argv[1], "-i", 2) == 0 && argv[1][2] != '\0'))) {
43 		/* Handle both "-i 2" and "-i2" */
44 		if (strcmp(argv[1], "-i") == 0) {
45 			idx = strtol(argv[2], NULL, 10);
46 			arg_offset = 3;
47 		} else {
48 			idx = strtol(&argv[1][2], NULL, 10);
49 			arg_offset = 2;
50 		}
51 		iface = net_if_get_by_index(idx);
52 		if (!iface) {
53 			shell_error(sh, "Interface index %d not found", idx);
54 			return -ENODEV;
55 		}
56 		net_if_get_name(iface, if_name, sizeof(if_name));
57 		if_name[sizeof(if_name) - 1] = '\0';
58 		iface_found = true;
59 	} else {
60 		/* Default to first Wi-Fi interface */
61 		iface = net_if_get_first_wifi();
62 		if (!iface) {
63 			shell_error(sh, "No Wi-Fi interface found");
64 			return -ENOENT;
65 		}
66 		net_if_get_name(iface, if_name, sizeof(if_name));
67 		if_name[sizeof(if_name) - 1] = '\0';
68 		arg_offset = 1;
69 		iface_found = true;
70 	}
71 
72 	if (!iface_found) {
73 		shell_error(sh, "No interface found");
74 		return -ENODEV;
75 	}
76 
77 	wpa_s = zephyr_get_handle_by_ifname(if_name);
78 	if (!wpa_s) {
79 		shell_error(sh, "No wpa_supplicant context for interface '%s'", if_name);
80 		return -ENODEV;
81 	}
82 
83 	if (argc <= arg_offset) {
84 		shell_error(sh, "Missing argument");
85 		return -EINVAL;
86 	}
87 
88 	argv[argc] = "interactive";
89 	argc++;
90 
91 	/* Remove wpa_cli from the argument list */
92 	return zephyr_wpa_ctrl_zephyr_cmd(wpa_s->ctrl_conn, argc - arg_offset, &argv[arg_offset]);
93 }
94 
95 #ifdef CONFIG_WIFI_NM_HOSTAPD_AP
cmd_hostapd_cli(const struct shell * sh,size_t argc,const char * argv[])96 static int cmd_hostapd_cli(const struct shell *sh, size_t argc, const char *argv[])
97 {
98 	struct net_if *iface = NULL;
99 	size_t arg_offset = 1;
100 	struct hostapd_iface *hapd_iface;
101 	int idx = -1;
102 	bool iface_found = false;
103 	char if_name[CONFIG_NET_INTERFACE_NAME_LEN + 1];
104 	int ret;
105 
106 	if (argc > 2 &&
107 	    ((strcmp(argv[1], "-i") == 0) ||
108 	     (strncmp(argv[1], "-i", 2) == 0 && argv[1][2] != '\0'))) {
109 		/* Handle both "-i 2" and "-i2" */
110 		if (strcmp(argv[1], "-i") == 0) {
111 			idx = strtol(argv[2], NULL, 10);
112 			arg_offset = 3;
113 		} else {
114 			idx = strtol(&argv[1][2], NULL, 10);
115 			arg_offset = 2;
116 		}
117 		iface = net_if_get_by_index(idx);
118 		if (!iface) {
119 			shell_error(sh, "Interface index %d not found", idx);
120 			return -ENODEV;
121 		}
122 		iface_found = true;
123 	} else {
124 		iface = net_if_get_wifi_sap();
125 		if (!iface) {
126 			shell_error(sh, "No Wi-Fi interface found");
127 			return -ENOENT;
128 		}
129 		arg_offset = 1;
130 		iface_found = true;
131 	}
132 
133 	if (!iface_found) {
134 		shell_error(sh, "No interface found");
135 		return -ENODEV;
136 	}
137 
138 	ret = net_if_get_name(iface, if_name, sizeof(if_name));
139 	if (!ret) {
140 		shell_error(sh, "Cannot get interface name (%d)", ret);
141 		return -ENODEV;
142 	}
143 
144 	hapd_iface = zephyr_get_hapd_handle_by_ifname(if_name);
145 	if (!hapd_iface) {
146 		shell_error(sh, "No hostapd context for interface '%s'", if_name);
147 		return -ENODEV;
148 	}
149 
150 	if (argc <= arg_offset) {
151 		shell_error(sh, "Missing argument");
152 		return -EINVAL;
153 	}
154 
155 	argv[argc] = "interactive";
156 	argc++;
157 
158 	/* Remove hostapd_cli from the argument list */
159 	return zephyr_hostapd_ctrl_zephyr_cmd(hapd_iface->ctrl_conn, argc - arg_offset,
160 					      &argv[arg_offset]);
161 }
162 #endif
163 
164 /* Persisting with "wpa_cli" naming for compatibility with Wi-Fi
165  * certification applications and scripts.
166  */
167 SHELL_CMD_REGISTER(wpa_cli,
168 		   NULL,
169 		   "wpa_cli [-i idx] <command> (only for internal use)",
170 		   cmd_wpa_cli);
171 #ifdef CONFIG_WIFI_NM_HOSTAPD_AP
172 SHELL_CMD_REGISTER(hostapd_cli, NULL,
173 		   "hostapd_cli [-i idx] <command> (only for internal use)",
174 		   cmd_hostapd_cli);
175 #endif
176