1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android-base/logging.h>
18 #include <android-base/parseint.h>
19 #include <gflags/gflags.h>
20
21 #include <cstdlib>
22 #include <functional>
23 #include <iomanip>
24 #include <iostream>
25 #include <string>
26 #include <unordered_map>
27 #include <vector>
28
29 #include "host/libs/config/cuttlefish_config.h"
30 #include "host/libs/wmediumd_controller/wmediumd_controller.h"
31
32 const std::string usageMessage =
33 "wmediumd control commandline utility\n\n"
34 " Usage: wmediumd_control [option] command [args...]\n\n"
35 " Commands:\n\n"
36 " set_snr mac1 mac2 snr\n"
37 " set SNR between two nodes. (0 <= snr <= 255)\n\n"
38 " reload_config [path]\n"
39 " force reload wmediumd configuration file\n\n"
40 " if path is not specified, reload current configuration file\n\n"
41 " start_pcap path\n"
42 " start packet capture and save capture result to file.\n"
43 " file format is pcap capture format.\n\n"
44 " stop_pcap\n"
45 " stop packet capture\n\n"
46 " list_stations\n"
47 " listing stations connected to wmediumd\n\n";
48
49 DEFINE_string(wmediumd_api_server, "",
50 "Unix socket path of wmediumd api server");
51
52 const int kMacAddrStringSize = 17;
53
ValidMacAddr(const std::string & macAddr)54 bool ValidMacAddr(const std::string& macAddr) {
55 if (macAddr.size() != kMacAddrStringSize) {
56 return false;
57 }
58
59 if (macAddr[2] != ':' || macAddr[5] != ':' || macAddr[8] != ':' ||
60 macAddr[11] != ':' || macAddr[14] != ':') {
61 return false;
62 }
63
64 for (int i = 0; i < kMacAddrStringSize; ++i) {
65 if ((i - 2) % 3 == 0) continue;
66 char c = macAddr[i];
67
68 if (isupper(c)) {
69 c = tolower(c);
70 }
71
72 if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) return false;
73 }
74
75 return true;
76 }
77
MacToString(const char * macAddr)78 std::string MacToString(const char* macAddr) {
79 std::stringstream result;
80
81 for (int i = 0; i < ETH_ALEN; i++) {
82 result << std::setfill('0') << std::setw(2) << std::right << std::hex
83 << static_cast<int>(static_cast<uint8_t>(macAddr[i]));
84
85 if (i != 5) {
86 result << ":";
87 }
88 }
89
90 return result.str();
91 }
92
HandleSetSnrCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)93 bool HandleSetSnrCommand(cuttlefish::WmediumdController& client,
94 const std::vector<std::string>& args) {
95 if (args.size() != 4) {
96 LOG(ERROR) << "error: set_snr must provide 3 options";
97 return false;
98 }
99
100 if (!ValidMacAddr(args[1])) {
101 LOG(ERROR) << "error: invalid mac address " << args[1];
102 return false;
103 }
104
105 if (!ValidMacAddr(args[2])) {
106 LOG(ERROR) << "error: invalid mac address " << args[2];
107 return false;
108 }
109
110 uint8_t snr = 0;
111
112 auto parseResult =
113 android::base::ParseUint<decltype(snr)>(args[3].c_str(), &snr);
114
115 if (!parseResult) {
116 if (errno == EINVAL) {
117 LOG(ERROR) << "error: cannot parse snr: " << args[3];
118 } else if (errno == ERANGE) {
119 LOG(ERROR) << "error: snr exceeded range: " << args[3];
120 }
121
122 return false;
123 }
124
125 if (!client.SetSnr(args[1], args[2], snr)) {
126 return false;
127 }
128
129 return true;
130 }
131
HandleReloadConfigCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)132 bool HandleReloadConfigCommand(cuttlefish::WmediumdController& client,
133 const std::vector<std::string>& args) {
134 if (args.size() > 2) {
135 LOG(ERROR) << "error: reload_config must provide 0 or 1 option";
136 return false;
137 }
138
139 if (args.size() == 2) {
140 return client.ReloadConfig(args[1]);
141 } else {
142 return client.ReloadCurrentConfig();
143 }
144 }
145
HandleStartPcapCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)146 bool HandleStartPcapCommand(cuttlefish::WmediumdController& client,
147 const std::vector<std::string>& args) {
148 if (args.size() != 2) {
149 LOG(ERROR) << "error: you must provide only 1 option(path)";
150 return false;
151 }
152
153 return client.StartPcap(args[1]);
154 }
155
HandleStopPcapCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)156 bool HandleStopPcapCommand(cuttlefish::WmediumdController& client,
157 const std::vector<std::string>& args) {
158 if (args.size() != 1) {
159 LOG(ERROR) << "error: you must not provide option";
160 return false;
161 }
162
163 return client.StopPcap();
164 }
165
HandleListStationsCommand(cuttlefish::WmediumdController & client,const std::vector<std::string> & args)166 bool HandleListStationsCommand(cuttlefish::WmediumdController& client,
167 const std::vector<std::string>& args) {
168 if (args.size() != 1) {
169 LOG(ERROR) << "error: you must not provide option";
170 return false;
171 }
172
173 auto result = client.GetStations();
174
175 if (!result) {
176 LOG(ERROR) << "error: failed to get stations";
177 return false;
178 }
179
180 auto stationList = result->GetStations();
181
182 std::cout << "Total stations : " << stationList.size() << std::endl
183 << std::endl;
184 std::cout << "Mac Address "
185 << "\t"
186 << "X Pos"
187 << "\t"
188 << "Y Pos"
189 << "\t"
190 << "TX Power" << std::endl;
191
192 for (auto& station : stationList) {
193 std::cout << MacToString(station.addr) << "\t" << std::setprecision(1)
194 << std::fixed << station.x << "\t" << std::setprecision(1)
195 << std::fixed << station.y << "\t" << station.tx_power
196 << std::endl;
197 }
198
199 std::cout << std::endl;
200
201 return true;
202 }
203
main(int argc,char ** argv)204 int main(int argc, char** argv) {
205 gflags::SetUsageMessage(usageMessage);
206 gflags::ParseCommandLineFlags(&argc, &argv, true);
207
208 std::vector<std::string> args;
209
210 for (int i = 1; i < argc; ++i) {
211 args.push_back(argv[i]);
212 }
213
214 if (args.size() == 0) {
215 LOG(ERROR) << "error: you must provide at least 1 argument";
216 gflags::ShowUsageWithFlags(argv[0]);
217 return -1;
218 }
219
220 std::string wmediumdApiServerPath(FLAGS_wmediumd_api_server);
221
222 if (wmediumdApiServerPath == "") {
223 const auto cuttlefishConfig = cuttlefish::CuttlefishConfig::Get();
224
225 if (!cuttlefishConfig) {
226 LOG(ERROR) << "error: cannot get global cuttlefish config";
227 return -1;
228 }
229
230 wmediumdApiServerPath = cuttlefishConfig->wmediumd_api_server_socket();
231 }
232
233 auto client = cuttlefish::WmediumdController::New(wmediumdApiServerPath);
234
235 if (!client) {
236 LOG(ERROR) << "error: cannot connect to " << wmediumdApiServerPath;
237 return -1;
238 }
239
240 auto commandMap =
241 std::unordered_map<std::string,
242 std::function<bool(cuttlefish::WmediumdController&,
243 const std::vector<std::string>&)>>{{
244 {"set_snr", HandleSetSnrCommand},
245 {"reload_config", HandleReloadConfigCommand},
246 {"start_pcap", HandleStartPcapCommand},
247 {"stop_pcap", HandleStopPcapCommand},
248 {"list_stations", HandleListStationsCommand},
249 }};
250
251 if (commandMap.find(args[0]) == std::end(commandMap)) {
252 LOG(ERROR) << "error: command " << args[0] << " does not exist";
253 gflags::ShowUsageWithFlags(argv[0]);
254 return -1;
255 }
256
257 if (!commandMap[args[0]](*client, args)) {
258 LOG(ERROR) << "error: failed to execute command " << args[0];
259 return -1;
260 }
261
262 return 0;
263 }
264