dhcpc.c

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

© 2000-2010 by contributors - visit http://www.ethernut.de/