| /* | |
| * 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 <linux/if_ether.h> | |
| // 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 <linux/if_tun.h> | |
| #undef ethhdr | |
| #endif | |
| #include <arpa/inet.h> | |
| #include <fcntl.h> | |
| #include <ifaddrs.h> | |
| #include <net/if.h> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <functional> | |
| #include <iomanip> | |
| #include <ios> | |
| #include <memory> | |
| #include <ostream> | |
| #include <set> | |
| #include <sstream> | |
| #include <streambuf> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include <android-base/logging.h> | |
| #include <android-base/strings.h> | |
| #include <fmt/format.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<std::uint8_t>(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__ | |
| SharedFD OpenTapInterface(const std::string& interface_name) { | |
| constexpr auto TUNTAP_DEV = "/dev/net/tun"; | |
| auto tap_fd = SharedFD::Open(TUNTAP_DEV, O_RDWR | O_NONBLOCK); | |
| if (!tap_fd->IsOpen()) { | |
| LOG(ERROR) << "Unable to open tun device: " << tap_fd->StrError(); | |
| return tap_fd; | |
| } | |
| struct ifreq ifr; | |
| memset(&ifr, 0, sizeof(ifr)); | |
| ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; | |
| strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ); | |
| int err = tap_fd->Ioctl(TUNSETIFF, &ifr); | |
| if (err < 0) { | |
| LOG(ERROR) << "Unable to connect to " << interface_name | |
| << " tap interface: " << tap_fd->StrError(); | |
| tap_fd->Close(); | |
| return SharedFD(); | |
| } | |
| // The interface's configuration may have been modified or just not set | |
| // correctly on creation. While qemu checks this and enforces the right | |
| // configuration, crosvm does not, so it needs to be set before it's passed to | |
| // it. | |
| tap_fd->Ioctl(TUNSETOFFLOAD, | |
| reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 | | |
| TUN_F_TSO6)); | |
| int len = SIZE_OF_VIRTIO_NET_HDR_V1; | |
| tap_fd->Ioctl(TUNSETVNETHDRSZ, &len); | |
| return tap_fd; | |
| } | |
| std::set<std::string> TapInterfacesInUse() { | |
| Command cmd("/bin/bash"); | |
| cmd.AddParameter("-c"); | |
| cmd.AddParameter("egrep -h -e \"^iff:.*\" /proc/*/fdinfo/*"); | |
| std::string stdin_str, stdout_str, stderr_str; | |
| RunWithManagedStdio(std::move(cmd), &stdin_str, &stdout_str, &stderr_str); | |
| auto lines = android::base::Split(stdout_str, "\n"); | |
| std::set<std::string> 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<std::uint8_t> 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 |