/* * Copyright (c) 2013-2024, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ #ifndef ROUTER_INFO_H__ #define ROUTER_INFO_H__ #include #include #include #include #include #include #include #include #include "Identity.h" #include "Profiling.h" #include "Family.h" namespace i2p { namespace data { const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets"; const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters"; const char ROUTER_INFO_PROPERTY_NETID[] = "netId"; const char ROUTER_INFO_PROPERTY_VERSION[] = "router.version"; const char ROUTER_INFO_PROPERTY_FAMILY[] = "family"; const char ROUTER_INFO_PROPERTY_FAMILY_SIG[] = "family.sig"; const char CAPS_FLAG_FLOODFILL = 'f'; const char CAPS_FLAG_HIDDEN = 'H'; const char CAPS_FLAG_REACHABLE = 'R'; const char CAPS_FLAG_UNREACHABLE = 'U'; /* bandwidth flags */ const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K'; /* < 12 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L'; /* 12-48 KBps */ const char CAPS_FLAG_LOW_BANDWIDTH3 = 'M'; /* 48-64 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'N'; /* 64-128 KBps */ const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'O'; /* 128-256 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH1 = 'P'; /* 256-2048 KBps */ const char CAPS_FLAG_EXTRA_BANDWIDTH2 = 'X'; /* > 2048 KBps */ // bandwidth limits in kBps const uint32_t LOW_BANDWIDTH_LIMIT = 48; const uint32_t HIGH_BANDWIDTH_LIMIT = 256; const uint32_t EXTRA_BANDWIDTH_LIMIT = 2048; // congesion flags const char CAPS_FLAG_MEDIUM_CONGESTION = 'D'; const char CAPS_FLAG_HIGH_CONGESTION = 'E'; const char CAPS_FLAG_REJECT_ALL_CONGESTION = 'G'; const char CAPS_FLAG_V4 = '4'; const char CAPS_FLAG_V6 = '6'; const char CAPS_FLAG_SSU2_TESTING = 'B'; const char CAPS_FLAG_SSU2_INTRODUCER = 'C'; const uint8_t COST_NTCP2_PUBLISHED = 3; const uint8_t COST_NTCP2_NON_PUBLISHED = 14; const uint8_t COST_SSU2_DIRECT = 8; const uint8_t COST_SSU2_NON_PUBLISHED = 15; const size_t MAX_RI_BUFFER_SIZE = 3072; // if RouterInfo exceeds 3K we consider it as malformed, might extend later const int HIGH_CONGESTION_INTERVAL = 15*60; // in seconds, 15 minutes const int INTRODUCER_UPDATE_INTERVAL = 20*60*1000; // in milliseconds, 20 minutes class RouterInfo: public RoutingDestination { public: enum SupportedTransportsIdx { eNTCP2V4Idx = 0, eNTCP2V6Idx, eSSU2V4Idx, eSSU2V6Idx, eNTCP2V6MeshIdx, eNumTransports }; #define TransportBit(tr) e##tr = (1 << e##tr##Idx) enum SupportedTransports { TransportBit(NTCP2V4), // 0x01 TransportBit(NTCP2V6), // 0x02 TransportBit(SSU2V4), // 0x04 TransportBit(SSU2V6), // 0x08 TransportBit(NTCP2V6Mesh), // 0x10 eAllTransports = 0xFF }; typedef uint8_t CompatibleTransports; enum Caps { eFloodfill = 0x01, eHighBandwidth = 0x02, eExtraBandwidth = 0x04, eReachable = 0x08, eHidden = 0x10, eUnreachable = 0x20 }; enum Congestion { eLowCongestion = 0, eMediumCongestion, eHighCongestion, eRejectAll }; enum AddressCaps { eV4 = 0x01, eV6 = 0x02, eSSUTesting = 0x04, eSSUIntroducer = 0x08 }; enum TransportStyle { eTransportUnknown = 0, eTransportNTCP2, eTransportSSU2 }; struct Introducer { Introducer (): iTag (0), iExp (0) { iH.Fill(0); }; IdentHash iH; uint32_t iTag; uint32_t iExp; }; struct SSUExt { int mtu; std::vector introducers; }; struct Address { TransportStyle transportStyle; boost::asio::ip::address host; Tag<32> s, i; // keys, i is first 16 bytes for NTCP2 and 32 bytes intro key for SSU int port; uint64_t date; uint8_t caps; bool published = false; std::unique_ptr ssu; // not null for SSU bool IsCompatible (const boost::asio::ip::address& other) const { return (IsV4 () && other.is_v4 ()) || (IsV6 () && other.is_v6 ()); } bool operator==(const Address& other) const { return transportStyle == other.transportStyle && host == other.host && port == other.port; } bool operator!=(const Address& other) const { return !(*this == other); } bool IsNTCP2 () const { return transportStyle == eTransportNTCP2; }; bool IsSSU2 () const { return transportStyle == eTransportSSU2; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; }; bool IsReachableSSU () const { return (bool)ssu && (published || UsesIntroducer ()); }; bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); }; bool IsIntroducer () const { return caps & eSSUIntroducer; }; bool IsPeerTesting () const { return caps & eSSUTesting; }; bool IsV4 () const { return (caps & AddressCaps::eV4) || (host.is_v4 () && !host.is_unspecified ()); }; bool IsV6 () const { return (caps & AddressCaps::eV6) || (host.is_v6 () && !host.is_unspecified ()); }; }; class Buffer: public std::array { public: Buffer () = default; Buffer (const uint8_t * buf, size_t len); }; typedef std::array, eNumTransports> Addresses; RouterInfo (const std::string& fullPath); RouterInfo (const RouterInfo& ) = default; RouterInfo& operator=(const RouterInfo& ) = default; RouterInfo (std::shared_ptr&& buf, size_t len); RouterInfo (const uint8_t * buf, size_t len); virtual ~RouterInfo (); std::shared_ptr GetRouterIdentity () const { return m_RouterIdentity; }; void SetRouterIdentity (std::shared_ptr identity); std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); }; uint64_t GetTimestamp () const { return m_Timestamp; }; int GetVersion () const { return m_Version; }; virtual void SetProperty (const std::string& key, const std::string& value) {}; virtual void ClearProperties () {}; boost::shared_ptr GetAddresses () const; // should be called for local RI only, otherwise must return shared_ptr std::shared_ptr GetNTCP2V4Address () const; std::shared_ptr GetNTCP2V6Address () const; std::shared_ptr GetPublishedNTCP2V4Address () const; std::shared_ptr GetPublishedNTCP2V6Address () const; std::shared_ptr GetYggdrasilAddress () const; std::shared_ptr GetSSU2V4Address () const; std::shared_ptr GetSSU2V6Address () const; std::shared_ptr GetSSU2Address (bool v4) const; void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,int port, uint8_t caps); // non published void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port); // published void RemoveNTCP2Address (bool v4); void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, int port, uint8_t caps); // non published void AddSSU2Address (const uint8_t * staticKey, const uint8_t * introKey, const boost::asio::ip::address& host, int port); // published void RemoveSSU2Address (bool v4); void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps void UpdateSupportedTransports (); void UpdateIntroducers (uint64_t ts); // ts in seconds bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; void ResetFlooldFill () { m_Caps &= ~Caps::eFloodfill; }; bool IsECIES () const { return m_RouterIdentity->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; bool IsNTCP2 (bool v4only = true) const; bool IsNTCP2V6 () const { return m_SupportedTransports & eNTCP2V6; }; bool IsSSU2V4 () const { return m_SupportedTransports & eSSU2V4; }; bool IsSSU2V6 () const { return m_SupportedTransports & eSSU2V6; }; bool IsV6 () const { return m_SupportedTransports & (eNTCP2V6 | eSSU2V6); }; bool IsV4 () const { return m_SupportedTransports & (eNTCP2V4 | eSSU2V4); }; bool IsMesh () const { return m_SupportedTransports & eNTCP2V6Mesh; }; void EnableV6 (); void DisableV6 (); void EnableV4 (); void DisableV4 (); void EnableMesh (); void DisableMesh (); bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; }; bool IsReachableFrom (const RouterInfo& other) const { return m_ReachableTransports & other.m_SupportedTransports; }; bool IsReachableBy (CompatibleTransports transports) const { return m_ReachableTransports & transports; }; CompatibleTransports GetCompatibleTransports (bool incoming) const { return incoming ? m_ReachableTransports : m_SupportedTransports; }; bool HasValidAddresses () const { return m_SupportedTransports; }; bool IsHidden () const { return m_Caps & eHidden; }; bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; }; bool IsExtraBandwidth () const { return m_Caps & RouterInfo::eExtraBandwidth; }; bool IsEligibleFloodfill () const; bool IsPublished (bool v4) const; bool IsSSU2PeerTesting (bool v4) const; bool IsSSU2Introducer (bool v4) const; bool IsHighCongestion (bool highBandwidth) const; uint8_t GetCaps () const { return m_Caps; }; char GetBandwidthCap() const { return m_BandwidthCap; }; void SetCaps (uint8_t caps) { m_Caps = caps; }; Congestion GetCongestion () const { return m_Congestion; }; void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; }; bool IsUnreachable () const { return m_IsUnreachable; }; void ExcludeReachableTransports (CompatibleTransports transports) { m_ReachableTransports &= ~transports; }; const uint8_t * GetBuffer () const { return m_Buffer ? m_Buffer->data () : nullptr; }; const uint8_t * LoadBuffer (const std::string& fullPath); // load if necessary size_t GetBufferLen () const { return m_BufferLen; }; bool IsUpdated () const { return m_IsUpdated; }; void SetUpdated (bool updated) { m_IsUpdated = updated; }; bool SaveToFile (const std::string& fullPath); std::shared_ptr GetProfile () const; void DropProfile () { m_Profile = nullptr; }; bool Update (const uint8_t * buf, size_t len); void DeleteBuffer () { m_Buffer = nullptr; }; bool IsNewer (const uint8_t * buf, size_t len) const; /** return true if we are in a router family and the signature is valid */ bool IsFamily (FamilyID famid) const; // implements RoutingDestination std::shared_ptr GetIdentity () const { return m_RouterIdentity; }; void Encrypt (const uint8_t * data, uint8_t * encrypted) const; bool IsDestination () const { return false; }; protected: RouterInfo (); uint8_t * GetBufferPointer (size_t offset = 0 ) { return m_Buffer->data () + offset; }; void UpdateBuffer (const uint8_t * buf, size_t len); void SetBufferLen (size_t len) { m_BufferLen = len; }; void RefreshTimestamp (); CompatibleTransports GetReachableTransports () const { return m_ReachableTransports; }; void SetReachableTransports (CompatibleTransports transports) { m_ReachableTransports = transports; }; void SetCongestion (Congestion c) { m_Congestion = c; }; private: bool LoadFile (const std::string& fullPath); void ReadFromFile (const std::string& fullPath); void ReadFromStream (std::istream& s); void ReadFromBuffer (bool verifySignature); size_t ReadString (char* str, size_t len, std::istream& s) const; void ExtractCaps (const char * value); uint8_t ExtractAddressCaps (const char * value) const; void UpdateIntroducers (std::shared_ptr
address, uint64_t ts); template std::shared_ptr GetAddress (Filter filter) const; virtual std::shared_ptr NewBuffer () const; virtual std::shared_ptr
NewAddress () const; virtual boost::shared_ptr NewAddresses () const; virtual std::shared_ptr NewIdentity (const uint8_t * buf, size_t len) const; private: FamilyID m_FamilyID; std::shared_ptr m_RouterIdentity; std::shared_ptr m_Buffer; size_t m_BufferLen; uint64_t m_Timestamp; // in milliseconds boost::shared_ptr m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 bool m_IsUpdated, m_IsUnreachable; CompatibleTransports m_SupportedTransports, m_ReachableTransports; uint8_t m_Caps; char m_BandwidthCap; int m_Version; Congestion m_Congestion; mutable std::shared_ptr m_Profile; }; class LocalRouterInfo: public RouterInfo { public: LocalRouterInfo () = default; void CreateBuffer (const PrivateKeys& privateKeys); void UpdateCaps (uint8_t caps); bool UpdateCongestion (Congestion c); // returns true if updated void SetProperty (const std::string& key, const std::string& value) override; void DeleteProperty (const std::string& key); std::string GetProperty (const std::string& key) const; void ClearProperties () override { m_Properties.clear (); }; bool AddSSU2Introducer (const Introducer& introducer, bool v4); bool RemoveSSU2Introducer (const IdentHash& h, bool v4); private: void WriteToStream (std::ostream& s) const; void UpdateCapsProperty (); void WriteString (const std::string& str, std::ostream& s) const; std::shared_ptr NewBuffer () const override; std::shared_ptr
NewAddress () const override; boost::shared_ptr NewAddresses () const override; std::shared_ptr NewIdentity (const uint8_t * buf, size_t len) const override; private: std::map m_Properties; }; } } #endif