1 //
2 // Copyright (C) 2022 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 #include "host/commands/run_cvd/launch/launch.h"
17 
18 #include <string>
19 #include <unordered_set>
20 #include <utility>
21 #include <vector>
22 
23 #include <fruit/fruit.h>
24 
25 #include "common/libs/fs/shared_fd.h"
26 #include "common/libs/utils/files.h"
27 #include "common/libs/utils/result.h"
28 #include "host/libs/config/command_source.h"
29 #include "host/libs/config/known_paths.h"
30 
31 namespace cuttlefish {
32 namespace {
33 
34 // NetsimServer launches netsim server with fifos for radio HALs.
35 //
36 // netsimd -s '{devices:[
37 //  {"name":"0.0.0.0:5000", "chips":[
38 //    {"kind":"BLUETOOTH", "fdIn":10, "fdOut":11}],
39 //   "device_kind": {
40 //     "name":"0.0.0.0:5000", "kind":"CUTTLEFISH"}},
41 //  {"name":"0.0.0.0:5010", "chips":[
42 //    {"kind":"BLUETOOTH", "fdIn":14, "fdOut":15}],
43 //   "device_kind": {
44 //     "name":"0.0.0.0:5010", "kind":"CUTTLEFISH"}}]}
45 
46 // Chip and Device classes pass SharedFD fifos between ResultSetup and Commands
47 // and format the netsim json command line.
48 
49 class Chip {
50  public:
51   SharedFD fd_in;
52   SharedFD fd_out;
53 
Chip(std::string kind)54   Chip(std::string kind) : kind_(kind) {}
55 
56   // Append the chip information as Json to the command.
Append(Command & c) const57   void Append(Command& c) const {
58     c.AppendToLastParameter(R"({"kind":")", kind_, R"(","fdIn":)", fd_in,
59                             R"(,"fdOut":)", fd_out, "}");
60   }
61 
62  private:
63   std::string kind_;
64 };
65 
66 class Device {
67  public:
Device(std::string name)68   Device(std::string name) : name_(name) {}
69 
Append(Command & c) const70   void Append(Command& c) const {
71     c.AppendToLastParameter(R"({"name":")", name_, R"(","chips":[)");
72     for (int i = 0; i < chips.size(); ++i) {
73       chips[i].Append(c);
74       if (chips.size() - i > 1) {
75         c.AppendToLastParameter(",");
76       }
77     }
78     c.AppendToLastParameter(R"(],"device_info":{"name":")", name_,
79                             R"(", "kind":"CUTTLEFISH"}})");
80   }
81 
82   std::vector<Chip> chips;
83 
84  private:
85   std::string name_;
86 };
87 
88 class NetsimServer : public CommandSource {
89  public:
INJECT(NetsimServer (const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))90   INJECT(NetsimServer(const CuttlefishConfig& config,
91                       const CuttlefishConfig::InstanceSpecific& instance))
92       : config_(config), instance_(instance) {}
93 
94   // CommandSource
Commands()95   Result<std::vector<MonitorCommand>> Commands() override {
96     Command netsimd(NetsimdBinary());
97     netsimd.AddParameter("-s");
98     AddDevicesParameter(netsimd);
99     // Release SharedFDs, they've been duped by Command
100     devices_.clear();
101     // Port configuration.
102     netsimd.AddParameter("--hci_port=", config_.rootcanal_hci_port());
103 
104     // When no connector is requested, add the instance number
105     if (config_.netsim_connector_instance_num() ==
106         config_.netsim_instance_num()) {
107       // external instance numbers start at 1 not 0
108       netsimd.AddParameter("--instance_num=",
109                            config_.netsim_instance_num() + 1);
110     } else {
111       // If instance_num is not the target, then inform netsim to forward
112       // packets to another netsim daemon that was launched from cuttlefish with
113       // a different instance_num.
114       netsimd.AddParameter("--connector_instance_num=",
115                            config_.netsim_connector_instance_num() + 1);
116     }
117 
118     // Add parameters from passthrough option --netsim-args.
119     for (auto const& arg : config_.netsim_args()) {
120       netsimd.AddParameter(arg);
121     }
122 
123     // Add command for forwarding the HCI port to a vsock server.
124     Command hci_vsock_proxy(SocketVsockProxyBinary());
125     hci_vsock_proxy.AddParameter("--server_type=vsock");
126     hci_vsock_proxy.AddParameter("--server_vsock_port=",
127                                  config_.rootcanal_hci_port());
128     hci_vsock_proxy.AddParameter("--server_vsock_id=",
129                                  instance_.vsock_guest_cid());
130     hci_vsock_proxy.AddParameter("--client_type=tcp");
131     hci_vsock_proxy.AddParameter("--client_tcp_host=127.0.0.1");
132     hci_vsock_proxy.AddParameter("--client_tcp_port=",
133                                  config_.rootcanal_hci_port());
134 
135     // Add command for forwarding the test port to a vsock server.
136     Command test_vsock_proxy(SocketVsockProxyBinary());
137     test_vsock_proxy.AddParameter("--server_type=vsock");
138     test_vsock_proxy.AddParameter("--server_vsock_port=",
139                                   config_.rootcanal_test_port());
140     test_vsock_proxy.AddParameter("--server_vsock_id=",
141                                   instance_.vsock_guest_cid());
142     test_vsock_proxy.AddParameter("--client_type=tcp");
143     test_vsock_proxy.AddParameter("--client_tcp_host=127.0.0.1");
144     test_vsock_proxy.AddParameter("--client_tcp_port=",
145                                   config_.rootcanal_test_port());
146 
147     std::vector<MonitorCommand> commands;
148     commands.emplace_back(std::move(netsimd));
149     commands.emplace_back(std::move(hci_vsock_proxy));
150     commands.emplace_back(std::move(test_vsock_proxy));
151     return commands;
152   }
153 
154   // Convert devices_ to json for netsimd -s <arg>. The devices_, created and
155   // validated during ResultSetup, contains all the SharedFDs and meta-data.
156 
AddDevicesParameter(Command & c)157   void AddDevicesParameter(Command& c) {
158     c.AddParameter(R"({"devices":[)");
159     for (int i = 0; i < devices_.size(); ++i) {
160       devices_[i].Append(c);
161       if (devices_.size() - i > 1) {
162         c.AppendToLastParameter(",");
163       }
164     }
165     c.AppendToLastParameter("]}");
166   }
167 
168   // SetupFeature
Name() const169   std::string Name() const override { return "Netsim"; }
Enabled() const170   bool Enabled() const override { return instance_.start_netsim(); }
171 
172  private:
Dependencies() const173   std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
174 
ResultSetup()175   Result<void> ResultSetup() {
176     auto netsimd = HostBinaryPath("netsimd");
177     CF_EXPECT(FileExists(netsimd),
178               "Failed to find netsimd binary: " << netsimd);
179 
180     for (const auto& instance : config_.Instances()) {
181       Device device(instance.adb_ip_and_port());
182       // Add bluetooth chip if enabled
183       if (config_.netsim_radio_enabled(
184               CuttlefishConfig::NetsimRadio::Bluetooth)) {
185         Chip chip("BLUETOOTH");
186         chip.fd_in = CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.in"));
187         chip.fd_out = CF_EXPECT(MakeFifo(instance, "bt_fifo_vm.out"));
188         device.chips.emplace_back(chip);
189       }
190       // Add uwb chip if enabled
191       if (config_.netsim_radio_enabled(CuttlefishConfig::NetsimRadio::Uwb)) {
192         Chip chip("UWB");
193         chip.fd_in = CF_EXPECT(MakeFifo(instance, "uwb_fifo_vm.in"));
194         chip.fd_out = CF_EXPECT(MakeFifo(instance, "uwb_fifo_vm.out"));
195         device.chips.emplace_back(chip);
196       }
197       // Add other chips if enabled
198       devices_.emplace_back(device);
199     }
200     return {};
201   }
202 
MakeFifo(const CuttlefishConfig::InstanceSpecific & instance,const char * relative_path)203   Result<SharedFD> MakeFifo(const CuttlefishConfig::InstanceSpecific& instance,
204                             const char* relative_path) {
205     auto path = instance.PerInstanceInternalPath(relative_path);
206     return CF_EXPECT(SharedFD::Fifo(path, 0660));
207   }
208 
209  private:
210   std::vector<Device> devices_;
211   const CuttlefishConfig& config_;
212   const CuttlefishConfig::InstanceSpecific& instance_;
213 };
214 
215 }  // namespace
216 
217 fruit::Component<fruit::Required<const CuttlefishConfig,
218                                  const CuttlefishConfig::InstanceSpecific>>
NetsimServerComponent()219 NetsimServerComponent() {
220   return fruit::createComponent()
221       .addMultibinding<CommandSource, NetsimServer>()
222       .addMultibinding<SetupFeature, NetsimServer>();
223 }
224 
225 }  // namespace cuttlefish
226