/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common/libs/utils/network.h" #ifdef __linux__ #include // Kernel headers don't mix well with userspace headers, but there is no // userspace header that provides the if_tun.h #defines. Include the kernel // header, but move conflicting definitions out of the way using macros. #define ethhdr __kernel_ethhdr #include #undef ethhdr #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/libs/utils/files.h" #include "common/libs/utils/subprocess.h" namespace cuttlefish { namespace { #ifdef __linux__ // This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but // the version of that header that ships with android in Pie does not include // that struct (it was added in Q). // This is what that struct looks like: // struct virtio_net_hdr_v1 { // u8 flags; // u8 gso_type; // u16 hdr_len; // u16 gso_size; // u16 csum_start; // u16 csum_offset; // u16 num_buffers; // }; static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12; #endif /** * Generate mac address following: * 00:1a:11:e0:cf:index * ________ __ ______ * | | | * | type (e0, e1, etc) */ void GenerateMacForInstance(int index, uint8_t type, std::uint8_t out[6]) { // the first octet must be even out[0] = 0x00; out[1] = 0x1a; out[2] = 0x11; out[3] = type; out[4] = 0xcf; out[5] = static_cast(index); } } // namespace bool NetworkInterfaceExists(const std::string& interface_name) { struct ifaddrs *ifa_list{}, *ifa{}; bool ret = false; getifaddrs(&ifa_list); for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) { if (strcmp(ifa->ifa_name, interface_name.c_str()) == 0) { ret = true; break; } } freeifaddrs(ifa_list); return ret; } #ifdef __linux__ static std::optional GrepCommand() { if (FileExists("/usr/bin/grep")) { return Command("/usr/bin/grep"); } else if (FileExists("/bin/grep")) { return Command("/bin/grep"); } else { return {}; } } std::set TapInterfacesInUse() { std::vector fdinfo_list; Result> processes = DirectoryContents("/proc"); if (!processes.ok()) { LOG(ERROR) << "Failed to get contents of `/proc/`"; return {}; } for (const std::string& process : *processes) { std::string fdinfo_path = fmt::format("/proc/{}/fdinfo", process); Result> fdinfos = DirectoryContents(fdinfo_path); if (!fdinfos.ok()) { LOG(VERBOSE) << "Failed to get contents of '" << fdinfo_path << "'"; continue; } for (const std::string& fdinfo : *fdinfos) { std::string path = fmt::format("/proc/{}/fdinfo/{}", process, fdinfo); fdinfo_list.emplace_back(std::move(path)); } } std::optional cmd = GrepCommand(); if (!cmd) { LOG(WARNING) << "Unable to test TAP interface usage"; return {}; } cmd->AddParameter("-E").AddParameter("-h").AddParameter("-e").AddParameter( "^iff:.*"); for (const std::string& fdinfo : fdinfo_list) { cmd->AddParameter(fdinfo); } std::string stdout_str, stderr_str; RunWithManagedStdio(std::move(*cmd), nullptr, &stdout_str, &stderr_str); auto lines = android::base::Split(stdout_str, "\n"); std::set tap_interfaces; for (const auto& line : lines) { if (line == "") { continue; } if (!android::base::StartsWith(line, "iff:\t")) { LOG(ERROR) << "Unexpected line \"" << line << "\""; continue; } tap_interfaces.insert(line.substr(std::string("iff:\t").size())); } return tap_interfaces; } #endif std::string MacAddressToString(const std::uint8_t mac[6]) { std::vector mac_vec(mac, mac + 6); return fmt::format("{:0>2x}", fmt::join(mac_vec, ":")); } std::string Ipv6ToString(const std::uint8_t ip[16]) { char ipv6_str[INET6_ADDRSTRLEN + 1]; inet_ntop(AF_INET6, ip, ipv6_str, sizeof(ipv6_str)); return std::string(ipv6_str); } void GenerateMobileMacForInstance(int index, std::uint8_t out[6]) { GenerateMacForInstance(index, 0xe0, out); } void GenerateEthMacForInstance(int index, std::uint8_t out[6]) { GenerateMacForInstance(index, 0xe1, out); } void GenerateWifiMacForInstance(int index, std::uint8_t out[6]) { GenerateMacForInstance(index, 0xe2, out); } /** * Linux uses mac to generate link-local IPv6 address following: * * 1. Get mac address (for example 00:1a:11:ee:cf:01) * 2. Throw ff:fe as a 3th and 4th octets (00:1a:11 :ff:fe: ee:cf:01) * 3. Flip 2th bit in the first octet (02: 1a:11:ff:fe:ee:cf:01) * 4. Use IPv6 format (021a:11ff:feee:cf01) * 5. Add prefix fe80:: (fe80::021a:11ff:feee:cf01 or fe80:0000:0000:0000:021a:11ff:feee:cf00) */ void GenerateCorrespondingIpv6ForMac(const std::uint8_t mac[6], std::uint8_t out[16]) { out[0] = 0xfe; out[1] = 0x80; // 2 - 7 octets are zero // need to invert 2th bit of the first octet out[8] = mac[0] ^ (1 << 1); out[9] = mac[1]; out[10] = mac[2]; out[11] = 0xff; out[12] = 0xfe; out[13] = mac[3]; out[14] = mac[4]; out[15] = mac[5]; } } // namespace cuttlefish