Move common/network/IPAddress.h and related to folly/
authorAnton Likhtarov <alikhtarov@fb.com>
Sat, 5 Apr 2014 02:00:31 +0000 (19:00 -0700)
committerDave Watson <davejwatson@fb.com>
Tue, 20 May 2014 19:53:44 +0000 (12:53 -0700)
Summary:
Moving our internal IP/Mac address libraries to folly/

Facebook:
We want to get rid of common/ dependencies in Mcrouter since we're going to open source it. Also looking at the original commit, seems like it's been the intention all along, so I just did it.

I tried to keep dependencies intact as much as possible. Changing projects to use this directly should be in separate diffs.

Test Plan:
Run folly/network and common/network unit tests.

Generate the list of targets with:

```
fbgs /$FILE.h | cut -f1 -d: | xargs -L1 dirname | cut -f2- -d/ | sort | uniq
```

Then fbconfig + fbmake. Will fix contbuild failures.

Revert Plan:

Reviewed By: simpkins@fb.com

FB internal diff: D1261089

14 files changed:
folly/Format-inl.h
folly/IPAddress.cpp [new file with mode: 0644]
folly/IPAddress.h [new file with mode: 0644]
folly/IPAddressException.h [new file with mode: 0644]
folly/IPAddressV4.cpp [new file with mode: 0644]
folly/IPAddressV4.h [new file with mode: 0644]
folly/IPAddressV6.cpp [new file with mode: 0644]
folly/IPAddressV6.h [new file with mode: 0644]
folly/MacAddress.cpp [new file with mode: 0644]
folly/MacAddress.h [new file with mode: 0644]
folly/detail/IPAddress.h [new file with mode: 0644]
folly/test/IPAddressTest.cpp [new file with mode: 0644]
folly/test/IPAddressTest.h [new file with mode: 0644]
folly/test/MacAddressTest.cpp [new file with mode: 0644]

index a4568b13b0dcfe631d42ec417eec2bf1820bc572..5575151c27c0cef40435c569f2d0ca0e436b640f 100644 (file)
 #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 {
@@ -1167,3 +1171,5 @@ toAppend(const Formatter<containerMode, Args...>& value, Tgt * result) {
 }
 
 }  // namespace folly
+
+#pragma GCC diagnostic pop
diff --git a/folly/IPAddress.cpp b/folly/IPAddress.cpp
new file mode 100644 (file)
index 0000000..9e902a1
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * 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
diff --git a/folly/IPAddress.h b/folly/IPAddress.h
new file mode 100644 (file)
index 0000000..c5f57aa
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * 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
diff --git a/folly/IPAddressException.h b/folly/IPAddressException.h
new file mode 100644 (file)
index 0000000..8aa4a9b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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
diff --git a/folly/IPAddressV4.cpp b/folly/IPAddressV4.cpp
new file mode 100644 (file)
index 0000000..e42416b
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * 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
diff --git a/folly/IPAddressV4.h b/folly/IPAddressV4.h
new file mode 100644 (file)
index 0000000..98198a3
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * 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
diff --git a/folly/IPAddressV6.cpp b/folly/IPAddressV6.cpp
new file mode 100644 (file)
index 0000000..65101d4
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+ * 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
diff --git a/folly/IPAddressV6.h b/folly/IPAddressV6.h
new file mode 100644 (file)
index 0000000..134d6de
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * 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
diff --git a/folly/MacAddress.cpp b/folly/MacAddress.cpp
new file mode 100644 (file)
index 0000000..6368483
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * 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
diff --git a/folly/MacAddress.h b/folly/MacAddress.h
new file mode 100644 (file)
index 0000000..fbc9bd2
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * 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
diff --git a/folly/detail/IPAddress.h b/folly/detail/IPAddress.h
new file mode 100644 (file)
index 0000000..7f1095e
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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
diff --git a/folly/test/IPAddressTest.cpp b/folly/test/IPAddressTest.cpp
new file mode 100644 (file)
index 0000000..b9cb43d
--- /dev/null
@@ -0,0 +1,1056 @@
+/*
+ * 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(&param.bytes[0]);
+    address = IPAddress(v4addr);
+  } else {
+    in6_addr v6addr = detail::Bytes::mkAddress6(&param.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));
diff --git a/folly/test/IPAddressTest.h b/folly/test/IPAddressTest.h
new file mode 100644 (file)
index 0000000..a5f4d89
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * 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
diff --git a/folly/test/MacAddressTest.cpp b/folly/test/MacAddressTest.cpp
new file mode 100644 (file)
index 0000000..0cea2b8
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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");
+}