• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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