blob: 9140fd075815dccc2de1e069c5d3dba5f75bbc59 [file] [log] [blame]
Hugo Benichi2ac4d072019-05-28 14:51:23 +09001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/net_util.h"
Hugo Benichi2ac4d072019-05-28 14:51:23 +09006
Hugo Benichie8758b52020-04-03 14:49:01 +09007#include <net/if.h>
8#include <string.h>
Hugo Benichi69c989d2021-03-01 00:23:39 +09009#include <sys/ioctl.h>
Hugo Benichie8758b52020-04-03 14:49:01 +090010
Garrick Evans260ff302019-07-25 11:22:50 +090011#include <fstream>
12#include <iostream>
13#include <random>
Hugo Benichie8758b52020-04-03 14:49:01 +090014#include <utility>
15#include <vector>
Garrick Evans260ff302019-07-25 11:22:50 +090016
17#include <base/logging.h>
Garrick Evansc7ae82c2019-09-04 16:25:10 +090018#include <base/strings/stringprintf.h>
19
Garrick Evans3388a032020-03-24 11:25:55 +090020namespace patchpanel {
Hugo Benichi2ac4d072019-05-28 14:51:23 +090021
Hugo Benichie8758b52020-04-03 14:49:01 +090022namespace {
23
24using flags_info_t = std::vector<std::pair<uint32_t, std::string>>;
25
26// Helper for pretty printing flags
27void AddFlags(std::ostream& stream,
28 uint32_t flags,
29 const flags_info_t& flags_info) {
30 if (flags == 0) {
31 stream << '0';
32 return;
33 }
34 std::string sep = "";
35 for (const auto& flag_descr : flags_info) {
36 if ((flags & flag_descr.first) == 0)
37 continue;
38 stream << sep << flag_descr.second;
39 sep = " | ";
40 }
41}
42
43const flags_info_t kRtentryRTF = {
44 {RTF_UP, "RTF_UP"}, {RTF_GATEWAY, "RTF_GATEWAY"},
45 {RTF_HOST, "RTF_HOST"}, {RTF_REINSTATE, "RTF_REINSTATE"},
46 {RTF_DYNAMIC, "RTF_DYNAMIC"}, {RTF_MODIFIED, "RTF_MODIFIED"},
47 {RTF_MTU, "RTF_MTU"}, {RTF_MSS, "RTF_MSS"},
48 {RTF_WINDOW, "RTF_WINDOW"}, {RTF_IRTT, "RTF_IRTT"},
49 {RTF_REJECT, "RTF_REJECT"},
50};
51
52} // namespace
53
Garrick Evans6f4fa3a2020-02-10 16:15:09 +090054uint32_t Ipv4Netmask(uint32_t prefix_len) {
55 return htonl((0xffffffffull << (32 - prefix_len)) & 0xffffffff);
56}
57
58uint32_t Ipv4BroadcastAddr(uint32_t base, uint32_t prefix_len) {
59 return (base | ~Ipv4Netmask(prefix_len));
60}
61
Hugo Benichi2ac4d072019-05-28 14:51:23 +090062std::string IPv4AddressToString(uint32_t addr) {
63 char buf[INET_ADDRSTRLEN] = {0};
64 struct in_addr ia;
65 ia.s_addr = addr;
66 return !inet_ntop(AF_INET, &ia, buf, sizeof(buf)) ? "" : buf;
67}
68
Hugo Benichiaf02c262021-01-26 10:24:10 +090069std::string IPv6AddressToString(const struct in6_addr& addr) {
70 char buf[INET6_ADDRSTRLEN] = {0};
71 return !inet_ntop(AF_INET6, &addr, buf, sizeof(buf)) ? "" : buf;
72}
73
Hugo Benichi2ac4d072019-05-28 14:51:23 +090074std::string IPv4AddressToCidrString(uint32_t addr, uint32_t prefix_length) {
75 return IPv4AddressToString(addr) + "/" + std::to_string(prefix_length);
76}
77
Garrick Evans54861622019-07-19 09:05:09 +090078std::string MacAddressToString(const MacAddress& addr) {
79 return base::StringPrintf("%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1],
80 addr[2], addr[3], addr[4], addr[5]);
81}
82
Garrick Evans260ff302019-07-25 11:22:50 +090083bool FindFirstIPv6Address(const std::string& ifname, struct in6_addr* address) {
84 struct ifaddrs* ifap;
85 struct ifaddrs* p;
86 bool found = false;
87
88 // Iterate through the linked list of all interface addresses to find
89 // the first IPv6 address for |ifname|.
90 if (getifaddrs(&ifap) < 0)
91 return false;
92
93 for (p = ifap; p; p = p->ifa_next) {
94 if (p->ifa_name != ifname || p->ifa_addr->sa_family != AF_INET6) {
95 continue;
96 }
97
98 if (address) {
99 struct sockaddr_in6* sa =
100 reinterpret_cast<struct sockaddr_in6*>(p->ifa_addr);
101 memcpy(address, &sa->sin6_addr, sizeof(*address));
102 }
103 found = true;
104 break;
105 }
106
107 freeifaddrs(ifap);
108 return found;
109}
110
111bool GenerateRandomIPv6Prefix(struct in6_addr* prefix, int len) {
112 std::mt19937 rng;
113 rng.seed(std::random_device()());
114 std::uniform_int_distribution<std::mt19937::result_type> randbyte(0, 255);
115
116 // TODO(cernekee): handle different prefix lengths
117 if (len != 64) {
118 LOG(DFATAL) << "Unexpected prefix length";
119 return false;
120 }
121
122 for (int i = 8; i < 16; i++)
123 prefix->s6_addr[i] = randbyte(rng);
124
125 // Set the universal/local flag, similar to a RFC 4941 address.
126 prefix->s6_addr[8] |= 0x40;
127 return true;
128}
129
Taoyu Lia0727dc2020-09-24 19:54:59 +0900130bool GenerateEUI64Address(in6_addr* address,
131 const in6_addr& prefix,
132 const MacAddress& mac) {
133 // RFC 4291, Appendix A: Insert 0xFF and 0xFE to form EUI-64, then flip
134 // universal/local bit
135 memcpy(address, &prefix, sizeof(in6_addr));
136 memcpy(&(address->s6_addr[8]), &(mac[0]), 3);
137 memcpy(&(address->s6_addr[13]), &(mac[3]), 3);
138 address->s6_addr[11] = 0xff;
139 address->s6_addr[12] = 0xfe;
140 address->s6_addr[8] ^= 0x2;
141 return true;
142}
143
Hugo Benichie8758b52020-04-03 14:49:01 +0900144void SetSockaddrIn(struct sockaddr* sockaddr, uint32_t addr) {
145 struct sockaddr_in* sockaddr_in =
146 reinterpret_cast<struct sockaddr_in*>(sockaddr);
147 sockaddr_in->sin_family = AF_INET;
148 sockaddr_in->sin_addr.s_addr = static_cast<in_addr_t>(addr);
149}
150
Garrick Evans260ff302019-07-25 11:22:50 +0900151std::ostream& operator<<(std::ostream& stream, const struct in_addr& addr) {
152 char buf[INET_ADDRSTRLEN];
153 inet_ntop(AF_INET, &addr, buf, sizeof(buf));
154 stream << buf;
155 return stream;
156}
157
158std::ostream& operator<<(std::ostream& stream, const struct in6_addr& addr) {
159 char buf[INET6_ADDRSTRLEN];
160 inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
161 stream << buf;
162 return stream;
163}
164
Hugo Benichidcc32392020-02-27 09:14:40 +0900165std::ostream& operator<<(std::ostream& stream, const struct sockaddr& addr) {
166 switch (addr.sa_family) {
Hugo Benichie8758b52020-04-03 14:49:01 +0900167 case 0:
168 return stream << "{unset}";
Hugo Benichidcc32392020-02-27 09:14:40 +0900169 case AF_INET:
170 return stream << (const struct sockaddr_in&)addr;
171 case AF_INET6:
172 return stream << (const struct sockaddr_in6&)addr;
173 case AF_UNIX:
174 return stream << (const struct sockaddr_un&)addr;
175 case AF_VSOCK:
176 return stream << (const struct sockaddr_vm&)addr;
177 default:
178 return stream << "{family: " << addr.sa_family << ", (unknown)}";
179 }
180}
181
182std::ostream& operator<<(std::ostream& stream,
183 const struct sockaddr_storage& addr) {
184 return stream << (const struct sockaddr&)addr;
185}
186
187std::ostream& operator<<(std::ostream& stream, const struct sockaddr_in& addr) {
188 char buf[INET_ADDRSTRLEN] = {0};
189 inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf));
190 return stream << "{family: AF_INET, port: " << ntohs(addr.sin_port)
191 << ", addr: " << buf << "}";
192}
193
194std::ostream& operator<<(std::ostream& stream,
195 const struct sockaddr_in6& addr) {
196 char buf[INET6_ADDRSTRLEN] = {0};
197 inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf));
198 return stream << "{family: AF_INET6, port: " << ntohs(addr.sin6_port)
199 << ", addr: " << buf << "}";
200}
201
202std::ostream& operator<<(std::ostream& stream, const struct sockaddr_un& addr) {
203 const size_t sun_path_length = sizeof(addr) - sizeof(sa_family_t);
204 // Add room for one extra char to ensure |buf| is a null terminated string
205 char buf[sun_path_length + 1] = {0};
206 memcpy(buf, addr.sun_path, sun_path_length);
207 if (buf[0] == '\0') {
208 buf[0] = '@';
209 }
210 return stream << "{family: AF_UNIX, path: " << buf << "}";
211}
212
213std::ostream& operator<<(std::ostream& stream, const struct sockaddr_vm& addr) {
214 return stream << "{family: AF_VSOCK, port: " << addr.svm_port
215 << ", cid: " << addr.svm_cid << "}";
216}
217
Hugo Benichie8758b52020-04-03 14:49:01 +0900218std::ostream& operator<<(std::ostream& stream, const struct rtentry& route) {
219 std::string rt_dev =
220 route.rt_dev ? std::string(route.rt_dev, strnlen(route.rt_dev, IFNAMSIZ))
221 : "null";
222 stream << "{rt_dst: " << route.rt_dst << ", rt_genmask: " << route.rt_genmask
223 << ", rt_gateway: " << route.rt_gateway << ", rt_dev: " << rt_dev
224 << ", rt_flags: ";
225 AddFlags(stream, route.rt_flags, kRtentryRTF);
226 return stream << "}";
227}
228
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900229uint16_t FoldChecksum(uint32_t sum) {
230 while (sum >> 16)
231 sum = (sum & 0xffff) + (sum >> 16);
232 return ~sum;
233}
234
235uint32_t NetChecksum(const void* data, ssize_t len) {
236 uint32_t sum = 0;
237 const uint16_t* word = reinterpret_cast<const uint16_t*>(data);
238 for (; len > 1; len -= 2)
239 sum += *word++;
240 if (len)
241 sum += *word & htons(0x0000ffff);
242 return sum;
243}
244
245uint16_t Ipv4Checksum(const iphdr* ip) {
246 uint32_t sum = NetChecksum(ip, sizeof(iphdr));
247 return FoldChecksum(sum);
248}
249
250uint16_t Udpv4Checksum(const iphdr* ip, const udphdr* udp) {
251 uint8_t pseudo_header[12];
252 memset(pseudo_header, 0, sizeof(pseudo_header));
253
254 // Fill in the pseudo-header.
255 memcpy(pseudo_header, &ip->saddr, sizeof(in_addr));
256 memcpy(pseudo_header + 4, &ip->daddr, sizeof(in_addr));
257 memcpy(pseudo_header + 9, &ip->protocol, sizeof(uint8_t));
258 memcpy(pseudo_header + 10, &udp->len, sizeof(uint16_t));
259
260 // Compute pseudo-header checksum
261 uint32_t sum = NetChecksum(pseudo_header, sizeof(pseudo_header));
262
263 // UDP
264 sum += NetChecksum(udp, ntohs(udp->len));
265
266 return FoldChecksum(sum);
267}
268
269uint16_t Icmpv6Checksum(const ip6_hdr* ip6, const icmp6_hdr* icmp6) {
270 uint32_t sum = 0;
271 // Src and Dst IP
272 for (size_t i = 0; i < (sizeof(struct in6_addr) >> 1); ++i)
273 sum += ip6->ip6_src.s6_addr16[i];
274 for (size_t i = 0; i < (sizeof(struct in6_addr) >> 1); ++i)
275 sum += ip6->ip6_dst.s6_addr16[i];
276
277 // Upper-Layer Packet Length
278 sum += ip6->ip6_plen;
279 // Next Header
280 sum += IPPROTO_ICMPV6 << 8;
281
282 // ICMP
283 sum += NetChecksum(icmp6, ntohs(ip6->ip6_plen));
284
285 return FoldChecksum(sum);
286}
287
Hugo Benichi69c989d2021-03-01 00:23:39 +0900288bool IsMulticastInterface(const std::string& ifname) {
289 if (ifname.empty()) {
290 return false;
291 }
292
293 int fd = socket(AF_INET, SOCK_DGRAM, 0);
294 if (fd < 0) {
295 // If IPv4 fails, try to open a socket using IPv6.
296 fd = socket(AF_INET6, SOCK_DGRAM, 0);
297 if (fd < 0) {
298 LOG(ERROR) << "Unable to create socket";
299 return false;
300 }
301 }
302
303 struct ifreq ifr;
304 memset(&ifr, 0, sizeof(ifr));
305 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
306 if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
307 PLOG(ERROR) << "SIOCGIFFLAGS failed for " << ifname;
308 close(fd);
309 return false;
310 }
311
312 close(fd);
313 return (ifr.ifr_flags & IFF_MULTICAST);
314}
315
Garrick Evans3388a032020-03-24 11:25:55 +0900316} // namespace patchpanel