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 // Kernel headers don't mix well with userspace headers, but there is no
20 // userspace header that provides the if_tun.h #defines. Include the kernel
21 // header, but move conflicting definitions out of the way using macros.
22 #define ethhdr __kernel_ethhdr
23 #include <linux/if_tun.h>
24 #undef ethhdr
25
26 #include <endian.h>
27 #include <fcntl.h>
28 #include <ifaddrs.h>
29 #include <linux/if_ether.h>
30 #include <linux/types.h>
31 #include <net/ethernet.h>
32 #include <net/if.h>
33 #include <netinet/ip.h>
34 #include <netinet/udp.h>
35
36 #include <cstdlib>
37 #include <cstring>
38 #include <functional>
39 #include <ios>
40 #include <memory>
41 #include <ostream>
42 #include <iomanip>
43 #include <set>
44 #include <string>
45 #include <utility>
46 #include <vector>
47
48 #include <android-base/logging.h>
49 #include <android-base/strings.h>
50
51 #include "common/libs/fs/shared_buf.h"
52 #include "common/libs/utils/subprocess.h"
53
54 namespace cuttlefish {
55 namespace {
56
57 // This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
58 // the version of that header that ships with android in Pie does not include
59 // that struct (it was added in Q).
60 // This is what that struct looks like:
61 // struct virtio_net_hdr_v1 {
62 // u8 flags;
63 // u8 gso_type;
64 // u16 hdr_len;
65 // u16 gso_size;
66 // u16 csum_start;
67 // u16 csum_offset;
68 // u16 num_buffers;
69 // };
70 static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12;
71
ParseAddress(const std::string & address,const std::string & separator,const std::size_t expected_size,int base,std::uint8_t * out)72 bool ParseAddress(const std::string& address, const std::string& separator,
73 const std::size_t expected_size, int base, std::uint8_t* out) {
74 auto components = android::base::Split(address, separator);
75 if (components.size() != expected_size) {
76 LOG(ERROR) << "Address \"" << address << "\" had wrong number of parts. "
77 << "Had " << components.size() << ", expected " << expected_size;
78 return false;
79 }
80 for (int i = 0; i < expected_size; i++) {
81 auto out_part = std::stoi(components[i], nullptr, base);
82 if (out_part < 0 || out_part > 255) {
83 LOG(ERROR) << "Address part " << i << " (" << out_part
84 << "): outside range [0,255]";
85 return false;
86 }
87 out[i] = (std::uint8_t) out_part;
88 }
89 return true;
90 }
91
ParseMacAddress(const std::string & address,std::uint8_t mac[6])92 bool ParseMacAddress(const std::string& address, std::uint8_t mac[6]) {
93 return ParseAddress(address, ":", 6, 16, mac);
94 }
95
ParseIpAddress(const std::string & address,std::uint8_t ip[4])96 bool ParseIpAddress(const std::string& address, std::uint8_t ip[4]) {
97 return ParseAddress(address, ".", 4, 10, ip);
98 }
99
100 /**
101 * Generate mac address following:
102 * 00:1a:11:e0:cf:index
103 * ________ __ ______
104 * | | |
105 * | type (e0, e1, etc)
106 */
GenerateMacForInstance(int index,uint8_t type,std::uint8_t out[6])107 void GenerateMacForInstance(int index, uint8_t type, std::uint8_t out[6]) {
108 // the first octet must be even
109 out[0] = 0x00;
110 out[1] = 0x1a;
111 out[2] = 0x11;
112 out[3] = type;
113 out[4] = 0xcf;
114 out[5] = static_cast<std::uint8_t>(index);
115 }
116
117 } // namespace
118
NetworkInterfaceExists(const std::string & interface_name)119 bool NetworkInterfaceExists(const std::string& interface_name) {
120 struct ifaddrs *ifa_list{}, *ifa{};
121 bool ret = false;
122 getifaddrs(&ifa_list);
123 for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
124 if (strcmp(ifa->ifa_name, interface_name.c_str()) == 0) {
125 ret = true;
126 break;
127 }
128 }
129 freeifaddrs(ifa_list);
130 return ret;
131 }
132
OpenTapInterface(const std::string & interface_name)133 SharedFD OpenTapInterface(const std::string& interface_name) {
134 constexpr auto TUNTAP_DEV = "/dev/net/tun";
135
136 auto tap_fd = SharedFD::Open(TUNTAP_DEV, O_RDWR | O_NONBLOCK);
137 if (!tap_fd->IsOpen()) {
138 LOG(ERROR) << "Unable to open tun device: " << tap_fd->StrError();
139 return tap_fd;
140 }
141
142 struct ifreq ifr;
143 memset(&ifr, 0, sizeof(ifr));
144 ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
145 strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ);
146
147 int err = tap_fd->Ioctl(TUNSETIFF, &ifr);
148 if (err < 0) {
149 LOG(ERROR) << "Unable to connect to " << interface_name
150 << " tap interface: " << tap_fd->StrError();
151 tap_fd->Close();
152 return SharedFD();
153 }
154
155 // The interface's configuration may have been modified or just not set
156 // correctly on creation. While qemu checks this and enforces the right
157 // configuration, crosvm does not, so it needs to be set before it's passed to
158 // it.
159 tap_fd->Ioctl(TUNSETOFFLOAD,
160 reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 |
161 TUN_F_TSO6));
162 int len = SIZE_OF_VIRTIO_NET_HDR_V1;
163 tap_fd->Ioctl(TUNSETVNETHDRSZ, &len);
164 return tap_fd;
165 }
166
TapInterfacesInUse()167 std::set<std::string> TapInterfacesInUse() {
168 Command cmd("/bin/bash");
169 cmd.AddParameter("-c");
170 cmd.AddParameter("egrep -h -e \"^iff:.*\" /proc/*/fdinfo/*");
171 std::string stdin_str, stdout_str, stderr_str;
172 RunWithManagedStdio(std::move(cmd), &stdin_str, &stdout_str, &stderr_str);
173 auto lines = android::base::Split(stdout_str, "\n");
174 std::set<std::string> tap_interfaces;
175 for (const auto& line : lines) {
176 if (line == "") {
177 continue;
178 }
179 if (!android::base::StartsWith(line, "iff:\t")) {
180 LOG(ERROR) << "Unexpected line \"" << line << "\"";
181 continue;
182 }
183 tap_interfaces.insert(line.substr(std::string("iff:\t").size()));
184 }
185 return tap_interfaces;
186 }
187
ParseDnsmasqLeases(SharedFD lease_file)188 std::vector<DnsmasqDhcp4Lease> ParseDnsmasqLeases(SharedFD lease_file) {
189 std::string lease_file_content;
190 if (ReadAll(lease_file, &lease_file_content) < 0) {
191 LOG(ERROR) << "Could not read lease_file: \"" << lease_file->StrError()
192 << "\". This may result in difficulty connecting to guest wifi.";
193 return {};
194 }
195 std::vector<DnsmasqDhcp4Lease> leases;
196 auto lease_file_lines = android::base::Split(lease_file_content, "\n");
197 for (const auto& line : lease_file_lines) {
198 if (line == "") {
199 continue;
200 }
201 auto line_elements = android::base::Split(line, " ");
202 if (line_elements.size() != 5) {
203 LOG(WARNING) << "Could not parse lease line: \"" << line << "\"\n";
204 continue;
205 }
206 DnsmasqDhcp4Lease lease;
207 lease.expiry = std::stoll(line_elements[0]);
208 if (!ParseMacAddress(line_elements[1], &lease.mac_address[0])) {
209 LOG(WARNING) << "Could not parse MAC address: \'" << line_elements[1]
210 << "\"";
211 continue;
212 }
213 if (!ParseIpAddress(line_elements[2], &lease.ip_address[0])) {
214 LOG(WARNING) << "Could not parse IP address: " << line_elements[2]
215 << "\"";
216 }
217 lease.hostname = line_elements[3];
218 lease.client_id = line_elements[4];
219 leases.push_back(lease);
220 }
221 return leases;
222 }
223
operator <<(std::ostream & out,const DnsmasqDhcp4Lease & lease)224 std::ostream& operator<<(std::ostream& out, const DnsmasqDhcp4Lease& lease) {
225 out << "DnsmasqDhcp4Lease(lease_time = \"" << std::dec << lease.expiry
226 << ", mac_address = \"" << std::hex;
227 for (int i = 0; i < 5; i++) {
228 out << (int) lease.mac_address[i] << ":";
229 }
230 out << (int) lease.mac_address[5] << "\", ip_address = \"" << std::dec;
231 for (int i = 0; i < 3; i++) {
232 out << (int) lease.ip_address[i] << ".";
233 }
234 return out << (int) lease.ip_address[3] << "\", hostname = \""
235 << lease.hostname << "\", client_id = \"" << lease.client_id
236 << "\")";
237 }
238
239 struct __attribute__((packed)) Dhcp4MessageTypeOption {
240 std::uint8_t code;
241 std::uint8_t len;
242 std::uint8_t message_type;
243 };
244
245 struct __attribute__((packed)) Dhcp4ServerIdentifier {
246 std::uint8_t code;
247 std::uint8_t len;
248 std::uint8_t server_ip[4];
249 };
250
251 struct __attribute__((packed)) Dhcp4ReleaseMessage {
252 std::uint8_t op;
253 std::uint8_t htype;
254 std::uint8_t hlen;
255 std::uint8_t hops;
256 __be32 xid;
257 __be16 secs;
258 __be16 flags;
259 std::uint8_t client_ip[4];
260 std::uint8_t assigned_ip[4];
261 std::uint8_t server_ip[4];
262 std::uint8_t gateway_ip[4];
263 std::uint8_t client_harware_address[16];
264 std::uint8_t server_name[64];
265 std::uint8_t boot_filename[128];
266 std::uint8_t magic_cookie[4];
267 Dhcp4MessageTypeOption message_type;
268 Dhcp4ServerIdentifier server_identifier;
269 std::uint8_t end_code;
270 };
271
272 struct __attribute__((packed)) CompleteReleaseFrame {
273 std::uint8_t vnet[SIZE_OF_VIRTIO_NET_HDR_V1];
274 ether_header eth;
275 iphdr ip;
276 udphdr udp;
277 Dhcp4ReleaseMessage dhcp;
278 };
279
ip_checksum(std::uint16_t * buf,std::size_t size)280 static std::uint16_t ip_checksum(std::uint16_t *buf, std::size_t size) {
281 std::uint32_t sum = 0;
282 for (std::size_t i = 0; i < size; i++) {
283 sum += buf[i];
284 }
285 sum = (sum >> 16) + (sum & 0xFFFF);
286 sum += sum >> 16;
287 return (std::uint16_t) ~sum;
288 }
289
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])290 bool ReleaseDhcp4(SharedFD tap, const std::uint8_t mac_address[6],
291 const std::uint8_t ip_address[4],
292 const std::uint8_t dhcp_server_ip[4]) {
293 CompleteReleaseFrame frame = {};
294 *reinterpret_cast<std::uint16_t*>(&frame.vnet[2]) = // hdr_len, little-endian
295 htole16(sizeof(ether_header) + sizeof(iphdr) + sizeof(udphdr));
296
297 memcpy(frame.eth.ether_shost, mac_address, 6);
298 memset(frame.eth.ether_dhost, 255, 6); // Broadcast
299 frame.eth.ether_type = htobe16(ETH_P_IP);
300
301 frame.ip.ihl = 5;
302 frame.ip.version = 4;
303 frame.ip.id = 0;
304 frame.ip.ttl = 64; // hops
305 frame.ip.protocol = 17; // UDP
306 memcpy((std::uint8_t*) &frame.ip.saddr, ip_address, 4);
307 frame.ip.daddr = *(std::uint32_t*) dhcp_server_ip;
308 frame.ip.tot_len = htobe16(sizeof(frame.ip) + sizeof(frame.udp)
309 + sizeof(frame.dhcp));
310 iphdr ip_copy = frame.ip; // original, it's in a packed struct
311 frame.ip.check = ip_checksum((unsigned short*) &ip_copy,
312 sizeof(ip_copy) / sizeof(short));
313
314 frame.udp.source = htobe16(68);
315 frame.udp.dest = htobe16(67);
316 frame.udp.len = htobe16(sizeof(frame.udp) + sizeof(frame.dhcp));
317
318 frame.dhcp.op = 1; /* bootrequest */
319 frame.dhcp.htype = 1; // Ethernet
320 frame.dhcp.hlen = 6; /* mac address length */
321 frame.dhcp.xid = rand();
322 frame.dhcp.secs = htobe16(3);
323 frame.dhcp.flags = 0;
324 memcpy(frame.dhcp.client_ip, ip_address, 4);
325 memcpy(frame.dhcp.client_harware_address, mac_address, 6);
326 std::uint8_t magic_cookie[4] = {99, 130, 83, 99};
327 memcpy(frame.dhcp.magic_cookie, magic_cookie, sizeof(magic_cookie));
328 frame.dhcp.message_type = { .code = 53, .len = 1, .message_type = 7 };
329 frame.dhcp.server_identifier.code = 54;
330 frame.dhcp.server_identifier.len = 4;
331 memcpy(frame.dhcp.server_identifier.server_ip, dhcp_server_ip, 4);
332 frame.dhcp.end_code = 255;
333
334 if (tap->Write((void*) &frame, sizeof(frame)) != sizeof(frame)) {
335 LOG(ERROR) << "Could not write dhcprelease frame: \"" << tap->StrError()
336 << "\". Connecting to wifi will likely not work.";
337 return false;
338 }
339 return true;
340 }
341
ReleaseDhcpLeases(const std::string & lease_path,SharedFD tap_fd,const std::uint8_t dhcp_server_ip[4])342 bool ReleaseDhcpLeases(const std::string& lease_path, SharedFD tap_fd,
343 const std::uint8_t dhcp_server_ip[4]) {
344 auto lease_file_fd = SharedFD::Open(lease_path, O_RDONLY);
345 if (!lease_file_fd->IsOpen()) {
346 LOG(ERROR) << "Could not open leases file \"" << lease_path << '"';
347 return false;
348 }
349 bool success = true;
350 auto dhcp_leases = ParseDnsmasqLeases(lease_file_fd);
351 for (auto& lease : dhcp_leases) {
352 if (!ReleaseDhcp4(tap_fd, lease.mac_address, lease.ip_address,
353 dhcp_server_ip)) {
354 LOG(ERROR) << "Failed to release " << lease;
355 success = false;
356 } else {
357 LOG(INFO) << "Successfully dropped " << lease;
358 }
359 }
360 return success;
361 }
362
MacAddressToString(const std::uint8_t mac[6])363 std::string MacAddressToString(const std::uint8_t mac[6]) {
364 std::stringstream result;
365
366 result << std::hex;
367 for (int i = 0; i < 6; i++) {
368 result << std::setfill('0') << std::setw(2)
369 << static_cast<int>(mac[i]);
370
371 if (i < 5) {
372 result << ':';
373 }
374 }
375
376 return result.str();
377 }
378
Ipv6ToString(const std::uint8_t ip[16])379 std::string Ipv6ToString(const std::uint8_t ip[16]) {
380 std::stringstream result;
381
382 result << std::hex;
383 for (int i = 0; i < 16; i = i + 2) {
384 result << std::setfill('0') << std::setw(2)
385 << static_cast<int>(ip[i])
386 << std::setfill('0') << std::setw(2)
387 << static_cast<int>(ip[i + 1]);
388
389 if (i < 14) {
390 result << ':';
391 }
392 }
393
394 return result.str();
395 }
396
GenerateMobileMacForInstance(int index,std::uint8_t out[6])397 void GenerateMobileMacForInstance(int index, std::uint8_t out[6]) {
398 GenerateMacForInstance(index, 0xe0, out);
399 }
400
GenerateEthMacForInstance(int index,std::uint8_t out[6])401 void GenerateEthMacForInstance(int index, std::uint8_t out[6]) {
402 GenerateMacForInstance(index, 0xe1, out);
403 }
404
GenerateWifiMacForInstance(int index,std::uint8_t out[6])405 void GenerateWifiMacForInstance(int index, std::uint8_t out[6]) {
406 GenerateMacForInstance(index, 0xe2, out);
407 }
408
409 /**
410 * Linux uses mac to generate link-local IPv6 address following:
411 *
412 * 1. Get mac address (for example 00:1a:11:ee:cf:01)
413 * 2. Throw ff:fe as a 3th and 4th octets (00:1a:11 :ff:fe: ee:cf:01)
414 * 3. Flip 2th bit in the first octet (02: 1a:11:ff:fe:ee:cf:01)
415 * 4. Use IPv6 format (021a:11ff:feee:cf01)
416 * 5. Add prefix fe80:: (fe80::021a:11ff:feee:cf01 or fe80:0000:0000:0000:021a:11ff:feee:cf00)
417 */
GenerateCorrespondingIpv6ForMac(const std::uint8_t mac[6],std::uint8_t out[16])418 void GenerateCorrespondingIpv6ForMac(const std::uint8_t mac[6], std::uint8_t out[16]) {
419 out[0] = 0xfe;
420 out[1] = 0x80;
421
422 // 2 - 7 octets are zero
423
424 // need to invert 2th bit of the first octet
425 out[8] = mac[0] ^ (1 << 1);
426 out[9] = mac[1];
427
428 out[10] = mac[2];
429 out[11] = 0xff;
430
431 out[12] = 0xfe;
432 out[13] = mac[3];
433
434 out[14] = mac[4];
435 out[15] = mac[5];
436 }
437
438 } // namespace cuttlefish
439