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 #ifdef __linux__
20 #include <linux/if_ether.h>
21 // Kernel headers don't mix well with userspace headers, but there is no
22 // userspace header that provides the if_tun.h #defines.  Include the kernel
23 // header, but move conflicting definitions out of the way using macros.
24 #define ethhdr __kernel_ethhdr
25 #include <linux/if_tun.h>
26 #undef ethhdr
27 #endif
28 
29 #include <arpa/inet.h>
30 #include <fcntl.h>
31 #include <ifaddrs.h>
32 #include <net/if.h>
33 
34 #include <cstdint>
35 #include <cstring>
36 #include <ostream>
37 #include <set>
38 #include <string>
39 #include <utility>
40 #include <vector>
41 
42 #include <android-base/logging.h>
43 #include <android-base/strings.h>
44 #include <fmt/ranges.h>
45 
46 #include "common/libs/utils/files.h"
47 #include "common/libs/utils/subprocess.h"
48 
49 namespace cuttlefish {
50 namespace {
51 
52 #ifdef __linux__
53 // This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
54 // the version of that header that ships with android in Pie does not include
55 // that struct (it was added in Q).
56 // This is what that struct looks like:
57 // struct virtio_net_hdr_v1 {
58 // u8 flags;
59 // u8 gso_type;
60 // u16 hdr_len;
61 // u16 gso_size;
62 // u16 csum_start;
63 // u16 csum_offset;
64 // u16 num_buffers;
65 // };
66 static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12;
67 #endif
68 
69 /**
70  * Generate mac address following:
71  * 00:1a:11:e0:cf:index
72  * ________ __    ______
73  *    |      |          |
74  *    |       type (e0, e1, etc)
75 */
GenerateMacForInstance(int index,uint8_t type,std::uint8_t out[6])76 void GenerateMacForInstance(int index, uint8_t type, std::uint8_t out[6]) {
77   // the first octet must be even
78   out[0] = 0x00;
79   out[1] = 0x1a;
80   out[2] = 0x11;
81   out[3] = type;
82   out[4] = 0xcf;
83   out[5] = static_cast<std::uint8_t>(index);
84 }
85 
86 }  // namespace
87 
NetworkInterfaceExists(const std::string & interface_name)88 bool NetworkInterfaceExists(const std::string& interface_name) {
89   struct ifaddrs *ifa_list{}, *ifa{};
90   bool ret = false;
91   getifaddrs(&ifa_list);
92   for (ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
93     if (strcmp(ifa->ifa_name, interface_name.c_str()) == 0) {
94       ret = true;
95       break;
96     }
97   }
98   freeifaddrs(ifa_list);
99   return ret;
100 }
101 
102 #ifdef __linux__
GrepCommand()103 static std::optional<Command> GrepCommand() {
104   if (FileExists("/usr/bin/grep")) {
105     return Command("/usr/bin/grep");
106   } else if (FileExists("/bin/grep")) {
107     return Command("/bin/grep");
108   } else {
109     return {};
110   }
111 }
112 
TapInterfacesInUse()113 std::set<std::string> TapInterfacesInUse() {
114   std::vector<std::string> fdinfo_list;
115 
116   Result<std::vector<std::string>> processes = DirectoryContents("/proc");
117   if (!processes.ok()) {
118     LOG(ERROR) << "Failed to get contents of `/proc/`";
119     return {};
120   }
121   for (const std::string& process : *processes) {
122     std::string fdinfo_path = fmt::format("/proc/{}/fdinfo", process);
123     Result<std::vector<std::string>> fdinfos = DirectoryContents(fdinfo_path);
124     if (!fdinfos.ok()) {
125       LOG(VERBOSE) << "Failed to get contents of '" << fdinfo_path << "'";
126       continue;
127     }
128     for (const std::string& fdinfo : *fdinfos) {
129       std::string path = fmt::format("/proc/{}/fdinfo/{}", process, fdinfo);
130       fdinfo_list.emplace_back(std::move(path));
131     }
132   }
133 
134   std::optional<Command> cmd = GrepCommand();
135   if (!cmd) {
136     LOG(WARNING) << "Unable to test TAP interface usage";
137     return {};
138   }
139   cmd->AddParameter("-E").AddParameter("-h").AddParameter("-e").AddParameter(
140       "^iff:.*");
141 
142   for (const std::string& fdinfo : fdinfo_list) {
143     cmd->AddParameter(fdinfo);
144   }
145 
146   std::string stdout_str, stderr_str;
147   RunWithManagedStdio(std::move(*cmd), nullptr, &stdout_str, &stderr_str);
148 
149   auto lines = android::base::Split(stdout_str, "\n");
150   std::set<std::string> tap_interfaces;
151   for (const auto& line : lines) {
152     if (line == "") {
153       continue;
154     }
155     if (!android::base::StartsWith(line, "iff:\t")) {
156       LOG(ERROR) << "Unexpected line \"" << line << "\"";
157       continue;
158     }
159     tap_interfaces.insert(line.substr(std::string("iff:\t").size()));
160   }
161   return tap_interfaces;
162 }
163 #endif
164 
MacAddressToString(const std::uint8_t mac[6])165 std::string MacAddressToString(const std::uint8_t mac[6]) {
166   std::vector<std::uint8_t> mac_vec(mac, mac + 6);
167   return fmt::format("{:0>2x}", fmt::join(mac_vec, ":"));
168 }
169 
Ipv6ToString(const std::uint8_t ip[16])170 std::string Ipv6ToString(const std::uint8_t ip[16]) {
171   char ipv6_str[INET6_ADDRSTRLEN + 1];
172   inet_ntop(AF_INET6, ip, ipv6_str, sizeof(ipv6_str));
173   return std::string(ipv6_str);
174 }
175 
GenerateMobileMacForInstance(int index,std::uint8_t out[6])176 void GenerateMobileMacForInstance(int index, std::uint8_t out[6]) {
177   GenerateMacForInstance(index, 0xe0, out);
178 }
179 
GenerateEthMacForInstance(int index,std::uint8_t out[6])180 void GenerateEthMacForInstance(int index, std::uint8_t out[6]) {
181   GenerateMacForInstance(index, 0xe1, out);
182 }
183 
GenerateWifiMacForInstance(int index,std::uint8_t out[6])184 void GenerateWifiMacForInstance(int index, std::uint8_t out[6]) {
185   GenerateMacForInstance(index, 0xe2, out);
186 }
187 
188 /**
189  * Linux uses mac to generate link-local IPv6 address following:
190  *
191  * 1. Get mac address (for example 00:1a:11:ee:cf:01)
192  * 2. Throw ff:fe as a 3th and 4th octets (00:1a:11 :ff:fe: ee:cf:01)
193  * 3. Flip 2th bit in the first octet (02: 1a:11:ff:fe:ee:cf:01)
194  * 4. Use IPv6 format (021a:11ff:feee:cf01)
195  * 5. Add prefix fe80:: (fe80::021a:11ff:feee:cf01 or fe80:0000:0000:0000:021a:11ff:feee:cf00)
196 */
GenerateCorrespondingIpv6ForMac(const std::uint8_t mac[6],std::uint8_t out[16])197 void GenerateCorrespondingIpv6ForMac(const std::uint8_t mac[6], std::uint8_t out[16]) {
198   out[0] = 0xfe;
199   out[1] = 0x80;
200 
201   // 2 - 7 octets are zero
202 
203   // need to invert 2th bit of the first octet
204   out[8] = mac[0] ^ (1 << 1);
205   out[9] = mac[1];
206 
207   out[10] = mac[2];
208   out[11] = 0xff;
209 
210   out[12] = 0xfe;
211   out[13] = mac[3];
212 
213   out[14] = mac[4];
214   out[15] = mac[5];
215 }
216 
217 }  // namespace cuttlefish
218