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