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 <sys/thread.h>
00200 #include <sys/event.h>
00201 #include <sys/timer.h>
00202 #include <sys/confnet.h>
00203 #include <sys/confos.h>
00204 
00205 #include <stdlib.h>
00206 #include <string.h>
00207 #include <time.h>
00208 #include <memdebug.h>
00209 
00210 #include <arpa/inet.h>
00211 #include <netinet/in.h>
00212 #include <netdb.h>
00213 #include <net/route.h>
00214 #include <sys/socket.h>
00215 #include <pro/dhcp.h>
00216 
00217 #ifdef NUTDEBUG
00218 #include <net/netdebug.h>
00219 #endif
00220 
00221 #if 0
00222 /* Use for local debugging. */
00223 #define NUTDEBUG
00224 #include <stdio.h>
00225 #define __tcp_trs stdout
00226 static uint_fast8_t __tcp_trf = 1;
00227 #endif
00228 
00233 
00240 
00246 #ifndef DHCP_SERVERPORT
00247 #define DHCP_SERVERPORT      67
00248 #endif
00249 
00255 #ifndef DHCP_CLIENTPORT
00256 #define DHCP_CLIENTPORT      68
00257 #endif
00258 
00268 #ifndef MAX_DHCP_MSGSIZE
00269 #define MAX_DHCP_MSGSIZE    576
00270 #endif
00271 
00279 #ifndef MIN_DHCP_MSGSIZE
00280 #define MIN_DHCP_MSGSIZE    300
00281 #endif
00282 
00298 #ifndef MAX_DHCP_BUFSIZE
00299 #define MAX_DHCP_BUFSIZE    1728
00300 #endif
00301 
00311 #ifndef MIN_DHCP_WAIT
00312 #define MIN_DHCP_WAIT       4000
00313 #endif
00314 
00323 #ifndef MAX_DHCP_WAIT
00324 #define MAX_DHCP_WAIT       64000
00325 #endif
00326 
00335 #ifndef MAX_DCHP_RETRIES
00336 #define MAX_DCHP_RETRIES    3
00337 #endif
00338 
00347 #ifndef MAX_DCHP_RELEASE_RETRIES
00348 #define MAX_DCHP_RELEASE_RETRIES    0
00349 #endif
00350 
00358 #ifndef DHCP_DEFAULT_LEASE
00359 #define DHCP_DEFAULT_LEASE  43200
00360 #endif
00361 
00367 #ifndef MAX_DHCP_NAPTIME
00368 #define MAX_DHCP_NAPTIME    4294967
00369 #endif
00370 
00376 #ifndef NUT_THREAD_DHCPSTACK
00377 #if defined(__AVR__)
00378 /* avr-gcc size optimized code used 192 bytes. */
00379 #define NUT_THREAD_DHCPSTACK    288
00380 #else
00381 /* arm-elf-gcc used 276 bytes with size optimized, 680 bytes with debug code. */
00382 #define NUT_THREAD_DHCPSTACK    384
00383 #endif
00384 #endif
00385 
00394 
00397 #define DHCP_DISCOVER   1
00398 
00403 #define DHCP_OFFER      2
00404 
00412 #define DHCP_REQUEST    3
00413 
00418 #define DHCP_DECLINE    4
00419 
00424 #define DHCP_ACK        5
00425 
00430 #define DHCP_NAK        6
00431 
00434 #define DHCP_RELEASE    7
00435 
00440 #define DHCP_INFORM     8
00441 
00451 
00458 #define DHCPOPT_PAD          0
00459 
00463 #define DHCPOPT_NETMASK      1
00464 
00468 #define DHCPOPT_GATEWAY      3
00469 
00473 #define DHCPOPT_DNS          6
00474 
00478 #define DHCPOPT_HOSTNAME     12
00479 
00483 #define DHCPOPT_DOMAIN       15
00484 
00488 #define DHCPOPT_BROADCAST    28
00489 
00493 #define DHCPOPT_REQESTIP     50
00494 
00498 #define DHCPOPT_LEASETIME    51
00499 
00503 #define DHCPOPT_MSGTYPE      53
00504 
00508 #define DHCPOPT_SID          54
00509 
00513 #define DHCPOPT_PARAMREQUEST 55
00514 
00518 #define DHCPOPT_MAXMSGSIZE   57
00519 
00523 #define DHCPOPT_RENEWALTIME  58
00524 
00528 #define DHCPOPT_REBINDTIME   59
00529 
00533 #define DHCPOPT_END          255
00534 
00540 typedef struct bootp BOOTP;
00541 
00545 struct __attribute__ ((packed)) bootp {
00546     uint8_t bp_op;              
00547     uint8_t bp_htype;           
00548     uint8_t bp_hlen;            
00549     uint8_t bp_hops;            
00550     uint32_t bp_xid;              
00551     uint16_t bp_secs;            
00552     uint16_t bp_flags;           
00553     uint32_t bp_ciaddr;           
00554     uint32_t bp_yiaddr;           
00555     uint32_t bp_siaddr;           
00556     uint32_t bp_giaddr;           
00557     uint8_t bp_chaddr[16];      
00558     char bp_sname[64];          
00559     char bp_file[128];          
00560     uint8_t bp_options[312];    
00561 };
00562 
00566 typedef struct dyn_cfg DYNCFG;
00567 
00571 struct dyn_cfg {
00572     uint8_t dyn_msgtyp;         
00573     uint32_t dyn_yiaddr;          
00574     uint32_t dyn_netmask;         
00575     uint32_t dyn_broadcast;       
00576     uint32_t dyn_gateway;         
00577     uint32_t dyn_pdns;            
00578     uint32_t dyn_sdns;            
00579     uint32_t dyn_sid;             
00580     uint32_t dyn_renewalTime;     
00581     uint32_t dyn_rebindTime;      
00582     uint32_t dyn_leaseTime;       
00583     uint8_t *dyn_hostname;      
00584     uint8_t *dyn_domain;        
00585 };
00586 
00593 static DYNCFG *dhcpConfig;
00594 
00601 static HANDLE dhcpThread;
00602 
00606 static uint8_t dhcpState;
00607 
00611 static int dhcpError;
00612 
00618 static HANDLE dhcpWake;
00619 
00625 static HANDLE dhcpDone;
00626 
00632 static uint32_t dhcpApiTimeout;
00633 
00640 static uint32_t dhcpApiStart;
00641 
00648 #ifdef __arm__
00649 static NUTDEVICE *dhcpDev;
00650 #endif
00651 
00663 static void copy_str(uint8_t ** dst, void *src, int len)
00664 {
00665     if (*dst) {
00666         free(*dst);
00667     }
00668     if ((*dst = malloc(len + 1)) != 0) {
00669         if (len) {
00670             memcpy(*dst, src, len);
00671         }
00672         *(*dst + len) = 0;
00673     }
00674 }
00675 
00683 static void ReleaseDynCfg(DYNCFG * dyncfg)
00684 {
00685     if (dyncfg) {
00686         if (dyncfg->dyn_hostname) {
00687             free(dyncfg->dyn_hostname);
00688         }
00689         if (dyncfg->dyn_domain) {
00690             free(dyncfg->dyn_domain);
00691         }
00692         free(dyncfg);
00693     }
00694 }
00695 
00707 static DYNCFG *ParseReply(BOOTP *bp, int len)
00708 {
00709     uint8_t *op;
00710     int left;
00711     DYNCFG *cfgp;
00712 
00713     /* Allocate and initialize a new structure. */
00714     if ((cfgp = malloc(sizeof(DYNCFG))) == 0) {
00715         return 0;
00716     }
00717     memset(cfgp, 0, sizeof(DYNCFG));
00718     cfgp->dyn_leaseTime = DHCP_DEFAULT_LEASE;
00719 
00720     /* Set the assigned IP address. */
00721     memcpy(&cfgp->dyn_yiaddr, &bp->bp_yiaddr, 4);
00722 
00723     /* 
00724      * Parse options until an end option is found or until we reached
00725      * the end of the message.
00726      */
00727     op = bp->bp_options + 4;
00728     left = len - (sizeof(*bp) - sizeof(bp->bp_options)) - 4;
00729     while (*op != DHCPOPT_END && left > 0) {
00730         uint8_t ol;
00731 
00732 #ifdef NUTDEBUG
00733         if (__tcp_trf) {
00734             fprintf(__tcp_trs, "[DHCP-Opt-%u]", *op);
00735         }
00736 #endif
00737         /* Pad option is used for boundary alignment. */
00738         if (*op == DHCPOPT_PAD) {
00739             op++;
00740             left--;
00741             continue;
00742         }
00743 
00744         /* Reject if option length exceeds total length. */
00745         if ((ol = *(op + 1)) > left) {
00746             break;
00747         }
00748 
00749         /* Type of this message. */
00750         if (*op == DHCPOPT_MSGTYPE) {
00751             if (ol != 1) {
00752                 break;
00753             }
00754             cfgp->dyn_msgtyp = *(op + 2);
00755         }
00756         /* Our host name. May or may not include the domain. */
00757         else if (*op == DHCPOPT_HOSTNAME) {
00758             copy_str(&cfgp->dyn_hostname, op + 2, ol);
00759         }
00760         /* Name of the domain we are in. */
00761         else if (*op == DHCPOPT_DOMAIN) {
00762             copy_str(&cfgp->dyn_domain, op + 2, ol);
00763         }
00764 
00765         /* All remaining options require at least 4 octets. */
00766         else if (ol >= 4) {
00767             /* Preset most often used long value. */
00768             uint32_t lval = *(op + 2);
00769             lval += (uint32_t)(*(op + 3)) << 8;
00770             lval += (uint32_t)(*(op + 4)) << 16;
00771             lval += (uint32_t)(*(op + 5)) << 24;
00772 
00773             /* Our IP network mask. */
00774             if (*op == DHCPOPT_NETMASK) {
00775                 cfgp->dyn_netmask = lval;
00776             }
00777             /* Our IP broadcast address. */
00778             else if (*op == DHCPOPT_BROADCAST) {
00779                 cfgp->dyn_broadcast = lval;
00780             }
00781             /* Our IP default gate. More than one gateway may be 
00782                specified. We take the fist one only and ignore the 
00783                rest. */
00784             else if (*op == DHCPOPT_GATEWAY) {
00785                 cfgp->dyn_gateway = lval;
00786             }
00787             /* Our DNS server. Updated by Jelle Martijn Kok to 
00788                support a secondary DNS. */
00789             else if (*op == DHCPOPT_DNS) {
00790                 cfgp->dyn_pdns = lval;
00791                 if (ol >= 8) {
00792                     cfgp->dyn_sdns = *(op + 6);
00793                     cfgp->dyn_sdns += (uint32_t)(*(op + 7)) << 8;
00794                     cfgp->dyn_sdns += (uint32_t)(*(op + 8)) << 16;
00795                     cfgp->dyn_sdns += (uint32_t)(*(op + 9)) << 24;
00796                 }
00797             }
00798             /* Server identifier. */
00799             else if (*op == DHCPOPT_SID) {
00800                 cfgp->dyn_sid = lval;
00801             }
00802             /* Renewal time. */
00803             else if (*op == DHCPOPT_RENEWALTIME) {
00804                 cfgp->dyn_renewalTime = ntohl(lval);
00805             }
00806             /* Rebinding time. */
00807             else if (*op == DHCPOPT_REBINDTIME) {
00808                 cfgp->dyn_rebindTime = ntohl(lval);
00809             }
00810             /* Total lease time granted. */
00811             else if (*op == DHCPOPT_LEASETIME) {
00812                 cfgp->dyn_leaseTime = ntohl(lval);
00813             }
00814         }
00815         op += ol + 2;
00816         left -= ol + 2;
00817     }
00818 
00819     /*
00820      * Discard this configuration if parsing stopped before reaching 
00821      * the end option or if we didn't receive an expected message type.
00822      */
00823     if (*op != DHCPOPT_END ||   /* */
00824         (cfgp->dyn_msgtyp != DHCP_OFFER &&      /* */
00825          cfgp->dyn_msgtyp != DHCP_ACK &&        /* */
00826          cfgp->dyn_msgtyp != DHCP_NAK)) {
00827 #ifdef NUTDEBUG
00828         if (__tcp_trf) {
00829             fprintf(__tcp_trs, "[DHCP-Parse Error]");
00830         }
00831 #endif
00832         ReleaseDynCfg(cfgp);
00833         return 0;
00834     }
00835 
00836     /* Calculate renewal and rebind times. */
00837     if (cfgp->dyn_renewalTime == 0) {
00838         cfgp->dyn_renewalTime = cfgp->dyn_leaseTime / 2;
00839     }
00840     if (cfgp->dyn_rebindTime == 0) {
00841         cfgp->dyn_rebindTime = cfgp->dyn_renewalTime +  /* */
00842             cfgp->dyn_renewalTime / 2 + /* */
00843             cfgp->dyn_renewalTime / 4;
00844     }
00845     return cfgp;
00846 }
00847 
00858 static size_t DhcpAddOption(uint8_t * op, uint8_t ot, void *ov, uint8_t len)
00859 {
00860     *op++ = ot;
00861     *op++ = len;
00862     memcpy(op, ov, len);
00863 
00864     return 2 + len;
00865 }
00866 
00876 static size_t DhcpAddByteOption(uint8_t * op, uint8_t ot, uint8_t ov)
00877 {
00878     *op++ = ot;
00879     *op++ = 1;
00880     *op++ = ov;
00881 
00882     return 3;
00883 }
00884 
00895 static size_t DhcpAddShortOption(uint8_t * op, uint8_t ot, uint16_t ov)
00896 {
00897     *op++ = ot;
00898     *op++ = 2;
00899     ov = htons(ov);
00900     memcpy(op, &ov, 2);
00901 
00902     return 4;
00903 }
00904 
00917 static size_t DhcpAddParmReqOption(uint8_t * op)
00918 {
00919     *op++ = DHCPOPT_PARAMREQUEST;
00920     *op++ = 3;                  /* Adjust when adding more options! */
00921     *op++ = DHCPOPT_NETMASK;    /* Typically sent by default, but play safe. */
00922     *op++ = DHCPOPT_GATEWAY;    /* We want a default gateway. */
00923     *op++ = DHCPOPT_DNS;        /* We want the DNS' IP. */
00924     return 5;                   /* Adjust when adding more options! */
00925 }
00926 
00948 static unsigned int DhcpPrepHeader(BOOTP *bp, uint8_t msgtyp, uint32_t xid, uint32_t ciaddr, uint16_t secs)
00949 {
00950     uint8_t *op;
00951 
00952     memset(bp, 0, sizeof(*bp));
00953     /* Clients send bootp requests (op code 1) only. */
00954     bp->bp_op = 1;
00955     /* Ethernet addresses are type 1 with 6 octets. */
00956     bp->bp_htype = 1;
00957     bp->bp_hlen = 6;
00958     memcpy(bp->bp_chaddr, confnet.cdn_mac, 6);
00959     /* Transaction identifier. */
00960     bp->bp_xid = xid;
00961     /* Seconds elapsed since address acquisition. */
00962     bp->bp_secs = htons(secs);
00963 
00964 #ifdef DHCP_BROADCAST_FLAG
00965     /*
00966      * We do not need the broadcast flag, because our stack accepts IP 
00967      * messages to any destination if no local address has been assigned,
00968      * However, we continue supporting this compile time option.
00969      */
00970     bp->bp_flags = htons(0x8000);
00971 #endif
00972 
00973     bp->bp_ciaddr = ciaddr;
00974 
00975     /* Add the DHCP magic cookie according to RFC 1497. */
00976     op = bp->bp_options;
00977     *op++ = 0x63;
00978     *op++ = 0x82;
00979     *op++ = 0x53;
00980     *op++ = 0x63;
00981 
00982     /* Add the DHCP message type option. */
00983     return DhcpAddByteOption(op, DHCPOPT_MSGTYPE, msgtyp) + 4;
00984 }
00985 
01004 static int DhcpSendMessage(UDPSOCKET * sock, uint32_t addr, BOOTP *bp, size_t len)
01005 {
01006     /* Add 'end of options'. */
01007     bp->bp_options[len++] = DHCPOPT_END;
01008 
01009     /* Maintain a BOOTP compatible minimum packet size of 300 octets. 
01010        Thanks to Tomohiro Haraikawa. */
01011     if ((len += sizeof(BOOTP) - sizeof(bp->bp_options)) < MIN_DHCP_MSGSIZE) {
01012         len = MIN_DHCP_MSGSIZE;
01013     }
01014 #ifdef NUTDEBUG
01015     if (__tcp_trf) {
01016         fprintf(__tcp_trs, "[DHCP-Send to %s]", inet_ntoa(addr));
01017     }
01018 #endif
01019     if (NutUdpSendTo(sock, addr, DHCP_SERVERPORT, bp, len) < 0) {
01020         dhcpError = DHCPERR_TRANSMIT;
01021         return -1;
01022     }
01023     return 0;
01024 }
01025 
01039 static int DhcpRecvMessage(UDPSOCKET * sock, uint32_t xid, BOOTP *bp, uint32_t tmo)
01040 {
01041     int rc;
01042     uint16_t port;
01043     uint32_t addr;
01044     uint32_t etim;
01045     uint32_t wtim;
01046 
01047     /* Set our start time. */
01048     etim = NutGetMillis();
01049     /* Set the initial receive timeout. */
01050     wtim = tmo;
01051     for (;;) {
01052         rc = NutUdpReceiveFrom(sock, &addr, &port, bp, sizeof(BOOTP), wtim);
01053 #ifdef NUTDEBUG
01054         if (__tcp_trf) {
01055             if (rc > 0) {
01056                 fprintf(__tcp_trs, "[DHCP-Recv from %s]", inet_ntoa(addr));
01057             } else if (rc < 0) {
01058                 fprintf(__tcp_trs, "[DHCP-Recv Error]");
01059             } else {
01060                 fprintf(__tcp_trs, "[DHCP-Recv Timeout %lu]", tmo);
01061             }
01062         }
01063 #endif
01064         /* Immediately return on receive errors and timeouts. */
01065         if (rc <= 0) {
01066             if (rc < 0) {
01067                 dhcpError = DHCPERR_RECEIVE;
01068             }
01069             break;
01070         }
01071         /* The message must at least include the BOOTP header plus five 
01072            bytes of options (magic and end). We are quite liberal here. */
01073         if (rc > sizeof(BOOTP) - sizeof(bp->bp_options) + 5) {
01074             /* The message must be a BOOTP reply with the expected XID. */
01075             if (bp->bp_op == 2 && bp->bp_xid == xid) {
01076                 /* Message is acceptable. */
01077                 break;
01078             }
01079         }
01080         /* Calculate the remaining timeout for not getting trapped here 
01081            on a busy network, which regularly broadcasts DHCP messages. */
01082         wtim = NutGetMillis() - etim;
01083         if (wtim >= tmo - 250) {
01084             /* Less than 250 ms left, return timeout. */
01085             rc = 0;
01086             break;
01087         }
01088         wtim = tmo - wtim;
01089     }
01090     return rc;
01091 }
01092 
01107 static int DhcpBroadcastDiscover(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, uint32_t raddr, uint16_t secs)
01108 {
01109     size_t optlen;
01110     int len;
01111     uint8_t *op = bp->bp_options;
01112 
01113     optlen = DhcpPrepHeader(bp, DHCP_DISCOVER, xid, 0, secs);
01114 
01115     /* Request a specific IP if one had been assigned previously. */
01116     if (raddr) {
01117         optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
01118     }
01119 
01120     optlen += DhcpAddParmReqOption(op + optlen);
01121 
01122     /* Pass host name if specified in confos structure.  
01123      * Win2k DHCP server can register this as dynamic DNS entry.
01124      * Also viewing DHCP lease table shows something sensible.
01125      */
01126     len = strlen(confos.hostname);
01127     if (len > 0) {
01128         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01129     }
01130 
01131     /* Request a maximum message size. */
01132     optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
01133 
01134     return DhcpSendMessage(sock, INADDR_BROADCAST, bp, optlen);
01135 }
01136 
01137 
01157 static int DhcpSendRequest(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid,        /* */
01158                            uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
01159 {
01160     size_t optlen;
01161     int len;
01162     uint8_t *op = bp->bp_options;
01163 
01164     /* Initialize the BOOTP header. */
01165     optlen = DhcpPrepHeader(bp, DHCP_REQUEST, xid, caddr, secs);
01166 
01167     /* Add specified options. */
01168     if (raddr) {
01169         optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
01170     }
01171     if (sid) {
01172         optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
01173     }
01174     optlen += DhcpAddParmReqOption(op + optlen);
01175 
01176     /* Pass host name if specified in confos structure.  */
01177     /* viewing DHCP lease table shows something sensible. */
01178     len = strlen(confos.hostname);
01179     if (len > 0) {
01180         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01181     }
01182 
01183     return DhcpSendMessage(sock, daddr, bp, optlen);
01184 }
01185 
01204 static int DhcpBroadcastRequest(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, /* */
01205                                 uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
01206 {
01207     return DhcpSendRequest(sock, INADDR_BROADCAST, bp, xid, caddr, raddr, sid, secs);
01208 }
01209 
01225 static int DhcpSendRelease(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr, uint32_t sid)
01226 {
01227     size_t optlen;
01228     uint8_t *op = bp->bp_options;
01229 
01230     /* Prepare BOOTP header. 'secs' is set to zero. */
01231     optlen = DhcpPrepHeader(bp, DHCP_RELEASE, xid, caddr, 0);
01232 
01233     /* Optionally add server identifier. */
01234     if (sid) {
01235         optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
01236     }
01237     return DhcpSendMessage(sock, daddr, bp, optlen);
01238 }
01239 
01252 static int DhcpSendInform(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr)
01253 {
01254     size_t optlen;
01255     size_t len;
01256     uint8_t *op = bp->bp_options;
01257 
01258     /* Prepare BOOTP header. 'secs' is set to zero. */
01259     optlen = DhcpPrepHeader(bp, DHCP_INFORM, xid, caddr, 0);
01260 
01261     /* Additional options we want. */
01262     optlen += DhcpAddParmReqOption(op + optlen);
01263 
01264     /* Add our configured host name. */
01265     len = strlen(confos.hostname);
01266     if (len > 0) {
01267         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01268     }
01269 
01270     /* We should provide the maximum message size. */
01271     optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
01272 
01273     return DhcpSendMessage(sock, daddr, bp, optlen);
01274 }
01275 
01276 
01287 static DYNCFG *CheckOffer(DYNCFG * dyncfg, BOOTP *bp, size_t len)
01288 {
01289     DYNCFG *offer;
01290 
01291     /* Parse the new offer. If it's invalid, return the current 
01292        configuration. */
01293     if ((offer = ParseReply(bp, len)) == 0) {
01294         return dyncfg;
01295     }
01296 
01297     /* Discard anything which is not an offer. */
01298     if (offer->dyn_msgtyp != DHCP_OFFER) {
01299         ReleaseDynCfg(offer);
01300         return dyncfg;
01301     }
01302 
01303     /* First offer, take it. */
01304     if (dyncfg == 0) {
01305         dyncfg = offer;
01306     }
01307 
01308     /* 
01309      * Check if the new offer is better than the current configuration:
01310      */
01311     else {
01312         /* If we remember a previously allocated IP which isn't in the
01313            current configuration but in the new offer, then let's take 
01314            the new one. */
01315         if (confnet.cdn_ip_addr & confnet.cdn_ip_mask) {
01316             if (dyncfg->dyn_yiaddr != confnet.cdn_ip_addr &&    /* */
01317                 offer->dyn_yiaddr == confnet.cdn_ip_addr) {
01318                 ReleaseDynCfg(dyncfg);
01319                 dyncfg = offer;
01320             }
01321         }
01322         /* In the second place prefer long lease times. */
01323         else if (offer->dyn_leaseTime > dyncfg->dyn_leaseTime) {
01324             ReleaseDynCfg(dyncfg);
01325             dyncfg = offer;
01326         }
01327         /* The new one deosn't offer anything interesting. Discard it. */
01328         else {
01329             ReleaseDynCfg(offer);
01330         }
01331     }
01332     return dyncfg;
01333 }
01334 
01348 THREAD(NutDhcpClient, arg)
01349 {
01350     DYNCFG *reply = 0;
01351     UDPSOCKET *sock = 0;
01352     BOOTP *bp = 0;
01353     int n;
01354     uint32_t xid;
01355     IFNET *nif;
01356     uint16_t secs = 0;
01357     uint32_t aqsTime = NutGetSeconds();
01358     uint32_t leaseTime = 0;
01359     uint32_t napTime;
01360     ureg_t retries;
01361     uint32_t tmo = MIN_DHCP_WAIT;
01362     uint32_t last_ip = confnet.cdn_ip_addr;
01363     uint32_t server_ip;
01364 
01365     /*
01366      * Hack alert: Our ARM port doesn't allow parameter
01367      * passing to threads.
01368      */
01369 #ifdef __arm__
01370     nif = dhcpDev->dev_icb;
01371 #else
01372     nif = ((NUTDEVICE *) arg)->dev_icb;
01373 #endif
01374 
01375     /* 
01376      * Generate a random transaction identifier based on our MAC 
01377      * address with the least significant byte of the MAC address 
01378      * becoming the most significant byte of the identifier. This 
01379      * should give a sufficient unique value when several Ethernuts 
01380      * are started concurrently. 
01381      */
01382     xid = 0;
01383     for (retries = 0; retries < sizeof(xid); retries++) {
01384         xid <<= 8;
01385         xid += nif->if_mac[5 - retries];
01386     }
01387     retries = 0;
01388 
01389     for (;;) {
01390 #ifdef NUTDEBUG
01391         if (__tcp_trf) {
01392             fprintf(__tcp_trs, "\n[%u.DHCP-", retries + 1);
01393             switch (dhcpState) {
01394             case DHCPST_INIT:
01395                 fprintf(__tcp_trs, "INIT]");
01396                 break;
01397             case DHCPST_SELECTING:
01398                 fprintf(__tcp_trs, "SELECTING]");
01399                 break;
01400             case DHCPST_REQUESTING:
01401                 fprintf(__tcp_trs, "REQUESTING]");
01402                 break;
01403             case DHCPST_REBOOTING:
01404                 fprintf(__tcp_trs, "REBOOTING %s]", inet_ntoa(last_ip));
01405                 break;
01406             case DHCPST_BOUND:
01407                 fprintf(__tcp_trs, "BOUND %lu]", NutGetSeconds() - leaseTime);
01408                 break;
01409             case DHCPST_RENEWING:
01410                 fprintf(__tcp_trs, "RENEWING %lu]", NutGetSeconds() - leaseTime);
01411                 break;
01412             case DHCPST_REBINDING:
01413                 fprintf(__tcp_trs, "REBINDING %lu]", NutGetSeconds() - leaseTime);
01414                 break;
01415             case DHCPST_INFORMING:
01416                 fprintf(__tcp_trs, "INFORMING]");
01417                 break;
01418             case DHCPST_RELEASING:
01419                 fprintf(__tcp_trs, "RELEASING]");
01420                 break;
01421             case DHCPST_IDLE:
01422                 if (dhcpError) {
01423                     fprintf(__tcp_trs, "ERROR %u]", dhcpError);
01424                 } else {
01425                     fprintf(__tcp_trs, "IDLE]");
01426                 }
01427                 break;
01428             default:
01429                 fprintf(__tcp_trs, "UNKNOWN %u]", dhcpState);
01430                 break;
01431             }
01432         }
01433 #endif
01434 
01435         /*
01436          * Setup some values based on the number of retry attempts.
01437          */
01438         server_ip = INADDR_BROADCAST;   /* Broadcasting is default. */
01439         if (retries) {
01440             /* Double our timeout on each retry. */
01441             tmo += tmo;
01442             if (tmo > MAX_DHCP_WAIT) {
01443                 tmo = MAX_DHCP_WAIT;
01444             }
01445         } else {
01446             /* Start with minimum timeout first. */
01447             tmo = MIN_DHCP_WAIT;
01448             /* Use a new xid for the first message in each state except 
01449              * when requesting, where we should continue using the xid 
01450              * from the offer message we received.
01451              */
01452             if (dhcpState != DHCPST_REQUESTING) {
01453                 xid++;
01454             }
01455 
01456             /* If we know the server's IP, try to unicast on the first 
01457                attempt. */
01458             if (dhcpConfig && dhcpConfig->dyn_sid) {
01459                 server_ip = dhcpConfig->dyn_sid;
01460             }
01461         }
01462 
01463         /*
01464          * Keep track of the API timeout.
01465          */
01466         if (dhcpState != DHCPST_IDLE && dhcpApiTimeout != NUT_WAIT_INFINITE) {
01467             uint32_t tt = NutGetMillis() - dhcpApiStart;
01468 
01469             if (dhcpApiTimeout <= tt) {
01470                 dhcpError = DHCPERR_TIMEOUT;
01471                 dhcpState = DHCPST_IDLE;
01472                 continue;
01473             }
01474             if ((tt = dhcpApiTimeout - tt) < tmo) {
01475                 tmo = tt;
01476             }
01477         }
01478 
01479         /*
01480          * Keep track of acquisition time.
01481          */
01482         if (dhcpState == DHCPST_SELECTING || dhcpState == DHCPST_RENEWING || dhcpState == DHCPST_REBINDING) {
01483             /* For retries make sure that secs doesn't overflow. */
01484             if (retries) {
01485                 if (NutGetSeconds() - aqsTime > 0xffffUL) {
01486                     secs = 0xffff;
01487                 } else {
01488                     secs = (uint16_t) (NutGetSeconds() - aqsTime);
01489                 }
01490             }
01491             /* For first transmissions make sure that secs is zero. */
01492             else {
01493                 aqsTime = NutGetSeconds();
01494                 secs = 0;
01495             }
01496         }
01497 
01498         /*
01499          * Release UDP socket and buffer in states with long inactive time.
01500          */
01501         if (dhcpState == DHCPST_BOUND || dhcpState == DHCPST_IDLE) {
01502             if (sock) {
01503                 NutUdpDestroySocket(sock);
01504                 sock = 0;
01505             }
01506             if (bp) {
01507                 free(bp);
01508                 bp = 0;
01509             }
01510         }
01511 
01512         /*
01513          * In all other states we need the socket and the buffer.
01514          */
01515         else {
01516             /*
01517              * Check if something else configured our interface.
01518              */
01519             if (dhcpConfig == 0 && nif->if_local_ip) {
01520                 /* If we need additional configuration, we can sent
01521                    a DHCP Inform message here. */
01522                 dhcpState = DHCPST_IDLE;
01523                 continue;
01524             }
01525 
01526             if (sock == 0 || bp == 0) {
01527                 if (sock == 0) {
01528                     sock = NutUdpCreateSocket(DHCP_CLIENTPORT);
01529                 }
01530                 if (bp == 0) {
01531                     bp = malloc(sizeof(BOOTP));
01532                 }
01533                 if (sock == 0 || bp == 0) {
01534                     /* Looks like we are out of memory. */
01535                     dhcpError = DHCPERR_SYSTEM;
01536                     dhcpState = DHCPST_IDLE;
01537                     /* At this point either socket or buffer may be allocated. 
01538                        Thus we need to jump back to the top of our state loop
01539                        to release it. */
01540                     continue;
01541                 }
01542 #if MAX_DHCP_BUFSIZE
01543                 {
01544                     uint16_t max_ms = MAX_DHCP_BUFSIZE;
01545                     NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
01546                 }
01547 #endif
01548             }
01549         }
01550 
01551         /*
01552          * (Re)Start.
01553          */
01554         if (dhcpState == DHCPST_INIT) {
01555             /* Clear the retry counter. */
01556             retries = 0;
01557             /* Use a new XID on each attempt. */
01558             xid++;
01559             /* Determine whether this is an initial boot or a reboot. */
01560             if ((last_ip & confnet.cdn_ip_mask) == 0) {
01561                 /* No previous IP, start from ground up. */
01562                 dhcpState = DHCPST_SELECTING;
01563             } else {
01564                 /* We got a previously allocated IP configuration from
01565                  * non-volatile configuration memory. Try to re-use it. */
01566                 dhcpState = DHCPST_REBOOTING;
01567             }
01568         }
01569 
01570         /*
01571          * Broadcast discover and collect incoming offers.
01572          */
01573         else if (dhcpState == DHCPST_SELECTING) {
01574             if (retries++ > MAX_DCHP_RETRIES) {
01575                 /* Too many retries while discovering DHCP. Give up. */
01576                 dhcpError = DHCPERR_TIMEOUT;
01577                 dhcpState = DHCPST_IDLE;
01578             }
01579             /* Send the discovering broadcast. */
01580             else if (DhcpBroadcastDiscover(sock, bp, xid, last_ip, secs) < 0) {
01581                 /* Fatal transmit error on broadcast. */
01582                 dhcpState = DHCPST_IDLE;
01583             } else {
01584                 /* Collect incoming offers. */
01585                 while ((n = DhcpRecvMessage(sock, xid, bp, tmo)) > 0) {
01586                     /* Check if this is a valid offer. */
01587                     if ((dhcpConfig = CheckOffer(dhcpConfig, bp, n)) != 0) {
01588                         /* If the callers timeout is low, do not collect
01589                            more than one. Thanks to Jelle Kok. */
01590                         if (dhcpApiTimeout < MIN_DHCP_WAIT * 3) {
01591                             break;
01592                         }
01593                         /* Switch to lowest timeout after we received
01594                            a first response. */
01595                         tmo = MIN_DHCP_WAIT;
01596                     }
01597                 }
01598                 /* Change to ERROR state on fatal receive errors. */
01599                 if (n < 0) {
01600                     dhcpState = DHCPST_IDLE;
01601                 }
01602                 /* Change to REQUESTING state if we got a valid offer.
01603                    Otherwise we stay in SELECTING state. */
01604                 else if (dhcpConfig) {
01605                     retries = 0;
01606                     dhcpState = DHCPST_REQUESTING;
01607                 }
01608             }
01609         }
01610 
01611         /*
01612          * Send request and wait for an acknowledge.
01613          */
01614         else if (dhcpState == DHCPST_REQUESTING) {
01615             if (retries++ > MAX_DCHP_RETRIES) {
01616                 /* Too many retries with this server, fall back to discovery. */
01617                 dhcpState = DHCPST_INIT;
01618             }
01619             /* Request an offered configuration. According to RFC 2131 this
01620                has to be broadcasted. */
01621             else if (DhcpBroadcastRequest(sock, bp, xid, 0, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid, secs) < 0) {
01622                 /* Fatal transmit error on broadcast. Give up. */
01623                 dhcpState = DHCPST_IDLE;
01624             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01625                 /* Fatal receive error. */
01626                 dhcpState = DHCPST_IDLE;
01627             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01628                 /* The server accepted our request. We are bound. */
01629                 if (reply->dyn_msgtyp == DHCP_ACK) {
01630                     ReleaseDynCfg(dhcpConfig);
01631                     dhcpConfig = reply;
01632                     reply = 0;
01633                     leaseTime = aqsTime;
01634                     dhcpState = DHCPST_BOUND;
01635                 }
01636                 /* The server declines a previously offered configuration. 
01637                    Restart discovery. */
01638                 else if (reply->dyn_msgtyp == DHCP_NAK) {
01639                     dhcpState = DHCPST_INIT;
01640                 }
01641             }
01642         }
01643 
01644         /*
01645          * Reusing a previously allocated network address after reboot.
01646          */
01647         else if (dhcpState == DHCPST_REBOOTING) {
01648             if (++retries > MAX_DCHP_RETRIES) {
01649                 /* Too many retries, fall back to discovery. */
01650                 last_ip = 0;
01651                 dhcpState = DHCPST_INIT;
01652             }
01653             /* Broadcast a request for our previous configuration. */
01654             else if (DhcpBroadcastRequest(sock, bp, xid, 0, last_ip, 0, secs) < 0) {
01655                 /* Fatal transmit error on broadcast. Give up. */
01656                 dhcpState = DHCPST_IDLE;
01657             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01658                 /* Fatal receive error. */
01659                 dhcpState = DHCPST_IDLE;
01660             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01661                 if (reply->dyn_msgtyp == DHCP_ACK) {
01662                     ReleaseDynCfg(dhcpConfig);
01663                     dhcpConfig = reply;
01664                     reply = 0;
01665                     leaseTime = aqsTime;
01666                     dhcpState = DHCPST_BOUND;
01667                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01668                     /* Either our previous address had been allocated by
01669                        someone else or we changed the network. Remove the
01670                        previous address and restart. */
01671                     last_ip = 0;
01672                     dhcpState = DHCPST_INIT;
01673                 }
01674             }
01675         }
01676 
01677         /*
01678          * Maintain lease time.
01679          */
01680         else if (dhcpState == DHCPST_BOUND) {
01681             retries = 0;
01682             NutEventBroadcast(&dhcpDone);
01683             if (dhcpConfig->dyn_renewalTime <= NutGetSeconds() - leaseTime) {
01684                 dhcpState = DHCPST_RENEWING;
01685             } else {
01686                 /* Calculate the remaining lease time and take a nap. */
01687                 napTime = dhcpConfig->dyn_renewalTime - (NutGetSeconds() - leaseTime);
01688                 if (napTime > MAX_DHCP_NAPTIME) {
01689                     napTime = MAX_DHCP_NAPTIME;
01690                 }
01691                 NutEventWait(&dhcpWake, napTime * 1000UL);
01692             }
01693         }
01694 
01695         /*
01696          * Waiting for an acknowledge of our renewal request.
01697          */
01698         else if (dhcpState == DHCPST_RENEWING) {
01699             retries++;
01700             if (tmo / 1000 > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
01701                 tmo = dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime) * 1000;
01702             }
01703             if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
01704                 retries = 0;
01705                 dhcpState = DHCPST_REBINDING;
01706             }
01707             /* Send a request to our leasing server. We must not include
01708                the server identifier. */
01709             else if (DhcpSendRequest(sock, dhcpConfig->dyn_sid, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) <
01710                      0) {
01711                 /* Unicast transmit error. */
01712                 retries = 0;
01713                 dhcpState = DHCPST_REBINDING;
01714             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01715                 /* Fatal receive error. */
01716                 dhcpState = DHCPST_IDLE;
01717             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01718                 if (reply->dyn_msgtyp == DHCP_ACK) {
01719                     /* Got an acknowledge, return to bound state. */
01720                     ReleaseDynCfg(dhcpConfig);
01721                     dhcpConfig = reply;
01722                     reply = 0;
01723                     leaseTime = aqsTime;
01724                     dhcpState = DHCPST_BOUND;
01725                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01726                     /* Unexpected NAK. */
01727                     retries = 0;
01728                     dhcpState = DHCPST_REBINDING;
01729                 }
01730             }
01731         }
01732 
01733         /*
01734          * Waiting for an acknowledge of our rebind request.
01735          */
01736         else if (dhcpState == DHCPST_REBINDING) {
01737             retries++;
01738             if (tmo > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
01739                 tmo = dhcpConfig->dyn_rebindTime - NutGetSeconds() - leaseTime;
01740             }
01741             if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
01742                 retries = 0;
01743                 dhcpState = DHCPST_REBINDING;
01744             }
01745             /* Broadcast a request for our previous configuration. We 
01746                must not include a server identifier. */
01747             else if (DhcpBroadcastRequest(sock, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) < 0) {
01748                 /* Fatal transmit error on broadcast. Give up. */
01749                 dhcpState = DHCPST_IDLE;
01750             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01751                 /* Fatal receive error. */
01752                 dhcpState = DHCPST_IDLE;
01753             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01754                 if (reply->dyn_msgtyp == DHCP_ACK) {
01755                     /* Got an acknowledge, return to bound state. */
01756                     ReleaseDynCfg(dhcpConfig);
01757                     dhcpConfig = reply;
01758                     reply = 0;
01759                     leaseTime = aqsTime;
01760                     dhcpState = DHCPST_BOUND;
01761                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01762                     /*
01763                      * We have a problem here if the last DHCP server died. 
01764                      * If a backup server exists, it may probe our IP address
01765                      * using ARP or ICMP. Our interface is up and responding,
01766                      * so the backup server may think that the IP address
01767                      * is in use and respond with NAK. Without shutting
01768                      * down our interface (not yet implemented) we are stuck.
01769                      * We switch to discovery state, but the problem remains.
01770                      */
01771                     dhcpState = DHCPST_INIT;
01772                 }
01773             }
01774         }
01775 
01776         /*
01777          * Send an inform and wait for its (optional) echo.
01778          */
01779         else if (dhcpState == DHCPST_INFORMING) {
01780             if (retries++ > MAX_DCHP_RETRIES) {
01781                 dhcpState = DHCPST_IDLE;
01782             } else if (DhcpSendInform(sock, server_ip, bp, xid, nif->if_local_ip) < 0) {
01783                 if (server_ip == INADDR_BROADCAST) {
01784                     dhcpState = DHCPST_IDLE;
01785                 }
01786             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) != 0) {
01787                 if (n > 0 &&    /* No receive error. */
01788                     (reply = ParseReply(bp, n)) != 0 && /* No parser error. */
01789                     reply->dyn_msgtyp == DHCP_ACK) {    /* Acknowledged. */
01790                     /* Take over this configuration. */
01791                     ReleaseDynCfg(dhcpConfig);
01792                     dhcpConfig = reply;
01793                     reply = 0;
01794                 }
01795                 dhcpState = DHCPST_IDLE;
01796             }
01797         }
01798 
01799         /*
01800          * Send a release and wait for its (optional) echo.
01801          */
01802         else if (dhcpState == DHCPST_RELEASING) {
01803             if (dhcpConfig == 0 ||      /* Not configured. */
01804                 retries++ > MAX_DCHP_RELEASE_RETRIES || /* Too many retries. */
01805                 DhcpSendRelease(sock, server_ip, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid) < 0) {
01806                 if (server_ip == INADDR_BROADCAST) {
01807                     dhcpState = DHCPST_IDLE;
01808                 }
01809             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01810                 /* Fatal receive error. */
01811                 dhcpState = DHCPST_IDLE;
01812             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01813                 if (reply->dyn_msgtyp == DHCP_ACK) {
01814                     dhcpState = DHCPST_IDLE;
01815                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01816                     dhcpState = DHCPST_IDLE;
01817                 }
01818             }
01819         }
01820 
01821         /*
01822          * We are done somehow. Either a fatal error occured or we 
01823          * reached the specified timeout time or our lease has been
01824          * release or something else configured our interface.
01825          * Release all resources and wait for a new API call to
01826          * wake us up.
01827          */
01828         else if (dhcpState == DHCPST_IDLE) {
01829             ReleaseDynCfg(dhcpConfig);
01830             dhcpConfig = 0;
01831             retries = 0;
01832             NutEventBroadcast(&dhcpDone);
01833             NutEventWait(&dhcpWake, NUT_WAIT_INFINITE);
01834         }
01835 
01836         /* Release any received reply. */
01837         if (reply) {
01838             ReleaseDynCfg(reply);
01839             reply = 0;
01840         }
01841     }
01842 }
01843 
01858 static int DhcpKick(CONST char *name, uint8_t state, uint32_t timeout)
01859 {
01860     NUTDEVICE *dev;
01861     IFNET *nif;
01862 
01863     /* Lookup the Ethernet device. */
01864     if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
01865         dev->dev_type != IFTYP_NET ||   /* Wrong type */
01866         (nif = dev->dev_icb) == 0 ||    /* No netif */
01867         nif->if_type != IFT_ETHER) {    /* Wrong if type */
01868         dhcpError = DHCPERR_BADDEV;
01869         return -1;
01870     }
01871 
01872     /* Initialize timeout checking. */
01873     dhcpApiStart = NutGetMillis();
01874     dhcpApiTimeout = timeout;
01875 
01876     dhcpState = state;
01877     if (dhcpThread == 0) {
01878 #ifdef __arm__
01879         dhcpDev = dev;
01880 #endif
01881         dhcpThread = NutThreadCreate("dhcpc", NutDhcpClient, dev, 
01882             (NUT_THREAD_DHCPSTACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD);
01883     }
01884     NutEventPost(&dhcpWake);
01885     NutEventWait(&dhcpDone, NUT_WAIT_INFINITE);
01886 
01887     return 0;
01888 }
01889 
01929 int NutDhcpIfConfig(CONST char *name, uint8_t * mac, uint32_t timeout)
01930 {
01931     uint8_t mac0[6];
01932     uint8_t macF[6];
01933     NUTDEVICE *dev;
01934     IFNET *nif;
01935 
01936     /*
01937      * Lookup the Ethernet device.
01938      */
01939     if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
01940         dev->dev_type != IFTYP_NET ||   /* Wrong type */
01941         (nif = dev->dev_icb) == 0 ||    /* No netif */
01942         nif->if_type != IFT_ETHER) {    /* Wrong if type */
01943         dhcpError = DHCPERR_BADDEV;
01944         return -1;
01945     }
01946 
01947     /*
01948      * We determine whether the interface is enabled by checking
01949      * the MAC address. This is so bloody brain dead.
01950      */
01951     memset(mac0, 0x00, sizeof(mac0));   /* Uses more code but less RAM... */
01952     memset(macF, 0xFF, sizeof(macF));   /* ...than init in declaration.   */
01953     if (memcmp(nif->if_mac, mac0, 6) == 0 || memcmp(nif->if_mac, macF, 6) == 0) {
01954         /*
01955          * If the caller specified a MAC address, we use it and
01956          * overwrite the configuration.
01957          */
01958         if (mac) {
01959             memcpy(confnet.cdn_mac, mac, sizeof(confnet.cdn_mac));
01960         }
01961 
01962         /*
01963          * If no MAC address has been specified, read the configuration 
01964          * from EEPROM. If this fails, we do not continue any further,
01965          * but let the caller know that something is wrong. He may call
01966          * us again with a valid MAC address.
01967          */
01968         else if (NutNetLoadConfig(name)) {
01969             dhcpError = DHCPERR_NOMAC;
01970             return -1;
01971         }
01972 
01973         /*
01974          * Copy the MAC address to the interface structure. This will
01975          * magically brain dead enable the interface.
01976          */
01977         memcpy(nif->if_mac, confnet.cdn_mac, 6);
01978         NutSleep(500);
01979     }
01980 
01981     /*
01982      * If the EEPROM contains a fixed network configuration, we skip DHCP.
01983      */
01984     if ((confnet.cdn_cip_addr & confnet.cdn_ip_mask) != 0) {
01985         confnet.cdn_ip_addr = confnet.cdn_cip_addr;
01986         NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
01987         return 0;
01988     }
01989 
01990     /*
01991      * Start the DHCP thread if not running or wake it up. Pass the caller's 
01992      * timeout to the thread and wait an infinite time. We rely on the thread 
01993      * to wake us up on timeout.
01994      */
01995     if (DhcpKick(name, DHCPST_INIT, timeout) == 0) {
01996         /*
01997          * The thread finished its task. If it reached the bound state, then
01998          * we got a valid configuration from DHCP.
01999          */
02000         if (dhcpState == DHCPST_BOUND) {
02001 #ifdef NUTDEBUG
02002             if (__tcp_trf) {
02003                 fprintf(__tcp_trs, "[DHCP-Config %s]", inet_ntoa(dhcpConfig->dyn_yiaddr));
02004             }
02005 #endif
02006             NutNetIfSetup(dev, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_netmask, dhcpConfig->dyn_gateway);
02007             NutDnsConfig2(0, 0, dhcpConfig->dyn_pdns, dhcpConfig->dyn_sdns);
02008             return 0;
02009         }
02010 
02011         /*
02012          * Our interface has been configured externally, possibly by auto 
02013          * ARP or a similar function implemented by the application.
02014          */
02015         if (nif->if_local_ip) {
02016 #ifdef NUTDEBUG
02017             if (__tcp_trf) {
02018                 fprintf(__tcp_trs, "[DHCP-External %s]", inet_ntoa(nif->if_local_ip));
02019             }
02020 #endif
02021             return 0;
02022         }
02023 
02024         /*
02025          * DHCP failed. In case we remember a previously allocated address, 
02026          * then let's use it.
02027          */
02028         if ((confnet.cdn_ip_addr & confnet.cdn_ip_mask) != 0) {
02029 #ifdef NUTDEBUG
02030             if (__tcp_trf) {
02031                 fprintf(__tcp_trs, "[DHCP-Reusing %s]", inet_ntoa(confnet.cdn_ip_addr));
02032             }
02033 #endif
02034             NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
02035             return 0;
02036         }
02037     }
02038     return -1;
02039 }
02040 
02057 int NutDhcpRelease(CONST char *name, uint32_t timeout)
02058 {
02059     /* Check the state. */
02060     if (dhcpState != DHCPST_BOUND) {
02061         dhcpError = DHCPERR_STATE;
02062         return -1;
02063     }
02064 
02065     /* Action! */
02066     return DhcpKick(name, DHCPST_RELEASING, timeout);
02067 }
02068 
02079 int NutDhcpInform(CONST char *name, uint32_t timeout)
02080 {
02081     /* Check the state. */
02082     if (dhcpState != DHCPST_IDLE) {
02083         dhcpError = DHCPERR_STATE;
02084         return -1;
02085     }
02086 
02087     /* Action! */
02088     return DhcpKick(name, DHCPST_INFORMING, timeout);
02089 }
02090 
02108 int NutDhcpStatus(CONST char *name)
02109 {
02110     return dhcpState;
02111 }
02112 
02131 int NutDhcpError(CONST char *name)
02132 {
02133     int rc = dhcpError;
02134     dhcpError = 0;
02135     return rc;
02136 }
02137 
02145 int NutDhcpIsConfigured(void)
02146 {
02147     return (dhcpState == DHCPST_BOUND);
02148 }
02149 

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/