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 <functional> 20 #include <iosfwd> 21 #include <memory> 22 #include <string> 23 #include <type_traits> 24 #include <utility> // std::pair 25 26 #include <folly/ConstexprMath.h> 27 #include <folly/IPAddressException.h> 28 #include <folly/IPAddressV4.h> 29 #include <folly/IPAddressV6.h> 30 #include <folly/Range.h> 31 #include <folly/detail/IPAddress.h> 32 #include <folly/lang/Exception.h> 33 34 namespace folly { 35 36 class IPAddress; 37 38 /** 39 * Pair of IPAddress, netmask 40 */ 41 typedef std::pair<IPAddress, uint8_t> CIDRNetwork; 42 43 /** 44 * Provides a unified interface for IP addresses. 45 * 46 * @note If you compare 2 IPAddress instances, v4-to-v6-mapped addresses are 47 * compared as V4 addresses. 48 * 49 * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO 50 * if working in host byte order. 51 * 52 * Example usage: 53 * @code 54 * IPAddress v4addr("192.0.2.129"); 55 * IPAddress v6map("::ffff:192.0.2.129"); 56 * CHECK(v4addr.inSubnet("192.0.2.0/24") == 57 * v4addr.inSubnet(IPAddress("192.0.2.0"), 24)); 58 * CHECK(v4addr.inSubnet("192.0.2.128/30")); 59 * CHECK(!v4addr.inSubnet("192.0.2.128/32")); 60 * CHECK(v4addr.asV4().toLong() == 2164392128); 61 * CHECK(v4addr.asV4().toLongHBO() == 3221226113); 62 * CHECK(v4addr.isV4()); 63 * CHECK(v6addr.isV6()); 64 * CHECK(v4addr == v6map); 65 * CHECK(v6map.isIPv4Mapped()); 66 * CHECK(v4addr.asV4() == IPAddress::createIPv4(v6map)); 67 * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6()); 68 * @encode 69 */ 70 class IPAddress { 71 private: 72 template <typename F> pick(F f)73 auto pick(F f) const { 74 return isV4() ? f(asV4()) : isV6() ? f(asV6()) : f(asNone()); 75 } 76 77 class IPAddressNone { 78 public: isZero()79 bool isZero() const { return true; } bitCount()80 size_t bitCount() const { return 0; } toJson()81 std::string toJson() const { 82 return "{family:'AF_UNSPEC', addr:'', hash:0}"; 83 } hash()84 std::size_t hash() const { return std::hash<uint64_t>{}(0); } isLoopback()85 bool isLoopback() const { 86 throw_exception<InvalidAddressFamilyException>("empty address"); 87 } isLinkLocal()88 bool isLinkLocal() const { 89 throw_exception<InvalidAddressFamilyException>("empty address"); 90 } isLinkLocalBroadcast()91 bool isLinkLocalBroadcast() const { 92 throw_exception<InvalidAddressFamilyException>("empty address"); 93 } isNonroutable()94 bool isNonroutable() const { 95 throw_exception<InvalidAddressFamilyException>("empty address"); 96 } isPrivate()97 bool isPrivate() const { 98 throw_exception<InvalidAddressFamilyException>("empty address"); 99 } isMulticast()100 bool isMulticast() const { 101 throw_exception<InvalidAddressFamilyException>("empty address"); 102 } mask(uint8_t numBits)103 IPAddress mask(uint8_t numBits) const { 104 (void)numBits; 105 return IPAddress(); 106 } str()107 std::string str() const { return ""; } toFullyQualified()108 std::string toFullyQualified() const { return ""; } toFullyQualifiedAppend(std::string & out)109 void toFullyQualifiedAppend(std::string& out) const { 110 (void)out; 111 return; 112 } version()113 uint8_t version() const { return 0; } bytes()114 const unsigned char* bytes() const { return nullptr; } 115 }; 116 asNone()117 IPAddressNone const& asNone() const { 118 if (!empty()) { 119 throw_exception<InvalidAddressFamilyException>("not empty"); 120 } 121 return addr_.ipNoneAddr; 122 } 123 124 public: 125 // returns true iff the input string can be parsed as an ip-address 126 static bool validate(StringPiece ip) noexcept; 127 128 // return the V4 representation of the address, converting it from V6 to V4 if 129 // needed. Note that this will throw an IPAddressFormatException if the V6 130 // address is not IPv4Mapped. 131 static IPAddressV4 createIPv4(const IPAddress& addr); 132 133 // return the V6 representation of the address, converting it from V4 to V6 if 134 // needed. 135 static IPAddressV6 createIPv6(const IPAddress& addr); 136 137 /** 138 * Create a network and mask from a CIDR formatted address string. 139 * @param [in] ipSlashCidr IP/CIDR formatted string to split 140 * @param [in] defaultCidr default value if no /N specified (if defaultCidr 141 * is -1, will use /32 for IPv4 and /128 for IPv6) 142 * @param [in] mask apply mask on the address or not, 143 * e.g. 192.168.13.46/24 => 192.168.13.0/24 144 * @return either pair with IPAddress network and uint8_t mask or 145 * CIDRNetworkError 146 */ 147 static Expected<CIDRNetwork, CIDRNetworkError> tryCreateNetwork( 148 StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true); 149 150 /** 151 * Create a network and mask from a CIDR formatted address string. 152 * Same as tryCreateNetwork() but throws IPAddressFormatException on error. 153 * The implementation calls tryCreateNetwork(...) underneath 154 * 155 * @throws IPAddressFormatException if invalid address 156 * @return pair with IPAddress network and uint8_t mask 157 */ 158 static CIDRNetwork createNetwork( 159 StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true); 160 161 /** 162 * Return a string representation of a CIDR block created with createNetwork. 163 * @param [in] network pair of address and cidr 164 * 165 * @return string representing the netblock 166 */ 167 static std::string networkToString(const CIDRNetwork& network); 168 169 /** 170 * Create a new IPAddress instance from the provided binary data 171 * in network byte order. 172 * @throws IPAddressFormatException if len is not 4 or 16 173 */ 174 static IPAddress fromBinary(ByteRange bytes); 175 176 /** 177 * Non-throwing version of fromBinary(). 178 * On failure returns IPAddressFormatError. 179 */ 180 static Expected<IPAddress, IPAddressFormatError> tryFromBinary( 181 ByteRange bytes) noexcept; 182 183 /** 184 * Tries to create a new IPAddress instance from provided string and 185 * returns it on success. Returns IPAddressFormatError on failure. 186 */ 187 static Expected<IPAddress, IPAddressFormatError> tryFromString( 188 StringPiece str) noexcept; 189 190 /** 191 * Create an IPAddress from a 32bit long (network byte order). 192 * @throws IPAddressFormatException 193 */ 194 static IPAddress fromLong(uint32_t src); 195 // Same as above, but host byte order 196 static IPAddress fromLongHBO(uint32_t src); 197 198 // Given 2 IPAddress,mask pairs extract the longest common IPAddress, 199 // mask pair 200 static CIDRNetwork longestCommonPrefix( 201 const CIDRNetwork& one, const CIDRNetwork& two); 202 203 /** 204 * Constructs an uninitialized IPAddress. 205 */ 206 IPAddress(); 207 208 /** 209 * Parse an IPAddress from a string representation. 210 * 211 * Formats accepted are exactly the same as the ones accepted by inet_pton(), 212 * using AF_INET6 if the string contains colons, and AF_INET otherwise; 213 * with the exception that the whole address can optionally be enclosed 214 * in square brackets. 215 * 216 * @throws IPAddressFormatException 217 */ 218 explicit IPAddress(StringPiece str); 219 220 /** 221 * Create an IPAddress from a sockaddr. 222 * @throws IPAddressFormatException if nullptr or not AF_INET or AF_INET6 223 */ 224 explicit IPAddress(const sockaddr* addr); 225 226 // Create an IPAddress from a V4 address 227 /* implicit */ IPAddress(const IPAddressV4 ipV4Addr) noexcept; 228 /* implicit */ IPAddress(const in_addr addr) noexcept; 229 230 // Create an IPAddress from a V6 address 231 /* implicit */ IPAddress(const IPAddressV6& ipV6Addr) noexcept; 232 /* implicit */ IPAddress(const in6_addr& addr) noexcept; 233 234 // Assign from V4 address 235 IPAddress& operator=(const IPAddressV4& ipv4_addr) noexcept; 236 237 // Assign from V6 address 238 IPAddress& operator=(const IPAddressV6& ipv6_addr) noexcept; 239 240 /** 241 * Converts an IPAddress to an IPAddressV4 instance. 242 * @note This is not some handy convenience wrapper to convert an IPv4 address 243 * to a mapped IPv6 address. If you want that use 244 * IPAddress::createIPv6(addr) 245 * @throws InvalidAddressFamilyException is not a V4 instance 246 */ asV4()247 const IPAddressV4& asV4() const { 248 if (UNLIKELY(!isV4())) { 249 asV4Throw(); 250 } 251 return addr_.ipV4Addr; 252 } 253 254 /** 255 * Converts an IPAddress to an IPAddressV6 instance. 256 * @throws InvalidAddressFamilyException is not a V6 instance 257 */ asV6()258 const IPAddressV6& asV6() const { 259 if (UNLIKELY(!isV6())) { 260 asV6Throw(); 261 } 262 return addr_.ipV6Addr; 263 } 264 265 // Return sa_family_t of IPAddress family()266 sa_family_t family() const { return family_; } 267 268 // Populate sockaddr_storage with an appropriate value 269 int toSockaddrStorage(sockaddr_storage* dest, uint16_t port = 0) const { 270 if (dest == nullptr) { 271 throw_exception<IPAddressFormatException>("dest must not be null"); 272 } 273 memset(dest, 0, sizeof(sockaddr_storage)); 274 dest->ss_family = family(); 275 276 if (isV4()) { 277 sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(dest); 278 sin->sin_addr = asV4().toAddr(); 279 sin->sin_port = port; 280 #if defined(__APPLE__) 281 sin->sin_len = sizeof(*sin); 282 #endif 283 return sizeof(*sin); 284 } else if (isV6()) { 285 sockaddr_in6* sin = reinterpret_cast<sockaddr_in6*>(dest); 286 sin->sin6_addr = asV6().toAddr(); 287 sin->sin6_port = port; 288 sin->sin6_scope_id = asV6().getScopeId(); 289 #if defined(__APPLE__) 290 sin->sin6_len = sizeof(*sin); 291 #endif 292 return sizeof(*sin); 293 } else { 294 throw_exception<InvalidAddressFamilyException>(family()); 295 } 296 } 297 298 /** 299 * Check if the address is found in the specified CIDR netblock. 300 * 301 * This will return false if the specified cidrNet is V4, but the address is 302 * V6. It will also return false if the specified cidrNet is V6 but the 303 * address is V4. This method will do the right thing in the case of a v6 304 * mapped v4 address. 305 * 306 * @note This is slower than the below counterparts. If perf is important use 307 * one of the two argument variations below. 308 * @param [in] cidrNetwork address in "192.168.1.0/24" format 309 * @throws IPAddressFormatException if no /mask 310 * @return true if address is part of specified subnet with cidr 311 */ 312 bool inSubnet(StringPiece cidrNetwork) const; 313 314 /** 315 * Check if an IPAddress belongs to a subnet. 316 * @param [in] subnet Subnet to check against (e.g. 192.168.1.0) 317 * @param [in] cidr CIDR for subnet (e.g. 24 for /24) 318 * @return true if address is part of specified subnet with cidr 319 */ 320 bool inSubnet(const IPAddress& subnet, uint8_t cidr) const; 321 322 /** 323 * Check if an IPAddress belongs to the subnet with the given mask. 324 * This is the same as inSubnet but the mask is provided instead of looked up 325 * from the cidr. 326 * @param [in] subnet Subnet to check against 327 * @param [in] mask The netmask for the subnet 328 * @return true if address is part of the specified subnet with mask 329 */ 330 bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const; 331 332 // @return true if address is a v4 mapped address isIPv4Mapped()333 bool isIPv4Mapped() const { return isV6() && asV6().isIPv4Mapped(); } 334 335 // @return true if address is uninitialized empty()336 bool empty() const { return family_ == AF_UNSPEC; } 337 338 // @return true if address is initialized 339 explicit operator bool() const { return !empty(); } 340 341 // @return true if this is an IPAddressV4 instance isV4()342 bool isV4() const { return family_ == AF_INET; } 343 344 // @return true if this is an IPAddressV6 instance isV6()345 bool isV6() const { return family_ == AF_INET6; } 346 347 // @return true if this address is all zeros isZero()348 bool isZero() const { 349 return pick([&](auto& _) { return _.isZero(); }); 350 } 351 352 // Number of bits in the address representation. bitCount()353 size_t bitCount() const { 354 return pick([&](auto& _) { return _.bitCount(); }); 355 } 356 // Number of bytes in the address representation. byteCount()357 size_t byteCount() const { return bitCount() / 8; } 358 // get nth most significant bit - 0 indexed getNthMSBit(size_t bitIndex)359 bool getNthMSBit(size_t bitIndex) const { 360 return detail::getNthMSBitImpl(*this, bitIndex, family()); 361 } 362 // get nth most significant byte - 0 indexed 363 uint8_t getNthMSByte(size_t byteIndex) const; 364 // get nth bit - 0 indexed getNthLSBit(size_t bitIndex)365 bool getNthLSBit(size_t bitIndex) const { 366 return getNthMSBit(bitCount() - bitIndex - 1); 367 } 368 // get nth byte - 0 indexed getNthLSByte(size_t byteIndex)369 uint8_t getNthLSByte(size_t byteIndex) const { 370 return getNthMSByte(byteCount() - byteIndex - 1); 371 } 372 /** 373 * Get human-readable string representation of the address. 374 * 375 * This prints a string representation of the address, for human consumption 376 * or logging. The string will take the form of a JSON object that looks like: 377 * {family:'AF_INET|AF_INET6', addr:'address', hash:long}. 378 */ toJson()379 std::string toJson() const { 380 return pick([&](auto& _) { return _.toJson(); }); 381 } 382 383 // Hash of address hash()384 std::size_t hash() const { 385 return pick([&](auto& _) { return _.hash(); }); 386 } 387 388 // Return true if the address qualifies as localhost. isLoopback()389 bool isLoopback() const { 390 return pick([&](auto& _) { return _.isLoopback(); }); 391 } 392 393 // Return true if the address qualifies as link local isLinkLocal()394 bool isLinkLocal() const { 395 return pick([&](auto& _) { return _.isLinkLocal(); }); 396 } 397 398 // Return true if the address qualifies as broadcast. isLinkLocalBroadcast()399 bool isLinkLocalBroadcast() const { 400 return pick([&](auto& _) { return _.isLinkLocalBroadcast(); }); 401 } 402 403 /** 404 * Return true if the address is a special purpose address, as per rfc6890 405 * (i.e. 0.0.0.0). 406 * For V6, true if the address is not in one of global scope blocks: 407 * 2000::/3, ffxe::/16. 408 */ isNonroutable()409 bool isNonroutable() const { 410 return pick([&](auto& _) { return _.isNonroutable(); }); 411 } 412 413 /** 414 * Return true if the address is private, as per rfc1918 and rfc4193 415 * (for example, 192.168.xxx.xxx or fc00::/7 addresses) 416 */ isPrivate()417 bool isPrivate() const { 418 return pick([&](auto& _) { return _.isPrivate(); }); 419 } 420 421 // Return true if the address is a multicast address. isMulticast()422 bool isMulticast() const { 423 return pick([&](auto& _) { return _.isMulticast(); }); 424 } 425 426 /** 427 * Creates IPAddress instance with all but most significant numBits set to 0. 428 * @param [in] numBits number of bits to mask 429 * @throws abort if numBits > bitCount() 430 * @return IPAddress instance with bits set to 0 431 */ mask(uint8_t numBits)432 IPAddress mask(uint8_t numBits) const { 433 return pick([&](auto& _) { return IPAddress(_.mask(numBits)); }); 434 } 435 436 /** 437 * Provides a string representation of address. 438 * @note The string representation is calculated on demand. 439 * @throws IPAddressFormatException on inet_ntop error 440 */ str()441 std::string str() const { 442 return pick([&](auto& _) { return _.str(); }); 443 } 444 445 /** 446 * Return the fully qualified string representation of the address. 447 * For V4 addresses this is the same as calling str(). For V6 addresses 448 * this is the hex representation with : characters inserted every 4 digits. 449 */ toFullyQualified()450 std::string toFullyQualified() const { 451 return pick([&](auto& _) { return _.toFullyQualified(); }); 452 } 453 454 /// Same as toFullyQualified but append to an output string. toFullyQualifiedAppend(std::string & out)455 void toFullyQualifiedAppend(std::string& out) const { 456 return pick([&](auto& _) { return _.toFullyQualifiedAppend(out); }); 457 } 458 459 // Address version (0 if empty, or 4 or 6 if nonempty) version()460 uint8_t version() const { 461 return pick([&](auto& _) { return _.version(); }); 462 } 463 464 /** 465 * Access to address bytes, in network byte order. 466 */ bytes()467 const unsigned char* bytes() const { 468 return pick([&](auto& _) { return _.bytes(); }); 469 } 470 471 private: 472 [[noreturn]] void asV4Throw() const; 473 [[noreturn]] void asV6Throw() const; 474 475 typedef union IPAddressV46 { 476 IPAddressNone ipNoneAddr; 477 IPAddressV4 ipV4Addr; 478 IPAddressV6 ipV6Addr; IPAddressV46()479 IPAddressV46() noexcept : ipNoneAddr() {} IPAddressV46(const IPAddressV4 & addr)480 explicit IPAddressV46(const IPAddressV4& addr) noexcept : ipV4Addr(addr) {} IPAddressV46(const IPAddressV6 & addr)481 explicit IPAddressV46(const IPAddressV6& addr) noexcept : ipV6Addr(addr) {} 482 } IPAddressV46; 483 IPAddressV46 addr_; 484 sa_family_t family_; 485 }; 486 487 // boost::hash uses hash_value() so this allows boost::hash to work 488 // automatically for IPAddress 489 std::size_t hash_value(const IPAddress& addr); 490 std::ostream& operator<<(std::ostream& os, const IPAddress& addr); 491 // Define toAppend() to allow IPAddress to be used with folly::to<string> 492 void toAppend(IPAddress addr, std::string* result); 493 void toAppend(IPAddress addr, fbstring* result); 494 495 /** 496 * Return true if two addresses are equal. 497 * 498 * @note This takes into consideration V4 mapped addresses as well. If one 499 * address is v4 mapped we compare the v4 addresses. 500 * 501 * @return true if the two addresses are equal. 502 */ 503 bool operator==(const IPAddress& addr1, const IPAddress& addr2); 504 // Return true if addr1 < addr2 505 bool operator<(const IPAddress& addr1, const IPAddress& addr2); 506 // Derived operators 507 inline bool operator!=(const IPAddress& a, const IPAddress& b) { 508 return !(a == b); 509 } 510 inline bool operator>(const IPAddress& a, const IPAddress& b) { 511 return b < a; 512 } 513 inline bool operator<=(const IPAddress& a, const IPAddress& b) { 514 return !(a > b); 515 } 516 inline bool operator>=(const IPAddress& a, const IPAddress& b) { 517 return !(a < b); 518 } 519 520 } // namespace folly 521 522 namespace std { 523 template <> 524 struct hash<folly::IPAddress> { 525 size_t operator()(const folly::IPAddress& addr) const { return addr.hash(); } 526 }; 527 } // namespace std 528