1 /*
2 * Copyright (c) 2013-2021, The PurpleI2P Project
3 *
4 * This file is part of Purple i2pd project and licensed under BSD3
5 *
6 * See full license text in LICENSE file at top of project tree
7 */
8 
9 #ifndef TUNNEL_H__
10 #define TUNNEL_H__
11 
12 #include <inttypes.h>
13 #include <map>
14 #include <unordered_map>
15 #include <list>
16 #include <vector>
17 #include <string>
18 #include <thread>
19 #include <mutex>
20 #include <memory>
21 #include "util.h"
22 #include "Queue.h"
23 #include "Crypto.h"
24 #include "TunnelConfig.h"
25 #include "TunnelPool.h"
26 #include "TransitTunnel.h"
27 #include "TunnelEndpoint.h"
28 #include "TunnelGateway.h"
29 #include "TunnelBase.h"
30 #include "I2NPProtocol.h"
31 
32 namespace i2p
33 {
34 namespace tunnel
35 {
36 	const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
37 	const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
38 	const int TUNNEL_RECREATION_THRESHOLD = 90; // 1.5 minutes
39 	const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
40 	const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message
41 	const int MAX_NUM_RECORDS = 8;
42 	const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds
43 
44 	const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12
45 	const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6
46 
47 	enum TunnelState
48 	{
49 		eTunnelStatePending,
50 		eTunnelStateBuildReplyReceived,
51 		eTunnelStateBuildFailed,
52 		eTunnelStateEstablished,
53 		eTunnelStateTestFailed,
54 		eTunnelStateFailed,
55 		eTunnelStateExpiring
56 	};
57 
58 	class OutboundTunnel;
59 	class InboundTunnel;
60 	class Tunnel: public TunnelBase
61 	{
62 		struct TunnelHop
63 		{
64 			std::shared_ptr<const i2p::data::IdentityEx> ident;
65 			i2p::crypto::TunnelDecryption decryption;
66 		};
67 
68 		public:
69 
70 			Tunnel (std::shared_ptr<const TunnelConfig> config);
71 			~Tunnel ();
72 
73 			void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
74 
GetTunnelConfig()75 			std::shared_ptr<const TunnelConfig> GetTunnelConfig () const { return m_Config; }
76 			std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
77 			std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
IsShortBuildMessage()78 			bool IsShortBuildMessage () const { return m_IsShortBuildMessage; };
GetFarEndTransports()79 			i2p::data::RouterInfo::CompatibleTransports GetFarEndTransports () const { return m_FarEndTransports; };
GetState()80 			TunnelState GetState () const { return m_State; };
81 			void SetState (TunnelState state);
IsEstablished()82 			bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
IsFailed()83 			bool IsFailed () const { return m_State == eTunnelStateFailed; };
IsRecreated()84 			bool IsRecreated () const { return m_IsRecreated; };
SetRecreated(bool recreated)85 			void SetRecreated (bool recreated) { m_IsRecreated = recreated; };
GetNumHops()86 			int GetNumHops () const { return m_Hops.size (); };
87 			virtual bool IsInbound() const = 0;
88 
GetTunnelPool()89 			std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
SetTunnelPool(std::shared_ptr<TunnelPool> pool)90 			void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
91 
92 			bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
93 
Print(std::stringstream &)94 			virtual void Print (std::stringstream&) const {};
95 
96 			// implements TunnelBase
97 			void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
98 			void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
99 
100 			/** @brief add latency sample */
AddLatencySample(const uint64_t ms)101 			void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; }
102 			/** @brief get this tunnel's estimated latency */
GetMeanLatency()103 			uint64_t GetMeanLatency() const { return m_Latency; }
104 			/** @brief return true if this tunnel's latency fits in range [lowerbound, upperbound] */
105 			bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
106 
LatencyIsKnown()107 			bool LatencyIsKnown() const { return m_Latency > 0; }
IsSlow()108 			bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); }
109 
110 		protected:
111 
112 			void PrintHops (std::stringstream& s) const;
113 
114 		private:
115 
116 			std::shared_ptr<const TunnelConfig> m_Config;
117 			std::vector<TunnelHop> m_Hops;
118 			bool m_IsShortBuildMessage;
119 			std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
120 			TunnelState m_State;
121 			i2p::data::RouterInfo::CompatibleTransports m_FarEndTransports;
122 			bool m_IsRecreated; // if tunnel is replaced by new, or new tunnel requested to replace
123 			uint64_t m_Latency; // in milliseconds
124 	};
125 
126 	class OutboundTunnel: public Tunnel
127 	{
128 		public:
129 
OutboundTunnel(std::shared_ptr<const TunnelConfig> config)130 			OutboundTunnel (std::shared_ptr<const TunnelConfig> config):
131 				Tunnel (config), m_Gateway (this), m_EndpointIdentHash (config->GetLastIdentHash ()) {};
132 
133 			void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
134 			virtual void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
GetEndpointIdentHash()135 			const i2p::data::IdentHash& GetEndpointIdentHash () const { return m_EndpointIdentHash; };
GetNumSentBytes()136 			virtual size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
137 			void Print (std::stringstream& s) const;
138 
139 			// implements TunnelBase
140 			void HandleTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage>&& tunnelMsg);
141 
IsInbound()142 			bool IsInbound() const { return false; }
143 
144 		private:
145 
146 			std::mutex m_SendMutex;
147 			TunnelGateway m_Gateway;
148 			i2p::data::IdentHash m_EndpointIdentHash;
149 	};
150 
151 	class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
152 	{
153 		public:
154 
InboundTunnel(std::shared_ptr<const TunnelConfig> config)155 			InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
156 			void HandleTunnelDataMsg (std::shared_ptr<I2NPMessage>&& msg);
GetNumReceivedBytes()157 			virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
158 			void Print (std::stringstream& s) const;
IsInbound()159 			bool IsInbound() const { return true; }
160 
161 			// override TunnelBase
Cleanup()162 			void Cleanup () { m_Endpoint.Cleanup (); };
163 
164 		private:
165 
166 			TunnelEndpoint m_Endpoint;
167 	};
168 
169 	class ZeroHopsInboundTunnel: public InboundTunnel
170 	{
171 		public:
172 
173 			ZeroHopsInboundTunnel ();
174 			void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
175 			void Print (std::stringstream& s) const;
GetNumReceivedBytes()176 			size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
177 
178 		private:
179 
180 			size_t m_NumReceivedBytes;
181 	};
182 
183 	class ZeroHopsOutboundTunnel: public OutboundTunnel
184 	{
185 		public:
186 
187 			ZeroHopsOutboundTunnel ();
188 			void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs);
189 			void Print (std::stringstream& s) const;
GetNumSentBytes()190 			size_t GetNumSentBytes () const { return m_NumSentBytes; };
191 
192 		private:
193 
194 			size_t m_NumSentBytes;
195 	};
196 
197 	class Tunnels
198 	{
199 		public:
200 
201 			Tunnels ();
202 			~Tunnels ();
203 			void Start ();
204 			void Stop ();
205 
206 			std::shared_ptr<InboundTunnel> GetPendingInboundTunnel (uint32_t replyMsgID);
207 			std::shared_ptr<OutboundTunnel> GetPendingOutboundTunnel (uint32_t replyMsgID);
208 			std::shared_ptr<InboundTunnel> GetNextInboundTunnel ();
209 			std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
GetExploratoryPool()210 			std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
211 			std::shared_ptr<TunnelBase> GetTunnel (uint32_t tunnelID);
212 			int GetTransitTunnelsExpirationTimeout ();
213 			void AddTransitTunnel (std::shared_ptr<TransitTunnel> tunnel);
214 			void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
215 			void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
216 			std::shared_ptr<InboundTunnel> CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel);
217 			std::shared_ptr<OutboundTunnel> CreateOutboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<TunnelPool> pool);
218 			void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
219 			void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
220 			void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
221 			void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
222 			std::shared_ptr<TunnelPool> CreateTunnelPool (int numInboundHops,
223 				int numOuboundHops, int numInboundTunnels, int numOutboundTunnels);
224 			void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
225 			void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
226 
227 			std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
228 
229 		private:
230 
231 			template<class TTunnel>
232 			std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config,
233 			    std::shared_ptr<TunnelPool> pool, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
234 
235 			template<class TTunnel>
236 			std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
237 
238 			void HandleTunnelGatewayMsg (std::shared_ptr<TunnelBase> tunnel, std::shared_ptr<I2NPMessage> msg);
239 
240 			void Run ();
241 			void ManageTunnels ();
242 			void ManageOutboundTunnels ();
243 			void ManageInboundTunnels ();
244 			void ManageTransitTunnels ();
245 			void ManagePendingTunnels ();
246 			template<class PendingTunnels>
247 			void ManagePendingTunnels (PendingTunnels& pendingTunnels);
248 			void ManageTunnelPools (uint64_t ts);
249 
250 			std::shared_ptr<ZeroHopsInboundTunnel> CreateZeroHopsInboundTunnel (std::shared_ptr<TunnelPool> pool);
251 			std::shared_ptr<ZeroHopsOutboundTunnel> CreateZeroHopsOutboundTunnel (std::shared_ptr<TunnelPool> pool);
252 
253 		private:
254 
255 			bool m_IsRunning;
256 			std::thread * m_Thread;
257 			std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_PendingInboundTunnels; // by replyMsgID
258 			std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
259 			std::list<std::shared_ptr<InboundTunnel> > m_InboundTunnels;
260 			std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
261 			std::list<std::shared_ptr<TransitTunnel> > m_TransitTunnels;
262 			std::unordered_map<uint32_t, std::shared_ptr<TunnelBase> > m_Tunnels; // tunnelID->tunnel known by this id
263 			std::mutex m_PoolsMutex;
264 			std::list<std::shared_ptr<TunnelPool>> m_Pools;
265 			std::shared_ptr<TunnelPool> m_ExploratoryPool;
266 			i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
267 			i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool;
268 			i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool;
269 
270 			// some stats
271 			int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations;
272 
273 		public:
274 
275 			// for HTTP only
decltype(m_OutboundTunnels)276 			const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
decltype(m_InboundTunnels)277 			const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
decltype(m_TransitTunnels)278 			const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; };
279 
280 			size_t CountTransitTunnels() const;
281 			size_t CountInboundTunnels() const;
282 			size_t CountOutboundTunnels() const;
283 
GetQueueSize()284 			int GetQueueSize () { return m_Queue.GetSize (); };
GetTunnelCreationSuccessRate()285 			int GetTunnelCreationSuccessRate () const // in percents
286 			{
287 				int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations;
288 				return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0;
289 			}
290 	};
291 
292 	extern Tunnels tunnels;
293 }
294 }
295 
296 #endif
297