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 #ifndef ZT_ARP_HPP
15 #define ZT_ARP_HPP
16 
17 #include <stdint.h>
18 
19 #include <utility>
20 
21 #include "../node/Constants.hpp"
22 #include "../node/Hashtable.hpp"
23 #include "../node/MAC.hpp"
24 
25 /**
26  * Maximum possible ARP length
27  *
28  * ARPs are 28 bytes in length, but specify a 128 byte buffer since
29  * some weird extensions we may support in the future can pad them
30  * out to as long as 72 bytes.
31  */
32 #define ZT_ARP_BUF_LENGTH 128
33 
34 /**
35  * Minimum permitted interval between sending ARP queries for a given IP
36  */
37 #define ZT_ARP_QUERY_INTERVAL 2000
38 
39 /**
40  * Maximum time between query and response, otherwise responses are discarded to prevent poisoning
41  */
42 #define ZT_ARP_QUERY_MAX_TTL 5000
43 
44 /**
45  * ARP expiration time
46  */
47 #define ZT_ARP_EXPIRE 600000
48 
49 namespace ZeroTier {
50 
51 /**
52  * ARP cache and resolver
53  *
54  * To implement ARP:
55  *
56  * (1) Call processIncomingArp() on all ARP packets received and then always
57  * check responseLen after calling. If it is non-zero, send the contents
58  * of response to responseDest.
59  *
60  * (2) Call query() to look up IP addresses, and then check queryLen. If it
61  * is non-zero, send the contents of query to queryDest (usually broadcast).
62  *
63  * Note that either of these functions can technically generate a response or
64  * a query at any time, so their result parameters for sending ARPs should
65  * always be checked.
66  *
67  * This class is not thread-safe and must be guarded if used in multi-threaded
68  * code.
69  */
70 class Arp
71 {
72 public:
73 	Arp();
74 
75 	/**
76 	 * Set a local IP entry that we should respond to ARPs for
77 	 *
78 	 * @param mac Our local MAC address
79 	 * @param ip IP in big-endian byte order (sin_addr.s_addr)
80 	 */
81 	void addLocal(uint32_t ip,const MAC &mac);
82 
83 	/**
84 	 * Delete a local IP entry or a cached ARP entry
85 	 *
86 	 * @param ip IP in big-endian byte order (sin_addr.s_addr)
87 	 */
88 	void remove(uint32_t ip);
89 
90 	/**
91 	 * Process ARP packets
92 	 *
93 	 * For ARP queries, a response is generated and responseLen is set to its
94 	 * frame payload length in bytes.
95 	 *
96 	 * For ARP responses, the cache is populated and the IP address entry that
97 	 * was learned is returned.
98 	 *
99 	 * @param arp ARP frame data
100 	 * @param len Length of ARP frame (usually 28)
101 	 * @param response Response buffer -- MUST be a minimum of ZT_ARP_BUF_LENGTH in size
102 	 * @param responseLen Response length, or set to 0 if no response
103 	 * @param responseDest Destination of response, or set to null if no response
104 	 * @return IP address learned or 0 if no new IPs in cache
105 	 */
106 	uint32_t processIncomingArp(const void *arp,unsigned int len,void *response,unsigned int &responseLen,MAC &responseDest);
107 
108 	/**
109 	 * Get the MAC corresponding to an IP, generating a query if needed
110 	 *
111 	 * This returns a MAC for a remote IP. The local MAC is returned for local
112 	 * IPs as well. It may also generate a query if the IP is not known or the
113 	 * entry needs to be refreshed. In this case queryLen will be set to a
114 	 * non-zero value, so this should always be checked on return even if the
115 	 * MAC returned is non-null.
116 	 *
117 	 * @param localMac Local MAC address of host interface
118      * @param localIp Local IP address of host interface
119 	 * @param targetIp IP to look up
120 	 * @param query Buffer for generated query -- MUST be a minimum of ZT_ARP_BUF_LENGTH in size
121 	 * @param queryLen Length of generated query, or set to 0 if no query generated
122 	 * @param queryDest Destination of query, or set to null if no query generated
123 	 * @return MAC or 0 if no cached entry for this IP
124 	 */
125 	MAC query(const MAC &localMac,uint32_t localIp,uint32_t targetIp,void *query,unsigned int &queryLen,MAC &queryDest);
126 
127 private:
128 	struct _ArpEntry
129 	{
_ArpEntryZeroTier::Arp::_ArpEntry130 		_ArpEntry() : lastQuerySent(0),lastResponseReceived(0),mac(),local(false) {}
131 		uint64_t lastQuerySent; // Time last query was sent or 0 for local IP
132 		uint64_t lastResponseReceived; // Time of last ARP response or 0 for local IP
133 		MAC mac; // MAC address of device responsible for IP or null if not known yet
134 		bool local; // True if this is a local ARP entry
135 	};
136 
137 	Hashtable< uint32_t,_ArpEntry > _cache;
138 	uint64_t _lastCleaned;
139 };
140 
141 } // namespace ZeroTier
142 
143 #endif
144