• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "host/libs/allocd/alloc_utils.h"
2 
3 #include <cstdint>
4 #include <fstream>
5 
6 #include "android-base/logging.h"
7 
8 namespace cuttlefish {
9 
RunExternalCommand(const std::string & command)10 int RunExternalCommand(const std::string& command) {
11   FILE* fp;
12   LOG(INFO) << "Running external command: " << command;
13   fp = popen(command.c_str(), "r");
14 
15   if (fp == nullptr) {
16     LOG(WARNING) << "Error running external command";
17     return -1;
18   }
19 
20   int status = pclose(fp);
21   int ret = -1;
22   if (status == -1) {
23     LOG(WARNING) << "pclose error";
24   } else {
25     if (WIFEXITED(status)) {
26       LOG(INFO) << "child process exited normally";
27       ret = WEXITSTATUS(status);
28     } else if (WIFSIGNALED(status)) {
29       LOG(WARNING) << "child process was terminated by a signal";
30     } else {
31       LOG(WARNING) << "child process did not terminate normally";
32     }
33   }
34   return ret;
35 }
36 
AddTapIface(const std::string & name)37 bool AddTapIface(const std::string& name) {
38   std::stringstream ss;
39   ss << "ip tuntap add dev " << name << " mode tap group cvdnetwork vnet_hdr";
40   auto add_command = ss.str();
41   LOG(INFO) << "Create tap interface: " << add_command;
42   int status = RunExternalCommand(add_command);
43   return status == 0;
44 }
45 
ShutdownIface(const std::string & name)46 bool ShutdownIface(const std::string& name) {
47   std::stringstream ss;
48   ss << "ip link set dev " << name << " down";
49   auto link_command = ss.str();
50   LOG(INFO) << "Shutdown tap interface: " << link_command;
51   int status = RunExternalCommand(link_command);
52 
53   return status == 0;
54 }
55 
BringUpIface(const std::string & name)56 bool BringUpIface(const std::string& name) {
57   std::stringstream ss;
58   ss << "ip link set dev " << name << " up";
59   auto link_command = ss.str();
60   LOG(INFO) << "Bring up tap interface: " << link_command;
61   int status = RunExternalCommand(link_command);
62 
63   return status == 0;
64 }
65 
CreateEthernetIface(const std::string & name,const std::string & bridge_name,bool has_ipv4_bridge,bool has_ipv6_bridge,bool use_ebtables_legacy)66 bool CreateEthernetIface(const std::string& name, const std::string& bridge_name,
67                          bool has_ipv4_bridge, bool has_ipv6_bridge,
68                          bool use_ebtables_legacy) {
69   // assume bridge exists
70 
71   EthernetNetworkConfig config{false, false, false};
72 
73   if (!CreateTap(name)) {
74     return false;
75   }
76 
77   config.has_tap = true;
78 
79   if (!LinkTapToBridge(name, bridge_name)) {
80     CleanupEthernetIface(name, config);
81     return false;
82   }
83 
84   if (!has_ipv4_bridge) {
85     if (!CreateEbtables(name, true, use_ebtables_legacy)) {
86       CleanupEthernetIface(name, config);
87       return false;
88     }
89     config.has_broute_ipv4 = true;
90   }
91 
92   if (!has_ipv6_bridge) {
93     if (CreateEbtables(name, false, use_ebtables_legacy)) {
94       CleanupEthernetIface(name, config);
95       return false;
96     }
97     config.has_broute_ipv6 = true;
98   }
99 
100   return true;
101 }
102 
MobileGatewayName(const std::string & ipaddr,uint16_t id)103 std::string MobileGatewayName(const std::string& ipaddr, uint16_t id) {
104   std::stringstream ss;
105   ss << ipaddr << "." << (4 * id + 1);
106   return ss.str();
107 }
108 
MobileNetworkName(const std::string & ipaddr,const std::string & netmask,uint16_t id)109 std::string MobileNetworkName(const std::string& ipaddr,
110                               const std::string& netmask, uint16_t id) {
111   std::stringstream ss;
112   ss << ipaddr << "." << (4 * id) << netmask;
113   return ss.str();
114 }
115 
CreateMobileIface(const std::string & name,uint16_t id,const std::string & ipaddr)116 bool CreateMobileIface(const std::string& name, uint16_t id,
117                        const std::string& ipaddr) {
118   if (id > kMaxIfaceNameId) {
119     LOG(ERROR) << "ID exceeds maximum value to assign a netmask: " << id;
120     return false;
121   }
122 
123   auto netmask = "/30";
124   auto gateway = MobileGatewayName(ipaddr, id);
125   auto network = MobileNetworkName(ipaddr, netmask, id);
126 
127   if (!CreateTap(name)) {
128     return false;
129   }
130 
131   if (!AddGateway(name, gateway, netmask)) {
132     DestroyIface(name);
133   }
134 
135   if (!IptableConfig(network, true)) {
136     DestroyGateway(name, gateway, netmask);
137     DestroyIface(name);
138     return false;
139   };
140 
141   return true;
142 }
143 
DestroyMobileIface(const std::string & name,uint16_t id,const std::string & ipaddr)144 bool DestroyMobileIface(const std::string& name, uint16_t id,
145                         const std::string& ipaddr) {
146   if (id > 63) {
147     LOG(ERROR) << "ID exceeds maximum value to assign a netmask: " << id;
148     return false;
149   }
150 
151   auto netmask = "/30";
152   auto gateway = MobileGatewayName(ipaddr, id);
153   auto network = MobileNetworkName(ipaddr, netmask, id);
154 
155   IptableConfig(network, false);
156   DestroyGateway(name, gateway, netmask);
157   return DestroyIface(name);
158 }
159 
AddGateway(const std::string & name,const std::string & gateway,const std::string & netmask)160 bool AddGateway(const std::string& name, const std::string& gateway,
161                 const std::string& netmask) {
162   std::stringstream ss;
163   ss << "ip addr add " << gateway << netmask << " broadcast + dev " << name;
164   auto command = ss.str();
165   LOG(INFO) << "setup gateway: " << command;
166   int status = RunExternalCommand(command);
167 
168   return status == 0;
169 }
170 
DestroyGateway(const std::string & name,const std::string & gateway,const std::string & netmask)171 bool DestroyGateway(const std::string& name, const std::string& gateway,
172                     const std::string& netmask) {
173   std::stringstream ss;
174   ss << "ip addr del " << gateway << netmask << " broadcast + dev " << name;
175   auto command = ss.str();
176   LOG(INFO) << "removing gateway: " << command;
177   int status = RunExternalCommand(command);
178 
179   return status == 0;
180 }
181 
DestroyEthernetIface(const std::string & name,bool has_ipv4_bridge,bool has_ipv6_bridge,bool use_ebtables_legacy)182 bool DestroyEthernetIface(const std::string& name, bool has_ipv4_bridge,
183                           bool has_ipv6_bridge, bool use_ebtables_legacy) {
184   if (!has_ipv6_bridge) {
185     DestroyEbtables(name, false, use_ebtables_legacy);
186   }
187 
188   if (!has_ipv4_bridge) {
189     DestroyEbtables(name, true, use_ebtables_legacy);
190   }
191 
192   return DestroyIface(name);
193 }
194 
CleanupEthernetIface(const std::string & name,const EthernetNetworkConfig & config)195 void CleanupEthernetIface(const std::string& name,
196                           const EthernetNetworkConfig& config) {
197   if (config.has_broute_ipv6) {
198     DestroyEbtables(name, false, config.use_ebtables_legacy);
199   }
200 
201   if (config.has_broute_ipv4) {
202     DestroyEbtables(name, true, config.use_ebtables_legacy);
203   }
204 
205   if (config.has_tap) {
206     DestroyIface(name);
207   }
208 }
209 
CreateEbtables(const std::string & name,bool use_ipv4,bool use_ebtables_legacy)210 bool CreateEbtables(const std::string& name, bool use_ipv4,
211                     bool use_ebtables_legacy) {
212   return EbtablesBroute(name, use_ipv4, true, use_ebtables_legacy) &&
213          EbtablesFilter(name, use_ipv4, true, use_ebtables_legacy);
214 }
215 
DestroyEbtables(const std::string & name,bool use_ipv4,bool use_ebtables_legacy)216 bool DestroyEbtables(const std::string& name, bool use_ipv4,
217                      bool use_ebtables_legacy) {
218   return EbtablesBroute(name, use_ipv4, false, use_ebtables_legacy) &&
219          EbtablesFilter(name, use_ipv4, false, use_ebtables_legacy);
220 }
221 
EbtablesBroute(const std::string & name,bool use_ipv4,bool add,bool use_ebtables_legacy)222 bool EbtablesBroute(const std::string& name, bool use_ipv4, bool add,
223                     bool use_ebtables_legacy) {
224   std::stringstream ss;
225   // we don't know the name of the ebtables program, but since we're going to
226   // exec this program name, make sure they can only choose between the two
227   // options we currently support, and not something they can overwrite
228   if (use_ebtables_legacy) {
229     ss << kEbtablesLegacyName;
230   } else {
231     ss << kEbtablesName;
232   }
233 
234   ss << " -t broute " << (add ? "-A" : "-D") << " BROUTING -p "
235      << (use_ipv4 ? "ipv4" : "ipv6") << " --in-if " << name << " -j DROP";
236   auto command = ss.str();
237   int status = RunExternalCommand(command);
238 
239   return status == 0;
240 }
241 
EbtablesFilter(const std::string & name,bool use_ipv4,bool add,bool use_ebtables_legacy)242 bool EbtablesFilter(const std::string& name, bool use_ipv4, bool add,
243                     bool use_ebtables_legacy) {
244   std::stringstream ss;
245   if (use_ebtables_legacy) {
246     ss << kEbtablesLegacyName;
247   } else {
248     ss << kEbtablesName;
249   }
250 
251   ss << " -t filter " << (add ? "-A" : "-D") << " FORWARD -p "
252      << (use_ipv4 ? "ipv4" : "ipv6") << " --out-if " << name << " -j DROP";
253   auto command = ss.str();
254   int status = RunExternalCommand(command);
255 
256   return status == 0;
257 }
258 
LinkTapToBridge(const std::string & tap_name,const std::string & bridge_name)259 bool LinkTapToBridge(const std::string& tap_name,
260                      const std::string& bridge_name) {
261   std::stringstream ss;
262   ss << "ip link set dev " << tap_name << " master " << bridge_name;
263   auto command = ss.str();
264   int status = RunExternalCommand(command);
265 
266   return status == 0;
267 }
268 
CreateTap(const std::string & name)269 bool CreateTap(const std::string& name) {
270   LOG(INFO) << "Attempt to create tap interface: " << name;
271   if (!AddTapIface(name)) {
272     LOG(WARNING) << "Failed to create tap interface: " << name;
273     return false;
274   }
275 
276   if (!BringUpIface(name)) {
277     LOG(WARNING) << "Failed to bring up tap interface: " << name;
278     DeleteIface(name);
279     return false;
280   }
281 
282   return true;
283 }
284 
DeleteIface(const std::string & name)285 bool DeleteIface(const std::string& name) {
286   std::stringstream ss;
287   ss << "ip link delete " << name;
288   auto link_command = ss.str();
289   LOG(INFO) << "Delete tap interface: " << link_command;
290   int status = RunExternalCommand(link_command);
291 
292   return status == 0;
293 }
294 
DestroyIface(const std::string & name)295 bool DestroyIface(const std::string& name) {
296   if (!ShutdownIface(name)) {
297     LOG(WARNING) << "Failed to shutdown tap interface: " << name;
298     // the interface might have already shutdown ... so ignore and try to remove
299     // the interface. In the future we could read from the pipe and handle this
300     // case more elegantly
301   }
302 
303   if (!DeleteIface(name)) {
304     LOG(WARNING) << "Failed to delete tap interface: " << name;
305     return false;
306   }
307 
308   return true;
309 }
310 
GetUserName(uid_t uid)311 std::optional<std::string> GetUserName(uid_t uid) {
312   passwd* pw = getpwuid(uid);
313   if (pw) {
314     std::string ret(pw->pw_name);
315     return ret;
316   }
317   return std::nullopt;
318 }
319 
CreateBridge(const std::string & name)320 bool CreateBridge(const std::string& name) {
321   std::stringstream ss;
322   ss << "ip link add name " << name
323      << " type bridge forward_delay 0 stp_state 0";
324 
325   auto command = ss.str();
326   LOG(INFO) << "create bridge: " << command;
327   int status = RunExternalCommand(command);
328 
329   if (status != 0) {
330     return false;
331   }
332 
333   return BringUpIface(name);
334 }
335 
DestroyBridge(const std::string & name)336 bool DestroyBridge(const std::string& name) { return DeleteIface(name); }
337 
SetupBridgeGateway(const std::string & bridge_name,const std::string & ipaddr)338 bool SetupBridgeGateway(const std::string& bridge_name,
339                         const std::string& ipaddr) {
340   GatewayConfig config{false, false, false};
341   auto gateway = ipaddr + ".1";
342   auto netmask = "/24";
343   auto network = ipaddr + ".0" + netmask;
344   auto dhcp_range = ipaddr + ".2," + ipaddr + ".255";
345 
346   if (!AddGateway(bridge_name, gateway, netmask)) {
347     return false;
348   }
349 
350   config.has_gateway = true;
351 
352   if (StartDnsmasq(bridge_name, gateway, dhcp_range)) {
353     CleanupBridgeGateway(bridge_name, ipaddr, config);
354     return false;
355   }
356 
357   config.has_dnsmasq = true;
358 
359   auto ret = IptableConfig(network, true);
360   if (!ret) {
361     CleanupBridgeGateway(bridge_name, ipaddr, config);
362     LOG(WARNING) << "Failed to setup ip tables";
363   }
364 
365   return ret;
366 }
367 
CleanupBridgeGateway(const std::string & name,const std::string & ipaddr,const GatewayConfig & config)368 void CleanupBridgeGateway(const std::string& name, const std::string& ipaddr,
369                           const GatewayConfig& config) {
370   auto gateway = ipaddr + ".1";
371   auto netmask = "/24";
372   auto network = ipaddr + ".0" + netmask;
373   auto dhcp_range = ipaddr + ".2," + ipaddr + ".255";
374 
375   if (config.has_iptable) {
376     IptableConfig(network, false);
377   }
378 
379   if (config.has_dnsmasq) {
380     StopDnsmasq(name);
381   }
382 
383   if (config.has_gateway) {
384     DestroyGateway(name, gateway, netmask);
385   }
386 }
387 
StartDnsmasq(const std::string & bridge_name,const std::string & gateway,const std::string & dhcp_range)388 bool StartDnsmasq(const std::string& bridge_name, const std::string& gateway,
389                   const std::string& dhcp_range) {
390   auto dns_servers = "8.8.8.8,8.8.4.4";
391   auto dns6_servers = "2001:4860:4860::8888,2001:4860:4860::8844";
392   std::stringstream ss;
393 
394   // clang-format off
395   ss <<
396   "dnsmasq"
397     " --port=0"
398     " --strict-order"
399     " --except-interface=lo"
400     " --interface=" << bridge_name <<
401     " --listen-address=" << gateway <<
402     " --bind-interfaces"
403     " --dhcp-range=" << dhcp_range <<
404     " --dhcp-option=\"option:dns-server," << dns_servers << "\""
405     " --dhcp-option=\"option6:dns-server," << dns6_servers << "\""
406     " --conf-file=\"\""
407     " --pid-file=/var/run/cuttlefish-dnsmasq-" << bridge_name << ".pid"
408     " --dhcp-leasefile=/var/run/cuttlefish-dnsmasq-" << bridge_name << ".leases"
409     " --dhcp-no-override ";
410   // clang-format on
411 
412   auto command = ss.str();
413   LOG(INFO) << "start_dnsmasq: " << command;
414   int status = RunExternalCommand(command);
415 
416   return status == 0;
417 }
418 
StopDnsmasq(const std::string & name)419 bool StopDnsmasq(const std::string& name) {
420   std::ifstream file;
421   std::string filename = "/var/run/cuttlefish-dnsmasq-" + name + ".pid";
422   LOG(INFO) << "stopping dsnmasq for interface: " << name;
423   file.open(filename);
424   if (file.is_open()) {
425     LOG(INFO) << "dnsmasq file:" << filename
426               << " could not be opened, assume dnsmaq has already stopped";
427     return true;
428   }
429 
430   std::string pid;
431   file >> pid;
432   file.close();
433   std::string command = "kill " + pid;
434   int status = RunExternalCommand(command);
435   auto ret = (status == 0);
436 
437   if (ret) {
438     LOG(INFO) << "dsnmasq for:" << name << "successfully stopped";
439   } else {
440     LOG(WARNING) << "Failed to stop dsnmasq for:" << name;
441   }
442   return ret;
443 }
444 
IptableConfig(const std::string & network,bool add)445 bool IptableConfig(const std::string& network, bool add) {
446   std::stringstream ss;
447   ss << "iptables -t nat " << (add ? "-A" : "-D") << " POSTROUTING -s "
448      << network << " -j MASQUERADE";
449 
450   auto command = ss.str();
451   LOG(INFO) << "iptable_config: " << command;
452   int status = RunExternalCommand(command);
453 
454   return status == 0;
455 }
456 
CreateEthernetBridgeIface(const std::string & name,const std::string & ipaddr)457 bool CreateEthernetBridgeIface(const std::string& name,
458                                const std::string& ipaddr) {
459   if (!CreateBridge(name)) {
460     return false;
461   }
462 
463   if (!SetupBridgeGateway(name, ipaddr)) {
464     DestroyBridge(name);
465     return false;
466   }
467 
468   return true;
469 }
470 
DestroyEthernetBridgeIface(const std::string & name,const std::string & ipaddr)471 bool DestroyEthernetBridgeIface(const std::string& name,
472                                 const std::string& ipaddr) {
473   GatewayConfig config{true, true, true};
474 
475   // Don't need to check if removing some part of the config failed, we need to
476   // remove the entire interface, so just ignore any error until the end
477   CleanupBridgeGateway(name, ipaddr, config);
478 
479   return DestroyBridge(name);
480 }
481 
482 }  // namespace cuttlefish
483