1 /*
2  * Copyright (c)2019 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: 2025-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 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include <set>
19 #include <vector>
20 
21 #include "Constants.hpp"
22 #include "SelfAwareness.hpp"
23 #include "RuntimeEnvironment.hpp"
24 #include "Node.hpp"
25 #include "Topology.hpp"
26 #include "Packet.hpp"
27 #include "Peer.hpp"
28 #include "Switch.hpp"
29 #include "Trace.hpp"
30 
31 // Entry timeout -- make it fairly long since this is just to prevent stale buildup
32 #define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
33 
34 namespace ZeroTier {
35 
36 class _ResetWithinScope
37 {
38 public:
_ResetWithinScope(void * tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope)39 	_ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
40 		_now(now),
41 		_tPtr(tPtr),
42 		_family(inetAddressFamily),
43 		_scope(scope) {}
44 
operator ()(Topology & t,const SharedPtr<Peer> & p)45 	inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); }
46 
47 private:
48 	uint64_t _now;
49 	void *_tPtr;
50 	int _family;
51 	InetAddress::IpScope _scope;
52 };
53 
SelfAwareness(const RuntimeEnvironment * renv)54 SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
55 	RR(renv),
56 	_phy(128)
57 {
58 }
59 
iam(void * tPtr,const Address & reporter,const int64_t receivedOnLocalSocket,const InetAddress & reporterPhysicalAddress,const InetAddress & myPhysicalAddress,bool trusted,int64_t now)60 void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,int64_t now)
61 {
62 	const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
63 
64 	if ((scope != reporterPhysicalAddress.ipScope())||(scope == InetAddress::IP_SCOPE_NONE)||(scope == InetAddress::IP_SCOPE_LOOPBACK)||(scope == InetAddress::IP_SCOPE_MULTICAST))
65 		return;
66 
67 	Mutex::Lock _l(_phy_m);
68 	PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
69 
70 	if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
71 		// Changes to external surface reported by trusted peers causes path reset in this scope
72 		RR->t->resettingPathsInScope(tPtr,reporter,reporterPhysicalAddress,myPhysicalAddress,scope);
73 
74 		entry.mySurface = myPhysicalAddress;
75 		entry.ts = now;
76 		entry.trusted = trusted;
77 
78 		// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
79 		// due to multiple reports of endpoint change.
80 		// Don't use 'entry' after this since hash table gets modified.
81 		{
82 			Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
83 			PhySurfaceKey *k = (PhySurfaceKey *)0;
84 			PhySurfaceEntry *e = (PhySurfaceEntry *)0;
85 			while (i.next(k,e)) {
86 				if ((k->reporterPhysicalAddress != reporterPhysicalAddress)&&(k->scope == scope))
87 					_phy.erase(*k);
88 			}
89 		}
90 
91 		// Reset all paths within this scope and address family
92 		_ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
93 		RR->topology->eachPeer<_ResetWithinScope &>(rset);
94 	} else {
95 		// Otherwise just update DB to use to determine external surface info
96 		entry.mySurface = myPhysicalAddress;
97 		entry.ts = now;
98 		entry.trusted = trusted;
99 	}
100 }
101 
clean(int64_t now)102 void SelfAwareness::clean(int64_t now)
103 {
104 	Mutex::Lock _l(_phy_m);
105 	Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
106 	PhySurfaceKey *k = (PhySurfaceKey *)0;
107 	PhySurfaceEntry *e = (PhySurfaceEntry *)0;
108 	while (i.next(k,e)) {
109 		if ((now - e->ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT)
110 			_phy.erase(*k);
111 	}
112 }
113 
114 } // namespace ZeroTier
115