FlatCombining: Use SaturatingSemaphore instead of multi-poster and non-blocking Baton
[folly.git] / folly / IPAddressV6.h
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #pragma once
18
19 #include <cstring>
20
21 #include <array>
22 #include <functional>
23 #include <iosfwd>
24 #include <map>
25 #include <stdexcept>
26
27 #include <folly/Expected.h>
28 #include <folly/FBString.h>
29 #include <folly/IPAddressException.h>
30 #include <folly/Optional.h>
31 #include <folly/Range.h>
32 #include <folly/detail/IPAddress.h>
33 #include <folly/hash/Hash.h>
34
35 namespace folly {
36
37 class IPAddress;
38 class IPAddressV4;
39 class IPAddressV6;
40 class MacAddress;
41
42 /**
43  * Pair of IPAddressV6, netmask
44  */
45 typedef std::pair<IPAddressV6, uint8_t> CIDRNetworkV6;
46
47 /**
48  * Specialization for IPv6 addresses
49  */
50 typedef std::array<uint8_t, 16> ByteArray16;
51
52 /**
53  * IPv6 variation of IPAddress.
54  *
55  * Added methods: createIPv4, getIPv4For6To4, is6To4,
56  *                isTeredo, isIPv4Mapped, tryCreateIPv4, type
57  *
58  * @see IPAddress
59  *
60  * Notes on scope ID parsing:
61  *
62  * getaddrinfo() uses if_nametoindex() to convert interface names
63  * into a numerical index. For instance,
64  * "fe80::202:c9ff:fec1:ee08%eth0" may return scope ID 2 on some
65  * hosts, but other numbers on other hosts. It will fail entirely on
66  * hosts without an eth0 interface.
67  *
68  * Serializing / Deserializing IPAddressB6's on different hosts
69  * that use link-local scoping probably won't work.
70  */
71 class IPAddressV6 {
72  public:
73   // V6 Address Type
74   enum Type {
75     TEREDO,
76     T6TO4,
77     NORMAL,
78   };
79   // A constructor parameter to indicate that we should create a link-local
80   // IPAddressV6.
81   enum LinkLocalTag {
82     LINK_LOCAL,
83   };
84   // Thrown when a type assertion fails
85   typedef std::runtime_error TypeError;
86
87   // Binary prefix for teredo networks
88   static const uint32_t PREFIX_TEREDO;
89   // Binary prefix for 6to4 networks
90   static const uint32_t PREFIX_6TO4;
91
92   // Size of std::string returned by toFullyQualified.
93   static constexpr size_t kToFullyQualifiedSize =
94       8 /*words*/ * 4 /*hex chars per word*/ + 7 /*separators*/;
95
96   // returns true iff the input string can be parsed as an ipv6-address
97   static bool validate(StringPiece ip) noexcept;
98
99   /**
100    * Create a new IPAddress instance from the provided binary data.
101    * @throws IPAddressFormatException if the input length is not 16 bytes.
102    */
103   static IPAddressV6 fromBinary(ByteRange bytes);
104
105   /**
106    * Non-throwing version of fromBinary().
107    * On failure returns IPAddressFormatError.
108    */
109   static Expected<IPAddressV6, IPAddressFormatError> tryFromBinary(
110       ByteRange bytes) noexcept;
111
112   /**
113    * Tries to create a new IPAddressV6 instance from provided string and
114    * returns it on success. Returns IPAddressFormatError on failure.
115    */
116   static Expected<IPAddressV6, IPAddressFormatError> tryFromString(
117       StringPiece str) noexcept;
118
119   /**
120    * Create a new IPAddress instance from the ip6.arpa representation.
121    * @throws IPAddressFormatException if the input is not a valid ip6.arpa
122    * representation
123    */
124   static IPAddressV6 fromInverseArpaName(const std::string& arpaname);
125
126   /**
127    * Returns the address as a Range.
128    */
129   ByteRange toBinary() const {
130     return ByteRange((const unsigned char*)&addr_.in6Addr_.s6_addr, 16);
131   }
132
133   /**
134    * Default constructor for IPAddressV6.
135    *
136    * The address value will be ::0
137    */
138   IPAddressV6();
139
140   // Create an IPAddressV6 from a string
141   // @throws IPAddressFormatException
142   //
143   explicit IPAddressV6(StringPiece ip);
144
145   // ByteArray16 constructor
146   explicit IPAddressV6(const ByteArray16& src) noexcept;
147
148   // in6_addr constructor
149   explicit IPAddressV6(const in6_addr& src) noexcept;
150
151   // sockaddr_in6 constructor
152   explicit IPAddressV6(const sockaddr_in6& src) noexcept;
153
154   /**
155    * Create a link-local IPAddressV6 from the specified ethernet MAC address.
156    */
157   IPAddressV6(LinkLocalTag tag, MacAddress mac);
158
159   // return the mapped V4 address
160   // @throws IPAddressFormatException if !isIPv4Mapped
161   IPAddressV4 createIPv4() const;
162
163   /**
164    * Return a V4 address if this is a 6To4 address.
165    * @throws TypeError if not a 6To4 address
166    */
167   IPAddressV4 getIPv4For6To4() const;
168
169   // Return true if a 6TO4 address
170   bool is6To4() const {
171     return type() == IPAddressV6::Type::T6TO4;
172   }
173
174   // Return true if a TEREDO address
175   bool isTeredo() const {
176     return type() == IPAddressV6::Type::TEREDO;
177   }
178
179   // return true if this is v4-to-v6-mapped
180   bool isIPv4Mapped() const;
181
182   // Return the V6 address type
183   Type type() const;
184
185   /**
186    * @see IPAddress#bitCount
187    * @returns 128
188    */
189   static constexpr size_t bitCount() {
190     return 128;
191   }
192
193   /**
194    * @see IPAddress#toJson
195    */
196   std::string toJson() const;
197
198   size_t hash() const;
199
200   // @see IPAddress#inSubnet
201   // @throws IPAddressFormatException if string doesn't contain a V6 address
202   bool inSubnet(StringPiece cidrNetwork) const;
203
204   // return true if address is in subnet
205   bool inSubnet(const IPAddressV6& subnet, uint8_t cidr) const {
206     return inSubnetWithMask(subnet, fetchMask(cidr));
207   }
208   bool inSubnetWithMask(const IPAddressV6& subnet, const ByteArray16& mask)
209       const;
210
211   // @see IPAddress#isLoopback
212   bool isLoopback() const;
213
214   // @see IPAddress#isNonroutable
215   bool isNonroutable() const {
216     return !isRoutable();
217   }
218
219   /**
220    * Return true if this address is routable.
221    */
222   bool isRoutable() const;
223
224   // @see IPAddress#isPrivate
225   bool isPrivate() const;
226
227   /**
228    * Return true if this is a link-local IPv6 address.
229    *
230    * Note that this only returns true for addresses in the fe80::/10 range.
231    * It returns false for the loopback address (::1), even though this address
232    * is also effectively has link-local scope.  It also returns false for
233    * link-scope and interface-scope multicast addresses.
234    */
235   bool isLinkLocal() const;
236
237   /**
238    * Return the mac address if this is a link-local IPv6 address.
239    *
240    * @return an Optional<MacAddress> union representing the mac address.
241    *
242    * If the address is not a link-local one it will return an empty Optional.
243    * You can use Optional::value() to check whether the mac address is not null.
244    */
245   Optional<MacAddress> getMacAddressFromLinkLocal() const;
246
247   /**
248    * Return the mac address if this is an auto-configured IPv6 address based on
249    * EUI-64
250    *
251    * @return an Optional<MacAddress> union representing the mac address.
252    * If the address is not based on EUI-64 it will return an empty Optional.
253    * You can use Optional::value() to check whether the mac address is not null.
254    */
255   Optional<MacAddress> getMacAddressFromEUI64() const;
256
257   /**
258    * Return true if this is a multicast address.
259    */
260   bool isMulticast() const;
261
262   /**
263    * Return the flags for a multicast address.
264    * This method may only be called on multicast addresses.
265    */
266   uint8_t getMulticastFlags() const;
267
268   /**
269    * Return the scope for a multicast address.
270    * This method may only be called on multicast addresses.
271    */
272   uint8_t getMulticastScope() const;
273
274   // @see IPAddress#isZero
275   bool isZero() const {
276     constexpr auto zero = ByteArray16{{}};
277     return 0 == std::memcmp(bytes(), zero.data(), zero.size());
278   }
279
280   bool isLinkLocalBroadcast() const;
281
282   // @see IPAddress#mask
283   IPAddressV6 mask(size_t numBits) const;
284
285   // return underlying in6_addr structure
286   in6_addr toAddr() const {
287     return addr_.in6Addr_;
288   }
289
290   uint16_t getScopeId() const {
291     return scope_;
292   }
293   void setScopeId(uint16_t scope) {
294     scope_ = scope;
295   }
296
297   sockaddr_in6 toSockAddr() const {
298     sockaddr_in6 addr;
299     memset(&addr, 0, sizeof(sockaddr_in6));
300     addr.sin6_family = AF_INET6;
301     addr.sin6_scope_id = scope_;
302     memcpy(&addr.sin6_addr, &addr_.in6Addr_, sizeof(in6_addr));
303     return addr;
304   }
305
306   ByteArray16 toByteArray() const {
307     ByteArray16 ba{{0}};
308     std::memcpy(ba.data(), bytes(), 16);
309     return ba;
310   }
311
312   // @see IPAddress#toFullyQualified
313   std::string toFullyQualified() const;
314
315   // @see IPAddress#toFullyQualifiedAppend
316   void toFullyQualifiedAppend(std::string& out) const;
317
318   std::string toInverseArpaName() const;
319
320   // @see IPAddress#str
321   std::string str() const;
322
323   // @see IPAddress#version
324   uint8_t version() const {
325     return 6;
326   }
327
328   /**
329    * Return the solicited-node multicast address for this address.
330    */
331   IPAddressV6 getSolicitedNodeAddress() const;
332
333   /**
334    * Return the mask associated with the given number of bits.
335    * If for instance numBits was 24 (e.g. /24) then the V4 mask returned should
336    * be {0xff, 0xff, 0xff, 0x00}.
337    * @param [in] numBits bitmask to retrieve
338    * @throws abort if numBits == 0 or numBits > bitCount()
339    * @return mask associated with numBits
340    */
341   static const ByteArray16 fetchMask(size_t numBits);
342   // Given 2 IPAddressV6,mask pairs extract the longest common IPAddress,
343   // mask pair
344   static CIDRNetworkV6 longestCommonPrefix(
345       const CIDRNetworkV6& one,
346       const CIDRNetworkV6& two);
347   // Number of bytes in the address representation.
348   static constexpr size_t byteCount() {
349     return 16;
350   }
351
352   // get nth most significant bit - 0 indexed
353   bool getNthMSBit(size_t bitIndex) const {
354     return detail::getNthMSBitImpl(*this, bitIndex, AF_INET6);
355   }
356   // get nth most significant byte - 0 indexed
357   uint8_t getNthMSByte(size_t byteIndex) const;
358   // get nth bit - 0 indexed
359   bool getNthLSBit(size_t bitIndex) const {
360     return getNthMSBit(bitCount() - bitIndex - 1);
361   }
362   // get nth byte - 0 indexed
363   uint8_t getNthLSByte(size_t byteIndex) const {
364     return getNthMSByte(byteCount() - byteIndex - 1);
365   }
366
367   const unsigned char* bytes() const {
368     return addr_.in6Addr_.s6_addr;
369   }
370
371  protected:
372   /**
373    * Helper that returns true if the address is in the binary subnet specified
374    * by addr.
375    */
376   bool inBinarySubnet(const std::array<uint8_t, 2> addr, size_t numBits) const;
377
378  private:
379   auto tie() const {
380     return std::tie(addr_.bytes_, scope_);
381   }
382
383  public:
384   friend inline bool operator==(const IPAddressV6& a, const IPAddressV6& b) {
385     return a.tie() == b.tie();
386   }
387   friend inline bool operator!=(const IPAddressV6& a, const IPAddressV6& b) {
388     return a.tie() != b.tie();
389   }
390   friend inline bool operator<(const IPAddressV6& a, const IPAddressV6& b) {
391     return a.tie() < b.tie();
392   }
393   friend inline bool operator>(const IPAddressV6& a, const IPAddressV6& b) {
394     return a.tie() > b.tie();
395   }
396   friend inline bool operator<=(const IPAddressV6& a, const IPAddressV6& b) {
397     return a.tie() <= b.tie();
398   }
399   friend inline bool operator>=(const IPAddressV6& a, const IPAddressV6& b) {
400     return a.tie() >= b.tie();
401   }
402
403  private:
404   union AddressStorage {
405     in6_addr in6Addr_;
406     ByteArray16 bytes_;
407     AddressStorage() {
408       std::memset(this, 0, sizeof(AddressStorage));
409     }
410     explicit AddressStorage(const ByteArray16& bytes) : bytes_(bytes) {}
411     explicit AddressStorage(const in6_addr& addr) : in6Addr_(addr) {}
412     explicit AddressStorage(MacAddress mac);
413   } addr_;
414
415   // Link-local scope id.  This should always be 0 for IPAddresses that
416   // are *not* link-local.
417   uint16_t scope_{0};
418
419   /**
420    * Set the current IPAddressV6 object to have the address specified by bytes.
421    * Returns IPAddressFormatError if bytes.size() is not 16.
422    */
423   Expected<Unit, IPAddressFormatError> trySetFromBinary(
424       ByteRange bytes) noexcept;
425 };
426
427 // boost::hash uses hash_value() so this allows boost::hash to work
428 // automatically for IPAddressV6
429 std::size_t hash_value(const IPAddressV6& addr);
430 std::ostream& operator<<(std::ostream& os, const IPAddressV6& addr);
431 // Define toAppend() to allow IPAddressV6 to be used with to<string>
432 void toAppend(IPAddressV6 addr, std::string* result);
433 void toAppend(IPAddressV6 addr, fbstring* result);
434
435 } // namespace folly
436
437 namespace std {
438 template <>
439 struct hash<folly::IPAddressV6> {
440   size_t operator()(const folly::IPAddressV6& addr) const {
441     return addr.hash();
442   }
443 };
444 } // namespace std