Nut/OS  4.10.3
API Reference
dhcpc.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2001-2005 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  * -
00033  * Portions Copyright (c) 1983, 1993 by
00034  *  The Regents of the University of California.  All rights reserved.
00035  *
00036  * Redistribution and use in source and binary forms, with or without
00037  * modification, are permitted provided that the following conditions
00038  * are met:
00039  * 1. Redistributions of source code must retain the above copyright
00040  *    notice, this list of conditions and the following disclaimer.
00041  * 2. Redistributions in binary form must reproduce the above copyright
00042  *    notice, this list of conditions and the following disclaimer in the
00043  *    documentation and/or other materials provided with the distribution.
00044  * 3. Neither the name of the University nor the names of its contributors
00045  *    may be used to endorse or promote products derived from this software
00046  *    without specific prior written permission.
00047  *
00048  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00049  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00050  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00051  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00052  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00053  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00054  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00055  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00056  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00057  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00058  * SUCH DAMAGE.
00059  * -
00060  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
00061  *
00062  * Permission to use, copy, modify, and distribute this software for any
00063  * purpose with or without fee is hereby granted, provided that the above
00064  * copyright notice and this permission notice appear in all copies, and that
00065  * the name of Digital Equipment Corporation not be used in advertising or
00066  * publicity pertaining to distribution of the document or software without
00067  * specific, written prior permission.
00068  *
00069  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
00070  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
00071  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
00072  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
00073  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
00074  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
00075  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00076  * SOFTWARE.
00077  */
00078 
00199 #include <cfg/os.h>
00200 #include <sys/thread.h>
00201 #include <sys/event.h>
00202 #include <sys/timer.h>
00203 #include <sys/confnet.h>
00204 #include <sys/confos.h>
00205 
00206 #include <stdlib.h>
00207 #include <string.h>
00208 #include <time.h>
00209 #include <memdebug.h>
00210 
00211 #include <arpa/inet.h>
00212 #include <netinet/in.h>
00213 #include <netdb.h>
00214 #include <net/route.h>
00215 #include <sys/socket.h>
00216 #include <pro/dhcp.h>
00217 
00218 #ifdef NUTDEBUG
00219 #include <net/netdebug.h>
00220 #endif
00221 
00222 #if 0
00223 /* Use for local debugging. */
00224 #define NUTDEBUG
00225 #include <stdio.h>
00226 #define __tcp_trs stdout
00227 static uint_fast8_t __tcp_trf = 1;
00228 #endif
00229 
00234 
00241 
00247 #ifndef DHCP_SERVERPORT
00248 #define DHCP_SERVERPORT      67
00249 #endif
00250 
00256 #ifndef DHCP_CLIENTPORT
00257 #define DHCP_CLIENTPORT      68
00258 #endif
00259 
00269 #ifndef MAX_DHCP_MSGSIZE
00270 #define MAX_DHCP_MSGSIZE    576
00271 #endif
00272 
00280 #ifndef MIN_DHCP_MSGSIZE
00281 #define MIN_DHCP_MSGSIZE    300
00282 #endif
00283 
00299 #ifndef MAX_DHCP_BUFSIZE
00300 #define MAX_DHCP_BUFSIZE    1728
00301 #endif
00302 
00312 #ifndef MIN_DHCP_WAIT
00313 #define MIN_DHCP_WAIT       4000
00314 #endif
00315 
00324 #ifndef MAX_DHCP_WAIT
00325 #define MAX_DHCP_WAIT       64000
00326 #endif
00327 
00336 #ifndef MAX_DCHP_RETRIES
00337 #define MAX_DCHP_RETRIES    3
00338 #endif
00339 
00348 #ifndef MAX_DCHP_RELEASE_RETRIES
00349 #define MAX_DCHP_RELEASE_RETRIES    0
00350 #endif
00351 
00359 #ifndef DHCP_DEFAULT_LEASE
00360 #define DHCP_DEFAULT_LEASE  43200
00361 #endif
00362 
00368 #ifndef MAX_DHCP_NAPTIME
00369 #define MAX_DHCP_NAPTIME    4294967
00370 #endif
00371 
00377 #ifndef NUT_THREAD_DHCPSTACK
00378 #if defined(__AVR__)
00379 #if defined(__GNUC__)
00380 /* avr-gcc size optimized code used 192 bytes. */
00381 #define NUT_THREAD_DHCPSTACK    288
00382 #else
00383 /* icc-avr v7.19 used 360 bytes. */
00384 #define NUT_THREAD_DHCPSTACK    512
00385 #endif
00386 #else
00387 /* arm-elf-gcc used 276 bytes with size optimized, 680 bytes with debug code. */
00388 /* arm-none-eabi creates stack overflow with 384 bytes on EIR 1.0 board. */
00389 #define NUT_THREAD_DHCPSTACK    512
00390 #endif
00391 #endif
00392 
00401 
00404 #define DHCP_DISCOVER   1
00405 
00410 #define DHCP_OFFER      2
00411 
00419 #define DHCP_REQUEST    3
00420 
00425 #define DHCP_DECLINE    4
00426 
00431 #define DHCP_ACK        5
00432 
00437 #define DHCP_NAK        6
00438 
00441 #define DHCP_RELEASE    7
00442 
00447 #define DHCP_INFORM     8
00448 
00458 
00465 #define DHCPOPT_PAD          0
00466 
00470 #define DHCPOPT_NETMASK      1
00471 
00475 #define DHCPOPT_GATEWAY      3
00476 
00480 #define DHCPOPT_DNS          6
00481 
00485 #define DHCPOPT_HOSTNAME     12
00486 
00490 #define DHCPOPT_DOMAIN       15
00491 
00495 #define DHCPOPT_BROADCAST    28
00496 
00500 #define DHCPOPT_REQESTIP     50
00501 
00505 #define DHCPOPT_LEASETIME    51
00506 
00510 #define DHCPOPT_MSGTYPE      53
00511 
00515 #define DHCPOPT_SID          54
00516 
00520 #define DHCPOPT_PARAMREQUEST 55
00521 
00525 #define DHCPOPT_MAXMSGSIZE   57
00526 
00530 #define DHCPOPT_RENEWALTIME  58
00531 
00535 #define DHCPOPT_REBINDTIME   59
00536 
00540 #define DHCPOPT_END          255
00541 
00547 typedef struct bootp BOOTP;
00548 
00552 struct __attribute__ ((packed)) bootp {
00553     uint8_t bp_op;              
00554     uint8_t bp_htype;           
00555     uint8_t bp_hlen;            
00556     uint8_t bp_hops;            
00557     uint32_t bp_xid;              
00558     uint16_t bp_secs;            
00559     uint16_t bp_flags;           
00560     uint32_t bp_ciaddr;           
00561     uint32_t bp_yiaddr;           
00562     uint32_t bp_siaddr;           
00563     uint32_t bp_giaddr;           
00564     uint8_t bp_chaddr[16];      
00565     char bp_sname[64];          
00566     char bp_file[128];          
00567     uint8_t bp_options[312];    
00568 };
00569 
00573 typedef struct dyn_cfg DYNCFG;
00574 
00578 struct dyn_cfg {
00579     uint8_t dyn_msgtyp;         
00580     uint32_t dyn_yiaddr;          
00581     uint32_t dyn_netmask;         
00582     uint32_t dyn_broadcast;       
00583     uint32_t dyn_gateway;         
00584     uint32_t dyn_pdns;            
00585     uint32_t dyn_sdns;            
00586     uint32_t dyn_sid;             
00587     uint32_t dyn_renewalTime;     
00588     uint32_t dyn_rebindTime;      
00589     uint32_t dyn_leaseTime;       
00590     uint8_t *dyn_hostname;      
00591     uint8_t *dyn_domain;        
00592 };
00593 
00600 static DYNCFG *dhcpConfig;
00601 
00608 static HANDLE dhcpThread;
00609 
00613 static uint8_t dhcpState;
00614 
00618 static int dhcpError;
00619 
00625 static HANDLE dhcpWake;
00626 
00632 static HANDLE dhcpDone;
00633 
00639 static uint32_t dhcpApiTimeout;
00640 
00647 static uint32_t dhcpApiStart;
00648 
00655 #ifdef __arm__
00656 static NUTDEVICE *dhcpDev;
00657 #endif
00658 
00670 static void copy_str(uint8_t ** dst, void *src, int len)
00671 {
00672     if (*dst) {
00673         free(*dst);
00674     }
00675     if ((*dst = malloc(len + 1)) != 0) {
00676         if (len) {
00677             memcpy(*dst, src, len);
00678         }
00679         *(*dst + len) = 0;
00680     }
00681 }
00682 
00690 static void ReleaseDynCfg(DYNCFG * dyncfg)
00691 {
00692     if (dyncfg) {
00693         if (dyncfg->dyn_hostname) {
00694             free(dyncfg->dyn_hostname);
00695         }
00696         if (dyncfg->dyn_domain) {
00697             free(dyncfg->dyn_domain);
00698         }
00699         free(dyncfg);
00700     }
00701 }
00702 
00714 static DYNCFG *ParseReply(BOOTP *bp, int len)
00715 {
00716     uint8_t *op;
00717     int left;
00718     DYNCFG *cfgp;
00719 
00720     /* Allocate and initialize a new structure. */
00721     if ((cfgp = malloc(sizeof(DYNCFG))) == 0) {
00722         return 0;
00723     }
00724     memset(cfgp, 0, sizeof(DYNCFG));
00725     cfgp->dyn_leaseTime = DHCP_DEFAULT_LEASE;
00726 
00727     /* Set the assigned IP address. */
00728     memcpy(&cfgp->dyn_yiaddr, &bp->bp_yiaddr, 4);
00729 
00730     /*
00731      * Parse options until an end option is found or until we reached
00732      * the end of the message.
00733      */
00734     op = bp->bp_options + 4;
00735     left = len - (sizeof(*bp) - sizeof(bp->bp_options)) - 4;
00736     while (*op != DHCPOPT_END && left > 0) {
00737         uint8_t ol;
00738 
00739 #ifdef NUTDEBUG
00740         if (__tcp_trf) {
00741             fprintf(__tcp_trs, "[DHCP-Opt-%u]", *op);
00742         }
00743 #endif
00744         /* Pad option is used for boundary alignment. */
00745         if (*op == DHCPOPT_PAD) {
00746             op++;
00747             left--;
00748             continue;
00749         }
00750 
00751         /* Reject if option length exceeds total length. */
00752         if ((ol = *(op + 1)) > left) {
00753             break;
00754         }
00755 
00756         /* Type of this message. */
00757         if (*op == DHCPOPT_MSGTYPE) {
00758             if (ol != 1) {
00759                 break;
00760             }
00761             cfgp->dyn_msgtyp = *(op + 2);
00762 
00763 #ifdef NUTDEBUG
00764             if (__tcp_trf & NET_DBG_DHCP) {
00765                 switch(cfgp->dyn_msgtyp) {
00766                     case DHCP_OFFER:
00767                         fprintf(__tcp_trs, "+MSGT-OFFER+\n");
00768                         break;
00769                     case DHCP_ACK:
00770                         fprintf(__tcp_trs, "+MSGT-ACK+\n");
00771                         break;
00772                     case DHCP_NAK:
00773                         fprintf(__tcp_trs, "-MSGT-NAK-\n");
00774                         break;
00775                     default:
00776                         fprintf(__tcp_trs, "#MSGT-UNK#\n");
00777                         break;
00778                 }
00779             }
00780 #endif
00781         }
00782         /* Our host name. May or may not include the domain. */
00783         else if (*op == DHCPOPT_HOSTNAME) {
00784             copy_str(&cfgp->dyn_hostname, op + 2, ol);
00785         }
00786         /* Name of the domain we are in. */
00787         else if (*op == DHCPOPT_DOMAIN) {
00788             copy_str(&cfgp->dyn_domain, op + 2, ol);
00789         }
00790 
00791         /* All remaining options require at least 4 octets. */
00792         else if (ol >= 4) {
00793             /* Preset most often used long value. */
00794             uint32_t lval = *(op + 2);
00795             lval += (uint32_t)(*(op + 3)) << 8;
00796             lval += (uint32_t)(*(op + 4)) << 16;
00797             lval += (uint32_t)(*(op + 5)) << 24;
00798 
00799             /* Our IP network mask. */
00800             if (*op == DHCPOPT_NETMASK) {
00801                 cfgp->dyn_netmask = lval;
00802             }
00803             /* Our IP broadcast address. */
00804             else if (*op == DHCPOPT_BROADCAST) {
00805                 cfgp->dyn_broadcast = lval;
00806             }
00807             /* Our IP default gate. More than one gateway may be
00808                specified. We take the fist one only and ignore the
00809                rest. */
00810             else if (*op == DHCPOPT_GATEWAY) {
00811                 cfgp->dyn_gateway = lval;
00812             }
00813             /* Our DNS server. Updated by Jelle Martijn Kok to
00814                support a secondary DNS. */
00815             else if (*op == DHCPOPT_DNS) {
00816                 cfgp->dyn_pdns = lval;
00817                 if (ol >= 8) {
00818                     cfgp->dyn_sdns = *(op + 6);
00819                     cfgp->dyn_sdns += (uint32_t)(*(op + 7)) << 8;
00820                     cfgp->dyn_sdns += (uint32_t)(*(op + 8)) << 16;
00821                     cfgp->dyn_sdns += (uint32_t)(*(op + 9)) << 24;
00822                 }
00823             }
00824             /* Server identifier. */
00825             else if (*op == DHCPOPT_SID) {
00826                 cfgp->dyn_sid = lval;
00827             }
00828             /* Renewal time. */
00829             else if (*op == DHCPOPT_RENEWALTIME) {
00830                 cfgp->dyn_renewalTime = ntohl(lval);
00831             }
00832             /* Rebinding time. */
00833             else if (*op == DHCPOPT_REBINDTIME) {
00834                 cfgp->dyn_rebindTime = ntohl(lval);
00835             }
00836             /* Total lease time granted. */
00837             else if (*op == DHCPOPT_LEASETIME) {
00838                 cfgp->dyn_leaseTime = ntohl(lval);
00839             }
00840         }
00841         op += ol + 2;
00842         left -= ol + 2;
00843     }
00844 
00845     /*
00846      * Discard this configuration if parsing stopped before reaching
00847      * the end option or if we didn't receive an expected message type.
00848      */
00849     if (*op != DHCPOPT_END ||
00850         (cfgp->dyn_msgtyp != DHCP_OFFER &&
00851          cfgp->dyn_msgtyp != DHCP_ACK &&
00852          cfgp->dyn_msgtyp != DHCP_NAK)) {
00853 #ifdef NUTDEBUG
00854         if (__tcp_trf & NET_DBG_DHCP) {
00855             fprintf(__tcp_trs, "[DHCP-Parse Error]");
00856         }
00857 #endif
00858         ReleaseDynCfg(cfgp);
00859         return 0;
00860     }
00861 
00862     /* Calculate renewal and rebind times. */
00863     if (cfgp->dyn_renewalTime == 0) {
00864         cfgp->dyn_renewalTime = cfgp->dyn_leaseTime / 2;
00865     }
00866     if (cfgp->dyn_rebindTime == 0) {
00867         cfgp->dyn_rebindTime = cfgp->dyn_renewalTime +  /* */
00868             cfgp->dyn_renewalTime / 2 + /* */
00869             cfgp->dyn_renewalTime / 4;
00870     }
00871     return cfgp;
00872 }
00873 
00884 static size_t DhcpAddOption(uint8_t * op, uint8_t ot, void *ov, uint8_t len)
00885 {
00886     *op++ = ot;
00887     *op++ = len;
00888     memcpy(op, ov, len);
00889 
00890     return 2 + len;
00891 }
00892 
00902 static size_t DhcpAddByteOption(uint8_t * op, uint8_t ot, uint8_t ov)
00903 {
00904     *op++ = ot;
00905     *op++ = 1;
00906     *op++ = ov;
00907 
00908     return 3;
00909 }
00910 
00921 static size_t DhcpAddShortOption(uint8_t * op, uint8_t ot, uint16_t ov)
00922 {
00923     *op++ = ot;
00924     *op++ = 2;
00925     ov = htons(ov);
00926     memcpy(op, &ov, 2);
00927 
00928     return 4;
00929 }
00930 
00943 static size_t DhcpAddParmReqOption(uint8_t * op)
00944 {
00945     *op++ = DHCPOPT_PARAMREQUEST;
00946     *op++ = 3;                  /* Adjust when adding more options! */
00947     *op++ = DHCPOPT_NETMASK;    /* Typically sent by default, but play safe. */
00948     *op++ = DHCPOPT_GATEWAY;    /* We want a default gateway. */
00949     *op++ = DHCPOPT_DNS;        /* We want the DNS' IP. */
00950     return 5;                   /* Adjust when adding more options! */
00951 }
00952 
00974 static unsigned int DhcpPrepHeader(BOOTP *bp, uint8_t msgtyp, uint32_t xid, uint32_t ciaddr, uint16_t secs)
00975 {
00976     uint8_t *op;
00977 
00978     memset(bp, 0, sizeof(*bp));
00979     /* Clients send bootp requests (op code 1) only. */
00980     bp->bp_op = 1;
00981     /* Ethernet addresses are type 1 with 6 octets. */
00982     bp->bp_htype = 1;
00983     bp->bp_hlen = 6;
00984     memcpy(bp->bp_chaddr, confnet.cdn_mac, 6);
00985     /* Transaction identifier. */
00986     bp->bp_xid = xid;
00987     /* Seconds elapsed since address acquisition. */
00988     bp->bp_secs = htons(secs);
00989 
00990 #ifdef DHCP_BROADCAST_FLAG
00991     /*
00992      * We do not need the broadcast flag, because our stack accepts IP
00993      * messages to any destination if no local address has been assigned,
00994      * However, we continue supporting this compile time option.
00995      */
00996     bp->bp_flags = htons(0x8000);
00997 #endif
00998 
00999     bp->bp_ciaddr = ciaddr;
01000 
01001     /* Add the DHCP magic cookie according to RFC 1497. */
01002     op = bp->bp_options;
01003     *op++ = 0x63;
01004     *op++ = 0x82;
01005     *op++ = 0x53;
01006     *op++ = 0x63;
01007 
01008     /* Add the DHCP message type option. */
01009     return DhcpAddByteOption(op, DHCPOPT_MSGTYPE, msgtyp) + 4;
01010 }
01011 
01030 static int DhcpSendMessage(UDPSOCKET * sock, uint32_t addr, BOOTP *bp, size_t len)
01031 {
01032     /* Add 'end of options'. */
01033     bp->bp_options[len++] = DHCPOPT_END;
01034 
01035     /* Maintain a BOOTP compatible minimum packet size of 300 octets.
01036        Thanks to Tomohiro Haraikawa. */
01037     if ((len += sizeof(BOOTP) - sizeof(bp->bp_options)) < MIN_DHCP_MSGSIZE) {
01038         len = MIN_DHCP_MSGSIZE;
01039     }
01040 #ifdef NUTDEBUG
01041     if (__tcp_trf & NET_DBG_DHCP) {
01042         fprintf(__tcp_trs, "[DHCP-Send to %s]", inet_ntoa(addr));
01043     }
01044 #endif
01045     if (NutUdpSendTo(sock, addr, DHCP_SERVERPORT, bp, len) < 0) {
01046         dhcpError = DHCPERR_TRANSMIT;
01047         return -1;
01048     }
01049     return 0;
01050 }
01051 
01065 static int DhcpRecvMessage(UDPSOCKET * sock, uint32_t xid, BOOTP *bp, uint32_t tmo)
01066 {
01067     int rc;
01068     uint16_t port;
01069     uint32_t addr;
01070     uint32_t etim;
01071     uint32_t wtim;
01072 
01073     /* Set our start time. */
01074     etim = NutGetMillis();
01075     /* Set the initial receive timeout. */
01076     wtim = tmo;
01077     for (;;) {
01078         rc = NutUdpReceiveFrom(sock, &addr, &port, bp, sizeof(BOOTP), wtim);
01079 #ifdef NUTDEBUG
01080         if (__tcp_trf & NET_DBG_DHCP) {
01081             if (rc > 0) {
01082                 fprintf(__tcp_trs, "[DHCP-Recv from %s]", inet_ntoa(addr));
01083             } else if (rc < 0) {
01084                 fprintf(__tcp_trs, "[DHCP-Recv Error]");
01085             } else {
01086                 fprintf(__tcp_trs, "[DHCP-Recv Timeout %lu]", tmo);
01087             }
01088         }
01089 #endif
01090         /* Immediately return on receive errors and timeouts. */
01091         if (rc <= 0) {
01092             if (rc < 0) {
01093                 dhcpError = DHCPERR_RECEIVE;
01094             }
01095             break;
01096         }
01097         /* The message must at least include the BOOTP header plus five
01098            bytes of options (magic and end). We are quite liberal here. */
01099         if (rc > sizeof(BOOTP) - sizeof(bp->bp_options) + 5) {
01100             /* The message must be a BOOTP reply with the expected XID. */
01101             if (bp->bp_op == 2 && bp->bp_xid == xid) {
01102                 /* Message is acceptable. */
01103                 break;
01104             }
01105         }
01106         /* Calculate the remaining timeout for not getting trapped here
01107            on a busy network, which regularly broadcasts DHCP messages. */
01108         wtim = NutGetMillis() - etim;
01109         if (wtim >= tmo - 250) {
01110             /* Less than 250 ms left, return timeout. */
01111             rc = 0;
01112             break;
01113         }
01114         wtim = tmo - wtim;
01115     }
01116     return rc;
01117 }
01118 
01133 static int DhcpBroadcastDiscover(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, uint32_t raddr, uint16_t secs)
01134 {
01135     size_t optlen;
01136     int len;
01137     uint8_t *op = bp->bp_options;
01138 
01139     optlen = DhcpPrepHeader(bp, DHCP_DISCOVER, xid, 0, secs);
01140 
01141     /* Request a specific IP if one had been assigned previously. */
01142     if (raddr) {
01143         optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
01144     }
01145 
01146     optlen += DhcpAddParmReqOption(op + optlen);
01147 
01148     /* Pass host name if specified in confos structure.
01149      * Win2k DHCP server can register this as dynamic DNS entry.
01150      * Also viewing DHCP lease table shows something sensible.
01151      */
01152     len = strlen(confos.hostname);
01153     if (len > 0) {
01154         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01155     }
01156 
01157     /* Request a maximum message size. */
01158     optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
01159 
01160     return DhcpSendMessage(sock, INADDR_BROADCAST, bp, optlen);
01161 }
01162 
01163 
01183 static int DhcpSendRequest(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid,        /* */
01184                            uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
01185 {
01186     size_t optlen;
01187     int len;
01188     uint8_t *op = bp->bp_options;
01189 
01190     /* Initialize the BOOTP header. */
01191     optlen = DhcpPrepHeader(bp, DHCP_REQUEST, xid, caddr, secs);
01192 
01193     /* Add specified options. */
01194     if (raddr) {
01195         optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
01196     }
01197     if (sid) {
01198         optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
01199     }
01200     optlen += DhcpAddParmReqOption(op + optlen);
01201 
01202     /* Pass host name if specified in confos structure.  */
01203     /* viewing DHCP lease table shows something sensible. */
01204     len = strlen(confos.hostname);
01205     if (len > 0) {
01206         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01207     }
01208 
01209     return DhcpSendMessage(sock, daddr, bp, optlen);
01210 }
01211 
01230 static int DhcpBroadcastRequest(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, /* */
01231                                 uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
01232 {
01233     return DhcpSendRequest(sock, INADDR_BROADCAST, bp, xid, caddr, raddr, sid, secs);
01234 }
01235 
01251 static int DhcpSendRelease(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr, uint32_t sid)
01252 {
01253     size_t optlen;
01254     uint8_t *op = bp->bp_options;
01255 
01256     /* Prepare BOOTP header. 'secs' is set to zero. */
01257     optlen = DhcpPrepHeader(bp, DHCP_RELEASE, xid, caddr, 0);
01258 
01259     /* Optionally add server identifier. */
01260     if (sid) {
01261         optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
01262     }
01263     return DhcpSendMessage(sock, daddr, bp, optlen);
01264 }
01265 
01278 static int DhcpSendInform(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr)
01279 {
01280     size_t optlen;
01281     size_t len;
01282     uint8_t *op = bp->bp_options;
01283 
01284     /* Prepare BOOTP header. 'secs' is set to zero. */
01285     optlen = DhcpPrepHeader(bp, DHCP_INFORM, xid, caddr, 0);
01286 
01287     /* Additional options we want. */
01288     optlen += DhcpAddParmReqOption(op + optlen);
01289 
01290     /* Add our configured host name. */
01291     len = strlen(confos.hostname);
01292     if (len > 0) {
01293         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01294     }
01295 
01296     /* We should provide the maximum message size. */
01297     optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
01298 
01299     return DhcpSendMessage(sock, daddr, bp, optlen);
01300 }
01301 
01302 
01313 static DYNCFG *CheckOffer(DYNCFG * dyncfg, BOOTP *bp, size_t len)
01314 {
01315     DYNCFG *offer;
01316 
01317 #ifdef NUTDEBUG
01318     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "\n[chkoffer %s ", inet_ntoa( dyncfg->dyn_yiaddr));
01319 #endif
01320 
01321     /* Parse the new offer. If it's invalid, return the current
01322        configuration. */
01323     if ((offer = ParseReply(bp, len)) == 0) {
01324 #ifdef NUTDEBUG
01325         if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "TAKE: %s]", inet_ntoa( offer->dyn_yiaddr));
01326 #endif
01327         return dyncfg;
01328     }
01329 
01330     /* Discard anything which is not an offer. */
01331     if (offer->dyn_msgtyp != DHCP_OFFER) {
01332         ReleaseDynCfg(offer);
01333 #ifdef NUTDEBUG
01334         if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "NO-OFFER]");
01335 #endif
01336         return dyncfg;
01337     }
01338 
01339     /* First offer, take it. */
01340     if (dyncfg == 0) {
01341         dyncfg = offer;
01342 #ifdef NUTDEBUG
01343         if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "FIRST: %s]", inet_ntoa(offer->dyn_yiaddr));
01344 #endif
01345     }
01346 
01347     /*
01348      * Check if the new offer is better than the current configuration:
01349      */
01350     else {
01351         /* If we remember a previously allocated IP which isn't in the
01352            current configuration but in the new offer, then let's take
01353            the new one. */
01354 #ifdef NUTDEBUG
01355         if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "PREV ");
01356 #endif
01357         if (confnet.cdn_ip_addr & confnet.cdn_ip_mask) {
01358             if (dyncfg->dyn_yiaddr != confnet.cdn_ip_addr &&    /* */
01359                 offer->dyn_yiaddr == confnet.cdn_ip_addr) {
01360                 ReleaseDynCfg(dyncfg);
01361                 dyncfg = offer;
01362 #ifdef NUTDEBUG
01363                 if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "OLD: %s]", inet_ntoa( dyncfg->dyn_yiaddr));
01364 #endif
01365             }
01366         }
01367         /* In the second place prefer long lease times. */
01368         else if (offer->dyn_leaseTime > dyncfg->dyn_leaseTime) {
01369             ReleaseDynCfg(dyncfg);
01370             dyncfg = offer;
01371 #ifdef NUTDEBUG
01372             if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "LONG: %s]", inet_ntoa( dyncfg->dyn_yiaddr));
01373 #endif
01374         }
01375         /* The new one deosn't offer anything interesting. Discard it. */
01376         else {
01377             ReleaseDynCfg(offer);
01378 #ifdef NUTDEBUG
01379             if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "DSIC]");
01380 #endif
01381         }
01382     }
01383     return dyncfg;
01384 }
01385 
01386 #ifdef NUTDEBUG
01387 void DhcpStateDebug( uint_fast8_t n, uint_fast8_t s, int li, int lt, ureg_t retries)
01388 {
01389     if (__tcp_trf & NET_DBG_DHCP) {
01390         if( n)
01391             fprintf(__tcp_trs, "\n[%u.DHCP-", retries + 1);
01392         else
01393             fprintf(__tcp_trs, "\n->[%u.DHCP-", retries + 1);
01394 
01395         switch (dhcpState) {
01396         case DHCPST_INIT:
01397             fprintf(__tcp_trs, "INIT]");
01398             break;
01399         case DHCPST_SELECTING:
01400             fprintf(__tcp_trs, "SELECTING]");
01401             break;
01402         case DHCPST_REQUESTING:
01403             fprintf(__tcp_trs, "REQUESTING]");
01404             break;
01405         case DHCPST_REBOOTING:
01406             fprintf(__tcp_trs, "REBOOTING %s]", inet_ntoa(li));
01407             break;
01408         case DHCPST_BOUND:
01409             fprintf(__tcp_trs, "BOUND %lu]", NutGetSeconds() - lt);
01410             break;
01411         case DHCPST_RENEWING:
01412             fprintf(__tcp_trs, "RENEWING %lu]", NutGetSeconds() - lt);
01413             break;
01414         case DHCPST_REBINDING:
01415             fprintf(__tcp_trs, "REBINDING %lu]", NutGetSeconds() - lt);
01416             break;
01417         case DHCPST_INFORMING:
01418             fprintf(__tcp_trs, "INFORMING]");
01419             break;
01420         case DHCPST_RELEASING:
01421             fprintf(__tcp_trs, "RELEASING]");
01422             break;
01423         case DHCPST_IDLE:
01424             if (dhcpError) {
01425                 fprintf(__tcp_trs, "ERROR %u]", dhcpError);
01426             } else {
01427                 fprintf(__tcp_trs, "IDLE]");
01428             }
01429             break;
01430         default:
01431             fprintf(__tcp_trs, "UNKNOWN %u]", dhcpState);
01432             break;
01433         }
01434     }
01435 }
01436 #endif
01437 
01451 THREAD(NutDhcpClient, arg)
01452 {
01453     DYNCFG *reply = 0;
01454     UDPSOCKET *sock = 0;
01455     BOOTP *bp = 0;
01456     int n;
01457     uint32_t xid;
01458     IFNET *nif;
01459     uint16_t secs = 0;
01460     uint32_t aqsTime = NutGetSeconds();
01461     uint32_t leaseTime = 0;
01462     uint32_t napTime;
01463     ureg_t retries;
01464     uint32_t tmo = MIN_DHCP_WAIT;
01465     uint32_t last_ip = confnet.cdn_ip_addr;
01466     uint32_t server_ip;
01467 
01468     /*
01469      * Hack alert: Our ARM port doesn't allow parameter
01470      * passing to threads.
01471      */
01472 #ifdef __arm__
01473     nif = dhcpDev->dev_icb;
01474 #else
01475     nif = ((NUTDEVICE *) arg)->dev_icb;
01476 #endif
01477 
01478     /*
01479      * Generate a random transaction identifier based on our MAC
01480      * address with the least significant byte of the MAC address
01481      * becoming the most significant byte of the identifier. This
01482      * should give a sufficient unique value when several Ethernuts
01483      * are started concurrently.
01484      */
01485     xid = 0;
01486     for (retries = 0; retries < sizeof(xid); retries++) {
01487         xid <<= 8;
01488         xid += nif->if_mac[5 - retries];
01489     }
01490     retries = 0;
01491 
01492     for (;;) {
01493 
01494 #ifdef NUTDEBUG
01495         DhcpStateDebug( 0, dhcpState, last_ip, leaseTime, retries);
01496 #endif
01497 
01498         /*
01499          * Setup some values based on the number of retry attempts.
01500          */
01501         server_ip = INADDR_BROADCAST;   /* Broadcasting is default. */
01502         if (retries) {
01503             /* Double our timeout on each retry. */
01504             tmo += tmo;
01505             if (tmo > MAX_DHCP_WAIT) {
01506                 tmo = MAX_DHCP_WAIT;
01507             }
01508         } else {
01509             /* Start with minimum timeout first. */
01510             tmo = MIN_DHCP_WAIT;
01511             /* Use a new xid for the first message in each state except
01512              * when requesting, where we should continue using the xid
01513              * from the offer message we received.
01514              */
01515             if (dhcpState != DHCPST_REQUESTING) {
01516                 xid++;
01517             }
01518 
01519             /* If we know the server's IP, try to unicast on the first
01520                attempt. */
01521             if (dhcpConfig && dhcpConfig->dyn_sid) {
01522                 server_ip = dhcpConfig->dyn_sid;
01523             }
01524         }
01525 
01526         /*
01527          * Keep track of the API timeout.
01528          */
01529         if (dhcpState != DHCPST_IDLE && dhcpApiTimeout != NUT_WAIT_INFINITE) {
01530             uint32_t tt = NutGetMillis() - dhcpApiStart;
01531 
01532             if (dhcpApiTimeout <= tt) {
01533                 dhcpError = DHCPERR_TIMEOUT;
01534                 dhcpState = DHCPST_IDLE;
01535                 continue;
01536             }
01537             if ((tt = dhcpApiTimeout - tt) < tmo) {
01538                 tmo = tt;
01539             }
01540         }
01541 
01542         /*
01543          * Keep track of acquisition time.
01544          */
01545         if ((dhcpState == DHCPST_SELECTING) || (dhcpState == DHCPST_RENEWING) || (dhcpState == DHCPST_REBINDING)) {
01546             /* For retries make sure that secs doesn't overflow. */
01547             if (retries) {
01548                 if (NutGetSeconds() - aqsTime > 0xffffUL) {
01549                     secs = 0xffff;
01550                 } else {
01551                     secs = (uint16_t) (NutGetSeconds() - aqsTime);
01552                 }
01553             }
01554             /* For first transmissions make sure that secs is zero. */
01555             else {
01556                 aqsTime = NutGetSeconds();
01557                 secs = 0;
01558             }
01559         }
01560 
01561         /*
01562          * Release UDP socket and buffer in states with long inactive time.
01563          */
01564         if ((dhcpState == DHCPST_BOUND) || (dhcpState == DHCPST_IDLE)) {
01565             if (sock) {
01566                 NutUdpDestroySocket(sock);
01567                 sock = 0;
01568             }
01569             if (bp) {
01570                 free(bp);
01571                 bp = 0;
01572             }
01573         }
01574 
01575         /*
01576          * In all other states we need the socket and the buffer.
01577          */
01578         else {
01579             /*
01580              * Check if something else configured our interface.
01581              */
01582             if (dhcpConfig == 0 && nif->if_local_ip) {
01583                 /* If we need additional configuration, we can sent
01584                    a DHCP Inform message here. */
01585                 dhcpState = DHCPST_IDLE;
01586                 continue;
01587             }
01588 
01589             if ((sock == 0) || (bp == 0)) {
01590                 if (sock == 0) {
01591                     sock = NutUdpCreateSocket(DHCP_CLIENTPORT);
01592                 }
01593                 if (bp == 0) {
01594                     bp = malloc(sizeof(BOOTP));
01595                 }
01596                 if ((sock == 0) || (bp == 0)) {
01597                     /* Looks like we are out of memory. */
01598                     dhcpError = DHCPERR_SYSTEM;
01599                     dhcpState = DHCPST_IDLE;
01600                     /* At this point either socket or buffer may be allocated.
01601                        Thus we need to jump back to the top of our state loop
01602                        to release it. */
01603                     continue;
01604                 }
01605 #if MAX_DHCP_BUFSIZE
01606                 {
01607                     uint16_t max_ms = MAX_DHCP_BUFSIZE;
01608                     NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
01609                 }
01610 #endif
01611             }
01612         }
01613 
01614         /*
01615          * (Re)Start.
01616          */
01617         if (dhcpState == DHCPST_INIT) {
01618             /* Clear the retry counter. */
01619             retries = 0;
01620             /* Use a new XID on each attempt. */
01621             xid++;
01622             /* Determine whether this is an initial boot or a reboot. */
01623             if ((last_ip & confnet.cdn_ip_mask) == 0) {
01624                 /* No previous IP, start from ground up. */
01625                 dhcpState = DHCPST_SELECTING;
01626             } else {
01627                 /* We got a previously allocated IP configuration from
01628                  * non-volatile configuration memory. Try to re-use it. */
01629                 dhcpState = DHCPST_REBOOTING;
01630             }
01631         }
01632 
01633 #ifdef NUTDEBUG
01634 //        DhcpStateDebug( 1, dhcpState, last_ip, leaseTime, retries);
01635 #endif
01636         /*
01637          * Broadcast discover and collect incoming offers.
01638          */
01639         else if (dhcpState == DHCPST_SELECTING) {
01640 #ifdef NUTDEBUG
01641             if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL: TICK %p]", dhcpConfig);
01642 #endif
01643 
01644             if (retries++ > MAX_DCHP_RETRIES) {
01645                 if( dhcpConfig) {
01646                     /* Ulrich says: we got at least one valid offer from a DHCP server */
01647                     reply = 0;
01648                     leaseTime = aqsTime;
01649                     dhcpState = DHCPST_BOUND;
01650                 }
01651                 else {
01652                     /* Too many retries while discovering DHCP. Give up. */
01653                     dhcpError = DHCPERR_TIMEOUT;
01654                     dhcpState = DHCPST_IDLE;
01655                 }
01656             }
01657             /* Send the discovering broadcast. */
01658             else if (DhcpBroadcastDiscover(sock, bp, xid, last_ip, secs) < 0) {
01659                 /* Fatal transmit error on broadcast. */
01660 #ifdef NUTDEBUG
01661                 if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-TXERR]");
01662 #endif
01663                 dhcpState = DHCPST_IDLE;
01664             } else {
01665                 /* Collect incoming offers. */
01666                 while ((n = DhcpRecvMessage(sock, xid, bp, tmo)) > 0) {
01667                     /* Check if this is a valid offer. */
01668                     if ((dhcpConfig = CheckOffer(dhcpConfig, bp, n)) != 0) {
01669 #ifdef NUTDEBUG
01670                         if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-OFFER n=%d]", n);
01671 #endif
01672                         /* If the callers timeout is low, do not collect
01673                            more than one. Thanks to Jelle Kok. */
01674                         if (dhcpApiTimeout < MIN_DHCP_WAIT * 3) {
01675 #ifdef NUTDEBUG
01676                             if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[NOTL]");
01677 #endif
01678                             break;
01679                         }
01680                         /* Switch to lowest timeout after we received
01681                            a first response. */
01682                         tmo = MIN_DHCP_WAIT;
01683                     }
01684                 }
01685                 /* Change to ERROR state on fatal receive errors. */
01686                 if (n < 0) {
01687 #ifdef NUTDEBUG
01688                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-FATERR]");
01689 #endif
01690                     dhcpState = DHCPST_IDLE;
01691                 }
01692                 /* Change to REQUESTING state if we got a valid offer.
01693                    Otherwise we stay in SELECTING state. */
01694                 else if (dhcpConfig) {
01695                     /* Wait for at least one period for other DHCP servers to offer */
01696                     retries = MAX_DCHP_RETRIES+1;
01697 //                    dhcpState = DHCPST_REQUESTING;
01698 #ifdef NUTDEBUG
01699                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[SEL-WAIT]");
01700 #endif
01701                 }
01702             }
01703         }
01704 
01705         /*
01706          * Send request and wait for an acknowledge.
01707          */
01708         else if (dhcpState == DHCPST_REQUESTING) {
01709             if (retries++ > MAX_DCHP_RETRIES) {
01710                 /* Too many retries with this server, fall back to discovery. */
01711                 dhcpState = DHCPST_INIT;
01712             }
01713             /* Request an offered configuration. According to RFC 2131 this
01714                has to be broadcasted. */
01715             else if (DhcpBroadcastRequest(sock, bp, xid, 0, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid, secs) < 0) {
01716                 /* Fatal transmit error on broadcast. Give up. */
01717                 dhcpState = DHCPST_IDLE;
01718             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01719                 /* Fatal receive error. */
01720                 dhcpState = DHCPST_IDLE;
01721             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01722                 /* The server accepted our request. We are bound. */
01723                 if (reply->dyn_msgtyp == DHCP_ACK) {
01724                     ReleaseDynCfg(dhcpConfig);
01725                     dhcpConfig = reply;
01726                     reply = 0;
01727                     leaseTime = aqsTime;
01728                     dhcpState = DHCPST_BOUND;
01729                 }
01730                 /* The server declines a previously offered configuration.
01731                    Restart discovery. */
01732                 else if (reply->dyn_msgtyp == DHCP_NAK) {
01733 #ifdef NUTDEBUG
01734         if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-NAK1]");
01735 #endif
01736                     dhcpState = DHCPST_INIT;
01737                 }
01738             }
01739         }
01740 
01741         /*
01742          * Reusing a previously allocated network address after reboot.
01743          */
01744         else if (dhcpState == DHCPST_REBOOTING) {
01745             if (++retries > MAX_DCHP_RETRIES) {
01746                 /* Too many retries, fall back to discovery. */
01747                 last_ip = 0;
01748                 dhcpState = DHCPST_INIT;
01749             }
01750             /* Broadcast a request for our previous configuration. */
01751             else if (DhcpBroadcastRequest(sock, bp, xid, 0, last_ip, 0, secs) < 0) {
01752                 /* Fatal transmit error on broadcast. Give up. */
01753 #ifdef NUTDEBUG
01754                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[BC-FATAL]");
01755 #endif
01756                 dhcpState = DHCPST_IDLE;
01757             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01758                 /* Fatal receive error. */
01759 #ifdef NUTDEBUG
01760                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[RCV-FATAL]");
01761 #endif
01762                 dhcpState = DHCPST_IDLE;
01763             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01764                 if (reply->dyn_msgtyp == DHCP_ACK) {
01765                     ReleaseDynCfg(dhcpConfig);
01766                     dhcpConfig = reply;
01767                     reply = 0;
01768                     leaseTime = aqsTime;
01769                     dhcpState = DHCPST_BOUND;
01770 #ifdef NUTDEBUG
01771                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[OK]");
01772 #endif
01773                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01774                     /* Either our previous address had been allocated by
01775                        someone else or we changed the network. Remove the
01776                        previous address and restart. */
01777 #ifdef NUTDEBUG
01778                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-NAK2]");
01779 #endif
01780                     last_ip = 0;
01781                     dhcpState = DHCPST_INIT;
01782                 }
01783             }
01784         }
01785 
01786         /*
01787          * Maintain lease time.
01788          */
01789         else if (dhcpState == DHCPST_BOUND) {
01790             retries = 0;
01791             NutEventBroadcast(&dhcpDone);
01792             if (dhcpConfig->dyn_renewalTime <= NutGetSeconds() - leaseTime) {
01793                 dhcpState = DHCPST_RENEWING;
01794             } else {
01795                 /* Calculate the remaining lease time and take a nap. */
01796                 napTime = dhcpConfig->dyn_renewalTime - (NutGetSeconds() - leaseTime);
01797                 if (napTime > MAX_DHCP_NAPTIME) {
01798                     napTime = MAX_DHCP_NAPTIME;
01799                 }
01800                 NutEventWait(&dhcpWake, napTime * 1000UL);
01801             }
01802         }
01803 
01804         /*
01805          * Waiting for an acknowledge of our renewal request.
01806          */
01807         else if (dhcpState == DHCPST_RENEWING) {
01808             retries++;
01809             if (tmo / 1000 > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
01810                 tmo = dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime) * 1000;
01811             }
01812             if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
01813                 retries = 0;
01814                 dhcpState = DHCPST_REBINDING;
01815             }
01816             /* Send a request to our leasing server. We must not include
01817                the server identifier. */
01818             else if (DhcpSendRequest(sock, dhcpConfig->dyn_sid, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) <
01819                      0) {
01820                 /* Unicast transmit error. */
01821                 retries = 0;
01822                 dhcpState = DHCPST_REBINDING;
01823             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01824                 /* Fatal receive error. */
01825                 dhcpState = DHCPST_IDLE;
01826             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01827                 if (reply->dyn_msgtyp == DHCP_ACK) {
01828                     /* Got an acknowledge, return to bound state. */
01829                     ReleaseDynCfg(dhcpConfig);
01830                     dhcpConfig = reply;
01831                     reply = 0;
01832                     leaseTime = aqsTime;
01833                     dhcpState = DHCPST_BOUND;
01834                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01835                     /* Unexpected NAK. */
01836 #ifdef NUTDEBUG
01837                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-UXNAK]");
01838 #endif
01839                     retries = 0;
01840                     dhcpState = DHCPST_REBINDING;
01841                 }
01842             }
01843         }
01844 
01845         /*
01846          * Waiting for an acknowledge of our rebind request.
01847          */
01848         else if (dhcpState == DHCPST_REBINDING) {
01849             retries++;
01850             if (tmo > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
01851                 tmo = dhcpConfig->dyn_rebindTime - NutGetSeconds() - leaseTime;
01852             }
01853             if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
01854                 retries = 0;
01855                 dhcpState = DHCPST_REBINDING;
01856             }
01857             /* Broadcast a request for our previous configuration. We
01858                must not include a server identifier. */
01859             else if (DhcpBroadcastRequest(sock, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) < 0) {
01860                 /* Fatal transmit error on broadcast. Give up. */
01861                 dhcpState = DHCPST_IDLE;
01862             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01863                 /* Fatal receive error. */
01864                 dhcpState = DHCPST_IDLE;
01865             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01866                 if (reply->dyn_msgtyp == DHCP_ACK) {
01867                     /* Got an acknowledge, return to bound state. */
01868                     ReleaseDynCfg(dhcpConfig);
01869                     dhcpConfig = reply;
01870                     reply = 0;
01871                     leaseTime = aqsTime;
01872                     dhcpState = DHCPST_BOUND;
01873                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01874                     /*
01875                      * We have a problem here if the last DHCP server died.
01876                      * If a backup server exists, it may probe our IP address
01877                      * using ARP or ICMP. Our interface is up and responding,
01878                      * so the backup server may think that the IP address
01879                      * is in use and respond with NAK. Without shutting
01880                      * down our interface (not yet implemented) we are stuck.
01881                      * We switch to discovery state, but the problem remains.
01882                      */
01883 #ifdef NUTDEBUG
01884                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-NAK3]");
01885 #endif
01886                     dhcpState = DHCPST_INIT;
01887                 }
01888             }
01889         }
01890 
01891         /*
01892          * Send an inform and wait for its (optional) echo.
01893          */
01894         else if (dhcpState == DHCPST_INFORMING) {
01895             if (retries++ > MAX_DCHP_RETRIES) {
01896                 dhcpState = DHCPST_IDLE;
01897             } else if (DhcpSendInform(sock, server_ip, bp, xid, nif->if_local_ip) < 0) {
01898                 if (server_ip == INADDR_BROADCAST) {
01899                     dhcpState = DHCPST_IDLE;
01900                 }
01901             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) != 0) {
01902                 if (n > 0 &&    /* No receive error. */
01903                     (reply = ParseReply(bp, n)) != 0 && /* No parser error. */
01904                     reply->dyn_msgtyp == DHCP_ACK) {    /* Acknowledged. */
01905                     /* Take over this configuration. */
01906                     ReleaseDynCfg(dhcpConfig);
01907                     dhcpConfig = reply;
01908                     reply = 0;
01909                 }
01910                 dhcpState = DHCPST_IDLE;
01911             }
01912         }
01913 
01914         /*
01915          * Send a release and wait for its (optional) echo.
01916          */
01917         else if (dhcpState == DHCPST_RELEASING) {
01918             if (dhcpConfig == 0 ||      /* Not configured. */
01919                 retries++ > MAX_DCHP_RELEASE_RETRIES || /* Too many retries. */
01920                 DhcpSendRelease(sock, server_ip, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid) < 0) {
01921                 if (server_ip == INADDR_BROADCAST) {
01922                     dhcpState = DHCPST_IDLE;
01923                 }
01924             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01925                 /* Fatal receive error. */
01926                 dhcpState = DHCPST_IDLE;
01927             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01928                 if (reply->dyn_msgtyp == DHCP_ACK) {
01929                     dhcpState = DHCPST_IDLE;
01930                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01931 #ifdef NUTDEBUG
01932                     if (__tcp_trf & NET_DBG_DHCP) fprintf(__tcp_trs, "[DHCP-FNAK]");
01933 #endif
01934                     dhcpState = DHCPST_IDLE;
01935                 }
01936             }
01937         }
01938 
01939         /*
01940          * We are done somehow. Either a fatal error occured or we
01941          * reached the specified timeout time or our lease has been
01942          * release or something else configured our interface.
01943          * Release all resources and wait for a new API call to
01944          * wake us up.
01945          */
01946         else if (dhcpState == DHCPST_IDLE) {
01947             ReleaseDynCfg(dhcpConfig);
01948             dhcpConfig = 0;
01949             retries = 0;
01950             NutEventBroadcast(&dhcpDone);
01951             NutEventWait(&dhcpWake, NUT_WAIT_INFINITE);
01952         }
01953 
01954         /* Release any received reply. */
01955         if (reply) {
01956             ReleaseDynCfg(reply);
01957             reply = 0;
01958         }
01959     }
01960 }
01961 
01976 static int DhcpKick(CONST char *name, uint8_t state, uint32_t timeout)
01977 {
01978     NUTDEVICE *dev;
01979     IFNET *nif;
01980 
01981     /* Lookup the Ethernet device. */
01982     if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
01983         dev->dev_type != IFTYP_NET ||   /* Wrong type */
01984         (nif = dev->dev_icb) == 0 ||    /* No netif */
01985         nif->if_type != IFT_ETHER) {    /* Wrong if type */
01986         dhcpError = DHCPERR_BADDEV;
01987         return -1;
01988     }
01989 
01990     /* Initialize timeout checking. */
01991     dhcpApiStart = NutGetMillis();
01992     dhcpApiTimeout = timeout;
01993 
01994     dhcpState = state;
01995     if (dhcpThread == 0) {
01996 #ifdef __arm__
01997         dhcpDev = dev;
01998 #endif
01999         dhcpThread = NutThreadCreate("dhcpc", NutDhcpClient, dev,
02000             (NUT_THREAD_DHCPSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
02001     }
02002     NutEventPost(&dhcpWake);
02003     NutEventWait(&dhcpDone, NUT_WAIT_INFINITE);
02004 
02005     return 0;
02006 }
02007 
02047 int NutDhcpIfConfig(CONST char *name, uint8_t * mac, uint32_t timeout)
02048 {
02049     uint8_t mac0[6];
02050     uint8_t macF[6];
02051     NUTDEVICE *dev;
02052     IFNET *nif;
02053 
02054     /*
02055      * Lookup the Ethernet device.
02056      */
02057     if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
02058         dev->dev_type != IFTYP_NET ||   /* Wrong type */
02059         (nif = dev->dev_icb) == 0 ||    /* No netif */
02060         nif->if_type != IFT_ETHER) {    /* Wrong if type */
02061         dhcpError = DHCPERR_BADDEV;
02062         return -1;
02063     }
02064 
02065     /*
02066      * We determine whether the interface is enabled by checking
02067      * the MAC address. This is so bloody brain dead.
02068      */
02069     memset(mac0, 0x00, sizeof(mac0));   /* Uses more code but less RAM... */
02070     memset(macF, 0xFF, sizeof(macF));   /* ...than init in declaration.   */
02071     if (memcmp(nif->if_mac, mac0, 6) == 0 || memcmp(nif->if_mac, macF, 6) == 0) {
02072         /*
02073          * If the caller specified a MAC address, we use it and
02074          * overwrite the configuration.
02075          */
02076         if (mac) {
02077             memcpy(confnet.cdn_mac, mac, sizeof(confnet.cdn_mac));
02078         }
02079 
02080         /*
02081          * If no MAC address has been specified, read the configuration
02082          * from EEPROM. If this fails, we do not continue any further,
02083          * but let the caller know that something is wrong. He may call
02084          * us again with a valid MAC address.
02085          */
02086         else if (NutNetLoadConfig(name)) {
02087             dhcpError = DHCPERR_NOMAC;
02088             return -1;
02089         }
02090 
02091         /*
02092          * Copy the MAC address to the interface structure. This will
02093          * magically brain dead enable the interface.
02094          */
02095         memcpy(nif->if_mac, confnet.cdn_mac, 6);
02096         NutSleep(500);
02097     }
02098 
02099     /*
02100      * Zero out the ip address and mask. This allows to switch between
02101      * DHCP and static IP addresses without resetting/power cycling.
02102      * See patch #2903940.
02103      */
02104     nif->if_local_ip = 0;
02105     nif->if_mask = confnet.cdn_ip_mask;
02106 
02107     /*
02108      * If the EEPROM contains a fixed network configuration, we skip DHCP.
02109      */
02110     if ((confnet.cdn_cip_addr & confnet.cdn_ip_mask) != 0) {
02111         /* Give up a previously allocated lease. See patch #2903940. */
02112         (void)NutDhcpRelease(name, (3*MIN_DHCP_WAIT));
02113         confnet.cdn_ip_addr = confnet.cdn_cip_addr;
02114         NutNetIfConfig2(name,
02115                         confnet.cdn_mac,
02116                         confnet.cdn_ip_addr,
02117                         confnet.cdn_ip_mask,
02118                         confnet.cdn_gateway);
02119         return 0;
02120     }
02121 
02122     /*
02123      * Start the DHCP thread if not running or wake it up. Pass the caller's
02124      * timeout to the thread and wait an infinite time. We rely on the thread
02125      * to wake us up on timeout.
02126      */
02127     if (DhcpKick(name, DHCPST_INIT, timeout) == 0) {
02128         /*
02129          * The thread finished its task. If it reached the bound state, then
02130          * we got a valid configuration from DHCP.
02131          */
02132         if (dhcpState == DHCPST_BOUND) {
02133 #ifdef NUTDEBUG
02134             if (__tcp_trf & NET_DBG_DHCP) {
02135                 fprintf(__tcp_trs, "[DHCP-Config %s]", inet_ntoa(dhcpConfig->dyn_yiaddr));
02136             }
02137 #endif
02138             NutNetIfSetup(dev, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_netmask, dhcpConfig->dyn_gateway);
02139             NutDnsConfig2(NULL, NULL, dhcpConfig->dyn_pdns, dhcpConfig->dyn_sdns);
02140             return 0;
02141         }
02142 
02143         /*
02144          * Our interface has been configured externally, possibly by auto
02145          * ARP or a similar function implemented by the application.
02146          */
02147         if (nif->if_local_ip) {
02148 #ifdef NUTDEBUG
02149             if (__tcp_trf & NET_DBG_DHCP) {
02150                 fprintf(__tcp_trs, "[DHCP-External %s]", inet_ntoa(nif->if_local_ip));
02151             }
02152 #endif
02153             return 0;
02154         }
02155 
02156         /*
02157          * DHCP failed. In case we remember a previously allocated address,
02158          * then let's use it.
02159          */
02160         if ((confnet.cdn_ip_addr & confnet.cdn_ip_mask) != 0) {
02161 #ifdef NUTDEBUG
02162             if (__tcp_trf & NET_DBG_DHCP) {
02163                 fprintf(__tcp_trs, "[DHCP-Reusing %s]", inet_ntoa(confnet.cdn_ip_addr));
02164             }
02165 #endif
02166             NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
02167             return 0;
02168         }
02169     }
02170     return -1;
02171 }
02172 
02189 int NutDhcpRelease(CONST char *name, uint32_t timeout)
02190 {
02191     /* Check the state. */
02192     if (dhcpState != DHCPST_BOUND) {
02193         dhcpError = DHCPERR_STATE;
02194         return -1;
02195     }
02196 
02197     /* Action! */
02198     return DhcpKick(name, DHCPST_RELEASING, timeout);
02199 }
02200 
02211 int NutDhcpInform(CONST char *name, uint32_t timeout)
02212 {
02213     /* Check the state. */
02214     if (dhcpState != DHCPST_IDLE) {
02215         dhcpError = DHCPERR_STATE;
02216         return -1;
02217     }
02218 
02219     /* Action! */
02220     return DhcpKick(name, DHCPST_INFORMING, timeout);
02221 }
02222 
02240 int NutDhcpStatus(CONST char *name)
02241 {
02242     return dhcpState;
02243 }
02244 
02263 int NutDhcpError(CONST char *name)
02264 {
02265     int rc = dhcpError;
02266     dhcpError = 0;
02267     return rc;
02268 }
02269 
02277 int NutDhcpIsConfigured(void)
02278 {
02279     return (dhcpState == DHCPST_BOUND);
02280 }
02281