[En-Nut-Discussion] Improvement of DHCP code

wiegelj wiegelj at gmx.de
Thu Nov 21 10:07:01 CET 2019


Hi folks,

I don't know if attachments are working in the mailing list, but I want
to share an improved version of the dhcp software in the attachement. It
is derived from the ethernut-5.1.0-1 version and has an additional fix
from Henrik (Thank you for this).

I hope that someone is checking in the source. I spent a lot of time
looking for problems in the dhcp code.

Best regards

Jörg

-------------- next part --------------
/*
 * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * For additional information see http://www.ethernut.de/
 *
 * -
 * Portions Copyright (c) 1983, 1993 by
 *  The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * -
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

/*!
 * \file pro/dhcpc.c
 * \brief DHCP client.
 *
 * \verbatim
 *
 * $Log$
 * Revision 1.27  2009/02/13 14:52:05  haraldkipp
 * Include memdebug.h for heap management debugging support.
 *
 * Revision 1.26  2009/02/06 15:37:40  haraldkipp
 * Added stack space multiplier and addend. Adjusted stack space.
 *
 * Revision 1.25  2009/01/17 11:26:52  haraldkipp
 * Getting rid of two remaining BSD types in favor of stdint.
 * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
 *
 * Revision 1.24  2008/10/05 16:46:15  haraldkipp
 * Added missing attributes 'packed'. This may fix a problem with
 * different ARM compiler optimization settings.
 *
 * Revision 1.23  2008/08/11 07:00:34  haraldkipp
 * BSD types replaced by stdint types (feature request #1282721).
 *
 * Revision 1.22  2006/08/01 07:38:41  haraldkipp
 * DHCP client failed because of alignment errors. Removed the dispensable
 * 'packed' attribute from the dyn_cfg structure.
 *
 * Revision 1.21  2006/01/23 19:52:10  haraldkipp
 * Added required typecasts before left shift.
 *
 * Revision 1.20  2006/01/23 17:35:54  haraldkipp
 * BOOTP and DYNCFG structures must be packed.
 * Fixed memory alignment bug, which retrieved wrong values from DHCP options.
 *
 * Revision 1.19  2005/08/02 17:47:04  haraldkipp
 * Major API documentation update.
 *
 * Revision 1.18  2005/04/05 17:44:57  haraldkipp
 * Made stack space configurable.
 *
 * Revision 1.17  2005/03/28 18:26:59  mrjones4u
 * Fixed non-release bug in DCHP client
 *
 * Revision 1.16  2005/02/03 14:33:56  haraldkipp
 * Several bug fixes and enhancements. The most important fix will
 * avoid hanging, when Ethernut is reset while ICMP pings are
 * received. A large number of changes make the client to follow
 * RFC 2131 more closely, like maintaining renewal and rebind time.
 * Two new API calls, NutDhcpStatus() and NutDhcpError(), allow
 * to query the current status of the DHCP client. The previously
 * introduced API call NutDhcpIsConfigured() has been marked
 * deprecated. Another problem was with timeout, because the client
 * continued negotiation with the server after the timeout time
 * given in the API called elapsed. Now all internal timeouts are
 * adjusted to the API timeout. Furthermore the previous version
 * let the client continue on fatal errors like UDP receive or UDP
 * broadcast failures. Now the client stops on such errors and
 * informs the caller. Finally the documentation has been enhanced.
 *
 * Revision 1.15  2004/12/31 10:51:40  drsung
 * Bugfixes from Michel Hendriks applied:
 * Allocate empty dyncfg's to prevent freeing twice of domain/hostname
 * Clear pointer after ReleaseDynCfg to prevent freeing twice
 * Make sure SelectOffer is never called with 0 offers
 * When falling back from rebooting to selecting, clear
 * the received ip address so NutDhcpIfConfig does not
 * assume it has been configured properly
 *
 * Revision 1.14  2004/04/15 07:16:47  drsung
 * Now it works... :-/
 *
 * Revision 1.13  2004/04/15 07:11:50  drsung
 * Added hostname support to NutDHCPDiscover
 *
 * Revision 1.12  2004/03/18 11:24:01  haraldkipp
 * Deprecated functions removed
 *
 * Revision 1.11  2004/03/03 17:53:28  drsung
 * Support for new field 'hostname' in structure confos added.
 *
 * Revision 1.10  2004/02/25 16:34:32  haraldkipp
 * New API added to relinguish the DHCP lease. Collecting more
 * than one offer is now disabled, if the application sets timeout
 * below three times of MAX_DHCP_WAIT (5 seconds). The lease renewal
 * will now start when 3/4 has elapsed, opposed to 1/2 used previously.
 * Finally the DHCP thread will sleep as long as possible, while the
 * previous version woke up every ten seconds.
 *
 * Revision 1.9  2004/02/02 18:54:43  drsung
 * gateway ip address was not set, if static network configuration from EEPROM is used.
 *
 * Revision 1.8  2004/01/14 17:50:41  drsung
 * Fix for Win2k DHCP server applied. Thanks to Damian Slee
 *
 * Revision 1.7  2003/11/03 16:22:59  haraldkipp
 * Renewal disabled if lease time is set to zero
 *
 * Revision 1.6  2003/10/13 10:18:08  haraldkipp
 * Using new seconds counter
 *
 * Revision 1.5  2003/08/07 09:09:08  haraldkipp
 * Redesign to follow RFC 2131 more closely.
 *
 * Revision 1.4  2003/07/20 18:25:40  haraldkipp
 * Support secondary DNS.
 *
 * Revision 1.3  2003/07/17 09:44:14  haraldkipp
 * Removed default MAC address and let the caller have a second chance.
 *
 * Revision 1.2  2003/05/15 14:25:38  haraldkipp
 * Some DHCP servers discard discover telegrams.
 * Thanks to Tomohiro Haraikawa.
 *
 * Revision 1.1.1.1  2003/05/09 14:41:57  haraldkipp
 * Initial using 3.2.1
 *
 * \endverbatim
 */

#include <cfg/os.h>
#include <sys/thread.h>
#include <sys/event.h>
#include <sys/timer.h>
#include <sys/confnet.h>
#include <sys/confos.h>

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <memdebug.h>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netdb.h>
#include <net/route.h>
#include <sys/socket.h>
#include <pro/dhcp.h>

#ifdef NUTDEBUG
#include <net/netdebug.h>
#endif

#if 0
/* Use for local debugging. */
#define NUTDEBUG
#include <stdio.h>
#define __tcp_trs stdout
static uint_fast8_t __tcp_trf = 1;
#endif

/*!
 * \addtogroup xgDHCPC
 */
/*@{*/

/*!
 * \name DHCP Client Configuration
 *
 * The Nut/OS Configurator may be used to override the default values.
 */
/*@{*/

/*!
 * \brief UDP port of DHCP server.
 *
 * \showinitializer
 */
#ifndef DHCP_SERVERPORT
#define DHCP_SERVERPORT      67
#endif

/*!
 * \brief UDP port of DHCP client.
 *
 * \showinitializer
 */
#ifndef DHCP_CLIENTPORT
#define DHCP_CLIENTPORT      68
#endif

/*!
 * \brief Maximum DHCP message size we can accept.
 *
 * RFC 2131 demands, that a DHCP client must be prepared to receive DHCP
 * messages with an options field length of at least 312 octets. This
 * implies that we must be able to accept messages of up to 576 octets.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_MSGSIZE
#define MAX_DHCP_MSGSIZE    576
#endif

/*!
 * \brief Minimum DHCP message length.
 *
 * Used to maintain BOOTP compatibility of outgoing messages.
 *
 * \showinitializer
 */
#ifndef MIN_DHCP_MSGSIZE
#define MIN_DHCP_MSGSIZE    300
#endif

/*!
 * \brief Maximum UDP buffer size used by the DHCP client.
 *
 * If this item is not equal zero, the DHCP client will use its value to
 * set #SO_RCVBUF by calling NutUdpSetSockOpt().
 *
 * If this item is set to zero, NutUdpSetSockOpt() is not called and the
 * UDP socket interface will buffer the last incoming datagram on the
 * #DHCP_CLIENTPORT socket port only. Any previously received datagram is
 * silently discarded. As long as one DHCP server is expected in the local
 * network, this will be fine and save some heap memory while DHCP is
 * active.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_BUFSIZE
#define MAX_DHCP_BUFSIZE    1728
#endif

/*!
 * \brief Minimum number of milliseconds to wait for a response.
 *
 * If we receive no response from a DHCP server within this time, we
 * will double this value up to \ref MAX_DHCP_WAIT and repeat our
 * request up to \ref MAX_DCHP_RETRIES times before giving up.
 *
 * \showinitializer
 */
#ifndef MIN_DHCP_WAIT
#define MIN_DHCP_WAIT       4000
#endif

/*!
 * \brief Maximum number of milliseconds to wait for a response.
 *
 * The timeout value for receiving server responses will be doubled
 * on each retry but limited by this value.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_WAIT
#define MAX_DHCP_WAIT       64000
#endif

/*!
 * \brief Maximum number of request retries.
 *
 * We will give up after resending this number of requests without
 * receiving a response.
 *
 * \showinitializer
 */
#ifndef MAX_DCHP_RETRIES
#define MAX_DCHP_RETRIES    3
#endif

/*!
 * \brief Maximum number of release retries.
 *
 * RFC 2131 doesn't specify a server response to release messages from
 * the client. If the message gets lost, then the lease isn't released.
 *
 * \showinitializer
 */
#ifndef MAX_DCHP_RELEASE_RETRIES
#define MAX_DCHP_RELEASE_RETRIES    0
#endif

/*!
 * \brief Default lease time in seconds.
 *
 * This value is used if the server doesn't provide a lease time.
 *
 * \showinitializer
 */
#ifndef DHCP_DEFAULT_LEASE
#define DHCP_DEFAULT_LEASE  43200
#endif

/*!
 * \brief Maximum sleep time in seconds.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_NAPTIME
#define MAX_DHCP_NAPTIME    4294967
#endif

/*!
 * \brief Stack size of the DHCP client thread.
 *
 * \showinitializer
 */
#ifndef NUT_THREAD_DHCPSTACK
#if defined(__AVR__)
#if defined(__GNUC__)
/* avr-gcc size optimized code used 192 bytes. */
#define NUT_THREAD_DHCPSTACK    288
#else
/* icc-avr v7.19 used 360 bytes. */
#define NUT_THREAD_DHCPSTACK    512
#endif
#else
/* arm-elf-gcc used 276 bytes with size optimized, 680 bytes with debug code. */
/* arm-none-eabi creates stack overflow with 384 bytes on EIR 1.0 board. */
#define NUT_THREAD_DHCPSTACK    512
#endif
#endif

/*@}*/

/*!
 * \name DHCP Message Types
 *
 * See RFC 2131.
 */
/*@{*/

/*! \brief Client broadcast to locate available servers.
 */
#define DHCP_DISCOVER   1

/*! \brief Server to client in response to DHCP_DISCOVER.
 *
 * Contains an offer of configuration parameters.
 */
#define DHCP_OFFER      2

/*! \brief Client message to servers.
 *
 * Used for
 * - requesting offered parameters from one server and implicitly declining offers from all others.
 * - confirming correctness of previously allocated address after, e.g., system reboot.
 * - extending the lease on a particular network address.
 */
#define DHCP_REQUEST    3

/*! \brief Client to server indicating network address is already in use.
 *
 * Not used by Nut/Net.
 */
#define DHCP_DECLINE    4

/*! \brief Server to client with configuration parameters.
 *
 * Contains committed network address.
 */
#define DHCP_ACK        5

/*! \brief Server to client indicating client's notion of network address is incorrect.
 *
 * May be caused by the client's move to new subnet or by expiration of the client's lease.
 */
#define DHCP_NAK        6

/*! \brief Client to server relinquishing network address and cancelling remaining lease.
 */
#define DHCP_RELEASE    7

/*! \brief Client to server, asking only for local configuration parameters.
 *
 * Used, if the client already has externally configured network address.
 */
#define DHCP_INFORM     8

/*@}*/


/*!
 * \name DHCP Options
 *
 * Nut/Net recognizes a subset of options defined in RFC 2132.
 */
/*@{*/

/*!
 * \brief DHCP pad option.
 *
 * The pad option can be used to cause subsequent fields to align on
 * word boundaries.
 */
#define DHCPOPT_PAD          0

/*!
 * \brief DHCP subnet mask option.
 */
#define DHCPOPT_NETMASK      1

/*!
 * \brief DHCP router option.
 */
#define DHCPOPT_GATEWAY      3

/*!
 * \brief DHCP domain name server option.
 */
#define DHCPOPT_DNS          6

/*!
 * \brief DHCP host name option.
 */
#define DHCPOPT_HOSTNAME     12

/*!
 * \brief DHCP domain name option.
 */
#define DHCPOPT_DOMAIN       15

/*!
 * \brief DHCP broadcast address option.
 */
#define DHCPOPT_BROADCAST    28

/*!
 * \brief DHCP requested IP address option.
 */
#define DHCPOPT_REQESTIP     50

/*!
 * \brief DHCP IP address lease time option.
 */
#define DHCPOPT_LEASETIME    51

/*!
 * \brief DHCP message type option.
 */
#define DHCPOPT_MSGTYPE      53

/*!
 * \brief DHCP server identifier option.
 */
#define DHCPOPT_SID          54

/*!
 * \brief DHCP parameter request list option.
 */
#define DHCPOPT_PARAMREQUEST 55

/*!
 * \brief Maximum DHCP message size option.
 */
#define DHCPOPT_MAXMSGSIZE   57

/*!
 * \brief DHCP renewal time option.
 */
#define DHCPOPT_RENEWALTIME  58

/*!
 * \brief DHCP rebinding time option.
 */
#define DHCPOPT_REBINDTIME   59

/*!
 * \brief DHCP end option.
 */
#define DHCPOPT_END          255

/*@}*/

/*!
 * \brief BOOTP message structure type.
 */
typedef struct bootp BOOTP;

/*!
 * \brief BOOTP message structure.
 */
struct __attribute__ ((packed)) bootp {
    uint8_t bp_op;              /*!< \brief Packet opcode type: 1=request, 2=reply */
    uint8_t bp_htype;           /*!< \brief Hardware address type: 1=Ethernet */
    uint8_t bp_hlen;            /*!< \brief Hardware address length: 6 for Ethernet */
    uint8_t bp_hops;            /*!< \brief Gateway hops */
    uint32_t bp_xid;              /*!< \brief Transaction ID */
    uint16_t bp_secs;            /*!< \brief Seconds since boot began */
    uint16_t bp_flags;           /*!< \brief RFC1532 broadcast, etc. */
    uint32_t bp_ciaddr;           /*!< \brief Client IP address */
    uint32_t bp_yiaddr;           /*!< \brief 'Your' IP address */
    uint32_t bp_siaddr;           /*!< \brief Server IP address */
    uint32_t bp_giaddr;           /*!< \brief Gateway IP address */
    uint8_t bp_chaddr[16];      /*!< \brief Client hardware address */
    char bp_sname[64];          /*!< \brief Server host name */
    char bp_file[128];          /*!< \brief Boot file name */
    uint8_t bp_options[312];    /*!< \brief Vendor-specific area */
};

/*!
 * \brief Dynamic configuration structure type.
 */
typedef struct dyn_cfg DYNCFG;

/*!
 * \brief Dynamic configuration structure.
 */
struct dyn_cfg {
    uint8_t dyn_msgtyp;         /*!< \brief DHCP message type */
    uint32_t dyn_yiaddr;          /*!< \brief Offered IP address. */
    uint32_t dyn_netmask;         /*!< \brief Local IP netmask. */
    uint32_t dyn_broadcast;       /*!< \brief Local IP broadcast address. */
    uint32_t dyn_gateway;         /*!< \brief Default gate IP address. */
    uint32_t dyn_pdns;            /*!< \brief Primary DNS IP address. */
    uint32_t dyn_sdns;            /*!< \brief Secondary DNS IP address. */
    uint32_t dyn_sid;             /*!< \brief Server identifier. */
    uint32_t dyn_renewalTime;     /*!< \brief Renewal time in seconds. */
    uint32_t dyn_rebindTime;      /*!< \brief Rebind time in seconds. */
    uint32_t dyn_leaseTime;       /*!< \brief Offered lease time in seconds. */
    uint8_t *dyn_hostname;      /*!< \brief Local hostname. */
    uint8_t *dyn_domain;        /*!< \brief Name of local domain. */
};

/*!
 * \brief Current configuration.
 *
 * This structure is filled by parsing offer or acknowledge messages from
 * the server.
 */
static DYNCFG *dhcpConfig;

/*!
 * \brief Client thread identifier.
 *
 * Used to determine if the client thread is running. Zero indicates that
 * it is not.
 */
static HANDLE dhcpThread;

/*!
 * \brief Current state of the DHCP client state machine.
 */
static uint8_t dhcpState;

/*!
 * \brief Latest DHCP error code.
 */
static int dhcpError;

/*!
 * \brief DHCP wake up queue.
 *
 * The DHCP state machine can be woken up by posting to this queue.
 */
static HANDLE dhcpWake;

/*!
 * \brief DHCP waiting queue.
 *
 * Application threads wait on this queue until DHCP success or failure.
 */
static HANDLE dhcpDone;

/*!
 * \brief Maximum number of milliseconds to wait on \ref dhcpDone.
 *
 * Specified by the application when calling the DHCP client API.
 */
static uint32_t dhcpApiTimeout;

/*!
 * \brief Time at which the application started to wait.
 *
 * Used in conjunction with \ref dhcpApiTimeout to limit the maximum
 * wait time for server responses.
 */
static uint32_t dhcpApiStart;

/*!
 * \brief DHCP device.
 *
 * The ARM port doesn't provide parameter passing to thread routines.
 * Thus we use a global variable to store the NUTDEVICE pointer.
 */
#ifdef __arm__
static NUTDEVICE *dhcpDev;
#endif

/*!
 * \brief Dynamic string copy.
 *
 * \param dst Points to a string pointer. If the pointer is not NULL, it
 *            is assumed that it points to a previously allocated buffer
 *            and this buffer will be released first. Then a new buffer
 *            will be allocated and the source string will be copied to
 *            this buffer.
 * \param src The source string. No delimiter required.
 * \param len Length of the source string.
 */
static void copy_str(uint8_t ** dst, void *src, int len)
{
    if (*dst) {
        free(*dst);
    }
    if ((*dst = malloc(len + 1)) != 0) {
        if (len) {
            memcpy(*dst, src, len);
        }
        *(*dst + len) = 0;
    }
}

/*!
 * \brief Release DYNCFG structure.
 *
 * Frees all memory occupied by a \ref DYNCFG structure.
 *
 * \param dyncfg This structure will be released.
 */
static void ReleaseDynCfg(DYNCFG * dyncfg)
{
    if (dyncfg) {
        if (dyncfg->dyn_hostname) {
            free(dyncfg->dyn_hostname);
        }
        if (dyncfg->dyn_domain) {
            free(dyncfg->dyn_domain);
        }
        free(dyncfg);
    }
}

/*!
 * \brief Parse a DHCP reply message.
 *
 * \param bp  Pointer to the reply message. The caller must make sure,
 *            that this contains a valid BOOTP reply header with at
 *            least five bytes in the options field.
 * \param len Number of valid bytes in the reply message.
 *
 * \return Pointer to config structure. Must be released by the caller.
 *         NULL is returned in case of parsing errors.
 */
static DYNCFG *ParseReply(BOOTP *bp, int len)
{
    uint8_t *op;
    int left;
    DYNCFG *cfgp;

    /* Allocate and initialize a new structure. */
    if ((cfgp = malloc(sizeof(DYNCFG))) == 0) {
        return 0;
    }
    memset(cfgp, 0, sizeof(DYNCFG));
    cfgp->dyn_leaseTime = DHCP_DEFAULT_LEASE;

    /* Set the assigned IP address. */
    memcpy(&cfgp->dyn_yiaddr, &bp->bp_yiaddr, 4);

    /*
     * Parse options until an end option is found or until we reached
     * the end of the message.
     */
    op = bp->bp_options + 4;
    left = len - (sizeof(*bp) - sizeof(bp->bp_options)) - 4;
    while (*op != DHCPOPT_END && left > 0) {
        uint8_t ol;

#ifdef NUTDEBUG
        if (__tcp_trf) {
            fprintf(__tcp_trs, "[DHCP-Opt-%u]", *op);
        }
#endif
        /* Pad option is used for boundary alignment. */
        if (*op == DHCPOPT_PAD) {
            op++;
            left--;
            continue;
        }

        /* Reject if option length exceeds total length. */
        if ((ol = *(op + 1)) > left) {
            break;
        }

        /* Type of this message. */
        if (*op == DHCPOPT_MSGTYPE) {
            if (ol != 1) {
                break;
            }
            cfgp->dyn_msgtyp = *(op + 2);

#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) {
                switch(cfgp->dyn_msgtyp) {
                    case DHCP_OFFER:
                        fprintf(__tcp_trs, "+MSGT-OFFER+\n");
                        break;
                    case DHCP_ACK:
                        fprintf(__tcp_trs, "+MSGT-ACK+\n");
                        break;
                    case DHCP_NAK:
                        fprintf(__tcp_trs, "-MSGT-NAK-\n");
                        break;
                    default:
                        fprintf(__tcp_trs, "#MSGT-UNK#\n");
                        break;
                }
            }
#endif
        }
        /* Our host name. May or may not include the domain. */
        else if (*op == DHCPOPT_HOSTNAME) {
            copy_str(&cfgp->dyn_hostname, op + 2, ol);
        }
        /* Name of the domain we are in. */
        else if (*op == DHCPOPT_DOMAIN) {
            copy_str(&cfgp->dyn_domain, op + 2, ol);
        }

        /* All remaining options require at least 4 octets. */
        else if (ol >= 4) {
            /* Preset most often used long value. */
            uint32_t lval = *(op + 2);
            lval += (uint32_t)(*(op + 3)) << 8;
            lval += (uint32_t)(*(op + 4)) << 16;
            lval += (uint32_t)(*(op + 5)) << 24;

            /* Our IP network mask. */
            if (*op == DHCPOPT_NETMASK) {
                cfgp->dyn_netmask = lval;
            }
            /* Our IP broadcast address. */
            else if (*op == DHCPOPT_BROADCAST) {
                cfgp->dyn_broadcast = lval;
            }
            /* Our IP default gate. More than one gateway may be
               specified. We take the fist one only and ignore the
               rest. */
            else if (*op == DHCPOPT_GATEWAY) {
                cfgp->dyn_gateway = lval;
            }
            /* Our DNS server. Updated by Jelle Martijn Kok to
               support a secondary DNS. */
            else if (*op == DHCPOPT_DNS) {
                cfgp->dyn_pdns = lval;
                if (ol >= 8) {
                    cfgp->dyn_sdns = *(op + 6);
                    cfgp->dyn_sdns += (uint32_t)(*(op + 7)) << 8;
                    cfgp->dyn_sdns += (uint32_t)(*(op + 8)) << 16;
                    cfgp->dyn_sdns += (uint32_t)(*(op + 9)) << 24;
                }
            }
            /* Server identifier. */
            else if (*op == DHCPOPT_SID) {
                cfgp->dyn_sid = lval;
            }
            /* Renewal time. */
            else if (*op == DHCPOPT_RENEWALTIME) {
                cfgp->dyn_renewalTime = ntohl(lval);
            }
            /* Rebinding time. */
            else if (*op == DHCPOPT_REBINDTIME) {
                cfgp->dyn_rebindTime = ntohl(lval);
            }
            /* Total lease time granted. */
            else if (*op == DHCPOPT_LEASETIME) {
                cfgp->dyn_leaseTime = ntohl(lval);
            }
        }
        op += ol + 2;
        left -= ol + 2;
    }

    /*
     * Discard this configuration if parsing stopped before reaching
     * the end option or if we didn't receive an expected message type.
     */
    if (*op != DHCPOPT_END ||
        (cfgp->dyn_msgtyp != DHCP_OFFER &&
         cfgp->dyn_msgtyp != DHCP_ACK &&
         cfgp->dyn_msgtyp != DHCP_NAK)) {
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) {
            fprintf(__tcp_trs, "[DHCP-Parse Error]");
        }
#endif
        ReleaseDynCfg(cfgp);
        return 0;
    }

    /* Calculate renewal and rebind times. */
    if (cfgp->dyn_renewalTime == 0) {
        cfgp->dyn_renewalTime = cfgp->dyn_leaseTime / 2;
    }
    if (cfgp->dyn_rebindTime == 0) {
        cfgp->dyn_rebindTime = cfgp->dyn_renewalTime +  /* */
            cfgp->dyn_renewalTime / 2 + /* */
            cfgp->dyn_renewalTime / 4;
    }
    return cfgp;
}

/*!
 * \brief Add variable length option.
 *
 * \param op  Pointer into the option buffer.
 * \param ot  Option type.
 * \param ov  Pointer to buffer to copy from.
 * \param len Number of octets to copy.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddOption(uint8_t * op, uint8_t ot, void *ov, uint8_t len)
{
    *op++ = ot;
    *op++ = len;
    memcpy(op, ov, len);

    return 2 + len;
}

/*!
 * \brief Add single octet option.
 *
 * \param op Pointer into the option buffer.
 * \param ot Option type.
 * \param ov Option value.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddByteOption(uint8_t * op, uint8_t ot, uint8_t ov)
{
    *op++ = ot;
    *op++ = 1;
    *op++ = ov;

    return 3;
}

/*!
 * \brief Add double octet option.
 *
 * \param op Pointer into the option buffer.
 * \param ot Option type.
 * \param ov Option value in host byte order. Will be converted to network
 *           byte order.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddShortOption(uint8_t * op, uint8_t ot, uint16_t ov)
{
    *op++ = ot;
    *op++ = 2;
    ov = htons(ov);
    memcpy(op, &ov, 2);

    return 4;
}

/*!
 * \brief Add parameter request option.
 *
 * RFC 2131 demands to use the same parameter request in discover and
 * request messages.
 *
 * \param op Pointer into the option buffer.
 * \param ot Option type.
 * \param ov Option value in host byte order.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddParmReqOption(uint8_t * op)
{
    *op++ = DHCPOPT_PARAMREQUEST;
    *op++ = 3;                  /* Adjust when adding more options! */
    *op++ = DHCPOPT_NETMASK;    /* Typically sent by default, but play safe. */
    *op++ = DHCPOPT_GATEWAY;    /* We want a default gateway. */
    *op++ = DHCPOPT_DNS;        /* We want the DNS' IP. */
    return 5;                   /* Adjust when adding more options! */
}

/*!
 * \brief Prepare a BOOTP request message header.
 *
 * A BOOTP header will be initialized at the specified buffer address
 * and the routine will add the DHCP magic cookie (RFC 1497) and an
 * additional option containing the specified DHCP message type.
 *
 * All other fields are cleared to zero.
 *
 * \param bp     Pointer to the buffer to initialize.
 * \param msgtyp DHCP message type, either \ref DHCP_DISCOVER,
 *               \ref DHCP_REQUEST or \ref DHCP_RELEASE.
 * \param xid    Random transaction identifier.
 * \param ciaddr Our IP address. Should be set to zero unless we are in
 *               BOUND, RENEW or REBINDING state and can respond to ARP
 *               requests.
 * \param secs   Seconds elapsed, since we began address acquisition or
 *               renewal.
 *
 * \return Total number of octets added.
 */
static unsigned int DhcpPrepHeader(BOOTP *bp, uint8_t msgtyp, uint32_t xid, uint32_t ciaddr, uint16_t secs)
{
    uint8_t *op;

    memset(bp, 0, sizeof(*bp));
    /* Clients send bootp requests (op code 1) only. */
    bp->bp_op = 1;
    /* Ethernet addresses are type 1 with 6 octets. */
    bp->bp_htype = 1;
    bp->bp_hlen = 6;
    memcpy(bp->bp_chaddr, confnet.cdn_mac, 6);
    /* Transaction identifier. */
    bp->bp_xid = xid;
    /* Seconds elapsed since address acquisition. */
    bp->bp_secs = htons(secs);

#ifdef DHCP_BROADCAST_FLAG
    /*
     * We do not need the broadcast flag, because our stack accepts IP
     * messages to any destination if no local address has been assigned,
     * However, we continue supporting this compile time option.
     */
    bp->bp_flags = htons(0x8000);
#endif

    bp->bp_ciaddr = ciaddr;

    /* Add the DHCP magic cookie according to RFC 1497. */
    op = bp->bp_options;
    *op++ = 0x63;
    *op++ = 0x82;
    *op++ = 0x53;
    *op++ = 0x63;

    /* Add the DHCP message type option. */
    return DhcpAddByteOption(op, DHCPOPT_MSGTYPE, msgtyp) + 4;
}

/*!
 * \brief Send a DHCP message to the server.
 *
 * This routine will also add an end of option identifier and take care,
 * that the message length will not fall below the minimum expected by
 * BOOTP.
 *
 * \param sock Socket descriptor. This pointer must have been
 *             retrieved by calling NutUdpCreateSocket().
 * \param addr Destination IP addres.
 * \param bp   Pointer to a buffer to be used for transmission.
 *             Must contain a fully initialized header and option
 *             fields.
 * \param len  Total number of DHCP option octets.
 *
 * \return 0 on success. On errors -1 is returned and \ref dhcpError will
 *         be set to \ref DHCPERR_TRANSMIT.
 */
static int DhcpSendMessage(UDPSOCKET * sock, uint32_t addr, BOOTP *bp, size_t len)
{
    /* Add 'end of options'. */
    bp->bp_options[len++] = DHCPOPT_END;

    /* Maintain a BOOTP compatible minimum packet size of 300 octets.
       Thanks to Tomohiro Haraikawa. */
    if ((len += sizeof(BOOTP) - sizeof(bp->bp_options)) < MIN_DHCP_MSGSIZE) {
        len = MIN_DHCP_MSGSIZE;
    }
#ifdef NUTDEBUG
    if (__tcp_trf & NET_DBG_DHCP) {
        fprintf(__tcp_trs, "[DHCP-Send to %s]", inet_ntoa(addr));
    }
#endif
    if (NutUdpSendTo(sock, addr, DHCP_SERVERPORT, bp, len) < 0) {
        dhcpError = DHCPERR_TRANSMIT;
        return -1;
    }
    return 0;
}

/*!
 * \brief Receive a DHCP reply from the server.
 *
 * \param sock Socket descriptor.
 * \param xid  Expected transaction identifier. Incoming messages with a
 *             different identifier are silently discarded.
 * \param bp   Pointer to the receive buffer.
 * \param tmo  Maximum number of milliseconds to wait for a valid message.
 *
 * \return The number of bytes received, if successful. The return
 *         value -1 indicates an error, in which case dhcpError is
 *         set to an error code. On timeout 0 is returned.
 */
static int DhcpRecvMessage(UDPSOCKET * sock, uint32_t xid, BOOTP *bp, uint32_t tmo)
{
    int rc;
    uint16_t port;
    uint32_t addr;
    uint32_t etim;
    uint32_t wtim;

    /* Set our start time. */
    etim = NutGetMillis();
    /* Set the initial receive timeout. */
    wtim = tmo;
    for (;;) {
        rc = NutUdpReceiveFrom(sock, &addr, &port, bp, sizeof(BOOTP), wtim);
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) {
            if (rc > 0) {
                fprintf(__tcp_trs, "[DHCP-Recv from %s]", inet_ntoa(addr));
            } else if (rc < 0) {
                fprintf(__tcp_trs, "[DHCP-Recv Error]");
            } else {
                fprintf(__tcp_trs, "[DHCP-Recv Timeout %lu]", tmo);
            }
        }
#endif
        /* Immediately return on receive errors and timeouts. */
        if (rc <= 0) {
            if (rc < 0) {
                dhcpError = DHCPERR_RECEIVE;
            }
            break;
        }
        /* The message must at least include the BOOTP header plus five
           bytes of options (magic and end). We are quite liberal here. */
        if (rc > sizeof(BOOTP) - sizeof(bp->bp_options) + 5) {
            /* The message must be a BOOTP reply with the expected XID. */
            if (bp->bp_op == 2 && bp->bp_xid == xid) {
                /* Message is acceptable. */
                break;
            }
        }
        /* Calculate the remaining timeout for not getting trapped here
           on a busy network, which regularly broadcasts DHCP messages. */
        wtim = NutGetMillis() - etim;
        if (wtim >= tmo - 250) {
            /* Less than 250 ms left, return timeout. */
            rc = 0;
            break;
        }
        wtim = tmo - wtim;
    }
    return rc;
}

/*!
 * \brief Broadcast a DHCP discover message.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved
 *              by calling NutUdpCreateSocket().
 * \param bp    Pointer to a buffer to be used for transmission. No specific
 *              initialization required.
 * \param xid   Random transaction identifier.
 * \param raddr Requested IP address. Optional.
 * \param secs  Seconds elapsed since start of address acquisition. Related
 *              requests must use the same value.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpBroadcastDiscover(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, uint32_t raddr, uint16_t secs)
{
    size_t optlen;
    int len;
    uint8_t *op = bp->bp_options;

    optlen = DhcpPrepHeader(bp, DHCP_DISCOVER, xid, 0, secs);

    /* Request a specific IP if one had been assigned previously. */
    if (raddr) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
    }

    optlen += DhcpAddParmReqOption(op + optlen);

    /* Pass host name if specified in confos structure.
     * Win2k DHCP server can register this as dynamic DNS entry.
     * Also viewing DHCP lease table shows something sensible.
     */
    len = strlen(confos.hostname);
    if (len > 0) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
    }

    /* Request a maximum message size. */
    optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);

    return DhcpSendMessage(sock, INADDR_BROADCAST, bp, optlen);
}


/*!
 * \brief Send a DHCP request message.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by
 *              calling NutUdpCreateSocket().
 * \param daddr IP address of the DHCP server or \ref INADDR_BROADCAST.
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Should be set only if we are able to respond
 *              to ARP requests. Otherwise must be set to 0.
 * \param raddr Requested IP address. Required.
 * \param sid   Server identifier. If this request is not an offer response,
 *              then set it to zero.
 * \param secs  Seconds elapsed since start of address acquisition. If this
 *              request is sent in reponse to an offer, the same value must
 *              be used.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpSendRequest(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid,        /* */
                           uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
{
    size_t optlen;
    int len;
    uint8_t *op = bp->bp_options;

    /* Initialize the BOOTP header. */
    optlen = DhcpPrepHeader(bp, DHCP_REQUEST, xid, caddr, secs);

    /* Add specified options. */
    if (raddr) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
    }
    if (sid) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
    }
    optlen += DhcpAddParmReqOption(op + optlen);

    /* Pass host name if specified in confos structure.  */
    /* viewing DHCP lease table shows something sensible. */
    len = strlen(confos.hostname);
    if (len > 0) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
    }

    return DhcpSendMessage(sock, daddr, bp, optlen);
}

/*!
 * \brief Broadcast a DHCP request message.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by
 *              calling NutUdpCreateSocket().
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Should be set only if we are able to respond
 *              to ARP requests. Otherwise must be set to 0.
 * \param raddr Requested IP address. Required.
 * \param sid   Server identifier. If this request is not an offer response,
 *              then set it to zero.
 * \param secs  Seconds elapsed since start of address acquisition. If this
 *              request is sent in reponse to an offer, the same value must
 *              be used.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpBroadcastRequest(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, /* */
                                uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
{
    return DhcpSendRequest(sock, INADDR_BROADCAST, bp, xid, caddr, raddr, sid, secs);
}

/*!
 * \brief Relinguish our DHCP lease.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by
 *              calling NutUdpCreateSocket().
 * \param daddr IP address of the DHCP server.
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Should be set only if we are able to respond
 *              to ARP requests. Otherwise must be set to 0.
 * \param sid   Server identifier. If this request is not an offer response,
 *              then set it to zero.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpSendRelease(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr, uint32_t sid)
{
    size_t optlen;
    uint8_t *op = bp->bp_options;

    /* Prepare BOOTP header. 'secs' is set to zero. */
    optlen = DhcpPrepHeader(bp, DHCP_RELEASE, xid, caddr, 0);

    /* Optionally add server identifier. */
    if (sid) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
    }
    return DhcpSendMessage(sock, daddr, bp, optlen);
}

/*!
 * \brief Inform DHCP about an externally allocated address.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by
 *              calling NutUdpCreateSocket().
 * \param daddr IP address of the DHCP server or INADDR_BROADCAST.
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Required.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpSendInform(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr)
{
    size_t optlen;
    size_t len;
    uint8_t *op = bp->bp_options;

    /* Prepare BOOTP header. 'secs' is set to zero. */
    optlen = DhcpPrepHeader(bp, DHCP_INFORM, xid, caddr, 0);

    /* Additional options we want. */
    optlen += DhcpAddParmReqOption(op + optlen);

    /* Add our configured host name. */
    len = strlen(confos.hostname);
    if (len > 0) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
    }

    /* We should provide the maximum message size. */
    optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);

    return DhcpSendMessage(sock, daddr, bp, optlen);
}


/*!
 * \brief Check a new offer.
 *
 * \param dyncfg Current configuration, may be NULL.
 * \param ip     DHCP server IP address.
 * \param bp     DHCP offer message.
 * \param len    DHCP message length.
 *
 * \return Updated configuration.
 */
static DYNCFG *CheckOffer(DYNCFG * dyncfg, BOOTP *bp, size_t len)
{
    DYNCFG *offer;

#ifdef NUTDEBUG
    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "\n[chkoffer %s ", inet_ntoa( dyncfg->dyn_yiaddr));
#endif

    /* Parse the new offer. If it's invalid, return the current
       configuration. */
    if ((offer = ParseReply(bp, len)) == 0) {
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "TAKE: %s]", inet_ntoa( offer->dyn_yiaddr));
#endif
        return dyncfg;
    }

    /* Discard anything which is not an offer. */
    if (offer->dyn_msgtyp != DHCP_OFFER) {
        ReleaseDynCfg(offer);
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "NO-OFFER]");
#endif
        return dyncfg;
    }

    /* First offer, take it. */
    if (dyncfg == 0) {
        dyncfg = offer;
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "FIRST: %s]", inet_ntoa(offer->dyn_yiaddr));
#endif
    }

    /*
     * Check if the new offer is better than the current configuration:
     */
    else {
        /* If we remember a previously allocated IP which isn't in the
           current configuration but in the new offer, then let's take
           the new one. */
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "PREV ");
#endif
        if (confnet.cdn_ip_addr & confnet.cdn_ip_mask) {
            if (dyncfg->dyn_yiaddr != confnet.cdn_ip_addr &&    /* */
                offer->dyn_yiaddr == confnet.cdn_ip_addr) {
                ReleaseDynCfg(dyncfg);
                dyncfg = offer;
#ifdef NUTDEBUG
                if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "OLD: %s]", inet_ntoa( dyncfg->dyn_yiaddr));
#endif
            }
        }
        /* In the second place prefer long lease times. */
        else if (offer->dyn_leaseTime > dyncfg->dyn_leaseTime) {
            ReleaseDynCfg(dyncfg);
            dyncfg = offer;
#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "LONG: %s]", inet_ntoa( dyncfg->dyn_yiaddr));
#endif
        }
        /* The new one deosn't offer anything interesting. Discard it. */
        else {
            ReleaseDynCfg(offer);
#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "DSIC]");
#endif
        }
    }
    return dyncfg;
}

#ifdef NUTDEBUG
void DhcpStateDebug( uint_fast8_t n, uint_fast8_t s, int li, int lt, ureg_t retries)
{
    if (__tcp_trf & NET_DBG_DHCP) {
        if( n)
            fprintf(__tcp_trs, "\n[%u.DHCP-", retries + 1);
        else
            fprintf(__tcp_trs, "\n->[%u.DHCP-", retries + 1);

        switch (dhcpState) {
        case DHCPST_INIT:
            fprintf(__tcp_trs, "INIT]");
            break;
        case DHCPST_SELECTING:
            fprintf(__tcp_trs, "SELECTING]");
            break;
        case DHCPST_REQUESTING:
            fprintf(__tcp_trs, "REQUESTING]");
            break;
        case DHCPST_REBOOTING:
            fprintf(__tcp_trs, "REBOOTING %s]", inet_ntoa(li));
            break;
        case DHCPST_BOUND:
            fprintf(__tcp_trs, "BOUND %lu]", NutGetSeconds() - lt);
            break;
        case DHCPST_RENEWING:
            fprintf(__tcp_trs, "RENEWING %lu]", NutGetSeconds() - lt);
            break;
        case DHCPST_REBINDING:
            fprintf(__tcp_trs, "REBINDING %lu]", NutGetSeconds() - lt);
            break;
        case DHCPST_INFORMING:
            fprintf(__tcp_trs, "INFORMING]");
            break;
        case DHCPST_RELEASING:
            fprintf(__tcp_trs, "RELEASING]");
            break;
        case DHCPST_IDLE:
            if (dhcpError) {
                fprintf(__tcp_trs, "ERROR %u]", dhcpError);
            } else {
                fprintf(__tcp_trs, "IDLE]");
            }
            break;
        default:
            fprintf(__tcp_trs, "UNKNOWN %u]", dhcpState);
            break;
        }
    }
}
#endif

/*! \fn NutDhcpClient(void *arg)
 * \brief DHCP client thread.
 *
 * This thread implements a DHCP state machine and is automatically started
 * when calling NutDhcpIfConfig() or NutDhcpInform().
 *
 * \bug We are not able to shutdown our interface, which may cause problems
 *      if out original DHCP server dies.
 *
 * \todo We are using a bunch of global variables, which must be associated
 *       to a specific interfase if we want to support more than one
 *       Ethernet port.
 */
THREAD(NutDhcpClient, arg)
{
    DYNCFG *reply = 0;
    UDPSOCKET *sock = 0;
    BOOTP *bp = 0;
    int n;
    uint32_t xid;
    IFNET *nif;
    uint16_t secs = 0;
    uint32_t aqsTime = NutGetSeconds();
    uint32_t leaseTime = 0;
    uint32_t napTime;
    ureg_t retries;
    uint32_t tmo = MIN_DHCP_WAIT;
    uint32_t last_ip = confnet.cdn_ip_addr;
    uint32_t server_ip;

    /*
     * Hack alert: Our ARM port doesn't allow parameter
     * passing to threads.
     */
#ifdef __arm__
    nif = dhcpDev->dev_icb;
#else
    nif = ((NUTDEVICE *) arg)->dev_icb;
#endif

    /*
     * Generate a random transaction identifier based on our MAC
     * address with the least significant byte of the MAC address
     * becoming the most significant byte of the identifier. This
     * should give a sufficient unique value when several Ethernuts
     * are started concurrently.
     */
    xid = 0;
    for (retries = 0; retries < sizeof(xid); retries++) {
        xid <<= 8;
        xid += nif->if_mac[5 - retries];
    }
    retries = 0;

    for (;;) {

#ifdef NUTDEBUG
        DhcpStateDebug( 0, dhcpState, last_ip, leaseTime, retries);
#endif

        /*
         * Setup some values based on the number of retry attempts.
         */
        server_ip = INADDR_BROADCAST;   /* Broadcasting is default. */
        if (retries) {
            /* Double our timeout on each retry. */
            tmo += tmo;
            if (tmo > MAX_DHCP_WAIT) {
                tmo = MAX_DHCP_WAIT;
            }
        } else {
            /* Start with minimum timeout first. */
            tmo = MIN_DHCP_WAIT;
            /* Use a new xid for the first message in each state except
             * when requesting, where we should continue using the xid
             * from the offer message we received.
             */
            if (dhcpState != DHCPST_REQUESTING) {
                xid++;
            }

            /* If we know the server's IP, try to unicast on the first
               attempt. */
            if (dhcpConfig && dhcpConfig->dyn_sid) {
                server_ip = dhcpConfig->dyn_sid;
            }
        }

        /*
         * Keep track of the API timeout.
         */
        if (dhcpState != DHCPST_IDLE && dhcpApiTimeout != NUT_WAIT_INFINITE) {
            uint32_t tt = NutGetMillis() - dhcpApiStart;

            if (dhcpApiTimeout <= tt) {
                dhcpError = DHCPERR_TIMEOUT;
                dhcpState = DHCPST_IDLE;
                continue;
            }
            if ((tt = dhcpApiTimeout - tt) < tmo) {
                tmo = tt;
            }
        }

        /*
         * Keep track of acquisition time.
         */
        if ((dhcpState == DHCPST_SELECTING) || (dhcpState == DHCPST_RENEWING) || (dhcpState == DHCPST_REBINDING)) {
            /* For retries make sure that secs doesn't overflow. */
            if (retries) {
                if (NutGetSeconds() - aqsTime > 0xffffUL) {
                    secs = 0xffff;
                } else {
                    secs = (uint16_t) (NutGetSeconds() - aqsTime);
                }
            }
            /* For first transmissions make sure that secs is zero. */
            else {
                aqsTime = NutGetSeconds();
                secs = 0;
            }
        }

        /*
         * Release UDP socket and buffer in states with long inactive time.
         */
        if ((dhcpState == DHCPST_BOUND) || (dhcpState == DHCPST_IDLE)) {
            if (sock) {
                NutUdpDestroySocket(sock);
                sock = 0;
            }
            if (bp) {
                free(bp);
                bp = 0;
            }
        }

        /*
         * In all other states we need the socket and the buffer.
         */
        else {
            /*
             * Check if something else configured our interface.
             */
            if (dhcpConfig == 0 && nif->if_local_ip) {
                /* If we need additional configuration, we can sent
                   a DHCP Inform message here. */
                dhcpState = DHCPST_IDLE;
                continue;
            }

            if ((sock == 0) || (bp == 0)) {
                if (sock == 0) {
                    sock = NutUdpCreateSocket(DHCP_CLIENTPORT);
                }
                if (bp == 0) {
                    bp = malloc(sizeof(BOOTP));
                }
                if ((sock == 0) || (bp == 0)) {
                    /* Looks like we are out of memory. */
                    dhcpError = DHCPERR_SYSTEM;
                    dhcpState = DHCPST_IDLE;
                    /* At this point either socket or buffer may be allocated.
                       Thus we need to jump back to the top of our state loop
                       to release it. */
                    continue;
                }
#if MAX_DHCP_BUFSIZE
                {
                    uint16_t max_ms = MAX_DHCP_BUFSIZE;
                    NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
                }
#endif
            }
        }

        /*
         * (Re)Start.
         */
        if (dhcpState == DHCPST_INIT) {
            /* Clear the retry counter. */
            retries = 0;
            /* Use a new XID on each attempt. */
            xid++;
            /* Determine whether this is an initial boot or a reboot. */
            if ((last_ip & confnet.cdn_ip_mask) == 0) {
                /* No previous IP, start from ground up. */
                dhcpState = DHCPST_SELECTING;
            } else {
                /* We got a previously allocated IP configuration from
                 * non-volatile configuration memory. Try to re-use it. */
                dhcpState = DHCPST_REBOOTING;
            }
        }


        /*
         * Broadcast discover and collect incoming offers.
         */
        else if (dhcpState == DHCPST_SELECTING) {
#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL: TICK %p]", dhcpConfig);
#endif

            if (retries++ > MAX_DCHP_RETRIES) {
                if( dhcpConfig) {
                    /* Ulrich says: we got at least one valid offer from a DHCP server */
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                }
                else {
                    /* Too many retries while discovering DHCP. Give up. */
                    dhcpError = DHCPERR_TIMEOUT;
                    dhcpState = DHCPST_IDLE;
                }
            }
            /* Send the discovering broadcast. */
            else if (DhcpBroadcastDiscover(sock, bp, xid, last_ip, secs) < 0) {
                /* Fatal transmit error on broadcast. */
#ifdef NUTDEBUG
                if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-TXERR]");
#endif
                dhcpState = DHCPST_IDLE;
            } else {
                /* Collect incoming offers. */
                while ((n = DhcpRecvMessage(sock, xid, bp, tmo)) > 0) {
                    /* Check if this is a valid offer. */
                    if ((dhcpConfig = CheckOffer(dhcpConfig, bp, n)) != 0) {
#ifdef NUTDEBUG
                        if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-OFFER n=%d]", n);
#endif
                        /* If the callers timeout is low, do not collect
                           more than one. Thanks to Jelle Kok. */
                        if (dhcpApiTimeout < MIN_DHCP_WAIT * 3) {
#ifdef NUTDEBUG
                            if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[NOTL]");
#endif
                            break;
                        }
                        /* Switch to lowest timeout after we received
                           a first response. */
                        tmo = MIN_DHCP_WAIT;
                    }
                }
                /* Change to ERROR state on fatal receive errors. */
                if (n < 0) {
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-FATERR]");
#endif
                    dhcpState = DHCPST_IDLE;
                }
                /* Change to REQUESTING state if we got a valid offer.
                   Otherwise we stay in SELECTING state. */
                else if (dhcpConfig) {
                    /* Wait for at least one period for other DHCP servers to offer */
                    retries = MAX_DCHP_RETRIES+1;
//                    dhcpState = DHCPST_REQUESTING;
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-WAIT]");
#endif
                }
            }
        }

		

        /*
         * Send request and wait for an acknowledge.
         */
        else if (dhcpState == DHCPST_REQUESTING) {
            if (retries++ > MAX_DCHP_RETRIES) {
                /* Too many retries with this server, fall back to discovery. */
                dhcpState = DHCPST_INIT;
            }
            /* Request an offered configuration. According to RFC 2131 this
               has to be broadcasted. */
            else if (DhcpBroadcastRequest(sock, bp, xid, 0, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid, secs) < 0) {
                /* Fatal transmit error on broadcast. Give up. */
                dhcpState = DHCPST_IDLE;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                /* The server accepted our request. We are bound. */
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                }
                /* The server declines a previously offered configuration.
                   Restart discovery. */
                else if (reply->dyn_msgtyp == DHCP_NAK) {
#ifdef NUTDEBUG
        if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-NAK1]");
#endif
                    dhcpState = DHCPST_INIT;
                }
            }
        }

        /*
         * Reusing a previously allocated network address after reboot.
         */
        else if (dhcpState == DHCPST_REBOOTING) {
            if (++retries > MAX_DCHP_RETRIES) {
                /* Too many retries, fall back to discovery. */
                last_ip = 0;
                dhcpState = DHCPST_INIT;
            }
            /* Broadcast a request for our previous configuration. */
            else if (DhcpBroadcastRequest(sock, bp, xid, 0, last_ip, 0, secs) < 0) {
                /* Fatal transmit error on broadcast. Give up. */
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[BC-FATAL]");
#endif
                dhcpState = DHCPST_IDLE;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[RCV-FATAL]");
#endif
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[OK]");
#endif
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    /* Either our previous address had been allocated by
                       someone else or we changed the network. Remove the
                       previous address and restart. */
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-NAK2]");
#endif
                    last_ip = 0;
                    dhcpState = DHCPST_INIT;
                }
            }
        }

        /*
         * Maintain lease time.
         */
        else if (dhcpState == DHCPST_BOUND) {
            retries = 0;
            NutEventBroadcastAsync(&dhcpDone); // Async wg. Racecondition
            if (dhcpConfig->dyn_renewalTime <= NutGetSeconds() - leaseTime) {
                dhcpState = DHCPST_RENEWING;
            } else {
                /* Calculate the remaining lease time and take a nap. */
                napTime = dhcpConfig->dyn_renewalTime - (NutGetSeconds() - leaseTime);
                if (napTime > MAX_DHCP_NAPTIME) {
                    napTime = MAX_DHCP_NAPTIME;
                }
                NutEventWait(&dhcpWake, napTime * 1000UL);
            }
        }

        /*
         * Waiting for an acknowledge of our renewal request.
         */
        else if (dhcpState == DHCPST_RENEWING) {
            retries++;
            if (tmo / 1000 > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
                tmo = dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime) * 1000;
            }
            if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
                retries = 0;
                dhcpState = DHCPST_REBINDING;
            }
            /* Send a request to our leasing server. We must not include
               the server identifier. */
            else if (DhcpSendRequest(sock, dhcpConfig->dyn_sid, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) <
                     0) {
                /* Unicast transmit error. */
                retries = 0;
                dhcpState = DHCPST_REBINDING;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    /* Got an acknowledge, return to bound state. */
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    /* Unexpected NAK. */
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-UXNAK]");
#endif
                    retries = 0;
                    dhcpState = DHCPST_REBINDING;
                }
            }
        }

        /*
         * Waiting for an acknowledge of our rebind request.
         */
        else if (dhcpState == DHCPST_REBINDING) {
            retries++;
            if (tmo > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
                tmo = dhcpConfig->dyn_rebindTime - NutGetSeconds() - leaseTime;
            }
            if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
                retries = 0;
                dhcpState = DHCPST_REBINDING;
            }
            /* Broadcast a request for our previous configuration. We
               must not include a server identifier. */
            else if (DhcpBroadcastRequest(sock, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) < 0) {
                /* Fatal transmit error on broadcast. Give up. */
                dhcpState = DHCPST_IDLE;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    /* Got an acknowledge, return to bound state. */
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    /*
                     * We have a problem here if the last DHCP server died.
                     * If a backup server exists, it may probe our IP address
                     * using ARP or ICMP. Our interface is up and responding,
                     * so the backup server may think that the IP address
                     * is in use and respond with NAK. Without shutting
                     * down our interface (not yet implemented) we are stuck.
                     * We switch to discovery state, but the problem remains.
                     */
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-NAK3]");
#endif
                    dhcpState = DHCPST_INIT;
                }
            }
        }

        /*
         * Send an inform and wait for its (optional) echo.
         */
        else if (dhcpState == DHCPST_INFORMING) {
            if (retries++ > MAX_DCHP_RETRIES) {
                dhcpState = DHCPST_IDLE;
            } else if (DhcpSendInform(sock, server_ip, bp, xid, nif->if_local_ip) < 0) {
                if (server_ip == INADDR_BROADCAST) {
                    dhcpState = DHCPST_IDLE;
                }
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) != 0) {
                if (n > 0 &&    /* No receive error. */
                    (reply = ParseReply(bp, n)) != 0 && /* No parser error. */
                    reply->dyn_msgtyp == DHCP_ACK) {    /* Acknowledged. */
                    /* Take over this configuration. */
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                }
                dhcpState = DHCPST_IDLE;
            }
        }

        /*
         * Send a release and wait for its (optional) echo.
         */
        else if (dhcpState == DHCPST_RELEASING) {
            if (dhcpConfig == 0 ||      /* Not configured. */
                retries++ > MAX_DCHP_RELEASE_RETRIES || /* Too many retries. */
                DhcpSendRelease(sock, server_ip, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid) < 0) {
                if (server_ip == INADDR_BROADCAST) {
                    dhcpState = DHCPST_IDLE;
                }
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    dhcpState = DHCPST_IDLE;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
#ifdef NUTDEBUG
                    if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-FNAK]");
#endif
                    dhcpState = DHCPST_IDLE;
                }
            }
        }

        /*
         * We are done somehow. Either a fatal error occured or we
         * reached the specified timeout time or our lease has been
         * release or something else configured our interface.
         * Release all resources and wait for a new API call to
         * wake us up.
         */
        else if (dhcpState == DHCPST_IDLE) {
            ReleaseDynCfg(dhcpConfig);
            dhcpConfig = 0;
            retries = 0;
            NutEventBroadcastAsync(&dhcpDone); // Async wg. Racedondition
			// if we have a device
            if(dhcpDev!=0)
                            {
						    	NutIpRouteDelAll(dhcpDev); // delete all routes, because they are needing Heap memory and this has to bee freed.
							}

			
			
            NutEventWait(&dhcpWake, NUT_WAIT_INFINITE);
        }
#ifdef NUTDEBUG
       DhcpStateDebug( 1, dhcpState, last_ip, leaseTime, retries);
#endif
        /* Release any received reply. */
        if (reply) {
            ReleaseDynCfg(reply);
            reply = 0;
        }
    }
}

/*!
 * \brief Activate the DHCP client thread.
 *
 * Start the DHCP thread if not running or wake it up. Pass the caller's
 * timeout to the thread and wait an infinite time. We rely on the thread
 * to wake us up on timeout.
 *
 * \param name    Name of the registered Ethernet device.
 * \param state   State to start with.
 * \param timeout Maximum number of milliseconds to wait. To disable
 *                timeout, set this parameter to \ref NUT_WAIT_INFINITE.
 *                This value must be larger than 3 times of \ref MIN_DHCP_WAIT
 *                to enable collection of offers from multiple servers.
 */
static int DhcpKick(const char *name, uint8_t state, uint32_t timeout)
{
    NUTDEVICE *dev;
    IFNET *nif;

    /* Lookup the Ethernet device. */
    if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
        dev->dev_type != IFTYP_NET ||   /* Wrong type */
        (nif = dev->dev_icb) == 0 ||    /* No netif */
        nif->if_type != IFT_ETHER) {    /* Wrong if type */
        dhcpError = DHCPERR_BADDEV;
        return -1;
    }

    /* Initialize timeout checking. */
    dhcpApiStart = NutGetMillis();
    dhcpApiTimeout = timeout;

    dhcpState = state;
    if (dhcpThread == 0) {
#ifdef __arm__
        dhcpDev = dev;
#endif
        dhcpThread = NutThreadCreate("dhcpc", NutDhcpClient, dev,
            (NUT_THREAD_DHCPSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
    }
    NutEventPostAsync(&dhcpWake);  // Error race condition
    NutEventWait(&dhcpDone, NUT_WAIT_INFINITE);

    return 0;
}

/*!
 * \brief Automatically configure an Ethernet network interface.
 *
 * If no MAC address is specified, this routine will try to read a
 * previously stored configuration from the EEPROM. If this retrieves
 * a fixed IP configuration, then the network interface will be
 * immediately configured with these values by calling NutNetIfConfig().
 * If no valid IP configuration has been read, then this routine will
 * start the DHCP client thread and wait upto a given number of
 * milliseconds for an acknowledged configuration from a DHCP server.
 *
 * If a MAC address has been specified, this routine will not read the
 * EEPROM configuration. If the application has set the global
 * \ref CONFNET structure to a valid IP configuration before calling
 * this function, then the network interface will be immediately
 * configured with these values by calling NutNetIfConfig(). Otherwise
 * the DHCP client thread will be started and this routine will wait
 * upto a given number of milliseconds for an acknowledged configuration
 * from a DHCP server.
 *
 * \param name    Name of the registered Ethernet device.
 * \param mac     MAC address of the destination. Set NULL to use the
 *                configuration stored in the EEPROM.
 * \param timeout Maximum number of milliseconds to wait. To disable
 *                timeout, set this parameter to \ref NUT_WAIT_INFINITE.
 *                Otherwise the value must be larger than 3 times of
 *                \ref MIN_DHCP_WAIT to enable collection of offers
 *                from multiple servers.
 *
 * \return 0 if the interface had been successfully configured. In most
 *         cases this information is sufficient, because the application
 *         will not care whether the configuration had been provided by
 *         a DHCP server or EEPROM values. However, if EEPROM values had
 *         been used, then no DNS servers had been set. The application
 *         can call NutDhcpStatus() to check the DHCP bound state. -1 is
 *         returned if the interface configuration failed. In this case
 *         NutDhcpError() can be called to get a more specific error
 *         code.
 */
int NutDhcpIfConfig(const char *name, uint8_t * mac, uint32_t timeout)
{
    NUTDEVICE *dev;
    IFNET *nif = NULL;

    /*
     * Verify the given Ethernet device.
     */
    dev = NutDeviceLookup(name);
    if (dev && dev->dev_type == IFTYP_NET) {
        nif = (IFNET *) dev->dev_icb;
    }
    if (nif == NULL || nif->if_type != IFT_ETHER) {
        /* Not a network device or wrong interface type. */
        dhcpError = DHCPERR_BADDEV;
        return -1;
    }

    /*
     * Determine the MAC address.
     */
    if (mac && !ETHER_IS_BROADCAST(mac)) {
         /* If the caller specified a valid MAC address, we use it
            to override the configuration. */
        memcpy(confnet.cdn_mac, mac, sizeof(confnet.cdn_mac));
    }
    else if (ETHER_IS_ZERO(nif->if_mac)) {
        /* The interface has not defined a MAC. Try to get one from
           non-volatile memory. */
        NutNetLoadConfig(name);
    }

    /*
     * Check if we have a valid MAC address. In order to maintain
     * backward compatibility, we accept anything which is neither
     * zero nor the broadcast address. Later versions may become more
     * restrictive and demand a valid unicast address.
     */
    if (ETHER_IS_ZERO(confnet.cdn_mac) || ETHER_IS_BROADCAST(confnet.cdn_mac)) {
        dhcpError = DHCPERR_NOMAC;
        return -1;
    }

    /*
     * Copy the MAC address to the interface structure. This will
     * magically enable the brain dead interface.
     */
    memcpy(nif->if_mac, confnet.cdn_mac, 6);
    NutSleep(500);

    /*
     * Zero out the ip address and mask. This allows to switch between
     * DHCP and static IP addresses without resetting/power cycling.
     * See patch #2903940.
     */
    nif->if_local_ip = 0;
    nif->if_mask = confnet.cdn_ip_mask;

    /*
     * If the EEPROM contains a fixed network configuration, we skip DHCP.
     */
    if (confnet.cdn_cip_addr & confnet.cdn_ip_mask) {
        /* Give up a previously allocated lease. See patch #2903940. */
        (void)NutDhcpRelease(name, (3*MIN_DHCP_WAIT));
        confnet.cdn_ip_addr = confnet.cdn_cip_addr;
        return NutNetIfConfig2(name,
                        confnet.cdn_mac,
                        confnet.cdn_ip_addr,
                        confnet.cdn_ip_mask,
                        confnet.cdn_gateway);
    }

    /*
     * Start the DHCP thread if not running or wake it up. Pass the caller's
     * timeout to the thread and wait an infinite time. We rely on the thread
     * to wake us up on timeout.
     */
    if (DhcpKick(name, DHCPST_INIT, timeout) == 0) {
        /*
         * The thread finished its task. If it reached the bound state, then
         * we got a valid configuration from DHCP.
         */
        if (dhcpState == DHCPST_BOUND) {
#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) {
                fprintf(__tcp_trs, "[DHCP-Config %s]", inet_ntoa(dhcpConfig->dyn_yiaddr));
            }
#endif
            NutNetIfSetup(dev, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_netmask, dhcpConfig->dyn_gateway);
            NutDnsConfig2(NULL, NULL, dhcpConfig->dyn_pdns, dhcpConfig->dyn_sdns);
            return 0;
        }

        /*
         * Our interface has been configured externally, possibly by auto
         * ARP or a similar function implemented by the application.
         */
        if (nif->if_local_ip) {
#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) {
                fprintf(__tcp_trs, "[DHCP-External %s]", inet_ntoa(nif->if_local_ip));
            }
#endif
            return 0;
        }

        /*
         * DHCP failed. In case we remember a previously allocated address,
         * then let's use it.
         */
        if ((confnet.cdn_ip_addr & confnet.cdn_ip_mask) != 0) {
#ifdef NUTDEBUG
            if (__tcp_trf & NET_DBG_DHCP) {
                fprintf(__tcp_trs, "[DHCP-Reusing %s]", inet_ntoa(confnet.cdn_ip_addr));
            }
#endif
            NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
            return 0;
        }
    }
    return -1;
}

/*!
 * \brief Relinguish our DHCP lease.
 *
 * This function may be called by the application if we are moving
 * to another network. It helps the DHCP server to tidy up his
 * allocation table, but is not a required DHCP function.
 *
 * Upon return, the system should be shut down within 20 seconds.
 *
 * The client must reside in state \ref DHCPST_BOUND.
 *
 * \param name    Name of the registered Ethernet device, currently ignored.
 * \param timeout Maximum number of milliseconds to wait.
 *
 * \return 0 on success or -1 in case of an error.
 */
int NutDhcpRelease(const char *name, uint32_t timeout)
{
    /* Check the state. */
    if (dhcpState != DHCPST_BOUND) {
        dhcpError = DHCPERR_STATE;
        return -1;
    }

    /* Action! */
    return DhcpKick(name, DHCPST_RELEASING, timeout);
}

/*!
 * \brief Inform DHCP about an allocated address.
 *
 * The client must reside in state \ref DHCPST_IDLE.
 *
 * \param name    Name of the registered Ethernet device, currently ignored.
 * \param timeout Maximum number of milliseconds to wait.
 *
 * \return 0 on success or -1 in case of an error.
 */
int NutDhcpInform(const char *name, uint32_t timeout)
{
    /* Check the state. */
    if (dhcpState != DHCPST_IDLE) {
        dhcpError = DHCPERR_STATE;
        return -1;
    }

    /* Action! */
    return DhcpKick(name, DHCPST_INFORMING, timeout);
}

/*!
 * \brief Return DHCP client status.
 *
 * \param name Name of the registered Ethernet device, currently ignored.
 *
 * \return DHCP status code, which may be any of the following:
 *         - \ref DHCPST_INIT
 *         - \ref DHCPST_SELECTING
 *         - \ref DHCPST_REQUESTING
 *         - \ref DHCPST_REBOOTING
 *         - \ref DHCPST_BOUND
 *         - \ref DHCPST_RENEWING
 *         - \ref DHCPST_REBINDING
 *         - \ref DHCPST_INFORMING
 *         - \ref DHCPST_RELEASING
 *         - \ref DHCPST_IDLE
 */
int NutDhcpStatus(const char *name)
{
    return dhcpState;
}

/*!
 * \brief Return DHCP error code.
 *
 * Possible error codes are
 *
 *         - \ref DHCPERR_TIMEOUT
 *         - \ref DHCPERR_NOMAC
 *         - \ref DHCPERR_BADDEV
 *         - \ref DHCPERR_SYSTEM
 *         - \ref DHCPERR_TRANSMIT
 *         - \ref DHCPERR_RECEIVE
 *
 * The error will be cleared upon return.
 *
 * \param name Name of the registered Ethernet device, currently ignored.
 *
 * \return DHCP error code or 0 if no error occured.
 */
int NutDhcpError(const char *name)
{
    int rc = dhcpError;
    dhcpError = 0;
    return rc;
}

/*!
 * \brief Check if DHCP has configured our interface
 *
 * \deprecated Applications should use NutDhcpStatus().
 *
 * \return 0 if DHCP is in bound state.
 */
int NutDhcpIsConfigured(void)
{
    return (dhcpState == DHCPST_BOUND);
}

/*@}*/


More information about the En-Nut-Discussion mailing list