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