blob: 7603eea08d67f80a674f5fe4fb94a0a96ff7a1f4 [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>
9
Garrick Evans260ff302019-07-25 11:22:50 +090010#include <fstream>
11#include <iostream>
12#include <random>
Hugo Benichie8758b52020-04-03 14:49:01 +090013#include <utility>
14#include <vector>
Garrick Evans260ff302019-07-25 11:22:50 +090015
16#include <base/logging.h>
Garrick Evansc7ae82c2019-09-04 16:25:10 +090017#include <base/strings/stringprintf.h>
18
Garrick Evans3388a032020-03-24 11:25:55 +090019namespace patchpanel {
Hugo Benichi2ac4d072019-05-28 14:51:23 +090020
Hugo Benichie8758b52020-04-03 14:49:01 +090021namespace {
22
23using flags_info_t = std::vector<std::pair<uint32_t, std::string>>;
24
25// Helper for pretty printing flags
26void AddFlags(std::ostream& stream,
27 uint32_t flags,
28 const flags_info_t& flags_info) {
29 if (flags == 0) {
30 stream << '0';
31 return;
32 }
33 std::string sep = "";
34 for (const auto& flag_descr : flags_info) {
35 if ((flags & flag_descr.first) == 0)
36 continue;
37 stream << sep << flag_descr.second;
38 sep = " | ";
39 }
40}
41
42const flags_info_t kRtentryRTF = {
43 {RTF_UP, "RTF_UP"}, {RTF_GATEWAY, "RTF_GATEWAY"},
44 {RTF_HOST, "RTF_HOST"}, {RTF_REINSTATE, "RTF_REINSTATE"},
45 {RTF_DYNAMIC, "RTF_DYNAMIC"}, {RTF_MODIFIED, "RTF_MODIFIED"},
46 {RTF_MTU, "RTF_MTU"}, {RTF_MSS, "RTF_MSS"},
47 {RTF_WINDOW, "RTF_WINDOW"}, {RTF_IRTT, "RTF_IRTT"},
48 {RTF_REJECT, "RTF_REJECT"},
49};
50
51} // namespace
52
Garrick Evans6f4fa3a2020-02-10 16:15:09 +090053uint32_t Ipv4Netmask(uint32_t prefix_len) {
54 return htonl((0xffffffffull << (32 - prefix_len)) & 0xffffffff);
55}
56
57uint32_t Ipv4BroadcastAddr(uint32_t base, uint32_t prefix_len) {
58 return (base | ~Ipv4Netmask(prefix_len));
59}
60
Hugo Benichi2ac4d072019-05-28 14:51:23 +090061std::string IPv4AddressToString(uint32_t addr) {
62 char buf[INET_ADDRSTRLEN] = {0};
63 struct in_addr ia;
64 ia.s_addr = addr;
65 return !inet_ntop(AF_INET, &ia, buf, sizeof(buf)) ? "" : buf;
66}
67
Hugo Benichiaf02c262021-01-26 10:24:10 +090068std::string IPv6AddressToString(const struct in6_addr& addr) {
69 char buf[INET6_ADDRSTRLEN] = {0};
70 return !inet_ntop(AF_INET6, &addr, buf, sizeof(buf)) ? "" : buf;
71}
72
Hugo Benichi2ac4d072019-05-28 14:51:23 +090073std::string IPv4AddressToCidrString(uint32_t addr, uint32_t prefix_length) {
74 return IPv4AddressToString(addr) + "/" + std::to_string(prefix_length);
75}
76
Garrick Evans54861622019-07-19 09:05:09 +090077std::string MacAddressToString(const MacAddress& addr) {
78 return base::StringPrintf("%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1],
79 addr[2], addr[3], addr[4], addr[5]);
80}
81
Garrick Evans260ff302019-07-25 11:22:50 +090082bool FindFirstIPv6Address(const std::string& ifname, struct in6_addr* address) {
83 struct ifaddrs* ifap;
84 struct ifaddrs* p;
85 bool found = false;
86
87 // Iterate through the linked list of all interface addresses to find
88 // the first IPv6 address for |ifname|.
89 if (getifaddrs(&ifap) < 0)
90 return false;
91
92 for (p = ifap; p; p = p->ifa_next) {
93 if (p->ifa_name != ifname || p->ifa_addr->sa_family != AF_INET6) {
94 continue;
95 }
96
97 if (address) {
98 struct sockaddr_in6* sa =
99 reinterpret_cast<struct sockaddr_in6*>(p->ifa_addr);
100 memcpy(address, &sa->sin6_addr, sizeof(*address));
101 }
102 found = true;
103 break;
104 }
105
106 freeifaddrs(ifap);
107 return found;
108}
109
110bool GenerateRandomIPv6Prefix(struct in6_addr* prefix, int len) {
111 std::mt19937 rng;
112 rng.seed(std::random_device()());
113 std::uniform_int_distribution<std::mt19937::result_type> randbyte(0, 255);
114
115 // TODO(cernekee): handle different prefix lengths
116 if (len != 64) {
117 LOG(DFATAL) << "Unexpected prefix length";
118 return false;
119 }
120
121 for (int i = 8; i < 16; i++)
122 prefix->s6_addr[i] = randbyte(rng);
123
124 // Set the universal/local flag, similar to a RFC 4941 address.
125 prefix->s6_addr[8] |= 0x40;
126 return true;
127}
128
Taoyu Lia0727dc2020-09-24 19:54:59 +0900129bool GenerateEUI64Address(in6_addr* address,
130 const in6_addr& prefix,
131 const MacAddress& mac) {
132 // RFC 4291, Appendix A: Insert 0xFF and 0xFE to form EUI-64, then flip
133 // universal/local bit
134 memcpy(address, &prefix, sizeof(in6_addr));
135 memcpy(&(address->s6_addr[8]), &(mac[0]), 3);
136 memcpy(&(address->s6_addr[13]), &(mac[3]), 3);
137 address->s6_addr[11] = 0xff;
138 address->s6_addr[12] = 0xfe;
139 address->s6_addr[8] ^= 0x2;
140 return true;
141}
142
Hugo Benichie8758b52020-04-03 14:49:01 +0900143void SetSockaddrIn(struct sockaddr* sockaddr, uint32_t addr) {
144 struct sockaddr_in* sockaddr_in =
145 reinterpret_cast<struct sockaddr_in*>(sockaddr);
146 sockaddr_in->sin_family = AF_INET;
147 sockaddr_in->sin_addr.s_addr = static_cast<in_addr_t>(addr);
148}
149
Garrick Evans260ff302019-07-25 11:22:50 +0900150std::ostream& operator<<(std::ostream& stream, const struct in_addr& addr) {
151 char buf[INET_ADDRSTRLEN];
152 inet_ntop(AF_INET, &addr, buf, sizeof(buf));
153 stream << buf;
154 return stream;
155}
156
157std::ostream& operator<<(std::ostream& stream, const struct in6_addr& addr) {
158 char buf[INET6_ADDRSTRLEN];
159 inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
160 stream << buf;
161 return stream;
162}
163
Hugo Benichidcc32392020-02-27 09:14:40 +0900164std::ostream& operator<<(std::ostream& stream, const struct sockaddr& addr) {
165 switch (addr.sa_family) {
Hugo Benichie8758b52020-04-03 14:49:01 +0900166 case 0:
167 return stream << "{unset}";
Hugo Benichidcc32392020-02-27 09:14:40 +0900168 case AF_INET:
169 return stream << (const struct sockaddr_in&)addr;
170 case AF_INET6:
171 return stream << (const struct sockaddr_in6&)addr;
172 case AF_UNIX:
173 return stream << (const struct sockaddr_un&)addr;
174 case AF_VSOCK:
175 return stream << (const struct sockaddr_vm&)addr;
176 default:
177 return stream << "{family: " << addr.sa_family << ", (unknown)}";
178 }
179}
180
181std::ostream& operator<<(std::ostream& stream,
182 const struct sockaddr_storage& addr) {
183 return stream << (const struct sockaddr&)addr;
184}
185
186std::ostream& operator<<(std::ostream& stream, const struct sockaddr_in& addr) {
187 char buf[INET_ADDRSTRLEN] = {0};
188 inet_ntop(AF_INET, &addr.sin_addr, buf, sizeof(buf));
189 return stream << "{family: AF_INET, port: " << ntohs(addr.sin_port)
190 << ", addr: " << buf << "}";
191}
192
193std::ostream& operator<<(std::ostream& stream,
194 const struct sockaddr_in6& addr) {
195 char buf[INET6_ADDRSTRLEN] = {0};
196 inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf));
197 return stream << "{family: AF_INET6, port: " << ntohs(addr.sin6_port)
198 << ", addr: " << buf << "}";
199}
200
201std::ostream& operator<<(std::ostream& stream, const struct sockaddr_un& addr) {
202 const size_t sun_path_length = sizeof(addr) - sizeof(sa_family_t);
203 // Add room for one extra char to ensure |buf| is a null terminated string
204 char buf[sun_path_length + 1] = {0};
205 memcpy(buf, addr.sun_path, sun_path_length);
206 if (buf[0] == '\0') {
207 buf[0] = '@';
208 }
209 return stream << "{family: AF_UNIX, path: " << buf << "}";
210}
211
212std::ostream& operator<<(std::ostream& stream, const struct sockaddr_vm& addr) {
213 return stream << "{family: AF_VSOCK, port: " << addr.svm_port
214 << ", cid: " << addr.svm_cid << "}";
215}
216
Hugo Benichie8758b52020-04-03 14:49:01 +0900217std::ostream& operator<<(std::ostream& stream, const struct rtentry& route) {
218 std::string rt_dev =
219 route.rt_dev ? std::string(route.rt_dev, strnlen(route.rt_dev, IFNAMSIZ))
220 : "null";
221 stream << "{rt_dst: " << route.rt_dst << ", rt_genmask: " << route.rt_genmask
222 << ", rt_gateway: " << route.rt_gateway << ", rt_dev: " << rt_dev
223 << ", rt_flags: ";
224 AddFlags(stream, route.rt_flags, kRtentryRTF);
225 return stream << "}";
226}
227
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900228uint16_t FoldChecksum(uint32_t sum) {
229 while (sum >> 16)
230 sum = (sum & 0xffff) + (sum >> 16);
231 return ~sum;
232}
233
234uint32_t NetChecksum(const void* data, ssize_t len) {
235 uint32_t sum = 0;
236 const uint16_t* word = reinterpret_cast<const uint16_t*>(data);
237 for (; len > 1; len -= 2)
238 sum += *word++;
239 if (len)
240 sum += *word & htons(0x0000ffff);
241 return sum;
242}
243
244uint16_t Ipv4Checksum(const iphdr* ip) {
245 uint32_t sum = NetChecksum(ip, sizeof(iphdr));
246 return FoldChecksum(sum);
247}
248
249uint16_t Udpv4Checksum(const iphdr* ip, const udphdr* udp) {
250 uint8_t pseudo_header[12];
251 memset(pseudo_header, 0, sizeof(pseudo_header));
252
253 // Fill in the pseudo-header.
254 memcpy(pseudo_header, &ip->saddr, sizeof(in_addr));
255 memcpy(pseudo_header + 4, &ip->daddr, sizeof(in_addr));
256 memcpy(pseudo_header + 9, &ip->protocol, sizeof(uint8_t));
257 memcpy(pseudo_header + 10, &udp->len, sizeof(uint16_t));
258
259 // Compute pseudo-header checksum
260 uint32_t sum = NetChecksum(pseudo_header, sizeof(pseudo_header));
261
262 // UDP
263 sum += NetChecksum(udp, ntohs(udp->len));
264
265 return FoldChecksum(sum);
266}
267
268uint16_t Icmpv6Checksum(const ip6_hdr* ip6, const icmp6_hdr* icmp6) {
269 uint32_t sum = 0;
270 // Src and Dst IP
271 for (size_t i = 0; i < (sizeof(struct in6_addr) >> 1); ++i)
272 sum += ip6->ip6_src.s6_addr16[i];
273 for (size_t i = 0; i < (sizeof(struct in6_addr) >> 1); ++i)
274 sum += ip6->ip6_dst.s6_addr16[i];
275
276 // Upper-Layer Packet Length
277 sum += ip6->ip6_plen;
278 // Next Header
279 sum += IPPROTO_ICMPV6 << 8;
280
281 // ICMP
282 sum += NetChecksum(icmp6, ntohs(ip6->ip6_plen));
283
284 return FoldChecksum(sum);
285}
286
Garrick Evans3388a032020-03-24 11:25:55 +0900287} // namespace patchpanel