2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <utility> // std::pair
25 #include <folly/IPAddressException.h>
26 #include <folly/IPAddressV4.h>
27 #include <folly/IPAddressV6.h>
28 #include <folly/Range.h>
29 #include <folly/detail/IPAddress.h>
36 * Pair of IPAddress, netmask
38 typedef std::pair<IPAddress, uint8_t> CIDRNetwork;
41 * Provides a unified interface for IP addresses.
43 * @note If you compare 2 IPAddress instances, v4-to-v6-mapped addresses are
44 * compared as V4 addresses.
46 * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO
47 * if working in host byte order.
51 * IPAddress v4addr("192.0.2.129");
52 * IPAddress v6map("::ffff:192.0.2.129");
53 * CHECK(v4addr.inSubnet("192.0.2.0/24") ==
54 * v4addr.inSubnet(IPAddress("192.0.2.0"), 24));
55 * CHECK(v4addr.inSubnet("192.0.2.128/30"));
56 * CHECK(!v4addr.inSubnet("192.0.2.128/32"));
57 * CHECK(v4addr.asV4().toLong() == 2164392128);
58 * CHECK(v4addr.asV4().toLongHBO() == 3221226113);
59 * CHECK(v4addr.isV4());
60 * CHECK(v6addr.isV6());
61 * CHECK(v4addr == v6map);
62 * CHECK(v6map.isIPv4Mapped());
63 * CHECK(v4addr.asV4() == IPAddress::createIPv4(v6map));
64 * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6());
70 auto pick(F f) const {
71 return isV4() ? f(asV4()) : f(asV6());
75 // returns true iff the input string can be parsed as an ip-address
76 static bool validate(StringPiece ip) noexcept;
78 // return the V4 representation of the address, converting it from V6 to V4 if
79 // needed. Note that this will throw an IPAddressFormatException if the V6
80 // address is not IPv4Mapped.
81 static IPAddressV4 createIPv4(const IPAddress& addr);
83 // return the V6 representation of the address, converting it from V4 to V6 if
85 static IPAddressV6 createIPv6(const IPAddress& addr);
88 * Create a network and mask from a CIDR formatted address string.
89 * @param [in] ipSlashCidr IP/CIDR formatted string to split
90 * @param [in] defaultCidr default value if no /N specified (if defaultCidr
91 * is -1, will use /32 for IPv4 and /128 for IPv6)
92 * @param [in] mask apply mask on the address or not,
93 * e.g. 192.168.13.46/24 => 192.168.13.0/24
94 * @throws IPAddressFormatException if invalid address
95 * @return pair with IPAddress network and uint8_t mask
97 static CIDRNetwork createNetwork(
98 StringPiece ipSlashCidr,
103 * Return a string representation of a CIDR block created with createNetwork.
104 * @param [in] network, pair of address and cidr
106 * @return string representing the netblock
108 static std::string networkToString(const CIDRNetwork& network);
111 * Create a new IPAddress instance from the provided binary data
112 * in network byte order.
113 * @throws IPAddressFormatException if len is not 4 or 16
115 static IPAddress fromBinary(ByteRange bytes);
118 * Non-throwing version of fromBinary().
119 * On failure returns IPAddressFormatError.
121 static Expected<IPAddress, IPAddressFormatError> tryFromBinary(
122 ByteRange bytes) noexcept;
125 * Tries to create a new IPAddress instance from provided string and
126 * returns it on success. Returns IPAddressFormatError on failure.
128 static Expected<IPAddress, IPAddressFormatError> tryFromString(
129 StringPiece str) noexcept;
132 * Create an IPAddress from a 32bit long (network byte order).
133 * @throws IPAddressFormatException
135 static IPAddress fromLong(uint32_t src);
136 // Same as above, but host byte order
137 static IPAddress fromLongHBO(uint32_t src);
139 // Given 2 IPAddress,mask pairs extract the longest common IPAddress,
141 static CIDRNetwork longestCommonPrefix(
142 const CIDRNetwork& one,
143 const CIDRNetwork& two);
146 * Constructs an uninitialized IPAddress.
151 * Parse an IPAddress from a string representation.
153 * Formats accepted are exactly the same as the ones accepted by inet_pton(),
154 * using AF_INET6 if the string contains colons, and AF_INET otherwise;
155 * with the exception that the whole address can optionally be enclosed
156 * in square brackets.
158 * @throws IPAddressFormatException
160 explicit IPAddress(StringPiece ip);
163 * Create an IPAddress from a sockaddr.
164 * @throws IPAddressFormatException if nullptr or not AF_INET or AF_INET6
166 explicit IPAddress(const sockaddr* addr);
168 // Create an IPAddress from a V4 address
169 /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept;
170 /* implicit */ IPAddress(const in_addr addr) noexcept;
172 // Create an IPAddress from a V6 address
173 /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept;
174 /* implicit */ IPAddress(const in6_addr& addr) noexcept;
176 // Assign from V4 address
177 IPAddress& operator=(const IPAddressV4& ipV4Addr) noexcept;
179 // Assign from V6 address
180 IPAddress& operator=(const IPAddressV6& ipV6Addr) noexcept;
183 * Converts an IPAddress to an IPAddressV4 instance.
184 * @note This is not some handy convenience wrapper to convert an IPv4 address
185 * to a mapped IPv6 address. If you want that use
186 * IPAddress::createIPv6(addr)
187 * @throws InvalidAddressFamilyException is not a V4 instance
189 const IPAddressV4& asV4() const {
190 if (UNLIKELY(!isV4())) {
193 return addr_.ipV4Addr;
197 * Converts an IPAddress to an IPAddressV6 instance.
198 * @throws InvalidAddressFamilyException is not a V6 instance
200 const IPAddressV6& asV6() const {
201 if (UNLIKELY(!isV6())) {
204 return addr_.ipV6Addr;
207 // Return sa_family_t of IPAddress
208 sa_family_t family() const {
212 // Populate sockaddr_storage with an appropriate value
213 int toSockaddrStorage(sockaddr_storage* dest, uint16_t port = 0) const {
214 if (dest == nullptr) {
215 throw IPAddressFormatException("dest must not be null");
217 memset(dest, 0, sizeof(sockaddr_storage));
218 dest->ss_family = family();
221 sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(dest);
222 sin->sin_addr = asV4().toAddr();
223 sin->sin_port = port;
224 #if defined(__APPLE__)
225 sin->sin_len = sizeof(*sin);
229 sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(dest);
230 sin->sin6_addr = asV6().toAddr();
231 sin->sin6_port = port;
232 sin->sin6_scope_id = asV6().getScopeId();
233 #if defined(__APPLE__)
234 sin->sin6_len = sizeof(*sin);
238 throw InvalidAddressFamilyException(family());
243 * Check if the address is found in the specified CIDR netblock.
245 * This will return false if the specified cidrNet is V4, but the address is
246 * V6. It will also return false if the specified cidrNet is V6 but the
247 * address is V4. This method will do the right thing in the case of a v6
250 * @note This is slower than the below counterparts. If perf is important use
251 * one of the two argument variations below.
252 * @param [in] ipSlashCidr address in "192.168.1.0/24" format
253 * @throws IPAddressFormatException if no /mask
254 * @return true if address is part of specified subnet with cidr
256 bool inSubnet(StringPiece ipSlashCidr) const;
259 * Check if an IPAddress belongs to a subnet.
260 * @param [in] subnet Subnet to check against (e.g. 192.168.1.0)
261 * @param [in] cidr CIDR for subnet (e.g. 24 for /24)
262 * @return true if address is part of specified subnet with cidr
264 bool inSubnet(const IPAddress& subnet, uint8_t cidr) const;
267 * Check if an IPAddress belongs to the subnet with the given mask.
268 * This is the same as inSubnet but the mask is provided instead of looked up
270 * @param [in] subnet Subnet to check against
271 * @param [in] mask The netmask for the subnet
272 * @return true if address is part of the specified subnet with mask
274 bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const;
276 // @return true if address is a v4 mapped address
277 bool isIPv4Mapped() const {
278 return isV6() && asV6().isIPv4Mapped();
281 // @return true if address is uninitialized
283 return family_ == AF_UNSPEC;
286 // @return true if address is initialized
287 explicit operator bool() const {
291 // @return true if this is an IPAddressV4 instance
293 return family_ == AF_INET;
296 // @return true if this is an IPAddressV6 instance
298 return family_ == AF_INET6;
301 // @return true if this address is all zeros
302 bool isZero() const {
303 return pick([&](auto& _) { return _.isZero(); });
306 // Number of bits in the address representation.
307 size_t bitCount() const {
308 return pick([&](auto& _) { return _.bitCount(); });
310 // Number of bytes in the address representation.
311 size_t byteCount() const {
312 return bitCount() / 8;
314 // get nth most significant bit - 0 indexed
315 bool getNthMSBit(size_t bitIndex) const {
316 return detail::getNthMSBitImpl(*this, bitIndex, family());
318 // get nth most significant byte - 0 indexed
319 uint8_t getNthMSByte(size_t byteIndex) const;
320 // get nth bit - 0 indexed
321 bool getNthLSBit(size_t bitIndex) const {
322 return getNthMSBit(bitCount() - bitIndex - 1);
324 // get nth byte - 0 indexed
325 uint8_t getNthLSByte(size_t byteIndex) const {
326 return getNthMSByte(byteCount() - byteIndex - 1);
329 * Get human-readable string representation of the address.
331 * This prints a string representation of the address, for human consumption
332 * or logging. The string will take the form of a JSON object that looks like:
333 * {family:'AF_INET|AF_INET6', addr:'address', hash:long}.
335 std::string toJson() const {
336 return pick([&](auto& _) { return _.toJson(); });
340 std::size_t hash() const {
341 return pick([&](auto& _) { return _.hash(); });
344 // Return true if the address qualifies as localhost.
345 bool isLoopback() const {
346 return pick([&](auto& _) { return _.isLoopback(); });
349 // Return true if the address qualifies as link local
350 bool isLinkLocal() const {
351 return pick([&](auto& _) { return _.isLinkLocal(); });
354 // Return true if the address qualifies as broadcast.
355 bool isLinkLocalBroadcast() const {
356 return pick([&](auto& _) { return _.isLinkLocalBroadcast(); });
360 * Return true if the address is a special purpose address, as per rfc6890
362 * For V6, true if the address is not in one of global scope blocks:
363 * 2000::/3, ffxe::/16.
365 bool isNonroutable() const {
366 return pick([&](auto& _) { return _.isNonroutable(); });
370 * Return true if the address is private, as per rfc1918 and rfc4193
371 * (for example, 192.168.xxx.xxx or fc00::/7 addresses)
373 bool isPrivate() const {
374 return pick([&](auto& _) { return _.isPrivate(); });
377 // Return true if the address is a multicast address.
378 bool isMulticast() const {
379 return pick([&](auto& _) { return _.isMulticast(); });
383 * Creates IPAddress instance with all but most significant numBits set to 0.
384 * @param [in] numBits number of bits to mask
385 * @throws abort if numBits > bitCount()
386 * @return IPAddress instance with bits set to 0
388 IPAddress mask(uint8_t numBits) const {
389 return pick([&](auto& _) { return IPAddress(_.mask(numBits)); });
393 * Provides a string representation of address.
394 * @note The string representation is calculated on demand.
395 * @throws IPAddressFormatException on inet_ntop error
397 std::string str() const {
398 return pick([&](auto& _) { return _.str(); });
402 * Return the fully qualified string representation of the address.
403 * For V4 addresses this is the same as calling str(). For V6 addresses
404 * this is the hex representation with : characters inserted every 4 digits.
406 std::string toFullyQualified() const {
407 return pick([&](auto& _) { return _.toFullyQualified(); });
410 /// Same as toFullyQualified but append to an output string.
411 void toFullyQualifiedAppend(std::string& out) const {
412 return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); });
415 // Address version (4 or 6)
416 uint8_t version() const {
417 return pick([&](auto& _) { return _.version(); });
421 * Access to address bytes, in network byte order.
423 const unsigned char* bytes() const {
424 return pick([&](auto& _) { return _.bytes(); });
428 [[noreturn]] void asV4Throw() const;
429 [[noreturn]] void asV6Throw() const;
431 typedef union IPAddressV46 {
432 IPAddressV4 ipV4Addr;
433 IPAddressV6 ipV6Addr;
434 // default constructor
435 IPAddressV46() noexcept {
436 std::memset(this, 0, sizeof(IPAddressV46));
438 explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {}
439 explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {}
445 // boost::hash uses hash_value() so this allows boost::hash to work
446 // automatically for IPAddress
447 std::size_t hash_value(const IPAddress& addr);
448 std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
449 // Define toAppend() to allow IPAddress to be used with folly::to<string>
450 void toAppend(IPAddress addr, std::string* result);
451 void toAppend(IPAddress addr, fbstring* result);
454 * Return true if two addresses are equal.
456 * @note This takes into consideration V4 mapped addresses as well. If one
457 * address is v4 mapped we compare the v4 addresses.
459 * @return true if the two addresses are equal.
461 bool operator==(const IPAddress& addr1, const IPAddress& addr2);
462 // Return true if addr1 < addr2
463 bool operator<(const IPAddress& addr1, const IPAddress& addr2);
465 inline bool operator!=(const IPAddress& a, const IPAddress& b) {
468 inline bool operator>(const IPAddress& a, const IPAddress& b) {
471 inline bool operator<=(const IPAddress& a, const IPAddress& b) {
474 inline bool operator>=(const IPAddress& a, const IPAddress& b) {
482 struct hash<folly::IPAddress> {
483 size_t operator()(const folly::IPAddress& addr) const {