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