2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
23 #include <utility> // std::pair
25 #include <boost/operators.hpp>
27 #include <folly/Conv.h>
28 #include <folly/Format.h>
29 #include <folly/Range.h>
30 #include <folly/IPAddressException.h>
31 #include <folly/IPAddressV4.h>
32 #include <folly/IPAddressV6.h>
33 #include <folly/detail/IPAddress.h>
40 * Pair of IPAddress, netmask
42 typedef std::pair<IPAddress, uint8_t> CIDRNetwork;
45 * Provides a unified interface for IP addresses.
47 * @note If you compare 2 IPAddress instances, v4-to-v6-mapped addresses are
48 * compared as V4 addresses.
50 * @note toLong/fromLong deal in network byte order, use toLongHBO/fromLongHBO
51 * if working in host byte order.
55 * IPAddress v4addr("192.0.2.129");
56 * IPAddress v6map("::ffff:192.0.2.129");
57 * CHECK(v4addr.inSubnet("192.0.2.0/24") ==
58 * v4addr.inSubnet(IPAddress("192.0.2.0"), 24));
59 * CHECK(v4addr.inSubnet("192.0.2.128/30"));
60 * CHECK(!v4addr.inSubnet("192.0.2.128/32"));
61 * CHECK(v4addr.asV4().toLong() == 2164392128);
62 * CHECK(v4addr.asV4().toLongHBO() == 3221226113);
63 * CHECK(v4addr.isV4());
64 * CHECK(v6addr.isV6());
65 * CHECK(v4addr == v6map);
66 * CHECK(v6map.isIPv4Mapped());
67 * CHECK(v4addr.asV4() == IPAddress::createIPv4(v6map));
68 * CHECK(IPAddress::createIPv6(v4addr) == v6map.asV6());
71 class IPAddress : boost::totally_ordered<IPAddress> {
73 // returns true iff the input string can be parsed as an ip-address
74 static bool validate(StringPiece ip);
76 // return the V4 representation of the address, converting it from V6 to V4 if
77 // needed. Note that this will throw an IPAddressFormatException if the V6
78 // address is not IPv4Mapped.
79 static IPAddressV4 createIPv4(const IPAddress& addr);
81 // return the V6 representation of the address, converting it from V4 to V6 if
83 static IPAddressV6 createIPv6(const IPAddress& addr);
86 * Create a network and mask from a CIDR formatted address string.
87 * @param [in] ipSlashCidr IP/CIDR formatted string to split
88 * @param [in] defaultCidr default value if no /N specified (if defaultCidr
89 * is -1, will use /32 for IPv4 and /128 for IPv6)
90 * @param [in] mask apply mask on the address or not,
91 * e.g. 192.168.13.46/24 => 192.168.13.0/24
92 * @throws IPAddressFormatException if invalid address
93 * @return pair with IPAddress network and uint8_t mask
95 static CIDRNetwork createNetwork(
96 StringPiece ipSlashCidr, int defaultCidr = -1, bool mask = true);
99 * Return a string representation of a CIDR block created with createNetwork.
100 * @param [in] network, pair of address and cidr
102 * @return string representing the netblock
104 static std::string networkToString(const CIDRNetwork& network) {
105 return network.first.str() + "/" + folly::to<std::string>(network.second);
109 * Create a new IPAddress instance from the provided binary data
110 * in network byte order.
111 * @throws IPAddressFormatException if len is not 4 or 16
113 static IPAddress fromBinary(ByteRange bytes);
116 * Create an IPAddress from a 32bit long (network byte order).
117 * @throws IPAddressFormatException
119 static IPAddress fromLong(uint32_t src);
120 // Same as above, but host byte order
121 static IPAddress fromLongHBO(uint32_t src);
123 // Given 2 IPAddress,mask pairs extract the longest common IPAddress,
125 static CIDRNetwork longestCommonPrefix(const CIDRNetwork& one,
126 const CIDRNetwork& two);
129 * Constructs an uninitialized IPAddress.
134 * Parse an IPAddress from a string representation.
136 * Formats accepted are exactly the same as the ones accepted by inet_pton(),
137 * using AF_INET6 if the string contains colons, and AF_INET otherwise;
138 * with the exception that the whole address can optionally be enclosed
139 * in square brackets.
141 * @throws IPAddressFormatException
143 explicit IPAddress(StringPiece ip);
146 * Create an IPAddress from a sockaddr.
147 * @throws IPAddressFormatException if nullptr or not AF_INET or AF_INET6
149 explicit IPAddress(const sockaddr* addr);
151 // Create an IPAddress from a V4 address
152 /* implicit */ IPAddress(const IPAddressV4 ipV4Addr);
153 /* implicit */ IPAddress(const in_addr addr);
155 // Create an IPAddress from a V6 address
156 /* implicit */ IPAddress(const IPAddressV6& ipV6Addr);
157 /* implicit */ IPAddress(const in6_addr& addr);
159 // Assign from V4 address
160 IPAddress& operator=(const IPAddressV4& ipV4Addr);
162 // Assign from V6 address
163 IPAddress& operator=(const IPAddressV6& ipV6Addr);
166 * Converts an IPAddress to an IPAddressV4 instance.
167 * @note This is not some handy convenience wrapper to convert an IPv4 address
168 * to a mapped IPv6 address. If you want that use
169 * IPAddress::createIPv6(addr)
170 * @throws IPAddressFormatException is not a V4 instance
172 const IPAddressV4& asV4() const {
174 auto familyName = detail::familyNameStr(family());
175 throw InvalidAddressFamilyException("Can't convert address with family ",
176 familyName, " to AF_INET address");
178 return addr_.ipV4Addr;
182 * Converts an IPAddress to an IPAddressV6 instance.
183 * @throws InvalidAddressFamilyException is not a V6 instance
185 const IPAddressV6& asV6() const {
187 auto familyName = detail::familyNameStr(family());
188 throw InvalidAddressFamilyException("Can't convert address with family ",
189 familyName, " to AF_INET6 address");
191 return addr_.ipV6Addr;
194 // Return sa_family_t of IPAddress
195 sa_family_t family() const { return family_; }
197 // Populate sockaddr_storage with an appropriate value
198 int toSockaddrStorage(sockaddr_storage *dest, uint16_t port = 0) const {
199 if (dest == nullptr) {
200 throw IPAddressFormatException("dest must not be null");
202 memset(dest, 0, sizeof(sockaddr_storage));
203 dest->ss_family = family();
205 sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(dest);
206 sin->sin_addr = asV4().toAddr();
207 sin->sin_port = port;
210 sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(dest);
211 sin->sin6_addr = asV6().toAddr();
212 sin->sin6_port = port;
213 sin->sin6_scope_id = asV6().getScopeId();
216 throw InvalidAddressFamilyException(family());
221 * Check if the address is found in the specified CIDR netblock.
223 * This will return false if the specified cidrNet is V4, but the address is
224 * V6. It will also return false if the specified cidrNet is V6 but the
225 * address is V4. This method will do the right thing in the case of a v6
228 * @note This is slower than the below counterparts. If perf is important use
229 * one of the two argument variations below.
230 * @param [in] ipSlashCidr address in "192.168.1.0/24" format
231 * @throws IPAddressFormatException if no /mask
232 * @return true if address is part of specified subnet with cidr
234 bool inSubnet(StringPiece ipSlashCidr) const;
237 * Check if an IPAddress belongs to a subnet.
238 * @param [in] subnet Subnet to check against (e.g. 192.168.1.0)
239 * @param [in] cidr CIDR for subnet (e.g. 24 for /24)
240 * @return true if address is part of specified subnet with cidr
242 bool inSubnet(const IPAddress& subnet, uint8_t cidr) const;
245 * Check if an IPAddress belongs to the subnet with the given mask.
246 * This is the same as inSubnet but the mask is provided instead of looked up
248 * @param [in] subnet Subnet to check against
249 * @param [in] mask The netmask for the subnet
250 * @return true if address is part of the specified subnet with mask
252 bool inSubnetWithMask(const IPAddress& subnet, ByteRange mask) const;
254 // @return true if address is a v4 mapped address
255 bool isIPv4Mapped() const {
256 return isV6() && asV6().isIPv4Mapped();
259 // @return true if address is uninitialized
260 bool empty() const { return (family_ == AF_UNSPEC); }
262 // @return true if address is initialized
263 explicit operator bool() const { return !empty(); }
265 // @return true if this is an IPAddressV4 instance
266 bool isV4() const { return (family_ == AF_INET); }
268 // @return true if this is an IPAddressV6 instance
269 bool isV6() const { return (family_ == AF_INET6); }
271 // @return true if this address is all zeros
272 bool isZero() const {
273 return isV4() ? asV4().isZero()
277 // Number of bits in the address representation.
278 size_t bitCount() const {
279 return isV4() ? IPAddressV4::bitCount()
280 : IPAddressV6::bitCount();
282 // Number of bytes in the address representation.
283 size_t byteCount() const {
284 return bitCount() / 8;
286 //get nth most significant bit - 0 indexed
287 bool getNthMSBit(size_t bitIndex) const {
288 return detail::getNthMSBitImpl(*this, bitIndex, family());
290 //get nth most significant byte - 0 indexed
291 uint8_t getNthMSByte(size_t byteIndex) const;
292 //get nth bit - 0 indexed
293 bool getNthLSBit(size_t bitIndex) const {
294 return getNthMSBit(bitCount() - bitIndex - 1);
296 //get nth byte - 0 indexed
297 uint8_t getNthLSByte(size_t byteIndex) const {
298 return getNthMSByte(byteCount() - byteIndex - 1);
301 * Get human-readable string representation of the address.
303 * This prints a string representation of the address, for human consumption
304 * or logging. The string will take the form of a JSON object that looks like:
305 * {family:'AF_INET|AF_INET6', addr:'address', hash:long}.
307 std::string toJson() const {
308 return isV4() ? asV4().toJson()
313 std::size_t hash() const {
314 return isV4() ? asV4().hash()
318 // Return true if the address qualifies as localhost.
319 bool isLoopback() const {
320 return isV4() ? asV4().isLoopback()
321 : asV6().isLoopback();
324 // Return true if the address qualifies as link local
325 bool isLinkLocal() const {
326 return isV4() ? asV4().isLinkLocal()
327 : asV6().isLinkLocal();
330 // Return true if the address qualifies as broadcast.
331 bool isLinkLocalBroadcast() const {
332 return isV4() ? asV4().isLinkLocalBroadcast()
333 : asV6().isLinkLocalBroadcast();
337 * Return true if the address is a special purpose address, as per rfc6890
339 * For V6, true if the address is not in one of global scope blocks:
340 * 2000::/3, ffxe::/16.
342 bool isNonroutable() const {
343 return isV4() ? asV4().isNonroutable()
344 : asV6().isNonroutable();
348 * Return true if the address is private, as per rfc1918 and rfc4193
349 * (for example, 192.168.xxx.xxx or fc00::/7 addresses)
351 bool isPrivate() const {
352 return isV4() ? asV4().isPrivate()
353 : asV6().isPrivate();
356 // Return true if the address is a multicast address.
357 bool isMulticast() const {
358 return isV4() ? asV4().isMulticast()
359 : asV6().isMulticast();
363 * Creates IPAddress instance with all but most significant numBits set to 0.
364 * @param [in] numBits number of bits to mask
365 * @throws abort if numBits > bitCount()
366 * @return IPAddress instance with bits set to 0
368 IPAddress mask(uint8_t numBits) const {
369 return isV4() ? IPAddress(asV4().mask(numBits))
370 : IPAddress(asV6().mask(numBits));
374 * Provides a string representation of address.
375 * @note The string representation is calculated on demand.
376 * @throws IPAddressFormatException on inet_ntop error
378 std::string str() const {
379 return isV4() ? asV4().str()
384 * Return the fully qualified string representation of the address.
385 * For V4 addresses this is the same as calling str(). For V6 addresses
386 * this is the hex representation with : characters inserted every 4 digits.
388 std::string toFullyQualified() const {
389 return isV4() ? asV4().toFullyQualified()
390 : asV6().toFullyQualified();
393 // Address version (4 or 6)
394 uint8_t version() const {
395 return isV4() ? asV4().version()
400 * Access to address bytes, in network byte order.
402 const unsigned char* bytes() const {
403 return isV4() ? asV4().bytes() : asV6().bytes();
407 typedef union IPAddressV46 {
408 IPAddressV4 ipV4Addr;
409 IPAddressV6 ipV6Addr;
410 // default constructor
412 std::memset(this, 0, sizeof(IPAddressV46));
414 explicit IPAddressV46(const IPAddressV4& addr): ipV4Addr(addr) {}
415 explicit IPAddressV46(const IPAddressV6& addr): ipV6Addr(addr) {}
421 // boost::hash uses hash_value() so this allows boost::hash to work
422 // automatically for IPAddress
423 std::size_t hash_value(const IPAddress& addr);
424 std::ostream& operator<<(std::ostream& os, const IPAddress& addr);
425 // Define toAppend() to allow IPAddress to be used with folly::to<string>
426 void toAppend(IPAddress addr, std::string* result);
427 void toAppend(IPAddress addr, fbstring* result);
430 * Return true if two addresses are equal.
432 * @note This takes into consideration V4 mapped addresses as well. If one
433 * address is v4 mapped we compare the v4 addresses.
435 * @return true if the two addresses are equal.
437 bool operator==(const IPAddress& addr1, const IPAddress& addr2);
438 // Return true if addr1 < addr2
439 bool operator<(const IPAddress& addr1, const IPAddress& addr2);
445 struct hash<folly::IPAddress> {
446 size_t operator()(const folly::IPAddress& addr) const {