X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FIPAddressV6.cpp;h=cdd82acc46a6fd0826c41917ad8e8fa87c11ee77;hb=db0ea224863ae8ffe332b19b44526ad6ff045ea7;hp=70428f3adbd69fa29920d880c2b13606d4869fe1;hpb=a45eee721bb6158fd26982dd815e3efe962e1c8c;p=folly.git diff --git a/folly/IPAddressV6.cpp b/folly/IPAddressV6.cpp index 70428f3a..cdd82acc 100644 --- a/folly/IPAddressV6.cpp +++ b/folly/IPAddressV6.cpp @@ -19,14 +19,25 @@ #include #include -#include - #include #include #include #include #include +#if !_WIN32 +#include +#else +// Because of the massive pain that is libnl, this can't go into the socket +// portability header as you can't include and in +// the same translation unit without getting errors -_-... +#include // @manual +#include // @manual + +// Alias the max size of an interface name to what posix expects. +#define IFNAMSIZ IF_NAMESIZE +#endif + using std::ostream; using std::string; @@ -51,32 +62,30 @@ void toAppend(IPAddressV6 addr, fbstring* result) { result->append(addr.str()); } -bool IPAddressV6::validate(StringPiece ip) { - if (ip.size() > 0 && ip.front() == '[' && ip.back() == ']') { - ip = ip.subpiece(1, ip.size() - 2); - } - - constexpr size_t kStrMaxLen = INET6_ADDRSTRLEN; - std::array ip_cstr; - const size_t len = std::min(ip.size(), kStrMaxLen); - std::memcpy(ip_cstr.data(), ip.data(), len); - ip_cstr[len] = 0; - struct in6_addr addr; - return 1 == inet_pton(AF_INET6, ip_cstr.data(), &addr); +bool IPAddressV6::validate(StringPiece ip) noexcept { + return tryFromString(ip).hasValue(); } // public default constructor -IPAddressV6::IPAddressV6() { -} +IPAddressV6::IPAddressV6() {} // public string constructor IPAddressV6::IPAddressV6(StringPiece addr) { - auto ip = addr.str(); + auto maybeIp = tryFromString(addr); + if (maybeIp.hasError()) { + throw IPAddressFormatException( + to("Invalid IPv6 address '", addr, "'")); + } + *this = std::move(maybeIp.value()); +} + +Expected IPAddressV6::tryFromString( + StringPiece str) noexcept { + auto ip = str.str(); // Allow addresses surrounded in brackets if (ip.size() < 2) { - throw IPAddressFormatException( - to("Invalid IPv6 address '", ip, "': address too short")); + return makeUnexpected(IPAddressFormatError::INVALID_IP); } if (ip.front() == '[' && ip.back() == ']') { ip = ip.substr(1, ip.size() - 2); @@ -88,40 +97,29 @@ IPAddressV6::IPAddressV6(StringPiece addr) { hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; - if (!getaddrinfo(ip.c_str(), nullptr, &hints, &result)) { - struct sockaddr_in6* ipAddr = (struct sockaddr_in6*)result->ai_addr; - addr_.in6Addr_ = ipAddr->sin6_addr; - scope_ = uint16_t(ipAddr->sin6_scope_id); - freeaddrinfo(result); - } else { - throw IPAddressFormatException( - to("Invalid IPv6 address '", ip, "'")); + if (::getaddrinfo(ip.c_str(), nullptr, &hints, &result) == 0) { + SCOPE_EXIT { + ::freeaddrinfo(result); + }; + const struct sockaddr_in6* sa = + reinterpret_cast(result->ai_addr); + return IPAddressV6(*sa); } + return makeUnexpected(IPAddressFormatError::INVALID_IP); } // in6_addr constructor -IPAddressV6::IPAddressV6(const in6_addr& src) - : addr_(src) -{ -} +IPAddressV6::IPAddressV6(const in6_addr& src) noexcept : addr_(src) {} // sockaddr_in6 constructor -IPAddressV6::IPAddressV6(const sockaddr_in6& src) - : addr_(src.sin6_addr) - , scope_(uint16_t(src.sin6_scope_id)) -{ -} +IPAddressV6::IPAddressV6(const sockaddr_in6& src) noexcept + : addr_(src.sin6_addr), scope_(uint16_t(src.sin6_scope_id)) {} // ByteArray16 constructor -IPAddressV6::IPAddressV6(const ByteArray16& src) - : addr_(src) -{ -} +IPAddressV6::IPAddressV6(const ByteArray16& src) noexcept : addr_(src) {} // link-local constructor -IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) - : addr_(mac) { -} +IPAddressV6::IPAddressV6(LinkLocalTag, MacAddress mac) : addr_(mac) {} IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) { // The link-local address uses modified EUI-64 format, @@ -140,20 +138,23 @@ IPAddressV6::AddressStorage::AddressStorage(MacAddress mac) { Optional IPAddressV6::getMacAddressFromLinkLocal() const { // Returned MacAddress must be constructed from a link-local IPv6 address. - if (!(addr_.bytes_[0] == 0xfe && addr_.bytes_[1] == 0x80 && - addr_.bytes_[2] == 0x00 && addr_.bytes_[3] == 0x00 && - addr_.bytes_[4] == 0x00 && addr_.bytes_[5] == 0x00 && - addr_.bytes_[6] == 0x00 && addr_.bytes_[7] == 0x00 && - addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { + if (!isLinkLocal()) { return folly::none; } - // The link-local address uses modified EUI-64 format, + return getMacAddressFromEUI64(); +} + +Optional IPAddressV6::getMacAddressFromEUI64() const { + if (!(addr_.bytes_[11] == 0xff && addr_.bytes_[12] == 0xfe)) { + return folly::none; + } + // The auto configured address uses modified EUI-64 format, // See RFC 4291 sections 2.5.1, 2.5.6, and Appendix A std::array bytes; - // Step 1: first 8 bytes are fe:80:00:00:00:00:00:00, and can be stripped + // Step 1: first 8 bytes are network prefix, and can be stripped // Step 2: invert the universal/local (U/L) flag (bit 7) bytes[0] = addr_.bytes_[8] ^ 0x02; - // Step 3: copy thhese bytes are they are + // Step 3: copy these bytes as they are bytes[1] = addr_.bytes_[9]; bytes[2] = addr_.bytes_[10]; // Step 4: strip bytes (0xfffe), which are bytes_[11] and bytes_[12] @@ -164,15 +165,34 @@ Optional IPAddressV6::getMacAddressFromLinkLocal() const { return Optional(MacAddress::fromBinary(range(bytes))); } -void IPAddressV6::setFromBinary(ByteRange bytes) { - if (bytes.size() != 16) { +IPAddressV6 IPAddressV6::fromBinary(ByteRange bytes) { + auto maybeIp = tryFromBinary(bytes); + if (maybeIp.hasError()) { throw IPAddressFormatException(to( - "Invalid IPv6 binary data: length must ", - "be 16 bytes, got ", + "Invalid IPv6 binary data: length must be 16 bytes, got ", bytes.size())); } + return maybeIp.value(); +} + +Expected IPAddressV6::tryFromBinary( + ByteRange bytes) noexcept { + IPAddressV6 addr; + auto setResult = addr.trySetFromBinary(bytes); + if (setResult.hasError()) { + return makeUnexpected(std::move(setResult.error())); + } + return addr; +} + +Expected IPAddressV6::trySetFromBinary( + ByteRange bytes) noexcept { + if (bytes.size() != 16) { + return makeUnexpected(IPAddressFormatError::INVALID_IP); + } memcpy(&addr_.in6Addr_.s6_addr, bytes.data(), sizeof(in6_addr)); scope_ = 0; + return unit; } // static @@ -219,9 +239,8 @@ static inline uint16_t unpack(uint8_t lobyte, uint8_t hibyte) { // 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) { +static inline void +unpackInto(const unsigned char* src, uint16_t* dest, size_t count) { for (size_t i = 0, hi = 1, lo = 0; i < count; i++) { dest[i] = unpack(src[hi], src[lo]); hi += 2; @@ -232,11 +251,11 @@ static inline void unpackInto(const unsigned char* src, // public IPAddressV4 IPAddressV6::getIPv4For6To4() const { if (!is6To4()) { - throw IPAddressV6::TypeError(format( - "Invalid IP '{}': not a 6to4 address", str()).str()); + throw IPAddressV6::TypeError( + sformat("Invalid IP '{}': not a 6to4 address", str())); } // convert 16x8 bytes into first 4x16 bytes - uint16_t ints[4] = {0,0,0,0}; + uint16_t ints[4] = {0, 0, 0, 0}; unpackInto(bytes(), ints, 4); // repack into 4x8 union { @@ -272,7 +291,7 @@ bool IPAddressV6::isIPv4Mapped() const { // public IPAddressV6::Type IPAddressV6::type() const { // convert 16x8 bytes into first 2x16 bytes - uint16_t ints[2] = {0,0}; + uint16_t ints[2] = {0, 0}; unpackInto(bytes(), ints, 2); if ((((uint32_t)ints[0] << 16) | ints[1]) == IPAddressV6::PREFIX_TEREDO) { @@ -288,8 +307,7 @@ IPAddressV6::Type IPAddressV6::type() const { // public string IPAddressV6::toJson() const { - return format( - "{{family:'AF_INET6', addr:'{}', hash:{}}}", str(), hash()).str(); + return sformat("{{family:'AF_INET6', addr:'{}', hash:{}}}", str(), hash()); } // public @@ -312,18 +330,18 @@ bool IPAddressV6::inSubnet(StringPiece cidrNetwork) const { auto subnetInfo = IPAddress::createNetwork(cidrNetwork); auto addr = subnetInfo.first; if (!addr.isV6()) { - throw IPAddressFormatException(to( - "Address '", addr.toJson(), "' ", "is not a V6 address")); + throw IPAddressFormatException( + sformat("Address '{}' is not a V6 address", addr.toJson())); } 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); +bool IPAddressV6::inSubnetWithMask( + const IPAddressV6& subnet, + const ByteArray16& cidrMask) const { + const auto mask = detail::Bytes::mask(toByteArray(), cidrMask); + const auto subMask = detail::Bytes::mask(subnet.toByteArray(), cidrMask); return (mask == subMask); } @@ -339,11 +357,11 @@ bool IPAddressV6::isLoopback() const { 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); + // 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 { @@ -384,11 +402,24 @@ IPAddressV6 IPAddressV6::getSolicitedNodeAddress() const { // 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]; + uint8_t bytes[16] = { + 0xff, + 0x02, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0xff, + addr_.bytes_[13], + addr_.bytes_[14], + addr_.bytes_[15], + }; return IPAddressV6::fromBinary(ByteRange(bytes, 16)); } @@ -397,7 +428,7 @@ IPAddressV6 IPAddressV6::mask(size_t numBits) const { static const auto bits = bitCount(); if (numBits > bits) { throw IPAddressFormatException( - to("numBits(", numBits, ") > bitCount(", bits, ")")); + sformat("numBits({}) > bitCount({})", numBits, bits)); } ByteArray16 ba = detail::Bytes::mask(fetchMask(numBits), addr_.bytes_); return IPAddressV6(ba); @@ -408,12 +439,9 @@ string IPAddressV6::str() const { char buffer[INET6_ADDRSTRLEN + IFNAMSIZ + 1]; if (!inet_ntop(AF_INET6, toAddr().s6_addr, buffer, INET6_ADDRSTRLEN)) { - throw IPAddressFormatException(to( - "Invalid address with hex ", - "'", + throw IPAddressFormatException(sformat( + "Invalid address with hex '{}' with error {}", detail::Bytes::toHex(bytes(), 16), - "'", - " with error ", strerror(errno))); } @@ -421,17 +449,14 @@ string IPAddressV6::str() const { if (scopeId != 0) { auto len = strlen(buffer); buffer[len] = '%'; + + auto errsv = errno; if (!if_indextoname(scopeId, buffer + len + 1)) { - throw IPAddressFormatException(to( - "Invalid scope for address with hex ", - "'", - detail::Bytes::toHex(bytes(), 16), - "%", - scopeId, - "'", - " with error ", - strerror(errno))); + // if we can't map the if because eg. it no longer exists, + // append the if index instead + snprintf(buffer + len + 1, IFNAMSIZ, "%u", scopeId); } + errno = errsv; } return string(buffer); @@ -464,8 +489,9 @@ string IPAddressV6::toInverseArpaName() const { uint8_t IPAddressV6::getNthMSByte(size_t byteIndex) const { const auto highestIndex = byteCount() - 1; if (byteIndex > highestIndex) { - throw std::invalid_argument(to("Byte index must be <= ", - to(highestIndex), " for addresses of type :", + throw std::invalid_argument(sformat( + "Byte index must be <= {} for addresses of type: {}", + highestIndex, detail::familyNameStr(AF_INET6))); } return bytes()[byteIndex]; @@ -501,9 +527,10 @@ CIDRNetworkV6 IPAddressV6::longestCommonPrefix( } // protected -bool IPAddressV6::inBinarySubnet(const std::array addr, - size_t numBits) const { +bool IPAddressV6::inBinarySubnet( + const std::array addr, + size_t numBits) const { auto masked = mask(numBits); return (std::memcmp(addr.data(), masked.bytes(), 2) == 0); } -} // folly +} // namespace folly