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

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