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 
00185 #include <sys/thread.h>
00186 #include <sys/event.h>
00187 #include <sys/timer.h>
00188 #include <sys/confnet.h>
00189 #include <sys/confos.h>
00190 
00191 #include <stdlib.h>
00192 #include <string.h>
00193 #include <time.h>
00194 
00195 #include <arpa/inet.h>
00196 #include <netinet/in.h>
00197 #include <netdb.h>
00198 #include <net/route.h>
00199 #include <sys/socket.h>
00200 #include <pro/dhcp.h>
00201 
00202 #ifdef NUTDEBUG
00203 #include <net/netdebug.h>
00204 #endif
00205 
00206 #if 0
00207 /* Use for local debugging. */
00208 #define NUTDEBUG
00209 #include <stdio.h>
00210 #define __tcp_trs stdout
00211 static uint_fast8_t __tcp_trf = 1;
00212 #endif
00213 
00218 
00225 
00231 #ifndef DHCP_SERVERPORT
00232 #define DHCP_SERVERPORT      67
00233 #endif
00234 
00240 #ifndef DHCP_CLIENTPORT
00241 #define DHCP_CLIENTPORT      68
00242 #endif
00243 
00253 #ifndef MAX_DHCP_MSGSIZE
00254 #define MAX_DHCP_MSGSIZE    576
00255 #endif
00256 
00264 #ifndef MIN_DHCP_MSGSIZE
00265 #define MIN_DHCP_MSGSIZE    300
00266 #endif
00267 
00283 #ifndef MAX_DHCP_BUFSIZE
00284 #define MAX_DHCP_BUFSIZE    1728
00285 #endif
00286 
00296 #ifndef MIN_DHCP_WAIT
00297 #define MIN_DHCP_WAIT       4000
00298 #endif
00299 
00308 #ifndef MAX_DHCP_WAIT
00309 #define MAX_DHCP_WAIT       64000
00310 #endif
00311 
00320 #ifndef MAX_DCHP_RETRIES
00321 #define MAX_DCHP_RETRIES    3
00322 #endif
00323 
00332 #ifndef MAX_DCHP_RELEASE_RETRIES
00333 #define MAX_DCHP_RELEASE_RETRIES    0
00334 #endif
00335 
00343 #ifndef DHCP_DEFAULT_LEASE
00344 #define DHCP_DEFAULT_LEASE  43200
00345 #endif
00346 
00352 #ifndef MAX_DHCP_NAPTIME
00353 #define MAX_DHCP_NAPTIME    4294967
00354 #endif
00355 
00361 #ifndef NUT_THREAD_DHCPSTACK
00362 #define NUT_THREAD_DHCPSTACK    512
00363 #endif
00364 
00373 
00376 #define DHCP_DISCOVER   1
00377 
00382 #define DHCP_OFFER      2
00383 
00391 #define DHCP_REQUEST    3
00392 
00397 #define DHCP_DECLINE    4
00398 
00403 #define DHCP_ACK        5
00404 
00409 #define DHCP_NAK        6
00410 
00413 #define DHCP_RELEASE    7
00414 
00419 #define DHCP_INFORM     8
00420 
00430 
00437 #define DHCPOPT_PAD          0
00438 
00442 #define DHCPOPT_NETMASK      1
00443 
00447 #define DHCPOPT_GATEWAY      3
00448 
00452 #define DHCPOPT_DNS          6
00453 
00457 #define DHCPOPT_HOSTNAME     12
00458 
00462 #define DHCPOPT_DOMAIN       15
00463 
00467 #define DHCPOPT_BROADCAST    28
00468 
00472 #define DHCPOPT_REQESTIP     50
00473 
00477 #define DHCPOPT_LEASETIME    51
00478 
00482 #define DHCPOPT_MSGTYPE      53
00483 
00487 #define DHCPOPT_SID          54
00488 
00492 #define DHCPOPT_PARAMREQUEST 55
00493 
00497 #define DHCPOPT_MAXMSGSIZE   57
00498 
00502 #define DHCPOPT_RENEWALTIME  58
00503 
00507 #define DHCPOPT_REBINDTIME   59
00508 
00512 #define DHCPOPT_END          255
00513 
00519 typedef struct __attribute__ ((packed)) bootp BOOTP;
00520 
00524 struct __attribute__ ((packed)) bootp {
00525     uint8_t bp_op;              
00526     uint8_t bp_htype;           
00527     uint8_t bp_hlen;            
00528     uint8_t bp_hops;            
00529     uint32_t bp_xid;              
00530     uint16_t bp_secs;            
00531     uint16_t bp_flags;           
00532     uint32_t bp_ciaddr;           
00533     uint32_t bp_yiaddr;           
00534     uint32_t bp_siaddr;           
00535     uint32_t bp_giaddr;           
00536     uint8_t bp_chaddr[16];      
00537     char bp_sname[64];          
00538     char bp_file[128];          
00539     uint8_t bp_options[312];    
00540 };
00541 
00545 typedef struct dyn_cfg DYNCFG;
00546 
00550 struct dyn_cfg {
00551     uint8_t dyn_msgtyp;         
00552     uint32_t dyn_yiaddr;          
00553     uint32_t dyn_netmask;         
00554     uint32_t dyn_broadcast;       
00555     uint32_t dyn_gateway;         
00556     uint32_t dyn_pdns;            
00557     uint32_t dyn_sdns;            
00558     uint32_t dyn_sid;             
00559     uint32_t dyn_renewalTime;     
00560     uint32_t dyn_rebindTime;      
00561     uint32_t dyn_leaseTime;       
00562     uint8_t *dyn_hostname;      
00563     uint8_t *dyn_domain;        
00564 };
00565 
00572 static DYNCFG *dhcpConfig;
00573 
00580 static HANDLE dhcpThread;
00581 
00585 static uint8_t dhcpState;
00586 
00590 static int dhcpError;
00591 
00597 static HANDLE dhcpWake;
00598 
00604 static HANDLE dhcpDone;
00605 
00611 static uint32_t dhcpApiTimeout;
00612 
00619 static uint32_t dhcpApiStart;
00620 
00627 #ifdef __arm__
00628 static NUTDEVICE *dhcpDev;
00629 #endif
00630 
00642 static void copy_str(uint8_t ** dst, void *src, int len)
00643 {
00644     if (*dst) {
00645         free(*dst);
00646     }
00647     if ((*dst = malloc(len + 1)) != 0) {
00648         if (len) {
00649             memcpy(*dst, src, len);
00650         }
00651         *(*dst + len) = 0;
00652     }
00653 }
00654 
00662 static void ReleaseDynCfg(DYNCFG * dyncfg)
00663 {
00664     if (dyncfg) {
00665         if (dyncfg->dyn_hostname) {
00666             free(dyncfg->dyn_hostname);
00667         }
00668         if (dyncfg->dyn_domain) {
00669             free(dyncfg->dyn_domain);
00670         }
00671         free(dyncfg);
00672     }
00673 }
00674 
00686 static DYNCFG *ParseReply(BOOTP *bp, int len)
00687 {
00688     uint8_t *op;
00689     int left;
00690     DYNCFG *cfgp;
00691 
00692     /* Allocate and initialize a new structure. */
00693     if ((cfgp = malloc(sizeof(DYNCFG))) == 0) {
00694         return 0;
00695     }
00696     memset(cfgp, 0, sizeof(DYNCFG));
00697     cfgp->dyn_leaseTime = DHCP_DEFAULT_LEASE;
00698 
00699     /* Set the assigned IP address. */
00700     memcpy(&cfgp->dyn_yiaddr, &bp->bp_yiaddr, 4);
00701 
00702     /* 
00703      * Parse options until an end option is found or until we reached
00704      * the end of the message.
00705      */
00706     op = bp->bp_options + 4;
00707     left = len - (sizeof(*bp) - sizeof(bp->bp_options)) - 4;
00708     while (*op != DHCPOPT_END && left > 0) {
00709         uint8_t ol;
00710 
00711 #ifdef NUTDEBUG
00712         if (__tcp_trf) {
00713             fprintf(__tcp_trs, "[DHCP-Opt-%u]", *op);
00714         }
00715 #endif
00716         /* Pad option is used for boundary alignment. */
00717         if (*op == DHCPOPT_PAD) {
00718             op++;
00719             left--;
00720             continue;
00721         }
00722 
00723         /* Reject if option length exceeds total length. */
00724         if ((ol = *(op + 1)) > left) {
00725             break;
00726         }
00727 
00728         /* Type of this message. */
00729         if (*op == DHCPOPT_MSGTYPE) {
00730             if (ol != 1) {
00731                 break;
00732             }
00733             cfgp->dyn_msgtyp = *(op + 2);
00734         }
00735         /* Our host name. May or may not include the domain. */
00736         else if (*op == DHCPOPT_HOSTNAME) {
00737             copy_str(&cfgp->dyn_hostname, op + 2, ol);
00738         }
00739         /* Name of the domain we are in. */
00740         else if (*op == DHCPOPT_DOMAIN) {
00741             copy_str(&cfgp->dyn_domain, op + 2, ol);
00742         }
00743 
00744         /* All remaining options require at least 4 octets. */
00745         else if (ol >= 4) {
00746             /* Preset most often used long value. */
00747             uint32_t lval = *(op + 2);
00748             lval += (uint32_t)(*(op + 3)) << 8;
00749             lval += (uint32_t)(*(op + 4)) << 16;
00750             lval += (uint32_t)(*(op + 5)) << 24;
00751 
00752             /* Our IP network mask. */
00753             if (*op == DHCPOPT_NETMASK) {
00754                 cfgp->dyn_netmask = lval;
00755             }
00756             /* Our IP broadcast address. */
00757             else if (*op == DHCPOPT_BROADCAST) {
00758                 cfgp->dyn_broadcast = lval;
00759             }
00760             /* Our IP default gate. More than one gateway may be 
00761                specified. We take the fist one only and ignore the 
00762                rest. */
00763             else if (*op == DHCPOPT_GATEWAY) {
00764                 cfgp->dyn_gateway = lval;
00765             }
00766             /* Our DNS server. Updated by Jelle Martijn Kok to 
00767                support a secondary DNS. */
00768             else if (*op == DHCPOPT_DNS) {
00769                 cfgp->dyn_pdns = lval;
00770                 if (ol >= 8) {
00771                     cfgp->dyn_sdns = *(op + 6);
00772                     cfgp->dyn_sdns += (uint32_t)(*(op + 7)) << 8;
00773                     cfgp->dyn_sdns += (uint32_t)(*(op + 8)) << 16;
00774                     cfgp->dyn_sdns += (uint32_t)(*(op + 9)) << 24;
00775                 }
00776             }
00777             /* Server identifier. */
00778             else if (*op == DHCPOPT_SID) {
00779                 cfgp->dyn_sid = lval;
00780             }
00781             /* Renewal time. */
00782             else if (*op == DHCPOPT_RENEWALTIME) {
00783                 cfgp->dyn_renewalTime = ntohl(lval);
00784             }
00785             /* Rebinding time. */
00786             else if (*op == DHCPOPT_REBINDTIME) {
00787                 cfgp->dyn_rebindTime = ntohl(lval);
00788             }
00789             /* Total lease time granted. */
00790             else if (*op == DHCPOPT_LEASETIME) {
00791                 cfgp->dyn_leaseTime = ntohl(lval);
00792             }
00793         }
00794         op += ol + 2;
00795         left -= ol + 2;
00796     }
00797 
00798     /*
00799      * Discard this configuration if parsing stopped before reaching 
00800      * the end option or if we didn't receive an expected message type.
00801      */
00802     if (*op != DHCPOPT_END ||   /* */
00803         (cfgp->dyn_msgtyp != DHCP_OFFER &&      /* */
00804          cfgp->dyn_msgtyp != DHCP_ACK &&        /* */
00805          cfgp->dyn_msgtyp != DHCP_NAK)) {
00806 #ifdef NUTDEBUG
00807         if (__tcp_trf) {
00808             fprintf(__tcp_trs, "[DHCP-Parse Error]");
00809         }
00810 #endif
00811         ReleaseDynCfg(cfgp);
00812         return 0;
00813     }
00814 
00815     /* Calculate renewal and rebind times. */
00816     if (cfgp->dyn_renewalTime == 0) {
00817         cfgp->dyn_renewalTime = cfgp->dyn_leaseTime / 2;
00818     }
00819     if (cfgp->dyn_rebindTime == 0) {
00820         cfgp->dyn_rebindTime = cfgp->dyn_renewalTime +  /* */
00821             cfgp->dyn_renewalTime / 2 + /* */
00822             cfgp->dyn_renewalTime / 4;
00823     }
00824     return cfgp;
00825 }
00826 
00837 static size_t DhcpAddOption(uint8_t * op, uint8_t ot, void *ov, uint8_t len)
00838 {
00839     *op++ = ot;
00840     *op++ = len;
00841     memcpy(op, ov, len);
00842 
00843     return 2 + len;
00844 }
00845 
00855 static size_t DhcpAddByteOption(uint8_t * op, uint8_t ot, uint8_t ov)
00856 {
00857     *op++ = ot;
00858     *op++ = 1;
00859     *op++ = ov;
00860 
00861     return 3;
00862 }
00863 
00874 static size_t DhcpAddShortOption(uint8_t * op, uint8_t ot, uint16_t ov)
00875 {
00876     *op++ = ot;
00877     *op++ = 2;
00878     ov = htons(ov);
00879     memcpy(op, &ov, 2);
00880 
00881     return 4;
00882 }
00883 
00896 static size_t DhcpAddParmReqOption(uint8_t * op)
00897 {
00898     *op++ = DHCPOPT_PARAMREQUEST;
00899     *op++ = 3;                  /* Adjust when adding more options! */
00900     *op++ = DHCPOPT_NETMASK;    /* Typically sent by default, but play safe. */
00901     *op++ = DHCPOPT_GATEWAY;    /* We want a default gateway. */
00902     *op++ = DHCPOPT_DNS;        /* We want the DNS' IP. */
00903     return 5;                   /* Adjust when adding more options! */
00904 }
00905 
00927 static u_int DhcpPrepHeader(BOOTP *bp, uint8_t msgtyp, uint32_t xid, uint32_t ciaddr, uint16_t secs)
00928 {
00929     uint8_t *op;
00930 
00931     memset(bp, 0, sizeof(*bp));
00932     /* Clients send bootp requests (op code 1) only. */
00933     bp->bp_op = 1;
00934     /* Ethernet addresses are type 1 with 6 octets. */
00935     bp->bp_htype = 1;
00936     bp->bp_hlen = 6;
00937     memcpy(bp->bp_chaddr, confnet.cdn_mac, 6);
00938     /* Transaction identifier. */
00939     bp->bp_xid = xid;
00940     /* Seconds elapsed since address acquisition. */
00941     bp->bp_secs = htons(secs);
00942 
00943 #ifdef DHCP_BROADCAST_FLAG
00944     /*
00945      * We do not need the broadcast flag, because our stack accepts IP 
00946      * messages to any destination if no local address has been assigned,
00947      * However, we continue supporting this compile time option.
00948      */
00949     bp->bp_flags = htons(0x8000);
00950 #endif
00951 
00952     bp->bp_ciaddr = ciaddr;
00953 
00954     /* Add the DHCP magic cookie according to RFC 1497. */
00955     op = bp->bp_options;
00956     *op++ = 0x63;
00957     *op++ = 0x82;
00958     *op++ = 0x53;
00959     *op++ = 0x63;
00960 
00961     /* Add the DHCP message type option. */
00962     return DhcpAddByteOption(op, DHCPOPT_MSGTYPE, msgtyp) + 4;
00963 }
00964 
00983 static int DhcpSendMessage(UDPSOCKET * sock, uint32_t addr, BOOTP *bp, size_t len)
00984 {
00985     /* Add 'end of options'. */
00986     bp->bp_options[len++] = DHCPOPT_END;
00987 
00988     /* Maintain a BOOTP compatible minimum packet size of 300 octets. 
00989        Thanks to Tomohiro Haraikawa. */
00990     if ((len += sizeof(BOOTP) - sizeof(bp->bp_options)) < MIN_DHCP_MSGSIZE) {
00991         len = MIN_DHCP_MSGSIZE;
00992     }
00993 #ifdef NUTDEBUG
00994     if (__tcp_trf) {
00995         fprintf(__tcp_trs, "[DHCP-Send to %s]", inet_ntoa(addr));
00996     }
00997 #endif
00998     if (NutUdpSendTo(sock, addr, DHCP_SERVERPORT, bp, len) < 0) {
00999         dhcpError = DHCPERR_TRANSMIT;
01000         return -1;
01001     }
01002     return 0;
01003 }
01004 
01018 static int DhcpRecvMessage(UDPSOCKET * sock, uint32_t xid, BOOTP *bp, uint32_t tmo)
01019 {
01020     int rc;
01021     uint16_t port;
01022     uint32_t addr;
01023     uint32_t etim;
01024     uint32_t wtim;
01025 
01026     /* Set our start time. */
01027     etim = NutGetMillis();
01028     /* Set the initial receive timeout. */
01029     wtim = tmo;
01030     for (;;) {
01031         rc = NutUdpReceiveFrom(sock, &addr, &port, bp, sizeof(BOOTP), wtim);
01032 #ifdef NUTDEBUG
01033         if (__tcp_trf) {
01034             if (rc > 0) {
01035                 fprintf(__tcp_trs, "[DHCP-Recv from %s]", inet_ntoa(addr));
01036             } else if (rc < 0) {
01037                 fprintf(__tcp_trs, "[DHCP-Recv Error]");
01038             } else {
01039                 fprintf(__tcp_trs, "[DHCP-Recv Timeout %lu]", tmo);
01040             }
01041         }
01042 #endif
01043         /* Immediately return on receive errors and timeouts. */
01044         if (rc <= 0) {
01045             if (rc < 0) {
01046                 dhcpError = DHCPERR_RECEIVE;
01047             }
01048             break;
01049         }
01050         /* The message must at least include the BOOTP header plus five 
01051            bytes of options (magic and end). We are quite liberal here. */
01052         if (rc > sizeof(BOOTP) - sizeof(bp->bp_options) + 5) {
01053             /* The message must be a BOOTP reply with the expected XID. */
01054             if (bp->bp_op == 2 && bp->bp_xid == xid) {
01055                 /* Message is acceptable. */
01056                 break;
01057             }
01058         }
01059         /* Calculate the remaining timeout for not getting trapped here 
01060            on a busy network, which regularly broadcasts DHCP messages. */
01061         wtim = NutGetMillis() - etim;
01062         if (wtim >= tmo - 250) {
01063             /* Less than 250 ms left, return timeout. */
01064             rc = 0;
01065             break;
01066         }
01067         wtim = tmo - wtim;
01068     }
01069     return rc;
01070 }
01071 
01086 static int DhcpBroadcastDiscover(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, uint32_t raddr, uint16_t secs)
01087 {
01088     size_t optlen;
01089     int len;
01090     uint8_t *op = bp->bp_options;
01091 
01092     optlen = DhcpPrepHeader(bp, DHCP_DISCOVER, xid, 0, secs);
01093 
01094     /* Request a specific IP if one had been assigned previously. */
01095     if (raddr) {
01096         optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
01097     }
01098 
01099     optlen += DhcpAddParmReqOption(op + optlen);
01100 
01101     /* Pass host name if specified in confos structure.  
01102      * Win2k DHCP server can register this as dynamic DNS entry.
01103      * Also viewing DHCP lease table shows something sensible.
01104      */
01105     len = strlen(confos.hostname);
01106     if (len > 0) {
01107         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01108     }
01109 
01110     /* Request a maximum message size. */
01111     optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
01112 
01113     return DhcpSendMessage(sock, INADDR_BROADCAST, bp, optlen);
01114 }
01115 
01116 
01136 static int DhcpSendRequest(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid,        /* */
01137                            uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
01138 {
01139     size_t optlen;
01140     int len;
01141     uint8_t *op = bp->bp_options;
01142 
01143     /* Initialize the BOOTP header. */
01144     optlen = DhcpPrepHeader(bp, DHCP_REQUEST, xid, caddr, secs);
01145 
01146     /* Add specified options. */
01147     if (raddr) {
01148         optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
01149     }
01150     if (sid) {
01151         optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
01152     }
01153     optlen += DhcpAddParmReqOption(op + optlen);
01154 
01155     /* Pass host name if specified in confos structure.  */
01156     /* viewing DHCP lease table shows something sensible. */
01157     len = strlen(confos.hostname);
01158     if (len > 0) {
01159         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01160     }
01161 
01162     return DhcpSendMessage(sock, daddr, bp, optlen);
01163 }
01164 
01183 static int DhcpBroadcastRequest(UDPSOCKET * sock, BOOTP *bp, uint32_t xid, /* */
01184                                 uint32_t caddr, uint32_t raddr, uint32_t sid, uint16_t secs)
01185 {
01186     return DhcpSendRequest(sock, INADDR_BROADCAST, bp, xid, caddr, raddr, sid, secs);
01187 }
01188 
01204 static int DhcpSendRelease(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr, uint32_t sid)
01205 {
01206     size_t optlen;
01207     uint8_t *op = bp->bp_options;
01208 
01209     /* Prepare BOOTP header. 'secs' is set to zero. */
01210     optlen = DhcpPrepHeader(bp, DHCP_RELEASE, xid, caddr, 0);
01211 
01212     /* Optionally add server identifier. */
01213     if (sid) {
01214         optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
01215     }
01216     return DhcpSendMessage(sock, daddr, bp, optlen);
01217 }
01218 
01231 static int DhcpSendInform(UDPSOCKET * sock, uint32_t daddr, BOOTP *bp, uint32_t xid, uint32_t caddr)
01232 {
01233     size_t optlen;
01234     size_t len;
01235     uint8_t *op = bp->bp_options;
01236 
01237     /* Prepare BOOTP header. 'secs' is set to zero. */
01238     optlen = DhcpPrepHeader(bp, DHCP_INFORM, xid, caddr, 0);
01239 
01240     /* Additional options we want. */
01241     optlen += DhcpAddParmReqOption(op + optlen);
01242 
01243     /* Add our configured host name. */
01244     len = strlen(confos.hostname);
01245     if (len > 0) {
01246         optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
01247     }
01248 
01249     /* We should provide the maximum message size. */
01250     optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);
01251 
01252     return DhcpSendMessage(sock, daddr, bp, optlen);
01253 }
01254 
01255 
01266 static DYNCFG *CheckOffer(DYNCFG * dyncfg, BOOTP *bp, size_t len)
01267 {
01268     DYNCFG *offer;
01269 
01270     /* Parse the new offer. If it's invalid, return the current 
01271        configuration. */
01272     if ((offer = ParseReply(bp, len)) == 0) {
01273         return dyncfg;
01274     }
01275 
01276     /* Discard anything which is not an offer. */
01277     if (offer->dyn_msgtyp != DHCP_OFFER) {
01278         ReleaseDynCfg(offer);
01279         return dyncfg;
01280     }
01281 
01282     /* First offer, take it. */
01283     if (dyncfg == 0) {
01284         dyncfg = offer;
01285     }
01286 
01287     /* 
01288      * Check if the new offer is better than the current configuration:
01289      */
01290     else {
01291         /* If we remember a previously allocated IP which isn't in the
01292            current configuration but in the new offer, then let's take 
01293            the new one. */
01294         if (confnet.cdn_ip_addr & confnet.cdn_ip_mask) {
01295             if (dyncfg->dyn_yiaddr != confnet.cdn_ip_addr &&    /* */
01296                 offer->dyn_yiaddr == confnet.cdn_ip_addr) {
01297                 ReleaseDynCfg(dyncfg);
01298                 dyncfg = offer;
01299             }
01300         }
01301         /* In the second place prefer long lease times. */
01302         else if (offer->dyn_leaseTime > dyncfg->dyn_leaseTime) {
01303             ReleaseDynCfg(dyncfg);
01304             dyncfg = offer;
01305         }
01306         /* The new one deosn't offer anything interesting. Discard it. */
01307         else {
01308             ReleaseDynCfg(offer);
01309         }
01310     }
01311     return dyncfg;
01312 }
01313 
01327 THREAD(NutDhcpClient, arg)
01328 {
01329     DYNCFG *reply = 0;
01330     UDPSOCKET *sock = 0;
01331     BOOTP *bp = 0;
01332     int n;
01333     uint32_t xid;
01334     IFNET *nif;
01335     uint16_t secs = 0;
01336     uint32_t aqsTime = NutGetSeconds();
01337     uint32_t leaseTime = 0;
01338     uint32_t napTime;
01339     ureg_t retries;
01340     uint32_t tmo = MIN_DHCP_WAIT;
01341     uint32_t last_ip = confnet.cdn_ip_addr;
01342     uint32_t server_ip;
01343 
01344     /*
01345      * Hack alert: Our ARM port doesn't allow parameter
01346      * passing to threads.
01347      */
01348 #ifdef __arm__
01349     nif = dhcpDev->dev_icb;
01350 #else
01351     nif = ((NUTDEVICE *) arg)->dev_icb;
01352 #endif
01353 
01354     /* 
01355      * Generate a random transaction identifier based on our MAC 
01356      * address with the least significant byte of the MAC address 
01357      * becoming the most significant byte of the identifier. This 
01358      * should give a sufficient unique value when several Ethernuts 
01359      * are started concurrently. 
01360      */
01361     xid = 0;
01362     for (retries = 0; retries < sizeof(xid); retries++) {
01363         xid <<= 8;
01364         xid += nif->if_mac[5 - retries];
01365     }
01366     retries = 0;
01367 
01368     for (;;) {
01369 #ifdef NUTDEBUG
01370         if (__tcp_trf) {
01371             fprintf(__tcp_trs, "\n[%u.DHCP-", retries + 1);
01372             switch (dhcpState) {
01373             case DHCPST_INIT:
01374                 fprintf(__tcp_trs, "INIT]");
01375                 break;
01376             case DHCPST_SELECTING:
01377                 fprintf(__tcp_trs, "SELECTING]");
01378                 break;
01379             case DHCPST_REQUESTING:
01380                 fprintf(__tcp_trs, "REQUESTING]");
01381                 break;
01382             case DHCPST_REBOOTING:
01383                 fprintf(__tcp_trs, "REBOOTING %s]", inet_ntoa(last_ip));
01384                 break;
01385             case DHCPST_BOUND:
01386                 fprintf(__tcp_trs, "BOUND %lu]", NutGetSeconds() - leaseTime);
01387                 break;
01388             case DHCPST_RENEWING:
01389                 fprintf(__tcp_trs, "RENEWING %lu]", NutGetSeconds() - leaseTime);
01390                 break;
01391             case DHCPST_REBINDING:
01392                 fprintf(__tcp_trs, "REBINDING %lu]", NutGetSeconds() - leaseTime);
01393                 break;
01394             case DHCPST_INFORMING:
01395                 fprintf(__tcp_trs, "INFORMING]");
01396                 break;
01397             case DHCPST_RELEASING:
01398                 fprintf(__tcp_trs, "RELEASING]");
01399                 break;
01400             case DHCPST_IDLE:
01401                 if (dhcpError) {
01402                     fprintf(__tcp_trs, "ERROR %u]", dhcpError);
01403                 } else {
01404                     fprintf(__tcp_trs, "IDLE]");
01405                 }
01406                 break;
01407             default:
01408                 fprintf(__tcp_trs, "UNKNOWN %u]", dhcpState);
01409                 break;
01410             }
01411         }
01412 #endif
01413 
01414         /*
01415          * Setup some values based on the number of retry attempts.
01416          */
01417         server_ip = INADDR_BROADCAST;   /* Broadcasting is default. */
01418         if (retries) {
01419             /* Double our timeout on each retry. */
01420             tmo += tmo;
01421             if (tmo > MAX_DHCP_WAIT) {
01422                 tmo = MAX_DHCP_WAIT;
01423             }
01424         } else {
01425             /* Start with minimum timeout first. */
01426             tmo = MIN_DHCP_WAIT;
01427             /* Use a new xid for the first message in each state except 
01428              * when requesting, where we should continue using the xid 
01429              * from the offer message we received.
01430              */
01431             if (dhcpState != DHCPST_REQUESTING) {
01432                 xid++;
01433             }
01434 
01435             /* If we know the server's IP, try to unicast on the first 
01436                attempt. */
01437             if (dhcpConfig && dhcpConfig->dyn_sid) {
01438                 server_ip = dhcpConfig->dyn_sid;
01439             }
01440         }
01441 
01442         /*
01443          * Keep track of the API timeout.
01444          */
01445         if (dhcpState != DHCPST_IDLE && dhcpApiTimeout != NUT_WAIT_INFINITE) {
01446             uint32_t tt = NutGetMillis() - dhcpApiStart;
01447 
01448             if (dhcpApiTimeout <= tt) {
01449                 dhcpError = DHCPERR_TIMEOUT;
01450                 dhcpState = DHCPST_IDLE;
01451                 continue;
01452             }
01453             if ((tt = dhcpApiTimeout - tt) < tmo) {
01454                 tmo = tt;
01455             }
01456         }
01457 
01458         /*
01459          * Keep track of acquisition time.
01460          */
01461         if (dhcpState == DHCPST_SELECTING || dhcpState == DHCPST_RENEWING || dhcpState == DHCPST_REBINDING) {
01462             /* For retries make sure that secs doesn't overflow. */
01463             if (retries) {
01464                 if (NutGetSeconds() - aqsTime > 0xffffUL) {
01465                     secs = 0xffff;
01466                 } else {
01467                     secs = (uint16_t) (NutGetSeconds() - aqsTime);
01468                 }
01469             }
01470             /* For first transmissions make sure that secs is zero. */
01471             else {
01472                 aqsTime = NutGetSeconds();
01473                 secs = 0;
01474             }
01475         }
01476 
01477         /*
01478          * Release UDP socket and buffer in states with long inactive time.
01479          */
01480         if (dhcpState == DHCPST_BOUND || dhcpState == DHCPST_IDLE) {
01481             if (sock) {
01482                 NutUdpDestroySocket(sock);
01483                 sock = 0;
01484             }
01485             if (bp) {
01486                 free(bp);
01487                 bp = 0;
01488             }
01489         }
01490 
01491         /*
01492          * In all other states we need the socket and the buffer.
01493          */
01494         else {
01495             /*
01496              * Check if something else configured our interface.
01497              */
01498             if (dhcpConfig == 0 && nif->if_local_ip) {
01499                 /* If we need additional configuration, we can sent
01500                    a DHCP Inform message here. */
01501                 dhcpState = DHCPST_IDLE;
01502                 continue;
01503             }
01504 
01505             if (sock == 0 || bp == 0) {
01506                 if (sock == 0) {
01507                     sock = NutUdpCreateSocket(DHCP_CLIENTPORT);
01508                 }
01509                 if (bp == 0) {
01510                     bp = malloc(sizeof(BOOTP));
01511                 }
01512                 if (sock == 0 || bp == 0) {
01513                     /* Looks like we are out of memory. */
01514                     dhcpError = DHCPERR_SYSTEM;
01515                     dhcpState = DHCPST_IDLE;
01516                     /* At this point either socket or buffer may be allocated. 
01517                        Thus we need to jump back to the top of our state loop
01518                        to release it. */
01519                     continue;
01520                 }
01521 #if MAX_DHCP_BUFSIZE
01522                 {
01523                     uint16_t max_ms = MAX_DHCP_BUFSIZE;
01524                     NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
01525                 }
01526 #endif
01527             }
01528         }
01529 
01530         /*
01531          * (Re)Start.
01532          */
01533         if (dhcpState == DHCPST_INIT) {
01534             /* Clear the retry counter. */
01535             retries = 0;
01536             /* Use a new XID on each attempt. */
01537             xid++;
01538             /* Determine whether this is an initial boot or a reboot. */
01539             if ((last_ip & confnet.cdn_ip_mask) == 0) {
01540                 /* No previous IP, start from ground up. */
01541                 dhcpState = DHCPST_SELECTING;
01542             } else {
01543                 /* We got a previously allocated IP configuration from
01544                  * non-volatile configuration memory. Try to re-use it. */
01545                 dhcpState = DHCPST_REBOOTING;
01546             }
01547         }
01548 
01549         /*
01550          * Broadcast discover and collect incoming offers.
01551          */
01552         else if (dhcpState == DHCPST_SELECTING) {
01553             if (retries++ > MAX_DCHP_RETRIES) {
01554                 /* Too many retries while discovering DHCP. Give up. */
01555                 dhcpError = DHCPERR_TIMEOUT;
01556                 dhcpState = DHCPST_IDLE;
01557             }
01558             /* Send the discovering broadcast. */
01559             else if (DhcpBroadcastDiscover(sock, bp, xid, last_ip, secs) < 0) {
01560                 /* Fatal transmit error on broadcast. */
01561                 dhcpState = DHCPST_IDLE;
01562             } else {
01563                 /* Collect incoming offers. */
01564                 while ((n = DhcpRecvMessage(sock, xid, bp, tmo)) > 0) {
01565                     /* Check if this is a valid offer. */
01566                     if ((dhcpConfig = CheckOffer(dhcpConfig, bp, n)) != 0) {
01567                         /* If the callers timeout is low, do not collect
01568                            more than one. Thanks to Jelle Kok. */
01569                         if (dhcpApiTimeout < MIN_DHCP_WAIT * 3) {
01570                             break;
01571                         }
01572                         /* Switch to lowest timeout after we received
01573                            a first response. */
01574                         tmo = MIN_DHCP_WAIT;
01575                     }
01576                 }
01577                 /* Change to ERROR state on fatal receive errors. */
01578                 if (n < 0) {
01579                     dhcpState = DHCPST_IDLE;
01580                 }
01581                 /* Change to REQUESTING state if we got a valid offer.
01582                    Otherwise we stay in SELECTING state. */
01583                 else if (dhcpConfig) {
01584                     retries = 0;
01585                     dhcpState = DHCPST_REQUESTING;
01586                 }
01587             }
01588         }
01589 
01590         /*
01591          * Send request and wait for an acknowledge.
01592          */
01593         else if (dhcpState == DHCPST_REQUESTING) {
01594             if (retries++ > MAX_DCHP_RETRIES) {
01595                 /* Too many retries with this server, fall back to discovery. */
01596                 dhcpState = DHCPST_INIT;
01597             }
01598             /* Request an offered configuration. According to RFC 2131 this
01599                has to be broadcasted. */
01600             else if (DhcpBroadcastRequest(sock, bp, xid, 0, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid, secs) < 0) {
01601                 /* Fatal transmit error on broadcast. Give up. */
01602                 dhcpState = DHCPST_IDLE;
01603             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01604                 /* Fatal receive error. */
01605                 dhcpState = DHCPST_IDLE;
01606             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01607                 /* The server accepted our request. We are bound. */
01608                 if (reply->dyn_msgtyp == DHCP_ACK) {
01609                     ReleaseDynCfg(dhcpConfig);
01610                     dhcpConfig = reply;
01611                     reply = 0;
01612                     leaseTime = aqsTime;
01613                     dhcpState = DHCPST_BOUND;
01614                 }
01615                 /* The server declines a previously offered configuration. 
01616                    Restart discovery. */
01617                 else if (reply->dyn_msgtyp == DHCP_NAK) {
01618                     dhcpState = DHCPST_INIT;
01619                 }
01620             }
01621         }
01622 
01623         /*
01624          * Reusing a previously allocated network address after reboot.
01625          */
01626         else if (dhcpState == DHCPST_REBOOTING) {
01627             if (++retries > MAX_DCHP_RETRIES) {
01628                 /* Too many retries, fall back to discovery. */
01629                 last_ip = 0;
01630                 dhcpState = DHCPST_INIT;
01631             }
01632             /* Broadcast a request for our previous configuration. */
01633             else if (DhcpBroadcastRequest(sock, bp, xid, 0, last_ip, 0, secs) < 0) {
01634                 /* Fatal transmit error on broadcast. Give up. */
01635                 dhcpState = DHCPST_IDLE;
01636             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01637                 /* Fatal receive error. */
01638                 dhcpState = DHCPST_IDLE;
01639             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01640                 if (reply->dyn_msgtyp == DHCP_ACK) {
01641                     ReleaseDynCfg(dhcpConfig);
01642                     dhcpConfig = reply;
01643                     reply = 0;
01644                     leaseTime = aqsTime;
01645                     dhcpState = DHCPST_BOUND;
01646                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01647                     /* Either our previous address had been allocated by
01648                        someone else or we changed the network. Remove the
01649                        previous address and restart. */
01650                     last_ip = 0;
01651                     dhcpState = DHCPST_INIT;
01652                 }
01653             }
01654         }
01655 
01656         /*
01657          * Maintain lease time.
01658          */
01659         else if (dhcpState == DHCPST_BOUND) {
01660             retries = 0;
01661             NutEventBroadcast(&dhcpDone);
01662             if (dhcpConfig->dyn_renewalTime <= NutGetSeconds() - leaseTime) {
01663                 dhcpState = DHCPST_RENEWING;
01664             } else {
01665                 /* Calculate the remaining lease time and take a nap. */
01666                 napTime = dhcpConfig->dyn_renewalTime - (NutGetSeconds() - leaseTime);
01667                 if (napTime > MAX_DHCP_NAPTIME) {
01668                     napTime = MAX_DHCP_NAPTIME;
01669                 }
01670                 NutEventWait(&dhcpWake, napTime * 1000UL);
01671             }
01672         }
01673 
01674         /*
01675          * Waiting for an acknowledge of our renewal request.
01676          */
01677         else if (dhcpState == DHCPST_RENEWING) {
01678             retries++;
01679             if (tmo / 1000 > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
01680                 tmo = dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime) * 1000;
01681             }
01682             if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
01683                 retries = 0;
01684                 dhcpState = DHCPST_REBINDING;
01685             }
01686             /* Send a request to our leasing server. We must not include
01687                the server identifier. */
01688             else if (DhcpSendRequest(sock, dhcpConfig->dyn_sid, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) <
01689                      0) {
01690                 /* Unicast transmit error. */
01691                 retries = 0;
01692                 dhcpState = DHCPST_REBINDING;
01693             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01694                 /* Fatal receive error. */
01695                 dhcpState = DHCPST_IDLE;
01696             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01697                 if (reply->dyn_msgtyp == DHCP_ACK) {
01698                     /* Got an acknowledge, return to bound state. */
01699                     ReleaseDynCfg(dhcpConfig);
01700                     dhcpConfig = reply;
01701                     reply = 0;
01702                     leaseTime = aqsTime;
01703                     dhcpState = DHCPST_BOUND;
01704                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01705                     /* Unexpected NAK. */
01706                     retries = 0;
01707                     dhcpState = DHCPST_REBINDING;
01708                 }
01709             }
01710         }
01711 
01712         /*
01713          * Waiting for an acknowledge of our rebind request.
01714          */
01715         else if (dhcpState == DHCPST_REBINDING) {
01716             retries++;
01717             if (tmo > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
01718                 tmo = dhcpConfig->dyn_rebindTime - NutGetSeconds() - leaseTime;
01719             }
01720             if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
01721                 retries = 0;
01722                 dhcpState = DHCPST_REBINDING;
01723             }
01724             /* Broadcast a request for our previous configuration. We 
01725                must not include a server identifier. */
01726             else if (DhcpBroadcastRequest(sock, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) < 0) {
01727                 /* Fatal transmit error on broadcast. Give up. */
01728                 dhcpState = DHCPST_IDLE;
01729             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01730                 /* Fatal receive error. */
01731                 dhcpState = DHCPST_IDLE;
01732             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01733                 if (reply->dyn_msgtyp == DHCP_ACK) {
01734                     /* Got an acknowledge, return to bound state. */
01735                     ReleaseDynCfg(dhcpConfig);
01736                     dhcpConfig = reply;
01737                     reply = 0;
01738                     leaseTime = aqsTime;
01739                     dhcpState = DHCPST_BOUND;
01740                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01741                     /*
01742                      * We have a problem here if the last DHCP server died. 
01743                      * If a backup server exists, it may probe our IP address
01744                      * using ARP or ICMP. Our interface is up and responding,
01745                      * so the backup server may think that the IP address
01746                      * is in use and respond with NAK. Without shutting
01747                      * down our interface (not yet implemented) we are stuck.
01748                      * We switch to discovery state, but the problem remains.
01749                      */
01750                     dhcpState = DHCPST_INIT;
01751                 }
01752             }
01753         }
01754 
01755         /*
01756          * Send an inform and wait for its (optional) echo.
01757          */
01758         else if (dhcpState == DHCPST_INFORMING) {
01759             if (retries++ > MAX_DCHP_RETRIES) {
01760                 dhcpState = DHCPST_IDLE;
01761             } else if (DhcpSendInform(sock, server_ip, bp, xid, nif->if_local_ip) < 0) {
01762                 if (server_ip == INADDR_BROADCAST) {
01763                     dhcpState = DHCPST_IDLE;
01764                 }
01765             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) != 0) {
01766                 if (n > 0 &&    /* No receive error. */
01767                     (reply = ParseReply(bp, n)) != 0 && /* No parser error. */
01768                     reply->dyn_msgtyp == DHCP_ACK) {    /* Acknowledged. */
01769                     /* Take over this configuration. */
01770                     ReleaseDynCfg(dhcpConfig);
01771                     dhcpConfig = reply;
01772                     reply = 0;
01773                 }
01774                 dhcpState = DHCPST_IDLE;
01775             }
01776         }
01777 
01778         /*
01779          * Send a release and wait for its (optional) echo.
01780          */
01781         else if (dhcpState == DHCPST_RELEASING) {
01782             if (dhcpConfig == 0 ||      /* Not configured. */
01783                 retries++ > MAX_DCHP_RELEASE_RETRIES || /* Too many retries. */
01784                 DhcpSendRelease(sock, server_ip, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid) < 0) {
01785                 if (server_ip == INADDR_BROADCAST) {
01786                     dhcpState = DHCPST_IDLE;
01787                 }
01788             } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
01789                 /* Fatal receive error. */
01790                 dhcpState = DHCPST_IDLE;
01791             } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
01792                 if (reply->dyn_msgtyp == DHCP_ACK) {
01793                     dhcpState = DHCPST_IDLE;
01794                 } else if (reply->dyn_msgtyp == DHCP_NAK) {
01795                     dhcpState = DHCPST_IDLE;
01796                 }
01797             }
01798         }
01799 
01800         /*
01801          * We are done somehow. Either a fatal error occured or we 
01802          * reached the specified timeout time or our lease has been
01803          * release or something else configured our interface.
01804          * Release all resources and wait for a new API call to
01805          * wake us up.
01806          */
01807         else if (dhcpState == DHCPST_IDLE) {
01808             ReleaseDynCfg(dhcpConfig);
01809             dhcpConfig = 0;
01810             retries = 0;
01811             NutEventBroadcast(&dhcpDone);
01812             NutEventWait(&dhcpWake, NUT_WAIT_INFINITE);
01813         }
01814 
01815         /* Release any received reply. */
01816         if (reply) {
01817             ReleaseDynCfg(reply);
01818             reply = 0;
01819         }
01820     }
01821 }
01822 
01837 static int DhcpKick(CONST char *name, uint8_t state, uint32_t timeout)
01838 {
01839     NUTDEVICE *dev;
01840     IFNET *nif;
01841 
01842     /* Lookup the Ethernet device. */
01843     if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
01844         dev->dev_type != IFTYP_NET ||   /* Wrong type */
01845         (nif = dev->dev_icb) == 0 ||    /* No netif */
01846         nif->if_type != IFT_ETHER) {    /* Wrong if type */
01847         dhcpError = DHCPERR_BADDEV;
01848         return -1;
01849     }
01850 
01851     /* Initialize timeout checking. */
01852     dhcpApiStart = NutGetMillis();
01853     dhcpApiTimeout = timeout;
01854 
01855     dhcpState = state;
01856     if (dhcpThread == 0) {
01857 #ifdef __arm__
01858         dhcpDev = dev;
01859 #endif
01860         dhcpThread = NutThreadCreate("dhcpc", NutDhcpClient, dev, NUT_THREAD_DHCPSTACK);
01861     }
01862     NutEventPost(&dhcpWake);
01863     NutEventWait(&dhcpDone, NUT_WAIT_INFINITE);
01864 
01865     return 0;
01866 }
01867 
01907 int NutDhcpIfConfig(CONST char *name, uint8_t * mac, uint32_t timeout)
01908 {
01909     uint8_t mac0[6];
01910     uint8_t macF[6];
01911     NUTDEVICE *dev;
01912     IFNET *nif;
01913 
01914     /*
01915      * Lookup the Ethernet device.
01916      */
01917     if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
01918         dev->dev_type != IFTYP_NET ||   /* Wrong type */
01919         (nif = dev->dev_icb) == 0 ||    /* No netif */
01920         nif->if_type != IFT_ETHER) {    /* Wrong if type */
01921         dhcpError = DHCPERR_BADDEV;
01922         return -1;
01923     }
01924 
01925     /*
01926      * We determine whether the interface is enabled by checking
01927      * the MAC address. This is so bloody brain dead.
01928      */
01929     memset(mac0, 0x00, sizeof(mac0));   /* Uses more code but less RAM... */
01930     memset(macF, 0xFF, sizeof(macF));   /* ...than init in declaration.   */
01931     if (memcmp(nif->if_mac, mac0, 6) == 0 || memcmp(nif->if_mac, macF, 6) == 0) {
01932         /*
01933          * If the caller specified a MAC address, we use it and
01934          * overwrite the configuration.
01935          */
01936         if (mac) {
01937             memcpy(confnet.cdn_mac, mac, sizeof(confnet.cdn_mac));
01938         }
01939 
01940         /*
01941          * If no MAC address has been specified, read the configuration 
01942          * from EEPROM. If this fails, we do not continue any further,
01943          * but let the caller know that something is wrong. He may call
01944          * us again with a valid MAC address.
01945          */
01946         else if (NutNetLoadConfig(name)) {
01947             dhcpError = DHCPERR_NOMAC;
01948             return -1;
01949         }
01950 
01951         /*
01952          * Copy the MAC address to the interface structure. This will
01953          * magically brain dead enable the interface.
01954          */
01955         memcpy(nif->if_mac, confnet.cdn_mac, 6);
01956         NutSleep(500);
01957     }
01958 
01959     /*
01960      * If the EEPROM contains a fixed network configuration, we skip DHCP.
01961      */
01962     if ((confnet.cdn_cip_addr & confnet.cdn_ip_mask) != 0) {
01963         confnet.cdn_ip_addr = confnet.cdn_cip_addr;
01964         NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
01965         return 0;
01966     }
01967 
01968     /*
01969      * Start the DHCP thread if not running or wake it up. Pass the caller's 
01970      * timeout to the thread and wait an infinite time. We rely on the thread 
01971      * to wake us up on timeout.
01972      */
01973     if (DhcpKick(name, DHCPST_INIT, timeout) == 0) {
01974         /*
01975          * The thread finished its task. If it reached the bound state, then
01976          * we got a valid configuration from DHCP.
01977          */
01978         if (dhcpState == DHCPST_BOUND) {
01979 #ifdef NUTDEBUG
01980             if (__tcp_trf) {
01981                 fprintf(__tcp_trs, "[DHCP-Config %s]", inet_ntoa(dhcpConfig->dyn_yiaddr));
01982             }
01983 #endif
01984             NutNetIfSetup(dev, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_netmask, dhcpConfig->dyn_gateway);
01985             NutDnsConfig2(0, 0, dhcpConfig->dyn_pdns, dhcpConfig->dyn_sdns);
01986             return 0;
01987         }
01988 
01989         /*
01990          * Our interface has been configured externally, possibly by auto 
01991          * ARP or a similar function implemented by the application.
01992          */
01993         if (nif->if_local_ip) {
01994 #ifdef NUTDEBUG
01995             if (__tcp_trf) {
01996                 fprintf(__tcp_trs, "[DHCP-External %s]", inet_ntoa(nif->if_local_ip));
01997             }
01998 #endif
01999             return 0;
02000         }
02001 
02002         /*
02003          * DHCP failed. In case we remember a previously allocated address, 
02004          * then let's use it.
02005          */
02006         if ((confnet.cdn_ip_addr & confnet.cdn_ip_mask) != 0) {
02007 #ifdef NUTDEBUG
02008             if (__tcp_trf) {
02009                 fprintf(__tcp_trs, "[DHCP-Reusing %s]", inet_ntoa(confnet.cdn_ip_addr));
02010             }
02011 #endif
02012             NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
02013             return 0;
02014         }
02015     }
02016     return -1;
02017 }
02018 
02035 int NutDhcpRelease(CONST char *name, uint32_t timeout)
02036 {
02037     /* Check the state. */
02038     if (dhcpState != DHCPST_BOUND) {
02039         dhcpError = DHCPERR_STATE;
02040         return -1;
02041     }
02042 
02043     /* Action! */
02044     return DhcpKick(name, DHCPST_RELEASING, timeout);
02045 }
02046 
02057 int NutDhcpInform(CONST char *name, uint32_t timeout)
02058 {
02059     /* Check the state. */
02060     if (dhcpState != DHCPST_IDLE) {
02061         dhcpError = DHCPERR_STATE;
02062         return -1;
02063     }
02064 
02065     /* Action! */
02066     return DhcpKick(name, DHCPST_INFORMING, timeout);
02067 }
02068 
02086 int NutDhcpStatus(CONST char *name)
02087 {
02088     return dhcpState;
02089 }
02090 
02109 int NutDhcpError(CONST char *name)
02110 {
02111     int rc = dhcpError;
02112     dhcpError = 0;
02113     return rc;
02114 }
02115 
02123 int NutDhcpIsConfigured(void)
02124 {
02125     return (dhcpState == DHCPST_BOUND);
02126 }
02127 

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