5 * - Resolution/caching of hardware addresses
10 #include "include/adapters_int.h"
11 #include "hwaddr_cache.h"
13 #include <semaphore.h>
14 #include <limits.h> // *INT_MAX
16 typedef struct sHWAdddrCache tHWAddrCache;
17 typedef struct sHWAdddrCachedAddr tHWAddrCachedAddr;
19 struct sHWAdddrCache {
20 const tInterface *Interface;
24 tHWAddrCachedAddr *First;
27 struct sHWAdddrCachedAddr {
28 tHWAddrCachedAddr *Next;
29 unsigned int WaitingCount; // While >0, cache will not be reused
30 tSemaphore WaitingSem;
35 #define PRImacaddr "%02x:%02x:%02x:%02x:%02x:%02x"
36 #define FMTmacaddr(a) (a).B[0],(a).B[1],(a).B[2],(a).B[3],(a).B[4],(a).B[5]
38 // Cache is sorted by usage (most recently used at top)
41 tHWAddrCache *HWCache_int_GetCache(tAdapter *Adapter, int AddrType)
43 static tHWAddrCache cache_v6 = {.MaxSize=64};
44 static tHWAddrCache cache_v4 = {.MaxSize=64};
47 case 4: return &cache_v4;
48 case 6: return &cache_v6;
54 bool memiszero(const void *mem, size_t length)
56 const Uint8 *mem8 = mem;
59 if(*mem8 != 0) return false;
65 tMacAddr HWCache_Resolve(tInterface *Interface, const void *DestAddr)
67 const size_t addrsize = IPStack_GetAddressSize(Interface->Type);
68 tHWAddrCache *cache = HWCache_int_GetCache(Interface->Adapter, Interface->Type);
70 LOG("DestAddr=%s", IPStack_PrintAddress(Interface->Type, DestAddr));
72 // Detect sending packets outside of the local network
73 if( IPStack_CompareAddress(Interface->Type, DestAddr, Interface->Address, Interface->SubnetBits) == 0 )
75 // Off-net, use a route
76 tRoute *route = IPStack_FindRoute(Interface->Type, Interface, DestAddr);
77 // If a route exists and the gateway isn't empty, use the gateway
80 if( !memiszero(route->NextHop, addrsize) )
82 // Update DestAddr to gateway address
83 DestAddr = route->NextHop;
88 // Zero gateway means force direct
95 LOG("No route to host");
100 else if( IPStack_AddressIsBroadcast(Interface->Type, DestAddr, Interface->SubnetBits) )
102 // Broadcast, send to bcast mac
104 return cMAC_BROADCAST;
110 LOG("DestAddr(2)=%s", IPStack_PrintAddress(Interface->Type, DestAddr));
112 Mutex_Acquire(&cache->Lock);
113 // 1. Search cache for this address
114 tHWAddrCachedAddr **pnp = &cache->First, *ca;
115 tHWAddrCachedAddr *last = NULL;
116 for( ca = cache->First; ca; ca = ca->Next )
118 LOG("ca = %p (%s => "PRImacaddr")",
119 ca, IPStack_PrintAddress(Interface->Type, ca->L3Addr),
120 FMTmacaddr(*ca->HWAddr)
122 if( memcmp(ca->L3Addr, DestAddr, addrsize) == 0 )
129 // Move to front, return
130 if( cache->First != ca )
132 ASSERT(pnp != &cache->First);
135 ca->Next = cache->First;
137 LOG("%p(%s) bumped", ca, IPStack_PrintAddress(Interface->Type, ca->L3Addr));
140 // If there's something waiting on this, odds are it's not populated
141 if( ca->WaitingCount > 0 )
143 ASSERT(ca->WaitingCount != UINT_MAX);
145 Mutex_Release(&cache->Lock);
147 // Wait until populated
148 LOG("Waiting on %p", ca);
149 Semaphore_Wait(&ca->WaitingSem, 1);
151 Mutex_Acquire(&cache->Lock);
152 ASSERT(ca->WaitingCount > 0);
155 tMacAddr ret = *(tMacAddr*)ca->HWAddr;
156 Mutex_Release(&cache->Lock);
158 LOG("Cached "PRImacaddr, FMTmacaddr(ret));
162 if( cache->nAddrs >= cache->MaxSize )
164 ASSERTC(cache->nAddrs, ==, cache->MaxSize);
166 // TODO: Need to pick the oldest entry with WaitingThreads==0
167 ASSERT(ca->WaitingCount == 0);
168 // Reuse the oldest item
170 LOG("Reuse entry for %p(%s)", ca, IPStack_PrintAddress(Interface->Type, ca->L3Addr));
175 ca = calloc( 1, sizeof(*ca) + addrsize + sizeof(tMacAddr) );
177 ca->HWAddr = (void*)( (char*)ca->L3Addr + addrsize );
178 LOG("New entry %p", ca);
180 memcpy(ca->L3Addr, DestAddr, addrsize);
181 memset(ca->HWAddr, 0, sizeof(tMacAddr));
183 // Shift to front of list
184 if( cache->First != ca )
187 ca->Next = cache->First;
191 // Mark cache entry as being waited upon
192 ASSERT(ca->WaitingCount == 0);
193 ca->WaitingCount = 1;
194 // Then release cache lock (so inbound packets can manipulate it)
195 Mutex_Release(&cache->Lock);
197 // Send a request for the address
198 switch(Interface->Type)
200 case 4: ARP_Request4(Interface, *(tIPv4*)DestAddr); break;
201 //case 6: ICMPv6_RequestND(Interface, DestAddr); break;
203 ASSERTC(Interface->Type, ==, 4);
204 ASSERTC(Interface->Type, ==, 6);
208 // Wait for up to 3000ms for the entry to populate
209 LOG("Waiting on new entry");
210 Time_ScheduleEvent(3000);
211 int rv = Semaphore_Wait(&ca->WaitingSem, 1);
213 // Lock, reduce waiting count, grab return, and release
214 Mutex_Acquire(&cache->Lock);
215 ASSERT(ca->WaitingCount > 0);
217 tMacAddr ret = *(tMacAddr*)ca->HWAddr;
218 Mutex_Release(&cache->Lock);
220 // NOTE: If entry wasn't populated, we'd return 0 anyway, this is
221 // just for logging purposes
224 // Interrupted, return zero MAC
225 LOG("Timeout/interrupt, return zero");
229 // Release `ca` (on error, HWAddr will be nul)
230 LOG("Requested "PRImacaddr, FMTmacaddr(ret));
234 void HWCache_Set(tAdapter *Adapter, int AddrType, const void *L3Addr, const tMacAddr *HWAddr)
236 const size_t addrsize = IPStack_GetAddressSize(AddrType);
237 tHWAddrCache *cache = HWCache_int_GetCache(Adapter, AddrType);
238 LOG("Set %s = "PRImacaddr,
239 IPStack_PrintAddress(AddrType, L3Addr),
242 // 1. Locate an existing entry
243 Mutex_Acquire(&cache->Lock);
244 tHWAddrCachedAddr *last_unused = NULL;
245 tHWAddrCachedAddr **pnp = &cache->First;
246 for( tHWAddrCachedAddr *ca = cache->First; ca; ca = ca->Next )
248 ASSERT(ca->Next != ca);
249 ASSERT(!ca->Next || ca->Next->Next != ca);
251 LOG("ca = %p (%s => "PRImacaddr")",
252 ca, IPStack_PrintAddress(AddrType, ca->L3Addr), FMTmacaddr(*ca->HWAddr)
254 if( ca->WaitingCount == 0 )
258 // 2. If found, set the cache and poke waiting threads
259 if( memcmp(L3Addr, ca->L3Addr, addrsize) == 0 )
261 if( ca->WaitingCount )
263 memcpy(ca->HWAddr, HWAddr, sizeof(tMacAddr));
264 Semaphore_Signal(&ca->WaitingSem, INT_MAX);
265 LOG("Found and cached");
267 else if( memcmp(HWAddr, ca->HWAddr, sizeof(tMacAddr)) )
269 LOG("Differs to cache");
273 LOG("Already known");
275 Mutex_Release(&cache->Lock);
279 // No existing entry, cache just in case
280 if( cache->nAddrs < cache->MaxSize )
284 tHWAddrCachedAddr *ca = calloc(1,sizeof(tHWAddrCachedAddr)+addrsize+sizeof(tMacAddr));
287 ca->HWAddr = (void*)( (char*)ca->L3Addr + addrsize );
288 memcpy(ca->L3Addr, L3Addr, addrsize);
289 memcpy(ca->HWAddr, HWAddr, sizeof(tMacAddr));
290 LOG("Cache in new entry");
292 else if( last_unused )
294 tHWAddrCachedAddr *ca = last_unused;
295 memcpy(ca->L3Addr, L3Addr, addrsize);
296 memcpy(ca->HWAddr, HWAddr, sizeof(tMacAddr));
298 LOG("Cache in old entry");
302 // None unused! What is this madness?
303 LOG("Not cached... cache being thrashed?");
305 Mutex_Release(&cache->Lock);