Expect.hpp 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #ifndef ZT_EXPECT_HPP
  14. #define ZT_EXPECT_HPP
  15. #include "Constants.hpp"
  16. #include "Utils.hpp"
  17. /**
  18. * Number of buckets to use to maintain a list of expected replies.
  19. *
  20. * More buckets means less chance of two packets tagging the same
  21. * bucket. This doesn't actually hurt anything since this class
  22. * behaves like a bloom filter: you can have false positives but
  23. * not false negatives.
  24. *
  25. * OKs are also cryptographically authenticated, so this is not a
  26. * huge problem, but this helps harden the system against replay
  27. * attacks for e.g. denial of service.
  28. */
  29. #define ZT_EXPECT_BUCKETS 131072
  30. /**
  31. * 1/2 the TTL for expected replies in milliseconds
  32. *
  33. * Making this a power of two improves efficiency a little by allowing bit
  34. * shift division.
  35. */
  36. #define ZT_EXPECT_TTL 4096LL
  37. namespace ZeroTier {
  38. /**
  39. * Tracker for expected OK replies to packet IDs of sent packets
  40. */
  41. class Expect
  42. {
  43. public:
  44. ZT_INLINE Expect() : _salt(Utils::getSecureRandomU64()) {}
  45. /**
  46. * Called by other code when something is sending a packet that may receive an OK response
  47. *
  48. * @param packetId Packet ID of packet being sent (be sure it's post-armor())
  49. * @param now Current time
  50. */
  51. ZT_INLINE void sending(const uint64_t packetId,const int64_t now) noexcept
  52. {
  53. _packetIdSent[Utils::hash64(packetId ^ _salt) % ZT_EXPECT_BUCKETS].store((int32_t)(now / ZT_EXPECT_TTL));
  54. }
  55. /**
  56. * Check whether an OK is expected for this packet
  57. *
  58. * @param inRePacketId
  59. * @param now
  60. * @return
  61. */
  62. ZT_INLINE bool expecting(const uint64_t inRePacketId,const int64_t now) const noexcept
  63. {
  64. return (((now / ZT_EXPECT_TTL) - (int64_t)_packetIdSent[Utils::hash64(inRePacketId ^ _salt) % ZT_EXPECT_BUCKETS].load()) <= 1);
  65. }
  66. private:
  67. // This is a static per-runtime salt that's XORed and mixed with the packet ID
  68. // to make it difficult for a third party to predict expected-reply buckets.
  69. // Such prediction would not be catastrophic but it's easy and good to harden
  70. // against it.
  71. const uint64_t _salt;
  72. // Each bucket contains a timestamp in units of the expect duration.
  73. std::atomic<int32_t> _packetIdSent[ZT_EXPECT_BUCKETS];
  74. };
  75. } // namespace ZeroTier
  76. #endif