#include "folly/Exception.h"
#include "folly/Traits.h"
+// Ignore -Wformat-nonliteral warnings within this file
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
namespace folly {
namespace detail {
}
} // namespace folly
+
+#pragma GCC diagnostic pop
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IPAddress.h"
+
+#include <limits>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "folly/String.h"
+
+using std::ostream;
+using std::string;
+using std::vector;
+
+namespace folly {
+
+// free functions
+size_t hash_value(const IPAddress& addr) {
+ return addr.hash();
+}
+ostream& operator<<(ostream& os, const IPAddress& addr) {
+ os << addr.str();
+ return os;
+}
+void toAppend(IPAddress addr, string* result) {
+ result->append(addr.str());
+}
+void toAppend(IPAddress addr, fbstring* result) {
+ result->append(addr.str());
+}
+
+// public static
+IPAddressV4 IPAddress::createIPv4(const IPAddress& addr) {
+ if (addr.isV4()) {
+ return addr.asV4();
+ } else {
+ return addr.asV6().createIPv4();
+ }
+}
+
+// public static
+IPAddressV6 IPAddress::createIPv6(const IPAddress& addr) {
+ if (addr.isV6()) {
+ return addr.asV6();
+ } else {
+ return addr.asV4().createIPv6();
+ }
+}
+
+// public static
+CIDRNetwork IPAddress::createNetwork(StringPiece ipSlashCidr,
+ int defaultCidr, /* = -1 */
+ bool applyMask /* = true */) {
+ if (defaultCidr > std::numeric_limits<uint8_t>::max()) {
+ throw std::range_error("defaultCidr must be <= UINT8_MAX");
+ }
+ vector<string> vec;
+ split("/", ipSlashCidr, vec);
+ vector<string>::size_type elemCount = vec.size();
+
+ if (elemCount == 0 || // weird invalid string
+ elemCount > 2) { // invalid string (IP/CIDR/extras)
+ throw IPAddressFormatException("Invalid ipSlashCidr specified. ",
+ "Expected IP/CIDR format, got ",
+ "'", ipSlashCidr, "'");
+ }
+ IPAddress subnet(vec.at(0));
+ uint8_t cidr = (defaultCidr > -1) ? defaultCidr : (subnet.isV4() ? 32 : 128);
+
+ if (elemCount == 2) {
+ try {
+ cidr = to<uint8_t>(vec.at(1));
+ } catch (...) {
+ throw IPAddressFormatException("Mask value ",
+ "'", vec.at(1), "' not a valid mask");
+ }
+ }
+ if (cidr > subnet.bitCount()) {
+ throw IPAddressFormatException("CIDR value '", cidr, "' ",
+ "is > network bit count ",
+ "'", subnet.bitCount(), "'");
+ }
+ return std::make_pair(applyMask ? subnet.mask(cidr) : subnet, cidr);
+}
+
+// public static
+IPAddress IPAddress::fromBinary(ByteRange bytes) {
+ if (bytes.size() == 4) {
+ return IPAddress(IPAddressV4::fromBinary(bytes));
+ } else if (bytes.size() == 16) {
+ return IPAddress(IPAddressV6::fromBinary(bytes));
+ } else {
+ string hexval = detail::Bytes::toHex(bytes.data(), bytes.size());
+ throw IPAddressFormatException("Invalid address with hex value ",
+ "'", hexval, "'");
+ }
+}
+
+// public static
+IPAddress IPAddress::fromLong(uint32_t src) {
+ return IPAddress(IPAddressV4::fromLong(src));
+}
+IPAddress IPAddress::fromLongHBO(uint32_t src) {
+ return IPAddress(IPAddressV4::fromLongHBO(src));
+}
+
+// default constructor
+IPAddress::IPAddress()
+ : addr_()
+ , family_(AF_UNSPEC)
+{
+}
+
+// public string constructor
+IPAddress::IPAddress(StringPiece addr)
+ : addr_()
+ , family_(AF_UNSPEC)
+{
+ string ip = addr.str(); // inet_pton() needs NUL-terminated string
+ auto throwFormatException = [&](const string& msg) {
+ throw IPAddressFormatException("Invalid IP '", ip, "': ", msg);
+ };
+
+ if (ip.size() < 2) {
+ throwFormatException("address too short");
+ }
+ if (ip.front() == '[' && ip.back() == ']') {
+ ip = ip.substr(1, ip.size() - 2);
+ }
+
+ // need to check for V4 address second, since IPv4-mapped IPv6 addresses may
+ // contain a period
+ if (ip.find(':') != string::npos) {
+ in6_addr ipAddr;
+ if (inet_pton(AF_INET6, ip.c_str(), &ipAddr) != 1) {
+ throwFormatException("inet_pton failed for V6 address");
+ }
+ addr_ = IPAddressV46(IPAddressV6(ipAddr));
+ family_ = AF_INET6;
+ } else if (ip.find('.') != string::npos) {
+ in_addr ipAddr;
+ if (inet_pton(AF_INET, ip.c_str(), &ipAddr) != 1) {
+ throwFormatException("inet_pton failed for V4 address");
+ }
+ addr_ = IPAddressV46(IPAddressV4(ipAddr));
+ family_ = AF_INET;
+ } else {
+ throwFormatException("invalid address format");
+ }
+}
+
+// public sockaddr constructor
+IPAddress::IPAddress(const sockaddr* addr)
+ : addr_()
+ , family_(AF_UNSPEC)
+{
+ if (addr == nullptr) {
+ throw IPAddressFormatException("sockaddr == nullptr");
+ }
+ family_ = addr->sa_family;
+ switch (addr->sa_family) {
+ case AF_INET: {
+ const sockaddr_in *v4addr = reinterpret_cast<const sockaddr_in*>(addr);
+ addr_.ipV4Addr = IPAddressV4(v4addr->sin_addr);
+ break;
+ }
+ case AF_INET6: {
+ const sockaddr_in6 *v6addr = reinterpret_cast<const sockaddr_in6*>(addr);
+ addr_.ipV6Addr = IPAddressV6(v6addr->sin6_addr);
+ break;
+ }
+ default:
+ throw InvalidAddressFamilyException(addr->sa_family);
+ }
+}
+
+// public ipv4 constructor
+IPAddress::IPAddress(const IPAddressV4 ipV4Addr)
+ : addr_(ipV4Addr)
+ , family_(AF_INET)
+{
+}
+
+// public ipv4 constructor
+IPAddress::IPAddress(const in_addr ipV4Addr)
+ : addr_(IPAddressV4(ipV4Addr))
+ , family_(AF_INET)
+{
+}
+
+// public ipv6 constructor
+IPAddress::IPAddress(const IPAddressV6& ipV6Addr)
+ : addr_(ipV6Addr)
+ , family_(AF_INET6)
+{
+}
+
+// public ipv6 constructor
+IPAddress::IPAddress(const in6_addr& ipV6Addr)
+ : addr_(IPAddressV6(ipV6Addr))
+ , family_(AF_INET6)
+{
+}
+
+// Assign from V4 address
+IPAddress& IPAddress::operator=(const IPAddressV4& ipv4_addr) {
+ addr_ = IPAddressV46(ipv4_addr);
+ family_ = AF_INET;
+ return *this;
+}
+
+// Assign from V6 address
+IPAddress& IPAddress::operator=(const IPAddressV6& ipv6_addr) {
+ addr_ = IPAddressV46(ipv6_addr);
+ family_ = AF_INET6;
+ return *this;
+}
+
+// public
+bool IPAddress::inSubnet(StringPiece cidrNetwork) const {
+ auto subnetInfo = IPAddress::createNetwork(cidrNetwork);
+ return inSubnet(subnetInfo.first, subnetInfo.second);
+}
+
+// public
+bool IPAddress::inSubnet(const IPAddress& subnet, uint8_t cidr) const {
+ if (bitCount() == subnet.bitCount()) {
+ if (isV4()) {
+ return asV4().inSubnet(subnet.asV4(), cidr);
+ } else {
+ return asV6().inSubnet(subnet.asV6(), cidr);
+ }
+ }
+ // an IPv4 address can never belong in a IPv6 subnet unless the IPv6 is a 6to4
+ // address and vice-versa
+ if (isV6()) {
+ const IPAddressV6& v6addr = asV6();
+ const IPAddressV4& v4subnet = subnet.asV4();
+ if (v6addr.is6To4()) {
+ return v6addr.getIPv4For6To4().inSubnet(v4subnet, cidr);
+ }
+ } else if (subnet.isV6()) {
+ const IPAddressV6& v6subnet = subnet.asV6();
+ const IPAddressV4& v4addr = asV4();
+ if (v6subnet.is6To4()) {
+ return v4addr.inSubnet(v6subnet.getIPv4For6To4(), cidr);
+ }
+ }
+ return false;
+}
+
+// public
+bool IPAddress::inSubnetWithMask(const IPAddress& subnet,
+ ByteRange mask) const {
+ auto mkByteArray4 = [&]() -> ByteArray4 {
+ ByteArray4 ba{{0}};
+ std::memcpy(ba.data(), mask.begin(), std::min<size_t>(mask.size(), 4));
+ return ba;
+ };
+
+ if (bitCount() == subnet.bitCount()) {
+ if (isV4()) {
+ return asV4().inSubnetWithMask(subnet.asV4(), mkByteArray4());
+ } else {
+ ByteArray16 ba{{0}};
+ std::memcpy(ba.data(), mask.begin(), std::min<size_t>(mask.size(), 16));
+ return asV6().inSubnetWithMask(subnet.asV6(), ba);
+ }
+ }
+
+ // an IPv4 address can never belong in a IPv6 subnet unless the IPv6 is a 6to4
+ // address and vice-versa
+ if (isV6()) {
+ const IPAddressV6& v6addr = asV6();
+ const IPAddressV4& v4subnet = subnet.asV4();
+ if (v6addr.is6To4()) {
+ return v6addr.getIPv4For6To4().inSubnetWithMask(v4subnet, mkByteArray4());
+ }
+ } else if (subnet.isV6()) {
+ const IPAddressV6& v6subnet = subnet.asV6();
+ const IPAddressV4& v4addr = asV4();
+ if (v6subnet.is6To4()) {
+ return v4addr.inSubnetWithMask(v6subnet.getIPv4For6To4(), mkByteArray4());
+ }
+ }
+ return false;
+}
+
+uint8_t IPAddress::getNthMSByte(size_t byteIndex) const {
+ const auto highestIndex = byteCount() - 1;
+ if (byteIndex > highestIndex) {
+ throw std::invalid_argument(to<string>("Byte index must be <= ",
+ to<string>(highestIndex), " for addresses of type :",
+ detail::familyNameStr(family())));
+ }
+ if (isV4()) {
+ return asV4().bytes()[byteIndex];
+ }
+ return asV6().bytes()[byteIndex];
+}
+
+// public
+bool operator==(const IPAddress& addr1, const IPAddress& addr2) {
+ if (addr1.family() == addr2.family()) {
+ if (addr1.isV6()) {
+ return (addr1.asV6() == addr2.asV6());
+ } else if (addr1.isV4()) {
+ return (addr1.asV4() == addr2.asV4());
+ } else {
+ CHECK_EQ(addr1.family(), AF_UNSPEC);
+ // Two default initialized AF_UNSPEC addresses should be considered equal.
+ // AF_UNSPEC is the only other value for which an IPAddress can be
+ // created, in the default constructor case.
+ return true;
+ }
+ }
+ // addr1 is v4 mapped v6 address, addr2 is v4
+ if (addr1.isIPv4Mapped()) {
+ if (IPAddress::createIPv4(addr1) == addr2.asV4()) {
+ return true;
+ }
+ }
+ // addr2 is v4 mapped v6 address, addr1 is v4
+ if (addr2.isIPv4Mapped()) {
+ if (IPAddress::createIPv4(addr2) == addr1.asV4()) {
+ return true;
+ }
+ }
+ // we only compare IPv4 and IPv6 addresses
+ return false;
+}
+
+bool operator<(const IPAddress& addr1, const IPAddress& addr2) {
+ if (addr1.family() == addr2.family()) {
+ if (addr1.isV6()) {
+ return (addr1.asV6() < addr2.asV6());
+ } else if (addr1.isV4()) {
+ return (addr1.asV4() < addr2.asV4());
+ } else {
+ CHECK_EQ(addr1.family(), AF_UNSPEC);
+ // Two default initialized AF_UNSPEC addresses can not be less than each
+ // other. AF_UNSPEC is the only other value for which an IPAddress can be
+ // created, in the default constructor case.
+ return false;
+ }
+ }
+ if (addr1.isV6()) {
+ // means addr2 is v4, convert it to a mapped v6 address and compare
+ return addr1.asV6() < addr2.asV4().createIPv6();
+ }
+ if (addr2.isV6()) {
+ // means addr2 is v6, convert addr1 to v4 mapped and compare
+ return addr1.asV4().createIPv6() < addr2.asV6();
+ }
+ return false;
+}
+
+CIDRNetwork
+IPAddress::longestCommonPrefix(const CIDRNetwork& one, const CIDRNetwork& two) {
+ if (one.first.family() != two.first.family()) {
+ throw std::invalid_argument(to<string>("Can't compute "
+ "longest common prefix between addresses of different families. "
+ "Passed: ", detail::familyNameStr(one.first.family()), " and ",
+ detail::familyNameStr(two.first.family())));
+ }
+ if (one.first.isV4()) {
+ auto prefix = IPAddressV4::longestCommonPrefix(
+ {one.first.asV4(), one.second},
+ {two.first.asV4(), two.second});
+ return {IPAddress(prefix.first), prefix.second};
+ } else if (one.first.isV6()) {
+ auto prefix = IPAddressV6::longestCommonPrefix(
+ {one.first.asV6(), one.second},
+ {two.first.asV6(), two.second});
+ return {IPAddress(prefix.first), prefix.second};
+ } else {
+ throw std::invalid_argument("Unknown address family");
+ }
+ return {IPAddress(0), 0};
+}
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility> // std::pair
+
+#include <boost/operators.hpp>
+
+#include "folly/Format.h"
+#include "folly/Range.h"
+#include "folly/IPAddressException.h"
+#include "folly/IPAddressV4.h"
+#include "folly/IPAddressV6.h"
+#include "folly/detail/IPAddress.h"
+
+namespace folly {
+
+class IPAddress;
+
+/**
+ * Pair of IPAddress, netmask
+ */
+typedef std::pair<IPAddress, uint8_t> CIDRNetwork;
+
+/**
+ * Provides a unified interface for IP addresses.
+ *
+ * @note If you compare 2 IPAddress instances, v4-to-v6-mapped addresses are
+ * compared as V4 addresses.
+ *
+ * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO
+ * if working in host byte order.
+ *
+ * Example usage:
+ * @code
+ * IPAddress v4addr("192.0.2.129");
+ * IPAddress v6map("::ffff:192.0.2.129");
+ * CHECK(v4addr.inSubnet("192.0.2.0/24") ==
+ * v4addr.inSubnet(IPAddress("192.0.2.0"), 24));
+ * CHECK(v4addr.inSubnet("192.0.2.128/30"));
+ * CHECK(!v4addr.inSubnet("192.0.2.128/32"));
+ * CHECK(v4addr.asV4().toLong() == 2164392128);
+ * CHECK(v4addr.asV4().toLongHBO() == 3221226113);
+ * CHECK(v4addr.isV4());
+ * CHECK(v6addr.isV6());
+ * CHECK(v4addr == v6map);
+ * CHECK(v6map.isIPv4Mapped());
+ * CHECK(v4addr.asV4() == IPAddress::createIPv4(v6map));
+ * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6());
+ * @encode
+ */
+class IPAddress : boost::totally_ordered<IPAddress> {
+ public:
+ // return the V4 representation of the address, converting it from V6 to V4 if
+ // needed. Note that this will throw an IPAddressFormatException if the V6
+ // address is not IPv4Mapped.
+ static IPAddressV4 createIPv4(const IPAddress& addr);
+
+ // return the V6 representation of the address, converting it from V4 to V6 if
+ // needed.
+ static IPAddressV6 createIPv6(const IPAddress& addr);
+
+ /**
+ * Create a network and mask from a CIDR formatted address string.
+ * @param [in] ipSlashCidr IP/CIDR formatted string to split
+ * @param [in] defaultCidr default value if no /N specified (if defaultCidr
+ * is -1, will use /32 for IPv4 and /128 for IPv6)
+ * @param [in] mask apply mask on the address or not,
+ * e.g. 192.168.13.46/24 => 192.168.13.0/24
+ * @throws IPAddressFormatException if invalid address
+ * @return pair with IPAddress network and uint8_t mask
+ */
+ static CIDRNetwork createNetwork(
+ StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true);
+
+ /**
+ * Return a string representation of a CIDR block created with createNetwork.
+ * @param [in] network, pair of address and cidr
+ *
+ * @return string representing the netblock
+ */
+ static std::string networkToString(const CIDRNetwork& network) {
+ return network.first.str() + "/" + std::to_string(network.second);
+ }
+
+ /**
+ * Create a new IPAddress instance from the provided binary data
+ * in network byte order.
+ * @throws IPAddressFormatException if len is not 4 or 16
+ */
+ static IPAddress fromBinary(ByteRange bytes);
+
+ /**
+ * Create an IPAddress from a 32bit long (network byte order).
+ * @throws IPAddressFormatException
+ */
+ static IPAddress fromLong(uint32_t src);
+ // Same as above, but host byte order
+ static IPAddress fromLongHBO(uint32_t src);
+
+ // Given 2 IPAddress,mask pairs extract the longest common IPAddress,
+ // mask pair
+ static CIDRNetwork longestCommonPrefix(const CIDRNetwork& one,
+ const CIDRNetwork& two);
+
+ /**
+ * Constructs an uninitialized IPAddress.
+ */
+ IPAddress();
+
+ /**
+ * Parse an IPAddress from a string representation.
+ *
+ * Formats accepted are exactly the same as the ones accepted by inet_pton(),
+ * using AF_INET6 if the string contains colons, and AF_INET otherwise;
+ * with the exception that the whole address can optionally be enclosed
+ * in square brackets.
+ *
+ * @throws IPAddressFormatException
+ */
+ explicit IPAddress(StringPiece ip);
+
+ /**
+ * Create an IPAddress from a sockaddr.
+ * @throws IPAddressFormatException if nullptr or not AF_INET or AF_INET6
+ */
+ explicit IPAddress(const sockaddr* addr);
+
+ // Create an IPAddress from a V4 address
+ /* implicit */ IPAddress(const IPAddressV4 ipV4Addr);
+ /* implicit */ IPAddress(const in_addr addr);
+
+ // Create an IPAddress from a V6 address
+ /* implicit */ IPAddress(const IPAddressV6& ipV6Addr);
+ /* implicit */ IPAddress(const in6_addr& addr);
+
+ // Assign from V4 address
+ IPAddress& operator=(const IPAddressV4& ipV4Addr);
+
+ // Assign from V6 address
+ IPAddress& operator=(const IPAddressV6& ipV6Addr);
+
+ /**
+ * Converts an IPAddress to an IPAddressV4 instance.
+ * @note This is not some handy convenience wrapper to convert an IPv4 address
+ * to a mapped IPv6 address. If you want that use
+ * IPAddress::createIPv6(addr)
+ * @throws IPAddressFormatException is not a V4 instance
+ */
+ const IPAddressV4& asV4() const {
+ if (!isV4()) {
+ auto familyName = detail::familyNameStr(family());
+ throw InvalidAddressFamilyException("Can't convert address with family ",
+ familyName, " to AF_INET address");
+ }
+ return addr_.ipV4Addr;
+ }
+
+ /**
+ * Converts an IPAddress to an IPAddressV6 instance.
+ * @throws InvalidAddressFamilyException is not a V6 instance
+ */
+ const IPAddressV6& asV6() const {
+ if (!isV6()) {
+ auto familyName = detail::familyNameStr(family());
+ throw InvalidAddressFamilyException("Can't convert address with family ",
+ familyName, " to AF_INET6 address");
+ }
+ return addr_.ipV6Addr;
+ }
+
+ // Return sa_family_t of IPAddress
+ sa_family_t family() const { return family_; }
+
+ // Populate sockaddr_storage with an appropriate value
+ int toSockaddrStorage(sockaddr_storage *dest, uint16_t port = 0) const {
+ if (dest == nullptr) {
+ throw IPAddressFormatException("dest must not be null");
+ }
+ memset(dest, 0, sizeof(sockaddr_storage));
+ dest->ss_family = family();
+ if (isV4()) {
+ sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(dest);
+ sin->sin_addr = asV4().toAddr();
+ sin->sin_port = port;
+ return sizeof(*sin);
+ } else if (isV6()) {
+ sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(dest);
+ sin->sin6_addr = asV6().toAddr();
+ sin->sin6_port = port;
+ return sizeof(*sin);
+ } else {
+ throw InvalidAddressFamilyException(family());
+ }
+ }
+
+ /**
+ * Check if the address is found in the specified CIDR netblock.
+ *
+ * This will return false if the specified cidrNet is V4, but the address is
+ * V6. It will also return false if the specified cidrNet is V6 but the
+ * address is V4. This method will do the right thing in the case of a v6
+ * mapped v4 address.
+ *
+ * @note This is slower than the below counterparts. If perf is important use
+ * one of the two argument variations below.
+ * @param [in] ipSlashCidr address in "192.168.1.0/24" format
+ * @throws IPAddressFormatException if no /mask
+ * @return true if address is part of specified subnet with cidr
+ */
+ bool inSubnet(StringPiece ipSlashCidr) const;
+
+ /**
+ * Check if an IPAddress belongs to a subnet.
+ * @param [in] subnet Subnet to check against (e.g. 192.168.1.0)
+ * @param [in] cidr CIDR for subnet (e.g. 24 for /24)
+ * @return true if address is part of specified subnet with cidr
+ */
+ bool inSubnet(const IPAddress& subnet, uint8_t cidr) const;
+
+ /**
+ * Check if an IPAddress belongs to the subnet with the given mask.
+ * This is the same as inSubnet but the mask is provided instead of looked up
+ * from the cidr.
+ * @param [in] subnet Subnet to check against
+ * @param [in] mask The netmask for the subnet
+ * @return true if address is part of the specified subnet with mask
+ */
+ bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const;
+
+ // @return true if address is a v4 mapped address
+ bool isIPv4Mapped() const {
+ return isV6() && asV6().isIPv4Mapped();
+ }
+
+ // @return true if this is an IPAddressV4 instance
+ bool isV4() const { return (family_ == AF_INET); }
+
+ // @return true if this is an IPAddressV6 instance
+ bool isV6() const { return (family_ == AF_INET6); }
+
+ // @return true if this address is all zeros
+ bool isZero() const {
+ return isV4() ? asV4().isZero()
+ : asV6().isZero();
+ }
+
+ // Number of bits in the address representation.
+ size_t bitCount() const {
+ return isV4() ? IPAddressV4::bitCount()
+ : IPAddressV6::bitCount();
+ }
+ // Number of bytes in the address representation.
+ size_t byteCount() const {
+ return bitCount() / 8;
+ }
+ //get nth most significant bit - 0 indexed
+ bool getNthMSBit(size_t bitIndex) const {
+ return detail::getNthMSBitImpl(*this, bitIndex, family());
+ }
+ //get nth most significant byte - 0 indexed
+ uint8_t getNthMSByte(size_t byteIndex) const;
+ //get nth bit - 0 indexed
+ bool getNthLSBit(size_t bitIndex) const {
+ return getNthMSBit(bitCount() - bitIndex - 1);
+ }
+ //get nth byte - 0 indexed
+ uint8_t getNthLSByte(size_t byteIndex) const {
+ return getNthMSByte(byteCount() - byteIndex - 1);
+ }
+ /**
+ * Get human-readable string representation of the address.
+ *
+ * This prints a string representation of the address, for human consumption
+ * or logging. The string will take the form of a JSON object that looks like:
+ * {family:'AF_INET|AF_INET6', addr:'address', hash:long}.
+ */
+ std::string toJson() const {
+ return isV4() ? asV4().toJson()
+ : asV6().toJson();
+ }
+
+ // Hash of address
+ std::size_t hash() const {
+ return isV4() ? asV4().hash()
+ : asV6().hash();
+ }
+
+ // Return true if the address qualifies as localhost.
+ bool isLoopback() const {
+ return isV4() ? asV4().isLoopback()
+ : asV6().isLoopback();
+ }
+
+ // Return true if the address qualifies as broadcast.
+ bool isLinkLocalBroadcast() const {
+ return isV4() ? asV4().isLinkLocalBroadcast()
+ : asV6().isLinkLocalBroadcast();
+ }
+
+ /**
+ * Return true if the address is a special purpose address, as per rfc6890
+ * (i.e. 0.0.0.0).
+ * For V6, true if the address is not in one of global scope blocks:
+ * 2000::/3, ffxe::/16.
+ */
+ bool isNonroutable() const {
+ return isV4() ? asV4().isNonroutable()
+ : asV6().isNonroutable();
+ }
+
+ /**
+ * Return true if the address is private, as per rfc1918 and rfc4193
+ * (for example, 192.168.xxx.xxx or fc00::/7 addresses)
+ */
+ bool isPrivate() const {
+ return isV4() ? asV4().isPrivate()
+ : asV6().isPrivate();
+ }
+
+ // Return true if the address is a multicast address.
+ bool isMulticast() const {
+ return isV4() ? asV4().isMulticast()
+ : asV6().isMulticast();
+ }
+
+ /**
+ * Creates IPAddress instance with all but most significant numBits set to 0.
+ * @param [in] numBits number of bits to mask
+ * @throws abort if numBits > bitCount()
+ * @return IPAddress instance with bits set to 0
+ */
+ IPAddress mask(uint8_t numBits) const {
+ return isV4() ? IPAddress(std::move(asV4().mask(numBits)))
+ : IPAddress(std::move(asV6().mask(numBits)));
+ }
+
+ /**
+ * Provides a string representation of address.
+ * @note The string representation is calculated on demand.
+ * @throws IPAddressFormatException on inet_ntop error
+ */
+ std::string str() const {
+ return isV4() ? asV4().str()
+ : asV6().str();
+ }
+
+ /**
+ * Return the fully qualified string representation of the address.
+ * For V4 addresses this is the same as calling str(). For V6 addresses
+ * this is the hex representation with : characters inserted every 4 digits.
+ */
+ std::string toFullyQualified() const {
+ return isV4() ? asV4().toFullyQualified()
+ : asV6().toFullyQualified();
+ }
+
+ // Address version (4 or 6)
+ uint8_t version() const {
+ return isV4() ? asV4().version()
+ : asV6().version();
+ }
+
+ /**
+ * Access to address bytes, in network byte order.
+ */
+ const unsigned char* bytes() const {
+ return isV4() ? asV4().bytes() : asV6().bytes();
+ }
+
+ private:
+ typedef union IPAddressV46 {
+ IPAddressV4 ipV4Addr;
+ IPAddressV6 ipV6Addr;
+ // default constructor
+ IPAddressV46() {
+ std::memset(this, 0, sizeof(IPAddressV46));
+ }
+ explicit IPAddressV46(const IPAddressV4& addr): ipV4Addr(addr) {}
+ explicit IPAddressV46(const IPAddressV6& addr): ipV6Addr(addr) {}
+ } IPAddressV46;
+ IPAddressV46 addr_;
+ sa_family_t family_;
+};
+
+// boost::hash uses hash_value() so this allows boost::hash to work
+// automatically for IPAddress
+std::size_t hash_value(const IPAddress& addr);
+std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
+// Define toAppend() to allow IPAddress to be used with folly::to<string>
+void toAppend(IPAddress addr, std::string* result);
+void toAppend(IPAddress addr, fbstring* result);
+
+/**
+ * Return true if two addresses are equal.
+ *
+ * @note This takes into consideration V4 mapped addresses as well. If one
+ * address is v4 mapped we compare the v4 addresses.
+ *
+ * @return true if the two addresses are equal.
+ */
+bool operator==(const IPAddress& addr1, const IPAddress& addr2);
+// Return true if addr1 < addr2
+bool operator<(const IPAddress& addr1, const IPAddress& addr2);
+
+} // folly
+
+namespace std {
+template<>
+struct hash<folly::IPAddress> {
+ size_t operator()(const folly::IPAddress& addr) const {
+ return addr.hash();
+ }
+};
+} // std
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <exception>
+#include <string>
+
+#include "folly/Conv.h"
+#include "folly/detail/IPAddress.h"
+
+namespace folly {
+
+/**
+ * Exception for invalid IP addresses.
+ */
+class IPAddressFormatException : public std::exception {
+ public:
+ explicit IPAddressFormatException(const std::string& msg)
+ : msg_(msg) {}
+ IPAddressFormatException(
+ const IPAddressFormatException& exception_) = default;
+ template<typename... Args>
+ explicit IPAddressFormatException(Args&&... args)
+ : msg_(to<std::string>(std::forward<Args>(args)...)) {}
+
+ virtual ~IPAddressFormatException() noexcept {}
+ virtual const char *what(void) const noexcept {
+ return msg_.c_str();
+ }
+
+ private:
+ const std::string msg_;
+};
+
+class InvalidAddressFamilyException : public IPAddressFormatException {
+ public:
+ explicit InvalidAddressFamilyException(const std::string& msg)
+ : IPAddressFormatException(msg) {}
+ InvalidAddressFamilyException(
+ const InvalidAddressFamilyException& ex) = default;
+ explicit InvalidAddressFamilyException(sa_family_t family)
+ : IPAddressFormatException("Address family " +
+ detail::familyNameStr(family) +
+ " is not AF_INET or AF_INET6") {}
+ template<typename... Args>
+ explicit InvalidAddressFamilyException(Args&&... args)
+ : IPAddressFormatException(std::forward<Args>(args)...) {}
+};
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IPAddressV4.h"
+
+#include <ostream>
+#include <string>
+
+#include "folly/Format.h"
+#include "folly/IPAddress.h"
+#include "folly/IPAddressV6.h"
+
+using std::ostream;
+using std::string;
+
+namespace folly {
+
+// free functions
+size_t hash_value(const IPAddressV4& addr) {
+ return addr.hash();
+}
+ostream& operator<<(ostream& os, const IPAddressV4& addr) {
+ os << addr.str();
+ return os;
+}
+void toAppend(IPAddressV4 addr, string* result) {
+ result->append(addr.str());
+}
+void toAppend(IPAddressV4 addr, fbstring* result) {
+ result->append(addr.str());
+}
+
+
+// public static
+IPAddressV4 IPAddressV4::fromLong(uint32_t src) {
+ in_addr addr;
+ addr.s_addr = src;
+ return IPAddressV4(addr);
+}
+
+IPAddressV4 IPAddressV4::fromLongHBO(uint32_t src) {
+ in_addr addr;
+ addr.s_addr = htonl(src);
+ return IPAddressV4(addr);
+}
+
+// static public
+uint32_t IPAddressV4::toLong(StringPiece ip) {
+ auto str = ip.str();
+ in_addr addr;
+ if (inet_pton(AF_INET, str.c_str(), &addr) != 1) {
+ throw IPAddressFormatException("Can't convert invalid IP '", ip, "' ",
+ "to long");
+ }
+ return addr.s_addr;
+}
+
+// static public
+uint32_t IPAddressV4::toLongHBO(StringPiece ip) {
+ return ntohl(IPAddressV4::toLong(ip));
+}
+
+// public default constructor
+IPAddressV4::IPAddressV4() {
+}
+
+// ByteArray4 constructor
+IPAddressV4::IPAddressV4(const ByteArray4& src)
+ : addr_(src)
+{
+}
+
+// public string constructor
+IPAddressV4::IPAddressV4(StringPiece addr)
+ : addr_()
+{
+ auto ip = addr.str();
+ if (inet_pton(AF_INET, ip.c_str(), &addr_.inAddr_) != 1) {
+ throw IPAddressFormatException("Invalid IPv4 address '", addr, "'");
+ }
+}
+
+// in_addr constructor
+IPAddressV4::IPAddressV4(const in_addr src)
+ : addr_(src)
+{
+}
+
+// public
+void IPAddressV4::setFromBinary(ByteRange bytes) {
+ if (bytes.size() != 4) {
+ throw IPAddressFormatException("Invalid IPv4 binary data: length must "
+ "be 4 bytes, got ", bytes.size());
+ }
+ memcpy(&addr_.inAddr_.s_addr, bytes.data(), sizeof(in_addr));
+}
+
+// public
+IPAddressV6 IPAddressV4::createIPv6() const {
+ ByteArray16 ba{{0}};
+ ba[10] = 0xff;
+ ba[11] = 0xff;
+ std::memcpy(&ba[12], bytes(), 4);
+ return IPAddressV6(ba);
+}
+
+// public
+string IPAddressV4::toJson() const {
+ return format(
+ "{{family:'AF_INET', addr:'{}', hash:{}}}", str(), hash()).str();
+}
+
+// public
+bool IPAddressV4::inSubnet(StringPiece cidrNetwork) const {
+ auto subnetInfo = IPAddress::createNetwork(cidrNetwork);
+ auto addr = subnetInfo.first;
+ if (!addr.isV4()) {
+ throw IPAddressFormatException("Address '", addr.toJson(), "' ",
+ "is not a V4 address");
+ }
+ return inSubnetWithMask(addr.asV4(), fetchMask(subnetInfo.second));
+}
+
+// public
+bool IPAddressV4::inSubnetWithMask(const IPAddressV4& subnet,
+ const ByteArray4 cidrMask) const {
+ const ByteArray4 mask = detail::Bytes::mask(toByteArray(), cidrMask);
+ const ByteArray4 subMask = detail::Bytes::mask(subnet.toByteArray(),
+ cidrMask);
+ return (mask == subMask);
+}
+
+// public
+bool IPAddressV4::isNonroutable() const {
+ auto ip = toLongHBO();
+ return isPrivate() ||
+ (ip <= 0x00FFFFFF) || // 0.0.0.0-0.255.255.255
+ (ip >= 0xC0000000 && ip <= 0xC00000FF) || // 192.0.0.0-192.0.0.255
+ (ip >= 0xC0000200 && ip <= 0xC00002FF) || // 192.0.2.0-192.0.2.255
+ (ip >= 0xC6120000 && ip <= 0xC613FFFF) || // 198.18.0.0-198.19.255.255
+ (ip >= 0xC6336400 && ip <= 0xC63364FF) || // 198.51.100.0-198.51.100.255
+ (ip >= 0xCB007100 && ip <= 0xCB0071FF) || // 203.0.113.0-203.0.113.255
+ (ip >= 0xE0000000 && ip <= 0xFFFFFFFF); // 224.0.0.0-255.255.255.255
+}
+
+// public
+bool IPAddressV4::isPrivate() const {
+ auto ip = toLongHBO();
+ return
+ (ip >= 0x0A000000 && ip <= 0x0AFFFFFF) || // 10.0.0.0-10.255.255.255
+ (ip >= 0x7F000000 && ip <= 0x7FFFFFFF) || // 127.0.0.0-127.255.255.255
+ (ip >= 0xA9FE0000 && ip <= 0xA9FEFFFF) || // 169.254.0.0-169.254.255.255
+ (ip >= 0xAC100000 && ip <= 0xAC1FFFFF) || // 172.16.0.0-172.31.255.255
+ (ip >= 0xC0A80000 && ip <= 0xC0A8FFFF); // 192.168.0.0-192.168.255.255
+}
+
+// public
+bool IPAddressV4::isMulticast() const {
+ return (toLongHBO() & 0xf0000000) == 0xe0000000;
+}
+
+// public
+IPAddressV4 IPAddressV4::mask(size_t numBits) const {
+ static const auto bits = bitCount();
+ if (numBits > bits) {
+ throw IPAddressFormatException("numBits(", numBits,
+ ") > bitsCount(", bits, ")");
+ }
+
+ ByteArray4 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_);
+ return IPAddressV4(ba);
+}
+
+// public
+// Taken from TSocketAddress::getAddressStrIPv4Fast
+string IPAddressV4::str() const {
+ char buf[INET_ADDRSTRLEN] = {0};
+ const uint8_t* ip = addr_.bytes_.data();
+ int pos = 0;
+ for (int k = 0; k < 4; ++k) {
+ uint8_t num = ip[k];
+
+ if (num >= 200) {
+ buf[pos++] = '2';
+ num -= 200;
+ } else if (num >= 100) {
+ buf[pos++] = '1';
+ num -= 100;
+ }
+
+ // num < 100
+ if (ip[k] >= 10) {
+ buf[pos++] = '0' + num / 10;
+ buf[pos++] = '0' + num % 10;
+ } else {
+ buf[pos++] = '0' + num;
+ }
+
+ buf[pos++] = '.';
+ }
+ buf[pos-1] = '\0';
+ string ipAddr(buf);
+ return std::move(ipAddr);
+}
+
+// public
+uint8_t IPAddressV4::getNthMSByte(size_t byteIndex) const {
+ const auto highestIndex = byteCount() - 1;
+ if (byteIndex > highestIndex) {
+ throw std::invalid_argument(to<string>("Byte index must be <= ",
+ to<string>(highestIndex), " for addresses of type :",
+ detail::familyNameStr(AF_INET)));
+ }
+ return bytes()[byteIndex];
+}
+// protected
+const ByteArray4 IPAddressV4::fetchMask(size_t numBits) {
+ static const uint8_t bits = bitCount();
+ if (numBits > bits) {
+ throw IPAddressFormatException("IPv4 addresses are 32 bits");
+ }
+ // masks_ is backed by an array so is zero indexed
+ return masks_[numBits];
+}
+
+// static private
+const std::array<ByteArray4, 33> IPAddressV4::masks_ = {{
+ {{0x00, 0x00, 0x00, 0x00}},
+ {{0x80, 0x00, 0x00, 0x00}},
+ {{0xc0, 0x00, 0x00, 0x00}},
+ {{0xe0, 0x00, 0x00, 0x00}},
+ {{0xf0, 0x00, 0x00, 0x00}},
+ {{0xf8, 0x00, 0x00, 0x00}},
+ {{0xfc, 0x00, 0x00, 0x00}},
+ {{0xfe, 0x00, 0x00, 0x00}},
+ {{0xff, 0x00, 0x00, 0x00}},
+ {{0xff, 0x80, 0x00, 0x00}},
+ {{0xff, 0xc0, 0x00, 0x00}},
+ {{0xff, 0xe0, 0x00, 0x00}},
+ {{0xff, 0xf0, 0x00, 0x00}},
+ {{0xff, 0xf8, 0x00, 0x00}},
+ {{0xff, 0xfc, 0x00, 0x00}},
+ {{0xff, 0xfe, 0x00, 0x00}},
+ {{0xff, 0xff, 0x00, 0x00}},
+ {{0xff, 0xff, 0x80, 0x00}},
+ {{0xff, 0xff, 0xc0, 0x00}},
+ {{0xff, 0xff, 0xe0, 0x00}},
+ {{0xff, 0xff, 0xf0, 0x00}},
+ {{0xff, 0xff, 0xf8, 0x00}},
+ {{0xff, 0xff, 0xfc, 0x00}},
+ {{0xff, 0xff, 0xfe, 0x00}},
+ {{0xff, 0xff, 0xff, 0x00}},
+ {{0xff, 0xff, 0xff, 0x80}},
+ {{0xff, 0xff, 0xff, 0xc0}},
+ {{0xff, 0xff, 0xff, 0xe0}},
+ {{0xff, 0xff, 0xff, 0xf0}},
+ {{0xff, 0xff, 0xff, 0xf8}},
+ {{0xff, 0xff, 0xff, 0xfc}},
+ {{0xff, 0xff, 0xff, 0xfe}},
+ {{0xff, 0xff, 0xff, 0xff}}
+}};
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iostream>
+
+#include <boost/operators.hpp>
+
+#include "folly/Hash.h"
+#include "folly/Range.h"
+#include "folly/detail/IPAddress.h"
+
+namespace folly {
+
+class IPAddress;
+class IPAddressV4;
+class IPAddressV6;
+
+/**
+ * Pair of IPAddressV4, netmask
+ */
+typedef std::pair<IPAddressV4, uint8_t> CIDRNetworkV4;
+
+/**
+ * Specialization for IPv4 addresses
+ */
+typedef std::array<uint8_t, 4> ByteArray4;
+
+/**
+ * IPv4 variation of IPAddress.
+ *
+ * Added methods: toLong, toLongHBO and createIPv6
+ *
+ * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO
+ * if working in host byte order.
+ *
+ * @see IPAddress
+ */
+class IPAddressV4 : boost::totally_ordered<IPAddressV4> {
+ public:
+ // create an IPAddressV4 instance from a uint32_t (network byte order)
+ static IPAddressV4 fromLong(uint32_t src);
+ // same as above but host byte order
+ static IPAddressV4 fromLongHBO(uint32_t src);
+
+ /**
+ * Create a new IPAddress instance from the provided binary data.
+ * @throws IPAddressFormatException if the input length is not 4 bytes.
+ */
+ static IPAddressV4 fromBinary(ByteRange bytes) {
+ IPAddressV4 addr;
+ addr.setFromBinary(bytes);
+ return addr;
+ }
+
+ /**
+ * Convert a IPv4 address string to a long in network byte order.
+ * @param [in] ip the address to convert
+ * @return the long representation of the address
+ */
+ static uint32_t toLong(StringPiece ip);
+ // Same as above, but in host byte order.
+ // This is slightly slower than toLong.
+ static uint32_t toLongHBO(StringPiece ip);
+
+ /**
+ * Default constructor for IPAddressV4.
+ *
+ * The address value will be 0.0.0.0
+ */
+ IPAddressV4();
+
+ // Create an IPAddressV4 from a string
+ // @throws IPAddressFormatException
+ explicit IPAddressV4(StringPiece ip);
+
+ // ByteArray4 constructor
+ explicit IPAddressV4(const ByteArray4& src);
+
+ // in_addr constructor
+ explicit IPAddressV4(const in_addr src);
+
+ // Return the V6 mapped representation of the address.
+ IPAddressV6 createIPv6() const;
+
+ // Return the long (network byte order) representation of the address.
+ uint32_t toLong() const {
+ return toAddr().s_addr;
+ }
+
+ // Return the long (host byte order) representation of the address.
+ // This is slightly slower than toLong.
+ uint32_t toLongHBO() const {
+ return ntohl(toLong());
+ }
+
+ /**
+ * @see IPAddress#bitCount
+ * @returns 32
+ */
+ static size_t bitCount() { return 32; }
+
+ /**
+ * @See IPAddress#toJson
+ */
+ std::string toJson() const;
+
+ size_t hash() const {
+ static const uint32_t seed = AF_INET;
+ uint32_t hashed = hash::fnv32_buf(&addr_, 4);
+ return hash::hash_combine(seed, hashed);
+ }
+
+ // @see IPAddress#inSubnet
+ // @throws IPAddressFormatException if string doesn't contain a V4 address
+ bool inSubnet(StringPiece cidrNetwork) const;
+
+ // return true if address is in subnet
+ bool inSubnet(const IPAddressV4& subnet, uint8_t cidr) const {
+ return inSubnetWithMask(subnet, fetchMask(cidr));
+ }
+ bool inSubnetWithMask(const IPAddressV4& subnet, const ByteArray4 mask) const;
+
+ // @see IPAddress#isLoopback
+ bool isLoopback() const {
+ return (INADDR_LOOPBACK == toLongHBO());
+ }
+
+ // @see IPAddress#isNonroutable
+ bool isNonroutable() const;
+
+ // @see IPAddress#isPrivate
+ bool isPrivate() const;
+
+ // @see IPAddress#isMulticast
+ bool isMulticast() const;
+
+ // @see IPAddress#isZero
+ bool isZero() const {
+ return detail::Bytes::isZero(bytes(), 4);
+ }
+
+ bool isLinkLocalBroadcast() const {
+ return (INADDR_BROADCAST == toLongHBO());
+ }
+
+ // @see IPAddress#mask
+ IPAddressV4 mask(size_t numBits) const;
+
+ // @see IPAddress#str
+ std::string str() const;
+
+ // return underlying in_addr structure
+ in_addr toAddr() const { return addr_.inAddr_; }
+
+ sockaddr_in toSockAddr() const {
+ sockaddr_in addr;
+ memset(&addr, 0, sizeof(sockaddr_in));
+ addr.sin_family = AF_INET;
+ memcpy(&addr.sin_addr, &addr_.inAddr_, sizeof(in_addr));
+ return addr;
+ }
+
+ ByteArray4 toByteArray() const {
+ ByteArray4 ba{{0}};
+ std::memcpy(ba.data(), bytes(), 4);
+ return std::move(ba);
+ }
+
+ // @see IPAddress#toFullyQualified
+ std::string toFullyQualified() const { return str(); }
+
+ // @see IPAddress#version
+ size_t version() const { return 4; }
+
+ /**
+ * Return the mask associated with the given number of bits.
+ * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should
+ * be {0xff, 0xff, 0xff, 0x00}.
+ * @param [in] numBits bitmask to retrieve
+ * @throws abort if numBits == 0 or numBits > bitCount()
+ * @return mask associated with numBits
+ */
+ static const ByteArray4 fetchMask(size_t numBits);
+
+ // Given 2 IPAddressV4,mask pairs extract the longest common IPAddress,
+ // mask pair
+ static CIDRNetworkV4 longestCommonPrefix(
+ const CIDRNetworkV4& one, const CIDRNetworkV4& two) {
+ auto prefix =
+ detail::Bytes::longestCommonPrefix(one.first.addr_.bytes_, one.second,
+ two.first.addr_.bytes_, two.second);
+ return {IPAddressV4(prefix.first), prefix.second};
+ }
+ // Number of bytes in the address representation.
+ static size_t byteCount() { return 4; }
+ //get nth most significant bit - 0 indexed
+ bool getNthMSBit(size_t bitIndex) const {
+ return detail::getNthMSBitImpl(*this, bitIndex, AF_INET);
+ }
+ //get nth most significant byte - 0 indexed
+ uint8_t getNthMSByte(size_t byteIndex) const;
+ //get nth bit - 0 indexed
+ bool getNthLSBit(size_t bitIndex) const {
+ return getNthMSBit(bitCount() - bitIndex - 1);
+ }
+ //get nth byte - 0 indexed
+ uint8_t getNthLSByte(size_t byteIndex) const {
+ return getNthMSByte(byteCount() - byteIndex - 1);
+ }
+
+ const unsigned char* bytes() const { return addr_.bytes_.data(); }
+
+ private:
+ union AddressStorage {
+ in_addr inAddr_;
+ ByteArray4 bytes_;
+ AddressStorage() {
+ std::memset(this, 0, sizeof(AddressStorage));
+ }
+ explicit AddressStorage(const ByteArray4 bytes): bytes_(bytes) {}
+ explicit AddressStorage(const in_addr addr): inAddr_(addr) {}
+ } addr_;
+
+ static const std::array<ByteArray4, 33> masks_;
+
+ /**
+ * Set the current IPAddressV4 object to have the address specified by bytes.
+ * @throws IPAddressFormatException if bytes.size() is not 4.
+ */
+ void setFromBinary(ByteRange bytes);
+};
+
+// boost::hash uses hash_value() so this allows boost::hash to work
+// automatically for IPAddressV4
+size_t hash_value(const IPAddressV4& addr);
+std::ostream& operator<<(std::ostream& os, const IPAddressV4& addr);
+// Define toAppend() to allow IPAddressV4 to be used with to<string>
+void toAppend(IPAddressV4 addr, std::string* result);
+void toAppend(IPAddressV4 addr, fbstring* result);
+
+/**
+ * Return true if two addresses are equal.
+ */
+inline bool operator==(const IPAddressV4& addr1, const IPAddressV4& addr2) {
+ return (addr1.toLong() == addr2.toLong());
+}
+// Return true if addr1 < addr2
+inline bool operator<(const IPAddressV4& addr1, const IPAddressV4& addr2) {
+ return (addr1.toLongHBO() < addr2.toLongHBO());
+}
+
+} // folly
+
+namespace std {
+template<>
+struct hash<folly::IPAddressV4> {
+ size_t operator()(const folly::IPAddressV4 addr) const {
+ return addr.hash();
+ }
+};
+} // std
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IPAddressV6.h"
+
+#include <ostream>
+#include <string>
+
+#include "folly/Format.h"
+#include "folly/IPAddress.h"
+#include "folly/IPAddressV4.h"
+#include "folly/MacAddress.h"
+
+using std::ostream;
+using std::string;
+
+namespace folly {
+
+// public static const
+const uint32_t IPAddressV6::PREFIX_TEREDO = 0x20010000;
+const uint32_t IPAddressV6::PREFIX_6TO4 = 0x2002;
+
+// free functions
+size_t hash_value(const IPAddressV6& addr) {
+ return addr.hash();
+}
+ostream& operator<<(ostream& os, const IPAddressV6& addr) {
+ os << addr.str();
+ return os;
+}
+void toAppend(IPAddressV6 addr, string* result) {
+ result->append(addr.str());
+}
+void toAppend(IPAddressV6 addr, fbstring* result) {
+ result->append(addr.str());
+}
+
+// public default constructor
+IPAddressV6::IPAddressV6() {
+}
+
+// public string constructor
+IPAddressV6::IPAddressV6(StringPiece addr) {
+ auto ip = addr.str();
+
+ // Allow addresses surrounded in brackets
+ if (ip.size() < 2) {
+ throw IPAddressFormatException("Invalid IPv6 address '", ip,
+ "': address too short");
+ }
+ if (ip.front() == '[' && ip.back() == ']') {
+ ip = ip.substr(1, ip.size() - 2);
+ }
+
+ if (inet_pton(AF_INET6, ip.c_str(), &addr_.in6Addr_) != 1) {
+ throw IPAddressFormatException("Invalid IPv6 address '", ip, "'");
+ }
+}
+
+// in6_addr constructor
+IPAddressV6::IPAddressV6(const in6_addr& src)
+ : addr_(src)
+{
+}
+
+// ByteArray16 constructor
+IPAddressV6::IPAddressV6(const ByteArray16& src)
+ : addr_(src)
+{
+}
+
+// link-local constructor
+IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac)
+ : addr_(mac) {
+}
+
+IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) {
+ // The link-local address uses modified EUI-64 format,
+ // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A
+ const auto* macBytes = mac.bytes();
+ memcpy(&bytes_.front(), "\xfe\x80\x00\x00\x00\x00\x00\x00", 8);
+ bytes_[8] = macBytes[0] ^ 0x02;
+ bytes_[9] = macBytes[1];
+ bytes_[10] = macBytes[2];
+ bytes_[11] = 0xff;
+ bytes_[12] = 0xfe;
+ bytes_[13] = macBytes[3];
+ bytes_[14] = macBytes[4];
+ bytes_[15] = macBytes[5];
+}
+
+void IPAddressV6::setFromBinary(ByteRange bytes) {
+ if (bytes.size() != 16) {
+ throw IPAddressFormatException("Invalid IPv6 binary data: length must "
+ "be 16 bytes, got ", bytes.size());
+ }
+ memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr));
+}
+
+// public
+IPAddressV4 IPAddressV6::createIPv4() const {
+ if (!isIPv4Mapped()) {
+ throw IPAddressFormatException("addr is not v4-to-v6-mapped");
+ }
+ const unsigned char* by = bytes();
+ return IPAddressV4(detail::Bytes::mkAddress4(&by[12]));
+}
+
+// convert two uint8_t bytes into a uint16_t as hibyte.lobyte
+static inline uint16_t unpack(uint8_t lobyte, uint8_t hibyte) {
+ return ((uint16_t)hibyte << 8) | (uint16_t)lobyte;
+}
+
+// given a src string, unpack count*2 bytes into dest
+// dest must have as much storage as count
+static inline void unpackInto(const unsigned char* src,
+ uint16_t* dest,
+ size_t count) {
+ for (int i = 0, hi = 1, lo = 0; i < count; i++) {
+ dest[i] = unpack(src[hi], src[lo]);
+ hi += 2;
+ lo += 2;
+ }
+}
+
+// public
+IPAddressV4 IPAddressV6::getIPv4For6To4() const {
+ if (!is6To4()) {
+ throw IPAddressV6::TypeError(format(
+ "Invalid IP '{}': not a 6to4 address", str()).str());
+ }
+ // convert 16x8 bytes into first 4x16 bytes
+ uint16_t ints[4] = {0,0,0,0};
+ unpackInto(bytes(), ints, 4);
+ // repack into 4x8
+ union {
+ unsigned char bytes[4];
+ in_addr addr;
+ } ipv4;
+ ipv4.bytes[0] = (uint8_t)((ints[1] & 0xFF00) >> 8);
+ ipv4.bytes[1] = (uint8_t)(ints[1] & 0x00FF);
+ ipv4.bytes[2] = (uint8_t)((ints[2] & 0xFF00) >> 8);
+ ipv4.bytes[3] = (uint8_t)(ints[2] & 0x00FF);
+ return IPAddressV4(ipv4.addr);
+}
+
+// public
+bool IPAddressV6::isIPv4Mapped() const {
+ // v4 mapped addresses have their first 10 bytes set to 0, the next 2 bytes
+ // set to 255 (0xff);
+ const unsigned char* by = bytes();
+
+ // check if first 10 bytes are 0
+ for (int i = 0; i < 10; i++) {
+ if (by[i] != 0x00) {
+ return false;
+ }
+ }
+ // check if bytes 11 and 12 are 255
+ if (by[10] == 0xff && by[11] == 0xff) {
+ return true;
+ }
+ return false;
+}
+
+// public
+IPAddressV6::Type IPAddressV6::type() const {
+ // convert 16x8 bytes into first 2x16 bytes
+ uint16_t ints[2] = {0,0};
+ unpackInto(bytes(), ints, 2);
+
+ if ((((uint32_t)ints[0] << 16) | ints[1]) == IPAddressV6::PREFIX_TEREDO) {
+ return Type::TEREDO;
+ }
+
+ if ((uint32_t)ints[0] == IPAddressV6::PREFIX_6TO4) {
+ return Type::T6TO4;
+ }
+
+ return Type::NORMAL;
+}
+
+// public
+string IPAddressV6::toJson() const {
+ return format(
+ "{{family:'AF_INET6', addr:'{}', hash:{}}}", str(), hash()).str();
+}
+
+// public
+size_t IPAddressV6::hash() const {
+ if (isIPv4Mapped()) {
+ /* An IPAddress containing this object would be equal (i.e. operator==)
+ to an IPAddress containing the corresponding IPv4.
+ So we must make sure that the hash values are the same as well */
+ return IPAddress::createIPv4(*this).hash();
+ }
+
+ static const uint64_t seed = AF_INET6;
+ uint64_t hash1 = 0, hash2 = 0;
+ hash::SpookyHashV2::Hash128(&addr_, 16, &hash1, &hash2);
+ return hash::hash_combine(seed, hash1, hash2);
+}
+
+// public
+bool IPAddressV6::inSubnet(StringPiece cidrNetwork) const {
+ auto subnetInfo = IPAddress::createNetwork(cidrNetwork);
+ auto addr = subnetInfo.first;
+ if (!addr.isV6()) {
+ throw IPAddressFormatException("Address '", addr.toJson(), "' ",
+ "is not a V6 address");
+ }
+ return inSubnetWithMask(addr.asV6(), fetchMask(subnetInfo.second));
+}
+
+// public
+bool IPAddressV6::inSubnetWithMask(const IPAddressV6& subnet,
+ const ByteArray16& cidrMask) const {
+ const ByteArray16 mask = detail::Bytes::mask(toByteArray(), cidrMask);
+ const ByteArray16 subMask = detail::Bytes::mask(subnet.toByteArray(),
+ cidrMask);
+ return (mask == subMask);
+}
+
+// public
+bool IPAddressV6::isLoopback() const {
+ const unsigned char* by = bytes();
+ for (int i = 0; i < 15; i++) {
+ if (by[i] != 0x00) {
+ return false;
+ }
+ }
+ return (by[15] == 0x01);
+}
+
+bool IPAddressV6::isRoutable() const {
+ return
+ // 2000::/3 is the only assigned global unicast block
+ inBinarySubnet({{0x20, 0x00}}, 3) ||
+ // ffxe::/16 are global scope multicast addresses,
+ // which are eligible to be routed over the internet
+ (isMulticast() && getMulticastScope() == 0xe);
+}
+
+bool IPAddressV6::isLinkLocalBroadcast() const {
+ static const IPAddressV6 kLinkLocalBroadcast("ff02::1");
+ return *this == kLinkLocalBroadcast;
+}
+
+// public
+bool IPAddressV6::isPrivate() const {
+ return isLoopback() || inBinarySubnet({{0xfc, 0x00}}, 7);
+}
+
+bool IPAddressV6::isLinkLocal() const {
+ return inBinarySubnet({{0xfe, 0x80}}, 10);
+}
+
+bool IPAddressV6::isMulticast() const {
+ return addr_.bytes_[0] == 0xff;
+}
+
+uint8_t IPAddressV6::getMulticastFlags() const {
+ DCHECK(isMulticast());
+ return ((addr_.bytes_[1] >> 4) & 0xf);
+}
+
+uint8_t IPAddressV6::getMulticastScope() const {
+ DCHECK(isMulticast());
+ return (addr_.bytes_[1] & 0xf);
+}
+
+IPAddressV6 IPAddressV6::getSolicitedNodeAddress() const {
+ // Solicted node addresses must be constructed from unicast (or anycast)
+ // addresses
+ DCHECK(!isMulticast());
+
+ uint8_t bytes[16] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00 };
+ bytes[13] = addr_.bytes_[13];
+ bytes[14] = addr_.bytes_[14];
+ bytes[15] = addr_.bytes_[15];
+ return IPAddressV6::fromBinary(ByteRange(bytes, 16));
+}
+
+// public
+IPAddressV6 IPAddressV6::mask(size_t numBits) const {
+ static const auto bits = bitCount();
+ if (numBits > bits) {
+ throw IPAddressFormatException("numBits(", numBits, ") > bitCount(",
+ bits, ")");
+ }
+ ByteArray16 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_);
+ return IPAddressV6(ba);
+}
+
+// public
+string IPAddressV6::str() const {
+ char buffer[INET6_ADDRSTRLEN] = {0};
+ if (!inet_ntop(AF_INET6, &addr_.in6Addr_, buffer, INET6_ADDRSTRLEN)) {
+ throw IPAddressFormatException("Invalid address with hex ",
+ "'", detail::Bytes::toHex(bytes(), 16), "'");
+ }
+ string ip(buffer);
+ return std::move(ip);
+}
+
+// public
+string IPAddressV6::toFullyQualified() const {
+ auto asHex = detail::Bytes::toHex(bytes(), 16);
+ uint8_t chunks = asHex.size() / 4;
+ for (int chunk = 1; chunk < chunks; chunk++) {
+ // position changes as new characters are inserted
+ int pos = (chunk*4) + (chunk - 1);
+ asHex.insert(pos, ":");
+ }
+ return asHex;
+}
+
+// public
+uint8_t IPAddressV6::getNthMSByte(size_t byteIndex) const {
+ const auto highestIndex = byteCount() - 1;
+ if (byteIndex > highestIndex) {
+ throw std::invalid_argument(to<string>("Byte index must be <= ",
+ to<string>(highestIndex), " for addresses of type :",
+ detail::familyNameStr(AF_INET6)));
+ }
+ return bytes()[byteIndex];
+}
+
+// protected
+const ByteArray16 IPAddressV6::fetchMask(size_t numBits) {
+ static const uint8_t bits = bitCount();
+ if (numBits > bits) {
+ throw IPAddressFormatException("IPv6 addresses are 128 bits.");
+ }
+ // masks_ is backed by an array so is zero indexed
+ return masks_[numBits];
+}
+
+// protected
+bool IPAddressV6::inBinarySubnet(const std::array<uint8_t, 2> addr,
+ size_t numBits) const {
+ const unsigned char* subbytes = mask(numBits).bytes();
+ return (std::memcmp(addr.data(), subbytes, 2) == 0);
+}
+
+// static private
+const std::array<ByteArray16, 129> IPAddressV6::masks_ = {{
+/* /0 */ {{ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /1 */ {{ 0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /2 */ {{ 0xc0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /3 */ {{ 0xe0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /4 */ {{ 0xf0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /5 */ {{ 0xf8,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /6 */ {{ 0xfc,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /7 */ {{ 0xfe,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /8 */ {{ 0xff,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /9 */ {{ 0xff,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /10 */ {{ 0xff,0xc0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /11 */ {{ 0xff,0xe0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /12 */ {{ 0xff,0xf0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /13 */ {{ 0xff,0xf8,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /14 */ {{ 0xff,0xfc,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /15 */ {{ 0xff,0xfe,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /16 */ {{ 0xff,0xff,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /17 */ {{ 0xff,0xff,0x80,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /18 */ {{ 0xff,0xff,0xc0,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /19 */ {{ 0xff,0xff,0xe0,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /20 */ {{ 0xff,0xff,0xf0,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /21 */ {{ 0xff,0xff,0xf8,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /22 */ {{ 0xff,0xff,0xfc,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /23 */ {{ 0xff,0xff,0xfe,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /24 */ {{ 0xff,0xff,0xff,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /25 */ {{ 0xff,0xff,0xff,0x80,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /26 */ {{ 0xff,0xff,0xff,0xc0,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /27 */ {{ 0xff,0xff,0xff,0xe0,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /28 */ {{ 0xff,0xff,0xff,0xf0,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /29 */ {{ 0xff,0xff,0xff,0xf8,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /30 */ {{ 0xff,0xff,0xff,0xfc,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /31 */ {{ 0xff,0xff,0xff,0xfe,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /32 */ {{ 0xff,0xff,0xff,0xff,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /33 */ {{ 0xff,0xff,0xff,0xff,
+ 0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /34 */ {{ 0xff,0xff,0xff,0xff,
+ 0xc0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /35 */ {{ 0xff,0xff,0xff,0xff,
+ 0xe0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /36 */ {{ 0xff,0xff,0xff,0xff,
+ 0xf0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /37 */ {{ 0xff,0xff,0xff,0xff,
+ 0xf8,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /38 */ {{ 0xff,0xff,0xff,0xff,
+ 0xfc,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /39 */ {{ 0xff,0xff,0xff,0xff,
+ 0xfe,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /40 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /41 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /42 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xc0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /43 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xe0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /44 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xf0,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /45 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xf8,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /46 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xfc,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /47 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xfe,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /48 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0x00,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /49 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0x80,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /50 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xc0,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /51 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe0,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /52 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xf0,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /53 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xf8,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /54 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xfc,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /55 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xfe,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /56 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x00,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /57 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x80,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /58 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xc0,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /59 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe0,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /60 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xf0,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /61 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xf8,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /62 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfc,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /63 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfe,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /64 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /65 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0x80,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /66 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xc0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /67 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xe0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /68 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xf0,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /69 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xf8,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /70 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xfc,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /71 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xfe,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /72 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /73 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0x80,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /74 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xc0,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /75 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xe0,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /76 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xf0,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /77 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xf8,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /78 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xfc,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /79 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xfe,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /80 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0x00,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /81 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0x80,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /82 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xc0,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /83 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe0,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /84 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xf0,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /85 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xf8,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /86 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xfc,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /87 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xfe,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /88 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x00,
+ 0x00,0x00,0x00,0x00 }},
+/* /89 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x80,
+ 0x00,0x00,0x00,0x00 }},
+/* /90 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xc0,
+ 0x00,0x00,0x00,0x00 }},
+/* /91 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe0,
+ 0x00,0x00,0x00,0x00 }},
+/* /92 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xf0,
+ 0x00,0x00,0x00,0x00 }},
+/* /93 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xf8,
+ 0x00,0x00,0x00,0x00 }},
+/* /94 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfc,
+ 0x00,0x00,0x00,0x00 }},
+/* /95 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfe,
+ 0x00,0x00,0x00,0x00 }},
+/* /96 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0x00,0x00,0x00,0x00 }},
+/* /97 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0x80,0x00,0x00,0x00 }},
+/* /98 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xc0,0x00,0x00,0x00 }},
+/* /99 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xe0,0x00,0x00,0x00 }},
+/* /100 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xf0,0x00,0x00,0x00 }},
+/* /101 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xf8,0x00,0x00,0x00 }},
+/* /102 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xfc,0x00,0x00,0x00 }},
+/* /103 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xfe,0x00,0x00,0x00 }},
+/* /104 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0x00,0x00,0x00 }},
+/* /105 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0x80,0x00,0x00 }},
+/* /106 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xc0,0x00,0x00 }},
+/* /107 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xe0,0x00,0x00 }},
+/* /108 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xf0,0x00,0x00 }},
+/* /109 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xf8,0x00,0x00 }},
+/* /110 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xfc,0x00,0x00 }},
+/* /111 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xfe,0x00,0x00 }},
+/* /112 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0x00,0x00 }},
+/* /113 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0x80,0x00 }},
+/* /114 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xc0,0x00 }},
+/* /115 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xe0,0x00 }},
+/* /116 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xf0,0x00 }},
+/* /117 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xf8,0x00 }},
+/* /118 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xfc,0x00 }},
+/* /119 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xfe,0x00 }},
+/* /120 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x00 }},
+/* /121 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0x80 }},
+/* /122 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xc0 }},
+/* /123 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xe0 }},
+/* /124 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xf0 }},
+/* /125 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xf8 }},
+/* /126 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfc }},
+/* /127 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xfe }},
+/* /128 */ {{ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff,
+ 0xff,0xff,0xff,0xff }},
+}};
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <iostream>
+#include <map>
+#include <stdexcept>
+
+#include <boost/operators.hpp>
+
+#include "folly/Hash.h"
+#include "folly/Range.h"
+#include "folly/detail/IPAddress.h"
+
+namespace folly {
+
+class IPAddress;
+class IPAddressV4;
+class IPAddressV6;
+class MacAddress;
+
+/**
+ * Pair of IPAddressV6, netmask
+ */
+typedef std::pair<IPAddressV6, uint8_t> CIDRNetworkV6;
+
+/**
+ * Specialization for IPv6 addresses
+ */
+typedef std::array<uint8_t, 16> ByteArray16;
+
+/**
+ * IPv6 variation of IPAddress.
+ *
+ * Added methods: createIPv4, getIPv4For6To4, is6To4,
+ * isTeredo, isIPv4Mapped, tryCreateIPv4, type
+ *
+ * @see IPAddress
+ */
+class IPAddressV6 : boost::totally_ordered<IPAddressV6> {
+ public:
+ // V6 Address Type
+ enum Type {
+ TEREDO, T6TO4, NORMAL,
+ };
+ // A constructor parameter to indicate that we should create a link-local
+ // IPAddressV6.
+ enum LinkLocalTag {
+ LINK_LOCAL,
+ };
+ // Thrown when a type assertion fails
+ typedef std::runtime_error TypeError;
+
+ // Binary prefix for teredo networks
+ static const uint32_t PREFIX_TEREDO;
+ // Binary prefix for 6to4 networks
+ static const uint32_t PREFIX_6TO4;
+
+ /**
+ * Create a new IPAddress instance from the provided binary data.
+ * @throws IPAddressFormatException if the input length is not 16 bytes.
+ */
+ static IPAddressV6 fromBinary(ByteRange bytes) {
+ IPAddressV6 addr;
+ addr.setFromBinary(bytes);
+ return addr;
+ }
+
+ /**
+ * Default constructor for IPAddressV6.
+ *
+ * The address value will be ::0
+ */
+ IPAddressV6();
+
+ // Create an IPAddressV6 from a string
+ // @throws IPAddressFormatException
+ explicit IPAddressV6(StringPiece ip);
+
+ // ByteArray16 constructor
+ explicit IPAddressV6(const ByteArray16& src);
+
+ // in6_addr constructor
+ explicit IPAddressV6(const in6_addr& src);
+
+ /**
+ * Create a link-local IPAddressV6 from the specified ethernet MAC address.
+ */
+ IPAddressV6(LinkLocalTag tag, MacAddress mac);
+
+ // return the mapped V4 address
+ // @throws IPAddressFormatException if !isIPv4Mapped
+ IPAddressV4 createIPv4() const;
+
+ /**
+ * Return a V4 address if this is a 6To4 address.
+ * @throws TypeError if not a 6To4 address
+ */
+ IPAddressV4 getIPv4For6To4() const;
+
+ // Return true if a 6TO4 address
+ bool is6To4() const {
+ return type() == IPAddressV6::Type::T6TO4;
+ }
+
+ // Return true if a TEREDO address
+ bool isTeredo() const {
+ return type() == IPAddressV6::Type::TEREDO;
+ }
+
+ // return true if this is v4-to-v6-mapped
+ bool isIPv4Mapped() const;
+
+ // Return the V6 address type
+ Type type() const;
+
+ /**
+ * @see IPAddress#bitCount
+ * @returns 128
+ */
+ static size_t bitCount() { return 128; }
+
+ /**
+ * @see IPAddress#toJson
+ */
+ std::string toJson() const;
+
+ size_t hash() const;
+
+ // @see IPAddress#inSubnet
+ // @throws IPAddressFormatException if string doesn't contain a V6 address
+ bool inSubnet(StringPiece cidrNetwork) const;
+
+ // return true if address is in subnet
+ bool inSubnet(const IPAddressV6& subnet, uint8_t cidr) const {
+ return inSubnetWithMask(subnet, fetchMask(cidr));
+ }
+ bool inSubnetWithMask(const IPAddressV6& subnet,
+ const ByteArray16& mask) const;
+
+ // @see IPAddress#isLoopback
+ bool isLoopback() const;
+
+ // @see IPAddress#isNonroutable
+ bool isNonroutable() const {
+ return !isRoutable();
+ }
+
+ /**
+ * Return true if this address is routable.
+ */
+ bool isRoutable() const;
+
+ // @see IPAddress#isPrivate
+ bool isPrivate() const;
+
+ /**
+ * Return true if this is a link-local IPv6 address.
+ *
+ * Note that this only returns true for addresses in the fe80::/10 range.
+ * It returns false for the loopback address (::1), even though this address
+ * is also effectively has link-local scope. It also returns false for
+ * link-scope and interface-scope multicast addresses.
+ */
+ bool isLinkLocal() const;
+
+ /**
+ * Return true if this is a multicast address.
+ */
+ bool isMulticast() const;
+
+ /**
+ * Return the flags for a multicast address.
+ * This method may only be called on multicast addresses.
+ */
+ uint8_t getMulticastFlags() const;
+
+ /**
+ * Return the scope for a multicast address.
+ * This method may only be called on multicast addresses.
+ */
+ uint8_t getMulticastScope() const;
+
+ // @see IPAddress#isZero
+ bool isZero() const {
+ return detail::Bytes::isZero(bytes(), 16);
+ }
+
+ bool isLinkLocalBroadcast() const;
+
+ // @see IPAddress#mask
+ IPAddressV6 mask(size_t numBits) const;
+
+ // return underlying in6_addr structure
+ in6_addr toAddr() const { return addr_.in6Addr_; }
+
+ sockaddr_in6 toSockAddr() const {
+ sockaddr_in6 addr;
+ memset(&addr, 0, sizeof(sockaddr_in6));
+ addr.sin6_family = AF_INET6;
+ memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr));
+ return addr;
+ }
+
+ ByteArray16 toByteArray() const {
+ ByteArray16 ba{{0}};
+ std::memcpy(ba.data(), bytes(), 16);
+ return ba;
+ }
+
+ // @see IPAddress#toFullyQualified
+ std::string toFullyQualified() const;
+
+ // @see IPAddress#str
+ std::string str() const;
+
+ // @see IPAddress#version
+ size_t version() const { return 6; }
+
+ /**
+ * Return the solicited-node multicast address for this address.
+ */
+ IPAddressV6 getSolicitedNodeAddress() const;
+
+ /**
+ * Return the mask associated with the given number of bits.
+ * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should
+ * be {0xff, 0xff, 0xff, 0x00}.
+ * @param [in] numBits bitmask to retrieve
+ * @throws abort if numBits == 0 or numBits > bitCount()
+ * @return mask associated with numBits
+ */
+ static const ByteArray16 fetchMask(size_t numBits);
+ // Given 2 IPAddressV6,mask pairs extract the longest common IPAddress,
+ // mask pair
+ static CIDRNetworkV6 longestCommonPrefix(const CIDRNetworkV6& one,
+ const CIDRNetworkV6& two) {
+ auto prefix = detail::Bytes::longestCommonPrefix(
+ one.first.addr_.bytes_, one.second,
+ two.first.addr_.bytes_, two.second);
+ return {IPAddressV6(prefix.first), prefix.second};
+ }
+ // Number of bytes in the address representation.
+ static constexpr size_t byteCount() { return 16; }
+
+ //get nth most significant bit - 0 indexed
+ bool getNthMSBit(size_t bitIndex) const {
+ return detail::getNthMSBitImpl(*this, bitIndex, AF_INET6);
+ }
+ //get nth most significant byte - 0 indexed
+ uint8_t getNthMSByte(size_t byteIndex) const;
+ //get nth bit - 0 indexed
+ bool getNthLSBit(size_t bitIndex) const {
+ return getNthMSBit(bitCount() - bitIndex - 1);
+ }
+ //get nth byte - 0 indexed
+ uint8_t getNthLSByte(size_t byteIndex) const {
+ return getNthMSByte(byteCount() - byteIndex - 1);
+ }
+
+ const unsigned char* bytes() const { return addr_.in6Addr_.s6_addr; }
+ protected:
+ /**
+ * Helper that returns true if the address is in the binary subnet specified
+ * by addr.
+ */
+ bool inBinarySubnet(const std::array<uint8_t, 2> addr,
+ size_t numBits) const;
+
+ private:
+ union AddressStorage {
+ in6_addr in6Addr_;
+ ByteArray16 bytes_;
+ AddressStorage() {
+ std::memset(this, 0, sizeof(AddressStorage));
+ }
+ explicit AddressStorage(const ByteArray16& bytes): bytes_(bytes) {}
+ explicit AddressStorage(const in6_addr& addr): in6Addr_(addr) {}
+ explicit AddressStorage(MacAddress mac);
+ } addr_;
+
+ static const std::array<ByteArray16, 129> masks_;
+
+ /**
+ * Set the current IPAddressV6 object to have the address specified by bytes.
+ * @throws IPAddressFormatException if bytes.size() is not 16.
+ */
+ void setFromBinary(ByteRange bytes);
+};
+
+// boost::hash uses hash_value() so this allows boost::hash to work
+// automatically for IPAddressV6
+std::size_t hash_value(const IPAddressV6& addr);
+std::ostream& operator<<(std::ostream& os, const IPAddressV6& addr);
+// Define toAppend() to allow IPAddressV6 to be used with to<string>
+void toAppend(IPAddressV6 addr, std::string* result);
+void toAppend(IPAddressV6 addr, fbstring* result);
+
+/**
+ * Return true if two addresses are equal.
+ */
+inline bool operator==(const IPAddressV6& addr1, const IPAddressV6& addr2) {
+ return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) == 0);
+}
+// Return true if addr1 < addr2
+inline bool operator<(const IPAddressV6& addr1, const IPAddressV6& addr2) {
+ return (std::memcmp(addr1.toAddr().s6_addr, addr2.toAddr().s6_addr, 16) < 0);
+}
+
+} // folly
+
+namespace std {
+template<>
+struct hash<folly::IPAddressV6> {
+ size_t operator()(const folly::IPAddressV6& addr) const {
+ return addr.hash();
+ }
+};
+} // std
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MacAddress.h"
+
+#include "folly/Exception.h"
+#include "folly/IPAddressV6.h"
+
+using std::invalid_argument;
+using std::string;
+
+namespace folly {
+
+const MacAddress MacAddress::BROADCAST{Endian::big(0xffffffffffffU)};
+const MacAddress MacAddress::ZERO;
+
+MacAddress::MacAddress(StringPiece str) {
+ memset(&bytes_, 0, 8);
+ parse(str);
+}
+
+MacAddress MacAddress::createMulticast(IPAddressV6 v6addr) {
+ // This method should only be used for multicast addresses.
+ DCHECK(v6addr.isMulticast());
+
+ uint8_t bytes[SIZE];
+ bytes[0] = 0x33;
+ bytes[1] = 0x33;
+ memcpy(bytes + 2, v6addr.bytes() + 12, 4);
+ return fromBinary(ByteRange(bytes, SIZE));
+}
+
+string MacAddress::toString() const {
+ static const char hexValues[] = "0123456789abcdef";
+ string result;
+ result.resize(17);
+ result[0] = hexValues[getByte(0) >> 4];
+ result[1] = hexValues[getByte(0) & 0xf];
+ result[2] = ':';
+ result[3] = hexValues[getByte(1) >> 4];
+ result[4] = hexValues[getByte(1) & 0xf];
+ result[5] = ':';
+ result[6] = hexValues[getByte(2) >> 4];
+ result[7] = hexValues[getByte(2) & 0xf];
+ result[8] = ':';
+ result[9] = hexValues[getByte(3) >> 4];
+ result[10] = hexValues[getByte(3) & 0xf];
+ result[11] = ':';
+ result[12] = hexValues[getByte(4) >> 4];
+ result[13] = hexValues[getByte(4) & 0xf];
+ result[14] = ':';
+ result[15] = hexValues[getByte(5) >> 4];
+ result[16] = hexValues[getByte(5) & 0xf];
+ return result;
+}
+
+void MacAddress::parse(StringPiece str) {
+ // Helper function to convert a single hex char into an integer
+ auto unhex = [](char c) -> int {
+ return c >= '0' && c <= '9' ? c - '0' :
+ c >= 'A' && c <= 'F' ? c - 'A' + 10 :
+ c >= 'a' && c <= 'f' ? c - 'a' + 10 :
+ -1;
+ };
+ auto isSeparatorChar = [](char c) {
+ return c == ':' || c == '-';
+ };
+
+ uint8_t parsed[SIZE];
+ auto p = str.begin();
+ for (unsigned int byteIndex = 0; byteIndex < SIZE; ++byteIndex) {
+ if (p == str.end()) {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": not enough digits"));
+ }
+
+ // Skip over ':' or '-' separators between bytes
+ if (byteIndex != 0 && isSeparatorChar(*p)) {
+ ++p;
+ if (p == str.end()) {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": not enough digits"));
+ }
+ }
+
+ // Parse the upper nibble
+ int upper = unhex(*p);
+ if (upper < 0) {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": contains non-hex digit"));
+ }
+ ++p;
+
+ // Parse the lower nibble
+ int lower;
+ if (p == str.end()) {
+ lower = upper;
+ upper = 0;
+ } else {
+ lower = unhex(*p);
+ if (lower < 0) {
+ // Also accept ':', '-', or '\0', to handle the case where one
+ // of the bytes was represented by just a single digit.
+ if (isSeparatorChar(*p)) {
+ lower = upper;
+ upper = 0;
+ } else {
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": contains non-hex digit"));
+ }
+ }
+ ++p;
+ }
+
+ // Update parsed with the newly parsed byte
+ parsed[byteIndex] = ((upper << 4) | lower);
+ }
+
+ if (p != str.end()) {
+ // String is too long to be a MAC address
+ throw invalid_argument(to<string>("invalid MAC address \"", str,
+ "\": found trailing characters"));
+ }
+
+ // Only update now that we have successfully parsed the entire
+ // string. This way we remain unchanged on error.
+ setFromBinary(ByteRange(parsed, SIZE));
+}
+
+void MacAddress::setFromBinary(ByteRange value) {
+ if (value.size() != SIZE) {
+ throw invalid_argument(to<string>("MAC address must be 6 bytes "
+ "long, got ", value.size()));
+ }
+ memcpy(bytes_ + 2, value.begin(), SIZE);
+}
+
+std::ostream& operator<<(std::ostream& os, MacAddress address) {
+ os << address.toString();
+ return os;
+}
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <iostream>
+
+#include <boost/operators.hpp>
+
+#include "folly/Bits.h"
+#include "folly/Conv.h"
+
+namespace folly {
+
+class IPAddressV6;
+
+/*
+ * MacAddress represents an IEEE 802 MAC address.
+ */
+class MacAddress : private boost::totally_ordered<MacAddress> {
+ public:
+ static constexpr size_t SIZE = 6;
+ static const MacAddress BROADCAST;
+ static const MacAddress ZERO;
+
+ /*
+ * Construct a zero-initialized MacAddress.
+ */
+ MacAddress() {
+ memset(&bytes_, 0, 8);
+ }
+
+ /*
+ * Parse a MacAddress from a human-readable string.
+ * The string must contain 6 one- or two-digit hexadecimal
+ * numbers, separated by dashes or colons.
+ * Examples: 00:02:C9:C8:F9:68 or 0-2-c9-c8-f9-68
+ */
+ explicit MacAddress(StringPiece str);
+
+ /*
+ * Construct a MAC address from its 6-byte binary value
+ */
+ static MacAddress fromBinary(ByteRange value) {
+ MacAddress ret;
+ ret.setFromBinary(value);
+ return ret;
+ }
+
+ /*
+ * Construct a MacAddress from a uint64_t in network byte order.
+ *
+ * The first two bytes are ignored, and the MAC address is taken from the
+ * latter 6 bytes.
+ *
+ * This is a static method rather than a constructor to avoid confusion
+ * between host and network byte order constructors.
+ */
+ static MacAddress fromNBO(uint64_t value) {
+ return MacAddress(value);
+ }
+
+ /*
+ * Construct a MacAddress from a uint64_t in host byte order.
+ *
+ * The most significant two bytes are ignored, and the MAC address is taken
+ * from the least significant 6 bytes.
+ *
+ * This is a static method rather than a constructor to avoid confusion
+ * between host and network byte order constructors.
+ */
+ static MacAddress fromHBO(uint64_t value) {
+ return MacAddress(Endian::big(value));
+ }
+
+ /*
+ * Construct the multicast MacAddress for the specified multicast IPv6
+ * address.
+ */
+ static MacAddress createMulticast(IPAddressV6 addr);
+
+ /*
+ * Get a pointer to the MAC address' binary value.
+ *
+ * The returned value points to internal storage inside the MacAddress
+ * object. It is only valid as long as the MacAddress, and its contents may
+ * change if the MacAddress is updated.
+ */
+ const uint8_t* bytes() const {
+ return bytes_ + 2;
+ }
+
+ /*
+ * Return the address as a uint64_t, in network byte order.
+ *
+ * The first two bytes will be 0, and the subsequent 6 bytes will contain
+ * the address in network byte order.
+ */
+ uint64_t u64NBO() const {
+ return packedBytes();
+ }
+
+ /*
+ * Return the address as a uint64_t, in host byte order.
+ *
+ * The two most significant bytes will be 0, and the remaining 6 bytes will
+ * contain the address. The most significant of these 6 bytes will contain
+ * the first byte that appear on the wire, and the least significant byte
+ * will contain the last byte.
+ */
+ uint64_t u64HBO() const {
+ // Endian::big() does what we want here, even though we are converting
+ // from big-endian to host byte order. This swaps if and only if
+ // the host byte order is little endian.
+ return Endian::big(packedBytes());
+ }
+
+ /*
+ * Return a human-readable representation of the MAC address.
+ */
+ std::string toString() const;
+
+ /*
+ * Update the current MacAddress object from a human-readable string.
+ */
+ void parse(StringPiece str);
+
+ /*
+ * Update the current MacAddress object from a 6-byte binary representation.
+ */
+ void setFromBinary(ByteRange value);
+
+ bool isBroadcast() const {
+ return *this == BROADCAST;
+ }
+ bool isMulticast() const {
+ return getByte(0) & 0x1;
+ }
+ bool isUnicast() const {
+ return !isMulticast();
+ }
+
+ /*
+ * Return true if this MAC address is locally administered.
+ *
+ * Locally administered addresses are assigned by the local network
+ * administrator, and are not guaranteed to be globally unique. (It is
+ * similar to IPv4's private address space.)
+ *
+ * Note that isLocallyAdministered() will return true for the broadcast
+ * address, since it has the locally administered bit set.
+ */
+ bool isLocallyAdministered() const {
+ return getByte(0) & 0x2;
+ }
+
+ // Equality and less-than operators.
+ // boost::totally_ordered provides the other comparison operators.
+
+ bool operator==(const MacAddress& other) const {
+ // All constructors and modifying methods make sure padding is 0,
+ // so we don't need to mask these bytes out when comparing here.
+ return packedBytes() == other.packedBytes();
+ }
+
+ bool operator<(const MacAddress& other) const {
+ return u64HBO() < other.u64HBO();
+ }
+
+ private:
+ explicit MacAddress(uint64_t valueNBO) {
+ memcpy(&bytes_, &valueNBO, 8);
+ // Set the pad bytes to 0.
+ // This allows us to easily compare two MacAddresses,
+ // without having to worry about differences in the padding.
+ bytes_[0] = 0;
+ bytes_[1] = 0;
+ }
+
+ /* We store the 6 bytes starting at bytes_[2] (most significant)
+ through bytes_[7] (least).
+ bytes_[0] and bytes_[1] are always equal to 0 to simplify comparisons.
+ */
+ unsigned char bytes_[8];
+
+ inline uint64_t getByte(size_t index) const {
+ return bytes_[index + 2];
+ }
+
+ uint64_t packedBytes() const {
+ uint64_t u64;
+ memcpy(&u64, bytes_, 8);
+ return u64;
+ }
+};
+
+/* Define toAppend() so to<string> will work */
+template <class Tgt>
+typename std::enable_if<IsSomeString<Tgt>::value>::type
+toAppend(MacAddress address, Tgt* result) {
+ toAppend(address.toString(), result);
+}
+
+std::ostream& operator<<(std::ostream& os, MacAddress address);
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <glog/logging.h>
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <type_traits>
+#include <vector>
+
+extern "C" {
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+}
+
+#include "folly/Conv.h"
+#include "folly/Format.h"
+
+namespace folly { namespace detail {
+
+inline std::string familyNameStr(sa_family_t family) {
+ switch (family) {
+ case AF_INET:
+ return "AF_INET";
+ case AF_INET6:
+ return "AF_INET6";
+ case AF_UNSPEC:
+ return "AF_UNSPEC";
+ case AF_UNIX:
+ return "AF_UNIX";
+ default:
+ return folly::format("sa_family_t({})",
+ folly::to<std::string>(family)).str();
+ }
+}
+
+template<typename IPAddrType>
+inline bool getNthMSBitImpl(const IPAddrType& ip, uint8_t bitIndex,
+ sa_family_t family) {
+ if (bitIndex >= ip.bitCount()) {
+ throw std::invalid_argument(folly::to<std::string>("Bit index must be < ",
+ ip.bitCount(), " for addresses of type :", familyNameStr(family)));
+ }
+ //Underlying bytes are in n/w byte order
+ return (ip.getNthMSByte(bitIndex / 8) & (0x80 >> (bitIndex % 8))) != 0;
+}
+
+/**
+ * Helper for working with unsigned char* or uint8_t* ByteArray values
+ */
+struct Bytes : private boost::noncopyable {
+ // return true if all values of src are zero
+ static bool isZero(const uint8_t* src, std::size_t len) {
+ for (auto i = 0; i < len; i++) {
+ if (src[i] != 0x00) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // mask the values from two byte arrays, returning a new byte array
+ template<std::size_t N>
+ static std::array<uint8_t, N> mask(const std::array<uint8_t, N>& a,
+ const std::array<uint8_t, N>& b) {
+ static_assert(N > 0, "Can't mask an empty ByteArray");
+ std::size_t asize = a.size();
+ std::array<uint8_t, N> ba{{0}};
+ for (int i = 0; i < asize; i++) {
+ ba[i] = a[i] & b[i];
+ }
+ return ba;
+ }
+
+ template<std::size_t N>
+ static std::pair<std::array<uint8_t, N>, uint8_t>
+ longestCommonPrefix(
+ const std::array<uint8_t, N>& one, uint8_t oneMask,
+ const std::array<uint8_t, N>& two, uint8_t twoMask) {
+ static constexpr auto kBitCount = N * 8;
+ static constexpr std::array<uint8_t, 8> kMasks {{
+ 0x80, // /1
+ 0xc0, // /2
+ 0xe0, // /3
+ 0xf0, // /4
+ 0xf8, // /5
+ 0xfc, // /6
+ 0xfe, // /7
+ 0xff // /8
+ }};
+ if (oneMask > kBitCount || twoMask > kBitCount) {
+ throw std::invalid_argument(folly::to<std::string>("Invalid mask "
+ "length: ", oneMask > twoMask ? oneMask : twoMask,
+ ". Mask length must be <= ", kBitCount));
+ }
+
+ auto mask = std::min(oneMask, twoMask);
+ uint8_t byteIndex = 0;
+ std::array<uint8_t, N> ba{{0}};
+ // Compare a byte at a time. Note - I measured compared this with
+ // going multiple bytes at a time (8, 4, 2 and 1). It turns out
+ // to be 20 - 25% slower for 4 and 16 byte arrays.
+ while (byteIndex * 8 <= mask && one[byteIndex] == two[byteIndex]) {
+ ba[byteIndex] = one[byteIndex];
+ ++byteIndex;
+ }
+ auto bitIndex = std::min(mask, (uint8_t)(byteIndex * 8));
+ // Compute the bit up to which the two byte arrays match in the
+ // unmatched byte.
+ // Here the check is bitIndex < mask since the 0th mask entry in
+ // kMasks array holds the mask for masking the MSb in this byte.
+ // We could instead make it hold so that no 0th entry masks no
+ // bits but thats a useless iteration.
+ while (bitIndex < mask && ((one[bitIndex / 8] & kMasks[bitIndex % 8]) ==
+ (two[bitIndex / 8] & kMasks[bitIndex % 8]))) {
+ ba[bitIndex / 8] = one[bitIndex / 8] & kMasks[bitIndex % 8];
+ ++bitIndex;
+ }
+ return {ba, bitIndex};
+ }
+
+ // create an in_addr from an uint8_t*
+ static inline in_addr mkAddress4(const uint8_t* src) {
+ union {
+ in_addr addr;
+ uint8_t bytes[4];
+ } addr;
+ std::memset(&addr, 0, 4);
+ std::memcpy(addr.bytes, src, 4);
+ return addr.addr;
+ }
+
+ // create an in6_addr from an uint8_t*
+ static inline in6_addr mkAddress6(const uint8_t* src) {
+ in6_addr addr;
+ std::memset(&addr, 0, 16);
+ std::memcpy(addr.s6_addr, src, 16);
+ return addr;
+ }
+
+ // convert an uint8_t* to its hex value
+ static std::string toHex(const uint8_t* src, std::size_t len) {
+ static const char* const lut = "0123456789abcdef";
+ std::stringstream ss;
+ for (int i = 0; i < len; i++) {
+ const unsigned char c = src[i];
+ ss << lut[c >> 4] << lut[c & 15];
+ }
+ return ss.str();
+ }
+
+ private:
+ Bytes() = delete;
+ ~Bytes() = delete;
+};
+
+}} // folly::detail
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "IPAddressTest.h"
+
+#include <gtest/gtest.h>
+
+#include "folly/Bits.h"
+#include "folly/Format.h"
+#include "folly/String.h"
+#include "folly/MacAddress.h"
+
+using namespace folly;
+using namespace std;
+
+// tests code example
+TEST(IPAddress, CodeExample) {
+ EXPECT_EQ(4, sizeof(IPAddressV4));
+ EXPECT_EQ(16, sizeof(IPAddressV6));
+ EXPECT_EQ(20, sizeof(IPAddress));
+ IPAddress v4addr("192.0.2.129");
+ IPAddress v6map("::ffff:192.0.2.129");
+ EXPECT_TRUE(v4addr.inSubnet("192.0.2.0/24"));
+ EXPECT_TRUE(v4addr.inSubnet(IPAddress("192.0.2.0"), 24));
+ EXPECT_TRUE(v4addr.inSubnet("192.0.2.128/30"));
+ EXPECT_FALSE(v4addr.inSubnet("192.0.2.128/32"));
+ EXPECT_EQ(2164392128, v4addr.asV4().toLong());
+ EXPECT_EQ(3221226113, v4addr.asV4().toLongHBO());
+ ASSERT_TRUE(v4addr.isV4());
+ ASSERT_TRUE(v6map.isV6());
+ EXPECT_TRUE(v4addr == v6map);
+ ASSERT_TRUE(v6map.isIPv4Mapped());
+ EXPECT_TRUE(v4addr.asV4() == IPAddress::createIPv4(v6map));
+ EXPECT_TRUE(IPAddress::createIPv6(v4addr) == v6map.asV6());
+}
+
+TEST(IPAddress, Ordering) {
+ IPAddress a1("0.1.1.1");
+ IPAddress a2("1.1.1.0");
+ EXPECT_TRUE(a1 < a2);
+
+ IPAddress b1("::ffff:0.1.1.1");
+ IPAddress b2("::ffff:1.1.1.0");
+ EXPECT_TRUE(b1 < b2);
+}
+
+TEST(IPAddress, InvalidAddressFamilyExceptions) {
+ // asV4
+ {
+ IPAddress addr;
+ EXPECT_THROW(addr.asV4(), InvalidAddressFamilyException);
+ }
+ // asV6
+ {
+ IPAddress addr;
+ EXPECT_THROW(addr.asV6(), InvalidAddressFamilyException);
+ }
+ // sockaddr ctor
+ {
+ // setup
+ sockaddr_in addr;
+ addr.sin_family = AF_UNSPEC;
+
+ EXPECT_THROW(IPAddress((sockaddr *)&addr), InvalidAddressFamilyException);
+ }
+}
+
+TEST(IPAddress, CreateNetwork) {
+ // test valid IPv4 network
+ {
+ auto net = IPAddress::createNetwork("192.168.0.1/24");
+ ASSERT_TRUE(net.first.isV4());
+ EXPECT_EQ("192.168.0.0", net.first.str());
+ EXPECT_EQ(24, net.second);
+ EXPECT_EQ("192.168.0.0/24", IPAddress::networkToString(net));
+ }
+ // test valid IPv4 network without applying mask
+ {
+ auto net = IPAddress::createNetwork("192.168.0.1/24", -1, false);
+ ASSERT_TRUE(net.first.isV4());
+ EXPECT_EQ("192.168.0.1", net.first.str());
+ EXPECT_EQ(24, net.second);
+ EXPECT_EQ("192.168.0.1/24", IPAddress::networkToString(net));
+ }
+ // test valid IPv6 network
+ {
+ auto net = IPAddress::createNetwork("1999::1/24");
+ ASSERT_TRUE(net.first.isV6());
+ EXPECT_EQ("1999::", net.first.str());
+ EXPECT_EQ(24, net.second);
+ EXPECT_EQ("1999::/24", IPAddress::networkToString(net));
+ }
+ // test valid IPv6 network without applying mask
+ {
+ auto net = IPAddress::createNetwork("1999::1/24", -1, false);
+ ASSERT_TRUE(net.first.isV6());
+ EXPECT_EQ("1999::1", net.first.str());
+ EXPECT_EQ(24, net.second);
+ EXPECT_EQ("1999::1/24", IPAddress::networkToString(net));
+ }
+ // test empty string
+ EXPECT_THROW(IPAddress::createNetwork(""), IPAddressFormatException);
+ // test multi slash string
+ EXPECT_THROW(IPAddress::createNetwork("192.168.0.1/24/36"),
+ IPAddressFormatException);
+ // test no slash string with default IPv4
+ {
+ auto net = IPAddress::createNetwork("192.168.0.1");
+ ASSERT_TRUE(net.first.isV4());
+ EXPECT_EQ("192.168.0.1", net.first.str());
+ EXPECT_EQ(32, net.second); // auto-detected
+ net = IPAddress::createNetwork("192.168.0.1", -1, false);
+ ASSERT_TRUE(net.first.isV4());
+ EXPECT_EQ("192.168.0.1", net.first.str());
+ EXPECT_EQ(32, net.second);
+ }
+ // test no slash string with default IPv6
+ {
+ auto net = IPAddress::createNetwork("1999::1");
+ ASSERT_TRUE(net.first.isV6());
+ EXPECT_EQ("1999::1", net.first.str());
+ EXPECT_EQ(128, net.second);
+ }
+ // test no slash string with invalid default
+ EXPECT_THROW(IPAddress::createNetwork("192.168.0.1", 33),
+ IPAddressFormatException);
+
+}
+
+// test assignment operators
+TEST(IPAddress, Assignment) {
+ static const string kIPv4Addr = "69.63.189.16";
+ static const string kIPv6Addr = "2620:0:1cfe:face:b00c::3";
+
+ // Test assigning IPAddressV6 addr to IPAddress (was V4)
+ {
+ IPAddress addr(kIPv4Addr);
+ IPAddressV6 addrV6 = IPAddress(kIPv6Addr).asV6();
+ EXPECT_TRUE(addr.isV4());
+ EXPECT_EQ(kIPv4Addr, addr.str());
+ addr = addrV6;
+ EXPECT_TRUE(addr.isV6());
+ EXPECT_EQ(kIPv6Addr, addr.str());
+ }
+ // Test assigning IPAddressV4 addr to IPAddress (was V6)
+ {
+ IPAddress addr(kIPv6Addr);
+ IPAddressV4 addrV4 = IPAddress(kIPv4Addr).asV4();
+ EXPECT_TRUE(addr.isV6());
+ EXPECT_EQ(kIPv6Addr, addr.str());
+ addr = addrV4;
+ EXPECT_TRUE(addr.isV4());
+ EXPECT_EQ(kIPv4Addr, addr.str());
+ }
+ // Test assigning IPAddress(v6) to IPAddress (was v4)
+ {
+ IPAddress addr(kIPv4Addr);
+ IPAddress addrV6 = IPAddress(kIPv6Addr);
+ EXPECT_TRUE(addr.isV4());
+ EXPECT_EQ(kIPv4Addr, addr.str());
+ addr = addrV6;
+ EXPECT_TRUE(addr.isV6());
+ EXPECT_EQ(kIPv6Addr, addr.str());
+ }
+ // Test assigning IPAddress(v4) to IPAddress (was v6)
+ {
+ IPAddress addr(kIPv6Addr);
+ IPAddress addrV4 = IPAddress(kIPv4Addr);
+ EXPECT_TRUE(addr.isV6());
+ EXPECT_EQ(kIPv6Addr, addr.str());
+ addr = addrV4;
+ EXPECT_TRUE(addr.isV4());
+ EXPECT_EQ(kIPv4Addr, addr.str());
+ }
+}
+
+// Test the default constructors
+TEST(IPAddress, CtorDefault) {
+ IPAddressV4 v4;
+ EXPECT_EQ(IPAddressV4("0.0.0.0"), v4);
+ IPAddressV6 v6;
+ EXPECT_EQ(IPAddressV6("::0"), v6);
+}
+
+// Test addresses constructed using a in[6]_addr value
+TEST_P(IPAddressTest, CtorAddress) {
+ AddressData param = GetParam();
+ IPAddress strAddr(param.address);
+ IPAddress address;
+
+ if (param.version == 4) {
+ in_addr v4addr = detail::Bytes::mkAddress4(¶m.bytes[0]);
+ address = IPAddress(v4addr);
+ } else {
+ in6_addr v6addr = detail::Bytes::mkAddress6(¶m.bytes[0]);
+ address = IPAddress(v6addr);
+ }
+ ExpectIsValid(address);
+ EXPECT_EQ(strAddr, address);
+}
+
+// Test addresses constructed using a binary address
+TEST_P(IPAddressTest, CtorBinary) {
+ AddressData param = GetParam();
+ IPAddress address;
+
+ if (param.version == 4) {
+ in_addr v4addr = AddressData::parseAddress4(param.address);
+ address = IPAddress::fromBinary(ByteRange((unsigned char*)&v4addr, 4));
+ } else {
+ in6_addr v6addr = AddressData::parseAddress6(param.address);
+ address = IPAddress::fromBinary(ByteRange((unsigned char*)&v6addr, 16));
+ }
+
+ ExpectIsValid(address);
+ EXPECT_EQ(IPAddress(param.address), address);
+}
+
+// Test addresses constructed using a string
+TEST_P(IPAddressTest, CtorString) {
+ AddressData param = GetParam();
+ IPAddress address(param.address);
+
+ ExpectIsValid(address);
+
+ // Test the direct version-specific constructor
+ if (param.version == 4) {
+ IPAddressV4 v4(param.address);
+ ExpectIsValid(IPAddress(v4));
+ EXPECT_THROW(IPAddressV6 v6(param.address), IPAddressFormatException);
+ } else if (param.version == 6) {
+ IPAddressV6 v6(param.address);
+ ExpectIsValid(IPAddress(v6));
+ EXPECT_THROW(IPAddressV4 v4(param.address), IPAddressFormatException);
+ }
+}
+
+TEST(IPAddress, CtorSockaddr) {
+ // test v4 address
+ {
+ // setup
+ sockaddr_in addr;
+ in_addr sin_addr;
+ sin_addr.s_addr = htonl(2122547223);
+ addr.sin_family = AF_INET;
+ addr.sin_addr = sin_addr;
+
+ IPAddress ipAddr((sockaddr *)&addr);
+ EXPECT_TRUE(ipAddr.isV4());
+ EXPECT_EQ("126.131.128.23", ipAddr.str());
+ }
+ // test v6 address
+ {
+ // setup
+ sockaddr_in6 addr;
+ in6_addr sin_addr;
+ ByteArray16 sec{{
+ // 2620:0:1cfe:face:b00c::3
+ 38,32,0,0,28,254,250,206,176,12,0,0,0,0,0,3
+ }};
+ std::memcpy(sin_addr.s6_addr, sec.data(), 16);
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr = sin_addr;
+
+ IPAddress ipAddr((sockaddr *)&addr);
+ EXPECT_TRUE(ipAddr.isV6());
+ EXPECT_EQ("2620:0:1cfe:face:b00c::3", ipAddr.str());
+ }
+ // test nullptr exception
+ {
+ sockaddr *addr = nullptr;
+ EXPECT_THROW(IPAddress((const sockaddr*)addr), IPAddressFormatException);
+ }
+ // test invalid family exception
+ {
+ // setup
+ sockaddr_in addr;
+ in_addr sin_addr;
+ sin_addr.s_addr = htonl(2122547223);
+ addr.sin_family = AF_UNSPEC;
+ addr.sin_addr = sin_addr;
+
+ EXPECT_THROW(IPAddress((sockaddr *)&addr), IPAddressFormatException);
+ }
+}
+
+TEST(IPAddress, ToSockaddrStorage) {
+ // test v4 address
+ {
+ string strAddr("126.131.128.23");
+ IPAddress addr(strAddr);
+ sockaddr_storage out;
+
+ ASSERT_TRUE(addr.isV4()); // test invariant
+ EXPECT_GT(addr.toSockaddrStorage(&out), 0);
+
+ IPAddress sockAddr((sockaddr*)&out);
+ ASSERT_TRUE(sockAddr.isV4());
+ EXPECT_EQ(strAddr, sockAddr.str());
+ }
+ // test v6 address
+ {
+ string strAddr("2620:0:1cfe:face:b00c::3");
+ IPAddress addr(strAddr);
+ sockaddr_storage out;
+
+ ASSERT_TRUE(addr.isV6()); // test invariant
+ EXPECT_GT(addr.toSockaddrStorage(&out), 0);
+
+ IPAddress sockAddr((sockaddr*)&out);
+ ASSERT_TRUE(sockAddr.isV6());
+ EXPECT_EQ(strAddr, sockAddr.str());
+ }
+ // test nullptr exception
+ {
+ sockaddr_storage *out = nullptr;
+ IPAddress addr("127.0.0.1");
+ EXPECT_THROW(addr.toSockaddrStorage(out), IPAddressFormatException);
+ }
+ // test invalid family exception
+ {
+ IPAddress addr;
+ sockaddr_storage out;
+ ASSERT_EQ(AF_UNSPEC, addr.family());
+ EXPECT_THROW(addr.toSockaddrStorage(&out), InvalidAddressFamilyException);
+ }
+}
+
+TEST(IPAddress, ToString) {
+ // Test with IPAddressV4
+ IPAddressV4 addr_10_0_0_1("10.0.0.1");
+ EXPECT_EQ("10.0.0.1", folly::to<string>(addr_10_0_0_1));
+ // Test with IPAddressV6
+ IPAddressV6 addr_1("::1");
+ EXPECT_EQ("::1", folly::to<string>(addr_1));
+ // Test with IPAddress, both V4 and V6
+ IPAddress addr_10_1_2_3("10.1.2.3");
+ EXPECT_EQ("10.1.2.3", folly::to<string>(addr_10_1_2_3));
+ IPAddress addr_1_2_3("1:2::3");
+ EXPECT_EQ("1:2::3", folly::to<string>(addr_1_2_3));
+
+ // Test a combination of all the above arguments
+ EXPECT_EQ("1:2::3 - 10.0.0.1 - ::1 - 10.1.2.3",
+ folly::to<string>(addr_1_2_3, " - ", addr_10_0_0_1,
+ " - ", addr_1, " - ", addr_10_1_2_3));
+}
+
+// Test that invalid string values are killed
+TEST_P(IPAddressCtorTest, InvalidCreation) {
+ string addr = GetParam();
+ EXPECT_THROW(IPAddress((const string)addr), IPAddressFormatException)
+ << "IPAddress(" << addr << ") "
+ << "should have thrown an IPAddressFormatException";
+}
+
+// Test that invalid binary values throw an exception
+TEST_P(IPAddressCtorBinaryTest, InvalidBinary) {
+ auto bin = GetParam();
+ EXPECT_THROW(IPAddress::fromBinary(ByteRange(&bin[0], bin.size())),
+ IPAddressFormatException);
+}
+
+// Test toFullyQualified()
+TEST(IPAddress, ToFullyQualifiedFb) {
+ IPAddress ip("2620:0:1cfe:face:b00c::3");
+ EXPECT_EQ("2620:0000:1cfe:face:b00c:0000:0000:0003", ip.toFullyQualified())
+ << ip;
+}
+TEST(IPAddress, ToFullyQualifiedLocal) {
+ IPAddress ip("::1");
+ EXPECT_EQ("0000:0000:0000:0000:0000:0000:0000:0001", ip.toFullyQualified())
+ << ip;
+}
+
+// test v4-v6 mapped addresses
+TEST_P(IPAddressMappedTest, MappedEqual) {
+ auto param = GetParam();
+ string mappedIp = param.first;
+ string otherIp = param.second;
+
+ auto mapped = IPAddress(mappedIp);
+ auto expected = IPAddress(otherIp);
+
+ EXPECT_EQ(expected, mapped);
+
+ IPAddress v6addr;
+ if (mapped.isV4()) {
+ v6addr = mapped.asV4().createIPv6();
+ } else if (expected.isV4()) {
+ v6addr = expected.asV4().createIPv6();
+ }
+ EXPECT_TRUE(v6addr.isV6());
+ EXPECT_TRUE(mapped == v6addr);
+ EXPECT_TRUE(expected == v6addr);
+}
+
+// Test subnet mask calculations
+TEST_P(IPAddressMaskTest, Masks) {
+ auto param = GetParam();
+
+ IPAddress ip(param.address);
+ IPAddress masked = ip.mask(param.mask);
+ EXPECT_EQ(param.subnet, masked.str())
+ << param.address << "/" << to_string(param.mask)
+ << " -> " << param.subnet;
+}
+
+// Test inSubnet calculations
+TEST_P(IPAddressMaskTest, InSubnet) {
+ auto param = GetParam();
+
+ IPAddress ip(param.address);
+ IPAddress subnet(param.subnet);
+ EXPECT_TRUE(ip.inSubnet(subnet, param.mask));
+}
+
+// Test boundary conditions for subnet calculations
+TEST_P(IPAddressMaskBoundaryTest, NonMaskedSubnet) {
+ auto param = GetParam();
+ IPAddress ip(param.address);
+ IPAddress subnet(param.subnet);
+ EXPECT_EQ(param.inSubnet, ip.inSubnet(subnet, param.mask));
+}
+
+// Test subnet calcs with 6to4 addresses
+TEST(IPAddress, InSubnetWith6to4) {
+ auto ip = IPAddress("2002:c000:022a::"); // 192.0.2.42
+ auto subnet = IPAddress("192.0.0.0");
+ EXPECT_TRUE(ip.inSubnet(subnet, 16));
+
+ auto ip2 = IPAddress("192.0.0.1");
+ auto subnet2 = IPAddress("2002:c000:0000::"); // 192.0.0.0
+ EXPECT_TRUE(ip2.inSubnet(subnet2, 14));
+
+ auto ip3 = IPAddress("2002:c000:022a::"); // 192.0.2.42
+ auto subnet3 = IPAddress("2002:c000:0000::"); // 192.0.0.0
+ EXPECT_TRUE(ip3.inSubnet(subnet3, 16));
+}
+
+static vector<pair<string, uint8_t> > invalidMasks = {
+ {"127.0.0.1", 33},
+ {"::1", 129},
+};
+TEST(IPAddress, InvalidMask) {
+ for (auto& tc : invalidMasks) {
+ uint8_t mask = tc.second;
+ auto ip = IPAddress(tc.first);
+ EXPECT_THROW(ip.mask(tc.second), IPAddressFormatException);
+ }
+}
+
+static vector<pair<string, IPAddressV6::Type> > v6types = {
+ {"::1", IPAddressV6::Type::NORMAL},
+ {"2620:0:1cfe:face:b00c::3", IPAddressV6::Type::NORMAL},
+ {"2001:0000:4136:e378:8000:63bf:3fff:fdd2", IPAddressV6::Type::TEREDO},
+ {"2002:c000:022a::", IPAddressV6::Type::T6TO4},
+};
+TEST(IPAddress, V6Types) {
+ auto mkName = [&](const IPAddressV6::Type t) -> string {
+ switch (t) {
+ case IPAddressV6::Type::TEREDO:
+ return "teredo";
+ case IPAddressV6::Type::T6TO4:
+ return "6to4";
+ default:
+ return "default";
+ }
+ };
+
+ for (auto& tc : v6types) {
+ auto ip = IPAddress(tc.first);
+ EXPECT_TRUE(ip.isV6());
+ IPAddressV6 ipv6 = ip.asV6();
+ EXPECT_EQ(tc.second, ipv6.type())
+ << "expected " << mkName(tc.second) << ", got " << mkName(ipv6.type());
+ switch (tc.second) {
+ case IPAddressV6::Type::TEREDO:
+ EXPECT_TRUE(ipv6.isTeredo()) << "isTeredo was false";
+ EXPECT_FALSE(ipv6.is6To4()) << "is6To4 was true";
+ break;
+ case IPAddressV6::Type::T6TO4:
+ EXPECT_TRUE(ipv6.is6To4()) << "is6To4 was false";
+ EXPECT_FALSE(ipv6.isTeredo()) << "isTeredo was true";
+ break;
+ case IPAddressV6::Type::NORMAL:
+ EXPECT_FALSE(ipv6.is6To4()) << "is6To4 was true";
+ EXPECT_FALSE(ipv6.isTeredo()) << "isTeredo was true";
+ break;
+ default:
+ throw std::range_error("Invalid expected type: " +
+ to_string(tc.second));
+ }
+ }
+}
+
+static vector<pair<string, uint32_t> > provideToLong = {
+ {"0.0.0.0", 0},
+ {"10.0.0.0", 167772160},
+ {"126.131.128.23", 2122547223},
+ {"192.168.0.0", 3232235520},
+};
+TEST(IPAddress, ToLong) {
+ for (auto& tc : provideToLong) {
+ auto ip = IPAddress(tc.first);
+ EXPECT_TRUE(ip.isV4());
+ IPAddressV4 ipv4 = ip.asV4();
+ EXPECT_EQ(tc.second, ipv4.toLongHBO());
+
+ auto ip2 = IPAddress::fromLongHBO(tc.second);
+ EXPECT_TRUE(ip2.isV4());
+ EXPECT_TRUE(tc.first.compare(ip2.str()) == 0);
+ EXPECT_EQ(tc.second, ip2.asV4().toLongHBO());
+
+ auto nla = htonl(tc.second);
+ auto ip3 = IPAddress::fromLong(nla);
+ EXPECT_TRUE(ip3.isV4());
+ EXPECT_TRUE(tc.first.compare(ip3.str()) == 0);
+ EXPECT_EQ(nla, ip3.asV4().toLong());
+ }
+}
+
+TEST(IPAddress, fromBinaryV4) {
+ for (auto& tc : provideToLong) {
+ SCOPED_TRACE(tc.first);
+ union {
+ uint8_t u8[4];
+ uint32_t u32;
+ } data;
+ data.u32 = Endian::big(tc.second);
+ ByteRange bytes(data.u8, 4);
+
+ auto fromBin = IPAddressV4::fromBinary(bytes);
+ IPAddressV4 fromStr(tc.first);
+ EXPECT_EQ(fromStr, fromBin);
+
+ IPAddressV4 addr2("0.0.0.0");
+ addr2 = IPAddressV4::fromBinary(bytes);
+ EXPECT_EQ(fromStr, addr2);
+
+ IPAddress genericAddr = IPAddress::fromBinary(bytes);
+ ASSERT_TRUE(genericAddr.isV4());
+ EXPECT_EQ(fromStr, genericAddr.asV4());
+ EXPECT_EQ(ByteRange(genericAddr.bytes(), genericAddr.byteCount()), bytes);
+ }
+
+ uint8_t data[20];
+ EXPECT_THROW(IPAddressV4::fromBinary(ByteRange(data, 3)),
+ IPAddressFormatException);
+ EXPECT_THROW(IPAddressV4::fromBinary(ByteRange(data, 16)),
+ IPAddressFormatException);
+ EXPECT_THROW(IPAddressV4::fromBinary(ByteRange(data, 20)),
+ IPAddressFormatException);
+}
+
+static vector<pair<string, vector<uint8_t> > > provideBinary16Bytes = {
+ {"::0",
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {"1::2",
+ {0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}},
+ {"fe80::0012:34ff:fe56:78ab",
+ {0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x12, 0x34, 0xff, 0xfe, 0x56, 0x78, 0xab}},
+ {"2001:db8:1234:5678:90ab:cdef:8765:4321",
+ {0x20, 0x01, 0x0d, 0xb8, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0xab, 0xcd, 0xef, 0x87, 0x65, 0x43, 0x21}},
+ {"::ffff:0:c0a8:1",
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0xc0, 0xa8, 0x00, 0x01}},
+};
+
+TEST(IPAddress, fromBinaryV6) {
+ for (auto& tc : provideBinary16Bytes) {
+ SCOPED_TRACE(tc.first);
+ ByteRange bytes(&tc.second.front(), tc.second.size());
+
+ auto fromBin = IPAddressV6::fromBinary(bytes);
+ IPAddressV6 fromStr(tc.first);
+ EXPECT_EQ(fromStr, fromBin);
+
+ IPAddressV6 addr2("::0");
+ addr2 = IPAddressV6::fromBinary(bytes);
+ EXPECT_EQ(fromStr, addr2);
+
+ IPAddress genericAddr = IPAddress::fromBinary(bytes);
+ ASSERT_TRUE(genericAddr.isV6());
+ EXPECT_EQ(fromStr, genericAddr.asV6());
+ EXPECT_EQ(ByteRange(genericAddr.bytes(), genericAddr.byteCount()), bytes);
+ }
+
+ uint8_t data[20];
+ EXPECT_THROW(IPAddressV6::fromBinary(ByteRange(data, 3)),
+ IPAddressFormatException);
+ EXPECT_THROW(IPAddressV6::fromBinary(ByteRange(data, 4)),
+ IPAddressFormatException);
+ EXPECT_THROW(IPAddressV6::fromBinary(ByteRange(data, 20)),
+ IPAddressFormatException);
+}
+
+TEST_P(IPAddressFlagTest, IsLoopback) {
+ AddressFlags param = GetParam();
+ IPAddress addr(param.address);
+
+ EXPECT_EQ(param.version, addr.version());
+ EXPECT_EQ(param.isLoopback(), addr.isLoopback());
+}
+
+TEST_P(IPAddressFlagTest, IsPrivate) {
+ AddressFlags param = GetParam();
+ IPAddress addr(param.address);
+
+ EXPECT_EQ(param.version, addr.version());
+ EXPECT_EQ(param.isPrivate(), addr.isPrivate()) << addr;
+}
+
+TEST_P(IPAddressFlagTest, IsNonroutable) {
+ AddressFlags param = GetParam();
+ IPAddress addr(param.address);
+
+ EXPECT_EQ(param.version, addr.version());
+ EXPECT_EQ(param.isNonroutable(), addr.isNonroutable()) << addr;
+}
+
+TEST_P(IPAddressFlagTest, IsZero) {
+ AddressFlags param = GetParam();
+ IPAddress addr(param.address);
+
+ EXPECT_EQ(param.version, addr.version());
+ EXPECT_EQ(param.isZero(), addr.isZero()) << addr;
+}
+
+TEST_P(IPAddressFlagTest, IsLinkLocal) {
+ AddressFlags param = GetParam();
+ if (param.version != 6) {
+ return;
+ }
+ IPAddressV6 addr(param.address);
+ EXPECT_EQ(param.isLinkLocal(), addr.isLinkLocal()) << addr;
+}
+
+TEST(IPAddress, CreateLinkLocal) {
+ IPAddressV6 addr(IPAddressV6::LINK_LOCAL, MacAddress("00:05:73:f9:46:fc"));
+ EXPECT_EQ(IPAddressV6("fe80::0205:73ff:fef9:46fc"), addr);
+
+ addr = IPAddressV6(IPAddressV6::LINK_LOCAL, MacAddress("02:00:00:12:34:56"));
+ EXPECT_EQ(IPAddressV6("fe80::ff:fe12:3456"), addr);
+}
+
+TEST_P(IPAddressFlagTest, IsLinkLocalBroadcast) {
+ AddressFlags param = GetParam();
+ IPAddress addr(param.address);
+ EXPECT_EQ(param.version, addr.version());
+ EXPECT_EQ(param.isLinkLocalBroadcast(), addr.isLinkLocalBroadcast());
+}
+
+TEST(IPAddress, SolicitedNodeAddress) {
+ // An example from RFC 4291 section 2.7.1
+ EXPECT_EQ(IPAddressV6("ff02::1:ff0e:8c6c"),
+ IPAddressV6("4037::01:800:200e:8c6c").getSolicitedNodeAddress());
+
+ // An example from wikipedia
+ // (http://en.wikipedia.org/wiki/Solicited-node_multicast_address)
+ EXPECT_EQ(IPAddressV6("ff02::1:ff28:9c5a"),
+ IPAddressV6("fe80::2aa:ff:fe28:9c5a").getSolicitedNodeAddress());
+}
+
+TEST_P(IPAddressByteAccessorTest, CheckBytes) {
+ auto addrData = GetParam();
+ IPAddress ip(addrData.address);
+ auto i = 0;
+ for (auto byitr = addrData.bytes.begin(); i < ip.byteCount(); ++i, ++byitr) {
+ EXPECT_EQ(*byitr, ip.getNthMSByte(i));
+ EXPECT_EQ(*byitr, ip.isV4() ?
+ ip.asV4().getNthMSByte(i) : ip.asV6().getNthMSByte(i));
+ }
+ i = 0;
+ for (auto byritr = addrData.bytes.rbegin(); i < ip.byteCount(); ++i,
+ ++byritr) {
+ EXPECT_EQ(*byritr, ip.getNthLSByte(i));
+ EXPECT_EQ(*byritr, ip.isV4() ?
+ ip.asV4().getNthLSByte(i) : ip.asV6().getNthLSByte(i));
+ }
+}
+
+TEST_P(IPAddressBitAccessorTest, CheckBits) {
+ auto addrData = GetParam();
+ auto littleEndianAddrData = addrData.bytes;
+ //IPAddress stores address data in n/w byte order.
+ reverse(littleEndianAddrData.begin(), littleEndianAddrData.end());
+ //Bit iterator goes from LSBit to MSBit
+ //We will traverse the IPAddress bits from 0 to bitCount -1
+ auto bitr = folly::makeBitIterator(littleEndianAddrData.begin());
+ IPAddress ip(addrData.address);
+ for (auto i = 0; i < ip.bitCount(); ++i) {
+ auto msbIndex = ip.bitCount() - i - 1;
+ EXPECT_EQ(*bitr, ip.getNthMSBit(msbIndex));
+ EXPECT_EQ(*bitr, ip.isV4() ? ip.asV4().getNthMSBit(msbIndex) :
+ ip.asV6().getNthMSBit(msbIndex));
+ EXPECT_EQ(*bitr, ip.getNthLSBit(i));
+ EXPECT_EQ(*bitr, ip.isV4() ? ip.asV4().getNthLSBit(i) :
+ ip.asV6().getNthLSBit(i));
+ ++bitr;
+ }
+}
+
+TEST(IPAddress, InvalidByteAccess) {
+ IPAddress ip4("10.10.10.10");
+ //MSByte, LSByte accessors are 0 indexed
+ EXPECT_THROW(ip4.getNthMSByte(ip4.byteCount()), std::invalid_argument);
+ EXPECT_THROW(ip4.getNthLSByte(ip4.byteCount()), std::invalid_argument);
+ EXPECT_THROW(ip4.getNthMSByte(-1), std::invalid_argument);
+ EXPECT_THROW(ip4.getNthLSByte(-1), std::invalid_argument);
+ auto asV4 = ip4.asV4();
+ EXPECT_THROW(asV4.getNthMSByte(asV4.byteCount()), std::invalid_argument);
+ EXPECT_THROW(asV4.getNthLSByte(asV4.byteCount()), std::invalid_argument);
+ EXPECT_THROW(asV4.getNthMSByte(-1), std::invalid_argument);
+ EXPECT_THROW(asV4.getNthLSByte(-1), std::invalid_argument);
+
+ IPAddress ip6("2620:0:1cfe:face:b00c::3");
+ EXPECT_THROW(ip6.getNthMSByte(ip6.byteCount()), std::invalid_argument);
+ EXPECT_THROW(ip6.getNthLSByte(ip6.byteCount()), std::invalid_argument);
+ EXPECT_THROW(ip6.getNthMSByte(-1), std::invalid_argument);
+ EXPECT_THROW(ip6.getNthLSByte(-1), std::invalid_argument);
+ auto asV6 = ip6.asV6();
+ EXPECT_THROW(asV6.getNthMSByte(asV6.byteCount()), std::invalid_argument);
+ EXPECT_THROW(asV6.getNthLSByte(asV6.byteCount()), std::invalid_argument);
+ EXPECT_THROW(asV6.getNthMSByte(-1), std::invalid_argument);
+ EXPECT_THROW(asV6.getNthLSByte(-1), std::invalid_argument);
+
+}
+
+TEST(IPAddress, InvalidBBitAccess) {
+ IPAddress ip4("10.10.10.10");
+ //MSByte, LSByte accessors are 0 indexed
+ EXPECT_THROW(ip4.getNthMSBit(ip4.bitCount()), std::invalid_argument);
+ EXPECT_THROW(ip4.getNthLSBit(ip4.bitCount()), std::invalid_argument);
+ EXPECT_THROW(ip4.getNthMSBit(-1), std::invalid_argument);
+ EXPECT_THROW(ip4.getNthLSBit(-1), std::invalid_argument);
+ auto asV4 = ip4.asV4();
+ EXPECT_THROW(asV4.getNthMSBit(asV4.bitCount()), std::invalid_argument);
+ EXPECT_THROW(asV4.getNthLSBit(asV4.bitCount()), std::invalid_argument);
+ EXPECT_THROW(asV4.getNthMSBit(-1), std::invalid_argument);
+ EXPECT_THROW(asV4.getNthLSBit(-1), std::invalid_argument);
+
+ IPAddress ip6("2620:0:1cfe:face:b00c::3");
+ EXPECT_THROW(ip6.getNthMSBit(ip6.bitCount()), std::invalid_argument);
+ EXPECT_THROW(ip6.getNthLSBit(ip6.bitCount()), std::invalid_argument);
+ EXPECT_THROW(ip6.getNthMSBit(-1), std::invalid_argument);
+ EXPECT_THROW(ip6.getNthLSBit(-1), std::invalid_argument);
+ auto asV6 = ip6.asV6();
+ EXPECT_THROW(asV6.getNthMSBit(asV6.bitCount()), std::invalid_argument);
+ EXPECT_THROW(asV6.getNthLSBit(asV6.bitCount()), std::invalid_argument);
+ EXPECT_THROW(asV6.getNthMSBit(-1), std::invalid_argument);
+ EXPECT_THROW(asV6.getNthLSBit(-1), std::invalid_argument);
+}
+
+TEST(IPAddress, LongestCommonPrefix) {
+ IPAddress ip10("10.0.0.0");
+ IPAddress ip11("11.0.0.0");
+ IPAddress ip12("12.0.0.0");
+ IPAddress ip128("128.0.0.0");
+ IPAddress ip10dot10("10.10.0.0");
+ auto prefix = IPAddress::longestCommonPrefix({ip10, 8}, {ip128, 8});
+ auto prefix4 = IPAddressV4::longestCommonPrefix({ip10.asV4(), 8},
+ {ip128.asV4(), 8});
+ // No bits match b/w 128/8 and 10/8
+ EXPECT_EQ(IPAddress("0.0.0.0"), prefix.first);
+ EXPECT_EQ(0, prefix.second);
+ EXPECT_EQ(IPAddressV4("0.0.0.0"), prefix4.first);
+ EXPECT_EQ(0, prefix4.second);
+
+ prefix = IPAddress::longestCommonPrefix({ip10, 8}, {ip10dot10, 16});
+ prefix4 = IPAddressV4::longestCommonPrefix({ip10.asV4(), 8},
+ {ip10dot10.asV4(), 16});
+ // Between 10/8 and 10.10/16, 10/8 is the longest common match
+ EXPECT_EQ(ip10, prefix.first);
+ EXPECT_EQ(8, prefix.second);
+ EXPECT_EQ(ip10.asV4(), prefix4.first);
+ EXPECT_EQ(8, prefix4.second);
+
+ prefix = IPAddress::longestCommonPrefix({ip11, 8}, {ip12, 8});
+ prefix4 = IPAddressV4::longestCommonPrefix({ip11.asV4(), 8},
+ {ip12.asV4(), 8});
+ // 12 = 1100, 11 = 1011, longest match - 1000 = 8
+ EXPECT_EQ(IPAddress("8.0.0.0"), prefix.first);
+ EXPECT_EQ(5, prefix.second);
+ EXPECT_EQ(IPAddressV4("8.0.0.0"), prefix4.first);
+ EXPECT_EQ(5, prefix4.second);
+
+ // Between 128/1 and 128/2, longest match 128/1
+ prefix = IPAddress::longestCommonPrefix({ip128, 1}, {ip128, 2});
+ prefix4 = IPAddressV4::longestCommonPrefix({ip128.asV4(), 1},
+ {ip128.asV4(), 2});
+ EXPECT_EQ(ip128, prefix.first);
+ EXPECT_EQ(1, prefix.second);
+ EXPECT_EQ(ip128.asV4(), prefix4.first);
+ EXPECT_EQ(1, prefix4.second);
+
+ IPAddress ip6("2620:0:1cfe:face:b00c::3");
+ prefix = IPAddress::longestCommonPrefix({ip6, ip6.bitCount()},
+ {ip6, ip6.bitCount()});
+ auto prefix6 = IPAddressV6::longestCommonPrefix(
+ {ip6.asV6(), IPAddressV6::bitCount()},
+ {ip6.asV6(), IPAddressV6::bitCount()});
+ // Longest common b/w me and myself is myself
+ EXPECT_EQ(ip6, prefix.first);
+ EXPECT_EQ(ip6.bitCount(), prefix.second);
+ EXPECT_EQ(ip6.asV6(), prefix6.first);
+ EXPECT_EQ(ip6.asV6().bitCount(), prefix6.second);
+
+ IPAddress ip6Zero("::");
+ prefix = IPAddress::longestCommonPrefix({ip6, ip6.bitCount()}, {ip6Zero, 0});
+ prefix6 = IPAddressV6::longestCommonPrefix(
+ {ip6.asV6(), IPAddressV6::bitCount()},
+ {ip6Zero.asV6(), 0});
+ // Longest common b/w :: (ipv6 equivalent of 0/0) is ::
+ EXPECT_EQ(ip6Zero, prefix.first);
+ EXPECT_EQ(0, prefix.second);
+
+ // Exceptional cases
+ EXPECT_THROW(IPAddress::longestCommonPrefix({ip10, 8}, {ip6, 128}),
+ std::invalid_argument);
+ EXPECT_THROW(IPAddress::longestCommonPrefix({ip10, ip10.bitCount() + 1},
+ {ip10, 8}),
+ std::invalid_argument);
+ EXPECT_THROW(IPAddressV4::longestCommonPrefix(
+ {ip10.asV4(), IPAddressV4::bitCount() + 1},
+ {ip10.asV4(), 8}),
+ std::invalid_argument);
+ EXPECT_THROW(IPAddress::longestCommonPrefix({ip6, ip6.bitCount() + 1},
+ {ip6, ip6.bitCount()}),
+ std::invalid_argument);
+ EXPECT_THROW(IPAddressV6::longestCommonPrefix(
+ {ip6.asV6(), IPAddressV6::bitCount() + 1},
+ {ip6.asV6(), IPAddressV6::bitCount()}),
+ std::invalid_argument);
+
+}
+
+static vector<AddressData> validAddressProvider = {
+ AddressData("127.0.0.1", {127,0,0,1}, 4),
+ AddressData("69.63.189.16", {69,63,189,16}, 4),
+ AddressData("0.0.0.0", {0,0,0,0}, 4),
+ AddressData("::1",
+ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 6),
+ AddressData("2620:0:1cfe:face:b00c::3",
+ {38,32,0,0,28,254,250,206,176,12,0,0,0,0,0,3}, 6),
+};
+
+static vector<string> invalidAddressProvider = {
+ "",
+ "foo",
+ "1.1.1.256",
+ "1",
+ ":1",
+ "127.0.0.1,127.0.0.1",
+ "[1234]",
+};
+
+static vector<ByteVector> invalidBinaryProvider = {
+ {0x31, 0x32, 0x37, 0x2e, 0x30, 0x30, 0x2e, 0x30, 0x2e, 0x31},
+ // foo
+ {0x66, 0x6f, 0x6f},
+ {0x00},
+ {0x00, 0x00},
+ {0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00, 0x00},
+ {0xff},
+};
+
+static const uint8_t IS_LOCAL = AddressFlags::IS_LOCAL;
+static const uint8_t IS_NONROUTABLE = AddressFlags::IS_NONROUTABLE;
+static const uint8_t IS_PRIVATE = AddressFlags::IS_PRIVATE;
+static const uint8_t IS_ZERO = AddressFlags::IS_ZERO;
+static const uint8_t IS_LINK_LOCAL =
+ AddressFlags::IS_LINK_LOCAL | IS_NONROUTABLE;
+static const uint8_t IS_PVT_NONROUTE = IS_NONROUTABLE | IS_PRIVATE;
+static const uint8_t IS_MULTICAST = AddressFlags::IS_MULTICAST;
+static const uint8_t IS_LINK_LOCAL_BROADCAST =
+AddressFlags::IS_LINK_LOCAL_BROADCAST;
+
+static vector<AddressFlags> flagProvider = {
+ // public v4
+ AddressFlags("69.63.176.1", 4, 0),
+ AddressFlags("128.12.65.3", 4, 0),
+ AddressFlags("192.0.1.0", 4, 0),
+ AddressFlags("198.51.101.0", 4, 0),
+ AddressFlags("203.0.114.0", 4, 0),
+ AddressFlags("128.12.64.115", 4, 0),
+
+ // public v6
+ AddressFlags("2620:0:1cfe:face:b00c::3", 6, 0),
+
+ // localhost
+ AddressFlags("127.0.0.1", 4, IS_LOCAL | IS_PVT_NONROUTE),
+ AddressFlags("::1", 6, IS_LOCAL | IS_PVT_NONROUTE),
+
+ // private v4
+ AddressFlags("10.0.0.0", 4, IS_PVT_NONROUTE),
+ AddressFlags("10.11.12.13", 4, IS_PVT_NONROUTE),
+ AddressFlags("10.255.255.255", 4, IS_PVT_NONROUTE),
+ AddressFlags("127.128.129.200", 4, IS_PVT_NONROUTE),
+ AddressFlags("127.255.255.255", 4, IS_PVT_NONROUTE),
+ AddressFlags("169.254.0.0", 4, IS_PVT_NONROUTE),
+ AddressFlags("192.168.0.0", 4, IS_PVT_NONROUTE),
+ AddressFlags("192.168.200.255", 4, IS_PVT_NONROUTE),
+ AddressFlags("192.168.255.255", 4, IS_PVT_NONROUTE),
+
+ // private v6
+ AddressFlags("fd01:1637:1c56:66af::", 6, IS_PVT_NONROUTE),
+
+ // non routable v4
+ AddressFlags("0.0.0.0", 4, IS_NONROUTABLE | IS_ZERO),
+ AddressFlags("0.255.255.255", 4, IS_NONROUTABLE),
+ AddressFlags("192.0.0.0", 4, IS_NONROUTABLE),
+ AddressFlags("192.0.2.0", 4, IS_NONROUTABLE),
+ AddressFlags("198.18.0.0", 4, IS_NONROUTABLE),
+ AddressFlags("198.19.255.255", 4, IS_NONROUTABLE),
+ AddressFlags("198.51.100.0", 4, IS_NONROUTABLE),
+ AddressFlags("198.51.100.255", 4, IS_NONROUTABLE),
+ AddressFlags("203.0.113.0", 4, IS_NONROUTABLE),
+ AddressFlags("203.0.113.255", 4, IS_NONROUTABLE),
+ AddressFlags("224.0.0.0", 4, IS_NONROUTABLE | IS_MULTICAST),
+ AddressFlags("240.0.0.0", 4, IS_NONROUTABLE),
+ AddressFlags("224.0.0.0", 4, IS_NONROUTABLE),
+ // v4 link local broadcast
+ AddressFlags("255.255.255.255", 4, IS_NONROUTABLE | IS_LINK_LOCAL_BROADCAST),
+
+ // non routable v6
+ AddressFlags("1999::1", 6, IS_NONROUTABLE),
+ AddressFlags("0::0", 6, IS_NONROUTABLE | IS_ZERO),
+ AddressFlags("0::0:0", 6, IS_NONROUTABLE | IS_ZERO),
+ AddressFlags("0:0:0::0", 6, IS_NONROUTABLE | IS_ZERO),
+
+ // link-local v6
+ AddressFlags("fe80::0205:73ff:fef9:46fc", 6, IS_LINK_LOCAL),
+ AddressFlags("fe80::0012:34ff:fe56:7890", 6, IS_LINK_LOCAL),
+
+ // multicast v4
+ AddressFlags("224.0.0.1", 4, IS_MULTICAST | IS_NONROUTABLE) ,
+ AddressFlags("224.0.0.251", 4, IS_MULTICAST | IS_NONROUTABLE),
+ AddressFlags("239.12.34.56", 4, IS_MULTICAST | IS_NONROUTABLE),
+
+ // multicast v6
+ AddressFlags("ff00::", 6, IS_MULTICAST | IS_NONROUTABLE),
+ AddressFlags("ff02:ffff::1", 6, IS_MULTICAST | IS_NONROUTABLE),
+ AddressFlags("ff02::101", 6, IS_MULTICAST | IS_NONROUTABLE),
+ AddressFlags("ff0e::101", 6, IS_MULTICAST),
+ // v6 link local broadcast
+ AddressFlags("ff02::1", 6, IS_NONROUTABLE | IS_LINK_LOCAL_BROADCAST),
+};
+
+static vector<pair<string, string> > mapProvider = {
+ {"::ffff:192.0.2.128", "192.0.2.128"},
+ {"192.0.2.128", "::ffff:192.0.2.128"},
+ {"::FFFF:129.144.52.38", "129.144.52.38"},
+ {"129.144.52.38", "::FFFF:129.144.52.38"},
+ {"0:0:0:0:0:FFFF:222.1.41.90", "222.1.41.90"},
+ {"::FFFF:222.1.41.90", "222.1.41.90"},
+};
+
+static vector<MaskData> masksProvider = {
+ MaskData("255.255.255.255", 1, "128.0.0.0"),
+ MaskData("255.255.255.255", 2, "192.0.0.0"),
+ MaskData("192.0.2.42", 16, "192.0.0.0"),
+ MaskData("255.255.255.255", 24, "255.255.255.0"),
+ MaskData("255.255.255.255", 32, "255.255.255.255"),
+ MaskData("10.10.10.10", 0, "0.0.0.0"),
+ MaskData("::1", 64, "::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 1, "::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 3, "2000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 6, "2400::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 7, "2600::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 11, "2620::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 36, "2620:0:1000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 37, "2620:0:1800::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 38, "2620:0:1c00::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 41, "2620:0:1c80::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 42, "2620:0:1cc0::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 43, "2620:0:1ce0::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 44, "2620:0:1cf0::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 45, "2620:0:1cf8::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 46, "2620:0:1cfc::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 47, "2620:0:1cfe::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 49, "2620:0:1cfe:8000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 50, "2620:0:1cfe:c000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 51, "2620:0:1cfe:e000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 52, "2620:0:1cfe:f000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 53, "2620:0:1cfe:f800::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 55, "2620:0:1cfe:fa00::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 57, "2620:0:1cfe:fa80::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 58, "2620:0:1cfe:fac0::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 61, "2620:0:1cfe:fac8::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 62, "2620:0:1cfe:facc::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 63, "2620:0:1cfe:face::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 65, "2620:0:1cfe:face:8000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 67, "2620:0:1cfe:face:a000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 68, "2620:0:1cfe:face:b000::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 77, "2620:0:1cfe:face:b008::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 78, "2620:0:1cfe:face:b00c::"),
+ MaskData("2620:0:1cfe:face:b00c::3", 127, "2620:0:1cfe:face:b00c::2"),
+ MaskData("2620:0:1cfe:face:b00c::3", 128, "2620:0:1cfe:face:b00c::3"),
+ MaskData("2620:0:1cfe:face:b00c::3", 0, "::")
+};
+
+static vector<MaskBoundaryData> maskBoundaryProvider = {
+ MaskBoundaryData("10.1.1.1", 24, "10.1.1.1", true),
+ MaskBoundaryData("10.1.1.1", 8, "10.1.2.3", true),
+ MaskBoundaryData("2620:0:1cfe:face:b00c::1", 48, "2620:0:1cfe::", true),
+ // addresses that are NOT in the same subnet once mask is applied
+ MaskBoundaryData("10.1.1.1", 24, "10.1.2.1", false),
+ MaskBoundaryData("10.1.1.1", 16, "10.2.3.4", false),
+ MaskBoundaryData("2620:0:1cfe:face:b00c::1", 48, "2620:0:1cfc::", false),
+};
+
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressTest,
+ ::testing::ValuesIn(validAddressProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressFlagTest,
+ ::testing::ValuesIn(flagProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressMappedTest,
+ ::testing::ValuesIn(mapProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressCtorTest,
+ ::testing::ValuesIn(invalidAddressProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressCtorBinaryTest,
+ ::testing::ValuesIn(invalidBinaryProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressMaskTest,
+ ::testing::ValuesIn(masksProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressMaskBoundaryTest,
+ ::testing::ValuesIn(maskBoundaryProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressByteAccessorTest,
+ ::testing::ValuesIn(validAddressProvider));
+INSTANTIATE_TEST_CASE_P(IPAddress,
+ IPAddressBitAccessorTest,
+ ::testing::ValuesIn(validAddressProvider));
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+}
+
+#include "folly/IPAddress.h"
+
+namespace folly {
+
+class IPAddress;
+
+typedef std::vector<uint8_t> ByteVector;
+
+struct AddressData {
+ std::string address;
+ ByteVector bytes;
+ uint8_t version;
+
+ AddressData(const std::string& address, const ByteVector& bytes,
+ uint8_t version)
+ : address(address), bytes(bytes), version(version) {}
+ AddressData(const std::string& address, uint8_t version)
+ : address(address), bytes(), version(version) {}
+ explicit AddressData(const std::string& address)
+ : address(address), bytes(), version(0) {}
+ AddressData(): address(""), bytes(), version(0) {}
+
+ static in_addr parseAddress4(const std::string& src) {
+ in_addr addr;
+ inet_pton(AF_INET, src.c_str(), &addr);
+ return addr;
+ }
+
+ static in6_addr parseAddress6(const std::string& src) {
+ in6_addr addr;
+ inet_pton(AF_INET6, src.c_str(), &addr);
+ return addr;
+ }
+};
+
+struct AddressFlags {
+ std::string address;
+ uint8_t flags;
+ uint8_t version;
+
+ static const uint8_t IS_LOCAL = 1 << 0;
+ static const uint8_t IS_NONROUTABLE = 1 << 1;
+ static const uint8_t IS_PRIVATE = 1 << 2;
+ static const uint8_t IS_ZERO = 1 << 3;
+ static const uint8_t IS_LINK_LOCAL = 1 << 4;
+ static const uint8_t IS_MULTICAST = 1 << 5;
+ static const uint8_t IS_LINK_LOCAL_BROADCAST = 1 << 6;
+
+ AddressFlags(const std::string& addr, uint8_t version, uint8_t flags)
+ : address(addr)
+ , flags(flags)
+ , version(version)
+ {}
+
+ bool isLoopback() const {
+ return (flags & IS_LOCAL);
+ }
+ bool isNonroutable() const {
+ return (flags & IS_NONROUTABLE);
+ }
+ bool isPrivate() const {
+ return (flags & IS_PRIVATE);
+ }
+ bool isZero() const {
+ return (flags & IS_ZERO);
+ }
+ bool isLinkLocal() const {
+ return (flags & IS_LINK_LOCAL);
+ }
+ bool isLinkLocalBroadcast() const {
+ return (flags & IS_LINK_LOCAL_BROADCAST);
+ }
+};
+
+struct MaskData {
+ std::string address;
+ uint8_t mask;
+ std::string subnet;
+ MaskData(const std::string& addr, uint8_t mask,
+ const std::string& subnet)
+ : address(addr)
+ , mask(mask)
+ , subnet(subnet)
+ {}
+};
+
+struct MaskBoundaryData : MaskData {
+ bool inSubnet;
+ MaskBoundaryData(const std::string& addr, uint8_t mask,
+ const std::string& subnet, bool inSubnet)
+ : MaskData(addr, mask, subnet)
+ , inSubnet(inSubnet)
+ {}
+};
+
+struct SerializeData {
+ std::string address;
+ ByteVector bytes;
+
+ SerializeData(const std::string& addr, const ByteVector& bytes)
+ : address(addr)
+ , bytes(bytes)
+ {}
+};
+
+struct IPAddressTest : public ::testing::TestWithParam<AddressData> {
+ void ExpectIsValid(const IPAddress& addr) {
+ AddressData param = GetParam();
+ EXPECT_EQ(param.version, addr.version());
+ EXPECT_EQ(param.address, addr.str());
+ if (param.version == 4) {
+ in_addr v4addr = AddressData::parseAddress4(param.address);
+ EXPECT_EQ(0, memcmp(&v4addr, addr.asV4().toByteArray().data(), 4));
+ EXPECT_TRUE(addr.isV4());
+ EXPECT_FALSE(addr.isV6());
+ } else {
+ in6_addr v6addr = AddressData::parseAddress6(param.address);
+ EXPECT_EQ(0, memcmp(&v6addr, addr.asV6().toByteArray().data(), 16));
+ EXPECT_TRUE(addr.isV6());
+ EXPECT_FALSE(addr.isV4());
+ }
+ }
+};
+struct IPAddressFlagTest : public ::testing::TestWithParam<AddressFlags> {};
+struct IPAddressCtorTest : public ::testing::TestWithParam<std::string> {};
+struct IPAddressCtorBinaryTest : public ::testing::TestWithParam<ByteVector> {};
+struct IPAddressMappedTest :
+ public ::testing::TestWithParam<std::pair<std::string,std::string> > {};
+struct IPAddressMaskTest : public ::testing::TestWithParam<MaskData> {};
+struct IPAddressMaskBoundaryTest :
+ public ::testing::TestWithParam<MaskBoundaryData> {};
+struct IPAddressSerializeTest :
+ public ::testing::TestWithParam<SerializeData> {};
+struct IPAddressByteAccessorTest:
+ public ::testing::TestWithParam<AddressData> {};
+struct IPAddressBitAccessorTest:
+ public ::testing::TestWithParam<AddressData> {};
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "folly/IPAddressV6.h"
+#include "folly/MacAddress.h"
+
+using folly::MacAddress;
+using folly::IPAddressV6;
+using folly::StringPiece;
+
+void testMAC(const std::string& str, uint64_t expectedHBO) {
+ SCOPED_TRACE(str);
+ MacAddress addr(str);
+ // Make sure parsing returned the expected value.
+ EXPECT_EQ(expectedHBO, addr.u64HBO());
+
+ // Perform additional checks on the MacAddress
+
+ // Check using operator==()
+ EXPECT_EQ(MacAddress::fromHBO(expectedHBO), addr);
+ // Check using operator==() when passing in non-zero padding bytes
+ EXPECT_EQ(MacAddress::fromHBO(expectedHBO | 0xa5a5000000000000), addr);
+
+ // Similar checks after converting to network byte order
+ uint64_t expectedNBO = folly::Endian::big(expectedHBO);
+ EXPECT_EQ(expectedNBO, addr.u64NBO());
+ EXPECT_EQ(MacAddress::fromNBO(expectedNBO), addr);
+ uint64_t nboWithPad = folly::Endian::big(expectedHBO | 0xa5a5000000000000);
+ EXPECT_EQ(MacAddress::fromNBO(nboWithPad), addr);
+
+ // Check they value returned by bytes()
+ uint8_t expectedBytes[8];
+ memcpy(expectedBytes, &expectedNBO, 8);
+ for (int n = 0; n < 6; ++n) {
+ EXPECT_EQ(expectedBytes[n + 2], addr.bytes()[n]);
+ }
+}
+
+TEST(MacAddress, parse) {
+ testMAC("12:34:56:78:9a:bc", 0x123456789abc);
+ testMAC("00-11-22-33-44-55", 0x1122334455);
+ testMAC("abcdef123456", 0xabcdef123456);
+ testMAC("1:2:3:4:5:6", 0x010203040506);
+ testMAC("0:0:0:0:0:0", 0);
+ testMAC("0:0:5e:0:1:1", 0x00005e000101);
+
+ EXPECT_THROW(MacAddress(""), std::invalid_argument);
+ EXPECT_THROW(MacAddress("0"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:34"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:3"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:x4:56:78:9a:bc"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12x34:56:78:9a:bc"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:34:56:78:9a:bc:de"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:34:56:78:9a:bcde"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:34:56:78:9a:bc "), std::invalid_argument);
+ EXPECT_THROW(MacAddress(" 12:34:56:78:9a:bc"), std::invalid_argument);
+ EXPECT_THROW(MacAddress("12:34:56:78:-1:bc"), std::invalid_argument);
+}
+
+void testFromBinary(const char* str, uint64_t expectedHBO) {
+ StringPiece bin(str, 6);
+ auto mac = MacAddress::fromBinary(bin);
+ SCOPED_TRACE(mac.toString());
+ EXPECT_EQ(expectedHBO, mac.u64HBO());
+}
+
+TEST(MacAddress, fromBinary) {
+ testFromBinary("\0\0\0\0\0\0", 0);
+ testFromBinary("\x12\x34\x56\x78\x9a\xbc", 0x123456789abc);
+ testFromBinary("\x11\x22\x33\x44\x55\x66", 0x112233445566);
+
+ StringPiece empty("");
+ EXPECT_THROW(MacAddress::fromBinary(empty), std::invalid_argument);
+ StringPiece tooShort("\x11", 1);
+ EXPECT_THROW(MacAddress::fromBinary(tooShort), std::invalid_argument);
+ StringPiece tooLong("\x11\x22\x33\x44\x55\x66\x77", 7);
+ EXPECT_THROW(MacAddress::fromBinary(tooLong), std::invalid_argument);
+}
+
+TEST(MacAddress, toString) {
+ EXPECT_EQ("12:34:56:78:9a:bc",
+ MacAddress::fromHBO(0x123456789abc).toString());
+ EXPECT_EQ("12:34:56:78:9a:bc", MacAddress("12:34:56:78:9a:bc").toString());
+ EXPECT_EQ("01:23:45:67:89:ab", MacAddress("01-23-45-67-89-ab").toString());
+ EXPECT_EQ("01:23:45:67:89:ab", MacAddress("0123456789ab").toString());
+}
+
+TEST(MacAddress, attributes) {
+ EXPECT_TRUE(MacAddress("ff:ff:ff:ff:ff:ff").isBroadcast());
+ EXPECT_FALSE(MacAddress("7f:ff:ff:ff:ff:ff").isBroadcast());
+ EXPECT_FALSE(MacAddress("7f:ff:ff:ff:ff:fe").isBroadcast());
+ EXPECT_FALSE(MacAddress("00:00:00:00:00:00").isBroadcast());
+ EXPECT_TRUE(MacAddress::fromNBO(0xffffffffffffffffU).isBroadcast());
+
+ EXPECT_TRUE(MacAddress("ff:ff:ff:ff:ff:ff").isMulticast());
+ EXPECT_TRUE(MacAddress("01:00:00:00:00:00").isMulticast());
+ EXPECT_FALSE(MacAddress("00:00:00:00:00:00").isMulticast());
+ EXPECT_FALSE(MacAddress("fe:ff:ff:ff:ff:ff").isMulticast());
+ EXPECT_FALSE(MacAddress("00:00:5e:00:01:01").isMulticast());
+
+ EXPECT_FALSE(MacAddress("ff:ff:ff:ff:ff:ff").isUnicast());
+ EXPECT_FALSE(MacAddress("01:00:00:00:00:00").isUnicast());
+ EXPECT_TRUE(MacAddress("00:00:00:00:00:00").isUnicast());
+ EXPECT_TRUE(MacAddress("fe:ff:ff:ff:ff:ff").isUnicast());
+ EXPECT_TRUE(MacAddress("00:00:5e:00:01:01").isUnicast());
+
+ EXPECT_TRUE(MacAddress("ff:ff:ff:ff:ff:ff").isLocallyAdministered());
+ EXPECT_TRUE(MacAddress("02:00:00:00:00:00").isLocallyAdministered());
+ EXPECT_FALSE(MacAddress("01:00:00:00:00:00").isLocallyAdministered());
+ EXPECT_FALSE(MacAddress("00:00:00:00:00:00").isLocallyAdministered());
+ EXPECT_FALSE(MacAddress("fd:ff:ff:ff:ff:ff").isLocallyAdministered());
+ EXPECT_TRUE(MacAddress("fe:ff:ff:ff:ff:ff").isLocallyAdministered());
+ EXPECT_FALSE(MacAddress("00:00:5e:00:01:01").isLocallyAdministered());
+ EXPECT_TRUE(MacAddress("02:12:34:56:78:9a").isLocallyAdministered());
+}
+
+TEST(MacAddress, createMulticast) {
+ EXPECT_EQ(MacAddress("33:33:00:01:00:03"),
+ MacAddress::createMulticast(IPAddressV6("ff02:dead:beef::1:3")));
+ EXPECT_EQ(MacAddress("33:33:12:34:56:78"),
+ MacAddress::createMulticast(IPAddressV6("ff02::abcd:1234:5678")));
+}
+
+void testCmp(const char* str1, const char* str2) {
+ SCOPED_TRACE(folly::to<std::string>(str1, " < ", str2));
+ MacAddress m1(str1);
+ MacAddress m2(str2);
+
+ // Test the comparison operators
+ EXPECT_TRUE(m1 < m2);
+ EXPECT_FALSE(m1 < m1);
+ EXPECT_TRUE(m1 <= m2);
+ EXPECT_TRUE(m2 > m1);
+ EXPECT_TRUE(m2 >= m1);
+ EXPECT_TRUE(m1 != m2);
+ EXPECT_TRUE(m1 == m1);
+ EXPECT_FALSE(m1 == m2);
+
+ // Also test the copy constructor and assignment operator
+ MacAddress copy(m1);
+ EXPECT_EQ(copy, m1);
+ copy = m2;
+ EXPECT_EQ(copy, m2);
+}
+
+TEST(MacAddress, ordering) {
+ testCmp("00:00:00:00:00:00", "00:00:00:00:00:01");
+ testCmp("00:00:00:00:00:01", "00:00:00:00:00:02");
+ testCmp("01:00:00:00:00:00", "02:00:00:00:00:00");
+ testCmp("00:00:00:00:00:01", "00:00:00:00:01:00");
+}