/* this function translates a target IP address into a destination physical address * NOTE: if the address could not be translated, the function returns zero. */ internal UInt64 TranslateIPAddressToPhysicalAddress(UInt32 ipAddress, Int64 timeoutInMachineTicks) { // if the ipAdderss is the broadcast address, return a broadcast MAC address if (ipAddress == 0xFFFFFFFF) { return(ETHERNET_BROADCAST_ADDRESS); } ArpCacheEntry arpEntry = null; // retrieve our existing ARP entry, if it exists lock (_arpCacheLock) { arpEntry = (ArpCacheEntry)_arpCache[ipAddress]; // if we retrieved an entry, make sure it has not timed out. if (arpEntry != null) { Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks; if (arpEntry.TimeoutTicks < nowTicks) { // if the entry has timed out, dispose of it; we will re-query the target _arpCache.Remove(ipAddress); arpEntry = null; } } } // if we were caching a valid ARP entry, return its PhysicalAddress now. if (arpEntry != null) { return(arpEntry.PhysicalAddress); } // if we did not obtain a valid ARP entry, query for one now. lock (_simultaneousArpRequestLock) /* lock our current ARP request...we can only have one request at a time. */ { Int32 waitTimeout; for (int iAttempt = 0; iAttempt < MAX_ARP_TRANSLATE_ATTEMPTS; iAttempt++) { // set the IP Address of our current ARP request _currentArpRequestProtocolAddress = ipAddress; _currentArpRequestPhysicalAddress = 0; // this will be set to a non-zero value if we get a response _currentArpRequestAnsweredEvent.Reset(); // send the ARP request SendArpRequest(ipAddress, timeoutInMachineTicks); // wait on a reply (for up to our timeout time or 1 second...whichever is less) waitTimeout = System.Math.Min((Int32)((timeoutInMachineTicks != Int64.MaxValue) ? Math.Max((timeoutInMachineTicks - Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks) / System.TimeSpan.TicksPerMillisecond, 0) : 1000), 1000); _currentArpRequestAnsweredEvent.WaitOne(waitTimeout, false); // if we received an ARP reply, add it to our cache table try { if (_currentArpRequestPhysicalAddress != 0) { lock (_arpCacheLock) { // first make sure that our cache table is not full; if it is full then remove the oldest entry (based on LastUsedTime) if (_arpCache.Count >= ARP_CACHE_MAXIMUM_ENTRIES) { Int64 oldestLastUsedTicks = Int64.MaxValue; UInt32 oldestKey = 0; foreach (UInt32 key in _arpCache.Keys) { if (((ArpCacheEntry)_arpCache[key]).LastUsedTicks < oldestLastUsedTicks) { oldestKey = key; oldestLastUsedTicks = ((ArpCacheEntry)_arpCache[key]).LastUsedTicks; } } _arpCache.Remove(oldestKey); } // then add our new cache entry and return the ARP reply's address. Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks; arpEntry = new ArpCacheEntry(_currentArpRequestPhysicalAddress, nowTicks + (TimeSpan.TicksPerSecond * DEFAULT_ARP_CACHE_TIMEOUT_IN_SECONDS), nowTicks); _arpCache.Add(_currentArpRequestProtocolAddress, arpEntry); } return(_currentArpRequestPhysicalAddress); } // if we're out of (user-specified) time without a reply, return zero (no address). if (Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks > timeoutInMachineTicks) { return(0); } } finally { _currentArpRequestProtocolAddress = 0; // no ARP request is in process now. } } } // if we could not get the address of the target, return zero. return(0); }
/* this function translates a target IP address into a destination physical address * NOTE: if the address could not be translated, the function returns zero. */ internal UInt64 TranslateIPAddressToPhysicalAddress(UInt32 ipAddress, Int64 timeoutInMachineTicks) { // if the ipAdderss is the broadcast address, return a broadcast MAC address if (ipAddress == 0xFFFFFFFF) return ETHERNET_BROADCAST_ADDRESS; ArpCacheEntry arpEntry = null; // retrieve our existing ARP entry, if it exists lock (_arpCacheLock) { arpEntry = (ArpCacheEntry)_arpCache[ipAddress]; // if we retrieved an entry, make sure it has not timed out. if (arpEntry != null) { Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks; if (arpEntry.TimeoutTicks < nowTicks) { // if the entry has timed out, dispose of it; we will re-query the target _arpCache.Remove(ipAddress); arpEntry = null; } } } // if we were caching a valid ARP entry, return its PhysicalAddress now. if (arpEntry != null) return arpEntry.PhysicalAddress; // if we did not obtain a valid ARP entry, query for one now. lock (_simultaneousArpRequestLock) /* lock our current ARP request...we can only have one request at a time. */ { Int32 waitTimeout; for (int iAttempt = 0; iAttempt < MAX_ARP_TRANSLATE_ATTEMPTS; iAttempt++) { // set the IP Address of our current ARP request _currentArpRequestProtocolAddress = ipAddress; _currentArpRequestPhysicalAddress = 0; // this will be set to a non-zero value if we get a response _currentArpRequestAnsweredEvent.Reset(); // send the ARP request SendArpRequest(ipAddress, timeoutInMachineTicks); // wait on a reply (for up to our timeout time or 1 second...whichever is less) waitTimeout = System.Math.Min((Int32)((timeoutInMachineTicks != Int64.MaxValue) ? Math.Max((timeoutInMachineTicks - Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks) / System.TimeSpan.TicksPerMillisecond, 0) : 1000), 1000); _currentArpRequestAnsweredEvent.WaitOne(waitTimeout, false); // if we received an ARP reply, add it to our cache table try { if (_currentArpRequestPhysicalAddress != 0) { lock (_arpCacheLock) { // first make sure that our cache table is not full; if it is full then remove the oldest entry (based on LastUsedTime) if (_arpCache.Count >= ARP_CACHE_MAXIMUM_ENTRIES) { Int64 oldestLastUsedTicks = Int64.MaxValue; UInt32 oldestKey = 0; foreach (UInt32 key in _arpCache.Keys) { if (((ArpCacheEntry)_arpCache[key]).LastUsedTicks < oldestLastUsedTicks) { oldestKey = key; oldestLastUsedTicks = ((ArpCacheEntry)_arpCache[key]).LastUsedTicks; } } _arpCache.Remove(oldestKey); } // then add our new cache entry and return the ARP reply's address. Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks; arpEntry = new ArpCacheEntry(_currentArpRequestPhysicalAddress, nowTicks + (TimeSpan.TicksPerSecond * DEFAULT_ARP_CACHE_TIMEOUT_IN_SECONDS), nowTicks); _arpCache.Add(_currentArpRequestProtocolAddress, arpEntry); } return _currentArpRequestPhysicalAddress; } // if we're out of (user-specified) time without a reply, return zero (no address). if (Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks > timeoutInMachineTicks) return 0; } finally { _currentArpRequestProtocolAddress = 0; // no ARP request is in process now. } } } // if we could not get the address of the target, return zero. return 0; }
void _ethernetInterface_ARPFrameReceived(object sender, byte[] buffer, int index, int count) { // verify that our ARP frame is long enough if (count < ARP_FRAME_BUFFER_LENGTH) { return; // discard packet } /* parse our ARP frame */ // first validate our frame's fixed header UInt16 hardwareType = (UInt16)((buffer[index] << 8) + buffer[index + 1]); if (hardwareType != HARDWARE_TYPE_ETHERNET) { return; // only Ethernet hardware is supported; discard frame } UInt16 protocolType = (UInt16)((buffer[index + 2] << 8) + buffer[index + 3]); if (protocolType != PROTOCOL_TYPE_IPV4) { return; // only IPv4 protocol is supported; discard frame } byte hardwareAddressSize = buffer[index + 4]; if (hardwareAddressSize != HARDWARE_ADDRESS_SIZE) { return; // invalid hardware address size } byte protocolAddressSize = buffer[index + 5]; if (protocolAddressSize != PROTOCOL_ADDRESS_SIZE) { return; // invalid protocol address size } ArpOperation operation = (ArpOperation)((buffer[index + 6] << 8) + buffer[index + 7]); // NOTE: we will validate the operation a bit later after we compare the incoming targetProtocolAddress. // retrieve the sender and target addresses UInt64 senderPhysicalAddress = (UInt64)(((UInt64)buffer[index + 8] << 40) + ((UInt64)buffer[index + 9] << 32) + ((UInt64)buffer[index + 10] << 24) + ((UInt64)buffer[index + 11] << 16) + ((UInt64)buffer[index + 12] << 8) + (UInt64)buffer[index + 13]); UInt32 senderProtocolAddress = (UInt32)(((UInt32)buffer[index + 14] << 24) + ((UInt32)buffer[index + 15] << 16) + ((UInt32)buffer[index + 16] << 8) + (UInt32)buffer[index + 17]); //UInt64 targetHardwareAddress = (UInt64)(((UInt64)buffer[index + 18] << 40) + ((UInt64)buffer[index + 19] << 32) + ((UInt64)buffer[index + 20] << 24) + ((UInt64)buffer[index + 21] << 16) + ((UInt64)buffer[index + 22] << 8) + (UInt64)buffer[index + 23]); UInt32 targetProtocolAddress = (UInt32)(((UInt32)buffer[index + 24] << 24) + ((UInt32)buffer[index + 25] << 16) + ((UInt32)buffer[index + 26] << 8) + (UInt32)buffer[index + 27]); // if the sender IP is already listed in our ARP cache then update the entry lock (_arpCacheLock) { ArpCacheEntry arpEntry = (ArpCacheEntry)_arpCache[senderProtocolAddress]; if (arpEntry != null) { arpEntry.PhysicalAddress = senderPhysicalAddress; Int64 nowTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks; arpEntry.TimeoutTicks = nowTicks + (TimeSpan.TicksPerSecond * DEFAULT_ARP_CACHE_TIMEOUT_IN_SECONDS); // NOTE: we do not update the LastUsedTicks property since this is not the result of a request for a non-existent cache entry } else { // do nothing. some implementation of ARP would create a cache entry for any incoming ARP packets...but we only cache a limited number of entries which we requested. } } if (operation == ArpOperation.ARP_OPERATION_REQUEST) { // if the request is asking for our IP protocol address, then send an ARP reply if (targetProtocolAddress == _ipv4ProtocolAddress) { // we do not want to block our RX thread, so queue a response on a worker thread SendArpGenericInBackground(senderPhysicalAddress, ArpOperation.ARP_OPERATION_REPLY, senderPhysicalAddress, senderProtocolAddress); } } else if (operation == ArpOperation.ARP_OPERATION_REPLY) { if (senderProtocolAddress == _currentArpRequestProtocolAddress) { _currentArpRequestPhysicalAddress = senderPhysicalAddress; _currentArpRequestAnsweredEvent.Set(); } } else { // invalid operation; discard frame } }