bool SendDhcpRequestAndWaitForAck(DhcpOffer dhcpOffer, UInt16 secondsElapsed, UInt32 dhcpServerIPAddress, UInt32 clientIPAddress, Int64 timeoutInMachineTicks)
        {
            bool success = false;

            // obtain an exclusive handle to the reserved socket
            int socketHandle = _ipv4Layer.CreateSocket(IPv4Layer.ProtocolType.Udp, timeoutInMachineTicks, true);
            // instantiate the reserved socket
            UdpSocket socket = (UdpSocket)_ipv4Layer.GetSocket(socketHandle);

            try
            {
                // bind the reserved socket to the DHCPv4 client port
                socket.Bind(0 /* IP_ADDRESS_ANY */, DHCP_CLIENT_PORT);

                // we will retry the DHCP request up to four times.  first delay will be 4 +/-1 seconds; second delay will be 8 +/-1 seconds; third delay will be 16 +/-1 seconds; fourth delay will be 32 +/-1 seconds.
                // if our current timeoutInMachineTicks is longer than 64 seconds (the maximum wait for DHCP transmission) then reduce it to the maximum
                timeoutInMachineTicks = (Int64)System.Math.Max(timeoutInMachineTicks, Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (TimeSpan.TicksPerSecond * 64));
                byte nextRetrySeconds = 4;
                Int64 nextRetryInMachineTicks = (Int64)(Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (((double)nextRetrySeconds + GenerateRandomPlusMinusOne()) * TimeSpan.TicksPerSecond));

                // set our clientIdentifier
                byte[] clientIdentifier = new byte[1 + HARDWARE_ADDRESS_SIZE];
                clientIdentifier[0] = HARDWARE_TYPE_ETHERNET;
                clientIdentifier[1] = (byte)((_physicalAddress >> 40) & 0xFF);
                clientIdentifier[2] = (byte)((_physicalAddress >> 32) & 0xFF);
                clientIdentifier[3] = (byte)((_physicalAddress >> 24) & 0xFF);
                clientIdentifier[4] = (byte)((_physicalAddress >> 16) & 0xFF);
                clientIdentifier[5] = (byte)((_physicalAddress >> 8) & 0xFF);
                clientIdentifier[6] = (byte)(_physicalAddress & 0xFF);

                byte[] parameterRequestList;
                if (_isDhcpDnsConfigEnabled)
                {
                    parameterRequestList = new byte[] { (byte)DhcpOptionCode.SubnetMask, (byte)DhcpOptionCode.Router, (byte)DhcpOptionCode.DomainNameServer };
                }
                else
                {
                    parameterRequestList = new byte[] { (byte)DhcpOptionCode.SubnetMask, (byte)DhcpOptionCode.Router };
                }

                byte[] maximumDhcpMessageSize = new byte[2];
                maximumDhcpMessageSize[0] = (byte)((DHCP_FRAME_BUFFER_LENGTH >> 8) & 0xFF);
                maximumDhcpMessageSize[1] = (byte)(DHCP_FRAME_BUFFER_LENGTH & 0xFF);

                byte[] requestedIPAddress = new byte[4];
                requestedIPAddress[0] = (byte)((dhcpOffer.IPAddress >> 24) & 0xFF);
                requestedIPAddress[1] = (byte)((dhcpOffer.IPAddress >> 16) & 0xFF);
                requestedIPAddress[2] = (byte)((dhcpOffer.IPAddress >> 8) & 0xFF);
                requestedIPAddress[3] = (byte)(dhcpOffer.IPAddress & 0xFF);

                byte[] serverIdentifier = new byte[4];
                serverIdentifier[0] = (byte)((dhcpOffer.ServerIdentifier >> 24) & 0xFF);
                serverIdentifier[1] = (byte)((dhcpOffer.ServerIdentifier >> 16) & 0xFF);
                serverIdentifier[2] = (byte)((dhcpOffer.ServerIdentifier >> 8) & 0xFF);
                serverIdentifier[3] = (byte)(dhcpOffer.ServerIdentifier & 0xFF);

                while (timeoutInMachineTicks > Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks)
                {
                    // assemble options
                    DhcpOption[] options = new DhcpOption[5];
                    options[0] = new DhcpOption(DhcpOptionCode.ClientIdentifier, clientIdentifier);
                    options[1] = new DhcpOption(DhcpOptionCode.ParameterRequestList, parameterRequestList);
                    options[2] = new DhcpOption(DhcpOptionCode.MaximumDhcpMessageSize, maximumDhcpMessageSize);
                    if (dhcpOffer.IPAddress != 0)
                        options[3] = new DhcpOption(DhcpOptionCode.RequestedIPAddress, requestedIPAddress);
                    if (dhcpOffer.ServerIdentifier != 0)
                        options[4] = new DhcpOption(DhcpOptionCode.ServerIdentifier, serverIdentifier);

                    // send DHCP message
                    SendDhcpMessage(socket, DhcpMessageType.DHCPREQUEST, dhcpServerIPAddress, dhcpOffer.TransactionID, secondsElapsed, clientIPAddress, _physicalAddress, options, timeoutInMachineTicks);

                    // wait for ACK/NAK
                    bool responseIsAck;
                    bool ackNakReceived = RetrieveAckNak(socket, dhcpOffer.TransactionID, _physicalAddress, ref dhcpOffer, out responseIsAck, nextRetryInMachineTicks);

                    if (ackNakReceived)
                    {
                        success = responseIsAck;
                        break;
                    }

                    secondsElapsed += nextRetrySeconds;
                    nextRetrySeconds *= 2;
                    nextRetryInMachineTicks = (Int64)(Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks + (((double)nextRetrySeconds + GenerateRandomPlusMinusOne()) * TimeSpan.TicksPerSecond));
                }
            }
            finally
            {
                // close the reserved socket
                _ipv4Layer.CloseSocket(socketHandle);
            }

            return success;
        }
        bool RetrieveAckNak(UdpSocket socket, UInt32 transactionID, UInt64 clientHardwareAddress, ref DhcpOffer dhcpOffer, out bool responseIsAck, Int64 timeoutInMachineTicks)
        {
            // set default ack/nak response
            responseIsAck = false;

            UInt32 assignedIPAddress;
            DhcpOption[] options;
            bool success = RetrieveDhcpMessage(socket, new DhcpMessageType[] { DhcpMessageType.DHCPACK, DhcpMessageType.DHCPNAK }, transactionID, clientHardwareAddress, out assignedIPAddress, out options, timeoutInMachineTicks);
            if (success == false)
            {
                return false; /* timeout */
            }

            // analyze return value and update offer values if necessary
            for (int iOption = 0; iOption < options.Length; iOption++)
            {
                switch (options[iOption].Code)
                {
                    case DhcpOptionCode.DhcpMessageType:
                        {
                            if (options[iOption].Value.Length >= 1)
                            {
                                responseIsAck = ((DhcpMessageType)options[iOption].Value[0] == DhcpMessageType.DHCPACK);
                            }
                        }
                        break;
                    case DhcpOptionCode.SubnetMask:
                        {
                            if (options[iOption].Value.Length >= 4)
                            {
                                dhcpOffer.SubnetMask = (
                                    (UInt32)(options[iOption].Value[0] << 24) +
                                    (UInt32)(options[iOption].Value[1] << 16) +
                                    (UInt32)(options[iOption].Value[2] << 8) +
                                    (UInt32)(options[iOption].Value[3])
                                    );
                            }
                        }
                        break;
                    case DhcpOptionCode.Router:
                        {
                            /* NOTE: this option may include multiple router addresses; we select the first entry as our default gateway */
                            if (options[iOption].Value.Length >= 4)
                            {
                                dhcpOffer.GatewayAddress = (
                                    (UInt32)(options[iOption].Value[0] << 24) +
                                    (UInt32)(options[iOption].Value[1] << 16) +
                                    (UInt32)(options[iOption].Value[2] << 8) +
                                    (UInt32)(options[iOption].Value[3])
                                    );
                            }
                        }
                        break;
                    case DhcpOptionCode.DomainNameServer:
                        {
                            /* NOTE: this option may include multiple DNS servers; we will only select up to the first two servers as our default DNS servers */
                            System.Collections.ArrayList dnsServerArrayList = new System.Collections.ArrayList();
                            for (int iDnsServer = 0; iDnsServer < options[iOption].Value.Length; iDnsServer += 4)
                            {
                                if (options[iOption].Value.Length >= iDnsServer + 4)
                                {
                                    UInt32 dnsServerAddress = (
                                        (UInt32)(options[iOption].Value[iDnsServer + 0] << 24) +
                                        (UInt32)(options[iOption].Value[iDnsServer + 1] << 16) +
                                        (UInt32)(options[iOption].Value[iDnsServer + 2] << 8) +
                                        (UInt32)(options[iOption].Value[iDnsServer + 3])
                                        );
                                    dnsServerArrayList.Add(dnsServerAddress);
                                }
                            }
                            dhcpOffer.DnsAddresses = (UInt32[])dnsServerArrayList.ToArray(typeof(UInt32));
                        }
                        break;
                    case DhcpOptionCode.IPAddressLeaseTime:
                        {
                            if (options[iOption].Value.Length >= 4)
                            {
                                dhcpOffer.LeaseExpirationTimeInSeconds = (
                                    (UInt32)(options[iOption].Value[0] << 24) +
                                    (UInt32)(options[iOption].Value[1] << 16) +
                                    (UInt32)(options[iOption].Value[2] << 8) +
                                    (UInt32)(options[iOption].Value[3])
                                    );
                            }
                        }
                        break;
                    case DhcpOptionCode.ServerIdentifier:
                        {
                            if (options[iOption].Value.Length >= 4)
                            {
                                dhcpOffer.ServerIdentifier = (
                                    (UInt32)(options[iOption].Value[0] << 24) +
                                    (UInt32)(options[iOption].Value[1] << 16) +
                                    (UInt32)(options[iOption].Value[2] << 8) +
                                    (UInt32)(options[iOption].Value[3])
                                    );
                            }
                        }
                        break;
                    case DhcpOptionCode.RenewalTimeValue:
                        {
                            if (options[iOption].Value.Length >= 4)
                            {
                                dhcpOffer.LeaseRenewalTimeInSeconds = (
                                    (UInt32)(options[iOption].Value[0] << 24) +
                                    (UInt32)(options[iOption].Value[1] << 16) +
                                    (UInt32)(options[iOption].Value[2] << 8) +
                                    (UInt32)(options[iOption].Value[3])
                                    );
                            }
                        }
                        break;
                    case DhcpOptionCode.RebindingTimeValue:
                        {
                            if (options[iOption].Value.Length >= 4)
                            {
                                dhcpOffer.LeaseRebindingTimeInSeconds = (
                                    (UInt32)(options[iOption].Value[0] << 24) +
                                    (UInt32)(options[iOption].Value[1] << 16) +
                                    (UInt32)(options[iOption].Value[2] << 8) +
                                    (UInt32)(options[iOption].Value[3])
                                    );
                            }
                        }
                        break;
                    case DhcpOptionCode.ClientIdentifier:
                        /* NOTE: we ignore this since our hardware address matches, although if we ever need to verify it then we can do so here */
                        break;
                }
            }

            return true; /* success */
        }
        void DhcpStateMachine()
        {
            while (true)
            {
                // wait for a change which requires the DHCPv4 state machine to process data
                _dhcpStateMachineEvent.WaitOne();

                if (_isDisposed)
                    return;

                if (_linkState == false)
                {
                    if (_isDhcpIpConfigEnabled && _dhcpStateMachineState != DhcpStateMachineState.InitReboot)
                    {
                        if (IpConfigChanged != null)
                            IpConfigChanged(this, 0, 0, 0);

                        if (_isDhcpDnsConfigEnabled)
                        {
                            if (DnsConfigChanged != null)
                                DnsConfigChanged(this, new UInt32[] { });
                        }
                    }

                    _dhcpStateMachineState = DhcpStateMachineState.InitReboot;
                }
                else /* if(_linkState == true) */
                {
                    // if DHCPv4 config is enabled, process our state machine now.
                    if (_isDhcpIpConfigEnabled)
                    {
                        Int64 currentMachineTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                        if (currentMachineTicks > _leaseExpirationTimeInMachineTicks)
                        {
                            // lease has expired
                            _ipConfigIPAddress = 0;
                            _ipConfigSubnetMask = 0;
                            _ipConfigGatewayAddress = 0;
                            _dhcpServerAddress = 0;
                            _leaseRenewalTimeInMachineTicks = Int64.MaxValue;
                            _leaseRebindingTimeInMachineTicks = Int64.MaxValue;
                            _leaseExpirationTimeInMachineTicks = Int64.MaxValue;
                            _dhcpStateMachineState = DhcpStateMachineState.InitReboot;

                            _dhcpStateMachineEvent.Set(); /* restart DHCP inquiry process */
                            continue;
                        }

                        switch (_dhcpStateMachineState)
                        {
                            case DhcpStateMachineState.InitReboot:
                                {
                                    /* we are now discovering DhcpServers and collecting offers */
                                    UInt16 secondsElapsed;
                                    DhcpOffer[] dhcpOffers = SendDhcpDiscoverAndCollectOffers(out secondsElapsed, Int64.MaxValue);

                                    if (dhcpOffers.Length == 0)
                                    {
                                        _dhcpStateMachineState = DhcpStateMachineState.InitReboot;
                                        _dhcpStateMachineEvent.Set(); /* restart DHCP inquiry process */
                                        break;
                                    }

                                    // we received one or more dhcp offers; generate a DhcpRequest now.
                                    DhcpOffer dhcpOffer = dhcpOffers[0];

                                    bool success = SendDhcpRequestAndWaitForAck(dhcpOffer, secondsElapsed, 0xFFFFFFFF, 0, Int64.MaxValue);
                                    if (!success)
                                    {
                                        _dhcpStateMachineState = DhcpStateMachineState.InitReboot;
                                        _dhcpStateMachineEvent.Set(); /* restart DHCP inquiry process */
                                        break;
                                    }

                                    // we have set our new IP address!
                                    _dhcpServerAddress = dhcpOffer.ServerIdentifier;
                                    _ipConfigIPAddress = dhcpOffer.IPAddress;
                                    _ipConfigSubnetMask = dhcpOffer.SubnetMask;
                                    _ipConfigGatewayAddress = dhcpOffer.GatewayAddress;
                                    _dhcpStateMachineState = DhcpStateMachineState.Bound;

                                    /* configure our renewing/rebinding/expiration timers */
                                    if (dhcpOffer.LeaseExpirationTimeInSeconds == UInt32.MaxValue)
                                    {
                                        _leaseExpirationTimeInMachineTicks = Int64.MaxValue;
                                        _leaseRenewalTimeInMachineTicks = Int64.MaxValue;
                                        _leaseRebindingTimeInMachineTicks = Int64.MaxValue;
                                    }
                                    else
                                    {
                                        _leaseExpirationTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseExpirationTimeInSeconds * TimeSpan.TicksPerSecond);
                                        _leaseRenewalTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseRenewalTimeInSeconds * TimeSpan.TicksPerSecond);
                                        _leaseRebindingTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseRebindingTimeInSeconds * TimeSpan.TicksPerSecond);

                                        _leaseUpdateTimer.Change((Int32)((_leaseRenewalTimeInMachineTicks - currentMachineTicks) / TimeSpan.TicksPerMillisecond), System.Threading.Timeout.Infinite);
                                    }

                                    if (IpConfigChanged != null)
                                    {
                                        IpConfigChanged(this, _ipConfigIPAddress, _ipConfigGatewayAddress, _ipConfigSubnetMask);
                                    }
                                    if ((_isDhcpDnsConfigEnabled) && (DnsConfigChanged != null))
                                    {
                                        DnsConfigChanged(this, dhcpOffer.DnsAddresses);
                                    }
                                }
                                break;
                            case DhcpStateMachineState.Bound:
                                {
                                    if (currentMachineTicks > _leaseRenewalTimeInMachineTicks)
                                    {
                                        _dhcpStateMachineState = DhcpStateMachineState.Renewing;

                                        DhcpOffer dhcpOffer = new DhcpOffer(GenerateRandomTransactionID(), 0, 0, 0, 0, new UInt32[0]);
                                        bool success = SendDhcpRequestAndWaitForAck(dhcpOffer, 0, _dhcpServerAddress, _ipConfigIPAddress, Int64.MaxValue);
                                        if (success)
                                        {
                                            _dhcpStateMachineState = DhcpStateMachineState.Bound;

                                            /* configure our renewing/rebinding/expiration timers */
                                            if (dhcpOffer.LeaseExpirationTimeInSeconds == UInt32.MaxValue)
                                            {
                                                _leaseExpirationTimeInMachineTicks = Int64.MaxValue;
                                                _leaseRenewalTimeInMachineTicks = Int64.MaxValue;
                                                _leaseRebindingTimeInMachineTicks = Int64.MaxValue;
                                            }
                                            else
                                            {
                                                _leaseExpirationTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseExpirationTimeInSeconds * TimeSpan.TicksPerSecond);
                                                _leaseRenewalTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseRenewalTimeInSeconds * TimeSpan.TicksPerSecond);
                                                _leaseRebindingTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseRebindingTimeInSeconds * TimeSpan.TicksPerSecond);
                                            }
                                        }
                                        else
                                        {
                                            _leaseUpdateTimer.Change((Int32)((_leaseRebindingTimeInMachineTicks - currentMachineTicks) / TimeSpan.TicksPerMillisecond), System.Threading.Timeout.Infinite);
                                        }
                                    }
                                    else
                                    {
                                        _leaseUpdateTimer.Change((Int32)((_leaseRenewalTimeInMachineTicks - currentMachineTicks) / TimeSpan.TicksPerMillisecond), System.Threading.Timeout.Infinite);
                                    }
                                }
                                break;
                            case DhcpStateMachineState.Renewing:
                                {
                                    if (currentMachineTicks > _leaseRebindingTimeInMachineTicks)
                                    {
                                        _dhcpStateMachineState = DhcpStateMachineState.Rebinding;

                                        DhcpOffer dhcpOffer = new DhcpOffer(GenerateRandomTransactionID(), 0, 0, 0, 0, new UInt32[0]);
                                        bool success = SendDhcpRequestAndWaitForAck(dhcpOffer, 0, 0xFFFFFFFF, _ipConfigIPAddress, Int64.MaxValue);
                                        if (success)
                                        {
                                            _dhcpStateMachineState = DhcpStateMachineState.Bound;

                                            /* configure our renewing/rebinding/expiration timers */
                                            if (dhcpOffer.LeaseExpirationTimeInSeconds == UInt32.MaxValue)
                                            {
                                                _leaseExpirationTimeInMachineTicks = Int64.MaxValue;
                                                _leaseRenewalTimeInMachineTicks = Int64.MaxValue;
                                                _leaseRebindingTimeInMachineTicks = Int64.MaxValue;
                                            }
                                            else
                                            {
                                                _leaseExpirationTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseExpirationTimeInSeconds * TimeSpan.TicksPerSecond);
                                                _leaseRenewalTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseRenewalTimeInSeconds * TimeSpan.TicksPerSecond);
                                                _leaseRebindingTimeInMachineTicks = currentMachineTicks + (dhcpOffer.LeaseRebindingTimeInSeconds * TimeSpan.TicksPerSecond);
                                            }
                                        }
                                        else
                                        {
                                            _leaseUpdateTimer.Change((Int32)((_leaseExpirationTimeInMachineTicks - currentMachineTicks) / TimeSpan.TicksPerMillisecond), System.Threading.Timeout.Infinite);
                                        }
                                    }
                                    else
                                    {
                                        _leaseUpdateTimer.Change((Int32)((_leaseRebindingTimeInMachineTicks - currentMachineTicks) / TimeSpan.TicksPerMillisecond), System.Threading.Timeout.Infinite);
                                    }
                                }
                                break;
                            //case DhcpStateMachineState.Rebinding:
                            //    break;
                        }
                    }
                }
            }
        }