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

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