1 | /* |
2 | * Copyright (c) Facebook, Inc. and its affiliates. |
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 <folly/IPAddressV6.h> |
18 | |
19 | #include <ostream> |
20 | #include <string> |
21 | |
22 | #include <folly/Format.h> |
23 | #include <folly/IPAddress.h> |
24 | #include <folly/IPAddressV4.h> |
25 | #include <folly/MacAddress.h> |
26 | #include <folly/detail/IPAddressSource.h> |
27 | |
28 | #ifndef _WIN32 |
29 | #include <net/if.h> |
30 | #else |
31 | // Because of the massive pain that is libnl, this can't go into the socket |
32 | // portability header as you can't include <linux/if.h> and <net/if.h> in |
33 | // the same translation unit without getting errors -_-... |
34 | #include <iphlpapi.h> // @manual |
35 | #include <ntddndis.h> // @manual |
36 | |
37 | // Alias the max size of an interface name to what posix expects. |
38 | #define IFNAMSIZ IF_NAMESIZE |
39 | #endif |
40 | |
41 | using std::ostream; |
42 | using std::string; |
43 | |
44 | namespace folly { |
45 | |
46 | // public static const |
47 | const uint32_t IPAddressV6::PREFIX_TEREDO = 0x20010000; |
48 | const uint32_t IPAddressV6::PREFIX_6TO4 = 0x2002; |
49 | |
50 | // free functions |
51 | size_t hash_value(const IPAddressV6& addr) { |
52 | return addr.hash(); |
53 | } |
54 | ostream& operator<<(ostream& os, const IPAddressV6& addr) { |
55 | os << addr.str(); |
56 | return os; |
57 | } |
58 | void toAppend(IPAddressV6 addr, string* result) { |
59 | result->append(addr.str()); |
60 | } |
61 | void toAppend(IPAddressV6 addr, fbstring* result) { |
62 | result->append(addr.str()); |
63 | } |
64 | |
65 | bool IPAddressV6::validate(StringPiece ip) noexcept { |
66 | return tryFromString(ip).hasValue(); |
67 | } |
68 | |
69 | // public default constructor |
70 | IPAddressV6::IPAddressV6() = default; |
71 | |
72 | // public string constructor |
73 | IPAddressV6::IPAddressV6(StringPiece addr) { |
74 | auto maybeIp = tryFromString(addr); |
75 | if (maybeIp.hasError()) { |
76 | throw IPAddressFormatException( |
77 | to<std::string>("Invalid IPv6 address '" , addr, "'" )); |
78 | } |
79 | *this = std::move(maybeIp.value()); |
80 | } |
81 | |
82 | Expected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromString( |
83 | StringPiece str) noexcept { |
84 | auto ip = str.str(); |
85 | |
86 | // Allow addresses surrounded in brackets |
87 | if (ip.size() < 2) { |
88 | return makeUnexpected(IPAddressFormatError::INVALID_IP); |
89 | } |
90 | if (ip.front() == '[' && ip.back() == ']') { |
91 | ip = ip.substr(1, ip.size() - 2); |
92 | } |
93 | |
94 | struct addrinfo* result; |
95 | struct addrinfo hints; |
96 | memset(&hints, 0, sizeof(hints)); |
97 | hints.ai_family = AF_INET6; |
98 | hints.ai_socktype = SOCK_STREAM; |
99 | hints.ai_flags = AI_NUMERICHOST; |
100 | if (::getaddrinfo(ip.c_str(), nullptr, &hints, &result) == 0) { |
101 | SCOPE_EXIT { |
102 | ::freeaddrinfo(result); |
103 | }; |
104 | const struct sockaddr_in6* sa = |
105 | reinterpret_cast<struct sockaddr_in6*>(result->ai_addr); |
106 | return IPAddressV6(*sa); |
107 | } |
108 | return makeUnexpected(IPAddressFormatError::INVALID_IP); |
109 | } |
110 | |
111 | // in6_addr constructor |
112 | IPAddressV6::IPAddressV6(const in6_addr& src) noexcept : addr_(src) {} |
113 | |
114 | // sockaddr_in6 constructor |
115 | IPAddressV6::IPAddressV6(const sockaddr_in6& src) noexcept |
116 | : addr_(src.sin6_addr), scope_(uint16_t(src.sin6_scope_id)) {} |
117 | |
118 | // ByteArray16 constructor |
119 | IPAddressV6::IPAddressV6(const ByteArray16& src) noexcept : addr_(src) {} |
120 | |
121 | // link-local constructor |
122 | IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) : addr_(mac) {} |
123 | |
124 | IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) { |
125 | // The link-local address uses modified EUI-64 format, |
126 | // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A |
127 | const auto* macBytes = mac.bytes(); |
128 | memcpy(&bytes_.front(), "\xfe\x80\x00\x00\x00\x00\x00\x00" , 8); |
129 | bytes_[8] = uint8_t(macBytes[0] ^ 0x02); |
130 | bytes_[9] = macBytes[1]; |
131 | bytes_[10] = macBytes[2]; |
132 | bytes_[11] = 0xff; |
133 | bytes_[12] = 0xfe; |
134 | bytes_[13] = macBytes[3]; |
135 | bytes_[14] = macBytes[4]; |
136 | bytes_[15] = macBytes[5]; |
137 | } |
138 | |
139 | Optional<MacAddress> IPAddressV6::getMacAddressFromLinkLocal() const { |
140 | // Returned MacAddress must be constructed from a link-local IPv6 address. |
141 | if (!isLinkLocal()) { |
142 | return folly::none; |
143 | } |
144 | return getMacAddressFromEUI64(); |
145 | } |
146 | |
147 | Optional<MacAddress> IPAddressV6::getMacAddressFromEUI64() const { |
148 | if (!(addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { |
149 | return folly::none; |
150 | } |
151 | // The auto configured address uses modified EUI-64 format, |
152 | // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A |
153 | std::array<uint8_t, MacAddress::SIZE> bytes; |
154 | // Step 1: first 8 bytes are network prefix, and can be stripped |
155 | // Step 2: invert the universal/local (U/L) flag (bit 7) |
156 | bytes[0] = addr_.bytes_[8] ^ 0x02; |
157 | // Step 3: copy these bytes as they are |
158 | bytes[1] = addr_.bytes_[9]; |
159 | bytes[2] = addr_.bytes_[10]; |
160 | // Step 4: strip bytes (0xfffe), which are bytes_[11] and bytes_[12] |
161 | // Step 5: copy the rest. |
162 | bytes[3] = addr_.bytes_[13]; |
163 | bytes[4] = addr_.bytes_[14]; |
164 | bytes[5] = addr_.bytes_[15]; |
165 | return Optional<MacAddress>(MacAddress::fromBinary(range(bytes))); |
166 | } |
167 | |
168 | IPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) { |
169 | auto maybeIp = tryFromBinary(bytes); |
170 | if (maybeIp.hasError()) { |
171 | throw IPAddressFormatException(to<std::string>( |
172 | "Invalid IPv6 binary data: length must be 16 bytes, got " , |
173 | bytes.size())); |
174 | } |
175 | return maybeIp.value(); |
176 | } |
177 | |
178 | Expected<IPAddressV6, IPAddressFormatError> IPAddressV6::tryFromBinary( |
179 | ByteRange bytes) noexcept { |
180 | IPAddressV6 addr; |
181 | auto setResult = addr.trySetFromBinary(bytes); |
182 | if (setResult.hasError()) { |
183 | return makeUnexpected(std::move(setResult.error())); |
184 | } |
185 | return addr; |
186 | } |
187 | |
188 | Expected<Unit, IPAddressFormatError> IPAddressV6::trySetFromBinary( |
189 | ByteRange bytes) noexcept { |
190 | if (bytes.size() != 16) { |
191 | return makeUnexpected(IPAddressFormatError::INVALID_IP); |
192 | } |
193 | memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr)); |
194 | scope_ = 0; |
195 | return unit; |
196 | } |
197 | |
198 | // static |
199 | IPAddressV6 IPAddressV6::fromInverseArpaName(const std::string& arpaname) { |
200 | auto piece = StringPiece(arpaname); |
201 | if (!piece.removeSuffix(".ip6.arpa" )) { |
202 | throw IPAddressFormatException(sformat( |
203 | "Invalid input. Should end with 'ip6.arpa'. Got '{}'" , arpaname)); |
204 | } |
205 | std::vector<StringPiece> pieces; |
206 | split("." , piece, pieces); |
207 | if (pieces.size() != 32) { |
208 | throw IPAddressFormatException(sformat("Invalid input. Got '{}'" , piece)); |
209 | } |
210 | std::array<char, IPAddressV6::kToFullyQualifiedSize> ip; |
211 | size_t pos = 0; |
212 | int count = 0; |
213 | for (size_t i = 1; i <= pieces.size(); i++) { |
214 | ip[pos] = pieces[pieces.size() - i][0]; |
215 | pos++; |
216 | count++; |
217 | // add ':' every 4 chars |
218 | if (count == 4 && pos < ip.size()) { |
219 | ip[pos++] = ':'; |
220 | count = 0; |
221 | } |
222 | } |
223 | return IPAddressV6(folly::range(ip)); |
224 | } |
225 | |
226 | // public |
227 | IPAddressV4 IPAddressV6::createIPv4() const { |
228 | if (!isIPv4Mapped()) { |
229 | throw IPAddressFormatException("addr is not v4-to-v6-mapped" ); |
230 | } |
231 | const unsigned char* by = bytes(); |
232 | return IPAddressV4(detail::Bytes::mkAddress4(&by[12])); |
233 | } |
234 | |
235 | // convert two uint8_t bytes into a uint16_t as hibyte.lobyte |
236 | static inline uint16_t unpack(uint8_t lobyte, uint8_t hibyte) { |
237 | return uint16_t((uint16_t(hibyte) << 8) | lobyte); |
238 | } |
239 | |
240 | // given a src string, unpack count*2 bytes into dest |
241 | // dest must have as much storage as count |
242 | static inline void |
243 | unpackInto(const unsigned char* src, uint16_t* dest, size_t count) { |
244 | for (size_t i = 0, hi = 1, lo = 0; i < count; i++) { |
245 | dest[i] = unpack(src[hi], src[lo]); |
246 | hi += 2; |
247 | lo += 2; |
248 | } |
249 | } |
250 | |
251 | // public |
252 | IPAddressV4 IPAddressV6::getIPv4For6To4() const { |
253 | if (!is6To4()) { |
254 | throw IPAddressV6::TypeError( |
255 | sformat("Invalid IP '{}': not a 6to4 address" , str())); |
256 | } |
257 | // convert 16x8 bytes into first 4x16 bytes |
258 | uint16_t ints[4] = {0, 0, 0, 0}; |
259 | unpackInto(bytes(), ints, 4); |
260 | // repack into 4x8 |
261 | union { |
262 | unsigned char bytes[4]; |
263 | in_addr addr; |
264 | } ipv4; |
265 | ipv4.bytes[0] = (uint8_t)((ints[1] & 0xFF00) >> 8); |
266 | ipv4.bytes[1] = (uint8_t)(ints[1] & 0x00FF); |
267 | ipv4.bytes[2] = (uint8_t)((ints[2] & 0xFF00) >> 8); |
268 | ipv4.bytes[3] = (uint8_t)(ints[2] & 0x00FF); |
269 | return IPAddressV4(ipv4.addr); |
270 | } |
271 | |
272 | // public |
273 | bool IPAddressV6::isIPv4Mapped() const { |
274 | // v4 mapped addresses have their first 10 bytes set to 0, the next 2 bytes |
275 | // set to 255 (0xff); |
276 | const unsigned char* by = bytes(); |
277 | |
278 | // check if first 10 bytes are 0 |
279 | for (int i = 0; i < 10; i++) { |
280 | if (by[i] != 0x00) { |
281 | return false; |
282 | } |
283 | } |
284 | // check if bytes 11 and 12 are 255 |
285 | return by[10] == 0xff && by[11] == 0xff; |
286 | } |
287 | |
288 | // public |
289 | IPAddressV6::Type IPAddressV6::type() const { |
290 | // convert 16x8 bytes into first 2x16 bytes |
291 | uint16_t ints[2] = {0, 0}; |
292 | unpackInto(bytes(), ints, 2); |
293 | |
294 | if ((((uint32_t)ints[0] << 16) | ints[1]) == IPAddressV6::PREFIX_TEREDO) { |
295 | return Type::TEREDO; |
296 | } |
297 | |
298 | if ((uint32_t)ints[0] == IPAddressV6::PREFIX_6TO4) { |
299 | return Type::T6TO4; |
300 | } |
301 | |
302 | return Type::NORMAL; |
303 | } |
304 | |
305 | // public |
306 | string IPAddressV6::toJson() const { |
307 | return sformat("{{family:'AF_INET6', addr:'{}', hash:{}}}" , str(), hash()); |
308 | } |
309 | |
310 | // public |
311 | size_t IPAddressV6::hash() const { |
312 | if (isIPv4Mapped()) { |
313 | /* An IPAddress containing this object would be equal (i.e. operator==) |
314 | to an IPAddress containing the corresponding IPv4. |
315 | So we must make sure that the hash values are the same as well */ |
316 | return IPAddress::createIPv4(*this).hash(); |
317 | } |
318 | |
319 | static const uint64_t seed = AF_INET6; |
320 | uint64_t hash1 = 0, hash2 = 0; |
321 | hash::SpookyHashV2::Hash128(&addr_, 16, &hash1, &hash2); |
322 | return hash::hash_combine(seed, hash1, hash2); |
323 | } |
324 | |
325 | // public |
326 | bool IPAddressV6::inSubnet(StringPiece cidrNetwork) const { |
327 | auto subnetInfo = IPAddress::createNetwork(cidrNetwork); |
328 | auto addr = subnetInfo.first; |
329 | if (!addr.isV6()) { |
330 | throw IPAddressFormatException( |
331 | sformat("Address '{}' is not a V6 address" , addr.toJson())); |
332 | } |
333 | return inSubnetWithMask(addr.asV6(), fetchMask(subnetInfo.second)); |
334 | } |
335 | |
336 | // public |
337 | bool IPAddressV6::inSubnetWithMask( |
338 | const IPAddressV6& subnet, |
339 | const ByteArray16& cidrMask) const { |
340 | const auto mask = detail::Bytes::mask(toByteArray(), cidrMask); |
341 | const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask); |
342 | return (mask == subMask); |
343 | } |
344 | |
345 | // public |
346 | bool IPAddressV6::isLoopback() const { |
347 | // Check if v4 mapped is loopback |
348 | if (isIPv4Mapped() && createIPv4().isLoopback()) { |
349 | return true; |
350 | } |
351 | auto socka = toSockAddr(); |
352 | return IN6_IS_ADDR_LOOPBACK(&socka.sin6_addr); |
353 | } |
354 | |
355 | bool IPAddressV6::isRoutable() const { |
356 | return |
357 | // 2000::/3 is the only assigned global unicast block |
358 | inBinarySubnet({{0x20, 0x00}}, 3) || |
359 | // ffxe::/16 are global scope multicast addresses, |
360 | // which are eligible to be routed over the internet |
361 | (isMulticast() && getMulticastScope() == 0xe); |
362 | } |
363 | |
364 | bool IPAddressV6::isLinkLocalBroadcast() const { |
365 | static const IPAddressV6 kLinkLocalBroadcast("ff02::1" ); |
366 | return *this == kLinkLocalBroadcast; |
367 | } |
368 | |
369 | // public |
370 | bool IPAddressV6::isPrivate() const { |
371 | // Check if mapped is private |
372 | if (isIPv4Mapped() && createIPv4().isPrivate()) { |
373 | return true; |
374 | } |
375 | return isLoopback() || inBinarySubnet({{0xfc, 0x00}}, 7); |
376 | } |
377 | |
378 | // public |
379 | bool IPAddressV6::isLinkLocal() const { |
380 | return inBinarySubnet({{0xfe, 0x80}}, 10); |
381 | } |
382 | |
383 | bool IPAddressV6::isMulticast() const { |
384 | return addr_.bytes_[0] == 0xff; |
385 | } |
386 | |
387 | uint8_t IPAddressV6::getMulticastFlags() const { |
388 | DCHECK(isMulticast()); |
389 | return uint8_t((addr_.bytes_[1] >> 4) & 0xf); |
390 | } |
391 | |
392 | uint8_t IPAddressV6::getMulticastScope() const { |
393 | DCHECK(isMulticast()); |
394 | return uint8_t(addr_.bytes_[1] & 0xf); |
395 | } |
396 | |
397 | IPAddressV6 IPAddressV6::getSolicitedNodeAddress() const { |
398 | // Solicted node addresses must be constructed from unicast (or anycast) |
399 | // addresses |
400 | DCHECK(!isMulticast()); |
401 | |
402 | uint8_t bytes[16] = { |
403 | 0xff, |
404 | 0x02, |
405 | 0x00, |
406 | 0x00, |
407 | 0x00, |
408 | 0x00, |
409 | 0x00, |
410 | 0x00, |
411 | 0x00, |
412 | 0x00, |
413 | 0x00, |
414 | 0x01, |
415 | 0xff, |
416 | addr_.bytes_[13], |
417 | addr_.bytes_[14], |
418 | addr_.bytes_[15], |
419 | }; |
420 | return IPAddressV6::fromBinary(ByteRange(bytes, 16)); |
421 | } |
422 | |
423 | // public |
424 | IPAddressV6 IPAddressV6::mask(size_t numBits) const { |
425 | static const auto bits = bitCount(); |
426 | if (numBits > bits) { |
427 | throw IPAddressFormatException( |
428 | sformat("numBits({}) > bitCount({})" , numBits, bits)); |
429 | } |
430 | ByteArray16 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_); |
431 | return IPAddressV6(ba); |
432 | } |
433 | |
434 | // public |
435 | string IPAddressV6::str() const { |
436 | char buffer[INET6_ADDRSTRLEN + IFNAMSIZ + 1]; |
437 | |
438 | if (!inet_ntop(AF_INET6, toAddr().s6_addr, buffer, INET6_ADDRSTRLEN)) { |
439 | throw IPAddressFormatException(sformat( |
440 | "Invalid address with hex '{}' with error {}" , |
441 | detail::Bytes::toHex(bytes(), 16), |
442 | errnoStr(errno))); |
443 | } |
444 | |
445 | auto scopeId = getScopeId(); |
446 | if (scopeId != 0) { |
447 | auto len = strlen(buffer); |
448 | buffer[len] = '%'; |
449 | |
450 | auto errsv = errno; |
451 | if (!if_indextoname(scopeId, buffer + len + 1)) { |
452 | // if we can't map the if because eg. it no longer exists, |
453 | // append the if index instead |
454 | snprintf(buffer + len + 1, IFNAMSIZ, "%u" , scopeId); |
455 | } |
456 | errno = errsv; |
457 | } |
458 | |
459 | return string(buffer); |
460 | } |
461 | |
462 | // public |
463 | string IPAddressV6::toFullyQualified() const { |
464 | return detail::fastIpv6ToString(addr_.in6Addr_); |
465 | } |
466 | |
467 | // public |
468 | void IPAddressV6::toFullyQualifiedAppend(std::string& out) const { |
469 | detail::fastIpv6AppendToString(addr_.in6Addr_, out); |
470 | } |
471 | |
472 | // public |
473 | string IPAddressV6::toInverseArpaName() const { |
474 | constexpr folly::StringPiece lut = "0123456789abcdef" ; |
475 | std::array<char, 32> a; |
476 | int j = 0; |
477 | for (int i = 15; i >= 0; i--) { |
478 | a[j] = (lut[bytes()[i] & 0xf]); |
479 | a[j + 1] = (lut[bytes()[i] >> 4]); |
480 | j += 2; |
481 | } |
482 | return sformat("{}.ip6.arpa" , join("." , a)); |
483 | } |
484 | |
485 | // public |
486 | uint8_t IPAddressV6::getNthMSByte(size_t byteIndex) const { |
487 | const auto highestIndex = byteCount() - 1; |
488 | if (byteIndex > highestIndex) { |
489 | throw std::invalid_argument(sformat( |
490 | "Byte index must be <= {} for addresses of type: {}" , |
491 | highestIndex, |
492 | detail::familyNameStr(AF_INET6))); |
493 | } |
494 | return bytes()[byteIndex]; |
495 | } |
496 | |
497 | // protected |
498 | ByteArray16 IPAddressV6::fetchMask(size_t numBits) { |
499 | static const size_t bits = bitCount(); |
500 | if (numBits > bits) { |
501 | throw IPAddressFormatException("IPv6 addresses are 128 bits." ); |
502 | } |
503 | if (numBits == 0) { |
504 | return {{0}}; |
505 | } |
506 | constexpr auto _0s = uint64_t(0); |
507 | constexpr auto _1s = ~_0s; |
508 | auto const fragment = Endian::big(_1s << ((128 - numBits) % 64)); |
509 | auto const hi = numBits <= 64 ? fragment : _1s; |
510 | auto const lo = numBits <= 64 ? _0s : fragment; |
511 | uint64_t const parts[] = {hi, lo}; |
512 | ByteArray16 arr; |
513 | std::memcpy(arr.data(), parts, sizeof(parts)); |
514 | return arr; |
515 | } |
516 | |
517 | // public static |
518 | CIDRNetworkV6 IPAddressV6::longestCommonPrefix( |
519 | const CIDRNetworkV6& one, |
520 | const CIDRNetworkV6& two) { |
521 | auto prefix = detail::Bytes::longestCommonPrefix( |
522 | one.first.addr_.bytes_, one.second, two.first.addr_.bytes_, two.second); |
523 | return {IPAddressV6(prefix.first), prefix.second}; |
524 | } |
525 | |
526 | // protected |
527 | bool IPAddressV6::inBinarySubnet( |
528 | const std::array<uint8_t, 2> addr, |
529 | size_t numBits) const { |
530 | auto masked = mask(numBits); |
531 | return (std::memcmp(addr.data(), masked.bytes(), 2) == 0); |
532 | } |
533 | } // namespace folly |
534 | |