1 /*
2  * Copyright (C) 2019 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 "common/libs/utils/network.h"
18 
19 #include <arpa/inet.h>
20 #include <linux/if.h>
21 #include <linux/if_tun.h>
22 #include <linux/types.h>
23 #include <linux/if_packet.h>
24 #include <netinet/ip.h>
25 #include <netinet/udp.h>
26 #include <netinet/ether.h>
27 #include <string.h>
28 
29 #include <android-base/strings.h>
30 #include "android-base/logging.h"
31 
32 #include "common/libs/fs/shared_buf.h"
33 #include "common/libs/utils/environment.h"
34 #include "common/libs/utils/subprocess.h"
35 
36 namespace cuttlefish {
37 namespace {
38 
DefaultHostArtifactsPath(const std::string & file_name)39 static std::string DefaultHostArtifactsPath(const std::string& file_name) {
40   return (StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
41           "/") +
42          file_name;
43 }
44 
45 // This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
46 // the version of that header that ships with android in Pie does not include
47 // that struct (it was added in Q).
48 // This is what that struct looks like:
49 // struct virtio_net_hdr_v1 {
50 // u8 flags;
51 // u8 gso_type;
52 // u16 hdr_len;
53 // u16 gso_size;
54 // u16 csum_start;
55 // u16 csum_offset;
56 // u16 num_buffers;
57 // };
58 static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12;
59 
ParseAddress(const std::string & address,const std::string & separator,const std::size_t expected_size,int base,std::uint8_t * out)60 bool ParseAddress(const std::string& address, const std::string& separator,
61                   const std::size_t expected_size, int base, std::uint8_t* out) {
62   auto components = android::base::Split(address, separator);
63   if (components.size() != expected_size) {
64     LOG(ERROR) << "Address \"" << address << "\" had wrong number of parts. "
65                << "Had " << components.size() << ", expected " << expected_size;
66     return false;
67   }
68   for (int i = 0; i < expected_size; i++) {
69     auto out_part = std::stoi(components[i], nullptr, base);
70     if (out_part < 0 || out_part > 255) {
71       LOG(ERROR) << "Address part " << i << " (" << out_part
72                  << "): outside range [0,255]";
73       return false;
74     }
75     out[i] = (std::uint8_t) out_part;
76   }
77   return true;
78 }
79 
ParseMacAddress(const std::string & address,std::uint8_t mac[6])80 bool ParseMacAddress(const std::string& address, std::uint8_t mac[6]) {
81   return ParseAddress(address, ":", 6, 16, mac);
82 }
83 
ParseIpAddress(const std::string & address,std::uint8_t ip[4])84 bool ParseIpAddress(const std::string& address, std::uint8_t ip[4]) {
85   return ParseAddress(address, ".", 4, 10, ip);
86 }
87 
88 }  // namespace
89 
OpenTapInterface(const std::string & interface_name)90 SharedFD OpenTapInterface(const std::string& interface_name) {
91   constexpr auto TUNTAP_DEV = "/dev/net/tun";
92 
93   auto tap_fd = SharedFD::Open(TUNTAP_DEV, O_RDWR | O_NONBLOCK);
94   if (!tap_fd->IsOpen()) {
95     LOG(ERROR) << "Unable to open tun device: " << tap_fd->StrError();
96     return tap_fd;
97   }
98 
99   if (HostArch() == "aarch64") {
100     auto tapsetiff_path = DefaultHostArtifactsPath("bin/tapsetiff");
101     Command cmd(tapsetiff_path);
102     cmd.AddParameter(tap_fd);
103     cmd.AddParameter(interface_name.c_str());
104     int ret = cmd.Start().Wait();
105     if (ret != 0) {
106       LOG(ERROR) << "Unable to run tapsetiff.py. Exited with status " << ret;
107       tap_fd->Close();
108       return SharedFD();
109     }
110   } else {
111     struct ifreq ifr;
112     memset(&ifr, 0, sizeof(ifr));
113     ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
114     strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ);
115 
116     int err = tap_fd->Ioctl(TUNSETIFF, &ifr);
117     if (err < 0) {
118       LOG(ERROR) << "Unable to connect to " << interface_name
119                  << " tap interface: " << tap_fd->StrError();
120       tap_fd->Close();
121       return SharedFD();
122     }
123 
124     // The interface's configuration may have been modified or just not set
125     // correctly on creation. While qemu checks this and enforces the right
126     // configuration, crosvm does not, so it needs to be set before it's passed to
127     // it.
128     tap_fd->Ioctl(TUNSETOFFLOAD,
129                   reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 |
130                                         TUN_F_TSO6));
131     int len = SIZE_OF_VIRTIO_NET_HDR_V1;
132     tap_fd->Ioctl(TUNSETVNETHDRSZ, &len);
133   }
134 
135   return tap_fd;
136 }
137 
TapInterfacesInUse()138 std::set<std::string> TapInterfacesInUse() {
139   Command cmd("/bin/bash");
140   cmd.AddParameter("-c");
141   cmd.AddParameter("egrep -h -e \"^iff:.*\" /proc/*/fdinfo/*");
142   std::string stdin, stdout, stderr;
143   RunWithManagedStdio(std::move(cmd), &stdin, &stdout, &stderr);
144   auto lines = android::base::Split(stdout, "\n");
145   std::set<std::string> tap_interfaces;
146   for (const auto& line : lines) {
147     if (line == "") {
148       continue;
149     }
150     if (!android::base::StartsWith(line, "iff:\t")) {
151       LOG(ERROR) << "Unexpected line \"" << line << "\"";
152       continue;
153     }
154     tap_interfaces.insert(line.substr(std::string("iff:\t").size()));
155   }
156   return tap_interfaces;
157 }
158 
ParseDnsmasqLeases(SharedFD lease_file)159 std::vector<DnsmasqDhcp4Lease> ParseDnsmasqLeases(SharedFD lease_file) {
160   std::string lease_file_content;
161   if (ReadAll(lease_file, &lease_file_content) < 0) {
162     LOG(ERROR) << "Could not read lease_file: \"" << lease_file->StrError()
163                << "\". This may result in difficulty connecting to guest wifi.";
164     return {};
165   }
166   std::vector<DnsmasqDhcp4Lease> leases;
167   auto lease_file_lines = android::base::Split(lease_file_content, "\n");
168   for (const auto& line : lease_file_lines) {
169     if (line == "") {
170       continue;
171     }
172     auto line_elements = android::base::Split(line, " ");
173     if (line_elements.size() != 5) {
174       LOG(WARNING) << "Could not parse lease line: \"" << line << "\"\n";
175       continue;
176     }
177     DnsmasqDhcp4Lease lease;
178     lease.expiry = std::stoll(line_elements[0]);
179     if (!ParseMacAddress(line_elements[1], &lease.mac_address[0])) {
180       LOG(WARNING) << "Could not parse MAC address: \'" << line_elements[1]
181                    << "\"";
182       continue;
183     }
184     if (!ParseIpAddress(line_elements[2], &lease.ip_address[0])) {
185       LOG(WARNING) << "Could not parse IP address: " << line_elements[2]
186                    << "\"";
187     }
188     lease.hostname = line_elements[3];
189     lease.client_id = line_elements[4];
190     leases.push_back(lease);
191   }
192   return leases;
193 }
194 
operator <<(std::ostream & out,const DnsmasqDhcp4Lease & lease)195 std::ostream& operator<<(std::ostream& out, const DnsmasqDhcp4Lease& lease) {
196   out << "DnsmasqDhcp4Lease(lease_time = \"" << std::dec << lease.expiry
197       << ", mac_address = \"" << std::hex;
198   for (int i = 0; i < 5; i++) {
199     out << (int) lease.mac_address[i] << ":";
200   }
201   out << (int) lease.mac_address[5] << "\", ip_address = \"" << std::dec;
202   for (int i = 0; i < 3; i++) {
203     out << (int) lease.ip_address[i] << ".";
204   }
205   return out << (int) lease.ip_address[3] << "\", hostname = \""
206              << lease.hostname << "\", client_id = \"" << lease.client_id
207              << "\")";
208 }
209 
210 struct __attribute__((packed)) Dhcp4MessageTypeOption {
211   std::uint8_t code;
212   std::uint8_t len;
213   std::uint8_t message_type;
214 };
215 
216 struct __attribute__((packed)) Dhcp4ServerIdentifier {
217   std::uint8_t code;
218   std::uint8_t len;
219   std::uint8_t server_ip[4];
220 };
221 
222 struct __attribute__((packed)) Dhcp4ReleaseMessage {
223   std::uint8_t op;
224   std::uint8_t htype;
225   std::uint8_t hlen;
226   std::uint8_t hops;
227   __be32 xid;
228   __be16 secs;
229   __be16 flags;
230   std::uint8_t client_ip[4];
231   std::uint8_t assigned_ip[4];
232   std::uint8_t server_ip[4];
233   std::uint8_t gateway_ip[4];
234   std::uint8_t client_harware_address[16];
235   std::uint8_t server_name[64];
236   std::uint8_t boot_filename[128];
237   std::uint8_t magic_cookie[4];
238   Dhcp4MessageTypeOption message_type;
239   Dhcp4ServerIdentifier server_identifier;
240   std::uint8_t end_code;
241 };
242 
243 struct __attribute__((packed)) CompleteReleaseFrame {
244   std::uint8_t vnet[SIZE_OF_VIRTIO_NET_HDR_V1];
245   ether_header eth;
246   iphdr ip;
247   udphdr udp;
248   Dhcp4ReleaseMessage dhcp;
249 };
250 
ip_checksum(std::uint16_t * buf,std::size_t size)251 static std::uint16_t ip_checksum(std::uint16_t *buf, std::size_t size) {
252   std::uint32_t sum = 0;
253   for (std::size_t i = 0; i < size; i++) {
254     sum += buf[i];
255   }
256   sum = (sum >> 16) + (sum & 0xFFFF);
257   sum += sum >> 16;
258   return (std::uint16_t) ~sum;
259 }
260 
ReleaseDhcp4(SharedFD tap,const std::uint8_t mac_address[6],const std::uint8_t ip_address[4],const std::uint8_t dhcp_server_ip[4])261 bool ReleaseDhcp4(SharedFD tap, const std::uint8_t mac_address[6],
262                   const std::uint8_t ip_address[4],
263                   const std::uint8_t dhcp_server_ip[4]) {
264   CompleteReleaseFrame frame = {};
265   *reinterpret_cast<std::uint16_t*>(&frame.vnet[2]) = // hdr_len, little-endian
266       htole16(sizeof(ether_header) + sizeof(iphdr) + sizeof(udphdr));
267 
268   memcpy(frame.eth.ether_shost, mac_address, 6);
269   memset(frame.eth.ether_dhost, 255, 6); // Broadcast
270   frame.eth.ether_type = htobe16(ETH_P_IP);
271 
272   frame.ip.ihl = 5;
273   frame.ip.version = 4;
274   frame.ip.id = 0;
275   frame.ip.ttl = 64; // hops
276   frame.ip.protocol = 17; // UDP
277   memcpy((std::uint8_t*) &frame.ip.saddr, ip_address, 4);
278   frame.ip.daddr = *(std::uint32_t*) dhcp_server_ip;
279   frame.ip.tot_len = htobe16(sizeof(frame.ip) + sizeof(frame.udp)
280                              + sizeof(frame.dhcp));
281   iphdr ip_copy = frame.ip; // original, it's in a packed struct
282   frame.ip.check = ip_checksum((unsigned short*) &ip_copy,
283                                sizeof(ip_copy) / sizeof(short));
284 
285   frame.udp.source = htobe16(68);
286   frame.udp.dest = htobe16(67);
287   frame.udp.len = htobe16(sizeof(frame.udp) + sizeof(frame.dhcp));
288 
289   frame.dhcp.op = 1; /* bootrequest */
290   frame.dhcp.htype = 1; // Ethernet
291   frame.dhcp.hlen = 6; /* mac address length */
292   frame.dhcp.xid = rand();
293   frame.dhcp.secs = htobe16(3);
294   frame.dhcp.flags = 0;
295   memcpy(frame.dhcp.client_ip, ip_address, 4);
296   memcpy(frame.dhcp.client_harware_address, mac_address, 6);
297   std::uint8_t magic_cookie[4] = {99, 130, 83, 99};
298   memcpy(frame.dhcp.magic_cookie, magic_cookie, sizeof(magic_cookie));
299   frame.dhcp.message_type = { .code = 53, .len = 1, .message_type = 7 };
300   frame.dhcp.server_identifier.code = 54;
301   frame.dhcp.server_identifier.len = 4;
302   memcpy(frame.dhcp.server_identifier.server_ip, dhcp_server_ip, 4);
303   frame.dhcp.end_code = 255;
304 
305   if (tap->Write((void*) &frame, sizeof(frame)) != sizeof(frame)) {
306     LOG(ERROR) << "Could not write dhcprelease frame: \"" << tap->StrError()
307                << "\". Connecting to wifi will likely not work.";
308     return false;
309   }
310   return true;
311 }
312 
313 }  // namespace cuttlefish
314