Commit dced19f5 authored by Anton Likhtarov's avatar Anton Likhtarov Committed by Dave Watson

Move common/network/IPAddress.h and related to folly/

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
parent fbdb6012
......@@ -21,6 +21,10 @@
#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
This diff is collapsed.
This diff is collapsed.
/*
* 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
/*
* 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
/*
* 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
This diff is collapsed.
/*
* 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
/*
* 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
/*
* 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
/*
* 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
This diff is collapsed.
/*
* 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
/*
* 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");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment