1 /*
2  * Copyright (C) 2024 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 "host/commands/assemble_cvd/network_flags.h"
18 
19 #include <arpa/inet.h>
20 #include <ifaddrs.h>
21 
22 #include <android-base/logging.h>
23 
24 namespace cuttlefish {
25 namespace {
26 
number_of_ones(unsigned long val)27 uint8_t number_of_ones(unsigned long val) {
28   uint8_t ret = 0;
29   while (val) {
30     ret += val % 2;
31     val >>= 1;
32   }
33   return ret;
34 }
35 
36 class NetConfig {
37  public:
38   uint8_t ril_prefixlen = -1;
39   std::string ril_ipaddr;
40   std::string ril_gateway;
41   std::string ril_dns;
42   std::string ril_broadcast;
43 
ObtainConfig(const std::string & interface,const std::string & dns)44   bool ObtainConfig(const std::string& interface, const std::string& dns) {
45     bool ret = ParseInterfaceAttributes(interface);
46     if (ret) {
47       ril_dns = dns;
48       LOG(DEBUG) << "Network config:";
49       LOG(DEBUG) << "ipaddr = " << ril_ipaddr;
50       LOG(DEBUG) << "gateway = " << ril_gateway;
51       LOG(DEBUG) << "dns = " << ril_dns;
52       LOG(DEBUG) << "broadcast = " << ril_broadcast;
53       LOG(DEBUG) << "prefix length = " << static_cast<int>(ril_prefixlen);
54     }
55     return ret;
56   }
57 
58  private:
ParseInterfaceAttributes(struct ifaddrs * ifa)59   bool ParseInterfaceAttributes(struct ifaddrs* ifa) {
60     struct sockaddr_in* sa;
61     char* addr_str;
62 
63     // Gateway
64     sa = reinterpret_cast<sockaddr_in*>(ifa->ifa_addr);
65     addr_str = inet_ntoa(sa->sin_addr);
66     this->ril_gateway = strtok(addr_str, "\n");
67     auto gateway_s_addr = ntohl(sa->sin_addr.s_addr);
68 
69     // Broadcast
70     sa = reinterpret_cast<sockaddr_in*>(ifa->ifa_broadaddr);
71     addr_str = inet_ntoa(sa->sin_addr);
72     this->ril_broadcast = strtok(addr_str, "\n");
73     auto broadcast_s_addr = ntohl(sa->sin_addr.s_addr);
74 
75     // Detect misconfigured network interfaces. All network interfaces must
76     // have a valid broadcast address set; if there is none set, glibc may
77     // return the interface address in the broadcast field. This causes
78     // no packets to be routed correctly from the guest.
79     if (this->ril_gateway == this->ril_broadcast) {
80       LOG(ERROR) << "Gateway and Broadcast addresses are the same on "
81                  << ifa->ifa_name << ", which is invalid.";
82       return false;
83     }
84 
85     // Netmask
86     sa = reinterpret_cast<sockaddr_in*>(ifa->ifa_netmask);
87     this->ril_prefixlen = number_of_ones(sa->sin_addr.s_addr);
88     auto netmask_s_addr = ntohl(sa->sin_addr.s_addr);
89 
90     // Address (Find an address in the network different than the network, the
91     // gateway and the broadcast)
92     auto network = gateway_s_addr & netmask_s_addr;
93     auto s_addr = network + 1;
94     // s_addr & ~netmask_s_addr is zero when s_addr wraps around the network
95     while (s_addr & ~netmask_s_addr) {
96       if (s_addr != gateway_s_addr && s_addr != broadcast_s_addr) {
97         break;
98       }
99       ++s_addr;
100     }
101     if (s_addr == network) {
102       LOG(ERROR) << "No available address found in interface " << ifa->ifa_name;
103       return false;
104     }
105     struct in_addr addr;
106     addr.s_addr = htonl(s_addr);
107     addr_str = inet_ntoa(addr);
108     this->ril_ipaddr = strtok(addr_str, "\n");
109     return true;
110   }
111 
ParseInterfaceAttributes(const std::string & interface)112   bool ParseInterfaceAttributes(const std::string& interface) {
113     struct ifaddrs *ifa_list{}, *ifa{};
114     bool ret = false;
115     getifaddrs(&ifa_list);
116     for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
117       if (strcmp(ifa->ifa_name, interface.c_str()) == 0 &&
118           ifa->ifa_addr->sa_family == AF_INET) {
119         ret = ParseInterfaceAttributes(ifa);
120         break;
121       }
122     }
123     freeifaddrs(ifa_list);
124     return ret;
125   }
126 };
127 
128 }  // namespace
129 
ConfigureNetworkSettings(const std::string & ril_dns_arg,const CuttlefishConfig::InstanceSpecific & const_instance,CuttlefishConfig::MutableInstanceSpecific & instance)130 Result<void> ConfigureNetworkSettings(
131     const std::string& ril_dns_arg,
132     const CuttlefishConfig::InstanceSpecific& const_instance,
133     CuttlefishConfig::MutableInstanceSpecific& instance) {
134   NetConfig netconfig;
135   // Check the mobile bridge first; this was the traditional way we configured
136   // the mobile interface. If that fails, it probably means we are using a
137   // newer version of cuttlefish-common, and we can use the tap device
138   // directly instead.
139   if (!netconfig.ObtainConfig(const_instance.mobile_bridge_name(),
140                               ril_dns_arg)) {
141     if (!netconfig.ObtainConfig(const_instance.mobile_tap_name(),
142                                 ril_dns_arg)) {
143       LOG(ERROR) << "Unable to get the network config. Assuming defaults.";
144       instance.set_ril_dns("8.8.8.8");
145       instance.set_ril_gateway("10.0.2.2");
146       instance.set_ril_ipaddr("10.0.2.15");
147       instance.set_ril_prefixlen(24);
148     }
149   }
150 
151   instance.set_ril_broadcast(netconfig.ril_broadcast);
152   instance.set_ril_dns(netconfig.ril_dns);
153   instance.set_ril_gateway(netconfig.ril_gateway);
154   instance.set_ril_ipaddr(netconfig.ril_ipaddr);
155   instance.set_ril_prefixlen(netconfig.ril_prefixlen);
156 
157   return {};
158 }
159 
160 }  // namespace cuttlefish