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 | #pragma once |
18 | |
19 | #include <cstring> |
20 | |
21 | #include <array> |
22 | #include <functional> |
23 | #include <iosfwd> |
24 | #include <map> |
25 | #include <stdexcept> |
26 | |
27 | #include <folly/Expected.h> |
28 | #include <folly/FBString.h> |
29 | #include <folly/IPAddressException.h> |
30 | #include <folly/Optional.h> |
31 | #include <folly/Range.h> |
32 | #include <folly/detail/IPAddress.h> |
33 | #include <folly/hash/Hash.h> |
34 | |
35 | namespace folly { |
36 | |
37 | class IPAddress; |
38 | class IPAddressV4; |
39 | class IPAddressV6; |
40 | class MacAddress; |
41 | |
42 | /** |
43 | * Pair of IPAddressV6, netmask |
44 | */ |
45 | typedef std::pair<IPAddressV6, uint8_t> CIDRNetworkV6; |
46 | |
47 | /** |
48 | * Specialization for IPv6 addresses |
49 | */ |
50 | typedef std::array<uint8_t, 16> ByteArray16; |
51 | |
52 | /** |
53 | * IPv6 variation of IPAddress. |
54 | * |
55 | * Added methods: createIPv4, getIPv4For6To4, is6To4, |
56 | * isTeredo, isIPv4Mapped, tryCreateIPv4, type |
57 | * |
58 | * @see IPAddress |
59 | * |
60 | * Notes on scope ID parsing: |
61 | * |
62 | * getaddrinfo() uses if_nametoindex() to convert interface names |
63 | * into a numerical index. For instance, |
64 | * "fe80::202:c9ff:fec1:ee08%eth0" may return scope ID 2 on some |
65 | * hosts, but other numbers on other hosts. It will fail entirely on |
66 | * hosts without an eth0 interface. |
67 | * |
68 | * Serializing / Deserializing IPAddressB6's on different hosts |
69 | * that use link-local scoping probably won't work. |
70 | */ |
71 | class IPAddressV6 { |
72 | public: |
73 | // V6 Address Type |
74 | enum Type { |
75 | TEREDO, |
76 | T6TO4, |
77 | NORMAL, |
78 | }; |
79 | // A constructor parameter to indicate that we should create a link-local |
80 | // IPAddressV6. |
81 | enum LinkLocalTag { |
82 | LINK_LOCAL, |
83 | }; |
84 | // Thrown when a type assertion fails |
85 | typedef std::runtime_error TypeError; |
86 | |
87 | // Binary prefix for teredo networks |
88 | static const uint32_t PREFIX_TEREDO; |
89 | // Binary prefix for 6to4 networks |
90 | static const uint32_t PREFIX_6TO4; |
91 | |
92 | // Size of std::string returned by toFullyQualified. |
93 | static constexpr size_t kToFullyQualifiedSize = |
94 | 8 /*words*/ * 4 /*hex chars per word*/ + 7 /*separators*/; |
95 | |
96 | // returns true iff the input string can be parsed as an ipv6-address |
97 | static bool validate(StringPiece ip) noexcept; |
98 | |
99 | /** |
100 | * Create a new IPAddress instance from the provided binary data. |
101 | * @throws IPAddressFormatException if the input length is not 16 bytes. |
102 | */ |
103 | static IPAddressV6 fromBinary(ByteRange bytes); |
104 | |
105 | /** |
106 | * Non-throwing version of fromBinary(). |
107 | * On failure returns IPAddressFormatError. |
108 | */ |
109 | static Expected<IPAddressV6, IPAddressFormatError> tryFromBinary( |
110 | ByteRange bytes) noexcept; |
111 | |
112 | /** |
113 | * Tries to create a new IPAddressV6 instance from provided string and |
114 | * returns it on success. Returns IPAddressFormatError on failure. |
115 | */ |
116 | static Expected<IPAddressV6, IPAddressFormatError> tryFromString( |
117 | StringPiece str) noexcept; |
118 | |
119 | /** |
120 | * Create a new IPAddress instance from the ip6.arpa representation. |
121 | * @throws IPAddressFormatException if the input is not a valid ip6.arpa |
122 | * representation |
123 | */ |
124 | static IPAddressV6 fromInverseArpaName(const std::string& arpaname); |
125 | |
126 | /** |
127 | * Returns the address as a Range. |
128 | */ |
129 | ByteRange toBinary() const { |
130 | return ByteRange((const unsigned char*)&addr_.in6Addr_.s6_addr, 16); |
131 | } |
132 | |
133 | /** |
134 | * Default constructor for IPAddressV6. |
135 | * |
136 | * The address value will be ::0 |
137 | */ |
138 | IPAddressV6(); |
139 | |
140 | // Create an IPAddressV6 from a string |
141 | // @throws IPAddressFormatException |
142 | // |
143 | explicit IPAddressV6(StringPiece addr); |
144 | |
145 | // ByteArray16 constructor |
146 | explicit IPAddressV6(const ByteArray16& src) noexcept; |
147 | |
148 | // in6_addr constructor |
149 | explicit IPAddressV6(const in6_addr& src) noexcept; |
150 | |
151 | // sockaddr_in6 constructor |
152 | explicit IPAddressV6(const sockaddr_in6& src) noexcept; |
153 | |
154 | /** |
155 | * Create a link-local IPAddressV6 from the specified ethernet MAC address. |
156 | */ |
157 | IPAddressV6(LinkLocalTag tag, MacAddress mac); |
158 | |
159 | // return the mapped V4 address |
160 | // @throws IPAddressFormatException if !isIPv4Mapped |
161 | IPAddressV4 createIPv4() const; |
162 | |
163 | /** |
164 | * Return a V4 address if this is a 6To4 address. |
165 | * @throws TypeError if not a 6To4 address |
166 | */ |
167 | IPAddressV4 getIPv4For6To4() const; |
168 | |
169 | // Return true if a 6TO4 address |
170 | bool is6To4() const { |
171 | return type() == IPAddressV6::Type::T6TO4; |
172 | } |
173 | |
174 | // Return true if a TEREDO address |
175 | bool isTeredo() const { |
176 | return type() == IPAddressV6::Type::TEREDO; |
177 | } |
178 | |
179 | // return true if this is v4-to-v6-mapped |
180 | bool isIPv4Mapped() const; |
181 | |
182 | // Return the V6 address type |
183 | Type type() const; |
184 | |
185 | /** |
186 | * @see IPAddress#bitCount |
187 | * @returns 128 |
188 | */ |
189 | static constexpr size_t bitCount() { |
190 | return 128; |
191 | } |
192 | |
193 | /** |
194 | * @see IPAddress#toJson |
195 | */ |
196 | std::string toJson() const; |
197 | |
198 | size_t hash() const; |
199 | |
200 | // @see IPAddress#inSubnet |
201 | // @throws IPAddressFormatException if string doesn't contain a V6 address |
202 | bool inSubnet(StringPiece cidrNetwork) const; |
203 | |
204 | // return true if address is in subnet |
205 | bool inSubnet(const IPAddressV6& subnet, uint8_t cidr) const { |
206 | return inSubnetWithMask(subnet, fetchMask(cidr)); |
207 | } |
208 | bool inSubnetWithMask(const IPAddressV6& subnet, const ByteArray16& mask) |
209 | const; |
210 | |
211 | // @see IPAddress#isLoopback |
212 | bool isLoopback() const; |
213 | |
214 | // @see IPAddress#isNonroutable |
215 | bool isNonroutable() const { |
216 | return !isRoutable(); |
217 | } |
218 | |
219 | /** |
220 | * Return true if this address is routable. |
221 | */ |
222 | bool isRoutable() const; |
223 | |
224 | // @see IPAddress#isPrivate |
225 | bool isPrivate() const; |
226 | |
227 | /** |
228 | * Return true if this is a link-local IPv6 address. |
229 | * |
230 | * Note that this only returns true for addresses in the fe80::/10 range. |
231 | * It returns false for the loopback address (::1), even though this address |
232 | * is also effectively has link-local scope. It also returns false for |
233 | * link-scope and interface-scope multicast addresses. |
234 | */ |
235 | bool isLinkLocal() const; |
236 | |
237 | /** |
238 | * Return the mac address if this is a link-local IPv6 address. |
239 | * |
240 | * @return an Optional<MacAddress> union representing the mac address. |
241 | * |
242 | * If the address is not a link-local one it will return an empty Optional. |
243 | * You can use Optional::value() to check whether the mac address is not null. |
244 | */ |
245 | Optional<MacAddress> getMacAddressFromLinkLocal() const; |
246 | |
247 | /** |
248 | * Return the mac address if this is an auto-configured IPv6 address based on |
249 | * EUI-64 |
250 | * |
251 | * @return an Optional<MacAddress> union representing the mac address. |
252 | * If the address is not based on EUI-64 it will return an empty Optional. |
253 | * You can use Optional::value() to check whether the mac address is not null. |
254 | */ |
255 | Optional<MacAddress> getMacAddressFromEUI64() const; |
256 | |
257 | /** |
258 | * Return true if this is a multicast address. |
259 | */ |
260 | bool isMulticast() const; |
261 | |
262 | /** |
263 | * Return the flags for a multicast address. |
264 | * This method may only be called on multicast addresses. |
265 | */ |
266 | uint8_t getMulticastFlags() const; |
267 | |
268 | /** |
269 | * Return the scope for a multicast address. |
270 | * This method may only be called on multicast addresses. |
271 | */ |
272 | uint8_t getMulticastScope() const; |
273 | |
274 | // @see IPAddress#isZero |
275 | bool isZero() const { |
276 | constexpr auto zero = ByteArray16{{}}; |
277 | return 0 == std::memcmp(bytes(), zero.data(), zero.size()); |
278 | } |
279 | |
280 | bool isLinkLocalBroadcast() const; |
281 | |
282 | // @see IPAddress#mask |
283 | IPAddressV6 mask(size_t numBits) const; |
284 | |
285 | // return underlying in6_addr structure |
286 | in6_addr toAddr() const { |
287 | return addr_.in6Addr_; |
288 | } |
289 | |
290 | uint16_t getScopeId() const { |
291 | return scope_; |
292 | } |
293 | void setScopeId(uint16_t scope) { |
294 | scope_ = scope; |
295 | } |
296 | |
297 | sockaddr_in6 toSockAddr() const { |
298 | sockaddr_in6 addr; |
299 | memset(&addr, 0, sizeof(sockaddr_in6)); |
300 | addr.sin6_family = AF_INET6; |
301 | addr.sin6_scope_id = scope_; |
302 | memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr)); |
303 | return addr; |
304 | } |
305 | |
306 | ByteArray16 toByteArray() const { |
307 | ByteArray16 ba{{0}}; |
308 | std::memcpy(ba.data(), bytes(), 16); |
309 | return ba; |
310 | } |
311 | |
312 | // @see IPAddress#toFullyQualified |
313 | std::string toFullyQualified() const; |
314 | |
315 | // @see IPAddress#toFullyQualifiedAppend |
316 | void toFullyQualifiedAppend(std::string& out) const; |
317 | |
318 | std::string toInverseArpaName() const; |
319 | |
320 | // @see IPAddress#str |
321 | std::string str() const; |
322 | |
323 | // @see IPAddress#version |
324 | uint8_t version() const { |
325 | return 6; |
326 | } |
327 | |
328 | /** |
329 | * Return the solicited-node multicast address for this address. |
330 | */ |
331 | IPAddressV6 getSolicitedNodeAddress() const; |
332 | |
333 | /** |
334 | * Return the mask associated with the given number of bits. |
335 | * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should |
336 | * be {0xff, 0xff, 0xff, 0x00}. |
337 | * @param [in] numBits bitmask to retrieve |
338 | * @throws abort if numBits == 0 or numBits > bitCount() |
339 | * @return mask associated with numBits |
340 | */ |
341 | static ByteArray16 fetchMask(size_t numBits); |
342 | // Given 2 IPAddressV6,mask pairs extract the longest common IPAddress, |
343 | // mask pair |
344 | static CIDRNetworkV6 longestCommonPrefix( |
345 | const CIDRNetworkV6& one, |
346 | const CIDRNetworkV6& two); |
347 | // Number of bytes in the address representation. |
348 | static constexpr size_t byteCount() { |
349 | return 16; |
350 | } |
351 | |
352 | // get nth most significant bit - 0 indexed |
353 | bool getNthMSBit(size_t bitIndex) const { |
354 | return detail::getNthMSBitImpl(*this, bitIndex, AF_INET6); |
355 | } |
356 | // get nth most significant byte - 0 indexed |
357 | uint8_t getNthMSByte(size_t byteIndex) const; |
358 | // get nth bit - 0 indexed |
359 | bool getNthLSBit(size_t bitIndex) const { |
360 | return getNthMSBit(bitCount() - bitIndex - 1); |
361 | } |
362 | // get nth byte - 0 indexed |
363 | uint8_t getNthLSByte(size_t byteIndex) const { |
364 | return getNthMSByte(byteCount() - byteIndex - 1); |
365 | } |
366 | |
367 | const unsigned char* bytes() const { |
368 | return addr_.in6Addr_.s6_addr; |
369 | } |
370 | |
371 | protected: |
372 | /** |
373 | * Helper that returns true if the address is in the binary subnet specified |
374 | * by addr. |
375 | */ |
376 | bool inBinarySubnet(const std::array<uint8_t, 2> addr, size_t numBits) const; |
377 | |
378 | private: |
379 | auto tie() const { |
380 | return std::tie(addr_.bytes_, scope_); |
381 | } |
382 | |
383 | public: |
384 | friend inline bool operator==(const IPAddressV6& a, const IPAddressV6& b) { |
385 | return a.tie() == b.tie(); |
386 | } |
387 | friend inline bool operator!=(const IPAddressV6& a, const IPAddressV6& b) { |
388 | return a.tie() != b.tie(); |
389 | } |
390 | friend inline bool operator<(const IPAddressV6& a, const IPAddressV6& b) { |
391 | return a.tie() < b.tie(); |
392 | } |
393 | friend inline bool operator>(const IPAddressV6& a, const IPAddressV6& b) { |
394 | return a.tie() > b.tie(); |
395 | } |
396 | friend inline bool operator<=(const IPAddressV6& a, const IPAddressV6& b) { |
397 | return a.tie() <= b.tie(); |
398 | } |
399 | friend inline bool operator>=(const IPAddressV6& a, const IPAddressV6& b) { |
400 | return a.tie() >= b.tie(); |
401 | } |
402 | |
403 | private: |
404 | union AddressStorage { |
405 | in6_addr in6Addr_; |
406 | ByteArray16 bytes_; |
407 | AddressStorage() { |
408 | std::memset(this, 0, sizeof(AddressStorage)); |
409 | } |
410 | explicit AddressStorage(const ByteArray16& bytes) : bytes_(bytes) {} |
411 | explicit AddressStorage(const in6_addr& addr) : in6Addr_(addr) {} |
412 | explicit AddressStorage(MacAddress mac); |
413 | } addr_; |
414 | |
415 | // Link-local scope id. This should always be 0 for IPAddresses that |
416 | // are *not* link-local. |
417 | uint16_t scope_{0}; |
418 | |
419 | /** |
420 | * Set the current IPAddressV6 object to have the address specified by bytes. |
421 | * Returns IPAddressFormatError if bytes.size() is not 16. |
422 | */ |
423 | Expected<Unit, IPAddressFormatError> trySetFromBinary( |
424 | ByteRange bytes) noexcept; |
425 | }; |
426 | |
427 | // boost::hash uses hash_value() so this allows boost::hash to work |
428 | // automatically for IPAddressV6 |
429 | std::size_t hash_value(const IPAddressV6& addr); |
430 | std::ostream& operator<<(std::ostream& os, const IPAddressV6& addr); |
431 | // Define toAppend() to allow IPAddressV6 to be used with to<string> |
432 | void toAppend(IPAddressV6 addr, std::string* result); |
433 | void toAppend(IPAddressV6 addr, fbstring* result); |
434 | |
435 | } // namespace folly |
436 | |
437 | namespace std { |
438 | template <> |
439 | struct hash<folly::IPAddressV6> { |
440 | size_t operator()(const folly::IPAddressV6& addr) const { |
441 | return addr.hash(); |
442 | } |
443 | }; |
444 | } // namespace std |
445 | |