/* 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);
        }
예제 #2
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
            }
        }